wedux-ui 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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/miniprogram_dist/behaviors/formField.js +35 -0
  4. package/miniprogram_dist/components/avatar/avatar.js +70 -0
  5. package/miniprogram_dist/components/avatar/avatar.json +4 -0
  6. package/miniprogram_dist/components/avatar/avatar.scss +68 -0
  7. package/miniprogram_dist/components/avatar/avatar.wxml +15 -0
  8. package/miniprogram_dist/components/avatar-group/avatar-group.js +45 -0
  9. package/miniprogram_dist/components/avatar-group/avatar-group.json +7 -0
  10. package/miniprogram_dist/components/avatar-group/avatar-group.scss +4 -0
  11. package/miniprogram_dist/components/avatar-group/avatar-group.wxml +3 -0
  12. package/miniprogram_dist/components/back-top/back-top.js +68 -0
  13. package/miniprogram_dist/components/back-top/back-top.json +4 -0
  14. package/miniprogram_dist/components/back-top/back-top.scss +43 -0
  15. package/miniprogram_dist/components/back-top/back-top.wxml +10 -0
  16. package/miniprogram_dist/components/badge/badge.js +100 -0
  17. package/miniprogram_dist/components/badge/badge.json +4 -0
  18. package/miniprogram_dist/components/badge/badge.scss +98 -0
  19. package/miniprogram_dist/components/badge/badge.wxml +11 -0
  20. package/miniprogram_dist/components/button/button.js +152 -0
  21. package/miniprogram_dist/components/button/button.json +4 -0
  22. package/miniprogram_dist/components/button/button.scss +499 -0
  23. package/miniprogram_dist/components/button/button.wxml +21 -0
  24. package/miniprogram_dist/components/button-group/button-group.js +51 -0
  25. package/miniprogram_dist/components/button-group/button-group.json +4 -0
  26. package/miniprogram_dist/components/button-group/button-group.scss +8 -0
  27. package/miniprogram_dist/components/button-group/button-group.wxml +3 -0
  28. package/miniprogram_dist/components/calendar/calendar.js +308 -0
  29. package/miniprogram_dist/components/calendar/calendar.json +4 -0
  30. package/miniprogram_dist/components/calendar/calendar.scss +141 -0
  31. package/miniprogram_dist/components/calendar/calendar.wxml +47 -0
  32. package/miniprogram_dist/components/card/card.js +44 -0
  33. package/miniprogram_dist/components/card/card.json +4 -0
  34. package/miniprogram_dist/components/card/card.scss +143 -0
  35. package/miniprogram_dist/components/card/card.wxml +31 -0
  36. package/miniprogram_dist/components/checkbox/checkbox.js +43 -0
  37. package/miniprogram_dist/components/checkbox/checkbox.json +4 -0
  38. package/miniprogram_dist/components/checkbox/checkbox.scss +77 -0
  39. package/miniprogram_dist/components/checkbox/checkbox.wxml +13 -0
  40. package/miniprogram_dist/components/checkbox-button/checkbox-button.js +43 -0
  41. package/miniprogram_dist/components/checkbox-button/checkbox-button.json +4 -0
  42. package/miniprogram_dist/components/checkbox-button/checkbox-button.scss +39 -0
  43. package/miniprogram_dist/components/checkbox-button/checkbox-button.wxml +8 -0
  44. package/miniprogram_dist/components/checkbox-group/checkbox-group.js +84 -0
  45. package/miniprogram_dist/components/checkbox-group/checkbox-group.json +4 -0
  46. package/miniprogram_dist/components/checkbox-group/checkbox-group.scss +9 -0
  47. package/miniprogram_dist/components/checkbox-group/checkbox-group.wxml +3 -0
  48. package/miniprogram_dist/components/color-picker/color-picker.js +348 -0
  49. package/miniprogram_dist/components/color-picker/color-picker.json +7 -0
  50. package/miniprogram_dist/components/color-picker/color-picker.scss +383 -0
  51. package/miniprogram_dist/components/color-picker/color-picker.wxml +232 -0
  52. package/miniprogram_dist/components/date-picker/date-picker.js +1289 -0
  53. package/miniprogram_dist/components/date-picker/date-picker.json +7 -0
  54. package/miniprogram_dist/components/date-picker/date-picker.scss +468 -0
  55. package/miniprogram_dist/components/date-picker/date-picker.wxml +214 -0
  56. package/miniprogram_dist/components/divider/divider.js +34 -0
  57. package/miniprogram_dist/components/divider/divider.json +4 -0
  58. package/miniprogram_dist/components/divider/divider.scss +75 -0
  59. package/miniprogram_dist/components/divider/divider.wxml +8 -0
  60. package/miniprogram_dist/components/drawer/drawer.js +104 -0
  61. package/miniprogram_dist/components/drawer/drawer.json +4 -0
  62. package/miniprogram_dist/components/drawer/drawer.scss +171 -0
  63. package/miniprogram_dist/components/drawer/drawer.wxml +22 -0
  64. package/miniprogram_dist/components/ellipsis/ellipsis.js +38 -0
  65. package/miniprogram_dist/components/ellipsis/ellipsis.json +4 -0
  66. package/miniprogram_dist/components/ellipsis/ellipsis.scss +22 -0
  67. package/miniprogram_dist/components/ellipsis/ellipsis.wxml +7 -0
  68. package/miniprogram_dist/components/flex/flex.js +81 -0
  69. package/miniprogram_dist/components/flex/flex.json +4 -0
  70. package/miniprogram_dist/components/flex/flex.scss +4 -0
  71. package/miniprogram_dist/components/flex/flex.wxml +3 -0
  72. package/miniprogram_dist/components/float-button/float-button.js +78 -0
  73. package/miniprogram_dist/components/float-button/float-button.json +4 -0
  74. package/miniprogram_dist/components/float-button/float-button.scss +54 -0
  75. package/miniprogram_dist/components/float-button/float-button.wxml +9 -0
  76. package/miniprogram_dist/components/form/form.js +142 -0
  77. package/miniprogram_dist/components/form/form.json +4 -0
  78. package/miniprogram_dist/components/form/form.scss +11 -0
  79. package/miniprogram_dist/components/form/form.wxml +3 -0
  80. package/miniprogram_dist/components/form/validator.js +220 -0
  81. package/miniprogram_dist/components/form-item/form-item.js +240 -0
  82. package/miniprogram_dist/components/form-item/form-item.json +4 -0
  83. package/miniprogram_dist/components/form-item/form-item.scss +59 -0
  84. package/miniprogram_dist/components/form-item/form-item.wxml +33 -0
  85. package/miniprogram_dist/components/gradient-text/gradient-text.js +54 -0
  86. package/miniprogram_dist/components/gradient-text/gradient-text.json +4 -0
  87. package/miniprogram_dist/components/gradient-text/gradient-text.scss +7 -0
  88. package/miniprogram_dist/components/gradient-text/gradient-text.wxml +1 -0
  89. package/miniprogram_dist/components/h/h.js +60 -0
  90. package/miniprogram_dist/components/h/h.json +4 -0
  91. package/miniprogram_dist/components/h/h.scss +53 -0
  92. package/miniprogram_dist/components/h/h.wxml +1 -0
  93. package/miniprogram_dist/components/highlight/highlight.js +77 -0
  94. package/miniprogram_dist/components/highlight/highlight.json +4 -0
  95. package/miniprogram_dist/components/highlight/highlight.scss +8 -0
  96. package/miniprogram_dist/components/highlight/highlight.wxml +12 -0
  97. package/miniprogram_dist/components/infinite-scroll/infinite-scroll.js +31 -0
  98. package/miniprogram_dist/components/infinite-scroll/infinite-scroll.json +4 -0
  99. package/miniprogram_dist/components/infinite-scroll/infinite-scroll.scss +31 -0
  100. package/miniprogram_dist/components/infinite-scroll/infinite-scroll.wxml +12 -0
  101. package/miniprogram_dist/components/input/input.js +59 -0
  102. package/miniprogram_dist/components/input/input.json +4 -0
  103. package/miniprogram_dist/components/input/input.scss +96 -0
  104. package/miniprogram_dist/components/input/input.wxml +34 -0
  105. package/miniprogram_dist/components/input-otp/input-otp.js +106 -0
  106. package/miniprogram_dist/components/input-otp/input-otp.json +4 -0
  107. package/miniprogram_dist/components/input-otp/input-otp.scss +122 -0
  108. package/miniprogram_dist/components/input-otp/input-otp.wxml +38 -0
  109. package/miniprogram_dist/components/layout/layout.js +50 -0
  110. package/miniprogram_dist/components/layout/layout.json +4 -0
  111. package/miniprogram_dist/components/layout/layout.scss +10 -0
  112. package/miniprogram_dist/components/layout/layout.wxml +3 -0
  113. package/miniprogram_dist/components/layout-content/layout-content.js +47 -0
  114. package/miniprogram_dist/components/layout-content/layout-content.json +4 -0
  115. package/miniprogram_dist/components/layout-content/layout-content.scss +5 -0
  116. package/miniprogram_dist/components/layout-content/layout-content.wxml +13 -0
  117. package/miniprogram_dist/components/layout-footer/layout-footer.js +59 -0
  118. package/miniprogram_dist/components/layout-footer/layout-footer.json +4 -0
  119. package/miniprogram_dist/components/layout-footer/layout-footer.scss +23 -0
  120. package/miniprogram_dist/components/layout-footer/layout-footer.wxml +16 -0
  121. package/miniprogram_dist/components/layout-header/layout-header.js +59 -0
  122. package/miniprogram_dist/components/layout-header/layout-header.json +4 -0
  123. package/miniprogram_dist/components/layout-header/layout-header.scss +23 -0
  124. package/miniprogram_dist/components/layout-header/layout-header.wxml +16 -0
  125. package/miniprogram_dist/components/layout-sider/layout-sider.js +48 -0
  126. package/miniprogram_dist/components/layout-sider/layout-sider.json +4 -0
  127. package/miniprogram_dist/components/layout-sider/layout-sider.scss +13 -0
  128. package/miniprogram_dist/components/layout-sider/layout-sider.wxml +5 -0
  129. package/miniprogram_dist/components/list/list.js +55 -0
  130. package/miniprogram_dist/components/list/list.json +4 -0
  131. package/miniprogram_dist/components/list/list.scss +51 -0
  132. package/miniprogram_dist/components/list/list.wxml +9 -0
  133. package/miniprogram_dist/components/list-item/list-item.js +24 -0
  134. package/miniprogram_dist/components/list-item/list-item.json +4 -0
  135. package/miniprogram_dist/components/list-item/list-item.scss +51 -0
  136. package/miniprogram_dist/components/list-item/list-item.wxml +14 -0
  137. package/miniprogram_dist/components/navigation-bar/navigation-bar.js +77 -0
  138. package/miniprogram_dist/components/navigation-bar/navigation-bar.json +4 -0
  139. package/miniprogram_dist/components/navigation-bar/navigation-bar.scss +63 -0
  140. package/miniprogram_dist/components/navigation-bar/navigation-bar.wxml +35 -0
  141. package/miniprogram_dist/components/number-animation/number-animation.js +124 -0
  142. package/miniprogram_dist/components/number-animation/number-animation.json +4 -0
  143. package/miniprogram_dist/components/number-animation/number-animation.scss +3 -0
  144. package/miniprogram_dist/components/number-animation/number-animation.wxml +1 -0
  145. package/miniprogram_dist/components/popover/popover.js +183 -0
  146. package/miniprogram_dist/components/popover/popover.json +4 -0
  147. package/miniprogram_dist/components/popover/popover.scss +69 -0
  148. package/miniprogram_dist/components/popover/popover.wxml +19 -0
  149. package/miniprogram_dist/components/qr-code/qr-code.js +216 -0
  150. package/miniprogram_dist/components/qr-code/qr-code.json +4 -0
  151. package/miniprogram_dist/components/qr-code/qr-code.scss +8 -0
  152. package/miniprogram_dist/components/qr-code/qr-code.wxml +16 -0
  153. package/miniprogram_dist/components/radio/radio.js +38 -0
  154. package/miniprogram_dist/components/radio/radio.json +4 -0
  155. package/miniprogram_dist/components/radio/radio.scss +50 -0
  156. package/miniprogram_dist/components/radio/radio.wxml +12 -0
  157. package/miniprogram_dist/components/radio-button/radio-button.js +39 -0
  158. package/miniprogram_dist/components/radio-button/radio-button.json +4 -0
  159. package/miniprogram_dist/components/radio-button/radio-button.scss +39 -0
  160. package/miniprogram_dist/components/radio-button/radio-button.wxml +8 -0
  161. package/miniprogram_dist/components/radio-group/radio-group.js +72 -0
  162. package/miniprogram_dist/components/radio-group/radio-group.json +4 -0
  163. package/miniprogram_dist/components/radio-group/radio-group.scss +9 -0
  164. package/miniprogram_dist/components/radio-group/radio-group.wxml +3 -0
  165. package/miniprogram_dist/components/rate/rate.js +90 -0
  166. package/miniprogram_dist/components/rate/rate.json +4 -0
  167. package/miniprogram_dist/components/rate/rate.scss +59 -0
  168. package/miniprogram_dist/components/rate/rate.wxml +16 -0
  169. package/miniprogram_dist/components/select/select.js +201 -0
  170. package/miniprogram_dist/components/select/select.json +7 -0
  171. package/miniprogram_dist/components/select/select.scss +235 -0
  172. package/miniprogram_dist/components/select/select.wxml +79 -0
  173. package/miniprogram_dist/components/stepper/stepper.js +113 -0
  174. package/miniprogram_dist/components/stepper/stepper.json +4 -0
  175. package/miniprogram_dist/components/stepper/stepper.scss +73 -0
  176. package/miniprogram_dist/components/stepper/stepper.wxml +23 -0
  177. package/miniprogram_dist/components/switch/switch.js +42 -0
  178. package/miniprogram_dist/components/switch/switch.json +4 -0
  179. package/miniprogram_dist/components/switch/switch.scss +77 -0
  180. package/miniprogram_dist/components/switch/switch.wxml +10 -0
  181. package/miniprogram_dist/components/tab-bar/tab-bar.js +53 -0
  182. package/miniprogram_dist/components/tab-bar/tab-bar.json +4 -0
  183. package/miniprogram_dist/components/tab-bar/tab-bar.scss +53 -0
  184. package/miniprogram_dist/components/tab-bar/tab-bar.wxml +12 -0
  185. package/miniprogram_dist/components/tag/tag.js +87 -0
  186. package/miniprogram_dist/components/tag/tag.json +4 -0
  187. package/miniprogram_dist/components/tag/tag.scss +138 -0
  188. package/miniprogram_dist/components/tag/tag.wxml +12 -0
  189. package/miniprogram_dist/components/textarea/textarea.js +46 -0
  190. package/miniprogram_dist/components/textarea/textarea.json +4 -0
  191. package/miniprogram_dist/components/textarea/textarea.scss +51 -0
  192. package/miniprogram_dist/components/textarea/textarea.wxml +20 -0
  193. package/miniprogram_dist/components/theme-provider/presets.js +101 -0
  194. package/miniprogram_dist/components/theme-provider/theme-provider.js +34 -0
  195. package/miniprogram_dist/components/theme-provider/theme-provider.json +4 -0
  196. package/miniprogram_dist/components/theme-provider/theme-provider.scss +3 -0
  197. package/miniprogram_dist/components/theme-provider/theme-provider.wxml +3 -0
  198. package/miniprogram_dist/components/time-picker/time-picker.js +136 -0
  199. package/miniprogram_dist/components/time-picker/time-picker.json +7 -0
  200. package/miniprogram_dist/components/time-picker/time-picker.scss +135 -0
  201. package/miniprogram_dist/components/time-picker/time-picker.wxml +47 -0
  202. package/miniprogram_dist/components/tooltip/tooltip.js +179 -0
  203. package/miniprogram_dist/components/tooltip/tooltip.json +4 -0
  204. package/miniprogram_dist/components/tooltip/tooltip.scss +66 -0
  205. package/miniprogram_dist/components/tooltip/tooltip.wxml +17 -0
  206. package/miniprogram_dist/components/tree/tree.js +647 -0
  207. package/miniprogram_dist/components/tree/tree.json +4 -0
  208. package/miniprogram_dist/components/tree/tree.scss +178 -0
  209. package/miniprogram_dist/components/tree/tree.wxml +59 -0
  210. package/miniprogram_dist/components/tree-select/tree-select.js +260 -0
  211. package/miniprogram_dist/components/tree-select/tree-select.json +8 -0
  212. package/miniprogram_dist/components/tree-select/tree-select.scss +250 -0
  213. package/miniprogram_dist/components/tree-select/tree-select.wxml +118 -0
  214. package/miniprogram_dist/components/upload/upload.js +387 -0
  215. package/miniprogram_dist/components/upload/upload.json +4 -0
  216. package/miniprogram_dist/components/upload/upload.scss +258 -0
  217. package/miniprogram_dist/components/upload/upload.wxml +142 -0
  218. package/miniprogram_dist/components/watermark/watermark.js +260 -0
  219. package/miniprogram_dist/components/watermark/watermark.json +4 -0
  220. package/miniprogram_dist/components/watermark/watermark.scss +35 -0
  221. package/miniprogram_dist/components/watermark/watermark.wxml +13 -0
  222. package/miniprogram_dist/libs/qrcodegen.js +714 -0
  223. package/miniprogram_dist/libs/seemly.min.js +547 -0
  224. package/miniprogram_dist/libs/tempo_1_0_0.js +1264 -0
  225. package/miniprogram_dist/libs/validator_13_56_26.min.js +5282 -0
  226. package/miniprogram_dist/styles/demo.scss +96 -0
  227. package/miniprogram_dist/styles/iconfont.scss +91 -0
  228. package/miniprogram_dist/styles/theme.scss +126 -0
  229. package/miniprogram_dist/styles/tokens.scss +292 -0
  230. package/miniprogram_dist/utils/relations.js +21 -0
  231. package/package.json +25 -0
@@ -0,0 +1,1289 @@
1
+ import { format, monthDays } from '../../libs/tempo_1_0_0';
2
+
3
+ const pad = (n) => String(n).padStart(2, '0');
4
+
5
+ const QUARTER_LABELS = ['一季度 (1-3月)', '二季度 (4-6月)', '三季度 (7-9月)', '四季度 (10-12月)'];
6
+ const QUARTER_SHORT = ['一季度', '二季度', '三季度', '四季度'];
7
+
8
+ const RANGE_TYPES = ['daterange', 'datetimerange', 'monthrange', 'yearrange', 'quarterrange'];
9
+
10
+ const DEFAULT_FORMATS = {
11
+ date: 'YYYY-MM-DD',
12
+ datetime: 'YYYY-MM-DD HH:mm:ss',
13
+ month: 'YYYY-MM',
14
+ year: 'YYYY',
15
+ daterange: 'YYYY-MM-DD',
16
+ datetimerange: 'MM-DD HH:mm',
17
+ monthrange: 'YYYY-MM',
18
+ yearrange: 'YYYY',
19
+ quarter: '',
20
+ quarterrange: '',
21
+ week: '',
22
+ };
23
+
24
+ const formField = require('../../behaviors/formField');
25
+
26
+ Component({
27
+ behaviors: ['wx://form-field', formField],
28
+
29
+ relations: {
30
+ '../form-item/form-item': {
31
+ type: 'ancestor',
32
+ },
33
+ },
34
+
35
+ properties: {
36
+ value: { type: null, value: null },
37
+ type: { type: String, value: 'date' },
38
+ placeholder: { type: String, value: '请选择日期' },
39
+ disabled: { type: Boolean, value: false },
40
+ readonly: { type: Boolean, value: false },
41
+ size: { type: String, value: '' },
42
+ status: { type: String, value: '' },
43
+ clearable: { type: Boolean, value: false },
44
+ format: { type: String, value: '' },
45
+ weekStartsOn: { type: Number, value: 1 },
46
+ },
47
+
48
+ data: {
49
+ _visible: false,
50
+ _panelMode: 'date',
51
+ _displayYear: 2024,
52
+ _displayMonth: 0,
53
+ _days: [],
54
+ _weekDays: ['日', '一', '二', '三', '四', '五', '六'],
55
+ _monthLabels: [
56
+ '1月',
57
+ '2月',
58
+ '3月',
59
+ '4月',
60
+ '5月',
61
+ '6月',
62
+ '7月',
63
+ '8月',
64
+ '9月',
65
+ '10月',
66
+ '11月',
67
+ '12月',
68
+ ],
69
+ _yearList: [],
70
+ _yearRangeStart: 2020,
71
+ _selectedYear: -1,
72
+ _selectedMonth: -1,
73
+ _selectedDay: -1,
74
+ _panelTitle: '',
75
+ _displayText: '',
76
+ _timeHours: [],
77
+ _timeMinutes: [],
78
+ _timeSeconds: [],
79
+ _timePickerValue: [0, 0, 0],
80
+
81
+ // Range fields
82
+ _isRange: false,
83
+ _rangeStart: null,
84
+ _rangeEnd: null,
85
+ _activeEndpoint: 'start',
86
+ _rangeStartText: '',
87
+ _rangeEndText: '',
88
+ _startTimePickerValue: [0, 0, 0],
89
+ _endTimePickerValue: [0, 0, 0],
90
+
91
+ // Quarter fields
92
+ _quarterLabels: QUARTER_LABELS,
93
+ _selectedQuarter: -1,
94
+ _quarterList: [],
95
+
96
+ // Week fields
97
+ _selectedWeekStart: null,
98
+ _selectedWeekEnd: null,
99
+
100
+ // Month list for range types (objects with state)
101
+ _monthList: [],
102
+ // Year list for range types (objects with state)
103
+ _yearObjList: [],
104
+ },
105
+
106
+ lifetimes: {
107
+ attached() {
108
+ this._initPanel();
109
+ },
110
+ },
111
+
112
+ observers: {
113
+ value() {
114
+ this._syncFromValue();
115
+ },
116
+ },
117
+
118
+ methods: {
119
+ _isRangeType() {
120
+ return RANGE_TYPES.includes(this.data.type);
121
+ },
122
+
123
+ _getDefaultFormat() {
124
+ return this.data.format || DEFAULT_FORMATS[this.data.type] || 'YYYY-MM-DD';
125
+ },
126
+
127
+ _getQuarter(month) {
128
+ return Math.floor(month / 3);
129
+ },
130
+
131
+ _formatQuarter(year, quarter) {
132
+ return `${year}年${QUARTER_SHORT[quarter]}`;
133
+ },
134
+
135
+ _getWeekNumber(date) {
136
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
137
+ d.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7));
138
+ const jan4 = new Date(d.getFullYear(), 0, 4);
139
+ return (
140
+ 1 +
141
+ Math.round(((d.getTime() - jan4.getTime()) / 86400000 - 3 + ((jan4.getDay() + 6) % 7)) / 7)
142
+ );
143
+ },
144
+
145
+ _getWeekRange(year, month, day) {
146
+ const weekStartsOn = this.data.weekStartsOn;
147
+ const d = new Date(year, month, day);
148
+ const dow = d.getDay();
149
+ const diff = (dow - weekStartsOn + 7) % 7;
150
+ const start = new Date(year, month, day - diff);
151
+ const end = new Date(start.getFullYear(), start.getMonth(), start.getDate() + 6);
152
+ return { start, end };
153
+ },
154
+
155
+ _formatWeek(date) {
156
+ const weekNum = this._getWeekNumber(date);
157
+ return `${date.getFullYear()}年第${weekNum}周`;
158
+ },
159
+
160
+ _dateToObj(d) {
161
+ return { year: d.getFullYear(), month: d.getMonth(), day: d.getDate() };
162
+ },
163
+
164
+ _compareDates(a, b) {
165
+ if (!a || !b) return 0;
166
+ if (a.year !== b.year) return a.year - b.year;
167
+ if (a.month !== b.month) return a.month - b.month;
168
+ return (a.day || 0) - (b.day || 0);
169
+ },
170
+
171
+ _isSameDate(a, b) {
172
+ if (!a || !b) return false;
173
+ return a.year === b.year && a.month === b.month && a.day === b.day;
174
+ },
175
+
176
+ _isBetweenDates(date, start, end) {
177
+ if (!start || !end || !date) return false;
178
+ return this._compareDates(date, start) > 0 && this._compareDates(date, end) < 0;
179
+ },
180
+
181
+ _isSameMonth(a, b) {
182
+ if (!a || !b) return false;
183
+ return a.year === b.year && a.month === b.month;
184
+ },
185
+
186
+ _isBetweenMonths(m, start, end) {
187
+ if (!start || !end) return false;
188
+ const mv = m.year * 12 + m.month;
189
+ const sv = start.year * 12 + start.month;
190
+ const ev = end.year * 12 + end.month;
191
+ return mv > sv && mv < ev;
192
+ },
193
+
194
+ _isSameQuarter(a, b) {
195
+ if (!a || !b) return false;
196
+ return a.year === b.year && a.quarter === b.quarter;
197
+ },
198
+
199
+ _isBetweenQuarters(q, start, end) {
200
+ if (!start || !end) return false;
201
+ const qv = q.year * 4 + q.quarter;
202
+ const sv = start.year * 4 + start.quarter;
203
+ const ev = end.year * 4 + end.quarter;
204
+ return qv > sv && qv < ev;
205
+ },
206
+
207
+ // ----- Init / Sync -----
208
+
209
+ _initPanel() {
210
+ const now = new Date();
211
+ const { type } = this.data;
212
+ const isRange = this._isRangeType();
213
+
214
+ if (type === 'datetime' || type === 'datetimerange') {
215
+ this._buildTimeColumns();
216
+ }
217
+
218
+ const panelMode = this._getInitialPanelMode();
219
+
220
+ const weekDays =
221
+ type === 'week' ? this._buildWeekDayHeaders() : ['日', '一', '二', '三', '四', '五', '六'];
222
+
223
+ this.setData({
224
+ _panelMode: panelMode,
225
+ _displayYear: now.getFullYear(),
226
+ _displayMonth: now.getMonth(),
227
+ _yearRangeStart: now.getFullYear() - (now.getFullYear() % 10),
228
+ _isRange: isRange,
229
+ _weekDays: weekDays,
230
+ });
231
+
232
+ this._syncFromValue();
233
+ this._buildPanel();
234
+ },
235
+
236
+ _getInitialPanelMode() {
237
+ const { type } = this.data;
238
+ if (type === 'month' || type === 'monthrange') return 'month';
239
+ if (type === 'year' || type === 'yearrange') return 'year';
240
+ if (type === 'quarter' || type === 'quarterrange') return 'quarter';
241
+ return 'date';
242
+ },
243
+
244
+ _buildWeekDayHeaders() {
245
+ const labels = ['日', '一', '二', '三', '四', '五', '六'];
246
+ const { weekStartsOn } = this.data;
247
+ if (weekStartsOn === 0) return labels;
248
+ return [...labels.slice(weekStartsOn), ...labels.slice(0, weekStartsOn)];
249
+ },
250
+
251
+ _syncFromValue() {
252
+ const { value, type } = this.data;
253
+ const isRange = this._isRangeType();
254
+
255
+ if (isRange) {
256
+ this._syncRangeFromValue();
257
+ } else if (type === 'quarter') {
258
+ this._syncQuarterFromValue();
259
+ } else if (type === 'week') {
260
+ this._syncWeekFromValue();
261
+ } else {
262
+ this._syncSingleFromValue();
263
+ }
264
+ this._buildPanel();
265
+ },
266
+
267
+ _syncSingleFromValue() {
268
+ const { value, type, _timeHours, _timeMinutes, _timeSeconds } = this.data;
269
+ if (!value && value !== 0) {
270
+ this.setData({
271
+ _displayText: '',
272
+ _selectedYear: -1,
273
+ _selectedMonth: -1,
274
+ _selectedDay: -1,
275
+ });
276
+ return;
277
+ }
278
+ const d = new Date(value);
279
+ if (isNaN(d.getTime())) return;
280
+
281
+ const fmt = this._getDefaultFormat();
282
+ const updates = {
283
+ _displayText: format(d, fmt),
284
+ _selectedYear: d.getFullYear(),
285
+ _selectedMonth: d.getMonth(),
286
+ _selectedDay: d.getDate(),
287
+ _displayYear: d.getFullYear(),
288
+ _displayMonth: d.getMonth(),
289
+ };
290
+
291
+ if (type === 'year') {
292
+ updates._yearRangeStart = d.getFullYear() - (d.getFullYear() % 10);
293
+ }
294
+
295
+ if (type === 'datetime' && _timeHours.length) {
296
+ updates._timePickerValue = [
297
+ _timeHours.indexOf(pad(d.getHours())),
298
+ _timeMinutes.indexOf(pad(d.getMinutes())),
299
+ _timeSeconds.indexOf(pad(d.getSeconds())),
300
+ ];
301
+ }
302
+
303
+ this.setData(updates);
304
+ },
305
+
306
+ _syncQuarterFromValue() {
307
+ const { value } = this.data;
308
+ if (!value && value !== 0) {
309
+ this.setData({ _displayText: '', _selectedQuarter: -1, _selectedYear: -1 });
310
+ return;
311
+ }
312
+ const d = new Date(value);
313
+ if (isNaN(d.getTime())) return;
314
+ const quarter = this._getQuarter(d.getMonth());
315
+ this.setData({
316
+ _displayText: this._formatQuarter(d.getFullYear(), quarter),
317
+ _selectedQuarter: quarter,
318
+ _selectedYear: d.getFullYear(),
319
+ _displayYear: d.getFullYear(),
320
+ });
321
+ },
322
+
323
+ _syncWeekFromValue() {
324
+ const { value } = this.data;
325
+ if (!value && value !== 0) {
326
+ this.setData({
327
+ _displayText: '',
328
+ _selectedWeekStart: null,
329
+ _selectedWeekEnd: null,
330
+ _selectedYear: -1,
331
+ });
332
+ return;
333
+ }
334
+ const d = new Date(value);
335
+ if (isNaN(d.getTime())) return;
336
+ const range = this._getWeekRange(d.getFullYear(), d.getMonth(), d.getDate());
337
+ this.setData({
338
+ _displayText: this._formatWeek(d),
339
+ _selectedWeekStart: this._dateToObj(range.start),
340
+ _selectedWeekEnd: this._dateToObj(range.end),
341
+ _selectedYear: d.getFullYear(),
342
+ _displayYear: d.getFullYear(),
343
+ _displayMonth: d.getMonth(),
344
+ });
345
+ },
346
+
347
+ _syncRangeFromValue() {
348
+ const { value, type } = this.data;
349
+ if (!Array.isArray(value) || value.length < 2) {
350
+ this.setData({
351
+ _displayText: '',
352
+ _rangeStart: null,
353
+ _rangeEnd: null,
354
+ _rangeStartText: '',
355
+ _rangeEndText: '',
356
+ });
357
+ return;
358
+ }
359
+
360
+ const startD = new Date(value[0]);
361
+ const endD = new Date(value[1]);
362
+ if (isNaN(startD.getTime()) || isNaN(endD.getTime())) return;
363
+
364
+ const startObj = this._dateToObj(startD);
365
+ const endObj = this._dateToObj(endD);
366
+
367
+ let startText, endText;
368
+ if (type === 'quarterrange') {
369
+ const sq = this._getQuarter(startD.getMonth());
370
+ const eq = this._getQuarter(endD.getMonth());
371
+ startObj.quarter = sq;
372
+ endObj.quarter = eq;
373
+ startText = this._formatQuarter(startD.getFullYear(), sq);
374
+ endText = this._formatQuarter(endD.getFullYear(), eq);
375
+ } else {
376
+ const fmt = this._getDefaultFormat();
377
+ if (type === 'datetimerange') {
378
+ startText = this._formatDatetimeRange(startD, endD, 'start');
379
+ endText = this._formatDatetimeRange(startD, endD, 'end');
380
+ } else {
381
+ startText = format(startD, fmt);
382
+ endText = format(endD, fmt);
383
+ }
384
+ }
385
+
386
+ const updates = {
387
+ _rangeStart: startObj,
388
+ _rangeEnd: endObj,
389
+ _rangeStartText: startText,
390
+ _rangeEndText: endText,
391
+ _displayText: `${startText} ~ ${endText}`,
392
+ _displayYear: startD.getFullYear(),
393
+ _displayMonth: startD.getMonth(),
394
+ };
395
+
396
+ if (type === 'yearrange') {
397
+ updates._yearRangeStart = startD.getFullYear() - (startD.getFullYear() % 10);
398
+ }
399
+
400
+ if (type === 'datetimerange' && this.data._timeHours.length) {
401
+ updates._startTimePickerValue = [
402
+ this.data._timeHours.indexOf(pad(startD.getHours())),
403
+ this.data._timeMinutes.indexOf(pad(startD.getMinutes())),
404
+ this.data._timeSeconds.indexOf(pad(startD.getSeconds())),
405
+ ];
406
+ updates._endTimePickerValue = [
407
+ this.data._timeHours.indexOf(pad(endD.getHours())),
408
+ this.data._timeMinutes.indexOf(pad(endD.getMinutes())),
409
+ this.data._timeSeconds.indexOf(pad(endD.getSeconds())),
410
+ ];
411
+ }
412
+
413
+ this.setData(updates);
414
+ },
415
+
416
+ _formatDatetimeRange(startD, endD, which) {
417
+ const d = which === 'start' ? startD : endD;
418
+ const fmt = this.data.format || '';
419
+ if (fmt) return format(d, fmt);
420
+ const crossYear = startD.getFullYear() !== endD.getFullYear();
421
+ if (crossYear) {
422
+ return format(d, 'YY/MM/DD HH:mm');
423
+ }
424
+ return format(d, 'MM-DD HH:mm');
425
+ },
426
+
427
+ // ----- Build -----
428
+
429
+ _buildPanel() {
430
+ this._updatePanelTitle();
431
+ const mode = this.data._panelMode;
432
+ if (mode === 'date') this._buildCalendar();
433
+ else if (mode === 'year') this._buildYearList();
434
+ else if (mode === 'month') this._buildMonthList();
435
+ else if (mode === 'quarter') this._buildQuarterList();
436
+ },
437
+
438
+ _buildCalendar() {
439
+ const { _displayYear: year, _displayMonth: month, type } = this.data;
440
+ const daysInMonth = monthDays(new Date(year, month, 1));
441
+ const firstDay = new Date(year, month, 1).getDay();
442
+ const weekStartsOn = type === 'week' ? this.data.weekStartsOn : 0;
443
+ const adjustedFirstDay = (firstDay - weekStartsOn + 7) % 7;
444
+
445
+ const today = new Date();
446
+ const ty = today.getFullYear();
447
+ const tm = today.getMonth();
448
+ const td = today.getDate();
449
+
450
+ const days = [];
451
+
452
+ const prevMonth = month === 0 ? 11 : month - 1;
453
+ const prevYear = month === 0 ? year - 1 : year;
454
+ const daysInPrevMonth = monthDays(new Date(prevYear, prevMonth, 1));
455
+
456
+ for (let i = adjustedFirstDay - 1; i >= 0; i--) {
457
+ const d = daysInPrevMonth - i;
458
+ days.push(this._buildDayObj(prevYear, prevMonth, d, false, ty, tm, td));
459
+ }
460
+
461
+ for (let d = 1; d <= daysInMonth; d++) {
462
+ days.push(this._buildDayObj(year, month, d, true, ty, tm, td));
463
+ }
464
+
465
+ const nextMonth = month === 11 ? 0 : month + 1;
466
+ const nextYear = month === 11 ? year + 1 : year;
467
+ const remaining = 42 - days.length;
468
+ for (let d = 1; d <= remaining; d++) {
469
+ days.push(this._buildDayObj(nextYear, nextMonth, d, false, ty, tm, td));
470
+ }
471
+
472
+ this.setData({ _days: days });
473
+ },
474
+
475
+ _buildDayObj(year, month, day, isCurrentMonth, ty, tm, td) {
476
+ const { type, _selectedYear: sy, _selectedMonth: sm, _selectedDay: sd } = this.data;
477
+ const obj = {
478
+ day,
479
+ year,
480
+ month,
481
+ isCurrentMonth,
482
+ isSelected: false,
483
+ isToday: year === ty && month === tm && day === td,
484
+ isRangeStart: false,
485
+ isRangeEnd: false,
486
+ isInRange: false,
487
+ };
488
+
489
+ const dateObj = { year, month, day };
490
+
491
+ if (type === 'week') {
492
+ const { _selectedWeekStart, _selectedWeekEnd } = this.data;
493
+ if (_selectedWeekStart && _selectedWeekEnd) {
494
+ obj.isRangeStart = this._isSameDate(dateObj, _selectedWeekStart);
495
+ obj.isRangeEnd = this._isSameDate(dateObj, _selectedWeekEnd);
496
+ obj.isInRange = this._isBetweenDates(dateObj, _selectedWeekStart, _selectedWeekEnd);
497
+ }
498
+ } else if (type === 'daterange' || type === 'datetimerange') {
499
+ const { _rangeStart, _rangeEnd } = this.data;
500
+ if (_rangeStart) {
501
+ obj.isRangeStart = this._isSameDate(dateObj, _rangeStart);
502
+ }
503
+ if (_rangeEnd) {
504
+ obj.isRangeEnd = this._isSameDate(dateObj, _rangeEnd);
505
+ }
506
+ if (_rangeStart && _rangeEnd) {
507
+ obj.isInRange = this._isBetweenDates(dateObj, _rangeStart, _rangeEnd);
508
+ }
509
+ } else {
510
+ obj.isSelected = year === sy && month === sm && day === sd;
511
+ }
512
+
513
+ return obj;
514
+ },
515
+
516
+ _buildMonthList() {
517
+ const { type, _displayYear, _selectedMonth, _selectedYear, _rangeStart, _rangeEnd } =
518
+ this.data;
519
+ const isRange = type === 'monthrange';
520
+ const labels = [
521
+ '1月',
522
+ '2月',
523
+ '3月',
524
+ '4月',
525
+ '5月',
526
+ '6月',
527
+ '7月',
528
+ '8月',
529
+ '9月',
530
+ '10月',
531
+ '11月',
532
+ '12月',
533
+ ];
534
+ const now = new Date();
535
+ const list = labels.map((label, i) => {
536
+ const mObj = { year: _displayYear, month: i };
537
+ const item = {
538
+ label,
539
+ month: i,
540
+ isSelected: false,
541
+ isRangeStart: false,
542
+ isRangeEnd: false,
543
+ isInRange: false,
544
+ isCurrent: _displayYear === now.getFullYear() && i === now.getMonth(),
545
+ };
546
+ if (isRange) {
547
+ if (_rangeStart) item.isRangeStart = this._isSameMonth(mObj, _rangeStart);
548
+ if (_rangeEnd) item.isRangeEnd = this._isSameMonth(mObj, _rangeEnd);
549
+ if (_rangeStart && _rangeEnd)
550
+ item.isInRange = this._isBetweenMonths(mObj, _rangeStart, _rangeEnd);
551
+ } else {
552
+ item.isSelected = _selectedYear === _displayYear && _selectedMonth === i;
553
+ }
554
+ return item;
555
+ });
556
+ this.setData({ _monthList: list });
557
+ },
558
+
559
+ _buildYearList() {
560
+ const start = this.data._yearRangeStart;
561
+ const { type, _selectedYear, _rangeStart, _rangeEnd } = this.data;
562
+ const isRange = type === 'yearrange';
563
+ const now = new Date();
564
+ const list = [];
565
+ for (let i = 0; i < 12; i++) {
566
+ const y = start + i;
567
+ const item = {
568
+ year: y,
569
+ isSelected: false,
570
+ isRangeStart: false,
571
+ isRangeEnd: false,
572
+ isInRange: false,
573
+ isCurrent: y === now.getFullYear(),
574
+ };
575
+ if (isRange) {
576
+ if (_rangeStart) item.isRangeStart = _rangeStart.year === y;
577
+ if (_rangeEnd) item.isRangeEnd = _rangeEnd.year === y;
578
+ if (_rangeStart && _rangeEnd) {
579
+ item.isInRange = y > _rangeStart.year && y < _rangeEnd.year;
580
+ }
581
+ } else {
582
+ item.isSelected = _selectedYear === y;
583
+ }
584
+ list.push(item);
585
+ }
586
+ this.setData({ _yearList: list, _yearObjList: list });
587
+ },
588
+
589
+ _buildQuarterList() {
590
+ const { type, _displayYear, _selectedQuarter, _selectedYear, _rangeStart, _rangeEnd } =
591
+ this.data;
592
+ const isRange = type === 'quarterrange';
593
+ const now = new Date();
594
+ const curQ = this._getQuarter(now.getMonth());
595
+ const list = QUARTER_LABELS.map((label, i) => {
596
+ const qObj = { year: _displayYear, quarter: i };
597
+ const item = {
598
+ label,
599
+ quarter: i,
600
+ isSelected: false,
601
+ isRangeStart: false,
602
+ isRangeEnd: false,
603
+ isInRange: false,
604
+ isCurrent: _displayYear === now.getFullYear() && i === curQ,
605
+ };
606
+ if (isRange) {
607
+ if (_rangeStart) item.isRangeStart = this._isSameQuarter(qObj, _rangeStart);
608
+ if (_rangeEnd) item.isRangeEnd = this._isSameQuarter(qObj, _rangeEnd);
609
+ if (_rangeStart && _rangeEnd)
610
+ item.isInRange = this._isBetweenQuarters(qObj, _rangeStart, _rangeEnd);
611
+ } else {
612
+ item.isSelected = _selectedYear === _displayYear && _selectedQuarter === i;
613
+ }
614
+ return item;
615
+ });
616
+ this.setData({ _quarterList: list });
617
+ },
618
+
619
+ _buildTimeColumns() {
620
+ const hours = [];
621
+ const minutes = [];
622
+ const seconds = [];
623
+ for (let i = 0; i < 24; i++) hours.push(pad(i));
624
+ for (let i = 0; i < 60; i++) minutes.push(pad(i));
625
+ for (let i = 0; i < 60; i++) seconds.push(pad(i));
626
+ this.setData({ _timeHours: hours, _timeMinutes: minutes, _timeSeconds: seconds });
627
+ },
628
+
629
+ _updatePanelTitle() {
630
+ const { _panelMode, _displayYear, _displayMonth, _yearRangeStart } = this.data;
631
+ let title = '';
632
+ if (_panelMode === 'date') {
633
+ title = `${_displayYear}年${_displayMonth + 1}月`;
634
+ } else if (_panelMode === 'month' || _panelMode === 'quarter') {
635
+ title = `${_displayYear}年`;
636
+ } else if (_panelMode === 'year') {
637
+ title = `${_yearRangeStart} – ${_yearRangeStart + 11}`;
638
+ }
639
+ this.setData({ _panelTitle: title });
640
+ },
641
+
642
+ _getHeaderTitle() {
643
+ const titles = {
644
+ date: '选择日期',
645
+ datetime: '选择日期时间',
646
+ month: '选择月份',
647
+ year: '选择年份',
648
+ daterange: '选择日期范围',
649
+ datetimerange: '选择日期时间范围',
650
+ monthrange: '选择月份范围',
651
+ yearrange: '选择年份范围',
652
+ quarter: '选择季度',
653
+ quarterrange: '选择季度范围',
654
+ week: '选择周',
655
+ };
656
+ return titles[this.data.type] || '选择日期';
657
+ },
658
+
659
+ // ----- Handlers -----
660
+
661
+ handleTap() {
662
+ if (this._isDisabled() || this.data.readonly) return;
663
+ const panelMode = this._getInitialPanelMode();
664
+
665
+ const updates = { _panelMode: panelMode };
666
+ if (this._isRangeType()) {
667
+ updates._activeEndpoint = 'start';
668
+ }
669
+
670
+ this.setData(updates);
671
+ this._buildPanel();
672
+ this.setData({ _visible: true });
673
+ },
674
+
675
+ handlePrev() {
676
+ const { _panelMode, _displayYear, _displayMonth, _yearRangeStart } = this.data;
677
+ if (_panelMode === 'date') {
678
+ let y = _displayYear;
679
+ let m = _displayMonth - 1;
680
+ if (m < 0) {
681
+ m = 11;
682
+ y--;
683
+ }
684
+ this.setData({ _displayYear: y, _displayMonth: m });
685
+ } else if (_panelMode === 'month' || _panelMode === 'quarter') {
686
+ this.setData({ _displayYear: _displayYear - 1 });
687
+ } else if (_panelMode === 'year') {
688
+ this.setData({ _yearRangeStart: _yearRangeStart - 12 });
689
+ }
690
+ this._buildPanel();
691
+ },
692
+
693
+ handleNext() {
694
+ const { _panelMode, _displayYear, _displayMonth, _yearRangeStart } = this.data;
695
+ if (_panelMode === 'date') {
696
+ let y = _displayYear;
697
+ let m = _displayMonth + 1;
698
+ if (m > 11) {
699
+ m = 0;
700
+ y++;
701
+ }
702
+ this.setData({ _displayYear: y, _displayMonth: m });
703
+ } else if (_panelMode === 'month' || _panelMode === 'quarter') {
704
+ this.setData({ _displayYear: _displayYear + 1 });
705
+ } else if (_panelMode === 'year') {
706
+ this.setData({ _yearRangeStart: _yearRangeStart + 12 });
707
+ }
708
+ this._buildPanel();
709
+ },
710
+
711
+ handleTitleTap() {
712
+ const { _panelMode, type } = this.data;
713
+ // Types that lock panel mode
714
+ if (['year', 'yearrange', 'month', 'monthrange', 'quarter', 'quarterrange'].includes(type))
715
+ return;
716
+ if (_panelMode === 'date') {
717
+ this.setData({ _panelMode: 'month' });
718
+ } else if (_panelMode === 'month') {
719
+ this.setData({ _panelMode: 'year' });
720
+ }
721
+ this._buildPanel();
722
+ },
723
+
724
+ handleDayTap(e) {
725
+ const { year, month, day } = e.currentTarget.dataset;
726
+ const { type } = this.data;
727
+
728
+ if (type === 'week') {
729
+ this._handleWeekDayTap(year, month, day);
730
+ } else if (type === 'daterange' || type === 'datetimerange') {
731
+ this._handleRangeDayTap(year, month, day);
732
+ } else {
733
+ this.setData({
734
+ _selectedYear: year,
735
+ _selectedMonth: month,
736
+ _selectedDay: day,
737
+ _displayYear: year,
738
+ _displayMonth: month,
739
+ });
740
+ this._buildCalendar();
741
+ }
742
+ },
743
+
744
+ _handleWeekDayTap(year, month, day) {
745
+ const range = this._getWeekRange(year, month, day);
746
+ this.setData({
747
+ _selectedWeekStart: this._dateToObj(range.start),
748
+ _selectedWeekEnd: this._dateToObj(range.end),
749
+ _displayYear: year,
750
+ _displayMonth: month,
751
+ });
752
+ this._buildCalendar();
753
+ },
754
+
755
+ _handleRangeDayTap(year, month, day) {
756
+ const { _rangeStart, _rangeEnd, _activeEndpoint } = this.data;
757
+ const tapped = { year, month, day };
758
+
759
+ if (_activeEndpoint === 'start') {
760
+ this.setData({
761
+ _rangeStart: tapped,
762
+ _rangeEnd: null,
763
+ _activeEndpoint: 'end',
764
+ _rangeStartText: format(new Date(year, month, day), 'YYYY-MM-DD'),
765
+ _rangeEndText: '',
766
+ _displayYear: year,
767
+ _displayMonth: month,
768
+ });
769
+ } else {
770
+ // Selecting end
771
+ if (_rangeStart && this._compareDates(tapped, _rangeStart) < 0) {
772
+ // Before start -> reset start
773
+ this.setData({
774
+ _rangeStart: tapped,
775
+ _rangeEnd: null,
776
+ _activeEndpoint: 'end',
777
+ _rangeStartText: format(new Date(year, month, day), 'YYYY-MM-DD'),
778
+ _rangeEndText: '',
779
+ });
780
+ } else {
781
+ this.setData({
782
+ _rangeEnd: tapped,
783
+ _rangeEndText: format(new Date(year, month, day), 'YYYY-MM-DD'),
784
+ });
785
+ }
786
+ }
787
+ this._buildCalendar();
788
+ },
789
+
790
+ handleMonthTap(e) {
791
+ const month = e.currentTarget.dataset.month;
792
+ const { type, _displayYear } = this.data;
793
+
794
+ if (type === 'monthrange') {
795
+ this._handleRangeMonthTap(_displayYear, month);
796
+ return;
797
+ }
798
+
799
+ this.setData({ _selectedMonth: month, _displayMonth: month });
800
+ if (type === 'month') {
801
+ this.setData({ _selectedYear: _displayYear });
802
+ } else {
803
+ // Drill back to date mode
804
+ this.setData({ _panelMode: 'date' });
805
+ this._buildPanel();
806
+ return;
807
+ }
808
+ this._updatePanelTitle();
809
+ this._buildMonthList();
810
+ },
811
+
812
+ _handleRangeMonthTap(year, month) {
813
+ const { _rangeStart, _activeEndpoint } = this.data;
814
+ const tapped = { year, month };
815
+
816
+ if (_activeEndpoint === 'start') {
817
+ this.setData({
818
+ _rangeStart: tapped,
819
+ _rangeEnd: null,
820
+ _activeEndpoint: 'end',
821
+ _rangeStartText: format(new Date(year, month, 1), 'YYYY-MM'),
822
+ _rangeEndText: '',
823
+ });
824
+ } else {
825
+ if (_rangeStart) {
826
+ const tappedVal = year * 12 + month;
827
+ const startVal = _rangeStart.year * 12 + _rangeStart.month;
828
+ if (tappedVal < startVal) {
829
+ this.setData({
830
+ _rangeStart: tapped,
831
+ _rangeEnd: null,
832
+ _activeEndpoint: 'end',
833
+ _rangeStartText: format(new Date(year, month, 1), 'YYYY-MM'),
834
+ _rangeEndText: '',
835
+ });
836
+ } else {
837
+ this.setData({
838
+ _rangeEnd: tapped,
839
+ _rangeEndText: format(new Date(year, month, 1), 'YYYY-MM'),
840
+ });
841
+ }
842
+ }
843
+ }
844
+ this._buildMonthList();
845
+ },
846
+
847
+ handleYearTap(e) {
848
+ const year = e.currentTarget.dataset.year;
849
+ const { type } = this.data;
850
+
851
+ if (type === 'yearrange') {
852
+ this._handleRangeYearTap(year);
853
+ return;
854
+ }
855
+
856
+ this.setData({ _selectedYear: year, _displayYear: year });
857
+ if (type === 'year') {
858
+ // stay
859
+ } else {
860
+ const backMode = ['month', 'monthrange'].includes(type)
861
+ ? 'month'
862
+ : ['quarter', 'quarterrange'].includes(type)
863
+ ? 'quarter'
864
+ : 'date';
865
+ this.setData({
866
+ _panelMode: backMode,
867
+ _yearRangeStart: year - (year % 10),
868
+ });
869
+ }
870
+ this._buildPanel();
871
+ },
872
+
873
+ _handleRangeYearTap(year) {
874
+ const { _rangeStart, _activeEndpoint } = this.data;
875
+
876
+ if (_activeEndpoint === 'start') {
877
+ this.setData({
878
+ _rangeStart: { year },
879
+ _rangeEnd: null,
880
+ _activeEndpoint: 'end',
881
+ _rangeStartText: `${year}`,
882
+ _rangeEndText: '',
883
+ });
884
+ } else {
885
+ if (_rangeStart && year < _rangeStart.year) {
886
+ this.setData({
887
+ _rangeStart: { year },
888
+ _rangeEnd: null,
889
+ _activeEndpoint: 'end',
890
+ _rangeStartText: `${year}`,
891
+ _rangeEndText: '',
892
+ });
893
+ } else {
894
+ this.setData({
895
+ _rangeEnd: { year },
896
+ _rangeEndText: `${year}`,
897
+ });
898
+ }
899
+ }
900
+ this._buildYearList();
901
+ },
902
+
903
+ handleQuarterTap(e) {
904
+ const quarter = e.currentTarget.dataset.quarter;
905
+ const { type, _displayYear } = this.data;
906
+
907
+ if (type === 'quarterrange') {
908
+ this._handleRangeQuarterTap(_displayYear, quarter);
909
+ return;
910
+ }
911
+
912
+ this.setData({
913
+ _selectedQuarter: quarter,
914
+ _selectedYear: _displayYear,
915
+ });
916
+ this._buildQuarterList();
917
+ },
918
+
919
+ _handleRangeQuarterTap(year, quarter) {
920
+ const { _rangeStart, _activeEndpoint } = this.data;
921
+ const tapped = { year, quarter };
922
+
923
+ if (_activeEndpoint === 'start') {
924
+ this.setData({
925
+ _rangeStart: tapped,
926
+ _rangeEnd: null,
927
+ _activeEndpoint: 'end',
928
+ _rangeStartText: this._formatQuarter(year, quarter),
929
+ _rangeEndText: '',
930
+ });
931
+ } else {
932
+ if (_rangeStart) {
933
+ const tappedVal = year * 4 + quarter;
934
+ const startVal = _rangeStart.year * 4 + _rangeStart.quarter;
935
+ if (tappedVal < startVal) {
936
+ this.setData({
937
+ _rangeStart: tapped,
938
+ _rangeEnd: null,
939
+ _activeEndpoint: 'end',
940
+ _rangeStartText: this._formatQuarter(year, quarter),
941
+ _rangeEndText: '',
942
+ });
943
+ } else {
944
+ this.setData({
945
+ _rangeEnd: tapped,
946
+ _rangeEndText: this._formatQuarter(year, quarter),
947
+ });
948
+ }
949
+ }
950
+ }
951
+ this._buildQuarterList();
952
+ },
953
+
954
+ handleEndpointTap(e) {
955
+ const endpoint = e.currentTarget.dataset.endpoint;
956
+ this.setData({ _activeEndpoint: endpoint });
957
+ },
958
+
959
+ handleTimeChange(e) {
960
+ const { type, _activeEndpoint } = this.data;
961
+ if (type === 'datetimerange') {
962
+ if (_activeEndpoint === 'start') {
963
+ this.setData({ _startTimePickerValue: e.detail.value });
964
+ } else {
965
+ this.setData({ _endTimePickerValue: e.detail.value });
966
+ }
967
+ } else {
968
+ this.setData({ _timePickerValue: e.detail.value });
969
+ }
970
+ },
971
+
972
+ handleShortcut(e) {
973
+ const action = e.currentTarget.dataset.action;
974
+ const now = new Date();
975
+ const { type } = this.data;
976
+
977
+ if (action === 'today') {
978
+ if (this._isRangeType()) {
979
+ const todayObj = this._dateToObj(now);
980
+ const todayFmt = format(now, 'YYYY-MM-DD');
981
+ this.setData({
982
+ _rangeStart: todayObj,
983
+ _rangeEnd: todayObj,
984
+ _rangeStartText: todayFmt,
985
+ _rangeEndText: todayFmt,
986
+ _displayYear: now.getFullYear(),
987
+ _displayMonth: now.getMonth(),
988
+ });
989
+ this._buildCalendar();
990
+ } else {
991
+ this.setData({
992
+ _selectedYear: now.getFullYear(),
993
+ _selectedMonth: now.getMonth(),
994
+ _selectedDay: now.getDate(),
995
+ _displayYear: now.getFullYear(),
996
+ _displayMonth: now.getMonth(),
997
+ });
998
+ this._buildCalendar();
999
+ }
1000
+ } else if (action === 'thisWeek') {
1001
+ const range = this._getWeekRange(now.getFullYear(), now.getMonth(), now.getDate());
1002
+ this.setData({
1003
+ _selectedWeekStart: this._dateToObj(range.start),
1004
+ _selectedWeekEnd: this._dateToObj(range.end),
1005
+ _displayYear: now.getFullYear(),
1006
+ _displayMonth: now.getMonth(),
1007
+ });
1008
+ this._buildCalendar();
1009
+ } else if (action === 'thisMonth') {
1010
+ if (type === 'monthrange') {
1011
+ const mObj = { year: now.getFullYear(), month: now.getMonth() };
1012
+ this.setData({
1013
+ _rangeStart: mObj,
1014
+ _rangeEnd: mObj,
1015
+ _rangeStartText: format(now, 'YYYY-MM'),
1016
+ _rangeEndText: format(now, 'YYYY-MM'),
1017
+ _displayYear: now.getFullYear(),
1018
+ });
1019
+ this._buildMonthList();
1020
+ }
1021
+ } else if (action === 'thisYear') {
1022
+ if (type === 'yearrange') {
1023
+ const yObj = { year: now.getFullYear() };
1024
+ this.setData({
1025
+ _rangeStart: yObj,
1026
+ _rangeEnd: yObj,
1027
+ _rangeStartText: `${now.getFullYear()}`,
1028
+ _rangeEndText: `${now.getFullYear()}`,
1029
+ _yearRangeStart: now.getFullYear() - (now.getFullYear() % 10),
1030
+ });
1031
+ this._buildYearList();
1032
+ }
1033
+ } else if (action === 'thisQuarter') {
1034
+ const curQ = this._getQuarter(now.getMonth());
1035
+ if (type === 'quarterrange') {
1036
+ const qObj = { year: now.getFullYear(), quarter: curQ };
1037
+ this.setData({
1038
+ _rangeStart: qObj,
1039
+ _rangeEnd: qObj,
1040
+ _rangeStartText: this._formatQuarter(now.getFullYear(), curQ),
1041
+ _rangeEndText: this._formatQuarter(now.getFullYear(), curQ),
1042
+ _displayYear: now.getFullYear(),
1043
+ });
1044
+ this._buildQuarterList();
1045
+ } else {
1046
+ this.setData({
1047
+ _selectedQuarter: curQ,
1048
+ _selectedYear: now.getFullYear(),
1049
+ _displayYear: now.getFullYear(),
1050
+ });
1051
+ this._buildQuarterList();
1052
+ }
1053
+ } else if (action === 'now') {
1054
+ // For datetimerange - set current time for active endpoint
1055
+ const { _activeEndpoint, _timeHours, _timeMinutes, _timeSeconds } = this.data;
1056
+ const hIdx = _timeHours.indexOf(pad(now.getHours()));
1057
+ const mIdx = _timeMinutes.indexOf(pad(now.getMinutes()));
1058
+ const sIdx = _timeSeconds.indexOf(pad(now.getSeconds()));
1059
+ if (type === 'datetimerange') {
1060
+ if (_activeEndpoint === 'start') {
1061
+ this.setData({ _startTimePickerValue: [hIdx, mIdx, sIdx] });
1062
+ } else {
1063
+ this.setData({ _endTimePickerValue: [hIdx, mIdx, sIdx] });
1064
+ }
1065
+ } else {
1066
+ this.setData({ _timePickerValue: [hIdx, mIdx, sIdx] });
1067
+ }
1068
+ }
1069
+ },
1070
+
1071
+ // ----- Confirm / Cancel / Clear -----
1072
+
1073
+ handleConfirm() {
1074
+ const { type } = this.data;
1075
+
1076
+ if (this._isRangeType()) {
1077
+ this._confirmRange();
1078
+ } else if (type === 'quarter') {
1079
+ this._confirmQuarter();
1080
+ } else if (type === 'week') {
1081
+ this._confirmWeek();
1082
+ } else {
1083
+ this._confirmSingle();
1084
+ }
1085
+ },
1086
+
1087
+ _confirmSingle() {
1088
+ const {
1089
+ type,
1090
+ _selectedYear: sy,
1091
+ _selectedMonth: sm,
1092
+ _selectedDay: sd,
1093
+ _timePickerValue,
1094
+ _timeHours,
1095
+ _timeMinutes,
1096
+ _timeSeconds,
1097
+ _displayYear,
1098
+ } = this.data;
1099
+
1100
+ let date;
1101
+ if (type === 'year') {
1102
+ if (sy < 0 && _displayYear > 0) {
1103
+ date = new Date(_displayYear, 0, 1);
1104
+ } else if (sy < 0) {
1105
+ this.setData({ _visible: false });
1106
+ return;
1107
+ } else {
1108
+ date = new Date(sy, 0, 1);
1109
+ }
1110
+ } else if (type === 'month') {
1111
+ if (sy < 0 || sm < 0) {
1112
+ this.setData({ _visible: false });
1113
+ return;
1114
+ }
1115
+ date = new Date(sy, sm, 1);
1116
+ } else if (type === 'datetime') {
1117
+ if (sy < 0 || sm < 0 || sd < 0) {
1118
+ this.setData({ _visible: false });
1119
+ return;
1120
+ }
1121
+ const h = parseInt(_timeHours[_timePickerValue[0]] || '0', 10);
1122
+ const m = parseInt(_timeMinutes[_timePickerValue[1]] || '0', 10);
1123
+ const s = parseInt(_timeSeconds[_timePickerValue[2]] || '0', 10);
1124
+ date = new Date(sy, sm, sd, h, m, s);
1125
+ } else {
1126
+ if (sy < 0 || sm < 0 || sd < 0) {
1127
+ this.setData({ _visible: false });
1128
+ return;
1129
+ }
1130
+ date = new Date(sy, sm, sd);
1131
+ }
1132
+
1133
+ const ts = date.getTime();
1134
+ const fmt = this._getDefaultFormat();
1135
+ this.setData({
1136
+ _visible: false,
1137
+ _displayText: format(date, fmt),
1138
+ _selectedYear: date.getFullYear(),
1139
+ _selectedMonth: date.getMonth(),
1140
+ _selectedDay: date.getDate(),
1141
+ });
1142
+ this.triggerEvent('update:value', { value: ts });
1143
+ this.triggerEvent('confirm', { value: ts, formatted: format(date, fmt) });
1144
+ this._notifyChange();
1145
+ },
1146
+
1147
+ _confirmQuarter() {
1148
+ const { _selectedQuarter, _selectedYear, _displayYear } = this.data;
1149
+ const year = _selectedYear >= 0 ? _selectedYear : _displayYear;
1150
+ const quarter = _selectedQuarter;
1151
+ if (quarter < 0) {
1152
+ this.setData({ _visible: false });
1153
+ return;
1154
+ }
1155
+ const date = new Date(year, quarter * 3, 1);
1156
+ const ts = date.getTime();
1157
+ const displayText = this._formatQuarter(year, quarter);
1158
+ this.setData({ _visible: false, _displayText: displayText });
1159
+ this.triggerEvent('update:value', { value: ts });
1160
+ this.triggerEvent('confirm', {
1161
+ value: ts,
1162
+ formatted: displayText,
1163
+ quarter: quarter + 1,
1164
+ year,
1165
+ });
1166
+ this._notifyChange();
1167
+ },
1168
+
1169
+ _confirmWeek() {
1170
+ const { _selectedWeekStart } = this.data;
1171
+ if (!_selectedWeekStart) {
1172
+ this.setData({ _visible: false });
1173
+ return;
1174
+ }
1175
+ const s = _selectedWeekStart;
1176
+ const startDate = new Date(s.year, s.month, s.day);
1177
+ const ts = startDate.getTime();
1178
+ const displayText = this._formatWeek(startDate);
1179
+ const e = this.data._selectedWeekEnd;
1180
+ const endDate = new Date(e.year, e.month, e.day);
1181
+
1182
+ this.setData({ _visible: false, _displayText: displayText });
1183
+ this.triggerEvent('update:value', { value: ts });
1184
+ this.triggerEvent('confirm', {
1185
+ value: ts,
1186
+ formatted: displayText,
1187
+ weekNumber: this._getWeekNumber(startDate),
1188
+ weekStart: ts,
1189
+ weekEnd: endDate.getTime(),
1190
+ });
1191
+ this._notifyChange();
1192
+ },
1193
+
1194
+ _confirmRange() {
1195
+ const {
1196
+ type,
1197
+ _rangeStart,
1198
+ _rangeEnd,
1199
+ _startTimePickerValue,
1200
+ _endTimePickerValue,
1201
+ _timeHours,
1202
+ _timeMinutes,
1203
+ _timeSeconds,
1204
+ } = this.data;
1205
+
1206
+ if (!_rangeStart) {
1207
+ this.setData({ _visible: false });
1208
+ return;
1209
+ }
1210
+
1211
+ // If end not set, default to start
1212
+ const rangeEnd = _rangeEnd || _rangeStart;
1213
+
1214
+ let startDate, endDate;
1215
+
1216
+ if (type === 'daterange') {
1217
+ startDate = new Date(_rangeStart.year, _rangeStart.month, _rangeStart.day);
1218
+ endDate = new Date(rangeEnd.year, rangeEnd.month, rangeEnd.day);
1219
+ } else if (type === 'datetimerange') {
1220
+ const sh = parseInt(_timeHours[_startTimePickerValue[0]] || '0', 10);
1221
+ const smin = parseInt(_timeMinutes[_startTimePickerValue[1]] || '0', 10);
1222
+ const ss = parseInt(_timeSeconds[_startTimePickerValue[2]] || '0', 10);
1223
+ const eh = parseInt(_timeHours[_endTimePickerValue[0]] || '0', 10);
1224
+ const emin = parseInt(_timeMinutes[_endTimePickerValue[1]] || '0', 10);
1225
+ const es = parseInt(_timeSeconds[_endTimePickerValue[2]] || '0', 10);
1226
+ startDate = new Date(_rangeStart.year, _rangeStart.month, _rangeStart.day, sh, smin, ss);
1227
+ endDate = new Date(rangeEnd.year, rangeEnd.month, rangeEnd.day, eh, emin, es);
1228
+ } else if (type === 'monthrange') {
1229
+ startDate = new Date(_rangeStart.year, _rangeStart.month, 1);
1230
+ endDate = new Date(rangeEnd.year, rangeEnd.month, 1);
1231
+ } else if (type === 'yearrange') {
1232
+ startDate = new Date(_rangeStart.year, 0, 1);
1233
+ endDate = new Date(rangeEnd.year, 0, 1);
1234
+ } else if (type === 'quarterrange') {
1235
+ startDate = new Date(_rangeStart.year, (_rangeStart.quarter || 0) * 3, 1);
1236
+ endDate = new Date(rangeEnd.year, (rangeEnd.quarter || 0) * 3, 1);
1237
+ }
1238
+
1239
+ const startTs = startDate.getTime();
1240
+ const endTs = endDate.getTime();
1241
+
1242
+ let startText, endText;
1243
+ if (type === 'quarterrange') {
1244
+ startText = this._formatQuarter(_rangeStart.year, _rangeStart.quarter || 0);
1245
+ endText = this._formatQuarter(rangeEnd.year, rangeEnd.quarter || 0);
1246
+ } else if (type === 'datetimerange') {
1247
+ startText = this._formatDatetimeRange(startDate, endDate, 'start');
1248
+ endText = this._formatDatetimeRange(startDate, endDate, 'end');
1249
+ } else {
1250
+ const fmt = this._getDefaultFormat();
1251
+ startText = format(startDate, fmt);
1252
+ endText = format(endDate, fmt);
1253
+ }
1254
+
1255
+ const displayText = `${startText} ~ ${endText}`;
1256
+
1257
+ this.setData({
1258
+ _visible: false,
1259
+ _displayText: displayText,
1260
+ _rangeStartText: startText,
1261
+ _rangeEndText: endText,
1262
+ });
1263
+ this.triggerEvent('update:value', { value: [startTs, endTs] });
1264
+ this.triggerEvent('confirm', { value: [startTs, endTs], formatted: [startText, endText] });
1265
+ this._notifyChange();
1266
+ },
1267
+
1268
+ handleCancel() {
1269
+ this.setData({ _visible: false });
1270
+ this._syncFromValue();
1271
+ },
1272
+
1273
+ handleClear() {
1274
+ this.triggerEvent('update:value', { value: null });
1275
+ this.triggerEvent('clear');
1276
+ this.setData({
1277
+ _displayText: '',
1278
+ _rangeStart: null,
1279
+ _rangeEnd: null,
1280
+ _rangeStartText: '',
1281
+ _rangeEndText: '',
1282
+ _selectedWeekStart: null,
1283
+ _selectedWeekEnd: null,
1284
+ _selectedQuarter: -1,
1285
+ });
1286
+ this._notifyChange();
1287
+ },
1288
+ },
1289
+ });