mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
layout optimization
This commit is contained in:
parent
ff835d677c
commit
212456e5c2
39
package-lock.json
generated
39
package-lock.json
generated
@ -11,8 +11,10 @@
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@tanstack/react-query": "^5.17.8",
|
||||
"@tiptap/extension-bubble-menu": "^2.14.0",
|
||||
"@tiptap/extension-color": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.14.0",
|
||||
"@tiptap/extension-link": "^2.14.0",
|
||||
"@tiptap/extension-placeholder": "^2.14.0",
|
||||
"@tiptap/extension-youtube": "^2.12.0",
|
||||
"@tiptap/pm": "^2.12.0",
|
||||
@ -2410,9 +2412,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.12.0.tgz",
|
||||
"integrity": "sha512-DYijoE0igV0Oi+ZppFsp2UrQsM/4HZtmmpD78BJM9zfCbd1YvAUIxmzmXr8uqU18OHd1uQy+/zvuNoUNYyw67g==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.14.0.tgz",
|
||||
"integrity": "sha512-sN15n0RjPh+2Asvxs7l47hVEvX6c0aPempU8QQWcPUlHoGf1D/XkyHXy6GWVPSxZ5Rj5uAwgKvhHsG/FJ/YGKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.7"
|
||||
@ -2593,9 +2595,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-image": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.12.0.tgz",
|
||||
"integrity": "sha512-wO+yrfMlnW3SYCb1Q1qAb+nt5WH6jnlQPTV6qdoIabRtW0puwMWULZDUgclPN5hxn8EXb9vBEu44egvH6hgkfQ==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.14.0.tgz",
|
||||
"integrity": "sha512-pYCUzZBgsxIvVGTzuW03cPz6PIrAo26xpoxqq4W090uMVoK0SgY5W5y0IqCdw4QyLkJ2/oNSFNc2EP9jVi1CcQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@ -2618,6 +2620,23 @@
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.14.0.tgz",
|
||||
"integrity": "sha512-fsqW7eRD2xoD6xy7eFrNPAdIuZ3eicA4jKC45Vcft/Xky0DJoIehlVBLxsPbfmv3f27EBrtPkg5+msLXkLyzJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.12.0.tgz",
|
||||
@ -8378,6 +8397,12 @@
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/linkifyjs": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.1.tgz",
|
||||
"integrity": "sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
|
||||
|
||||
@ -99,8 +99,10 @@
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@tanstack/react-query": "^5.17.8",
|
||||
"@tiptap/extension-bubble-menu": "^2.14.0",
|
||||
"@tiptap/extension-color": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.14.0",
|
||||
"@tiptap/extension-link": "^2.14.0",
|
||||
"@tiptap/extension-placeholder": "^2.14.0",
|
||||
"@tiptap/extension-youtube": "^2.12.0",
|
||||
"@tiptap/pm": "^2.12.0",
|
||||
|
||||
@ -5,6 +5,7 @@ import { Color } from '@tiptap/extension-color'
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import { Placeholder } from '@tiptap/extension-placeholder'
|
||||
import { Youtube } from '@tiptap/extension-youtube'
|
||||
import { Link } from '@tiptap/extension-link'
|
||||
import { EditorContent, useEditor } from '@tiptap/react'
|
||||
import { StarterKit } from '@tiptap/starter-kit'
|
||||
import { useEffect } from 'react'
|
||||
@ -56,6 +57,7 @@ export function RichTextEditor({
|
||||
}),
|
||||
Markdown,
|
||||
Image,
|
||||
Link,
|
||||
Youtube.configure({
|
||||
controls: false,
|
||||
nocookie: true,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import LinkIcon from '@heroicons/react/24/outline/LinkIcon'
|
||||
import PhotoIcon from '@heroicons/react/24/outline/PhotoIcon'
|
||||
import BoldIcon from '@heroicons/react/24/solid/BoldIcon'
|
||||
import H1Icon from '@heroicons/react/24/solid/H1Icon'
|
||||
import H2Icon from '@heroicons/react/24/solid/H2Icon'
|
||||
@ -6,6 +8,7 @@ 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 { useEditorState } from '@tiptap/react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { MdUndo, MdRedo, MdHorizontalRule } from 'react-icons/md'
|
||||
|
||||
import type { Editor } from '@tiptap/react'
|
||||
@ -31,6 +34,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
isHeading4: ctx.editor.isActive('heading', { level: 4 }),
|
||||
isHeading5: ctx.editor.isActive('heading', { level: 5 }),
|
||||
isHeading6: ctx.editor.isActive('heading', { level: 6 }),
|
||||
isHeading: ctx.editor.isActive('heading'),
|
||||
isBulletList: ctx.editor.isActive('bulletList'),
|
||||
isOrderedList: ctx.editor.isActive('orderedList'),
|
||||
isCodeBlock: ctx.editor.isActive('codeBlock'),
|
||||
@ -41,31 +45,29 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
},
|
||||
})
|
||||
|
||||
const addImage = useCallback(() => {
|
||||
const url = window.prompt('URL')
|
||||
|
||||
if (url) {
|
||||
editor.chain().focus().setImage({ src: url }).run()
|
||||
}
|
||||
}, [editor])
|
||||
|
||||
const [url, setUrl] = useState<string>('')
|
||||
|
||||
const setLink = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul
|
||||
className={
|
||||
'tw:menu tw:p-1 tw:menu-horizontal tw:flex-nowrap tw:overflow-x-hidden tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'
|
||||
'tw:menu tw:overflow-x-hidden tw:sm:overflow-visible tw:md:overflow-x-hidden tw:lg:overflow-visible tw:p-1 tw:menu-horizontal tw:flex-nowrap tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'
|
||||
}
|
||||
>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Bold'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
<BoldIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Italic'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<ItalicIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isHeading1 ? 'tw:bg-base-content/10' : ''}`}
|
||||
@ -93,7 +95,30 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
<H3Icon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div className='tw:w-[1px] tw:p-0 tw:mx-1 tw:bg-base-content/10 tw:my-1' />
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Bold'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
<BoldIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Italic'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<ItalicIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className='tw:w-[1px] tw:p-0 tw:mx-1 tw:bg-base-content/10 tw:my-1' />
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editorState.isBulletList ? 'tw:bg-base-content/10' : ''}`}
|
||||
@ -112,6 +137,63 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
<NumberedListIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className='tw:w-[1px] tw:p-0 tw:mx-1 tw:bg-base-content/10 tw:my-1' />
|
||||
</li>
|
||||
<li className='tw:hidden tw:sm:block tw:md:hidden tw:lg:block'>
|
||||
{!editor.isActive('link') ? (
|
||||
<div
|
||||
tabIndex={0}
|
||||
role='button'
|
||||
className='tw:dropdown tw:dropdown-end tw:px-1.5 tw:mx-0.5 tw:pt-1.5 tw:pb-0 tw:cursor-pointer '
|
||||
>
|
||||
<div
|
||||
className={`tw:tooltip tw:h-full ${editor.isActive('link') ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Link'
|
||||
>
|
||||
<LinkIcon className='tw:h-full tw:w-5' />
|
||||
</div>
|
||||
<div
|
||||
tabIndex={0}
|
||||
className='tw:dropdown-content tw:bg-base-200 tw:card tw:card-body tw:card-sm tw:bg-base-100 z-1 w-64 shadow-md'
|
||||
>
|
||||
<div className='tw:join'>
|
||||
<div>
|
||||
<label className='tw:input tw:validator tw:join-item tw:w-58'>
|
||||
<LinkIcon className='tw:h-full tw:w-4' />
|
||||
<input
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
type='url'
|
||||
placeholder='https://...'
|
||||
/>
|
||||
</label>
|
||||
<div className='tw:validator-hint tw:hidden'>Enter valid url</div>
|
||||
</div>
|
||||
<button
|
||||
className='tw:btn tw:btn-neutral tw:join-item'
|
||||
onClick={(e) => setLink(e)}
|
||||
>
|
||||
Set Link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 ${editor.isActive('link') ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='List'
|
||||
onClick={() => editor.chain().focus().extendMarkRange('link').unsetLink().run()}
|
||||
>
|
||||
<LinkIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div className='tw:tooltip tw:px-1.5 tw:mx-0.5' data-tip='Image' onClick={addImage}>
|
||||
<PhotoIcon className='tw:w-5 tw:h-5' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className='tw:tooltip tw:px-1'
|
||||
@ -148,6 +230,43 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
</button>
|
||||
</div>
|
||||
</div> */}
|
||||
{/** <BubbleMenu editor={editor} tippyOptions={{ duration: 100 }}>
|
||||
<div className='bubble-menu tw:card tw:card-body tw:rounded-box tw:border tw:border-base-content/20 tw:bg-base-200 tw:shadow'>
|
||||
<ul
|
||||
className={
|
||||
'tw:menu tw:p-1 tw:menu-horizontal tw:flex-nowrap tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'
|
||||
}
|
||||
>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-0.5 tw:cursor-pointer ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Bold'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
<BoldIcon className='tw:w-4 tw:h-4' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-1 tw:cursor-pointer ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Italic'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<ItalicIcon className='tw:w-4 tw:h-4' />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
className={`tw:tooltip tw:px-1.5 tw:mx-1 tw:cursor-pointer ${editor.isActive('link') ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Link'
|
||||
onClick={setLink}
|
||||
>
|
||||
<LinkIcon className='tw:w-4 tw:h-4' />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</BubbleMenu> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -14,3 +14,30 @@
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Bubble menu */
|
||||
.bubble-menu {
|
||||
background-color: var(--color-base-100);
|
||||
border: 1px solid var(--color-base-200);
|
||||
border-radius: 0.7rem;
|
||||
box-shadow: var(--shadow);
|
||||
display: flex;
|
||||
padding: 0.2rem;
|
||||
|
||||
button {
|
||||
background-color: unset;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-base-300);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--color-base-200);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-base-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user