pika-ux 1.0.0-beta.0

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 (292) hide show
  1. package/LICENSE +9 -0
  2. package/dist/icon-generator/generate-icon-ts-indices.js +78 -0
  3. package/dist/shadcn-postinstall/index.js +114 -0
  4. package/package.json +102 -0
  5. package/readme.md +50 -0
  6. package/scripts/setup.js +100 -0
  7. package/src/App.svelte +51 -0
  8. package/src/app.css +349 -0
  9. package/src/icons/ci/index.d.ts +5009 -0
  10. package/src/icons/lucide/index.d.ts +11274 -0
  11. package/src/index.ts +23 -0
  12. package/src/lib/docsite/Navigation.svelte +77 -0
  13. package/src/lib/docsite/pages/Colors.svelte +35 -0
  14. package/src/lib/docsite/pages/Components.svelte +50 -0
  15. package/src/lib/docsite/pages/GettingStarted.svelte +21 -0
  16. package/src/lib/docsite/pages/Icons.svelte +22 -0
  17. package/src/lib/docsite/pages/components/Button.svelte +40 -0
  18. package/src/main.ts +9 -0
  19. package/src/pika/chip/chip.svelte +95 -0
  20. package/src/pika/chip/index.ts +1 -0
  21. package/src/pika/combobox/combobox-types.ts +5 -0
  22. package/src/pika/combobox/combobox.svelte +221 -0
  23. package/src/pika/combobox/index.ts +2 -0
  24. package/src/pika/confirm-dialog/confirm-dialog.svelte +48 -0
  25. package/src/pika/confirm-dialog/index.ts +1 -0
  26. package/src/pika/copy-button/copy-button.svelte +134 -0
  27. package/src/pika/copy-button/index.ts +1 -0
  28. package/src/pika/create-copy-link-button/create-copy-link-button.svelte +133 -0
  29. package/src/pika/create-copy-link-button/index.ts +1 -0
  30. package/src/pika/date-picker/date-picker.svelte +33 -0
  31. package/src/pika/date-picker/index.ts +1 -0
  32. package/src/pika/date-range-picker/date-range-picker.svelte +48 -0
  33. package/src/pika/date-range-picker/index.ts +1 -0
  34. package/src/pika/date-time-picker/date-time-picker.svelte +336 -0
  35. package/src/pika/date-time-picker/index.ts +1 -0
  36. package/src/pika/expandable-container/expandable-container.svelte +155 -0
  37. package/src/pika/expandable-container/index.ts +1 -0
  38. package/src/pika/index.ts +29 -0
  39. package/src/pika/list/index.ts +2 -0
  40. package/src/pika/list/list-types.ts +5 -0
  41. package/src/pika/list/list.svelte +349 -0
  42. package/src/pika/markdown-editor/github.scss +87 -0
  43. package/src/pika/markdown-editor/index.ts +1 -0
  44. package/src/pika/markdown-editor/markdown-editor.svelte +44 -0
  45. package/src/pika/permanent-toast/index.ts +1 -0
  46. package/src/pika/permanent-toast/permanent-toast.svelte +47 -0
  47. package/src/pika/pika-alert/index.ts +1 -0
  48. package/src/pika/pika-alert/pika-alert.svelte +53 -0
  49. package/src/pika/pika-badge/index.ts +1 -0
  50. package/src/pika/pika-badge/pika-badge.svelte +61 -0
  51. package/src/pika/pika-table/index.ts +7 -0
  52. package/src/pika/pika-table/pika-table-cell.svelte +9 -0
  53. package/src/pika/pika-table/pika-table-checkbox.svelte +8 -0
  54. package/src/pika/pika-table/pika-table-column-header.svelte +88 -0
  55. package/src/pika/pika-table/pika-table-faceted-filter.svelte +109 -0
  56. package/src/pika/pika-table/pika-table-pagination.svelte +95 -0
  57. package/src/pika/pika-table/pika-table-row-actions.svelte +58 -0
  58. package/src/pika/pika-table/pika-table-toolbar.svelte +88 -0
  59. package/src/pika/pika-table/pika-table-view-options.svelte +35 -0
  60. package/src/pika/pika-table/pika-table.svelte +295 -0
  61. package/src/pika/pika-table/types.ts +106 -0
  62. package/src/pika/pika-tabs/index.ts +18 -0
  63. package/src/pika/pika-tabs/tabs-content.svelte +16 -0
  64. package/src/pika/pika-tabs/tabs-list.svelte +12 -0
  65. package/src/pika/pika-tabs/tabs-trigger.svelte +23 -0
  66. package/src/pika/popup-help/index.ts +1 -0
  67. package/src/pika/popup-help/popup-help.svelte +33 -0
  68. package/src/pika/simple-dropdown/index.ts +2 -0
  69. package/src/pika/simple-dropdown/simple-dropdown-types.ts +5 -0
  70. package/src/pika/simple-dropdown/simple-dropdown.svelte +288 -0
  71. package/src/pika/slideout/constants.ts +5 -0
  72. package/src/pika/slideout/context.svelte.ts +110 -0
  73. package/src/pika/slideout/index.ts +19 -0
  74. package/src/pika/slideout/slideout-content.svelte +36 -0
  75. package/src/pika/slideout/slideout-panel.svelte +126 -0
  76. package/src/pika/slideout/slideout-provider.svelte +49 -0
  77. package/src/pika/slideout/slideout-rail.svelte.die +69 -0
  78. package/src/pika/slideout/slideout.svelte +33 -0
  79. package/src/pika/slideout/slideout.svelte.old +113 -0
  80. package/src/pika/text-wave-shimmer/index.ts +1 -0
  81. package/src/pika/text-wave-shimmer/text-wave-shimmer.svelte +43 -0
  82. package/src/pika/tooltip-plus/index.ts +1 -0
  83. package/src/pika/tooltip-plus/tooltip-plus.svelte +42 -0
  84. package/src/shadcn/.DS_Store +0 -0
  85. package/src/shadcn/alert/alert-description.svelte +11 -0
  86. package/src/shadcn/alert/alert-title.svelte +24 -0
  87. package/src/shadcn/alert/alert.svelte +39 -0
  88. package/src/shadcn/alert/index.ts +14 -0
  89. package/src/shadcn/avatar/avatar-fallback.svelte +13 -0
  90. package/src/shadcn/avatar/avatar-image.svelte +13 -0
  91. package/src/shadcn/avatar/avatar.svelte +19 -0
  92. package/src/shadcn/avatar/index.ts +13 -0
  93. package/src/shadcn/badge/badge.svelte +48 -0
  94. package/src/shadcn/badge/index.ts +2 -0
  95. package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +12 -0
  96. package/src/shadcn/breadcrumb/breadcrumb-item.svelte +20 -0
  97. package/src/shadcn/breadcrumb/breadcrumb-link.svelte +31 -0
  98. package/src/shadcn/breadcrumb/breadcrumb-list.svelte +20 -0
  99. package/src/shadcn/breadcrumb/breadcrumb-page.svelte +23 -0
  100. package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +15 -0
  101. package/src/shadcn/breadcrumb/breadcrumb.svelte +15 -0
  102. package/src/shadcn/breadcrumb/index.ts +25 -0
  103. package/src/shadcn/button/button.svelte +81 -0
  104. package/src/shadcn/button/index.ts +17 -0
  105. package/src/shadcn/calendar/calendar-caption.svelte +76 -0
  106. package/src/shadcn/calendar/calendar-cell.svelte +19 -0
  107. package/src/shadcn/calendar/calendar-day.svelte +31 -0
  108. package/src/shadcn/calendar/calendar-grid-body.svelte +12 -0
  109. package/src/shadcn/calendar/calendar-grid-head.svelte +12 -0
  110. package/src/shadcn/calendar/calendar-grid-row.svelte +12 -0
  111. package/src/shadcn/calendar/calendar-grid.svelte +16 -0
  112. package/src/shadcn/calendar/calendar-head-cell.svelte +16 -0
  113. package/src/shadcn/calendar/calendar-header.svelte +16 -0
  114. package/src/shadcn/calendar/calendar-heading.svelte +12 -0
  115. package/src/shadcn/calendar/calendar-month-select.svelte +25 -0
  116. package/src/shadcn/calendar/calendar-month.svelte +15 -0
  117. package/src/shadcn/calendar/calendar-months.svelte +20 -0
  118. package/src/shadcn/calendar/calendar-nav.svelte +19 -0
  119. package/src/shadcn/calendar/calendar-next-button.svelte +19 -0
  120. package/src/shadcn/calendar/calendar-prev-button.svelte +19 -0
  121. package/src/shadcn/calendar/calendar-year-select.svelte +25 -0
  122. package/src/shadcn/calendar/calendar.svelte +61 -0
  123. package/src/shadcn/calendar/index.ts +30 -0
  124. package/src/shadcn/card/card-content.svelte +16 -0
  125. package/src/shadcn/card/card-description.svelte +16 -0
  126. package/src/shadcn/card/card-footer.svelte +16 -0
  127. package/src/shadcn/card/card-header.svelte +16 -0
  128. package/src/shadcn/card/card-title.svelte +25 -0
  129. package/src/shadcn/card/card.svelte +20 -0
  130. package/src/shadcn/card/index.ts +22 -0
  131. package/src/shadcn/carousel/carousel-content.svelte +39 -0
  132. package/src/shadcn/carousel/carousel-item.svelte +26 -0
  133. package/src/shadcn/carousel/carousel-next.svelte +30 -0
  134. package/src/shadcn/carousel/carousel-previous.svelte +30 -0
  135. package/src/shadcn/carousel/carousel.svelte +88 -0
  136. package/src/shadcn/carousel/context.ts +51 -0
  137. package/src/shadcn/carousel/index.ts +19 -0
  138. package/src/shadcn/checkbox/checkbox.svelte +36 -0
  139. package/src/shadcn/checkbox/index.ts +6 -0
  140. package/src/shadcn/collapsible/collapsible-content.svelte +7 -0
  141. package/src/shadcn/collapsible/collapsible-trigger.svelte +7 -0
  142. package/src/shadcn/collapsible/collapsible.svelte +11 -0
  143. package/src/shadcn/collapsible/index.ts +13 -0
  144. package/src/shadcn/command/command-dialog.svelte +40 -0
  145. package/src/shadcn/command/command-empty.svelte +13 -0
  146. package/src/shadcn/command/command-group.svelte +30 -0
  147. package/src/shadcn/command/command-input.svelte +21 -0
  148. package/src/shadcn/command/command-item.svelte +16 -0
  149. package/src/shadcn/command/command-link-item.svelte +16 -0
  150. package/src/shadcn/command/command-list.svelte +13 -0
  151. package/src/shadcn/command/command-separator.svelte +13 -0
  152. package/src/shadcn/command/command-shortcut.svelte +20 -0
  153. package/src/shadcn/command/command.svelte +19 -0
  154. package/src/shadcn/command/index.ts +40 -0
  155. package/src/shadcn/data-table/data-table.svelte.ts +141 -0
  156. package/src/shadcn/data-table/flex-render.svelte +36 -0
  157. package/src/shadcn/data-table/index.ts +3 -0
  158. package/src/shadcn/data-table/render-helpers.ts +111 -0
  159. package/src/shadcn/dialog/dialog-close.svelte +7 -0
  160. package/src/shadcn/dialog/dialog-content.svelte +43 -0
  161. package/src/shadcn/dialog/dialog-description.svelte +13 -0
  162. package/src/shadcn/dialog/dialog-footer.svelte +20 -0
  163. package/src/shadcn/dialog/dialog-header.svelte +20 -0
  164. package/src/shadcn/dialog/dialog-overlay.svelte +16 -0
  165. package/src/shadcn/dialog/dialog-title.svelte +13 -0
  166. package/src/shadcn/dialog/dialog-trigger.svelte +7 -0
  167. package/src/shadcn/dialog/index.ts +37 -0
  168. package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +41 -0
  169. package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +27 -0
  170. package/src/shadcn/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  171. package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +7 -0
  172. package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +27 -0
  173. package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +24 -0
  174. package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  175. package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +26 -0
  176. package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +13 -0
  177. package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  178. package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +16 -0
  179. package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  180. package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  181. package/src/shadcn/dropdown-menu/index.ts +49 -0
  182. package/src/shadcn/index.ts +40 -0
  183. package/src/shadcn/input/index.ts +7 -0
  184. package/src/shadcn/input/input.svelte +51 -0
  185. package/src/shadcn/is-mobile.svelte.ts +9 -0
  186. package/src/shadcn/label/index.ts +7 -0
  187. package/src/shadcn/label/label.svelte +16 -0
  188. package/src/shadcn/popover/index.ts +17 -0
  189. package/src/shadcn/popover/popover-content.svelte +29 -0
  190. package/src/shadcn/popover/popover-trigger.svelte +8 -0
  191. package/src/shadcn/radio-group/index.ts +10 -0
  192. package/src/shadcn/radio-group/radio-group-item.svelte +25 -0
  193. package/src/shadcn/radio-group/radio-group.svelte +19 -0
  194. package/src/shadcn/range-calendar/index.ts +30 -0
  195. package/src/shadcn/range-calendar/range-calendar-cell.svelte +19 -0
  196. package/src/shadcn/range-calendar/range-calendar-day.svelte +35 -0
  197. package/src/shadcn/range-calendar/range-calendar-grid-body.svelte +12 -0
  198. package/src/shadcn/range-calendar/range-calendar-grid-head.svelte +12 -0
  199. package/src/shadcn/range-calendar/range-calendar-grid-row.svelte +12 -0
  200. package/src/shadcn/range-calendar/range-calendar-grid.svelte +16 -0
  201. package/src/shadcn/range-calendar/range-calendar-head-cell.svelte +16 -0
  202. package/src/shadcn/range-calendar/range-calendar-header.svelte +16 -0
  203. package/src/shadcn/range-calendar/range-calendar-heading.svelte +16 -0
  204. package/src/shadcn/range-calendar/range-calendar-months.svelte +20 -0
  205. package/src/shadcn/range-calendar/range-calendar-next-button.svelte +18 -0
  206. package/src/shadcn/range-calendar/range-calendar-prev-button.svelte +18 -0
  207. package/src/shadcn/range-calendar/range-calendar.svelte +57 -0
  208. package/src/shadcn/resizable/index.ts +13 -0
  209. package/src/shadcn/resizable/resizable-handle.svelte +30 -0
  210. package/src/shadcn/resizable/resizable-pane-group.svelte +22 -0
  211. package/src/shadcn/scroll-area/index.ts +10 -0
  212. package/src/shadcn/scroll-area/scroll-area-scrollbar.svelte +28 -0
  213. package/src/shadcn/scroll-area/scroll-area.svelte +35 -0
  214. package/src/shadcn/select/index.ts +37 -0
  215. package/src/shadcn/select/select-content.svelte +38 -0
  216. package/src/shadcn/select/select-group-heading.svelte +21 -0
  217. package/src/shadcn/select/select-group.svelte +7 -0
  218. package/src/shadcn/select/select-item.svelte +31 -0
  219. package/src/shadcn/select/select-label.svelte +20 -0
  220. package/src/shadcn/select/select-scroll-down-button.svelte +11 -0
  221. package/src/shadcn/select/select-scroll-up-button.svelte +11 -0
  222. package/src/shadcn/select/select-separator.svelte +14 -0
  223. package/src/shadcn/select/select-trigger.svelte +30 -0
  224. package/src/shadcn/separator/index.ts +7 -0
  225. package/src/shadcn/separator/separator.svelte +16 -0
  226. package/src/shadcn/sheet/index.ts +36 -0
  227. package/src/shadcn/sheet/sheet-close.svelte +7 -0
  228. package/src/shadcn/sheet/sheet-content.svelte +66 -0
  229. package/src/shadcn/sheet/sheet-description.svelte +13 -0
  230. package/src/shadcn/sheet/sheet-footer.svelte +15 -0
  231. package/src/shadcn/sheet/sheet-header.svelte +15 -0
  232. package/src/shadcn/sheet/sheet-overlay.svelte +16 -0
  233. package/src/shadcn/sheet/sheet-title.svelte +13 -0
  234. package/src/shadcn/sheet/sheet-trigger.svelte +7 -0
  235. package/src/shadcn/sidebar/constants.ts +6 -0
  236. package/src/shadcn/sidebar/context.svelte.ts +80 -0
  237. package/src/shadcn/sidebar/index.ts +75 -0
  238. package/src/shadcn/sidebar/sidebar-content.svelte +24 -0
  239. package/src/shadcn/sidebar/sidebar-footer.svelte +21 -0
  240. package/src/shadcn/sidebar/sidebar-group-action.svelte +36 -0
  241. package/src/shadcn/sidebar/sidebar-group-content.svelte +21 -0
  242. package/src/shadcn/sidebar/sidebar-group-label.svelte +34 -0
  243. package/src/shadcn/sidebar/sidebar-group.svelte +21 -0
  244. package/src/shadcn/sidebar/sidebar-header.svelte +21 -0
  245. package/src/shadcn/sidebar/sidebar-input.svelte +21 -0
  246. package/src/shadcn/sidebar/sidebar-inset.svelte +24 -0
  247. package/src/shadcn/sidebar/sidebar-menu-action.svelte +43 -0
  248. package/src/shadcn/sidebar/sidebar-menu-badge.svelte +29 -0
  249. package/src/shadcn/sidebar/sidebar-menu-button.svelte +101 -0
  250. package/src/shadcn/sidebar/sidebar-menu-item.svelte +21 -0
  251. package/src/shadcn/sidebar/sidebar-menu-skeleton.svelte +36 -0
  252. package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +43 -0
  253. package/src/shadcn/sidebar/sidebar-menu-sub-item.svelte +21 -0
  254. package/src/shadcn/sidebar/sidebar-menu-sub.svelte +25 -0
  255. package/src/shadcn/sidebar/sidebar-menu.svelte +21 -0
  256. package/src/shadcn/sidebar/sidebar-provider.svelte +46 -0
  257. package/src/shadcn/sidebar/sidebar-rail.svelte +36 -0
  258. package/src/shadcn/sidebar/sidebar-separator.svelte +15 -0
  259. package/src/shadcn/sidebar/sidebar-trigger.svelte +35 -0
  260. package/src/shadcn/sidebar/sidebar.svelte +94 -0
  261. package/src/shadcn/skeleton/index.ts +7 -0
  262. package/src/shadcn/skeleton/skeleton.svelte +17 -0
  263. package/src/shadcn/slider/index.ts +7 -0
  264. package/src/shadcn/slider/slider.svelte +44 -0
  265. package/src/shadcn/sonner/index.ts +1 -0
  266. package/src/shadcn/sonner/sonner.svelte +13 -0
  267. package/src/shadcn/switch/index.ts +7 -0
  268. package/src/shadcn/switch/switch.svelte +27 -0
  269. package/src/shadcn/table/index.ts +28 -0
  270. package/src/shadcn/table/table-body.svelte +15 -0
  271. package/src/shadcn/table/table-caption.svelte +20 -0
  272. package/src/shadcn/table/table-cell.svelte +20 -0
  273. package/src/shadcn/table/table-footer.svelte +20 -0
  274. package/src/shadcn/table/table-head.svelte +23 -0
  275. package/src/shadcn/table/table-header.svelte +15 -0
  276. package/src/shadcn/table/table-row.svelte +23 -0
  277. package/src/shadcn/table/table.svelte +17 -0
  278. package/src/shadcn/tabs/index.ts +18 -0
  279. package/src/shadcn/tabs/tabs-content.svelte +21 -0
  280. package/src/shadcn/tabs/tabs-list.svelte +19 -0
  281. package/src/shadcn/tabs/tabs-trigger.svelte +21 -0
  282. package/src/shadcn/textarea/index.ts +7 -0
  283. package/src/shadcn/textarea/textarea.svelte +22 -0
  284. package/src/shadcn/toggle/index.ts +13 -0
  285. package/src/shadcn/toggle/toggle.svelte +51 -0
  286. package/src/shadcn/toggle-group/index.ts +10 -0
  287. package/src/shadcn/toggle-group/toggle-group-item.svelte +30 -0
  288. package/src/shadcn/toggle-group/toggle-group.svelte +41 -0
  289. package/src/shadcn/tooltip/index.ts +21 -0
  290. package/src/shadcn/tooltip/tooltip-content.svelte +47 -0
  291. package/src/shadcn/tooltip/tooltip-trigger.svelte +7 -0
  292. package/src/shadcn/utils.ts +14 -0
@@ -0,0 +1,336 @@
1
+ <script lang="ts">
2
+ import CalendarIcon from '$icons/lucide/calendar';
3
+ import X from '$icons/lucide/x';
4
+ import { Button } from '../../shadcn/button';
5
+ import { Calendar } from '../../shadcn/calendar';
6
+ import { Input } from '../../shadcn/input';
7
+ import * as Popover from '../../shadcn/popover';
8
+ import { cn } from '../../shadcn/utils';
9
+ import { CalendarDate, parseAbsolute, type DateValue } from '@internationalized/date';
10
+ import { isSameDay, parseISO } from 'date-fns';
11
+ import { formatInTimeZone, fromZonedTime } from 'date-fns-tz';
12
+
13
+ type Props = {
14
+ value?: string; // ISO string
15
+ onChange?: (value: string | undefined) => void;
16
+ placeHolder?: string;
17
+ minValue?: string; // ISO string
18
+ maxValue?: string; // ISO string
19
+ required?: boolean;
20
+ timezone: string; // Required timezone prop
21
+ classes?: string;
22
+
23
+ /**
24
+ * This is ignored if minValue or maxValue is not set. If both are set,
25
+ * this is similarly ignored.
26
+ *
27
+ * If true and minValue is set and the user sets the date to the same day as minValue
28
+ * but the time is before the time in minValue, the time will be set to the exact time
29
+ * of minValue.
30
+ *
31
+ * If true and maxValue is set and the user sets the date to the same day as maxValue
32
+ * but the time is after the time in maxValue, the time will be set to the exact time
33
+ * of maxValue.
34
+ */
35
+ autoCorrectTimeForSameDay?: boolean;
36
+ };
37
+
38
+ let { value = $bindable(), onChange, placeHolder, minValue, maxValue, required, timezone, classes, autoCorrectTimeForSameDay = false }: Props = $props();
39
+
40
+ // State for flashing red effect when auto-correction occurs
41
+ let isFlashingRed = $state(false);
42
+
43
+ // Helper function to trigger red flash effect
44
+ function triggerRedFlash() {
45
+ isFlashingRed = true;
46
+ setTimeout(() => {
47
+ isFlashingRed = false;
48
+ }, 600); // Flash for 600ms
49
+ }
50
+
51
+ // Convert ISO string to DateValue for the Calendar component
52
+ function isoToDateValue(isoString: string | undefined): DateValue | undefined {
53
+ if (!isoString) return undefined;
54
+ try {
55
+ return parseAbsolute(isoString, timezone);
56
+ } catch {
57
+ return undefined;
58
+ }
59
+ }
60
+
61
+ // Convert ISO string to date-only DateValue for Calendar min/max (ignores time)
62
+ function isoToDateOnlyValue(isoString: string | undefined): DateValue | undefined {
63
+ if (!isoString) return undefined;
64
+ try {
65
+ const date = parseISO(isoString);
66
+ return new CalendarDate(date.getFullYear(), date.getMonth() + 1, date.getDate());
67
+ } catch {
68
+ return undefined;
69
+ }
70
+ }
71
+
72
+ // Convert DateValue to ISO string
73
+ function dateValueToIso(dateValue: DateValue | undefined, hour: number = 0, minute: number = 0): string | undefined {
74
+ if (!dateValue) return undefined;
75
+
76
+ // Create a Date object in the specified timezone
77
+ const date = new Date(dateValue.year, dateValue.month - 1, dateValue.day, hour, minute);
78
+
79
+ // Convert to UTC and return ISO string
80
+ return fromZonedTime(date, timezone).toISOString();
81
+ }
82
+
83
+ // DATA FLOW EXPLANATION:
84
+ // 1. Main prop 'value' is an ISO string (what gets passed to parent)
85
+ // 2. Calendar component needs DateValue, so we use localDateValue as intermediate
86
+ // 3. When prop changes -> update localDateValue (effect below)
87
+ // 4. When user clicks calendar -> Calendar updates localDateValue via bind:value -> second effect detects this -> calls handleDateChange -> updates main value
88
+
89
+ // Create a local DateValue to track calendar changes
90
+ let localDateValue = $state<DateValue | undefined>(isoToDateValue(value));
91
+
92
+ // EFFECT 1: Prop -> Local (when parent updates the value prop)
93
+ $effect(() => {
94
+ localDateValue = isoToDateValue(value);
95
+ });
96
+
97
+ // EFFECT 2: Local -> Prop (when user interacts with calendar)
98
+ $effect(() => {
99
+ // Skip if localDateValue change was caused by the prop change above
100
+ if (localDateValue === isoToDateValue(value)) return;
101
+
102
+ // Call handleDateChange when localDateValue changes through calendar interaction
103
+ handleDateChange(localDateValue);
104
+ });
105
+
106
+ // Time handling - derive from ISO value
107
+ let hours = $derived.by(() => {
108
+ if (!value) return 0;
109
+ const date = parseISO(value);
110
+ // Parse the time in the specified timezone
111
+ const timeStr = formatInTimeZone(date, timezone, 'H');
112
+ return parseInt(timeStr);
113
+ });
114
+
115
+ let minutes = $derived.by(() => {
116
+ if (!value) return 0;
117
+ const date = parseISO(value);
118
+ const timeStr = formatInTimeZone(date, timezone, 'm');
119
+ return parseInt(timeStr);
120
+ });
121
+
122
+ let isPM = $derived(hours >= 12);
123
+
124
+ // Format for display using your timezone approach
125
+ const displayFormatter = $derived.by(() => {
126
+ if (!value) return '';
127
+ const date = parseISO(value);
128
+ return formatInTimeZone(date, timezone, 'M/d/yy h:mm a');
129
+ });
130
+
131
+ function isTimeValid(isoString: string, hour: number, minute: number): boolean {
132
+ if (!minValue && !maxValue) return true;
133
+
134
+ const testDate = parseISO(isoString);
135
+ const testDateTime = new Date(testDate.getFullYear(), testDate.getMonth(), testDate.getDate(), hour, minute);
136
+ const testIso = fromZonedTime(testDateTime, timezone).toISOString();
137
+
138
+ if (minValue && testIso < minValue) return false;
139
+ if (maxValue && testIso > maxValue) return false;
140
+
141
+ return true;
142
+ }
143
+
144
+ function handleTimeChange(type: 'hour' | 'minute', newValue: string) {
145
+ const num = parseInt(newValue) || 0;
146
+ let newHour = hours;
147
+ let newMinute = minutes;
148
+
149
+ if (type === 'hour') {
150
+ newHour = Math.max(0, Math.min(23, num));
151
+ } else {
152
+ newMinute = Math.max(0, Math.min(59, num));
153
+ }
154
+
155
+ // If no date is set, use today
156
+ let dateToUse = value ? parseISO(value) : new Date();
157
+ const newDateTime = new Date(dateToUse.getFullYear(), dateToUse.getMonth(), dateToUse.getDate(), newHour, newMinute);
158
+ let newIso = fromZonedTime(newDateTime, timezone).toISOString();
159
+
160
+ // Apply auto-correction if enabled and time is invalid
161
+ let autoCorrectionApplied = false;
162
+ if (!isTimeValid(newIso, newHour, newMinute) && autoCorrectTimeForSameDay && (minValue || maxValue) && !(minValue && maxValue)) {
163
+ const newDate = parseISO(newIso);
164
+ const originalIso = newIso;
165
+
166
+ // Case 1: minValue is set, same day, but time is before minValue
167
+ if (minValue && !maxValue) {
168
+ const minDateTime = parseISO(minValue);
169
+ if (isSameDay(newDate, minDateTime) && newDate < minDateTime) {
170
+ newIso = minValue;
171
+ autoCorrectionApplied = originalIso !== newIso;
172
+ }
173
+ }
174
+
175
+ // Case 2: maxValue is set, same day, but time is after maxValue
176
+ if (maxValue && !minValue) {
177
+ const maxDateTime = parseISO(maxValue);
178
+ if (isSameDay(newDate, maxDateTime) && newDate > maxDateTime) {
179
+ newIso = maxValue;
180
+ autoCorrectionApplied = originalIso !== newIso;
181
+ }
182
+ }
183
+ }
184
+
185
+ // Apply the change if it's now valid (either originally or after auto-correction)
186
+ if (isTimeValid(newIso, parseISO(newIso).getHours(), parseISO(newIso).getMinutes())) {
187
+ value = newIso;
188
+ onChange?.(newIso);
189
+
190
+ // Trigger red flash if auto-correction was applied
191
+ if (autoCorrectionApplied) {
192
+ triggerRedFlash();
193
+ }
194
+ }
195
+ }
196
+
197
+ function handleDateChange(newDate: DateValue | undefined) {
198
+ if (!newDate) {
199
+ if (required) return;
200
+ value = undefined;
201
+ onChange?.(undefined);
202
+ return;
203
+ }
204
+
205
+ // Preserve existing time or use current time
206
+ const currentHour = hours;
207
+ const currentMinute = minutes;
208
+
209
+ let newIso = dateValueToIso(newDate, currentHour, currentMinute);
210
+
211
+ if (newIso) {
212
+ let autoCorrectionApplied = false;
213
+
214
+ // Auto-correct time for same day if enabled and only one of min/max is set
215
+ if (autoCorrectTimeForSameDay && (minValue || maxValue) && !(minValue && maxValue)) {
216
+ const newDateTime = parseISO(newIso);
217
+ const originalIso = newIso;
218
+
219
+ // Case 1: minValue is set, same day, but time is before minValue
220
+ if (minValue && !maxValue) {
221
+ const minDateTime = parseISO(minValue);
222
+ if (isSameDay(newDateTime, minDateTime) && newDateTime < minDateTime) {
223
+ newIso = minValue;
224
+ autoCorrectionApplied = originalIso !== newIso;
225
+ }
226
+ }
227
+
228
+ // Case 2: maxValue is set, same day, but time is after maxValue
229
+ if (maxValue && !minValue) {
230
+ const maxDateTime = parseISO(maxValue);
231
+ if (isSameDay(newDateTime, maxDateTime) && newDateTime > maxDateTime) {
232
+ newIso = maxValue;
233
+ autoCorrectionApplied = originalIso !== newIso;
234
+ }
235
+ }
236
+ }
237
+
238
+ // Use normal validation if auto-correction didn't apply or wasn't enabled
239
+ if (isTimeValid(newIso, parseISO(newIso).getHours(), parseISO(newIso).getMinutes())) {
240
+ localDateValue = newDate;
241
+ value = newIso;
242
+ onChange?.(newIso);
243
+
244
+ // Trigger red flash if auto-correction was applied
245
+ if (autoCorrectionApplied) {
246
+ triggerRedFlash();
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ function handleClear(e: Event) {
253
+ e.stopPropagation();
254
+ localDateValue = undefined;
255
+ value = undefined;
256
+ onChange?.(undefined);
257
+ }
258
+ </script>
259
+
260
+ <Popover.Root>
261
+ <Popover.Trigger class={cn('justify-start text-left font-normal', !value && 'text-muted-foreground')}>
262
+ <div class={cn('relative')}>
263
+ <Button variant="outline" class={cn('justify-start text-left font-normal', !value && 'text-muted-foreground', classes)}>
264
+ <CalendarIcon class="h-5 w-5" />
265
+ {value ? displayFormatter : placeHolder || 'Pick a date/time'}
266
+ </Button>
267
+ {#if value && !required}
268
+ <button
269
+ type="button"
270
+ class="absolute top-1 right-2 h-7 hover:bg-muted rounded-sm text-muted-foreground hover:text-foreground"
271
+ onclick={handleClear}
272
+ onkeydown={(e) => e.key === 'Enter' && handleClear(e)}
273
+ >
274
+ <X class="h-4 w-4" />
275
+ </button>
276
+ {/if}
277
+ </div>
278
+ </Popover.Trigger>
279
+ <Popover.Content class="w-auto p-0">
280
+ <Calendar type="single" bind:value={localDateValue} minValue={isoToDateOnlyValue(minValue)} maxValue={isoToDateOnlyValue(maxValue)} preventDeselect={required} />
281
+ <div class="p-3 border-t border-border flex flex-col gap-2">
282
+ <div class="flex items-center gap-1">
283
+ <Input
284
+ type="number"
285
+ bind:value={
286
+ () => {
287
+ return hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
288
+ },
289
+ (newVal) => {
290
+ const newHour = newVal === 12 ? (isPM ? 12 : 0) : isPM ? newVal + 12 : newVal;
291
+ handleTimeChange('hour', newHour.toString());
292
+ }
293
+ }
294
+ min={1}
295
+ max={12}
296
+ class="w-16 {isFlashingRed ? 'text-destructive' : ''}"
297
+ />
298
+ <span>:</span>
299
+ <Input
300
+ type="number"
301
+ bind:value={
302
+ () => minutes,
303
+ (newVal) => {
304
+ const newMinute = newVal === 60 ? 0 : newVal;
305
+ handleTimeChange('minute', newMinute.toString());
306
+ }
307
+ }
308
+ min={0}
309
+ max={59}
310
+ class="w-16 {isFlashingRed ? 'text-destructive' : ''}"
311
+ />
312
+ <Button
313
+ variant="outline"
314
+ size="sm"
315
+ class="w-20"
316
+ onclick={() => {
317
+ const newHour = hours >= 12 ? hours - 12 : hours + 12;
318
+ handleTimeChange('hour', newHour.toString());
319
+ }}
320
+ >
321
+ {hours >= 12 ? 'PM' : 'AM'}
322
+ </Button>
323
+ </div>
324
+ {#if minValue}
325
+ <div class="text-xs transition-colors duration-200 {isFlashingRed ? 'text-destructive' : 'text-muted-foreground'}">
326
+ must be after {formatInTimeZone(parseISO(minValue), timezone, 'M/d/yy h:mm a')}
327
+ </div>
328
+ {/if}
329
+ {#if maxValue}
330
+ <div class="text-xs transition-colors duration-200 {isFlashingRed ? 'text-destructive' : 'text-muted-foreground'}">
331
+ must be before {formatInTimeZone(parseISO(maxValue), timezone, 'M/d/yy h:mm a')}
332
+ </div>
333
+ {/if}
334
+ </div>
335
+ </Popover.Content>
336
+ </Popover.Root>
@@ -0,0 +1 @@
1
+ export { default as DateTimePicker } from './date-time-picker.svelte';
@@ -0,0 +1,155 @@
1
+ <!--
2
+ Expandable Container Component
3
+
4
+ A versatile container component that can be expanded/collapsed and supports different use cases.
5
+
6
+ Supported Use Cases:
7
+ 1. Default Use Case
8
+ - Basic expandable container with title and content
9
+ - Can be expanded/collapsed via chevron button
10
+ - Supports disabled state
11
+
12
+ 2. Steps in Process Use Case
13
+ - Visualizes steps in a multi-step process
14
+ - Shows status indicators for each step:
15
+ - Green checkmark (✓) for completed steps
16
+ - Blue circle (○) for current active step
17
+ - Yellow dash (-) for future steps
18
+ - Requires stepsInProcess prop with:
19
+ - stepId: Current step identifier
20
+ - steps: Array of steps with their completion status
21
+
22
+
23
+ 3. Button Use Case
24
+ - A button that can be used to expand/collapse the container
25
+ - The button is a chevron with a title that can be used to expand/collapse the container
26
+ - The button is a ghost button
27
+
28
+ Props:
29
+ - forceExpanded: boolean (optional) - Forces container to stay expanded
30
+ - title: string - Title text for the container
31
+ - disabled: boolean (optional) - Disables interaction with content
32
+ - children: Snippet - Content to display when expanded
33
+ - useCase: 'steps-in-process' | 'default' | 'button' - Determines component behavior
34
+ - stepsInProcess: StepsInProcess<any> - Required for 'steps-in-process' use case
35
+ -->
36
+
37
+ <script lang="ts">
38
+ import ArrowBigRight from '$icons/lucide/arrow-big-right';
39
+ import Check from '$icons/lucide/check';
40
+ import ChevronRight from '$icons/lucide/chevron-right';
41
+ import Circle from '$icons/lucide/circle';
42
+ import Minus from '$icons/lucide/minus';
43
+ import { Button } from '../../shadcn/button';
44
+ import type { Snippet } from 'svelte';
45
+
46
+ type UseCase = 'steps-in-process' | 'default' | 'button';
47
+
48
+ export interface StepModel<T> {
49
+ // The ID of the step
50
+ stepId: T;
51
+
52
+ // Whether the step has been completed
53
+ done: boolean;
54
+ }
55
+
56
+ export interface StepsInProcess<T> {
57
+ // The ID of the current step this expandable container is representing
58
+ stepId: T;
59
+
60
+ // The steps that make up the process, in order
61
+ steps: StepModel<T>[];
62
+ }
63
+
64
+ interface Props {
65
+ forceExpanded?: boolean;
66
+ title: string;
67
+ disabled?: boolean;
68
+ children?: Snippet<[]>;
69
+ useCase?: UseCase;
70
+ stepsInProcess?: StepsInProcess<any>;
71
+ }
72
+
73
+ let { forceExpanded = false, title, disabled = false, children, useCase = 'default', stepsInProcess }: Props = $props();
74
+
75
+ let expanded = $state(false);
76
+
77
+ let statusObj = $derived.by(() => {
78
+ if (useCase === 'steps-in-process' && stepsInProcess) {
79
+ const currentStep = stepsInProcess.steps.find((step) => step.stepId === stepsInProcess.stepId);
80
+ const firstStepNotDone = stepsInProcess.steps.find((step) => !step.done);
81
+ if (currentStep?.done) {
82
+ // This step is done
83
+ return { iconColor: 'text-green-600', textColor: 'text-gray-600', icon: Check, titleIcon: undefined };
84
+ } else if (firstStepNotDone?.stepId === currentStep?.stepId) {
85
+ // This is the first step that hasn't been done
86
+ return {
87
+ iconColor: 'text-purple-600',
88
+ textColor: 'text-gray-600',
89
+ icon: Circle,
90
+ titleIcon: ArrowBigRight
91
+ };
92
+ } else {
93
+ // You can't do this step yet since it's after the current step
94
+ return { iconColor: 'text-yellow-600', textColor: 'text-gray-400', icon: Minus, titleIcon: undefined };
95
+ }
96
+ }
97
+
98
+ return undefined;
99
+ });
100
+
101
+ let forceExpandedComputed = $derived.by(() => {
102
+ if (useCase === 'steps-in-process' && stepsInProcess) {
103
+ const currentStep = stepsInProcess.steps.find((step) => step.stepId === stepsInProcess.stepId);
104
+ const firstStepNotDone = stepsInProcess.steps.find((step) => !step.done);
105
+ return currentStep?.stepId === firstStepNotDone?.stepId;
106
+ }
107
+ return forceExpanded;
108
+ });
109
+
110
+ $effect(() => {
111
+ const uc = useCase;
112
+ const sip = stepsInProcess;
113
+ if (uc === 'steps-in-process' && !sip) {
114
+ throw new Error('stepsInProcess is required for useCase "steps-in-process"');
115
+ }
116
+ });
117
+ </script>
118
+
119
+ {#if useCase === 'button'}
120
+ <Button variant="ghost" onclick={() => (expanded = !expanded)}>
121
+ <span class="font-medium">{title}</span>
122
+ <ChevronRight class="transition-transform duration-200 data-[state=open]:rotate-90" data-state={expanded || forceExpandedComputed ? 'open' : 'closed'} />
123
+ </Button>
124
+ {#if expanded || forceExpandedComputed}
125
+ <div class="p-4 {disabled ? 'opacity-60 pointer-events-none' : ''}">
126
+ {@render children?.()}
127
+ </div>
128
+ {/if}
129
+ {:else}
130
+ <div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden" data-state={expanded || forceExpandedComputed ? 'open' : 'closed'}>
131
+ <div class="flex items-center p-1 gap-3 pl-2 pr-2">
132
+ <div class="flex-grow flex items-center gap-1 {forceExpandedComputed ? 'h-8 pl-1' : ''}">
133
+ {#if !forceExpandedComputed}
134
+ <Button variant="ghost" class="p-1" onclick={() => (expanded = !expanded)}>
135
+ <ChevronRight class="transition-transform duration-200 data-[state=open]:rotate-90" data-state={expanded || forceExpandedComputed ? 'open' : 'closed'} />
136
+ </Button>
137
+ {:else if useCase === 'steps-in-process' && statusObj && statusObj.titleIcon}
138
+ <statusObj.titleIcon class="w-5 h-5 {statusObj.iconColor}" />
139
+ {/if}
140
+
141
+ <span class="font-medium {statusObj ? statusObj.textColor : ''}">{title}</span>
142
+ </div>
143
+
144
+ {#if useCase === 'steps-in-process' && statusObj}
145
+ <statusObj.icon class="w-5 h-5 {statusObj.iconColor}" />
146
+ {/if}
147
+ </div>
148
+
149
+ {#if expanded || forceExpandedComputed}
150
+ <div class="border-t border-gray-200 p-4 {disabled ? 'opacity-60 pointer-events-none' : ''}">
151
+ {@render children?.()}
152
+ </div>
153
+ {/if}
154
+ </div>
155
+ {/if}
@@ -0,0 +1 @@
1
+ export { default as ExpandableContainer } from './expandable-container.svelte';
@@ -0,0 +1,29 @@
1
+ // Pika UI Components - Non-conflicting exports
2
+ export * from './chip';
3
+ export * from './combobox';
4
+ export * from './confirm-dialog';
5
+ export * from './copy-button';
6
+ export * from './create-copy-link-button';
7
+ export * from './date-picker';
8
+ export * from './date-range-picker';
9
+ export * from './date-time-picker';
10
+ export * from './expandable-container';
11
+ export * from './markdown-editor';
12
+ export * from './permanent-toast';
13
+ export * from './pika-alert';
14
+ export * from './pika-badge';
15
+ export * from './pika-table';
16
+ export * from './popup-help';
17
+ export * from './simple-dropdown';
18
+ export * from './text-wave-shimmer';
19
+ export * from './tooltip-plus';
20
+
21
+ // Selective exports to avoid naming conflicts
22
+ // List component - renamed to avoid conflict with TabsList
23
+ export { List as PikaList, type ListMapping } from './list';
24
+
25
+ // Tabs component - only export aliased versions to avoid "Content" and "List" conflicts
26
+ export { Tabs, TabsContent, TabsList, TabsTrigger } from './pika-tabs';
27
+
28
+ // Slideout component - only export aliased versions to avoid "Content" conflict
29
+ export { Slideout, SlideoutContent, SlideoutPanel, SlideoutProvider, useSlideout } from './slideout';
@@ -0,0 +1,2 @@
1
+ export { default as List } from './list.svelte';
2
+ export type * from './list-types';
@@ -0,0 +1,5 @@
1
+ export interface ListMapping<T> {
2
+ value: (item: T) => string;
3
+ label: (item: T) => string;
4
+ secondaryLabel?: (item: T) => string;
5
+ }