@firecms/core 3.0.0-canary.170 → 3.0.0-canary.171

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.170",
4
+ "version": "3.0.0-canary.171",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -50,9 +50,9 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "dependencies": {
53
- "@firecms/editor": "^3.0.0-canary.170",
54
- "@firecms/formex": "^3.0.0-canary.170",
55
- "@firecms/ui": "^3.0.0-canary.170",
53
+ "@firecms/editor": "^3.0.0-canary.171",
54
+ "@firecms/formex": "^3.0.0-canary.171",
55
+ "@firecms/ui": "^3.0.0-canary.171",
56
56
  "@hello-pangea/dnd": "^17.0.0",
57
57
  "@radix-ui/react-portal": "^1.1.2",
58
58
  "clsx": "^2.1.1",
@@ -104,7 +104,7 @@
104
104
  "dist",
105
105
  "src"
106
106
  ],
107
- "gitHead": "21c8b5f6236a1824f5896b69bb4810c05326c5c7",
107
+ "gitHead": "02cba913b7bfc98199d4b700a37642e701e83c56",
108
108
  "publishConfig": {
109
109
  "access": "public"
110
110
  },
@@ -239,7 +239,7 @@ export function ArrayContainer<T>({
239
239
  variant={"text"}
240
240
  size={size === "small" ? "small" : "medium"}
241
241
  color="primary"
242
- disabled={disabled || value.length >= max}
242
+ disabled={disabled || value?.length >= max}
243
243
  startIcon={<AddIcon/>}
244
244
  onClick={insertInEnd}>
245
245
  {addLabel ?? "Add"}
@@ -262,7 +262,8 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
262
262
  fullPath,
263
263
  collection,
264
264
  entitiesDisplayedFirst,
265
- forceFilter
265
+ forceFilter,
266
+ updateUrl: false,
266
267
  });
267
268
 
268
269
  const {
@@ -35,12 +35,10 @@ const operationLabels = {
35
35
  const multipleSelectOperations = ["array-contains-any", "in", "not-in"];
36
36
 
37
37
  export function ReferenceFilterField({
38
- name,
39
38
  value,
40
39
  setValue,
41
40
  isArray,
42
41
  path,
43
- title,
44
42
  includeId = true,
45
43
  previewProperties,
46
44
  setHidden
@@ -50,9 +48,11 @@ export function ReferenceFilterField({
50
48
  ? ["array-contains"]
51
49
  : ["==", "!=", ">", "<", ">=", "<="];
52
50
 
53
- isArray
54
- ? possibleOperations.push("array-contains-any")
55
- : possibleOperations.push("in", "not-in");
51
+ if (isArray) {
52
+ possibleOperations.push("array-contains-any");
53
+ } else {
54
+ possibleOperations.push("in", "not-in");
55
+ }
56
56
 
57
57
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
58
58
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
@@ -144,7 +144,7 @@ export function ReferenceFilterField({
144
144
 
145
145
  return (
146
146
 
147
- <div className="flex w-[440px] flex-row">
147
+ <div className="flex w-[480px] flex-row">
148
148
  <div className="w-[140px]">
149
149
  <Select value={operation}
150
150
  size={"large"}
@@ -161,7 +161,7 @@ export function ReferenceFilterField({
161
161
  </Select>
162
162
  </div>
163
163
 
164
- <div className="flex-grow ml-2 h-full gap-2 flex flex-col">
164
+ <div className="flex-grow ml-2 h-full gap-2 flex flex-col w-[340px]">
165
165
 
166
166
  {internalValue && Array.isArray(internalValue) && <div>
167
167
  {internalValue.map((ref, index) => buildEntry(ref))}
@@ -5,6 +5,7 @@ import { useDataOrder } from "../../hooks/data/useDataOrder";
5
5
  import {
6
6
  Entity,
7
7
  EntityCollection,
8
+ EntityReference,
8
9
  EntityTableController,
9
10
  FilterValues,
10
11
  FireCMSContext,
@@ -128,11 +129,11 @@ export function useDataSourceTableController<M extends Record<string, any> = any
128
129
  }, [initialSort, forceFilter]);
129
130
 
130
131
  const {
131
- filterValues: initialFilteUrl,
132
+ filterValues: initialFilterUrl,
132
133
  sortBy: initialSortUrl,
133
134
  } = parseFilterAndSort(window.location.search);
134
135
 
135
- const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? initialFilteUrl ?? initialFilter ?? undefined);
136
+ const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? (updateUrl ? initialFilterUrl : undefined) ?? initialFilter ?? undefined);
136
137
  const [sortBy, setSortBy] = React.useState<[Extract<keyof M, string>, "asc" | "desc"] | undefined>(initialSortUrl ?? initialSortInternal);
137
138
 
138
139
  useUpdateUrl(filterValues, sortBy, searchString, updateUrl);
@@ -302,7 +303,7 @@ function useUpdateUrl<M extends Record<string, any> = any>(
302
303
  }, [filterValues, sortBy, searchString, updateUrl]);
303
304
  }
304
305
 
305
- function encodeFilterAndSort(filterValues?: FilterValues<string> | undefined, sortBy?: [string, "asc" | "desc"] | undefined) {
306
+ function encodeFilterAndSort(filterValues?: FilterValues<string>, sortBy?: [string, "asc" | "desc"] | undefined) {
306
307
  const entries: Record<string, string> = {};
307
308
  if (sortBy) {
308
309
  entries["__sort"] = encodeURIComponent(sortBy[0]);
@@ -311,8 +312,30 @@ function encodeFilterAndSort(filterValues?: FilterValues<string> | undefined, so
311
312
  if (filterValues) {
312
313
  Object.entries(filterValues).forEach(([key, value]) => {
313
314
  if (value) {
314
- entries[encodeURIComponent(`${key}_op`)] = encodeURIComponent(value[0]);
315
- entries[encodeURIComponent(`${key}_value`)] = encodeURIComponent(value[1]);
315
+ const [op, val] = value;
316
+ let encodedValue: any = val;
317
+ try {
318
+ if (typeof val === "object") {
319
+ if (val instanceof Date) {
320
+ encodedValue = val.toISOString();
321
+ } else if (Array.isArray(val)) {
322
+ encodedValue = JSON.stringify(val, (key, value) => {
323
+ if (value instanceof EntityReference) {
324
+ return encodeRef(value);
325
+ }
326
+ return value;
327
+ });
328
+ } else if (val instanceof EntityReference) {
329
+ encodedValue = encodeRef(val);
330
+ }
331
+ }
332
+ } catch (e) {
333
+ encodedValue = val;
334
+ }
335
+ if (encodedValue !== undefined) {
336
+ entries[encodeURIComponent(`${key}_op`)] = encodeURIComponent(op);
337
+ entries[encodeURIComponent(`${key}_value`)] = encodeURIComponent(encodedValue.toString());
338
+ }
316
339
  }
317
340
  });
318
341
  }
@@ -331,10 +354,14 @@ function parseFilterAndSort<M>(search: string): {
331
354
  let sortBy: [string, "asc" | "desc"] | undefined = undefined;
332
355
  entries.forEach((value, key) => {
333
356
  if (key === "__sort") {
334
- sortBy = [value, entries.get("__sort_order") as "asc" | "desc"];
357
+ sortBy = [decodeURIComponent(value), entries.get("__sort_order") as "asc" | "desc"];
335
358
  } else if (key.endsWith("_op")) {
336
- const filterValue = entries.get(`${key.replace("_op", "_value")}`);
337
- filterValues[key.replace("_op", "")] = [value as WhereFilterOp, filterValue as string];
359
+ const field = key.replace("_op", "");
360
+ const filterOp = decodeURIComponent(value) as WhereFilterOp;
361
+ const filterValStr = entries.get(`${field}_value`);
362
+ if (filterValStr !== null) {
363
+ filterValues[field] = [filterOp, decodeString(filterValStr)];
364
+ }
338
365
  }
339
366
  });
340
367
 
@@ -343,3 +370,51 @@ function parseFilterAndSort<M>(search: string): {
343
370
  sortBy
344
371
  }
345
372
  }
373
+
374
+ function isDate(dateString: string): boolean {
375
+ // Define a regex pattern that matches the exact date format: 2025-01-07T23:00:00.000Z
376
+ const regexPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
377
+
378
+ // Test the dateString against the regex pattern
379
+ if (!regexPattern.test(dateString)) {
380
+ return false;
381
+ }
382
+
383
+ // If the regex matches, further validate if it is a valid UTC date
384
+ const date = new Date(dateString);
385
+ return date.toISOString() === dateString;
386
+ }
387
+
388
+ function encodeRef(val: EntityReference) {
389
+ return `ref::${val.path}/${val.id}`;
390
+ }
391
+
392
+ function decodeString(val: string): EntityReference | Date | string {
393
+ let parsedFilterVal: any = val;
394
+ if (isDate(val)) {
395
+ try {
396
+ parsedFilterVal = new Date(val);
397
+ } catch (e) {
398
+ // ignore
399
+ }
400
+ }
401
+ if (typeof parsedFilterVal === "string") {
402
+ try {
403
+ parsedFilterVal = JSON.parse(parsedFilterVal, (key, value) => {
404
+ if (typeof value === "string" && value.startsWith("ref::")) {
405
+ const [path, id] = value.substring(5).split("/");
406
+ return new EntityReference(id, path);
407
+ }
408
+ return value;
409
+ });
410
+ } catch (e) {
411
+ // ignore
412
+ }
413
+ }
414
+
415
+ if (typeof parsedFilterVal === "string" && parsedFilterVal.startsWith("ref::")) {
416
+ const [path, id] = parsedFilterVal.substring(5).split("/");
417
+ return new EntityReference(id, path);
418
+ }
419
+ return parsedFilterVal;
420
+ }