diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss
index 01c381b..762c4be 100644
--- a/lib/ui/css/grid.scss
+++ b/lib/ui/css/grid.scss
@@ -348,7 +348,7 @@
}
}
- .ui-grid-date-cell {
+ .ui-date-cell {
line-height: 22px;
box-sizing: border-box;
padding: var(--spacing-cell);
diff --git a/lib/ui/date.d.ts b/lib/ui/date.d.ts
new file mode 100644
index 0000000..557062e
--- /dev/null
+++ b/lib/ui/date.d.ts
@@ -0,0 +1,71 @@
+/**
+ * 创建日期选择框
+ * @param min 最小可选日期
+ * @param max 最大可选日期
+ * @returns 返回创建的日期选择框
+ */
+export function createDateInput(min?: string, max?: string): HTMLInputElement;
+
+/**
+ * 格式化日期字符串
+ * @param date 要格式化的日期值
+ * 支持以下几种数据类型
+ * `"2024-01-26"`
+ * `"1/26/2024"`
+ * `"638418240000000000"`
+ * `new Date('2024-01-26')`
+ * @returns 格式化为 M/d/yyyy 的日期字符串
+ */
+export function formatDate(date: Date | number | string): string;
+
+/**
+ * 设置显示日期
+ * @param element 要设置显示日期的元素
+ * @param val 日期值,支持格式参见 {@linkcode formatDate}
+ */
+export function setDateValue(element: HTMLElement, val: Date | number | string): void;
+
+/**
+ * 从日期选择框获取日期值
+ * @param element 要获取的日期选择框
+ * @param formatter 自定义格式化函数,传入参数为 `Date` 类型
+ * @returns 默认返回日期 `ticks` 的字符串
+ */
+export function getDateValue(element: HTMLInputElement, formatter?: (date: Date) => string): string;
+
+/** 日期选择框类 */
+export class DateSelector {
+ /**
+ * 日期发生变化时触发的事件
+ * @param date 日期值,或者经自定义参数中格式化函数格式后的值
+ */
+ onDateChanged?: (date: Date | any) => void;
+
+ /**
+ * 日期选择框构造函数
+ * @param opts 日期选项参数
+ */
+ constructor(opts: {
+ /** 父容器元素,可以为 `string` 作为选择器 */
+ parent: HTMLElement | string,
+ /** 最小可选择日期 */
+ minDate?: string,
+ /** 最大可选择日期 */
+ maxDate?: string,
+ /**
+ * 自定义格式化函数,用于获取日期值时调用
+ * @param date 日期值
+ * @returns 返回格式化的值
+ */
+ valueFormatter?: (date: Date) => any
+ });
+
+ get enabled(): boolean;
+ set enabled(flag: boolean);
+
+ get value(): Date | any;
+ set value(val: Date | number | string);
+
+ set minDate(date: string);
+ set maxDate(date: string);
+}
\ No newline at end of file
diff --git a/lib/ui/date.js b/lib/ui/date.js
new file mode 100644
index 0000000..e01375a
--- /dev/null
+++ b/lib/ui/date.js
@@ -0,0 +1,153 @@
+import { createElement } from "../functions";
+
+export function createDateInput(min, max) {
+ const date = createElement('input', 'ui-date-cell');
+ date.required = true;
+ date.type = 'date';
+ if (min != null) {
+ date.min = min;
+ }
+ if (max != null) {
+ date.max = max;
+ }
+ return date;
+}
+
+function toDateValue(dt) {
+ if (isNaN(dt)) {
+ return '';
+ }
+ const month = String(dt.getMonth() + 1).padStart(2, '0');
+ const date = String(dt.getDate()).padStart(2, '0');
+ return `${dt.getFullYear()}-${month}-${date}`;
+}
+
+function resolveDate(s) {
+ if (s instanceof Date) {
+ return s;
+ }
+ const ticks = Number(s);
+ if (!isNaN(ticks) && ticks > 0) {
+ return new Date((ticks - 621355968e9) / 1e4);
+ }
+ return new Date(s);
+}
+
+export function formatDate(date) {
+ date = resolveDate(date);
+ if (date instanceof Date && !isNaN(date)) {
+ return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
+ }
+ return '';
+}
+
+export function setDateValue(element, val) {
+ if (element.tagName === 'INPUT') {
+ if (val === '') {
+ element.value = '';
+ } else if (isNaN(val)) {
+ if (/^\d{4}-\d{2}-\d{2}$/.test(val)) {
+ element.value = val;
+ } else if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
+ element.value = toDateValue(new Date(val));
+ } else {
+ element.value = '';
+ }
+ } else {
+ if (!(val instanceof Date)) {
+ val = new Date((val - 621355968e9) / 1e4);
+ }
+ element.value = toDateValue(val);
+ }
+ } else {
+ element.innerText = formatDate(val);
+ }
+}
+
+export function getDateValue(element, formatter) {
+ const date = element?.valueAsDate;
+ if (date instanceof Date && !isNaN(date)) {
+ const year = date.getFullYear();
+ if (year < 1900 || year > 9999) {
+ return '';
+ }
+ if (typeof formatter === 'function') {
+ return formatter(date);
+ }
+ return String(date.getTime() * 1e4 + 621355968e9);
+ }
+ return '';
+}
+
+export class DateSelector {
+ _var = {
+ parent: null,
+ options: null
+ };
+
+ onDateChanged;
+
+ constructor(opts) {
+ opts ??= {};
+ if (typeof opts.parent === 'string') {
+ opts.parent = document.querySelector(opts.parent);
+ }
+ if (!(opts.parent instanceof HTMLElement)) {
+ throw new Error('no specified parent.');
+ }
+ this._var.options = opts;
+ this._var.parent = opts.parent;
+
+ const el = createDateInput(opts.minDate, opts.maxDate);
+ el.addEventListener('blur', e => {
+ const date = this._getDate(e.target.valueAsDate);
+ if (date == null) {
+ e.target.value = '';
+ }
+ if (typeof this.onDateChanged === 'function') {
+ this.onDateChanged(date);
+ }
+ });
+ this._var.el = el;
+ parent.appendChild(el);
+ }
+
+ get enabled() { return !this._var.el.disabled }
+ set enabled(flag) {
+ this._var.el.disabled = flag === false;
+ }
+
+ get value() { return this._getDate(this._var.el.valueAsDate) }
+ set value(val) {
+ setDateValue(this._var.el, val);
+ }
+
+ /**
+ * @param {string} date
+ */
+ set minDate(date) {
+ this._var.el.min = date;
+ this._var.options.minDate = date;
+ }
+ /**
+ * @param {string} date
+ */
+ set maxDate(date) {
+ this._var.el.max = date;
+ this._var.options.maxDate = date;
+ }
+
+ _getDate(date) {
+ if (date instanceof Date && !isNaN(date)) {
+ const year = date.getFullYear();
+ if (year < 1900 || year > 9999) {
+ return null;
+ }
+ if (typeof this._var.options.valueFormatter === 'function') {
+ return this._var.options.valueFormatter(date);
+ }
+ return date;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/lib/ui/grid/column.js b/lib/ui/grid/column.js
index db5fd3d..8a00887 100644
--- a/lib/ui/grid/column.js
+++ b/lib/ui/grid/column.js
@@ -1,11 +1,11 @@
import { global } from "../../utility";
-// import { nullOrEmpty } from "../../utility/strings";
import { createElement } from "../../functions";
import { createIcon } from "../icon";
import { createCheckbox } from "../checkbox";
// import { setTooltip } from "../tooltip";
import { Dropdown } from "../dropdown";
import { convertCssStyle } from "../extension";
+import { createDateInput, formatDate, setDateValue, getDateValue } from "../date";
export class GridColumn {
static create() {
@@ -296,87 +296,25 @@ export class GridDateColumn extends GridColumn {
if (enabled === false) {
return super.create();
}
- const date = createElement('input', 'ui-grid-date-cell');
- date.required = true;
- date.type = 'date';
- if (col.dateMin != null) {
- date.min = col.dateMin;
- }
- if (col.dateMax != null) {
- date.max = col.dateMax;
- }
+ const date = createDateInput(col.dateMin, col.dateMax);
// date.addEventListener('change', trigger);
date.addEventListener('blur', trigger);
return date;
}
static setValue(element, val) {
- if (element.tagName === 'INPUT') {
- if (val === '') {
- element.value = '';
- } else if (isNaN(val)) {
- if (/^\d{4}-\d{2}-\d{2}$/.test(val)) {
- element.value = val;
- } else if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
- element.value = this._toDateValue(new Date(val));
- } else {
- element.value = '';
- }
- } else {
- if (!(val instanceof Date)) {
- val = new Date((val - 621355968e9) / 1e4);
- }
- element.value = this._toDateValue(val);
- }
- } else {
- element.innerText = this.formatDate(val);
- }
+ setDateValue(element, val);
}
static getValue(e, col) {
- const date = e.target?.valueAsDate;
- if (date instanceof Date && !isNaN(date)) {
- const year = date.getFullYear();
- if (year < 1900 || year > 9999) {
- return '';
- }
- if (typeof col.dateValueFormatter === 'function') {
- return col.dateValueFormatter(date);
- }
- return String(date.getTime() * 1e4 + 621355968e9);
- }
- return '';
+ return getDateValue(e.target, col.dateValueFormatter);
}
static setEnabled(element, enabled) {
element.disabled = enabled === false;
}
- static _toDateValue(dt) {
- if (isNaN(dt)) {
- return '';
- }
- const month = String(dt.getMonth() + 1).padStart(2, '0');
- const date = String(dt.getDate()).padStart(2, '0');
- return `${dt.getFullYear()}-${month}-${date}`;
- }
-
- static _resolveDate(s) {
- if (s instanceof Date) {
- return s;
- }
- const ticks = Number(s);
- if (!isNaN(ticks) && ticks > 0) {
- return new Date((ticks - 621355968e9) / 1e4);
- }
- return new Date(s);
- }
-
static formatDate(date) {
- date = this._resolveDate(date);
- if (date instanceof Date && !isNaN(date)) {
- return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
- }
- return '';
+ return formatDate(date);
}
}
\ No newline at end of file