wok-ui-ext 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.
- package/LICENSE +21 -0
- package/README.en.md +75 -0
- package/README.md +75 -0
- package/dist/alert/index.d.ts +10 -0
- package/dist/alert/index.js +48 -0
- package/dist/alert/style.css +116 -0
- package/dist/avatar/index.d.ts +9 -0
- package/dist/avatar/index.js +30 -0
- package/dist/avatar/style.css +32 -0
- package/dist/badge/index.d.ts +32 -0
- package/dist/badge/index.js +61 -0
- package/dist/badge/style.css +68 -0
- package/dist/breadcrumb/index.d.ts +25 -0
- package/dist/breadcrumb/index.js +47 -0
- package/dist/breadcrumb/style.css +35 -0
- package/dist/button/index.d.ts +34 -0
- package/dist/button/index.js +57 -0
- package/dist/button/style.css +133 -0
- package/dist/card/index.d.ts +25 -0
- package/dist/card/index.js +29 -0
- package/dist/card/style.css +23 -0
- package/dist/collapse/index.d.ts +89 -0
- package/dist/collapse/index.js +213 -0
- package/dist/collapse/style.css +76 -0
- package/dist/context-menu/context-menu.d.ts +113 -0
- package/dist/context-menu/context-menu.js +150 -0
- package/dist/context-menu/index.d.ts +30 -0
- package/dist/context-menu/index.js +42 -0
- package/dist/context-menu/style.css +46 -0
- package/dist/date-picker/calendar-panel.d.ts +44 -0
- package/dist/date-picker/calendar-panel.js +180 -0
- package/dist/date-picker/date-picker.d.ts +29 -0
- package/dist/date-picker/date-picker.js +81 -0
- package/dist/date-picker/date-utils.d.ts +63 -0
- package/dist/date-picker/date-utils.js +130 -0
- package/dist/date-picker/index.d.ts +7 -0
- package/dist/date-picker/index.js +5 -0
- package/dist/date-picker/layer.d.ts +19 -0
- package/dist/date-picker/layer.js +36 -0
- package/dist/date-picker/range-picker.d.ts +30 -0
- package/dist/date-picker/range-picker.js +117 -0
- package/dist/date-picker/style.css +161 -0
- package/dist/description-list/index.d.ts +37 -0
- package/dist/description-list/index.js +145 -0
- package/dist/description-list/style.css +63 -0
- package/dist/divider/index.d.ts +29 -0
- package/dist/divider/index.js +29 -0
- package/dist/divider/style.css +50 -0
- package/dist/drawer/index.d.ts +51 -0
- package/dist/drawer/index.js +183 -0
- package/dist/drawer/style.css +100 -0
- package/dist/dropdown/index.d.ts +38 -0
- package/dist/dropdown/index.js +97 -0
- package/dist/dropdown/style.css +39 -0
- package/dist/empty/index.d.ts +11 -0
- package/dist/empty/index.js +20 -0
- package/dist/empty/style.css +19 -0
- package/dist/floating-layer/index.d.ts +109 -0
- package/dist/floating-layer/index.js +337 -0
- package/dist/floating-layer/style.css +5 -0
- package/dist/form/form-item/index.d.ts +26 -0
- package/dist/form/form-item/index.js +21 -0
- package/dist/form/form-item/style.css +33 -0
- package/dist/form/form-modal/index.d.ts +18 -0
- package/dist/form/form-modal/index.js +38 -0
- package/dist/form/form.d.ts +21 -0
- package/dist/form/form.js +28 -0
- package/dist/form/index.d.ts +4 -0
- package/dist/form/index.js +4 -0
- package/dist/form/select/index.d.ts +48 -0
- package/dist/form/select/index.js +94 -0
- package/dist/form/select/style.css +29 -0
- package/dist/global.css +71 -0
- package/dist/i18n/de-DE.d.ts +2 -0
- package/dist/i18n/de-DE.js +36 -0
- package/dist/i18n/en-US.d.ts +2 -0
- package/dist/i18n/en-US.js +36 -0
- package/dist/i18n/es-ES.d.ts +2 -0
- package/dist/i18n/es-ES.js +36 -0
- package/dist/i18n/fr-FR.d.ts +2 -0
- package/dist/i18n/fr-FR.js +36 -0
- package/dist/i18n/index.d.ts +12 -0
- package/dist/i18n/index.js +39 -0
- package/dist/i18n/ja-JP.d.ts +2 -0
- package/dist/i18n/ja-JP.js +36 -0
- package/dist/i18n/ko-KR.d.ts +2 -0
- package/dist/i18n/ko-KR.js +36 -0
- package/dist/i18n/messages.d.ts +40 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/wok-ui/de-DE.d.ts +2 -0
- package/dist/i18n/wok-ui/de-DE.js +20 -0
- package/dist/i18n/wok-ui/es-ES.d.ts +2 -0
- package/dist/i18n/wok-ui/es-ES.js +20 -0
- package/dist/i18n/wok-ui/fr-FR.d.ts +2 -0
- package/dist/i18n/wok-ui/fr-FR.js +20 -0
- package/dist/i18n/wok-ui/ja-JP.d.ts +2 -0
- package/dist/i18n/wok-ui/ja-JP.js +20 -0
- package/dist/i18n/wok-ui/ko-KR.d.ts +2 -0
- package/dist/i18n/wok-ui/ko-KR.js +20 -0
- package/dist/i18n/wok-ui/zh-TW.d.ts +2 -0
- package/dist/i18n/wok-ui/zh-TW.js +20 -0
- package/dist/i18n/zh-CN.d.ts +2 -0
- package/dist/i18n/zh-CN.js +36 -0
- package/dist/i18n/zh-TW.d.ts +2 -0
- package/dist/i18n/zh-TW.js +36 -0
- package/dist/icons.d.ts +88 -0
- package/dist/icons.js +176 -0
- package/dist/image/index.d.ts +38 -0
- package/dist/image/index.js +97 -0
- package/dist/image/style.css +72 -0
- package/dist/image-preview/index.d.ts +26 -0
- package/dist/image-preview/index.js +134 -0
- package/dist/image-preview/style.css +96 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +45 -0
- package/dist/list/index.d.ts +32 -0
- package/dist/list/index.js +42 -0
- package/dist/list/list-item.d.ts +27 -0
- package/dist/list/list-item.js +51 -0
- package/dist/list/style.css +20 -0
- package/dist/loading/index.d.ts +10 -0
- package/dist/loading/index.js +46 -0
- package/dist/loading/style.css +48 -0
- package/dist/menu/index.d.ts +38 -0
- package/dist/menu/index.js +62 -0
- package/dist/menu/leaf-item.d.ts +12 -0
- package/dist/menu/leaf-item.js +41 -0
- package/dist/menu/style.css +117 -0
- package/dist/menu/sub-menu.d.ts +21 -0
- package/dist/menu/sub-menu.js +85 -0
- package/dist/menu/types.d.ts +36 -0
- package/dist/menu/types.js +1 -0
- package/dist/menu/utils.d.ts +11 -0
- package/dist/menu/utils.js +24 -0
- package/dist/modal/index.d.ts +23 -0
- package/dist/modal/index.js +79 -0
- package/dist/modal/modal.d.ts +76 -0
- package/dist/modal/modal.js +188 -0
- package/dist/modal/style.css +90 -0
- package/dist/pagination/index.d.ts +31 -0
- package/dist/pagination/index.js +215 -0
- package/dist/pagination/style.css +50 -0
- package/dist/popconfirm/api.d.ts +43 -0
- package/dist/popconfirm/api.js +77 -0
- package/dist/popconfirm/component.d.ts +47 -0
- package/dist/popconfirm/component.js +48 -0
- package/dist/popconfirm/index.d.ts +3 -0
- package/dist/popconfirm/index.js +3 -0
- package/dist/popconfirm/style.css +22 -0
- package/dist/popover/api.d.ts +36 -0
- package/dist/popover/api.js +61 -0
- package/dist/popover/component.d.ts +38 -0
- package/dist/popover/component.js +51 -0
- package/dist/popover/index.d.ts +3 -0
- package/dist/popover/index.js +3 -0
- package/dist/popover/style.css +102 -0
- package/dist/progress/index.d.ts +34 -0
- package/dist/progress/index.js +70 -0
- package/dist/progress/style.css +37 -0
- package/dist/quick-input/index.d.ts +2 -0
- package/dist/quick-input/index.js +2 -0
- package/dist/quick-input/number.d.ts +35 -0
- package/dist/quick-input/number.js +68 -0
- package/dist/quick-input/text.d.ts +20 -0
- package/dist/quick-input/text.js +61 -0
- package/dist/result/index.d.ts +39 -0
- package/dist/result/index.js +71 -0
- package/dist/result/style.css +41 -0
- package/dist/search-box/index.d.ts +26 -0
- package/dist/search-box/index.js +60 -0
- package/dist/search-box/style.css +22 -0
- package/dist/segmented/index.d.ts +42 -0
- package/dist/segmented/index.js +61 -0
- package/dist/segmented/style.css +28 -0
- package/dist/skeleton/index.d.ts +22 -0
- package/dist/skeleton/index.js +68 -0
- package/dist/skeleton/style.css +31 -0
- package/dist/steps/index.d.ts +46 -0
- package/dist/steps/index.js +95 -0
- package/dist/steps/style.css +181 -0
- package/dist/switch/index.d.ts +32 -0
- package/dist/switch/index.js +74 -0
- package/dist/switch/style.css +52 -0
- package/dist/table/body.d.ts +21 -0
- package/dist/table/body.js +35 -0
- package/dist/table/column.d.ts +78 -0
- package/dist/table/column.js +113 -0
- package/dist/table/header.d.ts +13 -0
- package/dist/table/header.js +15 -0
- package/dist/table/index.d.ts +41 -0
- package/dist/table/index.js +84 -0
- package/dist/table/row.d.ts +25 -0
- package/dist/table/row.js +74 -0
- package/dist/table/style.css +98 -0
- package/dist/tabs/index.d.ts +49 -0
- package/dist/tabs/index.js +76 -0
- package/dist/tabs/style.css +41 -0
- package/dist/tag/index.d.ts +53 -0
- package/dist/tag/index.js +100 -0
- package/dist/tag/style.css +134 -0
- package/dist/text/index.d.ts +104 -0
- package/dist/text/index.js +143 -0
- package/dist/text/style.css +113 -0
- package/dist/theme/index.d.ts +14 -0
- package/dist/theme/index.js +94 -0
- package/dist/theme/themes.d.ts +3 -0
- package/dist/theme/themes.js +323 -0
- package/dist/theme/types.d.ts +58 -0
- package/dist/theme/types.js +1 -0
- package/dist/timeline/index.d.ts +28 -0
- package/dist/timeline/index.js +35 -0
- package/dist/timeline/style.css +53 -0
- package/dist/toast/index.d.ts +53 -0
- package/dist/toast/index.js +131 -0
- package/dist/toast/style.css +76 -0
- package/dist/tooltip/api.d.ts +44 -0
- package/dist/tooltip/api.js +103 -0
- package/dist/tooltip/component.d.ts +28 -0
- package/dist/tooltip/component.js +35 -0
- package/dist/tooltip/index.d.ts +3 -0
- package/dist/tooltip/index.js +3 -0
- package/dist/tooltip/style.css +46 -0
- package/dist/tree/dragging-img.d.ts +13 -0
- package/dist/tree/dragging-img.js +23 -0
- package/dist/tree/index.d.ts +180 -0
- package/dist/tree/index.js +385 -0
- package/dist/tree/node-info.d.ts +63 -0
- package/dist/tree/node-info.js +128 -0
- package/dist/tree/node.d.ts +132 -0
- package/dist/tree/node.js +264 -0
- package/dist/tree/style.css +99 -0
- package/dist/upload/index.d.ts +48 -0
- package/dist/upload/index.js +141 -0
- package/dist/upload/style.css +41 -0
- package/package.json +51 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FullRenderingModule } from 'wok-ui';
|
|
2
|
+
export declare class DatePicker extends FullRenderingModule {
|
|
3
|
+
private readonly opts;
|
|
4
|
+
private value?;
|
|
5
|
+
private panelYear;
|
|
6
|
+
private panelMonth;
|
|
7
|
+
private popup?;
|
|
8
|
+
constructor(opts: {
|
|
9
|
+
value?: Date;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
required?: boolean | string;
|
|
12
|
+
min?: Date | {
|
|
13
|
+
min: Date;
|
|
14
|
+
errMsg: string;
|
|
15
|
+
};
|
|
16
|
+
max?: Date | {
|
|
17
|
+
max: Date;
|
|
18
|
+
errMsg: string;
|
|
19
|
+
};
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
disabledDate?: (date: Date) => boolean;
|
|
22
|
+
onChange?: (date?: Date) => void;
|
|
23
|
+
});
|
|
24
|
+
protected buildContent(): void;
|
|
25
|
+
private togglePopup;
|
|
26
|
+
private openPopup;
|
|
27
|
+
private selectDate;
|
|
28
|
+
destroy(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { FullRenderingModule } from 'wok-ui';
|
|
2
|
+
import { CalendarPanel } from './calendar-panel';
|
|
3
|
+
import { DatePickerLayer } from './layer';
|
|
4
|
+
import { formatDate, combineDateConstraints } from './date-utils';
|
|
5
|
+
export class DatePicker extends FullRenderingModule {
|
|
6
|
+
constructor(opts) {
|
|
7
|
+
super('wok-ui-ext-date-picker');
|
|
8
|
+
this.opts = opts;
|
|
9
|
+
const today = new Date();
|
|
10
|
+
this.value = opts.value;
|
|
11
|
+
const initDate = this.value ?? today;
|
|
12
|
+
this.panelYear = initDate.getFullYear();
|
|
13
|
+
this.panelMonth = initDate.getMonth();
|
|
14
|
+
this.render();
|
|
15
|
+
}
|
|
16
|
+
buildContent() {
|
|
17
|
+
this.addChild({
|
|
18
|
+
classNames: ['wok-ui-ext-date-picker-trigger', this.opts.disabled ? 'disabled' : ''],
|
|
19
|
+
children: this.value ? formatDate(this.value) : (this.opts.placeholder ?? '请选择日期'),
|
|
20
|
+
onClick: () => {
|
|
21
|
+
if (this.opts.disabled)
|
|
22
|
+
return;
|
|
23
|
+
this.togglePopup();
|
|
24
|
+
},
|
|
25
|
+
postHandle: el => {
|
|
26
|
+
if (!this.value) {
|
|
27
|
+
el.classList.add('placeholder');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
togglePopup() {
|
|
33
|
+
// 已打开则关闭
|
|
34
|
+
if (this.popup) {
|
|
35
|
+
this.popup.destroy();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const target = this.value ?? new Date();
|
|
39
|
+
this.panelYear = target.getFullYear();
|
|
40
|
+
this.panelMonth = target.getMonth();
|
|
41
|
+
const today = new Date();
|
|
42
|
+
const panel = new CalendarPanel({
|
|
43
|
+
year: this.panelYear,
|
|
44
|
+
month: this.panelMonth,
|
|
45
|
+
selectedDates: () => (this.value ? [this.value] : []),
|
|
46
|
+
today,
|
|
47
|
+
disabledDate: combineDateConstraints(this.opts.min, this.opts.max, this.opts.disabledDate),
|
|
48
|
+
onDayClick: date => this.selectDate(date),
|
|
49
|
+
onMonthChange: (year, month) => {
|
|
50
|
+
this.panelYear = year;
|
|
51
|
+
this.panelMonth = month;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
this.openPopup([panel]);
|
|
55
|
+
}
|
|
56
|
+
openPopup(panels) {
|
|
57
|
+
const popup = new DatePickerLayer({
|
|
58
|
+
target: this.el,
|
|
59
|
+
panels,
|
|
60
|
+
onClose: () => {
|
|
61
|
+
if (this.popup === popup) {
|
|
62
|
+
this.popup = undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.popup = popup;
|
|
67
|
+
popup.mount(document.body);
|
|
68
|
+
}
|
|
69
|
+
selectDate(date) {
|
|
70
|
+
if (combineDateConstraints(this.opts.min, this.opts.max, this.opts.disabledDate)(date))
|
|
71
|
+
return;
|
|
72
|
+
this.value = date;
|
|
73
|
+
this.popup?.destroy();
|
|
74
|
+
this.render();
|
|
75
|
+
this.opts.onChange?.(date);
|
|
76
|
+
}
|
|
77
|
+
destroy() {
|
|
78
|
+
this.popup?.destroy();
|
|
79
|
+
super.destroy();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取指定年月的天数
|
|
3
|
+
*/
|
|
4
|
+
export declare function getDaysInMonth(year: number, month: number): number;
|
|
5
|
+
/**
|
|
6
|
+
* 获取指定年月第一天是星期几(0=周日, 6=周六)
|
|
7
|
+
*/
|
|
8
|
+
export declare function getFirstDayOfWeek(year: number, month: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* 判断两个日期是否是同一天
|
|
11
|
+
*/
|
|
12
|
+
export declare function isSameDay(a: Date, b: Date): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 判断 date 是否在 [start, end] 范围内(含边界)
|
|
15
|
+
*/
|
|
16
|
+
export declare function isDateInRange(date: Date, start: Date, end: Date): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 比较两个日期:返回负数表示 a < b,0 表示相等,正数表示 a > b
|
|
19
|
+
*/
|
|
20
|
+
export declare function compareDate(a: Date, b: Date): number;
|
|
21
|
+
/**
|
|
22
|
+
* 日期增加/减少月份
|
|
23
|
+
*/
|
|
24
|
+
export declare function addMonths(date: Date, n: number): Date;
|
|
25
|
+
/**
|
|
26
|
+
* 格式化日期为 yyyy-MM-dd
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatDate(date: Date): string;
|
|
29
|
+
/**
|
|
30
|
+
* 格式化日期为 yyyy年M月d日
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatDateCN(date: Date): string;
|
|
33
|
+
/**
|
|
34
|
+
* 将 min / max / disabledDate 合并为一个统一的日期禁用回调。
|
|
35
|
+
* min 和 max 支持传 Date 或带错误信息的对象。
|
|
36
|
+
*/
|
|
37
|
+
export declare function combineDateConstraints(min?: Date | {
|
|
38
|
+
min: Date;
|
|
39
|
+
errMsg: string;
|
|
40
|
+
}, max?: Date | {
|
|
41
|
+
max: Date;
|
|
42
|
+
errMsg: string;
|
|
43
|
+
}, disabledDate?: (date: Date) => boolean): (date: Date) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 解析 yyyy-MM-dd 字符串为 Date
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseDate(str: string): Date | null;
|
|
48
|
+
/**
|
|
49
|
+
* 生成日历网格数据,固定 42 个格子(6行×7列)
|
|
50
|
+
*/
|
|
51
|
+
export interface CalendarDay {
|
|
52
|
+
date: Date;
|
|
53
|
+
/** 该日期实际所属的月份(可能与当前视图月份不同) */
|
|
54
|
+
month: number;
|
|
55
|
+
day: number;
|
|
56
|
+
/** 是否属于当前视图月份 */
|
|
57
|
+
isCurrentMonth: boolean;
|
|
58
|
+
}
|
|
59
|
+
export declare function getCalendarDays(year: number, month: number): CalendarDay[];
|
|
60
|
+
/** 中文星期缩写 */
|
|
61
|
+
export declare const WEEK_DAYS: string[];
|
|
62
|
+
/** 中文月份名 */
|
|
63
|
+
export declare const MONTH_NAMES: string[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取指定年月的天数
|
|
3
|
+
*/
|
|
4
|
+
export function getDaysInMonth(year, month) {
|
|
5
|
+
return new Date(year, month + 1, 0).getDate();
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 获取指定年月第一天是星期几(0=周日, 6=周六)
|
|
9
|
+
*/
|
|
10
|
+
export function getFirstDayOfWeek(year, month) {
|
|
11
|
+
return new Date(year, month, 1).getDay();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 判断两个日期是否是同一天
|
|
15
|
+
*/
|
|
16
|
+
export function isSameDay(a, b) {
|
|
17
|
+
return a.getFullYear() === b.getFullYear()
|
|
18
|
+
&& a.getMonth() === b.getMonth()
|
|
19
|
+
&& a.getDate() === b.getDate();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 判断 date 是否在 [start, end] 范围内(含边界)
|
|
23
|
+
*/
|
|
24
|
+
export function isDateInRange(date, start, end) {
|
|
25
|
+
const t = date.getTime();
|
|
26
|
+
return t >= start.getTime() && t <= end.getTime();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 比较两个日期:返回负数表示 a < b,0 表示相等,正数表示 a > b
|
|
30
|
+
*/
|
|
31
|
+
export function compareDate(a, b) {
|
|
32
|
+
return a.getTime() - b.getTime();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 日期增加/减少月份
|
|
36
|
+
*/
|
|
37
|
+
export function addMonths(date, n) {
|
|
38
|
+
const d = new Date(date);
|
|
39
|
+
d.setMonth(d.getMonth() + n);
|
|
40
|
+
return d;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 格式化日期为 yyyy-MM-dd
|
|
44
|
+
*/
|
|
45
|
+
export function formatDate(date) {
|
|
46
|
+
const y = date.getFullYear();
|
|
47
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
48
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
49
|
+
return `${y}-${m}-${d}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 格式化日期为 yyyy年M月d日
|
|
53
|
+
*/
|
|
54
|
+
export function formatDateCN(date) {
|
|
55
|
+
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 将 min / max / disabledDate 合并为一个统一的日期禁用回调。
|
|
59
|
+
* min 和 max 支持传 Date 或带错误信息的对象。
|
|
60
|
+
*/
|
|
61
|
+
export function combineDateConstraints(min, max, disabledDate) {
|
|
62
|
+
const minDate = min instanceof Date ? min : min?.min;
|
|
63
|
+
const maxDate = max instanceof Date ? max : max?.max;
|
|
64
|
+
return (date) => {
|
|
65
|
+
if (minDate && compareDate(date, minDate) < 0)
|
|
66
|
+
return true;
|
|
67
|
+
if (maxDate && compareDate(date, maxDate) > 0)
|
|
68
|
+
return true;
|
|
69
|
+
return disabledDate?.(date) ?? false;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 解析 yyyy-MM-dd 字符串为 Date
|
|
74
|
+
*/
|
|
75
|
+
export function parseDate(str) {
|
|
76
|
+
const match = str.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
|
77
|
+
if (!match)
|
|
78
|
+
return null;
|
|
79
|
+
const d = new Date(+match[1], +match[2] - 1, +match[3]);
|
|
80
|
+
if (isNaN(d.getTime()))
|
|
81
|
+
return null;
|
|
82
|
+
return d;
|
|
83
|
+
}
|
|
84
|
+
export function getCalendarDays(year, month) {
|
|
85
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
86
|
+
const firstDay = getFirstDayOfWeek(year, month);
|
|
87
|
+
const days = [];
|
|
88
|
+
// 填充上月末尾
|
|
89
|
+
const prevMonthDays = getDaysInMonth(year, month - 1); // month-1 可能为 -1,但 Date 会自动处理
|
|
90
|
+
const prevMonth = month === 0 ? 11 : month - 1;
|
|
91
|
+
const prevYear = month === 0 ? year - 1 : year;
|
|
92
|
+
for (let i = firstDay - 1; i >= 0; i--) {
|
|
93
|
+
const day = prevMonthDays - i;
|
|
94
|
+
days.push({
|
|
95
|
+
date: new Date(prevYear, prevMonth, day),
|
|
96
|
+
month: prevMonth,
|
|
97
|
+
day,
|
|
98
|
+
isCurrentMonth: false
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// 当月
|
|
102
|
+
for (let d = 1; d <= daysInMonth; d++) {
|
|
103
|
+
days.push({
|
|
104
|
+
date: new Date(year, month, d),
|
|
105
|
+
month,
|
|
106
|
+
day: d,
|
|
107
|
+
isCurrentMonth: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// 填充下月开头
|
|
111
|
+
const remaining = 42 - days.length;
|
|
112
|
+
const nextMonth = month === 11 ? 0 : month + 1;
|
|
113
|
+
const nextYear = month === 11 ? year + 1 : year;
|
|
114
|
+
for (let d = 1; d <= remaining; d++) {
|
|
115
|
+
days.push({
|
|
116
|
+
date: new Date(nextYear, nextMonth, d),
|
|
117
|
+
month: nextMonth,
|
|
118
|
+
day: d,
|
|
119
|
+
isCurrentMonth: false
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return days;
|
|
123
|
+
}
|
|
124
|
+
/** 中文星期缩写 */
|
|
125
|
+
export const WEEK_DAYS = ['日', '一', '二', '三', '四', '五', '六'];
|
|
126
|
+
/** 中文月份名 */
|
|
127
|
+
export const MONTH_NAMES = [
|
|
128
|
+
'1月', '2月', '3月', '4月', '5月', '6月',
|
|
129
|
+
'7月', '8月', '9月', '10月', '11月', '12月'
|
|
130
|
+
];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import './style.less';
|
|
2
|
+
export { DatePicker } from './date-picker';
|
|
3
|
+
export { DateRangePicker } from './range-picker';
|
|
4
|
+
export type { DateRange } from './range-picker';
|
|
5
|
+
export { CalendarPanel } from './calendar-panel';
|
|
6
|
+
export { formatDate, formatDateCN, parseDate, isSameDay, isDateInRange, compareDate, getDaysInMonth, getFirstDayOfWeek, getCalendarDays, addMonths } from './date-utils';
|
|
7
|
+
export type { CalendarDay } from './date-utils';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import './style.css';
|
|
2
|
+
export { DatePicker } from './date-picker';
|
|
3
|
+
export { DateRangePicker } from './range-picker';
|
|
4
|
+
export { CalendarPanel } from './calendar-panel';
|
|
5
|
+
export { formatDate, formatDateCN, parseDate, isSameDay, isDateInRange, compareDate, getDaysInMonth, getFirstDayOfWeek, getCalendarDays, addMonths } from './date-utils';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FloatingLayer } from '../floating-layer';
|
|
2
|
+
import { CalendarPanel } from './calendar-panel';
|
|
3
|
+
/**
|
|
4
|
+
* 日期选择浮层。挂载到 body,由 FloatingLayer 提供定位、滚动跟随、自动关闭。
|
|
5
|
+
* 单选包含一个日历面板,范围选择包含两个。
|
|
6
|
+
*/
|
|
7
|
+
export declare class DatePickerLayer extends FloatingLayer {
|
|
8
|
+
private readonly panels;
|
|
9
|
+
constructor(opts: {
|
|
10
|
+
target: HTMLElement;
|
|
11
|
+
panels: CalendarPanel[];
|
|
12
|
+
onClose?: () => void;
|
|
13
|
+
});
|
|
14
|
+
protected buildContent(): void;
|
|
15
|
+
/** 重绘面板,用于切换月份等需要整体重新构建内容的场景 */
|
|
16
|
+
refresh(): void;
|
|
17
|
+
/** 局部更新每个面板的 hover 预览 class */
|
|
18
|
+
setHoveredDate(hovered: Date | undefined): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FloatingLayer } from '../floating-layer';
|
|
2
|
+
/**
|
|
3
|
+
* 日期选择浮层。挂载到 body,由 FloatingLayer 提供定位、滚动跟随、自动关闭。
|
|
4
|
+
* 单选包含一个日历面板,范围选择包含两个。
|
|
5
|
+
*/
|
|
6
|
+
export class DatePickerLayer extends FloatingLayer {
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
super({ target: opts.target, placement: 'bottomLeft', offset: 6, onClose: opts.onClose });
|
|
9
|
+
this.el.classList.add('wok-ui-ext-date-picker-popup');
|
|
10
|
+
if (opts.panels.length > 1) {
|
|
11
|
+
this.el.classList.add('wok-ui-ext-date-range-popup');
|
|
12
|
+
}
|
|
13
|
+
this.panels = opts.panels;
|
|
14
|
+
}
|
|
15
|
+
buildContent() {
|
|
16
|
+
if (this.panels.length > 1) {
|
|
17
|
+
this.panels.forEach(panel => {
|
|
18
|
+
this.addChild({
|
|
19
|
+
classNames: 'wok-ui-ext-date-range-panel',
|
|
20
|
+
children: panel
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.addChild(this.panels[0]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** 重绘面板,用于切换月份等需要整体重新构建内容的场景 */
|
|
29
|
+
refresh() {
|
|
30
|
+
this.panels.forEach(p => p.refresh());
|
|
31
|
+
}
|
|
32
|
+
/** 局部更新每个面板的 hover 预览 class */
|
|
33
|
+
setHoveredDate(hovered) {
|
|
34
|
+
this.panels.forEach(p => p.setHoveredDate(hovered));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DivModule } from 'wok-ui';
|
|
2
|
+
export type DateRange = [Date, Date];
|
|
3
|
+
export declare class DateRangePicker extends DivModule {
|
|
4
|
+
private readonly opts;
|
|
5
|
+
private value?;
|
|
6
|
+
private hoveredDate?;
|
|
7
|
+
private popup?;
|
|
8
|
+
private textEl?;
|
|
9
|
+
constructor(opts: {
|
|
10
|
+
value?: DateRange;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
required?: boolean | string;
|
|
13
|
+
min?: Date | {
|
|
14
|
+
min: Date;
|
|
15
|
+
errMsg: string;
|
|
16
|
+
};
|
|
17
|
+
max?: Date | {
|
|
18
|
+
max: Date;
|
|
19
|
+
errMsg: string;
|
|
20
|
+
};
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
disabledDate?: (date: Date) => boolean;
|
|
23
|
+
onChange?: (range?: DateRange) => void;
|
|
24
|
+
});
|
|
25
|
+
private rangeText;
|
|
26
|
+
private updateText;
|
|
27
|
+
private togglePopup;
|
|
28
|
+
private handleDayClick;
|
|
29
|
+
destroy(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { DivModule } from 'wok-ui';
|
|
2
|
+
import { CalendarPanel } from './calendar-panel';
|
|
3
|
+
import { DatePickerLayer } from './layer';
|
|
4
|
+
import { formatDate, compareDate, addMonths, combineDateConstraints } from './date-utils';
|
|
5
|
+
export class DateRangePicker extends DivModule {
|
|
6
|
+
constructor(opts) {
|
|
7
|
+
super('wok-ui-ext-date-range-picker wok-ui-ext-date-picker');
|
|
8
|
+
this.opts = opts;
|
|
9
|
+
this.addChild({
|
|
10
|
+
classNames: ['wok-ui-ext-date-picker-trigger', this.opts.disabled ? 'disabled' : ''],
|
|
11
|
+
children: {
|
|
12
|
+
tag: 'span',
|
|
13
|
+
classNames: 'wok-ui-ext-date-picker-text',
|
|
14
|
+
innerText: this.rangeText(),
|
|
15
|
+
postHandle: el => {
|
|
16
|
+
this.textEl = el;
|
|
17
|
+
if (!this.value) {
|
|
18
|
+
el.parentElement?.classList.add('placeholder');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
onClick: () => {
|
|
23
|
+
if (this.opts.disabled)
|
|
24
|
+
return;
|
|
25
|
+
this.togglePopup();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
rangeText() {
|
|
30
|
+
if (!this.value) {
|
|
31
|
+
return this.opts.placeholder ?? '请选择日期范围';
|
|
32
|
+
}
|
|
33
|
+
const [start, end] = this.value;
|
|
34
|
+
if (compareDate(start, end) === 0) {
|
|
35
|
+
return `${formatDate(start)} ~ 结束日期`;
|
|
36
|
+
}
|
|
37
|
+
return `${formatDate(start)} ~ ${formatDate(end)}`;
|
|
38
|
+
}
|
|
39
|
+
updateText() {
|
|
40
|
+
if (this.textEl) {
|
|
41
|
+
this.textEl.innerText = this.rangeText();
|
|
42
|
+
if (this.value) {
|
|
43
|
+
this.textEl.parentElement?.classList.remove('placeholder');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.textEl.parentElement?.classList.add('placeholder');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
togglePopup() {
|
|
51
|
+
if (this.popup) {
|
|
52
|
+
this.popup.destroy();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.hoveredDate = undefined;
|
|
56
|
+
const today = new Date();
|
|
57
|
+
const selected = () => this.value ?? [];
|
|
58
|
+
const target = this.value?.[0] ?? today;
|
|
59
|
+
const leftYear = target.getFullYear();
|
|
60
|
+
const leftMonth = target.getMonth();
|
|
61
|
+
const rightDate = addMonths(new Date(leftYear, leftMonth, 1), 1);
|
|
62
|
+
const disabledDate = combineDateConstraints(this.opts.min, this.opts.max, this.opts.disabledDate);
|
|
63
|
+
const buildPanel = (year, month) => new CalendarPanel({
|
|
64
|
+
year,
|
|
65
|
+
month,
|
|
66
|
+
selectedDates: selected,
|
|
67
|
+
today,
|
|
68
|
+
disabledDate,
|
|
69
|
+
onDayClick: date => this.handleDayClick(date, disabledDate),
|
|
70
|
+
onDayHover: date => {
|
|
71
|
+
this.hoveredDate = date;
|
|
72
|
+
this.popup?.setHoveredDate(date);
|
|
73
|
+
},
|
|
74
|
+
onMonthChange: () => { }
|
|
75
|
+
});
|
|
76
|
+
const popup = new DatePickerLayer({
|
|
77
|
+
target: this.el,
|
|
78
|
+
panels: [buildPanel(leftYear, leftMonth), buildPanel(rightDate.getFullYear(), rightDate.getMonth())],
|
|
79
|
+
onClose: () => {
|
|
80
|
+
if (this.popup === popup) {
|
|
81
|
+
this.popup = undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.popup = popup;
|
|
86
|
+
popup.mount(document.body);
|
|
87
|
+
}
|
|
88
|
+
handleDayClick(date, disabledDate) {
|
|
89
|
+
if (disabledDate(date))
|
|
90
|
+
return;
|
|
91
|
+
if (!this.value) {
|
|
92
|
+
this.value = [date, date];
|
|
93
|
+
this.hoveredDate = date;
|
|
94
|
+
this.popup?.setHoveredDate(date);
|
|
95
|
+
this.updateText();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const [prevStart, prevEnd] = this.value;
|
|
99
|
+
if (compareDate(prevStart, prevEnd) === 0) {
|
|
100
|
+
this.value = compareDate(date, prevStart) < 0 ? [date, prevStart] : [prevStart, date];
|
|
101
|
+
this.hoveredDate = undefined;
|
|
102
|
+
this.popup?.destroy();
|
|
103
|
+
this.updateText();
|
|
104
|
+
this.opts.onChange?.(this.value);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
this.value = [date, date];
|
|
108
|
+
this.hoveredDate = date;
|
|
109
|
+
this.popup?.refresh();
|
|
110
|
+
this.updateText();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
destroy() {
|
|
114
|
+
this.popup?.destroy();
|
|
115
|
+
super.destroy();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
.wok-ui-ext-date-picker {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
user-select: none;
|
|
6
|
+
}
|
|
7
|
+
.wok-ui-ext-date-picker-trigger {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
width: 100%;
|
|
11
|
+
min-width: 160px;
|
|
12
|
+
padding: 9px 18px;
|
|
13
|
+
border: 1px solid var(--border-medium);
|
|
14
|
+
border-radius: var(--radius-xl);
|
|
15
|
+
font-size: var(--font-size-body);
|
|
16
|
+
color: var(--text-primary);
|
|
17
|
+
background: var(--bg-content);
|
|
18
|
+
transition: border-color var(--transition-speed), box-shadow var(--transition-speed);
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
}
|
|
22
|
+
.wok-ui-ext-date-picker-trigger:hover {
|
|
23
|
+
border-color: var(--color-primary);
|
|
24
|
+
}
|
|
25
|
+
.wok-ui-ext-date-picker-trigger.placeholder {
|
|
26
|
+
color: var(--text-placeholder);
|
|
27
|
+
}
|
|
28
|
+
.wok-ui-ext-date-picker-trigger.disabled {
|
|
29
|
+
opacity: 0.6;
|
|
30
|
+
cursor: not-allowed;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
.wok-ui-ext-date-picker-popup {
|
|
34
|
+
background: var(--bg-card);
|
|
35
|
+
border: 1px solid var(--border-medium);
|
|
36
|
+
border-radius: var(--radius-lg);
|
|
37
|
+
box-shadow: var(--shadow-lg);
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
.wok-ui-ext-calendar-panel {
|
|
41
|
+
padding: 16px 20px 20px;
|
|
42
|
+
width: 304px;
|
|
43
|
+
}
|
|
44
|
+
.wok-ui-ext-calendar-header {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: space-between;
|
|
48
|
+
margin-bottom: 16px;
|
|
49
|
+
padding: 0 4px;
|
|
50
|
+
}
|
|
51
|
+
.wok-ui-ext-calendar-header .wok-ui-ext-calendar-nav-btn {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
width: 28px;
|
|
56
|
+
height: 28px;
|
|
57
|
+
border-radius: var(--radius-sm);
|
|
58
|
+
color: var(--text-tertiary);
|
|
59
|
+
font-size: var(--font-size-xs);
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
transition: background var(--transition-speed), color var(--transition-speed);
|
|
62
|
+
}
|
|
63
|
+
.wok-ui-ext-calendar-header .wok-ui-ext-calendar-nav-btn:hover {
|
|
64
|
+
background: var(--bg-hover);
|
|
65
|
+
color: var(--color-primary);
|
|
66
|
+
}
|
|
67
|
+
.wok-ui-ext-calendar-header .wok-ui-ext-calendar-month-label {
|
|
68
|
+
font-size: var(--font-size-small);
|
|
69
|
+
font-weight: var(--font-weight-semibold);
|
|
70
|
+
color: var(--text-primary);
|
|
71
|
+
}
|
|
72
|
+
.wok-ui-ext-calendar-weekdays {
|
|
73
|
+
display: grid;
|
|
74
|
+
grid-template-columns: repeat(7, 1fr);
|
|
75
|
+
margin-bottom: 8px;
|
|
76
|
+
}
|
|
77
|
+
.wok-ui-ext-calendar-weekday {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
height: 32px;
|
|
82
|
+
font-size: var(--font-size-xs);
|
|
83
|
+
font-weight: var(--font-weight-medium);
|
|
84
|
+
color: var(--text-tertiary);
|
|
85
|
+
}
|
|
86
|
+
.wok-ui-ext-calendar-grid {
|
|
87
|
+
display: grid;
|
|
88
|
+
grid-template-columns: repeat(7, 1fr);
|
|
89
|
+
gap: 2px;
|
|
90
|
+
}
|
|
91
|
+
.wok-ui-ext-calendar-day {
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
height: 34px;
|
|
96
|
+
font-size: var(--font-size-small);
|
|
97
|
+
color: var(--text-primary);
|
|
98
|
+
border-radius: var(--radius-sm);
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
transition: background var(--transition-speed), color var(--transition-speed);
|
|
101
|
+
}
|
|
102
|
+
.wok-ui-ext-calendar-day:hover:not(.disabled):not(.selected) {
|
|
103
|
+
background: var(--bg-hover);
|
|
104
|
+
}
|
|
105
|
+
.wok-ui-ext-calendar-day.adjacent-month {
|
|
106
|
+
color: var(--text-disabled);
|
|
107
|
+
cursor: default;
|
|
108
|
+
}
|
|
109
|
+
.wok-ui-ext-calendar-day.adjacent-month:hover {
|
|
110
|
+
background: transparent;
|
|
111
|
+
}
|
|
112
|
+
.wok-ui-ext-calendar-day.today:not(.selected) {
|
|
113
|
+
color: var(--color-primary);
|
|
114
|
+
font-weight: var(--font-weight-semibold);
|
|
115
|
+
border: 1px solid var(--color-primary);
|
|
116
|
+
}
|
|
117
|
+
.wok-ui-ext-calendar-day.selected {
|
|
118
|
+
background: var(--color-primary);
|
|
119
|
+
color: #fff;
|
|
120
|
+
font-weight: var(--font-weight-semibold);
|
|
121
|
+
border-radius: 0;
|
|
122
|
+
}
|
|
123
|
+
.wok-ui-ext-calendar-day.selected.range-start {
|
|
124
|
+
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
|
|
125
|
+
}
|
|
126
|
+
.wok-ui-ext-calendar-day.selected.range-end {
|
|
127
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
128
|
+
}
|
|
129
|
+
.wok-ui-ext-calendar-day.selected:not(.range-start):not(.range-end) {
|
|
130
|
+
border-radius: var(--radius-sm);
|
|
131
|
+
}
|
|
132
|
+
.wok-ui-ext-calendar-day.in-range {
|
|
133
|
+
background: var(--color-primary-light);
|
|
134
|
+
color: var(--color-primary);
|
|
135
|
+
border-radius: 0;
|
|
136
|
+
}
|
|
137
|
+
.wok-ui-ext-calendar-day.in-range-hover {
|
|
138
|
+
background: var(--color-primary-soft);
|
|
139
|
+
border-radius: 0;
|
|
140
|
+
}
|
|
141
|
+
.wok-ui-ext-calendar-day.disabled {
|
|
142
|
+
color: var(--text-disabled);
|
|
143
|
+
cursor: not-allowed;
|
|
144
|
+
}
|
|
145
|
+
.wok-ui-ext-calendar-day.disabled:hover {
|
|
146
|
+
background: transparent;
|
|
147
|
+
}
|
|
148
|
+
.wok-ui-ext-date-range-picker .wok-ui-ext-date-picker-trigger {
|
|
149
|
+
min-width: 240px;
|
|
150
|
+
width: auto;
|
|
151
|
+
}
|
|
152
|
+
.wok-ui-ext-date-range-popup {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: row;
|
|
155
|
+
}
|
|
156
|
+
.wok-ui-ext-date-range-popup .wok-ui-ext-date-range-panel .wok-ui-ext-calendar-panel {
|
|
157
|
+
border-right: 1px solid var(--border-light);
|
|
158
|
+
}
|
|
159
|
+
.wok-ui-ext-date-range-popup .wok-ui-ext-date-range-panel .wok-ui-ext-calendar-panel:last-child {
|
|
160
|
+
border-right: none;
|
|
161
|
+
}
|