@asteby/metacore-runtime-react 49.5.0 → 49.7.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/business/customer-picker.d.ts +8 -2
  3. package/dist/business/customer-picker.d.ts.map +1 -1
  4. package/dist/business/customer-picker.js +30 -27
  5. package/dist/business/index.d.ts +1 -0
  6. package/dist/business/index.d.ts.map +1 -1
  7. package/dist/business/index.js +1 -0
  8. package/dist/business/line-product-cell.d.ts +8 -10
  9. package/dist/business/line-product-cell.d.ts.map +1 -1
  10. package/dist/business/line-product-cell.js +13 -90
  11. package/dist/business/product-options.d.ts +41 -0
  12. package/dist/business/product-options.d.ts.map +1 -0
  13. package/dist/business/product-options.js +58 -0
  14. package/dist/business/product-picker.d.ts +5 -1
  15. package/dist/business/product-picker.d.ts.map +1 -1
  16. package/dist/business/product-picker.js +26 -35
  17. package/dist/business/relate-documents.d.ts +1 -1
  18. package/dist/business/relate-documents.d.ts.map +1 -1
  19. package/dist/business/relate-documents.js +6 -8
  20. package/dist/business/vehicle-picker.d.ts +8 -2
  21. package/dist/business/vehicle-picker.d.ts.map +1 -1
  22. package/dist/business/vehicle-picker.js +28 -28
  23. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  24. package/dist/dialogs/dynamic-record.js +28 -43
  25. package/dist/dynamic-multi-select-field.d.ts.map +1 -1
  26. package/dist/dynamic-multi-select-field.js +36 -6
  27. package/dist/dynamic-select-field.d.ts +9 -23
  28. package/dist/dynamic-select-field.d.ts.map +1 -1
  29. package/dist/dynamic-select-field.js +105 -172
  30. package/dist/entity-select.d.ts +5 -2
  31. package/dist/entity-select.d.ts.map +1 -1
  32. package/dist/entity-select.js +23 -116
  33. package/dist/index.d.ts +9 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +10 -1
  36. package/dist/query-batch.d.ts.map +1 -1
  37. package/dist/query-batch.js +4 -0
  38. package/dist/record-picker-actions.d.ts +40 -2
  39. package/dist/record-picker-actions.d.ts.map +1 -1
  40. package/dist/record-picker-actions.js +44 -4
  41. package/dist/record-picker-dialog.d.ts +23 -0
  42. package/dist/record-picker-dialog.d.ts.map +1 -0
  43. package/dist/record-picker-dialog.js +37 -0
  44. package/dist/record-picker-option.d.ts +25 -0
  45. package/dist/record-picker-option.d.ts.map +1 -0
  46. package/dist/record-picker-option.js +55 -0
  47. package/dist/record-picker.d.ts +149 -0
  48. package/dist/record-picker.d.ts.map +1 -0
  49. package/dist/record-picker.js +372 -0
  50. package/dist/use-option-ids.d.ts +34 -0
  51. package/dist/use-option-ids.d.ts.map +1 -0
  52. package/dist/use-option-ids.js +138 -0
  53. package/dist/use-options-resolver.d.ts +4 -0
  54. package/dist/use-options-resolver.d.ts.map +1 -1
  55. package/dist/use-options-resolver.js +10 -1
  56. package/package.json +1 -1
  57. package/src/__tests__/business-pickers.test.tsx +6 -3
  58. package/src/__tests__/document-editor-inline-selects.test.tsx +2 -1
  59. package/src/__tests__/document-form-dialog.test.tsx +4 -3
  60. package/src/__tests__/document-lines-grid.test.tsx +2 -1
  61. package/src/__tests__/edit-field-polish.test.tsx +6 -3
  62. package/src/__tests__/option-ids.test.tsx +181 -0
  63. package/src/__tests__/record-picker.test.tsx +469 -0
  64. package/src/business/customer-picker.tsx +87 -98
  65. package/src/business/index.ts +1 -0
  66. package/src/business/line-product-cell.tsx +55 -180
  67. package/src/business/product-options.tsx +119 -0
  68. package/src/business/product-picker.tsx +63 -126
  69. package/src/business/relate-documents.tsx +36 -45
  70. package/src/business/vehicle-picker.tsx +76 -99
  71. package/src/dialogs/dynamic-record.tsx +44 -97
  72. package/src/dynamic-multi-select-field.tsx +53 -15
  73. package/src/dynamic-select-field.tsx +188 -379
  74. package/src/entity-select.tsx +53 -202
  75. package/src/index.ts +35 -2
  76. package/src/query-batch.ts +3 -0
  77. package/src/record-picker-actions.tsx +101 -8
  78. package/src/record-picker-dialog.tsx +80 -0
  79. package/src/record-picker-option.tsx +96 -0
  80. package/src/record-picker.tsx +971 -0
  81. package/src/use-option-ids.ts +168 -0
  82. package/src/use-options-resolver.ts +11 -1
  83. package/dist/business/use-async-search.d.ts +0 -10
  84. package/dist/business/use-async-search.d.ts.map +0 -1
  85. package/dist/business/use-async-search.js +0 -38
  86. package/src/business/use-async-search.ts +0 -43
@@ -0,0 +1,96 @@
1
+ // Option visuals shared by every RecordPicker configuration: a thumbnail
2
+ // (image / lucide icon name / initials fallback) and the leading visual of an
3
+ // option (image, else icon, else color dot, else initials).
4
+ import { useState } from 'react'
5
+ import { InitialsAvatar } from '@asteby/metacore-ui/primitives'
6
+ import { resolveColorCss } from '@asteby/metacore-ui/lib'
7
+ import { DynamicIcon, isLucideIconName } from './dynamic-icon'
8
+ import type { ResolvedOption } from './use-options-resolver'
9
+
10
+ /**
11
+ * Small square thumbnail for an option's `image`. When the option has no image
12
+ * it falls back to a deterministic initials avatar (shared `InitialsAvatar`)
13
+ * derived from `name`, so an imageless reference reads as a colored badge rather
14
+ * than an empty placeholder — and rows/triggers stay aligned. `size` is in
15
+ * pixels (kept small — 20–24px — so the picker reads as a list, not a gallery).
16
+ * Inline style for the box dimensions: arbitrary Tailwind classes from a
17
+ * federated addon don't always survive the host's class scan.
18
+ */
19
+ export function OptionThumb({
20
+ image,
21
+ name,
22
+ size = 20,
23
+ }: {
24
+ image?: string | null
25
+ name?: string | null
26
+ size?: number
27
+ }) {
28
+ const [broken, setBroken] = useState(false)
29
+ const box = { width: size, height: size }
30
+ // Missing or 404 image (bare avatar filenames often 404) → initials, never
31
+ // an invisible box that looks like "no avatar".
32
+ if (!image || broken) {
33
+ return <InitialsAvatar name={name} size={size} rounded="sm" tone="neutral" />
34
+ }
35
+ // A lucide icon name stored where an image url/path is expected (the `icon`
36
+ // form widget's icon mode) → render the glyph instead of a broken <img>.
37
+ if (isLucideIconName(image)) {
38
+ return (
39
+ <span className="flex shrink-0 items-center justify-center rounded-sm bg-muted" style={box} aria-hidden>
40
+ <DynamicIcon name={image} className="size-4" />
41
+ </span>
42
+ )
43
+ }
44
+ return (
45
+ <img
46
+ src={image}
47
+ alt=""
48
+ aria-hidden
49
+ loading="lazy"
50
+ className="shrink-0 rounded-sm object-cover"
51
+ style={box}
52
+ onError={() => setBroken(true)}
53
+ />
54
+ )
55
+ }
56
+
57
+ /**
58
+ * Leading visual for an option: a photo thumbnail (FK relations with an image),
59
+ * else a declared icon, else a color dot (enum/status options with a color).
60
+ * Returns null when the option carries none, so plain text options stay plain.
61
+ */
62
+ export function OptionLead({
63
+ option,
64
+ size = 20,
65
+ }: {
66
+ option?: Pick<ResolvedOption, 'image' | 'color' | 'icon' | 'label'> | null
67
+ size?: number
68
+ }) {
69
+ if (!option) return null
70
+ if (option.image) return <OptionThumb image={option.image} name={option.label} size={size} />
71
+ if (option.icon) {
72
+ return (
73
+ <span
74
+ className="flex shrink-0 items-center justify-center"
75
+ style={{ width: size, height: size, color: option.color ? resolveColorCss(option.color) : undefined }}
76
+ aria-hidden
77
+ >
78
+ <DynamicIcon name={option.icon} className="size-4" />
79
+ </span>
80
+ )
81
+ }
82
+ if (option.color) {
83
+ return (
84
+ <span
85
+ className="shrink-0 rounded-full"
86
+ style={{ width: Math.round(size * 0.5), height: Math.round(size * 0.5), background: resolveColorCss(option.color) }}
87
+ aria-hidden
88
+ />
89
+ )
90
+ }
91
+ // No image/icon/color: an imageless reference option. Show its initials
92
+ // (shared InitialsAvatar) so it stays visually aligned with the sibling
93
+ // options that DO carry a thumbnail, rather than a blank gap.
94
+ if (option.label) return <InitialsAvatar name={option.label} size={size} rounded="sm" tone="neutral" />
95
+ return null
96
+ }