mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
fixed flex layout
This commit is contained in:
parent
047c640a4e
commit
467fa6262e
15
package-lock.json
generated
15
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@tanstack/react-query": "^5.17.8",
|
||||
"@tiptap/extension-color": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.12.0",
|
||||
"@tiptap/extension-placeholder": "^2.14.0",
|
||||
"@tiptap/extension-youtube": "^2.12.0",
|
||||
"@tiptap/pm": "^2.12.0",
|
||||
"@tiptap/react": "^2.12.0",
|
||||
@ -2656,6 +2657,20 @@
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-placeholder": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.14.0.tgz",
|
||||
"integrity": "sha512-xzfjHvuukbch4i5O/5uyS2K2QgNEaMKi6e6GExTTgVwnFjKfJmgTqee33tt5JCqSItBvtSZlU3SX/vpiaIof+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.12.0.tgz",
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
"@tanstack/react-query": "^5.17.8",
|
||||
"@tiptap/extension-color": "^2.12.0",
|
||||
"@tiptap/extension-image": "^2.12.0",
|
||||
"@tiptap/extension-placeholder": "^2.14.0",
|
||||
"@tiptap/extension-youtube": "^2.12.0",
|
||||
"@tiptap/pm": "^2.12.0",
|
||||
"@tiptap/react": "^2.12.0",
|
||||
|
||||
@ -1,44 +1,18 @@
|
||||
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 Youtube from '@tiptap/extension-youtube'
|
||||
import { EditorProvider } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { useEffect, useState } from 'react'
|
||||
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 { EditorContent, useEditor } from '@tiptap/react'
|
||||
import { StarterKit } from '@tiptap/starter-kit'
|
||||
import { useEffect } from 'react'
|
||||
import { Markdown } from 'tiptap-markdown'
|
||||
|
||||
import { TextEditorMenu } from './TextEditorMenu'
|
||||
|
||||
import type { EditorEvents } from '@tiptap/react'
|
||||
|
||||
const extensions = [
|
||||
Color.configure({ types: [TextStyle.name, ListItem.name] }),
|
||||
// TextStyle.configure({ types: [ListItem.name] }),
|
||||
StarterKit.configure({
|
||||
bulletList: {
|
||||
keepMarks: true,
|
||||
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
|
||||
},
|
||||
orderedList: {
|
||||
keepMarks: true,
|
||||
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
|
||||
},
|
||||
}),
|
||||
Markdown,
|
||||
Image,
|
||||
Youtube.configure({
|
||||
controls: false,
|
||||
nocookie: true,
|
||||
width: 100,
|
||||
}),
|
||||
]
|
||||
|
||||
interface TextAreaProps {
|
||||
interface RichTextEditorProps {
|
||||
labelTitle?: string
|
||||
labelStyle?: string
|
||||
containerStyle?: string
|
||||
dataField?: string
|
||||
inputStyle?: string
|
||||
defaultValue: string
|
||||
placeholder?: string
|
||||
@ -52,48 +26,67 @@ interface TextAreaProps {
|
||||
*/
|
||||
export function RichTextEditor({
|
||||
labelTitle,
|
||||
dataField,
|
||||
labelStyle,
|
||||
containerStyle,
|
||||
inputStyle,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
required = true,
|
||||
updateFormValue,
|
||||
}: TextAreaProps) {
|
||||
// const ref = useRef<HTMLTextAreaElement>(null)
|
||||
const [inputValue, setInputValue] = useState<string>(defaultValue)
|
||||
}: RichTextEditorProps) {
|
||||
console.log(placeholder, required)
|
||||
|
||||
useEffect(() => {
|
||||
setInputValue(defaultValue)
|
||||
}, [defaultValue])
|
||||
|
||||
console.log(
|
||||
labelTitle,
|
||||
dataField,
|
||||
labelStyle,
|
||||
containerStyle,
|
||||
inputStyle,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
required,
|
||||
updateFormValue,
|
||||
)
|
||||
|
||||
const handleChange = (props: EditorEvents['update']) => {
|
||||
const newValue = props.editor.storage.markdown.getMarkdown()
|
||||
setInputValue(newValue)
|
||||
if (updateFormValue) {
|
||||
const handleChange = () => {
|
||||
const newValue: string | undefined = editor?.storage.markdown.getMarkdown()
|
||||
if (updateFormValue && newValue) {
|
||||
updateFormValue(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
Color.configure({ types: ['textStyle', 'listItem'] }),
|
||||
StarterKit.configure({
|
||||
bulletList: {
|
||||
keepMarks: true,
|
||||
keepAttributes: false,
|
||||
},
|
||||
orderedList: {
|
||||
keepMarks: true,
|
||||
keepAttributes: false,
|
||||
},
|
||||
}),
|
||||
Markdown,
|
||||
Image,
|
||||
Youtube.configure({
|
||||
controls: false,
|
||||
nocookie: true,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder,
|
||||
emptyEditorClass: 'is-editor-empty',
|
||||
}),
|
||||
],
|
||||
content: defaultValue,
|
||||
onUpdate: handleChange,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: `tw:h-full tw:max-h-full tw:p-2 tw:overflow-y-auto`,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (editor?.storage.markdown.getMarkdown() === '' || !editor?.storage.markdown.getMarkdown()) {
|
||||
editor?.commands.setContent(defaultValue)
|
||||
}
|
||||
}, [defaultValue, editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`tw:form-control tw:w-full tw:flex tw:flex-col tw:min-h-0 ${containerStyle ?? ''}`}
|
||||
>
|
||||
{labelTitle ? (
|
||||
<label className='tw:label'>
|
||||
<label className='tw:label tw:pb-1'>
|
||||
<span className={`tw:label-text tw:text-base-content ${labelStyle ?? ''}`}>
|
||||
{labelTitle}
|
||||
</span>
|
||||
@ -102,17 +95,12 @@ export function RichTextEditor({
|
||||
<div
|
||||
className={`editor-wrapper tw:border-base-content/20 tw:rounded-box tw:border tw:flex tw:flex-col tw:flex-1 tw:min-h-0`}
|
||||
>
|
||||
<EditorProvider
|
||||
slotBefore={<TextEditorMenu />}
|
||||
extensions={extensions}
|
||||
content={inputValue}
|
||||
onUpdate={handleChange}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
class: `tw:h-full tw:max-h-full tw:p-2 tw:overflow-y-auto`,
|
||||
},
|
||||
}}
|
||||
></EditorProvider>
|
||||
{editor ? (
|
||||
<>
|
||||
<TextEditorMenu editor={editor} />
|
||||
<EditorContent editor={editor} />
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -6,16 +6,12 @@ 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, useEditorState } from '@tiptap/react'
|
||||
import { Editor, useEditorState } from '@tiptap/react'
|
||||
import { FaQuoteLeft } from 'react-icons/fa6'
|
||||
import { MdUndo, MdRedo, MdHorizontalRule } from 'react-icons/md'
|
||||
|
||||
export const TextEditorMenu = () => {
|
||||
const { editor } = useCurrentEditor()
|
||||
export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
const editorState = useEditorState({
|
||||
editor,
|
||||
@ -63,112 +59,112 @@ export const TextEditorMenu = () => {
|
||||
<>
|
||||
<ul className='tw:menu tw:menu-horizontal tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'>
|
||||
<li>
|
||||
<a
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.canUndo ? '' : 'tw:opacity-50'}`}
|
||||
data-tip='Undo'
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
>
|
||||
<MdUndo className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.canRedo ? '' : 'tw:opacity-50'}`}
|
||||
data-tip='Redo'
|
||||
onClick={() => editor.chain().focus().redo().run()}
|
||||
>
|
||||
<MdRedo className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isBold ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Bold'
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
<BoldIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isItalic ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Italic'
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<ItalicIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isHeading1 ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isHeading1 ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Heading 1'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
>
|
||||
<H1Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isHeading2 ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isHeading2 ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Heading 2'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
>
|
||||
<H2Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isHeading3 ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isHeading3 ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Heading 3'
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
>
|
||||
<H3Icon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isBulletList ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isBulletList ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='List'
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
>
|
||||
<ListBulletIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isOrderedList ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isOrderedList ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='List'
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
>
|
||||
<NumberedListIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isCodeBlock ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isCodeBlock ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Code'
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
>
|
||||
<CodeBracketIcon className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className={`tw:tooltip ${editorState.isBlockquote ? 'tw:bg-base-100' : ''}`}
|
||||
<div
|
||||
className={`tw:tooltip ${editorState.isBlockquote ? 'tw:bg-base-content/10' : ''}`}
|
||||
data-tip='Quote'
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
>
|
||||
<FaQuoteLeft className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
<div
|
||||
className='tw:tooltip'
|
||||
data-tip='Horizontal Line'
|
||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||
>
|
||||
<MdHorizontalRule className='tw:w-5 tw:h-5' />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{/** <div className='control-group tiptap-toolbar'>
|
||||
|
||||
@ -12,7 +12,7 @@ export const ContactInfoForm = ({
|
||||
setState: React.Dispatch<React.SetStateAction<any>>
|
||||
}) => {
|
||||
return (
|
||||
<div className='tw:mt-4 tw:space-y-4'>
|
||||
<div className='tw:mt-2 tw:space-y-2'>
|
||||
<div>
|
||||
<label
|
||||
htmlFor='email'
|
||||
|
||||
@ -51,7 +51,7 @@ export const GroupSubheaderForm = ({
|
||||
}, [state.group_type, groupTypes])
|
||||
|
||||
return (
|
||||
<div className='tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:gap-6'>
|
||||
<div className='tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:gap-2'>
|
||||
<div>
|
||||
<label
|
||||
htmlFor='status'
|
||||
|
||||
@ -37,7 +37,9 @@ export const ProfileTextForm = ({
|
||||
}, [dataField])
|
||||
|
||||
return (
|
||||
<div className='tw:h-full tw:flex tw:flex-col tw:mt-4'>
|
||||
<div
|
||||
className={`tw:min-h-0 tw:flex tw:flex-col tw:mt-2 ${size === 'full' ? 'tw:flex-1' : 'tw:h-32 tw:flex-none'}`}
|
||||
>
|
||||
<div className='tw:flex tw:justify-between tw:items-center'>
|
||||
<label
|
||||
htmlFor='nextAppointment'
|
||||
@ -58,8 +60,7 @@ export const ProfileTextForm = ({
|
||||
}))
|
||||
}
|
||||
labelStyle={hideInputLabel ? 'tw:hidden' : ''}
|
||||
containerStyle={size === 'full' ? 'tw:grow' : 'tw:h-28 tw:flex-none'}
|
||||
inputStyle={size === 'full' ? 'tw:grow' : 'tw:h-28 tw:max-h-28 tw:flex-none'}
|
||||
containerStyle={size === 'full' ? 'tw:flex-1' : 'tw:h-28 tw:flex-none'}
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,7 @@ export const FlexForm = ({
|
||||
{...templateItem.item}
|
||||
/>
|
||||
) : (
|
||||
<div className='tw:mt-2' key={templateItem.id}>
|
||||
<div className='tw:mt-2 tw:flex-none' key={templateItem.id}>
|
||||
{templateItem.collection} form not found
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -100,7 +100,7 @@ export const TabsForm = ({
|
||||
text: v,
|
||||
}))
|
||||
}
|
||||
containerStyle='tw:grow'
|
||||
containerStyle='tw:pt-2 tw:grow'
|
||||
inputStyle={`tw:h-full ${!item.layer.itemType.show_start_end_input && 'tw:border-t-0 tw:rounded-tl-none'}`}
|
||||
/>
|
||||
<RichTextEditor
|
||||
@ -114,7 +114,7 @@ export const TabsForm = ({
|
||||
}))
|
||||
}
|
||||
inputStyle=''
|
||||
containerStyle='tw:pt-4 tw:h-32'
|
||||
containerStyle='tw:pt-2 tw:h-36 tw:flex-none'
|
||||
required={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -16,6 +16,13 @@
|
||||
.editor-wrapper div {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.tiptap p.is-editor-empty:first-child::before {
|
||||
color: #adb5bd;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user