From d4a57db3e86b3f43e590ca5bd430140ba3d67529 Mon Sep 17 00:00:00 2001 From: Valery Sizikova Date: Mon, 18 Sep 2023 12:10:56 +1000 Subject: [PATCH 1/4] editing in modal instead of inline --- packages/keystatic/src/form/api.tsx | 1 - .../component-blocks/chromeful-element.tsx | 126 +++++++++--------- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/packages/keystatic/src/form/api.tsx b/packages/keystatic/src/form/api.tsx index 1bc808af8..823404a32 100644 --- a/packages/keystatic/src/form/api.tsx +++ b/packages/keystatic/src/form/api.tsx @@ -292,7 +292,6 @@ export type ComponentBlock< chromeless?: false; toolbar?: (props: { props: Record; - onShowEditMode(): void; onRemove(): void; isValid: boolean; }) => ReactElement; diff --git a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx index 26313f83a..b0858bf63 100644 --- a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx +++ b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx @@ -3,19 +3,21 @@ import { ReactNode, useMemo, useState, - useCallback, Fragment, PropsWithChildren, forwardRef, Ref, + useId, } from 'react'; import { RenderElementProps, useSelected } from 'slate-react'; -import { ActionButton, Button } from '@keystar/ui/button'; +import { ActionButton, Button, ButtonGroup } from '@keystar/ui/button'; +import { Dialog, DialogTrigger } from '@keystar/ui/dialog'; import { FieldMessage } from '@keystar/ui/field'; import { trash2Icon } from '@keystar/ui/icon/icons/trash2Icon'; import { Icon } from '@keystar/ui/icon'; import { Flex } from '@keystar/ui/layout'; +import { Content } from '@keystar/ui/slots'; import { css, tokenSchema } from '@keystar/ui/style'; import { Tooltip, TooltipTrigger } from '@keystar/ui/tooltip'; import { Text } from '@keystar/ui/typography'; @@ -33,6 +35,11 @@ import { FormValueContentFromPreviewProps, NonChildFieldComponentSchema, } from '../../../../form-from-preview'; +import { + previewPropsToValue, + setValueToPreviewProps, +} from '../../../../get-value'; +import { createGetPreviewProps } from '../../../../preview-props'; import { NotEditable } from '../primitives'; import { blockElementSpacing } from '../ui-utils'; @@ -60,14 +67,6 @@ export function ChromefulComponentBlockElement(props: { [props.componentBlock, props.elementProps] ); - const [editMode, setEditMode] = useState(false); - const onCloseEditMode = useCallback(() => { - setEditMode(false); - }, []); - const onShowEditMode = useCallback(() => { - setEditMode(true); - }, []); - const ChromefulToolbar = props.componentBlock.toolbar ?? DefaultToolbarWithChrome; return ( @@ -83,26 +82,14 @@ export function ChromefulComponentBlockElement(props: { {props.componentBlock.label} - {editMode ? ( - - -
{props.children}
-
- ) : ( - - {props.renderedBlock} - - - )} + + {props.renderedBlock} + + ); @@ -153,11 +140,10 @@ const BlockPrimitive = forwardRef(function BlockPrimitive( }); function DefaultToolbarWithChrome({ - onShowEditMode, + props, onRemove, isValid, }: { - onShowEditMode(): void; onRemove(): void; props: any; isValid: boolean; @@ -167,9 +153,14 @@ function DefaultToolbarWithChrome({ - onShowEditMode()}> - {stringFormatter.format('edit')} - + + {stringFormatter.format('edit')} + {close => ( + + + + )} + @@ -190,38 +181,51 @@ function DefaultToolbarWithChrome({ function FormValue({ onClose, props, - isValid, }: { props: GenericPreviewProps; onClose(): void; - isValid: boolean; }) { + const stringFormatter = useLocalizedStringFormatter(l10nMessages); + const formId = useId(); const [forceValidation, setForceValidation] = useState(false); + const val: unknown[] = previewPropsToValue(props as any); + const [state, setState] = useState(val); + const previewProps = useMemo( + () => createGetPreviewProps(props.schema, setState, () => undefined), + [props.schema] + )(state); return ( - - - - + <> + + { + if (event.target !== event.currentTarget) return; + event.preventDefault(); + if (!clientSideValidateProp(props.schema, state, undefined)) { + setForceValidation(true); + } else { + setValueToPreviewProps(state, props); + onClose(); + } + }} + direction="column" + gap="xxlarge" + > + + + + + + + + ); } From 283be07b73fa58c466ae2b039832dbed2d861834 Mon Sep 17 00:00:00 2001 From: Valery Sizikova Date: Mon, 18 Sep 2023 12:14:23 +1000 Subject: [PATCH 2/4] changeset --- .changeset/moody-buttons-grow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/moody-buttons-grow.md diff --git a/.changeset/moody-buttons-grow.md b/.changeset/moody-buttons-grow.md new file mode 100644 index 000000000..b0108e730 --- /dev/null +++ b/.changeset/moody-buttons-grow.md @@ -0,0 +1,5 @@ +--- +'@keystatic/core': patch +--- + +Editor: fields editing in a modal instead of in-document From 55d7fa53cabc30e62ad393de5037412aa4d15d16 Mon Sep 17 00:00:00 2001 From: Valery Sizikova Date: Mon, 18 Sep 2023 13:47:20 +1000 Subject: [PATCH 3/4] fix per comments --- packages/keystatic/src/form/api.tsx | 1 + .../component-blocks/chromeful-element.tsx | 47 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/keystatic/src/form/api.tsx b/packages/keystatic/src/form/api.tsx index 823404a32..1bc808af8 100644 --- a/packages/keystatic/src/form/api.tsx +++ b/packages/keystatic/src/form/api.tsx @@ -292,6 +292,7 @@ export type ComponentBlock< chromeless?: false; toolbar?: (props: { props: Record; + onShowEditMode(): void; onRemove(): void; isValid: boolean; }) => ReactElement; diff --git a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx index b0858bf63..7a19f2158 100644 --- a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx +++ b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx @@ -8,11 +8,12 @@ import { forwardRef, Ref, useId, + useCallback, } from 'react'; import { RenderElementProps, useSelected } from 'slate-react'; import { ActionButton, Button, ButtonGroup } from '@keystar/ui/button'; -import { Dialog, DialogTrigger } from '@keystar/ui/dialog'; +import { Dialog, DialogContainer } from '@keystar/ui/dialog'; import { FieldMessage } from '@keystar/ui/field'; import { trash2Icon } from '@keystar/ui/icon/icons/trash2Icon'; import { Icon } from '@keystar/ui/icon'; @@ -20,7 +21,7 @@ import { Flex } from '@keystar/ui/layout'; import { Content } from '@keystar/ui/slots'; import { css, tokenSchema } from '@keystar/ui/style'; import { Tooltip, TooltipTrigger } from '@keystar/ui/tooltip'; -import { Text } from '@keystar/ui/typography'; +import { Heading, Text } from '@keystar/ui/typography'; import l10nMessages from '../../../../../app/l10n/index.json'; import { @@ -67,6 +68,14 @@ export function ChromefulComponentBlockElement(props: { [props.componentBlock, props.elementProps] ); + const [editMode, setEditMode] = useState(false); + const onCloseEditMode = useCallback(() => { + setEditMode(false); + }, []); + const onShowEditMode = useCallback(() => { + setEditMode(true); + }, []); + const ChromefulToolbar = props.componentBlock.toolbar ?? DefaultToolbarWithChrome; return ( @@ -88,7 +97,24 @@ export function ChromefulComponentBlockElement(props: { isValid={isValid} onRemove={props.onRemove} props={props.previewProps} + onShowEditMode={onShowEditMode} /> + onCloseEditMode()}> + {(() => { + if (!editMode) { + return; + } + return ( + + Edit {props.componentBlock.label} + + + ); + })()} + @@ -140,10 +166,11 @@ const BlockPrimitive = forwardRef(function BlockPrimitive( }); function DefaultToolbarWithChrome({ - props, + onShowEditMode, onRemove, isValid, }: { + onShowEditMode(): void; onRemove(): void; props: any; isValid: boolean; @@ -153,14 +180,9 @@ function DefaultToolbarWithChrome({ - - {stringFormatter.format('edit')} - {close => ( - - - - )} - + onShowEditMode()}> + {stringFormatter.format('edit')} + @@ -188,8 +210,7 @@ function FormValue({ const stringFormatter = useLocalizedStringFormatter(l10nMessages); const formId = useId(); const [forceValidation, setForceValidation] = useState(false); - const val: unknown[] = previewPropsToValue(props as any); - const [state, setState] = useState(val); + const [state, setState] = useState(previewPropsToValue(props as any)); const previewProps = useMemo( () => createGetPreviewProps(props.schema, setState, () => undefined), [props.schema] From 6ffa86a507403f5006f0190525f0b484bf88e78d Mon Sep 17 00:00:00 2001 From: Emma Hamilton Date: Mon, 18 Sep 2023 14:02:25 +1000 Subject: [PATCH 4/4] Use function to initialise state so it only happens once --- .../DocumentEditor/component-blocks/chromeful-element.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx index 7a19f2158..2fb75582d 100644 --- a/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx +++ b/packages/keystatic/src/form/fields/document/DocumentEditor/component-blocks/chromeful-element.tsx @@ -210,7 +210,7 @@ function FormValue({ const stringFormatter = useLocalizedStringFormatter(l10nMessages); const formId = useId(); const [forceValidation, setForceValidation] = useState(false); - const [state, setState] = useState(previewPropsToValue(props as any)); + const [state, setState] = useState(() => previewPropsToValue(props)); const previewProps = useMemo( () => createGetPreviewProps(props.schema, setState, () => undefined), [props.schema]