hazo_config 2.4.0 → 2.4.1

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/CHANGE_LOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.4.1 — 2026-06-22
4
+
5
+ ### Fixed
6
+ - The shadcn `Select` branch in `AppConfigListEditor`'s edit modal (≤ 10 options) crashed at runtime with *"A `<Select.Item />` must have a value prop that is not an empty string."* The clear/placeholder option used `value=""`, which Radix reserves for the cleared Root state and forbids on items. It now uses an internal sentinel value mapped back to `''` on change. (The `> 10` / `searchable` combobox branch was unaffected.)
7
+
3
8
  ## 2.4.0 — 2026-06-22
4
9
 
5
10
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/edit_modal.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAc/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,UAAU,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAChB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,IAAI,EACJ,IAAI,EACJ,IAAI,EAAE,YAAY,EAClB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,OAAO,EACP,SAAS,EACT,eAAgC,EAChC,SAAiB,GAClB,EAAE,cAAc,CAAC,CAAC,CAAC,qBAiUnB"}
1
+ {"version":3,"file":"edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/edit_modal.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAc/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQ5C,UAAU,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAChB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,IAAI,EACJ,IAAI,EACJ,IAAI,EAAE,YAAY,EAClB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,OAAO,EACP,SAAS,EACT,eAAgC,EAChC,SAAiB,GAClB,EAAE,cAAc,CAAC,CAAC,CAAC,qBAqUnB"}
@@ -11,6 +11,10 @@ import { ColorSwatchPicker } from './color_swatch_picker.js';
11
11
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select.js';
12
12
  import { Combobox } from '../../ui/combobox.js';
13
13
  import { slugify } from '../types.js';
14
+ // Sentinel for the "clear selection" item in a shadcn Select. Radix forbids an
15
+ // empty-string SelectItem value (it reserves '' for the cleared Root state), so
16
+ // the placeholder/clear option carries this value and is mapped back to '' on change.
17
+ const SELECT_CLEAR_VALUE = '__hazo_select_none__';
14
18
  export function EditModal({ open, mode, item: initial_item, columns, id_field, auto_id_from, id_editable_after_create, existing_ids, item_type_label, render_preview, on_save, on_cancel, max_width_class = 'sm:max-w-2xl', is_saving = false, }) {
15
19
  const [form_data, set_form_data] = useState({});
16
20
  const [errors, set_errors] = useState(new Map());
@@ -116,7 +120,7 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
116
120
  ? handle_id_change(e.target.value)
117
121
  : update_field(col.field, e.target.value), placeholder: col.placeholder, disabled: is_disabled, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', is_disabled && 'bg-gray-50 text-gray-500', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'number' && (_jsx(Input, { type: "number", value: value !== undefined && value !== null ? String(value) : '', onChange: (e) => update_field(col.field, e.target.value === '' ? '' : Number(e.target.value)), placeholder: col.placeholder, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'textarea' && (_jsx("textarea", { value: String(value ?? ''), onChange: (e) => update_field(col.field, e.target.value), placeholder: col.placeholder, rows: 3, className: cn('flex w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500/20 focus-visible:border-violet-300 disabled:cursor-not-allowed disabled:opacity-50', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'select' && col.options && ((col.searchable || col.options.length > 10)
118
122
  ? (_jsx(Combobox, { value: String(value ?? ''), onChange: (v) => update_field(col.field, v), options: [{ value: '', label: col.placeholder || 'Select...' }, ...col.options], placeholder: col.placeholder || 'Select...', error: !!error }))
119
- : (_jsxs(Select, { value: String(value ?? ''), onValueChange: (v) => update_field(col.field, v), children: [_jsx(SelectTrigger, { className: cn(error && 'border-red-400'), children: _jsx(SelectValue, { placeholder: col.placeholder || 'Select...' }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "", children: col.placeholder || 'Select...' }), col.options.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)))] })] }))), col.type === 'color_swatch' && col.color_options && (_jsx(ColorSwatchPicker, { value: String(value ?? ''), options: col.color_options, on_change: (color) => update_field(col.field, color) })), is_id_field && auto_id_from && mode === 'create' && !user_touched_id && (_jsxs("p", { className: "text-xs text-gray-400", children: ["Auto-generated from ", columns.find(c => c.field === auto_id_from)?.label?.toLowerCase() || auto_id_from] })), error && (_jsx("p", { className: "text-xs text-red-600", children: error }))] }, col.field));
123
+ : (_jsxs(Select, { value: String(value ?? ''), onValueChange: (v) => update_field(col.field, v === SELECT_CLEAR_VALUE ? '' : v), children: [_jsx(SelectTrigger, { className: cn(error && 'border-red-400'), children: _jsx(SelectValue, { placeholder: col.placeholder || 'Select...' }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: SELECT_CLEAR_VALUE, children: col.placeholder || 'Select...' }), col.options.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)))] })] }))), col.type === 'color_swatch' && col.color_options && (_jsx(ColorSwatchPicker, { value: String(value ?? ''), options: col.color_options, on_change: (color) => update_field(col.field, color) })), is_id_field && auto_id_from && mode === 'create' && !user_touched_id && (_jsxs("p", { className: "text-xs text-gray-400", children: ["Auto-generated from ", columns.find(c => c.field === auto_id_from)?.label?.toLowerCase() || auto_id_from] })), error && (_jsx("p", { className: "text-xs text-red-600", children: error }))] }, col.field));
120
124
  };
121
125
  return (_jsx(Dialog, { open: open, onOpenChange: (is_open) => { if (!is_open)
122
126
  on_cancel(); }, children: _jsxs(DialogContent, { className: cn('cls_edit_modal w-[95vw] rounded-2xl p-0 gap-0 overflow-hidden max-h-[90vh] flex flex-col', max_width_class), children: [_jsx(DialogHeader, { className: "px-6 pt-6 pb-4 shrink-0", children: _jsx(DialogTitle, { children: mode === 'create' ? `New ${item_type_label}` : `Edit ${item_type_label}` }) }), _jsxs("div", { className: "px-6 pb-4 space-y-4 overflow-y-auto flex-1 min-h-0", children: [render_preview && Object.keys(form_data).length > 0 && (_jsx("div", { className: "p-3 bg-gray-50 rounded-lg border border-gray-100", children: render_preview(form_data) })), columns.map((col) => render_field(col))] }), _jsxs(DialogFooter, { className: "bg-gray-50 px-6 py-4 border-t border-gray-100 sm:justify-between shrink-0", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: on_cancel, className: "rounded-full", children: "Cancel" }), _jsx(Button, { type: "button", onClick: handle_save, disabled: is_saving, className: "rounded-full bg-violet-600 hover:bg-violet-700 text-white disabled:opacity-50 disabled:cursor-not-allowed", children: is_saving ? 'Saving…' : mode === 'create' ? `Add ${item_type_label}` : 'Save Changes' })] })] }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_config",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "Config wrapper with error handling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -91,7 +91,7 @@
91
91
  "@types/react": "^18.3.3",
92
92
  "@types/react-dom": "^18.3.0",
93
93
  "@vitejs/plugin-react": "^4.2.0",
94
- "hazo_connect": "^3.8.0",
94
+ "hazo_connect": "^3.9.0",
95
95
  "jsdom": "^24.1.3",
96
96
  "postcss": "^8.4.49",
97
97
  "react": "^18.2.0",