@asteby/metacore-runtime-react 25.2.0 → 26.0.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 +32 -0
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +17 -6
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +2 -2
- package/dist/dynamic-select-field.d.ts +10 -7
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +17 -10
- package/package.json +3 -3
- package/src/dialogs/dynamic-record.tsx +20 -9
- package/src/dynamic-columns.tsx +8 -1
- package/src/dynamic-select-field.tsx +25 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 26.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ee5f7e8: feat(ui): InitialsAvatar — deterministic initials fallback for imageless references
|
|
8
|
+
|
|
9
|
+
New shared `InitialsAvatar` primitive: when a reference/option has no image it now
|
|
10
|
+
shows 1–2 uppercase initials on a background color derived deterministically from
|
|
11
|
+
the name (stable per name via the existing `optionColor` hash → curated palette),
|
|
12
|
+
instead of an empty placeholder box.
|
|
13
|
+
|
|
14
|
+
Wired into the three surfaces through ONE component so they never diverge: the
|
|
15
|
+
relation picker (`OptionThumb`/`OptionLead`), the dynamic-table relation cell
|
|
16
|
+
(`RelationCell`), and the read-only detail dialog. Existing image, icon, and color
|
|
17
|
+
rendering is unchanged; the avatar is purely the imageless fallback. Respects the
|
|
18
|
+
existing sizes (24px table, 22px detail, the picker's).
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- a8f94ba: fix(runtime-react): render `dynamic_select` por `type`, no solo por `ref`/`widget`
|
|
23
|
+
|
|
24
|
+
El renderer editable del `DynamicRecordDialog` solo pintaba el picker cuando
|
|
25
|
+
`getFieldRef(field)` o `field.widget === 'dynamic_select'`. Un campo con
|
|
26
|
+
`type: 'dynamic_select'` cuya fuente vive en `optionsConfig.source` (sin `ref`
|
|
27
|
+
FK — p. ej. `currency_code` → `currencies` / `POSOrgCurrency`) degradaba a un
|
|
28
|
+
input de texto libre. Se agrega `field.type === 'dynamic_select'` a la
|
|
29
|
+
condición; `DynamicSelectField` ya resuelve la fuente vía `resolveOptionsSource`
|
|
30
|
+
(ref o `optionsConfig.source`).
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [ee5f7e8]
|
|
33
|
+
- @asteby/metacore-ui@2.11.0
|
|
34
|
+
|
|
3
35
|
## 25.2.0
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
|
@@ -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":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAwC1C,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAgBjF,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEnE,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAK1F,YAAY,EAAE,WAAW,EAAE,CAAA;AAE3B,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,QAAQ;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;IACpH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,GAAG,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACjC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;IACxB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,SAAS,EAAE,CAAA;CAC5B;AAiCD,MAAM,WAAW,wBAAwB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;2DAEuD;IACvD,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IAClF;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACpG;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAA;IAC1C;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAK1D;AAaD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAgBtE;AAID,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,EAAE,GAAG,SAAS,CAExE;AAQD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CASrE;AASD,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,cAAc,GAAG,IAAI,CAa5F;AA6FD,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAUjE;AAMD,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,EAC9B,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GACjC,QAAQ,EAAE,CAMZ;AAED,wBAAgB,mBAAmB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,EACN,cAAc,EACd,aAAa,EACb,WAA8B,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACX,EAAE,wBAAwB,+BAoc1B;AA+DD,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,+BAWlF;AAyDD,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,KAAK,EAAE,QAAQ,EACf,MAAM,EACN,WAAW,EAAE,eAAe,EAC5B,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,GACzB,EAAE;IACC,KAAK,EAAE,QAAQ,CAAA;IACf,KAAK,EAAE,GAAG,CAAA;IACV,MAAM,EAAE,GAAG,CAAA;IACX,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB,+BAgQA;AAsID,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;IAC1D,KAAK,EAAE,QAAQ,CAAA;IACf,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC5B,iFAAiF;IACjF,MAAM,CAAC,EAAE,GAAG,CAAA;CACf,+BAyLA"}
|
|
@@ -629,6 +629,9 @@ function RelationViewValue({ field, value, record }) {
|
|
|
629
629
|
image: image ? getImageUrl(image) : null,
|
|
630
630
|
color: resolved?.color ?? null,
|
|
631
631
|
icon: resolved?.icon ?? null,
|
|
632
|
+
// Carry the label so an imageless reference falls back to its initials
|
|
633
|
+
// avatar (shared InitialsAvatar via OptionLead) instead of a blank lead.
|
|
634
|
+
label: label ?? '',
|
|
632
635
|
};
|
|
633
636
|
return (_jsxs("div", { className: "flex items-center gap-2 py-1", children: [_jsx(OptionLead, { option: lead, size: 24 }), _jsx("span", { className: "text-sm", children: label ?? '—' })] }));
|
|
634
637
|
}
|
|
@@ -876,11 +879,19 @@ export function EditField({ field, value, onChange, record }) {
|
|
|
876
879
|
if (field.type === 'image' || field.widget === 'upload') {
|
|
877
880
|
return _jsx(ImageUploadField, { field: field, value: value, onChange: onChange });
|
|
878
881
|
}
|
|
879
|
-
//
|
|
880
|
-
// `widget: 'dynamic_select'
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
|
|
882
|
+
// Dynamic pickers: a `ref` (kernel-derived belongs_to target), an explicit
|
|
883
|
+
// `widget: 'dynamic_select'`, OR a `type: 'dynamic_select'` (the kernel maps
|
|
884
|
+
// any column carrying an `optionsConfig` source — e.g. `currencies` /
|
|
885
|
+
// `POSOrgCurrency` — to this type) renders the SDK's async searchable picker,
|
|
886
|
+
// with option thumbnails and the inline-create "+", against the options
|
|
887
|
+
// endpoint resolved by `resolveOptionsSource` (ref → /api/options/<ref>,
|
|
888
|
+
// else optionsConfig.source → /api/options/<source>). Without the `type`
|
|
889
|
+
// check a source-backed picker with no `ref` silently degraded to a raw text
|
|
890
|
+
// input. Static inline `options` are handled by the enum <Select> branch below.
|
|
891
|
+
if ((getFieldRef(field) ||
|
|
892
|
+
field.type === 'dynamic_select' ||
|
|
893
|
+
field.widget === 'dynamic_select') &&
|
|
894
|
+
!field.options?.length) {
|
|
884
895
|
return (_jsx(DynamicSelectField, { field: field, value: value, onChange: onChange,
|
|
885
896
|
// Seed the trigger with the related record's NAME (from the
|
|
886
897
|
// backend-injected FK sibling, key without `_id`) so an existing
|
|
@@ -1016,6 +1027,6 @@ function SearchField({ field, value, onChange }) {
|
|
|
1016
1027
|
setSelectedLabel(itemLabel);
|
|
1017
1028
|
setOpen(false);
|
|
1018
1029
|
setQuery('');
|
|
1019
|
-
}, children: [isSelected && _jsx(Check, { className: "mr-2 h-3.5 w-3.5 shrink-0 text-primary" }),
|
|
1030
|
+
}, children: [isSelected && _jsx(Check, { className: "mr-2 h-3.5 w-3.5 shrink-0 text-primary" }), _jsx(OptionThumb, { image: item.image, name: itemLabel, size: 20 }), _jsxs("div", { className: "flex flex-col min-w-0 ml-2", children: [_jsx("span", { className: "truncate", children: itemLabel }), item.description && (_jsx("span", { className: "text-[11px] text-muted-foreground truncate", children: item.description }))] })] }, itemValue));
|
|
1020
1031
|
}) })) })] }) })] }));
|
|
1021
1032
|
}
|
|
@@ -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;
|
|
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;AAwC9C,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;AAyCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAiB5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OACqB,CAAA;AAwFhF;;;;;;;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;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOzE,CAAA;AAoJD;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAqmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
|
package/dist/dynamic-columns.js
CHANGED
|
@@ -17,7 +17,7 @@ import { format } from 'date-fns';
|
|
|
17
17
|
import { es, enUS } from 'date-fns/locale';
|
|
18
18
|
import * as icons from 'lucide-react';
|
|
19
19
|
import { MoreHorizontal } from 'lucide-react';
|
|
20
|
-
import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Checkbox, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@asteby/metacore-ui';
|
|
20
|
+
import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Checkbox, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, InitialsAvatar, } from '@asteby/metacore-ui';
|
|
21
21
|
import { DataTableColumnHeader, FilterableColumnHeader, } from '@asteby/metacore-ui/data-table';
|
|
22
22
|
import { generateBadgeStyles, getInitials, relationChipStyles, } from '@asteby/metacore-ui/lib';
|
|
23
23
|
import { Progress } from './dialogs/_primitives';
|
|
@@ -376,7 +376,7 @@ const RelationCell = ({ col, row, getImageUrl }) => {
|
|
|
376
376
|
// alive yet stay visually distinct from option/status badges. Inline style
|
|
377
377
|
// (hex-derived) bypasses the host tailwind safelist.
|
|
378
378
|
const chipStyles = relationChipStyles(display, { isDark });
|
|
379
|
-
return (_jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-sm font-medium", style: chipStyles, title: subtitle ? `${display} · ${subtitle}` : display, children: [image
|
|
379
|
+
return (_jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-sm font-medium", style: chipStyles, title: subtitle ? `${display} · ${subtitle}` : display, children: [image ? (_jsx(RelationThumbnail, { src: image, alt: display, getImageUrl: getImageUrl, size: subtitle ? 24 : 18 })) : (_jsx(InitialsAvatar, { name: display, size: subtitle ? 24 : 18, rounded: "sm" })), subtitle ? (_jsxs("span", { className: "flex flex-col leading-tight min-w-0", children: [_jsx("span", { className: "truncate", children: display }), _jsx("span", { className: "truncate text-[0.7rem] opacity-70", children: subtitle })] })) : (_jsx("span", { className: "truncate", children: display }))] }));
|
|
380
380
|
};
|
|
381
381
|
/**
|
|
382
382
|
* Renders a SAP-style polymorphic source-document reference as a navigable
|
|
@@ -7,14 +7,17 @@ import type { ActionFieldDef } from './types';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const DEFAULT_DEPENDS_HINT = "Selecciona primero el campo del que depende";
|
|
9
9
|
/**
|
|
10
|
-
* Small square thumbnail for an option's `image`.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* Small square thumbnail for an option's `image`. When the option has no image
|
|
11
|
+
* it falls back to a deterministic initials avatar (shared `InitialsAvatar`)
|
|
12
|
+
* derived from `name`, so an imageless reference reads as a colored badge rather
|
|
13
|
+
* than an empty placeholder — and rows/triggers stay aligned. `size` is in
|
|
14
|
+
* pixels (kept small — 20–24px — so the picker reads as a list, not a gallery).
|
|
15
|
+
* Inline style for the box dimensions: arbitrary Tailwind classes from a
|
|
16
|
+
* federated addon don't always survive the host's class scan.
|
|
15
17
|
*/
|
|
16
|
-
export declare function OptionThumb({ image, size }: {
|
|
18
|
+
export declare function OptionThumb({ image, name, size, }: {
|
|
17
19
|
image?: string | null;
|
|
20
|
+
name?: string | null;
|
|
18
21
|
size?: number;
|
|
19
22
|
}): import("react").JSX.Element;
|
|
20
23
|
/**
|
|
@@ -23,7 +26,7 @@ export declare function OptionThumb({ image, size }: {
|
|
|
23
26
|
* Returns null when the option carries none, so plain text options stay plain.
|
|
24
27
|
*/
|
|
25
28
|
export declare function OptionLead({ option, size, }: {
|
|
26
|
-
option?: Pick<ResolvedOption, 'image' | 'color' | 'icon'> | null;
|
|
29
|
+
option?: Pick<ResolvedOption, 'image' | 'color' | 'icon' | 'label'> | null;
|
|
27
30
|
size?: number;
|
|
28
31
|
}): import("react").JSX.Element | null;
|
|
29
32
|
export interface DynamicSelectFieldProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AAwCA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAS,GACZ,EAAE;IACC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,+BAoBA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCA4BA;AAqBD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,GACnB,EAAE,uBAAuB,+BAwOzB;AAED,eAAe,kBAAkB,CAAA"}
|
|
@@ -23,8 +23,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
23
23
|
// value). A dedicated `?ids=` lookup is a follow-up; create flows — the common
|
|
24
24
|
// case — start empty and never hit this.
|
|
25
25
|
import { useEffect, useRef, useState } from 'react';
|
|
26
|
-
import { Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Popover, PopoverContent, PopoverTrigger, } from '@asteby/metacore-ui/primitives';
|
|
27
|
-
import { Check, ChevronsUpDown,
|
|
26
|
+
import { Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Popover, PopoverContent, PopoverTrigger, InitialsAvatar, } from '@asteby/metacore-ui/primitives';
|
|
27
|
+
import { Check, ChevronsUpDown, Loader2, Plus } from 'lucide-react';
|
|
28
28
|
import { resolveColorCss } from '@asteby/metacore-ui/lib';
|
|
29
29
|
import { DynamicIcon } from './dynamic-icon';
|
|
30
30
|
import { useOptionsResolver } from './use-options-resolver';
|
|
@@ -36,16 +36,18 @@ import { getDependsOn, getFieldRef, resolveOptionsSource } from './dynamic-form-
|
|
|
36
36
|
*/
|
|
37
37
|
export const DEFAULT_DEPENDS_HINT = 'Selecciona primero el campo del que depende';
|
|
38
38
|
/**
|
|
39
|
-
* Small square thumbnail for an option's `image`.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* Small square thumbnail for an option's `image`. When the option has no image
|
|
40
|
+
* it falls back to a deterministic initials avatar (shared `InitialsAvatar`)
|
|
41
|
+
* derived from `name`, so an imageless reference reads as a colored badge rather
|
|
42
|
+
* than an empty placeholder — and rows/triggers stay aligned. `size` is in
|
|
43
|
+
* pixels (kept small — 20–24px — so the picker reads as a list, not a gallery).
|
|
44
|
+
* Inline style for the box dimensions: arbitrary Tailwind classes from a
|
|
45
|
+
* federated addon don't always survive the host's class scan.
|
|
44
46
|
*/
|
|
45
|
-
export function OptionThumb({ image, size = 20 }) {
|
|
47
|
+
export function OptionThumb({ image, name, size = 20, }) {
|
|
46
48
|
const box = { width: size, height: size };
|
|
47
49
|
if (!image) {
|
|
48
|
-
return
|
|
50
|
+
return _jsx(InitialsAvatar, { name: name, size: size, rounded: "sm" });
|
|
49
51
|
}
|
|
50
52
|
return (_jsx("img", { src: image, alt: "", "aria-hidden": true, loading: "lazy", className: "shrink-0 rounded-sm object-cover", style: box,
|
|
51
53
|
// A broken image url shouldn't leave a torn-icon glyph; collapse to
|
|
@@ -63,13 +65,18 @@ export function OptionLead({ option, size = 20, }) {
|
|
|
63
65
|
if (!option)
|
|
64
66
|
return null;
|
|
65
67
|
if (option.image)
|
|
66
|
-
return _jsx(OptionThumb, { image: option.image, size: size });
|
|
68
|
+
return _jsx(OptionThumb, { image: option.image, name: option.label, size: size });
|
|
67
69
|
if (option.icon) {
|
|
68
70
|
return (_jsx("span", { className: "flex shrink-0 items-center justify-center", style: { width: size, height: size, color: option.color ? resolveColorCss(option.color) : undefined }, "aria-hidden": true, children: _jsx(DynamicIcon, { name: option.icon, className: "size-4" }) }));
|
|
69
71
|
}
|
|
70
72
|
if (option.color) {
|
|
71
73
|
return (_jsx("span", { className: "shrink-0 rounded-full", style: { width: Math.round(size * 0.5), height: Math.round(size * 0.5), background: resolveColorCss(option.color) }, "aria-hidden": true }));
|
|
72
74
|
}
|
|
75
|
+
// No image/icon/color: an imageless reference option. Show its initials
|
|
76
|
+
// (shared InitialsAvatar) so it stays visually aligned with the sibling
|
|
77
|
+
// options that DO carry a thumbnail, rather than a blank gap.
|
|
78
|
+
if (option.label)
|
|
79
|
+
return _jsx(InitialsAvatar, { name: option.label, size: size, rounded: "sm" });
|
|
73
80
|
return null;
|
|
74
81
|
}
|
|
75
82
|
/** True when any option (or the selected one) carries a renderable visual. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.0.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"sonner": ">=1.7",
|
|
39
39
|
"zustand": ">=5",
|
|
40
40
|
"@asteby/metacore-sdk": "^3.3.0",
|
|
41
|
-
"@asteby/metacore-ui": "^2.
|
|
41
|
+
"@asteby/metacore-ui": "^2.11.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@tanstack/react-router": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
70
|
"@asteby/metacore-sdk": "3.3.0",
|
|
71
|
-
"@asteby/metacore-ui": "2.
|
|
71
|
+
"@asteby/metacore-ui": "2.11.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
|
@@ -1054,10 +1054,13 @@ function RelationViewValue({ field, value, record }: { field: FieldDef; value: a
|
|
|
1054
1054
|
return <p className="text-sm py-1 text-muted-foreground">—</p>
|
|
1055
1055
|
}
|
|
1056
1056
|
|
|
1057
|
-
const lead: Pick<ResolvedOption, 'image' | 'color' | 'icon'> = {
|
|
1057
|
+
const lead: Pick<ResolvedOption, 'image' | 'color' | 'icon' | 'label'> = {
|
|
1058
1058
|
image: image ? getImageUrl(image) : null,
|
|
1059
1059
|
color: resolved?.color ?? null,
|
|
1060
1060
|
icon: resolved?.icon ?? null,
|
|
1061
|
+
// Carry the label so an imageless reference falls back to its initials
|
|
1062
|
+
// avatar (shared InitialsAvatar via OptionLead) instead of a blank lead.
|
|
1063
|
+
label: label ?? '',
|
|
1061
1064
|
}
|
|
1062
1065
|
|
|
1063
1066
|
return (
|
|
@@ -1540,11 +1543,21 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1540
1543
|
return <ImageUploadField field={field} value={value} onChange={onChange} />
|
|
1541
1544
|
}
|
|
1542
1545
|
|
|
1543
|
-
//
|
|
1544
|
-
// `widget: 'dynamic_select'
|
|
1545
|
-
//
|
|
1546
|
-
//
|
|
1547
|
-
|
|
1546
|
+
// Dynamic pickers: a `ref` (kernel-derived belongs_to target), an explicit
|
|
1547
|
+
// `widget: 'dynamic_select'`, OR a `type: 'dynamic_select'` (the kernel maps
|
|
1548
|
+
// any column carrying an `optionsConfig` source — e.g. `currencies` /
|
|
1549
|
+
// `POSOrgCurrency` — to this type) renders the SDK's async searchable picker,
|
|
1550
|
+
// with option thumbnails and the inline-create "+", against the options
|
|
1551
|
+
// endpoint resolved by `resolveOptionsSource` (ref → /api/options/<ref>,
|
|
1552
|
+
// else optionsConfig.source → /api/options/<source>). Without the `type`
|
|
1553
|
+
// check a source-backed picker with no `ref` silently degraded to a raw text
|
|
1554
|
+
// input. Static inline `options` are handled by the enum <Select> branch below.
|
|
1555
|
+
if (
|
|
1556
|
+
(getFieldRef(field as ActionFieldDef) ||
|
|
1557
|
+
field.type === 'dynamic_select' ||
|
|
1558
|
+
field.widget === 'dynamic_select') &&
|
|
1559
|
+
!field.options?.length
|
|
1560
|
+
) {
|
|
1548
1561
|
return (
|
|
1549
1562
|
<DynamicSelectField
|
|
1550
1563
|
field={field as ActionFieldDef}
|
|
@@ -1817,9 +1830,7 @@ function SearchField({ field, value, onChange }: { field: FieldDef; value: any;
|
|
|
1817
1830
|
}}
|
|
1818
1831
|
>
|
|
1819
1832
|
{isSelected && <Check className="mr-2 h-3.5 w-3.5 shrink-0 text-primary" />}
|
|
1820
|
-
{item.image
|
|
1821
|
-
<OptionThumb image={item.image} size={20} />
|
|
1822
|
-
)}
|
|
1833
|
+
<OptionThumb image={item.image} name={itemLabel} size={20} />
|
|
1823
1834
|
<div className="flex flex-col min-w-0 ml-2">
|
|
1824
1835
|
<span className="truncate">{itemLabel}</span>
|
|
1825
1836
|
{item.description && (
|
package/src/dynamic-columns.tsx
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
DropdownMenuContent,
|
|
30
30
|
DropdownMenuItem,
|
|
31
31
|
DropdownMenuTrigger,
|
|
32
|
+
InitialsAvatar,
|
|
32
33
|
} from '@asteby/metacore-ui'
|
|
33
34
|
import {
|
|
34
35
|
DataTableColumnHeader,
|
|
@@ -510,8 +511,14 @@ const RelationCell: React.FC<{
|
|
|
510
511
|
style={chipStyles}
|
|
511
512
|
title={subtitle ? `${display} · ${subtitle}` : display}
|
|
512
513
|
>
|
|
513
|
-
{image
|
|
514
|
+
{image ? (
|
|
514
515
|
<RelationThumbnail src={image} alt={display} getImageUrl={getImageUrl} size={subtitle ? 24 : 18} />
|
|
516
|
+
) : (
|
|
517
|
+
// No image on the resolved sibling: a deterministic initials avatar
|
|
518
|
+
// (shared InitialsAvatar) keyed on the label, so the "producto"-style
|
|
519
|
+
// cross-addon relation reads as a colored badge instead of text-only —
|
|
520
|
+
// matching the picker and detail surfaces.
|
|
521
|
+
<InitialsAvatar name={display} size={subtitle ? 24 : 18} rounded="sm" />
|
|
515
522
|
)}
|
|
516
523
|
{subtitle ? (
|
|
517
524
|
<span className="flex flex-col leading-tight min-w-0">
|
|
@@ -33,8 +33,9 @@ import {
|
|
|
33
33
|
Popover,
|
|
34
34
|
PopoverContent,
|
|
35
35
|
PopoverTrigger,
|
|
36
|
+
InitialsAvatar,
|
|
36
37
|
} from '@asteby/metacore-ui/primitives'
|
|
37
|
-
import { Check, ChevronsUpDown,
|
|
38
|
+
import { Check, ChevronsUpDown, Loader2, Plus } from 'lucide-react'
|
|
38
39
|
import { resolveColorCss } from '@asteby/metacore-ui/lib'
|
|
39
40
|
import { DynamicIcon } from './dynamic-icon'
|
|
40
41
|
import { useOptionsResolver, type ResolvedOption } from './use-options-resolver'
|
|
@@ -49,24 +50,26 @@ import type { ActionFieldDef } from './types'
|
|
|
49
50
|
export const DEFAULT_DEPENDS_HINT = 'Selecciona primero el campo del que depende'
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
|
-
* Small square thumbnail for an option's `image`.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
* Small square thumbnail for an option's `image`. When the option has no image
|
|
54
|
+
* it falls back to a deterministic initials avatar (shared `InitialsAvatar`)
|
|
55
|
+
* derived from `name`, so an imageless reference reads as a colored badge rather
|
|
56
|
+
* than an empty placeholder — and rows/triggers stay aligned. `size` is in
|
|
57
|
+
* pixels (kept small — 20–24px — so the picker reads as a list, not a gallery).
|
|
58
|
+
* Inline style for the box dimensions: arbitrary Tailwind classes from a
|
|
59
|
+
* federated addon don't always survive the host's class scan.
|
|
57
60
|
*/
|
|
58
|
-
export function OptionThumb({
|
|
61
|
+
export function OptionThumb({
|
|
62
|
+
image,
|
|
63
|
+
name,
|
|
64
|
+
size = 20,
|
|
65
|
+
}: {
|
|
66
|
+
image?: string | null
|
|
67
|
+
name?: string | null
|
|
68
|
+
size?: number
|
|
69
|
+
}) {
|
|
59
70
|
const box = { width: size, height: size }
|
|
60
71
|
if (!image) {
|
|
61
|
-
return
|
|
62
|
-
<span
|
|
63
|
-
className="text-muted-foreground bg-muted flex shrink-0 items-center justify-center rounded-sm"
|
|
64
|
-
style={box}
|
|
65
|
-
aria-hidden
|
|
66
|
-
>
|
|
67
|
-
<ImageIcon className="size-3 opacity-60" />
|
|
68
|
-
</span>
|
|
69
|
-
)
|
|
72
|
+
return <InitialsAvatar name={name} size={size} rounded="sm" />
|
|
70
73
|
}
|
|
71
74
|
return (
|
|
72
75
|
<img
|
|
@@ -94,11 +97,11 @@ export function OptionLead({
|
|
|
94
97
|
option,
|
|
95
98
|
size = 20,
|
|
96
99
|
}: {
|
|
97
|
-
option?: Pick<ResolvedOption, 'image' | 'color' | 'icon'> | null
|
|
100
|
+
option?: Pick<ResolvedOption, 'image' | 'color' | 'icon' | 'label'> | null
|
|
98
101
|
size?: number
|
|
99
102
|
}) {
|
|
100
103
|
if (!option) return null
|
|
101
|
-
if (option.image) return <OptionThumb image={option.image} size={size} />
|
|
104
|
+
if (option.image) return <OptionThumb image={option.image} name={option.label} size={size} />
|
|
102
105
|
if (option.icon) {
|
|
103
106
|
return (
|
|
104
107
|
<span
|
|
@@ -119,6 +122,10 @@ export function OptionLead({
|
|
|
119
122
|
/>
|
|
120
123
|
)
|
|
121
124
|
}
|
|
125
|
+
// No image/icon/color: an imageless reference option. Show its initials
|
|
126
|
+
// (shared InitialsAvatar) so it stays visually aligned with the sibling
|
|
127
|
+
// options that DO carry a thumbnail, rather than a blank gap.
|
|
128
|
+
if (option.label) return <InitialsAvatar name={option.label} size={size} rounded="sm" />
|
|
122
129
|
return null
|
|
123
130
|
}
|
|
124
131
|
|