@asteby/metacore-runtime-react 18.28.0 → 18.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 18.28.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 1896101: fix(line-items): non-select cells (number/text/date/switch/select/textarea) now honor a per-field `readonly` flag (set via PrefillSpec.lock), rendering disabled. Lets a receive-goods modal show read-only progress columns (ordered / already-received) alongside the editable qty.
8
+
9
+ ## 18.28.1
10
+
11
+ ### Patch Changes
12
+
13
+ - f346352: fix(line-items): a locked dynamic_select no longer flashes the raw id while its label is resolving — it shows a loading hint, then the name + thumbnail, instead of String(value).
14
+
3
15
  ## 18.28.0
4
16
 
5
17
  ### Minor Changes
@@ -83,6 +83,13 @@ function BalanceBadge({ state, }) {
83
83
  // a scalar widget).
84
84
  function CellRenderer({ field, value, onChange, disabled, formValues, rowValues }) {
85
85
  const widget = resolveWidget(field);
86
+ // Per-field read-only: a column locked by a PrefillSpec.lock (e.g. the
87
+ // "ordered" / "already received" progress columns of a receive-goods modal)
88
+ // renders disabled so it shows context without being editable. Tolerates the
89
+ // snake_case alias the kernel may serve.
90
+ const ro = !!field.readonly ||
91
+ !!field.read_only;
92
+ const off = disabled || ro;
86
93
  // Cascade scope for a cell with `dependsOn`: resolved from this row first
87
94
  // (a sibling cell) then the header form (e.g. `source_warehouse_id`).
88
95
  const dependsValue = getDependsOn(field)
@@ -91,8 +98,6 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
91
98
  // Async searchable picker per row cell — e.g. the account_id column of a
92
99
  // journal entry's debit/credit lines. Same widget as the flat form.
93
100
  if (widget === 'dynamic_select') {
94
- const ro = !!field.readonly ||
95
- !!field.read_only;
96
101
  return _jsx(DynamicSelectField, { field: field, value: value, onChange: onChange, dependsValue: dependsValue, readonly: ro });
97
102
  }
98
103
  if (widget === 'select' && (field.ref || getOptionsConfig(field)?.source)) {
@@ -101,19 +106,19 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
101
106
  switch (widget) {
102
107
  case 'textarea':
103
108
  case 'richtext':
104
- return (_jsx(Textarea, { value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, disabled: disabled, rows: 2 }));
109
+ return (_jsx(Textarea, { value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, disabled: off, rows: 2 }));
105
110
  case 'color':
106
- return (_jsx(Input, { type: "color", value: value || '#000000', onChange: (e) => onChange(e.target.value), disabled: disabled }));
111
+ return (_jsx(Input, { type: "color", value: value || '#000000', onChange: (e) => onChange(e.target.value), disabled: off }));
107
112
  case 'select':
108
- return (_jsxs(Select, { value: value || '', onValueChange: onChange, disabled: disabled, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: field.placeholder || 'Seleccionar...' }) }), _jsx(SelectContent, { children: field.options?.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }));
113
+ return (_jsxs(Select, { value: value || '', onValueChange: onChange, disabled: off, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: field.placeholder || 'Seleccionar...' }) }), _jsx(SelectContent, { children: field.options?.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }));
109
114
  case 'switch':
110
- return _jsx(Switch, { checked: !!value, onCheckedChange: onChange, disabled: disabled });
115
+ return _jsx(Switch, { checked: !!value, onCheckedChange: onChange, disabled: off });
111
116
  case 'number':
112
- return (_jsx(Input, { type: "number", value: value ?? '', onChange: (e) => onChange(e.target.valueAsNumber || ''), placeholder: field.placeholder, disabled: disabled }));
117
+ return (_jsx(Input, { type: "number", value: value ?? '', onChange: (e) => onChange(e.target.valueAsNumber || ''), placeholder: field.placeholder, disabled: off }));
113
118
  case 'date':
114
- return (_jsx(Input, { type: "date", value: value || '', onChange: (e) => onChange(e.target.value), disabled: disabled }));
119
+ return (_jsx(Input, { type: "date", value: value || '', onChange: (e) => onChange(e.target.value), disabled: off }));
115
120
  default:
116
- return (_jsx(Input, { type: field.type === 'email' ? 'email' : field.type === 'url' ? 'url' : 'text', value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, disabled: disabled }));
121
+ return (_jsx(Input, { type: field.type === 'email' ? 'email' : field.type === 'url' ? 'url' : 'text', value: value || '', onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, disabled: off }));
117
122
  }
118
123
  }
119
124
  function RefCell({ field, value, onChange, disabled, formValues, rowValues }) {
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BA4BzF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC,GAAG,IAAI,CAAA;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCAwBA;AAqBD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,GACnB,EAAE,uBAAuB,+BAsOzB;AAED,eAAe,kBAAkB,CAAA"}
1
+ {"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BA4BzF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC,GAAG,IAAI,CAAA;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCAwBA;AAqBD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,GACnB,EAAE,uBAAuB,+BAwOzB;AAED,eAAe,kBAAkB,CAAA"}
@@ -181,7 +181,7 @@ export function DynamicSelectField({ field, value, onChange, seedOption, depends
181
181
  // the eager fetch is in flight the label falls back to the seed/raw value,
182
182
  // then snaps to the name once options arrive.
183
183
  if (readonly) {
184
- return (_jsx(Button, { type: "button", variant: "outline", role: "combobox", id: field.key, disabled: true, "aria-readonly": "true", className: "w-full min-w-0 cursor-default justify-start font-normal opacity-100", children: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-2 text-left", children: [hasVisual && value ? _jsx(OptionLead, { option: selectedOption, size: 20 }) : null, _jsx("span", { className: 'min-w-0 flex-1 truncate ' + (selectedLabel ? '' : 'text-muted-foreground'), children: selectedLabel || (loading ? 'Cargando…' : field.placeholder || '—') })] }) }));
184
+ return (_jsx(Button, { type: "button", variant: "outline", role: "combobox", id: field.key, disabled: true, "aria-readonly": "true", className: "w-full min-w-0 cursor-default justify-start font-normal opacity-100", children: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-2 text-left", children: [selectedOption ? _jsx(OptionLead, { option: selectedOption, size: 20 }) : null, _jsx("span", { className: 'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground'), children: selectedOption?.label ?? (loading ? 'Cargando…' : field.placeholder || '—') })] }) }));
185
185
  }
186
186
  // w-full + min-w-0: as a grid cell child, the row must be allowed to shrink
187
187
  // to the cell. Without min-w-0 the combobox+button row sizes to its content
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "18.28.0",
3
+ "version": "18.28.2",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -259,6 +259,13 @@ interface CellRendererProps {
259
259
  // a scalar widget).
260
260
  function CellRenderer({ field, value, onChange, disabled, formValues, rowValues }: CellRendererProps) {
261
261
  const widget = resolveWidget(field)
262
+ // Per-field read-only: a column locked by a PrefillSpec.lock (e.g. the
263
+ // "ordered" / "already received" progress columns of a receive-goods modal)
264
+ // renders disabled so it shows context without being editable. Tolerates the
265
+ // snake_case alias the kernel may serve.
266
+ const ro = !!(field as { readonly?: boolean; read_only?: boolean }).readonly ||
267
+ !!(field as { readonly?: boolean; read_only?: boolean }).read_only
268
+ const off = disabled || ro
262
269
  // Cascade scope for a cell with `dependsOn`: resolved from this row first
263
270
  // (a sibling cell) then the header form (e.g. `source_warehouse_id`).
264
271
  const dependsValue = getDependsOn(field)
@@ -267,8 +274,6 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
267
274
  // Async searchable picker per row cell — e.g. the account_id column of a
268
275
  // journal entry's debit/credit lines. Same widget as the flat form.
269
276
  if (widget === 'dynamic_select') {
270
- const ro = !!(field as { readonly?: boolean; read_only?: boolean }).readonly ||
271
- !!(field as { readonly?: boolean; read_only?: boolean }).read_only
272
277
  return <DynamicSelectField field={field} value={value} onChange={onChange} dependsValue={dependsValue} readonly={ro} />
273
278
  }
274
279
  if (widget === 'select' && (field.ref || getOptionsConfig(field)?.source)) {
@@ -291,7 +296,7 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
291
296
  value={value || ''}
292
297
  onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)}
293
298
  placeholder={field.placeholder}
294
- disabled={disabled}
299
+ disabled={off}
295
300
  rows={2}
296
301
  />
297
302
  )
@@ -301,12 +306,12 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
301
306
  type="color"
302
307
  value={value || '#000000'}
303
308
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
304
- disabled={disabled}
309
+ disabled={off}
305
310
  />
306
311
  )
307
312
  case 'select':
308
313
  return (
309
- <Select value={value || ''} onValueChange={onChange} disabled={disabled}>
314
+ <Select value={value || ''} onValueChange={onChange} disabled={off}>
310
315
  <SelectTrigger className="w-full">
311
316
  <SelectValue placeholder={field.placeholder || 'Seleccionar...'} />
312
317
  </SelectTrigger>
@@ -320,7 +325,7 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
320
325
  </Select>
321
326
  )
322
327
  case 'switch':
323
- return <Switch checked={!!value} onCheckedChange={onChange} disabled={disabled} />
328
+ return <Switch checked={!!value} onCheckedChange={onChange} disabled={off} />
324
329
  case 'number':
325
330
  return (
326
331
  <Input
@@ -328,7 +333,7 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
328
333
  value={value ?? ''}
329
334
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.valueAsNumber || '')}
330
335
  placeholder={field.placeholder}
331
- disabled={disabled}
336
+ disabled={off}
332
337
  />
333
338
  )
334
339
  case 'date':
@@ -337,7 +342,7 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
337
342
  type="date"
338
343
  value={value || ''}
339
344
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
340
- disabled={disabled}
345
+ disabled={off}
341
346
  />
342
347
  )
343
348
  default:
@@ -347,7 +352,7 @@ function CellRenderer({ field, value, onChange, disabled, formValues, rowValues
347
352
  value={value || ''}
348
353
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
349
354
  placeholder={field.placeholder}
350
- disabled={disabled}
355
+ disabled={off}
351
356
  />
352
357
  )
353
358
  }
@@ -298,9 +298,11 @@ export function DynamicSelectField({
298
298
  className="w-full min-w-0 cursor-default justify-start font-normal opacity-100"
299
299
  >
300
300
  <span className="flex min-w-0 flex-1 items-center gap-2 text-left">
301
- {hasVisual && value ? <OptionLead option={selectedOption} size={20} /> : null}
302
- <span className={'min-w-0 flex-1 truncate ' + (selectedLabel ? '' : 'text-muted-foreground')}>
303
- {selectedLabel || (loading ? 'Cargando…' : field.placeholder || '—')}
301
+ {selectedOption ? <OptionLead option={selectedOption} size={20} /> : null}
302
+ <span className={'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground')}>
303
+ {/* Never flash the raw id: until the eager fetch resolves the
304
+ option, show a loading hint instead of String(value). */}
305
+ {selectedOption?.label ?? (loading ? 'Cargando…' : field.placeholder || '—')}
304
306
  </span>
305
307
  </span>
306
308
  </Button>