@simplysm/solid 13.0.71 → 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 (215) 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.d.ts.map +1 -1
  6. package/dist/components/data/sheet/DataSheet.js +13 -16
  7. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  8. package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
  9. package/dist/components/data/sheet/DataSheet.styles.js +1 -1
  10. package/dist/components/data/sheet/DataSheet.styles.js.map +1 -1
  11. package/dist/components/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
  12. package/dist/components/data/sheet/DataSheetConfigDialog.js +27 -9
  13. package/dist/components/data/sheet/DataSheetConfigDialog.js.map +2 -2
  14. package/dist/components/disclosure/Dialog.d.ts +1 -1
  15. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  16. package/dist/components/disclosure/Dialog.js +5 -5
  17. package/dist/components/disclosure/Dialog.js.map +2 -2
  18. package/dist/components/disclosure/dialogZIndex.d.ts +1 -1
  19. package/dist/components/features/crud-detail/CrudDetail.js +23 -23
  20. package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
  21. package/dist/components/features/crud-sheet/CrudSheet.js +49 -49
  22. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  23. package/dist/components/features/crud-sheet/types.d.ts +4 -4
  24. package/dist/components/features/crud-sheet/types.d.ts.map +1 -1
  25. package/dist/components/features/data-select-button/DataSelectButton.d.ts +25 -7
  26. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  27. package/dist/components/features/data-select-button/DataSelectButton.js +27 -12
  28. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  29. package/dist/components/features/permission-table/PermissionTable.js +4 -4
  30. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  31. package/dist/components/features/shared-data/SharedDataSelect.d.ts +22 -10
  32. package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
  33. package/dist/components/features/shared-data/SharedDataSelect.js +113 -29
  34. package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
  35. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts +3 -3
  36. package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
  37. package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
  38. package/dist/components/features/shared-data/SharedDataSelectList.js +5 -4
  39. package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
  40. package/dist/components/feedback/notification/NotificationBanner.js +3 -3
  41. package/dist/components/feedback/notification/NotificationBanner.js.map +2 -2
  42. package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
  43. package/dist/components/feedback/notification/NotificationBell.js +12 -5
  44. package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
  45. package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
  46. package/dist/components/feedback/notification/NotificationProvider.js +3 -1
  47. package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
  48. package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
  49. package/dist/components/form-control/ThemeToggle.js +9 -6
  50. package/dist/components/form-control/ThemeToggle.js.map +2 -2
  51. package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
  52. package/dist/components/form-control/checkbox/Checkbox.js +3 -1
  53. package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
  54. package/dist/components/form-control/checkbox/CheckboxGroup.js +1 -1
  55. package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
  56. package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
  57. package/dist/components/form-control/checkbox/Radio.js +3 -1
  58. package/dist/components/form-control/checkbox/Radio.js.map +2 -2
  59. package/dist/components/form-control/checkbox/RadioGroup.js +1 -1
  60. package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
  61. package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
  62. package/dist/components/form-control/color-picker/ColorPicker.js +3 -1
  63. package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
  64. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  65. package/dist/components/form-control/combobox/Combobox.js +9 -5
  66. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  67. package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -9
  68. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  69. package/dist/components/form-control/editor/EditorToolbar.js +3 -3
  70. package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
  71. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  72. package/dist/components/form-control/field/DatePicker.js +9 -3
  73. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  74. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  75. package/dist/components/form-control/field/DateTimePicker.js +9 -3
  76. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  77. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  78. package/dist/components/form-control/field/NumberInput.js +9 -3
  79. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  80. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  81. package/dist/components/form-control/field/TextInput.js +10 -4
  82. package/dist/components/form-control/field/TextInput.js.map +2 -2
  83. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  84. package/dist/components/form-control/field/Textarea.js +9 -3
  85. package/dist/components/form-control/field/Textarea.js.map +2 -2
  86. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  87. package/dist/components/form-control/field/TimePicker.js +9 -3
  88. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  89. package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
  90. package/dist/components/form-control/numpad/Numpad.js +5 -1
  91. package/dist/components/form-control/numpad/Numpad.js.map +2 -2
  92. package/dist/components/form-control/select/Select.js +7 -7
  93. package/dist/components/form-control/select/Select.js.map +2 -2
  94. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  95. package/dist/components/form-control/state-preset/StatePreset.js +42 -20
  96. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  97. package/dist/components/layout/sidebar/SidebarContainer.js +3 -3
  98. package/dist/components/layout/sidebar/SidebarContainer.js.map +2 -2
  99. package/dist/components/layout/sidebar/SidebarMenu.d.ts.map +1 -1
  100. package/dist/components/layout/sidebar/SidebarMenu.js +5 -2
  101. package/dist/components/layout/sidebar/SidebarMenu.js.map +2 -2
  102. package/dist/components/layout/topbar/Topbar.js +3 -4
  103. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  104. package/dist/components/layout/topbar/TopbarMenu.js +3 -3
  105. package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
  106. package/dist/hooks/createSelectionGroup.d.ts +2 -2
  107. package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
  108. package/dist/hooks/createSelectionGroup.js +5 -2
  109. package/dist/hooks/createSelectionGroup.js.map +2 -2
  110. package/dist/providers/i18n/I18nContext.d.ts +0 -4
  111. package/dist/providers/i18n/I18nContext.d.ts.map +1 -1
  112. package/dist/providers/i18n/I18nContext.js +1 -5
  113. package/dist/providers/i18n/I18nContext.js.map +2 -2
  114. package/dist/providers/i18n/locales/en.d.ts +38 -0
  115. package/dist/providers/i18n/locales/en.d.ts.map +1 -1
  116. package/dist/providers/i18n/locales/en.js +39 -1
  117. package/dist/providers/i18n/locales/en.js.map +1 -1
  118. package/dist/providers/i18n/locales/ko.d.ts +38 -0
  119. package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
  120. package/dist/providers/i18n/locales/ko.js +39 -1
  121. package/dist/providers/i18n/locales/ko.js.map +1 -1
  122. package/package.json +6 -6
  123. package/src/components/data/calendar/Calendar.tsx +3 -4
  124. package/src/components/data/sheet/DataSheet.styles.ts +1 -1
  125. package/src/components/data/sheet/DataSheet.tsx +14 -15
  126. package/src/components/data/sheet/DataSheetConfigDialog.tsx +12 -10
  127. package/src/components/data/sheet/types.ts +1 -1
  128. package/src/components/disclosure/Dialog.tsx +10 -10
  129. package/src/components/disclosure/dialogZIndex.ts +1 -1
  130. package/src/components/features/crud-detail/CrudDetail.tsx +25 -25
  131. package/src/components/features/crud-sheet/CrudSheet.tsx +53 -53
  132. package/src/components/features/crud-sheet/types.ts +4 -4
  133. package/src/components/features/data-select-button/DataSelectButton.tsx +51 -21
  134. package/src/components/features/permission-table/PermissionTable.tsx +3 -3
  135. package/src/components/features/shared-data/SharedDataSelect.tsx +172 -33
  136. package/src/components/features/shared-data/SharedDataSelectButton.tsx +3 -2
  137. package/src/components/features/shared-data/SharedDataSelectList.tsx +4 -4
  138. package/src/components/feedback/notification/NotificationBanner.tsx +3 -3
  139. package/src/components/feedback/notification/NotificationBell.tsx +6 -4
  140. package/src/components/feedback/notification/NotificationProvider.tsx +3 -1
  141. package/src/components/form-control/ThemeToggle.tsx +10 -6
  142. package/src/components/form-control/checkbox/Checkbox.tsx +4 -1
  143. package/src/components/form-control/checkbox/CheckboxGroup.tsx +1 -1
  144. package/src/components/form-control/checkbox/Radio.tsx +4 -1
  145. package/src/components/form-control/checkbox/RadioGroup.tsx +1 -1
  146. package/src/components/form-control/color-picker/ColorPicker.tsx +4 -1
  147. package/src/components/form-control/combobox/Combobox.tsx +6 -3
  148. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +8 -8
  149. package/src/components/form-control/editor/EditorToolbar.tsx +23 -23
  150. package/src/components/form-control/field/DatePicker.tsx +6 -3
  151. package/src/components/form-control/field/DateTimePicker.tsx +6 -3
  152. package/src/components/form-control/field/NumberInput.tsx +6 -3
  153. package/src/components/form-control/field/TextInput.tsx +7 -4
  154. package/src/components/form-control/field/Textarea.tsx +6 -3
  155. package/src/components/form-control/field/TimePicker.tsx +6 -3
  156. package/src/components/form-control/numpad/Numpad.tsx +3 -1
  157. package/src/components/form-control/select/Select.tsx +7 -7
  158. package/src/components/form-control/state-preset/StatePreset.tsx +14 -12
  159. package/src/components/layout/sidebar/SidebarContainer.tsx +3 -3
  160. package/src/components/layout/sidebar/SidebarMenu.tsx +3 -1
  161. package/src/components/layout/topbar/Topbar.tsx +3 -3
  162. package/src/components/layout/topbar/TopbarMenu.tsx +3 -3
  163. package/src/hooks/createSelectionGroup.tsx +8 -4
  164. package/src/providers/i18n/I18nContext.tsx +0 -7
  165. package/src/providers/i18n/locales/en.ts +38 -0
  166. package/src/providers/i18n/locales/ko.ts +38 -0
  167. package/tailwind.config.ts +2 -2
  168. package/tests/components/data/kanban/Kanban.selection.spec.tsx +34 -24
  169. package/tests/components/disclosure/Dialog.spec.tsx +28 -28
  170. package/tests/components/disclosure/DialogProvider.spec.tsx +51 -25
  171. package/tests/components/features/address/AddressSearch.spec.tsx +12 -4
  172. package/tests/components/features/crud-detail/CrudDetail.spec.tsx +1 -0
  173. package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +30 -6
  174. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +77 -56
  175. package/tests/components/features/permission-table/PermissionTable.spec.tsx +12 -8
  176. package/tests/components/features/shared-data/SharedDataSelect.spec.tsx +172 -0
  177. package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +14 -2
  178. package/tests/components/feedback/notification/LiveRegion.spec.tsx +20 -9
  179. package/tests/components/feedback/notification/NotificationBanner.spec.tsx +64 -46
  180. package/tests/components/feedback/notification/NotificationBell.spec.tsx +70 -51
  181. package/tests/components/feedback/notification/NotificationContext.spec.tsx +105 -78
  182. package/tests/components/form-control/checkbox/Checkbox.spec.tsx +25 -20
  183. package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +53 -30
  184. package/tests/components/form-control/checkbox/Radio.spec.tsx +25 -20
  185. package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +53 -30
  186. package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +24 -15
  187. package/tests/components/form-control/combobox/Combobox.spec.tsx +92 -59
  188. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +2 -2
  189. package/tests/components/form-control/field/DatePicker.spec.tsx +50 -44
  190. package/tests/components/form-control/field/DateTimePicker.spec.tsx +51 -45
  191. package/tests/components/form-control/field/NumberInput.spec.tsx +53 -47
  192. package/tests/components/form-control/field/TextInput.spec.tsx +50 -44
  193. package/tests/components/form-control/field/Textarea.spec.tsx +35 -29
  194. package/tests/components/form-control/field/TimePicker.spec.tsx +43 -37
  195. package/tests/components/form-control/numpad/Numpad.spec.tsx +175 -25
  196. package/tests/components/form-control/select/Select.spec.tsx +5 -0
  197. package/tests/components/form-control/select/SelectItem.spec.tsx +1 -0
  198. package/tests/components/layout/sidebar/Sidebar.spec.tsx +79 -35
  199. package/tests/components/layout/sidebar/SidebarContainer.spec.tsx +1 -0
  200. package/tests/components/layout/sidebar/SidebarMenu.spec.tsx +28 -17
  201. package/tests/components/layout/topbar/TopbarActions.spec.tsx +41 -23
  202. package/tests/components/layout/topbar/createTopbarActions.spec.tsx +1 -0
  203. package/tests/hooks/usePrint.spec.tsx +1 -1
  204. package/tests/hooks/useRouterLink.spec.tsx +2 -0
  205. package/tests/hooks/useSyncConfig.spec.tsx +1 -0
  206. package/tests/providers/ErrorLoggerProvider.spec.tsx +1 -0
  207. package/tests/providers/PwaUpdateProvider.spec.tsx +16 -6
  208. package/tests/providers/ServiceClientContext.spec.tsx +40 -25
  209. package/tests/providers/i18n/I18nContext.spec.tsx +3 -4
  210. package/tests/providers/shared-data/SharedDataProvider.spec.tsx +2 -0
  211. package/dist/hooks/usePrint.d.ts +0 -3
  212. package/dist/hooks/usePrint.d.ts.map +0 -1
  213. package/dist/hooks/usePrint.js +0 -5
  214. package/dist/hooks/usePrint.js.map +0 -6
  215. package/src/hooks/usePrint.ts +0 -2
@@ -8,6 +8,7 @@ import { useSyncConfig } from "../../../hooks/useSyncConfig";
8
8
  import { useNotification } from "../../feedback/notification/NotificationContext";
9
9
  import { Icon } from "../../display/Icon";
10
10
  import { textPlaceholder } from "../../../styles/tokens.styles";
11
+ import { useI18n } from "../../../providers/i18n/I18nContext";
11
12
  import type { ComponentSize } from "../../../styles/tokens.styles";
12
13
  import { iconButtonBase } from "../../../styles/patterns.styles";
13
14
 
@@ -116,6 +117,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
116
117
  ]);
117
118
 
118
119
  const notification = useNotification();
120
+ const i18n = useI18n();
119
121
 
120
122
  // presetKey is an identifier set only once at mount, evaluate immediately to capture
121
123
  /* eslint-disable solid/reactivity */
@@ -147,7 +149,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
147
149
  }
148
150
 
149
151
  if (presets().some((p) => p.name === name)) {
150
- notification.warning("Duplicate name", "A preset with this name already exists.");
152
+ notification.warning(i18n.t("statePreset.duplicateName"), i18n.t("statePreset.duplicateMessage"));
151
153
  return;
152
154
  }
153
155
 
@@ -156,7 +158,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
156
158
  state: objClone(local.value),
157
159
  };
158
160
  setPresets([...presets(), newPreset]);
159
- notification.info("Preset saved", `Preset "${name}" has been saved.`);
161
+ notification.info(i18n.t("statePreset.saved"), i18n.t("statePreset.savedMessage", { name }));
160
162
  setAdding(false);
161
163
  setInputValue("");
162
164
  }
@@ -177,11 +179,11 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
177
179
  setPresets(updated);
178
180
 
179
181
  const notiId = notification.info(
180
- "Preset overwritten",
181
- `Preset "${presetName}" has been updated with the current state.`,
182
+ i18n.t("statePreset.overwritten"),
183
+ i18n.t("statePreset.overwrittenMessage", { name: presetName }),
182
184
  {
183
185
  action: {
184
- label: "Undo",
186
+ label: i18n.t("statePreset.undo"),
185
187
  onClick: () => {
186
188
  setPresets(snapshot);
187
189
  notification.remove(notiId);
@@ -198,9 +200,9 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
198
200
  const updated = snapshot.filter((_, i) => i !== index);
199
201
  setPresets(updated);
200
202
 
201
- const notiId = notification.info("Preset deleted", `Preset "${presetName}" has been deleted.`, {
203
+ const notiId = notification.info(i18n.t("statePreset.deleted"), i18n.t("statePreset.deletedMessage", { name: presetName }), {
202
204
  action: {
203
- label: "Undo",
205
+ label: i18n.t("statePreset.undo"),
204
206
  onClick: () => {
205
207
  setPresets(snapshot);
206
208
  notification.remove(notiId);
@@ -242,7 +244,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
242
244
  type="button"
243
245
  class={resolvedStarBtnClass()}
244
246
  onClick={handleStartAdd}
245
- title="Add preset"
247
+ title={i18n.t("statePreset.addPreset")}
246
248
  >
247
249
  <Icon icon={IconStar} size={iconSize} />
248
250
  </button>
@@ -255,7 +257,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
255
257
  type="button"
256
258
  class={chipNameBtnClass}
257
259
  onClick={() => handleRestore(preset)}
258
- title={`Apply preset "${preset.name}"`}
260
+ title={preset.name}
259
261
  >
260
262
  {preset.name}
261
263
  </button>
@@ -263,7 +265,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
263
265
  type="button"
264
266
  class={resolvedIconBtnClass()}
265
267
  onClick={() => handleOverwrite(index())}
266
- title="Overwrite with current state"
268
+ title={i18n.t("statePreset.overwrite")}
267
269
  >
268
270
  <Icon icon={IconDeviceFloppy} size={iconSize} />
269
271
  </button>
@@ -271,7 +273,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
271
273
  type="button"
272
274
  class={resolvedIconBtnClass()}
273
275
  onClick={() => handleDelete(index())}
274
- title="Delete preset"
276
+ title={i18n.t("statePreset.deletePreset")}
275
277
  >
276
278
  <Icon icon={IconX} size={iconSize} />
277
279
  </button>
@@ -288,7 +290,7 @@ function StatePresetInner<TValue>(props: StatePresetProps<TValue>): JSX.Element
288
290
  }}
289
291
  type="text"
290
292
  class={resolvedInputClass()}
291
- placeholder="Name..."
293
+ placeholder={i18n.t("statePreset.namePlaceholder")}
292
294
  autocomplete="one-time-code"
293
295
  value={inputValue()}
294
296
  onInput={(e) => setInputValue(e.currentTarget.value)}
@@ -12,7 +12,7 @@ import clsx from "clsx";
12
12
  import { twMerge } from "tailwind-merge";
13
13
  import { SidebarContext, SM_MEDIA_QUERY } from "./SidebarContext";
14
14
  import { mergeStyles } from "../../../helpers/mergeStyles";
15
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
15
+ import { useI18n } from "../../../providers/i18n/I18nContext";
16
16
 
17
17
  const backdropClass = clsx(
18
18
  "absolute",
@@ -62,7 +62,7 @@ export interface SidebarContainerProps extends JSX.HTMLAttributes<HTMLDivElement
62
62
  export const SidebarContainer: ParentComponent<SidebarContainerProps> = (props) => {
63
63
  const [local, rest] = splitProps(props, ["children", "class", "style"]);
64
64
 
65
- const i18n = useI18nOptional();
65
+ const i18n = useI18n();
66
66
  const [toggle, setToggle] = createSignal(false);
67
67
 
68
68
  // Detect Tailwind sm: breakpoint
@@ -113,7 +113,7 @@ export const SidebarContainer: ParentComponent<SidebarContainerProps> = (props)
113
113
  onClick={handleBackdropClick}
114
114
  onKeyDown={(e) => e.key === "Escape" && handleBackdropClick()}
115
115
  role="button"
116
- aria-label={i18n?.t("sidebar.closeSidebar") ?? "Close sidebar"}
116
+ aria-label={i18n.t("sidebar.closeSidebar")}
117
117
  tabIndex={0}
118
118
  />
119
119
  </Show>
@@ -19,6 +19,7 @@ import type { AppMenu } from "../../../helpers/createAppStructure";
19
19
  import { Icon } from "../../display/Icon";
20
20
  import { List } from "../../data/list/List";
21
21
  import { ListItem } from "../../data/list/ListItem";
22
+ import { useI18n } from "../../../providers/i18n/I18nContext";
22
23
 
23
24
  const headerClass = clsx(
24
25
  "px-4",
@@ -72,6 +73,7 @@ const MenuContext = createContext<MenuContextValue>();
72
73
  */
73
74
  export const SidebarMenu: Component<SidebarMenuProps> = (props) => {
74
75
  const [local, rest] = splitProps(props, ["menus", "class"]);
76
+ const i18n = useI18n();
75
77
 
76
78
  const location = useLocation();
77
79
 
@@ -107,7 +109,7 @@ export const SidebarMenu: Component<SidebarMenuProps> = (props) => {
107
109
  return (
108
110
  <MenuContext.Provider value={{ initialOpenItems }}>
109
111
  <div {...rest} data-sidebar-menu class={getClassName()}>
110
- <div class={headerClass}>MENU</div>
112
+ <div class={headerClass}>{i18n.t("sidebarMenu.menu")}</div>
111
113
  <List inset>
112
114
  <For each={local.menus}>{(menu) => <MenuItem menu={menu} size="lg" />}</For>
113
115
  </List>
@@ -5,7 +5,7 @@ import { Icon } from "../../display/Icon";
5
5
  import { twMerge } from "tailwind-merge";
6
6
  import { Button } from "../../form-control/Button";
7
7
  import { useSidebarContextOptional } from "../sidebar/SidebarContext";
8
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
8
+ import { useI18n } from "../../../providers/i18n/I18nContext";
9
9
  import { TopbarActions } from "./TopbarActions";
10
10
  import { TopbarContainer } from "./TopbarContainer";
11
11
  import { TopbarMenu } from "./TopbarMenu";
@@ -71,7 +71,7 @@ const TopbarBase: ParentComponent<TopbarProps> = (props) => {
71
71
 
72
72
  // Optional use of SidebarContext (toggle button not shown if Context doesn't exist)
73
73
  const sidebarContext = useSidebarContextOptional();
74
- const i18n = useI18nOptional();
74
+ const i18n = useI18n();
75
75
 
76
76
  const handleToggle = () => {
77
77
  sidebarContext?.setToggle((v) => !v);
@@ -82,7 +82,7 @@ const TopbarBase: ParentComponent<TopbarProps> = (props) => {
82
82
  return (
83
83
  <header {...rest} data-topbar class={getClassName()}>
84
84
  <Show when={sidebarContext}>
85
- <Button variant="ghost" onClick={handleToggle} class="p-2" aria-label={i18n?.t("topbar.toggleSidebar") ?? "Toggle sidebar"}>
85
+ <Button variant="ghost" onClick={handleToggle} class="p-2" aria-label={i18n.t("topbar.toggleSidebar")}>
86
86
  <Icon icon={IconMenu2} size="1.5em" />
87
87
  </Button>
88
88
  </Show>
@@ -16,7 +16,7 @@ import { Button } from "../../form-control/Button";
16
16
  import { Dropdown } from "../../disclosure/Dropdown";
17
17
  import { List } from "../../data/list/List";
18
18
  import { ListItem } from "../../data/list/ListItem";
19
- import { useI18nOptional } from "../../../providers/i18n/I18nContext";
19
+ import { useI18n } from "../../../providers/i18n/I18nContext";
20
20
 
21
21
  const desktopNavBaseClass = clsx("hidden sm:flex", "flex-row gap-1", "items-center");
22
22
  const mobileWrapperClass = clsx("flex sm:hidden");
@@ -64,7 +64,7 @@ export interface TopbarMenuProps extends Omit<JSX.HTMLAttributes<HTMLElement>, "
64
64
  export const TopbarMenu: Component<TopbarMenuProps> = (props) => {
65
65
  const [local, rest] = splitProps(props, ["menus", "class"]);
66
66
  const [mobileMenuOpen, setMobileMenuOpen] = createSignal(false);
67
- const i18n = useI18nOptional();
67
+ const i18n = useI18n();
68
68
 
69
69
  return (
70
70
  <>
@@ -79,7 +79,7 @@ export const TopbarMenu: Component<TopbarMenuProps> = (props) => {
79
79
  <Dropdown.Trigger>
80
80
  <Button
81
81
  variant="ghost"
82
- aria-label={i18n?.t("topbarMenu.menu") ?? "Menu"}
82
+ aria-label={i18n.t("topbarMenu.menu")}
83
83
  aria-haspopup="menu"
84
84
  aria-expanded={mobileMenuOpen()}
85
85
  >
@@ -10,6 +10,7 @@ import { twMerge } from "tailwind-merge";
10
10
  import { createControllableSignal } from "./createControllableSignal";
11
11
  import { Invalid } from "../components/form-control/Invalid";
12
12
  import type { CheckboxSize } from "../components/form-control/checkbox/Checkbox.styles";
13
+ import { useI18n } from "../providers/i18n/I18nContext";
13
14
 
14
15
  interface SelectionItemComponentProps {
15
16
  value: boolean;
@@ -42,14 +43,14 @@ interface MultiGroupConfig {
42
43
  mode: "multiple";
43
44
  contextName: string;
44
45
  ItemComponent: (props: SelectionItemComponentProps) => JSX.Element;
45
- emptyErrorMsg: string;
46
+ emptyErrorMsgKey: string;
46
47
  }
47
48
 
48
49
  interface SingleGroupConfig {
49
50
  mode: "single";
50
51
  contextName: string;
51
52
  ItemComponent: (props: SelectionItemComponentProps) => JSX.Element;
52
- emptyErrorMsg: string;
53
+ emptyErrorMsgKey: string;
53
54
  }
54
55
 
55
56
  interface SelectionGroupItemProps<TValue> {
@@ -148,6 +149,9 @@ export function createSelectionGroup(config: MultiGroupConfig | SingleGroupConfi
148
149
  }
149
150
 
150
151
  const GroupInner: ParentComponent<MultiGroupProps<unknown>> = (props) => {
152
+ const i18n = useI18n();
153
+ const resolvedErrorMsg = () => i18n.t(config.emptyErrorMsgKey);
154
+
151
155
  const [local, rest] = splitProps(props, [
152
156
  "value",
153
157
  "onValueChange",
@@ -205,11 +209,11 @@ export function createSelectionGroup(config: MultiGroupConfig | SingleGroupConfi
205
209
  const errorMsg = createMemo(() => {
206
210
  if (config.mode === "multiple") {
207
211
  const v = local.value ?? [];
208
- if (local.required && v.length === 0) return config.emptyErrorMsg;
212
+ if (local.required && v.length === 0) return resolvedErrorMsg();
209
213
  return (local.validate as ((v: unknown[]) => string | undefined) | undefined)?.(v);
210
214
  } else {
211
215
  const v = local.value as unknown | undefined;
212
- if (local.required && (v === undefined || v === null)) return config.emptyErrorMsg;
216
+ if (local.required && (v === undefined || v === null)) return resolvedErrorMsg();
213
217
  return (local.validate as ((v: unknown | undefined) => string | undefined) | undefined)?.(
214
218
  v,
215
219
  );
@@ -38,13 +38,6 @@ export function useI18n(): I18nContextValue {
38
38
  return context;
39
39
  }
40
40
 
41
- /**
42
- * Get i18n context value (optional, returns undefined if not in provider)
43
- */
44
- export function useI18nOptional(): I18nContextValue | undefined {
45
- return useContext(I18nContext);
46
- }
47
-
48
41
  /**
49
42
  * I18n Provider component
50
43
  *
@@ -144,6 +144,7 @@ export default {
144
144
  },
145
145
  sharedDataSelectList: {
146
146
  searchPlaceholder: "Search...",
147
+ unspecified: "Unspecified",
147
148
  },
148
149
  dataSelectButton: {
149
150
  deselect: "Deselect",
@@ -158,4 +159,41 @@ export default {
158
159
  topbarMenu: {
159
160
  menu: "Menu",
160
161
  },
162
+ notificationBell: {
163
+ notifications: "Notifications",
164
+ clearAll: "Clear All",
165
+ noNotifications: "No notifications",
166
+ unreadCount: "{{count}} notifications",
167
+ },
168
+ notificationProvider: {
169
+ prefix: "Notification:",
170
+ },
171
+ themeToggle: {
172
+ light: "Light mode",
173
+ system: "System settings",
174
+ dark: "Dark mode",
175
+ },
176
+ combobox: {
177
+ searching: "Searching...",
178
+ noResults: "No results found",
179
+ },
180
+ numpad: {
181
+ enter: "ENT",
182
+ },
183
+ sidebarMenu: {
184
+ menu: "MENU",
185
+ },
186
+ validation: {
187
+ required: "This is a required field",
188
+ requiredField: "Required field",
189
+ requiredSelection: "This is a required selection",
190
+ selectItem: "Please select an item",
191
+ minLength: "Enter at least {{min}} characters",
192
+ maxLength: "Enter up to {{max}} characters",
193
+ invalidFormat: "The input format is invalid",
194
+ minValue: "Minimum value is {{min}}",
195
+ maxValue: "Maximum value is {{max}}",
196
+ minDate: "Must be greater than or equal to {{min}}",
197
+ maxDate: "Must be less than or equal to {{max}}",
198
+ },
161
199
  };
@@ -144,6 +144,7 @@ export default {
144
144
  },
145
145
  sharedDataSelectList: {
146
146
  searchPlaceholder: "검색...",
147
+ unspecified: "미지정",
147
148
  },
148
149
  dataSelectButton: {
149
150
  deselect: "선택 해제",
@@ -158,4 +159,41 @@ export default {
158
159
  topbarMenu: {
159
160
  menu: "메뉴",
160
161
  },
162
+ notificationBell: {
163
+ notifications: "알림",
164
+ clearAll: "모두 지우기",
165
+ noNotifications: "알림 없음",
166
+ unreadCount: "{{count}}개의 알림",
167
+ },
168
+ notificationProvider: {
169
+ prefix: "알림:",
170
+ },
171
+ themeToggle: {
172
+ light: "라이트 모드",
173
+ system: "시스템 설정",
174
+ dark: "다크 모드",
175
+ },
176
+ combobox: {
177
+ searching: "검색 중...",
178
+ noResults: "결과 없음",
179
+ },
180
+ numpad: {
181
+ enter: "입력",
182
+ },
183
+ sidebarMenu: {
184
+ menu: "메뉴",
185
+ },
186
+ validation: {
187
+ required: "필수 입력 항목입니다",
188
+ requiredField: "필수 항목",
189
+ requiredSelection: "필수 선택 항목입니다",
190
+ selectItem: "항목을 선택해 주세요",
191
+ minLength: "{{min}}자 이상 입력해 주세요",
192
+ maxLength: "{{max}}자 이하로 입력해 주세요",
193
+ invalidFormat: "입력 형식이 올바르지 않습니다",
194
+ minValue: "최솟값은 {{min}}입니다",
195
+ maxValue: "최댓값은 {{max}}입니다",
196
+ minDate: "{{min}} 이상이어야 합니다",
197
+ maxDate: "{{max}} 이하이어야 합니다",
198
+ },
161
199
  };
@@ -50,8 +50,8 @@ export default {
50
50
  "sidebar-backdrop": "99",
51
51
  "busy": "500",
52
52
  "dropdown": "1000",
53
- "modal-backdrop": "1999",
54
- "modal": "2000",
53
+ "dialog-backdrop": "1999",
54
+ "dialog": "2000",
55
55
  "notification": "3000",
56
56
  },
57
57
  },
@@ -1,38 +1,48 @@
1
1
  import { render, fireEvent } from "@solidjs/testing-library";
2
2
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
  import { Kanban } from "../../../../src/components/data/kanban/Kanban";
4
+ import { I18nProvider } from "../../../../src/providers/i18n/I18nContext";
5
+ import { ConfigProvider } from "../../../../src/providers/ConfigContext";
4
6
 
5
7
  describe("Kanban selection system", () => {
8
+ beforeEach(() => {
9
+ localStorage.setItem("test.i18n-locale", JSON.stringify("en"));
10
+ });
11
+
6
12
  function renderKanban(options?: {
7
13
  selectedValues?: unknown[];
8
14
  onSelectedValuesChange?: (v: unknown[]) => void;
9
15
  selectable?: boolean;
10
16
  }) {
11
17
  return render(() => (
12
- <Kanban
13
- selectedValues={options?.selectedValues}
14
- onSelectedValuesChange={options?.onSelectedValuesChange}
15
- class="h-[400px]"
16
- >
17
- <Kanban.Lane value="lane-1">
18
- <Kanban.LaneTitle>Lane 1</Kanban.LaneTitle>
19
- <Kanban.Card value={1} selectable={options?.selectable ?? true} contentClass="p-2">
20
- Card 1
21
- </Kanban.Card>
22
- <Kanban.Card value={2} selectable={options?.selectable ?? true} contentClass="p-2">
23
- Card 2
24
- </Kanban.Card>
25
- <Kanban.Card value={3} selectable={false} contentClass="p-2">
26
- Card 3 (not selectable)
27
- </Kanban.Card>
28
- </Kanban.Lane>
29
- <Kanban.Lane value="lane-2">
30
- <Kanban.LaneTitle>Lane 2</Kanban.LaneTitle>
31
- <Kanban.Card value={4} selectable contentClass="p-2">
32
- Card 4
33
- </Kanban.Card>
34
- </Kanban.Lane>
35
- </Kanban>
18
+ <ConfigProvider clientName="test">
19
+ <I18nProvider>
20
+ <Kanban
21
+ selectedValues={options?.selectedValues}
22
+ onSelectedValuesChange={options?.onSelectedValuesChange}
23
+ class="h-[400px]"
24
+ >
25
+ <Kanban.Lane value="lane-1">
26
+ <Kanban.LaneTitle>Lane 1</Kanban.LaneTitle>
27
+ <Kanban.Card value={1} selectable={options?.selectable ?? true} contentClass="p-2">
28
+ Card 1
29
+ </Kanban.Card>
30
+ <Kanban.Card value={2} selectable={options?.selectable ?? true} contentClass="p-2">
31
+ Card 2
32
+ </Kanban.Card>
33
+ <Kanban.Card value={3} selectable={false} contentClass="p-2">
34
+ Card 3 (not selectable)
35
+ </Kanban.Card>
36
+ </Kanban.Lane>
37
+ <Kanban.Lane value="lane-2">
38
+ <Kanban.LaneTitle>Lane 2</Kanban.LaneTitle>
39
+ <Kanban.Card value={4} selectable contentClass="p-2">
40
+ Card 4
41
+ </Kanban.Card>
42
+ </Kanban.Lane>
43
+ </Kanban>
44
+ </I18nProvider>
45
+ </ConfigProvider>
36
46
  ));
37
47
  }
38
48