@asteby/metacore-runtime-react 49.5.0 → 49.6.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 +6 -0
- package/dist/business/customer-picker.d.ts +8 -2
- package/dist/business/customer-picker.d.ts.map +1 -1
- package/dist/business/customer-picker.js +30 -27
- 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-product-cell.d.ts +8 -10
- package/dist/business/line-product-cell.d.ts.map +1 -1
- package/dist/business/line-product-cell.js +13 -90
- package/dist/business/product-options.d.ts +41 -0
- package/dist/business/product-options.d.ts.map +1 -0
- package/dist/business/product-options.js +58 -0
- package/dist/business/product-picker.d.ts +5 -1
- package/dist/business/product-picker.d.ts.map +1 -1
- package/dist/business/product-picker.js +26 -35
- package/dist/business/relate-documents.d.ts +1 -1
- package/dist/business/relate-documents.d.ts.map +1 -1
- package/dist/business/relate-documents.js +6 -8
- package/dist/business/vehicle-picker.d.ts +8 -2
- package/dist/business/vehicle-picker.d.ts.map +1 -1
- package/dist/business/vehicle-picker.js +28 -28
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +28 -43
- package/dist/dynamic-multi-select-field.d.ts.map +1 -1
- package/dist/dynamic-multi-select-field.js +17 -5
- package/dist/dynamic-select-field.d.ts +7 -23
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +67 -161
- package/dist/entity-select.d.ts +5 -2
- package/dist/entity-select.d.ts.map +1 -1
- package/dist/entity-select.js +23 -116
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/record-picker-actions.d.ts +40 -2
- package/dist/record-picker-actions.d.ts.map +1 -1
- package/dist/record-picker-actions.js +44 -4
- package/dist/record-picker-dialog.d.ts +23 -0
- package/dist/record-picker-dialog.d.ts.map +1 -0
- package/dist/record-picker-dialog.js +37 -0
- package/dist/record-picker-option.d.ts +25 -0
- package/dist/record-picker-option.d.ts.map +1 -0
- package/dist/record-picker-option.js +55 -0
- package/dist/record-picker.d.ts +149 -0
- package/dist/record-picker.d.ts.map +1 -0
- package/dist/record-picker.js +372 -0
- package/package.json +1 -1
- package/src/__tests__/business-pickers.test.tsx +6 -3
- package/src/__tests__/document-editor-inline-selects.test.tsx +2 -1
- package/src/__tests__/document-form-dialog.test.tsx +4 -3
- package/src/__tests__/document-lines-grid.test.tsx +2 -1
- package/src/__tests__/record-picker.test.tsx +469 -0
- package/src/business/customer-picker.tsx +87 -98
- package/src/business/index.ts +1 -0
- package/src/business/line-product-cell.tsx +55 -180
- package/src/business/product-options.tsx +119 -0
- package/src/business/product-picker.tsx +63 -126
- package/src/business/relate-documents.tsx +36 -45
- package/src/business/vehicle-picker.tsx +76 -99
- package/src/dialogs/dynamic-record.tsx +44 -97
- package/src/dynamic-multi-select-field.tsx +39 -13
- package/src/dynamic-select-field.tsx +143 -368
- package/src/entity-select.tsx +53 -202
- package/src/index.ts +27 -2
- package/src/record-picker-actions.tsx +101 -8
- package/src/record-picker-dialog.tsx +80 -0
- package/src/record-picker-option.tsx +96 -0
- package/src/record-picker.tsx +971 -0
- package/dist/business/use-async-search.d.ts +0 -10
- package/dist/business/use-async-search.d.ts.map +0 -1
- package/dist/business/use-async-search.js +0 -38
- package/src/business/use-async-search.ts +0 -43
|
@@ -9,19 +9,57 @@ export interface RecordPickerActionProps {
|
|
|
9
9
|
onCreate?: () => void;
|
|
10
10
|
onEdit?: () => void;
|
|
11
11
|
disabled?: boolean;
|
|
12
|
+
/** Accessible name override for the "+" (default `Crear <label>`). */
|
|
13
|
+
createLabel?: string;
|
|
14
|
+
/** Accessible name override for the pencil (default `Editar <label>`). */
|
|
15
|
+
editLabel?: string;
|
|
16
|
+
/** `cell` → compact, borderless segment for table cells. */
|
|
17
|
+
variant?: 'field' | 'cell';
|
|
18
|
+
className?: string;
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
21
|
* Joined trailing segment: pencil when there is a value (and `onEdit`), "+"
|
|
15
22
|
* otherwise (and `onCreate`). Renders nothing when neither applies — callers
|
|
16
23
|
* should then drop {@link JOINED_TRIGGER_CLASS} from the trigger.
|
|
17
24
|
*/
|
|
18
|
-
export declare function RecordPickerAction({ hasValue, label, onCreate, onEdit, disabled }: RecordPickerActionProps): import("react").JSX.Element | null;
|
|
25
|
+
export declare function RecordPickerAction({ hasValue, label, onCreate, onEdit, disabled, createLabel, editLabel, variant, className, }: RecordPickerActionProps): import("react").JSX.Element | null;
|
|
19
26
|
/** True when a picker will render a joined action for the given state. */
|
|
20
27
|
export declare function hasRecordPickerAction(hasValue: boolean, onCreate?: unknown, onEdit?: unknown): boolean;
|
|
21
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* "Crear …" row at the foot of a cmdk `<Command>` list.
|
|
30
|
+
*
|
|
31
|
+
* @deprecated <RecordPicker> renders its own create footer (with the searched
|
|
32
|
+
* text prefilled). Kept for hand-written cmdk lists in consumers.
|
|
33
|
+
*/
|
|
22
34
|
export declare function PickerCreateItem({ label, onSelect, children }: {
|
|
23
35
|
label: string;
|
|
24
36
|
onSelect: () => void;
|
|
25
37
|
children?: ReactNode;
|
|
26
38
|
}): import("react").JSX.Element;
|
|
39
|
+
/** Detail of the `metacore:create-record` event (contract with the host). */
|
|
40
|
+
export interface RecordCreateRequest {
|
|
41
|
+
model: string;
|
|
42
|
+
/** Seed values for the new record. Keys that aren't fields of the model are ignored by the dialog. */
|
|
43
|
+
defaults?: Record<string, unknown>;
|
|
44
|
+
/** Fields rendered locked in the create modal. */
|
|
45
|
+
lockedFields?: string[];
|
|
46
|
+
onCreated?: (record: any) => void;
|
|
47
|
+
}
|
|
48
|
+
/** Detail of the `metacore:edit-record` event (contract with the host). */
|
|
49
|
+
export interface RecordEditRequest {
|
|
50
|
+
model: string;
|
|
51
|
+
recordId: string;
|
|
52
|
+
onSaved?: (record: any) => void;
|
|
53
|
+
}
|
|
54
|
+
/** Ask the host to open `model`'s create modal. No-op outside a browser. */
|
|
55
|
+
export declare function requestRecordCreate(req: RecordCreateRequest): void;
|
|
56
|
+
/** Ask the host to open the edit modal of `recordId`. No-op outside a browser. */
|
|
57
|
+
export declare function requestRecordEdit(req: RecordEditRequest): void;
|
|
58
|
+
/** Display label of a saved record: `labelField`, else name / label / title. */
|
|
59
|
+
export declare function recordLabel(rec: Record<string, unknown> | null | undefined, labelField?: string): string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Create defaults with the searched text seeded into `field` (the model's
|
|
62
|
+
* label column: name, plate…). Explicit `defaults` win over the prefill.
|
|
63
|
+
*/
|
|
64
|
+
export declare function withSearchPrefill(query: string | undefined, field: string | null | undefined, defaults?: Record<string, unknown>): Record<string, unknown> | undefined;
|
|
27
65
|
//# sourceMappingURL=record-picker-actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-picker-actions.d.ts","sourceRoot":"","sources":["../src/record-picker-actions.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record-picker-actions.d.ts","sourceRoot":"","sources":["../src/record-picker-actions.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,sCAAsC,CAAA;AAEvE,MAAM,WAAW,uBAAuB;IACpC,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAC/B,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,WAAW,EACX,SAAS,EACT,OAAiB,EACjB,SAAS,GACZ,EAAE,uBAAuB,sCA6BzB;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAEtG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAY5H;AAWD,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,sGAAsG;IACtG,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAA;CACpC;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAA;CAClC;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAGlE;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAG9D;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,UAAU,SAAS,GAAG,MAAM,GAAG,SAAS,CAOpH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAIrC"}
|
|
@@ -8,25 +8,65 @@ export const JOINED_TRIGGER_CLASS = 'rounded-r-none focus-visible:z-10';
|
|
|
8
8
|
* otherwise (and `onCreate`). Renders nothing when neither applies — callers
|
|
9
9
|
* should then drop {@link JOINED_TRIGGER_CLASS} from the trigger.
|
|
10
10
|
*/
|
|
11
|
-
export function RecordPickerAction({ hasValue, label, onCreate, onEdit, disabled }) {
|
|
11
|
+
export function RecordPickerAction({ hasValue, label, onCreate, onEdit, disabled, createLabel, editLabel, variant = 'field', className, }) {
|
|
12
12
|
const edit = hasValue && !!onEdit;
|
|
13
13
|
const create = !hasValue && !!onCreate;
|
|
14
14
|
if (!edit && !create)
|
|
15
15
|
return null;
|
|
16
|
-
const name = edit ? `Editar ${label}` : `Crear ${label}`;
|
|
16
|
+
const name = edit ? editLabel ?? `Editar ${label}` : createLabel ?? `Crear ${label}`;
|
|
17
|
+
const size = variant === 'cell' ? 'size-8 border-transparent bg-transparent shadow-none' : 'size-9';
|
|
17
18
|
return (_jsx(Button, { type: "button", variant: "outline", size: "icon", disabled: disabled, onClick: (e) => {
|
|
18
19
|
e.stopPropagation();
|
|
19
20
|
if (edit)
|
|
20
21
|
onEdit?.();
|
|
21
22
|
else
|
|
22
23
|
onCreate?.();
|
|
23
|
-
}, "aria-label": name, title: name, "data-slot": "record-picker-action", "data-action": edit ? 'edit' : 'create', className:
|
|
24
|
+
}, "aria-label": name, title: name, "data-slot": "record-picker-action", "data-action": edit ? 'edit' : 'create', className: `-ml-px ${size} shrink-0 rounded-l-none text-muted-foreground hover:text-foreground focus-visible:z-10` +
|
|
25
|
+
(className ? ` ${className}` : ''), children: edit ? _jsx(Pencil, { className: "size-4" }) : _jsx(Plus, { className: "size-4" }) }));
|
|
24
26
|
}
|
|
25
27
|
/** True when a picker will render a joined action for the given state. */
|
|
26
28
|
export function hasRecordPickerAction(hasValue, onCreate, onEdit) {
|
|
27
29
|
return hasValue ? !!onEdit : !!onCreate;
|
|
28
30
|
}
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* "Crear …" row at the foot of a cmdk `<Command>` list.
|
|
33
|
+
*
|
|
34
|
+
* @deprecated <RecordPicker> renders its own create footer (with the searched
|
|
35
|
+
* text prefilled). Kept for hand-written cmdk lists in consumers.
|
|
36
|
+
*/
|
|
30
37
|
export function PickerCreateItem({ label, onSelect, children }) {
|
|
31
38
|
return (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, {}), _jsx(CommandGroup, { children: _jsxs(CommandItem, { value: "__create__", onSelect: onSelect, "data-slot": "picker-create", className: "text-primary", children: [_jsx(Plus, { className: "mr-2 size-4 shrink-0" }), _jsx("span", { className: "truncate", children: children ?? `Crear ${label}` })] }) })] }));
|
|
32
39
|
}
|
|
40
|
+
/** Ask the host to open `model`'s create modal. No-op outside a browser. */
|
|
41
|
+
export function requestRecordCreate(req) {
|
|
42
|
+
if (typeof window === 'undefined')
|
|
43
|
+
return;
|
|
44
|
+
window.dispatchEvent(new CustomEvent('metacore:create-record', { detail: req }));
|
|
45
|
+
}
|
|
46
|
+
/** Ask the host to open the edit modal of `recordId`. No-op outside a browser. */
|
|
47
|
+
export function requestRecordEdit(req) {
|
|
48
|
+
if (typeof window === 'undefined')
|
|
49
|
+
return;
|
|
50
|
+
window.dispatchEvent(new CustomEvent('metacore:edit-record', { detail: req }));
|
|
51
|
+
}
|
|
52
|
+
/** Display label of a saved record: `labelField`, else name / label / title. */
|
|
53
|
+
export function recordLabel(rec, labelField = 'name') {
|
|
54
|
+
if (!rec)
|
|
55
|
+
return undefined;
|
|
56
|
+
for (const k of [labelField, 'name', 'label', 'title']) {
|
|
57
|
+
const v = rec[k];
|
|
58
|
+
if (v != null && v !== '')
|
|
59
|
+
return String(v);
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create defaults with the searched text seeded into `field` (the model's
|
|
65
|
+
* label column: name, plate…). Explicit `defaults` win over the prefill.
|
|
66
|
+
*/
|
|
67
|
+
export function withSearchPrefill(query, field, defaults) {
|
|
68
|
+
const q = (query ?? '').trim();
|
|
69
|
+
if (!q || !field)
|
|
70
|
+
return defaults;
|
|
71
|
+
return { [field]: q, ...(defaults ?? {}) };
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface UseRecordPickerDialogOptions {
|
|
3
|
+
/** Kernel model key. */
|
|
4
|
+
model: string;
|
|
5
|
+
/** CRUD base. Default `/data/<model>/me`; edit PUTs `<base>/<id>`. */
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
/** Field seeded with the searched text on create (null = none). Default "name". */
|
|
8
|
+
prefillField?: string | null;
|
|
9
|
+
/** Seed values for create (win over the prefill). */
|
|
10
|
+
createDefaults?: Record<string, unknown>;
|
|
11
|
+
/** Fields of `createDefaults` rendered locked on create. */
|
|
12
|
+
lockedCreateFields?: string[];
|
|
13
|
+
/** The saved record (`{ id, ...fields }`). */
|
|
14
|
+
onSaved: (record: Record<string, unknown>, kind: 'create' | 'edit') => void;
|
|
15
|
+
}
|
|
16
|
+
export interface UseRecordPickerDialogResult {
|
|
17
|
+
openCreate: (query?: string) => void;
|
|
18
|
+
openEdit: (id: string) => void;
|
|
19
|
+
/** Render it next to the picker. */
|
|
20
|
+
dialog: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare function useRecordPickerDialog({ model, endpoint, prefillField, createDefaults, lockedCreateFields, onSaved, }: UseRecordPickerDialogOptions): UseRecordPickerDialogResult;
|
|
23
|
+
//# sourceMappingURL=record-picker-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-picker-dialog.d.ts","sourceRoot":"","sources":["../src/record-picker-dialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAKhD,MAAM,WAAW,4BAA4B;IACzC,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,4DAA4D;IAC5D,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,8CAA8C;IAC9C,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,KAAK,IAAI,CAAA;CAC9E;AAED,MAAM,WAAW,2BAA2B;IACxC,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,oCAAoC;IACpC,MAAM,EAAE,SAAS,CAAA;CACpB;AAID,wBAAgB,qBAAqB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,YAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,OAAO,GACV,EAAE,4BAA4B,GAAG,2BAA2B,CAkC5D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// useRecordPickerDialog — create/edit plumbing for pickers on hand-written
|
|
3
|
+
// screens that sit outside the host's RecordCreateBridge (POS, federated
|
|
4
|
+
// panels): the in-tree, metadata-driven <CreateRecordDialog> posting to the
|
|
5
|
+
// standard org-scoped CRUD (`/data/<model>/me`). Declarative pickers use the
|
|
6
|
+
// host events instead (requestRecordCreate / requestRecordEdit).
|
|
7
|
+
//
|
|
8
|
+
// One implementation for EntitySelect, CustomerPicker and VehiclePicker: the
|
|
9
|
+
// searched text prefills the new record's label field, the saved record is
|
|
10
|
+
// handed back to select it (create) or refresh its label (edit).
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { CreateRecordDialog } from './dialogs/create-record-dialog';
|
|
13
|
+
import { useApi } from './api-context';
|
|
14
|
+
import { withSearchPrefill } from './record-picker-actions';
|
|
15
|
+
const unwrap = (res) => ((res.data?.data ?? res.data) ?? {});
|
|
16
|
+
export function useRecordPickerDialog({ model, endpoint, prefillField = 'name', createDefaults, lockedCreateFields, onSaved, }) {
|
|
17
|
+
const api = useApi();
|
|
18
|
+
const base = endpoint ?? `/data/${model}/me`;
|
|
19
|
+
const [state, setState] = useState(null);
|
|
20
|
+
const dialog = state ? (_jsx(CreateRecordDialog, { modelKey: model, open: true, onOpenChange: (o) => {
|
|
21
|
+
if (!o)
|
|
22
|
+
setState(null);
|
|
23
|
+
}, recordId: state.recordId, endpoint: base, defaults: state.recordId ? undefined : state.defaults, lockedFields: state.recordId ? undefined : lockedCreateFields, onCreate: async (data) => {
|
|
24
|
+
const rec = unwrap(await api.post(base, data));
|
|
25
|
+
onSaved(rec, 'create');
|
|
26
|
+
return rec.id != null ? { id: String(rec.id) } : undefined;
|
|
27
|
+
}, onUpdate: async (id, data) => {
|
|
28
|
+
const rec = unwrap(await api.put(`${base}/${id}`, data));
|
|
29
|
+
onSaved({ ...rec, id: rec.id ?? id }, 'edit');
|
|
30
|
+
return { id: String(id) };
|
|
31
|
+
} })) : null;
|
|
32
|
+
return {
|
|
33
|
+
openCreate: (query = '') => setState({ defaults: withSearchPrefill(query, prefillField, createDefaults) }),
|
|
34
|
+
openEdit: (id) => setState({ recordId: id }),
|
|
35
|
+
dialog,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ResolvedOption } from './use-options-resolver';
|
|
2
|
+
/**
|
|
3
|
+
* Small square thumbnail for an option's `image`. When the option has no image
|
|
4
|
+
* it falls back to a deterministic initials avatar (shared `InitialsAvatar`)
|
|
5
|
+
* derived from `name`, so an imageless reference reads as a colored badge rather
|
|
6
|
+
* than an empty placeholder — and rows/triggers stay aligned. `size` is in
|
|
7
|
+
* pixels (kept small — 20–24px — so the picker reads as a list, not a gallery).
|
|
8
|
+
* Inline style for the box dimensions: arbitrary Tailwind classes from a
|
|
9
|
+
* federated addon don't always survive the host's class scan.
|
|
10
|
+
*/
|
|
11
|
+
export declare function OptionThumb({ image, name, size, }: {
|
|
12
|
+
image?: string | null;
|
|
13
|
+
name?: string | null;
|
|
14
|
+
size?: number;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* Leading visual for an option: a photo thumbnail (FK relations with an image),
|
|
18
|
+
* else a declared icon, else a color dot (enum/status options with a color).
|
|
19
|
+
* Returns null when the option carries none, so plain text options stay plain.
|
|
20
|
+
*/
|
|
21
|
+
export declare function OptionLead({ option, size, }: {
|
|
22
|
+
option?: Pick<ResolvedOption, 'image' | 'color' | 'icon' | 'label'> | null;
|
|
23
|
+
size?: number;
|
|
24
|
+
}): import("react").JSX.Element | null;
|
|
25
|
+
//# sourceMappingURL=record-picker-option.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-picker-option.d.ts","sourceRoot":"","sources":["../src/record-picker-option.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D;;;;;;;;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,+BA4BA;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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Option visuals shared by every RecordPicker configuration: a thumbnail
|
|
3
|
+
// (image / lucide icon name / initials fallback) and the leading visual of an
|
|
4
|
+
// option (image, else icon, else color dot, else initials).
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { InitialsAvatar } from '@asteby/metacore-ui/primitives';
|
|
7
|
+
import { resolveColorCss } from '@asteby/metacore-ui/lib';
|
|
8
|
+
import { DynamicIcon, isLucideIconName } from './dynamic-icon';
|
|
9
|
+
/**
|
|
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.
|
|
17
|
+
*/
|
|
18
|
+
export function OptionThumb({ image, name, size = 20, }) {
|
|
19
|
+
const [broken, setBroken] = useState(false);
|
|
20
|
+
const box = { width: size, height: size };
|
|
21
|
+
// Missing or 404 image (bare avatar filenames often 404) → initials, never
|
|
22
|
+
// an invisible box that looks like "no avatar".
|
|
23
|
+
if (!image || broken) {
|
|
24
|
+
return _jsx(InitialsAvatar, { name: name, size: size, rounded: "sm", tone: "neutral" });
|
|
25
|
+
}
|
|
26
|
+
// A lucide icon name stored where an image url/path is expected (the `icon`
|
|
27
|
+
// form widget's icon mode) → render the glyph instead of a broken <img>.
|
|
28
|
+
if (isLucideIconName(image)) {
|
|
29
|
+
return (_jsx("span", { className: "flex shrink-0 items-center justify-center rounded-sm bg-muted", style: box, "aria-hidden": true, children: _jsx(DynamicIcon, { name: image, className: "size-4" }) }));
|
|
30
|
+
}
|
|
31
|
+
return (_jsx("img", { src: image, alt: "", "aria-hidden": true, loading: "lazy", className: "shrink-0 rounded-sm object-cover", style: box, onError: () => setBroken(true) }));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Leading visual for an option: a photo thumbnail (FK relations with an image),
|
|
35
|
+
* else a declared icon, else a color dot (enum/status options with a color).
|
|
36
|
+
* Returns null when the option carries none, so plain text options stay plain.
|
|
37
|
+
*/
|
|
38
|
+
export function OptionLead({ option, size = 20, }) {
|
|
39
|
+
if (!option)
|
|
40
|
+
return null;
|
|
41
|
+
if (option.image)
|
|
42
|
+
return _jsx(OptionThumb, { image: option.image, name: option.label, size: size });
|
|
43
|
+
if (option.icon) {
|
|
44
|
+
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" }) }));
|
|
45
|
+
}
|
|
46
|
+
if (option.color) {
|
|
47
|
+
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 }));
|
|
48
|
+
}
|
|
49
|
+
// No image/icon/color: an imageless reference option. Show its initials
|
|
50
|
+
// (shared InitialsAvatar) so it stays visually aligned with the sibling
|
|
51
|
+
// options that DO carry a thumbnail, rather than a blank gap.
|
|
52
|
+
if (option.label)
|
|
53
|
+
return _jsx(InitialsAvatar, { name: option.label, size: size, rounded: "sm", tone: "neutral" });
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { type KeyboardEvent, type ReactNode, type Ref } from 'react';
|
|
2
|
+
import { type LucideIcon } from 'lucide-react';
|
|
3
|
+
/**
|
|
4
|
+
* z-index of the portaled list. Dialogs/sheets sit at 50; the list must stay
|
|
5
|
+
* above the modal it was opened from regardless of portal order.
|
|
6
|
+
*/
|
|
7
|
+
export declare const RECORD_PICKER_Z_INDEX = 60;
|
|
8
|
+
export interface UseRecordSearchOptions {
|
|
9
|
+
/** Characters required before searching (default 2). 0 = search on empty too. */
|
|
10
|
+
minChars?: number;
|
|
11
|
+
/** Debounce in ms (default 250). */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** When false nothing is fetched and results reset (e.g. list closed). Default true. */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface UseRecordSearchResult<T> {
|
|
17
|
+
results: T[];
|
|
18
|
+
/** A request is in flight. */
|
|
19
|
+
loading: boolean;
|
|
20
|
+
/** The last request failed. */
|
|
21
|
+
error: boolean;
|
|
22
|
+
/** The debounce has not caught up with the typed text yet. */
|
|
23
|
+
pending: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Debounced async search with cancellation. `search` must be stable (useCallback). */
|
|
26
|
+
export declare function useRecordSearch<T>(query: string, search: (q: string, signal: AbortSignal) => Promise<T[]>, opts?: UseRecordSearchOptions): UseRecordSearchResult<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Stable identity for a caller's search function: always calls the latest one,
|
|
29
|
+
* so an inline (non-memoized) fetcher doesn't re-trigger the search on every
|
|
30
|
+
* render.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useLatestSearch<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
|
|
33
|
+
export interface RecordPickerItemState {
|
|
34
|
+
/** Keyboard / pointer highlight. */
|
|
35
|
+
active: boolean;
|
|
36
|
+
/** Part of the current value. */
|
|
37
|
+
selected: boolean;
|
|
38
|
+
disabled: boolean;
|
|
39
|
+
}
|
|
40
|
+
export type RecordPickerVariant = 'field' | 'cell';
|
|
41
|
+
export type RecordPickerTrigger = 'button' | 'input';
|
|
42
|
+
export interface RecordPickerProps<T> {
|
|
43
|
+
/** Stable id of an item (the stored value). */
|
|
44
|
+
getKey: (item: T) => string;
|
|
45
|
+
/** Display text of an item. */
|
|
46
|
+
getLabel: (item: T) => string;
|
|
47
|
+
/** Caller-owned list (resolver, static options, a search the caller drives). Wins over `search`. */
|
|
48
|
+
items?: readonly T[];
|
|
49
|
+
/** Async source run here (debounced, cancelled, gated by `minChars`). Must be stable. */
|
|
50
|
+
search?: (q: string, signal: AbortSignal) => Promise<T[]>;
|
|
51
|
+
/** Characters required before searching / opening the input list. Default 0 (button) / 2 (input). */
|
|
52
|
+
minChars?: number;
|
|
53
|
+
/** Debounce for `search` (ms). Default 250. */
|
|
54
|
+
debounceMs?: number;
|
|
55
|
+
/** Loading flag for caller-owned `items`. */
|
|
56
|
+
loading?: boolean;
|
|
57
|
+
/** Error flag for caller-owned `items`. */
|
|
58
|
+
error?: boolean;
|
|
59
|
+
query?: string;
|
|
60
|
+
onQueryChange?: (query: string) => void;
|
|
61
|
+
/** Button: popover open. Input: the user is actively searching. */
|
|
62
|
+
open?: boolean;
|
|
63
|
+
onOpenChange?: (open: boolean) => void;
|
|
64
|
+
multiple?: boolean;
|
|
65
|
+
/** Current value: an id (single) or ids (multiple). */
|
|
66
|
+
value?: string | number | null | readonly (string | number)[];
|
|
67
|
+
/** Resolved item(s) of the current value, for the trigger. */
|
|
68
|
+
selected?: T | readonly T[] | null;
|
|
69
|
+
/** An item was picked (in `multiple`, toggle it). */
|
|
70
|
+
onSelect: (item: T) => void;
|
|
71
|
+
/** Shows a clear (×) when there is a value. */
|
|
72
|
+
onClear?: () => void;
|
|
73
|
+
/** Chip remove in `multiple`. */
|
|
74
|
+
onRemove?: (item: T) => void;
|
|
75
|
+
/** Close after a pick. Default: true for single, false for multiple. */
|
|
76
|
+
closeOnSelect?: boolean;
|
|
77
|
+
/** Full option body override. */
|
|
78
|
+
renderItem?: (item: T, state: RecordPickerItemState) => ReactNode;
|
|
79
|
+
/** Leading visual (avatar / initials / thumbnail / icon). */
|
|
80
|
+
renderLead?: (item: T, where: 'option' | 'value') => ReactNode;
|
|
81
|
+
/** Subtitle under the label (SKU, RFC, phone…). */
|
|
82
|
+
getDescription?: (item: T) => ReactNode;
|
|
83
|
+
/** Right-aligned content (price, stock badge…). */
|
|
84
|
+
renderTrailing?: (item: T, where: 'option' | 'value') => ReactNode;
|
|
85
|
+
/** Trigger content override (button) / selected display (input). */
|
|
86
|
+
renderValue?: (selected: T | null) => ReactNode;
|
|
87
|
+
isItemDisabled?: (item: T) => boolean;
|
|
88
|
+
/** Check mark column. Default: true in button mode. */
|
|
89
|
+
showCheck?: boolean;
|
|
90
|
+
placeholder?: string;
|
|
91
|
+
searchPlaceholder?: string;
|
|
92
|
+
emptyText?: ReactNode;
|
|
93
|
+
loadingText?: ReactNode;
|
|
94
|
+
errorText?: ReactNode;
|
|
95
|
+
/** Shown below `minChars` in button mode. */
|
|
96
|
+
minCharsText?: ReactNode;
|
|
97
|
+
clearLabel?: string;
|
|
98
|
+
/** Accessible name of the listbox. */
|
|
99
|
+
listLabel?: string;
|
|
100
|
+
/** Human name of the referenced model, for "Crear …" / "Editar …". */
|
|
101
|
+
entityLabel?: string;
|
|
102
|
+
/** "+" (empty) and list footer. Receives the searched text to prefill. */
|
|
103
|
+
onCreate?: (query: string) => void;
|
|
104
|
+
/** Pencil (with a value). */
|
|
105
|
+
onEdit?: () => void;
|
|
106
|
+
createLabel?: string;
|
|
107
|
+
editLabel?: string;
|
|
108
|
+
/** When the "Crear …" footer shows: always (default), only with no hits, or never. */
|
|
109
|
+
createFooter?: 'always' | 'empty' | 'never';
|
|
110
|
+
createFooterLabel?: (query: string) => ReactNode;
|
|
111
|
+
/** Hide the joined +/pencil segment (keeps the footer). */
|
|
112
|
+
hideJoinedAction?: boolean;
|
|
113
|
+
trigger?: RecordPickerTrigger;
|
|
114
|
+
variant?: RecordPickerVariant;
|
|
115
|
+
/** Input mode: the typed text IS the value (never shows a selected display). */
|
|
116
|
+
freeText?: boolean;
|
|
117
|
+
icon?: LucideIcon;
|
|
118
|
+
id?: string;
|
|
119
|
+
ariaLabel?: string;
|
|
120
|
+
disabled?: boolean;
|
|
121
|
+
/** Locked display of the current value (no list, no actions). */
|
|
122
|
+
readOnly?: boolean;
|
|
123
|
+
invalid?: boolean;
|
|
124
|
+
/** data-slot of the root (keeps legacy hooks like `dynamic-select`). */
|
|
125
|
+
slot?: string;
|
|
126
|
+
/** data-slot of the input-mode anchor. */
|
|
127
|
+
anchorSlot?: string;
|
|
128
|
+
/** data-slot of the portaled list container. */
|
|
129
|
+
contentSlot?: string;
|
|
130
|
+
className?: string;
|
|
131
|
+
triggerClassName?: string;
|
|
132
|
+
/** Extra attributes for the trigger / text box (data-*, …). */
|
|
133
|
+
triggerProps?: Record<string, unknown>;
|
|
134
|
+
inputRef?: Ref<HTMLInputElement>;
|
|
135
|
+
/** Floor for the list width (CSS length). Default 14rem. */
|
|
136
|
+
minListWidth?: string;
|
|
137
|
+
/** Rendered after the joined group (e.g. a scan button). */
|
|
138
|
+
after?: ReactNode;
|
|
139
|
+
/** Rendered under the control (badges, hints). */
|
|
140
|
+
below?: ReactNode;
|
|
141
|
+
/** Rendered at the top of the list. */
|
|
142
|
+
beforeList?: ReactNode;
|
|
143
|
+
/** Runs first on every key in the text box; `preventDefault()` to take over. */
|
|
144
|
+
onInputKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
145
|
+
/** Keys the picker did not consume (closed list, Enter with no hits…). */
|
|
146
|
+
onUnhandledKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
147
|
+
}
|
|
148
|
+
export declare function RecordPicker<T>(props: RecordPickerProps<T>): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
|
|
149
|
+
//# sourceMappingURL=record-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-picker.d.ts","sourceRoot":"","sources":["../src/record-picker.tsx"],"names":[],"mappings":"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"}
|