@asteby/metacore-runtime-react 49.7.0 → 49.9.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 +25 -0
- package/dist/business/document-editor-model.d.ts +27 -2
- package/dist/business/document-editor-model.d.ts.map +1 -1
- package/dist/business/document-editor-model.js +53 -3
- package/dist/business/document-editor.d.ts +7 -2
- package/dist/business/document-editor.d.ts.map +1 -1
- package/dist/business/document-editor.js +121 -22
- package/dist/business/document-lines.d.ts +21 -1
- package/dist/business/document-lines.d.ts.map +1 -1
- package/dist/business/document-lines.js +69 -7
- package/dist/business/index.d.ts +1 -0
- package/dist/business/index.d.ts.map +1 -1
- package/dist/business/index.js +1 -0
- package/dist/business/line-items-editor.d.ts +21 -1
- package/dist/business/line-items-editor.d.ts.map +1 -1
- package/dist/business/line-items-editor.js +74 -15
- package/dist/business/line-items.d.ts +28 -0
- package/dist/business/line-items.d.ts.map +1 -1
- package/dist/business/line-items.js +1 -0
- package/dist/business/line-product-cell.d.ts +6 -0
- package/dist/business/line-product-cell.d.ts.map +1 -1
- package/dist/business/line-product-cell.js +9 -3
- package/dist/business/product-display.d.ts +18 -0
- package/dist/business/product-display.d.ts.map +1 -0
- package/dist/business/product-display.js +40 -0
- package/dist/business/product-options.d.ts +6 -1
- package/dist/business/product-options.d.ts.map +1 -1
- package/dist/business/product-options.js +11 -2
- package/dist/business/product-search.d.ts +8 -0
- package/dist/business/product-search.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +1 -1
- package/dist/dynamic-select-field.d.ts +7 -1
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +18 -4
- package/dist/field-grid.d.ts +4 -3
- package/dist/field-grid.d.ts.map +1 -1
- package/dist/field-grid.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/option-display.d.ts +68 -0
- package/dist/option-display.d.ts.map +1 -0
- package/dist/option-display.js +135 -0
- package/dist/query-batch.d.ts +1 -1
- package/dist/query-batch.d.ts.map +1 -1
- package/dist/query-batch.js +3 -1
- package/dist/record-picker.d.ts +2 -0
- package/dist/record-picker.d.ts.map +1 -1
- package/dist/record-picker.js +11 -4
- package/dist/use-options-resolver.d.ts +16 -1
- package/dist/use-options-resolver.d.ts.map +1 -1
- package/dist/use-options-resolver.js +38 -6
- package/package.json +1 -1
- package/src/__tests__/document-editor-inline-selects.test.tsx +11 -4
- package/src/__tests__/line-row-create-edit.test.tsx +312 -0
- package/src/__tests__/option-display.test.tsx +241 -0
- package/src/business/document-editor-model.ts +60 -2
- package/src/business/document-editor.tsx +143 -19
- package/src/business/document-lines.ts +79 -7
- package/src/business/index.ts +1 -0
- package/src/business/line-items-editor.tsx +158 -30
- package/src/business/line-items.ts +31 -0
- package/src/business/line-product-cell.tsx +32 -5
- package/src/business/product-display.ts +62 -0
- package/src/business/product-options.tsx +11 -2
- package/src/business/product-search.ts +8 -0
- package/src/dialogs/dynamic-record.tsx +1 -1
- package/src/dynamic-select-field.tsx +45 -1
- package/src/field-grid.tsx +5 -4
- package/src/index.ts +10 -0
- package/src/option-display.tsx +255 -0
- package/src/query-batch.ts +2 -0
- package/src/record-picker.tsx +16 -3
- package/src/use-options-resolver.ts +49 -4
package/src/record-picker.tsx
CHANGED
|
@@ -268,6 +268,8 @@ export interface RecordPickerProps<T> {
|
|
|
268
268
|
inputRef?: Ref<HTMLInputElement>
|
|
269
269
|
/** Floor for the list width (CSS length). Default 14rem. */
|
|
270
270
|
minListWidth?: string
|
|
271
|
+
/** Ceiling for the list width (CSS length), e.g. under a very wide trigger. Default: none. */
|
|
272
|
+
maxListWidth?: string
|
|
271
273
|
/** Rendered after the joined group (e.g. a scan button). */
|
|
272
274
|
after?: ReactNode
|
|
273
275
|
/** Rendered under the control (badges, hints). */
|
|
@@ -351,6 +353,7 @@ export function RecordPicker<T>(props: RecordPickerProps<T>) {
|
|
|
351
353
|
triggerProps,
|
|
352
354
|
inputRef,
|
|
353
355
|
minListWidth = '14rem',
|
|
356
|
+
maxListWidth,
|
|
354
357
|
after,
|
|
355
358
|
below,
|
|
356
359
|
beforeList,
|
|
@@ -555,7 +558,15 @@ export function RecordPicker<T>(props: RecordPickerProps<T>) {
|
|
|
555
558
|
)
|
|
556
559
|
|
|
557
560
|
const list = (
|
|
558
|
-
<div
|
|
561
|
+
<div
|
|
562
|
+
className="overflow-y-auto overflow-x-hidden overscroll-contain"
|
|
563
|
+
data-slot="record-picker-list"
|
|
564
|
+
// Never taller than the room Radix measured on the side it opened
|
|
565
|
+
// (flipped above the field near the bottom of a modal it used to
|
|
566
|
+
// spill past the top of the viewport): 18rem at most, minus the
|
|
567
|
+
// search box in button mode; the list scrolls inside.
|
|
568
|
+
style={{ maxHeight: `min(18rem, calc(var(--radix-popover-content-available-height, 18rem) - ${isInput ? '2px' : '2.75rem'}))` }}
|
|
569
|
+
>
|
|
559
570
|
{beforeList}
|
|
560
571
|
{status}
|
|
561
572
|
<ul
|
|
@@ -596,7 +607,7 @@ export function RecordPicker<T>(props: RecordPickerProps<T>) {
|
|
|
596
607
|
) : null}
|
|
597
608
|
{renderLead?.(item, 'option')}
|
|
598
609
|
<span className="flex min-w-0 flex-1 flex-col">
|
|
599
|
-
<span className="
|
|
610
|
+
<span className="line-clamp-2 break-words">{getLabel(item)}</span>
|
|
600
611
|
{desc != null && desc !== '' ? (
|
|
601
612
|
<span className="truncate text-xs text-muted-foreground">{desc}</span>
|
|
602
613
|
) : null}
|
|
@@ -636,7 +647,9 @@ export function RecordPicker<T>(props: RecordPickerProps<T>) {
|
|
|
636
647
|
)
|
|
637
648
|
|
|
638
649
|
const contentStyle = {
|
|
639
|
-
width:
|
|
650
|
+
width: maxListWidth
|
|
651
|
+
? `min(max(var(--radix-popover-trigger-width), ${minListWidth}), ${maxListWidth})`
|
|
652
|
+
: `max(var(--radix-popover-trigger-width), ${minListWidth})`,
|
|
640
653
|
maxWidth: 'calc(100vw - 1rem)',
|
|
641
654
|
zIndex: RECORD_PICKER_Z_INDEX,
|
|
642
655
|
}
|
|
@@ -14,6 +14,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
|
|
14
14
|
import { useApi } from './api-context'
|
|
15
15
|
import { applyOptionFilter, type OptionFilterRule } from './option-filter'
|
|
16
16
|
import { loadQueryPart, optionsBatchToken, optionsModelFromUrl } from './query-batch'
|
|
17
|
+
import type { OptionDisplayData } from './option-display'
|
|
17
18
|
|
|
18
19
|
export interface ResolvedOption {
|
|
19
20
|
/** Canonical id (server-side primary key). */
|
|
@@ -28,6 +29,12 @@ export interface ResolvedOption {
|
|
|
28
29
|
image?: string | null
|
|
29
30
|
color?: string | null
|
|
30
31
|
icon?: string | null
|
|
32
|
+
/**
|
|
33
|
+
* Presentación declarativa resuelta por el kernel (manifest v3
|
|
34
|
+
* `option_display`): título, subtítulo, imagen, métricas a la derecha con
|
|
35
|
+
* tono (precio, stock…) y badges. Ausente en hosts/modelos sin display.
|
|
36
|
+
*/
|
|
37
|
+
display?: OptionDisplayData | null
|
|
31
38
|
/**
|
|
32
39
|
* Campos extra del payload (precio, costo, sku, tasa) que no son decoración.
|
|
33
40
|
* Solo se llena cuando el option trae números o strings además de id/label.
|
|
@@ -98,6 +105,12 @@ export interface UseOptionsResolverArgs {
|
|
|
98
105
|
optionFilter?: OptionFilterRule[]
|
|
99
106
|
/** Current selection: never hidden by `optionFilter`, so its label survives. */
|
|
100
107
|
keepValue?: unknown
|
|
108
|
+
/**
|
|
109
|
+
* Picker context for the option display's contributed metrics, sent as
|
|
110
|
+
* `?ctx.<key>=<value>` (e.g. `{ warehouse_id }` of the document being
|
|
111
|
+
* edited → stock in that warehouse). Empty values are dropped.
|
|
112
|
+
*/
|
|
113
|
+
context?: Record<string, string | null | undefined>
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
export interface UseOptionsResolverResult {
|
|
@@ -157,8 +170,34 @@ export function optionsRequestKey(
|
|
|
157
170
|
query: string,
|
|
158
171
|
limit: number | undefined,
|
|
159
172
|
filter: string | undefined,
|
|
173
|
+
context?: Record<string, string>,
|
|
160
174
|
): string {
|
|
161
|
-
|
|
175
|
+
const parts = [scope, url, field, query, String(limit ?? ''), filter ?? '']
|
|
176
|
+
const ctx = contextKey(context)
|
|
177
|
+
if (ctx) parts.push(ctx)
|
|
178
|
+
return parts.join('\n')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Normalized picker context: string values only, empty dropped (null when none). */
|
|
182
|
+
export function normalizeOptionsContext(
|
|
183
|
+
context: Record<string, string | null | undefined> | undefined,
|
|
184
|
+
): Record<string, string> | undefined {
|
|
185
|
+
if (!context) return undefined
|
|
186
|
+
const out: Record<string, string> = {}
|
|
187
|
+
for (const [k, v] of Object.entries(context)) {
|
|
188
|
+
if (v == null) continue
|
|
189
|
+
const s = String(v).trim()
|
|
190
|
+
if (s) out[k] = s
|
|
191
|
+
}
|
|
192
|
+
return Object.keys(out).length > 0 ? out : undefined
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function contextKey(context: Record<string, string> | undefined): string {
|
|
196
|
+
if (!context) return ''
|
|
197
|
+
return Object.keys(context)
|
|
198
|
+
.sort()
|
|
199
|
+
.map((k) => `${k}=${context[k]}`)
|
|
200
|
+
.join('&')
|
|
162
201
|
}
|
|
163
202
|
|
|
164
203
|
/** Org + branch of the host session: part of every options cache key. */
|
|
@@ -203,6 +242,8 @@ export function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsReso
|
|
|
203
242
|
optionFilter,
|
|
204
243
|
keepValue,
|
|
205
244
|
} = args
|
|
245
|
+
const context = normalizeOptionsContext(args.context)
|
|
246
|
+
const ctxSig = contextKey(context)
|
|
206
247
|
|
|
207
248
|
const api = useApi()
|
|
208
249
|
const [options, setOptions] = useState<ResolvedOption[]>([])
|
|
@@ -251,6 +292,7 @@ export function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsReso
|
|
|
251
292
|
query ?? '',
|
|
252
293
|
limit,
|
|
253
294
|
filterValue,
|
|
295
|
+
context,
|
|
254
296
|
)
|
|
255
297
|
if (refreshKey !== seenRefresh.current) {
|
|
256
298
|
seenRefresh.current = refreshKey
|
|
@@ -278,13 +320,14 @@ export function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsReso
|
|
|
278
320
|
if (query) params.q = query
|
|
279
321
|
if (typeof limit === 'number' && limit > 0) params.limit = limit
|
|
280
322
|
if (filterValue) params.filter_value = filterValue
|
|
323
|
+
for (const [k, v] of Object.entries(context ?? {})) params[`ctx.${k}`] = v
|
|
281
324
|
const model = optionsModelFromUrl(url)
|
|
282
325
|
pending = (async () => {
|
|
283
326
|
if (model) {
|
|
284
327
|
try {
|
|
285
328
|
const part = await loadQueryPart(
|
|
286
329
|
api,
|
|
287
|
-
optionsBatchToken(model, effectiveField, query ?? '', limit, filterValue),
|
|
330
|
+
optionsBatchToken(model, effectiveField, query ?? '', limit, filterValue, context),
|
|
288
331
|
)
|
|
289
332
|
if (!part.success) {
|
|
290
333
|
throw new Error(part.message || 'options resolver: unsuccessful response')
|
|
@@ -340,7 +383,8 @@ export function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsReso
|
|
|
340
383
|
return () => {
|
|
341
384
|
cancelled = true
|
|
342
385
|
}
|
|
343
|
-
|
|
386
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- context is keyed by ctxSig
|
|
387
|
+
}, [api, url, effectiveField, query, limit, enabled, filterValue, refreshKey, ctxSig])
|
|
344
388
|
|
|
345
389
|
// Rules arrive as a fresh array each render; key on their content.
|
|
346
390
|
const filterKey = optionFilter && optionFilter.length > 0 ? JSON.stringify(optionFilter) : ''
|
|
@@ -364,7 +408,7 @@ export function useOptionsResolver(args: UseOptionsResolverArgs): UseOptionsReso
|
|
|
364
408
|
* id/value and label/name fields for legacy parity — we accept either
|
|
365
409
|
* and surface a stable shape downstream.
|
|
366
410
|
*/
|
|
367
|
-
const OPTION_KNOWN_KEYS = new Set(['id', 'value', 'label', 'name', 'description', 'image', 'color', 'icon'])
|
|
411
|
+
const OPTION_KNOWN_KEYS = new Set(['id', 'value', 'label', 'name', 'description', 'image', 'color', 'icon', 'display'])
|
|
368
412
|
|
|
369
413
|
export function projectOption(raw: any): ResolvedOption {
|
|
370
414
|
const id = raw?.id ?? raw?.value ?? ''
|
|
@@ -385,6 +429,7 @@ export function projectOption(raw: any): ResolvedOption {
|
|
|
385
429
|
image: raw?.image ?? null,
|
|
386
430
|
color: raw?.color ?? null,
|
|
387
431
|
icon: raw?.icon ?? null,
|
|
432
|
+
...(raw?.display && typeof raw.display === 'object' ? { display: raw.display as OptionDisplayData } : {}),
|
|
388
433
|
...(Object.keys(meta).length > 0 ? { meta } : {}),
|
|
389
434
|
}
|
|
390
435
|
}
|