@torpor/ui 0.1.6 → 0.2.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 (278) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/Accordion/Accordion.torp +2 -2
  3. package/dist/Accordion/AccordionContent.torp +2 -2
  4. package/dist/Accordion/AccordionHeader.torp +2 -2
  5. package/dist/Accordion/AccordionItem.torp +2 -2
  6. package/dist/Accordion/AccordionTrigger.torp +2 -2
  7. package/dist/Accordion/index.d.ts +92 -0
  8. package/dist/Breadcrumb/Breadcrumb.torp +33 -0
  9. package/dist/Breadcrumb/BreadcrumbItem.torp +19 -0
  10. package/dist/Breadcrumb/BreadcrumbSeparator.torp +16 -0
  11. package/dist/Breadcrumb/index.d.ts +56 -0
  12. package/dist/Breadcrumb/index.js +14 -0
  13. package/dist/Calendar/Calendar.torp +265 -0
  14. package/dist/Calendar/CalendarDay.torp +67 -0
  15. package/dist/Calendar/CalendarTypes.ts +22 -0
  16. package/dist/Calendar/index.d.ts +38 -0
  17. package/dist/Calendar/index.js +10 -0
  18. package/dist/Calendar/index.js.map +1 -0
  19. package/dist/Dialog/Dialog.torp +3 -3
  20. package/dist/Dialog/DialogBody.torp +2 -2
  21. package/dist/Dialog/DialogButton.torp +2 -2
  22. package/dist/Dialog/DialogFooter.torp +2 -2
  23. package/dist/Dialog/DialogHeader.torp +2 -2
  24. package/dist/Dialog/index.d.ts +92 -0
  25. package/dist/Disclosure/Disclosure.torp +69 -0
  26. package/dist/Disclosure/DisclosureContent.torp +36 -0
  27. package/dist/Disclosure/DisclosureHeader.torp +34 -0
  28. package/dist/Disclosure/DisclosureTrigger.torp +36 -0
  29. package/dist/Disclosure/DisclosureTypes.ts +14 -0
  30. package/dist/Disclosure/index.d.ts +74 -0
  31. package/dist/Disclosure/index.js +18 -0
  32. package/dist/Disclosure/index.js.map +1 -0
  33. package/{src/Offscreen/Offscreen.torp → dist/Drawer/Drawer.torp} +10 -19
  34. package/dist/Drawer/index.d.ts +20 -0
  35. package/dist/Drawer/index.js +6 -0
  36. package/dist/Drawer/index.js.map +1 -0
  37. package/dist/Menu/Menu.torp +160 -0
  38. package/dist/Menu/MenuButton.torp +53 -0
  39. package/dist/Menu/MenuCheck.torp +70 -0
  40. package/dist/Menu/MenuGroup.torp +19 -0
  41. package/dist/Menu/MenuIndicator.torp +29 -0
  42. package/dist/Menu/MenuPopout.torp +71 -0
  43. package/dist/Menu/MenuPopoutContent.torp +74 -0
  44. package/dist/Menu/MenuPopoutTrigger.torp +117 -0
  45. package/dist/Menu/MenuRadio.torp +88 -0
  46. package/dist/Menu/MenuRadioGroup.torp +86 -0
  47. package/dist/Menu/MenuSeparator.torp +14 -0
  48. package/dist/Menu/MenuTypes.ts +58 -0
  49. package/dist/Menu/index.d.ts +200 -0
  50. package/dist/Menu/index.js +46 -0
  51. package/dist/Menu/index.js.map +1 -0
  52. package/dist/MenuBar/MenuBar.torp +150 -0
  53. package/dist/MenuBar/MenuBarItem.torp +104 -0
  54. package/dist/MenuBar/MenuBarItemContent.torp +108 -0
  55. package/dist/MenuBar/MenuBarItemTrigger.torp +89 -0
  56. package/dist/MenuBar/MenuBarTypes.ts +36 -0
  57. package/dist/MenuBar/index.d.ts +74 -0
  58. package/dist/MenuBar/index.js +18 -0
  59. package/dist/MenuBar/index.js.map +1 -0
  60. package/dist/NavMenu/NavMenu.torp +2 -2
  61. package/dist/NavMenu/NavMenuButton.torp +2 -2
  62. package/dist/NavMenu/NavMenuElement.torp +2 -2
  63. package/dist/NavMenu/NavMenuGroup.torp +2 -2
  64. package/dist/NavMenu/NavMenuGroupHeader.torp +2 -2
  65. package/dist/NavMenu/NavMenuLink.torp +2 -2
  66. package/dist/NavMenu/NavMenuPopout.torp +2 -2
  67. package/dist/NavMenu/NavMenuPopoutButton.torp +3 -3
  68. package/dist/NavMenu/NavMenuPopoutContent.torp +29 -27
  69. package/dist/NavMenu/NavMenuSeparator.torp +2 -2
  70. package/dist/NavMenu/index.d.ts +182 -0
  71. package/dist/Notification/Notification.torp +48 -0
  72. package/dist/Notification/NotificationClose.torp +28 -0
  73. package/dist/Notification/NotificationContainer.torp +14 -0
  74. package/dist/Notification/NotificationContent.torp +13 -0
  75. package/dist/Notification/NotificationTypes.ts +5 -0
  76. package/dist/Notification/index.d.ts +74 -0
  77. package/dist/Notification/index.js +36 -0
  78. package/dist/Notification/index.js.map +1 -0
  79. package/dist/Notification/showNotification.ts +30 -0
  80. package/dist/Popover/Popover.torp +2 -2
  81. package/dist/Popover/PopoverContent.torp +31 -28
  82. package/dist/Popover/PopoverContentAnchored.torp +39 -38
  83. package/dist/Popover/PopoverContentContextual.torp +39 -36
  84. package/dist/Popover/PopoverOverlay.torp +3 -3
  85. package/dist/Popover/PopoverTrigger.torp +2 -2
  86. package/dist/Popover/PopoverTriggerContextual.torp +2 -2
  87. package/dist/Popover/PopoverTriggerHover.torp +3 -3
  88. package/dist/Popover/index.d.ts +146 -0
  89. package/dist/TabGroup/TabContent.torp +2 -2
  90. package/dist/TabGroup/TabGroup.torp +2 -2
  91. package/dist/TabGroup/TabHeader.torp +3 -3
  92. package/dist/TabGroup/TabList.torp +2 -2
  93. package/dist/TabGroup/TabTrigger.torp +2 -2
  94. package/dist/TabGroup/index.d.ts +92 -0
  95. package/dist/icons/ArrowDown.torp +15 -0
  96. package/dist/icons/ArrowLeft.torp +15 -0
  97. package/dist/icons/ArrowRight.torp +15 -0
  98. package/dist/icons/ArrowUp.torp +15 -0
  99. package/dist/icons/Bars.torp +17 -0
  100. package/dist/icons/Check.torp +15 -0
  101. package/dist/icons/ChevronDown.torp +15 -0
  102. package/dist/icons/ChevronLeft.torp +15 -0
  103. package/dist/icons/ChevronRight.torp +15 -0
  104. package/dist/icons/ChevronUp.torp +15 -0
  105. package/dist/icons/Cross.torp +16 -0
  106. package/dist/icons/Loading.torp +16 -0
  107. package/dist/icons/Minus.torp +15 -0
  108. package/dist/icons/Plus.torp +16 -0
  109. package/dist/icons/index.d.ts +254 -0
  110. package/dist/icons/index.js +58 -0
  111. package/dist/icons/index.js.map +1 -0
  112. package/dist/{motion.d.cts → index.d.ts} +2 -2
  113. package/dist/metafile-esm.json +1 -1
  114. package/dist/motion/fade.ts +7 -0
  115. package/dist/motion/index.d.ts +2 -0
  116. package/dist/{motion.js → motion/index.js} +1 -1
  117. package/dist/motion/index.js.map +1 -0
  118. package/dist/mount/index.d.ts +2 -0
  119. package/dist/utils/dateUtils.ts +244 -0
  120. package/dist/utils/index.d.ts +2 -0
  121. package/dist/utils/setPopoverPosition.ts +4 -0
  122. package/package.json +51 -35
  123. package/scripts/postbuild.ts +42 -5
  124. package/src/Accordion/Accordion.torp +2 -2
  125. package/src/Accordion/AccordionContent.torp +2 -2
  126. package/src/Accordion/AccordionHeader.torp +2 -2
  127. package/src/Accordion/AccordionItem.torp +2 -2
  128. package/src/Accordion/AccordionTrigger.torp +2 -2
  129. package/src/Breadcrumb/Breadcrumb.torp +33 -0
  130. package/src/Breadcrumb/BreadcrumbItem.torp +19 -0
  131. package/src/Breadcrumb/BreadcrumbSeparator.torp +16 -0
  132. package/src/Breadcrumb/index.ts +5 -0
  133. package/src/Calendar/Calendar.torp +265 -0
  134. package/src/Calendar/CalendarDay.torp +67 -0
  135. package/src/Calendar/CalendarTypes.ts +22 -0
  136. package/src/Calendar/index.ts +4 -0
  137. package/src/Dialog/Dialog.torp +3 -3
  138. package/src/Dialog/DialogBody.torp +2 -2
  139. package/src/Dialog/DialogButton.torp +2 -2
  140. package/src/Dialog/DialogFooter.torp +2 -2
  141. package/src/Dialog/DialogHeader.torp +2 -2
  142. package/src/Disclosure/Disclosure.torp +69 -0
  143. package/src/Disclosure/DisclosureContent.torp +36 -0
  144. package/src/Disclosure/DisclosureHeader.torp +34 -0
  145. package/src/Disclosure/DisclosureTrigger.torp +36 -0
  146. package/src/Disclosure/DisclosureTypes.ts +14 -0
  147. package/src/Disclosure/index.ts +6 -0
  148. package/{dist/Offscreen/Offscreen.torp → src/Drawer/Drawer.torp} +10 -19
  149. package/src/Drawer/index.ts +3 -0
  150. package/src/Menu/Menu.torp +160 -0
  151. package/src/Menu/MenuButton.torp +53 -0
  152. package/src/Menu/MenuCheck.torp +70 -0
  153. package/src/Menu/MenuGroup.torp +19 -0
  154. package/src/Menu/MenuIndicator.torp +29 -0
  155. package/src/Menu/MenuPopout.torp +71 -0
  156. package/src/Menu/MenuPopoutContent.torp +74 -0
  157. package/src/Menu/MenuPopoutTrigger.torp +117 -0
  158. package/src/Menu/MenuRadio.torp +88 -0
  159. package/src/Menu/MenuRadioGroup.torp +86 -0
  160. package/src/Menu/MenuSeparator.torp +14 -0
  161. package/src/Menu/MenuTypes.ts +58 -0
  162. package/src/Menu/index.ts +25 -0
  163. package/src/MenuBar/MenuBar.torp +150 -0
  164. package/src/MenuBar/MenuBarItem.torp +104 -0
  165. package/src/MenuBar/MenuBarItemContent.torp +108 -0
  166. package/src/MenuBar/MenuBarItemTrigger.torp +89 -0
  167. package/src/MenuBar/MenuBarTypes.ts +36 -0
  168. package/src/MenuBar/index.ts +6 -0
  169. package/src/NavMenu/NavMenu.torp +2 -2
  170. package/src/NavMenu/NavMenuButton.torp +2 -2
  171. package/src/NavMenu/NavMenuElement.torp +2 -2
  172. package/src/NavMenu/NavMenuGroup.torp +2 -2
  173. package/src/NavMenu/NavMenuGroupHeader.torp +2 -2
  174. package/src/NavMenu/NavMenuLink.torp +2 -2
  175. package/src/NavMenu/NavMenuPopout.torp +2 -2
  176. package/src/NavMenu/NavMenuPopoutButton.torp +3 -3
  177. package/src/NavMenu/NavMenuPopoutContent.torp +29 -27
  178. package/src/NavMenu/NavMenuSeparator.torp +2 -2
  179. package/src/Notification/Notification.torp +48 -0
  180. package/src/Notification/NotificationClose.torp +28 -0
  181. package/src/Notification/NotificationContainer.torp +14 -0
  182. package/src/Notification/NotificationContent.torp +13 -0
  183. package/src/Notification/NotificationTypes.ts +5 -0
  184. package/src/Notification/index.ts +13 -0
  185. package/src/Notification/showNotification.ts +30 -0
  186. package/src/Popover/Popover.torp +2 -2
  187. package/src/Popover/PopoverContent.torp +31 -28
  188. package/src/Popover/PopoverContentAnchored.torp +39 -38
  189. package/src/Popover/PopoverContentContextual.torp +39 -36
  190. package/src/Popover/PopoverOverlay.torp +3 -3
  191. package/src/Popover/PopoverTrigger.torp +2 -2
  192. package/src/Popover/PopoverTriggerContextual.torp +2 -2
  193. package/src/Popover/PopoverTriggerHover.torp +3 -3
  194. package/src/Slider/Slider.torp +83 -0
  195. package/src/Slider/index.ts +3 -0
  196. package/src/TabGroup/TabContent.torp +2 -2
  197. package/src/TabGroup/TabGroup.torp +2 -2
  198. package/src/TabGroup/TabHeader.torp +3 -3
  199. package/src/TabGroup/TabList.torp +2 -2
  200. package/src/TabGroup/TabTrigger.torp +2 -2
  201. package/src/icons/ArrowDown.torp +15 -0
  202. package/src/icons/ArrowLeft.torp +15 -0
  203. package/src/icons/ArrowRight.torp +15 -0
  204. package/src/icons/ArrowUp.torp +15 -0
  205. package/src/icons/Bars.torp +17 -0
  206. package/src/icons/Check.torp +15 -0
  207. package/src/icons/ChevronDown.torp +15 -0
  208. package/src/icons/ChevronLeft.torp +15 -0
  209. package/src/icons/ChevronRight.torp +15 -0
  210. package/src/icons/ChevronUp.torp +15 -0
  211. package/src/icons/Cross.torp +16 -0
  212. package/src/icons/Loading.torp +16 -0
  213. package/src/icons/Minus.torp +15 -0
  214. package/src/icons/Plus.torp +16 -0
  215. package/src/icons/index.ts +31 -0
  216. package/src/motion/fade.ts +2 -2
  217. package/src/motion/index.ts +3 -0
  218. package/src/utils/dateUtils.ts +244 -0
  219. package/src/utils/setPopoverPosition.ts +4 -0
  220. package/test/Breadcrumb/accessibility.test.ts +29 -0
  221. package/test/Breadcrumb/basic.test.ts +17 -0
  222. package/test/Breadcrumb/components/BreadcrumbTest.torp +15 -0
  223. package/test/Calendar/components/CalendarTest.torp +9 -0
  224. package/test/Calendar/default.test.ts +18 -0
  225. package/test/Dialog/components/DialogAccessibility.torp +5 -5
  226. package/test/Dialog/components/DialogFunction.torp +4 -6
  227. package/test/Dialog/components/DialogVariable.torp +19 -21
  228. package/test/Disclosure/accessibility.test.ts +30 -0
  229. package/test/Disclosure/components/DisclosureTest.torp +17 -0
  230. package/test/Disclosure/default.test.ts +15 -0
  231. package/test/Disclosure/toggle.test.ts +19 -0
  232. package/test/Menu/accessibility.test.ts +82 -0
  233. package/test/Menu/basic.test.ts +57 -0
  234. package/test/Menu/components/MenuAccessibility.torp +59 -0
  235. package/test/Menu/components/MenuBasic.torp +58 -0
  236. package/test/MenuBar/accessibility.test.ts +86 -0
  237. package/test/MenuBar/basic.test.ts +21 -0
  238. package/test/MenuBar/components/MenuBarAccessibility.torp +34 -0
  239. package/test/MenuBar/components/MenuBarBasic.torp +25 -0
  240. package/test/Notification/accessibility.test.ts +21 -0
  241. package/test/Notification/components/CustomNotification.torp +14 -0
  242. package/test/Notification/components/NotificationAccessibility.torp +14 -0
  243. package/test/Notification/components/NotificationFunction.torp +20 -0
  244. package/test/Notification/components/NotificationVariable.torp +22 -0
  245. package/test/Notification/function.test.ts +25 -0
  246. package/test/Notification/variable.test.ts +25 -0
  247. package/test/Popover/components/CustomDialog.torp +25 -0
  248. package/test/Popover/components/PromptFunction.torp +19 -0
  249. package/test/Popover/components/PromptVariable.torp +38 -0
  250. package/test/Popover/function.test.ts +26 -0
  251. package/test/Popover/variable.test.ts +27 -0
  252. package/tsup.config.ts +12 -4
  253. package/dist/Accordion/index.cjs +0 -53
  254. package/dist/Accordion/index.cjs.map +0 -1
  255. package/dist/Dialog/index.cjs +0 -53
  256. package/dist/Dialog/index.cjs.map +0 -1
  257. package/dist/NavMenu/index.cjs +0 -78
  258. package/dist/NavMenu/index.cjs.map +0 -1
  259. package/dist/Offscreen/index.cjs +0 -33
  260. package/dist/Offscreen/index.cjs.map +0 -1
  261. package/dist/Offscreen/index.js +0 -6
  262. package/dist/Popover/index.cjs +0 -222
  263. package/dist/Popover/index.cjs.map +0 -1
  264. package/dist/TabGroup/index.cjs +0 -53
  265. package/dist/TabGroup/index.cjs.map +0 -1
  266. package/dist/metafile-cjs.json +0 -1
  267. package/dist/motion.cjs +0 -34
  268. package/dist/motion.cjs.map +0 -1
  269. package/dist/motion.d.ts +0 -6
  270. package/dist/motion.js.map +0 -1
  271. package/src/Accordion.ts +0 -7
  272. package/src/Dialog.ts +0 -7
  273. package/src/NavMenu.ts +0 -23
  274. package/src/Offscreen/index.ts +0 -3
  275. package/src/Popover.ts +0 -29
  276. package/src/TabGroup.ts +0 -7
  277. package/src/motion.ts +0 -3
  278. /package/dist/{Offscreen → Breadcrumb}/index.js.map +0 -0
@@ -0,0 +1,265 @@
1
+ import { ReactiveDate } from "@torpor/view";
2
+ import { areDatesEqual, dayNames, monthNames } from "../utils/dateUtils";
3
+ import CalendarDay from "./CalendarDay.torp";
4
+ import { type CalendarContext, type DayState, type CalendarEvent, CalendarContextName } from "./CalendarTypes";
5
+ import ChevronLeft from "../icons/ChevronLeft.torp";
6
+ import ChevronRight from "../icons/ChevronRight.torp";
7
+
8
+ // TODO: Split this out into Calendar, CalendarHeader, CalendarPreviousTrigger, CalendarGrid etc
9
+
10
+ export default function Calendar() {
11
+ $props.startOfWeek ??= 1;
12
+ $props.selectable ??= false;
13
+ $props.value ??= new Date();
14
+ $props.events ??= [];
15
+
16
+ // Ensure the value is a reactive date
17
+ $props.value = new ReactiveDate($unwrap($props.value));
18
+
19
+ // A note on dates:
20
+ // * value is the selected value
21
+ // * activeDate is the date that is active when the user is navigating via keyboard
22
+ // * visibleDate is the date that is currently being displayed in the calendar (really only the month part is important here)
23
+
24
+ let $state = $watch({
25
+ activeDate: $props.value,
26
+ visibleDate: $props.value,
27
+ //get days() {
28
+ // // TODO: $cache
29
+ // return buildDays($state.visibleDate, $props.startOfWeek)
30
+ //},
31
+ });
32
+
33
+ let visibleStartDate = $props.value;
34
+ let visibleEndDate = $props.value;
35
+ let container: HTMLElement;
36
+ let refocus = false;
37
+
38
+ const context: CalendarContext = {
39
+ registerDay
40
+ };
41
+ $context[CalendarContextName] = context;
42
+
43
+ $run(() => {
44
+ $state.days = buildDays($state.visibleDate, $props.startOfWeek)
45
+
46
+ // HACK: Shouldn't need this
47
+ if (refocus) {
48
+ refocus = false;
49
+ const el = container.getElementsByClassName("active")[0];
50
+ if (el && el.children[0] instanceof HTMLElement) {
51
+ el.children[0].focus();
52
+ }
53
+ }
54
+ });
55
+
56
+ function registerDay(date: ReactiveDate, setActive: (value: boolean) => void) {
57
+ const day = $state.days.find((day) => areDatesEqual(day.date, date));
58
+ if (day) {
59
+ day.setActive = setActive;
60
+ }
61
+ }
62
+
63
+ function buildDays(date: ReactiveDate, start: number): DayState[] {
64
+ const newDays = [];
65
+
66
+ // Set the first day to the start of the week before the first day of
67
+ // the month (e.g. Monday the 28th of the previous month)
68
+ visibleStartDate = new Date();
69
+ visibleStartDate.setFullYear(date.getFullYear(), date.getMonth(), 1);
70
+ visibleStartDate.setDate(
71
+ visibleStartDate.getDate() - ((visibleStartDate.getDay() + 7 - start) % 7)
72
+ );
73
+
74
+ // Set the last day to the end of the week after the last day of the
75
+ // month (e.g. Sunday the 6th of the next month)
76
+ visibleEndDate = new Date();
77
+ visibleEndDate.setFullYear(date.getFullYear(), date.getMonth() + 1, 0);
78
+ visibleEndDate.setDate(
79
+ visibleEndDate.getDate() + ((start - 1 - visibleEndDate.getDay() + 7) % 7)
80
+ );
81
+
82
+ const dayDiff =
83
+ Math.round((visibleEndDate.getTime() - visibleStartDate.getTime()) / (1000 * 60 * 60 * 24)) +
84
+ 1;
85
+
86
+ for (let i = 0; i < dayDiff; i++) {
87
+ const dayDate = new ReactiveDate();
88
+ dayDate.setFullYear(
89
+ visibleStartDate.getFullYear(),
90
+ visibleStartDate.getMonth(),
91
+ visibleStartDate.getDate() + i
92
+ );
93
+ dayDate.setHours(0);
94
+ dayDate.setMinutes(0);
95
+ dayDate.setSeconds(0);
96
+ dayDate.setMilliseconds(0);
97
+ const newDay: DayState = {
98
+ date: dayDate,
99
+ muted: dayDate.getMonth() !== date.getMonth(),
100
+ active: areDatesEqual(dayDate, $state.activeDate)
101
+ };
102
+ newDays.push(newDay);
103
+ }
104
+
105
+ return newDays;
106
+ }
107
+
108
+ function setValue(date: Date) {
109
+ $state.value = new ReactiveDate(date);
110
+
111
+ // Also set the active date so that the user knows where they are
112
+ setActiveDate(date);
113
+
114
+ if ($props.onchange) $props.onchange(value);
115
+ }
116
+
117
+ function setActiveDate(date: Date) {
118
+ $state.activeDate = new ReactiveDate(date);
119
+
120
+ // If the selected date is outside the visible date range, move the date range
121
+ if (date < visibleStartDate || date > visibleEndDate) {
122
+ refocus = true;
123
+ $state.visibleDate = new ReactiveDate(date);
124
+ if ($props.onchangedate) $props.onchangedate(date);
125
+ }
126
+
127
+ $state.days.forEach((day) => {
128
+ const active = areDatesEqual(day.date, new Date(date));
129
+ if (day.setActive) {
130
+ day.setActive(active);
131
+ }
132
+ day.active = active;
133
+ });
134
+ }
135
+
136
+ function handleFocus(e: FocusEvent & { currentTarget: EventTarget & HTMLElement }) {
137
+ const el = e.currentTarget.getElementsByClassName("active")[0];
138
+ if (el && el.children[0] instanceof HTMLElement) {
139
+ el.children[0].focus();
140
+ e.preventDefault();
141
+ }
142
+ }
143
+
144
+ function handlePrevMonth(e: Event) {
145
+ e.preventDefault();
146
+ $state.visibleDate.setMonth($state.visibleDate.getMonth() - 1);
147
+ if ($props.onchangedate) $props.onchangedate(visibleDate);
148
+ }
149
+
150
+ function handleNextMonth(e: Event) {
151
+ e.preventDefault();
152
+ $state.visibleDate.setMonth($state.visibleDate.getMonth() + 1);
153
+ if ($props.onchangedate) $props.onchangedate(visibleDate);
154
+ }
155
+
156
+ async function handleDayClick(date: ReactiveDate) {
157
+ if (!$props.selectable) return;
158
+
159
+ setValue(date);
160
+ }
161
+
162
+ function handleKey(e: KeyboardEvent) {
163
+ if (!$props.selectable) return;
164
+
165
+ switch (e.key) {
166
+ case "Enter": {
167
+ e.preventDefault();
168
+ setValue($state.activeDate);
169
+ break;
170
+ }
171
+ case "Esc":
172
+ case "Escape": {
173
+ e.preventDefault();
174
+ if ($props.onclose) $props.onclose();
175
+ break;
176
+ }
177
+ case "Left":
178
+ case "ArrowLeft": {
179
+ e.preventDefault();
180
+ // Move to the date one day before
181
+ const newDate = new Date($state.activeDate.getTime() - 1 * 86400000)
182
+ setActiveDate(newDate);
183
+ break;
184
+ }
185
+ case "Up":
186
+ case "ArrowUp": {
187
+ e.preventDefault();
188
+ // Move to the date seven days before
189
+ const newDate = new Date($state.activeDate.getTime() - 7 * 86400000)
190
+ setActiveDate(newDate);
191
+ break;
192
+ }
193
+ case "Right":
194
+ case "ArrowRight": {
195
+ e.preventDefault();
196
+ // Move to the date one day after
197
+ const newDate = new Date($state.activeDate.getTime() + 1 * 86400000)
198
+ setActiveDate(newDate);
199
+ break;
200
+ }
201
+ case "Down":
202
+ case "ArrowDown": {
203
+ e.preventDefault();
204
+ // Move to the date seven days after
205
+ const newDate = new Date($state.activeDate.getTime() + 7 * 86400000)
206
+ setActiveDate(newDate);
207
+ break;
208
+ }
209
+ }
210
+ }
211
+
212
+ @render {
213
+ <div
214
+ {$props.id}
215
+ class={`torp-calendar ${$props.class ?? ""}`.trim()}
216
+ &ref={container}
217
+ >
218
+ <div class={`torp-calendar-header ${$props.class ?? ""}`.trim()}>
219
+ <button class={`torp-calendar-previous-trigger ${$props.class ?? ""}`.trim()} onclick={handlePrevMonth}>
220
+ <slot name="prev-button">
221
+ <ChevronLeft />
222
+ </slot>
223
+ </button>
224
+ <span class={`torp-calendar-month-name ${$props.class ?? ""}`.trim()}>
225
+ {monthNames[$state.visibleDate.getMonth()] + " " + $state.visibleDate.getFullYear()}
226
+ </span>
227
+ <button class={`torp-calendar-next-trigger ${$props.class ?? ""}`.trim()} onclick={handleNextMonth}>
228
+ <slot name="next-button">
229
+ <ChevronRight />
230
+ </slot>
231
+ </button>
232
+ </div>
233
+ <div
234
+ class={`torp-calendar-grid ${$props.class ?? ""}`.trim()}
235
+ tabindex={$props.selectable ? 0 : -1}
236
+ onfocus={handleFocus}
237
+ onkeydown={handleKey}
238
+ >
239
+ @for (let index of [0, 1, 2, 3, 4, 5, 6]) {
240
+ <span>
241
+ {dayNames[(Math.abs($props.startOfWeek) + index) % 7].substring(0, 2)}
242
+ </span>
243
+ }
244
+ @for (let day of $state.days) {
245
+ @key = day.date;
246
+ <CalendarDay
247
+ {day.date}
248
+ {day.muted}
249
+ {day.active}
250
+ {$props.selectable}
251
+ events={$props.events.filter((ev) => areDatesEqual(new Date(ev.date), day.date))}
252
+ onclick={handleDayClick}
253
+ />
254
+ }
255
+ </div>
256
+ </div>
257
+ }
258
+
259
+ @style {
260
+ .torp-calendar-grid {
261
+ display: grid;
262
+ grid-template-columns: repeat(7, 1fr);
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,67 @@
1
+ import { areDatesEqual } from "../utils/dateUtils";
2
+ import { type CalendarContext, type CalendarEvent, CalendarContextName } from "./CalendarTypes";
3
+
4
+ export default function CalendarDay() {
5
+ $props.date ??= new Date();
6
+ $props.selectable ??= false;
7
+ $props.muted ??= false;
8
+ $props.active ??= false;
9
+ $props.events ??= [];
10
+
11
+ // HACK: can't unwrap in @render
12
+ let date = $unwrap($props.date);
13
+
14
+ // Register this item with the parent Calendar, which will handle toggling active for all days
15
+ const { registerDay } = $context[CalendarContextName] as CalendarContext;
16
+ registerDay($props.date, setActive);
17
+
18
+ // This function is called by the parent Calendar to set this item's active value
19
+ function setActive(value: boolean) {
20
+ //if (active !== value) {
21
+ // active = value;
22
+ // dispatch("activeChange", value);
23
+ //}
24
+ }
25
+
26
+ function isToday() {
27
+ const today = new Date();
28
+ return areDatesEqual(date, today);
29
+ }
30
+
31
+ async function handleClick() {
32
+ //dispatch("click", date);
33
+ if ($props.onclick) $props.onclick($props.date);
34
+ }
35
+
36
+ async function handleKey() {
37
+ //dispatch("click", date);
38
+ if ($props.onkeydown) $props.onkeydown();
39
+ }
40
+
41
+ @render {
42
+ <div
43
+ {$props.id}
44
+ class={[
45
+ `torp-calendar-day ${$props.class ?? ""}`.trim(),
46
+ {
47
+ muted: $props.muted,
48
+ today: isToday(),
49
+ active: $props.selectable && $props.active
50
+ }
51
+ ]}
52
+ >
53
+ @if ($props.selectable) {
54
+ <button type="button" tabindex="-1" onclick={handleClick} onkeydown={handleKey}>
55
+ <span>{date.getDate()}</span>
56
+ </button>
57
+ } else {
58
+ <span>{date.getDate()}</span>
59
+ }
60
+ <ul class={`torp-calendar-day-events ${$props.class ?? ""}`.trim()}>
61
+ @for (ev of $props.events) {
62
+ <li title={ev.content} />
63
+ }
64
+ </ul>
65
+ </div>
66
+ }
67
+ }
@@ -0,0 +1,22 @@
1
+ export const CalendarContextName = "Calendar";
2
+
3
+ export interface CalendarContext {
4
+ // The registerDay function is called from each CalendarDay to register itself with this
5
+ // Calendar. They pass us a setActive method that we can call
6
+ registerDay: (date: Date, setActive: (value: boolean) => void) => void;
7
+ }
8
+
9
+ export interface DayState {
10
+ date: Date;
11
+ muted: boolean;
12
+ state: {
13
+ active: boolean;
14
+ };
15
+ setActive?: (value: boolean) => void;
16
+ }
17
+
18
+ export interface CalendarEvent {
19
+ date: Date;
20
+ content: string;
21
+ color: string;
22
+ }
@@ -0,0 +1,4 @@
1
+ import Calendar from "./Calendar.torp";
2
+ import CalendarDay from "./CalendarDay.torp";
3
+
4
+ export { Calendar, CalendarDay };
@@ -138,7 +138,7 @@ export default function Dialog($props: any) {
138
138
 
139
139
  @render {
140
140
  <div
141
- id={$props.id}
141
+ {$props.id}
142
142
  class={`torp-dialog ${$props.class ?? ""}`.trim()}
143
143
  tabindex="0"
144
144
  role={$props.alert ? "alertdialog" : "dialog"}
@@ -147,10 +147,10 @@ export default function Dialog($props: any) {
147
147
  aria-labelledby={!$props.ariaLabel && $state.headerId ? $state.headerId : undefined}
148
148
  aria-describedby={$state.bodyId ? $state.bodyId : undefined}
149
149
  onkeydown={handleKey}
150
- :onmount={(el) => trapFocus(el, trapFocusOptions)}
150
+ onmount={(el) => trapFocus(el, trapFocusOptions)}
151
151
  &ref={div}
152
152
  >
153
- <:slot />
153
+ <slot />
154
154
  </div>
155
155
  }
156
156
  }
@@ -25,10 +25,10 @@ export default function DialogBody($props: any) {
25
25
 
26
26
  @render {
27
27
  <div
28
- id={$props.id}
28
+ {$props.id}
29
29
  class={`torp-dialog-body ${$props.class ?? ""}`.trim()}
30
30
  >
31
- <:slot />
31
+ <slot />
32
32
  </div>
33
33
  }
34
34
  }
@@ -36,13 +36,13 @@ export default function DialogButton($props: any) {
36
36
  @render {
37
37
  <button
38
38
  type="button"
39
- id={$props.id}
39
+ {$props.id}
40
40
  class={`torp-dialog-button ${$props.class ?? ""}`.trim()}
41
41
  disabled={$props.disabled}
42
42
  onclick={handleClick}
43
43
  &ref={button}
44
44
  >
45
- <:slot />
45
+ <slot />
46
46
  </button>
47
47
  }
48
48
  }
@@ -4,10 +4,10 @@
4
4
  export default function DialogFooter($props: any) {
5
5
  @render {
6
6
  <div
7
- id={$props.id}
7
+ {$props.id}
8
8
  class={`torp-dialog-footer ${$props.class ?? ""}`.trim()}
9
9
  >
10
- <:slot />
10
+ <slot />
11
11
  </div>
12
12
  }
13
13
  }
@@ -25,10 +25,10 @@ export default function DialogHeader($props: any) {
25
25
 
26
26
  @render {
27
27
  <div
28
- id={$props.id}
28
+ {$props.id}
29
29
  class={`torp-dialog-footer ${$props.class ?? ""}`.trim()}
30
30
  >
31
- <:slot />
31
+ <slot />
32
32
  </div>
33
33
  }
34
34
  }
@@ -0,0 +1,69 @@
1
+ import { DisclosureContextName, type DisclosureContext } from "./DisclosureTypes";
2
+
3
+ /**
4
+ * An interactive header that enables users to reveal or hide an associated section of content.
5
+ *
6
+ * See [the WAI ARIA guidelines for Disclosures](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/).
7
+ *
8
+ * The structure of a Disclosure is:
9
+ *
10
+ * ```
11
+ * <Disclosure>
12
+ * <DisclosureHeader>
13
+ * <DisclosureTrigger />
14
+ * </DisclosureHeader>
15
+ * <DisclosureContent />
16
+ * </Disclosure>
17
+ * ```
18
+ *
19
+ * @prop {boolean} [expanded] -- Whether the Disclosure is expanded and its content visible
20
+ * @prop {boolean} [disabled] -- If set to true, the user cannot interact with this Disclosure
21
+ * @prop {(boolean) => void} [ontoggle] -- Raised when this item is opened or closed
22
+ */
23
+ export default function Disclosure($props: any) {
24
+ $props.expanded ??= false;
25
+ $props.disabled ??= false;
26
+
27
+ let $state: DisclosureState = $watch({
28
+ expanded: $props.expanded && $props.expanded !== "false",
29
+ disabled: $props.disabled,
30
+ get dataState() {
31
+ return this.expanded ? "open" : "closed";
32
+ },
33
+ });
34
+
35
+ // Update $state with $props
36
+ $run(() => {
37
+ $state.expanded = $props.expanded && $props.expanded !== "false";
38
+ $state.disabled = $props.disabled;
39
+ });
40
+
41
+ // Put stuff into a DisclosureContext
42
+ const itemContext: DisclosureContext = {
43
+ state: $state,
44
+ };
45
+ $context[DisclosureContextName] = itemContext;
46
+
47
+ // Dispatch the toggle event when the value of expanded changes
48
+ let firstToggle = true;
49
+ $run(() => {
50
+ const ev = $props.ontoggle;
51
+ const expanded = $state.expanded;
52
+ if (firstToggle) {
53
+ firstToggle = false;
54
+ } else if (ev) {
55
+ ev(expanded);
56
+ }
57
+ });
58
+
59
+ @render {
60
+ <div
61
+ {$props.id}
62
+ class={`torp-disclosure-item ${$props.class ?? ""}`.trim()}
63
+ data-state={$state.dataState}
64
+ data-disabled={$state.dataDisabled}
65
+ >
66
+ <slot />
67
+ </div>
68
+ }
69
+ }
@@ -0,0 +1,36 @@
1
+ import getId from "../utils/getId";
2
+ import { DisclosureContextName, type DisclosureContext } from "./DisclosureTypes";
3
+
4
+ /**
5
+ * Content for a Disclosure, which can be revealed or hidden by an associated DisclosureTrigger.
6
+ *
7
+ * @prop {string} [id] -- An ID for the DisclosureContent's div container
8
+ * @prop {boolean} [ariaRegion] -- Whether this content should act as a region. Set this when the content contains heading elements or a nested Disclosure
9
+ */
10
+ export default function DisclosureContent($props: any) {
11
+ $props.id ||= getId();
12
+
13
+ const context = $context[DisclosureContextName] as DisclosureContext;
14
+ if (!context) {
15
+ throw new Error("DisclosureContent must be contained within a Disclosure");
16
+ }
17
+ let $state = $context[DisclosureContextName].state
18
+
19
+ // Set the content ID so that it can be used with aria-controls in DisclosureTrigger
20
+ $state.contentId = $props.id;
21
+
22
+ @render {
23
+ <div
24
+ {$props.id}
25
+ class={`torp-disclosure-content ${$props.class ?? ""}`.trim()}
26
+ role={$props.ariaRegion ? "region" : undefined}
27
+ aria-labelledby={$state.headerId}
28
+ data-state={$state.dataState}
29
+ data-disabled={$state.dataDisabled}
30
+ >
31
+ @if ($state.expanded) {
32
+ <slot />
33
+ }
34
+ </div>
35
+ }
36
+ }
@@ -0,0 +1,34 @@
1
+ import { DisclosureContextName, type DisclosureContext } from "./DisclosureTypes";
2
+
3
+ /**
4
+ * The header for a Disclosure, which contains the DisclosureTrigger.
5
+ *
6
+ * The structure of a DisclosureHeader is:
7
+ *
8
+ * ```
9
+ * <DisclosureHeader>
10
+ * <DisclosureTrigger />
11
+ * </DisclosureHeader>
12
+ * ```
13
+ *
14
+ * @prop {"h1" | "h2" | "h3" | "h4" | "h5" | "h6"} as -- The type of HTML header to use
15
+ */
16
+ export default function DisclosureHeader($props: any) {
17
+ const context = $context[DisclosureContextName] as DisclosureContext;
18
+ if (!context) {
19
+ throw new Error("DisclosureHeader must be contained within a Disclosure");
20
+ }
21
+ let $state = $context[DisclosureContextName].state
22
+
23
+ @render {
24
+ @// TODO: $props.as
25
+ <h4
26
+ {$props.id}
27
+ class={`torp-disclosure-header ${$props.class ?? ""}`.trim()}
28
+ data-state={$state.dataState}
29
+ data-disabled={$state.dataDisabled}
30
+ >
31
+ <slot />
32
+ </h4>
33
+ }
34
+ }
@@ -0,0 +1,36 @@
1
+ import getId from "../utils/getId";
2
+ import { DisclosureContextName, type DisclosureContext } from "./DisclosureTypes";
3
+
4
+ /**
5
+ * A header button for a Disclosure, which can be used to reveal or hide an associated DisclosureContent.
6
+ */
7
+ export default function DisclosureTrigger() {
8
+ $props.id ||= getId();
9
+
10
+ // Get stuff out of the DisclosureContext
11
+ const context = $context[DisclosureContextName] as DisclosureContext;
12
+ if (!context) {
13
+ throw new Error("DisclosureTrigger must be contained within a Disclosure");
14
+ }
15
+ let $state = $context[DisclosureContextName].state;
16
+
17
+ // Set the header ID so that it can be used with aria-labelledby in DisclosureContent
18
+ $state.headerId = $props.id;
19
+
20
+ @render {
21
+ <button
22
+ type="button"
23
+ {$props.id}
24
+ class={`torp-disclosure-trigger ${$props.class ?? ""}`.trim()}
25
+ disabled={$state.disabled || $state.parentDisabled}
26
+ aria-expanded={$state.expanded.toString()}
27
+ aria-disabled={$state.disabled}
28
+ aria-controls={$state.contentId}
29
+ data-state={$state.dataState}
30
+ data-disabled={$state.disabled}
31
+ onclick={() => $state.expanded = !$state.expanded}
32
+ >
33
+ <slot />
34
+ </button>
35
+ }
36
+ }
@@ -0,0 +1,14 @@
1
+ export const DisclosureContextName = "Disclosure";
2
+
3
+ export interface DisclosureContext {
4
+ /** The reactive state for a Disclosure */
5
+ state: DisclosureState;
6
+ }
7
+
8
+ export interface DisclosureState {
9
+ headerId: string;
10
+ contentId: string;
11
+ expanded: boolean;
12
+ disabled: boolean;
13
+ dataState: "open" | "closed";
14
+ }
@@ -0,0 +1,6 @@
1
+ import Disclosure from "./Disclosure.torp";
2
+ import DisclosureContent from "./DisclosureContent.torp";
3
+ import DisclosureHeader from "./DisclosureHeader.torp";
4
+ import DisclosureTrigger from "./DisclosureTrigger.torp";
5
+
6
+ export { Disclosure, DisclosureHeader, DisclosureTrigger, DisclosureContent };
@@ -1,6 +1,6 @@
1
1
 
2
- export default function Offscreen($props: any) {
3
- $props.visible ||= true;
2
+ export default function Drawer($props: any) {
3
+ $props.visible ??= true;
4
4
  $props.position ||= "left";
5
5
 
6
6
  let offscreen: HTMLElement | undefined;
@@ -66,25 +66,16 @@ export default function Offscreen($props: any) {
66
66
  }
67
67
  }
68
68
 
69
- function close() {
70
- $props.visible = false;
71
- if ($props.onclose) {
72
- $props.onclose()
73
- }
74
- }
75
-
76
69
  @render {
77
70
  @if ($props.visible) {
78
- <div onclick={close} >
79
- <div
80
- id={$props.id}
81
- class={`torp-offscreen ${$props.class ?? ""}`.trim()}
82
- style="position: fixed;"
83
- :transition={transitionArgs()}
84
- &ref={offscreen}
85
- >
86
- <:slot />
87
- </div>
71
+ <div
72
+ {$props.id}
73
+ class={`torp-offscreen ${$props.class ?? ""}`.trim()}
74
+ style="position: fixed;"
75
+ transition={transitionArgs()}
76
+ &ref={offscreen}
77
+ >
78
+ <slot />
88
79
  </div>
89
80
  }
90
81
  }
@@ -0,0 +1,3 @@
1
+ import Drawer from "./Drawer.torp";
2
+
3
+ export { Drawer };