iter(g138): 日期选择支持区间模式
This commit is contained in:
@@ -8,7 +8,8 @@ import { fmt, pad, parse, shiftDay, today } from './dateKit.js';
|
||||
/*** 日期选择:单月网格 + 上下月翻页 + 今天快捷键,value 信号双向绑定,方向键/PageUp/PageDown 可导航 ***/
|
||||
export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
// —— 状态集中声明 ——
|
||||
const value: Signal<string> = props.value ?? signal(''); // 已选日期
|
||||
const value: Signal<string> = props.value ?? signal(''); // 已选日期(区间模式下是起点)
|
||||
const end: Signal<string> = props.end ?? signal(''); // 区间终点,非区间模式恒为空
|
||||
const open = signal(false); // 弹层开合
|
||||
const place = signal<'top' | 'bottom'>('bottom'); // 实际方位
|
||||
const initial = parse(value()) ?? today(); // 初始视图:跟随已选,否则今天
|
||||
@@ -41,10 +42,25 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
view([d.getFullYear(), d.getMonth()]);
|
||||
}
|
||||
|
||||
/*** 提交:写值 + 回调 + 收起 + 焦点归还触发器 ***/
|
||||
/*** 提交:单日模式写值即收起;区间模式首点定起点并留在弹层,次点补终点后收起 ***/
|
||||
function commit(date: string): void {
|
||||
value(date);
|
||||
props.onChange?.(date);
|
||||
if (!props.range) {
|
||||
value(date);
|
||||
props.onChange?.(date);
|
||||
open(false);
|
||||
trigger.focus();
|
||||
return;
|
||||
}
|
||||
if (!value() || end()) { // 未选起点或上一段区间已完整:从这个日期重开一段
|
||||
value(date);
|
||||
end('');
|
||||
render(); // 清掉上一段的区间底色
|
||||
return;
|
||||
}
|
||||
const [start, stop] = date < value() ? [date, value()] : [value(), date]; // 点早了就自动交换
|
||||
value(start);
|
||||
end(stop);
|
||||
props.onRangeChange?.(start, stop);
|
||||
open(false);
|
||||
trigger.focus();
|
||||
}
|
||||
@@ -62,14 +78,16 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
for (let d = 1; d <= total; d++) {
|
||||
const date = fmt(y, m, d);
|
||||
const dead = outOfRange(date);
|
||||
const cls = `ua-datepicker-day${date === value() ? ' is-selected' : ''}${date === fmt(...today()) ? ' is-today' : ''}${dead ? ' is-disabled' : ''}`;
|
||||
const edge = date === value() || Boolean(props.range && end() && date === end()); // 区间两端
|
||||
const between = Boolean(props.range && value() && end() && date > value() && date < end()); // 区间内部
|
||||
const cls = `ua-datepicker-day${edge ? ' is-selected' : ''}${between ? ' is-in-range' : ''}${date === fmt(...today()) ? ' is-today' : ''}${dead ? ' is-disabled' : ''}`;
|
||||
cells.push(
|
||||
h('button', {
|
||||
type: 'button',
|
||||
class: cls,
|
||||
role: 'gridcell',
|
||||
tabindex: date === focus() ? '0' : '-1', // 漫游 tabindex:只有焦点日可 Tab 进入
|
||||
'aria-selected': String(date === value()),
|
||||
'aria-selected': String(edge),
|
||||
disabled: dead,
|
||||
'aria-label': date,
|
||||
onClick: () => commit(date),
|
||||
@@ -84,6 +102,12 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
});
|
||||
}
|
||||
|
||||
/*** 触发器文案:单日回显日期;区间回显「起 ~ 止」,只定了起点时后面跟省略号 ***/
|
||||
const labelText = (): string => {
|
||||
if (!props.range) return value() || props.placeholder || '请选择日期';
|
||||
if (!value()) return props.placeholder || '请选择日期范围';
|
||||
return end() ? `${value()} ~ ${end()}` : `${value()} ~ …`;
|
||||
};
|
||||
const trigger = h(
|
||||
'button',
|
||||
{
|
||||
@@ -101,7 +125,7 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
open(!open());
|
||||
},
|
||||
},
|
||||
h('span', { class: () => `ua-datepicker-value${value() ? '' : ' is-empty'}` }, () => value() || props.placeholder || '请选择日期'),
|
||||
h('span', { class: () => `ua-datepicker-value${value() ? '' : ' is-empty'}` }, () => labelText()),
|
||||
h('span', { class: 'ua-datepicker-caret', 'aria-hidden': 'true' }, '▤'),
|
||||
);
|
||||
const root = h('div', { class: () => `ua-datepicker${open() ? ' ua-open' : ''}` }, trigger, pop) as HTMLElement;
|
||||
|
||||
@@ -594,8 +594,11 @@ export interface AutoCompleteProps {
|
||||
|
||||
/*** 日期选择入参 ***/
|
||||
export interface DatePickerProps {
|
||||
value?: Signal<string>; // 选中日期(YYYY-MM-DD);不传则内部维护
|
||||
placeholder?: string; // 未选时占位文案;缺省「请选择日期」
|
||||
value?: Signal<string>; // 选中日期(YYYY-MM-DD);区间模式下是起始日
|
||||
range?: boolean; // 区间模式:首次点击定起点、再次点击定终点(颠倒自动交换);缺省 false
|
||||
end?: Signal<string>; // 区间模式的结束日;不传则内部维护
|
||||
onRangeChange?: (start: string, end: string) => void; // 区间两端都选定后的回调(已按先后排序)
|
||||
placeholder?: string; // 未选时占位文案;缺省「请选择日期」(区间模式缺省「请选择日期范围」)
|
||||
min?: string; // 最早可选日期(含);早于此的日置灰
|
||||
max?: string; // 最晚可选日期(含);晚于此的日置灰
|
||||
disabled?: boolean | Signal<boolean>; // 禁用触发器
|
||||
|
||||
Reference in New Issue
Block a user