Skip to content

Commit

Permalink
feat: add max items for scalar arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
foyarash committed Feb 5, 2025
1 parent 4cf5935 commit 295c0e1
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-kids-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@premieroctet/next-admin": patch
---

feat: add max items for scalar arrays
4 changes: 2 additions & 2 deletions apps/example/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ export const options: NextAdminOptions = {
input: <DatePicker />,
},
posts: {
display: "list",
orderField: "order",
display: "table",
},
avatar: {
format: "file",
Expand Down Expand Up @@ -301,6 +300,7 @@ export const options: NextAdminOptions = {
return faker.image.url({ width: 200, height: 200 });
},
},
maxLength: 5,
},
},
display: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ const FileWidget = (props: Props) => {
return [selectedFiles[0]];
}

return [...old, ...Array.from(selectedFiles)];
const newArray = [...old, ...Array.from(selectedFiles)];

if (props.schema?.maxItems) {
return newArray.slice(0, props.schema.maxItems);
}

return newArray;
});
}
};
Expand Down Expand Up @@ -82,7 +88,13 @@ const FileWidget = (props: Props) => {
setFieldDirty(props.name);
setFiles((old) => {
if (acceptsMultipleFiles) {
return [...old, ...Array.from(event.dataTransfer.files)];
const newArray = [...old, ...Array.from(event.dataTransfer.files)];

if (props.schema?.maxItems) {
return newArray.slice(0, props.schema.maxItems);
}

return newArray;
}

return [event.dataTransfer.files[0]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,19 @@ const ScalarArrayField = ({
};

const onAddNewItem = () => {
setFormDataList((prev) => [
...prev,
{
id: crypto.randomUUID(),
value: "",
},
]);
setFormDataList((prev) => {
if (schema.maxItems && prev.length >= schema.maxItems) {
return prev;
}

return [
...prev,
{
id: crypto.randomUUID(),
value: "",
},
];
});
setFieldDirty(name);
};

Expand All @@ -115,7 +121,10 @@ const ScalarArrayField = ({
<Button
type="button"
className="w-fit"
disabled={disabled}
disabled={
disabled ||
(schema.maxItems ? formDataList.length >= schema.maxItems : false)
}
onClick={onAddNewItem}
>
{t("form.widgets.scalar_array.add")}
Expand Down
3 changes: 3 additions & 0 deletions packages/next-admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ type OptionFormatterFromRelationshipSearch<
};
}[RelationshipSearch<ModelFromProperty<T, P>>["field"]];

export type ScalarArray = string[] | number[] | boolean[];

export type EditFieldsOptions<T extends ModelName> = {
[P in Field<T>]?: {
/**
Expand Down Expand Up @@ -284,6 +286,7 @@ export type EditFieldsOptions<T extends ModelName> = {
* a function that takes the field value as parameter and returns a boolean to determine if the field is displayed in the form.
*/
visible?: (value: ModelWithoutRelationships<T>) => boolean;
maxLength?: Model<T>[P] extends ScalarArray ? number : never;
} & (P extends keyof ObjectField<T>
? OptionFormatterFromRelationshipSearch<T, P> &
(
Expand Down
21 changes: 20 additions & 1 deletion packages/next-admin/src/utils/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,8 @@ export const transformSchema = <M extends ModelName>(
fillRelationInSchema(resource, options),
fillDescriptionInSchema(resource, edit),
addCustomProperties(resource, edit),
orderSchema(resource, options)
orderSchema(resource, options),
applyArrayMaxLength(resource, edit)
);

export const applyVisiblePropertiesInSchema = <M extends ModelName>(
Expand Down Expand Up @@ -1294,6 +1295,24 @@ export const addCustomProperties =
return schema;
};

export const applyArrayMaxLength =
<M extends ModelName>(resource: M, editOptions: EditOptions<M>) =>
(schema: Schema) => {
const modelName = resource;
const modelSchema = schema.definitions[
modelName
] as SchemaDefinitions[ModelName];
if (!modelSchema) return schema;
Object.entries(modelSchema.properties).forEach(([name]) => {
const propertyName = name as Field<typeof modelName>;
const fieldValue = schema.definitions[modelName].properties[propertyName];
if (fieldValue && editOptions?.fields?.[propertyName]?.maxLength) {
fieldValue.maxItems = editOptions?.fields?.[propertyName]?.maxLength;
}
});
return schema;
};

export const getResourceFromParams = (
params: string[],
resources: Prisma.ModelName[]
Expand Down

0 comments on commit 295c0e1

Please sign in to comment.