@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 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":"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"}
@@ -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
- // FK columns: a `ref` (kernel-derived belongs_to target) or an explicit
880
- // `widget: 'dynamic_select'` renders the SDK's async searchable picker — with
881
- // option thumbnails and the inline-create "+"against /api/options/<ref>.
882
- // Static inline `options` are handled by the enum <Select> branch below.
883
- if ((getFieldRef(field) || field.widget === 'dynamic_select') && !field.options?.length) {
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` sourcee.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" }), item.image && (_jsx(OptionThumb, { image: item.image, 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));
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;AAuC9C,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;AA8ID;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAqmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,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"}
@@ -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 && (_jsx(RelationThumbnail, { src: image, alt: display, getImageUrl: getImageUrl, size: subtitle ? 24 : 18 })), 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 }))] }));
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`. Falls back to a neutral
11
- * placeholder icon when the option has no image so rows/triggers stay aligned.
12
- * `size` is in pixels (kept small 20–24px — so the picker reads as a list,
13
- * not a gallery). Inline style for the box dimensions: arbitrary Tailwind
14
- * classes from a federated addon don't always survive the host's class scan.
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":"AAuCA,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;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BA4BzF;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,CAAC,GAAG,IAAI,CAAA;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCAwBA;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"}
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, ImageIcon, Loader2, Plus } from 'lucide-react';
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`. Falls back to a neutral
40
- * placeholder icon when the option has no image so rows/triggers stay aligned.
41
- * `size` is in pixels (kept small 20–24px — so the picker reads as a list,
42
- * not a gallery). Inline style for the box dimensions: arbitrary Tailwind
43
- * classes from a federated addon don't always survive the host's class scan.
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 (_jsx("span", { className: "text-muted-foreground bg-muted flex shrink-0 items-center justify-center rounded-sm", style: box, "aria-hidden": true, children: _jsx(ImageIcon, { className: "size-3 opacity-60" }) }));
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": "25.2.0",
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.10.1"
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.10.1"
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
- // FK columns: a `ref` (kernel-derived belongs_to target) or an explicit
1544
- // `widget: 'dynamic_select'` renders the SDK's async searchable picker — with
1545
- // option thumbnails and the inline-create "+"against /api/options/<ref>.
1546
- // Static inline `options` are handled by the enum <Select> branch below.
1547
- if ((getFieldRef(field as ActionFieldDef) || field.widget === 'dynamic_select') && !field.options?.length) {
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` sourcee.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 && (
@@ -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, ImageIcon, Loader2, Plus } from 'lucide-react'
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`. Falls back to a neutral
53
- * placeholder icon when the option has no image so rows/triggers stay aligned.
54
- * `size` is in pixels (kept small 20–24px — so the picker reads as a list,
55
- * not a gallery). Inline style for the box dimensions: arbitrary Tailwind
56
- * classes from a federated addon don't always survive the host's class scan.
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({ image, size = 20 }: { image?: string | null; size?: number }) {
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