Example of jsx code
Example of jsx code
"use client"import React, { useCallback, useMemo, useState } from 'react'import isHotkey from 'is-hotkey'import { Editable, withReact, useSlate, Slate } from 'slate-react'import { Editor, Transforms, createEditor, Element as SlateElement, // Use this for isElement checks Node,} from 'slate'import { withHistory } from 'slate-history'import Prism from 'prismjs'// Import languages and basic themeimport 'prismjs/components/prism-javascript'import 'prismjs/components/prism-css'import 'prismjs/components/prism-markdown'import { Button, Icon, Toolbar } from './components'// --- Helper: Calculate nested Prism token lengths ---const getLength = (token) => { if (typeof token === 'string') return token.length if (typeof token.content === 'string') return token.content.length return token.content.reduce((l, t) => l + getLength(t), 0)}const HOTKEYS = { 'mod+b': 'bold', 'mod+i': 'italic', 'mod+u': 'underline', 'mod+`': 'code',}const LIST_TYPES = ['numbered-list', 'bulleted-list']const TEXT_ALIGN_TYPES = ['left', 'center', 'right', 'justify']const SlateEditor = ({ onChange }) => { const [data, setData] = useState([ { type: 'paragraph', children: [{ text: 'Write some code below...' }], }, { type: 'code-block', children: [{ text: 'const hello = "world";\nconsole.log(hello);' }], }, ]) const editor = useMemo(() => withHistory(withReact(createEditor())), []) // --- The Highlighting Logic --- const decorate = useCallback(([node, path]) => { const ranges = [] // Correctly using SlateElement.isElement instead of Node.isElement if (SlateElement.isElement(node) && node.type === 'code-block') { const text = Node.string(node) const tokens = Prism.tokenize(text, Prism.languages.javascript) let start = 0 for (const token of tokens) { const length = getLength(token) const end = start + length if (typeof token !== 'string') { ranges.push({ [token.type]: true, anchor: { path: [...path, 0], offset: start }, focus: { path: [...path, 0], offset: end }, }) } start = end } } return ranges }, []) const renderElement = useCallback(props => <Element {...props} />, []) const renderLeaf = useCallback(props => <Leaf {...props} />, []) return ( <div className='border px-4 py-2 focus:ring-1 ring-blue-500 rounded-md bg-white'> <Slate editor={editor} initialValue={data} onChange={(value) => { setData(value) if (onChange) onChange(value) }} > <Toolbar> <MarkButton format="bold" icon="format_bold" /> <MarkButton format="italic" icon="format_italic" /> <MarkButton format="code" icon="code" /> <BlockButton format="code-block" icon="terminal" /> <BlockButton format="heading-one" icon="looks_one" /> <BlockButton format="bulleted-list" icon="format_list_bulleted" /> </Toolbar> <Editable decorate={decorate} renderElement={renderElement} renderLeaf={renderLeaf} placeholder="Enter some rich text…" spellCheck={false} autoFocus className='outline-none focus:outline-none min-h-[300px] font-sans' onKeyDown={event => { for (const hotkey in HOTKEYS) { if (isHotkey(hotkey, event)) { event.preventDefault() toggleMark(editor, HOTKEYS[hotkey]) } } }} /> </Slate> </div> )}// --- Block & Mark Logic ---const toggleBlock = (editor, format) => { const isActive = isBlockActive(editor, format, TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type') const isList = LIST_TYPES.includes(format) Transforms.unwrapNodes(editor, { match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && LIST_TYPES.includes(n.type), split: true, }) const newProperties = TEXT_ALIGN_TYPES.includes(format) ? { align: isActive ? undefined : format } : { type: isActive ? 'paragraph' : isList ? 'list-item' : format } Transforms.setNodes(editor, newProperties) if (!isActive && isList) { const block = { type: format, children: [] } Transforms.wrapNodes(editor, block) }}const toggleMark = (editor, format) => { const isActive = isMarkActive(editor, format) isActive ? Editor.removeMark(editor, format) : Editor.addMark(editor, format, true)}const isBlockActive = (editor, format, blockType = 'type') => { const { selection } = editor if (!selection) return false const [match] = Array.from(Editor.nodes(editor, { at: Editor.unhangRange(editor, selection), match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && n[blockType] === format, })) return !!match}const isMarkActive = (editor, format) => { const marks = Editor.marks(editor) return marks ? marks[format] === true : false}// --- Rendering Components ---export const Element = ({ attributes, children, element }) => { const style = { textAlign: element.align } switch (element.type) { case 'code-block': return ( <pre {...attributes} className="bg-gray-900 text-gray-100 p-4 rounded-md font-mono my-4 overflow-x-auto"> <code>{children}</code> </pre> ) case 'heading-one': return <h1 className="text-2xl font-bold mb-2" style={style} {...attributes}>{children}</h1> case 'bulleted-list': return <ul className='list-disc ml-6 mb-2' style={style} {...attributes}>{children}</ul> case 'list-item': return <li style={style} {...attributes}>{children}</li> default: return <div className="mb-2" style={style} {...attributes}>{children}</div> }}export const Leaf = ({ attributes, children, leaf }) => { if (leaf.bold) children = <strong>{children}</strong> if (leaf.code) children = <code className="bg-gray-100 px-1 rounded text-red-500 font-mono text-sm">{children}</code> if (leaf.italic) children = <em>{children}</em> if (leaf.underline) children = <u>{children}</u> // Syntax highlighting classes (Tailwind) const tokenClasses = [ leaf.comment && 'text-gray-500 italic', leaf.keyword && 'text-purple-400 font-bold', leaf.tag && 'text-red-400', leaf.punctuation && 'text-gray-400', leaf.string && 'text-green-400', leaf.function && 'text-blue-400', leaf.operator && 'text-yellow-400', leaf.number && 'text-orange-400', ].filter(Boolean).join(' ') return ( <span {...attributes} className={tokenClasses}> {children} </span> )}const BlockButton = ({ format, icon }) => { const editor = useSlate() const active = isBlockActive(editor, format) return ( <Button active={active} onMouseDown={e => { e.preventDefault(); toggleBlock(editor, format); }}> <Icon className={active ? 'text-blue-600' : 'text-gray-500'}>{icon}</Icon> </Button> )}const MarkButton = ({ format, icon }) => { const editor = useSlate() const active = isMarkActive(editor, format) return ( <Button active={active} onMouseDown={e => { e.preventDefault(); toggleMark(editor, format); }}> <Icon className={active ? 'text-blue-600' : 'text-gray-500'}>{icon}</Icon> </Button> )}export default SlateEditorsdcds