diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index d5f30a5832..0d172dab56 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -118,6 +118,8 @@ type PropertyProps = { onDelete: (id: UserProp["id"]) => void; setCssProperty: SetProperty; currentStyle: Style; + required: boolean; + existingProps: string[]; }; const Property = ({ @@ -128,32 +130,51 @@ const Property = ({ onDelete, setCssProperty, currentStyle, + required, + existingProps, }: PropertyProps) => { - const meta = getComponentMetaProps(component); + const metaProps = getComponentMetaProps(component); - const argType = meta[userProp.prop as keyof typeof meta]; + const argType = metaProps[userProp.prop as keyof typeof metaProps]; const isInvalid = userProp.prop != null && userProp.prop.length > 0 && typeof argType === "undefined" && !userProp.prop.match(/^data-(.)+/); - const allProps = Object.keys(meta); + const allProps = Object.keys(metaProps).filter( + (propName) => existingProps.includes(propName) === false + ); return ( <> - { - if (name != null) { - onChangePropName(name); - } - }} - onSubmit={onChangePropName} - isInvalid={isInvalid} - isReadonly={userProp.required ?? false} - /> + {required ? ( + + ) : ( + { + if (name != null) { + onChangePropName(name); + } + }} + onSubmit={(name) => { + if (existingProps.includes(name) === false) { + onChangePropName(name); + } + // @todo: show error or invalid state + }} + isInvalid={isInvalid} + isReadonly={required} + /> + )} {isInvalid ? ( @@ -169,16 +190,15 @@ const Property = ({ currentStyle={currentStyle} /> )} - {userProp.required !== true && ( - - )} + ); }; @@ -198,6 +218,7 @@ export const PropsPanel = ({ handleChangePropName, handleChangePropValue, handleDeleteProp, + isRequired, } = usePropsLogic({ selectedInstanceData, publish }); const { setProperty: setCssProperty, currentStyle } = useStyleData({ @@ -217,6 +238,8 @@ export const PropsPanel = ({ ); + const existingProps = userProps.map((userProp) => userProp.prop); + return ( @@ -248,6 +271,8 @@ export const PropsPanel = ({ setCssProperty={setCssProperty} currentStyle={currentStyle} onDelete={handleDeleteProp} + required={isRequired(userProp)} + existingProps={existingProps} /> ))} diff --git a/apps/designer/app/designer/features/props-panel/use-props-logic.ts b/apps/designer/app/designer/features/props-panel/use-props-logic.ts index c77ee9bcbf..f12573555c 100644 --- a/apps/designer/app/designer/features/props-panel/use-props-logic.ts +++ b/apps/designer/app/designer/features/props-panel/use-props-logic.ts @@ -162,18 +162,20 @@ export const usePropsLogic = ({ ? [] : selectedInstanceData.props.props; - // Prefer ordering from `initialProps` - const [userProps, setUserProps] = useState>(() => + const [requiredProps] = useState>(() => uniqBy( [ ...getInitialProps(selectedInstanceData), ...getRequiredProps(selectedInstanceData), ...getPropsWithDefaultValue(selectedInstanceData), - ...props, ], "prop" ) ); + // Prefer ordering from `initialProps` + const [userProps, setUserProps] = useState>(() => + uniqBy([...requiredProps, ...props], "prop") + ); const updateProps = (updates: UserPropsUpdates["updates"]) => { publish({ @@ -274,11 +276,18 @@ export const usePropsLogic = ({ ]); }; + const isRequired = (prop: UserProp) => { + return requiredProps.some( + (requiredProp) => requiredProp.prop === prop.prop + ); + }; + return { addEmptyProp, userProps, handleChangePropName, handleChangePropValue, handleDeleteProp, + isRequired, }; };