@simplysm/solid 13.0.53 → 13.0.56
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/README.md +6 -2
- package/dist/components/data/crud-detail/CrudDetail.d.ts +14 -0
- package/dist/components/data/crud-detail/CrudDetail.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetail.js +348 -0
- package/dist/components/data/crud-detail/CrudDetail.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailTools.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailTools.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailTools.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailTools.js.map +6 -0
- package/dist/components/data/crud-detail/types.d.ts +45 -0
- package/dist/components/data/crud-detail/types.d.ts.map +1 -0
- package/dist/components/data/crud-detail/types.js +1 -0
- package/dist/components/data/crud-detail/types.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheet.d.ts +17 -0
- package/dist/components/data/crud-sheet/CrudSheet.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheet.js +679 -0
- package/dist/components/data/crud-sheet/CrudSheet.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts +5 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.js +29 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.js.map +6 -0
- package/dist/components/data/crud-sheet/types.d.ts +109 -0
- package/dist/components/data/crud-sheet/types.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/types.js +1 -0
- package/dist/components/data/crud-sheet/types.js.map +6 -0
- package/dist/components/data/kanban/Kanban.d.ts.map +1 -1
- package/dist/components/data/kanban/Kanban.js +137 -138
- package/dist/components/data/kanban/Kanban.js.map +2 -2
- package/dist/components/data/kanban/KanbanContext.d.ts +5 -1
- package/dist/components/data/kanban/KanbanContext.d.ts.map +1 -1
- package/dist/components/data/kanban/KanbanContext.js.map +1 -1
- package/dist/components/data/list/ListItem.d.ts.map +1 -1
- package/dist/components/data/list/ListItem.js +109 -99
- package/dist/components/data/list/ListItem.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.css +28 -10
- package/dist/components/data/sheet/DataSheet.js +1 -1
- package/dist/components/data/sheet/DataSheet.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js.map +1 -1
- package/dist/components/disclosure/Dialog.d.ts +16 -10
- package/dist/components/disclosure/Dialog.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.js +126 -91
- package/dist/components/disclosure/Dialog.js.map +2 -2
- package/dist/components/disclosure/DialogContext.d.ts +2 -4
- package/dist/components/disclosure/DialogContext.d.ts.map +1 -1
- package/dist/components/disclosure/DialogContext.js.map +1 -1
- package/dist/components/disclosure/DialogProvider.d.ts.map +1 -1
- package/dist/components/disclosure/DialogProvider.js +14 -9
- package/dist/components/disclosure/DialogProvider.js.map +2 -2
- package/dist/components/disclosure/Dropdown.d.ts +46 -22
- package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
- package/dist/components/disclosure/Dropdown.js +100 -65
- package/dist/components/disclosure/Dropdown.js.map +2 -2
- package/dist/components/feedback/notification/NotificationBanner.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBanner.js +3 -3
- package/dist/components/feedback/notification/NotificationBanner.js.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.js +84 -84
- package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
- package/dist/components/form-control/Invalid.js +1 -1
- package/dist/components/form-control/combobox/Combobox.d.ts +6 -3
- package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/form-control/combobox/Combobox.js +150 -168
- package/dist/components/form-control/combobox/Combobox.js.map +2 -2
- package/dist/components/form-control/combobox/ComboboxContext.d.ts +3 -0
- package/dist/components/form-control/combobox/ComboboxContext.d.ts.map +1 -1
- package/dist/components/form-control/combobox/ComboboxContext.js.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts +0 -2
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -17
- package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DatePicker.js +3 -2
- package/dist/components/form-control/field/DatePicker.js.map +2 -2
- package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DateTimePicker.js +3 -2
- package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
- package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
- package/dist/components/form-control/field/Field.styles.js +2 -1
- package/dist/components/form-control/field/Field.styles.js.map +1 -1
- package/dist/components/form-control/field/NumberInput.d.ts +15 -5
- package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
- package/dist/components/form-control/field/NumberInput.js +181 -141
- package/dist/components/form-control/field/NumberInput.js.map +2 -2
- package/dist/components/form-control/field/TextInput.d.ts +9 -5
- package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
- package/dist/components/form-control/field/TextInput.js +199 -154
- package/dist/components/form-control/field/TextInput.js.map +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/TimePicker.js +3 -2
- package/dist/components/form-control/field/TimePicker.js.map +2 -2
- package/dist/components/form-control/select/Select.d.ts +3 -3
- package/dist/components/form-control/select/Select.d.ts.map +1 -1
- package/dist/components/form-control/select/Select.js +116 -100
- package/dist/components/form-control/select/Select.js.map +2 -2
- package/dist/components/form-control/select/SelectContext.d.ts +9 -1
- package/dist/components/form-control/select/SelectContext.d.ts.map +1 -1
- package/dist/components/form-control/select/SelectContext.js.map +1 -1
- package/dist/components/form-control/select/SelectItem.d.ts.map +1 -1
- package/dist/components/form-control/select/SelectItem.js +77 -67
- package/dist/components/form-control/select/SelectItem.js.map +2 -2
- package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
- package/dist/components/form-control/state-preset/StatePreset.js +1 -1
- package/dist/components/form-control/state-preset/StatePreset.js.map +1 -1
- package/dist/components/layout/topbar/Topbar.d.ts +2 -0
- package/dist/components/layout/topbar/Topbar.d.ts.map +1 -1
- package/dist/components/layout/topbar/Topbar.js +2 -0
- package/dist/components/layout/topbar/Topbar.js.map +2 -2
- package/dist/components/layout/topbar/TopbarActions.d.ts +3 -0
- package/dist/components/layout/topbar/TopbarActions.d.ts.map +1 -0
- package/dist/components/layout/topbar/TopbarActions.js +17 -0
- package/dist/components/layout/topbar/TopbarActions.js.map +6 -0
- package/dist/components/layout/topbar/TopbarContainer.d.ts +1 -1
- package/dist/components/layout/topbar/TopbarContainer.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarContainer.js +21 -12
- package/dist/components/layout/topbar/TopbarContainer.js.map +2 -2
- package/dist/components/layout/topbar/TopbarContext.d.ts +9 -0
- package/dist/components/layout/topbar/TopbarContext.d.ts.map +1 -0
- package/dist/components/layout/topbar/TopbarContext.js +29 -0
- package/dist/components/layout/topbar/TopbarContext.js.map +6 -0
- package/dist/components/layout/topbar/TopbarMenu.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarMenu.js +63 -57
- package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
- package/dist/components/layout/topbar/TopbarUser.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarUser.js +53 -54
- package/dist/components/layout/topbar/TopbarUser.js.map +2 -2
- package/dist/hooks/createControllableStore.d.ts +29 -0
- package/dist/hooks/createControllableStore.d.ts.map +1 -0
- package/dist/hooks/createControllableStore.js +19 -0
- package/dist/hooks/createControllableStore.js.map +6 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/styles/patterns.styles.d.ts.map +1 -1
- package/dist/styles/patterns.styles.js +7 -1
- package/dist/styles/patterns.styles.js.map +1 -1
- package/docs/data-components.md +428 -0
- package/docs/disclosure.md +65 -35
- package/docs/form-controls.md +18 -3
- package/docs/helpers.md +0 -39
- package/docs/hooks.md +39 -0
- package/docs/layout.md +70 -1
- package/package.json +4 -3
- package/src/components/data/crud-detail/CrudDetail.tsx +346 -0
- package/src/components/data/crud-detail/CrudDetailAfter.tsx +19 -0
- package/src/components/data/crud-detail/CrudDetailBefore.tsx +19 -0
- package/src/components/data/crud-detail/CrudDetailTools.tsx +19 -0
- package/src/components/data/crud-detail/types.ts +58 -0
- package/src/components/data/crud-sheet/CrudSheet.tsx +628 -0
- package/src/components/data/crud-sheet/CrudSheetColumn.tsx +34 -0
- package/src/components/data/crud-sheet/CrudSheetFilter.tsx +21 -0
- package/src/components/data/crud-sheet/CrudSheetHeader.tsx +19 -0
- package/src/components/data/crud-sheet/CrudSheetTools.tsx +21 -0
- package/src/components/data/crud-sheet/types.ts +133 -0
- package/src/components/data/kanban/Kanban.tsx +72 -65
- package/src/components/data/kanban/KanbanContext.ts +7 -1
- package/src/components/data/list/ListItem.tsx +31 -18
- package/src/components/data/sheet/DataSheet.css +28 -10
- package/src/components/data/sheet/DataSheet.styles.ts +1 -1
- package/src/components/data/sheet/DataSheet.tsx +1 -1
- package/src/components/disclosure/Dialog.tsx +143 -105
- package/src/components/disclosure/DialogContext.ts +2 -4
- package/src/components/disclosure/DialogProvider.tsx +4 -2
- package/src/components/disclosure/Dropdown.tsx +174 -86
- package/src/components/feedback/notification/NotificationBanner.tsx +3 -9
- package/src/components/feedback/notification/NotificationBell.tsx +51 -57
- package/src/components/form-control/Invalid.tsx +1 -1
- package/src/components/form-control/combobox/Combobox.tsx +109 -133
- package/src/components/form-control/combobox/ComboboxContext.ts +4 -1
- package/src/components/form-control/date-range-picker/DateRangePicker.tsx +6 -16
- package/src/components/form-control/field/DatePicker.tsx +4 -1
- package/src/components/form-control/field/DateTimePicker.tsx +3 -0
- package/src/components/form-control/field/Field.styles.ts +1 -0
- package/src/components/form-control/field/NumberInput.tsx +131 -86
- package/src/components/form-control/field/TextInput.tsx +139 -88
- package/src/components/form-control/field/TimePicker.tsx +3 -0
- package/src/components/form-control/select/Select.tsx +85 -67
- package/src/components/form-control/select/SelectContext.ts +12 -1
- package/src/components/form-control/select/SelectItem.tsx +39 -18
- package/src/components/form-control/state-preset/StatePreset.tsx +1 -0
- package/src/components/layout/topbar/Topbar.tsx +3 -0
- package/src/components/layout/topbar/TopbarActions.tsx +8 -0
- package/src/components/layout/topbar/TopbarContainer.tsx +9 -5
- package/src/components/layout/topbar/TopbarContext.ts +36 -0
- package/src/components/layout/topbar/TopbarMenu.tsx +52 -55
- package/src/components/layout/topbar/TopbarUser.tsx +28 -31
- package/src/hooks/createControllableStore.ts +47 -0
- package/src/index.ts +6 -1
- package/src/styles/patterns.styles.ts +7 -1
- package/tailwind.css +4 -0
- package/dist/helpers/splitSlots.d.ts +0 -25
- package/dist/helpers/splitSlots.d.ts.map +0 -1
- package/dist/helpers/splitSlots.js +0 -25
- package/dist/helpers/splitSlots.js.map +0 -6
- package/dist/hooks/createItemTemplate.d.ts +0 -17
- package/dist/hooks/createItemTemplate.d.ts.map +0 -1
- package/dist/hooks/createItemTemplate.js +0 -40
- package/dist/hooks/createItemTemplate.js.map +0 -6
- package/src/helpers/splitSlots.ts +0 -51
- package/src/hooks/createItemTemplate.tsx +0 -42
package/docs/data-components.md
CHANGED
|
@@ -331,3 +331,431 @@ interface PermissionItem<TModule = string> {
|
|
|
331
331
|
```
|
|
332
332
|
|
|
333
333
|
**Cascading behavior:** Checking a parent checks all children. Unchecking `perms[0]` (base permission) automatically unchecks all other permissions for that item.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## CrudSheet
|
|
338
|
+
|
|
339
|
+
Full-featured CRUD data sheet component. Wraps `DataSheet` with built-in search, filter form, inline editing, modal editing, Excel import/export, select mode, pagination, sorting, and topbar action integration. Uses compound component pattern with `CrudSheet.Column`, `CrudSheet.Filter`, `CrudSheet.Tools`, and `CrudSheet.Header`.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
import { CrudSheet, type SearchResult, type SortingDef } from "@simplysm/solid";
|
|
343
|
+
|
|
344
|
+
interface User {
|
|
345
|
+
id?: number;
|
|
346
|
+
name: string;
|
|
347
|
+
email: string;
|
|
348
|
+
isDeleted?: boolean;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
interface UserFilter {
|
|
352
|
+
keyword: string;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Inline editing mode
|
|
356
|
+
<CrudSheet<User, UserFilter>
|
|
357
|
+
search={async (filter, page, sorts) => {
|
|
358
|
+
const result = await api.getUsers(filter, page, sorts);
|
|
359
|
+
return { items: result.items, pageCount: result.pageCount };
|
|
360
|
+
}}
|
|
361
|
+
getItemKey={(item) => item.id}
|
|
362
|
+
persistKey="user-crud"
|
|
363
|
+
itemsPerPage={20}
|
|
364
|
+
filterInitial={{ keyword: "" }}
|
|
365
|
+
inlineEdit={{
|
|
366
|
+
newItem: () => ({ name: "", email: "" }),
|
|
367
|
+
submit: async (diffs) => { await api.saveUsers(diffs); },
|
|
368
|
+
deleteProp: "isDeleted",
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
<CrudSheet.Filter>
|
|
372
|
+
{(filter, setFilter) => (
|
|
373
|
+
<FormGroup.Item label="Keyword">
|
|
374
|
+
<TextInput value={filter.keyword} onValueChange={(v) => setFilter("keyword", v)} />
|
|
375
|
+
</FormGroup.Item>
|
|
376
|
+
)}
|
|
377
|
+
</CrudSheet.Filter>
|
|
378
|
+
<CrudSheet.Column key="name" header="Name" class="px-2 py-1">
|
|
379
|
+
{(ctx) => (
|
|
380
|
+
<TextInput value={ctx.item.name} onValueChange={(v) => ctx.setItem("name", v)} />
|
|
381
|
+
)}
|
|
382
|
+
</CrudSheet.Column>
|
|
383
|
+
<CrudSheet.Column key="email" header="Email" class="px-2 py-1">
|
|
384
|
+
{(ctx) => (
|
|
385
|
+
<TextInput value={ctx.item.email} onValueChange={(v) => ctx.setItem("email", v)} />
|
|
386
|
+
)}
|
|
387
|
+
</CrudSheet.Column>
|
|
388
|
+
</CrudSheet>
|
|
389
|
+
|
|
390
|
+
// Modal editing mode
|
|
391
|
+
<CrudSheet<User, UserFilter>
|
|
392
|
+
search={async (filter, page, sorts) => {
|
|
393
|
+
return await api.getUsers(filter, page, sorts);
|
|
394
|
+
}}
|
|
395
|
+
getItemKey={(item) => item.id}
|
|
396
|
+
persistKey="user-modal-crud"
|
|
397
|
+
itemsPerPage={20}
|
|
398
|
+
modalEdit={{
|
|
399
|
+
editItem: async (item) => {
|
|
400
|
+
const result = await dialog.show((close) => <UserEditDialog item={item} onClose={close} />);
|
|
401
|
+
return result === true;
|
|
402
|
+
},
|
|
403
|
+
deleteItems: async (items) => {
|
|
404
|
+
if (!confirm("Delete selected items?")) return false;
|
|
405
|
+
await api.deleteUsers(items.map((i) => i.id!));
|
|
406
|
+
return true;
|
|
407
|
+
},
|
|
408
|
+
}}
|
|
409
|
+
>
|
|
410
|
+
<CrudSheet.Column key="name" header="Name" editable class="px-2 py-1">
|
|
411
|
+
{(ctx) => <>{ctx.item.name}</>}
|
|
412
|
+
</CrudSheet.Column>
|
|
413
|
+
<CrudSheet.Column key="email" header="Email" class="px-2 py-1">
|
|
414
|
+
{(ctx) => <>{ctx.item.email}</>}
|
|
415
|
+
</CrudSheet.Column>
|
|
416
|
+
</CrudSheet>
|
|
417
|
+
|
|
418
|
+
// Select mode (for picker dialogs)
|
|
419
|
+
<CrudSheet<User, UserFilter>
|
|
420
|
+
search={async (filter, page, sorts) => {
|
|
421
|
+
return await api.getUsers(filter, page, sorts);
|
|
422
|
+
}}
|
|
423
|
+
getItemKey={(item) => item.id}
|
|
424
|
+
persistKey="user-select"
|
|
425
|
+
itemsPerPage={20}
|
|
426
|
+
selectMode="multi"
|
|
427
|
+
onSelect={(result) => {
|
|
428
|
+
// result.items: selected User[]
|
|
429
|
+
// result.keys: selected (string | number)[]
|
|
430
|
+
onConfirm(result.items);
|
|
431
|
+
}}
|
|
432
|
+
>
|
|
433
|
+
<CrudSheet.Column key="name" header="Name" class="px-2 py-1">
|
|
434
|
+
{(ctx) => <>{ctx.item.name}</>}
|
|
435
|
+
</CrudSheet.Column>
|
|
436
|
+
</CrudSheet>
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**CrudSheet Props:**
|
|
440
|
+
|
|
441
|
+
| Prop | Type | Default | Description |
|
|
442
|
+
|------|------|---------|-------------|
|
|
443
|
+
| `search` | `(filter: TFilter, page: number, sorts: SortingDef[]) => Promise<SearchResult<TItem>>` | **(required)** | Search function. `page=0` means no pagination |
|
|
444
|
+
| `getItemKey` | `(item: TItem) => string \| number \| undefined` | **(required)** | Unique key extractor for diff tracking |
|
|
445
|
+
| `persistKey` | `string` | - | LocalStorage key for column configuration |
|
|
446
|
+
| `itemsPerPage` | `number` | - | Items per page (enables pagination when set) |
|
|
447
|
+
| `editable` | `() => boolean` | `() => true` | Whether editing is allowed |
|
|
448
|
+
| `itemEditable` | `(item: TItem) => boolean` | `() => true` | Per-item edit control (modal edit mode) |
|
|
449
|
+
| `itemDeletable` | `(item: TItem) => boolean` | `() => true` | Per-item delete control |
|
|
450
|
+
| `filterInitial` | `TFilter` | - | Initial filter state |
|
|
451
|
+
| `items` | `TItem[]` | - | Controlled items (external state) |
|
|
452
|
+
| `onItemsChange` | `(items: TItem[]) => void` | - | Items change callback (controlled mode) |
|
|
453
|
+
| `inlineEdit` | `InlineEditConfig<TItem>` | - | Inline editing configuration (mutually exclusive with `modalEdit`) |
|
|
454
|
+
| `modalEdit` | `ModalEditConfig<TItem>` | - | Modal editing configuration (mutually exclusive with `inlineEdit`) |
|
|
455
|
+
| `excel` | `ExcelConfig<TItem>` | - | Excel download/upload configuration |
|
|
456
|
+
| `selectMode` | `"single" \| "multi"` | - | Select mode (disables editing, shows selection UI) |
|
|
457
|
+
| `onSelect` | `(result: SelectResult<TItem>) => void` | - | Select confirmation callback |
|
|
458
|
+
| `hideAutoTools` | `boolean` | - | Hide auto-generated toolbar buttons |
|
|
459
|
+
| `class` | `string` | - | CSS class |
|
|
460
|
+
|
|
461
|
+
**InlineEditConfig:**
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
interface InlineEditConfig<TItem> {
|
|
465
|
+
submit: (diffs: ArrayDiffs2Result<TItem>[]) => Promise<void>;
|
|
466
|
+
newItem: () => TItem;
|
|
467
|
+
deleteProp?: keyof TItem & string;
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
| Field | Description |
|
|
472
|
+
|-------|-------------|
|
|
473
|
+
| `submit` | Save function receiving changed items (inserted/updated/deleted diffs) |
|
|
474
|
+
| `newItem` | Factory function for creating new empty rows |
|
|
475
|
+
| `deleteProp` | Property name for soft-delete flag (e.g., `"isDeleted"`) |
|
|
476
|
+
|
|
477
|
+
**ModalEditConfig:**
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
interface ModalEditConfig<TItem> {
|
|
481
|
+
editItem: (item?: TItem) => Promise<boolean>;
|
|
482
|
+
deleteItems?: (items: TItem[]) => Promise<boolean>;
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
| Field | Description |
|
|
487
|
+
|-------|-------------|
|
|
488
|
+
| `editItem` | Open edit dialog. `undefined` = new item. Return `true` to refresh |
|
|
489
|
+
| `deleteItems` | Delete selected items. Return `true` to refresh |
|
|
490
|
+
|
|
491
|
+
**ExcelConfig:**
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
interface ExcelConfig<TItem> {
|
|
495
|
+
download: (items: TItem[]) => Promise<void>;
|
|
496
|
+
upload?: (file: File) => Promise<void>;
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**SelectResult:**
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
interface SelectResult<TItem> {
|
|
504
|
+
items: TItem[];
|
|
505
|
+
keys: (string | number)[];
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Sub-components:**
|
|
510
|
+
|
|
511
|
+
- `CrudSheet.Column` -- Column definition (extends `DataSheet.Column` props + `editable`)
|
|
512
|
+
- `CrudSheet.Filter` -- Filter form render prop
|
|
513
|
+
- `CrudSheet.Tools` -- Custom toolbar render prop
|
|
514
|
+
- `CrudSheet.Header` -- Header area above filter
|
|
515
|
+
|
|
516
|
+
**CrudSheet.Column Props:**
|
|
517
|
+
|
|
518
|
+
Inherits all `DataSheet.Column` props (`key`, `header`, `width`, `fixed`, `hidden`, `sortable`, `resizable`, `class`, `headerContent`, `headerStyle`, `summary`, `tooltip`, `collapse`) plus:
|
|
519
|
+
|
|
520
|
+
| Prop | Type | Default | Description |
|
|
521
|
+
|------|------|---------|-------------|
|
|
522
|
+
| `editable` | `boolean` | `false` | Wrap cell with edit link (modal edit mode only) |
|
|
523
|
+
| `children` | `(ctx: CrudSheetCellContext<TItem>) => JSX.Element` | **(required)** | Cell render function |
|
|
524
|
+
|
|
525
|
+
**CrudSheetCellContext:**
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
interface CrudSheetCellContext<TItem> {
|
|
529
|
+
item: TItem;
|
|
530
|
+
index: number;
|
|
531
|
+
row: number;
|
|
532
|
+
depth: number;
|
|
533
|
+
setItem: <TKey extends keyof TItem>(key: TKey, value: TItem[TKey]) => void;
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
`setItem` updates a specific field of the current row item (inline edit mode).
|
|
538
|
+
|
|
539
|
+
**CrudSheet.Filter:**
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
<CrudSheet.Filter>
|
|
543
|
+
{(filter, setFilter) => (
|
|
544
|
+
<FormGroup.Item label="Search">
|
|
545
|
+
<TextInput value={filter.keyword} onValueChange={(v) => setFilter("keyword", v)} />
|
|
546
|
+
</FormGroup.Item>
|
|
547
|
+
)}
|
|
548
|
+
</CrudSheet.Filter>
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Receives `filter` (store) and `setFilter` (`SetStoreFunction`) as render prop arguments.
|
|
552
|
+
|
|
553
|
+
**CrudSheet.Tools:**
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
<CrudSheet.Tools>
|
|
557
|
+
{(ctx) => (
|
|
558
|
+
<Button size="sm" onClick={() => ctx.refresh()}>Custom Refresh</Button>
|
|
559
|
+
)}
|
|
560
|
+
</CrudSheet.Tools>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
**CrudSheetContext (Tools render prop):**
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
interface CrudSheetContext<TItem> {
|
|
567
|
+
items(): TItem[];
|
|
568
|
+
selectedItems(): TItem[];
|
|
569
|
+
page(): number;
|
|
570
|
+
sorts(): SortingDef[];
|
|
571
|
+
busy(): boolean;
|
|
572
|
+
hasChanges(): boolean;
|
|
573
|
+
save(): Promise<void>;
|
|
574
|
+
refresh(): Promise<void>;
|
|
575
|
+
addItem(): void;
|
|
576
|
+
setPage(page: number): void;
|
|
577
|
+
setSorts(sorts: SortingDef[]): void;
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
**CrudSheet.Header:**
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
<CrudSheet.Header>
|
|
585
|
+
<h2 class="p-2 text-lg font-bold">User Management</h2>
|
|
586
|
+
</CrudSheet.Header>
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Renders static content above the filter area.
|
|
590
|
+
|
|
591
|
+
**Keyboard shortcuts:**
|
|
592
|
+
- `Ctrl+S` -- Save (inline edit mode)
|
|
593
|
+
- `Ctrl+Alt+L` -- Refresh
|
|
594
|
+
|
|
595
|
+
**Topbar integration:** When used inside `Topbar.Container`, CrudSheet automatically registers Save and Refresh buttons in the topbar via `createTopbarActions`.
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## CrudDetail
|
|
600
|
+
|
|
601
|
+
CRUD detail form component for single-record editing. Provides load, save, soft-delete/restore, change detection, and topbar action integration. Works both as a standalone page component and inside a `Dialog` (modal mode).
|
|
602
|
+
|
|
603
|
+
```tsx
|
|
604
|
+
import { CrudDetail, type CrudDetailInfo } from "@simplysm/solid";
|
|
605
|
+
import { FormTable, TextInput, NumberInput } from "@simplysm/solid";
|
|
606
|
+
|
|
607
|
+
interface User {
|
|
608
|
+
id?: number;
|
|
609
|
+
name: string;
|
|
610
|
+
age: number;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Page mode (standalone)
|
|
614
|
+
<CrudDetail<User>
|
|
615
|
+
load={async () => {
|
|
616
|
+
const user = await api.getUser(userId);
|
|
617
|
+
return {
|
|
618
|
+
data: user,
|
|
619
|
+
info: { isNew: user.id == null, isDeleted: false, lastModifiedAt: user.updatedAt },
|
|
620
|
+
};
|
|
621
|
+
}}
|
|
622
|
+
submit={async (data) => {
|
|
623
|
+
await api.saveUser(data);
|
|
624
|
+
return true; // return true to trigger success notification + refresh/close
|
|
625
|
+
}}
|
|
626
|
+
toggleDelete={async (del) => {
|
|
627
|
+
await api.toggleDeleteUser(userId, del);
|
|
628
|
+
return true;
|
|
629
|
+
}}
|
|
630
|
+
>
|
|
631
|
+
{(ctx) => (
|
|
632
|
+
<FormTable>
|
|
633
|
+
<tbody>
|
|
634
|
+
<tr>
|
|
635
|
+
<th>Name</th>
|
|
636
|
+
<td>
|
|
637
|
+
<TextInput value={ctx.data.name} onValueChange={(v) => ctx.setData("name", v)} />
|
|
638
|
+
</td>
|
|
639
|
+
</tr>
|
|
640
|
+
<tr>
|
|
641
|
+
<th>Age</th>
|
|
642
|
+
<td>
|
|
643
|
+
<NumberInput value={ctx.data.age} onValueChange={(v) => ctx.setData("age", v!)} />
|
|
644
|
+
</td>
|
|
645
|
+
</tr>
|
|
646
|
+
</tbody>
|
|
647
|
+
</FormTable>
|
|
648
|
+
)}
|
|
649
|
+
</CrudDetail>
|
|
650
|
+
|
|
651
|
+
// Modal mode (inside Dialog via useDialog)
|
|
652
|
+
const dialog = useDialog();
|
|
653
|
+
|
|
654
|
+
const handleEdit = async () => {
|
|
655
|
+
const result = await dialog.show<boolean>(
|
|
656
|
+
() => (
|
|
657
|
+
<CrudDetail<User>
|
|
658
|
+
load={async () => ({ data: user, info: { isNew: false, isDeleted: false } })}
|
|
659
|
+
submit={async (data) => { await api.saveUser(data); return true; }}
|
|
660
|
+
>
|
|
661
|
+
{(ctx) => (
|
|
662
|
+
<FormTable>
|
|
663
|
+
<tbody>
|
|
664
|
+
<tr>
|
|
665
|
+
<th>Name</th>
|
|
666
|
+
<td><TextInput value={ctx.data.name} onValueChange={(v) => ctx.setData("name", v)} /></td>
|
|
667
|
+
</tr>
|
|
668
|
+
</tbody>
|
|
669
|
+
</FormTable>
|
|
670
|
+
)}
|
|
671
|
+
</CrudDetail>
|
|
672
|
+
),
|
|
673
|
+
{ header: "Edit User", width: 500 },
|
|
674
|
+
);
|
|
675
|
+
if (result) { /* saved successfully */ }
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// With custom tools and before/after slots
|
|
679
|
+
<CrudDetail<User>
|
|
680
|
+
load={loadUser}
|
|
681
|
+
submit={saveUser}
|
|
682
|
+
editable={hasEditPermission()}
|
|
683
|
+
deletable={hasDeletePermission()}
|
|
684
|
+
>
|
|
685
|
+
{(ctx) => (
|
|
686
|
+
<>
|
|
687
|
+
<CrudDetail.Tools>
|
|
688
|
+
<Button size="sm" variant="ghost" onClick={() => ctx.refresh()}>Custom Refresh</Button>
|
|
689
|
+
</CrudDetail.Tools>
|
|
690
|
+
<CrudDetail.Before>
|
|
691
|
+
<div class="p-2 bg-info-50">Info banner (outside form)</div>
|
|
692
|
+
</CrudDetail.Before>
|
|
693
|
+
<FormTable>
|
|
694
|
+
<tbody>
|
|
695
|
+
<tr>
|
|
696
|
+
<th>Name</th>
|
|
697
|
+
<td><TextInput value={ctx.data.name} onValueChange={(v) => ctx.setData("name", v)} /></td>
|
|
698
|
+
</tr>
|
|
699
|
+
</tbody>
|
|
700
|
+
</FormTable>
|
|
701
|
+
<CrudDetail.After>
|
|
702
|
+
<div class="p-2">Footer content (outside form)</div>
|
|
703
|
+
</CrudDetail.After>
|
|
704
|
+
</>
|
|
705
|
+
)}
|
|
706
|
+
</CrudDetail>
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**CrudDetail Props:**
|
|
710
|
+
|
|
711
|
+
| Prop | Type | Default | Description |
|
|
712
|
+
|------|------|---------|-------------|
|
|
713
|
+
| `load` | `() => Promise<{ data: TData; info: CrudDetailInfo }>` | **(required)** | Load function returning data and metadata |
|
|
714
|
+
| `children` | `(ctx: CrudDetailContext<TData>) => JSX.Element` | **(required)** | Render prop receiving context |
|
|
715
|
+
| `submit` | `(data: TData) => Promise<boolean \| undefined>` | - | Save function. Return `true` to trigger success notification |
|
|
716
|
+
| `toggleDelete` | `(del: boolean) => Promise<boolean \| undefined>` | - | Soft-delete/restore function. `del=true` for delete, `false` for restore |
|
|
717
|
+
| `editable` | `() => boolean` | `() => true` | Whether editing is allowed |
|
|
718
|
+
| `deletable` | `() => boolean` | `() => true` | Whether delete/restore is allowed |
|
|
719
|
+
| `data` | `TData` | - | Controlled data state |
|
|
720
|
+
| `onDataChange` | `(data: TData) => void` | - | Data change callback (controlled mode) |
|
|
721
|
+
| `class` | `string` | - | CSS class |
|
|
722
|
+
|
|
723
|
+
**CrudDetailInfo:**
|
|
724
|
+
|
|
725
|
+
```typescript
|
|
726
|
+
interface CrudDetailInfo {
|
|
727
|
+
isNew: boolean;
|
|
728
|
+
isDeleted: boolean;
|
|
729
|
+
lastModifiedAt?: DateTime;
|
|
730
|
+
lastModifiedBy?: string;
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
**CrudDetailContext (render prop argument):**
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
interface CrudDetailContext<TData> {
|
|
738
|
+
data: TData;
|
|
739
|
+
setData: SetStoreFunction<TData>;
|
|
740
|
+
info: () => CrudDetailInfo;
|
|
741
|
+
busy: () => boolean;
|
|
742
|
+
hasChanges: () => boolean;
|
|
743
|
+
save: () => Promise<void>;
|
|
744
|
+
refresh: () => Promise<void>;
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
**Sub-components:**
|
|
749
|
+
- `CrudDetail.Tools` -- Custom toolbar buttons (rendered in the toolbar area)
|
|
750
|
+
- `CrudDetail.Before` -- Content rendered before the form (outside `<form>`)
|
|
751
|
+
- `CrudDetail.After` -- Content rendered after the form (outside `<form>`)
|
|
752
|
+
|
|
753
|
+
**Page vs Modal mode:**
|
|
754
|
+
- **Page mode**: Toolbar with Save/Refresh/Delete buttons appears at the top. When inside `Topbar.Container`, Save and Refresh are also registered in the topbar via `createTopbarActions`.
|
|
755
|
+
- **Modal mode** (inside `useDialog`): Refresh button appears in the dialog header. Save/Delete buttons appear in the bottom bar. On save/delete success, the dialog closes with `true` as the return value.
|
|
756
|
+
|
|
757
|
+
**Keyboard shortcuts:**
|
|
758
|
+
- `Ctrl+S` -- Save
|
|
759
|
+
- `Ctrl+Alt+L` -- Refresh
|
|
760
|
+
|
|
761
|
+
**Change detection:** On refresh, if unsaved changes exist, a confirmation dialog is shown. On save, if no changes exist (and not a new record), an info notification is shown instead.
|
package/docs/disclosure.md
CHANGED
|
@@ -59,37 +59,56 @@ Animation is automatically disabled when `prefers-reduced-motion` is set.
|
|
|
59
59
|
|
|
60
60
|
## Dropdown
|
|
61
61
|
|
|
62
|
-
Positioned dropdown popup
|
|
62
|
+
Positioned dropdown popup using compound components. Trigger click auto-toggles open state.
|
|
63
63
|
|
|
64
64
|
```tsx
|
|
65
|
-
import { Dropdown, Button } from "@simplysm/solid";
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
import { Dropdown, Button, List } from "@simplysm/solid";
|
|
66
|
+
|
|
67
|
+
// Trigger/Content compound components
|
|
68
|
+
<Dropdown>
|
|
69
|
+
<Dropdown.Trigger>
|
|
70
|
+
<Button>Open</Button>
|
|
71
|
+
</Dropdown.Trigger>
|
|
72
|
+
<Dropdown.Content>
|
|
73
|
+
<p class="p-3">Dropdown content</p>
|
|
74
|
+
</Dropdown.Content>
|
|
75
|
+
</Dropdown>
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
<Dropdown
|
|
73
|
-
<
|
|
77
|
+
// Controlled open state
|
|
78
|
+
<Dropdown open={open()} onOpenChange={setOpen}>
|
|
79
|
+
<Dropdown.Trigger>
|
|
80
|
+
<Button>Open</Button>
|
|
81
|
+
</Dropdown.Trigger>
|
|
82
|
+
<Dropdown.Content>
|
|
83
|
+
<p class="p-3">Dropdown content</p>
|
|
84
|
+
</Dropdown.Content>
|
|
74
85
|
</Dropdown>
|
|
75
86
|
|
|
76
|
-
// Context menu (absolute position)
|
|
87
|
+
// Context menu (absolute position, no Trigger)
|
|
77
88
|
<Dropdown position={{ x: 100, y: 200 }} open={menuOpen()} onOpenChange={setMenuOpen}>
|
|
78
|
-
<
|
|
79
|
-
<List
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
<Dropdown.Content>
|
|
90
|
+
<List inset>
|
|
91
|
+
<List.Item>Menu item 1</List.Item>
|
|
92
|
+
<List.Item>Menu item 2</List.Item>
|
|
93
|
+
</List>
|
|
94
|
+
</Dropdown.Content>
|
|
82
95
|
</Dropdown>
|
|
83
96
|
```
|
|
84
97
|
|
|
85
98
|
| Prop | Type | Default | Description |
|
|
86
99
|
|------|------|---------|-------------|
|
|
87
|
-
| `
|
|
88
|
-
| `position` | `{ x: number; y: number }` | - | Absolute position (mutually exclusive with triggerRef) |
|
|
100
|
+
| `position` | `{ x: number; y: number }` | - | Absolute position (context menu mode, no Trigger needed) |
|
|
89
101
|
| `open` | `boolean` | - | Open state |
|
|
90
102
|
| `onOpenChange` | `(open: boolean) => void` | - | State change callback |
|
|
91
103
|
| `maxHeight` | `number` | `300` | Maximum height (px) |
|
|
104
|
+
| `disabled` | `boolean` | - | Disabled state (Trigger click ignored) |
|
|
92
105
|
| `keyboardNav` | `boolean` | - | Enable keyboard navigation (used by Select, etc.) |
|
|
106
|
+
| `class` | `string` | - | Additional CSS class for popup |
|
|
107
|
+
| `style` | `JSX.CSSProperties` | - | Inline style for popup |
|
|
108
|
+
|
|
109
|
+
**Sub-components:**
|
|
110
|
+
- `Dropdown.Trigger` -- Trigger element wrapper (click to toggle)
|
|
111
|
+
- `Dropdown.Content` -- Dropdown popup content
|
|
93
112
|
|
|
94
113
|
---
|
|
95
114
|
|
|
@@ -106,28 +125,32 @@ import { createSignal } from "solid-js";
|
|
|
106
125
|
const [open, setOpen] = createSignal(false);
|
|
107
126
|
|
|
108
127
|
<Button onClick={() => setOpen(true)}>Open</Button>
|
|
109
|
-
<Dialog
|
|
110
|
-
|
|
111
|
-
open={open()}
|
|
112
|
-
onOpenChange={setOpen}
|
|
113
|
-
closeOnBackdrop
|
|
114
|
-
width={600}
|
|
115
|
-
>
|
|
128
|
+
<Dialog open={open()} onOpenChange={setOpen} closeOnBackdrop width={600}>
|
|
129
|
+
<Dialog.Header>Dialog Title</Dialog.Header>
|
|
116
130
|
<div class="p-4">
|
|
117
131
|
Dialog content
|
|
118
132
|
</div>
|
|
119
133
|
</Dialog>
|
|
120
134
|
|
|
135
|
+
// With header action buttons
|
|
136
|
+
<Dialog open={open()} onOpenChange={setOpen}>
|
|
137
|
+
<Dialog.Header>My Dialog</Dialog.Header>
|
|
138
|
+
<Dialog.Action>
|
|
139
|
+
<Button size="sm" variant="ghost">Help</Button>
|
|
140
|
+
</Dialog.Action>
|
|
141
|
+
<div class="p-4">Dialog content</div>
|
|
142
|
+
</Dialog>
|
|
143
|
+
|
|
121
144
|
// Floating mode (no backdrop)
|
|
122
|
-
<Dialog
|
|
123
|
-
|
|
124
|
-
open={open()}
|
|
125
|
-
onOpenChange={setOpen}
|
|
126
|
-
float
|
|
127
|
-
position="bottom-right"
|
|
128
|
-
>
|
|
145
|
+
<Dialog open={open()} onOpenChange={setOpen} float position="bottom-right">
|
|
146
|
+
<Dialog.Header>Notification</Dialog.Header>
|
|
129
147
|
<div class="p-4">Floating dialog</div>
|
|
130
148
|
</Dialog>
|
|
149
|
+
|
|
150
|
+
// No header (content only)
|
|
151
|
+
<Dialog open={open()} onOpenChange={setOpen}>
|
|
152
|
+
<div class="p-4">Dialog without header</div>
|
|
153
|
+
</Dialog>
|
|
131
154
|
```
|
|
132
155
|
|
|
133
156
|
**Programmatic usage with `useDialog`:**
|
|
@@ -158,7 +181,7 @@ function MyPage() {
|
|
|
158
181
|
const handleOpen = async () => {
|
|
159
182
|
const result = await dialog.show<string>(
|
|
160
183
|
() => <EditDialog />,
|
|
161
|
-
{
|
|
184
|
+
{ header: "Edit Name", width: 400, closeOnBackdrop: true },
|
|
162
185
|
);
|
|
163
186
|
if (result != null) {
|
|
164
187
|
// result is the value passed to dialogInstance.close()
|
|
@@ -176,8 +199,6 @@ function MyPage() {
|
|
|
176
199
|
|------|------|---------|-------------|
|
|
177
200
|
| `open` | `boolean` | - | Open state |
|
|
178
201
|
| `onOpenChange` | `(open: boolean) => void` | - | State change callback |
|
|
179
|
-
| `title` | `string` | **(required)** | Modal title |
|
|
180
|
-
| `hideHeader` | `boolean` | - | Hide header |
|
|
181
202
|
| `closable` | `boolean` | `true` | Show close button |
|
|
182
203
|
| `closeOnBackdrop` | `boolean` | - | Close on backdrop click |
|
|
183
204
|
| `closeOnEscape` | `boolean` | `true` | Close on Escape key |
|
|
@@ -190,19 +211,28 @@ function MyPage() {
|
|
|
190
211
|
| `minWidth` | `number` | - | Minimum width (px) |
|
|
191
212
|
| `minHeight` | `number` | - | Minimum height (px) |
|
|
192
213
|
| `position` | `"bottom-right" \| "top-right"` | - | Fixed position |
|
|
193
|
-
| `headerAction` | `JSX.Element` | - | Header action area |
|
|
194
214
|
| `headerStyle` | `JSX.CSSProperties \| string` | - | Header style |
|
|
195
215
|
| `canDeactivate` | `() => boolean` | - | Pre-close confirmation function |
|
|
196
216
|
| `onCloseComplete` | `() => void` | - | Post-close animation callback |
|
|
197
217
|
| `class` | `string` | - | Additional CSS class applied to the dialog element |
|
|
198
218
|
|
|
219
|
+
**Sub-components:**
|
|
220
|
+
- `Dialog.Header` -- Dialog title (renders as `<h5>`, sets `aria-labelledby` on the dialog)
|
|
221
|
+
- `Dialog.Action` -- Header action area (rendered between header text and close button)
|
|
222
|
+
|
|
223
|
+
> The header bar (including close button) is only rendered when `Dialog.Header` is present. If no `Dialog.Header` is provided, the dialog renders content only with no header bar.
|
|
224
|
+
|
|
199
225
|
**useDialog API:**
|
|
200
226
|
|
|
201
227
|
| Method | Signature | Description |
|
|
202
228
|
|--------|-----------|-------------|
|
|
203
229
|
| `show` | `<T>(factory: () => JSX.Element, options: DialogShowOptions) => Promise<T \| undefined>` | Open dialog, returns result on close |
|
|
204
230
|
|
|
205
|
-
`DialogShowOptions` accepts all Dialog props except `open`, `onOpenChange`, and `children
|
|
231
|
+
`DialogShowOptions` accepts all Dialog props except `open`, `onOpenChange`, `onCloseComplete`, and `children`, plus:
|
|
232
|
+
|
|
233
|
+
| Option | Type | Description |
|
|
234
|
+
|--------|------|-------------|
|
|
235
|
+
| `header` | `JSX.Element` | Dialog header content (renders inside `Dialog.Header`) |
|
|
206
236
|
|
|
207
237
|
**useDialogInstance API:**
|
|
208
238
|
|
package/docs/form-controls.md
CHANGED
|
@@ -31,6 +31,8 @@ Text input field with format mask and IME (Korean, etc.) composition support.
|
|
|
31
31
|
|
|
32
32
|
```tsx
|
|
33
33
|
import { TextInput } from "@simplysm/solid";
|
|
34
|
+
import { IconSearch } from "@tabler/icons-solidjs";
|
|
35
|
+
import { Icon } from "@simplysm/solid";
|
|
34
36
|
|
|
35
37
|
// Basic usage
|
|
36
38
|
<TextInput value={name()} onValueChange={setName} placeholder="Enter name" />
|
|
@@ -41,6 +43,11 @@ import { TextInput } from "@simplysm/solid";
|
|
|
41
43
|
// Format mask (e.g., phone number)
|
|
42
44
|
<TextInput format="XXX-XXXX-XXXX" value={phone()} onValueChange={setPhone} />
|
|
43
45
|
|
|
46
|
+
// With prefix
|
|
47
|
+
<TextInput value={query()} onValueChange={setQuery}>
|
|
48
|
+
<TextInput.Prefix><Icon icon={IconSearch} /></TextInput.Prefix>
|
|
49
|
+
</TextInput>
|
|
50
|
+
|
|
44
51
|
// With validation
|
|
45
52
|
<TextInput required minLength={3} value={name()} onValueChange={setName} />
|
|
46
53
|
<TextInput
|
|
@@ -56,7 +63,6 @@ import { TextInput } from "@simplysm/solid";
|
|
|
56
63
|
| `onValueChange` | `(value: string) => void` | - | Value change callback |
|
|
57
64
|
| `type` | `"text" \| "password" \| "email"` | `"text"` | Input type |
|
|
58
65
|
| `format` | `string` | - | Input format (`X` represents character position, rest are separators) |
|
|
59
|
-
| `prefixIcon` | `Component<TablerIconProps>` | - | Prefix icon (Tabler Icons component) |
|
|
60
66
|
| `placeholder` | `string` | - | Placeholder |
|
|
61
67
|
| `title` | `string` | - | Tooltip title |
|
|
62
68
|
| `autocomplete` | `JSX.HTMLAutocomplete` | - | HTML autocomplete attribute |
|
|
@@ -73,6 +79,9 @@ import { TextInput } from "@simplysm/solid";
|
|
|
73
79
|
| `validate` | `(value: string) => string \| undefined` | - | Custom validation function |
|
|
74
80
|
| `touchMode` | `boolean` | - | Show error only after field loses focus |
|
|
75
81
|
|
|
82
|
+
**Sub-components:**
|
|
83
|
+
- `TextInput.Prefix` -- Prefix element (icon, text, etc.) displayed before the input
|
|
84
|
+
|
|
76
85
|
---
|
|
77
86
|
|
|
78
87
|
## NumberInput
|
|
@@ -91,6 +100,11 @@ import { NumberInput } from "@simplysm/solid";
|
|
|
91
100
|
// Minimum 2 decimal places
|
|
92
101
|
<NumberInput value={price()} minDigits={2} />
|
|
93
102
|
|
|
103
|
+
// With prefix
|
|
104
|
+
<NumberInput value={price()} onValueChange={setPrice}>
|
|
105
|
+
<NumberInput.Prefix>₩</NumberInput.Prefix>
|
|
106
|
+
</NumberInput>
|
|
107
|
+
|
|
94
108
|
// With validation
|
|
95
109
|
<NumberInput required min={0} max={100} value={score()} onValueChange={setScore} />
|
|
96
110
|
```
|
|
@@ -101,7 +115,6 @@ import { NumberInput } from "@simplysm/solid";
|
|
|
101
115
|
| `onValueChange` | `(value: number \| undefined) => void` | - | Value change callback |
|
|
102
116
|
| `comma` | `boolean` | `true` | Show thousand separators |
|
|
103
117
|
| `minDigits` | `number` | - | Minimum decimal places |
|
|
104
|
-
| `prefixIcon` | `Component<TablerIconProps>` | - | Prefix icon (Tabler Icons component) |
|
|
105
118
|
| `placeholder` | `string` | - | Placeholder |
|
|
106
119
|
| `title` | `string` | - | Tooltip title |
|
|
107
120
|
| `disabled` | `boolean` | - | Disabled state |
|
|
@@ -116,6 +129,9 @@ import { NumberInput } from "@simplysm/solid";
|
|
|
116
129
|
| `validate` | `(value: number \| undefined) => string \| undefined` | - | Custom validation function |
|
|
117
130
|
| `touchMode` | `boolean` | - | Show error only after field loses focus |
|
|
118
131
|
|
|
132
|
+
**Sub-components:**
|
|
133
|
+
- `NumberInput.Prefix` -- Prefix element (currency symbol, icon, etc.) displayed before the input
|
|
134
|
+
|
|
119
135
|
---
|
|
120
136
|
|
|
121
137
|
## DatePicker
|
|
@@ -296,7 +312,6 @@ const [to, setTo] = createSignal<DateOnly>();
|
|
|
296
312
|
| `required` | `boolean` | - | Required field |
|
|
297
313
|
| `disabled` | `boolean` | - | Disabled state |
|
|
298
314
|
| `size` | `"xs" \| "sm" \| "lg" \| "xl"` | - | Size |
|
|
299
|
-
| `periodLabels` | `Partial<Record<DateRangePeriodType, string>>` | `{ day: "일", month: "월", range: "범위" }` | Period type labels (Korean by default) |
|
|
300
315
|
| `class` | `string` | - | Additional CSS class |
|
|
301
316
|
| `style` | `JSX.CSSProperties` | - | Inline style |
|
|
302
317
|
|