-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Recipe Block #2323
Open
joshbermanssw
wants to merge
39
commits into
main
Choose a base branch
from
recipes-poc
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
✨ Recipe Block #2323
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
2d79307
create recipe block PoC
joshbermanssw 7ca9a73
highlight color
joshbermanssw ea77fec
ui changes
joshbermanssw 21f7d53
1st attempt to make sticky
joshbermanssw 987ad45
fix sticky
joshbermanssw 9b8448e
more ui
joshbermanssw a0a8fa4
lhs rhs height sizing
joshbermanssw 4fb06c6
add instruction click scroll to line
joshbermanssw 381e4d1
rm lhs useRef
joshbermanssw edc5c24
fix LHS heighting
joshbermanssw fc8f77a
Recipe rendering in docs too as .mdx
joshbermanssw 00687fe
sm and md view #1
joshbermanssw 5e4c3d4
highlight lines styling back
joshbermanssw ca71d7f
change tooltip
joshbermanssw fa1b15c
template modifictions
joshbermanssw 1bb6f7d
docAndBlog fixes
joshbermanssw 6397c15
make tool bar sticky
joshbermanssw e475b25
change codeblock name
joshbermanssw e61180f
handle down click arrow
joshbermanssw d6c2abc
bg-color match
joshbermanssw 86ddc7e
down arrow
joshbermanssw 438673c
fixed button and void overlay
joshbermanssw 7386a90
extrapolate hidden logic
joshbermanssw 5a6c891
upd z-index
joshbermanssw e85972d
rm index for RecipeBlock
joshbermanssw 59b2f2a
fix rounding issues
joshbermanssw 05cf771
rm border
joshbermanssw 5fb4993
fix scrolling
joshbermanssw 2b8a080
TinaCMS content update
tina-cloud-app[bot] 63bc6ec
closing animation
joshbermanssw 09e5c02
remove unneccessary content
joshbermanssw 32ad732
use vars instead of arbitrary numbers
joshbermanssw 4166a66
add comment
joshbermanssw a43feca
extrapolate styles
joshbermanssw f5e95be
fix re-render
joshbermanssw 7c6a95e
logic to hide the down button when the container isnt scrollable
joshbermanssw c638276
comments
joshbermanssw 3df6cbc
extrapolate all components
joshbermanssw 9a29fa6
move extrapolated components to different dir
joshbermanssw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
export const RecipeBlock = { | ||
name: 'recipeBlock', | ||
label: 'Recipe Block', | ||
fields: [ | ||
{ | ||
name: 'title', | ||
label: 'Heading Title', | ||
type: 'string', | ||
}, | ||
{ | ||
name: 'description', | ||
label: 'Description', | ||
type: 'string', | ||
}, | ||
{ | ||
name: 'codeblock', | ||
label: 'Code Block', | ||
type: 'rich-text', | ||
}, | ||
{ | ||
name: 'instruction', | ||
label: 'Instruction', | ||
type: 'object', | ||
list: true, | ||
ui: { | ||
itemProps: (item) => { | ||
return { label: item?.header } | ||
}, | ||
}, | ||
fields: [ | ||
{ | ||
name: 'header', | ||
label: 'Header', | ||
type: 'string', | ||
}, | ||
{ | ||
name: 'itemDescription', | ||
label: 'Item Description', | ||
type: 'string', | ||
}, | ||
{ | ||
name: 'codeLineStart', | ||
label: 'Code Line Start', | ||
type: 'number', | ||
description: 'Please note that if you enter negative values, it will highlight from 0 to your end number' | ||
}, | ||
{ | ||
name: 'codeLineEnd', | ||
label: 'Code Line End', | ||
type: 'number', | ||
description: 'Please note that highlighting will not work if your end number is > than your start number' | ||
} | ||
], | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { FaChevronCircleDown } from 'react-icons/fa'; | ||
import { TinaMarkdown } from 'tinacms/dist/rich-text'; | ||
import { customHighlightCSS } from '../styles/RecipeCSS'; | ||
import { CodeToolbar } from '../ui/recipeComponent/RecipeCodeToolBar'; | ||
import CodeBlockWithHighlightLines from '../ui/recipeComponent/RecipeCodeBlockWithHighlight'; | ||
|
||
export const RecipeBlock = ({ data }) => { | ||
const { title, description, codeblock, instruction } = data; | ||
|
||
const [highlightLines, setHighlightLines] = useState(''); | ||
const [clickedInstruction, setClickedInstruction] = useState<number | null>( | ||
null | ||
); | ||
//LHSheight is the height used for the instructions block when the screen is >= 1024px | ||
const [LHSheight, setLHSheight] = useState<string | null>(null); | ||
const [CodeBlockWidth, setCodeBlockWidth] = useState<string | null>(null); | ||
const [isBottomOfInstructions, setIsBottomOfInstructions] = | ||
useState<boolean>(false); | ||
|
||
const codeblockRef = useRef<HTMLDivElement>(null); | ||
const instructionBlockRefs = useRef<HTMLDivElement>(null); //the entire instructions container | ||
const instructionRefs = useRef<(HTMLDivElement | null)[]>([]); //list of individual objects in the instruction block | ||
|
||
useEffect(() => { | ||
const style = document.createElement('style'); | ||
style.textContent = customHighlightCSS; | ||
document.head.appendChild(style); | ||
|
||
return () => { | ||
document.head.removeChild(style); | ||
}; | ||
}, [highlightLines]); | ||
|
||
useEffect(() => { | ||
setLHSheight(`${codeblockRef.current?.offsetHeight}`); | ||
setCodeBlockWidth(`${codeblockRef.current?.offsetWidth}`); | ||
}); | ||
|
||
const checkIfBottom = (event: React.UIEvent<HTMLDivElement>) => { | ||
const { scrollHeight, scrollTop, clientHeight } = event.currentTarget; | ||
setIsBottomOfInstructions(scrollHeight - scrollTop <= clientHeight + 10); | ||
}; | ||
|
||
const handleInstructionClick = ( | ||
index: number, | ||
codeLineStart?: number, | ||
codeLineEnd?: number | ||
) => { | ||
setHighlightLines(`${codeLineStart}-${codeLineEnd}`); | ||
setClickedInstruction(index === clickedInstruction ? null : index); | ||
|
||
const linePixelheight = 24; | ||
const linePixelBuffer = 15; // gives the moving logic some breathing room | ||
|
||
if (codeblockRef.current) { | ||
codeblockRef.current.scrollTo({ | ||
top: linePixelheight * codeLineStart - linePixelBuffer, | ||
behavior: 'smooth', | ||
}); | ||
} | ||
|
||
if (window.innerWidth < 1024 && instructionRefs.current[index]) { | ||
instructionRefs.current[index].scrollIntoView({ | ||
behavior: 'smooth', | ||
block: 'nearest', | ||
}); | ||
} | ||
}; | ||
|
||
const handleDownArrowClick = () => { | ||
const lastInstruction = | ||
instructionRefs.current[instructionRefs.current.length - 1]; | ||
if (lastInstruction) { | ||
lastInstruction.scrollIntoView({ | ||
behavior: 'smooth', | ||
block: 'nearest', | ||
}); | ||
} | ||
}; | ||
|
||
//height used for the instructions container when the screen is < 1024px. Maintains 1:2 ratio of instruction to code | ||
const smAndMbHeight = LHSheight ? `${Number(LHSheight) / 2}px` : null; | ||
|
||
const calculateInstructionsHeight = () => { | ||
return instructionRefs.current.reduce((total, ref) => { | ||
return total + (ref?.offsetHeight || 0); | ||
}, 0); | ||
}; | ||
|
||
const checkIfScrollable = () => { | ||
if (typeof window !== 'undefined' && window.innerWidth < 1024) { | ||
return ( | ||
calculateInstructionsHeight() >= parseInt(smAndMbHeight || '0', 10) //this is necessary because the smAndMbHeight actually has a 'px' suffix, parseInt will remove it | ||
); | ||
} else { | ||
return calculateInstructionsHeight() > parseInt(LHSheight || '0', 10); | ||
} | ||
}; | ||
|
||
|
||
return ( | ||
<div className="recipe-block-container mt-20 relative"> | ||
<div className="title-description px-10"> | ||
<h2 className="font-tuner text-orange-500 text-2xl"> | ||
{title || 'Default Title'} | ||
</h2> | ||
<p className="font-light py-2 text-base"> | ||
{description || 'Default Description'} | ||
</p> | ||
</div> | ||
|
||
<div className="content-wrapper flex flex-col lg:flex-row px-10 items-stretch"> | ||
<div | ||
className="instructions bg-gray-800 relative lg:w-1/3 max-h-50vh flex-shrink-0 flex-grow rounded-tl-xl rounded-br-xl lg:rounded-br-none rounded-tr-xl lg:rounded-tr-none lg:rounded-bl-xl flex flex-col" | ||
ref={instructionBlockRefs} | ||
style={{ | ||
height: | ||
typeof window !== 'undefined' && window.innerWidth >= 1024 | ||
? `${LHSheight}px` | ||
: `${smAndMbHeight}`, | ||
}} | ||
> | ||
<div className={`${isBottomOfInstructions ? 'hidden' : ''}`}> | ||
<div | ||
className={`absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-black opacity-60 lg:rounded-bl-xl pointer-events-none `} | ||
></div> | ||
<FaChevronCircleDown | ||
onClick={handleDownArrowClick} | ||
className={`absolute bottom-4 left-1/2 transform -translate-x-1/2 w-7 h-7 text-xl text-white cursor-pointer shadow-md | ||
${checkIfScrollable() ? '' : 'hidden'}`} | ||
/> | ||
</div> | ||
|
||
<div | ||
className="overflow-auto rounded-tl-xl rounded-bl-xl rounded-tr-xl lg:rounded-tr-none" | ||
onScroll={checkIfBottom} | ||
> | ||
{instruction?.map((inst, idx) => ( | ||
<div | ||
key={idx} | ||
ref={(el) => (instructionRefs.current[idx] = el)} | ||
className={`instruction-item cursor-pointer p-4 border-gray-700 border-y bg-gray-800 text-white | ||
${clickedInstruction === idx ? 'bg-slate-600' : ''} `} | ||
onClick={() => | ||
handleInstructionClick( | ||
idx, | ||
inst.codeLineStart, | ||
inst.codeLineEnd | ||
) | ||
} | ||
> | ||
<h5 className="font-tuner">{`${idx + 1}. ${ | ||
inst.header || 'Default Header' | ||
}`}</h5> | ||
<div | ||
className={`overflow-auto transition-all ease-in-out ${ | ||
clickedInstruction === idx | ||
? 'duration-500 max-h-full opacity-100' | ||
: 'duration-0 max-h-0 opacity-0' | ||
}`} | ||
> | ||
<span className="mt-2"> | ||
{inst.itemDescription || 'Default Item Description'} | ||
</span> | ||
</div> | ||
</div> | ||
)) || <p>No instructions available.</p>} | ||
</div> | ||
</div> | ||
|
||
<div | ||
ref={codeblockRef} | ||
className="codeblock bg-gray-800 lg:w-2/3 max-h-50vh overflow-auto lg:rounded-tr-xl rounded-bl-xl lg:rounded-bl-none rounded-br-xl " | ||
> | ||
{codeblock ? ( | ||
<div> | ||
<TinaMarkdown | ||
key={highlightLines} | ||
content={codeblock} | ||
components={{ | ||
code_block: (props) => ( | ||
<CodeBlockWithHighlightLines | ||
{...props} | ||
highlightLines={highlightLines} | ||
/> | ||
), | ||
}} | ||
/> | ||
</div> | ||
) : ( | ||
<p>No code block available.</p> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default RecipeBlock; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Custom CSS to override PrismJS themes | ||
export const customHighlightCSS = ` | ||
:not(pre) > code[class*="language-"], | ||
pre[class*="language-"] { | ||
color: white; | ||
background: #111827; | ||
} | ||
|
||
pre[class*="language-"] > code[class*="language-"] { | ||
position: relative; | ||
} | ||
|
||
pre[class*="language-"]{ | ||
padding: 0.5rem; | ||
} | ||
|
||
.line-numbers-rows > span:before { | ||
content: counter(linenumber); | ||
color: #9FFCEF; | ||
display: block; | ||
padding-right: 0.8em; | ||
text-align: right; | ||
} | ||
|
||
.line-highlight { | ||
background: rgba(71, 85, 105, 0.25); | ||
} | ||
|
||
.line-numbers .line-numbers-rows { | ||
border-right: 1px solid #6B7280; | ||
} | ||
|
||
pre[class*="language-"] { | ||
padding: 1em; | ||
margin: 0 0 0.5em 0; | ||
overflow: auto; | ||
} | ||
|
||
pre[class*="language-"] ::selection { | ||
background: white; | ||
color: black; | ||
} | ||
`; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @JackDevAU
I dont like that we need to have a
TinaMarkdown
rich text field here to get the code block.My suggested solution
ui
override with a nice code editorTinaMarkdown