mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
menu-bar
This commit is contained in:
parent
3a319c6f09
commit
391530aba1
34
package-lock.json
generated
34
package-lock.json
generated
@ -23,6 +23,7 @@
|
||||
"leaflet.locatecontrol": "^0.79.0",
|
||||
"radash": "^12.1.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-image-crop": "^10.1.8",
|
||||
"react-inlinesvg": "^4.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
@ -6318,9 +6319,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
|
||||
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@ -11114,6 +11115,15 @@
|
||||
"react": "16.8 - 19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-image-crop": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-10.1.8.tgz",
|
||||
@ -12543,13 +12553,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -13241,18 +13251,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.0.tgz",
|
||||
"integrity": "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==",
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.34.9",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"tinyglobby": "^0.2.13"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"leaflet.locatecontrol": "^0.79.0",
|
||||
"radash": "^12.1.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-image-crop": "^10.1.8",
|
||||
"react-inlinesvg": "^4.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
|
||||
@ -1,210 +1,17 @@
|
||||
import { Color } from '@tiptap/extension-color'
|
||||
import Color from '@tiptap/extension-color'
|
||||
import Image from '@tiptap/extension-image'
|
||||
import ListItem from '@tiptap/extension-list-item'
|
||||
import TextStyle from '@tiptap/extension-text-style'
|
||||
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
|
||||
import Youtube from '@tiptap/extension-youtube'
|
||||
import { EditorProvider } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { useState } from 'react'
|
||||
import { Markdown } from 'tiptap-markdown'
|
||||
import Youtube from '@tiptap/extension-youtube'
|
||||
|
||||
import { TextEditorMenu } from './TextEditorMenu'
|
||||
|
||||
import type { EditorEvents } from '@tiptap/react'
|
||||
|
||||
const MenuBar = () => {
|
||||
const { editor } = useCurrentEditor()
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [height, setHeight] = useState(480)
|
||||
const [width, setWidth] = useState(640)
|
||||
|
||||
const addYoutubeVideo = () => {
|
||||
const url = prompt('Enter YouTube URL')
|
||||
|
||||
if (url) {
|
||||
editor.commands.setYoutubeVideo({
|
||||
src: url,
|
||||
width: Math.max(320, width) || 640,
|
||||
height: Math.max(180, height) || 480,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='control-group tiptap-toolbar'>
|
||||
<div className='button-group'>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
disabled={!editor.can().chain().focus().toggleBold().run()}
|
||||
className={editor.isActive('bold') ? 'is-active' : ''}
|
||||
>
|
||||
Bold
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
disabled={!editor.can().chain().focus().toggleItalic().run()}
|
||||
className={editor.isActive('italic') ? 'is-active' : ''}
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
disabled={!editor.can().chain().focus().toggleStrike().run()}
|
||||
className={editor.isActive('strike') ? 'is-active' : ''}
|
||||
>
|
||||
Strike
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleCode().run()}
|
||||
disabled={!editor.can().chain().focus().toggleCode().run()}
|
||||
className={editor.isActive('code') ? 'is-active' : ''}
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button type='button' onClick={() => editor.chain().focus().unsetAllMarks().run()}>
|
||||
Clear marks
|
||||
</button>
|
||||
<button type='button' onClick={() => editor.chain().focus().clearNodes().run()}>
|
||||
Clear nodes
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().setParagraph().run()}
|
||||
className={editor.isActive('paragraph') ? 'is-active' : ''}
|
||||
>
|
||||
Paragraph
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
className={editor.isActive('heading', { level: 1 }) ? 'is-active' : ''}
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
className={editor.isActive('heading', { level: 2 }) ? 'is-active' : ''}
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
className={editor.isActive('heading', { level: 3 }) ? 'is-active' : ''}
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 4 }).run()}
|
||||
className={editor.isActive('heading', { level: 4 }) ? 'is-active' : ''}
|
||||
>
|
||||
H4
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 5 }).run()}
|
||||
className={editor.isActive('heading', { level: 5 }) ? 'is-active' : ''}
|
||||
>
|
||||
H5
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 6 }).run()}
|
||||
className={editor.isActive('heading', { level: 6 }) ? 'is-active' : ''}
|
||||
>
|
||||
H6
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
className={editor.isActive('bulletList') ? 'is-active' : ''}
|
||||
>
|
||||
Bullet list
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
className={editor.isActive('orderedList') ? 'is-active' : ''}
|
||||
>
|
||||
Ordered list
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
className={editor.isActive('codeBlock') ? 'is-active' : ''}
|
||||
>
|
||||
Code block
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
className={editor.isActive('blockquote') ? 'is-active' : ''}
|
||||
>
|
||||
Blockquote
|
||||
</button>
|
||||
<button type='button' onClick={() => editor.chain().focus().setHorizontalRule().run()}>
|
||||
Horizontal rule
|
||||
</button>
|
||||
<button type='button' onClick={() => editor.chain().focus().setHardBreak().run()}>
|
||||
Hard break
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
disabled={!editor.can().chain().focus().undo().run()}
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().redo().run()}
|
||||
disabled={!editor.can().chain().focus().redo().run()}
|
||||
>
|
||||
Redo
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => editor.chain().focus().setColor('#958DF1').run()}
|
||||
className={editor.isActive('textStyle', { color: '#958DF1' }) ? 'is-active' : ''}
|
||||
>
|
||||
Purple
|
||||
</button>
|
||||
</div>
|
||||
<div className='button-group'>
|
||||
<input
|
||||
id='width'
|
||||
type='number'
|
||||
min='320'
|
||||
max='1024'
|
||||
placeholder='width'
|
||||
value={width}
|
||||
onChange={(event) => setWidth(parseInt(event.target.value))}
|
||||
/>
|
||||
<input
|
||||
id='height'
|
||||
type='number'
|
||||
min='180'
|
||||
max='720'
|
||||
placeholder='height'
|
||||
value={height}
|
||||
onChange={(event) => setHeight(parseInt(event.target.value))}
|
||||
/>
|
||||
<button id='add' onClick={addYoutubeVideo}>
|
||||
Add YouTube video
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const extensions = [
|
||||
Color.configure({ types: [TextStyle.name, ListItem.name] }),
|
||||
// TextStyle.configure({ types: [ListItem.name] }),
|
||||
@ -281,16 +88,30 @@ export function RichTextEditor({
|
||||
}
|
||||
|
||||
return (
|
||||
<EditorProvider
|
||||
slotBefore={<MenuBar />}
|
||||
extensions={extensions}
|
||||
content={inputValue}
|
||||
onUpdate={handleChange}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
class: 'prose prose-sm sm:prose-base lg:prose-lg xl:prose-2xl m-5 focus:outline-none',
|
||||
},
|
||||
}}
|
||||
></EditorProvider>
|
||||
<div className={`tw:form-control tw:w-full ${containerStyle ?? ''}`}>
|
||||
{labelTitle ? (
|
||||
<label className='tw:label'>
|
||||
<span className={`tw:label-text tw:text-base-content ${labelStyle ?? ''}`}>
|
||||
{labelTitle}
|
||||
</span>
|
||||
</label>
|
||||
) : null}
|
||||
<EditorProvider
|
||||
slotBefore={
|
||||
<>
|
||||
<br />
|
||||
<TextEditorMenu />
|
||||
</>
|
||||
}
|
||||
extensions={extensions}
|
||||
content={inputValue}
|
||||
onUpdate={handleChange}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
class: 'prose prose-sm sm:prose-base lg:prose-lg xl:prose-2xl m-5 focus:outline-none',
|
||||
},
|
||||
}}
|
||||
></EditorProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
153
src/Components/Input/TextEditorMenu.tsx
Normal file
153
src/Components/Input/TextEditorMenu.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import BoldIcon from '@heroicons/react/24/solid/BoldIcon'
|
||||
import CodeBracketIcon from '@heroicons/react/24/solid/CodeBracketIcon'
|
||||
import H1Icon from '@heroicons/react/24/solid/H1Icon'
|
||||
import H2Icon from '@heroicons/react/24/solid/H2Icon'
|
||||
import H3Icon from '@heroicons/react/24/solid/H3Icon'
|
||||
import ItalicIcon from '@heroicons/react/24/solid/ItalicIcon'
|
||||
import ListBulletIcon from '@heroicons/react/24/solid/ListBulletIcon'
|
||||
import NumberedListIcon from '@heroicons/react/24/solid/NumberedListIcon'
|
||||
import { useCurrentEditor } from '@tiptap/react'
|
||||
import { FaQuoteLeft } from 'react-icons/fa6'
|
||||
import { MdUndo, MdRedo, MdHorizontalRule } from 'react-icons/md'
|
||||
|
||||
export const TextEditorMenu = () => {
|
||||
const { editor } = useCurrentEditor()
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
const addYoutubeVideo = () => {
|
||||
const url = prompt('Enter YouTube URL')
|
||||
|
||||
if (url) {
|
||||
editor.commands.setYoutubeVideo({
|
||||
src: url,
|
||||
width: Math.max(320) || 640,
|
||||
height: Math.max(180) || 480,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul className='tw:menu tw:menu-horizontal tw:bg-base-200 tw:rounded-box tw:mt-6'>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Bold'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
<BoldIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Italic'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<ItalicIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
>
|
||||
<H1Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
>
|
||||
<H2Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
>
|
||||
<H3Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
>
|
||||
<ListBulletIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
>
|
||||
<NumberedListIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
>
|
||||
<CodeBracketIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
>
|
||||
<FaQuoteLeft className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().redo().run()}
|
||||
>
|
||||
<MdRedo className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
>
|
||||
<MdUndo className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className='tw:tooltip'
|
||||
data-tip='Details'
|
||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||
>
|
||||
<MdHorizontalRule className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{/** <div className='control-group tiptap-toolbar'>
|
||||
<div className='button-group'>
|
||||
<button id='add' className='btn btn-sm' onClick={addYoutubeVideo}>
|
||||
Add YouTube video
|
||||
</button>
|
||||
</div>
|
||||
</div> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,13 +1,3 @@
|
||||
.tiptap-toolbar {
|
||||
button {
|
||||
border: 2px solid black;
|
||||
padding: 6px;
|
||||
width: 80x;
|
||||
border-radius: 8px;
|
||||
background-color: #C1BCAC;
|
||||
}
|
||||
}
|
||||
|
||||
.tiptap h1,
|
||||
.tiptap h2,
|
||||
.tiptap h3,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user