@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.
Files changed (222) hide show
  1. package/README.md +6 -2
  2. package/dist/components/data/crud-detail/CrudDetail.d.ts +14 -0
  3. package/dist/components/data/crud-detail/CrudDetail.d.ts.map +1 -0
  4. package/dist/components/data/crud-detail/CrudDetail.js +348 -0
  5. package/dist/components/data/crud-detail/CrudDetail.js.map +6 -0
  6. package/dist/components/data/crud-detail/CrudDetailAfter.d.ts +7 -0
  7. package/dist/components/data/crud-detail/CrudDetailAfter.d.ts.map +1 -0
  8. package/dist/components/data/crud-detail/CrudDetailAfter.js +14 -0
  9. package/dist/components/data/crud-detail/CrudDetailAfter.js.map +6 -0
  10. package/dist/components/data/crud-detail/CrudDetailBefore.d.ts +7 -0
  11. package/dist/components/data/crud-detail/CrudDetailBefore.d.ts.map +1 -0
  12. package/dist/components/data/crud-detail/CrudDetailBefore.js +14 -0
  13. package/dist/components/data/crud-detail/CrudDetailBefore.js.map +6 -0
  14. package/dist/components/data/crud-detail/CrudDetailTools.d.ts +7 -0
  15. package/dist/components/data/crud-detail/CrudDetailTools.d.ts.map +1 -0
  16. package/dist/components/data/crud-detail/CrudDetailTools.js +14 -0
  17. package/dist/components/data/crud-detail/CrudDetailTools.js.map +6 -0
  18. package/dist/components/data/crud-detail/types.d.ts +45 -0
  19. package/dist/components/data/crud-detail/types.d.ts.map +1 -0
  20. package/dist/components/data/crud-detail/types.js +1 -0
  21. package/dist/components/data/crud-detail/types.js.map +6 -0
  22. package/dist/components/data/crud-sheet/CrudSheet.d.ts +17 -0
  23. package/dist/components/data/crud-sheet/CrudSheet.d.ts.map +1 -0
  24. package/dist/components/data/crud-sheet/CrudSheet.js +679 -0
  25. package/dist/components/data/crud-sheet/CrudSheet.js.map +6 -0
  26. package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts +5 -0
  27. package/dist/components/data/crud-sheet/CrudSheetColumn.d.ts.map +1 -0
  28. package/dist/components/data/crud-sheet/CrudSheetColumn.js +29 -0
  29. package/dist/components/data/crud-sheet/CrudSheetColumn.js.map +6 -0
  30. package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts +7 -0
  31. package/dist/components/data/crud-sheet/CrudSheetFilter.d.ts.map +1 -0
  32. package/dist/components/data/crud-sheet/CrudSheetFilter.js +14 -0
  33. package/dist/components/data/crud-sheet/CrudSheetFilter.js.map +6 -0
  34. package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts +7 -0
  35. package/dist/components/data/crud-sheet/CrudSheetHeader.d.ts.map +1 -0
  36. package/dist/components/data/crud-sheet/CrudSheetHeader.js +14 -0
  37. package/dist/components/data/crud-sheet/CrudSheetHeader.js.map +6 -0
  38. package/dist/components/data/crud-sheet/CrudSheetTools.d.ts +7 -0
  39. package/dist/components/data/crud-sheet/CrudSheetTools.d.ts.map +1 -0
  40. package/dist/components/data/crud-sheet/CrudSheetTools.js +14 -0
  41. package/dist/components/data/crud-sheet/CrudSheetTools.js.map +6 -0
  42. package/dist/components/data/crud-sheet/types.d.ts +109 -0
  43. package/dist/components/data/crud-sheet/types.d.ts.map +1 -0
  44. package/dist/components/data/crud-sheet/types.js +1 -0
  45. package/dist/components/data/crud-sheet/types.js.map +6 -0
  46. package/dist/components/data/kanban/Kanban.d.ts.map +1 -1
  47. package/dist/components/data/kanban/Kanban.js +137 -138
  48. package/dist/components/data/kanban/Kanban.js.map +2 -2
  49. package/dist/components/data/kanban/KanbanContext.d.ts +5 -1
  50. package/dist/components/data/kanban/KanbanContext.d.ts.map +1 -1
  51. package/dist/components/data/kanban/KanbanContext.js.map +1 -1
  52. package/dist/components/data/list/ListItem.d.ts.map +1 -1
  53. package/dist/components/data/list/ListItem.js +109 -99
  54. package/dist/components/data/list/ListItem.js.map +2 -2
  55. package/dist/components/data/sheet/DataSheet.css +28 -10
  56. package/dist/components/data/sheet/DataSheet.js +1 -1
  57. package/dist/components/data/sheet/DataSheet.js.map +2 -2
  58. package/dist/components/data/sheet/DataSheet.styles.d.ts.map +1 -1
  59. package/dist/components/data/sheet/DataSheet.styles.js +1 -1
  60. package/dist/components/data/sheet/DataSheet.styles.js.map +1 -1
  61. package/dist/components/disclosure/Dialog.d.ts +16 -10
  62. package/dist/components/disclosure/Dialog.d.ts.map +1 -1
  63. package/dist/components/disclosure/Dialog.js +126 -91
  64. package/dist/components/disclosure/Dialog.js.map +2 -2
  65. package/dist/components/disclosure/DialogContext.d.ts +2 -4
  66. package/dist/components/disclosure/DialogContext.d.ts.map +1 -1
  67. package/dist/components/disclosure/DialogContext.js.map +1 -1
  68. package/dist/components/disclosure/DialogProvider.d.ts.map +1 -1
  69. package/dist/components/disclosure/DialogProvider.js +14 -9
  70. package/dist/components/disclosure/DialogProvider.js.map +2 -2
  71. package/dist/components/disclosure/Dropdown.d.ts +46 -22
  72. package/dist/components/disclosure/Dropdown.d.ts.map +1 -1
  73. package/dist/components/disclosure/Dropdown.js +100 -65
  74. package/dist/components/disclosure/Dropdown.js.map +2 -2
  75. package/dist/components/feedback/notification/NotificationBanner.d.ts.map +1 -1
  76. package/dist/components/feedback/notification/NotificationBanner.js +3 -3
  77. package/dist/components/feedback/notification/NotificationBanner.js.map +1 -1
  78. package/dist/components/feedback/notification/NotificationBell.d.ts.map +1 -1
  79. package/dist/components/feedback/notification/NotificationBell.js +84 -84
  80. package/dist/components/feedback/notification/NotificationBell.js.map +2 -2
  81. package/dist/components/form-control/Invalid.js +1 -1
  82. package/dist/components/form-control/combobox/Combobox.d.ts +6 -3
  83. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  84. package/dist/components/form-control/combobox/Combobox.js +150 -168
  85. package/dist/components/form-control/combobox/Combobox.js.map +2 -2
  86. package/dist/components/form-control/combobox/ComboboxContext.d.ts +3 -0
  87. package/dist/components/form-control/combobox/ComboboxContext.d.ts.map +1 -1
  88. package/dist/components/form-control/combobox/ComboboxContext.js.map +1 -1
  89. package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts +0 -2
  90. package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts.map +1 -1
  91. package/dist/components/form-control/date-range-picker/DateRangePicker.js +9 -17
  92. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  93. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  94. package/dist/components/form-control/field/DatePicker.js +3 -2
  95. package/dist/components/form-control/field/DatePicker.js.map +2 -2
  96. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  97. package/dist/components/form-control/field/DateTimePicker.js +3 -2
  98. package/dist/components/form-control/field/DateTimePicker.js.map +2 -2
  99. package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
  100. package/dist/components/form-control/field/Field.styles.js +2 -1
  101. package/dist/components/form-control/field/Field.styles.js.map +1 -1
  102. package/dist/components/form-control/field/NumberInput.d.ts +15 -5
  103. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  104. package/dist/components/form-control/field/NumberInput.js +181 -141
  105. package/dist/components/form-control/field/NumberInput.js.map +2 -2
  106. package/dist/components/form-control/field/TextInput.d.ts +9 -5
  107. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  108. package/dist/components/form-control/field/TextInput.js +199 -154
  109. package/dist/components/form-control/field/TextInput.js.map +2 -2
  110. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  111. package/dist/components/form-control/field/TimePicker.js +3 -2
  112. package/dist/components/form-control/field/TimePicker.js.map +2 -2
  113. package/dist/components/form-control/select/Select.d.ts +3 -3
  114. package/dist/components/form-control/select/Select.d.ts.map +1 -1
  115. package/dist/components/form-control/select/Select.js +116 -100
  116. package/dist/components/form-control/select/Select.js.map +2 -2
  117. package/dist/components/form-control/select/SelectContext.d.ts +9 -1
  118. package/dist/components/form-control/select/SelectContext.d.ts.map +1 -1
  119. package/dist/components/form-control/select/SelectContext.js.map +1 -1
  120. package/dist/components/form-control/select/SelectItem.d.ts.map +1 -1
  121. package/dist/components/form-control/select/SelectItem.js +77 -67
  122. package/dist/components/form-control/select/SelectItem.js.map +2 -2
  123. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  124. package/dist/components/form-control/state-preset/StatePreset.js +1 -1
  125. package/dist/components/form-control/state-preset/StatePreset.js.map +1 -1
  126. package/dist/components/layout/topbar/Topbar.d.ts +2 -0
  127. package/dist/components/layout/topbar/Topbar.d.ts.map +1 -1
  128. package/dist/components/layout/topbar/Topbar.js +2 -0
  129. package/dist/components/layout/topbar/Topbar.js.map +2 -2
  130. package/dist/components/layout/topbar/TopbarActions.d.ts +3 -0
  131. package/dist/components/layout/topbar/TopbarActions.d.ts.map +1 -0
  132. package/dist/components/layout/topbar/TopbarActions.js +17 -0
  133. package/dist/components/layout/topbar/TopbarActions.js.map +6 -0
  134. package/dist/components/layout/topbar/TopbarContainer.d.ts +1 -1
  135. package/dist/components/layout/topbar/TopbarContainer.d.ts.map +1 -1
  136. package/dist/components/layout/topbar/TopbarContainer.js +21 -12
  137. package/dist/components/layout/topbar/TopbarContainer.js.map +2 -2
  138. package/dist/components/layout/topbar/TopbarContext.d.ts +9 -0
  139. package/dist/components/layout/topbar/TopbarContext.d.ts.map +1 -0
  140. package/dist/components/layout/topbar/TopbarContext.js +29 -0
  141. package/dist/components/layout/topbar/TopbarContext.js.map +6 -0
  142. package/dist/components/layout/topbar/TopbarMenu.d.ts.map +1 -1
  143. package/dist/components/layout/topbar/TopbarMenu.js +63 -57
  144. package/dist/components/layout/topbar/TopbarMenu.js.map +2 -2
  145. package/dist/components/layout/topbar/TopbarUser.d.ts.map +1 -1
  146. package/dist/components/layout/topbar/TopbarUser.js +53 -54
  147. package/dist/components/layout/topbar/TopbarUser.js.map +2 -2
  148. package/dist/hooks/createControllableStore.d.ts +29 -0
  149. package/dist/hooks/createControllableStore.d.ts.map +1 -0
  150. package/dist/hooks/createControllableStore.js +19 -0
  151. package/dist/hooks/createControllableStore.js.map +6 -0
  152. package/dist/index.d.ts +6 -1
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +7 -2
  155. package/dist/index.js.map +1 -1
  156. package/dist/styles/patterns.styles.d.ts.map +1 -1
  157. package/dist/styles/patterns.styles.js +7 -1
  158. package/dist/styles/patterns.styles.js.map +1 -1
  159. package/docs/data-components.md +428 -0
  160. package/docs/disclosure.md +65 -35
  161. package/docs/form-controls.md +18 -3
  162. package/docs/helpers.md +0 -39
  163. package/docs/hooks.md +39 -0
  164. package/docs/layout.md +70 -1
  165. package/package.json +4 -3
  166. package/src/components/data/crud-detail/CrudDetail.tsx +346 -0
  167. package/src/components/data/crud-detail/CrudDetailAfter.tsx +19 -0
  168. package/src/components/data/crud-detail/CrudDetailBefore.tsx +19 -0
  169. package/src/components/data/crud-detail/CrudDetailTools.tsx +19 -0
  170. package/src/components/data/crud-detail/types.ts +58 -0
  171. package/src/components/data/crud-sheet/CrudSheet.tsx +628 -0
  172. package/src/components/data/crud-sheet/CrudSheetColumn.tsx +34 -0
  173. package/src/components/data/crud-sheet/CrudSheetFilter.tsx +21 -0
  174. package/src/components/data/crud-sheet/CrudSheetHeader.tsx +19 -0
  175. package/src/components/data/crud-sheet/CrudSheetTools.tsx +21 -0
  176. package/src/components/data/crud-sheet/types.ts +133 -0
  177. package/src/components/data/kanban/Kanban.tsx +72 -65
  178. package/src/components/data/kanban/KanbanContext.ts +7 -1
  179. package/src/components/data/list/ListItem.tsx +31 -18
  180. package/src/components/data/sheet/DataSheet.css +28 -10
  181. package/src/components/data/sheet/DataSheet.styles.ts +1 -1
  182. package/src/components/data/sheet/DataSheet.tsx +1 -1
  183. package/src/components/disclosure/Dialog.tsx +143 -105
  184. package/src/components/disclosure/DialogContext.ts +2 -4
  185. package/src/components/disclosure/DialogProvider.tsx +4 -2
  186. package/src/components/disclosure/Dropdown.tsx +174 -86
  187. package/src/components/feedback/notification/NotificationBanner.tsx +3 -9
  188. package/src/components/feedback/notification/NotificationBell.tsx +51 -57
  189. package/src/components/form-control/Invalid.tsx +1 -1
  190. package/src/components/form-control/combobox/Combobox.tsx +109 -133
  191. package/src/components/form-control/combobox/ComboboxContext.ts +4 -1
  192. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +6 -16
  193. package/src/components/form-control/field/DatePicker.tsx +4 -1
  194. package/src/components/form-control/field/DateTimePicker.tsx +3 -0
  195. package/src/components/form-control/field/Field.styles.ts +1 -0
  196. package/src/components/form-control/field/NumberInput.tsx +131 -86
  197. package/src/components/form-control/field/TextInput.tsx +139 -88
  198. package/src/components/form-control/field/TimePicker.tsx +3 -0
  199. package/src/components/form-control/select/Select.tsx +85 -67
  200. package/src/components/form-control/select/SelectContext.ts +12 -1
  201. package/src/components/form-control/select/SelectItem.tsx +39 -18
  202. package/src/components/form-control/state-preset/StatePreset.tsx +1 -0
  203. package/src/components/layout/topbar/Topbar.tsx +3 -0
  204. package/src/components/layout/topbar/TopbarActions.tsx +8 -0
  205. package/src/components/layout/topbar/TopbarContainer.tsx +9 -5
  206. package/src/components/layout/topbar/TopbarContext.ts +36 -0
  207. package/src/components/layout/topbar/TopbarMenu.tsx +52 -55
  208. package/src/components/layout/topbar/TopbarUser.tsx +28 -31
  209. package/src/hooks/createControllableStore.ts +47 -0
  210. package/src/index.ts +6 -1
  211. package/src/styles/patterns.styles.ts +7 -1
  212. package/tailwind.css +4 -0
  213. package/dist/helpers/splitSlots.d.ts +0 -25
  214. package/dist/helpers/splitSlots.d.ts.map +0 -1
  215. package/dist/helpers/splitSlots.js +0 -25
  216. package/dist/helpers/splitSlots.js.map +0 -6
  217. package/dist/hooks/createItemTemplate.d.ts +0 -17
  218. package/dist/hooks/createItemTemplate.d.ts.map +0 -1
  219. package/dist/hooks/createItemTemplate.js +0 -40
  220. package/dist/hooks/createItemTemplate.js.map +0 -6
  221. package/src/helpers/splitSlots.ts +0 -51
  222. package/src/hooks/createItemTemplate.tsx +0 -42
@@ -1,25 +1,14 @@
1
- import {
2
- children,
3
- createMemo,
4
- createSignal,
5
- For,
6
- type JSX,
7
- onCleanup,
8
- Show,
9
- splitProps,
10
- } from "solid-js";
1
+ import { createMemo, createSignal, For, type JSX, onCleanup, Show, splitProps } from "solid-js";
11
2
  import clsx from "clsx";
12
3
  import { IconChevronDown, IconLoader2 } from "@tabler/icons-solidjs";
13
4
  import { DebounceQueue } from "@simplysm/core-common";
14
5
  import { Icon } from "../../display/Icon";
15
6
  import { Dropdown } from "../../disclosure/Dropdown";
16
7
  import { List } from "../../data/list/List";
17
- import { ComboboxContext, type ComboboxContextValue } from "./ComboboxContext";
8
+ import { ComboboxContext, type ComboboxContextValue, useComboboxContext } from "./ComboboxContext";
18
9
  import { ComboboxItem } from "./ComboboxItem";
19
10
  import { ripple } from "../../../directives/ripple";
20
11
  import { createControllableSignal } from "../../../hooks/createControllableSignal";
21
- import { createItemTemplate } from "../../../hooks/createItemTemplate";
22
- import { splitSlots } from "../../../helpers/splitSlots";
23
12
  import { type ComponentSize, textMuted } from "../../../styles/tokens.styles";
24
13
  import { chevronWrapperClass, getTriggerClass } from "../DropdownTrigger.styles";
25
14
  import { Invalid } from "../Invalid";
@@ -36,8 +25,18 @@ const inputClass = clsx(
36
25
 
37
26
  const noResultsClass = clsx("px-3 py-2", textMuted);
38
27
 
39
- const { TemplateSlot: ComboboxItemTemplate, getTemplate: getComboboxItemTemplate } =
40
- createItemTemplate<[item: unknown, index: number]>("data-combobox-item-template");
28
+ /**
29
+ * 아이템 템플릿 서브 컴포넌트
30
+ */
31
+ const ComboboxItemTemplate = <TArgs extends unknown[]>(props: {
32
+ children: (...args: TArgs) => JSX.Element;
33
+ }) => {
34
+ const ctx = useComboboxContext();
35
+ // eslint-disable-next-line solid/reactivity -- 렌더 함수를 signal에 저장, JSX tracked scope에서 호출됨
36
+ ctx.setItemTemplate(props.children as (...args: unknown[]) => JSX.Element);
37
+ onCleanup(() => ctx.setItemTemplate(undefined));
38
+ return null;
39
+ };
41
40
 
42
41
  // Props 정의
43
42
  export interface ComboboxProps<TValue = unknown> {
@@ -150,8 +149,6 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
150
149
  "touchMode",
151
150
  ]);
152
151
 
153
- let triggerRef!: HTMLDivElement;
154
-
155
152
  // 상태
156
153
  const [open, setOpen] = createSignal(false);
157
154
  const [query, setQuery] = createSignal("");
@@ -190,11 +187,19 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
190
187
  setOpen(false);
191
188
  };
192
189
 
190
+ // 아이템 템플릿 signal
191
+ const [itemTemplate, _setItemTemplate] = createSignal<
192
+ ((...args: unknown[]) => JSX.Element) | undefined
193
+ >();
194
+ const setItemTemplate = (fn: ((...args: unknown[]) => JSX.Element) | undefined) =>
195
+ _setItemTemplate(() => fn);
196
+
193
197
  // Context 값
194
198
  const contextValue: ComboboxContextValue<T> = {
195
199
  isSelected,
196
200
  selectValue,
197
201
  closeDropdown,
202
+ setItemTemplate,
198
203
  };
199
204
 
200
205
  // 검색 실행
@@ -224,24 +229,11 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
224
229
  }
225
230
  };
226
231
 
227
- // 트리거 클릭
228
- const handleTriggerClick = (e: MouseEvent) => {
229
- if (local.disabled) return;
230
-
231
- // input 클릭 시 드롭다운 토글
232
- const target = e.target as HTMLElement;
233
- if (target.tagName === "INPUT") {
234
- if (!open()) {
235
- setOpen(true);
236
- performSearch(query());
237
- }
238
- } else {
239
- // 다른 영역 클릭 시 토글
240
- const wasOpen = open();
241
- setOpen(!wasOpen);
242
- if (!wasOpen) {
243
- performSearch(query());
244
- }
232
+ // Dropdown 열림/닫힘 변경 핸들러
233
+ const handleOpenChange = (isOpen: boolean) => {
234
+ setOpen(isOpen);
235
+ if (isOpen) {
236
+ performSearch(query());
245
237
  }
246
238
  };
247
239
 
@@ -283,122 +275,106 @@ export const Combobox: ComboboxComponent = <T,>(props: ComboboxProps<T>) => {
283
275
  // 참고: 초기 검색은 handleTriggerClick에서 수행됨
284
276
  // 입력 시에는 handleInput에서 performSearch가 호출됨
285
277
 
286
- // 내부 컴포넌트
287
- const ComboboxInner = (innerProps: { children?: JSX.Element }) => {
288
- const resolved = children(() => innerProps.children);
289
- const [slots, childItems] = splitSlots(resolved, ["comboboxItemTemplate"] as const);
290
-
291
- // itemTemplate 함수 추출
292
- const getItemTemplate = (): ((item: T, index: number) => JSX.Element) | undefined => {
293
- return getComboboxItemTemplate(slots().comboboxItemTemplate) as
294
- | ((item: T, index: number) => JSX.Element)
295
- | undefined;
296
- };
297
-
298
- // 선택된 값 또는 입력 표시
299
- const renderDisplayContent = (): JSX.Element => {
300
- const currentValue = getValue();
301
-
302
- // 드롭다운이 열려있거나 값이 없으면 입력 필드 표시
303
- if (open() || currentValue === undefined) {
304
- return (
305
- <input
306
- ref={(el) => {
307
- // 드롭다운이 열릴 때 input에 포커스
308
- if (open()) {
309
- requestAnimationFrame(() => el.focus());
310
- }
311
- }}
312
- type="text"
313
- class={inputClass}
314
- value={query()}
315
- placeholder={currentValue === undefined ? local.placeholder : undefined}
316
- disabled={local.disabled}
317
- onInput={handleInput}
318
- />
319
- );
320
- }
321
-
322
- // 값이 있고 드롭다운이 닫혀있으면 값 표시
323
- return <div class="truncate">{local.renderValue(currentValue)}</div>;
324
- };
278
+ // 선택된 값 또는 입력 표시
279
+ const renderDisplayContent = (): JSX.Element => {
280
+ const currentValue = getValue();
325
281
 
326
- // 아이템 렌더링
327
- const renderItems = (): JSX.Element => {
328
- const itemTemplate = getItemTemplate();
282
+ // 드롭다운이 열려있거나 값이 없으면 입력 필드 표시
283
+ if (open() || currentValue === undefined) {
284
+ return (
285
+ <input
286
+ ref={(el) => {
287
+ // 드롭다운이 열릴 때 input에 포커스
288
+ if (open()) {
289
+ requestAnimationFrame(() => el.focus());
290
+ }
291
+ }}
292
+ type="text"
293
+ class={inputClass}
294
+ value={query()}
295
+ placeholder={currentValue === undefined ? local.placeholder : undefined}
296
+ disabled={local.disabled}
297
+ autocomplete="one-time-code"
298
+ onInput={handleInput}
299
+ />
300
+ );
301
+ }
329
302
 
330
- // 로딩
331
- if (busyCount() > 0) {
332
- return <div class={noResultsClass}>검색 중...</div>;
333
- }
303
+ // 값이 있고 드롭다운이 닫혀있으면 값 표시
304
+ return <div class="truncate">{local.renderValue(currentValue)}</div>;
305
+ };
334
306
 
335
- // children 방식 (아이템이 직접 전달된 경우)
336
- const resolvedChildren = childItems();
337
- if (resolvedChildren.length > 0) {
338
- return <>{resolvedChildren}</>;
339
- }
307
+ // 아이템 렌더링
308
+ const renderItems = (): JSX.Element => {
309
+ const template = itemTemplate() as ((item: T, index: number) => JSX.Element) | undefined;
340
310
 
341
- // items가 비어있는 경우
342
- if (items().length === 0) {
343
- return <div class={noResultsClass}>검색 결과가 없습니다</div>;
344
- }
311
+ // 로딩
312
+ if (busyCount() > 0) {
313
+ return <div class={noResultsClass}>검색 중...</div>;
314
+ }
345
315
 
346
- // ItemTemplate 방식
347
- if (itemTemplate) {
348
- return (
349
- <For each={items()}>
350
- {(item, index) => (
351
- <ComboboxItem value={item}>{itemTemplate(item, index())}</ComboboxItem>
352
- )}
353
- </For>
354
- );
355
- }
316
+ // items가 비어있는 경우
317
+ if (items().length === 0) {
318
+ return <div class={noResultsClass}>검색 결과가 없습니다</div>;
319
+ }
356
320
 
357
- // 기본 렌더링
321
+ // ItemTemplate 방식
322
+ if (template) {
358
323
  return (
359
324
  <For each={items()}>
360
- {(item) => <ComboboxItem value={item}>{String(item)}</ComboboxItem>}
325
+ {(item, index) => <ComboboxItem value={item}>{template(item, index())}</ComboboxItem>}
361
326
  </For>
362
327
  );
363
- };
328
+ }
364
329
 
330
+ // 기본 렌더링
365
331
  return (
366
- <div {...rest} data-combobox class={local.inset ? "flex" : "inline-flex"}>
367
- <div
368
- ref={triggerRef}
369
- use:ripple={!local.disabled}
370
- role="combobox"
371
- aria-haspopup="listbox"
372
- aria-expanded={open()}
373
- aria-disabled={local.disabled || undefined}
374
- aria-required={local.required || undefined}
375
- tabIndex={local.disabled ? -1 : 0}
376
- class={getTriggerClassName()}
377
- style={local.style}
378
- onClick={handleTriggerClick}
379
- onKeyDown={handleTriggerKeyDown}
380
- >
381
- <div class={selectedValueClass}>{renderDisplayContent()}</div>
382
- <div class={chevronWrapperClass}>
383
- <Show when={busyCount() > 0} fallback={<Icon icon={IconChevronDown} size="1em" />}>
384
- <Icon icon={IconLoader2} size="1em" class="animate-spin" />
385
- </Show>
386
- </div>
387
- </div>
388
-
389
- <Dropdown triggerRef={() => triggerRef} open={open()} onOpenChange={setOpen} keyboardNav>
390
- <List inset role="listbox">
391
- {renderItems()}
392
- </List>
393
- </Dropdown>
394
- </div>
332
+ <For each={items()}>{(item) => <ComboboxItem value={item}>{String(item)}</ComboboxItem>}</For>
395
333
  );
396
334
  };
397
335
 
398
336
  return (
399
337
  <Invalid message={errorMsg()} variant="border" touchMode={local.touchMode}>
400
338
  <ComboboxContext.Provider value={contextValue as ComboboxContextValue}>
401
- <ComboboxInner>{local.children}</ComboboxInner>
339
+ <div {...rest} data-combobox class={local.inset ? "flex" : "inline-flex"}>
340
+ <Dropdown
341
+ disabled={local.disabled}
342
+ open={open()}
343
+ onOpenChange={handleOpenChange}
344
+ keyboardNav
345
+ >
346
+ <Dropdown.Trigger>
347
+ <div
348
+ use:ripple={!local.disabled}
349
+ role="combobox"
350
+ aria-haspopup="listbox"
351
+ aria-expanded={open()}
352
+ aria-disabled={local.disabled || undefined}
353
+ aria-required={local.required || undefined}
354
+ tabIndex={local.disabled ? -1 : 0}
355
+ class={getTriggerClassName()}
356
+ style={local.style}
357
+ onKeyDown={handleTriggerKeyDown}
358
+ >
359
+ <div class={selectedValueClass}>{renderDisplayContent()}</div>
360
+ <div class={chevronWrapperClass}>
361
+ <Show
362
+ when={busyCount() > 0}
363
+ fallback={<Icon icon={IconChevronDown} size="1em" />}
364
+ >
365
+ <Icon icon={IconLoader2} size="1em" class="animate-spin" />
366
+ </Show>
367
+ </div>
368
+ </div>
369
+ </Dropdown.Trigger>
370
+ <Dropdown.Content>
371
+ <List inset role="listbox">
372
+ {local.children}
373
+ {renderItems()}
374
+ </List>
375
+ </Dropdown.Content>
376
+ </Dropdown>
377
+ </div>
402
378
  </ComboboxContext.Provider>
403
379
  </Invalid>
404
380
  );
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from "solid-js";
1
+ import { type JSX, createContext, useContext } from "solid-js";
2
2
 
3
3
  export interface ComboboxContextValue<TValue = unknown> {
4
4
  /** 값이 선택되어 있는지 확인 */
@@ -9,6 +9,9 @@ export interface ComboboxContextValue<TValue = unknown> {
9
9
 
10
10
  /** 드롭다운 닫기 */
11
11
  closeDropdown: () => void;
12
+
13
+ /** 아이템 템플릿 등록 */
14
+ setItemTemplate: (fn: ((...args: unknown[]) => JSX.Element) | undefined) => void;
12
15
  }
13
16
 
14
17
  export const ComboboxContext = createContext<ComboboxContextValue>();
@@ -39,9 +39,6 @@ export interface DateRangePickerProps {
39
39
  /** 사이즈 */
40
40
  size?: FieldSize;
41
41
 
42
- /** 기간 타입 라벨 (기본값: { day: "일", month: "월", range: "범위" }) */
43
- periodLabels?: Partial<Record<DateRangePeriodType, string>>;
44
-
45
42
  /** 커스텀 class */
46
43
  class?: string;
47
44
 
@@ -90,19 +87,10 @@ export const DateRangePicker: Component<DateRangePickerProps> = (props) => {
90
87
  "required",
91
88
  "disabled",
92
89
  "size",
93
- "periodLabels",
94
90
  "class",
95
91
  "style",
96
92
  ]);
97
93
 
98
- // 기간 타입 라벨
99
- const labels = (): Record<DateRangePeriodType, string> => ({
100
- day: "일",
101
- month: "월",
102
- range: "범위",
103
- ...local.periodLabels,
104
- });
105
-
106
94
  // controlled/uncontrolled 패턴
107
95
  const [periodType, setPeriodType] = createControllableSignal({
108
96
  value: () => local.periodType ?? ("range" as DateRangePeriodType),
@@ -170,15 +158,17 @@ export const DateRangePicker: Component<DateRangePickerProps> = (props) => {
170
158
  <Select
171
159
  value={periodType()}
172
160
  onValueChange={handlePeriodTypeChange}
173
- renderValue={(v: DateRangePeriodType) => <>{labels()[v]}</>}
161
+ renderValue={(v: DateRangePeriodType) => (
162
+ <>{{ day: "일", month: "월", range: "범위" }[v]}</>
163
+ )}
174
164
  required
175
165
  disabled={local.disabled}
176
166
  size={local.size}
177
167
  inset
178
168
  >
179
- <Select.Item value={"day" as DateRangePeriodType}>{labels().day}</Select.Item>
180
- <Select.Item value={"month" as DateRangePeriodType}>{labels().month}</Select.Item>
181
- <Select.Item value={"range" as DateRangePeriodType}>{labels().range}</Select.Item>
169
+ <Select.Item value={"day" as DateRangePeriodType}>일</Select.Item>
170
+ <Select.Item value={"month" as DateRangePeriodType}>월</Select.Item>
171
+ <Select.Item value={"range" as DateRangePeriodType}>범위</Select.Item>
182
172
  </Select>
183
173
 
184
174
  <Show
@@ -3,7 +3,7 @@ import { type Component, createMemo, type JSX, Show, splitProps } from "solid-js
3
3
  import { twMerge } from "tailwind-merge";
4
4
  import { DateOnly } from "@simplysm/core-common";
5
5
  import { createControllableSignal } from "../../../hooks/createControllableSignal";
6
- import { type FieldSize, fieldInputClass, getFieldWrapperClass } from "./Field.styles";
6
+ import { fieldInputClass, type FieldSize, getFieldWrapperClass } from "./Field.styles";
7
7
  import { Invalid } from "../../form-control/Invalid";
8
8
 
9
9
  type DatePickerUnit = "year" | "month" | "date";
@@ -181,6 +181,7 @@ export const DatePicker: Component<DatePickerProps> = (props) => {
181
181
  disabled: local.disabled,
182
182
  inset: local.inset,
183
183
  includeCustomClass: includeCustomClass && local.class,
184
+ extra: "min-w-32",
184
185
  });
185
186
 
186
187
  // 편집 가능 여부
@@ -231,6 +232,7 @@ export const DatePicker: Component<DatePickerProps> = (props) => {
231
232
  title={local.title}
232
233
  min={formatDateValue(local.min, fieldType())}
233
234
  max={formatDateValue(local.max, fieldType())}
235
+ autocomplete="one-time-code"
234
236
  onChange={handleChange}
235
237
  />
236
238
  </div>
@@ -257,6 +259,7 @@ export const DatePicker: Component<DatePickerProps> = (props) => {
257
259
  title={local.title}
258
260
  min={formatDateValue(local.min, fieldType())}
259
261
  max={formatDateValue(local.max, fieldType())}
262
+ autocomplete="one-time-code"
260
263
  onChange={handleChange}
261
264
  />
262
265
  </div>
@@ -176,6 +176,7 @@ export const DateTimePicker: Component<DateTimePickerProps> = (props) => {
176
176
  disabled: local.disabled,
177
177
  inset: local.inset,
178
178
  includeCustomClass: includeCustomClass && local.class,
179
+ extra: "min-w-44",
179
180
  });
180
181
 
181
182
  // 편집 가능 여부
@@ -230,6 +231,7 @@ export const DateTimePicker: Component<DateTimePickerProps> = (props) => {
230
231
  min={formatDateTimeValue(local.min, fieldType())}
231
232
  max={formatDateTimeValue(local.max, fieldType())}
232
233
  step={getStep()}
234
+ autocomplete="one-time-code"
233
235
  onChange={handleChange}
234
236
  />
235
237
  </div>
@@ -262,6 +264,7 @@ export const DateTimePicker: Component<DateTimePickerProps> = (props) => {
262
264
  min={formatDateTimeValue(local.min, fieldType())}
263
265
  max={formatDateTimeValue(local.max, fieldType())}
264
266
  step={getStep()}
267
+ autocomplete="one-time-code"
265
268
  onChange={handleChange}
266
269
  />
267
270
  </div>
@@ -22,6 +22,7 @@ export const fieldBaseClass = clsx(
22
22
  fieldSurface,
23
23
  "px-2 py-1",
24
24
  "h-field",
25
+ "[text-decoration:inherit]",
25
26
  );
26
27
 
27
28
  // 사이즈별 스타일