Skip to content

Commit

Permalink
Merge branch 'main' into settings-phone-search-option
Browse files Browse the repository at this point in the history
  • Loading branch information
guillim authored Dec 24, 2024
2 parents af7c1f0 + cba38ab commit a80184b
Show file tree
Hide file tree
Showing 69 changed files with 5,916 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -167,8 +165,6 @@ export const FieldInput = ({
onTab={onTab}
onShiftTab={onShiftTab}
/>
) : isFieldRichText(fieldDefinition) ? (
<RichTextFieldInput />
) : isFieldArray(fieldDefinition) ? (
<ArrayFieldInput
onCancel={onCancel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -111,6 +113,10 @@ export const usePersistField = () => {
isFieldRawJson(fieldDefinition) &&
isFieldRawJsonValue(valueToPersist);

const fieldIsRichText =
isFieldRichText(fieldDefinition) &&
isFieldRichTextValue(valueToPersist);

const fieldIsArray =
isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist);

Expand All @@ -131,7 +137,8 @@ export const usePersistField = () => {
fieldIsMultiSelect ||
fieldIsAddress ||
fieldIsRawJson ||
fieldIsArray;
fieldIsArray ||
fieldIsRichText;

if (isValuePersistable) {
const fieldName = fieldDefinition.metadata.fieldName;
Expand Down
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 : '';

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,
};
};
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 = () => {
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,
};
};
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,
});

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
Expand Up @@ -46,14 +46,16 @@ export type FieldDateMetadata = {
};
};

export type FieldNumberVariant = 'number' | 'percentage';

export type FieldNumberMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
placeHolder: string;
isPositive?: boolean;
settings?: {
decimals?: number;
type?: 'percentage' | 'number';
type?: FieldNumberVariant;
};
};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
FieldRatingMetadata,
FieldRawJsonMetadata,
FieldRelationMetadata,
FieldRichTextMetadata,
FieldSelectMetadata,
FieldTextMetadata,
FieldUuidMetadata,
Expand Down Expand Up @@ -68,7 +69,7 @@ type AssertFieldMetadataFunction = <
: E extends 'RAW_JSON'
? FieldRawJsonMetadata
: E extends 'RICH_TEXT'
? FieldTextMetadata
? FieldRichTextMetadata
: E extends 'ACTOR'
? FieldActorMetadata
: E extends 'ARRAY'
Expand Down
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(),
]);

export const isFieldRichTextValue = (
fieldValue: unknown,
): fieldValue is FieldRichTextValue =>
richTextSchema.safeParse(fieldValue).success;
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -15,6 +16,10 @@ export const RecordTableCellsVisible = () => {
visibleTableColumnsComponentSelector,
);

if (!isNonEmptyArray(visibleTableColumns)) {
return null;
}

const tableColumnsAfterFirst = visibleTableColumns.slice(1);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const SettingsOptionIconCustomizer = ({
<StyledIconCustomizer zoom={zoom} rotate={rotate}>
<Icon
size={theme.icon.size.lg}
color={theme.IllustrationIcon.color.grey}
color={theme.IllustrationIcon.color.gray}
stroke={theme.icon.stroke.md}
/>
</StyledIconCustomizer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' }",
category: 'Basic',
} as const satisfies SettingsFieldTypeConfig<FieldRichTextValue>,
[FieldMetadataType.Array]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ interface BlockEditorProps {
onBlur?: () => void;
onPaste?: (event: ClipboardEvent) => void;
onChange?: () => void;
readonly?: boolean;
}

const StyledEditor = styled.div`
width: 100%;
& .editor {
background: ${({ theme }) => theme.background.primary};
font-size: 13px;
color: ${({ theme }) => theme.font.color.primary};
min-height: 400px;
}
& .editor [class^='_inlineContent']:before {
color: ${({ theme }) => theme.font.color.tertiary};
Expand Down Expand Up @@ -124,6 +127,7 @@ export const BlockEditor = ({
onBlur,
onChange,
onPaste,
readonly,
}: BlockEditorProps) => {
const theme = useTheme();
const blockNoteTheme = theme.name === 'light' ? 'light' : 'dark';
Expand Down Expand Up @@ -155,6 +159,7 @@ export const BlockEditor = ({
theme={blockNoteTheme}
slashMenu={false}
sideMenu={false}
editable={!readonly}
>
<CustomSideMenu editor={editor} />
<SuggestionMenuController
Expand Down
Loading

0 comments on commit a80184b

Please sign in to comment.