Skip to content

Commit

Permalink
ArrayContainer now can hold children state
Browse files Browse the repository at this point in the history
  • Loading branch information
fgatti675 committed Oct 28, 2024
1 parent cf30cd7 commit 3d051d1
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 158 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import equal from "react-fast-compare"

import { ArrayContainer, EnumValueConfig, EnumValues, FieldCaption, } from "@firecms/core";
import { ArrayContainer, ArrayEntryParams, EnumValueConfig, EnumValues, FieldCaption, } from "@firecms/core";
import {
AutoAwesomeIcon,
Badge,
Expand Down Expand Up @@ -121,7 +121,10 @@ function EnumFormFields({
const inferredValuesRef = React.useRef(new Set());
const inferredValues = inferredValuesRef.current;

const buildEntry = (index: number, internalId: number) => {
const buildEntry = ({
index,
internalId
}:ArrayEntryParams) => {
const justAdded = lastInternalIdAdded === internalId;
const entryError = errors?.enumValues && errors?.enumValues[index];
return <EnumEntry index={index}
Expand Down
49 changes: 36 additions & 13 deletions packages/firecms_core/src/components/ArrayContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ import {
useOutsideAlerter
} from "@firecms/ui";

export type ArrayEntryParams = {
index: number,
internalId: number,
isDragging: boolean,
storedProps?: object,
storeProps: (props: object) => void
};

export type ArrayEntryBuilder = (params: ArrayEntryParams) => React.ReactNode;

export interface ArrayContainerProps<T> {
droppableId: string;
value: T[];
addLabel: string;
buildEntry: (index: number, internalId: number, isDragging:boolean) => React.ReactNode;
buildEntry: ArrayEntryBuilder;
disabled?: boolean;
size?: "small" | "medium";
onInternalIdAdded?: (id: number) => void;
Expand Down Expand Up @@ -67,6 +77,12 @@ export function ArrayContainer<T>({
? Object.values(internalIdsRef.current)
: []);

const itemCustomPropsRef = useRef<Record<number, object>>({});

const updateItemCustomProps = useCallback((internalId: number, customProps: object) => {
itemCustomPropsRef.current[internalId] = customProps;
}, []);

useEffect(() => {
if (hasValue && value && value.length !== internalIds.length) {
const newInternalIds = value.map((v, index) => {
Expand Down Expand Up @@ -149,6 +165,8 @@ export function ArrayContainer<T>({
remove={remove}
copy={copy}
isDragging={snapshot.isDragging}
storedProps={itemCustomPropsRef.current[internalId]}
updateItemCustomProps={updateItemCustomProps}
/>
);
}}
Expand Down Expand Up @@ -176,6 +194,8 @@ export function ArrayContainer<T>({
remove={remove}
copy={copy}
isDragging={snapshot.isDragging}
storedProps={itemCustomPropsRef.current[internalId]}
updateItemCustomProps={updateItemCustomProps}
/>
)}
</Draggable>
Expand Down Expand Up @@ -208,10 +228,12 @@ type ArrayContainerItemProps = {
internalId: number,
size?: "small" | "medium",
disabled: boolean,
buildEntry: (index: number, internalId: number, isDragging: boolean) => React.ReactNode,
buildEntry: ArrayEntryBuilder,
remove: (index: number) => void,
copy: (index: number) => void,
isDragging: boolean,
storedProps?: object,
updateItemCustomProps: (internalId: number, props: object) => void
};

export function ArrayContainerItem({
Expand All @@ -223,29 +245,30 @@ export function ArrayContainerItem({
buildEntry,
remove,
copy,
isDragging
isDragging,
storedProps,
updateItemCustomProps
}: ArrayContainerItemProps) {

const [onHover, setOnHover] = React.useState(false);
const setOnHoverTrue = useCallback(() => setOnHover(true), []);
const setOnHoverFalse = useCallback(() => setOnHover(false), []);

return <div
onMouseEnter={setOnHoverTrue}
onMouseMove={setOnHoverTrue}
onMouseLeave={setOnHoverFalse}
ref={provided.innerRef}
{...provided.draggableProps}
style={provided.draggableProps.style}
className={`${
(isDragging || onHover) ? "hover:bg-slate-50 dark:hover:bg-gray-800 dark:hover:bg-opacity-20" : ""
} mb-1 rounded-md opacity-100`}
!isDragging ? "hover:bg-slate-50 dark:hover:bg-gray-800 dark:hover:bg-opacity-20" : ""
} rounded-md opacity-100`}
>
<div
className="flex items-start">
<div
className="flex-grow w-[calc(100%-48px)] text-text-primary dark:text-text-primary-dark">
{buildEntry(index, internalId, isDragging)}
{buildEntry({
index,
internalId,
isDragging,
storedProps,
storeProps: (props: object) => updateItemCustomProps(internalId, props)
})}
</div>
<ArrayItemOptions direction={size === "small" ? "row" : "column"}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
entity?: Entity<M>
}) {

console.log("values", entity?.values);

const fireCMSContext = useFireCMSContext();
const customizationController = useCustomizationController();

Expand Down
39 changes: 22 additions & 17 deletions packages/firecms_core/src/form/PropertyFieldBinding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
minimalistView,
autoFocus,
index,
size
size,
onPropertyChange
}: PropertyFieldBindingProps<T, M>): ReactElement<PropertyFieldBindingProps<T, M>> {

const customizationController = useCustomizationController();
Expand Down Expand Up @@ -124,6 +125,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
if (!propertyConfig) {
console.log("INTERNAL: Could not find field config for property", {
propertyKey,
property,
resolvedProperty,
fields: customizationController.propertyConfigs,
propertyConfig
Expand Down Expand Up @@ -159,13 +161,14 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
partOfArray,
minimalistView,
autoFocus,
size
size,
onPropertyChange
};

return <FieldInternal
Component={Component as ComponentType<FieldProps>}
componentProps={componentProps}
fieldProps={fieldProps}/>;
formexFieldProps={fieldProps}/>;
}}
</Field>
);
Expand All @@ -191,26 +194,27 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
autoFocus,
context,
disabled,
size
size,
onPropertyChange
},
fieldProps
formexFieldProps
}:
{
Component: ComponentType<FieldProps<T, any, M>>,
componentProps: ResolvedPropertyFieldBindingProps<T, M>,
fieldProps: FormexFieldProps<T, any>
formexFieldProps: FormexFieldProps<T, any>
}) {

const { plugins } = useCustomizationController();

const customFieldProps: any = property.customProps;
const value = fieldProps.field.value;
// const initialValue = fieldProps.meta.initialValue;
const error = getIn(fieldProps.form.errors, propertyKey);
const touched = getIn(fieldProps.form.touched, propertyKey);
const value = formexFieldProps.field.value;
// const initialValue = formexFieldProps.meta.initialValue;
const error = getIn(formexFieldProps.form.errors, propertyKey);
const touched = getIn(formexFieldProps.form.touched, propertyKey);

const showError: boolean = error &&
(fieldProps.form.submitCount > 0 || property.validation?.unique) &&
(formexFieldProps.form.submitCount > 0 || property.validation?.unique) &&
(!Array.isArray(error) || !!error.filter((e: any) => !!e).length);

const WrappedComponent: ComponentType<FieldProps<T, any, M>> | null = useWrappedComponent({
Expand All @@ -223,16 +227,16 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
});
const UsedComponent: ComponentType<FieldProps<T>> = WrappedComponent ?? Component;

const isSubmitting = fieldProps.form.isSubmitting;
const isSubmitting = formexFieldProps.form.isSubmitting;

const setValue = useCallback((value: T | null, shouldValidate?: boolean) => {
fieldProps.form.setFieldTouched(propertyKey, true, false);
fieldProps.form.setFieldValue(propertyKey, value, shouldValidate);
formexFieldProps.form.setFieldTouched(propertyKey, true, false);
formexFieldProps.form.setFieldValue(propertyKey, value, shouldValidate);
}, []);

const setFieldValue = useCallback((otherPropertyKey: string, value: CMSType | null, shouldValidate?: boolean) => {
fieldProps.form.setFieldTouched(propertyKey, true, false);
fieldProps.form.setFieldValue(otherPropertyKey, value, shouldValidate);
formexFieldProps.form.setFieldTouched(propertyKey, true, false);
formexFieldProps.form.setFieldValue(otherPropertyKey, value, shouldValidate);
}, []);

const cmsFieldProps: FieldProps<T, CustomProps, M> = {
Expand All @@ -253,7 +257,8 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
autoFocus: autoFocus ?? false,
customProps: customFieldProps,
context,
size
size,
onPropertyChange
};

return (
Expand Down
47 changes: 0 additions & 47 deletions packages/firecms_core/src/form/components/FormikArrayContainer.tsx

This file was deleted.

1 change: 0 additions & 1 deletion packages/firecms_core/src/form/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from "./FormikArrayContainer";
export * from "./FieldHelperText";
export * from "./LabelWithIcon";
export * from "./LabelWithIconAndTooltip";
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useCallback, useMemo } from "react";
import { Entity, EntityCollection, EntityReference, FieldProps, ResolvedProperty } from "../../types";
import { ReferencePreview } from "../../preview";
import { FieldHelperText, FormikArrayContainer, LabelWithIconAndTooltip } from "../components";
import { ErrorView } from "../../components";
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
import { ArrayContainer, ArrayEntryParams, ErrorView } from "../../components";
import { getIconForProperty, getReferenceFrom } from "../../util";

import { useNavigationController, useReferenceDialog } from "../../hooks";
Expand Down Expand Up @@ -74,7 +74,12 @@ export function ArrayOfReferencesFieldBinding({
referenceDialogController.open();
};

const buildEntry = useCallback((index: number, internalId: number) => {
const buildEntry = useCallback(({
index,
internalId,
storedProps,
storeProps
}: ArrayEntryParams) => {
const entryValue = value && value.length > index ? value[index] : undefined;
if (!entryValue)
return <div>Internal ERROR</div>;
Expand Down Expand Up @@ -109,13 +114,14 @@ export function ArrayOfReferencesFieldBinding({

{collection && <div className={"group"}>

<FormikArrayContainer value={value}
addLabel={property.name ? "Add reference to " + property.name : "Add reference"}
name={propertyKey}
buildEntry={buildEntry}
disabled={isSubmitting}
setFieldValue={setFieldValue}
newDefaultEntry={property.of.defaultValue}/>
<ArrayContainer droppableId={propertyKey}
value={value}
disabled={isSubmitting}
buildEntry={buildEntry}
addLabel={property.name ? "Add reference to " + property.name : "Add reference"}
newDefaultEntry={property.of.defaultValue}
onValueChange={(value) => setFieldValue(propertyKey, value)}
/>

<Button
className="my-4 justify-center text-left"
Expand Down
Loading

0 comments on commit 3d051d1

Please sign in to comment.