@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.
- package/README.md +209 -202
- package/dist/components/data/calendar/Calendar.d.ts.map +1 -1
- package/dist/components/data/calendar/Calendar.js +3 -11
- package/dist/components/data/calendar/Calendar.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.js +13 -16
- 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/data/sheet/DataSheetConfigDialog.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheetConfigDialog.js +27 -9
- package/dist/components/data/sheet/DataSheetConfigDialog.js.map +2 -2
- package/dist/components/disclosure/Dialog.d.ts +1 -1
- package/dist/components/disclosure/Dialog.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.js +5 -5
- package/dist/components/disclosure/Dialog.js.map +2 -2
- package/dist/components/disclosure/dialogZIndex.d.ts +1 -1
- package/dist/components/features/crud-detail/CrudDetail.js +23 -23
- package/dist/components/features/crud-detail/CrudDetail.js.map +2 -2
- package/dist/components/features/crud-sheet/CrudSheet.js +49 -49
- package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
- package/dist/components/features/crud-sheet/types.d.ts +4 -4
- package/dist/components/features/crud-sheet/types.d.ts.map +1 -1
- package/dist/components/features/data-select-button/DataSelectButton.d.ts +25 -7
- package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
- package/dist/components/features/data-select-button/DataSelectButton.js +27 -12
- package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
- package/dist/components/features/permission-table/PermissionTable.js +4 -4
- package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
- package/dist/components/features/shared-data/SharedDataSelect.d.ts +22 -10
- package/dist/components/features/shared-data/SharedDataSelect.d.ts.map +1 -1
- package/dist/components/features/shared-data/SharedDataSelect.js +113 -29
- package/dist/components/features/shared-data/SharedDataSelect.js.map +2 -2
- package/dist/components/features/shared-data/SharedDataSelectButton.d.ts +3 -3
- package/dist/components/features/shared-data/SharedDataSelectButton.d.ts.map +1 -1
- package/dist/components/features/shared-data/SharedDataSelectButton.js.map +1 -1
- package/dist/components/features/shared-data/SharedDataSelectList.js +5 -4
- package/dist/components/features/shared-data/SharedDataSelectList.js.map +2 -2
- package/dist/components/feedback/notification/NotificationBanner.js +3 -3
- package/dist/components/feedback/notification/NotificationBanner.js.map +2 -2
- package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.js +12 -5
- package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
- package/dist/components/feedback/notification/NotificationProvider.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationProvider.js +3 -1
- package/dist/components/feedback/notification/NotificationProvider.js.map +2 -2
- package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
- package/dist/components/form-control/ThemeToggle.js +9 -6
- package/dist/components/form-control/ThemeToggle.js.map +2 -2
- package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/Checkbox.js +3 -1
- package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
- package/dist/components/form-control/checkbox/CheckboxGroup.js +1 -1
- package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
- package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/Radio.js +3 -1
- package/dist/components/form-control/checkbox/Radio.js.map +2 -2
- package/dist/components/form-control/checkbox/RadioGroup.js +1 -1
- package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
- package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
- package/dist/components/form-control/color-picker/ColorPicker.js +3 -1
- package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
- package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/form-control/combobox/Combobox.js +9 -5
- package/dist/components/form-control/combobox/Combobox.js.map +2 -2
- package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -9
- package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
- package/dist/components/form-control/editor/EditorToolbar.js +3 -3
- package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DatePicker.js +9 -3
- 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 +9 -3
- package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
- package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
- package/dist/components/form-control/field/NumberInput.js +9 -3
- package/dist/components/form-control/field/NumberInput.js.map +2 -2
- package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
- package/dist/components/form-control/field/TextInput.js +10 -4
- package/dist/components/form-control/field/TextInput.js.map +2 -2
- package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
- package/dist/components/form-control/field/Textarea.js +9 -3
- package/dist/components/form-control/field/Textarea.js.map +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/TimePicker.js +9 -3
- package/dist/components/form-control/field/TimePicker.js.map +2 -2
- package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
- package/dist/components/form-control/numpad/Numpad.js +5 -1
- package/dist/components/form-control/numpad/Numpad.js.map +2 -2
- package/dist/components/form-control/select/Select.js +7 -7
- package/dist/components/form-control/select/Select.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 +42 -20
- package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
- package/dist/components/layout/sidebar/SidebarContainer.js +3 -3
- package/dist/components/layout/sidebar/SidebarContainer.js.map +2 -2
- package/dist/components/layout/sidebar/SidebarMenu.d.ts.map +1 -1
- package/dist/components/layout/sidebar/SidebarMenu.js +5 -2
- package/dist/components/layout/sidebar/SidebarMenu.js.map +2 -2
- package/dist/components/layout/topbar/Topbar.js +3 -4
- package/dist/components/layout/topbar/Topbar.js.map +2 -2
- package/dist/components/layout/topbar/TopbarMenu.js +3 -3
- package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
- package/dist/hooks/createSelectionGroup.d.ts +2 -2
- package/dist/hooks/createSelectionGroup.d.ts.map +1 -1
- package/dist/hooks/createSelectionGroup.js +5 -2
- package/dist/hooks/createSelectionGroup.js.map +2 -2
- package/dist/providers/i18n/I18nContext.d.ts +0 -4
- package/dist/providers/i18n/I18nContext.d.ts.map +1 -1
- package/dist/providers/i18n/I18nContext.js +1 -5
- package/dist/providers/i18n/I18nContext.js.map +2 -2
- package/dist/providers/i18n/locales/en.d.ts +38 -0
- package/dist/providers/i18n/locales/en.d.ts.map +1 -1
- package/dist/providers/i18n/locales/en.js +39 -1
- package/dist/providers/i18n/locales/en.js.map +1 -1
- package/dist/providers/i18n/locales/ko.d.ts +38 -0
- package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
- package/dist/providers/i18n/locales/ko.js +39 -1
- package/dist/providers/i18n/locales/ko.js.map +1 -1
- package/package.json +6 -6
- package/src/components/data/calendar/Calendar.tsx +3 -4
- package/src/components/data/sheet/DataSheet.styles.ts +1 -1
- package/src/components/data/sheet/DataSheet.tsx +14 -15
- package/src/components/data/sheet/DataSheetConfigDialog.tsx +12 -10
- package/src/components/data/sheet/types.ts +1 -1
- package/src/components/disclosure/Dialog.tsx +10 -10
- package/src/components/disclosure/dialogZIndex.ts +1 -1
- package/src/components/features/crud-detail/CrudDetail.tsx +25 -25
- package/src/components/features/crud-sheet/CrudSheet.tsx +53 -53
- package/src/components/features/crud-sheet/types.ts +4 -4
- package/src/components/features/data-select-button/DataSelectButton.tsx +51 -21
- package/src/components/features/permission-table/PermissionTable.tsx +3 -3
- package/src/components/features/shared-data/SharedDataSelect.tsx +172 -33
- package/src/components/features/shared-data/SharedDataSelectButton.tsx +3 -2
- package/src/components/features/shared-data/SharedDataSelectList.tsx +4 -4
- package/src/components/feedback/notification/NotificationBanner.tsx +3 -3
- package/src/components/feedback/notification/NotificationBell.tsx +6 -4
- package/src/components/feedback/notification/NotificationProvider.tsx +3 -1
- package/src/components/form-control/ThemeToggle.tsx +10 -6
- package/src/components/form-control/checkbox/Checkbox.tsx +4 -1
- package/src/components/form-control/checkbox/CheckboxGroup.tsx +1 -1
- package/src/components/form-control/checkbox/Radio.tsx +4 -1
- package/src/components/form-control/checkbox/RadioGroup.tsx +1 -1
- package/src/components/form-control/color-picker/ColorPicker.tsx +4 -1
- package/src/components/form-control/combobox/Combobox.tsx +6 -3
- package/src/components/form-control/date-range-picker/DateRangePicker.tsx +8 -8
- package/src/components/form-control/editor/EditorToolbar.tsx +23 -23
- package/src/components/form-control/field/DatePicker.tsx +6 -3
- package/src/components/form-control/field/DateTimePicker.tsx +6 -3
- package/src/components/form-control/field/NumberInput.tsx +6 -3
- package/src/components/form-control/field/TextInput.tsx +7 -4
- package/src/components/form-control/field/Textarea.tsx +6 -3
- package/src/components/form-control/field/TimePicker.tsx +6 -3
- package/src/components/form-control/numpad/Numpad.tsx +3 -1
- package/src/components/form-control/select/Select.tsx +7 -7
- package/src/components/form-control/state-preset/StatePreset.tsx +14 -12
- package/src/components/layout/sidebar/SidebarContainer.tsx +3 -3
- package/src/components/layout/sidebar/SidebarMenu.tsx +3 -1
- package/src/components/layout/topbar/Topbar.tsx +3 -3
- package/src/components/layout/topbar/TopbarMenu.tsx +3 -3
- package/src/hooks/createSelectionGroup.tsx +8 -4
- package/src/providers/i18n/I18nContext.tsx +0 -7
- package/src/providers/i18n/locales/en.ts +38 -0
- package/src/providers/i18n/locales/ko.ts +38 -0
- package/tailwind.config.ts +2 -2
- package/tests/components/data/kanban/Kanban.selection.spec.tsx +34 -24
- package/tests/components/disclosure/Dialog.spec.tsx +28 -28
- package/tests/components/disclosure/DialogProvider.spec.tsx +51 -25
- package/tests/components/features/address/AddressSearch.spec.tsx +12 -4
- package/tests/components/features/crud-detail/CrudDetail.spec.tsx +1 -0
- package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +30 -6
- package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +77 -56
- package/tests/components/features/permission-table/PermissionTable.spec.tsx +12 -8
- package/tests/components/features/shared-data/SharedDataSelect.spec.tsx +172 -0
- package/tests/components/features/shared-data/SharedDataSelectList.spec.tsx +14 -2
- package/tests/components/feedback/notification/LiveRegion.spec.tsx +20 -9
- package/tests/components/feedback/notification/NotificationBanner.spec.tsx +64 -46
- package/tests/components/feedback/notification/NotificationBell.spec.tsx +70 -51
- package/tests/components/feedback/notification/NotificationContext.spec.tsx +105 -78
- package/tests/components/form-control/checkbox/Checkbox.spec.tsx +25 -20
- package/tests/components/form-control/checkbox/CheckboxGroup.spec.tsx +53 -30
- package/tests/components/form-control/checkbox/Radio.spec.tsx +25 -20
- package/tests/components/form-control/checkbox/RadioGroup.spec.tsx +53 -30
- package/tests/components/form-control/color-picker/ColorPicker.spec.tsx +24 -15
- package/tests/components/form-control/combobox/Combobox.spec.tsx +92 -59
- package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +2 -2
- package/tests/components/form-control/field/DatePicker.spec.tsx +50 -44
- package/tests/components/form-control/field/DateTimePicker.spec.tsx +51 -45
- package/tests/components/form-control/field/NumberInput.spec.tsx +53 -47
- package/tests/components/form-control/field/TextInput.spec.tsx +50 -44
- package/tests/components/form-control/field/Textarea.spec.tsx +35 -29
- package/tests/components/form-control/field/TimePicker.spec.tsx +43 -37
- package/tests/components/form-control/numpad/Numpad.spec.tsx +175 -25
- package/tests/components/form-control/select/Select.spec.tsx +5 -0
- package/tests/components/form-control/select/SelectItem.spec.tsx +1 -0
- package/tests/components/layout/sidebar/Sidebar.spec.tsx +79 -35
- package/tests/components/layout/sidebar/SidebarContainer.spec.tsx +1 -0
- package/tests/components/layout/sidebar/SidebarMenu.spec.tsx +28 -17
- package/tests/components/layout/topbar/TopbarActions.spec.tsx +41 -23
- package/tests/components/layout/topbar/createTopbarActions.spec.tsx +1 -0
- package/tests/hooks/usePrint.spec.tsx +1 -1
- package/tests/hooks/useRouterLink.spec.tsx +2 -0
- package/tests/hooks/useSyncConfig.spec.tsx +1 -0
- package/tests/providers/ErrorLoggerProvider.spec.tsx +1 -0
- package/tests/providers/PwaUpdateProvider.spec.tsx +16 -6
- package/tests/providers/ServiceClientContext.spec.tsx +40 -25
- package/tests/providers/i18n/I18nContext.spec.tsx +3 -4
- package/tests/providers/shared-data/SharedDataProvider.spec.tsx +2 -0
- package/dist/hooks/usePrint.d.ts +0 -3
- package/dist/hooks/usePrint.d.ts.map +0 -1
- package/dist/hooks/usePrint.js +0 -5
- package/dist/hooks/usePrint.js.map +0 -6
- package/src/hooks/usePrint.ts +0 -2
|
@@ -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 {
|
|
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
|
|
29
|
-
export interface
|
|
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
|
|
43
|
-
|
|
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
|
-
"
|
|
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 =
|
|
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 "
|
|
185
|
+
if (keys.length === 0) return i18n.t("validation.requiredField");
|
|
168
186
|
}
|
|
169
187
|
return local.validate?.(v);
|
|
170
188
|
});
|
|
171
189
|
|
|
172
|
-
// Open
|
|
173
|
-
const
|
|
190
|
+
// Open dialog
|
|
191
|
+
const handleOpenDialog = async () => {
|
|
174
192
|
if (local.disabled) return;
|
|
175
193
|
|
|
176
|
-
const result = await dialog.show<
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
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
|
|
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
|
|
299
|
+
onClick={() => void handleOpenDialog()}
|
|
270
300
|
tabIndex={-1}
|
|
271
|
-
aria-label={i18n
|
|
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 {
|
|
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 =
|
|
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
|
|
273
|
+
header={i18n.t("permissionTable.permissionItem")}
|
|
274
274
|
sortable={false}
|
|
275
275
|
resizable={false}
|
|
276
276
|
>
|
|
@@ -1,20 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
children as resolveChildren,
|
|
3
|
+
type Component,
|
|
4
|
+
createMemo,
|
|
5
|
+
For,
|
|
6
|
+
type JSX,
|
|
7
|
+
mergeProps,
|
|
8
|
+
splitProps,
|
|
9
|
+
} from "solid-js";
|
|
10
|
+
import { IconSearch } from "@tabler/icons-solidjs";
|
|
3
11
|
import { type SharedDataAccessor } from "../../../providers/shared-data/SharedDataContext";
|
|
4
12
|
import { Select, type SelectProps } from "../../form-control/select/Select";
|
|
5
13
|
import { Icon } from "../../display/Icon";
|
|
6
14
|
import { useDialog } from "../../disclosure/DialogContext";
|
|
7
|
-
import {
|
|
15
|
+
import { useDialogInstance } from "../../disclosure/DialogInstanceContext";
|
|
16
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
8
17
|
import { type ComponentSize } from "../../../styles/tokens.styles";
|
|
18
|
+
import {
|
|
19
|
+
type DataSelectDialogResult,
|
|
20
|
+
type DialogConfig,
|
|
21
|
+
} from "../data-select-button/DataSelectButton";
|
|
22
|
+
|
|
23
|
+
// -- Slot detection --
|
|
24
|
+
const ITEM_TEMPLATE_BRAND = Symbol("SharedDataSelect.ItemTemplate");
|
|
25
|
+
const ACTION_BRAND = Symbol("SharedDataSelect.Action");
|
|
26
|
+
|
|
27
|
+
interface ItemTemplateDef {
|
|
28
|
+
__brand: typeof ITEM_TEMPLATE_BRAND;
|
|
29
|
+
children: (item: any, index: number, depth: number) => JSX.Element;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ActionDef {
|
|
33
|
+
__brand: typeof ACTION_BRAND;
|
|
34
|
+
children: JSX.Element;
|
|
35
|
+
onClick?: (e: MouseEvent) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isItemTemplateDef(v: unknown): v is ItemTemplateDef {
|
|
39
|
+
return v != null && typeof v === "object" && "__brand" in v && (v as any).__brand === ITEM_TEMPLATE_BRAND;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isActionDef(v: unknown): v is ActionDef {
|
|
43
|
+
return v != null && typeof v === "object" && "__brand" in v && (v as any).__brand === ACTION_BRAND;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// -- Compound components --
|
|
47
|
+
const ItemTemplate: Component<{
|
|
48
|
+
children: (item: any, index: number, depth: number) => JSX.Element;
|
|
49
|
+
}> = (props) => {
|
|
50
|
+
// eslint-disable-next-line solid/reactivity -- factory function, not reactive JSX
|
|
51
|
+
return (() => ({
|
|
52
|
+
__brand: ITEM_TEMPLATE_BRAND,
|
|
53
|
+
children: props.children,
|
|
54
|
+
})) as unknown as JSX.Element;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const Action: Component<{
|
|
58
|
+
children?: JSX.Element;
|
|
59
|
+
onClick?: (e: MouseEvent) => void;
|
|
60
|
+
}> = (props) => {
|
|
61
|
+
// eslint-disable-next-line solid/reactivity -- factory function, not reactive JSX
|
|
62
|
+
return (() => ({
|
|
63
|
+
__brand: ACTION_BRAND,
|
|
64
|
+
children: props.children,
|
|
65
|
+
onClick: props.onClick,
|
|
66
|
+
})) as unknown as JSX.Element;
|
|
67
|
+
};
|
|
9
68
|
|
|
10
69
|
/** SharedDataSelect Props */
|
|
11
70
|
export interface SharedDataSelectProps<TItem> {
|
|
12
71
|
/** Shared data accessor */
|
|
13
72
|
data: SharedDataAccessor<TItem>;
|
|
14
73
|
|
|
15
|
-
/** Currently selected value */
|
|
74
|
+
/** Currently selected key value (translated to item internally) */
|
|
16
75
|
value?: unknown;
|
|
17
|
-
/** Value change callback */
|
|
76
|
+
/** Value change callback (receives key, not item) */
|
|
18
77
|
onValueChange?: (value: unknown) => void;
|
|
19
78
|
/** Multiple selection mode */
|
|
20
79
|
multiple?: boolean;
|
|
@@ -29,21 +88,37 @@ export interface SharedDataSelectProps<TItem> {
|
|
|
29
88
|
|
|
30
89
|
/** Item filter function */
|
|
31
90
|
filterFn?: (item: TItem, index: number) => boolean;
|
|
32
|
-
/** Selection
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
91
|
+
/** Selection dialog configuration */
|
|
92
|
+
dialog?: DialogConfig;
|
|
93
|
+
|
|
94
|
+
/** Compound children: ItemTemplate, Action */
|
|
95
|
+
children: JSX.Element;
|
|
96
|
+
}
|
|
36
97
|
|
|
37
|
-
|
|
38
|
-
|
|
98
|
+
interface SharedDataSelectComponent {
|
|
99
|
+
<TItem>(props: SharedDataSelectProps<TItem>): JSX.Element;
|
|
100
|
+
ItemTemplate: typeof ItemTemplate;
|
|
101
|
+
Action: typeof Action;
|
|
39
102
|
}
|
|
40
103
|
|
|
41
|
-
|
|
42
|
-
const [local, rest] = splitProps(props, [
|
|
104
|
+
const SharedDataSelectBase = <TItem,>(props: SharedDataSelectProps<TItem>): JSX.Element => {
|
|
105
|
+
const [local, rest] = splitProps(props, [
|
|
106
|
+
"data", "filterFn", "dialog", "children",
|
|
107
|
+
]);
|
|
43
108
|
|
|
44
|
-
const i18n =
|
|
109
|
+
const i18n = useI18n();
|
|
45
110
|
const dialog = useDialog();
|
|
46
111
|
|
|
112
|
+
// Resolve compound children
|
|
113
|
+
const resolved = resolveChildren(() => local.children);
|
|
114
|
+
const defs = createMemo(() => {
|
|
115
|
+
const arr = resolved.toArray();
|
|
116
|
+
return {
|
|
117
|
+
itemTemplate: arr.find(isItemTemplateDef) as unknown as ItemTemplateDef | undefined,
|
|
118
|
+
actions: arr.filter(isActionDef) as unknown as ActionDef[],
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
47
122
|
// Items with filterFn applied
|
|
48
123
|
const items = createMemo(() => {
|
|
49
124
|
const allItems = local.data.items();
|
|
@@ -51,21 +126,77 @@ export function SharedDataSelect<TItem>(props: SharedDataSelectProps<TItem>): JS
|
|
|
51
126
|
return allItems.filter(local.filterFn);
|
|
52
127
|
});
|
|
53
128
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
|
|
129
|
+
// Normalize value to keys array
|
|
130
|
+
const normalizeKeys = (value: unknown): (string | number)[] => {
|
|
131
|
+
if (value === undefined || value === null) return [];
|
|
132
|
+
if (Array.isArray(value)) return value;
|
|
133
|
+
return [value as string | number];
|
|
58
134
|
};
|
|
59
135
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
136
|
+
// Translate key(s) to item(s) for Select's value prop
|
|
137
|
+
const keyToItem = (key: string | number): TItem | undefined => {
|
|
138
|
+
return local.data.get(key);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const valueAsItem = createMemo((): TItem | TItem[] | undefined => {
|
|
142
|
+
const key = rest.value;
|
|
143
|
+
if (key === undefined || key === null) return undefined;
|
|
144
|
+
if (Array.isArray(key)) {
|
|
145
|
+
return key.map((k) => keyToItem(k as string | number)).filter((v): v is TItem => v !== undefined);
|
|
146
|
+
}
|
|
147
|
+
return keyToItem(key as string | number);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Translate item back to key for onValueChange callback
|
|
151
|
+
const itemToKey = (item: TItem | TItem[] | undefined): unknown => {
|
|
152
|
+
if (item === undefined || item === null) return undefined;
|
|
153
|
+
if (Array.isArray(item)) return item.map((i) => local.data.getKey(i));
|
|
154
|
+
return local.data.getKey(item);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Open dialog and handle selection result
|
|
158
|
+
const handleOpenDialog = async () => {
|
|
159
|
+
if (!local.dialog) return;
|
|
160
|
+
|
|
161
|
+
const dialogConfig = local.dialog;
|
|
162
|
+
const result = await dialog.show<DataSelectDialogResult<string | number>>(
|
|
163
|
+
() => {
|
|
164
|
+
const instance = useDialogInstance<DataSelectDialogResult<string | number>>();
|
|
165
|
+
return (
|
|
166
|
+
<dialogConfig.component
|
|
167
|
+
{...(dialogConfig.props ?? {})}
|
|
168
|
+
selectMode={rest.multiple ? "multiple" : "single"}
|
|
169
|
+
selectedKeys={normalizeKeys(rest.value)}
|
|
170
|
+
onSelect={(r: { keys: (string | number)[] }) =>
|
|
171
|
+
instance?.close({ selectedKeys: r.keys })
|
|
172
|
+
}
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
dialogConfig.option ?? {},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (result) {
|
|
180
|
+
const newKeys = result.selectedKeys;
|
|
181
|
+
if (rest.multiple) {
|
|
182
|
+
rest.onValueChange?.(newKeys);
|
|
183
|
+
} else {
|
|
184
|
+
rest.onValueChange?.(newKeys.length > 0 ? newKeys[0] : undefined);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
64
187
|
};
|
|
65
188
|
|
|
66
|
-
// Use mergeProps + as for Select's discriminated union (multiple: true | false?) and TItem → unknown conversion
|
|
67
|
-
// Wrap with getter to satisfy SolidJS reactivity lint rules
|
|
68
189
|
const selectProps = mergeProps(rest, {
|
|
190
|
+
get value() {
|
|
191
|
+
return valueAsItem();
|
|
192
|
+
},
|
|
193
|
+
get onValueChange() {
|
|
194
|
+
if (!rest.onValueChange) return undefined;
|
|
195
|
+
// eslint-disable-next-line solid/reactivity -- inside getter, tracked scope
|
|
196
|
+
return (item: TItem | TItem[] | undefined) => {
|
|
197
|
+
rest.onValueChange!(itemToKey(item));
|
|
198
|
+
};
|
|
199
|
+
},
|
|
69
200
|
get items() {
|
|
70
201
|
return items();
|
|
71
202
|
},
|
|
@@ -87,17 +218,25 @@ export function SharedDataSelect<TItem>(props: SharedDataSelectProps<TItem>): JS
|
|
|
87
218
|
|
|
88
219
|
return (
|
|
89
220
|
<Select {...selectProps}>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<Select.Action onClick={() => void handleOpenModal()} aria-label={i18n?.t("sharedDataSelect.search") ?? "Search"}>
|
|
93
|
-
<Icon icon={IconSearch} />
|
|
94
|
-
</Select.Action>
|
|
221
|
+
{defs().itemTemplate && (
|
|
222
|
+
<Select.ItemTemplate>{defs().itemTemplate!.children}</Select.ItemTemplate>
|
|
95
223
|
)}
|
|
96
|
-
{local.
|
|
97
|
-
<Select.Action onClick={() => void
|
|
98
|
-
<Icon icon={
|
|
224
|
+
{local.dialog && (
|
|
225
|
+
<Select.Action onClick={() => void handleOpenDialog()} aria-label={i18n.t("sharedDataSelect.search")}>
|
|
226
|
+
<Icon icon={IconSearch} />
|
|
99
227
|
</Select.Action>
|
|
100
228
|
)}
|
|
229
|
+
<For each={defs().actions}>
|
|
230
|
+
{(action) => (
|
|
231
|
+
<Select.Action onClick={action.onClick}>
|
|
232
|
+
{action.children}
|
|
233
|
+
</Select.Action>
|
|
234
|
+
)}
|
|
235
|
+
</For>
|
|
101
236
|
</Select>
|
|
102
237
|
);
|
|
103
|
-
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export const SharedDataSelect: SharedDataSelectComponent = SharedDataSelectBase as any;
|
|
241
|
+
SharedDataSelect.ItemTemplate = ItemTemplate;
|
|
242
|
+
SharedDataSelect.Action = Action;
|
|
@@ -3,6 +3,7 @@ import { type SharedDataAccessor } from "../../../providers/shared-data/SharedDa
|
|
|
3
3
|
import {
|
|
4
4
|
DataSelectButton,
|
|
5
5
|
type DataSelectButtonProps,
|
|
6
|
+
type DialogConfig,
|
|
6
7
|
} from "../data-select-button/DataSelectButton";
|
|
7
8
|
import { type ComponentSize } from "../../../styles/tokens.styles";
|
|
8
9
|
|
|
@@ -26,8 +27,8 @@ export interface SharedDataSelectButtonProps<TItem> {
|
|
|
26
27
|
/** Borderless style */
|
|
27
28
|
inset?: boolean;
|
|
28
29
|
|
|
29
|
-
/** Selection
|
|
30
|
-
|
|
30
|
+
/** Selection dialog configuration */
|
|
31
|
+
dialog: DialogConfig;
|
|
31
32
|
/** Item rendering function */
|
|
32
33
|
children: (item: TItem) => JSX.Element;
|
|
33
34
|
}
|
|
@@ -5,7 +5,7 @@ import { type SharedDataAccessor } from "../../../providers/shared-data/SharedDa
|
|
|
5
5
|
import { List } from "../../data/list/List";
|
|
6
6
|
import { Pagination } from "../../data/Pagination";
|
|
7
7
|
import { TextInput } from "../../form-control/field/TextInput";
|
|
8
|
-
import {
|
|
8
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
9
9
|
import { textMuted } from "../../../styles/tokens.styles";
|
|
10
10
|
import { createSlotSignal } from "../../../hooks/createSlotSignal";
|
|
11
11
|
import {
|
|
@@ -77,7 +77,7 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
|
|
|
77
77
|
"header",
|
|
78
78
|
]);
|
|
79
79
|
|
|
80
|
-
const i18n =
|
|
80
|
+
const i18n = useI18n();
|
|
81
81
|
|
|
82
82
|
// ─── Slot signals ──────────────────────────────────────
|
|
83
83
|
|
|
@@ -203,7 +203,7 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
|
|
|
203
203
|
<TextInput
|
|
204
204
|
value={searchText()}
|
|
205
205
|
onValueChange={setSearchText}
|
|
206
|
-
placeholder={i18n
|
|
206
|
+
placeholder={i18n.t("sharedDataSelectList.searchPlaceholder")}
|
|
207
207
|
class={"w-full"}
|
|
208
208
|
/>
|
|
209
209
|
</div>
|
|
@@ -231,7 +231,7 @@ export const SharedDataSelectList: SharedDataSelectListComponent = (<TItem,>(
|
|
|
231
231
|
disabled={local.disabled}
|
|
232
232
|
onClick={() => handleSelect(undefined)}
|
|
233
233
|
>
|
|
234
|
-
<span class={textMuted}>
|
|
234
|
+
<span class={textMuted}>{i18n.t("sharedDataSelectList.unspecified")}</span>
|
|
235
235
|
</List.Item>
|
|
236
236
|
</Show>
|
|
237
237
|
|
|
@@ -3,7 +3,7 @@ import { Portal } from "solid-js/web";
|
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
import { IconX } from "@tabler/icons-solidjs";
|
|
5
5
|
import { useNotification } from "./NotificationContext";
|
|
6
|
-
import {
|
|
6
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
7
7
|
import { Icon } from "../../display/Icon";
|
|
8
8
|
import { themeTokens } from "../../../styles/tokens.styles";
|
|
9
9
|
|
|
@@ -39,7 +39,7 @@ const dismissButtonClass = clsx("rounded", "p-1", "hover:bg-white/20");
|
|
|
39
39
|
|
|
40
40
|
export const NotificationBanner: Component = () => {
|
|
41
41
|
const notification = useNotification();
|
|
42
|
-
const i18n =
|
|
42
|
+
const i18n = useI18n();
|
|
43
43
|
|
|
44
44
|
const handleDismiss = () => {
|
|
45
45
|
notification.dismissBanner();
|
|
@@ -74,7 +74,7 @@ export const NotificationBanner: Component = () => {
|
|
|
74
74
|
</Show>
|
|
75
75
|
<button
|
|
76
76
|
type="button"
|
|
77
|
-
aria-label={i18n
|
|
77
|
+
aria-label={i18n.t("notification.close")}
|
|
78
78
|
class={dismissButtonClass}
|
|
79
79
|
onClick={handleDismiss}
|
|
80
80
|
>
|
|
@@ -7,6 +7,7 @@ import { Dropdown } from "../../disclosure/Dropdown";
|
|
|
7
7
|
import { Icon } from "../../display/Icon";
|
|
8
8
|
import { NotificationBanner } from "./NotificationBanner";
|
|
9
9
|
import { iconButtonBase } from "../../../styles/patterns.styles";
|
|
10
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
10
11
|
|
|
11
12
|
export interface NotificationBellProps {
|
|
12
13
|
showBanner?: boolean;
|
|
@@ -52,6 +53,7 @@ const itemTimeClass = clsx("mt-1 text-xs", "text-base-400");
|
|
|
52
53
|
|
|
53
54
|
export const NotificationBell: Component<NotificationBellProps> = (props) => {
|
|
54
55
|
const notification = useNotification();
|
|
56
|
+
const i18n = useI18n();
|
|
55
57
|
const [open, setOpen] = createSignal(false);
|
|
56
58
|
|
|
57
59
|
const handleClear = () => {
|
|
@@ -78,7 +80,7 @@ export const NotificationBell: Component<NotificationBellProps> = (props) => {
|
|
|
78
80
|
type="button"
|
|
79
81
|
data-notification-bell
|
|
80
82
|
class={buttonClass}
|
|
81
|
-
aria-label={
|
|
83
|
+
aria-label={i18n.t("notificationBell.unreadCount", { count: String(notification.unreadCount()) })}
|
|
82
84
|
aria-haspopup="true"
|
|
83
85
|
aria-expanded={open()}
|
|
84
86
|
>
|
|
@@ -93,7 +95,7 @@ export const NotificationBell: Component<NotificationBellProps> = (props) => {
|
|
|
93
95
|
<Dropdown.Content>
|
|
94
96
|
<div class="w-80 p-2">
|
|
95
97
|
<div class={dropdownHeaderClass}>
|
|
96
|
-
<span class="font-bold">
|
|
98
|
+
<span class="font-bold">{i18n.t("notificationBell.notifications")}</span>
|
|
97
99
|
<Show when={notification.items().length > 0}>
|
|
98
100
|
<button
|
|
99
101
|
type="button"
|
|
@@ -101,14 +103,14 @@ export const NotificationBell: Component<NotificationBellProps> = (props) => {
|
|
|
101
103
|
class={clearButtonClass}
|
|
102
104
|
onClick={handleClear}
|
|
103
105
|
>
|
|
104
|
-
|
|
106
|
+
{i18n.t("notificationBell.clearAll")}
|
|
105
107
|
</button>
|
|
106
108
|
</Show>
|
|
107
109
|
</div>
|
|
108
110
|
|
|
109
111
|
<Show
|
|
110
112
|
when={notification.items().length > 0}
|
|
111
|
-
fallback={<div class={emptyClass}>
|
|
113
|
+
fallback={<div class={emptyClass}>{i18n.t("notificationBell.noNotifications")}</div>}
|
|
112
114
|
>
|
|
113
115
|
<div class={listClass}>
|
|
114
116
|
<For each={[...notification.items()].reverse()}>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type NotificationUpdateOptions,
|
|
9
9
|
} from "./NotificationContext";
|
|
10
10
|
import { useLogger } from "../../../hooks/useLogger";
|
|
11
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
11
12
|
|
|
12
13
|
const MAX_ITEMS = 50;
|
|
13
14
|
|
|
@@ -22,6 +23,7 @@ const MAX_ITEMS = 50;
|
|
|
22
23
|
*/
|
|
23
24
|
export const NotificationProvider: ParentComponent = (props) => {
|
|
24
25
|
const logger = useLogger();
|
|
26
|
+
const i18n = useI18n();
|
|
25
27
|
const [items, setItems] = createSignal<NotificationItem[]>([]);
|
|
26
28
|
const [dismissedBannerId, setDismissedBannerId] = createSignal<string | null>(null);
|
|
27
29
|
|
|
@@ -158,7 +160,7 @@ export const NotificationProvider: ParentComponent = (props) => {
|
|
|
158
160
|
{/* Screen reader Live Region */}
|
|
159
161
|
<div role="status" aria-live="polite" aria-atomic="true" class="sr-only">
|
|
160
162
|
<Show when={latestUnread()}>
|
|
161
|
-
{(item) =>
|
|
163
|
+
{(item) => `${i18n.t("notificationProvider.prefix")} ${item().title} ${item().message ?? ""}`}
|
|
162
164
|
</Show>
|
|
163
165
|
</div>
|
|
164
166
|
{props.children}
|
|
@@ -5,6 +5,7 @@ import { useTheme, type ThemeMode } from "../../providers/ThemeContext";
|
|
|
5
5
|
import { Icon } from "../display/Icon";
|
|
6
6
|
import { ripple } from "../../directives/ripple";
|
|
7
7
|
import { iconButtonBase } from "../../styles/patterns.styles";
|
|
8
|
+
import { useI18n } from "../../providers/i18n/I18nContext";
|
|
8
9
|
|
|
9
10
|
void ripple;
|
|
10
11
|
|
|
@@ -18,10 +19,10 @@ const iconSizes: Record<"sm" | "lg", string> = {
|
|
|
18
19
|
lg: "1.5em",
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
light: "
|
|
23
|
-
system: "
|
|
24
|
-
dark: "
|
|
22
|
+
const modeLabelKeys: Record<ThemeMode, string> = {
|
|
23
|
+
light: "themeToggle.light",
|
|
24
|
+
system: "themeToggle.system",
|
|
25
|
+
dark: "themeToggle.dark",
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export interface ThemeToggleProps extends Omit<
|
|
@@ -54,6 +55,9 @@ export const ThemeToggle: Component<ThemeToggleProps> = (props) => {
|
|
|
54
55
|
const [local, rest] = splitProps(props, ["class", "size"]);
|
|
55
56
|
|
|
56
57
|
const { mode, cycleMode } = useTheme();
|
|
58
|
+
const i18n = useI18n();
|
|
59
|
+
|
|
60
|
+
const modeLabel = () => i18n.t(modeLabelKeys[mode()]);
|
|
57
61
|
|
|
58
62
|
const getClassName = () =>
|
|
59
63
|
twMerge(iconButtonBase, "p-1.5", local.size && sizeClasses[local.size], local.class);
|
|
@@ -68,8 +72,8 @@ export const ThemeToggle: Component<ThemeToggleProps> = (props) => {
|
|
|
68
72
|
type="button"
|
|
69
73
|
class={getClassName()}
|
|
70
74
|
onClick={cycleMode}
|
|
71
|
-
title={
|
|
72
|
-
aria-label={
|
|
75
|
+
title={modeLabel()}
|
|
76
|
+
aria-label={modeLabel()}
|
|
73
77
|
>
|
|
74
78
|
<Switch>
|
|
75
79
|
<Match when={mode() === "light"}>
|
|
@@ -4,6 +4,7 @@ import { IconCheck } from "@tabler/icons-solidjs";
|
|
|
4
4
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
5
5
|
import { ripple } from "../../../directives/ripple";
|
|
6
6
|
import { Icon } from "../../display/Icon";
|
|
7
|
+
import { useI18n } from "../../../providers/i18n/I18nContext";
|
|
7
8
|
import {
|
|
8
9
|
type CheckboxSize,
|
|
9
10
|
checkboxBaseClass,
|
|
@@ -51,6 +52,8 @@ export const Checkbox: ParentComponent<CheckboxProps> = (props) => {
|
|
|
51
52
|
"children",
|
|
52
53
|
]);
|
|
53
54
|
|
|
55
|
+
const i18n = useI18n();
|
|
56
|
+
|
|
54
57
|
const [value, setValue] = createControllableSignal({
|
|
55
58
|
value: () => local.value ?? false,
|
|
56
59
|
onChange: () => local.onValueChange,
|
|
@@ -84,7 +87,7 @@ export const Checkbox: ParentComponent<CheckboxProps> = (props) => {
|
|
|
84
87
|
|
|
85
88
|
const errorMsg = createMemo(() => {
|
|
86
89
|
const v = local.value ?? false;
|
|
87
|
-
if (local.required && !v) return "
|
|
90
|
+
if (local.required && !v) return i18n.t("validation.requiredSelection");
|
|
88
91
|
return local.validate?.(v);
|
|
89
92
|
});
|
|
90
93
|
|