@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,1195 @@
1
+ # DatePicker
2
+
3
+ A comprehensive date picker component built with Vue 3 Composition API and TypeScript. The DatePicker provides an intuitive interface for selecting dates with support for both compact and full calendar views, smart positioning using Floating UI, and smooth scrolling through months.
4
+
5
+ ## Installation/Import
6
+
7
+ ```typescript
8
+ import { DatePicker } from "@umbra-ui/core";
9
+ ```
10
+
11
+ **Dependencies:**
12
+
13
+ - Vue 3.x
14
+ - @floating-ui/vue (for positioning)
15
+ - @umbra-ui/icons (for calendar and chevron icons)
16
+
17
+ ## Basic Usage
18
+
19
+ ```vue
20
+ <script setup lang="ts">
21
+ import { ref } from "vue";
22
+ import { DatePicker } from "@umbra-ui/core";
23
+
24
+ const selectedDate = ref(new Date());
25
+
26
+ const handleDateChange = (date: Date) => {
27
+ console.log("Selected date:", date);
28
+ console.log("Formatted date:", date.toLocaleDateString());
29
+ };
30
+ </script>
31
+
32
+ <template>
33
+ <div class="app">
34
+ <h2>Select a Date</h2>
35
+
36
+ <DatePicker v-model:date="selectedDate" @update:date="handleDateChange" />
37
+
38
+ <div v-if="selectedDate" class="date-info">
39
+ <p>Selected: {{ selectedDate.toLocaleDateString() }}</p>
40
+ <p>ISO String: {{ selectedDate.toISOString() }}</p>
41
+ </div>
42
+ </div>
43
+ </template>
44
+
45
+ <style module>
46
+ .app {
47
+ padding: 24px;
48
+ max-width: 400px;
49
+ }
50
+
51
+ .date-info {
52
+ margin-top: 20px;
53
+ padding: 16px;
54
+ background: #f8f9fa;
55
+ border-radius: 8px;
56
+ }
57
+
58
+ .date-info p {
59
+ margin: 0 0 8px 0;
60
+ color: #495057;
61
+ }
62
+
63
+ .date-info p:last-child {
64
+ margin-bottom: 0;
65
+ }
66
+ </style>
67
+ ```
68
+
69
+ ## Props
70
+
71
+ | Prop Name | Type | Required | Default | Description |
72
+ | --------- | ------ | -------- | ------------ | ----------------------- |
73
+ | `date` | `Date` | Yes | `new Date()` | Currently selected date |
74
+
75
+ ## Events
76
+
77
+ | Event Name | Payload Type | Description |
78
+ | ------------- | ------------ | ------------------------------- |
79
+ | `update:date` | `Date` | Emitted when a date is selected |
80
+
81
+ ## CSS Customization
82
+
83
+ ### Layout Variables
84
+
85
+ ```css
86
+ .date-picker {
87
+ --picker-button-bg: #ffffff;
88
+ --picker-button-border: 1px solid #e9ecef;
89
+ --picker-button-hover-bg: #f8f9fa;
90
+ --picker-button-hover-border: 1px solid #dee2e6;
91
+ --picker-button-hover-shadow: rgba(0, 0, 0, 0.1);
92
+ --picker-button-hover-inset-shadow: rgba(255, 255, 255, 0.1);
93
+ --picker-button-selected-bg: #e3f2fd;
94
+ --picker-picker-bg: #ffffff;
95
+ --picker-picker-border: 1px solid #e9ecef;
96
+ --picker-picker-shadow: rgba(0, 0, 0, 0.1);
97
+ --picker-picker-inset-shadow: rgba(255, 255, 255, 0.1);
98
+ --picker-bg-subtle: #f8f9fa;
99
+ --picker-border-light: #e9ecef;
100
+ --picker-text-primary: #212529;
101
+ --picker-text-secondary: #6c757d;
102
+ --picker-bg-hover: #f8f9fa;
103
+ --picker-overlay-bg: rgba(0, 0, 0, 0.1);
104
+ --datepicker-weekdays-opacity: 0.5;
105
+ --datepicker-day-selected-bg: #acd8fc;
106
+ --datepicker-day-selected-border: #5eb1ef;
107
+ }
108
+ ```
109
+
110
+ ### Container Styling
111
+
112
+ ```css
113
+ .date-picker {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: 0.235rem;
117
+ align-items: start;
118
+ }
119
+ ```
120
+
121
+ ### Button Styling
122
+
123
+ ```css
124
+ .date-picker .button {
125
+ padding: 0.588rem;
126
+ border-radius: 0.353rem;
127
+ cursor: default;
128
+ user-select: none;
129
+ display: flex;
130
+ align-items: center;
131
+ gap: 0.588rem;
132
+ transition: all 0.3s ease;
133
+ background-color: var(--picker-button-bg);
134
+ border: var(--picker-button-border);
135
+ }
136
+ ```
137
+
138
+ ### Calendar Styling
139
+
140
+ ```css
141
+ .date-picker .picker {
142
+ position: absolute;
143
+ background-color: var(--picker-picker-bg);
144
+ border-radius: 0.353rem;
145
+ overflow: hidden;
146
+ box-shadow: var(--picker-picker-shadow);
147
+ border: var(--picker-picker-border);
148
+ z-index: 1000;
149
+ }
150
+
151
+ .date-picker .days {
152
+ display: grid;
153
+ grid-template-columns: repeat(7, 1fr);
154
+ gap: 0;
155
+ align-items: center;
156
+ max-height: 32rem;
157
+ overflow: auto;
158
+ color: var(--picker-text-primary);
159
+ }
160
+ ```
161
+
162
+ ## Examples
163
+
164
+ ### Event Date Selector
165
+
166
+ ```vue
167
+ <script setup lang="ts">
168
+ import { ref } from "vue";
169
+ import { DatePicker } from "@umbra-ui/core";
170
+
171
+ const eventDate = ref(new Date());
172
+ const eventTitle = ref("");
173
+
174
+ const handleDateChange = (date: Date) => {
175
+ console.log("Event date changed to:", date);
176
+ };
177
+
178
+ const isDateInPast = (date: Date) => {
179
+ const today = new Date();
180
+ today.setHours(0, 0, 0, 0);
181
+ return date < today;
182
+ };
183
+ </script>
184
+
185
+ <template>
186
+ <div class="event-planner">
187
+ <div class="planner-header">
188
+ <h3>Event Planner</h3>
189
+ <p>Schedule your next event</p>
190
+ </div>
191
+
192
+ <div class="form-section">
193
+ <label class="form-label">Event Title</label>
194
+ <input
195
+ v-model="eventTitle"
196
+ type="text"
197
+ placeholder="Enter event title..."
198
+ class="form-input"
199
+ />
200
+ </div>
201
+
202
+ <div class="form-section">
203
+ <label class="form-label">Event Date</label>
204
+ <DatePicker v-model:date="eventDate" @update:date="handleDateChange" />
205
+
206
+ <div v-if="isDateInPast(eventDate)" class="warning">
207
+ ⚠️ This date is in the past
208
+ </div>
209
+ </div>
210
+
211
+ <div class="event-preview">
212
+ <h4>Event Preview</h4>
213
+ <div class="preview-card">
214
+ <h5>{{ eventTitle || "Untitled Event" }}</h5>
215
+ <p>
216
+ {{
217
+ eventDate.toLocaleDateString("en-US", {
218
+ weekday: "long",
219
+ year: "numeric",
220
+ month: "long",
221
+ day: "numeric",
222
+ })
223
+ }}
224
+ </p>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </template>
229
+
230
+ <style module>
231
+ .event-planner {
232
+ padding: 24px;
233
+ max-width: 500px;
234
+ background: #ffffff;
235
+ border-radius: 12px;
236
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
237
+ }
238
+
239
+ .planner-header {
240
+ margin-bottom: 32px;
241
+ }
242
+
243
+ .planner-header h3 {
244
+ font-size: 24px;
245
+ font-weight: 600;
246
+ color: #1a202c;
247
+ margin: 0 0 8px 0;
248
+ }
249
+
250
+ .planner-header p {
251
+ color: #718096;
252
+ margin: 0;
253
+ }
254
+
255
+ .form-section {
256
+ margin-bottom: 24px;
257
+ }
258
+
259
+ .form-label {
260
+ display: block;
261
+ font-size: 14px;
262
+ font-weight: 500;
263
+ color: #374151;
264
+ margin-bottom: 8px;
265
+ }
266
+
267
+ .form-input {
268
+ width: 100%;
269
+ padding: 12px;
270
+ border: 1px solid #d1d5db;
271
+ border-radius: 6px;
272
+ font-size: 14px;
273
+ transition: border-color 0.2s ease;
274
+ }
275
+
276
+ .form-input:focus {
277
+ outline: none;
278
+ border-color: #3b82f6;
279
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
280
+ }
281
+
282
+ .warning {
283
+ margin-top: 8px;
284
+ padding: 8px 12px;
285
+ background: #fef3c7;
286
+ color: #92400e;
287
+ border-radius: 4px;
288
+ font-size: 12px;
289
+ font-weight: 500;
290
+ }
291
+
292
+ .event-preview {
293
+ margin-top: 32px;
294
+ }
295
+
296
+ .event-preview h4 {
297
+ font-size: 18px;
298
+ font-weight: 500;
299
+ color: #2d3748;
300
+ margin: 0 0 16px 0;
301
+ }
302
+
303
+ .preview-card {
304
+ padding: 16px;
305
+ background: #f7fafc;
306
+ border-radius: 8px;
307
+ border-left: 4px solid #3b82f6;
308
+ }
309
+
310
+ .preview-card h5 {
311
+ font-size: 16px;
312
+ font-weight: 600;
313
+ color: #2d3748;
314
+ margin: 0 0 8px 0;
315
+ }
316
+
317
+ .preview-card p {
318
+ color: #4a5568;
319
+ margin: 0;
320
+ }
321
+ </style>
322
+ ```
323
+
324
+ ### Appointment Scheduler
325
+
326
+ ```vue
327
+ <script setup lang="ts">
328
+ import { ref, computed } from "vue";
329
+ import { DatePicker } from "@umbra-ui/core";
330
+
331
+ const appointmentDate = ref(new Date());
332
+ const appointmentTime = ref("09:00");
333
+ const appointmentType = ref("consultation");
334
+
335
+ const availableTimes = [
336
+ "09:00",
337
+ "09:30",
338
+ "10:00",
339
+ "10:30",
340
+ "11:00",
341
+ "11:30",
342
+ "14:00",
343
+ "14:30",
344
+ "15:00",
345
+ "15:30",
346
+ "16:00",
347
+ "16:30",
348
+ ];
349
+
350
+ const isWeekend = computed(() => {
351
+ const day = appointmentDate.value.getDay();
352
+ return day === 0 || day === 6; // Sunday or Saturday
353
+ });
354
+
355
+ const isDateInPast = computed(() => {
356
+ const today = new Date();
357
+ today.setHours(0, 0, 0, 0);
358
+ return appointmentDate.value < today;
359
+ });
360
+
361
+ const canSchedule = computed(() => {
362
+ return !isWeekend.value && !isDateInPast.value && appointmentTime.value;
363
+ });
364
+
365
+ const handleDateChange = (date: Date) => {
366
+ console.log("Appointment date changed to:", date);
367
+ };
368
+ </script>
369
+
370
+ <template>
371
+ <div class="appointment-scheduler">
372
+ <div class="scheduler-header">
373
+ <h3>Schedule Appointment</h3>
374
+ <p>Book your appointment with us</p>
375
+ </div>
376
+
377
+ <div class="form-section">
378
+ <label class="form-label">Appointment Type</label>
379
+ <select v-model="appointmentType" class="form-select">
380
+ <option value="consultation">Consultation</option>
381
+ <option value="follow-up">Follow-up</option>
382
+ <option value="checkup">Checkup</option>
383
+ <option value="emergency">Emergency</option>
384
+ </select>
385
+ </div>
386
+
387
+ <div class="form-section">
388
+ <label class="form-label">Date</label>
389
+ <DatePicker
390
+ v-model:date="appointmentDate"
391
+ @update:date="handleDateChange"
392
+ />
393
+
394
+ <div v-if="isWeekend" class="error">
395
+ ❌ We don't schedule appointments on weekends
396
+ </div>
397
+ <div v-else-if="isDateInPast" class="error">
398
+ ❌ Cannot schedule appointments in the past
399
+ </div>
400
+ </div>
401
+
402
+ <div class="form-section">
403
+ <label class="form-label">Time</label>
404
+ <div class="time-grid">
405
+ <button
406
+ v-for="time in availableTimes"
407
+ :key="time"
408
+ :class="['time-btn', { active: appointmentTime === time }]"
409
+ @click="appointmentTime = time"
410
+ >
411
+ {{ time }}
412
+ </button>
413
+ </div>
414
+ </div>
415
+
416
+ <div class="appointment-summary">
417
+ <h4>Appointment Summary</h4>
418
+ <div class="summary-card">
419
+ <div class="summary-item">
420
+ <span class="label">Type:</span>
421
+ <span class="value">{{ appointmentType }}</span>
422
+ </div>
423
+ <div class="summary-item">
424
+ <span class="label">Date:</span>
425
+ <span class="value">{{ appointmentDate.toLocaleDateString() }}</span>
426
+ </div>
427
+ <div class="summary-item">
428
+ <span class="label">Time:</span>
429
+ <span class="value">{{ appointmentTime }}</span>
430
+ </div>
431
+ </div>
432
+
433
+ <button :disabled="!canSchedule" class="schedule-btn">
434
+ {{ canSchedule ? "Schedule Appointment" : "Select Valid Date & Time" }}
435
+ </button>
436
+ </div>
437
+ </div>
438
+ </template>
439
+
440
+ <style module>
441
+ .appointment-scheduler {
442
+ padding: 24px;
443
+ max-width: 600px;
444
+ background: #ffffff;
445
+ border-radius: 12px;
446
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
447
+ }
448
+
449
+ .scheduler-header {
450
+ margin-bottom: 32px;
451
+ }
452
+
453
+ .scheduler-header h3 {
454
+ font-size: 24px;
455
+ font-weight: 600;
456
+ color: #1a202c;
457
+ margin: 0 0 8px 0;
458
+ }
459
+
460
+ .scheduler-header p {
461
+ color: #718096;
462
+ margin: 0;
463
+ }
464
+
465
+ .form-section {
466
+ margin-bottom: 24px;
467
+ }
468
+
469
+ .form-label {
470
+ display: block;
471
+ font-size: 14px;
472
+ font-weight: 500;
473
+ color: #374151;
474
+ margin-bottom: 8px;
475
+ }
476
+
477
+ .form-select {
478
+ width: 100%;
479
+ padding: 12px;
480
+ border: 1px solid #d1d5db;
481
+ border-radius: 6px;
482
+ font-size: 14px;
483
+ background: white;
484
+ cursor: pointer;
485
+ }
486
+
487
+ .error {
488
+ margin-top: 8px;
489
+ padding: 8px 12px;
490
+ background: #fee2e2;
491
+ color: #dc2626;
492
+ border-radius: 4px;
493
+ font-size: 12px;
494
+ font-weight: 500;
495
+ }
496
+
497
+ .time-grid {
498
+ display: grid;
499
+ grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
500
+ gap: 8px;
501
+ }
502
+
503
+ .time-btn {
504
+ padding: 8px 12px;
505
+ border: 1px solid #d1d5db;
506
+ border-radius: 6px;
507
+ background: white;
508
+ color: #374151;
509
+ font-size: 12px;
510
+ font-weight: 500;
511
+ cursor: pointer;
512
+ transition: all 0.2s ease;
513
+ }
514
+
515
+ .time-btn:hover {
516
+ border-color: #3b82f6;
517
+ background: #f0f9ff;
518
+ }
519
+
520
+ .time-btn.active {
521
+ background: #3b82f6;
522
+ color: white;
523
+ border-color: #3b82f6;
524
+ }
525
+
526
+ .appointment-summary {
527
+ margin-top: 32px;
528
+ padding-top: 24px;
529
+ border-top: 1px solid #e5e7eb;
530
+ }
531
+
532
+ .appointment-summary h4 {
533
+ font-size: 18px;
534
+ font-weight: 500;
535
+ color: #2d3748;
536
+ margin: 0 0 16px 0;
537
+ }
538
+
539
+ .summary-card {
540
+ background: #f7fafc;
541
+ border-radius: 8px;
542
+ padding: 16px;
543
+ margin-bottom: 16px;
544
+ }
545
+
546
+ .summary-item {
547
+ display: flex;
548
+ justify-content: space-between;
549
+ align-items: center;
550
+ margin-bottom: 8px;
551
+ }
552
+
553
+ .summary-item:last-child {
554
+ margin-bottom: 0;
555
+ }
556
+
557
+ .label {
558
+ font-weight: 500;
559
+ color: #4a5568;
560
+ }
561
+
562
+ .value {
563
+ color: #2d3748;
564
+ font-weight: 600;
565
+ }
566
+
567
+ .schedule-btn {
568
+ width: 100%;
569
+ padding: 12px;
570
+ background: #3b82f6;
571
+ color: white;
572
+ border: none;
573
+ border-radius: 6px;
574
+ font-size: 16px;
575
+ font-weight: 600;
576
+ cursor: pointer;
577
+ transition: background 0.2s ease;
578
+ }
579
+
580
+ .schedule-btn:hover:not(:disabled) {
581
+ background: #2563eb;
582
+ }
583
+
584
+ .schedule-btn:disabled {
585
+ background: #9ca3af;
586
+ cursor: not-allowed;
587
+ }
588
+ </style>
589
+ ```
590
+
591
+ ### Date Range Selector
592
+
593
+ ```vue
594
+ <script setup lang="ts">
595
+ import { ref, computed } from "vue";
596
+ import { DatePicker } from "@umbra-ui/core";
597
+
598
+ const startDate = ref(new Date());
599
+ const endDate = ref(new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)); // 7 days from now
600
+
601
+ const dateRange = computed(() => {
602
+ const diffTime = Math.abs(
603
+ endDate.value.getTime() - startDate.value.getTime()
604
+ );
605
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
606
+ return diffDays;
607
+ });
608
+
609
+ const isValidRange = computed(() => {
610
+ return endDate.value >= startDate.value;
611
+ });
612
+
613
+ const handleStartDateChange = (date: Date) => {
614
+ startDate.value = date;
615
+ // Ensure end date is not before start date
616
+ if (endDate.value < startDate.value) {
617
+ endDate.value = new Date(startDate.value.getTime() + 24 * 60 * 60 * 1000);
618
+ }
619
+ };
620
+
621
+ const handleEndDateChange = (date: Date) => {
622
+ endDate.value = date;
623
+ };
624
+ </script>
625
+
626
+ <template>
627
+ <div class="date-range-selector">
628
+ <div class="selector-header">
629
+ <h3>Date Range Selector</h3>
630
+ <p>Select your travel dates</p>
631
+ </div>
632
+
633
+ <div class="date-inputs">
634
+ <div class="date-input">
635
+ <label class="form-label">Check-in Date</label>
636
+ <DatePicker
637
+ v-model:date="startDate"
638
+ @update:date="handleStartDateChange"
639
+ />
640
+ </div>
641
+
642
+ <div class="date-input">
643
+ <label class="form-label">Check-out Date</label>
644
+ <DatePicker v-model:date="endDate" @update:date="handleEndDateChange" />
645
+ </div>
646
+ </div>
647
+
648
+ <div v-if="!isValidRange" class="error">
649
+ ❌ Check-out date must be after check-in date
650
+ </div>
651
+
652
+ <div v-else class="range-summary">
653
+ <h4>Booking Summary</h4>
654
+ <div class="summary-grid">
655
+ <div class="summary-item">
656
+ <span class="label">Check-in:</span>
657
+ <span class="value">{{ startDate.toLocaleDateString() }}</span>
658
+ </div>
659
+ <div class="summary-item">
660
+ <span class="label">Check-out:</span>
661
+ <span class="value">{{ endDate.toLocaleDateString() }}</span>
662
+ </div>
663
+ <div class="summary-item">
664
+ <span class="label">Duration:</span>
665
+ <span class="value"
666
+ >{{ dateRange }} {{ dateRange === 1 ? "day" : "days" }}</span
667
+ >
668
+ </div>
669
+ </div>
670
+
671
+ <button :disabled="!isValidRange" class="book-btn">
672
+ Book for {{ dateRange }} {{ dateRange === 1 ? "day" : "days" }}
673
+ </button>
674
+ </div>
675
+ </div>
676
+ </template>
677
+
678
+ <style module>
679
+ .date-range-selector {
680
+ padding: 24px;
681
+ max-width: 600px;
682
+ background: #ffffff;
683
+ border-radius: 12px;
684
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
685
+ }
686
+
687
+ .selector-header {
688
+ margin-bottom: 32px;
689
+ }
690
+
691
+ .selector-header h3 {
692
+ font-size: 24px;
693
+ font-weight: 600;
694
+ color: #1a202c;
695
+ margin: 0 0 8px 0;
696
+ }
697
+
698
+ .selector-header p {
699
+ color: #718096;
700
+ margin: 0;
701
+ }
702
+
703
+ .date-inputs {
704
+ display: grid;
705
+ grid-template-columns: 1fr 1fr;
706
+ gap: 24px;
707
+ margin-bottom: 24px;
708
+ }
709
+
710
+ .date-input {
711
+ text-align: center;
712
+ }
713
+
714
+ .form-label {
715
+ display: block;
716
+ font-size: 14px;
717
+ font-weight: 500;
718
+ color: #374151;
719
+ margin-bottom: 12px;
720
+ }
721
+
722
+ .error {
723
+ margin-bottom: 24px;
724
+ padding: 12px;
725
+ background: #fee2e2;
726
+ color: #dc2626;
727
+ border-radius: 6px;
728
+ text-align: center;
729
+ font-weight: 500;
730
+ }
731
+
732
+ .range-summary {
733
+ padding-top: 24px;
734
+ border-top: 1px solid #e5e7eb;
735
+ }
736
+
737
+ .range-summary h4 {
738
+ font-size: 18px;
739
+ font-weight: 500;
740
+ color: #2d3748;
741
+ margin: 0 0 16px 0;
742
+ }
743
+
744
+ .summary-grid {
745
+ display: grid;
746
+ grid-template-columns: repeat(3, 1fr);
747
+ gap: 16px;
748
+ margin-bottom: 24px;
749
+ }
750
+
751
+ .summary-item {
752
+ text-align: center;
753
+ padding: 16px;
754
+ background: #f7fafc;
755
+ border-radius: 8px;
756
+ }
757
+
758
+ .label {
759
+ display: block;
760
+ font-size: 12px;
761
+ font-weight: 500;
762
+ color: #6b7280;
763
+ margin-bottom: 4px;
764
+ text-transform: uppercase;
765
+ letter-spacing: 0.05em;
766
+ }
767
+
768
+ .value {
769
+ display: block;
770
+ font-size: 16px;
771
+ font-weight: 600;
772
+ color: #1f2937;
773
+ }
774
+
775
+ .book-btn {
776
+ width: 100%;
777
+ padding: 16px;
778
+ background: #059669;
779
+ color: white;
780
+ border: none;
781
+ border-radius: 8px;
782
+ font-size: 16px;
783
+ font-weight: 600;
784
+ cursor: pointer;
785
+ transition: background 0.2s ease;
786
+ }
787
+
788
+ .book-btn:hover:not(:disabled) {
789
+ background: #047857;
790
+ }
791
+
792
+ .book-btn:disabled {
793
+ background: #9ca3af;
794
+ cursor: not-allowed;
795
+ }
796
+ </style>
797
+ ```
798
+
799
+ ### Birthday Selector
800
+
801
+ ```vue
802
+ <script setup lang="ts">
803
+ import { ref, computed } from "vue";
804
+ import { DatePicker } from "@umbra-ui/core";
805
+
806
+ const birthday = ref(new Date(1990, 0, 1)); // Default to January 1, 1990
807
+
808
+ const age = computed(() => {
809
+ const today = new Date();
810
+ const birthDate = new Date(birthday.value);
811
+ let age = today.getFullYear() - birthDate.getFullYear();
812
+ const monthDiff = today.getMonth() - birthDate.getMonth();
813
+
814
+ if (
815
+ monthDiff < 0 ||
816
+ (monthDiff === 0 && today.getDate() < birthDate.getDate())
817
+ ) {
818
+ age--;
819
+ }
820
+
821
+ return age;
822
+ });
823
+
824
+ const zodiacSign = computed(() => {
825
+ const month = birthday.value.getMonth() + 1;
826
+ const day = birthday.value.getDate();
827
+
828
+ const signs = [
829
+ { name: "Capricorn", start: [12, 22], end: [1, 19] },
830
+ { name: "Aquarius", start: [1, 20], end: [2, 18] },
831
+ { name: "Pisces", start: [2, 19], end: [3, 20] },
832
+ { name: "Aries", start: [3, 21], end: [4, 19] },
833
+ { name: "Taurus", start: [4, 20], end: [5, 20] },
834
+ { name: "Gemini", start: [5, 21], end: [6, 20] },
835
+ { name: "Cancer", start: [6, 21], end: [7, 22] },
836
+ { name: "Leo", start: [7, 23], end: [8, 22] },
837
+ { name: "Virgo", start: [8, 23], end: [9, 22] },
838
+ { name: "Libra", start: [9, 23], end: [10, 22] },
839
+ { name: "Scorpio", start: [10, 23], end: [11, 21] },
840
+ { name: "Sagittarius", start: [11, 22], end: [12, 21] },
841
+ ];
842
+
843
+ for (const sign of signs) {
844
+ const [startMonth, startDay] = sign.start;
845
+ const [endMonth, endDay] = sign.end;
846
+
847
+ if (
848
+ (month === startMonth && day >= startDay) ||
849
+ (month === endMonth && day <= endDay) ||
850
+ (startMonth > endMonth && (month > startMonth || month < endMonth))
851
+ ) {
852
+ return sign.name;
853
+ }
854
+ }
855
+
856
+ return "Unknown";
857
+ });
858
+
859
+ const isFutureDate = computed(() => {
860
+ const today = new Date();
861
+ today.setHours(0, 0, 0, 0);
862
+ return birthday.value > today;
863
+ });
864
+
865
+ const handleBirthdayChange = (date: Date) => {
866
+ birthday.value = date;
867
+ console.log("Birthday changed to:", date);
868
+ };
869
+ </script>
870
+
871
+ <template>
872
+ <div class="birthday-selector">
873
+ <div class="selector-header">
874
+ <h3>Birthday Information</h3>
875
+ <p>Tell us about your special day</p>
876
+ </div>
877
+
878
+ <div class="form-section">
879
+ <label class="form-label">Birthday</label>
880
+ <DatePicker v-model:date="birthday" @update:date="handleBirthdayChange" />
881
+
882
+ <div v-if="isFutureDate" class="warning">
883
+ ⚠️ This date is in the future
884
+ </div>
885
+ </div>
886
+
887
+ <div class="birthday-info">
888
+ <h4>Your Birthday Details</h4>
889
+ <div class="info-grid">
890
+ <div class="info-item">
891
+ <span class="label">Age</span>
892
+ <span class="value">{{ age }} years old</span>
893
+ </div>
894
+ <div class="info-item">
895
+ <span class="label">Zodiac Sign</span>
896
+ <span class="value">{{ zodiacSign }}</span>
897
+ </div>
898
+ <div class="info-item">
899
+ <span class="label">Birthday</span>
900
+ <span class="value">{{
901
+ birthday.toLocaleDateString("en-US", {
902
+ weekday: "long",
903
+ year: "numeric",
904
+ month: "long",
905
+ day: "numeric",
906
+ })
907
+ }}</span>
908
+ </div>
909
+ </div>
910
+
911
+ <div class="birthday-message">
912
+ <p>🎉 Your birthday is a special day worth celebrating!</p>
913
+ </div>
914
+ </div>
915
+ </div>
916
+ </template>
917
+
918
+ <style module>
919
+ .birthday-selector {
920
+ padding: 24px;
921
+ max-width: 500px;
922
+ background: #ffffff;
923
+ border-radius: 12px;
924
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
925
+ }
926
+
927
+ .selector-header {
928
+ margin-bottom: 32px;
929
+ }
930
+
931
+ .selector-header h3 {
932
+ font-size: 24px;
933
+ font-weight: 600;
934
+ color: #1a202c;
935
+ margin: 0 0 8px 0;
936
+ }
937
+
938
+ .selector-header p {
939
+ color: #718096;
940
+ margin: 0;
941
+ }
942
+
943
+ .form-section {
944
+ margin-bottom: 32px;
945
+ }
946
+
947
+ .form-label {
948
+ display: block;
949
+ font-size: 14px;
950
+ font-weight: 500;
951
+ color: #374151;
952
+ margin-bottom: 12px;
953
+ }
954
+
955
+ .warning {
956
+ margin-top: 8px;
957
+ padding: 8px 12px;
958
+ background: #fef3c7;
959
+ color: #92400e;
960
+ border-radius: 4px;
961
+ font-size: 12px;
962
+ font-weight: 500;
963
+ }
964
+
965
+ .birthday-info {
966
+ padding-top: 24px;
967
+ border-top: 1px solid #e5e7eb;
968
+ }
969
+
970
+ .birthday-info h4 {
971
+ font-size: 18px;
972
+ font-weight: 500;
973
+ color: #2d3748;
974
+ margin: 0 0 20px 0;
975
+ }
976
+
977
+ .info-grid {
978
+ display: grid;
979
+ gap: 16px;
980
+ margin-bottom: 24px;
981
+ }
982
+
983
+ .info-item {
984
+ display: flex;
985
+ justify-content: space-between;
986
+ align-items: center;
987
+ padding: 16px;
988
+ background: #f7fafc;
989
+ border-radius: 8px;
990
+ }
991
+
992
+ .label {
993
+ font-weight: 500;
994
+ color: #4a5568;
995
+ }
996
+
997
+ .value {
998
+ color: #2d3748;
999
+ font-weight: 600;
1000
+ }
1001
+
1002
+ .birthday-message {
1003
+ text-align: center;
1004
+ padding: 20px;
1005
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1006
+ color: white;
1007
+ border-radius: 8px;
1008
+ }
1009
+
1010
+ .birthday-message p {
1011
+ margin: 0;
1012
+ font-size: 16px;
1013
+ font-weight: 500;
1014
+ }
1015
+ </style>
1016
+ ```
1017
+
1018
+ ## Advanced Usage
1019
+
1020
+ ### Custom Date Validation
1021
+
1022
+ ```vue
1023
+ <script setup lang="ts">
1024
+ import { ref, computed } from "vue";
1025
+ import { DatePicker } from "@umbra-ui/core";
1026
+
1027
+ const selectedDate = ref(new Date());
1028
+ const minDate = new Date(2024, 0, 1); // January 1, 2024
1029
+ const maxDate = new Date(2024, 11, 31); // December 31, 2024
1030
+
1031
+ const isDateValid = computed(() => {
1032
+ return selectedDate.value >= minDate && selectedDate.value <= maxDate;
1033
+ });
1034
+
1035
+ const validationMessage = computed(() => {
1036
+ if (selectedDate.value < minDate) {
1037
+ return `Date must be after ${minDate.toLocaleDateString()}`;
1038
+ }
1039
+ if (selectedDate.value > maxDate) {
1040
+ return `Date must be before ${maxDate.toLocaleDateString()}`;
1041
+ }
1042
+ return "Date is valid";
1043
+ });
1044
+
1045
+ const handleDateChange = (date: Date) => {
1046
+ selectedDate.value = date;
1047
+ console.log("Date validation:", isDateValid.value);
1048
+ };
1049
+ </script>
1050
+
1051
+ <template>
1052
+ <div class="date-validation">
1053
+ <h3>Date Validation Example</h3>
1054
+
1055
+ <div class="form-section">
1056
+ <label class="form-label">Select Date (2024 only)</label>
1057
+ <DatePicker v-model:date="selectedDate" @update:date="handleDateChange" />
1058
+
1059
+ <div
1060
+ :class="[
1061
+ 'validation-message',
1062
+ {
1063
+ valid: isDateValid,
1064
+ invalid: !isDateValid,
1065
+ },
1066
+ ]"
1067
+ >
1068
+ {{ validationMessage }}
1069
+ </div>
1070
+ </div>
1071
+
1072
+ <div class="date-info">
1073
+ <p><strong>Selected:</strong> {{ selectedDate.toLocaleDateString() }}</p>
1074
+ <p><strong>Valid:</strong> {{ isDateValid ? "Yes" : "No" }}</p>
1075
+ </div>
1076
+ </div>
1077
+ </template>
1078
+
1079
+ <style module>
1080
+ .date-validation {
1081
+ padding: 24px;
1082
+ max-width: 400px;
1083
+ }
1084
+
1085
+ .form-section {
1086
+ margin-bottom: 24px;
1087
+ }
1088
+
1089
+ .form-label {
1090
+ display: block;
1091
+ font-size: 14px;
1092
+ font-weight: 500;
1093
+ color: #374151;
1094
+ margin-bottom: 12px;
1095
+ }
1096
+
1097
+ .validation-message {
1098
+ margin-top: 8px;
1099
+ padding: 8px 12px;
1100
+ border-radius: 4px;
1101
+ font-size: 12px;
1102
+ font-weight: 500;
1103
+ }
1104
+
1105
+ .validation-message.valid {
1106
+ background: #d1fae5;
1107
+ color: #065f46;
1108
+ }
1109
+
1110
+ .validation-message.invalid {
1111
+ background: #fee2e2;
1112
+ color: #dc2626;
1113
+ }
1114
+
1115
+ .date-info {
1116
+ padding: 16px;
1117
+ background: #f7fafc;
1118
+ border-radius: 8px;
1119
+ }
1120
+
1121
+ .date-info p {
1122
+ margin: 0 0 8px 0;
1123
+ color: #4a5568;
1124
+ }
1125
+
1126
+ .date-info p:last-child {
1127
+ margin-bottom: 0;
1128
+ }
1129
+ </style>
1130
+ ```
1131
+
1132
+ ## Performance Considerations
1133
+
1134
+ - **Floating UI**: Smart positioning with automatic updates and cleanup
1135
+ - **Teleport**: Overlay and picker are teleported to body for proper z-index management
1136
+ - **Event Cleanup**: Proper cleanup of auto-update listeners on unmount
1137
+ - **Lazy Loading**: Months are generated on-demand during scrolling
1138
+ - **Smooth Scrolling**: Optimized scroll handling with debouncing
1139
+
1140
+ ## Accessibility
1141
+
1142
+ - **Keyboard Navigation**: Support for keyboard interaction
1143
+ - **Screen Reader Support**: Proper ARIA labels and semantic structure
1144
+ - **Focus Management**: Focus is properly managed during interactions
1145
+ - **Date Information**: Displays formatted date strings for clarity
1146
+ - **High Contrast**: Supports high contrast mode with appropriate styling
1147
+
1148
+ ## Browser Support
1149
+
1150
+ - **Modern Browsers**: Chrome 88+, Firefox 85+, Safari 14+, Edge 88+
1151
+ - **Mobile Browsers**: iOS Safari 14+, Chrome Mobile 88+
1152
+ - **CSS Features**: Uses CSS Grid and modern layout features
1153
+ - **JavaScript**: Requires ES2020+ support
1154
+
1155
+ ## Troubleshooting
1156
+
1157
+ ### Common Issues
1158
+
1159
+ 1. **Picker not positioning correctly**: Ensure the trigger button has proper positioning context
1160
+ 2. **Date not updating**: Check that the v-model binding is correctly set up
1161
+ 3. **Calendar not showing**: Verify that the showPopover state is properly managed
1162
+ 4. **Scrolling issues**: Ensure the scroll container has proper height constraints
1163
+
1164
+ ### Debug Mode
1165
+
1166
+ ```vue
1167
+ <script setup lang="ts">
1168
+ import { ref, watch } from "vue";
1169
+ import { DatePicker } from "@umbra-ui/core";
1170
+
1171
+ const selectedDate = ref(new Date());
1172
+
1173
+ // Watch for changes
1174
+ watch(selectedDate, (newDate) => {
1175
+ console.log("Selected date changed:", newDate);
1176
+ console.log("Date string:", newDate.toLocaleDateString());
1177
+ });
1178
+ </script>
1179
+ ```
1180
+
1181
+ ## Migration Guide
1182
+
1183
+ ### From v1 to v2
1184
+
1185
+ - `v-model` prop is now `v-model:date`
1186
+ - Event names have been updated to use kebab-case
1187
+ - Calendar system has been improved with better scrolling
1188
+ - Floating UI integration has been enhanced
1189
+
1190
+ ### Breaking Changes
1191
+
1192
+ - Removed `value` prop in favor of `v-model:date`
1193
+ - Changed event names from camelCase to kebab-case
1194
+ - Updated date prop to use Date object instead of string
1195
+ - Modified CSS class naming convention