Skip to content

Commit

Permalink
feat: integrate default value
Browse files Browse the repository at this point in the history
  • Loading branch information
sashuk committed Jun 26, 2023
1 parent b51f4c5 commit f94dd0f
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const formConfig: FormConfigProps = {

const april2nd = new Date(2023, 3, 2)

const INITIAL_RTE_VALUE = '<p>Rich Text Editor</p>'
const INITIAL_RTE_VALUE =
'<p>Rich Text Editor</p><br/><br/><p>With empty lines</p>'

const Example = () => (
<ConfigProvider value={formConfig}>
Expand Down
2 changes: 2 additions & 0 deletions packages/picasso-forms/src/RichTextEditor/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type InternalProps = RichTextEditorProps & { value: string }

export const RichTextEditor = (props: Props) => {
const { onChange, defaultValue, label, titleCase, ...rest } = props

const [value, setValue] = useState('')
const {
mutators: { setHasMultilineCounter },
Expand All @@ -36,6 +37,7 @@ export const RichTextEditor = (props: Props) => {
},
[onChange, setValue]
)

const hiddenInputId = `${props.id}-hidden-input`

return (
Expand Down
4 changes: 3 additions & 1 deletion packages/picasso/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
"debounce": "^1.2.1",
"@lexical/rich-text": "^0.9.2",
"detect-browser": "^5.3.0",
"emoji-mart": "^5.5.2",
"glider-js": "^1.7.8",
"hast-to-hyperscript": "^9.0.1",
"hast-util-from-dom": "^3.0.0",
"hast-util-sanitize": "^3.0.2",
"hast-util-to-html": "^7.1.3",
"lexical": "^0.9.1",
"hast-util-to-dom": "^3.1.1",
"lexical": "^0.11.1",
"quill": "^1.3.7",
"quill-emoji": "^0.2.0",
"quill-paste-smart": "^1.4.9",
Expand Down
23 changes: 18 additions & 5 deletions packages/picasso/src/LexicalEditor/LexicalEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { makeStyles } from '@material-ui/core/styles'
import { LexicalComposer } from '@lexical/react/LexicalComposer'
import type { InitialConfigType } from '@lexical/react/LexicalComposer'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'
import { HeadingNode } from '@lexical/rich-text'
import { $generateHtmlFromNodes } from '@lexical/html'
import { ListItemNode, ListNode } from '@lexical/list'
import { $isRootTextContentEmpty } from '@lexical/text'
import type { LexicalEditor as LexicalEditorType } from 'lexical'

import { createLexicalTheme } from './utils'
import { CustomOnChangePlugin } from './plugins'
import { createLexicalTheme, setEditorValue } from './utils'
import noop from '../utils/noop'
import Container from '../Container'
import Typography from '../Typography'
Expand All @@ -25,6 +26,7 @@ import ToolbarPlugin from '../LexicalEditorToolbarPlugin'
import LexicalTextLengthPlugin from '../LexicalTextLengthPlugin'
import LexicalListPlugin from '../LexicalListPlugin'
import LexicalHeadingsReplacementPlugin from '../LexicalHeadingsReplacementPlugin'
import type { ASTType } from '../RichText'

const useStyles = makeStyles<Theme>(styles, {
name: 'LexicalEditor',
Expand All @@ -38,7 +40,7 @@ export type Props = BaseProps & {
/** Indicates that an element is to be focused on page load */
autoFocus?: boolean
/** Default value in [HAST](https://github.com/syntax-tree/hast) format */
// defaultValue?: ASTType
defaultValue?: ASTType
/**
* This Boolean attribute indicates that the user cannot interact with the control.
*/
Expand Down Expand Up @@ -85,7 +87,7 @@ const LexicalEditor = forwardRef<HTMLDivElement, Props>(function LexicalEditor(
const {
// plugins,
autoFocus = false,
// defaultValue,
defaultValue,
disabled = false,
id,
onChange = noop,
Expand Down Expand Up @@ -130,6 +132,11 @@ const LexicalEditor = forwardRef<HTMLDivElement, Props>(function LexicalEditor(

const editorConfig: InitialConfigType = useMemo(
() => ({
editorState: (editor: LexicalEditorType) => {
if (defaultValue) {
setEditorValue(editor, defaultValue)
}
},
theme,
onError(error: Error) {
throw error
Expand All @@ -143,6 +150,7 @@ const LexicalEditor = forwardRef<HTMLDivElement, Props>(function LexicalEditor(

const handleChange = useCallback(
(editorState, editor) => {
console.log('@@@ internal handleChange')
editorState.read(() => {
const isEmpty = $isRootTextContentEmpty(editor.isComposing(), false)

Expand Down Expand Up @@ -171,7 +179,12 @@ const LexicalEditor = forwardRef<HTMLDivElement, Props>(function LexicalEditor(
// remount Toolbar when disabled
key={`${disabled || !isFocused}`}
/>
<OnChangePlugin ignoreSelectionChange onChange={handleChange} />
<CustomOnChangePlugin
ignoreSelectionChange
triggerOnChangeOnStart={!!defaultValue}
onChange={handleChange}
/>
{/* <OnChangePlugin ignoreSelectionChange onChange={handleChange} /> */}
{autoFocus && <AutoFocusPlugin />}

<LexicalHeadingsReplacementPlugin />
Expand Down
97 changes: 97 additions & 0 deletions packages/picasso/src/LexicalEditor/plugins/CustomOnChangePlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict'

import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import type { EditorState, LexicalEditor } from 'lexical'

const react = require('react')

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
const CAN_USE_DOM =
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
const useLayoutEffectImpl = CAN_USE_DOM
? react.useLayoutEffect
: react.useEffect
const useLayoutEffect = useLayoutEffectImpl

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
const CustomOnChangePlugin = ({
ignoreHistoryMergeTagChange = true,
ignoreSelectionChange = false,
triggerOnChangeOnStart = false,
onChange,
}: {
ignoreHistoryMergeTagChange?: boolean
ignoreSelectionChange?: boolean
triggerOnChangeOnStart?: boolean
onChange: (
editorState: EditorState,
editor: LexicalEditor,
tags: Set<string>
) => void
}) => {
const [editor] = useLexicalComposerContext()

useLayoutEffect(() => {
if (onChange) {
return editor.registerUpdateListener(
({
editorState,
dirtyElements,
dirtyLeaves,
prevEditorState,
tags,
}) => {
if (triggerOnChangeOnStart && prevEditorState.isEmpty()) {
onChange(editorState, editor, tags)

return
}

if (
(ignoreSelectionChange &&
dirtyElements.size === 0 &&
dirtyLeaves.size === 0) ||
(ignoreHistoryMergeTagChange && tags.has('history-merge')) ||
prevEditorState.isEmpty()
) {
return
}

onChange(editorState, editor, tags)
}
)
}
}, [editor, ignoreHistoryMergeTagChange, ignoreSelectionChange, onChange])

return null
}

export default CustomOnChangePlugin
1 change: 1 addition & 0 deletions packages/picasso/src/LexicalEditor/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as CustomOnChangePlugin } from './CustomOnChangePlugin'
10 changes: 10 additions & 0 deletions packages/picasso/src/LexicalEditor/utils/getDomValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { HastNode } from 'hast-util-to-dom/lib'
import toHtml from 'hast-util-to-html'

import type { ASTType } from '../../RichText'

export const getDomValue = (value: ASTType) => {
const parser = new DOMParser()

return parser.parseFromString(toHtml(value as HastNode), 'text/html')
}
2 changes: 2 additions & 0 deletions packages/picasso/src/LexicalEditor/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export { synchronizeToolbarState } from './synchronizeToolbarState'
export { toolbarStateReducer } from './toolbarState'
export { getLexicalNode } from './getLexicalNode'
export { createLexicalTheme } from './createLexicalTheme'
export { getDomValue } from './getDomValue'
export { setEditorValue } from './setEditorValue'
41 changes: 41 additions & 0 deletions packages/picasso/src/LexicalEditor/utils/setEditorValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { LexicalEditor as LexicalEditorType } from 'lexical'
import {
$createParagraphNode,
$isLineBreakNode,
$getRoot,
$isDecoratorNode,
$isElementNode,
} from 'lexical'
import { $generateNodesFromDOM } from '@lexical/html'

import type { ASTType } from '../../RichText'
import { getDomValue } from './getDomValue'

export const setEditorValue = (editor: LexicalEditorType, value: ASTType) => {
const domValue = getDomValue(value)

editor.update(
() => {
const root = $getRoot()
const lexicalValueNodes = $generateNodesFromDOM(editor, domValue)

lexicalValueNodes.forEach(node => {
if ($isElementNode(node) || $isDecoratorNode(node)) {
root.append(node)
} else if ($isLineBreakNode(node)) {
const paragraphNode = $createParagraphNode()

root.append(paragraphNode)
} else {
const paragraphNode = $createParagraphNode()

paragraphNode.append(node)
root.append(paragraphNode)
}
})
},
{
discrete: true,
}
)
}
3 changes: 2 additions & 1 deletion packages/picasso/src/RichTextEditor/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const RichTextEditor = forwardRef<HTMLDivElement, Props>(
// plugins,
autoFocus = false,
className,
// defaultValue,
defaultValue,
disabled,
id,
onChange = noop,
Expand Down Expand Up @@ -188,6 +188,7 @@ export const RichTextEditor = forwardRef<HTMLDivElement, Props>(
testIds={testIds}
disabled={disabled}
autoFocus={autoFocus}
defaultValue={defaultValue}
/>
{hiddenInputId && (
// Native `for` attribute on label does not work for div target
Expand Down
21 changes: 17 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12921,6 +12921,14 @@ hast-util-sanitize@^3.0.2:
dependencies:
xtend "^4.0.0"

hast-util-to-dom@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/hast-util-to-dom/-/hast-util-to-dom-3.1.1.tgz#7cd9f925b0cff8ec2c95474ef896b1865fcefd62"
integrity sha512-hDiYqOapuWzLPDMADCD5z6re/07OQOpQuT2YO5hxPjaxWTtgcbjqCjlv4KtyMuEQiW4wKTIPoK+japvbZ5zqxg==
dependencies:
property-information "^6.0.0"
web-namespaces "^2.0.0"

hast-util-to-html@^7.1.3:
version "7.1.3"
resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz"
Expand Down Expand Up @@ -15421,10 +15429,10 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"

lexical@^0.9.1:
version "0.9.2"
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.9.2.tgz#b4e910322270a7ad06fc7e625066ffa43fa1d4fb"
integrity sha512-8S3fNd053/QD5DJ4wePlAY4Rc5k2LFvg2lBcmKLpPUZ+1/1tr35Kdyr5wyFWJKy1rbEUqjP74Hvly/lcXSipag==
lexical@^0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.11.1.tgz#a4ca061c16d9798b3c0b9f2f8e89cc077385e4ea"
integrity sha512-PhAGADxqzwJldmkVK5tvkaARTULCdeJjfhxWTnJQXTAlApE9heJir7SWxbxeUx1G5gdKZQFicGhOQlDXJmma2Q==

li@^1.3.0:
version "1.3.0"
Expand Down Expand Up @@ -23604,6 +23612,11 @@ web-namespaces@^1.0.0:
resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz"
integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==

web-namespaces@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==

webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
Expand Down

0 comments on commit f94dd0f

Please sign in to comment.