@firecms/collection_editor 3.3.0-canary.d3242eb → 3.4.0-canary.00f9be5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -72,6 +72,34 @@ export function DisplaySettingsForm({
72
72
  return result;
73
73
  }, [resolvedCollection.properties]);
74
74
 
75
+ // Get image properties (for imageProperty), used as the thumbnail in Cards/Kanban views
76
+ const imageProperties = useMemo(() => {
77
+ const result: { key: string; label: string; property: Property; }[] = [];
78
+ if (!resolvedCollection.properties) return result;
79
+
80
+ const isImageProperty = (prop: any): boolean => {
81
+ if (!prop || !("dataType" in prop)) return false;
82
+ if (prop.dataType === "string") {
83
+ return Boolean(prop.storage?.acceptedFiles?.includes("image/*")) || prop.url === "image";
84
+ }
85
+ if (prop.dataType === "array" && prop.of) {
86
+ return isImageProperty(prop.of);
87
+ }
88
+ return false;
89
+ };
90
+
91
+ Object.entries(resolvedCollection.properties).forEach(([key, prop]) => {
92
+ if (isImageProperty(prop)) {
93
+ result.push({
94
+ key,
95
+ label: (prop as Property).name || key,
96
+ property: prop as Property
97
+ });
98
+ }
99
+ });
100
+ return result;
101
+ }, [resolvedCollection.properties]);
102
+
75
103
  const showErrors = submitCount > 0;
76
104
 
77
105
  // Document ID generation value
@@ -246,6 +274,83 @@ export function DisplaySettingsForm({
246
274
  })()}
247
275
  </div>
248
276
 
277
+ {/* Image Property (used as thumbnail in Cards/Kanban views) */}
278
+ <div className={"col-span-12 mt-4"}>
279
+ {(() => {
280
+ const imagePropertyMissing = Boolean(values.imageProperty) &&
281
+ !imageProperties.some(p => p.key === values.imageProperty);
282
+
283
+ return (
284
+ <>
285
+ <Typography variant={"label"} color={"secondary"} className={"ml-3.5"}>{t("image_property")}</Typography>
286
+ <Select
287
+ key={`image-select-${imageProperties.length}`}
288
+ name="imageProperty"
289
+ size={"large"}
290
+ fullWidth={true}
291
+ position={"item-aligned"}
292
+ disabled={imageProperties.length === 0}
293
+ error={imagePropertyMissing}
294
+ value={(values.imageProperty as string) ?? ""}
295
+ onValueChange={(v) => {
296
+ setFieldValue("imageProperty", v || undefined);
297
+ }}
298
+ renderValue={(value) => {
299
+ if (imagePropertyMissing) {
300
+ return <span className="text-red-500">{value} ({t("not_found_suffix")})</span>;
301
+ }
302
+ const prop = imageProperties.find(p => p.key === value);
303
+ if (!prop) return t("select_a_property");
304
+ const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
305
+ return (
306
+ <div className="flex items-center gap-2">
307
+ <PropertyConfigBadge propertyConfig={fieldConfig} />
308
+ <span>{prop.label}</span>
309
+ </div>
310
+ );
311
+ }}
312
+ endAdornment={values.imageProperty ? (
313
+ <IconButton
314
+ size="small"
315
+ onClick={(e) => {
316
+ e.stopPropagation();
317
+ setFieldValue("imageProperty", undefined);
318
+ }}
319
+ >
320
+ <CloseIcon size="small" />
321
+ </IconButton>
322
+ ) : undefined}
323
+ >
324
+ {imageProperties.map((prop) => {
325
+ const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
326
+ return (
327
+ <SelectItem key={prop.key} value={prop.key}>
328
+ <div className="flex items-center gap-3">
329
+ <PropertyConfigBadge propertyConfig={fieldConfig} />
330
+ <div>
331
+ <div>{prop.label}</div>
332
+ <Typography variant="caption" color="secondary">
333
+ {fieldConfig?.name || prop.label}
334
+ </Typography>
335
+ </div>
336
+ </div>
337
+ </SelectItem>
338
+ );
339
+ })}
340
+ </Select>
341
+ <FieldCaption error={imagePropertyMissing}>
342
+ {imagePropertyMissing
343
+ ? t("image_property_not_found", { property: values.imageProperty as string ?? "" })
344
+ : imageProperties.length === 0
345
+ ? t("no_image_properties")
346
+ : t("image_property_description")
347
+ }
348
+ </FieldCaption>
349
+ </>
350
+ );
351
+ })()}
352
+ </div>
353
+
249
354
  {/* Default row size */}
250
355
  <div className={"col-span-12"}>
251
356
  <Select
@@ -498,13 +498,11 @@ function PropertyEditFormFields({
498
498
  showErrors={showErrors} />;
499
499
  } else if (selectedFieldConfigId === "file_upload") {
500
500
  childComponent =
501
- <StoragePropertyField existing={existing}
502
- multiple={false}
501
+ <StoragePropertyField multiple={false}
503
502
  disabled={disabled} />;
504
503
  } else if (selectedFieldConfigId === "multi_file_upload") {
505
504
  childComponent =
506
- <StoragePropertyField existing={existing}
507
- multiple={true}
505
+ <StoragePropertyField multiple={true}
508
506
  disabled={disabled} />;
509
507
  } else if (selectedFieldConfigId === "switch") {
510
508
  childComponent = <BooleanPropertyField disabled={disabled} />;
@@ -20,11 +20,9 @@ import { useTranslation } from "@firecms/core";
20
20
 
21
21
  export function StoragePropertyField({
22
22
  multiple,
23
- existing,
24
23
  disabled
25
24
  }: {
26
25
  multiple: boolean;
27
- existing: boolean;
28
26
  disabled: boolean;
29
27
  }) {
30
28
 
@@ -179,7 +177,7 @@ export function StoragePropertyField({
179
177
  }: FormexFieldProps) => {
180
178
  return <SwitchControl
181
179
  label={t("include_bucket_url")}
182
- disabled={existing || disabled}
180
+ disabled={disabled}
183
181
  form={form}
184
182
  field={field}/>;
185
183
  }}
@@ -197,7 +195,7 @@ export function StoragePropertyField({
197
195
  }: FormexFieldProps) => {
198
196
  return <SwitchControl
199
197
  label={t("save_url_instead_of_path")}
200
- disabled={existing || disabled}
198
+ disabled={disabled}
201
199
  form={form}
202
200
  field={field}/>;
203
201
  }}
@@ -19,12 +19,6 @@ import { EditorEntityAction } from "./ui/EditorEntityAction";
19
19
  import { KanbanSetupAction } from "./ui/KanbanSetupAction";
20
20
  import { AddKanbanColumnAction } from "./ui/AddKanbanColumnAction";
21
21
  import { collectionEditorTranslationsEn } from "./locales/en";
22
- import { collectionEditorTranslationsEs } from "./locales/es";
23
- import { collectionEditorTranslationsDe } from "./locales/de";
24
- import { collectionEditorTranslationsFr } from "./locales/fr";
25
- import { collectionEditorTranslationsIt } from "./locales/it";
26
- import { collectionEditorTranslationsHi } from "./locales/hi";
27
- import { collectionEditorTranslationsPt } from "./locales/pt";
28
22
 
29
23
  export interface CollectionConfigControllerProps<EC extends PersistedCollection = PersistedCollection, USER extends User = User> {
30
24
 
@@ -139,13 +133,16 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
139
133
  ActionsTop: EditorEntityAction,
140
134
  },
141
135
  i18n: {
136
+ // Only English is bundled; the rest load when that language is selected.
137
+ // Seven languages of strings in every plugin bundle is dead weight for
138
+ // the six nobody is reading.
142
139
  en: collectionEditorTranslationsEn,
143
- es: collectionEditorTranslationsEs,
144
- de: collectionEditorTranslationsDe,
145
- fr: collectionEditorTranslationsFr,
146
- it: collectionEditorTranslationsIt,
147
- hi: collectionEditorTranslationsHi,
148
- pt: collectionEditorTranslationsPt
140
+ es: () => import("./locales/es").then((m) => m.collectionEditorTranslationsEs),
141
+ de: () => import("./locales/de").then((m) => m.collectionEditorTranslationsDe),
142
+ fr: () => import("./locales/fr").then((m) => m.collectionEditorTranslationsFr),
143
+ it: () => import("./locales/it").then((m) => m.collectionEditorTranslationsIt),
144
+ hi: () => import("./locales/hi").then((m) => m.collectionEditorTranslationsHi),
145
+ pt: () => import("./locales/pt").then((m) => m.collectionEditorTranslationsPt)
149
146
  }
150
147
  };
151
148
  }