@umbra.ui/core 0.1.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 (272) hide show
  1. package/dist/components/controls/Dropdown/types.d.ts +5 -0
  2. package/dist/components/controls/Dropdown/types.d.ts.map +1 -0
  3. package/dist/components/controls/Dropdown/types.js +1 -0
  4. package/dist/components/controls/SegmentedControl/types.d.ts +6 -0
  5. package/dist/components/controls/SegmentedControl/types.d.ts.map +1 -0
  6. package/dist/components/controls/SegmentedControl/types.js +1 -0
  7. package/dist/components/dialogs/Alert/types.d.ts +7 -0
  8. package/dist/components/dialogs/Alert/types.d.ts.map +1 -0
  9. package/dist/components/dialogs/Alert/types.js +1 -0
  10. package/dist/components/dialogs/Toast/types.d.ts +34 -0
  11. package/dist/components/dialogs/Toast/types.d.ts.map +1 -0
  12. package/dist/components/dialogs/Toast/types.js +10 -0
  13. package/dist/components/dialogs/Toast/useToast.d.ts +36 -0
  14. package/dist/components/dialogs/Toast/useToast.d.ts.map +1 -0
  15. package/dist/components/dialogs/Toast/useToast.js +90 -0
  16. package/dist/components/indicators/Tooltip/tooltip.d.ts +3 -0
  17. package/dist/components/indicators/Tooltip/tooltip.d.ts.map +1 -0
  18. package/dist/components/indicators/Tooltip/tooltip.js +33 -0
  19. package/dist/components/indicators/Tooltip/types.d.ts +14 -0
  20. package/dist/components/indicators/Tooltip/types.d.ts.map +1 -0
  21. package/dist/components/indicators/Tooltip/types.js +1 -0
  22. package/dist/components/indicators/Tooltip/useTooltip.d.ts +18 -0
  23. package/dist/components/indicators/Tooltip/useTooltip.d.ts.map +1 -0
  24. package/dist/components/indicators/Tooltip/useTooltip.js +57 -0
  25. package/dist/components/inputs/Tags/tag-bar-styles.d.ts +14 -0
  26. package/dist/components/inputs/Tags/tag-bar-styles.d.ts.map +1 -0
  27. package/dist/components/inputs/Tags/tag-bar-styles.js +313 -0
  28. package/dist/components/inputs/Tags/types.d.ts +93 -0
  29. package/dist/components/inputs/Tags/types.d.ts.map +1 -0
  30. package/dist/components/inputs/Tags/types.js +216 -0
  31. package/dist/components/inputs/search/types.d.ts +9 -0
  32. package/dist/components/inputs/search/types.d.ts.map +1 -0
  33. package/dist/components/inputs/search/types.js +1 -0
  34. package/dist/components/navigation/adaptive/types.d.ts +16 -0
  35. package/dist/components/navigation/adaptive/types.d.ts.map +1 -0
  36. package/dist/components/navigation/adaptive/types.js +1 -0
  37. package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts +27 -0
  38. package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts.map +1 -0
  39. package/dist/components/navigation/adaptive/useAdaptiveLayout.js +40 -0
  40. package/dist/components/navigation/adaptive/useBreakpoints.d.ts +6 -0
  41. package/dist/components/navigation/adaptive/useBreakpoints.d.ts.map +1 -0
  42. package/dist/components/navigation/adaptive/useBreakpoints.js +37 -0
  43. package/dist/components/navigation/adaptive/useContainerMonitor.d.ts +93 -0
  44. package/dist/components/navigation/adaptive/useContainerMonitor.d.ts.map +1 -0
  45. package/dist/components/navigation/adaptive/useContainerMonitor.js +145 -0
  46. package/dist/components/navigation/adaptive/useViewAnimation.d.ts +31 -0
  47. package/dist/components/navigation/adaptive/useViewAnimation.d.ts.map +1 -0
  48. package/dist/components/navigation/adaptive/useViewAnimation.js +591 -0
  49. package/dist/components/navigation/adaptive/useViewResize.d.ts +52 -0
  50. package/dist/components/navigation/adaptive/useViewResize.d.ts.map +1 -0
  51. package/dist/components/navigation/adaptive/useViewResize.js +146 -0
  52. package/dist/components/navigation/navstack/useNavigationStack.d.ts +25 -0
  53. package/dist/components/navigation/navstack/useNavigationStack.d.ts.map +1 -0
  54. package/dist/components/navigation/navstack/useNavigationStack.js +133 -0
  55. package/dist/components/navigation/slideover/useSlideoverController.d.ts +20 -0
  56. package/dist/components/navigation/slideover/useSlideoverController.d.ts.map +1 -0
  57. package/dist/components/navigation/slideover/useSlideoverController.js +267 -0
  58. package/dist/components/navigation/splitview/useSplitViewController.d.ts +20 -0
  59. package/dist/components/navigation/splitview/useSplitViewController.d.ts.map +1 -0
  60. package/dist/components/navigation/splitview/useSplitViewController.js +325 -0
  61. package/dist/components/navigation/tabcontroller/types.d.ts +21 -0
  62. package/dist/components/navigation/tabcontroller/types.d.ts.map +1 -0
  63. package/dist/components/navigation/tabcontroller/types.js +1 -0
  64. package/dist/components/navigation/tabcontroller/useTabController.d.ts +5 -0
  65. package/dist/components/navigation/tabcontroller/useTabController.d.ts.map +1 -0
  66. package/dist/components/navigation/tabcontroller/useTabController.js +10 -0
  67. package/dist/components/navigation/types.d.ts +8 -0
  68. package/dist/components/navigation/types.d.ts.map +1 -0
  69. package/dist/components/navigation/types.js +1 -0
  70. package/dist/components/pickers/CollectionPicker/types.d.ts +11 -0
  71. package/dist/components/pickers/CollectionPicker/types.d.ts.map +1 -0
  72. package/dist/components/pickers/CollectionPicker/types.js +1 -0
  73. package/dist/components/pickers/ColorPicker/colors.d.ts +13 -0
  74. package/dist/components/pickers/ColorPicker/colors.d.ts.map +1 -0
  75. package/dist/components/pickers/ColorPicker/colors.js +266 -0
  76. package/dist/components/pickers/FilePicker/types.d.ts +10 -0
  77. package/dist/components/pickers/FilePicker/types.d.ts.map +1 -0
  78. package/dist/components/pickers/FilePicker/types.js +1 -0
  79. package/dist/index.d.ts +91 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +196 -0
  82. package/dist/theme.d.ts +73 -0
  83. package/dist/theme.d.ts.map +1 -0
  84. package/dist/theme.js +279 -0
  85. package/dist/themes/blank.d.ts +7 -0
  86. package/dist/themes/blank.d.ts.map +1 -0
  87. package/dist/themes/blank.js +543 -0
  88. package/dist/themes/crimson-dark.d.ts +4 -0
  89. package/dist/themes/crimson-dark.d.ts.map +1 -0
  90. package/dist/themes/crimson-dark.js +552 -0
  91. package/dist/themes/cyan-light.d.ts +4 -0
  92. package/dist/themes/cyan-light.d.ts.map +1 -0
  93. package/dist/themes/cyan-light.js +552 -0
  94. package/dist/themes/dark.d.ts +4 -0
  95. package/dist/themes/dark.d.ts.map +1 -0
  96. package/dist/themes/dark.js +551 -0
  97. package/dist/themes/gold-dark.d.ts +4 -0
  98. package/dist/themes/gold-dark.d.ts.map +1 -0
  99. package/dist/themes/gold-dark.js +552 -0
  100. package/dist/themes/grass-dark.d.ts +4 -0
  101. package/dist/themes/grass-dark.d.ts.map +1 -0
  102. package/dist/themes/grass-dark.js +552 -0
  103. package/dist/themes/indigo.d.ts +4 -0
  104. package/dist/themes/indigo.d.ts.map +1 -0
  105. package/dist/themes/indigo.js +552 -0
  106. package/dist/themes/light.d.ts +4 -0
  107. package/dist/themes/light.d.ts.map +1 -0
  108. package/dist/themes/light.js +551 -0
  109. package/dist/themes/orange-dark.d.ts +4 -0
  110. package/dist/themes/orange-dark.d.ts.map +1 -0
  111. package/dist/themes/orange-dark.js +551 -0
  112. package/dist/themes/orange-light.d.ts +4 -0
  113. package/dist/themes/orange-light.d.ts.map +1 -0
  114. package/dist/themes/orange-light.js +551 -0
  115. package/package.json +62 -0
  116. package/src/components/controls/Button/Button.vue +417 -0
  117. package/src/components/controls/Button/README.md +348 -0
  118. package/src/components/controls/Button/theme.css +200 -0
  119. package/src/components/controls/Checkbox/Checkbox.vue +164 -0
  120. package/src/components/controls/Checkbox/README.md +441 -0
  121. package/src/components/controls/Checkbox/theme.css +36 -0
  122. package/src/components/controls/Dropdown/Dropdown.vue +476 -0
  123. package/src/components/controls/Dropdown/README.md +370 -0
  124. package/src/components/controls/Dropdown/theme.css +50 -0
  125. package/src/components/controls/Dropdown/types.ts +6 -0
  126. package/src/components/controls/IconButton/IconButton.vue +267 -0
  127. package/src/components/controls/IconButton/README.md +502 -0
  128. package/src/components/controls/IconButton/theme.css +89 -0
  129. package/src/components/controls/Radio/README.md +591 -0
  130. package/src/components/controls/Radio/Radio.vue +89 -0
  131. package/src/components/controls/Radio/theme.css +14 -0
  132. package/src/components/controls/RangeSlider/README.md +608 -0
  133. package/src/components/controls/RangeSlider/RangeSlider.vue +535 -0
  134. package/src/components/controls/RangeSlider/theme.css +80 -0
  135. package/src/components/controls/SegmentedControl/README.md +587 -0
  136. package/src/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
  137. package/src/components/controls/SegmentedControl/theme.css +60 -0
  138. package/src/components/controls/SegmentedControl/types.ts +5 -0
  139. package/src/components/controls/Slider/README.md +627 -0
  140. package/src/components/controls/Slider/Slider.vue +260 -0
  141. package/src/components/controls/Slider/theme.css +74 -0
  142. package/src/components/controls/Stepper/README.md +601 -0
  143. package/src/components/controls/Stepper/Stepper.vue +103 -0
  144. package/src/components/controls/Stepper/theme.css +53 -0
  145. package/src/components/controls/Switch/README.md +667 -0
  146. package/src/components/controls/Switch/Switch.vue +127 -0
  147. package/src/components/controls/Switch/theme.css +42 -0
  148. package/src/components/dialogs/Alert/Alert.vue +218 -0
  149. package/src/components/dialogs/Alert/README.md +450 -0
  150. package/src/components/dialogs/Alert/theme.css +44 -0
  151. package/src/components/dialogs/Alert/types.ts +11 -0
  152. package/src/components/dialogs/Toast/README.md +522 -0
  153. package/src/components/dialogs/Toast/Toast.vue +296 -0
  154. package/src/components/dialogs/Toast/ToastContainer.vue +330 -0
  155. package/src/components/dialogs/Toast/theme.css +44 -0
  156. package/src/components/dialogs/Toast/types.ts +46 -0
  157. package/src/components/dialogs/Toast/useToast.ts +127 -0
  158. package/src/components/indicators/ProgressBar/ProgressBar.vue +98 -0
  159. package/src/components/indicators/ProgressBar/README.md +744 -0
  160. package/src/components/indicators/ProgressBar/theme.css +36 -0
  161. package/src/components/indicators/Tooltip/README.md +723 -0
  162. package/src/components/indicators/Tooltip/TooltipProvider.vue +142 -0
  163. package/src/components/indicators/Tooltip/theme.css +18 -0
  164. package/src/components/indicators/Tooltip/tooltip.ts +48 -0
  165. package/src/components/indicators/Tooltip/types.ts +15 -0
  166. package/src/components/indicators/Tooltip/useTooltip.ts +71 -0
  167. package/src/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
  168. package/src/components/inputs/AutogrowTextView/README.md +643 -0
  169. package/src/components/inputs/AutogrowTextView/theme.css +28 -0
  170. package/src/components/inputs/InputCard/InputCard.vue +600 -0
  171. package/src/components/inputs/InputCard/README.md +636 -0
  172. package/src/components/inputs/InputEmail/InputEmail.vue +698 -0
  173. package/src/components/inputs/InputEmail/README.md +764 -0
  174. package/src/components/inputs/InputNumber/InputNumber.vue +300 -0
  175. package/src/components/inputs/InputNumber/README.md +749 -0
  176. package/src/components/inputs/InputPhone/InputPhone.vue +645 -0
  177. package/src/components/inputs/InputPhone/README.md +636 -0
  178. package/src/components/inputs/InputSecure/InputSecure.vue +646 -0
  179. package/src/components/inputs/InputSecure/README.md +771 -0
  180. package/src/components/inputs/InputText/InputText.vue +225 -0
  181. package/src/components/inputs/InputText/README.md +844 -0
  182. package/src/components/inputs/OTP/OTP.vue +349 -0
  183. package/src/components/inputs/OTP/README.md +736 -0
  184. package/src/components/inputs/OTP/theme.css +50 -0
  185. package/src/components/inputs/StringCapture/README.md +718 -0
  186. package/src/components/inputs/StringCapture/StringCapture.vue +315 -0
  187. package/src/components/inputs/StringCapture/theme.css +86 -0
  188. package/src/components/inputs/Tags/README.md +897 -0
  189. package/src/components/inputs/Tags/TagBar.vue +793 -0
  190. package/src/components/inputs/Tags/TagCreation.vue +219 -0
  191. package/src/components/inputs/Tags/TagPicker.vue +380 -0
  192. package/src/components/inputs/Tags/tag-bar-styles.ts +354 -0
  193. package/src/components/inputs/Tags/theme.css +121 -0
  194. package/src/components/inputs/Tags/types.ts +346 -0
  195. package/src/components/inputs/search/README.md +759 -0
  196. package/src/components/inputs/search/SearchBar.vue +394 -0
  197. package/src/components/inputs/search/SearchResults.vue +310 -0
  198. package/src/components/inputs/search/theme.css +187 -0
  199. package/src/components/inputs/search/types.ts +8 -0
  200. package/src/components/inputs/theme.css +102 -0
  201. package/src/components/menus/ActionMenu/ActionMenu.vue +383 -0
  202. package/src/components/menus/ActionMenu/README.md +825 -0
  203. package/src/components/menus/ActionMenu/theme.css +93 -0
  204. package/src/components/models/Popover/Popover.vue +551 -0
  205. package/src/components/models/Popover/README.md +885 -0
  206. package/src/components/models/Popover/theme.css +52 -0
  207. package/src/components/models/Sheet/README.md +1159 -0
  208. package/src/components/models/Sheet/Sheet.vue +465 -0
  209. package/src/components/models/Sheet/theme.css +72 -0
  210. package/src/components/models/Sidebar/README.md +1228 -0
  211. package/src/components/models/Sidebar/Sidebar.vue +480 -0
  212. package/src/components/models/Sidebar/theme.css +90 -0
  213. package/src/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
  214. package/src/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
  215. package/src/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
  216. package/src/components/navigation/adaptive/README.md +768 -0
  217. package/src/components/navigation/adaptive/types.ts +19 -0
  218. package/src/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
  219. package/src/components/navigation/adaptive/useBreakpoints.ts +41 -0
  220. package/src/components/navigation/adaptive/useContainerMonitor.ts +214 -0
  221. package/src/components/navigation/adaptive/useViewAnimation.ts +721 -0
  222. package/src/components/navigation/adaptive/useViewResize.ts +211 -0
  223. package/src/components/navigation/navstack/NavigationStack.vue +180 -0
  224. package/src/components/navigation/navstack/README.md +994 -0
  225. package/src/components/navigation/navstack/useNavigationStack.ts +164 -0
  226. package/src/components/navigation/slideover/README.md +1275 -0
  227. package/src/components/navigation/slideover/SlideoverController.vue +287 -0
  228. package/src/components/navigation/slideover/useSlideoverController.ts +320 -0
  229. package/src/components/navigation/splitview/README.md +1115 -0
  230. package/src/components/navigation/splitview/SplitViewController.vue +176 -0
  231. package/src/components/navigation/splitview/useSplitViewController.ts +388 -0
  232. package/src/components/navigation/tabcontroller/README.md +919 -0
  233. package/src/components/navigation/tabcontroller/TabController.vue +307 -0
  234. package/src/components/navigation/tabcontroller/TabItem.vue +57 -0
  235. package/src/components/navigation/tabcontroller/types.ts +24 -0
  236. package/src/components/navigation/tabcontroller/useTabController.ts +18 -0
  237. package/src/components/navigation/theme.css +91 -0
  238. package/src/components/navigation/types.ts +7 -0
  239. package/src/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
  240. package/src/components/pickers/CollectionPicker/README.md +1115 -0
  241. package/src/components/pickers/CollectionPicker/theme.css +14 -0
  242. package/src/components/pickers/CollectionPicker/types.ts +11 -0
  243. package/src/components/pickers/ColorPicker/ColorPicker.vue +376 -0
  244. package/src/components/pickers/ColorPicker/README.md +1439 -0
  245. package/src/components/pickers/ColorPicker/colors.ts +299 -0
  246. package/src/components/pickers/ColorPicker/theme.css +32 -0
  247. package/src/components/pickers/DatePicker/DatePicker.vue +660 -0
  248. package/src/components/pickers/DatePicker/README.md +1195 -0
  249. package/src/components/pickers/DatePicker/theme.css +22 -0
  250. package/src/components/pickers/FilePicker/FilePicker.vue +534 -0
  251. package/src/components/pickers/FilePicker/README.md +1542 -0
  252. package/src/components/pickers/FilePicker/theme.css +48 -0
  253. package/src/components/pickers/FilePicker/types.ts +10 -0
  254. package/src/components/pickers/IconPicker/IconPicker.vue +327 -0
  255. package/src/components/pickers/IconPicker/README.md +1161 -0
  256. package/src/components/pickers/IconPicker/theme.css +28 -0
  257. package/src/components/pickers/theme.css +82 -0
  258. package/src/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
  259. package/src/components/views/MarkdownViewer/README.md +833 -0
  260. package/src/components/views/MarkdownViewer/theme.css +130 -0
  261. package/src/index.ts +263 -0
  262. package/src/theme.ts +378 -0
  263. package/src/themes/crimson-dark.ts +556 -0
  264. package/src/themes/cyan-light.ts +556 -0
  265. package/src/themes/dark.ts +557 -0
  266. package/src/themes/gold-dark.ts +556 -0
  267. package/src/themes/grass-dark.ts +556 -0
  268. package/src/themes/indigo.ts +556 -0
  269. package/src/themes/light.ts +557 -0
  270. package/src/themes/orange-dark.ts +557 -0
  271. package/src/themes/orange-light.ts +557 -0
  272. package/src/vue.d.ts +45 -0
@@ -0,0 +1,660 @@
1
+ <script setup lang="ts">
2
+ import { ChevronRightIcon, CalendarDaysIcon } from "@umbra-ui/icons";
3
+ // - Imports
4
+ import { ref, watch, onMounted, nextTick, computed, onUnmounted } from "vue";
5
+ import {
6
+ offset,
7
+ flip,
8
+ shift,
9
+ size,
10
+ computePosition,
11
+ hide,
12
+ autoUpdate,
13
+ } from "@floating-ui/vue";
14
+ import "./theme.css";
15
+ // - Interfaces
16
+ interface Month {
17
+ title: string;
18
+ days: Day[];
19
+ }
20
+ interface Day {
21
+ key: number;
22
+ day: number;
23
+ inCurrentMonth: boolean;
24
+ year: number;
25
+ month: number;
26
+ date: Date;
27
+ }
28
+ // - Props
29
+ export interface Props {
30
+ date: Date;
31
+ }
32
+ const props = withDefaults(defineProps<Props>(), {
33
+ date: () => new Date(),
34
+ });
35
+ // - Emits
36
+ const emits = defineEmits(["update:date"]);
37
+ // - State Management
38
+ const weekdays = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"];
39
+ const days = ref<Day[]>([]);
40
+ const showAlldays = ref<boolean>(false);
41
+ const months = ref<Month[]>([]);
42
+ const selectedDate = ref<Date>(props.date);
43
+ // - Computed Properties
44
+ watch(
45
+ () => props.date,
46
+ (newValue) => {
47
+ selectedDate.value = newValue;
48
+ }
49
+ );
50
+ const dateString = computed(() => {
51
+ return new Intl.DateTimeFormat("en-US", {
52
+ weekday: "long",
53
+ month: "long",
54
+ day: "numeric",
55
+ }).format(selectedDate.value);
56
+ });
57
+
58
+ const yearString = computed(() => {
59
+ return new Intl.DateTimeFormat("en-US", {
60
+ year: "numeric",
61
+ }).format(selectedDate.value);
62
+ });
63
+ // - Element References
64
+ const showPopover = ref<boolean>(false);
65
+ const button = ref<HTMLElement | null>(null);
66
+ const picker = ref<HTMLElement | null>(null);
67
+ const container = ref<HTMLElement | null>(null);
68
+ const overlay = ref<HTMLElement | null>(null);
69
+
70
+ // - Position tracking
71
+ let cleanupAutoUpdate: (() => void) | null = null;
72
+
73
+ // - Lifecycle
74
+ onMounted(async () => {
75
+ setupCompactCalendar();
76
+ });
77
+
78
+ onUnmounted(() => {
79
+ if (cleanupAutoUpdate) {
80
+ cleanupAutoUpdate();
81
+ }
82
+ });
83
+
84
+ // - Calendar Setup Toggling
85
+ const setupCompactCalendar = () => {
86
+ months.value = [];
87
+ // set up days for compact calendar
88
+ days.value = generateMonth(new Date(props.date)).days;
89
+ };
90
+ const setupFullCalendar = async () => {
91
+ allowScroll.value = false;
92
+ days.value = [];
93
+
94
+ // set up months with the next 5 months
95
+ for (let i = 0; i < 4; i++) {
96
+ months.value.push(
97
+ generateMonth(
98
+ new Date(props.date.getFullYear(), props.date.getMonth() + i, 1)
99
+ )
100
+ );
101
+ }
102
+
103
+ // set up months with the previous five months
104
+ for (let i = 0; i < 4; i++) {
105
+ fetchPreviousMonth();
106
+ }
107
+
108
+ // scroll back to the month in the compact calendar
109
+ await nextTick();
110
+ if (scrollview.value) {
111
+ scrollview.value.scrollTop = 470;
112
+ }
113
+
114
+ // allow the month labels to show when the user scrolls
115
+ setTimeout(() => {
116
+ allowScroll.value = true;
117
+ }, 200);
118
+ };
119
+ const toggleCalendar = async () => {
120
+ showAlldays.value = !showAlldays.value;
121
+
122
+ if (showAlldays.value) {
123
+ setupFullCalendar();
124
+ } else {
125
+ setupCompactCalendar();
126
+ }
127
+ };
128
+ // - Date Calculations
129
+ const fetchNextMonth = () => {
130
+ const month = months.value[months.value.length - 1];
131
+ const day = month.days[Math.floor(month.days.length / 2)];
132
+ const date = new Date(day.year, day.month, day.day);
133
+ date.setMonth(date.getMonth() + 1);
134
+ months.value.push(generateMonth(date));
135
+ };
136
+ const fetchPreviousMonth = () => {
137
+ const month = months.value[0];
138
+ const day = month.days[Math.floor(month.days.length / 2)];
139
+ const date = new Date(day.year, day.month, day.day);
140
+ date.setMonth(date.getMonth() - 1);
141
+ months.value.unshift(generateMonth(date));
142
+ };
143
+ function generateMonth(date: Date): Month {
144
+ const firstDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
145
+ const daysInMonth = new Date(
146
+ date.getFullYear(),
147
+ date.getMonth() + 1,
148
+ 0
149
+ ).getDate();
150
+
151
+ // Calculate the offset based on the day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
152
+ let startDay = firstDayOfMonth.getDay(); // Adjusted startDay calculation
153
+
154
+ // If the startDay is 0 (Sunday), set it to 7 for consistency
155
+ startDay = startDay === 0 ? 7 : startDay;
156
+
157
+ const generatedDays: Day[] = [];
158
+
159
+ const year = date.getFullYear();
160
+ const month = date.getMonth();
161
+
162
+ for (let i = 1; i <= daysInMonth; i++) {
163
+ const currentDate = new Date(year, month, i);
164
+
165
+ let inCurrentMonth = true;
166
+ generatedDays.push({
167
+ key: i, // Key starts from 1
168
+ day: i,
169
+ inCurrentMonth,
170
+ year,
171
+ month,
172
+ date: currentDate, // Set the newly added date field
173
+ });
174
+ }
175
+
176
+ // Fill in days from the previous month if needed to complete the week
177
+ const previousMonthLastDay = new Date(year, month, 0).getDate();
178
+ const previousMonthDaysNeeded = startDay - 1;
179
+ for (
180
+ let i = previousMonthLastDay - previousMonthDaysNeeded + 1;
181
+ i <= previousMonthLastDay;
182
+ i++
183
+ ) {
184
+ const currentDate = new Date(year, month - 1, i); // Calculate date for the previous month
185
+ generatedDays.unshift({
186
+ key: i,
187
+ day: i,
188
+ inCurrentMonth: false,
189
+ year: month === 0 ? year - 1 : year,
190
+ month: month === 0 ? 11 : month - 1,
191
+ date: currentDate, // Set the newly added date field
192
+ });
193
+ }
194
+
195
+ // Fill in days from the next month if needed to complete the grid
196
+ const remainingDays = 35 - generatedDays.length;
197
+ for (let i = 1; i <= remainingDays; i++) {
198
+ const nextMonthDate = new Date(year, month + 1, i); // Calculate date for the next month
199
+ generatedDays.push({
200
+ key: i,
201
+ day: i,
202
+ inCurrentMonth: false,
203
+ year: month === 11 ? year + 1 : year,
204
+ month: month === 11 ? 0 : month + 1,
205
+ date: nextMonthDate, // Set the newly added date field
206
+ });
207
+ }
208
+
209
+ const monthTitle = new Intl.DateTimeFormat("en-US", {
210
+ month: "long",
211
+ year: "numeric",
212
+ }).format(date);
213
+ return {
214
+ title: monthTitle,
215
+ days: generatedDays,
216
+ };
217
+ }
218
+
219
+ // - Handle Day Cell Appearance
220
+ function drawBorderTop(day: Day): boolean {
221
+ if (day.inCurrentMonth && day.day <= 7) {
222
+ return true;
223
+ }
224
+ return false;
225
+ }
226
+ function drawBorderLeft(day: Day, index: number): boolean {
227
+ if (index === 0) {
228
+ return false;
229
+ }
230
+ if (day.inCurrentMonth && day.day === 1) {
231
+ return true;
232
+ }
233
+ return false;
234
+ }
235
+ function setMarginTopOffset(month: Month, index: number): boolean {
236
+ if (index === 0) {
237
+ return false;
238
+ }
239
+ if (month.days[0].inCurrentMonth) {
240
+ return false;
241
+ }
242
+ return true;
243
+ }
244
+
245
+ // - Date Selection
246
+ function isDaySelected(day: Day): boolean {
247
+ return selectedDate.value.getTime() === day.date.getTime();
248
+ }
249
+
250
+ function select(date: Date) {
251
+ selectedDate.value = date;
252
+ emits("update:date", date);
253
+ }
254
+
255
+ // - Handle Scrolling
256
+ const scrollview = ref<HTMLElement | null>(null);
257
+ const fetchInProgress = ref<boolean>(false);
258
+ const allowScroll = ref<boolean>(false);
259
+ const isScrolling = ref<boolean>(false);
260
+ let scrollTimeout: NodeJS.Timeout | undefined;
261
+ const handleScroll = () => {
262
+ // Set isScrolling to true when scrolling starts
263
+ if (allowScroll.value) {
264
+ isScrolling.value = true;
265
+ }
266
+
267
+ // Clear the timeout if it exists
268
+ if (scrollTimeout !== undefined) {
269
+ clearTimeout(scrollTimeout);
270
+ }
271
+
272
+ // Set a timeout to reset isScrolling after a short delay (e.g., 200ms)
273
+ scrollTimeout = setTimeout(() => {
274
+ isScrolling.value = false;
275
+ }, 400); // Adjust the delay as needed
276
+
277
+ if (!scrollview.value) return;
278
+
279
+ const percentage =
280
+ (scrollview.value.scrollTop /
281
+ (scrollview.value.scrollHeight - scrollview.value.clientHeight)) *
282
+ 100;
283
+ if (percentage <= 25 && !fetchInProgress.value) {
284
+ fetchInProgress.value = true;
285
+ fetchPreviousMonth();
286
+ fetchInProgress.value = false;
287
+ }
288
+ // fetch more data to show if the user has scrolled more than 75% down
289
+ else if (percentage > 75 && !fetchInProgress.value) {
290
+ fetchInProgress.value = true;
291
+ fetchNextMonth();
292
+ fetchInProgress.value = false;
293
+ }
294
+ };
295
+
296
+ // - Popover Management
297
+ const togglePopover = () => {
298
+ showPopover.value = !showPopover.value;
299
+ if (showPopover.value) {
300
+ nextTick(() => {
301
+ updatePopoverPosition();
302
+ });
303
+ } else {
304
+ // revert back to the small calendar
305
+ showAlldays.value = false;
306
+ setupCompactCalendar();
307
+
308
+ // Clean up auto-update
309
+ if (cleanupAutoUpdate) {
310
+ cleanupAutoUpdate();
311
+ cleanupAutoUpdate = null;
312
+ }
313
+ }
314
+ };
315
+
316
+ const updatePopoverPosition = async () => {
317
+ if (!button.value || !picker.value) return;
318
+
319
+ // Wait for the DOM to be updated
320
+ await nextTick();
321
+
322
+ // Clean up any existing auto-update
323
+ if (cleanupAutoUpdate) {
324
+ cleanupAutoUpdate();
325
+ }
326
+
327
+ // Set up auto-update to track position changes
328
+ cleanupAutoUpdate = autoUpdate(button.value, picker.value, () => {
329
+ computePosition(button.value!, picker.value!, {
330
+ placement: "bottom-start",
331
+ middleware: [
332
+ offset(4), // px between anchor and popover
333
+ flip(), // switch side of space becomes too narrow
334
+ shift(),
335
+ size({
336
+ padding: 20,
337
+ apply({
338
+ availableWidth,
339
+ availableHeight,
340
+ elements,
341
+ }: {
342
+ availableWidth: number;
343
+ availableHeight: number;
344
+ elements: {
345
+ floating: {
346
+ style: {
347
+ maxWidth: string;
348
+ maxHeight: string;
349
+ };
350
+ };
351
+ };
352
+ }) {
353
+ // Change styles, e.g.
354
+ Object.assign(elements.floating.style, {
355
+ maxWidth: `${availableWidth}px`,
356
+ maxHeight: `${availableHeight}px`,
357
+ });
358
+ },
359
+ }),
360
+ ],
361
+ }).then(({ x, y }) => {
362
+ if (picker.value) {
363
+ Object.assign(picker.value.style, {
364
+ left: `${x}px`,
365
+ top: `${y}px`,
366
+ });
367
+ }
368
+ });
369
+ });
370
+ };
371
+
372
+ const handleOverlayClick = () => {
373
+ togglePopover();
374
+ };
375
+ </script>
376
+
377
+ <template>
378
+ <div :class="$style.container" ref="container">
379
+ <div
380
+ :class="[
381
+ $style.button,
382
+ showPopover ? $style.button_selected : $style.button_normal,
383
+ ]"
384
+ @click="togglePopover"
385
+ ref="button"
386
+ >
387
+ <CalendarDaysIcon :size="16" />
388
+ <p :class="['callout', $style.button_label]">{{ dateString }}</p>
389
+ <p :class="['callout', $style.button_sublabel]">{{ yearString }}</p>
390
+ </div>
391
+
392
+ <!-- Teleport the overlay and picker to body -->
393
+ <Teleport to="body">
394
+ <div
395
+ v-if="showPopover"
396
+ :class="$style.overlay"
397
+ ref="overlay"
398
+ @click="handleOverlayClick"
399
+ ></div>
400
+ <div v-if="showPopover" :class="$style.picker" ref="picker">
401
+ <div :class="$style.weekdays">
402
+ <p v-for="weekday in weekdays" :key="weekday">{{ weekday }}</p>
403
+ </div>
404
+ <div v-if="!showAlldays" :class="[$style.days]">
405
+ <p
406
+ v-for="day in days"
407
+ :key="`compact-${day.year}-${day.month}-${day.day}`"
408
+ :class="[
409
+ 'callout',
410
+ $style.day,
411
+ isDaySelected(day) ? $style.day_selected : '',
412
+ ]"
413
+ :style="{ opacity: day.inCurrentMonth ? 1 : 0.5 }"
414
+ @click="select(day.date)"
415
+ >
416
+ {{ day.day }}
417
+ </p>
418
+ <ChevronRightIcon
419
+ :class="[$style.day, $style.more_dates_button]"
420
+ @click="toggleCalendar"
421
+ />
422
+ </div>
423
+ <div
424
+ v-else
425
+ :class="$style.months"
426
+ ref="scrollview"
427
+ @scroll="handleScroll"
428
+ >
429
+ <div
430
+ v-for="(month, index) in months"
431
+ :key="month.title"
432
+ :class="[
433
+ $style.month,
434
+ setMarginTopOffset(month, index) ? $style.margin_top_offset : '',
435
+ ]"
436
+ >
437
+ <div
438
+ :class="$style.month_days"
439
+ :style="{ opacity: isScrolling ? 0.15 : 1 }"
440
+ >
441
+ <p
442
+ v-for="(day, index) in month.days"
443
+ :key="`full-${day.year}-${day.month}-${day.day}`"
444
+ :class="[
445
+ 'callout',
446
+ $style.day,
447
+ drawBorderTop(day) ? $style.border_top : '',
448
+ drawBorderLeft(day, index) ? $style.border_left : '',
449
+ isDaySelected(day) ? $style.day_selected : '',
450
+ ]"
451
+ :style="{
452
+ opacity: day.inCurrentMonth ? 1 : 0,
453
+ pointerEvents: day.inCurrentMonth ? 'auto' : 'none',
454
+ zIndex: day.inCurrentMonth ? 1 : 0,
455
+ }"
456
+ @click="select(day.date)"
457
+ >
458
+ {{ day.day }}
459
+ </p>
460
+ </div>
461
+ <div
462
+ :class="$style.month_header"
463
+ :style="{ opacity: isScrolling ? 1 : 0 }"
464
+ >
465
+ <p class="headline">{{ month.title }}</p>
466
+ </div>
467
+ </div>
468
+ </div>
469
+ </div>
470
+ </Teleport>
471
+ </div>
472
+ </template>
473
+
474
+ <style module>
475
+ .container {
476
+ display: flex;
477
+ flex-direction: column;
478
+ gap: 0.235rem;
479
+ align-items: start;
480
+ }
481
+ .button {
482
+ padding-top: 0.588rem;
483
+ padding-bottom: 0.588rem;
484
+ border-radius: 0.353rem;
485
+ cursor: default;
486
+ user-select: none;
487
+ display: flex;
488
+ align-items: center;
489
+ gap: 0.588rem;
490
+ transition: padding-left 0.3s, padding-right 0.3s, background-color 0.3s,
491
+ box-shadow 0.3s;
492
+ }
493
+ .button_normal {
494
+ background-color: var(--picker-button-bg);
495
+ border: var(--picker-button-border);
496
+ }
497
+ .button_normal:hover {
498
+ background-color: var(--picker-button-hover-bg);
499
+ padding-left: 0.588rem;
500
+ padding-right: 0.588rem;
501
+ box-shadow: 0px 1px 0px 0px var(--picker-button-hover-shadow),
502
+ inset 0px 1px 0px 0px var(--picker-button-hover-inset-shadow);
503
+ border: var(--picker-button-hover-border);
504
+ }
505
+ .button_selected {
506
+ background-color: var(--picker-button-selected-bg);
507
+ padding-left: 0.588rem;
508
+ padding-right: 0.588rem;
509
+ border: var(--picker-button-hover-border);
510
+ }
511
+ .button_label {
512
+ font-weight: 700 !important;
513
+ font-variation-settings: "wght" 700 !important;
514
+ }
515
+ .button_sublabel {
516
+ opacity: 0.5;
517
+ }
518
+ .picker {
519
+ position: absolute;
520
+ top: 0;
521
+ left: 0;
522
+ background-color: var(--picker-picker-bg);
523
+ border-radius: 0.353rem;
524
+ overflow: hidden;
525
+ box-shadow: 0px 1px 0px 0px var(--picker-picker-shadow),
526
+ inset 0px 1px 0px 0px var(--picker-picker-inset-shadow);
527
+ border: var(--picker-picker-border);
528
+ z-index: 1000;
529
+ }
530
+ .weekdays {
531
+ display: grid;
532
+ grid-template-columns: repeat(7, 1fr);
533
+ align-items: center;
534
+ padding-left: 0.294rem;
535
+ padding-right: 0.294rem;
536
+ min-height: 2rem;
537
+ background-color: var(--picker-bg-subtle);
538
+ border-bottom: 1px solid var(--picker-border-light);
539
+ }
540
+ .weekdays p {
541
+ opacity: var(--datepicker-weekdays-opacity);
542
+ font-size: 0.765rem;
543
+ min-width: 2.588rem;
544
+ text-align: center;
545
+ color: var(--picker-text-secondary);
546
+ }
547
+ .days {
548
+ display: grid;
549
+ grid-template-columns: repeat(7, 1fr);
550
+ gap: 0;
551
+ align-items: center;
552
+ max-height: 32rem;
553
+ overflow: auto;
554
+ color: var(--picker-text-primary);
555
+ }
556
+
557
+ .days :nth-last-child(2) {
558
+ display: none;
559
+ }
560
+
561
+ .months {
562
+ display: flex;
563
+ flex-direction: column;
564
+ max-height: 32rem;
565
+ overflow: auto;
566
+ }
567
+ .months::-webkit-scrollbar {
568
+ display: none;
569
+ }
570
+ .month {
571
+ display: grid;
572
+ grid-template-columns: 1fr;
573
+ grid-template-rows: 1fr;
574
+ grid-template-areas: "content";
575
+ }
576
+ .month_header {
577
+ padding-left: 0.706rem;
578
+ padding-right: 0.706rem;
579
+ padding-top: 0.5rem;
580
+ padding-bottom: 0.5rem;
581
+ grid-area: content;
582
+ display: flex;
583
+ align-items: center;
584
+ justify-content: center;
585
+ transition: opacity 0.3s ease;
586
+ user-select: none;
587
+ cursor: default;
588
+ pointer-events: none;
589
+ color: var(--picker-text-primary);
590
+ }
591
+
592
+ .month_days {
593
+ display: grid;
594
+ grid-template-columns: repeat(7, 1fr);
595
+ gap: 0;
596
+ align-items: center;
597
+ max-height: 32rem;
598
+ overflow: auto;
599
+ grid-area: content;
600
+ transition: opacity 0.3s ease;
601
+ color: var(--picker-text-primary);
602
+ }
603
+
604
+ .day {
605
+ min-width: 2.588rem;
606
+ height: 2rem;
607
+ text-align: center;
608
+ display: flex;
609
+ align-items: center;
610
+ justify-content: center;
611
+ user-select: none;
612
+ cursor: default;
613
+ border-radius: 0.235rem;
614
+ padding: 0.294rem;
615
+ }
616
+ .day:hover {
617
+ background-color: var(--picker-bg-hover);
618
+ }
619
+ .day_selected {
620
+ background-color: var(--datepicker-day-selected-bg);
621
+ border-radius: 999px;
622
+ border: 1px solid var(--datepicker-day-selected-border);
623
+ }
624
+ .day_selected:hover {
625
+ background-color: var(--datepicker-day-selected-bg);
626
+ }
627
+ .border_top {
628
+ border-top: 1px solid var(--picker-border-light);
629
+ border-top-right-radius: 0;
630
+ border-top-left-radius: 0;
631
+ }
632
+ .border_left {
633
+ border-left: 1px solid var(--picker-border-light);
634
+ border-bottom-left-radius: 0;
635
+ }
636
+ .margin_top_offset {
637
+ margin-top: -2rem;
638
+ }
639
+
640
+ .more_dates_button {
641
+ width: 2.588rem;
642
+ height: 2rem;
643
+ padding: 0.471rem;
644
+ background-color: var(--picker-selection-bar-bg);
645
+ }
646
+ .more_dates_button:hover {
647
+ background-color: var(--picker-bg-hover);
648
+ }
649
+
650
+ .overlay {
651
+ position: fixed;
652
+ top: 0;
653
+ left: 0;
654
+ width: 100%;
655
+ height: 100%;
656
+ background-color: var(--picker-overlay-bg);
657
+ opacity: 0;
658
+ z-index: 999;
659
+ }
660
+ </style>