@tsed/react-formio 3.0.0-rc.14 → 3.0.0-rc.15

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.
@@ -1,3 +1,7 @@
1
1
  import { ColumnDef, ColumnDefResolved } from '@tanstack/react-table';
2
2
  import { FormType } from '../../../interfaces';
3
- export declare function mapFormToColumns<Data = any>(form: FormType, columns?: ColumnDefResolved<Data, any>[]): ColumnDef<Data, any>[];
3
+ export declare function mapFormToColumns<Data = any>({ form, columns, prefix }: {
4
+ form: FormType;
5
+ columns?: ColumnDefResolved<Data, any>[];
6
+ prefix?: string;
7
+ }): ColumnDef<Data, any>[];
@@ -1,50 +1,66 @@
1
- import { createColumnHelper as s } from "@tanstack/react-table";
2
- import { c as p } from "../../../chunks/cloneDeep.js";
3
- import { g as u } from "../../../chunks/_commonjsHelpers.js";
4
- import { r as i } from "../../../chunks/get.js";
5
- import { getComponent as d } from "../../../registries/components.js";
6
- var f = i();
7
- const y = /* @__PURE__ */ u(f), C = {
1
+ import { createColumnHelper as f } from "@tanstack/react-table";
2
+ import { c as y } from "../../../chunks/cloneDeep.js";
3
+ import { g as C } from "../../../chunks/_commonjsHelpers.js";
4
+ import { r as g } from "../../../chunks/get.js";
5
+ import { getComponent as b } from "../../../registries/components.js";
6
+ var x = g();
7
+ const h = /* @__PURE__ */ C(x), K = {
8
8
  number: "range",
9
9
  currency: "range",
10
10
  checkbox: "boolean"
11
- }, b = {
11
+ }, k = {
12
12
  date: "date",
13
13
  datetime: "date",
14
14
  number: "number",
15
15
  currency: "currency",
16
16
  checkbox: "boolean"
17
17
  };
18
- function P(a, m = []) {
19
- const c = s(), r = p(m);
20
- return a.components.flatMap((e) => e.type === "tabs" ? e.components?.flatMap((t) => t.components) : [e]).filter((e) => e?.tableView).map((e) => {
21
- const t = e, o = r.findIndex(({ accessorKey: l }) => l === `data.${t.key}`);
22
- let n = r[o];
23
- return n && r.splice(o, 1), c.accessor(`data.${t.key}`, {
18
+ function m(r) {
19
+ if ("id" in r && typeof r.id == "string")
20
+ return r.id;
21
+ if ("accessorKey" in r && typeof r.accessorKey == "string")
22
+ return r.accessorKey;
23
+ }
24
+ function S({
25
+ form: r,
26
+ columns: p = [],
27
+ prefix: l = "data."
28
+ }) {
29
+ const d = f(), n = y(p);
30
+ return [...r.components.flatMap((e) => e.type === "tabs" ? e.components?.flatMap((t) => t.components) : [e]).filter((e) => e?.tableView).map((e) => {
31
+ const t = e, o = `${l}${t.key}`, a = /* @__PURE__ */ new Set([t.key, o]), i = n.findIndex((u) => {
32
+ const c = m(u);
33
+ return c ? a.has(c) : !1;
34
+ });
35
+ let s = n[i];
36
+ return s && n.splice(i, 1), d.accessor(o, {
24
37
  header: (t.label || t.title || t.key)?.replace(/:/, ""),
25
38
  meta: {
26
- type: b[t.type] || t.type,
39
+ type: k[t.type] || t.type,
27
40
  filter: {
28
- ...n?.meta?.filter,
29
- variant: C[t.type] || "text"
41
+ ...s?.meta?.filter,
42
+ variant: K[t.type] || "text"
30
43
  },
31
- ...n?.meta || {}
44
+ ...s?.meta || {}
32
45
  },
33
- ...n || {}
46
+ ...s || {}
34
47
  });
35
- }).concat(r).map((e, t) => {
36
- const o = d([`Cell.${e.id}`, `Cell.${e.meta?.type}`, "Cell"]);
48
+ }), ...n].reduce((e, t) => {
49
+ const o = m(t);
50
+ return o && e.some((a) => m(a) === o) || e.push(t), e;
51
+ }, []).map((e, t) => {
52
+ const o = b([`Cell.${e.id}`, `Cell.${e.meta?.type}`, "Cell"]);
37
53
  return {
38
54
  ...e,
39
55
  meta: {
40
56
  ...e?.meta,
41
- order: y(e, "meta.order", t * 10)
57
+ order: h(e, "meta.order", t * 10)
42
58
  },
43
59
  cell: e.cell || o
44
60
  };
45
61
  }).sort((e, t) => e.meta.order > t.meta.order ? 1 : -1);
46
62
  }
47
63
  export {
48
- P as mapFormToColumns
64
+ S as mapFormToColumns
49
65
  };
50
66
  //# sourceMappingURL=mapFormToColumns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapFormToColumns.js","sources":["../../../../src/molecules/table/utils/mapFormToColumns.tsx"],"sourcesContent":["import \"../interfaces/extends\";\n\nimport { ColumnDef, ColumnDefResolved, createColumnHelper } from \"@tanstack/react-table\";\nimport cloneDeep from \"lodash/cloneDeep\";\nimport get from \"lodash/get\";\n\nimport type { ComponentType, FormType } from \"../../../interfaces\";\nimport { getComponent } from \"../../../registries/components\";\nimport type { DefaultCell } from \"../components/DefaultCell\";\nimport type { FilterVariants } from \"../filters/Filters.js\";\n\nconst MAP_FILTER_TYPES: Record<string, FilterVariants> = {\n number: \"range\",\n currency: \"range\",\n checkbox: \"boolean\"\n} as const;\n\nconst MAP_TYPES = {\n date: \"date\",\n datetime: \"date\",\n number: \"number\",\n currency: \"currency\",\n checkbox: \"boolean\"\n} as const;\n\nexport function mapFormToColumns<Data = any>(form: FormType, columns: ColumnDefResolved<Data, any>[] = []): ColumnDef<Data, any>[] {\n const columnHelper = createColumnHelper<Data>();\n const columnsToKeep = cloneDeep(columns);\n\n const columnsFromComponents = form.components\n .flatMap((component) => {\n if (component.type === \"tabs\") {\n return component.components?.flatMap((subComponent: ComponentType) => subComponent.components);\n }\n\n return [component];\n })\n .filter((component) => component?.tableView)\n .map((c) => {\n const component = c as ComponentType;\n\n const columnIndex = columnsToKeep.findIndex(({ accessorKey }) => {\n return accessorKey === `data.${component.key}`;\n });\n\n let column = columnsToKeep[columnIndex];\n\n if (column) {\n columnsToKeep.splice(columnIndex, 1);\n }\n\n return columnHelper.accessor(`data.${component.key}` as any, {\n header: (component.label || component.title || component.key)?.replace(/:/, \"\"),\n meta: {\n type: MAP_TYPES[component.type as keyof typeof MAP_TYPES] || component.type,\n filter: {\n ...column?.meta?.filter,\n variant: MAP_FILTER_TYPES[component.type!] || \"text\"\n },\n ...(column?.meta || {})\n },\n ...(column || {})\n });\n });\n\n const mergedColumns = columnsFromComponents.concat(columnsToKeep as any[]).map((column, index) => {\n const Cell = getComponent<typeof DefaultCell>([`Cell.${column.id}`, `Cell.${column.meta?.type}`, \"Cell\"]);\n\n return {\n ...column,\n meta: {\n ...column?.meta,\n order: get(column, \"meta.order\", index * 10)\n },\n cell: column.cell || Cell\n };\n });\n\n return mergedColumns.sort((a, b) => (a.meta.order > b.meta.order ? 1 : -1)) as ColumnDef<Data, any>[];\n}\n"],"names":["MAP_FILTER_TYPES","MAP_TYPES","mapFormToColumns","form","columns","columnHelper","createColumnHelper","columnsToKeep","cloneDeep","component","subComponent","c","columnIndex","accessorKey","column","index","Cell","getComponent","get","a","b"],"mappings":";;;;;;gCAWMA,IAAmD;AAAA,EACvD,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AACZ,GAEMC,IAAY;AAAA,EAChB,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AACZ;AAEO,SAASC,EAA6BC,GAAgBC,IAA0C,IAA4B;AACjI,QAAMC,IAAeC,EAAA,GACfC,IAAgBC,EAAUJ,CAAO;AAmDvC,SAjD8BD,EAAK,WAChC,QAAQ,CAACM,MACJA,EAAU,SAAS,SACdA,EAAU,YAAY,QAAQ,CAACC,MAAgCA,EAAa,UAAU,IAGxF,CAACD,CAAS,CAClB,EACA,OAAO,CAACA,MAAcA,GAAW,SAAS,EAC1C,IAAI,CAACE,MAAM;AACV,UAAMF,IAAYE,GAEZC,IAAcL,EAAc,UAAU,CAAC,EAAE,aAAAM,QACtCA,MAAgB,QAAQJ,EAAU,GAAG,EAC7C;AAED,QAAIK,IAASP,EAAcK,CAAW;AAEtC,WAAIE,KACFP,EAAc,OAAOK,GAAa,CAAC,GAG9BP,EAAa,SAAS,QAAQI,EAAU,GAAG,IAAW;AAAA,MAC3D,SAASA,EAAU,SAASA,EAAU,SAASA,EAAU,MAAM,QAAQ,KAAK,EAAE;AAAA,MAC9E,MAAM;AAAA,QACJ,MAAMR,EAAUQ,EAAU,IAA8B,KAAKA,EAAU;AAAA,QACvE,QAAQ;AAAA,UACN,GAAGK,GAAQ,MAAM;AAAA,UACjB,SAASd,EAAiBS,EAAU,IAAK,KAAK;AAAA,QAAA;AAAA,QAEhD,GAAIK,GAAQ,QAAQ,CAAA;AAAA,MAAC;AAAA,MAEvB,GAAIA,KAAU,CAAA;AAAA,IAAC,CAChB;AAAA,EACH,CAAC,EAEyC,OAAOP,CAAsB,EAAE,IAAI,CAACO,GAAQC,MAAU;AAChG,UAAMC,IAAOC,EAAiC,CAAC,QAAQH,EAAO,EAAE,IAAI,QAAQA,EAAO,MAAM,IAAI,IAAI,MAAM,CAAC;AAExG,WAAO;AAAA,MACL,GAAGA;AAAA,MACH,MAAM;AAAA,QACJ,GAAGA,GAAQ;AAAA,QACX,OAAOI,EAAIJ,GAAQ,cAAcC,IAAQ,EAAE;AAAA,MAAA;AAAA,MAE7C,MAAMD,EAAO,QAAQE;AAAA,IAAA;AAAA,EAEzB,CAAC,EAEoB,KAAK,CAACG,GAAGC,MAAOD,EAAE,KAAK,QAAQC,EAAE,KAAK,QAAQ,IAAI,EAAG;AAC5E;"}
1
+ {"version":3,"file":"mapFormToColumns.js","sources":["../../../../src/molecules/table/utils/mapFormToColumns.tsx"],"sourcesContent":["import \"../interfaces/extends\";\n\nimport { ColumnDef, ColumnDefResolved, createColumnHelper } from \"@tanstack/react-table\";\nimport cloneDeep from \"lodash/cloneDeep\";\nimport get from \"lodash/get\";\n\nimport type { ComponentType, FormType } from \"../../../interfaces\";\nimport { getComponent } from \"../../../registries/components\";\nimport type { DefaultCell } from \"../components/DefaultCell\";\nimport type { FilterVariants } from \"../filters/Filters.js\";\n\nconst MAP_FILTER_TYPES: Record<string, FilterVariants> = {\n number: \"range\",\n currency: \"range\",\n checkbox: \"boolean\"\n} as const;\n\nconst MAP_TYPES = {\n date: \"date\",\n datetime: \"date\",\n number: \"number\",\n currency: \"currency\",\n checkbox: \"boolean\"\n} as const;\n\nfunction getColumnIdentity<Data>(column: ColumnDef<Data, any> | ColumnDefResolved<Data, any>) {\n if (\"id\" in column && typeof column.id === \"string\") {\n return column.id;\n }\n\n if (\"accessorKey\" in column && typeof column.accessorKey === \"string\") {\n return column.accessorKey;\n }\n\n return undefined;\n}\n\nexport function mapFormToColumns<Data = any>({\n form,\n columns = [],\n prefix = \"data.\"\n}: {\n form: FormType;\n columns?: ColumnDefResolved<Data, any>[];\n prefix?: string;\n}): ColumnDef<Data, any>[] {\n const columnHelper = createColumnHelper<Data>();\n const columnsToKeep = cloneDeep(columns);\n\n const columnsFromComponents = form.components\n .flatMap((component) => {\n if (component.type === \"tabs\") {\n return component.components?.flatMap((subComponent: ComponentType) => subComponent.components);\n }\n\n return [component];\n })\n .filter((component) => component?.tableView)\n .map((c) => {\n const component = c as ComponentType;\n const componentColumnKey = `${prefix}${component.key}`;\n const matchingKeys = new Set([component.key, componentColumnKey]);\n\n const columnIndex = columnsToKeep.findIndex((column) => {\n const identity = getColumnIdentity(column);\n\n return identity ? matchingKeys.has(identity) : false;\n });\n\n let column = columnsToKeep[columnIndex];\n\n if (column) {\n columnsToKeep.splice(columnIndex, 1);\n }\n\n return columnHelper.accessor(componentColumnKey as any, {\n header: (component.label || component.title || component.key)?.replace(/:/, \"\"),\n meta: {\n type: (MAP_TYPES[component.type as keyof typeof MAP_TYPES] || component.type) as any,\n filter: {\n ...column?.meta?.filter,\n variant: MAP_FILTER_TYPES[component.type as keyof typeof MAP_FILTER_TYPES] || \"text\"\n },\n ...(column?.meta || {})\n },\n ...(column || {})\n });\n });\n\n const dedupedColumns = [...columnsFromComponents, ...(columnsToKeep as any[])].reduce<ColumnDef<Data, any>[]>((acc, column) => {\n const identity = getColumnIdentity(column);\n\n if (identity && acc.some((existingColumn) => getColumnIdentity(existingColumn) === identity)) {\n return acc;\n }\n\n acc.push(column);\n return acc;\n }, []);\n\n const mergedColumns = dedupedColumns.map((column, index) => {\n const Cell = getComponent<typeof DefaultCell>([`Cell.${column.id}`, `Cell.${column.meta?.type}`, \"Cell\"]);\n\n return {\n ...column,\n meta: {\n ...column?.meta,\n order: get(column, \"meta.order\", index * 10)\n },\n cell: column.cell || Cell\n };\n });\n\n return mergedColumns.sort((a, b) => (a.meta.order > b.meta.order ? 1 : -1)) as ColumnDef<Data, any>[];\n}\n"],"names":["MAP_FILTER_TYPES","MAP_TYPES","getColumnIdentity","column","mapFormToColumns","form","columns","prefix","columnHelper","createColumnHelper","columnsToKeep","cloneDeep","component","subComponent","c","componentColumnKey","matchingKeys","columnIndex","identity","acc","existingColumn","index","Cell","getComponent","get","a","b"],"mappings":";;;;;;gCAWMA,IAAmD;AAAA,EACvD,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AACZ,GAEMC,IAAY;AAAA,EAChB,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,SAASC,EAAwBC,GAA6D;AAC5F,MAAI,QAAQA,KAAU,OAAOA,EAAO,MAAO;AACzC,WAAOA,EAAO;AAGhB,MAAI,iBAAiBA,KAAU,OAAOA,EAAO,eAAgB;AAC3D,WAAOA,EAAO;AAIlB;AAEO,SAASC,EAA6B;AAAA,EAC3C,MAAAC;AAAA,EACA,SAAAC,IAAU,CAAA;AAAA,EACV,QAAAC,IAAS;AACX,GAI2B;AACzB,QAAMC,IAAeC,EAAA,GACfC,IAAgBC,EAAUL,CAAO;AAkEvC,SAxBuB,CAAC,GAxCMD,EAAK,WAChC,QAAQ,CAACO,MACJA,EAAU,SAAS,SACdA,EAAU,YAAY,QAAQ,CAACC,MAAgCA,EAAa,UAAU,IAGxF,CAACD,CAAS,CAClB,EACA,OAAO,CAACA,MAAcA,GAAW,SAAS,EAC1C,IAAI,CAACE,MAAM;AACV,UAAMF,IAAYE,GACZC,IAAqB,GAAGR,CAAM,GAAGK,EAAU,GAAG,IAC9CI,IAAe,oBAAI,IAAI,CAACJ,EAAU,KAAKG,CAAkB,CAAC,GAE1DE,IAAcP,EAAc,UAAU,CAACP,MAAW;AACtD,YAAMe,IAAWhB,EAAkBC,CAAM;AAEzC,aAAOe,IAAWF,EAAa,IAAIE,CAAQ,IAAI;AAAA,IACjD,CAAC;AAED,QAAIf,IAASO,EAAcO,CAAW;AAEtC,WAAId,KACFO,EAAc,OAAOO,GAAa,CAAC,GAG9BT,EAAa,SAASO,GAA2B;AAAA,MACtD,SAASH,EAAU,SAASA,EAAU,SAASA,EAAU,MAAM,QAAQ,KAAK,EAAE;AAAA,MAC9E,MAAM;AAAA,QACJ,MAAOX,EAAUW,EAAU,IAA8B,KAAKA,EAAU;AAAA,QACxE,QAAQ;AAAA,UACN,GAAGT,GAAQ,MAAM;AAAA,UACjB,SAASH,EAAiBY,EAAU,IAAqC,KAAK;AAAA,QAAA;AAAA,QAEhF,GAAIT,GAAQ,QAAQ,CAAA;AAAA,MAAC;AAAA,MAEvB,GAAIA,KAAU,CAAA;AAAA,IAAC,CAChB;AAAA,EACH,CAAC,GAE+C,GAAIO,CAAuB,EAAE,OAA+B,CAACS,GAAKhB,MAAW;AAC7H,UAAMe,IAAWhB,EAAkBC,CAAM;AAEzC,WAAIe,KAAYC,EAAI,KAAK,CAACC,MAAmBlB,EAAkBkB,CAAc,MAAMF,CAAQ,KAI3FC,EAAI,KAAKhB,CAAM,GACRgB;AAAA,EACT,GAAG,CAAA,CAAE,EAEgC,IAAI,CAAChB,GAAQkB,MAAU;AAC1D,UAAMC,IAAOC,EAAiC,CAAC,QAAQpB,EAAO,EAAE,IAAI,QAAQA,EAAO,MAAM,IAAI,IAAI,MAAM,CAAC;AAExG,WAAO;AAAA,MACL,GAAGA;AAAA,MACH,MAAM;AAAA,QACJ,GAAGA,GAAQ;AAAA,QACX,OAAOqB,EAAIrB,GAAQ,cAAckB,IAAQ,EAAE;AAAA,MAAA;AAAA,MAE7C,MAAMlB,EAAO,QAAQmB;AAAA,IAAA;AAAA,EAEzB,CAAC,EAEoB,KAAK,CAACG,GAAGC,MAAOD,EAAE,KAAK,QAAQC,EAAE,KAAK,QAAQ,IAAI,EAAG;AAC5E;"}
@@ -2,7 +2,7 @@ import { jsx as n } from "react/jsx-runtime";
2
2
  import { Table as s } from "../../../molecules/table/Table.js";
3
3
  import { mapFormToColumns as t } from "../../../molecules/table/utils/mapFormToColumns.js";
4
4
  function e({ form: o, ...m }) {
5
- const r = o && t(o);
5
+ const r = o && t({ form: o });
6
6
  return /* @__PURE__ */ n(s, { ...m, columns: r });
7
7
  }
8
8
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"SubmissionsTable.js","sources":["../../../../src/organisms/table/submissions/SubmissionsTable.tsx"],"sourcesContent":["import type { FormType, SubmissionType } from \"../../../interfaces\";\nimport type { JSONRecord } from \"../../../interfaces/JSONRecord\";\nimport { Table, type TableProps } from \"../../../molecules/table/Table\";\nimport { mapFormToColumns } from \"../../../molecules/table/utils/mapFormToColumns.js\";\n\nexport type SubmissionsTableProps<Data extends object = JSONRecord> = Omit<TableProps<SubmissionType<Data>>, \"columns\"> & {\n form?: FormType;\n};\n\nexport function SubmissionsTable<Data extends object = JSONRecord>({ form, ...props }: SubmissionsTableProps<Data>) {\n const columns: any[] | undefined = form && mapFormToColumns(form);\n\n return <Table {...(props as any)} columns={columns!} />;\n}\n"],"names":["SubmissionsTable","form","props","columns","mapFormToColumns","jsx","Table"],"mappings":";;;AASO,SAASA,EAAmD,EAAE,MAAAC,GAAM,GAAGC,KAAsC;AAClH,QAAMC,IAA6BF,KAAQG,EAAiBH,CAAI;AAEhE,SAAO,gBAAAI,EAACC,GAAA,EAAO,GAAIJ,GAAe,SAAAC,EAAA,CAAmB;AACvD;"}
1
+ {"version":3,"file":"SubmissionsTable.js","sources":["../../../../src/organisms/table/submissions/SubmissionsTable.tsx"],"sourcesContent":["import type { FormType, SubmissionType } from \"../../../interfaces\";\nimport type { JSONRecord } from \"../../../interfaces/JSONRecord\";\nimport { Table, type TableProps } from \"../../../molecules/table/Table\";\nimport { mapFormToColumns } from \"../../../molecules/table/utils/mapFormToColumns.js\";\n\nexport type SubmissionsTableProps<Data extends object = JSONRecord> = Omit<TableProps<SubmissionType<Data>>, \"columns\"> & {\n form?: FormType;\n};\n\nexport function SubmissionsTable<Data extends object = JSONRecord>({ form, ...props }: SubmissionsTableProps<Data>) {\n const columns: any[] | undefined = form && mapFormToColumns({ form: form });\n\n return <Table {...(props as any)} columns={columns!} />;\n}\n"],"names":["SubmissionsTable","form","props","columns","mapFormToColumns","jsx","Table"],"mappings":";;;AASO,SAASA,EAAmD,EAAE,MAAAC,GAAM,GAAGC,KAAsC;AAClH,QAAMC,IAA6BF,KAAQG,EAAiB,EAAE,MAAAH,GAAY;AAE1E,SAAO,gBAAAI,EAACC,GAAA,EAAO,GAAIJ,GAAe,SAAAC,EAAA,CAAmB;AACvD;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsed/react-formio",
3
- "version": "3.0.0-rc.14",
3
+ "version": "3.0.0-rc.15",
4
4
  "description": "Provide a react formio wrapper. Written in TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@tanstack/react-table": ">=8.20.6",
47
- "@tsed/tailwind-formio": "3.0.0-rc.14",
48
- "@tsed/typescript": "3.0.0-rc.14",
47
+ "@tsed/tailwind-formio": "3.0.0-rc.15",
48
+ "@tsed/typescript": "3.0.0-rc.15",
49
49
  "microbundle": "0.13.0",
50
50
  "vite": "7.1.5",
51
51
  "vitest": "3.2.4"
@@ -74,7 +74,7 @@ type Story = StoryObj<typeof Table<ProductSubmission>>;
74
74
  export const Usage: Story = {
75
75
  args: {
76
76
  data: formSubmissions as unknown as ProductSubmission[],
77
- columns: mapFormToColumns(FormType as any),
77
+ columns: mapFormToColumns({ form: FormType as any }),
78
78
  operations: [
79
79
  {
80
80
  title: "Edit",
@@ -183,17 +183,20 @@ export const Usage: Story = {
183
183
  export const WithFilters: Story = {
184
184
  args: {
185
185
  data: formSubmissions as unknown as ProductSubmission[],
186
- columns: mapFormToColumns(FormType as any, [
187
- {
188
- accessorKey: "data.id",
189
- meta: {
190
- filter: {
191
- variant: "select",
192
- layout: "react"
186
+ columns: mapFormToColumns({
187
+ form: FormType as any,
188
+ columns: [
189
+ {
190
+ accessorKey: "data.id",
191
+ meta: {
192
+ filter: {
193
+ variant: "select",
194
+ layout: "react"
195
+ }
193
196
  }
194
197
  }
195
- }
196
- ]),
198
+ ]
199
+ }),
197
200
  operations: [
198
201
  {
199
202
  title: "Edit",
@@ -220,7 +223,7 @@ export const WithFilters: Story = {
220
223
  export const WithPaginationOptions: Story = {
221
224
  args: {
222
225
  data: formSubmissions as unknown as ProductSubmission[],
223
- columns: mapFormToColumns(FormType as any),
226
+ columns: mapFormToColumns({ form: FormType as any }),
224
227
  operations: [
225
228
  {
226
229
  title: "Edit",
@@ -246,67 +249,70 @@ export const WithPaginationOptions: Story = {
246
249
  export const WithCustomCell: Story = {
247
250
  args: {
248
251
  data: formSubmissions as unknown as ProductSubmission[],
249
- columns: mapFormToColumns<ProductSubmission>(FormType as any, [
250
- {
251
- accessorKey: "data.id",
252
- meta: {
253
- filter: {
254
- variant: "select",
255
- layout: "react"
252
+ columns: mapFormToColumns<ProductSubmission>({
253
+ form: FormType as any,
254
+ columns: [
255
+ {
256
+ accessorKey: "data.id",
257
+ meta: {
258
+ filter: {
259
+ variant: "select",
260
+ layout: "react"
261
+ },
262
+ cellProps: {
263
+ colSpan: 2
264
+ }
256
265
  },
257
- cellProps: {
258
- colSpan: 2
266
+ cell: (context) => {
267
+ return (
268
+ <div className='flex space-x-4 align-items-center'>
269
+ <div className='max-w-[80px]'>
270
+ <img className='max-w-[80px] rounded-md' src={context.row.original.data.image} alt={context.row.original.data.label} />
271
+ </div>
272
+ <div>
273
+ <div className='text-lg text-primary'>{context.row.original.data.label}</div>
274
+ <div className='text-xs'>{context.getValue()}</div>
275
+ </div>
276
+ </div>
277
+ );
259
278
  }
260
279
  },
261
- cell: (context) => {
262
- return (
263
- <div className='flex space-x-4 align-items-center'>
264
- <div className='max-w-[80px]'>
265
- <img className='max-w-[80px] rounded-md' src={context.row.original.data.image} alt={context.row.original.data.label} />
266
- </div>
267
- <div>
268
- <div className='text-lg text-primary'>{context.row.original.data.label}</div>
269
- <div className='text-xs'>{context.getValue()}</div>
270
- </div>
271
- </div>
272
- );
273
- }
274
- },
275
- {
276
- accessorKey: "data.label",
277
- meta: {
278
- hidden: true
279
- }
280
- },
281
- {
282
- accessorKey: "data.description",
283
- meta: {
284
- filter: {
285
- variant: "text",
286
- disableDatalist: true
280
+ {
281
+ accessorKey: "data.label",
282
+ meta: {
283
+ hidden: true
287
284
  }
288
- }
289
- },
290
- {
291
- accessorKey: "data.price",
292
- cell: (context) => {
293
- const value = context.getValue();
294
-
295
- if (value === undefined) {
296
- return "-";
285
+ },
286
+ {
287
+ accessorKey: "data.description",
288
+ meta: {
289
+ filter: {
290
+ variant: "text",
291
+ disableDatalist: true
292
+ }
293
+ }
294
+ },
295
+ {
296
+ accessorKey: "data.price",
297
+ cell: (context) => {
298
+ const value = context.getValue();
299
+
300
+ if (value === undefined) {
301
+ return "-";
302
+ }
303
+
304
+ return (
305
+ <div className='text-right'>
306
+ {Intl.NumberFormat("fr-FR", {
307
+ style: "currency",
308
+ currency: context.row.original.data.currency
309
+ }).format(context.getValue())}
310
+ </div>
311
+ );
297
312
  }
298
-
299
- return (
300
- <div className='text-right'>
301
- {Intl.NumberFormat("fr-FR", {
302
- style: "currency",
303
- currency: context.row.original.data.currency
304
- }).format(context.getValue())}
305
- </div>
306
- );
307
313
  }
308
- }
309
- ]),
314
+ ]
315
+ }),
310
316
  operations: [
311
317
  {
312
318
  title: "Edit",
@@ -1,9 +1,10 @@
1
+ import { DefaultCellBoolean } from "../components/DefaultBooleanCell";
1
2
  import { DefaultCell } from "../components/DefaultCell";
2
3
  import { DefaultDateCell } from "../components/DefaultDateCell";
3
4
  import { mapFormToColumns } from "./mapFormToColumns";
4
5
 
5
6
  describe("mapFormToColumns", () => {
6
- it("should use DefaultCell for mapped table columns", () => {
7
+ it("should use DefaultCellBoolean for mapped checkbox table columns", () => {
7
8
  const form = {
8
9
  components: [
9
10
  {
@@ -15,12 +16,13 @@ describe("mapFormToColumns", () => {
15
16
  ]
16
17
  } as any;
17
18
 
18
- const [column] = mapFormToColumns(form) as any[];
19
+ const [column] = mapFormToColumns({ form: form }) as any[];
19
20
 
20
21
  expect(column.accessorKey).toEqual("data.enabled");
21
22
  expect(column.header).toEqual("Enabled");
23
+ expect(column.meta.type).toEqual("boolean");
22
24
  expect(column.meta.filter.variant).toEqual("boolean");
23
- expect(column.cell).toBe(DefaultCell);
25
+ expect(column.cell).toBe(DefaultCellBoolean);
24
26
  });
25
27
 
26
28
  it("should fallback to DefaultCell for kept columns without a cell renderer", () => {
@@ -28,12 +30,45 @@ describe("mapFormToColumns", () => {
28
30
  components: []
29
31
  } as any;
30
32
 
31
- const [column] = mapFormToColumns(form, [{ accessorKey: "data.other", header: "Other" } as any]) as any[];
33
+ const [column] = mapFormToColumns({ form: form, columns: [{ accessorKey: "data.other", header: "Other" } as any] }) as any[];
32
34
 
33
35
  expect(column.accessorKey).toEqual("data.other");
34
36
  expect(column.cell).toBe(DefaultCell);
35
37
  });
36
38
 
39
+ it("should merge a kept column matched by component key", () => {
40
+ const form = {
41
+ components: [
42
+ {
43
+ type: "textfield",
44
+ key: "name",
45
+ label: "Name:",
46
+ tableView: true
47
+ }
48
+ ]
49
+ } as any;
50
+
51
+ const [column] = mapFormToColumns({
52
+ form: form,
53
+ columns: [
54
+ {
55
+ id: "name",
56
+ header: "Custom name",
57
+ meta: {
58
+ order: 5,
59
+ filter: {
60
+ variant: "text"
61
+ }
62
+ }
63
+ } as any
64
+ ]
65
+ }) as any[];
66
+
67
+ expect(column.accessorKey).toEqual("data.name");
68
+ expect(column.header).toEqual("Custom name");
69
+ expect(column.meta.order).toEqual(5);
70
+ });
71
+
37
72
  it("should use DefaultDateCell for mapped datetime columns", () => {
38
73
  const form = {
39
74
  components: [
@@ -46,10 +81,36 @@ describe("mapFormToColumns", () => {
46
81
  ]
47
82
  } as any;
48
83
 
49
- const [column] = mapFormToColumns(form) as any[];
84
+ const [column] = mapFormToColumns({ form: form }) as any[];
50
85
 
51
86
  expect(column.accessorKey).toEqual("data.createdAt");
52
87
  expect(column.meta.type).toEqual("date");
53
88
  expect(column.cell).toBe(DefaultDateCell);
54
89
  });
90
+
91
+ it("should dedupe columns when a kept column matches a mapped accessor key", () => {
92
+ const form = {
93
+ components: [
94
+ {
95
+ type: "textfield",
96
+ key: "email",
97
+ label: "Email:",
98
+ tableView: true
99
+ }
100
+ ]
101
+ } as any;
102
+
103
+ const columns = mapFormToColumns({
104
+ form: form,
105
+ columns: [
106
+ {
107
+ accessorKey: "data.email",
108
+ header: "Email"
109
+ } as any
110
+ ]
111
+ }) as any[];
112
+
113
+ expect(columns).toHaveLength(1);
114
+ expect(columns[0].accessorKey).toEqual("data.email");
115
+ });
55
116
  });
@@ -23,7 +23,27 @@ const MAP_TYPES = {
23
23
  checkbox: "boolean"
24
24
  } as const;
25
25
 
26
- export function mapFormToColumns<Data = any>(form: FormType, columns: ColumnDefResolved<Data, any>[] = []): ColumnDef<Data, any>[] {
26
+ function getColumnIdentity<Data>(column: ColumnDef<Data, any> | ColumnDefResolved<Data, any>) {
27
+ if ("id" in column && typeof column.id === "string") {
28
+ return column.id;
29
+ }
30
+
31
+ if ("accessorKey" in column && typeof column.accessorKey === "string") {
32
+ return column.accessorKey;
33
+ }
34
+
35
+ return undefined;
36
+ }
37
+
38
+ export function mapFormToColumns<Data = any>({
39
+ form,
40
+ columns = [],
41
+ prefix = "data."
42
+ }: {
43
+ form: FormType;
44
+ columns?: ColumnDefResolved<Data, any>[];
45
+ prefix?: string;
46
+ }): ColumnDef<Data, any>[] {
27
47
  const columnHelper = createColumnHelper<Data>();
28
48
  const columnsToKeep = cloneDeep(columns);
29
49
 
@@ -38,9 +58,13 @@ export function mapFormToColumns<Data = any>(form: FormType, columns: ColumnDefR
38
58
  .filter((component) => component?.tableView)
39
59
  .map((c) => {
40
60
  const component = c as ComponentType;
61
+ const componentColumnKey = `${prefix}${component.key}`;
62
+ const matchingKeys = new Set([component.key, componentColumnKey]);
41
63
 
42
- const columnIndex = columnsToKeep.findIndex(({ accessorKey }) => {
43
- return accessorKey === `data.${component.key}`;
64
+ const columnIndex = columnsToKeep.findIndex((column) => {
65
+ const identity = getColumnIdentity(column);
66
+
67
+ return identity ? matchingKeys.has(identity) : false;
44
68
  });
45
69
 
46
70
  let column = columnsToKeep[columnIndex];
@@ -49,13 +73,13 @@ export function mapFormToColumns<Data = any>(form: FormType, columns: ColumnDefR
49
73
  columnsToKeep.splice(columnIndex, 1);
50
74
  }
51
75
 
52
- return columnHelper.accessor(`data.${component.key}` as any, {
76
+ return columnHelper.accessor(componentColumnKey as any, {
53
77
  header: (component.label || component.title || component.key)?.replace(/:/, ""),
54
78
  meta: {
55
- type: MAP_TYPES[component.type as keyof typeof MAP_TYPES] || component.type,
79
+ type: (MAP_TYPES[component.type as keyof typeof MAP_TYPES] || component.type) as any,
56
80
  filter: {
57
81
  ...column?.meta?.filter,
58
- variant: MAP_FILTER_TYPES[component.type!] || "text"
82
+ variant: MAP_FILTER_TYPES[component.type as keyof typeof MAP_FILTER_TYPES] || "text"
59
83
  },
60
84
  ...(column?.meta || {})
61
85
  },
@@ -63,7 +87,18 @@ export function mapFormToColumns<Data = any>(form: FormType, columns: ColumnDefR
63
87
  });
64
88
  });
65
89
 
66
- const mergedColumns = columnsFromComponents.concat(columnsToKeep as any[]).map((column, index) => {
90
+ const dedupedColumns = [...columnsFromComponents, ...(columnsToKeep as any[])].reduce<ColumnDef<Data, any>[]>((acc, column) => {
91
+ const identity = getColumnIdentity(column);
92
+
93
+ if (identity && acc.some((existingColumn) => getColumnIdentity(existingColumn) === identity)) {
94
+ return acc;
95
+ }
96
+
97
+ acc.push(column);
98
+ return acc;
99
+ }, []);
100
+
101
+ const mergedColumns = dedupedColumns.map((column, index) => {
67
102
  const Cell = getComponent<typeof DefaultCell>([`Cell.${column.id}`, `Cell.${column.meta?.type}`, "Cell"]);
68
103
 
69
104
  return {
@@ -8,7 +8,7 @@ export type SubmissionsTableProps<Data extends object = JSONRecord> = Omit<Table
8
8
  };
9
9
 
10
10
  export function SubmissionsTable<Data extends object = JSONRecord>({ form, ...props }: SubmissionsTableProps<Data>) {
11
- const columns: any[] | undefined = form && mapFormToColumns(form);
11
+ const columns: any[] | undefined = form && mapFormToColumns({ form: form });
12
12
 
13
13
  return <Table {...(props as any)} columns={columns!} />;
14
14
  }