iter(g158): 日期选择支持快捷区间预设
This commit is contained in:
+8
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "ui-arch-registry-index/0.1",
|
||||
"generatedAt": "2026-10-26T07:54:36.201Z",
|
||||
"generatedAt": "2026-10-26T08:00:04.262Z",
|
||||
"repoRoot": ".",
|
||||
"packages": [
|
||||
{
|
||||
@@ -2689,6 +2689,11 @@
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "禁用触发器"
|
||||
},
|
||||
{
|
||||
"n": "presets",
|
||||
"t": "DatePreset[]",
|
||||
"d": "快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(date: string) => void",
|
||||
@@ -2703,7 +2708,8 @@
|
||||
"min / max 为闭区间,越界日期置灰不可点也不可键盘选中",
|
||||
"value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)",
|
||||
"选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条",
|
||||
"区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号"
|
||||
"区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号",
|
||||
"传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+2
-2
@@ -995,13 +995,13 @@ const content = h(
|
||||
section(
|
||||
'sec-datepicker',
|
||||
'日期选择',
|
||||
'datePicker:单月网格,翻月 / 今天 / ←→↑↓ 逐日逐周 / PageUp↓ 逐月,Esc 收起;min/max 闭区间置灰越界日。',
|
||||
'datePicker:单月网格,翻月 / 今天 / ←→↑↓ 逐日逐周 / PageUp↓ 逐月,Esc 收起;min/max 闭区间置灰越界日;区间模式可配 presets 快捷按钮。',
|
||||
h(
|
||||
'div',
|
||||
{ class: 'row' },
|
||||
datePicker({ value: dateVal, onChange: (d) => toast.success(`已选 ${d}`) }),
|
||||
datePicker({ placeholder: '只能选 10 月下旬', min: '2026-10-20', max: '2026-10-31' }),
|
||||
datePicker({ range: true, value: rangeFrom, end: rangeTo, onRangeChange: (a, b) => toast.success(`区间 ${a} ~ ${b}`) }),
|
||||
datePicker({ range: true, value: rangeFrom, end: rangeTo, presets: [{ label: '最近 7 天', days: 7 }, { label: '最近 30 天', days: 30 }, { label: '10 月上旬', from: '2026-10-01', to: '2026-10-15' }], onRangeChange: (a, b) => toast.success(`区间 ${a} ~ ${b}`) }),
|
||||
h('span', { class: 'out' }, () => (dateVal() ? `当前日期:${dateVal()}` : '未选择')),
|
||||
h('span', { class: 'out' }, () => (rangeFrom() ? `区间:${rangeFrom()} ~ ${rangeTo() || '待定'}` : '区间未选择')),
|
||||
),
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||
|
||||
> 自动生成于 2026-10-26T07:54:36.201Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 自动生成于 2026-10-26T08:00:04.262Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||
|
||||
## 快速上手
|
||||
@@ -1406,6 +1406,7 @@ autoComplete({ value: acValue, placeholder: '输入组件名', options: [{ value
|
||||
- min:string —— 最早可选日期(含,YYYY-MM-DD);更早的日置灰不可点
|
||||
- max:string —— 最晚可选日期(含,YYYY-MM-DD);更晚的日置灰不可点
|
||||
- disabled:boolean | Signal<boolean> —— 禁用触发器
|
||||
- presets:DatePreset[] —— 快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起
|
||||
- onChange:(date: string) => void —— 选中回调(点击日期或 Enter)
|
||||
|
||||
示例:
|
||||
@@ -1423,6 +1424,7 @@ datePicker({ value: dateVal, min: '2026-10-20', max: '2026-10-31', onChange: (d)
|
||||
5. value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)
|
||||
6. 选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条
|
||||
7. 区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号
|
||||
8. 传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange
|
||||
|
||||
现场演示:apps/docs/demo.html#sec-datepicker
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { effect, h, signal } from '@ui-arch/frame';
|
||||
import type { Signal } from '@ui-arch/frame';
|
||||
import { bindOutside, pushOverlay } from './overlay.js';
|
||||
import { flip } from './position.js';
|
||||
import type { DatePickerProps } from './types.js';
|
||||
import type { DatePickerProps, DatePreset } from './types.js';
|
||||
import { fmt, pad, parse, shiftDay, today } from './dateKit.js';
|
||||
|
||||
/*** 日期选择:单月网格 + 上下月翻页 + 今天快捷键,value 信号双向绑定,方向键/PageUp/PageDown 可导航 ***/
|
||||
@@ -26,10 +26,16 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
label,
|
||||
h('button', { type: 'button', class: 'ua-datepicker-nav', 'aria-label': '下一月', onClick: () => turn(1) }, '›'),
|
||||
);
|
||||
const presetRow = props.range && props.presets?.length // 快捷区间只在区间模式有意义
|
||||
? h('div', { class: 'ua-datepicker-presets' }, ...props.presets.map((p) => h('button', {
|
||||
type: 'button', class: 'ua-datepicker-preset', onClick: () => applyPreset(p),
|
||||
}, p.label)))
|
||||
: null;
|
||||
const pop = h(
|
||||
'div',
|
||||
{ class: () => `ua-datepicker-pop${open() ? ' ua-open' : ''}${place() === 'top' ? ' is-flip' : ''}` },
|
||||
head,
|
||||
...(presetRow ? [presetRow] : []),
|
||||
h('div', { class: 'ua-datepicker-week' }, ...['日', '一', '二', '三', '四', '五', '六'].map((w) => h('span', {}, w))),
|
||||
grid,
|
||||
h('div', { class: 'ua-datepicker-foot' }, h('button', { type: 'button', class: 'ua-datepicker-today', onClick: () => commit(fmt(...today())) }, '今天')),
|
||||
@@ -65,6 +71,19 @@ export function datePicker(props: DatePickerProps): HTMLElement {
|
||||
trigger.focus();
|
||||
}
|
||||
|
||||
/*** 应用快捷区间:days 优先(今天往前共 N 天),否则用绝对 from/to;按 min/max 钳制后写值并收起 ***/
|
||||
function applyPreset(p: DatePreset): void {
|
||||
const stop = p.days ? fmt(...today()) : (p.to || fmt(...today()));
|
||||
const start = p.days ? shiftDay(stop, -(p.days - 1)) : (p.from || stop);
|
||||
const [lo, hi] = start <= stop ? [start, stop] : [stop, start]; // 防御:from 晚于 to 时自动交换
|
||||
const fit = (v: string): string => (props.min && v < props.min ? props.min : props.max && v > props.max ? props.max : v); // 钳进合法区间
|
||||
value(fit(lo));
|
||||
end(fit(hi));
|
||||
props.onRangeChange?.(value(), end());
|
||||
open(false);
|
||||
trigger.focus();
|
||||
}
|
||||
|
||||
/*** 是否越界:min / max 为闭区间,缺省视为不限制 ***/
|
||||
const outOfRange = (date: string): boolean => (props.min ? date < props.min : false) || (props.max ? date > props.max : false);
|
||||
|
||||
|
||||
@@ -597,6 +597,14 @@ export interface AutoCompleteProps {
|
||||
onSelect?: (option: AutoCompleteOption) => void; // 选中回调(点击或 Enter)
|
||||
}
|
||||
|
||||
/*** 日期快捷区间:days 与 from/to 二选一,days 优先 ***/
|
||||
export interface DatePreset {
|
||||
label: string; // 按钮文案,如「最近 7 天」
|
||||
days?: number; // 相对今天的天数(含今天):7 表示今天往前共 7 天
|
||||
from?: string; // 绝对起始日(YYYY-MM-DD),未给 days 时使用
|
||||
to?: string; // 绝对结束日(YYYY-MM-DD),未给 days 时使用;缺省取今天
|
||||
}
|
||||
|
||||
/*** 日期选择入参 ***/
|
||||
export interface DatePickerProps {
|
||||
value?: Signal<string>; // 选中日期(YYYY-MM-DD);区间模式下是起始日
|
||||
@@ -607,6 +615,7 @@ export interface DatePickerProps {
|
||||
min?: string; // 最早可选日期(含);早于此的日置灰
|
||||
max?: string; // 最晚可选日期(含);晚于此的日置灰
|
||||
disabled?: boolean | Signal<boolean>; // 禁用触发器
|
||||
presets?: DatePreset[]; // 快捷区间按钮组(仅区间模式渲染);缺省不显示
|
||||
onChange?: (date: string) => void; // 选中回调(点击或 Enter)
|
||||
}
|
||||
|
||||
|
||||
@@ -2560,6 +2560,11 @@
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "禁用触发器"
|
||||
},
|
||||
{
|
||||
"n": "presets",
|
||||
"t": "DatePreset[]",
|
||||
"d": "快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(date: string) => void",
|
||||
@@ -2574,7 +2579,8 @@
|
||||
"min / max 为闭区间,越界日期置灰不可点也不可键盘选中",
|
||||
"value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)",
|
||||
"选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条",
|
||||
"区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号"
|
||||
"区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号",
|
||||
"传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3242,6 +3242,29 @@ button.ua-step-dot:hover {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ua-datepicker-presets {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--ua-space-1);
|
||||
padding-bottom: var(--ua-space-1);
|
||||
margin-bottom: var(--ua-space-1);
|
||||
border-bottom: 1px solid var(--ua-color-border);
|
||||
}
|
||||
.ua-datepicker-preset {
|
||||
padding: 2px 8px;
|
||||
font-size: var(--ua-font-size-sm);
|
||||
color: var(--ua-color-text-muted);
|
||||
background: transparent;
|
||||
border: 1px solid var(--ua-color-border);
|
||||
border-radius: var(--ua-radius-sm);
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, border-color 0.15s, background-color 0.15s;
|
||||
}
|
||||
.ua-datepicker-preset:hover {
|
||||
color: var(--ua-color-accent);
|
||||
border-color: var(--ua-color-accent);
|
||||
background: color-mix(in srgb, var(--ua-color-accent) 8%, transparent);
|
||||
}
|
||||
@keyframes ua-dp-in {
|
||||
from { opacity: 0; transform: translateY(-4px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
@@ -3250,6 +3273,7 @@ button.ua-step-dot:hover {
|
||||
.ua-datepicker-pop.ua-open { animation: none; }
|
||||
.ua-datepicker-trigger,
|
||||
.ua-datepicker-day,
|
||||
.ua-datepicker-preset,
|
||||
.ua-datepicker-nav { transition: none; }
|
||||
}
|
||||
.ua-backtop {
|
||||
|
||||
Reference in New Issue
Block a user