@asteby/metacore-runtime-react 34.1.0 → 35.1.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 +7 -86
- package/dist/action-modal-dispatcher.d.ts +4 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +102 -23
- package/dist/addon-loader.d.ts +1 -1
- package/dist/addon-loader.d.ts.map +1 -1
- package/dist/addon-loader.js +8 -1
- package/dist/color-picker-field.d.ts +13 -0
- package/dist/color-picker-field.d.ts.map +1 -0
- package/dist/color-picker-field.js +181 -0
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +99 -61
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +23 -8
- package/dist/dynamic-form.d.ts +1 -0
- package/dist/dynamic-form.d.ts.map +1 -1
- package/dist/dynamic-form.js +3 -1
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +6 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/permissions-manager.d.ts +5 -0
- package/dist/permissions-manager.d.ts.map +1 -1
- package/dist/permissions-manager.js +67 -23
- package/dist/use-print-document.d.ts +10 -1
- package/dist/use-print-document.d.ts.map +1 -1
- package/dist/use-print-document.js +28 -1
- package/package.json +1 -1
- package/src/__tests__/color-picker-field.test.tsx +18 -0
- package/src/__tests__/filename-from-disposition.test.ts +30 -0
- package/src/__tests__/prefill-scalar-from-record.test.ts +37 -0
- package/src/__tests__/record-detail-display.test.tsx +30 -0
- package/src/action-modal-dispatcher.tsx +110 -22
- package/src/addon-loader.tsx +7 -1
- package/src/color-picker-field.tsx +296 -0
- package/src/dialogs/dynamic-record.tsx +139 -65
- package/src/dynamic-columns.tsx +28 -12
- package/src/dynamic-form.tsx +9 -1
- package/src/dynamic-select-field.tsx +7 -3
- package/src/index.ts +2 -0
- package/src/permissions-manager.tsx +98 -39
- package/src/use-print-document.ts +38 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,88 +1,16 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 35.1.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- 431f813: Role chips: Lucide icon + modern HSL color picker (popover SV plane / hue / hex) in PermissionsManager; ColorPickerField also used for DynamicForm color fields.
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- 7bff072: Locale-aware FilePickButton: hide native file chrome so labels follow app language (es/en). UploadField and ImportDialog use it.
|
|
14
|
-
|
|
15
|
-
## 34.0.1
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- 45b5f91: Fix relation/brand thumbnails (RelationCell) being cropped: switch from
|
|
20
|
-
`object-cover` to `object-contain` on a padded neutral background so
|
|
21
|
-
wide/rectangular logos (brand, supplier, category images) render in full
|
|
22
|
-
instead of clipped to a square.
|
|
23
|
-
|
|
24
|
-
## 34.0.0
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- Updated dependencies [5c51b7f]
|
|
29
|
-
- @asteby/metacore-ui@2.16.0
|
|
30
|
-
|
|
31
|
-
## 33.1.1
|
|
32
|
-
|
|
33
|
-
### Patch Changes
|
|
34
|
-
|
|
35
|
-
- 921a74c: Crear una categoría (u otro registro) desde el "+" de un modal ya no
|
|
36
|
-
valida el formulario de atrás. Cada diálogo tiene su propio `form` id, y
|
|
37
|
-
el toast de validación lista los mensajes del validator.
|
|
38
|
-
|
|
39
|
-
## 33.1.0
|
|
40
|
-
|
|
41
|
-
### Minor Changes
|
|
42
|
-
|
|
43
|
-
- 9eab4e0: `EntitySelect` now accepts `createDefaults` and `lockedCreateFields` to seed
|
|
44
|
-
and lock fields in its inline "+" create dialog. Motivating case: the POS
|
|
45
|
-
vehicle picker creating a vehicle for an already-selected customer — the
|
|
46
|
-
customer field should come pre-filled and not be changeable from that
|
|
47
|
-
context, instead of the user having to search/select it again (and risking
|
|
48
|
-
attaching the vehicle to the wrong customer).
|
|
49
|
-
|
|
50
|
-
- `createDefaults?: Record<string, unknown>` — seeds the create dialog's
|
|
51
|
-
form (passed through as `CreateRecordDialog.defaults`).
|
|
52
|
-
- `lockedCreateFields?: string[]` — keys from `createDefaults` that render
|
|
53
|
-
visible-but-disabled on create instead of editable (passed through as the
|
|
54
|
-
new `CreateRecordDialog.lockedFields` / `DynamicRecordDialog.lockedFields`).
|
|
55
|
-
|
|
56
|
-
New `lockedFields` support in `DynamicRecordDialog`/`CreateRecordDialog`:
|
|
57
|
-
unlike a `readonly` field (excluded entirely on create), a locked field stays
|
|
58
|
-
visible so the user sees what value is being submitted, rendered through the
|
|
59
|
-
same `ReadonlyEditField` used for edit-mode readonly fields. Also fixes
|
|
60
|
-
`ReadonlyEditField` for relation/`Ref` fields (readonly-on-edit and now
|
|
61
|
-
locked-on-create): it previously rendered the raw foreign id, and now
|
|
62
|
-
resolves and shows the related record's label like `RelationViewValue`
|
|
63
|
-
already does in view mode.
|
|
64
|
-
|
|
65
|
-
## 33.0.0
|
|
66
|
-
|
|
67
|
-
### Patch Changes
|
|
68
|
-
|
|
69
|
-
- Updated dependencies [e74975a]
|
|
70
|
-
- @asteby/metacore-sdk@3.6.0
|
|
71
|
-
|
|
72
|
-
## 32.1.0
|
|
73
|
-
|
|
74
|
-
### Minor Changes
|
|
75
|
-
|
|
76
|
-
- fa25f99: Laravel-style field validator: localize 422 `{errors:{field:[{code,params}]}}`
|
|
77
|
-
into the operator's language (es/en catalogs, overridable via `validation.*`),
|
|
78
|
-
map keys to translated labels, and pre-flight the same regex/min/max/custom
|
|
79
|
-
rules the kernel enforces — no more generic "validation failed" / `[object Object]`.
|
|
80
|
-
|
|
81
|
-
## 32.0.0
|
|
9
|
+
## 35.0.0
|
|
82
10
|
|
|
83
11
|
### Minor Changes
|
|
84
12
|
|
|
85
|
-
-
|
|
13
|
+
- Addon fiber hot-swap without a page reload: Registry.scope/unbind, Plugin.register
|
|
86
14
|
may return a Disposable, AddonLoader re-registers federation remotes when `?v=`
|
|
87
15
|
changes, and purgeAddonFrontendCache drops only that addon's SW cache (L1).
|
|
88
16
|
Hosts call acknowledgeRunningVersion after a successful swap instead of
|
|
@@ -90,18 +18,11 @@
|
|
|
90
18
|
|
|
91
19
|
### Patch Changes
|
|
92
20
|
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
- Updated dependencies [5a932ed]
|
|
21
|
+
- 7Leguas wave: registry action bridge, dynamic record/columns display, addon-loader error UX, notifications dropdown polish.
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- Updated dependencies
|
|
97
24
|
- @asteby/metacore-sdk@3.5.0
|
|
98
25
|
|
|
99
|
-
## 31.1.1
|
|
100
|
-
|
|
101
|
-
### Patch Changes
|
|
102
|
-
|
|
103
|
-
- 00caf65: PermissionsManager: add "Otorgar todo"/"Quitar todo" buttons (all modules, not just the open one) and a "Otorgar todos los permisos" checkbox when creating a role — grants full catalog access immediately instead of requiring every module to be checked one by one.
|
|
104
|
-
|
|
105
26
|
## 31.1.0
|
|
106
27
|
|
|
107
28
|
### Minor Changes
|
|
@@ -20,5 +20,9 @@ export interface PrefillSpec {
|
|
|
20
20
|
export declare function isPrefillSpec(v: unknown): v is PrefillSpec;
|
|
21
21
|
export declare function applyPrefillLock(field: ActionFieldDef): ActionFieldDef;
|
|
22
22
|
export declare function buildPrefillRows(spec: PrefillSpec, record: any): Array<Record<string, any>>;
|
|
23
|
+
export declare function unwrapRecordScalar(value: unknown): unknown;
|
|
24
|
+
export declare function readRecordPath(record: any, path: string): unknown;
|
|
25
|
+
/** Scalar seed for one action field from the row being acted on. */
|
|
26
|
+
export declare function scalarDefaultFromRecord(field: ActionFieldDef, record: any): unknown;
|
|
23
27
|
export declare function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess, }: ActionModalProps): import("react").JSX.Element | null;
|
|
24
28
|
//# sourceMappingURL=action-modal-dispatcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAoElB"}
|
|
1
|
+
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASjE;AAUD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAgBnF;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAoElB"}
|
|
@@ -89,6 +89,68 @@ export function buildPrefillRows(spec, record) {
|
|
|
89
89
|
}
|
|
90
90
|
return rows;
|
|
91
91
|
}
|
|
92
|
+
// ---- scalar prefill from the acted-on record --------------------------------
|
|
93
|
+
//
|
|
94
|
+
// Row actions (stamp / refactura / cancel-with-reason) should open with the
|
|
95
|
+
// current record's values, not empty selects. Manifest declares explicit paths
|
|
96
|
+
// via `defaultFromRecord` (string or string[] fallback chain). When omitted,
|
|
97
|
+
// the field seeds from record[field.key] if present.
|
|
98
|
+
export function unwrapRecordScalar(value) {
|
|
99
|
+
if (value === null || value === undefined)
|
|
100
|
+
return value;
|
|
101
|
+
if (typeof value !== 'object' || value instanceof Date)
|
|
102
|
+
return value;
|
|
103
|
+
if (Array.isArray(value))
|
|
104
|
+
return value;
|
|
105
|
+
const o = value;
|
|
106
|
+
if ('value' in o && (typeof o.value === 'string' || typeof o.value === 'number')) {
|
|
107
|
+
return o.value;
|
|
108
|
+
}
|
|
109
|
+
if ('id' in o && (typeof o.id === 'string' || typeof o.id === 'number')) {
|
|
110
|
+
return o.id;
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
export function readRecordPath(record, path) {
|
|
115
|
+
if (!record || !path)
|
|
116
|
+
return undefined;
|
|
117
|
+
const parts = path.split('.');
|
|
118
|
+
let cur = record;
|
|
119
|
+
for (const p of parts) {
|
|
120
|
+
if (cur == null || typeof cur !== 'object')
|
|
121
|
+
return undefined;
|
|
122
|
+
cur = cur[p];
|
|
123
|
+
}
|
|
124
|
+
return unwrapRecordScalar(cur);
|
|
125
|
+
}
|
|
126
|
+
function defaultFromRecordSpec(field) {
|
|
127
|
+
const f = field;
|
|
128
|
+
return f.defaultFromRecord ?? f.default_from_record;
|
|
129
|
+
}
|
|
130
|
+
/** Scalar seed for one action field from the row being acted on. */
|
|
131
|
+
export function scalarDefaultFromRecord(field, record) {
|
|
132
|
+
if (!record)
|
|
133
|
+
return undefined;
|
|
134
|
+
const spec = defaultFromRecordSpec(field);
|
|
135
|
+
if (typeof spec === 'string') {
|
|
136
|
+
const v = readRecordPath(record, spec);
|
|
137
|
+
if (v !== undefined && v !== null && v !== '')
|
|
138
|
+
return v;
|
|
139
|
+
}
|
|
140
|
+
else if (Array.isArray(spec)) {
|
|
141
|
+
for (const path of spec) {
|
|
142
|
+
const v = readRecordPath(record, path);
|
|
143
|
+
if (v !== undefined && v !== null && v !== '')
|
|
144
|
+
return v;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (field.key) {
|
|
148
|
+
const v = readRecordPath(record, field.key);
|
|
149
|
+
if (v !== undefined && v !== null && v !== '')
|
|
150
|
+
return v;
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
92
154
|
export function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess, }) {
|
|
93
155
|
const CustomComponent = useMemo(() => getActionComponent(model, action.key), [model, action.key]);
|
|
94
156
|
if (CustomComponent) {
|
|
@@ -191,13 +253,16 @@ function selectPreviewColumns(columns, record) {
|
|
|
191
253
|
lineItemRows.push({ col, value, lineItems: true });
|
|
192
254
|
continue;
|
|
193
255
|
}
|
|
194
|
-
// Relación (ref / search / dynamic_select / *_id): solo si el sibling
|
|
195
|
-
// resolvió a un label legible;
|
|
256
|
+
// Relación (ref / search / dynamic_select / uuid *_id): solo si el sibling
|
|
257
|
+
// resolvió a un label legible; text `*_id` (external_id) no es FK.
|
|
258
|
+
const t = String(col.type || '').toLowerCase();
|
|
196
259
|
const isRelation = !!getFieldRef(col) ||
|
|
197
260
|
col.type === 'search' ||
|
|
198
261
|
col.type === 'relation' ||
|
|
199
262
|
col.widget === 'dynamic_select' ||
|
|
200
|
-
(typeof col.key === 'string' &&
|
|
263
|
+
(typeof col.key === 'string' &&
|
|
264
|
+
col.key.endsWith('_id') &&
|
|
265
|
+
(t === 'uuid' || t === 'search' || t === 'relation' || t === 'dynamic_select' || t === 'belongs_to'));
|
|
201
266
|
if (isRelation) {
|
|
202
267
|
const sib = relationSiblingValue(col, record);
|
|
203
268
|
const label = typeof sib === 'string' ? sib : objectLabel(sib);
|
|
@@ -361,20 +426,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
361
426
|
}, [open, model, recordId, api]);
|
|
362
427
|
useEffect(() => {
|
|
363
428
|
if (open && action.fields) {
|
|
364
|
-
|
|
365
|
-
for (const field of action.fields) {
|
|
366
|
-
if (isLineItemsField(field)) {
|
|
367
|
-
const dv = lineItemsDefault(field);
|
|
368
|
-
defaults[field.key] = isPrefillSpec(dv)
|
|
369
|
-
? buildPrefillRows(dv, record)
|
|
370
|
-
: Array.isArray(dv)
|
|
371
|
-
? dv
|
|
372
|
-
: [];
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
defaults[field.key] = field.defaultValue ?? (field.type === 'boolean' ? false : '');
|
|
376
|
-
}
|
|
377
|
-
setFormData(defaults);
|
|
429
|
+
setFormData(buildFieldDefaults(action.fields, record));
|
|
378
430
|
setFieldErrors({});
|
|
379
431
|
}
|
|
380
432
|
}, [open, action.fields, record]);
|
|
@@ -439,18 +491,19 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
439
491
|
// unlike an arbitrary Tailwind class that the host's scan may drop), capped
|
|
440
492
|
// to the viewport so it stays responsive on phones.
|
|
441
493
|
const hasLineItems = useMemo(() => (action.fields ?? []).some(isLineItemsField), [action.fields]);
|
|
494
|
+
const embedRelations = hasLineItems || !!action.embedRelations;
|
|
442
495
|
const explicitWidth = action.modalWidth;
|
|
443
496
|
const widthPx = explicitWidth != null
|
|
444
497
|
? (typeof explicitWidth === 'number' ? `${explicitWidth}px` : explicitWidth)
|
|
445
498
|
: hasLineItems
|
|
446
499
|
? '820px'
|
|
447
500
|
: undefined;
|
|
448
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), action.confirmMessage && _jsx(DialogDescription, { children: action.confirmMessage })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsxs(FieldGrid, { children: [action.fields?.map((field) => {
|
|
501
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), action.confirmMessage && _jsx(DialogDescription, { children: tl(action.confirmMessage) })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsxs(FieldGrid, { children: [action.fields?.map((field) => {
|
|
449
502
|
const fullWidth = isLineItemsField(field) ||
|
|
450
503
|
resolveWidget(field) === 'textarea' ||
|
|
451
504
|
resolveWidget(field) === 'richtext';
|
|
452
|
-
return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
|
|
453
|
-
}), relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true }) }))] }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] })] }) }));
|
|
505
|
+
return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
|
|
506
|
+
}), embedRelations && relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true }) }))] }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] })] }) }));
|
|
454
507
|
}
|
|
455
508
|
// buildFieldDefaults seeds formData for a set of action fields, honoring the
|
|
456
509
|
// same line-items prefill spec + boolean/empty rules GenericActionModal uses, so
|
|
@@ -467,6 +520,11 @@ function buildFieldDefaults(fields, record) {
|
|
|
467
520
|
: [];
|
|
468
521
|
continue;
|
|
469
522
|
}
|
|
523
|
+
const fromRecord = scalarDefaultFromRecord(field, record);
|
|
524
|
+
if (fromRecord !== undefined && fromRecord !== null && fromRecord !== '') {
|
|
525
|
+
defaults[field.key] = fromRecord;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
470
528
|
defaults[field.key] = field.defaultValue ?? (field.type === 'boolean' ? false : '');
|
|
471
529
|
}
|
|
472
530
|
return defaults;
|
|
@@ -561,15 +619,36 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
|
|
|
561
619
|
const fullWidth = isLineItemsField(field) ||
|
|
562
620
|
resolveWidget(field) === 'textarea' ||
|
|
563
621
|
resolveWidget(field) === 'richtext';
|
|
564
|
-
return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
|
|
622
|
+
return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record)] }, field.key));
|
|
565
623
|
}) }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [stepIndex > 0 ? (_jsx(Button, { variant: "outline", onClick: goBack, disabled: executing, children: t('common.back', { defaultValue: 'Atrás' }) })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') })), isLast ? (_jsxs(Button, { onClick: submit, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })) : (_jsx(Button, { onClick: goNext, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: t('common.next', { defaultValue: 'Siguiente' }) }))] })] }) }));
|
|
566
624
|
}
|
|
625
|
+
function seedOptionFromRecord(field, value, record) {
|
|
626
|
+
if (!record || !field.key.endsWith('_id'))
|
|
627
|
+
return undefined;
|
|
628
|
+
const siblingKey = field.key.replace(/_id$/, '');
|
|
629
|
+
const sib = record[siblingKey];
|
|
630
|
+
if (!sib || typeof sib !== 'object')
|
|
631
|
+
return undefined;
|
|
632
|
+
const label = sib.label ?? sib.name ?? '';
|
|
633
|
+
if (!label && !sib.image)
|
|
634
|
+
return undefined;
|
|
635
|
+
const id = String(sib.value ?? sib.id ?? value ?? '');
|
|
636
|
+
return {
|
|
637
|
+
id,
|
|
638
|
+
value: id,
|
|
639
|
+
label: String(label),
|
|
640
|
+
name: String(label),
|
|
641
|
+
image: sib.image,
|
|
642
|
+
color: sib.color,
|
|
643
|
+
icon: sib.icon,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
567
646
|
function renderField(field, value, onChange,
|
|
568
647
|
// Full current form values — lets a line-items grid (and any cascading
|
|
569
648
|
// header picker) resolve a `dependsOn` reference against sibling header
|
|
570
649
|
// fields. Omitted by callers that have no surrounding form (the field is
|
|
571
650
|
// then treated as having no resolvable dependency).
|
|
572
|
-
formValues) {
|
|
651
|
+
formValues, record) {
|
|
573
652
|
// Repeatable line-items group → row grid (value is an array of row objects).
|
|
574
653
|
// The header form values flow in so a cell can depend on a header field.
|
|
575
654
|
if (isLineItemsField(field)) {
|
|
@@ -586,7 +665,7 @@ formValues) {
|
|
|
586
665
|
const dependsValue = getDependsOn(field)
|
|
587
666
|
? resolveDependsValue(field, formValues)
|
|
588
667
|
: undefined;
|
|
589
|
-
return _jsx(DynamicSelectField, { field: field, value: value, onChange: onChange, dependsValue: dependsValue });
|
|
668
|
+
return (_jsx(DynamicSelectField, { field: field, value: value, onChange: onChange, dependsValue: dependsValue, seedOption: seedOptionFromRecord(field, value, record) }));
|
|
590
669
|
}
|
|
591
670
|
// File upload → themed picker that POSTs the file to the host upload
|
|
592
671
|
// endpoint and stores the returned url/path. Kept in sync with DynamicForm.
|
package/dist/addon-loader.d.ts
CHANGED
|
@@ -44,5 +44,5 @@ export interface AddonLoaderProps {
|
|
|
44
44
|
layout?: AddonLayout;
|
|
45
45
|
children?: React.ReactNode;
|
|
46
46
|
}
|
|
47
|
-
export declare function AddonLoader({ scope, url, module, api, hostRegistry, addonKey, unbindKey, fallback, onReady, onError, layout, children, }: AddonLoaderProps): import("react").JSX.Element;
|
|
47
|
+
export declare function AddonLoader({ scope, url, module, api, hostRegistry, addonKey, unbindKey, fallback, onReady, onError, layout, children, }: AddonLoaderProps): import("react").JSX.Element | null;
|
|
48
48
|
//# sourceMappingURL=addon-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addon-loader.d.ts","sourceRoot":"","sources":["../src/addon-loader.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAc3E,MAAM,WAAW,gBAAgB;IAC7B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,GAAG,EAAE,QAAQ,CAAA;IACb;;;;OAIG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAA;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AA4DD,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,GAAG,EACH,MAAqB,EACrB,GAAG,EACH,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,QAAe,EACf,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,GACX,EAAE,gBAAgB
|
|
1
|
+
{"version":3,"file":"addon-loader.d.ts","sourceRoot":"","sources":["../src/addon-loader.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAc3E,MAAM,WAAW,gBAAgB;IAC7B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,GAAG,EAAE,QAAQ,CAAA;IACb;;;;OAIG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAA;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AA4DD,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,GAAG,EACH,MAAqB,EACrB,GAAG,EACH,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,QAAe,EACf,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,GACX,EAAE,gBAAgB,sCAmElB"}
|
package/dist/addon-loader.js
CHANGED
|
@@ -117,7 +117,14 @@ export function AddonLoader({ scope, url, module = './register', api, hostRegist
|
|
|
117
117
|
}, [scope, url, module, addonKey, unbindKey, hostRegistry]);
|
|
118
118
|
if (status === 'loading')
|
|
119
119
|
return _jsx(_Fragment, { children: fallback });
|
|
120
|
-
if (status === 'error')
|
|
120
|
+
if (status === 'error') {
|
|
121
|
+
// Hosts that pass `onError` (toast + telemetry) should not also paint
|
|
122
|
+
// a second, persistent inline banner — DynamicAddonLoaders mounts one
|
|
123
|
+
// fiber per installed addon and a visible error stack reads as a broken
|
|
124
|
+
// shell even when only one remote failed transiently.
|
|
125
|
+
if (onError)
|
|
126
|
+
return null;
|
|
121
127
|
return _jsxs("div", { className: "text-sm text-red-500", children: ["Addon load error: ", error?.message] });
|
|
128
|
+
}
|
|
122
129
|
return _jsx(_Fragment, { children: children });
|
|
123
130
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const DEFAULT_ROLE_COLOR = "#3b82f6";
|
|
2
|
+
export interface ColorPickerFieldProps {
|
|
3
|
+
value?: string;
|
|
4
|
+
onChange: (hex: string) => void;
|
|
5
|
+
/** Optional label for a11y on the trigger. */
|
|
6
|
+
'aria-label'?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Normalize to #rrggbb or '' if invalid. */
|
|
11
|
+
export declare function normalizeHex(raw: unknown): string;
|
|
12
|
+
export declare function ColorPickerField({ value, onChange, 'aria-label': ariaLabel, className, disabled, }: ColorPickerFieldProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=color-picker-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-picker-field.d.ts","sourceRoot":"","sources":["../src/color-picker-field.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAE3C,MAAM,WAAW,qBAAqB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAQD,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAajD;AAwED,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,YAAY,EAAE,SAAmB,EACjC,SAAS,EACT,QAAQ,GACX,EAAE,qBAAqB,+BA4KvB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ColorPickerField — modern popover color control (SV plane + hue + hex).
|
|
3
|
+
// Used by PermissionsManager role dialog and DynamicForm `type: "color"`.
|
|
4
|
+
// Value is always a #rrggbb string (empty → falls back to DEFAULT_HEX visually).
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { Pipette } from 'lucide-react';
|
|
7
|
+
import { cn } from '@asteby/metacore-ui/lib';
|
|
8
|
+
import { Button, Input, Popover, PopoverContent, PopoverTrigger, } from '@asteby/metacore-ui/primitives';
|
|
9
|
+
export const DEFAULT_ROLE_COLOR = '#3b82f6';
|
|
10
|
+
function clamp(n, min, max) {
|
|
11
|
+
return Math.min(max, Math.max(min, n));
|
|
12
|
+
}
|
|
13
|
+
/** Normalize to #rrggbb or '' if invalid. */
|
|
14
|
+
export function normalizeHex(raw) {
|
|
15
|
+
if (typeof raw !== 'string')
|
|
16
|
+
return '';
|
|
17
|
+
let s = raw.trim();
|
|
18
|
+
if (!s)
|
|
19
|
+
return '';
|
|
20
|
+
if (s[0] !== '#')
|
|
21
|
+
s = `#${s}`;
|
|
22
|
+
if (/^#[0-9a-fA-F]{3}$/.test(s)) {
|
|
23
|
+
const r = s[1], g = s[2], b = s[3];
|
|
24
|
+
s = `#${r}${r}${g}${g}${b}${b}`;
|
|
25
|
+
}
|
|
26
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(s))
|
|
27
|
+
return '';
|
|
28
|
+
return s.toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
function hexToRgb(hex) {
|
|
31
|
+
const n = normalizeHex(hex);
|
|
32
|
+
if (!n)
|
|
33
|
+
return null;
|
|
34
|
+
return {
|
|
35
|
+
r: parseInt(n.slice(1, 3), 16),
|
|
36
|
+
g: parseInt(n.slice(3, 5), 16),
|
|
37
|
+
b: parseInt(n.slice(5, 7), 16),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function rgbToHex(r, g, b) {
|
|
41
|
+
const h = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0');
|
|
42
|
+
return `#${h(r)}${h(g)}${h(b)}`;
|
|
43
|
+
}
|
|
44
|
+
function rgbToHsv(r, g, b) {
|
|
45
|
+
r /= 255;
|
|
46
|
+
g /= 255;
|
|
47
|
+
b /= 255;
|
|
48
|
+
const max = Math.max(r, g, b);
|
|
49
|
+
const min = Math.min(r, g, b);
|
|
50
|
+
const d = max - min;
|
|
51
|
+
let h = 0;
|
|
52
|
+
if (d !== 0) {
|
|
53
|
+
if (max === r)
|
|
54
|
+
h = ((g - b) / d) % 6;
|
|
55
|
+
else if (max === g)
|
|
56
|
+
h = (b - r) / d + 2;
|
|
57
|
+
else
|
|
58
|
+
h = (r - g) / d + 4;
|
|
59
|
+
h *= 60;
|
|
60
|
+
if (h < 0)
|
|
61
|
+
h += 360;
|
|
62
|
+
}
|
|
63
|
+
const s = max === 0 ? 0 : d / max;
|
|
64
|
+
return { h, s, v: max };
|
|
65
|
+
}
|
|
66
|
+
function hsvToRgb(h, s, v) {
|
|
67
|
+
const c = v * s;
|
|
68
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
69
|
+
const m = v - c;
|
|
70
|
+
let rp = 0, gp = 0, bp = 0;
|
|
71
|
+
if (h < 60)
|
|
72
|
+
[rp, gp, bp] = [c, x, 0];
|
|
73
|
+
else if (h < 120)
|
|
74
|
+
[rp, gp, bp] = [x, c, 0];
|
|
75
|
+
else if (h < 180)
|
|
76
|
+
[rp, gp, bp] = [0, c, x];
|
|
77
|
+
else if (h < 240)
|
|
78
|
+
[rp, gp, bp] = [0, x, c];
|
|
79
|
+
else if (h < 300)
|
|
80
|
+
[rp, gp, bp] = [x, 0, c];
|
|
81
|
+
else
|
|
82
|
+
[rp, gp, bp] = [c, 0, x];
|
|
83
|
+
return {
|
|
84
|
+
r: (rp + m) * 255,
|
|
85
|
+
g: (gp + m) * 255,
|
|
86
|
+
b: (bp + m) * 255,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function hexToHsv(hex) {
|
|
90
|
+
const rgb = hexToRgb(hex) || hexToRgb(DEFAULT_ROLE_COLOR);
|
|
91
|
+
return rgbToHsv(rgb.r, rgb.g, rgb.b);
|
|
92
|
+
}
|
|
93
|
+
function hsvToHex(hsv) {
|
|
94
|
+
const { r, g, b } = hsvToRgb(hsv.h, hsv.s, hsv.v);
|
|
95
|
+
return rgbToHex(r, g, b);
|
|
96
|
+
}
|
|
97
|
+
function hueCss(h) {
|
|
98
|
+
const { r, g, b } = hsvToRgb(h, 1, 1);
|
|
99
|
+
return rgbToHex(r, g, b);
|
|
100
|
+
}
|
|
101
|
+
export function ColorPickerField({ value, onChange, 'aria-label': ariaLabel = 'Color', className, disabled, }) {
|
|
102
|
+
const hex = normalizeHex(value) || DEFAULT_ROLE_COLOR;
|
|
103
|
+
const [open, setOpen] = useState(false);
|
|
104
|
+
const [hsv, setHsv] = useState(() => hexToHsv(hex));
|
|
105
|
+
const [hexDraft, setHexDraft] = useState(hex);
|
|
106
|
+
const svRef = useRef(null);
|
|
107
|
+
const dragging = useRef(false);
|
|
108
|
+
// Sync from external value when popover closed (or first open).
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (dragging.current)
|
|
111
|
+
return;
|
|
112
|
+
const next = normalizeHex(value) || DEFAULT_ROLE_COLOR;
|
|
113
|
+
setHsv(hexToHsv(next));
|
|
114
|
+
setHexDraft(next);
|
|
115
|
+
}, [value, open]);
|
|
116
|
+
const commit = useCallback((next) => {
|
|
117
|
+
setHsv(next);
|
|
118
|
+
const out = hsvToHex(next);
|
|
119
|
+
setHexDraft(out);
|
|
120
|
+
onChange(out);
|
|
121
|
+
}, [onChange]);
|
|
122
|
+
const setFromPointer = useCallback((clientX, clientY) => {
|
|
123
|
+
const el = svRef.current;
|
|
124
|
+
if (!el)
|
|
125
|
+
return;
|
|
126
|
+
const rect = el.getBoundingClientRect();
|
|
127
|
+
const s = clamp((clientX - rect.left) / rect.width, 0, 1);
|
|
128
|
+
const v = clamp(1 - (clientY - rect.top) / rect.height, 0, 1);
|
|
129
|
+
commit({ ...hsv, s, v });
|
|
130
|
+
}, [commit, hsv]);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (!open)
|
|
133
|
+
return;
|
|
134
|
+
const onMove = (e) => {
|
|
135
|
+
if (!dragging.current)
|
|
136
|
+
return;
|
|
137
|
+
setFromPointer(e.clientX, e.clientY);
|
|
138
|
+
};
|
|
139
|
+
const onUp = () => {
|
|
140
|
+
dragging.current = false;
|
|
141
|
+
};
|
|
142
|
+
window.addEventListener('pointermove', onMove);
|
|
143
|
+
window.addEventListener('pointerup', onUp);
|
|
144
|
+
return () => {
|
|
145
|
+
window.removeEventListener('pointermove', onMove);
|
|
146
|
+
window.removeEventListener('pointerup', onUp);
|
|
147
|
+
};
|
|
148
|
+
}, [open, setFromPointer]);
|
|
149
|
+
const svStyle = useMemo(() => ({
|
|
150
|
+
background: `
|
|
151
|
+
linear-gradient(to top, #000, transparent),
|
|
152
|
+
linear-gradient(to right, #fff, ${hueCss(hsv.h)})
|
|
153
|
+
`,
|
|
154
|
+
}), [hsv.h]);
|
|
155
|
+
const applyHexDraft = () => {
|
|
156
|
+
const n = normalizeHex(hexDraft);
|
|
157
|
+
if (!n) {
|
|
158
|
+
setHexDraft(hsvToHex(hsv));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
setHsv(hexToHsv(n));
|
|
162
|
+
setHexDraft(n);
|
|
163
|
+
onChange(n);
|
|
164
|
+
};
|
|
165
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { type: "button", variant: "outline", disabled: disabled, "aria-label": ariaLabel, className: cn('h-10 w-full justify-start gap-3 px-2.5 font-normal', className), children: [_jsx("span", { className: "h-6 w-6 shrink-0 rounded-md border border-black/10 shadow-sm ring-1 ring-black/5 dark:border-white/10", style: { background: hex }, "aria-hidden": true }), _jsx("span", { className: "font-mono text-sm uppercase tracking-wide text-foreground", children: hex }), _jsx(Pipette, { className: "ml-auto h-3.5 w-3.5 text-muted-foreground" })] }) }), _jsx(PopoverContent, { className: "w-[260px] p-3", align: "start", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { ref: svRef, role: "slider", "aria-label": "Saturaci\u00F3n y brillo", tabIndex: 0, className: "relative h-36 w-full cursor-crosshair touch-none overflow-hidden rounded-lg border border-border/60", style: svStyle, onPointerDown: (e) => {
|
|
166
|
+
dragging.current = true;
|
|
167
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
168
|
+
setFromPointer(e.clientX, e.clientY);
|
|
169
|
+
}, children: _jsx("span", { className: "pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md ring-1 ring-black/30", style: {
|
|
170
|
+
left: `${hsv.s * 100}%`,
|
|
171
|
+
top: `${(1 - hsv.v) * 100}%`,
|
|
172
|
+
background: hsvToHex(hsv),
|
|
173
|
+
} }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("label", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Tono" }), _jsx("input", { type: "range", min: 0, max: 360, step: 1, value: Math.round(hsv.h), "aria-label": "Tono", onChange: (e) => commit({ ...hsv, h: Number(e.target.value) }), className: "h-3 w-full cursor-pointer appearance-none rounded-full", style: {
|
|
174
|
+
background: 'linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)',
|
|
175
|
+
} })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-9 w-9 shrink-0 rounded-md border border-black/10 shadow-inner ring-1 ring-black/5", style: { background: hsvToHex(hsv) }, "aria-hidden": true }), _jsx(Input, { value: hexDraft, spellCheck: false, "aria-label": "Hexadecimal", className: "h-9 font-mono uppercase", onChange: (e) => setHexDraft(e.target.value), onBlur: applyHexDraft, onKeyDown: (e) => {
|
|
176
|
+
if (e.key === 'Enter') {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
applyHexDraft();
|
|
179
|
+
}
|
|
180
|
+
} })] })] }) })] }));
|
|
181
|
+
}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
|