@simplysm/solid 13.0.29 → 13.0.31
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 +10 -5
- package/dist/components/data/Pagination.d.ts +4 -5
- package/dist/components/data/Pagination.d.ts.map +1 -1
- package/dist/components/data/Pagination.js +14 -14
- package/dist/components/data/Pagination.js.map +2 -2
- package/dist/components/data/Table.js +1 -1
- package/dist/components/data/calendar/Calendar.js +1 -1
- package/dist/components/data/kanban/Kanban.d.ts +9 -9
- package/dist/components/data/kanban/Kanban.d.ts.map +1 -1
- package/dist/components/data/kanban/Kanban.js +4 -4
- package/dist/components/data/kanban/Kanban.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.js +102 -107
- package/dist/components/data/sheet/DataSheet.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.styles.js +1 -1
- package/dist/components/data/sheet/types.d.ts +2 -2
- package/dist/components/data/sheet/types.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.d.ts +8 -8
- package/dist/components/disclosure/Dialog.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.js +64 -69
- package/dist/components/disclosure/Dialog.js.map +2 -2
- package/dist/components/disclosure/DialogContext.d.ts +4 -4
- package/dist/components/disclosure/DialogContext.d.ts.map +1 -1
- package/dist/components/disclosure/DialogProvider.js +8 -8
- package/dist/components/disclosure/DialogProvider.js.map +2 -2
- package/dist/components/feedback/Progress.d.ts +3 -3
- package/dist/components/feedback/Progress.d.ts.map +1 -1
- package/dist/components/feedback/Progress.js +1 -1
- package/dist/components/feedback/Progress.js.map +2 -2
- package/dist/components/feedback/busy/BusyContainer.d.ts +1 -0
- package/dist/components/feedback/busy/BusyContainer.d.ts.map +1 -1
- package/dist/components/feedback/busy/BusyContainer.js +13 -6
- package/dist/components/feedback/busy/BusyContainer.js.map +2 -2
- package/dist/components/feedback/notification/NotificationBanner.js +1 -1
- package/dist/components/feedback/notification/NotificationBanner.js.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.js +4 -2
- 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 +1 -0
- package/dist/components/feedback/notification/NotificationProvider.js.map +1 -1
- package/dist/components/form-control/Invalid.d.ts +4 -2
- package/dist/components/form-control/Invalid.d.ts.map +1 -1
- package/dist/components/form-control/Invalid.js +81 -41
- package/dist/components/form-control/Invalid.js.map +2 -2
- package/dist/components/form-control/ThemeToggle.d.ts.map +1 -1
- package/dist/components/form-control/ThemeToggle.js +4 -5
- package/dist/components/form-control/ThemeToggle.js.map +2 -2
- package/dist/components/form-control/checkbox/Checkbox.d.ts +4 -2
- package/dist/components/form-control/checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/Checkbox.js +65 -52
- package/dist/components/form-control/checkbox/Checkbox.js.map +2 -2
- package/dist/components/form-control/checkbox/Checkbox.styles.d.ts +1 -2
- package/dist/components/form-control/checkbox/Checkbox.styles.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/Checkbox.styles.js +3 -9
- package/dist/components/form-control/checkbox/Checkbox.styles.js.map +1 -1
- package/dist/components/form-control/checkbox/CheckboxGroup.d.ts +9 -9
- package/dist/components/form-control/checkbox/CheckboxGroup.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/CheckboxGroup.js +10 -82
- package/dist/components/form-control/checkbox/CheckboxGroup.js.map +2 -2
- package/dist/components/form-control/checkbox/Radio.d.ts +4 -2
- package/dist/components/form-control/checkbox/Radio.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/Radio.js +64 -51
- package/dist/components/form-control/checkbox/Radio.js.map +2 -2
- package/dist/components/form-control/checkbox/RadioGroup.d.ts +9 -9
- package/dist/components/form-control/checkbox/RadioGroup.d.ts.map +1 -1
- package/dist/components/form-control/checkbox/RadioGroup.js +10 -77
- package/dist/components/form-control/checkbox/RadioGroup.js.map +2 -2
- package/dist/components/form-control/color-picker/ColorPicker.d.ts +8 -3
- package/dist/components/form-control/color-picker/ColorPicker.d.ts.map +1 -1
- package/dist/components/form-control/color-picker/ColorPicker.js +43 -26
- package/dist/components/form-control/color-picker/ColorPicker.js.map +2 -2
- package/dist/components/form-control/combobox/Combobox.d.ts +8 -8
- package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/form-control/combobox/Combobox.js +72 -59
- package/dist/components/form-control/combobox/Combobox.js.map +2 -2
- package/dist/components/form-control/editor/EditorToolbar.d.ts.map +1 -1
- package/dist/components/form-control/editor/EditorToolbar.js +3 -2
- package/dist/components/form-control/editor/EditorToolbar.js.map +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts +6 -0
- package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DatePicker.js +138 -117
- package/dist/components/form-control/field/DatePicker.js.map +2 -2
- package/dist/components/form-control/field/DateTimePicker.d.ts +6 -0
- package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DateTimePicker.js +138 -115
- package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
- package/dist/components/form-control/field/Field.styles.d.ts +14 -0
- package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
- package/dist/components/form-control/field/Field.styles.js +30 -0
- package/dist/components/form-control/field/Field.styles.js.map +1 -1
- package/dist/components/form-control/field/FieldPlaceholder.d.ts +7 -0
- package/dist/components/form-control/field/FieldPlaceholder.d.ts.map +1 -0
- package/dist/components/form-control/field/FieldPlaceholder.js +34 -0
- package/dist/components/form-control/field/FieldPlaceholder.js.map +6 -0
- package/dist/components/form-control/field/NumberInput.d.ts +10 -0
- package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
- package/dist/components/form-control/field/NumberInput.js +149 -115
- package/dist/components/form-control/field/NumberInput.js.map +2 -2
- package/dist/components/form-control/field/TextInput.d.ts +12 -0
- package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
- package/dist/components/form-control/field/TextInput.js +162 -116
- package/dist/components/form-control/field/TextInput.js.map +2 -2
- package/dist/components/form-control/field/Textarea.d.ts +10 -0
- package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
- package/dist/components/form-control/field/Textarea.js +156 -121
- package/dist/components/form-control/field/Textarea.js.map +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts +10 -0
- package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/TimePicker.js +126 -94
- package/dist/components/form-control/field/TimePicker.js.map +2 -2
- package/dist/components/form-control/select/Select.d.ts +7 -9
- package/dist/components/form-control/select/Select.d.ts.map +1 -1
- package/dist/components/form-control/select/Select.js +71 -60
- 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 +2 -1
- package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
- package/dist/components/layout/sidebar/SidebarMenu.js +1 -1
- package/dist/components/layout/sidebar/SidebarMenu.js.map +1 -1
- package/dist/components/layout/sidebar/SidebarUser.js +2 -2
- package/dist/components/layout/sidebar/SidebarUser.js.map +1 -1
- package/dist/hooks/createItemTemplate.d.ts +17 -0
- package/dist/hooks/createItemTemplate.d.ts.map +1 -0
- package/dist/hooks/createItemTemplate.js +40 -0
- package/dist/hooks/createItemTemplate.js.map +6 -0
- package/dist/hooks/createPointerDrag.d.ts +13 -0
- package/dist/hooks/createPointerDrag.d.ts.map +1 -0
- package/dist/hooks/createPointerDrag.js +15 -0
- package/dist/hooks/createPointerDrag.js.map +6 -0
- package/dist/hooks/createSelectionGroup.d.ts +70 -0
- package/dist/hooks/createSelectionGroup.d.ts.map +1 -0
- package/dist/hooks/createSelectionGroup.js +141 -0
- package/dist/hooks/createSelectionGroup.js.map +6 -0
- package/dist/hooks/useLocalStorage.d.ts +5 -3
- package/dist/hooks/useLocalStorage.d.ts.map +1 -1
- package/dist/hooks/useLocalStorage.js.map +1 -1
- package/dist/hooks/{createPwaUpdate.d.ts → usePwaUpdate.d.ts} +2 -2
- package/dist/hooks/usePwaUpdate.d.ts.map +1 -0
- package/dist/hooks/{createPwaUpdate.js → usePwaUpdate.js} +3 -3
- package/dist/hooks/usePwaUpdate.js.map +6 -0
- package/dist/hooks/useSyncConfig.d.ts +3 -3
- package/dist/hooks/useSyncConfig.d.ts.map +1 -1
- package/dist/hooks/useSyncConfig.js +6 -7
- package/dist/hooks/useSyncConfig.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/providers/InitializeProvider.js +2 -2
- package/dist/providers/InitializeProvider.js.map +2 -2
- package/dist/providers/ThemeContext.d.ts.map +1 -1
- package/dist/providers/ThemeContext.js +2 -1
- package/dist/providers/ThemeContext.js.map +2 -2
- package/dist/styles/patterns.styles.d.ts +1 -0
- package/dist/styles/patterns.styles.d.ts.map +1 -1
- package/dist/styles/patterns.styles.js +11 -0
- package/dist/styles/patterns.styles.js.map +1 -1
- package/dist/styles/tokens.styles.d.ts +1 -0
- package/dist/styles/tokens.styles.d.ts.map +1 -1
- package/dist/styles/tokens.styles.js.map +1 -1
- package/docs/data-components.md +34 -5
- package/docs/disclosure.md +28 -8
- package/docs/feedback.md +25 -2
- package/docs/form-controls.md +289 -33
- package/docs/hooks.md +19 -7
- package/docs/layout.md +12 -0
- package/docs/providers.md +120 -8
- package/docs/styling.md +90 -0
- package/package.json +3 -3
- package/src/components/data/Pagination.tsx +20 -21
- package/src/components/data/Table.tsx +1 -1
- package/src/components/data/calendar/Calendar.tsx +1 -1
- package/src/components/data/kanban/Kanban.tsx +18 -25
- package/src/components/data/sheet/DataSheet.styles.ts +1 -1
- package/src/components/data/sheet/DataSheet.tsx +122 -131
- package/src/components/data/sheet/types.ts +2 -2
- package/src/components/disclosure/Dialog.tsx +87 -100
- package/src/components/disclosure/DialogContext.ts +4 -4
- package/src/components/disclosure/DialogProvider.tsx +4 -4
- package/src/components/feedback/Progress.tsx +9 -5
- package/src/components/feedback/busy/BusyContainer.tsx +9 -5
- package/src/components/feedback/notification/NotificationBanner.tsx +1 -1
- package/src/components/feedback/notification/NotificationBell.tsx +4 -12
- package/src/components/feedback/notification/NotificationProvider.tsx +1 -0
- package/src/components/form-control/Invalid.tsx +114 -52
- package/src/components/form-control/ThemeToggle.tsx +4 -17
- package/src/components/form-control/checkbox/Checkbox.styles.ts +2 -9
- package/src/components/form-control/checkbox/Checkbox.tsx +39 -28
- package/src/components/form-control/checkbox/CheckboxGroup.tsx +18 -97
- package/src/components/form-control/checkbox/Radio.tsx +39 -28
- package/src/components/form-control/checkbox/RadioGroup.tsx +18 -92
- package/src/components/form-control/color-picker/ColorPicker.tsx +36 -16
- package/src/components/form-control/combobox/Combobox.tsx +43 -33
- package/src/components/form-control/editor/EditorToolbar.tsx +3 -14
- package/src/components/form-control/field/DatePicker.tsx +99 -97
- package/src/components/form-control/field/DateTimePicker.tsx +107 -95
- package/src/components/form-control/field/Field.styles.ts +45 -0
- package/src/components/form-control/field/FieldPlaceholder.tsx +18 -0
- package/src/components/form-control/field/NumberInput.tsx +122 -94
- package/src/components/form-control/field/TextInput.tsx +119 -95
- package/src/components/form-control/field/Textarea.tsx +124 -98
- package/src/components/form-control/field/TimePicker.tsx +101 -75
- package/src/components/form-control/select/Select.tsx +52 -44
- package/src/components/form-control/state-preset/StatePreset.tsx +2 -8
- package/src/components/layout/sidebar/SidebarMenu.tsx +1 -1
- package/src/components/layout/sidebar/SidebarUser.tsx +3 -3
- package/src/hooks/createItemTemplate.tsx +42 -0
- package/src/hooks/createPointerDrag.ts +28 -0
- package/src/hooks/createSelectionGroup.tsx +235 -0
- package/src/hooks/useLocalStorage.ts +8 -4
- package/src/hooks/{createPwaUpdate.ts → usePwaUpdate.ts} +1 -1
- package/src/hooks/useSyncConfig.ts +9 -13
- package/src/index.ts +1 -3
- package/src/providers/InitializeProvider.tsx +2 -2
- package/src/providers/ThemeContext.tsx +2 -1
- package/src/styles/patterns.styles.ts +12 -0
- package/src/styles/tokens.styles.ts +1 -0
- package/dist/hooks/createPwaUpdate.d.ts.map +0 -1
- package/dist/hooks/createPwaUpdate.js.map +0 -6
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { type JSX, type ParentComponent, Show, splitProps } from "solid-js";
|
|
1
|
+
import { type JSX, type ParentComponent, Show, splitProps, createMemo } from "solid-js";
|
|
2
2
|
import { twMerge } from "tailwind-merge";
|
|
3
3
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
4
4
|
import { ripple } from "../../../directives/ripple";
|
|
5
5
|
import clsx from "clsx";
|
|
6
6
|
import {
|
|
7
|
-
type CheckboxTheme,
|
|
8
7
|
type CheckboxSize,
|
|
9
8
|
checkboxBaseClass,
|
|
10
9
|
indicatorBaseClass,
|
|
11
|
-
|
|
10
|
+
checkedClass,
|
|
12
11
|
checkboxSizeClasses,
|
|
13
12
|
checkboxInsetClass,
|
|
14
13
|
checkboxInsetSizeHeightClasses,
|
|
15
14
|
checkboxInlineClass,
|
|
16
15
|
checkboxDisabledClass,
|
|
17
16
|
} from "./Checkbox.styles";
|
|
17
|
+
import { Invalid } from "../Invalid";
|
|
18
18
|
|
|
19
19
|
const radioDotClass = clsx("size-2", "rounded-full", "bg-current");
|
|
20
20
|
|
|
@@ -26,9 +26,11 @@ export interface RadioProps {
|
|
|
26
26
|
onValueChange?: (value: boolean) => void;
|
|
27
27
|
disabled?: boolean;
|
|
28
28
|
size?: CheckboxSize;
|
|
29
|
-
theme?: CheckboxTheme;
|
|
30
29
|
inset?: boolean;
|
|
31
30
|
inline?: boolean;
|
|
31
|
+
required?: boolean;
|
|
32
|
+
validate?: (value: boolean) => string | undefined;
|
|
33
|
+
touchMode?: boolean;
|
|
32
34
|
class?: string;
|
|
33
35
|
style?: JSX.CSSProperties;
|
|
34
36
|
children?: JSX.Element;
|
|
@@ -40,9 +42,11 @@ export const Radio: ParentComponent<RadioProps> = (props) => {
|
|
|
40
42
|
"onValueChange",
|
|
41
43
|
"disabled",
|
|
42
44
|
"size",
|
|
43
|
-
"theme",
|
|
44
45
|
"inset",
|
|
45
46
|
"inline",
|
|
47
|
+
"required",
|
|
48
|
+
"validate",
|
|
49
|
+
"touchMode",
|
|
46
50
|
"class",
|
|
47
51
|
"style",
|
|
48
52
|
"children",
|
|
@@ -76,32 +80,39 @@ export const Radio: ParentComponent<RadioProps> = (props) => {
|
|
|
76
80
|
local.class,
|
|
77
81
|
);
|
|
78
82
|
|
|
79
|
-
const getIndicatorClass = () =>
|
|
80
|
-
|
|
83
|
+
const getIndicatorClass = () =>
|
|
84
|
+
twMerge(indicatorBaseClass, "rounded-full", value() && checkedClass);
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
const errorMsg = createMemo(() => {
|
|
87
|
+
const v = local.value ?? false;
|
|
88
|
+
if (local.required && !v) return "필수 선택 항목입니다";
|
|
89
|
+
return local.validate?.(v);
|
|
90
|
+
});
|
|
84
91
|
|
|
85
92
|
return (
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<div class={
|
|
100
|
-
|
|
93
|
+
<Invalid message={errorMsg()} variant="border" touchMode={local.touchMode}>
|
|
94
|
+
<div class="inline-flex">
|
|
95
|
+
<label
|
|
96
|
+
{...rest}
|
|
97
|
+
use:ripple={!local.disabled}
|
|
98
|
+
role="radio"
|
|
99
|
+
aria-checked={value()}
|
|
100
|
+
tabIndex={local.disabled ? -1 : 0}
|
|
101
|
+
class={getWrapperClass()}
|
|
102
|
+
style={local.style}
|
|
103
|
+
onClick={handleClick}
|
|
104
|
+
onKeyDown={handleKeyDown}
|
|
105
|
+
>
|
|
106
|
+
<div class={getIndicatorClass()}>
|
|
107
|
+
<Show when={value()}>
|
|
108
|
+
<div class={radioDotClass} />
|
|
109
|
+
</Show>
|
|
110
|
+
</div>
|
|
111
|
+
<Show when={local.children}>
|
|
112
|
+
<span>{local.children}</span>
|
|
113
|
+
</Show>
|
|
114
|
+
</label>
|
|
101
115
|
</div>
|
|
102
|
-
|
|
103
|
-
<span>{local.children}</span>
|
|
104
|
-
</Show>
|
|
105
|
-
</label>
|
|
116
|
+
</Invalid>
|
|
106
117
|
);
|
|
107
118
|
};
|
|
@@ -1,60 +1,18 @@
|
|
|
1
|
-
import { type JSX
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
|
-
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
1
|
+
import { type JSX } from "solid-js";
|
|
4
2
|
import { Radio } from "./Radio";
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
interface RadioGroupContextValue<TValue> {
|
|
8
|
-
value: () => TValue | undefined;
|
|
9
|
-
select: (item: TValue) => void;
|
|
10
|
-
disabled: () => boolean;
|
|
11
|
-
size: () => CheckboxSize | undefined;
|
|
12
|
-
theme: () => CheckboxTheme | undefined;
|
|
13
|
-
inline: () => boolean;
|
|
14
|
-
inset: () => boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const RadioGroupContext = createContext<RadioGroupContextValue<any>>();
|
|
18
|
-
|
|
19
|
-
// --- RadioGroup.Item ---
|
|
20
|
-
|
|
21
|
-
interface RadioGroupItemProps<TValue> {
|
|
22
|
-
value: TValue;
|
|
23
|
-
disabled?: boolean;
|
|
24
|
-
children?: JSX.Element;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function RadioGroupItemInner<TValue>(props: RadioGroupItemProps<TValue>) {
|
|
28
|
-
const ctx = useContext(RadioGroupContext);
|
|
29
|
-
if (!ctx) throw new Error("RadioGroup.Item은 RadioGroup 내부에서만 사용할 수 있습니다");
|
|
30
|
-
|
|
31
|
-
const isSelected = () => ctx.value() === props.value;
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<Radio
|
|
35
|
-
value={isSelected()}
|
|
36
|
-
onValueChange={() => ctx.select(props.value)}
|
|
37
|
-
disabled={props.disabled ?? ctx.disabled()}
|
|
38
|
-
size={ctx.size()}
|
|
39
|
-
theme={ctx.theme()}
|
|
40
|
-
inline={ctx.inline()}
|
|
41
|
-
inset={ctx.inset()}
|
|
42
|
-
>
|
|
43
|
-
{props.children}
|
|
44
|
-
</Radio>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// --- RadioGroup ---
|
|
3
|
+
import { createSelectionGroup } from "../../../hooks/createSelectionGroup";
|
|
4
|
+
import type { CheckboxSize } from "./Checkbox.styles";
|
|
49
5
|
|
|
50
6
|
interface RadioGroupProps<TValue> {
|
|
51
7
|
value?: TValue;
|
|
52
8
|
onValueChange?: (value: TValue) => void;
|
|
53
9
|
disabled?: boolean;
|
|
54
10
|
size?: CheckboxSize;
|
|
55
|
-
theme?: CheckboxTheme;
|
|
56
11
|
inline?: boolean;
|
|
57
12
|
inset?: boolean;
|
|
13
|
+
required?: boolean;
|
|
14
|
+
validate?: (value: TValue | undefined) => string | undefined;
|
|
15
|
+
touchMode?: boolean;
|
|
58
16
|
class?: string;
|
|
59
17
|
style?: JSX.CSSProperties;
|
|
60
18
|
children?: JSX.Element;
|
|
@@ -62,50 +20,18 @@ interface RadioGroupProps<TValue> {
|
|
|
62
20
|
|
|
63
21
|
interface RadioGroupComponent {
|
|
64
22
|
<TValue = unknown>(props: RadioGroupProps<TValue>): JSX.Element;
|
|
65
|
-
Item:
|
|
23
|
+
Item: <TValue = unknown>(props: {
|
|
24
|
+
value: TValue;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
children?: JSX.Element;
|
|
27
|
+
}) => JSX.Element;
|
|
66
28
|
}
|
|
67
29
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"theme",
|
|
75
|
-
"inline",
|
|
76
|
-
"inset",
|
|
77
|
-
"class",
|
|
78
|
-
"style",
|
|
79
|
-
"children",
|
|
80
|
-
]);
|
|
81
|
-
|
|
82
|
-
const [value, setValue] = createControllableSignal({
|
|
83
|
-
value: () => local.value,
|
|
84
|
-
onChange: () => local.onValueChange,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const select = (item: unknown) => {
|
|
88
|
-
setValue(item);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const contextValue: RadioGroupContextValue<unknown> = {
|
|
92
|
-
value,
|
|
93
|
-
select,
|
|
94
|
-
disabled: () => local.disabled ?? false,
|
|
95
|
-
size: () => local.size,
|
|
96
|
-
theme: () => local.theme,
|
|
97
|
-
inline: () => local.inline ?? false,
|
|
98
|
-
inset: () => local.inset ?? false,
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<RadioGroupContext.Provider value={contextValue}>
|
|
103
|
-
<div {...rest} class={twMerge("inline-flex", local.class)} style={local.style}>
|
|
104
|
-
{local.children}
|
|
105
|
-
</div>
|
|
106
|
-
</RadioGroupContext.Provider>
|
|
107
|
-
);
|
|
108
|
-
};
|
|
30
|
+
const { Group } = createSelectionGroup({
|
|
31
|
+
mode: "single",
|
|
32
|
+
contextName: "RadioGroup",
|
|
33
|
+
ItemComponent: Radio,
|
|
34
|
+
emptyErrorMsg: "항목을 선택해 주세요",
|
|
35
|
+
});
|
|
109
36
|
|
|
110
|
-
export const RadioGroup =
|
|
111
|
-
RadioGroup.Item = RadioGroupItemInner;
|
|
37
|
+
export const RadioGroup = Group as unknown as RadioGroupComponent;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Component, type JSX, splitProps } from "solid-js";
|
|
1
|
+
import { type Component, createMemo, type JSX, splitProps } from "solid-js";
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
import { twMerge } from "tailwind-merge";
|
|
4
4
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
5
|
-
|
|
6
|
-
type
|
|
5
|
+
import { Invalid } from "../Invalid";
|
|
6
|
+
import { type ComponentSizeCompact } from "../../../styles/tokens.styles";
|
|
7
7
|
|
|
8
8
|
// 기본 스타일
|
|
9
9
|
const baseClass = clsx(
|
|
@@ -20,7 +20,7 @@ const baseClass = clsx(
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
// 사이즈별 스타일
|
|
23
|
-
const sizeClasses: Record<
|
|
23
|
+
const sizeClasses: Record<ComponentSizeCompact, string> = {
|
|
24
24
|
sm: "size-field-sm",
|
|
25
25
|
lg: "size-field-lg",
|
|
26
26
|
};
|
|
@@ -48,7 +48,16 @@ export interface ColorPickerProps {
|
|
|
48
48
|
disabled?: boolean;
|
|
49
49
|
|
|
50
50
|
/** 사이즈 */
|
|
51
|
-
size?:
|
|
51
|
+
size?: ComponentSizeCompact;
|
|
52
|
+
|
|
53
|
+
/** 필수 입력 여부 */
|
|
54
|
+
required?: boolean;
|
|
55
|
+
|
|
56
|
+
/** 커스텀 유효성 검사 함수 */
|
|
57
|
+
validate?: (value: string | undefined) => string | undefined;
|
|
58
|
+
|
|
59
|
+
/** touchMode: 포커스 해제 후에만 에러 표시 */
|
|
60
|
+
touchMode?: boolean;
|
|
52
61
|
|
|
53
62
|
/** 커스텀 class */
|
|
54
63
|
class?: string;
|
|
@@ -72,6 +81,9 @@ export const ColorPicker: Component<ColorPickerProps> = (props) => {
|
|
|
72
81
|
"title",
|
|
73
82
|
"disabled",
|
|
74
83
|
"size",
|
|
84
|
+
"required",
|
|
85
|
+
"validate",
|
|
86
|
+
"touchMode",
|
|
75
87
|
"class",
|
|
76
88
|
"style",
|
|
77
89
|
]);
|
|
@@ -93,17 +105,25 @@ export const ColorPicker: Component<ColorPickerProps> = (props) => {
|
|
|
93
105
|
local.class,
|
|
94
106
|
);
|
|
95
107
|
|
|
108
|
+
const errorMsg = createMemo(() => {
|
|
109
|
+
const v = props.value;
|
|
110
|
+
if (local.required && (v === undefined || v === "")) return "필수 입력 항목입니다";
|
|
111
|
+
return local.validate?.(v);
|
|
112
|
+
});
|
|
113
|
+
|
|
96
114
|
return (
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
<Invalid variant="border" message={errorMsg()} touchMode={local.touchMode}>
|
|
116
|
+
<input
|
|
117
|
+
{...rest}
|
|
118
|
+
data-color-picker
|
|
119
|
+
type="color"
|
|
120
|
+
class={getClassName()}
|
|
121
|
+
style={local.style}
|
|
122
|
+
value={value()}
|
|
123
|
+
title={local.title}
|
|
124
|
+
disabled={local.disabled}
|
|
125
|
+
onInput={handleInput}
|
|
126
|
+
/>
|
|
127
|
+
</Invalid>
|
|
108
128
|
);
|
|
109
129
|
};
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
children,
|
|
3
|
+
createMemo,
|
|
4
|
+
createSignal,
|
|
5
|
+
For,
|
|
6
|
+
type JSX,
|
|
7
|
+
onCleanup,
|
|
8
|
+
Show,
|
|
9
|
+
splitProps,
|
|
10
|
+
} from "solid-js";
|
|
2
11
|
import clsx from "clsx";
|
|
3
12
|
import { IconChevronDown, IconLoader2 } from "@tabler/icons-solidjs";
|
|
4
13
|
import { DebounceQueue } from "@simplysm/core-common";
|
|
@@ -9,9 +18,11 @@ import { ComboboxContext, type ComboboxContextValue } from "./ComboboxContext";
|
|
|
9
18
|
import { ComboboxItem } from "./ComboboxItem";
|
|
10
19
|
import { ripple } from "../../../directives/ripple";
|
|
11
20
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
21
|
+
import { createItemTemplate } from "../../../hooks/createItemTemplate";
|
|
12
22
|
import { splitSlots } from "../../../helpers/splitSlots";
|
|
13
23
|
import { type ComponentSize, textMuted } from "../../../styles/tokens.styles";
|
|
14
24
|
import { chevronWrapperClass, getTriggerClass } from "../DropdownTrigger.styles";
|
|
25
|
+
import { Invalid } from "../Invalid";
|
|
15
26
|
|
|
16
27
|
void ripple;
|
|
17
28
|
|
|
@@ -25,25 +36,8 @@ const inputClass = clsx(
|
|
|
25
36
|
|
|
26
37
|
const noResultsClass = clsx("px-3 py-2", textMuted);
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
interface ComboboxItemTemplateProps<TItem> {
|
|
32
|
-
children: (item: TItem, index: number) => JSX.Element;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 템플릿 함수를 저장하는 전역 WeakMap
|
|
36
|
-
const templateFnMap = new WeakMap<HTMLElement, (item: unknown, index: number) => JSX.Element>();
|
|
37
|
-
|
|
38
|
-
const ComboboxItemTemplate = <T,>(props: ComboboxItemTemplateProps<T>) => (
|
|
39
|
-
<span
|
|
40
|
-
ref={(el) => {
|
|
41
|
-
templateFnMap.set(el, props.children as (item: unknown, index: number) => JSX.Element);
|
|
42
|
-
}}
|
|
43
|
-
data-combobox-item-template
|
|
44
|
-
style={{ display: "none" }}
|
|
45
|
-
/>
|
|
46
|
-
);
|
|
39
|
+
const { TemplateSlot: ComboboxItemTemplate, getTemplate: getComboboxItemTemplate } =
|
|
40
|
+
createItemTemplate<[item: unknown, index: number]>("data-combobox-item-template");
|
|
47
41
|
|
|
48
42
|
// Props 정의
|
|
49
43
|
export interface ComboboxProps<TValue = unknown> {
|
|
@@ -54,7 +48,7 @@ export interface ComboboxProps<TValue = unknown> {
|
|
|
54
48
|
onValueChange?: (value: TValue) => void;
|
|
55
49
|
|
|
56
50
|
/** 아이템 로드 함수 (필수) */
|
|
57
|
-
loadItems: (query: string) => Promise<TValue[]>;
|
|
51
|
+
loadItems: (query: string) => TValue[] | Promise<TValue[]>;
|
|
58
52
|
|
|
59
53
|
/** 디바운스 딜레이 (기본값: 300ms) */
|
|
60
54
|
debounceMs?: number;
|
|
@@ -74,6 +68,12 @@ export interface ComboboxProps<TValue = unknown> {
|
|
|
74
68
|
/** 필수 입력 */
|
|
75
69
|
required?: boolean;
|
|
76
70
|
|
|
71
|
+
/** 커스텀 유효성 검사 함수 */
|
|
72
|
+
validate?: (value: TValue | undefined) => string | undefined;
|
|
73
|
+
|
|
74
|
+
/** touchMode: 포커스 해제 후에만 에러 표시 */
|
|
75
|
+
touchMode?: boolean;
|
|
76
|
+
|
|
77
77
|
/** 플레이스홀더 */
|
|
78
78
|
placeholder?: string;
|
|
79
79
|
|
|
@@ -146,6 +146,8 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
146
146
|
"placeholder",
|
|
147
147
|
"size",
|
|
148
148
|
"inset",
|
|
149
|
+
"validate",
|
|
150
|
+
"touchMode",
|
|
149
151
|
]);
|
|
150
152
|
|
|
151
153
|
let triggerRef!: HTMLDivElement;
|
|
@@ -154,7 +156,7 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
154
156
|
const [open, setOpen] = createSignal(false);
|
|
155
157
|
const [query, setQuery] = createSignal("");
|
|
156
158
|
const [items, setItems] = createSignal<T[]>([]);
|
|
157
|
-
const [
|
|
159
|
+
const [busyCount, setBusyCount] = createSignal(0);
|
|
158
160
|
|
|
159
161
|
// 선택된 값 관리 (controlled/uncontrolled 패턴)
|
|
160
162
|
const [getValue, setInternalValue] = createControllableSignal<T | undefined>({
|
|
@@ -200,12 +202,12 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
200
202
|
// loadItems 함수 참조를 캡처하여 사용
|
|
201
203
|
const loadItemsFn = local.loadItems;
|
|
202
204
|
debounceQueue.run(async () => {
|
|
203
|
-
|
|
205
|
+
setBusyCount((c) => c + 1);
|
|
204
206
|
try {
|
|
205
|
-
const result = await loadItemsFn(searchQuery);
|
|
207
|
+
const result = await Promise.resolve(loadItemsFn(searchQuery));
|
|
206
208
|
setItems(result);
|
|
207
209
|
} finally {
|
|
208
|
-
|
|
210
|
+
setBusyCount((c) => c - 1);
|
|
209
211
|
}
|
|
210
212
|
});
|
|
211
213
|
};
|
|
@@ -261,6 +263,14 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
261
263
|
}
|
|
262
264
|
};
|
|
263
265
|
|
|
266
|
+
// 유효성 검사 메시지
|
|
267
|
+
const errorMsg = createMemo(() => {
|
|
268
|
+
const v = getValue();
|
|
269
|
+
if (local.required && (v === undefined || v === null || v === ""))
|
|
270
|
+
return "필수 입력 항목입니다";
|
|
271
|
+
return local.validate?.(v);
|
|
272
|
+
});
|
|
273
|
+
|
|
264
274
|
// 트리거 클래스
|
|
265
275
|
const getTriggerClassName = () =>
|
|
266
276
|
getTriggerClass({
|
|
@@ -280,9 +290,7 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
280
290
|
|
|
281
291
|
// itemTemplate 함수 추출
|
|
282
292
|
const getItemTemplate = (): ((item: T, index: number) => JSX.Element) | undefined => {
|
|
283
|
-
|
|
284
|
-
if (templateSlots.length === 0) return undefined;
|
|
285
|
-
return templateFnMap.get(templateSlots[0]) as
|
|
293
|
+
return getComboboxItemTemplate(slots().comboboxItemTemplate) as
|
|
286
294
|
| ((item: T, index: number) => JSX.Element)
|
|
287
295
|
| undefined;
|
|
288
296
|
};
|
|
@@ -320,7 +328,7 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
320
328
|
const itemTemplate = getItemTemplate();
|
|
321
329
|
|
|
322
330
|
// 로딩 중
|
|
323
|
-
if (
|
|
331
|
+
if (busyCount() > 0) {
|
|
324
332
|
return <div class={noResultsClass}>검색 중...</div>;
|
|
325
333
|
}
|
|
326
334
|
|
|
@@ -372,7 +380,7 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
372
380
|
>
|
|
373
381
|
<div class={selectedValueClass}>{renderDisplayContent()}</div>
|
|
374
382
|
<div class={chevronWrapperClass}>
|
|
375
|
-
<Show when={
|
|
383
|
+
<Show when={busyCount() > 0} fallback={<Icon icon={IconChevronDown} size="1em" />}>
|
|
376
384
|
<Icon icon={IconLoader2} size="1em" class="animate-spin" />
|
|
377
385
|
</Show>
|
|
378
386
|
</div>
|
|
@@ -388,9 +396,11 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
|
|
|
388
396
|
};
|
|
389
397
|
|
|
390
398
|
return (
|
|
391
|
-
<
|
|
392
|
-
<
|
|
393
|
-
|
|
399
|
+
<Invalid message={errorMsg()} variant="border" touchMode={local.touchMode}>
|
|
400
|
+
<ComboboxContext.Provider value={contextValue as ComboboxContextValue}>
|
|
401
|
+
<ComboboxInner>{local.children}</ComboboxInner>
|
|
402
|
+
</ComboboxContext.Provider>
|
|
403
|
+
</Invalid>
|
|
394
404
|
);
|
|
395
405
|
};
|
|
396
406
|
|
|
@@ -3,6 +3,7 @@ import clsx from "clsx";
|
|
|
3
3
|
import { twMerge } from "tailwind-merge";
|
|
4
4
|
import type { Editor } from "@tiptap/core";
|
|
5
5
|
import { createEditorTransaction } from "solid-tiptap";
|
|
6
|
+
import { iconButtonBase } from "../../../styles/patterns.styles";
|
|
6
7
|
import {
|
|
7
8
|
IconBold,
|
|
8
9
|
IconItalic,
|
|
@@ -39,13 +40,7 @@ const toolbarClass = clsx(
|
|
|
39
40
|
);
|
|
40
41
|
|
|
41
42
|
// 툴바 버튼 기본 스타일
|
|
42
|
-
const toolbarBtnClass =
|
|
43
|
-
"inline-flex items-center justify-center",
|
|
44
|
-
"size-7 rounded",
|
|
45
|
-
"text-base-600 dark:text-base-400",
|
|
46
|
-
"hover:bg-base-100 dark:hover:bg-base-800",
|
|
47
|
-
"transition-colors",
|
|
48
|
-
);
|
|
43
|
+
const toolbarBtnClass = twMerge(iconButtonBase, "size-7");
|
|
49
44
|
|
|
50
45
|
// 툴바 버튼 활성 스타일
|
|
51
46
|
const toolbarBtnActiveClass = clsx(
|
|
@@ -57,13 +52,7 @@ const toolbarBtnActiveClass = clsx(
|
|
|
57
52
|
const separatorClass = clsx("mx-1 h-5 w-px", "bg-base-300 dark:bg-base-700");
|
|
58
53
|
|
|
59
54
|
// 색상 선택 label 스타일
|
|
60
|
-
const colorLabelClass =
|
|
61
|
-
"relative inline-flex items-center justify-center",
|
|
62
|
-
"size-7 cursor-pointer rounded",
|
|
63
|
-
"text-base-600 dark:text-base-400",
|
|
64
|
-
"hover:bg-base-100 dark:hover:bg-base-800",
|
|
65
|
-
"transition-colors",
|
|
66
|
-
);
|
|
55
|
+
const colorLabelClass = twMerge(iconButtonBase, "relative", "size-7");
|
|
67
56
|
|
|
68
57
|
// 색상 input 숨기기 스타일
|
|
69
58
|
const colorInputClass = clsx("absolute opacity-0", "size-0");
|