-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactored and improved seeds #8695
Changes from 30 commits
5077f95
9bfd3aa
afa0c9c
50c3df5
047f44a
dad62c4
5c8b606
6c3426c
253da19
4bfbb0b
214d0d8
8ae3386
3fce159
1247412
61ed304
61ef4eb
c4e121a
a29acc0
b1433d0
894a794
378e390
4fc18fb
7bfcb47
59a48b2
1f53d0f
7da7f1a
5d9afc2
4a210bc
59478a0
6e36303
5bcb56f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import { useTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useTextFieldDisplay'; | ||
import { useRichTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay'; | ||
import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; | ||
import { PartialBlock } from '@blocknote/core'; | ||
|
||
export const RichTextFieldDisplay = () => { | ||
const { fieldValue } = useTextFieldDisplay(); | ||
const parsedField = | ||
fieldValue === '' ? null : (JSON.parse(fieldValue) as PartialBlock[]); | ||
const { fieldValue } = useRichTextFieldDisplay(); | ||
|
||
return <>{getFirstNonEmptyLineOfRichText(parsedField)}</>; | ||
return ( | ||
<div> | ||
<span>{getFirstNonEmptyLineOfRichText(fieldValue)}</span> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { useContext } from 'react'; | ||
import { useRecoilState, useRecoilValue } from 'recoil'; | ||
|
||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; | ||
import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; | ||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; | ||
import { FieldMetadataType } from '~/generated-metadata/graphql'; | ||
|
||
import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; | ||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; | ||
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; | ||
import { PartialBlock } from '@blocknote/core'; | ||
import { isNonEmptyString } from '@sniptt/guards'; | ||
import { FieldContext } from '../../contexts/FieldContext'; | ||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; | ||
|
||
export const useRichTextField = () => { | ||
const { recordId, fieldDefinition, hotkeyScope, maxWidth } = | ||
useContext(FieldContext); | ||
|
||
assertFieldMetadata( | ||
FieldMetadataType.RichText, | ||
isFieldRichText, | ||
fieldDefinition, | ||
); | ||
|
||
const fieldName = fieldDefinition.metadata.fieldName; | ||
|
||
const [fieldValue, setFieldValue] = useRecoilState<FieldRichTextValue>( | ||
recordStoreFamilySelector({ | ||
recordId, | ||
fieldName: fieldName, | ||
}), | ||
); | ||
const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : ''; | ||
lucasbordeau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const { setDraftValue, getDraftValueSelector } = | ||
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`); | ||
|
||
const draftValue = useRecoilValue(getDraftValueSelector()); | ||
|
||
const draftValueParsed: PartialBlock[] = isNonEmptyString(draftValue) | ||
? JSON.parse(draftValue) | ||
: draftValue; | ||
lucasbordeau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const persistField = usePersistField(); | ||
|
||
const persistRichTextField = (nextValue: PartialBlock[]) => { | ||
if (!nextValue) { | ||
persistField(null); | ||
} else { | ||
const parsedValueToPersist = JSON.stringify(nextValue); | ||
|
||
persistField(parsedValueToPersist); | ||
} | ||
}; | ||
|
||
return { | ||
draftValue: draftValueParsed, | ||
setDraftValue, | ||
maxWidth, | ||
fieldDefinition, | ||
fieldValue: fieldRichTextValue, | ||
setFieldValue, | ||
hotkeyScope, | ||
persistRichTextField, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useContext } from 'react'; | ||
|
||
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; | ||
|
||
import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; | ||
import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; | ||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; | ||
import { PartialBlock } from '@blocknote/core'; | ||
import { FieldMetadataType } from '~/generated-metadata/graphql'; | ||
import { parseJson } from '~/utils/parseJson'; | ||
import { FieldContext } from '../../contexts/FieldContext'; | ||
|
||
export const useRichTextFieldDisplay = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: hook uses FieldContext but doesn't handle case where context is undefined |
||
const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); | ||
|
||
assertFieldMetadata( | ||
FieldMetadataType.RichText, | ||
isFieldRichText, | ||
fieldDefinition, | ||
); | ||
|
||
const fieldName = fieldDefinition.metadata.fieldName; | ||
|
||
const fieldValue = useRecordFieldValue<FieldRichTextValue | undefined>( | ||
recordId, | ||
fieldName, | ||
); | ||
|
||
const fieldValueParsed = parseJson<PartialBlock[]>(fieldValue); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: parseJson could return undefined/null - need to handle this case explicitly to avoid runtime errors when using fieldValueParsed |
||
|
||
return { | ||
fieldDefinition, | ||
fieldValue: fieldValueParsed, | ||
hotkeyScope, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,59 @@ | ||
import { RichTextFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextFieldDisplay'; | ||
import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema'; | ||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; | ||
import { useRichTextField } from '@/object-record/record-field/meta-types/hooks/useRichTextField'; | ||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; | ||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents'; | ||
import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; | ||
import { BlockEditorComponentInstanceContext } from '@/ui/input/editor/contexts/BlockEditorCompoponeInstanceContext'; | ||
import { PartialBlock } from '@blocknote/core'; | ||
import { useCreateBlockNote } from '@blocknote/react'; | ||
import styled from '@emotion/styled'; | ||
|
||
export const RichTextFieldInput = () => { | ||
return <RichTextFieldDisplay />; | ||
import { useContext, useRef } from 'react'; | ||
|
||
const StyledRichTextContainer = styled.div` | ||
height: 400px; | ||
width: 500px; | ||
|
||
overflow: auto; | ||
`; | ||
|
||
export type RichTextFieldInputProps = { | ||
onClickOutside?: FieldInputClickOutsideEvent; | ||
}; | ||
|
||
export const RichTextFieldInput = ({ | ||
onClickOutside, | ||
}: RichTextFieldInputProps) => { | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
const { recordId } = useContext(FieldContext); | ||
const { draftValue, hotkeyScope, persistRichTextField, fieldDefinition } = | ||
useRichTextField(); | ||
|
||
const editor = useCreateBlockNote({ | ||
initialContent: draftValue, | ||
domAttributes: { editor: { class: 'editor' } }, | ||
schema: BLOCK_SCHEMA, | ||
}); | ||
|
||
lucasbordeau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const handleClickOutside = (event: MouseEvent | TouchEvent) => { | ||
onClickOutside?.(() => persistRichTextField(editor.document), event); | ||
}; | ||
|
||
useRegisterInputEvents<PartialBlock[]>({ | ||
inputRef: containerRef, | ||
inputValue: draftValue, | ||
onClickOutside: handleClickOutside, | ||
hotkeyScope, | ||
}); | ||
|
||
return ( | ||
<StyledRichTextContainer ref={containerRef}> | ||
<BlockEditorComponentInstanceContext.Provider | ||
value={{ instanceId: `${recordId}-${fieldDefinition.fieldMetadataId}` }} | ||
> | ||
<BlockEditor editor={editor} /> | ||
</BlockEditorComponentInstanceContext.Provider> | ||
</StyledRichTextContainer> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { z } from 'zod'; | ||
import { FieldRichTextValue } from '../FieldMetadata'; | ||
|
||
export const richTextSchema: z.ZodType<FieldRichTextValue> = z.union([ | ||
z.null(), // Exclude literal values other than null | ||
z.string(), | ||
]); | ||
Comment on lines
+4
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Schema only allows null or string, but rich text content is typically stored as JSON string of PartialBlock[] based on usage in useRichTextField.ts |
||
|
||
export const isFieldRichTextValue = ( | ||
fieldValue: unknown, | ||
): fieldValue is FieldRichTextValue => | ||
richTextSchema.safeParse(fieldValue).success; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,7 +125,7 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel | |
[FieldMetadataType.RichText]: { | ||
label: 'Rich Text', | ||
Icon: IllustrationIconSetting, | ||
exampleValue: { key: 'value' }, | ||
exampleValue: "{ key: 'value' }", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: The RichText example value should be an object, not a string. This will cause type errors with FieldRichTextValue. Keep it as |
||
category: 'Basic', | ||
} as const satisfies SettingsFieldTypeConfig<FieldRichTextValue>, | ||
[FieldMetadataType.Array]: { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 let's revert this