Skip to content
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

initial commit of custom menu inside a code-block #545

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion examples/full/components/NotionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,14 @@ export const NotionPage = ({
g.recordMap = recordMap
g.block = block
}

const codeMenuActions = {
copy: (content: string, language: string, block: any) => {
console.log('copy', content, language, block)
},
menu: (content: string, language: string, block: any) => {
console.log('menu', content, language, block)
}
}
const socialDescription = 'React Notion X Demo'
const socialImage =
'https://react-notion-x-demo.transitivebullsh.it/social.jpg'
Expand Down Expand Up @@ -152,6 +159,7 @@ export const NotionPage = ({
rootDomain={rootDomain}
rootPageId={rootPageId}
previewImages={previewImagesEnabled}
customFunctions={{ codeMenuActions: codeMenuActions.menu }}
components={{
// NOTE (transitive-bullshit 3/12/2023): I'm disabling next/image for this repo for now because the amount of traffic started costing me hundreds of dollars a month in Vercel image optimization costs. I'll probably re-enable it in the future if I can find a better solution.
// nextImage: Image,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-notion-x/src/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ export const Block: React.FC<BlockProps> = (props) => {
)

case 'code':
return <components.Code block={block as types.CodeBlock} />
return <components.Code block={block as types.CodeBlock} ctx={ctx} />

case 'column_list':
return <div className={cs('notion-row', blockId)}>{children}</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/react-notion-x/src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface NotionContext {
defaultPageIcon?: string
defaultPageCover?: string
defaultPageCoverPosition?: number

customFunctions?: any
zoom: any
}

Expand Down Expand Up @@ -71,7 +71,7 @@ export interface PartialNotionContext {
defaultPageIcon?: string
defaultPageCover?: string
defaultPageCoverPosition?: number

customFunctions?: any
zoom?: any
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions packages/react-notion-x/src/icons/menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react'

function MenuIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
fill='currentColor'
viewBox='0 0 24 24'
width='1em'
{...props} // Spread the props onto the SVG element
>
<path
fillRule='evenodd'
d='M12 4a1 1 0 100 2 1 1 0 000-2zm3 1a3 3 0 11-6 0 3 3 0 016 0zm-3 6a1 1 0 100 2 1 1 0 000-2zm3 1a3 3 0 11-6 0 3 3 0 016 0zm-4 7a1 1 0 112 0 1 1 0 01-2 0zm1 3a3 3 0 100-6 3 3 0 000 6z'
clipRule='evenodd'
></path>
</svg>
)
}

export default MenuIcon
3 changes: 3 additions & 0 deletions packages/react-notion-x/src/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const NotionRenderer: React.FC<{
blockId?: string
hideBlockId?: boolean
disableHeader?: boolean
customFunctions?: any
}> = ({
components,
recordMap,
Expand All @@ -81,6 +82,7 @@ export const NotionRenderer: React.FC<{
defaultPageIcon,
defaultPageCover,
defaultPageCoverPosition,
customFunctions,
...rest
}) => {
const zoom = React.useMemo(
Expand Down Expand Up @@ -118,6 +120,7 @@ export const NotionRenderer: React.FC<{
defaultPageCover={defaultPageCover}
defaultPageCoverPosition={defaultPageCoverPosition}
zoom={isImageZoomable ? zoom : null}
customFunctions={customFunctions}
>
<NotionBlockRenderer {...rest} />
</NotionContextProvider>
Expand Down
59 changes: 58 additions & 1 deletion packages/react-notion-x/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ svg.notion-page-icon {
.notion-code-copy {
position: absolute;
top: 1em;
right: 1em;
right: 5em;
user-select: none;
z-index: 9;
transition: opacity 0.2s cubic-bezier(0.3, 0, 0.5, 1);
Expand Down Expand Up @@ -997,6 +997,63 @@ svg.notion-page-icon {
color: #fff;
border-radius: 6px;
}
.notion-code-menu {
position: absolute;
top: 1em;
right: 1em;
user-select: none;
z-index: 9;
transition: opacity 0.2s cubic-bezier(0.3, 0, 0.5, 1);
opacity: 0; /* Start with hidden menu to reduce UI clutter */
}

.notion-code:hover .notion-code-menu {
opacity: 1; /* Show the menu on hover */
}

.notion-code-menu-button {
display: inline-block;
padding: 0.6em;
font-size: 1.25em;
line-height: 1em;
cursor: pointer;
transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1), color 0.2s cubic-bezier(0.3, 0, 0.5, 1), border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
box-shadow: 0 1px 0 rgba(27, 31, 36, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25);
background-color: #f6f8fa;
color: #24292f;
border: 1px solid rgba(27, 31, 36, 0.15);
border-radius: 6px;
}

.notion-code-menu-button:hover {
background-color: #f3f4f6;
border-color: rgba(27, 31, 36, 0.15);
}

.notion-code-menu-button:active {
background: hsla(220, 14%, 93%, 1);
border-color: rgba(27, 31, 36, 0.15);
transition: none;
}

.notion-code-menu-tooltip {
display: none; /* Keep tooltip hidden initially */
position: absolute;
bottom: -38px; /* Position the tooltip below the button */
left: 50%;
transform: translateX(-50%); /* Center the tooltip */
z-index: 99;
font-size: 14px;
white-space: nowrap; /* Ensure the tooltip text is on a single line */
background-color: #333; /* Background color for tooltip */
color: white; /* Text color for tooltip */
padding: 5px 8px; /* Padding around the tooltip text */
border-radius: 4px; /* Rounded corners for the tooltip */
}

.notion-code .notion-code-menu-button:hover + .notion-code-menu-tooltip {
display: block; /* Show the tooltip when hovering over the menu button */
}

.notion-column {
display: flex;
Expand Down
31 changes: 26 additions & 5 deletions packages/react-notion-x/src/third-party/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ import 'prismjs/components/prism-tsx.min.js'
import 'prismjs/components/prism-typescript.min.js'

import { Text } from '../components/text'
import { useNotionContext } from '../context'
import CopyIcon from '../icons/copy'
import MenuIcon from '../icons/menu'
import { cs } from '../utils'

export const Code: React.FC<{
block: CodeBlock
defaultLanguage?: string
className?: string
}> = ({ block, defaultLanguage = 'typescript', className }) => {
ctx: any
}> = ({ block, defaultLanguage = 'typescript', className, ctx }) => {
const [isCopied, setIsCopied] = React.useState(false)
const copyTimeout = React.useRef<number>()
const { recordMap } = useNotionContext()
const { recordMap, customFunctions } = ctx
const content = getBlockTitle(block, recordMap)
const language = (
block.properties?.language?.[0]?.[0] || defaultLanguage
).toLowerCase()
const caption = block.properties.caption

const codeRef = React.useRef()
React.useEffect(() => {
if (codeRef.current) {
Expand Down Expand Up @@ -64,19 +64,40 @@ export const Code: React.FC<{
</div>
)

const handleCodeMenu = () => {
setIsMenuOpen(!isMenuOpen)
if (customFunctions.codeMenuActions) {
customFunctions.codeMenuActions(content, language, block)
}
setIsMenuOpen(false)
}

const MenuButton = (
<div className='notion-code-menu-button' onClick={handleCodeMenu}>
<MenuIcon />
</div>
)
const [isMenuOpen, setIsMenuOpen] = React.useState(false)

return (
<>
<pre className={cs('notion-code', className)}>
<div className='notion-code-copy'>
{copyButton}

{isCopied && (
<div className='notion-code-copy-tooltip'>
<div>{isCopied ? 'Copied' : 'Copy'}</div>
</div>
)}
</div>

<div className='notion-code-menu'>
{MenuButton}
<div className='notion-code-menu-tooltip'>
<div>{isMenuOpen ? 'Menu Open' : 'Menu'}</div>
</div>
</div>

<code className={`language-${language}`} ref={codeRef}>
{content}
</code>
Expand Down