Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
skogsmaskin committed Oct 31, 2023
1 parent 42b0fad commit 932d160
Show file tree
Hide file tree
Showing 23 changed files with 909 additions and 20 deletions.
2 changes: 2 additions & 0 deletions dev/test-studio/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {schemaTypes} from './schema'
import {defaultDocumentNode, newDocumentOptions, structure} from './structure'
import {workshopTool} from './workshop'
import {presenceTool} from './plugins/presence'
import {scratchPadTool} from 'sanity/scratchPad'
import {
CustomLayout,
CustomLogo,
Expand Down Expand Up @@ -85,6 +86,7 @@ const sharedSettings = definePlugin({
structure,
defaultDocumentNode,
}),
scratchPadTool(),
languageFilter({
defaultLanguages: ['nb'],
supportedLanguages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import {BaseRange, Transforms, Text, NodeEntry, Range as SlateRange} from 'slate'
import React, {
forwardRef,
KeyboardEvent,
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import React, {forwardRef, KeyboardEvent, useCallback, useEffect, useMemo, useState} from 'react'
import {
Editable as SlateEditable,
ReactEditor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class PortableTextEditor extends React.Component<PortableTextEditorProps>
super(props)

if (!props.schemaType) {
throw new Error('PortableTextEditor: missing "type" property')
throw new Error('PortableTextEditor: missing "schemaType" property')
}

if (props.incomingPatches$) {
Expand Down Expand Up @@ -271,4 +271,8 @@ export class PortableTextEditor extends React.Component<PortableTextEditorProps>
debug(`Host toggling mark`, mark)
editor.editable?.toggleMark(mark)
}
static getFragment = (editor: PortableTextEditor): PortableTextBlock[] | undefined => {
debug(`Host getting fragment`)
return editor.editable?.getFragment()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,9 @@ export function createWithEditableAPI(
editor.insertBreak()
editor.onChange()
},
getFragment: () => {
return fromSlateValue(editor.getFragment(), types.block.name)
},
})
return editor
}
Expand Down
4 changes: 3 additions & 1 deletion packages/@sanity/portable-text-editor/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
import {Subject, Observable} from 'rxjs'
import {Descendant, Node as SlateNode, Operation as SlateOperation} from 'slate'
import {ReactEditor} from 'slate-react'
import {FocusEvent} from 'react'
import {FocusEvent, PropsWithChildren, ReactElement} from 'react'
import {DOMNode} from 'slate-react/dist/utils/dom'
import type {Patch} from '../types/patch'
import {PortableTextEditor} from '../editor/PortableTextEditor'

Expand All @@ -42,6 +43,7 @@ export interface EditableAPI {
focusBlock: () => PortableTextBlock | undefined
focusChild: () => PortableTextChild | undefined
getSelection: () => EditorSelection
getFragment: () => PortableTextBlock[] | undefined
getValue: () => PortableTextBlock[] | undefined
hasBlockStyle: (style: string) => boolean
hasListStyle: (listStyle: string) => boolean
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/exports/scratchPad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '../src/scratchPad'
15 changes: 15 additions & 0 deletions packages/sanity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@
"import": "./lib/router.esm.js",
"default": "./lib/router.esm.js"
},
"./scratchPad": {
"types": "./lib/exports/scratchPad.d.ts",
"source": "./exports/scratchPad.ts",
"require": "./lib/scratchPad.js",
"node": {
"import": "./lib/scratchPad.cjs.mjs",
"require": "./lib/scratchPad.js"
},
"import": "./lib/scratchPad.esm.js",
"default": "./lib/scratchPad.esm.js"
},
"./package.json": "./package.json"
},
"main": "./lib/index.js",
Expand All @@ -94,6 +105,9 @@
"desk": [
"./lib/exports/desk.d.ts"
],
"scratchPad": [
"./lib/exports/scratchPad.d.ts"
],
"router": [
"./lib/exports/router.d.ts"
]
Expand All @@ -107,6 +121,7 @@
"bin",
"cli.js",
"desk.js",
"scratchPad.js",
"lib",
"router.js",
"src",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export function Compositor(props: Omit<InputProps, 'schemaType' | 'arrayFunction
renderInput,
renderItem,
renderPreview,
scrollElement,
],
)

Expand Down Expand Up @@ -410,7 +411,6 @@ export function Compositor(props: Omit<InputProps, 'schemaType' | 'arrayFunction
[
ariaDescribedBy,
editorHotkeys,
editorHotkeys,
isActive,
isFullscreen,
onItemOpen,
Expand All @@ -423,15 +423,7 @@ export function Compositor(props: Omit<InputProps, 'schemaType' | 'arrayFunction
editorRenderAnnotation,
editorRenderBlock,
editorRenderChild,
handleToggleFullscreen,
initialSelection,
isActive,
isFullscreen,
onCopy,
onItemOpen,
onPaste,
path,
readOnly,
scrollElement,
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface ArrayOfObjectsComponents {
diff?: ComponentType<any>
field?: ComponentType<ArrayFieldProps>
inlineBlock?: ComponentType<BlockProps>
input?: ComponentType<ArrayOfObjectsInputProps>
input?: ComponentType<ArrayOfObjectsInputProps | PortableTextInputProps>
item?: ComponentType<ObjectItemProps>
preview?: ComponentType<PreviewProps>
}
Expand Down
190 changes: 190 additions & 0 deletions packages/sanity/src/scratchPad/components/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import {Path, Schema, ValidationContext, ValidationMarker} from '@sanity/types'
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {validateDocument} from '../../core/validation'
import {applyAll} from '../../core/form/patch/applyPatch'
import {useScratchPad} from '../hooks/useScratchPad'
import {
EMPTY_ARRAY,
FormBuilder,
FormBuilderProps,
getExpandOperations,
noop,
PatchEvent,
setAtPath,
StateTree,
useFormState,
} from 'sanity'

export function ScratchPadForm({schema}: {schema: Schema}) {
const [validation, setValidation] = useState<ValidationMarker[]>([])
const [openPath, onSetOpenPath] = useState<Path>([])
const [fieldGroupState, onSetFieldGroupState] = useState<StateTree<string>>()
const [collapsedPaths, onSetCollapsedPath] = useState<StateTree<boolean>>()
const [collapsedFieldSets, onSetCollapsedFieldSets] = useState<StateTree<boolean>>()
const {patchChannel, document, setDocument} = useScratchPad()
const [focusPath, setFocusPath] = useState<Path>(() => [])

useEffect(() => {
patchChannel.publish({
type: 'mutation',
patches: [],
snapshot: document,
})
}, [document, patchChannel])

const schemaType = schema.get('scratchPadDocument')

if (!schemaType) {
throw new Error('missing schema type')
}

if (schemaType.jsonType !== 'object') {
throw new Error('schema type is not an object')
}

useEffect(() => {
validateStaticDocument(document, schema, (result) => setValidation(result))
}, [document, schema])

const formState = useFormState(schemaType, {
focusPath,
collapsedPaths,
collapsedFieldSets,
comparisonValue: null,
fieldGroupState,
openPath,
presence: EMPTY_ARRAY,
validation,
value: document,
})

const formStateRef = useRef(formState)
formStateRef.current = formState

const handleFocus = useCallback(
(nextFocusPath: Path) => {
setFocusPath(nextFocusPath)
},
[setFocusPath],
)

const handleBlur = useCallback(() => {
setFocusPath([])
}, [setFocusPath])

const patchRef = useRef<(event: PatchEvent) => void>(() => {
throw new Error('Nope')
})

patchRef.current = (event: PatchEvent) => {
setDocument((currentDocumentValue) => applyAll(currentDocumentValue, event.patches))
}

const handleChange = useCallback((event: any) => patchRef.current(event), [])

const handleOnSetCollapsedPath = useCallback((path: Path, collapsed: boolean) => {
onSetCollapsedPath((prevState) => setAtPath(prevState, path, collapsed))
}, [])

const handleOnSetCollapsedFieldSet = useCallback((path: Path, collapsed: boolean) => {
onSetCollapsedFieldSets((prevState) => setAtPath(prevState, path, collapsed))
}, [])

const handleSetActiveFieldGroup = useCallback(
(path: Path, groupName: string) =>
onSetFieldGroupState((prevState) => setAtPath(prevState, path, groupName)),
[],
)

const setOpenPath = useCallback(
(path: Path) => {
const ops = getExpandOperations(formStateRef.current!, path)
ops.forEach((op) => {
if (op.type === 'expandPath') {
onSetCollapsedPath((prevState) => setAtPath(prevState, op.path, false))
}
if (op.type === 'expandFieldSet') {
onSetCollapsedFieldSets((prevState) => setAtPath(prevState, op.path, false))
}
if (op.type === 'setSelectedGroup') {
onSetFieldGroupState((prevState) => setAtPath(prevState, op.path, op.groupName))
}
})
onSetOpenPath(path)
},
[formStateRef],
)

const formBuilderProps: FormBuilderProps = useMemo(
() => ({
// eslint-disable-next-line camelcase
__internal_patchChannel: patchChannel,
changed: false,
changesOpen: false,
collapsedFieldSets: undefined,
collapsedPaths: undefined,
focused: formState?.focused,
focusPath: formState?.focusPath || EMPTY_ARRAY,
groups: formState?.groups || EMPTY_ARRAY,
id: formState?.id || '',
level: formState?.level || 0,
members: formState?.members || EMPTY_ARRAY,
onChange: handleChange,
onFieldGroupSelect: noop,
onPathBlur: handleBlur,
onPathFocus: handleFocus,
onPathOpen: setOpenPath,
onSelectFieldGroup: handleSetActiveFieldGroup,
onSetFieldSetCollapsed: handleOnSetCollapsedFieldSet,
onSetPathCollapsed: handleOnSetCollapsedPath,
path: EMPTY_ARRAY,
presence: EMPTY_ARRAY,
schemaType: formState?.schemaType || schemaType,
validation,
value: formState?.value,
}),
[
formState?.focusPath,
formState?.focused,
formState?.groups,
formState?.id,
formState?.level,
formState?.members,
formState?.schemaType,
formState?.value,
handleBlur,
handleChange,
handleFocus,
handleOnSetCollapsedFieldSet,
handleOnSetCollapsedPath,
handleSetActiveFieldGroup,
patchChannel,
schemaType,
setOpenPath,
validation,
],
)

return <FormBuilder {...formBuilderProps} />
}

async function validateStaticDocument(
document: any,
schema: any,
setCallback: (result: ValidationMarker[]) => void,
) {
const result = await validateDocument(getClient, document, schema)
setCallback(result)
}

const client = createMockSanityClient() as any as ReturnType<ValidationContext['getClient']>
const getClient = (options: {apiVersion: string}) => client

export function createMockSanityClient() {
const _client = {
fetch: (query: string) => Promise.resolve(null) as Promise<any>,
withConfig: () => _client,
}

return _client
}
27 changes: 27 additions & 0 deletions packages/sanity/src/scratchPad/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'
import {Card} from '@sanity/ui'
import {Pane, PaneContent, PaneLayout} from '../../desk/components'
import {schema} from '../config'
import {ScratchPadAssistant} from './assistant/Assistant'
import {ScratchPadForm} from './Form'

export default function ScratchPadLayout() {
return (
<PaneLayout height="fill">
<Pane id="scratch-pad-input-pane">
<PaneContent>
<Card padding={3}>
<ScratchPadForm schema={schema} />
</Card>
</PaneContent>
</Pane>
<Pane id="scratch-pad-assistant-pane">
<PaneContent>
<Card padding={3}>
<ScratchPadAssistant />
</Card>
</PaneContent>
</Pane>
</PaneLayout>
)
}
15 changes: 15 additions & 0 deletions packages/sanity/src/scratchPad/components/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, {useRef} from 'react'
import {PortableTextEditor} from '@sanity/portable-text-editor'
import {ScratchPadProvider} from '../context/ScratchPadProvider'
import ScratchPadLayout from './Layout'

export default function ScratchPadRoot() {
const editorRef = useRef<PortableTextEditor | null>(null)
const assistantPromptRef = useRef<HTMLTextAreaElement | null>(null)

return (
<ScratchPadProvider editorRef={editorRef} assistantPromptRef={assistantPromptRef}>
<ScratchPadLayout />
</ScratchPadProvider>
)
}
Loading

0 comments on commit 932d160

Please sign in to comment.