@simplysm/solid 13.0.72 → 13.0.74

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.
Files changed (210) hide show
  1. package/README.md +209 -202
  2. package/dist/components/data/calendar/Calendar.d.ts.map +1 -1
  3. package/dist/components/data/calendar/Calendar.js +3 -11
  4. package/dist/components/data/calendar/Calendar.js.map +2 -2
  5. package/dist/components/data/sheet/DataSheet.js +10 -10
  6. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  7. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
  8. package/dist/components/data/sheet/DataSheetConfigDialog.js +27 -9
  9. package/dist/components/data/sheet/DataSheetConfigDialog.js.map +2 -2
  10. package/dist/components/disclosure/Dialog.d.ts +1 -1
  11. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  12. package/dist/components/disclosure/Dialog.js +5 -5
  13. package/dist/components/disclosure/Dialog.js.map +2 -2
  14. package/dist/components/disclosure/dialogZIndex.d.ts +1 -1
  15. package/dist/components/features/crud-detail/CrudDetail.js +23 -23
  16. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  17. package/dist/components/features/crud-sheet/CrudSheet.js +49 -49
  18. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  19. package/dist/components/features/crud-sheet/types.d.ts +4 -4
  20. package/dist/components/features/crud-sheet/types.d.ts.map +1 -1
  21. package/dist/components/features/data-select-button/DataSelectButton.d.ts +25 -7
  22. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  23. package/dist/components/features/data-select-button/DataSelectButton.js +27 -12
  24. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  25. package/dist/components/features/permission-table/PermissionTable.js +4 -4
  26. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  27. package/dist/components/features/shared-data/SharedDataSelect.d.ts +22 -10
  28. package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
  29. package/dist/components/features/shared-data/SharedDataSelect.js +113 -29
  30. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  31. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts +3 -3
  32. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
  33. package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
  34. package/dist/components/features/shared-data/SharedDataSelectList.js +5 -4
  35. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  36. package/dist/components/feedback/notification/NotificationBanner.js +3 -3
  37. package/dist/components/feedback/notification/NotificationBanner.js.map +2 -2
  38. package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
  39. package/dist/components/feedback/notification/NotificationBell.js +12 -5
  40. package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
  41. package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
  42. package/dist/components/feedback/notification/NotificationProvider.js +3 -1
  43. package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
  44. package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
  45. package/dist/components/form-control/ThemeToggle.js +9 -6
  46. package/dist/components/form-control/ThemeToggle.js.map +2 -2
  47. package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
  48. package/dist/components/form-control/checkbox/Checkbox.js +3 -1
  49. package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
  50. package/dist/components/form-control/checkbox/CheckboxGroup.js +1 -1
  51. package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
  52. package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
  53. package/dist/components/form-control/checkbox/Radio.js +3 -1
  54. package/dist/components/form-control/checkbox/Radio.js.map +2 -2
  55. package/dist/components/form-control/checkbox/RadioGroup.js +1 -1
  56. package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
  57. package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
  58. package/dist/components/form-control/color-picker/ColorPicker.js +3 -1
  59. package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
  60. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  61. package/dist/components/form-control/combobox/Combobox.js +9 -5
  62. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  63. package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -9
  64. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  65. package/dist/components/form-control/editor/EditorToolbar.js +3 -3
  66. package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
  67. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  68. package/dist/components/form-control/field/DatePicker.js +9 -3
  69. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  70. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  71. package/dist/components/form-control/field/DateTimePicker.js +9 -3
  72. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  73. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  74. package/dist/components/form-control/field/NumberInput.js +9 -3
  75. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  76. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  77. package/dist/components/form-control/field/TextInput.js +10 -4
  78. package/dist/components/form-control/field/TextInput.js.map +2 -2
  79. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  80. package/dist/components/form-control/field/Textarea.js +9 -3
  81. package/dist/components/form-control/field/Textarea.js.map +2 -2
  82. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  83. package/dist/components/form-control/field/TimePicker.js +9 -3
  84. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  85. package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
  86. package/dist/components/form-control/numpad/Numpad.js +5 -1
  87. package/dist/components/form-control/numpad/Numpad.js.map +2 -2
  88. package/dist/components/form-control/select/Select.js +7 -7
  89. package/dist/components/form-control/select/Select.js.map +2 -2
  90. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  91. package/dist/components/form-control/state-preset/StatePreset.js +42 -20
  92. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  93. package/dist/components/layout/sidebar/SidebarContainer.js +3 -3
  94. package/dist/components/layout/sidebar/SidebarContainer.js.map +2 -2
  95. package/dist/components/layout/sidebar/SidebarMenu.d.ts.map +1 -1
  96. package/dist/components/layout/sidebar/SidebarMenu.js +5 -2
  97. package/dist/components/layout/sidebar/SidebarMenu.js.map +2 -2
  98. package/dist/components/layout/topbar/Topbar.js +3 -4
  99. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  100. package/dist/components/layout/topbar/TopbarMenu.js +3 -3
  101. package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
  102. package/dist/hooks/createSelectionGroup.d.ts +2 -2
  103. package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
  104. package/dist/hooks/createSelectionGroup.js +5 -2
  105. package/dist/hooks/createSelectionGroup.js.map +2 -2
  106. package/dist/providers/i18n/I18nContext.d.ts +0 -4
  107. package/dist/providers/i18n/I18nContext.d.ts.map +1 -1
  108. package/dist/providers/i18n/I18nContext.js +1 -5
  109. package/dist/providers/i18n/I18nContext.js.map +2 -2
  110. package/dist/providers/i18n/locales/en.d.ts +38 -0
  111. package/dist/providers/i18n/locales/en.d.ts.map +1 -1
  112. package/dist/providers/i18n/locales/en.js +39 -1
  113. package/dist/providers/i18n/locales/en.js.map +1 -1
  114. package/dist/providers/i18n/locales/ko.d.ts +38 -0
  115. package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
  116. package/dist/providers/i18n/locales/ko.js +39 -1
  117. package/dist/providers/i18n/locales/ko.js.map +1 -1
  118. package/package.json +6 -6
  119. package/src/components/data/calendar/Calendar.tsx +3 -4
  120. package/src/components/data/sheet/DataSheet.tsx +11 -11
  121. package/src/components/data/sheet/DataSheetConfigDialog.tsx +12 -10
  122. package/src/components/data/sheet/types.ts +1 -1
  123. package/src/components/disclosure/Dialog.tsx +10 -10
  124. package/src/components/disclosure/dialogZIndex.ts +1 -1
  125. package/src/components/features/crud-detail/CrudDetail.tsx +25 -25
  126. package/src/components/features/crud-sheet/CrudSheet.tsx +53 -53
  127. package/src/components/features/crud-sheet/types.ts +4 -4
  128. package/src/components/features/data-select-button/DataSelectButton.tsx +51 -21
  129. package/src/components/features/permission-table/PermissionTable.tsx +3 -3
  130. package/src/components/features/shared-data/SharedDataSelect.tsx +172 -33
  131. package/src/components/features/shared-data/SharedDataSelectButton.tsx +3 -2
  132. package/src/components/features/shared-data/SharedDataSelectList.tsx +4 -4
  133. package/src/components/feedback/notification/NotificationBanner.tsx +3 -3
  134. package/src/components/feedback/notification/NotificationBell.tsx +6 -4
  135. package/src/components/feedback/notification/NotificationProvider.tsx +3 -1
  136. package/src/components/form-control/ThemeToggle.tsx +10 -6
  137. package/src/components/form-control/checkbox/Checkbox.tsx +4 -1
  138. package/src/components/form-control/checkbox/CheckboxGroup.tsx +1 -1
  139. package/src/components/form-control/checkbox/Radio.tsx +4 -1
  140. package/src/components/form-control/checkbox/RadioGroup.tsx +1 -1
  141. package/src/components/form-control/color-picker/ColorPicker.tsx +4 -1
  142. package/src/components/form-control/combobox/Combobox.tsx +6 -3
  143. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +8 -8
  144. package/src/components/form-control/editor/EditorToolbar.tsx +23 -23
  145. package/src/components/form-control/field/DatePicker.tsx +6 -3
  146. package/src/components/form-control/field/DateTimePicker.tsx +6 -3
  147. package/src/components/form-control/field/NumberInput.tsx +6 -3
  148. package/src/components/form-control/field/TextInput.tsx +7 -4
  149. package/src/components/form-control/field/Textarea.tsx +6 -3
  150. package/src/components/form-control/field/TimePicker.tsx +6 -3
  151. package/src/components/form-control/numpad/Numpad.tsx +3 -1
  152. package/src/components/form-control/select/Select.tsx +7 -7
  153. package/src/components/form-control/state-preset/StatePreset.tsx +14 -12
  154. package/src/components/layout/sidebar/SidebarContainer.tsx +3 -3
  155. package/src/components/layout/sidebar/SidebarMenu.tsx +3 -1
  156. package/src/components/layout/topbar/Topbar.tsx +3 -3
  157. package/src/components/layout/topbar/TopbarMenu.tsx +3 -3
  158. package/src/hooks/createSelectionGroup.tsx +8 -4
  159. package/src/providers/i18n/I18nContext.tsx +0 -7
  160. package/src/providers/i18n/locales/en.ts +38 -0
  161. package/src/providers/i18n/locales/ko.ts +38 -0
  162. package/tailwind.config.ts +2 -2
  163. package/tests/components/data/kanban/Kanban.selection.spec.tsx +34 -24
  164. package/tests/components/disclosure/Dialog.spec.tsx +28 -28
  165. package/tests/components/disclosure/DialogProvider.spec.tsx +51 -25
  166. package/tests/components/features/address/AddressSearch.spec.tsx +12 -4
  167. package/tests/components/features/crud-detail/CrudDetail.spec.tsx +1 -0
  168. package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +30 -6
  169. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +77 -56
  170. package/tests/components/features/permission-table/PermissionTable.spec.tsx +12 -8
  171. package/tests/components/features/shared-data/SharedDataSelect.spec.tsx +172 -0
  172. package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +14 -2
  173. package/tests/components/feedback/notification/LiveRegion.spec.tsx +20 -9
  174. package/tests/components/feedback/notification/NotificationBanner.spec.tsx +64 -46
  175. package/tests/components/feedback/notification/NotificationBell.spec.tsx +70 -51
  176. package/tests/components/feedback/notification/NotificationContext.spec.tsx +105 -78
  177. package/tests/components/form-control/checkbox/Checkbox.spec.tsx +25 -20
  178. package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +53 -30
  179. package/tests/components/form-control/checkbox/Radio.spec.tsx +25 -20
  180. package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +53 -30
  181. package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +24 -15
  182. package/tests/components/form-control/combobox/Combobox.spec.tsx +92 -59
  183. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +2 -2
  184. package/tests/components/form-control/field/DatePicker.spec.tsx +50 -44
  185. package/tests/components/form-control/field/DateTimePicker.spec.tsx +51 -45
  186. package/tests/components/form-control/field/NumberInput.spec.tsx +53 -47
  187. package/tests/components/form-control/field/TextInput.spec.tsx +50 -44
  188. package/tests/components/form-control/field/Textarea.spec.tsx +35 -29
  189. package/tests/components/form-control/field/TimePicker.spec.tsx +43 -37
  190. package/tests/components/form-control/numpad/Numpad.spec.tsx +175 -25
  191. package/tests/components/form-control/select/Select.spec.tsx +5 -0
  192. package/tests/components/form-control/select/SelectItem.spec.tsx +1 -0
  193. package/tests/components/layout/sidebar/Sidebar.spec.tsx +79 -35
  194. package/tests/components/layout/sidebar/SidebarContainer.spec.tsx +1 -0
  195. package/tests/components/layout/sidebar/SidebarMenu.spec.tsx +28 -17
  196. package/tests/components/layout/topbar/TopbarActions.spec.tsx +41 -23
  197. package/tests/components/layout/topbar/createTopbarActions.spec.tsx +1 -0
  198. package/tests/hooks/usePrint.spec.tsx +1 -1
  199. package/tests/hooks/useRouterLink.spec.tsx +2 -0
  200. package/tests/hooks/useSyncConfig.spec.tsx +1 -0
  201. package/tests/providers/ErrorLoggerProvider.spec.tsx +1 -0
  202. package/tests/providers/PwaUpdateProvider.spec.tsx +16 -6
  203. package/tests/providers/ServiceClientContext.spec.tsx +40 -25
  204. package/tests/providers/i18n/I18nContext.spec.tsx +3 -4
  205. package/tests/providers/shared-data/SharedDataProvider.spec.tsx +2 -0
  206. package/dist/hooks/usePrint.d.ts +0 -3
  207. package/dist/hooks/usePrint.d.ts.map +0 -1
  208. package/dist/hooks/usePrint.js +0 -5
  209. package/dist/hooks/usePrint.js.map +0 -6
  210. package/src/hooks/usePrint.ts +0 -2
@@ -21,7 +21,7 @@ import { DataSheet } from "../../data/sheet/DataSheet";
21
21
  import { DataSheetColumn } from "../../data/sheet/DataSheetColumn";
22
22
  import { BusyContainer } from "../../feedback/busy/BusyContainer";
23
23
  import { useNotification } from "../../feedback/notification/NotificationContext";
24
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
24
+ import { useI18n } from "../../../providers/i18n/I18nContext";
25
25
  import { Button } from "../../form-control/Button";
26
26
  import { Icon } from "../../display/Icon";
27
27
  import { FormGroup } from "../../layout/FormGroup";
@@ -85,7 +85,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
85
85
  "items",
86
86
  "onItemsChange",
87
87
  "inlineEdit",
88
- "modalEdit",
88
+ "dialogEdit",
89
89
  "excel",
90
90
  "selectMode",
91
91
  "onSelect",
@@ -95,12 +95,12 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
95
95
  ]);
96
96
 
97
97
  const noti = useNotification();
98
- const i18n = useI18nOptional();
98
+ const i18n = useI18n();
99
99
  const topbarCtx = useContext(TopbarContext);
100
100
  const dialogInstance = useDialogInstance();
101
- const isModal = dialogInstance !== undefined;
101
+ const isInDialog = dialogInstance !== undefined;
102
102
  const isSelectMode = () => local.selectMode != null;
103
- const canEdit = () => (isSelectMode() ? false : (local.editable ?? true));
103
+ const canEdit = () => (isInDialog && isSelectMode() ? false : (local.editable ?? true));
104
104
 
105
105
  // -- Children Resolution --
106
106
  const resolved = children(() => local.children);
@@ -168,7 +168,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
168
168
  try {
169
169
  await refresh();
170
170
  } catch (err) {
171
- noti.error(err, i18n?.t("crudSheet.lookupFailed") ?? "Lookup failed");
171
+ noti.error(err, i18n.t("crudSheet.lookupFailed"));
172
172
  }
173
173
  setBusyCount((c) => c - 1);
174
174
  setReady(true);
@@ -192,7 +192,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
192
192
  function checkIgnoreChanges(): boolean {
193
193
  if (!local.inlineEdit) return true;
194
194
  if (getItemDiffs().length === 0) return true;
195
- return confirm(i18n?.t("crudSheet.discardChanges") ?? "You have unsaved changes. Discard them?");
195
+ return confirm(i18n.t("crudSheet.discardChanges"));
196
196
  }
197
197
 
198
198
  // -- Filter --
@@ -243,18 +243,18 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
243
243
  const diffs = getItemDiffs();
244
244
 
245
245
  if (diffs.length === 0) {
246
- noti.info(i18n?.t("crudSheet.notice") ?? "Notice", i18n?.t("crudSheet.noChanges") ?? "No changes to save.");
246
+ noti.info(i18n.t("crudSheet.notice"), i18n.t("crudSheet.noChanges"));
247
247
  return;
248
248
  }
249
249
 
250
250
  setBusyCount((c) => c + 1);
251
251
  try {
252
252
  await local.inlineEdit.submit(diffs);
253
- noti.success(i18n?.t("crudSheet.saveCompleted") ?? "Save completed", i18n?.t("crudSheet.saveSuccess") ?? "Saved successfully.");
253
+ noti.success(i18n.t("crudSheet.saveCompleted"), i18n.t("crudSheet.saveSuccess"));
254
254
  await refresh();
255
255
  local.onSubmitted?.();
256
256
  } catch (err) {
257
- noti.error(err, i18n?.t("crudSheet.saveFailed") ?? "Save failed");
257
+ noti.error(err, i18n.t("crudSheet.saveFailed"));
258
258
  }
259
259
  setBusyCount((c) => c - 1);
260
260
  }
@@ -264,47 +264,47 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
264
264
  await handleSave();
265
265
  }
266
266
 
267
- // -- Modal Edit --
267
+ // -- Dialog Edit --
268
268
  async function handleEditItem(item?: TItem) {
269
- if (!local.modalEdit) return;
270
- const result = await local.modalEdit.editItem(item);
269
+ if (!local.dialogEdit) return;
270
+ const result = await local.dialogEdit.editItem(item);
271
271
  if (!result) return;
272
272
 
273
273
  setBusyCount((c) => c + 1);
274
274
  try {
275
275
  await refresh();
276
276
  } catch (err) {
277
- noti.error(err, i18n?.t("crudSheet.lookupFailed") ?? "Lookup failed");
277
+ noti.error(err, i18n.t("crudSheet.lookupFailed"));
278
278
  }
279
279
  setBusyCount((c) => c - 1);
280
280
  }
281
281
 
282
282
  async function handleDeleteItems() {
283
- if (!local.modalEdit?.deleteItems) return;
284
- const result = await local.modalEdit.deleteItems(selectedItems());
283
+ if (!local.dialogEdit?.deleteItems) return;
284
+ const result = await local.dialogEdit.deleteItems(selectedItems());
285
285
  if (!result) return;
286
286
 
287
287
  setBusyCount((c) => c + 1);
288
288
  try {
289
289
  await refresh();
290
- noti.success(i18n?.t("crudSheet.deleteCompleted") ?? "Delete completed", i18n?.t("crudSheet.deleteSuccess") ?? "Deleted successfully.");
290
+ noti.success(i18n.t("crudSheet.deleteCompleted"), i18n.t("crudSheet.deleteSuccess"));
291
291
  } catch (err) {
292
- noti.error(err, i18n?.t("crudSheet.deleteFailed") ?? "Delete failed");
292
+ noti.error(err, i18n.t("crudSheet.deleteFailed"));
293
293
  }
294
294
  setBusyCount((c) => c - 1);
295
295
  }
296
296
 
297
297
  async function handleRestoreItems() {
298
- if (!local.modalEdit?.restoreItems) return;
299
- const result = await local.modalEdit.restoreItems(selectedItems());
298
+ if (!local.dialogEdit?.restoreItems) return;
299
+ const result = await local.dialogEdit.restoreItems(selectedItems());
300
300
  if (!result) return;
301
301
 
302
302
  setBusyCount((c) => c + 1);
303
303
  try {
304
304
  await refresh();
305
- noti.success(i18n?.t("crudSheet.restoreCompleted") ?? "Restore completed", i18n?.t("crudSheet.restoreSuccess") ?? "Restored successfully.");
305
+ noti.success(i18n.t("crudSheet.restoreCompleted"), i18n.t("crudSheet.restoreSuccess"));
306
306
  } catch (err) {
307
- noti.error(err, i18n?.t("crudSheet.restoreFailed") ?? "Restore failed");
307
+ noti.error(err, i18n.t("crudSheet.restoreFailed"));
308
308
  }
309
309
  setBusyCount((c) => c - 1);
310
310
  }
@@ -318,7 +318,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
318
318
  const result = await local.search(lastFilter(), undefined, sorts());
319
319
  await local.excel.download(result.items);
320
320
  } catch (err) {
321
- noti.error(err, i18n?.t("crudSheet.excelDownloadFailed") ?? "Excel download failed");
321
+ noti.error(err, i18n.t("crudSheet.excelDownloadFailed"));
322
322
  }
323
323
  setBusyCount((c) => c - 1);
324
324
  }
@@ -336,10 +336,10 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
336
336
  setBusyCount((c) => c + 1);
337
337
  try {
338
338
  await local.excel!.upload!(file);
339
- noti.success(i18n?.t("crudSheet.excelCompleted") ?? "Completed", i18n?.t("crudSheet.excelUploadSuccess") ?? "Excel upload completed successfully.");
339
+ noti.success(i18n.t("crudSheet.excelCompleted"), i18n.t("crudSheet.excelUploadSuccess"));
340
340
  await refresh();
341
341
  } catch (err) {
342
- noti.error(err, i18n?.t("crudSheet.excelUploadFailed") ?? "Excel upload failed");
342
+ noti.error(err, i18n.t("crudSheet.excelUploadFailed"));
343
343
  }
344
344
  setBusyCount((c) => c - 1);
345
345
  };
@@ -413,7 +413,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
413
413
 
414
414
  // -- Route Leave Guard --
415
415
  // eslint-disable-next-line solid/reactivity -- inlineEdit is used only for initial value
416
- if (!isModal && local.inlineEdit) {
416
+ if (!isInDialog && local.inlineEdit) {
417
417
  try {
418
418
  useBeforeLeave((e) => {
419
419
  if (!checkIgnoreChanges()) {
@@ -437,12 +437,12 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
437
437
  onClick={() => formRef?.requestSubmit()}
438
438
  >
439
439
  <Icon icon={IconDeviceFloppy} class="mr-1" />
440
- {i18n?.t("crudSheet.save") ?? "Save"}
440
+ {i18n.t("crudSheet.save")}
441
441
  </Button>
442
442
  </Show>
443
443
  <Button size="lg" variant="ghost" theme="info" onClick={handleRefresh}>
444
444
  <Icon icon={IconRefresh} class="mr-1" />
445
- {i18n?.t("crudSheet.refresh") ?? "Refresh"}
445
+ {i18n.t("crudSheet.refresh")}
446
446
  </Button>
447
447
  </>
448
448
  ));
@@ -475,8 +475,8 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
475
475
 
476
476
  return (
477
477
  <>
478
- {/* Modal mode: Dialog.Action (refresh button in header) */}
479
- <Show when={isModal}>
478
+ {/* Dialog mode: Dialog.Action (refresh button in header) */}
479
+ <Show when={isInDialog}>
480
480
  <Dialog.Action>
481
481
  <button
482
482
  class="flex items-center px-2 text-base-400 hover:text-base-600"
@@ -493,7 +493,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
493
493
  class={clsx("flex h-full flex-col", local.class)}
494
494
  >
495
495
  {/* Control mode: inline save/refresh bar */}
496
- <Show when={!isModal && !topbarCtx}>
496
+ <Show when={!isInDialog && !topbarCtx}>
497
497
  <div class="flex gap-2 p-2 pb-0">
498
498
  <Show when={canEdit() && local.inlineEdit}>
499
499
  <Button
@@ -503,12 +503,12 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
503
503
  onClick={() => formRef?.requestSubmit()}
504
504
  >
505
505
  <Icon icon={IconDeviceFloppy} class="mr-1" />
506
- {i18n?.t("crudSheet.save") ?? "Save"}
506
+ {i18n.t("crudSheet.save")}
507
507
  </Button>
508
508
  </Show>
509
509
  <Button size="sm" theme="info" variant="ghost" onClick={handleRefresh}>
510
510
  <Icon icon={IconRefresh} class="mr-1" />
511
- {i18n?.t("crudSheet.refresh") ?? "Refresh"}
511
+ {i18n.t("crudSheet.refresh")}
512
512
  </Button>
513
513
  </div>
514
514
  </Show>
@@ -524,7 +524,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
524
524
  <FormGroup.Item>
525
525
  <Button type="submit" theme="info" variant="solid">
526
526
  <Icon icon={IconSearch} class="mr-1" />
527
- {i18n?.t("crudSheet.search") ?? "Search"}
527
+ {i18n.t("crudSheet.search")}
528
528
  </Button>
529
529
  </FormGroup.Item>
530
530
  {filterDef().children(filter, setFilter)}
@@ -534,18 +534,18 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
534
534
  </Show>
535
535
 
536
536
  {/* Toolbar */}
537
- <Show when={!isSelectMode()}>
537
+ <Show when={!(isInDialog && isSelectMode())}>
538
538
  <div class="flex gap-2 p-2 pb-0">
539
539
  <Show when={!local.hideAutoTools}>
540
540
  {/* Inline edit buttons */}
541
541
  <Show when={canEdit() && local.inlineEdit}>
542
542
  <Button size="sm" theme="primary" variant="ghost" onClick={handleAddRow}>
543
- <Icon icon={IconPlus} class="mr-1" />{i18n?.t("crudSheet.addRow") ?? "Add Row"}
543
+ <Icon icon={IconPlus} class="mr-1" />{i18n.t("crudSheet.addRow")}
544
544
  </Button>
545
545
  </Show>
546
546
 
547
- {/* Modal edit buttons */}
548
- <Show when={canEdit() && local.modalEdit}>
547
+ {/* Dialog edit buttons */}
548
+ <Show when={canEdit() && local.dialogEdit}>
549
549
  <Button
550
550
  size="sm"
551
551
  theme="primary"
@@ -553,10 +553,10 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
553
553
  onClick={() => void handleEditItem()}
554
554
  >
555
555
  <Icon icon={IconPlus} class="mr-1" />
556
- {i18n?.t("crudSheet.register") ?? "Register"}
556
+ {i18n.t("crudSheet.register")}
557
557
  </Button>
558
558
  </Show>
559
- <Show when={canEdit() && local.modalEdit?.deleteItems}>
559
+ <Show when={canEdit() && local.dialogEdit?.deleteItems}>
560
560
  <Button
561
561
  size="sm"
562
562
  theme="danger"
@@ -572,10 +572,10 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
572
572
  }
573
573
  >
574
574
  <Icon icon={IconTrash} class="mr-1" />
575
- {i18n?.t("crudSheet.deleteSelected") ?? "Delete Selected"}
575
+ {i18n.t("crudSheet.deleteSelected")}
576
576
  </Button>
577
577
  </Show>
578
- <Show when={canEdit() && local.modalEdit?.restoreItems}>
578
+ <Show when={canEdit() && local.dialogEdit?.restoreItems}>
579
579
  <Button
580
580
  size="sm"
581
581
  theme="warning"
@@ -587,7 +587,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
587
587
  }
588
588
  >
589
589
  <Icon icon={IconTrashOff} class="mr-1" />
590
- {i18n?.t("crudSheet.restoreSelected") ?? "Restore Selected"}
590
+ {i18n.t("crudSheet.restoreSelected")}
591
591
  </Button>
592
592
  </Show>
593
593
 
@@ -595,13 +595,13 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
595
595
  <Show when={canEdit() && local.excel?.upload}>
596
596
  <Button size="sm" theme="success" variant="ghost" onClick={handleExcelUpload}>
597
597
  <Icon icon={IconUpload} class="mr-1" />
598
- {i18n?.t("crudSheet.excelUpload") ?? "Excel Upload"}
598
+ {i18n.t("crudSheet.excelUpload")}
599
599
  </Button>
600
600
  </Show>
601
601
  <Show when={local.excel}>
602
602
  <Button size="sm" theme="success" variant="ghost" onClick={handleExcelDownload}>
603
603
  <Icon icon={IconFileExcel} class="mr-1" />
604
- {i18n?.t("crudSheet.excelDownload") ?? "Excel Download"}
604
+ {i18n.t("crudSheet.excelDownload")}
605
605
  </Button>
606
606
  </Show>
607
607
  </Show>
@@ -626,7 +626,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
626
626
  selectMode={
627
627
  isSelectMode()
628
628
  ? local.selectMode
629
- : local.modalEdit?.deleteItems != null || local.modalEdit?.restoreItems != null
629
+ : local.dialogEdit?.deleteItems != null || local.dialogEdit?.restoreItems != null
630
630
  ? "multiple"
631
631
  : undefined
632
632
  }
@@ -689,9 +689,9 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
689
689
  },
690
690
  };
691
691
 
692
- // modalEdit editable column -- wrap with edit link
692
+ // dialogEdit editable column -- wrap with edit link
693
693
  if (
694
- local.modalEdit &&
694
+ local.dialogEdit &&
695
695
  col.editTrigger &&
696
696
  canEdit() &&
697
697
  (local.itemEditable?.(dsCtx.item) ?? true)
@@ -721,7 +721,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
721
721
 
722
722
  {/* Auto lastModified columns */}
723
723
  <Show when={local.lastModifiedAtProp}>
724
- <DataSheetColumn<TItem> key={local.lastModifiedAtProp!} header={i18n?.t("crudSheet.lastModified") ?? "Last Modified"} hidden>
724
+ <DataSheetColumn<TItem> key={local.lastModifiedAtProp!} header={i18n.t("crudSheet.lastModified")} hidden>
725
725
  {(dsCtx) => (
726
726
  <div class="px-2 py-1 text-center">
727
727
  {(
@@ -735,7 +735,7 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
735
735
  </Show>
736
736
 
737
737
  <Show when={local.lastModifiedByProp}>
738
- <DataSheetColumn<TItem> key={local.lastModifiedByProp!} header={i18n?.t("crudSheet.modifiedBy") ?? "Modified By"} hidden>
738
+ <DataSheetColumn<TItem> key={local.lastModifiedByProp!} header={i18n.t("crudSheet.modifiedBy")} hidden>
739
739
  {(dsCtx) => (
740
740
  <div class="px-2 py-1 text-center">
741
741
  {objGetChainValue(dsCtx.item, local.lastModifiedByProp!, true) as string}
@@ -747,17 +747,17 @@ const CrudSheetBase = <TItem, TFilter extends Record<string, any>>(
747
747
  </form>
748
748
 
749
749
  {/* Select mode bottom bar */}
750
- <Show when={isModal && isSelectMode()}>
750
+ <Show when={isInDialog && isSelectMode()}>
751
751
  <div class="flex gap-2 border-t p-2">
752
752
  <div class="flex-1" />
753
753
  <Show when={selectedItems().length > 0}>
754
754
  <Button size="sm" theme="danger" onClick={handleSelectCancel}>
755
- {local.selectMode === "multiple" ? (i18n?.t("crudSheet.deselectAll") ?? "Deselect All") : (i18n?.t("crudSheet.deselect") ?? "Deselect")}
755
+ {local.selectMode === "multiple" ? i18n.t("crudSheet.deselectAll") : i18n.t("crudSheet.deselect")}
756
756
  </Button>
757
757
  </Show>
758
758
  <Show when={local.selectMode === "multiple"}>
759
759
  <Button size="sm" theme="primary" onClick={handleSelectConfirm}>
760
- {i18n?.t("crudSheet.confirm") ?? "Confirm"} ({selectedItems().length})
760
+ {i18n.t("crudSheet.confirm")} ({selectedItems().length})
761
761
  </Button>
762
762
  </Show>
763
763
  </div>
@@ -19,7 +19,7 @@ export interface InlineEditConfig<TItem> {
19
19
  diffsExcludes?: string[];
20
20
  }
21
21
 
22
- export interface ModalEditConfig<TItem> {
22
+ export interface DialogEditConfig<TItem> {
23
23
  editItem: (item?: TItem) => Promise<boolean | undefined>;
24
24
  deleteItems?: (items: TItem[]) => Promise<boolean>;
25
25
  restoreItems?: (items: TItem[]) => Promise<boolean>;
@@ -70,9 +70,9 @@ export type CrudSheetProps<TItem, TFilter extends Record<string, any>> = CrudShe
70
70
  TFilter
71
71
  > &
72
72
  (
73
- | { inlineEdit: InlineEditConfig<TItem>; modalEdit?: never }
74
- | { modalEdit: ModalEditConfig<TItem>; inlineEdit?: never }
75
- | { inlineEdit?: never; modalEdit?: never }
73
+ | { inlineEdit: InlineEditConfig<TItem>; dialogEdit?: never }
74
+ | { dialogEdit: DialogEditConfig<TItem>; inlineEdit?: never }
75
+ | { inlineEdit?: never; dialogEdit?: never }
76
76
  );
77
77
 
78
78
  interface CrudSheetBaseProps<TItem, TFilter extends Record<string, any>> {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ type Component,
2
3
  createEffect,
3
4
  createMemo,
4
5
  createResource,
@@ -15,7 +16,8 @@ import { IconSearch, IconX } from "@tabler/icons-solidjs";
15
16
  import { Icon } from "../../display/Icon";
16
17
  import { Invalid } from "../../form-control/Invalid";
17
18
  import { useDialog, type DialogShowOptions } from "../../disclosure/DialogContext";
18
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
19
+ import { useDialogInstance } from "../../disclosure/DialogInstanceContext";
20
+ import { useI18n } from "../../../providers/i18n/I18nContext";
19
21
  import { createControllableSignal } from "../../../hooks/createControllableSignal";
20
22
  import { type ComponentSize, textMuted } from "../../../styles/tokens.styles";
21
23
  import {
@@ -25,11 +27,31 @@ import {
25
27
  triggerSizeClasses,
26
28
  } from "../../form-control/DropdownTrigger.styles";
27
29
 
28
- /** Result interface returned from modal */
29
- export interface DataSelectModalResult<TKey> {
30
+ /** Result interface returned from dialog */
31
+ export interface DataSelectDialogResult<TKey> {
30
32
  selectedKeys: TKey[];
31
33
  }
32
34
 
35
+ /** Props automatically injected into dialog component by DataSelectButton/SharedDataSelect */
36
+ export interface InjectedSelectProps {
37
+ /** Selection mode */
38
+ selectMode: "single" | "multiple";
39
+ /** Currently selected keys */
40
+ selectedKeys: (string | number)[];
41
+ /** Selection callback — automatically closes dialog with result */
42
+ onSelect: (result: { keys: (string | number)[] }) => void;
43
+ }
44
+
45
+ /** Declarative dialog configuration */
46
+ export interface DialogConfig<TUserProps = any> {
47
+ /** Dialog component (must accept InjectedSelectProps) */
48
+ component: Component<TUserProps & InjectedSelectProps>;
49
+ /** User-defined props for the component */
50
+ props?: TUserProps;
51
+ /** Dialog options (header, size, etc.) */
52
+ option?: DialogShowOptions;
53
+ }
54
+
33
55
  /** DataSelectButton Props */
34
56
  export interface DataSelectButtonProps<TItem, TKey = string | number> {
35
57
  /** Currently selected key(s) (single or multiple) */
@@ -39,8 +61,8 @@ export interface DataSelectButtonProps<TItem, TKey = string | number> {
39
61
 
40
62
  /** Function to load items by key */
41
63
  load: (keys: TKey[]) => TItem[] | Promise<TItem[]>;
42
- /** Selection modal component factory */
43
- modal: () => JSX.Element;
64
+ /** Selection dialog configuration */
65
+ dialog: DialogConfig;
44
66
  /** Item rendering function */
45
67
  renderItem: (item: TItem) => JSX.Element;
46
68
 
@@ -59,9 +81,6 @@ export interface DataSelectButtonProps<TItem, TKey = string | number> {
59
81
  validate?: (value: unknown) => string | undefined;
60
82
  /** touchMode: show error only after focus is lost */
61
83
  touchMode?: boolean;
62
-
63
- /** Dialog options */
64
- dialogOptions?: DialogShowOptions;
65
84
  }
66
85
 
67
86
  // Styles
@@ -100,7 +119,7 @@ export function DataSelectButton<TItem, TKey = string | number>(
100
119
  "value",
101
120
  "onValueChange",
102
121
  "load",
103
- "modal",
122
+ "dialog",
104
123
  "renderItem",
105
124
  "multiple",
106
125
  "required",
@@ -109,10 +128,9 @@ export function DataSelectButton<TItem, TKey = string | number>(
109
128
  "inset",
110
129
  "validate",
111
130
  "touchMode",
112
- "dialogOptions",
113
131
  ]);
114
132
 
115
- const i18n = useI18nOptional();
133
+ const i18n = useI18n();
116
134
  const dialog = useDialog();
117
135
 
118
136
  // Always normalize value to array
@@ -164,18 +182,30 @@ export function DataSelectButton<TItem, TKey = string | number>(
164
182
  const v = getValue();
165
183
  if (local.required) {
166
184
  const keys = normalizeKeys(v);
167
- if (keys.length === 0) return "Required field";
185
+ if (keys.length === 0) return i18n.t("validation.requiredField");
168
186
  }
169
187
  return local.validate?.(v);
170
188
  });
171
189
 
172
- // Open modal
173
- const handleOpenModal = async () => {
190
+ // Open dialog
191
+ const handleOpenDialog = async () => {
174
192
  if (local.disabled) return;
175
193
 
176
- const result = await dialog.show<DataSelectModalResult<TKey>>(
177
- local.modal,
178
- local.dialogOptions ?? {},
194
+ const result = await dialog.show<DataSelectDialogResult<TKey>>(
195
+ () => {
196
+ const instance = useDialogInstance<DataSelectDialogResult<TKey>>();
197
+ return (
198
+ <local.dialog.component
199
+ {...(local.dialog.props ?? {})}
200
+ selectMode={local.multiple ? "multiple" : "single"}
201
+ selectedKeys={normalizeKeys(getValue()) as (string | number)[]}
202
+ onSelect={(r: { keys: (string | number)[] }) =>
203
+ instance?.close({ selectedKeys: r.keys as TKey[] })
204
+ }
205
+ />
206
+ );
207
+ },
208
+ local.dialog.option ?? {},
179
209
  );
180
210
 
181
211
  if (result) {
@@ -243,7 +273,7 @@ export function DataSelectButton<TItem, TKey = string | number>(
243
273
  if (local.disabled) return;
244
274
  if (e.key === "Enter" || e.key === " ") {
245
275
  e.preventDefault();
246
- void handleOpenModal();
276
+ void handleOpenDialog();
247
277
  }
248
278
  }}
249
279
  >
@@ -256,7 +286,7 @@ export function DataSelectButton<TItem, TKey = string | number>(
256
286
  class={twMerge(actionButtonClass, "text-base-400 hover:text-danger-500")}
257
287
  onClick={handleClear}
258
288
  tabIndex={-1}
259
- aria-label={i18n?.t("dataSelectButton.deselect") ?? "Deselect"}
289
+ aria-label={i18n.t("dataSelectButton.deselect")}
260
290
  >
261
291
  <Icon icon={IconX} size="0.875em" />
262
292
  </button>
@@ -266,9 +296,9 @@ export function DataSelectButton<TItem, TKey = string | number>(
266
296
  type="button"
267
297
  data-search-button
268
298
  class={twMerge(actionButtonClass, "text-base-400 hover:text-primary-500")}
269
- onClick={() => void handleOpenModal()}
299
+ onClick={() => void handleOpenDialog()}
270
300
  tabIndex={-1}
271
- aria-label={i18n?.t("dataSelectButton.search") ?? "Search"}
301
+ aria-label={i18n.t("dataSelectButton.search")}
272
302
  >
273
303
  <Icon icon={IconSearch} size="0.875em" />
274
304
  </button>
@@ -14,7 +14,7 @@ import { DataSheet } from "../../data/sheet/DataSheet";
14
14
  import { Checkbox } from "../../form-control/checkbox/Checkbox";
15
15
  import { borderDefault } from "../../../styles/tokens.styles";
16
16
  import type { AppPerm } from "../../../helpers/createAppStructure";
17
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
17
+ import { useI18n } from "../../../providers/i18n/I18nContext";
18
18
 
19
19
  const titleCellClass = clsx("flex items-stretch", "px-2");
20
20
  const indentGuideWrapperClass = clsx("mr-1 flex w-3", "justify-center");
@@ -215,7 +215,7 @@ export const PermissionTable: Component<PermissionTableProps> = (props) => {
215
215
  "style",
216
216
  ]);
217
217
 
218
- const i18n = useI18nOptional();
218
+ const i18n = useI18n();
219
219
 
220
220
  // Visible top-level items (preserve object reference)
221
221
  const visibleItems = createMemo(() => {
@@ -270,7 +270,7 @@ export const PermissionTable: Component<PermissionTableProps> = (props) => {
270
270
  >
271
271
  <DataSheet.Column
272
272
  key="title"
273
- header={i18n?.t("permissionTable.permissionItem") ?? "Permission Item"}
273
+ header={i18n.t("permissionTable.permissionItem")}
274
274
  sortable={false}
275
275
  resizable={false}
276
276
  >