@asteby/metacore-runtime-react 18.23.0 → 18.24.0
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/CHANGELOG.md +18 -0
- package/dist/collection-cell.d.ts +7 -1
- package/dist/collection-cell.d.ts.map +1 -1
- package/dist/collection-cell.js +87 -25
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +4 -2
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +1 -1
- package/package.json +1 -1
- package/src/collection-cell.tsx +137 -18
- package/src/dialogs/dynamic-record.tsx +4 -0
- package/src/dynamic-columns.tsx +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 18.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 39c43ea: jsonb line-item `ref` cells render as relation chips (icon/photo + name).
|
|
8
|
+
|
|
9
|
+
In `CollectionCell` (table popover, inline detail view, and the read-only edit
|
|
10
|
+
field), a resolved ref sub-field — e.g. `product_id` inside a transfer's `items`
|
|
11
|
+
— now renders with the same "pro" relation look the FK table columns use: a
|
|
12
|
+
subtle deterministic tint, the resolved record's thumbnail (product photo / logo
|
|
13
|
+
/ avatar, resolved via the threaded `getImageUrl`) or a generic entity icon
|
|
14
|
+
fallback, and the resolved name — instead of a truncated uuid.
|
|
15
|
+
|
|
16
|
+
`CollectionCell` gains an optional `getImageUrl` prop, threaded from the columns
|
|
17
|
+
factory and from the record dialog's `ImageUrlContext`. Backend-agnostic: it
|
|
18
|
+
drives off the backend-injected `{ value, label, image }` sibling; an unresolved
|
|
19
|
+
ref still falls back to the scalar value.
|
|
20
|
+
|
|
3
21
|
## 18.23.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -57,6 +57,12 @@ export interface CollectionCellProps {
|
|
|
57
57
|
* behaviour is unchanged.
|
|
58
58
|
*/
|
|
59
59
|
itemFields?: ItemField[];
|
|
60
|
+
/**
|
|
61
|
+
* Resolves a stored image path to a displayable URL (same resolver the table
|
|
62
|
+
* columns use). Threaded to the ref-chip thumbnails so resolved line-item
|
|
63
|
+
* relations show a product photo / logo / avatar like the FK columns do.
|
|
64
|
+
*/
|
|
65
|
+
getImageUrl?: (path: string) => string;
|
|
60
66
|
/**
|
|
61
67
|
* Presentation mode.
|
|
62
68
|
* - `'badge'` (default): the compact count/preview badge that opens a
|
|
@@ -77,5 +83,5 @@ export interface CollectionCellProps {
|
|
|
77
83
|
* itemFields schema (localized headers + resolved ref labels) and the
|
|
78
84
|
* locale-aware generic fallback.
|
|
79
85
|
*/
|
|
80
|
-
export declare function CollectionCell({ value, maxInline, locale, t, itemFields, variant, }: CollectionCellProps): React.JSX.Element;
|
|
86
|
+
export declare function CollectionCell({ value, maxInline, locale, t, itemFields, getImageUrl, variant, }: CollectionCellProps): React.JSX.Element;
|
|
81
87
|
//# sourceMappingURL=collection-cell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection-cell.d.ts","sourceRoot":"","sources":["../src/collection-cell.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"collection-cell.d.ts","sourceRoot":"","sources":["../src/collection-cell.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAkD9B,sFAAsF;AACtF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,MAAM,CAAA;AAE9D;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACtB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAA;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AA6KD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAWR;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAcR;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYnD;AAuMD,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,OAAO,CAAA;IACd,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,CAAC,CAAC,EAAE,SAAS,CAAA;IACb;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,SAAa,EACb,MAAM,EACN,CAAC,EACD,UAAU,EACV,WAAW,EACX,OAAiB,GACpB,EAAE,mBAAmB,qBA2HrB"}
|
package/dist/collection-cell.js
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Generic, brand-neutral table-cell renderer for jsonb / array / object column
|
|
3
|
+
// values. Kernel-derived dynamic tables surface raw jsonb columns (line items,
|
|
4
|
+
// nested config blobs, scalar arrays) with no per-column metadata; without this
|
|
5
|
+
// they rendered as raw `JSON.stringify(value)` which is unreadable. This renders
|
|
6
|
+
// a compact trigger (count badge / inline pairs) plus a Popover with a clean
|
|
7
|
+
// mini-table — no per-addon config required, safe on any shape.
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import { Box, List } from 'lucide-react';
|
|
10
|
+
import { Avatar, AvatarFallback, AvatarImage, Badge, Popover, PopoverContent, PopoverTrigger, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, cn, } from '@asteby/metacore-ui';
|
|
11
|
+
import { getInitials, relationChipStyles } from '@asteby/metacore-ui/lib';
|
|
4
12
|
import { humanizeToken } from './dynamic-columns-helpers';
|
|
13
|
+
/**
|
|
14
|
+
* Tracks the host's dark-mode class on <html> so relation chips pick a tint
|
|
15
|
+
* tuned for the active theme. Replicated from dynamic-columns (kept local to
|
|
16
|
+
* avoid a cross-module import); mirror changes if that one evolves.
|
|
17
|
+
*/
|
|
18
|
+
function useIsDarkTheme() {
|
|
19
|
+
const [isDark, setIsDark] = React.useState(() => typeof document !== 'undefined' &&
|
|
20
|
+
document.documentElement.classList.contains('dark'));
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (typeof document === 'undefined')
|
|
23
|
+
return;
|
|
24
|
+
const sync = () => setIsDark(document.documentElement.classList.contains('dark'));
|
|
25
|
+
sync();
|
|
26
|
+
const observer = new MutationObserver(sync);
|
|
27
|
+
observer.observe(document.documentElement, {
|
|
28
|
+
attributes: true,
|
|
29
|
+
attributeFilter: ['class'],
|
|
30
|
+
});
|
|
31
|
+
return () => observer.disconnect();
|
|
32
|
+
}, []);
|
|
33
|
+
return isDark;
|
|
34
|
+
}
|
|
5
35
|
const UUID_LIKE_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
36
|
/**
|
|
7
37
|
* Resolves the backend-injected resolved sibling key for a ref item-field,
|
|
@@ -12,27 +42,59 @@ function siblingKeyFor(key) {
|
|
|
12
42
|
return key.endsWith('_id') ? key.slice(0, -3) : `${key}_label`;
|
|
13
43
|
}
|
|
14
44
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `formatScalar` (truncated uuid). Non-ref fields render `formatScalar(value)`.
|
|
45
|
+
* Reads the backend-injected resolved sibling for a `ref` item-field (the FK
|
|
46
|
+
* key without `_id`, else `<key>_label`). Returns the normalized `{label,
|
|
47
|
+
* image}` when present, a `{label}` for a bare-string sibling, or null when the
|
|
48
|
+
* ref is unresolved (so the caller falls back to the raw value).
|
|
20
49
|
*/
|
|
21
|
-
function
|
|
22
|
-
if (field.ref)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
50
|
+
function resolvedRefFor(field, row) {
|
|
51
|
+
if (!field.ref)
|
|
52
|
+
return null;
|
|
53
|
+
const sibling = row[siblingKeyFor(field.key)];
|
|
54
|
+
if (sibling && typeof sibling === 'object' && !Array.isArray(sibling)) {
|
|
55
|
+
const obj = sibling;
|
|
56
|
+
const label = obj.label;
|
|
57
|
+
if (label !== undefined && label !== null && label !== '') {
|
|
58
|
+
return {
|
|
59
|
+
label: String(label),
|
|
60
|
+
value: obj.value,
|
|
61
|
+
image: typeof obj.image === 'string' && obj.image !== ''
|
|
62
|
+
? obj.image
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
32
65
|
}
|
|
33
66
|
}
|
|
67
|
+
else if (typeof sibling === 'string' && sibling !== '') {
|
|
68
|
+
return { label: sibling };
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Plain-text value for one declared item-field — used for the no-JS popover
|
|
74
|
+
* `title` tooltip (which can't render JSX). Ref fields show the resolved label;
|
|
75
|
+
* everything else uses `formatScalar` (truncated uuid for unresolved ids).
|
|
76
|
+
*/
|
|
77
|
+
function itemFieldText(field, row) {
|
|
78
|
+
const ref = resolvedRefFor(field, row);
|
|
79
|
+
if (ref?.label)
|
|
80
|
+
return ref.label;
|
|
34
81
|
return formatScalar(row[field.key]);
|
|
35
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Visual cell for one declared item-field. A resolved `ref` renders as a
|
|
85
|
+
* relation chip (subtle deterministic tint + product thumbnail or a generic
|
|
86
|
+
* entity icon + name) — the same "pro" look the table FK columns use — so jsonb
|
|
87
|
+
* line items read like first-class relations instead of raw uuids. Non-ref (or
|
|
88
|
+
* unresolved) fields render the plain scalar.
|
|
89
|
+
*/
|
|
90
|
+
function ItemFieldCell({ field, row, getImageUrl, }) {
|
|
91
|
+
const isDark = useIsDarkTheme();
|
|
92
|
+
const ref = resolvedRefFor(field, row);
|
|
93
|
+
if (ref?.label) {
|
|
94
|
+
return (_jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium", style: relationChipStyles(ref.label, { isDark }), title: ref.label, children: [ref.image ? (_jsxs(Avatar, { className: "shrink-0 rounded-sm ring-1 ring-border/40", style: { width: 18, height: 18 }, children: [_jsx(AvatarImage, { src: getImageUrl ? getImageUrl(ref.image) : ref.image, alt: ref.label, className: "object-cover" }), _jsx(AvatarFallback, { className: "rounded-sm bg-primary/10 text-[8px] font-bold text-primary", children: getInitials(ref.label) })] })) : (_jsx(Box, { className: "h-3 w-3 shrink-0 opacity-70" })), _jsx("span", { className: "truncate", children: ref.label })] }));
|
|
95
|
+
}
|
|
96
|
+
return _jsx(_Fragment, { children: formatScalar(row[field.key]) });
|
|
97
|
+
}
|
|
36
98
|
/** Normalize an org/UI language tag to a base language code (`es-MX` → `es`). */
|
|
37
99
|
function baseLang(locale) {
|
|
38
100
|
return (locale || 'en').toLowerCase().split('-')[0];
|
|
@@ -194,14 +256,14 @@ function unionKeys(rows) {
|
|
|
194
256
|
return seen;
|
|
195
257
|
}
|
|
196
258
|
const PANEL_CLASS = 'w-auto max-w-[480px] max-h-[320px] overflow-auto p-0';
|
|
197
|
-
function MiniTable({ rows, locale, t, itemFields, }) {
|
|
259
|
+
function MiniTable({ rows, locale, t, itemFields, getImageUrl, }) {
|
|
198
260
|
// Schema-driven path: a declared `item_fields` schema fixes the column
|
|
199
261
|
// order + headers (already localized by the backend, used VERBATIM) and
|
|
200
262
|
// resolves ref columns to the injected sibling label instead of the raw
|
|
201
263
|
// uuid. Sibling/raw keys not covered by the schema are dropped from the
|
|
202
264
|
// table (the schema is the source of truth for what to surface).
|
|
203
265
|
if (itemFields && itemFields.length > 0) {
|
|
204
|
-
return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableHead, { className: "text-xs whitespace-nowrap", children: field.label }, field.key))) }) }), _jsx(TableBody, { children: rows.map((row, i) => (_jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableCell, { className: "text-xs whitespace-nowrap", children:
|
|
266
|
+
return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableHead, { className: "text-xs whitespace-nowrap", children: field.label }, field.key))) }) }), _jsx(TableBody, { children: rows.map((row, i) => (_jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableCell, { className: "text-xs whitespace-nowrap", children: _jsx(ItemFieldCell, { field: field, row: row, getImageUrl: getImageUrl }) }, field.key))) }, i))) })] }));
|
|
205
267
|
}
|
|
206
268
|
const keys = unionKeys(rows);
|
|
207
269
|
if (keys.length === 0) {
|
|
@@ -229,7 +291,7 @@ function PopoverShell({ label, title, children, icon = true, }) {
|
|
|
229
291
|
* itemFields schema (localized headers + resolved ref labels) and the
|
|
230
292
|
* locale-aware generic fallback.
|
|
231
293
|
*/
|
|
232
|
-
export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, variant = 'badge', }) {
|
|
294
|
+
export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, getImageUrl, variant = 'badge', }) {
|
|
233
295
|
const parsed = parseValue(value);
|
|
234
296
|
const inline = variant === 'inline';
|
|
235
297
|
// Empty-ish → muted dash.
|
|
@@ -253,7 +315,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
253
315
|
// Inline mode (detail view): render the mini-table directly, no
|
|
254
316
|
// badge/popover. The same schema-driven path applies.
|
|
255
317
|
if (inline) {
|
|
256
|
-
return (_jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields }));
|
|
318
|
+
return (_jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields, getImageUrl: getImageUrl }));
|
|
257
319
|
}
|
|
258
320
|
const count = rows.length;
|
|
259
321
|
const label = countLabel(count, locale, t);
|
|
@@ -264,7 +326,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
264
326
|
const title = hasSchema
|
|
265
327
|
? rows
|
|
266
328
|
.map((row) => itemFields
|
|
267
|
-
.map((field) => `${field.label}: ${
|
|
329
|
+
.map((field) => `${field.label}: ${itemFieldText(field, row)}`)
|
|
268
330
|
.join(', '))
|
|
269
331
|
.join(' | ')
|
|
270
332
|
: rows
|
|
@@ -272,7 +334,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
272
334
|
.map(([k, v]) => `${prettifyKey(k, locale, t)}: ${formatScalar(v)}`)
|
|
273
335
|
.join(', '))
|
|
274
336
|
.join(' | ');
|
|
275
|
-
return (_jsx(PopoverShell, { label: label, title: title, children: _jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields }) }));
|
|
337
|
+
return (_jsx(PopoverShell, { label: label, title: title, children: _jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields, getImageUrl: getImageUrl }) }));
|
|
276
338
|
}
|
|
277
339
|
// Array of scalars (or mixed). Inline mode renders the full list; badge
|
|
278
340
|
// mode previews the first N joined with a "+N" overflow trigger.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
|
|
@@ -661,6 +661,7 @@ function IconNameViewValue({ name }) {
|
|
|
661
661
|
// empty objects keep the "—" marker (CollectionCell renders a muted dash, which
|
|
662
662
|
// we normalize to the em-dash the detail view uses elsewhere).
|
|
663
663
|
function StructuredViewValue({ value, field, locale, t, }) {
|
|
664
|
+
const getImageUrl = useContext(ImageUrlContext);
|
|
664
665
|
const isEmpty = value === null ||
|
|
665
666
|
value === undefined ||
|
|
666
667
|
value === '' ||
|
|
@@ -671,16 +672,17 @@ function StructuredViewValue({ value, field, locale, t, }) {
|
|
|
671
672
|
if (isEmpty) {
|
|
672
673
|
return _jsx("p", { className: "text-sm py-1 text-muted-foreground", children: "\u2014" });
|
|
673
674
|
}
|
|
674
|
-
return (_jsx("div", { className: "text-sm py-1", children: _jsx(CollectionCell, { value: value, itemFields: field?.itemFields ?? field?.item_fields, variant: "inline", locale: locale, t: t }) }));
|
|
675
|
+
return (_jsx("div", { className: "text-sm py-1", children: _jsx(CollectionCell, { value: value, itemFields: field?.itemFields ?? field?.item_fields, variant: "inline", locale: locale, t: t, getImageUrl: getImageUrl }) }));
|
|
675
676
|
}
|
|
676
677
|
export function EditField({ field, value, onChange, record }) {
|
|
677
678
|
const { t, i18n } = useTranslation();
|
|
679
|
+
const editFieldImageUrl = useContext(ImageUrlContext);
|
|
678
680
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
679
681
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
680
682
|
// with the same inline table the detail view uses — a localized, ref-resolved
|
|
681
683
|
// mini-table — instead of an input that stringifies to "[object Object]".
|
|
682
684
|
if (isLineItemsField(field, value)) {
|
|
683
|
-
return (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "rounded-md border bg-muted/30 p-2", children: _jsx(CollectionCell, { value: value, itemFields: fieldItemFields(field), variant: "inline", locale: i18n.language, t: t }) }), _jsx("p", { className: "text-[11px] text-muted-foreground", children: t('datatable.readOnly', { defaultValue: 'Solo lectura' }) })] }));
|
|
685
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "rounded-md border bg-muted/30 p-2", children: _jsx(CollectionCell, { value: value, itemFields: fieldItemFields(field), variant: "inline", locale: i18n.language, t: t, getImageUrl: editFieldImageUrl }) }), _jsx("p", { className: "text-[11px] text-muted-foreground", children: t('datatable.readOnly', { defaultValue: 'Solo lectura' }) })] }));
|
|
684
686
|
}
|
|
685
687
|
if (field.type === 'boolean') {
|
|
686
688
|
return (_jsxs("div", { className: "flex items-center gap-2 py-1", children: [_jsx(Switch, { checked: !!value, onCheckedChange: onChange }), _jsx("span", { className: "text-sm text-muted-foreground", children: value ? 'Sí' : 'No' })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAiC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AA8DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAqKD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAsID;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAiC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AA8DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAqKD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAsID;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CA+nBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
|
package/dist/dynamic-columns.js
CHANGED
|
@@ -736,7 +736,7 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
|
|
|
736
736
|
}
|
|
737
737
|
default: {
|
|
738
738
|
if (typeof value === 'object' && value !== null) {
|
|
739
|
-
return (_jsx(CollectionCell, { value: value, locale: currentLanguage, t: t, itemFields: col.itemFields ?? col.item_fields }));
|
|
739
|
+
return (_jsx(CollectionCell, { value: value, locale: currentLanguage, t: t, itemFields: col.itemFields ?? col.item_fields, getImageUrl: getImageUrl }));
|
|
740
740
|
}
|
|
741
741
|
if (col.key === 'description' ||
|
|
742
742
|
col.key === 'features' ||
|
package/package.json
CHANGED
package/src/collection-cell.tsx
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
// mini-table — no per-addon config required, safe on any shape.
|
|
7
7
|
|
|
8
8
|
import * as React from 'react'
|
|
9
|
-
import { List } from 'lucide-react'
|
|
9
|
+
import { Box, List } from 'lucide-react'
|
|
10
10
|
import {
|
|
11
|
+
Avatar,
|
|
12
|
+
AvatarFallback,
|
|
13
|
+
AvatarImage,
|
|
11
14
|
Badge,
|
|
12
15
|
Popover,
|
|
13
16
|
PopoverContent,
|
|
@@ -20,8 +23,35 @@ import {
|
|
|
20
23
|
TableRow,
|
|
21
24
|
cn,
|
|
22
25
|
} from '@asteby/metacore-ui'
|
|
26
|
+
import { getInitials, relationChipStyles } from '@asteby/metacore-ui/lib'
|
|
23
27
|
import { humanizeToken } from './dynamic-columns-helpers'
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Tracks the host's dark-mode class on <html> so relation chips pick a tint
|
|
31
|
+
* tuned for the active theme. Replicated from dynamic-columns (kept local to
|
|
32
|
+
* avoid a cross-module import); mirror changes if that one evolves.
|
|
33
|
+
*/
|
|
34
|
+
function useIsDarkTheme(): boolean {
|
|
35
|
+
const [isDark, setIsDark] = React.useState(
|
|
36
|
+
() =>
|
|
37
|
+
typeof document !== 'undefined' &&
|
|
38
|
+
document.documentElement.classList.contains('dark')
|
|
39
|
+
)
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (typeof document === 'undefined') return
|
|
42
|
+
const sync = () =>
|
|
43
|
+
setIsDark(document.documentElement.classList.contains('dark'))
|
|
44
|
+
sync()
|
|
45
|
+
const observer = new MutationObserver(sync)
|
|
46
|
+
observer.observe(document.documentElement, {
|
|
47
|
+
attributes: true,
|
|
48
|
+
attributeFilter: ['class'],
|
|
49
|
+
})
|
|
50
|
+
return () => observer.disconnect()
|
|
51
|
+
}, [])
|
|
52
|
+
return isDark
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
const UUID_LIKE_RE =
|
|
26
56
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
27
57
|
|
|
@@ -56,31 +86,105 @@ function siblingKeyFor(key: string): string {
|
|
|
56
86
|
return key.endsWith('_id') ? key.slice(0, -3) : `${key}_label`
|
|
57
87
|
}
|
|
58
88
|
|
|
89
|
+
/** The backend-injected resolved sibling for a ref item-field. */
|
|
90
|
+
interface ResolvedRef {
|
|
91
|
+
label?: string
|
|
92
|
+
value?: unknown
|
|
93
|
+
/** Optional thumbnail (product photo, logo, avatar) resolved by the backend. */
|
|
94
|
+
image?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
59
97
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `formatScalar` (truncated uuid). Non-ref fields render `formatScalar(value)`.
|
|
98
|
+
* Reads the backend-injected resolved sibling for a `ref` item-field (the FK
|
|
99
|
+
* key without `_id`, else `<key>_label`). Returns the normalized `{label,
|
|
100
|
+
* image}` when present, a `{label}` for a bare-string sibling, or null when the
|
|
101
|
+
* ref is unresolved (so the caller falls back to the raw value).
|
|
65
102
|
*/
|
|
66
|
-
function
|
|
103
|
+
function resolvedRefFor(
|
|
67
104
|
field: ItemField,
|
|
68
105
|
row: Record<string, unknown>,
|
|
69
|
-
):
|
|
70
|
-
if (field.ref)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
): ResolvedRef | null {
|
|
107
|
+
if (!field.ref) return null
|
|
108
|
+
const sibling = row[siblingKeyFor(field.key)]
|
|
109
|
+
if (sibling && typeof sibling === 'object' && !Array.isArray(sibling)) {
|
|
110
|
+
const obj = sibling as Record<string, unknown>
|
|
111
|
+
const label = obj.label
|
|
112
|
+
if (label !== undefined && label !== null && label !== '') {
|
|
113
|
+
return {
|
|
114
|
+
label: String(label),
|
|
115
|
+
value: obj.value,
|
|
116
|
+
image:
|
|
117
|
+
typeof obj.image === 'string' && obj.image !== ''
|
|
118
|
+
? obj.image
|
|
119
|
+
: undefined,
|
|
76
120
|
}
|
|
77
|
-
} else if (typeof sibling === 'string' && sibling !== '') {
|
|
78
|
-
return sibling
|
|
79
121
|
}
|
|
122
|
+
} else if (typeof sibling === 'string' && sibling !== '') {
|
|
123
|
+
return { label: sibling }
|
|
80
124
|
}
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Plain-text value for one declared item-field — used for the no-JS popover
|
|
130
|
+
* `title` tooltip (which can't render JSX). Ref fields show the resolved label;
|
|
131
|
+
* everything else uses `formatScalar` (truncated uuid for unresolved ids).
|
|
132
|
+
*/
|
|
133
|
+
function itemFieldText(field: ItemField, row: Record<string, unknown>): string {
|
|
134
|
+
const ref = resolvedRefFor(field, row)
|
|
135
|
+
if (ref?.label) return ref.label
|
|
81
136
|
return formatScalar(row[field.key])
|
|
82
137
|
}
|
|
83
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Visual cell for one declared item-field. A resolved `ref` renders as a
|
|
141
|
+
* relation chip (subtle deterministic tint + product thumbnail or a generic
|
|
142
|
+
* entity icon + name) — the same "pro" look the table FK columns use — so jsonb
|
|
143
|
+
* line items read like first-class relations instead of raw uuids. Non-ref (or
|
|
144
|
+
* unresolved) fields render the plain scalar.
|
|
145
|
+
*/
|
|
146
|
+
function ItemFieldCell({
|
|
147
|
+
field,
|
|
148
|
+
row,
|
|
149
|
+
getImageUrl,
|
|
150
|
+
}: {
|
|
151
|
+
field: ItemField
|
|
152
|
+
row: Record<string, unknown>
|
|
153
|
+
getImageUrl?: (path: string) => string
|
|
154
|
+
}): React.ReactElement {
|
|
155
|
+
const isDark = useIsDarkTheme()
|
|
156
|
+
const ref = resolvedRefFor(field, row)
|
|
157
|
+
if (ref?.label) {
|
|
158
|
+
return (
|
|
159
|
+
<span
|
|
160
|
+
className="inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium"
|
|
161
|
+
style={relationChipStyles(ref.label, { isDark })}
|
|
162
|
+
title={ref.label}
|
|
163
|
+
>
|
|
164
|
+
{ref.image ? (
|
|
165
|
+
<Avatar
|
|
166
|
+
className="shrink-0 rounded-sm ring-1 ring-border/40"
|
|
167
|
+
style={{ width: 18, height: 18 }}
|
|
168
|
+
>
|
|
169
|
+
<AvatarImage
|
|
170
|
+
src={getImageUrl ? getImageUrl(ref.image) : ref.image}
|
|
171
|
+
alt={ref.label}
|
|
172
|
+
className="object-cover"
|
|
173
|
+
/>
|
|
174
|
+
<AvatarFallback className="rounded-sm bg-primary/10 text-[8px] font-bold text-primary">
|
|
175
|
+
{getInitials(ref.label)}
|
|
176
|
+
</AvatarFallback>
|
|
177
|
+
</Avatar>
|
|
178
|
+
) : (
|
|
179
|
+
<Box className="h-3 w-3 shrink-0 opacity-70" />
|
|
180
|
+
)}
|
|
181
|
+
<span className="truncate">{ref.label}</span>
|
|
182
|
+
</span>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
return <>{formatScalar(row[field.key])}</>
|
|
186
|
+
}
|
|
187
|
+
|
|
84
188
|
/** Normalize an org/UI language tag to a base language code (`es-MX` → `es`). */
|
|
85
189
|
function baseLang(locale?: string): string {
|
|
86
190
|
return (locale || 'en').toLowerCase().split('-')[0]
|
|
@@ -258,11 +362,13 @@ function MiniTable({
|
|
|
258
362
|
locale,
|
|
259
363
|
t,
|
|
260
364
|
itemFields,
|
|
365
|
+
getImageUrl,
|
|
261
366
|
}: {
|
|
262
367
|
rows: Record<string, unknown>[]
|
|
263
368
|
locale?: string
|
|
264
369
|
t?: Translate
|
|
265
370
|
itemFields?: ItemField[]
|
|
371
|
+
getImageUrl?: (path: string) => string
|
|
266
372
|
}) {
|
|
267
373
|
// Schema-driven path: a declared `item_fields` schema fixes the column
|
|
268
374
|
// order + headers (already localized by the backend, used VERBATIM) and
|
|
@@ -292,7 +398,11 @@ function MiniTable({
|
|
|
292
398
|
key={field.key}
|
|
293
399
|
className="text-xs whitespace-nowrap"
|
|
294
400
|
>
|
|
295
|
-
|
|
401
|
+
<ItemFieldCell
|
|
402
|
+
field={field}
|
|
403
|
+
row={row}
|
|
404
|
+
getImageUrl={getImageUrl}
|
|
405
|
+
/>
|
|
296
406
|
</TableCell>
|
|
297
407
|
))}
|
|
298
408
|
</TableRow>
|
|
@@ -421,6 +531,12 @@ export interface CollectionCellProps {
|
|
|
421
531
|
* behaviour is unchanged.
|
|
422
532
|
*/
|
|
423
533
|
itemFields?: ItemField[]
|
|
534
|
+
/**
|
|
535
|
+
* Resolves a stored image path to a displayable URL (same resolver the table
|
|
536
|
+
* columns use). Threaded to the ref-chip thumbnails so resolved line-item
|
|
537
|
+
* relations show a product photo / logo / avatar like the FK columns do.
|
|
538
|
+
*/
|
|
539
|
+
getImageUrl?: (path: string) => string
|
|
424
540
|
/**
|
|
425
541
|
* Presentation mode.
|
|
426
542
|
* - `'badge'` (default): the compact count/preview badge that opens a
|
|
@@ -448,6 +564,7 @@ export function CollectionCell({
|
|
|
448
564
|
locale,
|
|
449
565
|
t,
|
|
450
566
|
itemFields,
|
|
567
|
+
getImageUrl,
|
|
451
568
|
variant = 'badge',
|
|
452
569
|
}: CollectionCellProps) {
|
|
453
570
|
const parsed = parseValue(value)
|
|
@@ -491,6 +608,7 @@ export function CollectionCell({
|
|
|
491
608
|
locale={locale}
|
|
492
609
|
t={t}
|
|
493
610
|
itemFields={itemFields}
|
|
611
|
+
getImageUrl={getImageUrl}
|
|
494
612
|
/>
|
|
495
613
|
)
|
|
496
614
|
}
|
|
@@ -506,7 +624,7 @@ export function CollectionCell({
|
|
|
506
624
|
itemFields!
|
|
507
625
|
.map(
|
|
508
626
|
(field) =>
|
|
509
|
-
`${field.label}: ${
|
|
627
|
+
`${field.label}: ${itemFieldText(field, row)}`
|
|
510
628
|
)
|
|
511
629
|
.join(', ')
|
|
512
630
|
)
|
|
@@ -528,6 +646,7 @@ export function CollectionCell({
|
|
|
528
646
|
locale={locale}
|
|
529
647
|
t={t}
|
|
530
648
|
itemFields={itemFields}
|
|
649
|
+
getImageUrl={getImageUrl}
|
|
531
650
|
/>
|
|
532
651
|
</PopoverShell>
|
|
533
652
|
)
|
|
@@ -1182,6 +1182,7 @@ function StructuredViewValue({
|
|
|
1182
1182
|
locale?: string
|
|
1183
1183
|
t?: (key: string, options?: any) => string
|
|
1184
1184
|
}) {
|
|
1185
|
+
const getImageUrl = useContext(ImageUrlContext)
|
|
1185
1186
|
const isEmpty =
|
|
1186
1187
|
value === null ||
|
|
1187
1188
|
value === undefined ||
|
|
@@ -1201,6 +1202,7 @@ function StructuredViewValue({
|
|
|
1201
1202
|
variant="inline"
|
|
1202
1203
|
locale={locale}
|
|
1203
1204
|
t={t}
|
|
1205
|
+
getImageUrl={getImageUrl}
|
|
1204
1206
|
/>
|
|
1205
1207
|
</div>
|
|
1206
1208
|
)
|
|
@@ -1214,6 +1216,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1214
1216
|
record?: any
|
|
1215
1217
|
}) {
|
|
1216
1218
|
const { t, i18n } = useTranslation()
|
|
1219
|
+
const editFieldImageUrl = useContext(ImageUrlContext)
|
|
1217
1220
|
|
|
1218
1221
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
1219
1222
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
@@ -1229,6 +1232,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1229
1232
|
variant="inline"
|
|
1230
1233
|
locale={i18n.language}
|
|
1231
1234
|
t={t}
|
|
1235
|
+
getImageUrl={editFieldImageUrl}
|
|
1232
1236
|
/>
|
|
1233
1237
|
</div>
|
|
1234
1238
|
<p className="text-[11px] text-muted-foreground">
|