Refactored and improved seeds (#8695)
- Added a new Seeder service to help with custom object seeds - Added RichTextFieldInput to edit a rich text field directly on the table, but deactivated it for now.
This commit is contained in:
@ -17,7 +17,6 @@ import { isFieldRelationToOneObject } from '@/object-record/record-field/types/g
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
|
||||
import { RichTextFieldInput } from '@/object-record/record-field/meta-types/input/components/RichTextFieldInput';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
@ -31,7 +30,6 @@ import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/is
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
|
||||
@ -167,8 +165,6 @@ export const FieldInput = ({
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldRichText(fieldDefinition) ? (
|
||||
<RichTextFieldInput />
|
||||
) : isFieldArray(fieldDefinition) ? (
|
||||
<ArrayFieldInput
|
||||
onCancel={onCancel}
|
||||
|
||||
@ -28,6 +28,8 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor
|
||||
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue';
|
||||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
|
||||
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue';
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue';
|
||||
@ -111,6 +113,10 @@ export const usePersistField = () => {
|
||||
isFieldRawJson(fieldDefinition) &&
|
||||
isFieldRawJsonValue(valueToPersist);
|
||||
|
||||
const fieldIsRichText =
|
||||
isFieldRichText(fieldDefinition) &&
|
||||
isFieldRichTextValue(valueToPersist);
|
||||
|
||||
const fieldIsArray =
|
||||
isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist);
|
||||
|
||||
@ -131,7 +137,8 @@ export const usePersistField = () => {
|
||||
fieldIsMultiSelect ||
|
||||
fieldIsAddress ||
|
||||
fieldIsRawJson ||
|
||||
fieldIsArray;
|
||||
fieldIsArray ||
|
||||
fieldIsRichText;
|
||||
|
||||
if (isValuePersistable) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 : '';
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`);
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
const draftValueParsed: PartialBlock[] = isNonEmptyString(draftValue)
|
||||
? JSON.parse(draftValue)
|
||||
: draftValue;
|
||||
|
||||
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,
|
||||
};
|
||||
};
|
||||
@ -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 = () => {
|
||||
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);
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue: fieldValueParsed,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
||||
@ -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,
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -46,6 +46,8 @@ export type FieldDateMetadata = {
|
||||
};
|
||||
};
|
||||
|
||||
export type FieldNumberVariant = 'number' | 'percentage';
|
||||
|
||||
export type FieldNumberMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
@ -53,7 +55,7 @@ export type FieldNumberMetadata = {
|
||||
isPositive?: boolean;
|
||||
settings?: {
|
||||
decimals?: number;
|
||||
type?: 'percentage' | 'number';
|
||||
type?: FieldNumberVariant;
|
||||
};
|
||||
};
|
||||
|
||||
@ -209,6 +211,7 @@ export type FieldMetadata =
|
||||
| FieldActorMetadata
|
||||
| FieldArrayMetadata
|
||||
| FieldTsVectorMetadata;
|
||||
|
||||
export type FieldTextValue = string;
|
||||
export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ?
|
||||
export type FieldDateTimeValue = string | null;
|
||||
@ -255,7 +258,7 @@ export type FieldRelationValue<
|
||||
export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[];
|
||||
export type FieldJsonValue = Record<string, Json> | Json[] | null;
|
||||
|
||||
export type FieldRichTextValue = Record<string, Json> | Json[] | null;
|
||||
export type FieldRichTextValue = null | string;
|
||||
|
||||
export type FieldActorValue = {
|
||||
source: string;
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
FieldRatingMetadata,
|
||||
FieldRawJsonMetadata,
|
||||
FieldRelationMetadata,
|
||||
FieldRichTextMetadata,
|
||||
FieldSelectMetadata,
|
||||
FieldTextMetadata,
|
||||
FieldUuidMetadata,
|
||||
@ -68,7 +69,7 @@ type AssertFieldMetadataFunction = <
|
||||
: E extends 'RAW_JSON'
|
||||
? FieldRawJsonMetadata
|
||||
: E extends 'RICH_TEXT'
|
||||
? FieldTextMetadata
|
||||
? FieldRichTextMetadata
|
||||
: E extends 'ACTOR'
|
||||
? FieldActorMetadata
|
||||
: E extends 'ARRAY'
|
||||
|
||||
@ -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(),
|
||||
]);
|
||||
|
||||
export const isFieldRichTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldRichTextValue =>
|
||||
richTextSchema.safeParse(fieldValue).success;
|
||||
@ -5,6 +5,7 @@ import { RecordTableCellWrapper } from '@/object-record/record-table/record-tabl
|
||||
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
|
||||
|
||||
export const RecordTableCellsVisible = () => {
|
||||
const { isSelected } = useRecordTableRowContextOrThrow();
|
||||
@ -15,6 +16,10 @@ export const RecordTableCellsVisible = () => {
|
||||
visibleTableColumnsComponentSelector,
|
||||
);
|
||||
|
||||
if (!isNonEmptyArray(visibleTableColumns)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tableColumnsAfterFirst = visibleTableColumns.slice(1);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user