cdui-js 1.0.22 → 1.0.24
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.
- package/css/all.css +1 -2
- package/css/datewidget.css +9 -0
- package/css/{datepicker.css → dropdown.css} +6 -10
- package/css/popup.css +38 -19
- package/demo/css/css.md +6 -10
- package/demo/src/App.tsx +22 -2
- package/demo/src/css/atomic.css +6 -8
- package/demo/src/pages/Canlendar.tsx +4 -4
- package/demo/src/pages/ComboBox.tsx +17 -14
- package/demo/src/pages/DatePicker.tsx +6 -1
- package/demo/src/pages/Form.tsx +70 -45
- package/package.json +1 -1
- package/src/components/Canlendar.tsx +91 -48
- package/src/components/ComboBox.tsx +57 -51
- package/src/components/DatePicker.tsx +146 -60
- package/src/components/Form.tsx +1 -0
- package/src/components/MonthPicker.tsx +1 -1
- package/src/components/MonthWidget.tsx +65 -36
- package/src/components/YearPicker.tsx +1 -1
- package/src/components/YearWidget.tsx +62 -138
- package/src/{components/Dialog.ts → dialog.ts} +13 -6
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +4 -2
- package/src/popup.ts +341 -0
- package/src/reactive.ts +4 -1
- package/src/ssr/render.ts +5 -5
- package/css/combobox.css +0 -38
- package/demo/src/pages/MobileDatePicker.tsx +0 -5
- package/src/components/MobileDatePicker.tsx +0 -444
- package/src/components/Popup.tsx +0 -228
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { JSX } from '../jsx';
|
|
2
|
-
import { createSignal, combineClass, omitProps } from '../reactive';
|
|
3
|
-
import {
|
|
2
|
+
import { createSignal, combineClass, omitProps, render } from '../reactive';
|
|
3
|
+
import { Canlendar as i18n } from '../i18n';
|
|
4
4
|
import { replaceTemplate } from '../template';
|
|
5
|
+
|
|
5
6
|
import { For } from './For';
|
|
7
|
+
import { MonthWidget } from './MonthWidget';
|
|
8
|
+
|
|
9
|
+
export const parseDate = (value: Date | string | number) => {
|
|
10
|
+
if (value) {
|
|
11
|
+
switch (typeof value) {
|
|
12
|
+
case 'number':
|
|
13
|
+
return new Date(value);
|
|
14
|
+
|
|
15
|
+
case 'string':
|
|
16
|
+
return new Date(value.replace(/\//g, '-'));
|
|
17
|
+
|
|
18
|
+
default:
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const getYearMonth = (date: Date) => {
|
|
25
|
+
return [date.getFullYear(), date.getMonth() + 1] as const;
|
|
26
|
+
};
|
|
6
27
|
|
|
7
28
|
// 渲染日期项
|
|
8
29
|
const renderDates = (
|
|
@@ -16,9 +37,9 @@ const renderDates = (
|
|
|
16
37
|
selectedValue?: Date,
|
|
17
38
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
18
39
|
) => {
|
|
19
|
-
let today = todayValue.getFullYear() === year && todayValue.getMonth() === month ? todayValue.getDate() : -1;
|
|
40
|
+
let today = todayValue.getFullYear() === year && todayValue.getMonth() + 1 === month ? todayValue.getDate() : -1;
|
|
20
41
|
let selected =
|
|
21
|
-
selectedValue && selectedValue.getFullYear() === year && selectedValue.getMonth() === month
|
|
42
|
+
selectedValue && selectedValue.getFullYear() === year && selectedValue.getMonth() + 1 === month
|
|
22
43
|
? selectedValue.getDate()
|
|
23
44
|
: -1;
|
|
24
45
|
|
|
@@ -26,7 +47,7 @@ const renderDates = (
|
|
|
26
47
|
items.push(
|
|
27
48
|
`<span class="datewidget-item${className}${today === i ? ' today' : ''}${selected === i ? ' selected' : ''}${
|
|
28
49
|
disableFn && disableFn(year, month, i) ? ' disabled' : ''
|
|
29
|
-
}" data-
|
|
50
|
+
}" data-day="${year + '|' + month + '|' + i}">${i}</span>`,
|
|
30
51
|
);
|
|
31
52
|
}
|
|
32
53
|
};
|
|
@@ -74,14 +95,14 @@ const renderNextMonthItems = (
|
|
|
74
95
|
};
|
|
75
96
|
|
|
76
97
|
const renderItems = (
|
|
77
|
-
currentValue:
|
|
98
|
+
currentValue: readonly [year: number, month: number],
|
|
78
99
|
selectedValue?: Date,
|
|
79
100
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
80
101
|
) => {
|
|
81
102
|
let today = new Date();
|
|
82
|
-
let year = currentValue
|
|
83
|
-
let month = currentValue
|
|
84
|
-
let firstDate = new Date(year, month, 1); // 获取当前月的第一天
|
|
103
|
+
let year = currentValue[0];
|
|
104
|
+
let month = currentValue[1];
|
|
105
|
+
let firstDate = new Date(year, month - 1, 1); // 获取当前月的第一天
|
|
85
106
|
|
|
86
107
|
let firstWeek = firstDate.getDay();
|
|
87
108
|
let items = [];
|
|
@@ -95,55 +116,69 @@ const renderItems = (
|
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
// 获取当前月的天数
|
|
98
|
-
days = new Date(year, month
|
|
119
|
+
days = new Date(year, month, 0).getDate();
|
|
120
|
+
|
|
99
121
|
// 渲染本月日期
|
|
100
122
|
renderDates(items, year, month, 1, days, '', today, selectedValue, disableFn);
|
|
101
123
|
|
|
102
124
|
// 当前月最后一天没有占满,渲染下月数据
|
|
103
125
|
if ((index += days) < 42) {
|
|
104
|
-
renderNextMonthItems(items, year, month, 42 - index, today, selectedValue, disableFn);
|
|
126
|
+
renderNextMonthItems(items, year, month + 1, 42 - index, today, selectedValue, disableFn);
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
return items.join('');
|
|
108
130
|
};
|
|
109
131
|
|
|
110
|
-
const switchMonth = (value:
|
|
111
|
-
let
|
|
112
|
-
let month = value
|
|
132
|
+
const switchMonth = (value: readonly [year: number, month: number], offset: 1 | -1) => {
|
|
133
|
+
let year = value[0];
|
|
134
|
+
let month = value[1] + offset;
|
|
113
135
|
|
|
114
|
-
|
|
136
|
+
if (month > 12) {
|
|
137
|
+
year++;
|
|
138
|
+
month = 1;
|
|
139
|
+
} else if (!month) {
|
|
140
|
+
year--;
|
|
141
|
+
month = 12;
|
|
142
|
+
}
|
|
115
143
|
|
|
116
|
-
return
|
|
144
|
+
return [year, month] as const;
|
|
117
145
|
};
|
|
118
146
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
case 'number':
|
|
123
|
-
return new Date(value);
|
|
147
|
+
const formatMonth = (month: number) => {
|
|
148
|
+
return month > 9 ? month : '0' + month;
|
|
149
|
+
};
|
|
124
150
|
|
|
125
|
-
|
|
126
|
-
|
|
151
|
+
const showMonthWidget = (dom: HTMLElement, setCurrentValue: (value) => void, selectedValue?: Date) => {
|
|
152
|
+
let value =
|
|
153
|
+
selectedValue && ([selectedValue.getFullYear(), selectedValue.getMonth() + 1] as [year: number, month: number]);
|
|
127
154
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
155
|
+
render(
|
|
156
|
+
() => (
|
|
157
|
+
<MonthWidget
|
|
158
|
+
value={value}
|
|
159
|
+
style={{ position: 'absolute', top: '0', left: '0', width: '100%', border: 'none' }}
|
|
160
|
+
onchange={(event) => {
|
|
161
|
+
let result = event.detail;
|
|
133
162
|
|
|
134
|
-
|
|
135
|
-
let month = value.getMonth() + 1;
|
|
163
|
+
dom.removeChild(event.target as HTMLElement);
|
|
136
164
|
|
|
137
|
-
|
|
165
|
+
if (!selectedValue || result[0] !== value[0] || result[1] !== value[1]) {
|
|
166
|
+
setCurrentValue(result);
|
|
167
|
+
}
|
|
168
|
+
}}
|
|
169
|
+
></MonthWidget>
|
|
170
|
+
),
|
|
171
|
+
dom,
|
|
172
|
+
);
|
|
138
173
|
};
|
|
139
174
|
|
|
140
|
-
const OMIT_PROPS = ['class', 'value', '
|
|
175
|
+
const OMIT_PROPS = ['class', 'value', 'disableFn'] as const;
|
|
141
176
|
|
|
142
177
|
/**
|
|
143
178
|
* 日历组件
|
|
144
179
|
*/
|
|
145
180
|
export const Canlendar = (
|
|
146
|
-
props: Omit<JSX.HTMLAttributes<never>, 'children'> & {
|
|
181
|
+
props: Omit<JSX.HTMLAttributes<never>, 'children' | 'onchange'> & {
|
|
147
182
|
/**
|
|
148
183
|
* 日期值
|
|
149
184
|
*/
|
|
@@ -151,24 +186,27 @@ export const Canlendar = (
|
|
|
151
186
|
/**
|
|
152
187
|
* 值变更事件
|
|
153
188
|
*/
|
|
154
|
-
|
|
189
|
+
onchange?: (event: CustomEvent<Date>) => void;
|
|
155
190
|
/**
|
|
156
191
|
* 禁用函数
|
|
157
192
|
*/
|
|
158
193
|
disableFn?: (year: number, month: number, date: number) => boolean;
|
|
159
194
|
},
|
|
160
195
|
) => {
|
|
161
|
-
let
|
|
162
|
-
let domBody: HTMLElement;
|
|
196
|
+
let dom: HTMLElement;
|
|
163
197
|
|
|
164
198
|
const [selectedValue, setSelectedValue] = createSignal(parseDate(props.value));
|
|
165
|
-
const [currentValue, setCurrentValue] = createSignal(selectedValue() || new Date());
|
|
199
|
+
const [currentValue, setCurrentValue] = createSignal(getYearMonth(selectedValue() || new Date()));
|
|
166
200
|
|
|
167
201
|
return (
|
|
168
|
-
<div
|
|
202
|
+
<div
|
|
203
|
+
ref={dom as any}
|
|
204
|
+
class={combineClass('canlendar datewidget', props.class)}
|
|
205
|
+
{...(omitProps(props, OMIT_PROPS) as any)}
|
|
206
|
+
>
|
|
169
207
|
<div class="datewidget-header canlendar-header">
|
|
170
|
-
<div
|
|
171
|
-
{replaceTemplate(i18n.Title, currentValue()
|
|
208
|
+
<div class="datewidget-title" onclick={() => showMonthWidget(dom, setCurrentValue, selectedValue())}>
|
|
209
|
+
{replaceTemplate(i18n.Title, currentValue()[0], formatMonth(currentValue()[1]))}
|
|
172
210
|
</div>
|
|
173
211
|
<svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(currentValue(), -1))}>
|
|
174
212
|
<use href="#icon-backward"></use>
|
|
@@ -181,21 +219,26 @@ export const Canlendar = (
|
|
|
181
219
|
<For each={i18n.Weeks}>{(item) => <span>{item}</span>}</For>
|
|
182
220
|
</div>
|
|
183
221
|
<div
|
|
184
|
-
ref={domBody as any}
|
|
185
222
|
class="datewidget-body canlendar-body"
|
|
186
223
|
onclick={(event) => {
|
|
187
224
|
let target = event.target as HTMLElement;
|
|
188
|
-
let
|
|
225
|
+
let day;
|
|
189
226
|
|
|
190
|
-
while (target && target !==
|
|
191
|
-
if ((
|
|
227
|
+
while (target && target !== dom) {
|
|
228
|
+
if ((day = target.dataset.day)) {
|
|
192
229
|
// 没有选中
|
|
193
230
|
if (!target.classList.contains('selected')) {
|
|
194
|
-
|
|
195
|
-
|
|
231
|
+
day = day.split('|');
|
|
232
|
+
day = new Date(day[0] | 0, (day[1] | 0) - 1, day[2] | 0);
|
|
233
|
+
|
|
234
|
+
setSelectedValue(day);
|
|
196
235
|
|
|
197
|
-
|
|
198
|
-
|
|
236
|
+
dom.dispatchEvent(
|
|
237
|
+
new CustomEvent('change', {
|
|
238
|
+
detail: day,
|
|
239
|
+
bubbles: true, // 允许事件冒泡
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
199
242
|
}
|
|
200
243
|
|
|
201
244
|
break;
|
|
@@ -1,48 +1,49 @@
|
|
|
1
1
|
import { JSX } from '../jsx';
|
|
2
2
|
import { combineClass, omitProps, useContext, watch } from '../reactive';
|
|
3
3
|
import { disableAutoCloseEvent } from '../dom';
|
|
4
|
-
import {
|
|
4
|
+
import { PopupApi, initDropdown } from '../popup';
|
|
5
|
+
|
|
5
6
|
import { FormItemContext } from './provider';
|
|
6
7
|
|
|
7
|
-
const OMIT_PROPS = ['class', 'value', '
|
|
8
|
+
const OMIT_PROPS = ['class', 'value', 'format', /*'readonly', */ 'popup', 'popupOnFocus', 'api'] as const;
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* 下拉框组件
|
|
11
12
|
*/
|
|
12
13
|
export const ComboBox = (
|
|
13
|
-
props?: JSX.HTMLAttributes<never> &
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
props?: Omit<JSX.HTMLAttributes<never>, 'children'> & {
|
|
15
|
+
/**
|
|
16
|
+
* 弹出层
|
|
17
|
+
*/
|
|
18
|
+
popup: () => JSX.Element;
|
|
19
|
+
/**
|
|
20
|
+
* 值
|
|
21
|
+
*/
|
|
22
|
+
value?: any;
|
|
23
|
+
/**
|
|
24
|
+
* 值样式
|
|
25
|
+
*
|
|
26
|
+
* @param value 当前值
|
|
27
|
+
*/
|
|
28
|
+
format?(value: any): string;
|
|
29
|
+
// /**
|
|
30
|
+
// * 是否只读
|
|
31
|
+
// */
|
|
32
|
+
// readonly?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* 获取焦点时是否自动弹出
|
|
35
|
+
*/
|
|
36
|
+
popupOnFocus?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 值变更事件
|
|
39
|
+
*/
|
|
40
|
+
onchange?: (event: CustomEvent<Date>) => void;
|
|
41
|
+
/**
|
|
42
|
+
* 外部调用接口
|
|
43
|
+
*/
|
|
44
|
+
api: (api: PopupApi) => void;
|
|
45
|
+
},
|
|
38
46
|
) => {
|
|
39
|
-
let popup: PopupApi;
|
|
40
|
-
|
|
41
|
-
const initApi = (api) => {
|
|
42
|
-
popup = api;
|
|
43
|
-
props.api && props.api(popup);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
47
|
const formItem = useContext(FormItemContext);
|
|
47
48
|
|
|
48
49
|
const initFormItem = (dom: HTMLInputElement) => {
|
|
@@ -54,24 +55,29 @@ export const ComboBox = (
|
|
|
54
55
|
formItem.init(dom);
|
|
55
56
|
};
|
|
56
57
|
|
|
58
|
+
let api: PopupApi;
|
|
59
|
+
|
|
57
60
|
return (
|
|
58
|
-
<div
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{props.
|
|
74
|
-
|
|
61
|
+
<div
|
|
62
|
+
ref={(dom) => {
|
|
63
|
+
api = initDropdown(dom, () => <div class="combobox-popup">{props.popup()}</div>);
|
|
64
|
+
props.api && props.api(api);
|
|
65
|
+
}}
|
|
66
|
+
class={combineClass('combobox', props.class)}
|
|
67
|
+
{...disableAutoCloseEvent}
|
|
68
|
+
{...omitProps(props, OMIT_PROPS)}
|
|
69
|
+
>
|
|
70
|
+
<input
|
|
71
|
+
ref={formItem && initFormItem}
|
|
72
|
+
class="combobox-input"
|
|
73
|
+
value={props.format ? props.format(props.value) : '' + (props.value || '')}
|
|
74
|
+
readonly={true}
|
|
75
|
+
onfocus={() => props.popupOnFocus && api.openPopup()}
|
|
76
|
+
onclick={() => !props.popupOnFocus && api.togglePopup()}
|
|
77
|
+
></input>
|
|
78
|
+
<svg class="combobox-icon icon icon-s" aria-hidden={true} onclick={() => api.togglePopup()}>
|
|
79
|
+
<use href="#icon-dropdown"></use>
|
|
80
|
+
</svg>
|
|
75
81
|
</div>
|
|
76
82
|
);
|
|
77
83
|
};
|
|
@@ -1,58 +1,94 @@
|
|
|
1
1
|
import { JSX } from '../jsx';
|
|
2
|
-
import { combineClass, createSignal, omitProps } from '../reactive';
|
|
2
|
+
import { combineClass, createSignal, omitProps, render, useContext, watch } from '../reactive';
|
|
3
3
|
import { disableAutoCloseEvent } from '../dom';
|
|
4
|
-
import {
|
|
5
|
-
import { Popup, PopupApi } from './Popup';
|
|
6
|
-
import { parseDate } from './Canlendar';
|
|
4
|
+
import { initDropdown, PopupApi } from '../popup';
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
6
|
+
import { Canlendar, parseDate } from './Canlendar';
|
|
7
|
+
import { FormItemContext } from './provider';
|
|
11
8
|
|
|
12
|
-
const
|
|
9
|
+
const Date_regex = /[YyMDdHhmsSQq]+/g;
|
|
10
|
+
const Date_zeros = ['', '0', '00', '000', '0000'];
|
|
13
11
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const DATE_GET_YEAR = 'getFullYear';
|
|
13
|
+
const DATE_GET_MONTH = 'getMonth';
|
|
14
|
+
const DATE_GET_DATE = 'getDate';
|
|
15
|
+
const DATE_GET_HOUR = 'getHours';
|
|
16
|
+
const DATE_GET_MINUTE = 'getMinutes';
|
|
17
|
+
const DATE_GET_SECOND = 'getSeconds';
|
|
18
|
+
|
|
19
|
+
export const formatDate = (date: Date, format: string) => {
|
|
20
|
+
if (date) {
|
|
21
|
+
switch (format || (format = 'yyyy-MM-dd')) {
|
|
22
|
+
case 'GMT':
|
|
23
|
+
case 'ISO':
|
|
24
|
+
case 'UTC':
|
|
25
|
+
case 'Date':
|
|
26
|
+
case 'Time':
|
|
27
|
+
case 'Locale':
|
|
28
|
+
case 'LocaleDate':
|
|
29
|
+
case 'LocaleTime':
|
|
30
|
+
return (date as unknown as { [key: string]: Function })['to' + format + 'String']();
|
|
31
|
+
|
|
32
|
+
default:
|
|
33
|
+
return format.replace(Date_regex, (text) => {
|
|
34
|
+
let length = text.length;
|
|
35
|
+
let value;
|
|
36
|
+
|
|
37
|
+
switch (text[0]) {
|
|
38
|
+
case 'y':
|
|
39
|
+
case 'Y':
|
|
40
|
+
value = date[DATE_GET_YEAR]();
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case 'M':
|
|
44
|
+
value = date[DATE_GET_MONTH]() + 1;
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'd':
|
|
48
|
+
value = date[DATE_GET_DATE]();
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'H':
|
|
52
|
+
case 'h':
|
|
53
|
+
value = date[DATE_GET_HOUR]();
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case 'm':
|
|
57
|
+
value = date[DATE_GET_MINUTE]();
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
case 's':
|
|
61
|
+
value = date[DATE_GET_SECOND]();
|
|
62
|
+
break;
|
|
21
63
|
|
|
22
|
-
|
|
23
|
-
|
|
64
|
+
case 'S':
|
|
65
|
+
value = date.getMilliseconds();
|
|
66
|
+
break;
|
|
24
67
|
|
|
25
|
-
|
|
68
|
+
case 'Q':
|
|
69
|
+
case 'q':
|
|
70
|
+
value = ((date[DATE_GET_MONTH]() + 3) / 3) | 0;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
text = '' + value;
|
|
75
|
+
length -= text.length;
|
|
76
|
+
|
|
77
|
+
return length <= 0 ? text : Date_zeros[length] + text;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return '';
|
|
26
83
|
};
|
|
27
84
|
|
|
28
|
-
|
|
29
|
-
// let year = value.getFullYear();
|
|
30
|
-
// let month = value.getMonth() + 1;
|
|
31
|
-
// let date = value.getDate();
|
|
32
|
-
|
|
33
|
-
// return [
|
|
34
|
-
// year > 5 ? [year - 2, year - 1, year, year + 1, year + 2]: [1,2,3,4,5],
|
|
35
|
-
// [formatMonth(month - 2), ]
|
|
36
|
-
// ];
|
|
37
|
-
// };
|
|
38
|
-
|
|
39
|
-
// const MobileDatePicker = (yearList: number[], monthList: string[], dateList: string[]) => {
|
|
40
|
-
// return (
|
|
41
|
-
// <div>
|
|
42
|
-
// <MobileTouchScroll items={yearList}></MobileTouchScroll>
|
|
43
|
-
// <MobileTouchScroll items={monthList}></MobileTouchScroll>
|
|
44
|
-
// <MobileTouchScroll items={dateList}></MobileTouchScroll>
|
|
45
|
-
// </div>
|
|
46
|
-
// );
|
|
47
|
-
// };
|
|
48
|
-
|
|
49
|
-
const OMIT_PROPS = ['class', 'value', 'readonly', 'format', 'children'] as const;
|
|
85
|
+
const OMIT_PROPS = ['class', 'value', 'format', /*'readonly',*/ 'popupOnFocus', 'disableFn', 'api'] as const;
|
|
50
86
|
|
|
51
87
|
/**
|
|
52
88
|
* 日期选择组件
|
|
53
89
|
*/
|
|
54
90
|
export const DatePicker = (
|
|
55
|
-
props?: JSX.HTMLAttributes<never> & {
|
|
91
|
+
props?: Omit<JSX.HTMLAttributes<never>, 'children' | 'onchange'> & {
|
|
56
92
|
/**
|
|
57
93
|
* 值
|
|
58
94
|
*/
|
|
@@ -61,32 +97,82 @@ export const DatePicker = (
|
|
|
61
97
|
* 日期格式
|
|
62
98
|
*/
|
|
63
99
|
format?: string;
|
|
100
|
+
// /**
|
|
101
|
+
// * 是否只读
|
|
102
|
+
// */
|
|
103
|
+
// readonly?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* 获取焦点时是否自动弹出
|
|
106
|
+
*/
|
|
107
|
+
popupOnFocus?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* 值变更事件
|
|
110
|
+
*/
|
|
111
|
+
onchange?: (event: CustomEvent<Date>) => void;
|
|
64
112
|
/**
|
|
65
|
-
*
|
|
113
|
+
* 禁用函数
|
|
66
114
|
*/
|
|
67
|
-
|
|
115
|
+
disableFn?: (year: number, month: number, date: number) => boolean;
|
|
116
|
+
/**
|
|
117
|
+
* 外部调用接口
|
|
118
|
+
*/
|
|
119
|
+
api?: (api: PopupApi) => void;
|
|
68
120
|
},
|
|
69
121
|
) => {
|
|
70
|
-
let
|
|
122
|
+
let api: PopupApi;
|
|
123
|
+
|
|
124
|
+
const formItem = useContext(FormItemContext);
|
|
71
125
|
|
|
72
|
-
const
|
|
126
|
+
const initFormItem = (dom: HTMLElement) => {
|
|
127
|
+
watch(
|
|
128
|
+
() => props.value,
|
|
129
|
+
(value) => formItem.setValue(value),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
formItem.init(dom);
|
|
133
|
+
};
|
|
73
134
|
|
|
74
135
|
return (
|
|
75
|
-
<div
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
136
|
+
<div
|
|
137
|
+
ref={(dom: HTMLElement) => {
|
|
138
|
+
api = initDropdown(
|
|
139
|
+
dom,
|
|
140
|
+
() => (
|
|
141
|
+
<Canlendar
|
|
142
|
+
value={props.value}
|
|
143
|
+
onchange={(event) => {
|
|
144
|
+
dom.dispatchEvent(
|
|
145
|
+
new CustomEvent('change', {
|
|
146
|
+
detail: event.detail,
|
|
147
|
+
bubbles: true, // 允许事件冒泡
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
api.closePopup();
|
|
152
|
+
}}
|
|
153
|
+
disableFn={props.disableFn}
|
|
154
|
+
></Canlendar>
|
|
155
|
+
),
|
|
156
|
+
{ alignWidth: false },
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
formItem && initFormItem(dom);
|
|
160
|
+
props.api && props.api(api);
|
|
161
|
+
}}
|
|
162
|
+
class={combineClass('datepicker', props.class)}
|
|
163
|
+
{...disableAutoCloseEvent}
|
|
164
|
+
{...(omitProps(props, OMIT_PROPS) as any)}
|
|
165
|
+
>
|
|
166
|
+
<input
|
|
167
|
+
class="datepicker-input"
|
|
168
|
+
value={formatDate(parseDate(props.value), props.format)}
|
|
169
|
+
readonly={true}
|
|
170
|
+
onfocus={() => props.popupOnFocus && api.openPopup()}
|
|
171
|
+
onclick={() => !props.popupOnFocus && api.togglePopup()}
|
|
172
|
+
></input>
|
|
173
|
+
<svg class="datepicker-icon icon icon-s" aria-hidden={true} onclick={() => api.togglePopup()}>
|
|
174
|
+
<use href="#icon-dropdown"></use>
|
|
175
|
+
</svg>
|
|
90
176
|
</div>
|
|
91
177
|
);
|
|
92
178
|
};
|
package/src/components/Form.tsx
CHANGED