@simplysm/solid 13.0.53 → 13.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/components/data/crud-detail/CrudDetail.d.ts +14 -0
- package/dist/components/data/crud-detail/CrudDetail.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetail.js +348 -0
- package/dist/components/data/crud-detail/CrudDetail.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailAfter.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailBefore.js.map +6 -0
- package/dist/components/data/crud-detail/CrudDetailTools.d.ts +7 -0
- package/dist/components/data/crud-detail/CrudDetailTools.d.ts.map +1 -0
- package/dist/components/data/crud-detail/CrudDetailTools.js +14 -0
- package/dist/components/data/crud-detail/CrudDetailTools.js.map +6 -0
- package/dist/components/data/crud-detail/types.d.ts +45 -0
- package/dist/components/data/crud-detail/types.d.ts.map +1 -0
- package/dist/components/data/crud-detail/types.js +1 -0
- package/dist/components/data/crud-detail/types.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheet.d.ts +17 -0
- package/dist/components/data/crud-sheet/CrudSheet.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheet.js +679 -0
- package/dist/components/data/crud-sheet/CrudSheet.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts +5 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.js +29 -0
- package/dist/components/data/crud-sheet/CrudSheetColumn.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetFilter.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetHeader.js.map +6 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.d.ts +7 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.js +14 -0
- package/dist/components/data/crud-sheet/CrudSheetTools.js.map +6 -0
- package/dist/components/data/crud-sheet/types.d.ts +109 -0
- package/dist/components/data/crud-sheet/types.d.ts.map +1 -0
- package/dist/components/data/crud-sheet/types.js +1 -0
- package/dist/components/data/crud-sheet/types.js.map +6 -0
- package/dist/components/data/kanban/Kanban.d.ts.map +1 -1
- package/dist/components/data/kanban/Kanban.js +137 -138
- package/dist/components/data/kanban/Kanban.js.map +2 -2
- package/dist/components/data/kanban/KanbanContext.d.ts +5 -1
- package/dist/components/data/kanban/KanbanContext.d.ts.map +1 -1
- package/dist/components/data/kanban/KanbanContext.js.map +1 -1
- package/dist/components/data/list/ListItem.d.ts.map +1 -1
- package/dist/components/data/list/ListItem.js +109 -99
- package/dist/components/data/list/ListItem.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.css +28 -10
- package/dist/components/data/sheet/DataSheet.js +1 -1
- package/dist/components/data/sheet/DataSheet.js.map +2 -2
- package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js +1 -1
- package/dist/components/data/sheet/DataSheet.styles.js.map +1 -1
- package/dist/components/disclosure/Dialog.d.ts +16 -10
- package/dist/components/disclosure/Dialog.d.ts.map +1 -1
- package/dist/components/disclosure/Dialog.js +126 -91
- package/dist/components/disclosure/Dialog.js.map +2 -2
- package/dist/components/disclosure/DialogContext.d.ts +2 -4
- package/dist/components/disclosure/DialogContext.d.ts.map +1 -1
- package/dist/components/disclosure/DialogContext.js.map +1 -1
- package/dist/components/disclosure/DialogProvider.d.ts.map +1 -1
- package/dist/components/disclosure/DialogProvider.js +14 -9
- package/dist/components/disclosure/DialogProvider.js.map +2 -2
- package/dist/components/disclosure/Dropdown.d.ts +46 -22
- package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
- package/dist/components/disclosure/Dropdown.js +100 -65
- package/dist/components/disclosure/Dropdown.js.map +2 -2
- package/dist/components/feedback/notification/NotificationBanner.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBanner.js +3 -3
- package/dist/components/feedback/notification/NotificationBanner.js.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
- package/dist/components/feedback/notification/NotificationBell.js +84 -84
- package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
- package/dist/components/form-control/Invalid.js +1 -1
- package/dist/components/form-control/combobox/Combobox.d.ts +6 -3
- package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/form-control/combobox/Combobox.js +150 -168
- package/dist/components/form-control/combobox/Combobox.js.map +2 -2
- package/dist/components/form-control/combobox/ComboboxContext.d.ts +3 -0
- package/dist/components/form-control/combobox/ComboboxContext.d.ts.map +1 -1
- package/dist/components/form-control/combobox/ComboboxContext.js.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts +0 -2
- package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -17
- package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
- package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DatePicker.js +3 -2
- package/dist/components/form-control/field/DatePicker.js.map +2 -2
- package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/DateTimePicker.js +3 -2
- package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
- package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
- package/dist/components/form-control/field/Field.styles.js +2 -1
- package/dist/components/form-control/field/Field.styles.js.map +1 -1
- package/dist/components/form-control/field/NumberInput.d.ts +15 -5
- package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
- package/dist/components/form-control/field/NumberInput.js +181 -141
- package/dist/components/form-control/field/NumberInput.js.map +2 -2
- package/dist/components/form-control/field/TextInput.d.ts +9 -5
- package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
- package/dist/components/form-control/field/TextInput.js +199 -154
- package/dist/components/form-control/field/TextInput.js.map +2 -2
- package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
- package/dist/components/form-control/field/TimePicker.js +3 -2
- package/dist/components/form-control/field/TimePicker.js.map +2 -2
- package/dist/components/form-control/select/Select.d.ts +3 -3
- package/dist/components/form-control/select/Select.d.ts.map +1 -1
- package/dist/components/form-control/select/Select.js +116 -100
- package/dist/components/form-control/select/Select.js.map +2 -2
- package/dist/components/form-control/select/SelectContext.d.ts +9 -1
- package/dist/components/form-control/select/SelectContext.d.ts.map +1 -1
- package/dist/components/form-control/select/SelectContext.js.map +1 -1
- package/dist/components/form-control/select/SelectItem.d.ts.map +1 -1
- package/dist/components/form-control/select/SelectItem.js +77 -67
- package/dist/components/form-control/select/SelectItem.js.map +2 -2
- package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
- package/dist/components/form-control/state-preset/StatePreset.js +1 -1
- package/dist/components/form-control/state-preset/StatePreset.js.map +1 -1
- package/dist/components/layout/topbar/Topbar.d.ts +2 -0
- package/dist/components/layout/topbar/Topbar.d.ts.map +1 -1
- package/dist/components/layout/topbar/Topbar.js +2 -0
- package/dist/components/layout/topbar/Topbar.js.map +2 -2
- package/dist/components/layout/topbar/TopbarActions.d.ts +3 -0
- package/dist/components/layout/topbar/TopbarActions.d.ts.map +1 -0
- package/dist/components/layout/topbar/TopbarActions.js +17 -0
- package/dist/components/layout/topbar/TopbarActions.js.map +6 -0
- package/dist/components/layout/topbar/TopbarContainer.d.ts +1 -1
- package/dist/components/layout/topbar/TopbarContainer.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarContainer.js +21 -12
- package/dist/components/layout/topbar/TopbarContainer.js.map +2 -2
- package/dist/components/layout/topbar/TopbarContext.d.ts +9 -0
- package/dist/components/layout/topbar/TopbarContext.d.ts.map +1 -0
- package/dist/components/layout/topbar/TopbarContext.js +29 -0
- package/dist/components/layout/topbar/TopbarContext.js.map +6 -0
- package/dist/components/layout/topbar/TopbarMenu.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarMenu.js +63 -57
- package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
- package/dist/components/layout/topbar/TopbarUser.d.ts.map +1 -1
- package/dist/components/layout/topbar/TopbarUser.js +53 -54
- package/dist/components/layout/topbar/TopbarUser.js.map +2 -2
- package/dist/hooks/createControllableStore.d.ts +29 -0
- package/dist/hooks/createControllableStore.d.ts.map +1 -0
- package/dist/hooks/createControllableStore.js +19 -0
- package/dist/hooks/createControllableStore.js.map +6 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/styles/patterns.styles.d.ts.map +1 -1
- package/dist/styles/patterns.styles.js +7 -1
- package/dist/styles/patterns.styles.js.map +1 -1
- package/docs/data-components.md +428 -0
- package/docs/disclosure.md +65 -35
- package/docs/form-controls.md +18 -3
- package/docs/helpers.md +0 -39
- package/docs/hooks.md +39 -0
- package/docs/layout.md +70 -1
- package/package.json +4 -3
- package/src/components/data/crud-detail/CrudDetail.tsx +346 -0
- package/src/components/data/crud-detail/CrudDetailAfter.tsx +19 -0
- package/src/components/data/crud-detail/CrudDetailBefore.tsx +19 -0
- package/src/components/data/crud-detail/CrudDetailTools.tsx +19 -0
- package/src/components/data/crud-detail/types.ts +58 -0
- package/src/components/data/crud-sheet/CrudSheet.tsx +628 -0
- package/src/components/data/crud-sheet/CrudSheetColumn.tsx +34 -0
- package/src/components/data/crud-sheet/CrudSheetFilter.tsx +21 -0
- package/src/components/data/crud-sheet/CrudSheetHeader.tsx +19 -0
- package/src/components/data/crud-sheet/CrudSheetTools.tsx +21 -0
- package/src/components/data/crud-sheet/types.ts +133 -0
- package/src/components/data/kanban/Kanban.tsx +72 -65
- package/src/components/data/kanban/KanbanContext.ts +7 -1
- package/src/components/data/list/ListItem.tsx +31 -18
- package/src/components/data/sheet/DataSheet.css +28 -10
- package/src/components/data/sheet/DataSheet.styles.ts +1 -1
- package/src/components/data/sheet/DataSheet.tsx +1 -1
- package/src/components/disclosure/Dialog.tsx +143 -105
- package/src/components/disclosure/DialogContext.ts +2 -4
- package/src/components/disclosure/DialogProvider.tsx +4 -2
- package/src/components/disclosure/Dropdown.tsx +174 -86
- package/src/components/feedback/notification/NotificationBanner.tsx +3 -9
- package/src/components/feedback/notification/NotificationBell.tsx +51 -57
- package/src/components/form-control/Invalid.tsx +1 -1
- package/src/components/form-control/combobox/Combobox.tsx +109 -133
- package/src/components/form-control/combobox/ComboboxContext.ts +4 -1
- package/src/components/form-control/date-range-picker/DateRangePicker.tsx +6 -16
- package/src/components/form-control/field/DatePicker.tsx +4 -1
- package/src/components/form-control/field/DateTimePicker.tsx +3 -0
- package/src/components/form-control/field/Field.styles.ts +1 -0
- package/src/components/form-control/field/NumberInput.tsx +131 -86
- package/src/components/form-control/field/TextInput.tsx +139 -88
- package/src/components/form-control/field/TimePicker.tsx +3 -0
- package/src/components/form-control/select/Select.tsx +85 -67
- package/src/components/form-control/select/SelectContext.ts +12 -1
- package/src/components/form-control/select/SelectItem.tsx +39 -18
- package/src/components/form-control/state-preset/StatePreset.tsx +1 -0
- package/src/components/layout/topbar/Topbar.tsx +3 -0
- package/src/components/layout/topbar/TopbarActions.tsx +8 -0
- package/src/components/layout/topbar/TopbarContainer.tsx +9 -5
- package/src/components/layout/topbar/TopbarContext.ts +36 -0
- package/src/components/layout/topbar/TopbarMenu.tsx +52 -55
- package/src/components/layout/topbar/TopbarUser.tsx +28 -31
- package/src/hooks/createControllableStore.ts +47 -0
- package/src/index.ts +6 -1
- package/src/styles/patterns.styles.ts +7 -1
- package/tailwind.css +4 -0
- package/dist/helpers/splitSlots.d.ts +0 -25
- package/dist/helpers/splitSlots.d.ts.map +0 -1
- package/dist/helpers/splitSlots.js +0 -25
- package/dist/helpers/splitSlots.js.map +0 -6
- package/dist/hooks/createItemTemplate.d.ts +0 -17
- package/dist/hooks/createItemTemplate.d.ts.map +0 -1
- package/dist/hooks/createItemTemplate.js +0 -40
- package/dist/hooks/createItemTemplate.js.map +0 -6
- package/src/helpers/splitSlots.ts +0 -51
- package/src/hooks/createItemTemplate.tsx +0 -42
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { JSX } from "solid-js";
|
|
2
|
+
import type { CrudSheetToolsDef } from "./types";
|
|
3
|
+
|
|
4
|
+
export function isCrudSheetToolsDef(value: unknown): value is CrudSheetToolsDef<any> {
|
|
5
|
+
return (
|
|
6
|
+
value != null &&
|
|
7
|
+
typeof value === "object" &&
|
|
8
|
+
(value as Record<string, unknown>)["__type"] === "crud-sheet-tools"
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* eslint-disable solid/reactivity -- plain object 반환 패턴으로 reactive context 불필요 */
|
|
13
|
+
export function CrudSheetTools<_TItem>(props: {
|
|
14
|
+
children: (ctx: any) => JSX.Element;
|
|
15
|
+
}): JSX.Element {
|
|
16
|
+
return {
|
|
17
|
+
__type: "crud-sheet-tools",
|
|
18
|
+
children: props.children,
|
|
19
|
+
} as unknown as JSX.Element;
|
|
20
|
+
}
|
|
21
|
+
/* eslint-enable solid/reactivity */
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { JSX } from "solid-js";
|
|
2
|
+
import type { SetStoreFunction } from "solid-js/store";
|
|
3
|
+
import type { ArrayDiffs2Result } from "@simplysm/core-common";
|
|
4
|
+
import type { DataSheetColumnProps, SortingDef } from "../sheet/types";
|
|
5
|
+
|
|
6
|
+
// ── Search ──
|
|
7
|
+
|
|
8
|
+
export interface SearchResult<TItem> {
|
|
9
|
+
items: TItem[];
|
|
10
|
+
pageCount?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ── Feature Configs ──
|
|
14
|
+
|
|
15
|
+
export interface InlineEditConfig<TItem> {
|
|
16
|
+
submit: (diffs: ArrayDiffs2Result<TItem>[]) => Promise<void>;
|
|
17
|
+
newItem: () => TItem;
|
|
18
|
+
deleteProp?: keyof TItem & string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ModalEditConfig<TItem> {
|
|
22
|
+
editItem: (item?: TItem) => Promise<boolean>;
|
|
23
|
+
deleteItems?: (items: TItem[]) => Promise<boolean>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ExcelConfig<TItem> {
|
|
27
|
+
download: (items: TItem[]) => Promise<void>;
|
|
28
|
+
upload?: (file: File) => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SelectResult<TItem> {
|
|
32
|
+
items: TItem[];
|
|
33
|
+
keys: (string | number)[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── Cell Context ──
|
|
37
|
+
|
|
38
|
+
export interface CrudSheetCellContext<TItem> {
|
|
39
|
+
item: TItem;
|
|
40
|
+
index: number;
|
|
41
|
+
row: number;
|
|
42
|
+
depth: number;
|
|
43
|
+
setItem: <TKey extends keyof TItem>(key: TKey, value: TItem[TKey]) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── CrudSheet Context (for Tools render prop) ──
|
|
47
|
+
|
|
48
|
+
export interface CrudSheetContext<TItem> {
|
|
49
|
+
items(): TItem[];
|
|
50
|
+
selectedItems(): TItem[];
|
|
51
|
+
page(): number;
|
|
52
|
+
sorts(): SortingDef[];
|
|
53
|
+
busy(): boolean;
|
|
54
|
+
hasChanges(): boolean;
|
|
55
|
+
|
|
56
|
+
save(): Promise<void>;
|
|
57
|
+
refresh(): Promise<void>;
|
|
58
|
+
addItem(): void;
|
|
59
|
+
setPage(page: number): void;
|
|
60
|
+
setSorts(sorts: SortingDef[]): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Props ──
|
|
64
|
+
|
|
65
|
+
export type CrudSheetProps<TItem, TFilter extends Record<string, any>> = CrudSheetBaseProps<
|
|
66
|
+
TItem,
|
|
67
|
+
TFilter
|
|
68
|
+
> &
|
|
69
|
+
(
|
|
70
|
+
| { inlineEdit: InlineEditConfig<TItem>; modalEdit?: never }
|
|
71
|
+
| { modalEdit: ModalEditConfig<TItem>; inlineEdit?: never }
|
|
72
|
+
| { inlineEdit?: never; modalEdit?: never }
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
interface CrudSheetBaseProps<TItem, TFilter extends Record<string, any>> {
|
|
76
|
+
search: (filter: TFilter, page: number, sorts: SortingDef[]) => Promise<SearchResult<TItem>>;
|
|
77
|
+
getItemKey: (item: TItem) => string | number | undefined;
|
|
78
|
+
persistKey?: string;
|
|
79
|
+
itemsPerPage?: number;
|
|
80
|
+
editable?: boolean;
|
|
81
|
+
itemEditable?: (item: TItem) => boolean;
|
|
82
|
+
itemDeletable?: (item: TItem) => boolean;
|
|
83
|
+
filterInitial?: TFilter;
|
|
84
|
+
items?: TItem[];
|
|
85
|
+
onItemsChange?: (items: TItem[]) => void;
|
|
86
|
+
excel?: ExcelConfig<TItem>;
|
|
87
|
+
selectMode?: "single" | "multi";
|
|
88
|
+
onSelect?: (result: SelectResult<TItem>) => void;
|
|
89
|
+
hideAutoTools?: boolean;
|
|
90
|
+
class?: string;
|
|
91
|
+
children: JSX.Element;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Sub-component Defs ──
|
|
95
|
+
|
|
96
|
+
export interface CrudSheetColumnDef<TItem> {
|
|
97
|
+
__type: "crud-sheet-column";
|
|
98
|
+
key: string;
|
|
99
|
+
header: string[];
|
|
100
|
+
headerContent?: () => JSX.Element;
|
|
101
|
+
headerStyle?: string;
|
|
102
|
+
summary?: () => JSX.Element;
|
|
103
|
+
tooltip?: string;
|
|
104
|
+
fixed: boolean;
|
|
105
|
+
hidden: boolean;
|
|
106
|
+
collapse: boolean;
|
|
107
|
+
width?: string;
|
|
108
|
+
class?: string;
|
|
109
|
+
sortable: boolean;
|
|
110
|
+
resizable: boolean;
|
|
111
|
+
editable: boolean;
|
|
112
|
+
cell: (ctx: CrudSheetCellContext<TItem>) => JSX.Element;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface CrudSheetColumnProps<TItem> extends Omit<DataSheetColumnProps<TItem>, "children"> {
|
|
116
|
+
editable?: boolean;
|
|
117
|
+
children: (ctx: CrudSheetCellContext<TItem>) => JSX.Element;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface CrudSheetFilterDef<TFilter> {
|
|
121
|
+
__type: "crud-sheet-filter";
|
|
122
|
+
children: (filter: TFilter, setFilter: SetStoreFunction<TFilter>) => JSX.Element;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface CrudSheetToolsDef<TItem> {
|
|
126
|
+
__type: "crud-sheet-tools";
|
|
127
|
+
children: (ctx: CrudSheetContext<TItem>) => JSX.Element;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface CrudSheetHeaderDef {
|
|
131
|
+
__type: "crud-sheet-header";
|
|
132
|
+
children: JSX.Element;
|
|
133
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
children,
|
|
3
2
|
createEffect,
|
|
4
3
|
createMemo,
|
|
5
4
|
createSignal,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
type ParentComponent,
|
|
10
9
|
Show,
|
|
11
10
|
splitProps,
|
|
11
|
+
useContext,
|
|
12
12
|
} from "solid-js";
|
|
13
13
|
import clsx from "clsx";
|
|
14
14
|
import { twMerge } from "tailwind-merge";
|
|
@@ -18,7 +18,6 @@ import { Checkbox } from "../../form-control/checkbox/Checkbox";
|
|
|
18
18
|
import { Icon } from "../../display/Icon";
|
|
19
19
|
import { BusyContainer } from "../../feedback/busy/BusyContainer";
|
|
20
20
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
21
|
-
import { splitSlots } from "../../../helpers/splitSlots";
|
|
22
21
|
import "./Kanban.css";
|
|
23
22
|
import { iconButtonBase } from "../../../styles/patterns.styles";
|
|
24
23
|
import {
|
|
@@ -35,15 +34,23 @@ import {
|
|
|
35
34
|
|
|
36
35
|
// ─── KanbanLaneTitle ─────────────────────────────────────────────
|
|
37
36
|
|
|
38
|
-
const KanbanLaneTitle: ParentComponent = (props) =>
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
const KanbanLaneTitle: ParentComponent = (props) => {
|
|
38
|
+
const ctx = useContext(KanbanLaneContext)!;
|
|
39
|
+
// eslint-disable-next-line solid/reactivity -- 슬롯 accessor로 저장, JSX tracked scope에서 호출됨
|
|
40
|
+
ctx.setTitle(() => props.children);
|
|
41
|
+
onCleanup(() => ctx.setTitle(undefined));
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
41
44
|
|
|
42
45
|
// ─── KanbanLaneTools ─────────────────────────────────────────────
|
|
43
46
|
|
|
44
|
-
const KanbanLaneTools: ParentComponent = (props) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const KanbanLaneTools: ParentComponent = (props) => {
|
|
48
|
+
const ctx = useContext(KanbanLaneContext)!;
|
|
49
|
+
// eslint-disable-next-line solid/reactivity -- 슬롯 accessor로 저장, JSX tracked scope에서 호출됨
|
|
50
|
+
ctx.setTools(() => props.children);
|
|
51
|
+
onCleanup(() => ctx.setTools(undefined));
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
47
54
|
|
|
48
55
|
// ─── KanbanCard ──────────────────────────────────────────────────
|
|
49
56
|
|
|
@@ -378,64 +385,66 @@ const KanbanLane: ParentComponent<KanbanLaneProps> = (props) => {
|
|
|
378
385
|
}
|
|
379
386
|
};
|
|
380
387
|
|
|
388
|
+
// Slot signals
|
|
389
|
+
type SlotAccessor = (() => JSX.Element) | undefined;
|
|
390
|
+
const [title, _setTitle] = createSignal<SlotAccessor>();
|
|
391
|
+
const [tools, _setTools] = createSignal<SlotAccessor>();
|
|
392
|
+
const setTitle = (content: SlotAccessor) => _setTitle(() => content);
|
|
393
|
+
const setTools = (content: SlotAccessor) => _setTools(() => content);
|
|
394
|
+
|
|
381
395
|
const laneContextValue: KanbanLaneContextValue = {
|
|
382
396
|
value: () => local.value,
|
|
383
397
|
dropTarget,
|
|
384
398
|
setDropTarget,
|
|
385
399
|
registerCard,
|
|
386
400
|
unregisterCard,
|
|
401
|
+
setTitle,
|
|
402
|
+
setTools,
|
|
387
403
|
};
|
|
388
404
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const resolved = children(() => innerProps.children);
|
|
392
|
-
const [slots, content] = splitSlots(resolved, ["kanbanLaneTitle", "kanbanLaneTools"] as const);
|
|
393
|
-
|
|
394
|
-
const hasHeader = () =>
|
|
395
|
-
local.collapsible ||
|
|
396
|
-
hasSelectableCards() ||
|
|
397
|
-
slots().kanbanLaneTitle.length > 0 ||
|
|
398
|
-
slots().kanbanLaneTools.length > 0;
|
|
399
|
-
|
|
400
|
-
// placeholder div (Lane이 소유, DOM 직접 제어)
|
|
401
|
-
let bodyRef: HTMLDivElement | undefined;
|
|
402
|
-
const placeholderEl = document.createElement("div");
|
|
403
|
-
placeholderEl.className = placeholderBaseClass;
|
|
404
|
-
|
|
405
|
-
createEffect(() => {
|
|
406
|
-
const target = dropTarget();
|
|
407
|
-
const dc = boardCtx.dragCard();
|
|
408
|
-
|
|
409
|
-
if (!target || !dc || !bodyRef) {
|
|
410
|
-
if (placeholderEl.parentNode) {
|
|
411
|
-
placeholderEl.remove();
|
|
412
|
-
}
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// placeholder 높이 설정
|
|
417
|
-
placeholderEl.style.height = `${dc.heightOnDrag}px`;
|
|
405
|
+
const hasHeader = () =>
|
|
406
|
+
local.collapsible || hasSelectableCards() || title() != null || tools() != null;
|
|
418
407
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// 이미 올바른 위치면 DOM 조작 생략
|
|
424
|
-
if (placeholderEl.parentNode === bodyRef && placeholderEl.nextSibling === referenceNode) {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
408
|
+
// placeholder div (Lane이 소유, DOM 직접 제어)
|
|
409
|
+
let bodyRef: HTMLDivElement | undefined;
|
|
410
|
+
const placeholderEl = document.createElement("div");
|
|
411
|
+
placeholderEl.className = placeholderBaseClass;
|
|
427
412
|
|
|
428
|
-
|
|
429
|
-
|
|
413
|
+
createEffect(() => {
|
|
414
|
+
const target = dropTarget();
|
|
415
|
+
const dc = boardCtx.dragCard();
|
|
430
416
|
|
|
431
|
-
|
|
432
|
-
onCleanup(() => {
|
|
417
|
+
if (!target || !dc || !bodyRef) {
|
|
433
418
|
if (placeholderEl.parentNode) {
|
|
434
419
|
placeholderEl.remove();
|
|
435
420
|
}
|
|
436
|
-
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// placeholder 높이 설정
|
|
425
|
+
placeholderEl.style.height = `${dc.heightOnDrag}px`;
|
|
426
|
+
|
|
427
|
+
// 삽입 위치 계산
|
|
428
|
+
const referenceNode =
|
|
429
|
+
target.position === "before" ? target.element : target.element.nextElementSibling;
|
|
437
430
|
|
|
438
|
-
|
|
431
|
+
// 이미 올바른 위치면 DOM 조작 생략
|
|
432
|
+
if (placeholderEl.parentNode === bodyRef && placeholderEl.nextSibling === referenceNode) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
bodyRef.insertBefore(placeholderEl, referenceNode);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// placeholder cleanup
|
|
440
|
+
onCleanup(() => {
|
|
441
|
+
if (placeholderEl.parentNode) {
|
|
442
|
+
placeholderEl.remove();
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
return (
|
|
447
|
+
<KanbanLaneContext.Provider value={laneContextValue}>
|
|
439
448
|
<BusyContainer busy={local.busy} variant="bar">
|
|
440
449
|
<div
|
|
441
450
|
{...rest}
|
|
@@ -460,25 +469,23 @@ const KanbanLane: ParentComponent<KanbanLaneProps> = (props) => {
|
|
|
460
469
|
<Show when={hasSelectableCards()}>
|
|
461
470
|
<Checkbox value={isAllSelected()} onValueChange={handleSelectAll} inline />
|
|
462
471
|
</Show>
|
|
463
|
-
<div class="flex-1">
|
|
464
|
-
|
|
465
|
-
|
|
472
|
+
<div class="flex-1">
|
|
473
|
+
<Show when={title()}>{title()!()}</Show>
|
|
474
|
+
</div>
|
|
475
|
+
<Show when={tools()}>
|
|
476
|
+
<div class={laneToolsClass}>{tools()!()}</div>
|
|
466
477
|
</Show>
|
|
467
478
|
</div>
|
|
468
479
|
</Show>
|
|
469
|
-
<
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
480
|
+
<div
|
|
481
|
+
ref={bodyRef}
|
|
482
|
+
class={laneBodyBaseClass}
|
|
483
|
+
style={{ display: collapsed() ? "none" : undefined }}
|
|
484
|
+
>
|
|
485
|
+
{local.children}
|
|
486
|
+
</div>
|
|
474
487
|
</div>
|
|
475
488
|
</BusyContainer>
|
|
476
|
-
);
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
return (
|
|
480
|
-
<KanbanLaneContext.Provider value={laneContextValue}>
|
|
481
|
-
<LaneInner>{local.children}</LaneInner>
|
|
482
489
|
</KanbanLaneContext.Provider>
|
|
483
490
|
);
|
|
484
491
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext, type Accessor, type Setter } from "solid-js";
|
|
1
|
+
import { createContext, useContext, type Accessor, type JSX, type Setter } from "solid-js";
|
|
2
2
|
|
|
3
3
|
// ── 타입 ──────────────────────────────────────────────────────
|
|
4
4
|
|
|
@@ -50,6 +50,8 @@ export function useKanbanContext(): KanbanContextValue {
|
|
|
50
50
|
|
|
51
51
|
// ── Lane Context ───────────────────────────────────────────────
|
|
52
52
|
|
|
53
|
+
type SlotAccessor = (() => JSX.Element) | undefined;
|
|
54
|
+
|
|
53
55
|
export interface KanbanLaneContextValue<L = unknown, T = unknown> {
|
|
54
56
|
value: Accessor<L | undefined>;
|
|
55
57
|
dropTarget: Accessor<KanbanDropTarget<T> | undefined>;
|
|
@@ -58,6 +60,10 @@ export interface KanbanLaneContextValue<L = unknown, T = unknown> {
|
|
|
58
60
|
// Card registration (Phase 4)
|
|
59
61
|
registerCard: (id: string, info: { value: T | undefined; selectable: boolean }) => void;
|
|
60
62
|
unregisterCard: (id: string) => void;
|
|
63
|
+
|
|
64
|
+
// Slot registration
|
|
65
|
+
setTitle: (content: SlotAccessor) => void;
|
|
66
|
+
setTools: (content: SlotAccessor) => void;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export const KanbanLaneContext = createContext<KanbanLaneContextValue>();
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
children,
|
|
3
2
|
type Component,
|
|
3
|
+
createContext,
|
|
4
|
+
createSignal,
|
|
4
5
|
type JSX,
|
|
6
|
+
onCleanup,
|
|
5
7
|
type ParentComponent,
|
|
6
8
|
Show,
|
|
7
9
|
splitProps,
|
|
10
|
+
useContext,
|
|
8
11
|
} from "solid-js";
|
|
9
12
|
import { IconChevronDown, type IconProps } from "@tabler/icons-solidjs";
|
|
10
13
|
import { Icon } from "../../display/Icon";
|
|
@@ -15,7 +18,6 @@ import { Collapse } from "../../disclosure/Collapse";
|
|
|
15
18
|
import { createControllableSignal } from "../../../hooks/createControllableSignal";
|
|
16
19
|
import { useListContext } from "./ListContext";
|
|
17
20
|
import { List } from "./List";
|
|
18
|
-
import { splitSlots } from "../../../helpers/splitSlots";
|
|
19
21
|
import {
|
|
20
22
|
listItemBaseClass,
|
|
21
23
|
listItemSizeClasses,
|
|
@@ -30,6 +32,14 @@ import type { ComponentSize } from "../../../styles/tokens.styles";
|
|
|
30
32
|
|
|
31
33
|
void ripple;
|
|
32
34
|
|
|
35
|
+
type SlotAccessor = (() => JSX.Element) | undefined;
|
|
36
|
+
|
|
37
|
+
interface ListItemSlotsContextValue {
|
|
38
|
+
setChildren: (content: SlotAccessor) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ListItemSlotsContext = createContext<ListItemSlotsContextValue>();
|
|
42
|
+
|
|
33
43
|
const chevronClass = clsx("transition-transform duration-200 motion-reduce:transition-none");
|
|
34
44
|
|
|
35
45
|
/**
|
|
@@ -50,14 +60,13 @@ const chevronClass = clsx("transition-transform duration-200 motion-reduce:trans
|
|
|
50
60
|
* </List.Item>
|
|
51
61
|
* ```
|
|
52
62
|
*/
|
|
53
|
-
const ListItemChildren: ParentComponent = (props) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
63
|
+
const ListItemChildren: ParentComponent = (props) => {
|
|
64
|
+
const ctx = useContext(ListItemSlotsContext)!;
|
|
65
|
+
// eslint-disable-next-line solid/reactivity -- 슬롯 accessor로 저장, JSX tracked scope에서 호출됨
|
|
66
|
+
ctx.setChildren(() => props.children);
|
|
67
|
+
onCleanup(() => ctx.setChildren(undefined));
|
|
68
|
+
return null;
|
|
69
|
+
};
|
|
61
70
|
|
|
62
71
|
export interface ListItemProps extends Omit<
|
|
63
72
|
JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
@@ -157,10 +166,9 @@ export const ListItem: ListItemComponent = (props) => {
|
|
|
157
166
|
onChange: () => local.onOpenChange,
|
|
158
167
|
});
|
|
159
168
|
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
const hasChildren = () => slots().listItemChildren.length > 0;
|
|
169
|
+
const [childrenSlot, _setChildrenSlot] = createSignal<SlotAccessor>();
|
|
170
|
+
const setChildrenSlot = (content: SlotAccessor) => _setChildrenSlot(() => content);
|
|
171
|
+
const hasChildren = () => childrenSlot() !== undefined;
|
|
164
172
|
|
|
165
173
|
const useRipple = () => !(local.readonly || local.disabled);
|
|
166
174
|
|
|
@@ -189,7 +197,7 @@ export const ListItem: ListItemComponent = (props) => {
|
|
|
189
197
|
const getSelectedIconClassName = () => getListItemSelectedIconClass(local.selected ?? false);
|
|
190
198
|
|
|
191
199
|
return (
|
|
192
|
-
|
|
200
|
+
<ListItemSlotsContext.Provider value={{ setChildren: setChildrenSlot }}>
|
|
193
201
|
<button
|
|
194
202
|
{...rest}
|
|
195
203
|
type="button"
|
|
@@ -215,17 +223,22 @@ export const ListItem: ListItemComponent = (props) => {
|
|
|
215
223
|
<Show when={local.selectedIcon && !hasChildren()}>
|
|
216
224
|
<Icon icon={local.selectedIcon!} class={getSelectedIconClassName()} />
|
|
217
225
|
</Show>
|
|
218
|
-
<span class={listItemContentClass}>{
|
|
226
|
+
<span class={listItemContentClass}>{local.children}</span>
|
|
219
227
|
<Show when={hasChildren()}>
|
|
220
228
|
<Icon icon={IconChevronDown} size="1em" class={getChevronClassName()} />
|
|
221
229
|
</Show>
|
|
222
230
|
</button>
|
|
223
231
|
<Show when={hasChildren()}>
|
|
224
232
|
<Collapse open={openState()} data-collapsed={!openState() || undefined}>
|
|
225
|
-
|
|
233
|
+
<div class="flex">
|
|
234
|
+
<div class={listItemIndentGuideClass} />
|
|
235
|
+
<List inset class="flex-1">
|
|
236
|
+
{childrenSlot()!()}
|
|
237
|
+
</List>
|
|
238
|
+
</div>
|
|
226
239
|
</Collapse>
|
|
227
240
|
</Show>
|
|
228
|
-
|
|
241
|
+
</ListItemSlotsContext.Provider>
|
|
229
242
|
);
|
|
230
243
|
};
|
|
231
244
|
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
[data-sheet] tbody tr
|
|
2
|
-
|
|
1
|
+
[data-sheet] tbody tr {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/* ::after 공통 — content가 없으면 렌더링되지 않으므로 각 상태에서 content 설정 */
|
|
6
|
+
[data-sheet] tbody tr::after {
|
|
7
|
+
position: absolute;
|
|
8
|
+
top: 0;
|
|
9
|
+
right: 0;
|
|
10
|
+
bottom: 0;
|
|
11
|
+
left: 0;
|
|
12
|
+
pointer-events: none;
|
|
13
|
+
z-index: 10;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-sheet] tbody tr:hover::after {
|
|
17
|
+
content: "";
|
|
18
|
+
background: rgb(0 0 0 / 3%);
|
|
3
19
|
}
|
|
4
20
|
|
|
5
21
|
/* 선택 행 시각 효과 — 호버보다 약간 더 진하게 */
|
|
6
|
-
[data-sheet] tbody tr[data-selected]
|
|
7
|
-
|
|
22
|
+
[data-sheet] tbody tr[data-selected]::after {
|
|
23
|
+
content: "";
|
|
24
|
+
background: rgb(0 0 0 / 5%);
|
|
8
25
|
}
|
|
9
26
|
|
|
10
27
|
/* 드래그 중인 행 */
|
|
@@ -13,14 +30,15 @@
|
|
|
13
30
|
}
|
|
14
31
|
|
|
15
32
|
/* inside 드롭 대상 행 */
|
|
16
|
-
[data-sheet] tbody tr[data-drag-over="inside"]
|
|
17
|
-
|
|
33
|
+
[data-sheet] tbody tr[data-drag-over="inside"]::after {
|
|
34
|
+
content: "";
|
|
35
|
+
background: rgb(59 130 246 / 10%);
|
|
18
36
|
}
|
|
19
37
|
|
|
20
|
-
.dark [data-sheet] tbody tr:hover
|
|
21
|
-
|
|
38
|
+
.dark [data-sheet] tbody tr:hover::after {
|
|
39
|
+
background: rgb(255 255 255 / 4%);
|
|
22
40
|
}
|
|
23
41
|
|
|
24
|
-
.dark [data-sheet] tbody tr[data-selected]
|
|
25
|
-
|
|
42
|
+
.dark [data-sheet] tbody tr[data-selected]::after {
|
|
43
|
+
background: rgb(255 255 255 / 6%);
|
|
26
44
|
}
|
|
@@ -62,7 +62,7 @@ export const toolbarClass = clsx(
|
|
|
62
62
|
export const fixedClass = "sticky";
|
|
63
63
|
|
|
64
64
|
// 고정/비고정 경계 시각 효과 — 고정 컬럼의 마지막 셀에 적용
|
|
65
|
-
export const fixedLastClass = clsx("border-r
|
|
65
|
+
export const fixedLastClass = clsx("border-r border-r-base-400", "dark:border-r-base-600");
|
|
66
66
|
|
|
67
67
|
// 리사이저 핸들 (헤더 셀 우측 드래그 영역)
|
|
68
68
|
export const resizerClass = clsx(
|
|
@@ -187,7 +187,7 @@ export const DataSheet: DataSheetComponent = <T,>(props: DataSheetProps<T>) => {
|
|
|
187
187
|
const result = await modal.show<DataSheetConfig>(
|
|
188
188
|
() => <DataSheetConfigDialog columnInfos={columnInfos} currentConfig={currentConfig} />,
|
|
189
189
|
{
|
|
190
|
-
|
|
190
|
+
header: "시트 설정",
|
|
191
191
|
closeOnBackdrop: true,
|
|
192
192
|
closeOnEscape: true,
|
|
193
193
|
},
|