mtrl 0.2.5 → 0.2.7

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 (196) hide show
  1. package/index.ts +18 -0
  2. package/package.json +1 -1
  3. package/src/components/badge/_styles.scss +123 -115
  4. package/src/components/badge/api.ts +57 -59
  5. package/src/components/badge/badge.ts +16 -2
  6. package/src/components/badge/config.ts +65 -11
  7. package/src/components/badge/constants.ts +22 -12
  8. package/src/components/badge/features.ts +44 -40
  9. package/src/components/badge/types.ts +42 -30
  10. package/src/components/bottom-app-bar/_styles.scss +103 -0
  11. package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
  12. package/src/components/bottom-app-bar/config.ts +73 -0
  13. package/src/components/bottom-app-bar/index.ts +11 -0
  14. package/src/components/bottom-app-bar/types.ts +108 -0
  15. package/src/components/button/_styles.scss +0 -66
  16. package/src/components/button/api.ts +5 -0
  17. package/src/components/button/button.ts +0 -2
  18. package/src/components/button/config.ts +5 -0
  19. package/src/components/button/constants.ts +0 -6
  20. package/src/components/button/index.ts +2 -2
  21. package/src/components/button/types.ts +7 -7
  22. package/src/components/card/_styles.scss +67 -25
  23. package/src/components/card/api.ts +54 -3
  24. package/src/components/card/card.ts +25 -6
  25. package/src/components/card/config.ts +189 -22
  26. package/src/components/card/constants.ts +20 -19
  27. package/src/components/card/content.ts +299 -2
  28. package/src/components/card/features.ts +158 -4
  29. package/src/components/card/index.ts +31 -9
  30. package/src/components/card/types.ts +166 -15
  31. package/src/components/checkbox/_styles.scss +0 -2
  32. package/src/components/chip/chip.ts +1 -9
  33. package/src/components/chip/constants.ts +0 -10
  34. package/src/components/chip/index.ts +1 -1
  35. package/src/components/chip/types.ts +1 -4
  36. package/src/components/datepicker/_styles.scss +358 -0
  37. package/src/components/datepicker/api.ts +272 -0
  38. package/src/components/datepicker/config.ts +144 -0
  39. package/src/components/datepicker/constants.ts +98 -0
  40. package/src/components/datepicker/datepicker.ts +346 -0
  41. package/src/components/datepicker/index.ts +9 -0
  42. package/src/components/datepicker/render.ts +452 -0
  43. package/src/components/datepicker/types.ts +268 -0
  44. package/src/components/datepicker/utils.ts +290 -0
  45. package/src/components/dialog/_styles.scss +174 -128
  46. package/src/components/dialog/api.ts +48 -13
  47. package/src/components/dialog/config.ts +9 -5
  48. package/src/components/dialog/dialog.ts +6 -3
  49. package/src/components/dialog/features.ts +290 -130
  50. package/src/components/dialog/types.ts +7 -4
  51. package/src/components/divider/_styles.scss +57 -0
  52. package/src/components/divider/config.ts +81 -0
  53. package/src/components/divider/divider.ts +37 -0
  54. package/src/components/divider/features.ts +207 -0
  55. package/src/components/divider/index.ts +5 -0
  56. package/src/components/divider/types.ts +55 -0
  57. package/src/components/extended-fab/_styles.scss +267 -0
  58. package/src/components/extended-fab/api.ts +141 -0
  59. package/src/components/extended-fab/config.ts +108 -0
  60. package/src/components/extended-fab/constants.ts +36 -0
  61. package/src/components/extended-fab/extended-fab.ts +125 -0
  62. package/src/components/extended-fab/index.ts +4 -0
  63. package/src/components/extended-fab/types.ts +287 -0
  64. package/src/components/fab/_styles.scss +225 -0
  65. package/src/components/fab/api.ts +97 -0
  66. package/src/components/fab/config.ts +94 -0
  67. package/src/components/fab/constants.ts +41 -0
  68. package/src/components/fab/fab.ts +67 -0
  69. package/src/components/fab/index.ts +4 -0
  70. package/src/components/fab/types.ts +234 -0
  71. package/src/components/navigation/_styles.scss +1 -0
  72. package/src/components/navigation/api.ts +78 -50
  73. package/src/components/navigation/features/items.ts +280 -0
  74. package/src/components/navigation/nav-item.ts +72 -23
  75. package/src/components/navigation/navigation.ts +54 -2
  76. package/src/components/navigation/types.ts +210 -188
  77. package/src/components/progress/_styles.scss +0 -65
  78. package/src/components/progress/config.ts +1 -2
  79. package/src/components/progress/constants.ts +0 -14
  80. package/src/components/progress/index.ts +1 -1
  81. package/src/components/progress/progress.ts +1 -4
  82. package/src/components/progress/types.ts +1 -4
  83. package/src/components/radios/_styles.scss +0 -45
  84. package/src/components/radios/api.ts +85 -60
  85. package/src/components/radios/config.ts +1 -2
  86. package/src/components/radios/constants.ts +0 -9
  87. package/src/components/radios/index.ts +1 -1
  88. package/src/components/radios/radio.ts +34 -11
  89. package/src/components/radios/radios.ts +2 -1
  90. package/src/components/radios/types.ts +1 -7
  91. package/src/components/search/_styles.scss +306 -0
  92. package/src/components/search/api.ts +203 -0
  93. package/src/components/search/config.ts +87 -0
  94. package/src/components/search/constants.ts +21 -0
  95. package/src/components/search/features/index.ts +4 -0
  96. package/src/components/search/features/search.ts +718 -0
  97. package/src/components/search/features/states.ts +165 -0
  98. package/src/components/search/features/structure.ts +198 -0
  99. package/src/components/search/index.ts +10 -0
  100. package/src/components/search/search.ts +52 -0
  101. package/src/components/search/types.ts +163 -0
  102. package/src/components/segmented-button/_styles.scss +117 -0
  103. package/src/components/segmented-button/config.ts +67 -0
  104. package/src/components/segmented-button/constants.ts +42 -0
  105. package/src/components/segmented-button/index.ts +4 -0
  106. package/src/components/segmented-button/segment.ts +155 -0
  107. package/src/components/segmented-button/segmented-button.ts +250 -0
  108. package/src/components/segmented-button/types.ts +219 -0
  109. package/src/components/slider/_styles.scss +221 -168
  110. package/src/components/slider/accessibility.md +59 -0
  111. package/src/components/slider/api.ts +41 -120
  112. package/src/components/slider/config.ts +51 -49
  113. package/src/components/slider/features/handlers.ts +495 -0
  114. package/src/components/slider/features/index.ts +1 -2
  115. package/src/components/slider/features/slider.ts +66 -84
  116. package/src/components/slider/features/states.ts +195 -0
  117. package/src/components/slider/features/structure.ts +141 -184
  118. package/src/components/slider/features/ui.ts +150 -201
  119. package/src/components/slider/index.ts +2 -11
  120. package/src/components/slider/slider.ts +9 -12
  121. package/src/components/slider/types.ts +39 -24
  122. package/src/components/switch/_styles.scss +0 -2
  123. package/src/components/tabs/_styles.scss +346 -154
  124. package/src/components/tabs/api.ts +178 -400
  125. package/src/components/tabs/config.ts +46 -52
  126. package/src/components/tabs/constants.ts +85 -8
  127. package/src/components/tabs/features.ts +403 -0
  128. package/src/components/tabs/index.ts +60 -3
  129. package/src/components/tabs/indicator.ts +285 -0
  130. package/src/components/tabs/responsive.ts +144 -0
  131. package/src/components/tabs/scroll-indicators.ts +149 -0
  132. package/src/components/tabs/state.ts +186 -0
  133. package/src/components/tabs/tab-api.ts +258 -0
  134. package/src/components/tabs/tab.ts +255 -0
  135. package/src/components/tabs/tabs.ts +50 -31
  136. package/src/components/tabs/types.ts +332 -128
  137. package/src/components/tabs/utils.ts +107 -0
  138. package/src/components/textfield/_styles.scss +0 -98
  139. package/src/components/textfield/config.ts +2 -3
  140. package/src/components/textfield/constants.ts +0 -14
  141. package/src/components/textfield/index.ts +2 -2
  142. package/src/components/textfield/textfield.ts +0 -2
  143. package/src/components/textfield/types.ts +1 -4
  144. package/src/components/timepicker/README.md +277 -0
  145. package/src/components/timepicker/_styles.scss +451 -0
  146. package/src/components/timepicker/api.ts +632 -0
  147. package/src/components/timepicker/clockdial.ts +482 -0
  148. package/src/components/timepicker/config.ts +130 -0
  149. package/src/components/timepicker/constants.ts +138 -0
  150. package/src/components/timepicker/index.ts +8 -0
  151. package/src/components/timepicker/render.ts +613 -0
  152. package/src/components/timepicker/timepicker.ts +117 -0
  153. package/src/components/timepicker/types.ts +336 -0
  154. package/src/components/timepicker/utils.ts +241 -0
  155. package/src/components/top-app-bar/_styles.scss +225 -0
  156. package/src/components/top-app-bar/config.ts +83 -0
  157. package/src/components/top-app-bar/index.ts +11 -0
  158. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  159. package/src/components/top-app-bar/types.ts +140 -0
  160. package/src/core/build/_ripple.scss +6 -6
  161. package/src/core/build/ripple.ts +72 -95
  162. package/src/core/compose/component.ts +1 -1
  163. package/src/core/compose/features/badge.ts +79 -0
  164. package/src/core/compose/features/icon.ts +3 -1
  165. package/src/core/compose/features/index.ts +3 -1
  166. package/src/core/compose/features/ripple.ts +4 -1
  167. package/src/core/compose/features/textlabel.ts +26 -2
  168. package/src/core/dom/create.ts +5 -0
  169. package/src/index.ts +9 -0
  170. package/src/styles/abstract/_theme.scss +115 -3
  171. package/src/styles/themes/_autumn.scss +21 -0
  172. package/src/styles/themes/_base-theme.scss +61 -0
  173. package/src/styles/themes/_baseline.scss +58 -0
  174. package/src/styles/themes/_bluekhaki.scss +125 -0
  175. package/src/styles/themes/_brownbeige.scss +125 -0
  176. package/src/styles/themes/_browngreen.scss +125 -0
  177. package/src/styles/themes/_forest.scss +6 -0
  178. package/src/styles/themes/_greenbeige.scss +125 -0
  179. package/src/styles/themes/_material.scss +125 -0
  180. package/src/styles/themes/_ocean.scss +6 -0
  181. package/src/styles/themes/_sageivory.scss +125 -0
  182. package/src/styles/themes/_spring.scss +6 -0
  183. package/src/styles/themes/_summer.scss +5 -0
  184. package/src/styles/themes/_sunset.scss +5 -0
  185. package/src/styles/themes/_tealcaramel.scss +125 -0
  186. package/src/styles/themes/_winter.scss +6 -0
  187. package/src/components/card/actions.ts +0 -48
  188. package/src/components/card/header.ts +0 -88
  189. package/src/components/card/media.ts +0 -52
  190. package/src/components/navigation/features/items.js +0 -192
  191. package/src/components/slider/features/appearance.ts +0 -94
  192. package/src/components/slider/features/disabled.ts +0 -43
  193. package/src/components/slider/features/events.ts +0 -164
  194. package/src/components/slider/features/interactions.ts +0 -261
  195. package/src/components/slider/features/keyboard.ts +0 -112
  196. package/src/core/collection/adapters/mongodb.js +0 -232
@@ -0,0 +1,144 @@
1
+ // src/components/datepicker/config.ts
2
+ import {
3
+ createComponentConfig,
4
+ createElementConfig,
5
+ BaseComponentConfig
6
+ } from '../../core/config/component-config';
7
+ import { DatePickerConfig } from './types';
8
+ import {
9
+ DATEPICKER_VARIANTS,
10
+ DATEPICKER_VIEWS,
11
+ DATEPICKER_SELECTION_MODES,
12
+ DEFAULT_DATE_FORMAT
13
+ } from './constants';
14
+
15
+ /**
16
+ * Default configuration for the DatePicker component
17
+ */
18
+ export const defaultConfig: DatePickerConfig = {
19
+ variant: DATEPICKER_VARIANTS.DOCKED,
20
+ initialView: DATEPICKER_VIEWS.DAY,
21
+ selectionMode: DATEPICKER_SELECTION_MODES.SINGLE,
22
+ dateFormat: DEFAULT_DATE_FORMAT,
23
+ animate: true
24
+ };
25
+
26
+ /**
27
+ * Creates the base configuration for DatePicker component
28
+ * @param {DatePickerConfig} config - User provided configuration
29
+ * @returns {DatePickerConfig} Complete configuration with defaults applied
30
+ */
31
+ export const createBaseConfig = (config: DatePickerConfig = {}): DatePickerConfig => {
32
+ const baseConfig = createComponentConfig(defaultConfig, config, 'datepicker') as DatePickerConfig;
33
+
34
+ // Set closeOnSelect default based on variant
35
+ if (baseConfig.closeOnSelect === undefined) {
36
+ baseConfig.closeOnSelect = baseConfig.variant !== DATEPICKER_VARIANTS.DOCKED;
37
+ }
38
+
39
+ return baseConfig;
40
+ };
41
+
42
+ /**
43
+ * Generates element configuration for the DatePicker container
44
+ * @param {DatePickerConfig} config - DatePicker configuration
45
+ * @returns {Object} Element configuration object for withElement
46
+ */
47
+ export const getContainerConfig = (config: DatePickerConfig) => {
48
+ return createElementConfig(config, {
49
+ tag: 'div',
50
+ attrs: {
51
+ role: 'application',
52
+ 'aria-label': 'Date Picker',
53
+ tabindex: '-1'
54
+ },
55
+ className: [
56
+ `${config.prefix}-datepicker-container`,
57
+ config.class
58
+ ],
59
+ forwardEvents: {
60
+ keydown: true,
61
+ click: true
62
+ },
63
+ interactive: true
64
+ });
65
+ };
66
+
67
+ /**
68
+ * Generates element configuration for the input field
69
+ * @param {DatePickerConfig} config - DatePicker configuration
70
+ * @returns {Object} Element configuration object for input field
71
+ */
72
+ export const getInputConfig = (config: DatePickerConfig) => {
73
+ // Create the attributes object
74
+ const attrs: Record<string, any> = {
75
+ type: 'text',
76
+ placeholder: config.placeholder || config.dateFormat,
77
+ autocomplete: 'off',
78
+ readonly: true
79
+ };
80
+
81
+ // Only add disabled attribute if it's explicitly true
82
+ if (config.disabled === true) {
83
+ attrs.disabled = true;
84
+ }
85
+
86
+ return createElementConfig(config, {
87
+ tag: 'input',
88
+ attrs,
89
+ className: `${config.prefix}-datepicker-input`,
90
+ forwardEvents: {
91
+ focus: true,
92
+ blur: true,
93
+ click: true
94
+ },
95
+ interactive: true
96
+ });
97
+ };
98
+
99
+ /**
100
+ * Generates element configuration for the calendar container
101
+ * @param {DatePickerConfig} config - DatePicker configuration
102
+ * @returns {Object} Element configuration object for calendar container
103
+ */
104
+ export const getCalendarConfig = (config: DatePickerConfig) => {
105
+ return createElementConfig(config, {
106
+ tag: 'div',
107
+ attrs: {
108
+ role: 'dialog',
109
+ 'aria-modal': config.variant !== DATEPICKER_VARIANTS.DOCKED ? 'true' : 'false'
110
+ },
111
+ className: [
112
+ `${config.prefix}-datepicker-calendar`,
113
+ `${config.prefix}-datepicker-${config.variant}`,
114
+ config.selectionMode === DATEPICKER_SELECTION_MODES.RANGE ?
115
+ `${config.prefix}-datepicker-range` : ''
116
+ ],
117
+ forwardEvents: {
118
+ keydown: true,
119
+ click: true
120
+ }
121
+ });
122
+ };
123
+
124
+ /**
125
+ * Creates API configuration for the DatePicker component
126
+ * @param {Object} comp - Component with features like disabled and lifecycle
127
+ * @returns {Object} API configuration object
128
+ */
129
+ export const getApiConfig = (comp: any) => ({
130
+ disabled: {
131
+ enable: () => comp.disabled.enable(),
132
+ disable: () => comp.disabled.disable()
133
+ },
134
+ lifecycle: {
135
+ destroy: () => comp.lifecycle.destroy()
136
+ },
137
+ events: {
138
+ on: comp.on,
139
+ off: comp.off,
140
+ emit: comp.emit
141
+ }
142
+ });
143
+
144
+ export default defaultConfig;
@@ -0,0 +1,98 @@
1
+ // src/components/datepicker/constants.ts
2
+
3
+ /**
4
+ * DatePicker variant types
5
+ */
6
+ export const DATEPICKER_VARIANTS = {
7
+ /** Displays inline with a text field above the calendar */
8
+ DOCKED: 'docked',
9
+
10
+ /** Displays as a modal dialog */
11
+ MODAL: 'modal',
12
+
13
+ /** Displays as a modal dialog with text input */
14
+ MODAL_INPUT: 'modal-input'
15
+ };
16
+
17
+ /**
18
+ * DatePicker view types
19
+ */
20
+ export const DATEPICKER_VIEWS = {
21
+ /** Calendar day selection view */
22
+ DAY: 'day',
23
+
24
+ /** Month selection view */
25
+ MONTH: 'month',
26
+
27
+ /** Year selection view */
28
+ YEAR: 'year'
29
+ };
30
+
31
+ /**
32
+ * DatePicker selection modes
33
+ */
34
+ export const DATEPICKER_SELECTION_MODES = {
35
+ /** Single date selection */
36
+ SINGLE: 'single',
37
+
38
+ /** Date range selection */
39
+ RANGE: 'range'
40
+ };
41
+
42
+ /**
43
+ * Day names for the calendar
44
+ */
45
+ export const DAY_NAMES = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
46
+
47
+ /**
48
+ * Month names for the calendar
49
+ */
50
+ export const MONTH_NAMES = [
51
+ 'January', 'February', 'March', 'April',
52
+ 'May', 'June', 'July', 'August',
53
+ 'September', 'October', 'November', 'December'
54
+ ];
55
+
56
+ /**
57
+ * Month names abbreviated
58
+ */
59
+ export const MONTH_NAMES_SHORT = [
60
+ 'Jan', 'Feb', 'Mar', 'Apr',
61
+ 'May', 'Jun', 'Jul', 'Aug',
62
+ 'Sep', 'Oct', 'Nov', 'Dec'
63
+ ];
64
+
65
+ /**
66
+ * Default format for displaying dates
67
+ */
68
+ export const DEFAULT_DATE_FORMAT = 'MM/DD/YYYY';
69
+
70
+ /**
71
+ * CSS class name for today's date
72
+ */
73
+ export const TODAY_CLASS = 'today';
74
+
75
+ /**
76
+ * CSS class name for selected date
77
+ */
78
+ export const SELECTED_CLASS = 'selected';
79
+
80
+ /**
81
+ * CSS class name for dates outside the current month
82
+ */
83
+ export const OUTSIDE_MONTH_CLASS = 'outside-month';
84
+
85
+ /**
86
+ * CSS class for the first date in a range
87
+ */
88
+ export const RANGE_START_CLASS = 'range-start';
89
+
90
+ /**
91
+ * CSS class for the last date in a range
92
+ */
93
+ export const RANGE_END_CLASS = 'range-end';
94
+
95
+ /**
96
+ * CSS class for dates between start and end in a range
97
+ */
98
+ export const RANGE_MIDDLE_CLASS = 'range-middle';
@@ -0,0 +1,346 @@
1
+ // src/components/datepicker/datepicker.ts
2
+ import { PREFIX } from '../../core/config';
3
+ import { pipe } from '../../core/compose';
4
+ import { createBase, withElement } from '../../core/compose/component';
5
+ import {
6
+ withEvents,
7
+ withDisabled,
8
+ withLifecycle
9
+ } from '../../core/compose/features';
10
+
11
+ import { withAPI } from './api';
12
+ import { DatePickerConfig } from './types';
13
+ import {
14
+ DATEPICKER_VARIANTS,
15
+ DATEPICKER_VIEWS,
16
+ DATEPICKER_SELECTION_MODES
17
+ } from './constants';
18
+ import {
19
+ createBaseConfig,
20
+ getContainerConfig,
21
+ getInputConfig,
22
+ getCalendarConfig,
23
+ getApiConfig
24
+ } from './config';
25
+ import {
26
+ formatDate,
27
+ parseDate,
28
+ isSameDay
29
+ } from './utils';
30
+ import {
31
+ renderCalendar
32
+ } from './render';
33
+ import { createElement } from '../../core/dom/create';
34
+
35
+ /**
36
+ * Creates a new DatePicker component
37
+ * @param {DatePickerConfig} config - DatePicker configuration object
38
+ * @returns {DatePickerComponent} DatePicker component instance
39
+ */
40
+ const createDatePicker = (config: DatePickerConfig = {}) => {
41
+ const baseConfig = createBaseConfig(config);
42
+
43
+ try {
44
+ // Initialize state
45
+ const state: any = {
46
+ isOpen: false,
47
+ selectedDate: null,
48
+ rangeEndDate: null,
49
+ currentView: baseConfig.initialView,
50
+ currentMonth: new Date().getMonth(),
51
+ currentYear: new Date().getFullYear(),
52
+ minDate: baseConfig.minDate ? parseDate(baseConfig.minDate) : null,
53
+ maxDate: baseConfig.maxDate ? parseDate(baseConfig.maxDate) : null,
54
+ dateFormat: baseConfig.dateFormat,
55
+ variant: baseConfig.variant,
56
+ selectionMode: baseConfig.selectionMode,
57
+ closeOnSelect: baseConfig.closeOnSelect,
58
+ prefix: baseConfig.prefix || PREFIX,
59
+ calendarElement: null,
60
+ input: null,
61
+
62
+ updateInputValue(): void {
63
+ if (!this.input) return;
64
+
65
+ if (!this.selectedDate) {
66
+ this.input.value = '';
67
+ return;
68
+ }
69
+
70
+ // Format for range selection
71
+ if (this.selectionMode === DATEPICKER_SELECTION_MODES.RANGE && this.rangeEndDate) {
72
+ const startStr = formatDate(this.selectedDate, this.dateFormat);
73
+ const endStr = formatDate(this.rangeEndDate, this.dateFormat);
74
+ this.input.value = `${startStr} - ${endStr}`;
75
+ return;
76
+ }
77
+
78
+ // Format for single selection
79
+ this.input.value = formatDate(this.selectedDate, this.dateFormat);
80
+ },
81
+
82
+ updateCalendar(): void {
83
+ if (!this.isOpen || !this.calendarElement) return;
84
+
85
+ // Clear existing content
86
+ this.calendarElement.innerHTML = '';
87
+
88
+ // Render calendar content
89
+ const calendar = renderCalendar(this, (event, data) => {
90
+ switch (event) {
91
+ case 'dateSelected':
92
+ this.handleDateSelection(data.date);
93
+ break;
94
+
95
+ case 'monthSelected':
96
+ this.currentMonth = data.month;
97
+ this.currentView = DATEPICKER_VIEWS.DAY;
98
+ this.updateCalendar();
99
+ break;
100
+
101
+ case 'yearSelected':
102
+ this.currentYear = data.year;
103
+ this.currentView = DATEPICKER_VIEWS.MONTH;
104
+ this.updateCalendar();
105
+ break;
106
+
107
+ case 'viewChange':
108
+ this.currentView = data.view;
109
+ this.updateCalendar();
110
+ break;
111
+
112
+ case 'prevMonth':
113
+ this.prevMonth();
114
+ break;
115
+
116
+ case 'nextMonth':
117
+ this.nextMonth();
118
+ break;
119
+
120
+ case 'prevYear':
121
+ this.prevYear();
122
+ break;
123
+
124
+ case 'nextYear':
125
+ this.nextYear();
126
+ break;
127
+
128
+ case 'prevYearRange':
129
+ this.currentYear -= 20;
130
+ this.updateCalendar();
131
+ break;
132
+
133
+ case 'nextYearRange':
134
+ this.currentYear += 20;
135
+ this.updateCalendar();
136
+ break;
137
+
138
+ case 'cancel':
139
+ this.isOpen = false;
140
+ this.render();
141
+ break;
142
+
143
+ case 'confirm':
144
+ this.isOpen = false;
145
+ this.render();
146
+ break;
147
+ }
148
+ });
149
+
150
+ this.calendarElement.appendChild(calendar);
151
+ },
152
+
153
+ handleDateSelection(date: Date): void {
154
+ // Range selection
155
+ if (this.selectionMode === DATEPICKER_SELECTION_MODES.RANGE) {
156
+ // If no date is selected yet or both dates are already selected, start a new range
157
+ if (!this.selectedDate || (this.selectedDate && this.rangeEndDate)) {
158
+ this.selectedDate = date;
159
+ this.rangeEndDate = null;
160
+ }
161
+ // If start date is selected but not end date
162
+ else {
163
+ // If selected date is before the start date, swap them
164
+ if (date < this.selectedDate) {
165
+ this.rangeEndDate = this.selectedDate;
166
+ this.selectedDate = date;
167
+ } else {
168
+ this.rangeEndDate = date;
169
+ }
170
+
171
+ // Close if needed
172
+ if (this.closeOnSelect) {
173
+ this.isOpen = false;
174
+ }
175
+ }
176
+ }
177
+ // Single date selection
178
+ else {
179
+ this.selectedDate = date;
180
+
181
+ // Close if needed
182
+ if (this.closeOnSelect) {
183
+ this.isOpen = false;
184
+ }
185
+ }
186
+
187
+ // Update the input value
188
+ this.updateInputValue();
189
+
190
+ // Update the calendar view
191
+ this.updateCalendar();
192
+
193
+ // Emit change event
194
+ component.emit('change', {
195
+ value: this.selectedDate,
196
+ rangeEndDate: this.rangeEndDate,
197
+ formattedValue: this.input.value
198
+ });
199
+ },
200
+
201
+ prevMonth(): void {
202
+ if (this.currentMonth === 0) {
203
+ this.currentMonth = 11;
204
+ this.currentYear--;
205
+ } else {
206
+ this.currentMonth--;
207
+ }
208
+ this.updateCalendar();
209
+ },
210
+
211
+ nextMonth(): void {
212
+ if (this.currentMonth === 11) {
213
+ this.currentMonth = 0;
214
+ this.currentYear++;
215
+ } else {
216
+ this.currentMonth++;
217
+ }
218
+ this.updateCalendar();
219
+ },
220
+
221
+ prevYear(): void {
222
+ this.currentYear--;
223
+ this.updateCalendar();
224
+ },
225
+
226
+ nextYear(): void {
227
+ this.currentYear++;
228
+ this.updateCalendar();
229
+ },
230
+
231
+ render(): void {
232
+ // Show/hide calendar
233
+ if (this.calendarElement) {
234
+ if (this.isOpen) {
235
+ this.calendarElement.style.display = 'block';
236
+ this.updateCalendar();
237
+
238
+ // Focus on the calendar
239
+ setTimeout(() => {
240
+ this.calendarElement.focus();
241
+ }, 10);
242
+ } else {
243
+ this.calendarElement.style.display = 'none';
244
+ }
245
+ }
246
+ }
247
+ };
248
+
249
+ // Initialize with provided value if any
250
+ if (baseConfig.value) {
251
+ if (Array.isArray(baseConfig.value) && baseConfig.selectionMode === DATEPICKER_SELECTION_MODES.RANGE) {
252
+ const start = parseDate(baseConfig.value[0]);
253
+ const end = parseDate(baseConfig.value[1]);
254
+
255
+ if (start && end) {
256
+ state.selectedDate = start;
257
+ state.rangeEndDate = end;
258
+
259
+ // Set current month/year to selected date
260
+ state.currentMonth = start.getMonth();
261
+ state.currentYear = start.getFullYear();
262
+ }
263
+ } else {
264
+ const date = parseDate(baseConfig.value as Date | string);
265
+ if (date) {
266
+ state.selectedDate = date;
267
+ state.currentMonth = date.getMonth();
268
+ state.currentYear = date.getFullYear();
269
+ }
270
+ }
271
+ }
272
+
273
+ // Create the component
274
+ const component = pipe(
275
+ createBase,
276
+ withEvents(),
277
+ withElement(getContainerConfig(baseConfig)),
278
+ withDisabled(baseConfig),
279
+ withLifecycle()
280
+ )(baseConfig);
281
+
282
+ // Create input element
283
+ const inputComponent = pipe(
284
+ createBase,
285
+ withElement(getInputConfig(baseConfig))
286
+ )(baseConfig);
287
+
288
+ state.input = inputComponent.element;
289
+ component.element.appendChild(state.input);
290
+
291
+ // Update input value
292
+ state.updateInputValue();
293
+
294
+ // Create calendar element
295
+ const calendarConfig = getCalendarConfig(baseConfig);
296
+ state.calendarElement = createElement({
297
+ tag: 'div',
298
+ className: calendarConfig.className,
299
+ attrs: calendarConfig.attrs
300
+ });
301
+
302
+ // Initially hide calendar
303
+ state.calendarElement.style.display = 'none';
304
+
305
+ // Add calendar to container
306
+ component.element.appendChild(state.calendarElement);
307
+
308
+ // Add event listeners
309
+ state.input.addEventListener('click', () => {
310
+ if (!component.element.classList.contains('disabled')) {
311
+ state.isOpen = !state.isOpen;
312
+ state.render();
313
+
314
+ if (state.isOpen) {
315
+ component.emit('open', { value: state.selectedDate });
316
+ } else {
317
+ component.emit('close', { value: state.selectedDate });
318
+ }
319
+ }
320
+ });
321
+
322
+ // BUGFIX: Prevent calendar close when clicking inside the calendar
323
+ state.calendarElement.addEventListener('click', (event) => {
324
+ // Stop propagation to prevent the document click handler from closing the calendar
325
+ event.stopPropagation();
326
+ });
327
+
328
+ // Handle outside clicks
329
+ document.addEventListener('click', (event) => {
330
+ if (state.isOpen &&
331
+ !component.element.contains(event.target as Node)) {
332
+ state.isOpen = false;
333
+ state.render();
334
+ component.emit('close', { value: state.selectedDate });
335
+ }
336
+ });
337
+
338
+ // Enhance with API
339
+ return withAPI(state, getApiConfig(component))(component);
340
+ } catch (error) {
341
+ console.error('DatePicker creation error:', error);
342
+ throw new Error(`Failed to create DatePicker: ${(error as Error).message}`);
343
+ }
344
+ };
345
+
346
+ export default createDatePicker;
@@ -0,0 +1,9 @@
1
+ // src/components/datepicker/index.ts
2
+ export { default } from './datepicker'
3
+ export {
4
+ DATEPICKER_VARIANTS,
5
+ DATEPICKER_VIEWS,
6
+ DATEPICKER_SELECTION_MODES,
7
+ DEFAULT_DATE_FORMAT
8
+ } from './constants'
9
+ export { DatePickerConfig, DatePickerComponent } from './types'