diff --git a/lib/ui.js b/lib/ui.js index 4a8167d..92c96ec 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -10,6 +10,7 @@ import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, Gr import { Popup, createPopup, showAlert, showConfirm } from "./ui/popup"; import { createPicture, createAudio, createVideo, createFile } from './ui/media'; import { validation, convertCssStyle } from './ui/extension'; +import { createDateInput, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date'; export { createElement, @@ -40,6 +41,12 @@ export { createPopup, showAlert, showConfirm, + // dateSelector + createDateInput, + formatDate, + setDateValue, + getDateValue, + DateSelector, // media createPicture, createAudio, diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss index 762c4be..5c492aa 100644 --- a/lib/ui/css/grid.scss +++ b/lib/ui/css/grid.scss @@ -320,7 +320,7 @@ } .ui-check-wrapper { - display: flex; + display: inline-flex; justify-content: center; .ui-check-inner { diff --git a/lib/ui/date.d.ts b/lib/ui/date.d.ts index 557062e..e5e3715 100644 --- a/lib/ui/date.d.ts +++ b/lib/ui/date.d.ts @@ -1,10 +1,11 @@ /** - * 创建日期选择框 + * 创建或转换日期选择框 * @param min 最小可选日期 * @param max 最大可选日期 - * @returns 返回创建的日期选择框 + * @param element 转换该元素为日期选择框 + * @returns 返回创建或转换的日期选择框 */ -export function createDateInput(min?: string, max?: string): HTMLInputElement; +export function createDateInput(min?: string, max?: string, element?: HTMLInputElement): HTMLInputElement; /** * 格式化日期字符串 @@ -36,22 +37,48 @@ export function getDateValue(element: HTMLInputElement, formatter?: (date: Date) /** 日期选择框类 */ export class DateSelector { /** - * 日期发生变化时触发的事件 - * @param date 日期值,或者经自定义参数中格式化函数格式后的值 + * 把父容器下所有匹配 `input[data-type="date"]` 的元素修改为统一的日期选择框

+ * 解析的属性为 `id`, `class`, `data-min`, `data-max`, `disabled` + * @param dom 父元素 + * @param trigger 日期设置事件触发函数(上下文为触发设置日期的 `DateSelector` 实例) + * @example + * HTML: + * ```html + * + * ``` + * js: + * ```js + * const libUI = window['lib-ui']; + * const DateSelector = libUI.DateSelector; + * const formatDate = libUI.formatDate; + * + * + * // 解析 document.body 下所有符合条件的元素,转换为日期选择框,第二个参数可选 + * DateSelector.resolve(document.body, function (date) { + * console.log(`element(#${this.element.id}), date changed to: ${formatDate(date)}`); + * // 当日期选择改变时,控制台将会输出:element(#dateFrom), date changed to: 1/30/2024 + * }); + * + * + * // 在其他地方调用时 + * const value = document.querySelector('#dateFrom').value; + * console.log(`dateFrom.value = '${value}', formatted: '${formatDate(value)}'`); + * // 控制台会输出:dateFrom.value = '2024-01-30', formatted: '1/30/2024' + * ``` */ - onDateChanged?: (date: Date | any) => void; + static resolve(dom?: HTMLElement, trigger?: (date: Date) => void): HTMLElement; /** * 日期选择框构造函数 * @param opts 日期选项参数 */ constructor(opts: { - /** 父容器元素,可以为 `string` 作为选择器 */ - parent: HTMLElement | string, /** 最小可选择日期 */ minDate?: string, /** 最大可选择日期 */ maxDate?: string, + /** 是否启用 */ + enabled?: boolean, /** * 自定义格式化函数,用于获取日期值时调用 * @param date 日期值 @@ -60,12 +87,40 @@ export class DateSelector { valueFormatter?: (date: Date) => any }); + /** + * 创建或转换日期选择框元素 + * @param element 转换该元素为日期选择框 + * @returns 返回创建或转换的日期选择元素 + */ + create(element?: HTMLInputElement): HTMLInputElement; + + /** 获取日期选择框元素 */ + get element(): HTMLInputElement; + + /** 获取日期选择框是否启用 */ get enabled(): boolean; + /** 设置日期选择框启用状态 */ set enabled(flag: boolean); + /** 获取设置的日期值,或经过格式化函数返回的值 */ get value(): Date | any; + /** 设置日期值,支持的格式参见 {@linkcode formatDate} */ set value(val: Date | number | string); + /** 获取最小可选择日期 */ + get minDate(): string; + /** 设置最小可选择日期 */ set minDate(date: string); + + /** 获取最大可选择日期 */ + get maxDate(): string; + /** 设置最大可选择日期 */ set maxDate(date: string); + + /** + * 日期发生变化时触发的事件 + * @param date 日期值,或者经自定义参数中格式化函数格式后的值 + * @eventProperty + */ + onDateChanged?: (date: Date | any) => void; } \ No newline at end of file diff --git a/lib/ui/date.js b/lib/ui/date.js index e01375a..018efd2 100644 --- a/lib/ui/date.js +++ b/lib/ui/date.js @@ -1,7 +1,13 @@ import { createElement } from "../functions"; -export function createDateInput(min, max) { - const date = createElement('input', 'ui-date-cell'); +export function createDateInput(min, max, element) { + let date; + if (element instanceof HTMLInputElement) { + date = element; + date.classList.add('ui-date-cell'); + } else { + date = createElement('input', 'ui-date-cell'); + } date.required = true; date.type = 'date'; if (min != null) { @@ -89,16 +95,15 @@ export class DateSelector { 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); + create(element) { + const opts = this._var.options; + const el = createDateInput(opts.minDate, opts.maxDate, element); + if (element == null) { + el.disabled = opts.enabled === false; + } el.addEventListener('blur', e => { const date = this._getDate(e.target.valueAsDate); if (date == null) { @@ -109,9 +114,11 @@ export class DateSelector { } }); this._var.el = el; - parent.appendChild(el); + return el; } + get element() { return this._var.el } + get enabled() { return !this._var.el.disabled } set enabled(flag) { this._var.el.disabled = flag === false; @@ -122,16 +129,13 @@ export class DateSelector { setDateValue(this._var.el, val); } - /** - * @param {string} date - */ + get minDate() { return this._var.el.min } set minDate(date) { this._var.el.min = date; this._var.options.minDate = date; } - /** - * @param {string} date - */ + + get maxDate() { return this._var.el.max } set maxDate(date) { this._var.el.max = date; this._var.options.maxDate = date; @@ -150,4 +154,23 @@ export class DateSelector { } return null; } + + static resolve(dom = document.body, trigger) { + const dates = dom.querySelectorAll('input[data-type="date"]'); + for (let dat of dates) { + const val = dat.value; + const dateSelector = new DateSelector({ + minDate: dat.getAttribute('data-min'), + maxDate: dat.getAttribute('data-max') + }); + if (typeof trigger === 'function') { + dateSelector.onDateChanged = date => trigger.call(dateSelector, date); + } + dat.removeAttribute('data-type'); + dat.removeAttribute('data-min'); + dat.removeAttribute('data-max'); + dateSelector.create(dat); + dateSelector.value = val; + } + } } \ No newline at end of file diff --git a/lib/ui/dropdown.d.ts b/lib/ui/dropdown.d.ts index 14da60f..b69dbcc 100644 --- a/lib/ui/dropdown.d.ts +++ b/lib/ui/dropdown.d.ts @@ -47,11 +47,13 @@ export interface DropdownOptions { /** 下拉框类 */ export class Dropdown { /** - * 把父元素下的所有 `select` 元素修改为统一下拉框组件 + * 把父元素下的所有 `select` 元素修改为统一下拉框组件

+ * 解析的属性为 `value`, `disabled`, `tabIndex` * @param dom 父元素 + * @param trigger 选中事件触发函数(上下文为触发选中的 `Dropdown` 实例) * @returns 返回该父元素 */ - static resolve(dom?: HTMLElement): HTMLElement; + static resolve(dom?: HTMLElement, trigger?: (item: DropdownItem) => void): HTMLElement; /** * 下拉框的构造函数 diff --git a/lib/ui/dropdown.js b/lib/ui/dropdown.js index 26e93e3..58e27ba 100644 --- a/lib/ui/dropdown.js +++ b/lib/ui/dropdown.js @@ -560,7 +560,7 @@ export class Dropdown { } } - static resolve(dom = document.body) { + static resolve(dom = document.body, trigger) { const selects = dom.querySelectorAll('select'); for (let sel of selects) { const source = [...sel.children].map(it => { @@ -572,6 +572,9 @@ export class Dropdown { tabIndex: sel.tabIndex }); drop.source = source; + if (typeof trigger === 'function') { + drop.onSelected = item => trigger.call(drop, item); + } sel.parentElement.replaceChild(drop.create(), sel); } return dom; diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js index e94587d..0cd2ce9 100644 --- a/lib/ui/grid/grid.js +++ b/lib/ui/grid/grid.js @@ -975,6 +975,11 @@ export class Grid { th.addEventListener('mousedown', e => this._onDragStart(e, col)); } const wrapper = createElement('div'); + if (col.align === 'right') { + wrapper.style.justifyContent = 'flex-end'; + } else if (col.align === 'center') { + wrapper.style.justifyContent = 'center'; + } th.appendChild(wrapper); if (!this.readonly && col.enabled !== false && col.allcheck && isCheckbox) { const check = createCheckbox({ @@ -1239,6 +1244,8 @@ export class Grid { const style = col.styleFilter(item); if (style != null) { type.setStyle(element, style); + } else { + element.style.cssText = ''; } } if (col.events != null) { @@ -1562,7 +1569,12 @@ export class Grid { const th = filter.parentElement; const width = th.offsetWidth; panel.style.top = `${th.offsetHeight + this._var.el.scrollTop}px`; - panel.style.left = (th.offsetLeft + (width > FilterPanelWidth ? width - FilterPanelWidth : 0)) + 'px'; + const offsetLeft = th.offsetLeft; + const totalWidth = th.parentElement.offsetWidth; + const left = offsetLeft + FilterPanelWidth > totalWidth ? + totalWidth - FilterPanelWidth : + offsetLeft + (width > FilterPanelWidth ? width - FilterPanelWidth : 0); + panel.style.left = `${left}px`; // search let searchbox; diff --git a/typedoc.json b/typedoc.json index 32db27f..53db88a 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,5 +1,6 @@ { "entryPoints": [ + "lib/ui/date.d.ts", "lib/ui/dropdown.d.ts", "lib/ui/grid/column.d.ts", "lib/ui/grid/grid.d.ts"