UNPKG

@gez/date-time-kit

Version:

386 lines (369 loc) 12.6 kB
import { type DateTimeGranularity, debounce, getCurrentTzOffset, granHelper } from '../../utils'; import { type Weeks, weekKey } from '../calendar'; import type { Ele as PeriodSelectorEle } from '../period-selector'; import { Ele as PopoverEle, type EventMap as PopoverEvent } from '../popover'; import { clearupPopEleAttrSync2Parent, isPopoverAttrKey, parentPopAttrSync2PopEle, popEleAttrSync2Parent, popoverAttrKeys, type reExportPopoverAttrs } from '../popover/attr-sync-helper'; import { type BaseAttrs, type BaseEmits, type Emit2EventMap, UiBase } from '../web-component-base'; import styleStr from './index.css'; import html, { utcText } from './index.html'; import { type DataLimit, type GenPeriodTimesOptions, type PeriodTimeInfo, type QuickGenPeriodTimesOptions, type QuickKey, UTCInfo2LocaleInfo, genPeriodTimes, localeInfo2UTCInfo, quickGenPeriodTime, quickGenPeriodTimeInfo, quickGenPeriodTimes, quickKeys } from './quick-key'; export { type QuickKey, type DataLimit, type GenPeriodTimesOptions, type QuickGenPeriodTimesOptions, type PeriodTimeInfo, type Weeks, genPeriodTimes, quickGenPeriodTime, quickGenPeriodTimes, quickGenPeriodTimeInfo, localeInfo2UTCInfo, UTCInfo2LocaleInfo, quickKeys, weekKey }; export const granularityList = granHelper.dateTime.list; export type Granularity = DateTimeGranularity; export type Attrs = BaseAttrs & reExportPopoverAttrs & { /** * Timezone in minutes. For example: UTC+05:45 => `-345`, UTC-01:00 => `60`. * * @default * new Date().getTimezoneOffset() // locale timezone in minutes */ 'tz-offset'?: number; /** * Set which day of the week is the first day. * @type `'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'` * @default 'sun' */ 'week-start-at'?: Weeks; /** * Quick selection key. * * @default 'all' */ 'quick-key'?: QuickKey; /** * Start locale time of the quick selection. Only works in custom mode. */ 'start-time'?: string | number | ''; /** * End locale time of the quick selection. Only works in custom mode. */ 'end-time'?: string | number | ''; /** * 选择器的粒度,表示最小可选的时间单位。默认为 millisecond。 * 例如设置为 'minute',则表示只能选择到分钟,秒和毫秒将被忽略。忽略的时间单位视情况重置为 0 或 23 或 59 或 999。 */ 'min-granularity'?: Granularity; /** * Exclude some quick selection options. * * @example * ```ts * exclude-field="last7Days, last30Days, timezone" * ``` */ 'exclude-field'?: (QuickKey | 'timezone')[]; }; export interface Emits extends BaseEmits { 'time-changed': PeriodTimeInfo; 'open-change': boolean; } export type EventMap = Emit2EventMap<Emits>; /** * 快速选择下拉选项 */ export class Ele extends UiBase<Attrs, Emits> { public static readonly tagName = 'dt-quick-selector' as const; protected static _style = styleStr; protected static _template = html; static get observedAttributes(): string[] { return [ ...(super.observedAttributes as (keyof BaseAttrs)[]), 'tz-offset', 'week-start-at', 'quick-key', 'start-time', 'end-time', 'min-granularity', 'exclude-field', ...popoverAttrKeys ] satisfies (keyof Attrs)[]; } public get tzOffset() { return +this._getAttr('tz-offset', '' + getCurrentTzOffset()); } public set tzOffset(v: number) { if (!Number.isSafeInteger(v)) return; this.setAttribute('tz-offset', '' + v); } public get quickKey() { return this._getAttr('quick-key', 'all'); } public set quickKey(val: QuickKey) { if (!quickKeys.includes(val)) return; this.setAttribute('quick-key', val); } public get weekStartAt() { return this._getAttr('week-start-at', 'sun'); } public set weekStartAt(val: Weeks) { if (!weekKey.includes(val)) return; this.setAttribute('week-start-at', val); } public get startTime() { const v = this._getAttr('start-time', ''); if (v === '') return ''; return new Date(Number.isNaN(+v) ? v : +v); } public set startTime(val: number | string | Date) { if (val === '') { this.removeAttribute('start-time'); return; } const v = new Date(val); if (Number.isNaN(+v)) return; this.setAttribute('start-time', +v + ''); } public get endTime() { const v = this._getAttr('end-time', '' + this.startTime); if (v === '') return ''; return new Date(Number.isNaN(+v) ? v : +v); } public set endTime(val: number | string | Date) { if (val === '') { this.removeAttribute('end-time'); return; } const v = new Date(val); if (Number.isNaN(+v)) return; this.setAttribute('end-time', +v + ''); } public get minGranularity() { return this._getAttr('min-granularity', 'millisecond'); } public set minGranularity(val: Granularity) { if (!granHelper.dateTime.has(val)) return; this.setAttribute('min-granularity', val); } public get excludeField() { const v = this._getAttr('exclude-field', '') || ''; if (v === '') return []; return (v as string).split(',').map((i) => i.trim()) as ( | QuickKey | 'timezone' )[]; } public set excludeField(v: (QuickKey | 'timezone')[]) { if (!Array.isArray(v) || v.length === 0) { this.removeAttribute('exclude-field'); return; } const arr = v.filter( (i) => quickKeys.includes(i as QuickKey) || i === 'timezone' ); this.setAttribute('exclude-field', arr.join(',')); } get _staticEls() { return { ...super._staticEls, periodSelector: this.$0<PeriodSelectorEle>`dt-period-selector`!, popover: this.$0<PopoverEle>`dt-popover`! } as const; } public connectedCallback() { if (!super.connectedCallback()) return; this._renderTz(); this._updateRadio(); this._updatePeriodSelector(); const { _els } = this; this._bindEvt(_els.popover)('open-change', this._onPopoverChange); this._bindEvt`.tz-trigger`('click', this._onTzTriggerClick); this._bindEvt`.custom-trigger`('click', this._onCustomTriggerClick); this._bindEvt`.menu.tz .title svg`('click', this._onBackBtnClick); this._bindEvt`.menu.custom .title svg`('click', this._onBackBtnClick); this._bindEvt`.menu`('change', this._onRadioChange); this._bindEvt`#reset`('click', this._updatePeriodSelector); this._bindEvt`#done`('click', this._onDoneBtnClick); popEleAttrSync2Parent(this, this._els.popover); } public disconnectedCallback() { clearupPopEleAttrSync2Parent(this); return super.disconnectedCallback(); } protected _onAttrChanged( name: string, oldValue: string | null, newValue: string | null ) { super._onAttrChanged(name, oldValue, newValue); if (isPopoverAttrKey(name)) { parentPopAttrSync2PopEle( name, oldValue, newValue, this._els.popover ); return; } if (name === 'tz-offset') { this._renderTz(); this._dispatchTimeChangeEvent(); } if (name === 'quick-key') { this._updateRadio(); this._dispatchTimeChangeEvent(); } if (name === 'week-start-at') { this._updatePeriodSelector(); } if (name === 'start-time' || name === 'end-time') { if (this.quickKey !== 'custom') return; this._updatePeriodSelector(); } } private _updatePeriodSelector = super._genRenderFn(() => { this._els.periodSelector.weekStartAt = this.weekStartAt; if (this.$0`.menu.custom`?.style.display === 'none') return; const ele = this._els.periodSelector; const startTime = this.startTime; const endTime = this.endTime; if (startTime !== '' && endTime !== '') { ele.timeStart = startTime; ele.timeEnd = endTime; } else { const defaultPeriod = this.quickGenPeriodTime('last30Days'); ele.timeStart = defaultPeriod.start; ele.timeEnd = defaultPeriod.end; } ele.minGranularity = this.minGranularity; ele.showCalendarDatePoint(); }); private _renderTz = super._genRenderFn(() => { const tzOffset = this.tzOffset; this.$<HTMLInputElement>`input[name="tz"]`.forEach((radio) => { radio.checked = +radio.value === tzOffset; }); this.$0`.tz-trigger bdo`!.textContent = utcText(tzOffset); }); private _updateRadio = super._genRenderFn(() => { const quickKey = this.quickKey; this .$0<HTMLInputElement>`input[name="radio"][value="${quickKey}"]`!.checked = true; }); private _onPopoverChange = (e: PopoverEvent['open-change']) => { if (!(e.target instanceof PopoverEle)) return; if (e.detail === false) { this._showMenu('top'); } }; private _showMenu(type: 'top' | 'tz' | 'custom') { this.$`.menu`.forEach((menu) => menu.classList.contains(type) ? (menu.slot = 'pop') : menu.removeAttribute('slot') ); if (type === 'custom') { this._updatePeriodSelector(); } } private _onTzTriggerClick = () => this._showMenu('tz'); private _onCustomTriggerClick = (e: Event) => { e.preventDefault(); this._showMenu('custom'); }; private _onBackBtnClick = () => this._showMenu('top'); private _dispatchTimeChangeEvent = debounce(() => { const quickKey = this.quickKey; if (quickKey !== 'custom') { const t = this.quickGenPeriodTimeInfo(quickKey); this.dispatchEvent('time-changed', t, true); return; } this.dispatchEvent( 'time-changed', { tzOffset: this.tzOffset, start: this.startTime as Date, end: this.endTime as Date, type: 'custom' }, true ); }); private _onRadioChange = (e: Event) => { if (!(e.target instanceof HTMLInputElement)) return; if (e.target.type !== 'radio') return; const { name, value } = e.target; if (name === 'radio') { const v = value as QuickKey; if (v === 'custom') return; this.quickKey = v; } else if (name === 'tz') { this.tzOffset = +value; } }; private _onDoneBtnClick = (_e: Event) => { const selector = this._els.periodSelector; selector.abortSelecting(); this._showMenu('top'); let { timeStart, timeEnd } = selector; if (timeStart > timeEnd) [timeStart, timeEnd] = [timeEnd, timeStart]; this.startTime = timeStart; this.endTime = timeEnd; this.quickKey = 'custom'; this._dispatchTimeChangeEvent(); }; public readonly genPeriodTimes = (options: GenPeriodTimesOptions) => genPeriodTimes({ weekStartAt: this.weekStartAt, ...options }); public readonly quickGenPeriodTimes = <T extends DataLimit = DataLimit>( periods: T[] ) => quickGenPeriodTimes({ weekStartAt: this.weekStartAt, periods }); public readonly quickGenPeriodTime = <T extends DataLimit = DataLimit>( period: T ) => quickGenPeriodTime(period, { weekStartAt: this.weekStartAt }); public readonly quickGenPeriodTimeInfo = <T extends DataLimit = DataLimit>( type: T ) => quickGenPeriodTimeInfo( type, { weekStartAt: this.weekStartAt }, this.tzOffset ); } Ele.define();