@qijenchen/design-system 0.1.0-beta.144 → 0.1.0-beta.145

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/components/DateGrid/date-grid.d.ts +15 -1
  2. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  3. package/dist/components/DateGrid/date-grid.js +77 -22
  4. package/dist/components/DateGrid/date-grid.js.map +1 -1
  5. package/dist/components/DateGrid/index.js +5 -1
  6. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  7. package/dist/components/DatePicker/date-picker.js +58 -19
  8. package/dist/components/DatePicker/date-picker.js.map +1 -1
  9. package/dist/components/DatePicker/range-preview.d.ts +46 -0
  10. package/dist/components/DatePicker/range-preview.d.ts.map +1 -0
  11. package/dist/components/DatePicker/range-preview.js +41 -0
  12. package/dist/components/DatePicker/range-preview.js.map +1 -0
  13. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/audit-architecture.md +1 -1
  14. package/ds-canonical/fork/common/closed-tool-execution.mjs +31 -0
  15. package/ds-canonical/fork/consumer/lib/closed-tool-execution.mjs +31 -0
  16. package/ds-canonical/fork/consumer/lock.json +10 -10
  17. package/ds-canonical/fork/consumer/managed-files/2d1ef5617a5ce3c39ad9aae1c42af7d199f9270c0a0ccb490b63d62dbc6913db.blob +31 -0
  18. package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +189 -38
  19. package/ds-canonical/fork/consumer/managed-files/61c6aca6d7f04bedbf556eaa9d3043681fe48cbe0e93a8ccb1d69a7c5e23fdcf.blob +6 -1
  20. package/ds-canonical/fork/governance.lock +10 -10
  21. package/ds-canonical/fork/manifest.json +10 -10
  22. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/audit-architecture.md +1 -1
  23. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/audit-architecture.md +1 -1
  24. package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +1 -1
  25. package/ds-canonical/references/focus-canonical.md +45 -8
  26. package/ds-canonical/references/principle-dim-map.json +5 -0
  27. package/ds-canonical/rules/story-rules.md +7 -0
  28. package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +25 -1
  29. package/ds-canonical/skills/visual-audit/references/audit-architecture.md +1 -1
  30. package/ds-story-manifest.json +5 -3
  31. package/package.json +1 -1
  32. package/src/components/Breadcrumb/breadcrumb.stories.tsx +2 -0
  33. package/src/components/Button/button.stories.tsx +4 -0
  34. package/src/components/Calendar/calendar.stories.tsx +2 -0
  35. package/src/components/Combobox/combobox.stories.tsx +2 -0
  36. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +2 -2
  37. package/src/components/DateGrid/date-grid.spec.md +45 -3
  38. package/src/components/DateGrid/date-grid.tsx +102 -21
  39. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +15 -3
  40. package/src/components/DatePicker/date-picker.spec.md +33 -6
  41. package/src/components/DatePicker/date-picker.stories.tsx +54 -30
  42. package/src/components/DatePicker/date-picker.tsx +70 -18
  43. package/src/components/DatePicker/range-preview.ts +82 -0
  44. package/src/components/Dialog/dialog.stories.tsx +4 -0
  45. package/src/components/FileItem/file-item.stories.tsx +2 -0
  46. package/src/components/FileUpload/file-upload.stories.tsx +2 -0
  47. package/src/components/PeoplePicker/people-picker.stories.tsx +4 -0
  48. package/src/components/Steps/steps.stories.tsx +2 -0
  49. package/src/components/TreeView/tree-view.stories.tsx +2 -0
  50. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +2 -0
  51. package/src/styles/base.css +17 -0
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.js","sources":["../../../src/components/DatePicker/date-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\n// code-quality-allow: file-size — foundational composite(DatePicker single + Range + showTime + format/ISO helpers + TimePickerSidePanel sub-components),拆 sub-file 會 (a) 增 cross-file context binding 複雜度 (b) M21 過度抽象(每 helper 1 consumer)。等 inline filter UI 真接入第 2 consumer 再拆。\nimport * as React from 'react'\nimport { X, Calendar as CalendarIcon, ArrowRight } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'\nimport { ScrollArea } from '@/design-system/components/ScrollArea/scroll-area'\nimport { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from '@/design-system/components/Popover/popover'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'\nimport { useTruncated } from '@/design-system/hooks/use-truncated'\nimport { TruncatedText } from '@/design-system/patterns/element-anatomy/truncated-text'\nimport { DateGrid } from '@/design-system/components/DateGrid/date-grid'\nimport { Button } from '@/design-system/components/Button/button'\nimport { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport {\n TimeColumns,\n isoToTimeParts,\n timePartsToString,\n type TimeParts,\n type TimeStep,\n} from '@/design-system/components/TimePicker/time-columns'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Format ──────────────────────────────────────────────────────────────────\n\nexport interface DateFormatOptions {\n /** Intl.DateTimeFormat options(預設 { year: 'numeric', month: '2-digit', day: '2-digit' }) */\n formatOptions?: Intl.DateTimeFormatOptions\n /** locale(預設 'en-US') */\n locale?: string\n}\n\n/**\n * Default format:**YYYY/MM/DD**(對齊 Ant Design 順序,year-first ISO-like)。\n * 棄 `en-US` `MM/DD/YYYY`(month-first 美式)— 美式順序在 international DS 反直覺\n * (跟 ISO date 視覺對不上,跟 sort 順序也對不上)。Ant / Material X / Apple HIG\n * 一致 year-first。Consumer 想自訂可傳 `formatOptions` + `locale`。\n */\n// 2026-07-06 D3 perf:formatOptions/locale 顯式傳入時走 Intl.DateTimeFormat(貴 constructor)。\n// DataTable date 欄轉發此 config → 每 cell × 每次表格 re-render 重建。module-level cache 重用實例\n// (無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。\nconst dtfCache = new Map<string, Intl.DateTimeFormat>()\nfunction getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + '|' + JSON.stringify(options)\n let fmt = dtfCache.get(key)\n if (!fmt) {\n fmt = new Intl.DateTimeFormat(locale, options)\n dtfCache.set(key, fmt)\n }\n return fmt\n}\n\nfunction formatDate(\n value: string | number | Date,\n options: DateFormatOptions = {},\n): string {\n // TZ-safe:date-only / datetime ISO string 走 isoToDate(local-time 語意,new Date(y,m-1,d))\n // 而非 new Date('YYYY-MM-DD')(ECMAScript 以 UTC 解析 → 負時區 off-by-one,如 LA 顯前一天)。\n // 非 ISO string(RFC 'Mar 12 2025' 等)fallback new Date();number timestamp 直接 new Date()。\n const date =\n value instanceof Date\n ? value\n : typeof value === 'string'\n ? (isoToDate(value) ?? new Date(value))\n : new Date(value)\n if (Number.isNaN(date.getTime())) return String(value)\n // 若 consumer 顯式傳 formatOptions / locale → 走 Intl.DateTimeFormat\n if (options.formatOptions || options.locale) {\n return getDateTimeFormatter(options.locale ?? 'en-US', options.formatOptions ?? { year: 'numeric', month: '2-digit', day: '2-digit' }).format(date)\n }\n // 預設:YYYY/MM/DD(直接組,locale-independent + 視覺穩定)\n const y = date.getFullYear()\n const m = String(date.getMonth() + 1).padStart(2, '0')\n const d = String(date.getDate()).padStart(2, '0')\n return `${y}/${m}/${d}`\n}\n\n/** 顯示用:date 或 datetime,根據 showTime / showSeconds 切換 */\nfunction formatDateOrDateTime(\n iso: string | null | undefined,\n showTime: boolean,\n showSeconds: boolean,\n options: DateFormatOptions = {},\n): string {\n if (!iso) return ''\n const dateText = formatDate(iso, options)\n if (!showTime) return dateText\n const time = isoToTimeParts(iso)\n if (!time) return dateText\n return `${dateText} ${timePartsToString(time, showSeconds)}`\n}\n\n// ── ISO <-> Date conversion ─────────────────────────────────────────────────\n// date-only:'YYYY-MM-DD'(local-time 語意,不帶時區)\n// datetime :'YYYY-MM-DDTHH:MM:SS'(同 local-time 語意)\n\nfunction isoToDate(iso: string | null | undefined): Date | undefined {\n if (!iso) return undefined\n const datePart = iso.slice(0, 10)\n const [y, m, d] = datePart.split('-').map(Number)\n if (!y || !m || !d) return undefined\n const date = new Date(y, m - 1, d)\n const time = isoToTimeParts(iso)\n if (time) {\n date.setHours(time.hours, time.minutes, time.seconds)\n }\n return date\n}\n\n/**\n * Issue 10 typed input parser(2026-05-10):接 user 自由輸入字串,parse 出 ISO date(YYYY-MM-DD)\n * 或 ISO datetime(YYYY-MM-DDTHH:MM[:SS])。\n *\n * 支援 format(per Material X DatePicker / Ant DatePicker typed input idiom):\n * - `YYYY-MM-DD` / `YYYY/MM/DD` / `YYYY.MM.DD`(ISO + 慣例 separator,明確 parse branch)\n * - `YYYY-MM-DD HH:MM[:SS]` / `YYYY-MM-DDTHH:MM[:SS]`(datetime,showTime 才接)\n * - native `Date.parse()` fallback(handle 'Mar 12 2025' 等英文 RFC;US `MM/DD/YYYY` 亦由此判讀,ambiguous)\n * ⚠️ 不支援 EU `DD/MM/YYYY`:無專屬 parser branch;native Date.parse 把 slash 日期當 US MM/DD,\n * `31/12/2026`(月 > 12)直接 Invalid。v1 known-gap,需 EU 請走 formatOptions/locale 或另加 parser branch。\n *\n * Return:`{ iso, valid }`。Invalid → `valid=false` + `iso=null`,UI 顯 aria-invalid。\n * Partial input(打到一半)→ valid=false 但不顯 error UI(consumer 用 onBlur / Enter 才檢驗)。\n *\n * 對齊 Material X-DatePicker `formats` parser + Ant DatePicker `format` array + dayjs.parse。\n */\nfunction parseDateInput(input: string, opts: { allowTime: boolean }): { iso: string | null; valid: boolean } {\n const trimmed = input.trim()\n if (trimmed === '') return { iso: null, valid: true } // empty = no value, OK\n // 1. ISO date YYYY-MM-DD or with separators / . /(可選 time YYYY-MM-DD[T| ]HH:MM[:SS])\n const isoDateMatch = trimmed.match(/^(\\d{4})[-/.](\\d{1,2})[-/.](\\d{1,2})(?:[T\\s](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?)?$/)\n if (isoDateMatch) {\n const [, y, m, d, hh, mm, ss] = isoDateMatch\n const date = new Date(Number(y), Number(m) - 1, Number(d), Number(hh ?? 0), Number(mm ?? 0), Number(ss ?? 0))\n if (isNaN(date.getTime()) || date.getMonth() !== Number(m) - 1) return { iso: null, valid: false }\n // time 分量越界校驗:JS Date 會 silently normalize(25:99:99 → 隔日某時,getTime 仍 valid、\n // getMonth 未必變),但回傳 iso 用的是原始越界字串 → 需在此擋掉 hh>23 / mm>59 / ss>59。\n if (hh != null && (Number(hh) > 23 || Number(mm) > 59 || Number(ss ?? 0) > 59)) return { iso: null, valid: false }\n const datePart = dateToIso(date)\n if (hh != null && opts.allowTime) {\n const h2 = String(Number(hh)).padStart(2, '0')\n const m2 = String(Number(mm)).padStart(2, '0')\n const s2 = String(Number(ss ?? 0)).padStart(2, '0')\n return { iso: `${datePart}T${h2}:${m2}:${s2}`, valid: true }\n }\n return { iso: datePart, valid: true }\n }\n // 2. US / EU locale MM/DD/YYYY or DD/MM/YYYY — ambiguous;v1 fallback Date.parse 並接受結果\n // 3. native Date.parse fallback(RFC-style 'Mar 12 2025')\n const parsed = new Date(trimmed)\n if (!isNaN(parsed.getTime())) {\n const datePart = dateToIso(parsed)\n if (opts.allowTime && (trimmed.includes(':') || trimmed.includes('T'))) {\n const h2 = String(parsed.getHours()).padStart(2, '0')\n const m2 = String(parsed.getMinutes()).padStart(2, '0')\n const s2 = String(parsed.getSeconds()).padStart(2, '0')\n return { iso: `${datePart}T${h2}:${m2}:${s2}`, valid: true }\n }\n return { iso: datePart, valid: true }\n }\n return { iso: null, valid: false }\n}\n\nfunction dateToIso(date: Date | undefined): string {\n if (!date) return ''\n const y = date.getFullYear()\n const m = String(date.getMonth() + 1).padStart(2, '0')\n const d = String(date.getDate()).padStart(2, '0')\n return `${y}-${m}-${d}`\n}\n\nfunction combineDateAndTime(date: Date, time: TimeParts): string {\n const datePart = dateToIso(date)\n const hh = String(time.hours).padStart(2, '0')\n const mi = String(time.minutes).padStart(2, '0')\n const ss = String(time.seconds).padStart(2, '0')\n return `${datePart}T${hh}:${mi}:${ss}`\n}\n\nfunction nowIsoDateTime(): string {\n const d = new Date()\n return combineDateAndTime(d, {\n hours: d.getHours(),\n minutes: d.getMinutes(),\n seconds: d.getSeconds(),\n })\n}\n\nfunction addDays(date: Date, n: number): Date {\n const next = new Date(date)\n next.setDate(next.getDate() + n)\n return next\n}\n\n// ── TimePickerSidePanel ────────────────────────────────────────────────\n//\n// DatePicker showTime / Range showTime 共用的右側時間 panel(canonical 2026-05-03 v8)。\n//\n// ── Caption row alignment canonical(永遠跟 calendar 年月對齊)──\n// 結構必須符合 DateGrid month_caption 同樣的 pt-3 + h-field-xs + mb-3 規格,讓 title\n// 跟 calendar 「April 2026」字 baseline 在同一 Y 座標(垂直對齊)。\n// Y 座標推導:\n// - panel root 的上內距**讀跟 DateGrid 根一樣的運算式**,所以「對齊」是綁定的、不是靠人記得同步\n// - h-field-xs = 24px header,title 純 flex items-center justify-center → 真正水平+垂直置中\n// - mb-3 = 12px gap 對齊 DateGrid month_caption mb-3\n// → title text center Y = 12 + 12 = 24px(from CalendarTimeContainer top)\n// → calendar caption text center Y = 12(p-3 top)+ 12(caption row half)= 24px ✓ 同一 Y\n// 2026-09-18:原本兩邊各寫死 `p-3` / `pt-3`(都是字面 12),靠這行 ⚠️ 註解提醒人「改一邊要記得改另一邊」。\n// DateGrid 的根已改讀 `var(--item-px,var(--field-px))`,這裡跟著讀同一個運算式 ——\n// **任何容器覆寫 `--item-px`,兩邊會一起動**,不再有「其中一邊忘了跟」的空間(先前那種靠註解的雙鎖是紙老虎)。\n// 下方 `mb-3`(caption → 星期列的 12px)兩邊仍是字面值,但它不受根內距影響,維持原樣。\n//\n// ── Header divider canonical(無 border-b)──\n// Header 下方無 divider,對齊 DateGrid month_caption(無 border-b,只 mb-3 gap)。\n// DS internal canonical(M23)優先於 Ant time-picker header divider 慣例 — 兩 panel\n// 同層級 caption 視覺對稱,引入 divider 會破對稱。\n//\n// ── Bottom padding canonical(0)──\n// Root 用 pt-3 而非 py-3:bottom = 0,讓 columns 連續延伸到 SurfaceFooter border-t。\n// Ant / Material time picker idiom — time list 視覺感「continuous scroll」延伸到 footer\n// divider,bottom padding 12px 反而讓 list 看起來「截斷」。\n// 此處與 DateGrid p-3(bottom 12)有意 asymmetric:Calendar cells 不該撞 footer divider\n// (cells 是離散 grid),time list 是 scroll list 撞 divider 反而合理。\n\ninterface TimePickerSidePanelProps {\n value?: TimeParts\n onChange: (next: TimeParts) => void\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n}\n\nfunction TimePickerSidePanel({\n value,\n onChange,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n className,\n}: TimePickerSidePanelProps & { className?: string }) {\n // Dynamic header text — 顯示當前選擇的 HH:MM(對齊 user Q4 + Ant idiom)\n const headerText = value\n ? timePartsToString(value, showSeconds)\n : (showSeconds ? '--:--:--' : '--:--')\n\n return (\n <div className={cn('flex flex-col h-full pt-[var(--item-px,var(--field-px))]', className)}>\n {/* Header 純結構:h-field-xs (24px) + flex 水平+垂直置中 + mb-3 (12px gap) */}\n <div className=\"h-field-xs flex items-center justify-center mb-3\">\n <span className=\"text-body font-medium tabular-nums\">{headerText}</span>\n </div>\n {/* Columns:flex-1 填滿剩餘 height,無 horizontal padding(填滿容器寬度) */}\n <div className=\"flex-1 min-h-0 flex\">\n <TimeColumns\n value={value}\n onChange={onChange}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n </div>\n </div>\n )\n}\n\n/**\n * showTime panel container — 包 DateGrid + TimePicker side panel,DateGrid 主導 row 高度,\n * TimePicker absolute 撐滿同高,不影響 layout。Spacer div 留 layout 寬度給 absolute panel。\n */\nconst TIME_PANEL_WIDTH = (showSeconds: boolean) => showSeconds ? 'w-60' : 'w-40'\n\ninterface CalendarTimeContainerProps {\n showTime: boolean\n showSeconds: boolean\n calendar: React.ReactNode\n timePanel?: React.ReactNode\n}\n\nfunction CalendarTimeContainer({ showTime, showSeconds, calendar, timePanel }: CalendarTimeContainerProps) {\n if (!showTime) return <>{calendar}</>\n return (\n <div className=\"relative\">\n <div className=\"flex flex-row\">\n {calendar}\n {/* Spacer 佔 layout 寬度給 absolute TimePicker;border-l 在這層,不在 absolute 層\n (避免 stacking + border 雙繪) */}\n <div className={cn('shrink-0 border-l border-divider', TIME_PANEL_WIDTH(showSeconds))} />\n </div>\n {/* TimePicker absolute 撐滿 DateGrid 高度(top-0 bottom-0),right-0 對齊 spacer */}\n <div className={cn('absolute top-0 right-0 bottom-0', TIME_PANEL_WIDTH(showSeconds))}>\n {timePanel}\n </div>\n </div>\n )\n}\n\n// ── DatePicker(single)──────────────────────────────────────────────────\n\nexport interface DatePickerProps\n extends DateFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'value' | 'onChange' | 'placeholder' | 'defaultValue'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮(value 寬 + slot 寬 + gap + 內 padding);\n * chrome 與互動不變。SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */\n width?: FieldWidth\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** ISO date(YYYY-MM-DD)或 ISO datetime(YYYY-MM-DDTHH:MM:SS,當 showTime=true) */\n value?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n className?: string\n disabled?: boolean\n /** 允許清空已選值 */\n clearable?: boolean\n /** 啟用時間欄位(時 / 分 [/ 秒]),Ant idiom — value 變 ISO datetime */\n showTime?: boolean\n /** showTime 時是否顯示秒 */\n showSeconds?: boolean\n /** showTime 分鐘步進(會議常用 15) */\n minuteStep?: TimeStep\n /** showTime 秒鐘步進 */\n secondStep?: TimeStep\n /**\n * 是否需 OK 確認才提交,預設 showTime=true 時為 true(對齊 Ant DatePicker showTime)\n * — datetime picker user 習慣編完才 commit,避免 calendar 點到就關。\n */\n needConfirm?: boolean\n /**\n * View 態是否渲 Calendar icon + Field naked wrapper(D-path opt-in,2026-05-08)\n * — DataTable cell view↔edit 像素級對齊用。預設 false(裸 span,backward compat)。\n * 設 true 時 view 走 fieldWrapperStyles(naked variant)+ ItemSuffix CalendarIcon,\n * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。\n */\n showDisplayEndIcon?: boolean\n /** Initial open state(uncontrolled)— DataTable cell-as-input 1-step open canonical */\n defaultOpen?: boolean\n /** open state 變更 callback。DataTable cell-as-input 用:open=false → cell exit edit */\n onOpenChange?: (open: boolean) => void\n /**\n * Issue 10 typed input(2026-05-10):trigger 內渲 real `<input>` 接 user 鍵盤輸入,\n * 同時保持 Calendar icon trigger 開啟 calendar 選擇(對齊 Material X DatePicker / Ant\n * DatePicker / Notion typed-date idiom)。\n *\n * 預設 false(backward-compat)— trigger 仍是 `<div role=\"combobox\">` + `<span>` 文字。\n *\n * Behavior(opt-in `typeable=true`):\n * - `<input>` 取代 `<span>` displayValue,user 可直接打字\n * - Partial input(打到一半,如 \"2025-\")allow,**不**即時驗證\n * - `Enter` / `Blur` → `parseDateInput` 解析 → 合法 commit `onChange`;不合法 set\n * aria-invalid + keep draft visible(user 可繼續修)\n * - `Esc` → reset draft 回 committed value\n * - IME composition 期間不觸發驗證(中日韓輸入法 onCompositionStart/End 攔截)\n * - Calendar pick → 同步 input draft + commit(走原 path)\n * - **點欄位任何地方都開日曆,焦點留在輸入框可繼續打字**(2026-09-07;Ant 官方文件\n * 「By clicking the input box, you can select a date from a popup calendar」+ inputReadOnly:false)\n * - **鍵盤 ArrowDown / Alt+ArrowDown 開啟時焦點進日曆** —— W3C APG date-picker combobox 逐字\n * 「opened by activating the choose date button or by moving keyboard focus to the combobox and\n * pressing Down Arrow or Alt + Down Arrow」;焦點不進去就走不了日期格,那條不能為了打字犧牲\n *\n * **v1 limits**:\n * - format detection ISO YYYY-MM-DD / YYYY/MM/DD / Date.parse fallback(`parseDateInput`)\n * - US `MM/DD/YYYY` vs EU `DD/MM/YYYY` ambiguous → fallback native parser\n * - 未支援 locale-aware format prop(v2 加 `dateFormat?: string`)\n *\n * 對齊 Material X-DatePicker `format` prop / Ant DatePicker `format` array / Notion typed-input。\n */\n typeable?: boolean\n}\n\n// Trigger uses `<div role=\"combobox\" tabIndex={...}>` instead of `<button>` —\n// 對齊 Combobox / Select / TimePicker 同 pattern,避免 ItemInlineAction(內部 button)\n// 構成 nested-interactive(axe serious)。Radix PopoverTrigger 只 compose onClick(非\n// native button → 無 Enter/Space→click),Enter/Space 開 popover 由本檔自建 onKeyDown 補\n// (對齊 select.tsx desktop trigger canonical);typeable 模式交由內層 <input> 自管鍵盤。\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n mode,\n variant: variantProp,\n width,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n formatOptions,\n locale,\n showTime = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n needConfirm: needConfirmProp,\n showDisplayEndIcon = false,\n defaultOpen = false,\n onOpenChange,\n typeable = false,\n id: idProp,\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledByProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix:<Field size>/cell surface-size 對 DatePicker 生效\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"view\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const needConfirm = needConfirmProp ?? showTime // datetime 預設需確認\n const [open, setOpenState] = React.useState(defaultOpen)\n const setOpen = React.useCallback((next: boolean) => { setOpenState(next); onOpenChange?.(next) }, [onOpenChange])\n // Radix Popover 的預設 contentId 在 content 未掛載時仍會留在 trigger。\n // DatePicker 改用穩定 ID,且只在 dialog 已掛載(open)時輸出 aria-controls,\n // 避免 closed state 的懸空 IDREF;typeable 模式則把關聯放到真正的 combobox input。\n const popupId = React.useId()\n const [draft, setDraft] = React.useState<string | null>(value ?? null)\n const resolvedPlaceholder = placeholder ?? (showTime ? 'YYYY/MM/DD HH:MM' : 'YYYY/MM/DD')\n // a11y:role=\"combobox\" 必須有 accessible name(aria-label / labelledby / fieldCtx label)\n const accessibleName = ariaLabelProp ?? (ariaLabelledByProp ? undefined : (fieldCtx?.id ? undefined : resolvedPlaceholder))\n\n // Sync draft from value:closed 期間恆跟隨 + open false→true 同步(避免 popover 開啟期間\n // value 改變 clobber user 的編輯)。\n // 2026-07-14 audit Dim 26(V4 state shadow):needConfirm trigger 讀 draft,原「只在 false→true\n // 同步」使 controlled value 於關閉期間更新後,trigger 仍顯示舊值直到再次開啟 —— 改為\n // 只要 open=false 就同步(與 range picker 分支 L83x 同款 canonical)。\n const lastOpenRef = React.useRef(open)\n React.useEffect(() => {\n if (!open || !lastOpenRef.current) setDraft(value ?? null)\n lastOpenRef.current = open\n }, [open, value])\n\n // Display value canonical(2026-05-02 fix):\n // needConfirm=true(showTime 預設)→ trigger 讀 draft,user 點 calendar 看到 input 即時更新\n // needConfirm=false → trigger 讀 value(committed,符合非確認流程)\n const displayValue = needConfirm ? draft : (value ?? null)\n const displayDate = React.useMemo(() => isoToDate(displayValue), [displayValue])\n const draftDate = React.useMemo(() => isoToDate(draft), [draft])\n const draftTime = isoToTimeParts(draft) ?? { hours: 0, minutes: 0, seconds: 0 }\n const showClear = clearable && (needConfirm ? draft : value) && isEditable\n\n const displayCommitted = formatDateOrDateTime(value, showTime, showSeconds, { formatOptions, locale })\n const displayLive = formatDateOrDateTime(displayValue, showTime, showSeconds, { formatOptions, locale })\n\n // Issue 10 typed input(2026-05-10):draft string + invalid flag + IME composition guard。\n const [inputDraft, setInputDraft] = React.useState<string>(displayLive)\n const [inputInvalid, setInputInvalid] = React.useState(false)\n const composingRef = React.useRef(false)\n // 2026-09-07:記住浮層是**怎麼被打開的**。\n // 指標開啟 → 焦點留在輸入框(使用者正在用滑鼠,可能還想打字;日曆用點的即可)\n // 鍵盤開啟 → 焦點進日曆(否則鍵盤使用者 navigate 不了日期格,那是 W3C APG\n // date-picker combobox 明文要求「focus moves into the dialog」的理由)\n // 兩條路各自成立,不是二選一 —— 這也是 user 2026-09-07 問「同時出現不是更好用嗎」的正解:\n // 好用的是**滑鼠那條**,鍵盤那條不能為了它犧牲可操作性。\n const openedByPointerRef = React.useRef(false)\n const typedInputRef = React.useRef<HTMLInputElement | null>(null)\n // 指標開啟時把焦點送回輸入框。\n // **不用 `onOpenAutoFocus` 攔截**:實測那個事件在本組合下根本沒被派發\n //(探針顯示 handler 從未執行,焦點卻仍被移到日曆內的按鈕),追 Radix 內部只會愈追愈深。\n // 這裡改成「開啟後下一幀把焦點要回來」—— 不依賴任何第三方內部行為,而且驗得到。\n React.useEffect(() => {\n if (!open || !typeable || !openedByPointerRef.current) return\n const id = requestAnimationFrame(() => typedInputRef.current?.focus())\n return () => cancelAnimationFrame(id)\n }, [open, typeable])\n // Sync input draft from committed displayLive(value change from outside)— 不要在 user\n // 打字期間覆寫。透過 ref 比較:committed vs current draft 是否從相同 source。\n const lastDisplayLiveRef = React.useRef(displayLive)\n React.useEffect(() => {\n if (lastDisplayLiveRef.current !== displayLive) {\n setInputDraft(displayLive)\n setInputInvalid(false)\n lastDisplayLiveRef.current = displayLive\n }\n }, [displayLive])\n const handleInputCommit = React.useCallback((raw: string) => {\n const { iso, valid } = parseDateInput(raw, { allowTime: showTime })\n if (!valid) { setInputInvalid(true); return }\n setInputInvalid(false)\n if (iso === null) {\n // empty input = clear value\n onChange?.('')\n setDraft(null)\n } else {\n onChange?.(iso)\n setDraft(iso)\n }\n }, [onChange, showTime])\n\n // 截斷 tooltip(tooltip.spec.md:32 截斷 → 顯完整內容,僅實際截斷時):非 typeable edit\n // trigger 的值 span(bareInputStyles + truncate)是截斷元素;trigger = 整個 field wrapper\n // (interactive host,hover 直達)、量測內層值 span — useTruncated 自組 pattern\n // (truncated-text.spec.md「trigger 需自控」指定解;範本 inline-edit.tsx:251)。\n // typeable 走 <input>(可捲動編輯,無截斷補救需求);popover 開啟時靜默(open 是 hover 之外的 reveal 路徑)。\n // ⚠️ hook 必在下方 view / readonly early return 之前(React #310 hook-count 鐵律,同 select.tsx 教訓)。\n const { ref: triggerTruncationRef, isTruncated: triggerTruncated } = useTruncated<HTMLSpanElement>({\n deps: [!typeable && displayValue ? displayLive : ''],\n })\n const triggerTooltipActive = !typeable && !!displayValue && triggerTruncated && !open\n\n // mode='view'(Phase B2 2026-05-05):純內容輸出 — 對齊原 DatePickerDisplay sub-component(retired)。\n // Default(showDisplayEndIcon=false):無 Field wrapper / 無 Calendar icon — backward compat 裸 span。\n // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix Calendar,\n // 與 edit 同結構消除 cell view↔edit 像素偏移(Layer-B padding mismatch)。\n if (resolvedMode === 'view') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) view typography canonical):bare span 套\n // `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>\n // 截斷必附 tooltip(tooltip.spec.md:32)— 改消費 truncated-text primitive(同 input.tsx:196 同型修)\n return <TruncatedText className={cn(fieldDisplayTextClass(size), className)}>{displayCommitted}</TruncatedText>\n }\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'view', variant, width, size }), className)}\n data-field-mode=\"view\"\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32;data-table.spec.md:183 view 態元件自管 truncation) */}\n <TruncatedText className={cn(bareInputStyles, 'flex-1 min-w-0', !value && fieldEmptyColorClass(resolvedMode))}>\n {value ? displayCommitted : emptyDisplay}\n </TruncatedText>\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n )\n }\n\n // readonly / disabled\n if (!isEditable) {\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, width, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...(props as React.HTMLAttributes<HTMLDivElement>)}\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— readonly/disabled 無編輯入口,tooltip 是唯一補救路徑(同 Range readonly 分支同型) */}\n <TruncatedText className={cn('flex-1', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {value\n ? displayCommitted\n : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>\n }\n </TruncatedText>\n {/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示(純值、不可開) / disabled 保留(fg-disabled,對齊原生 <select disabled>);\n naked cell 依 showDisplayEndIcon=isEditable(維持 2026-05-10 cell canonical「非可編欄不顯」)*/}\n {(variant === 'naked' ? showDisplayEndIcon : resolvedMode === 'disabled') && (\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className={resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'} aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n const triggerText = displayValue\n ? displayLive\n : <span className=\"text-fg-muted\">{resolvedPlaceholder}</span>\n\n const commitDraft = (next: string | null) => {\n if (needConfirm) setDraft(next)\n else onChange?.(next ?? '')\n }\n const handleConfirm = () => { onChange?.(draft ?? ''); setOpen(false) }\n const handleNow = () => {\n const now = showTime ? nowIsoDateTime() : dateToIso(new Date())\n commitDraft(now)\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n {/* 截斷 tooltip:恆 wrap(truncated-text.spec.md always-wrap),未截斷或 popover 開啟時\n open={false} 靜默。TooltipTrigger 放最內側(緊貼 div):PopoverTrigger 注入的\n data-state(Field open 邊框樣式依賴)在 TooltipTrigger 內以 triggerProps 後 spread\n 勝出;反序會被 tooltip 的 data-state 蓋掉。 */}\n <Tooltip open={triggerTooltipActive ? undefined : false}>\n <PopoverTrigger asChild type={undefined}>\n <TooltipTrigger asChild>\n <div\n ref={ref}\n // a11y(2026-07-14 dim-10 修):typeable 模式 combobox 語意(name / state / 鍵盤)\n // 集中在內層 <input>(APG editable-combobox idiom)— 外層 div 移除 role / tabIndex\n // / aria-*,否則形成「關掉鍵盤 handler 的假 combobox tab stop」+ 內層 input 變成\n // 第二個未命名 tab stop(雙重焦點站)。非 typeable 維持原 div-as-combobox canonical。\n id={typeable ? undefined : (idProp ?? fieldCtx?.id)}\n role={typeable ? undefined : 'combobox'}\n tabIndex={typeable ? undefined : (disabled ? -1 : 0)}\n aria-disabled={typeable ? undefined : (disabled || undefined)}\n aria-label={typeable ? undefined : accessibleName}\n aria-labelledby={typeable ? undefined : (ariaLabelledByProp ?? fieldCtx?.labelId)}\n aria-invalid={typeable ? undefined : (error || undefined)}\n aria-required={typeable ? undefined : (fieldCtx?.required || undefined)}\n aria-describedby={typeable ? undefined : (ariaDescribedByProp ?? fieldCtx?.descriptionId)}\n aria-errormessage={typeable ? undefined : (ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined))}\n aria-haspopup={typeable ? undefined : 'dialog'}\n aria-expanded={typeable ? undefined : open}\n aria-controls={typeable || !open ? undefined : popupId}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n // Radix PopoverTrigger 只 compose onClick(onOpenToggle),`<div>` trigger 無\n // native Enter/Space→click → 自建 onKeyDown 開 popover(對齊 select.tsx desktop\n // trigger canonical:Enter/Space → 開;Esc → 關)。typeable 模式不攔 — 內層\n // <input> 自有 Enter/Esc 語意(commit / reset draft),Calendar icon click 開\n // popover(Material/Ant typed-date idiom),避免 div 層 keydown 與 input 衝突。\n onKeyDown={typeable ? undefined : (e) => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(true) }\n if (e.key === 'Escape') setOpen(false)\n }}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, width, size, error }),\n 'text-left cursor-pointer',\n // @focus-suppress B — B Field 家族輸入控件;承擔者:欄位邊框轉 primary(field-wrapper.tsx:57)\n 'focus-visible:outline-none',\n className,\n )}\n {...props}\n >\n {typeable ? (\n // Issue 10 typed input(2026-05-10):real `<input>` 接 user 鍵盤打字。\n // 2026-09-07 訂正:原本這裡寫「Click 在 input 上不 propagate…Calendar icon 點才開\n // popover(Material/**Ant** typed-date idiom)」—— **對 Ant 而言是反的**。\n // Ant Design 官方文件逐字:「By clicking the input box, you can select a date from a\n // popup calendar」,且 `inputReadOnly` 預設 false(可同時打字)。\n // 現行:點欄位任何地方都開,焦點留在輸入框(見上方 openedByPointerRef)。\n // a11y(2026-07-14 dim-10 修):input 持完整 combobox 語意(id/name/state,APG\n // editable-combobox)— 外層 div 已讓位(見上方 role/tabIndex 註解);ArrowDown\n // 開 popover 補鍵盤開啟路徑(原僅 icon click 可開,鍵盤 user 無入口)。\n <input\n ref={typedInputRef}\n type=\"text\"\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n aria-controls={open ? popupId : undefined}\n aria-label={accessibleName}\n aria-labelledby={ariaLabelledByProp ?? fieldCtx?.labelId}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? ((inputInvalid || error) ? fieldCtx?.errorId : undefined)}\n disabled={disabled}\n className={cn(bareInputStyles, 'truncate', !inputDraft && 'placeholder:text-fg-muted')}\n value={inputDraft}\n placeholder={resolvedPlaceholder}\n aria-invalid={inputInvalid || error || undefined}\n onChange={(e) => { setInputDraft(e.target.value); setInputInvalid(false) }}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n if (composingRef.current) return\n if (e.key === 'Enter') { e.preventDefault(); handleInputCommit(inputDraft) }\n if (e.key === 'Escape') { setInputDraft(displayLive); setInputInvalid(false); e.preventDefault() }\n if (e.key === 'ArrowDown' && !open) { e.preventDefault(); openedByPointerRef.current = false; setOpen(true) }\n }}\n onBlur={() => { if (!composingRef.current) handleInputCommit(inputDraft) }}\n // 2026-09-07(user 抓「點下去為何不是直接開日曆同時可以打字」):\n // 原本這裡 `stopPropagation` 把點擊吞掉,所以只有點到圖示那一小塊才會開。\n // Ant Design 官方文件逐字:「By clicking the input box, you can select a date\n // from a popup calendar」,而且 `inputReadOnly` 預設 false(可同時打字)——\n // 我們原本註解寫「Calendar icon 仍 click 開 popover(Material/**Ant** idiom)」\n // 對 Ant 而言是**反的**。現在點欄位任何地方都開,焦點留在輸入框。\n onPointerDown={() => { openedByPointerRef.current = true }}\n />\n ) : (\n // ref = 截斷量測點(值 span 是截斷元素;tooltip trigger 在外層 host div,見 triggerTruncationRef 註解)\n <span ref={triggerTruncationRef} className={cn(bareInputStyles, 'truncate', !displayValue && 'text-fg-muted')}>\n {triggerText}\n </span>\n )}\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除日期', // i18n-allow: DS default inline-action label\n // Clear = 立刻 commit + 同步 draft(對齊 user 體感 / Ant trigger X 慣例)\n // 不走 needConfirm「等確定」語義 — X 在 trigger 上是 standard clear affordance,\n // 應立刻清空。dual-state 必同步:value('') + draft(null),否則 popover 開\n // 著時 displayValue=draft 仍顯示舊值(see line 318: displayValue = needConfirm ? draft : value)。\n onClick: (e) => {\n e?.stopPropagation()\n onChange?.('')\n setDraft(null)\n },\n }}\n />\n )}\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n </TooltipTrigger>\n </PopoverTrigger>\n <TooltipContent>{triggerTooltipActive ? displayLive : null}</TooltipContent>\n </Tooltip>\n <PopoverContent\n id={popupId}\n className=\"w-auto p-0\"\n align=\"start\"\n aria-label=\"日期選擇\" // i18n-allow: DS default dialog label\n >\n {/* a11y(2026-07-14 dim-10 修):Radix PopoverContent 自帶 role=\"dialog\"(popover.tsx:58)\n — 內層 div 原本重複 role=\"dialog\" 形成雙層 nested unnamed dialog,改為純 layout div,\n accessible name 收斂到外層 PopoverContent aria-label(單一 dialog 層)。\n 2026-05-06 v9.1:`flex flex-col flex-1 min-h-0` 完成 M25 chain — viewport\n 壓縮時 dialog 縮 + 內 calendar/footer 排序;原無 chain 致 calendar 末行被\n overflow-hidden 切掉、footer 推出 popover(user 報「位置改變就壞掉」根因)。 */}\n <div className=\"flex flex-col flex-1 min-h-0\">\n {/* Calendar 區包 ScrollArea(2026-08-12 自 raw overflow-y-auto 遷移,跨 OS 一致 overlay 捲軸;M25 chain 由 flex-1 min-h-0 照傳):viewport 壓縮時 calendar 內滾(Material / Carbon\n date picker idiom)。footer 永遠 in-view(SurfaceFooter shrink-0)。 */}\n <ScrollArea className=\"flex-1 min-h-0\">\n <CalendarTimeContainer\n showTime={showTime}\n showSeconds={showSeconds}\n calendar={\n <DateGrid\n mode=\"single\"\n selected={displayDate}\n onSelect={(date) => {\n if (!date) return\n if (showTime) {\n commitDraft(combineDateAndTime(date, draftTime))\n } else {\n commitDraft(dateToIso(date))\n if (!needConfirm) setOpen(false)\n }\n }}\n defaultMonth={displayDate ?? undefined}\n autoFocus\n />\n }\n timePanel={\n <TimePickerSidePanel\n value={draftTime}\n onChange={(time) => {\n const target = draftDate ?? new Date()\n commitDraft(combineDateAndTime(target, time))\n }}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n }\n />\n </ScrollArea>\n {(showTime || needConfirm) && (\n // Footer:消費 SurfaceFooter SSOT(border-t + py-tight;左右內距不覆寫 = 預設 loose 16px,\n // 對齊的是第一個日期格左緣(DateGrid p-3 12 + border-spacing-1 4 = 16),實測差 0。\n // 判準 owner:patterns/overlay-surface/overlay-surface.spec.md「要對齊誰」;\n // ⚠️ 兩個 16 是不相干的字面值剛好相等,改 DateGrid p-3 時 footer 不會跟著動,\n // 不再 hand-coded p-2 / Separator / ml-auto wrapper 三層垃圾)。\n // 「此刻」加 mr-auto 把後面 button 推右(對齊 Ant `marginInlineStart: auto` on OK)。\n // 2026-07-05 D4:gate 由 `showTime` 改 `(showTime || needConfirm)` 對齊 Range(:1068)—\n // date-only `needConfirm` 時 onSelect 只寫 draft 且不關 popover,footer「確定」是唯一\n // commit 路徑,原 gate 漏渲 footer = draft 永遠無法 commit 的死路。\n <SurfaceFooter className=\"px-[var(--item-px,var(--field-px))]\">\n <Button variant=\"tertiary\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">此刻</Button>\n {needConfirm ? (\n <Button variant=\"primary\" size=\"sm\" onClick={handleConfirm} disabled={!draft}>確定</Button>\n ) : (\n <Button variant=\"tertiary\" size=\"sm\" onClick={() => setOpen(false)}>關閉</Button>\n )}\n </SurfaceFooter>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n }\n)\nDatePicker.displayName = 'DatePicker'\n\n// ── DatePickerRange ─────────────────────────────────────────────────────────\n//\n// Canonical 2026-05-02 v4 — 全對齊 Ant Design RangePicker(WebFetch 實證):\n//\n// **showTime Range**(rc-picker `multiplePanel = false` 證實):\n// - **1 calendar + 1 time panel**(等同 single DateTimePicker layout)\n// - 沒 range track 視覺(計算上跟 single 一樣)\n// - footer **無「此刻」按鈕**(rc-picker `showNow={multiple ? false : showNow}` 證實)\n// - Click flow:click input → open popup for activeEnd → 編 → 點「確定」commit activeEnd\n// → if start: switch activeEnd='end' + popup 維持 open;if end: close popup\n// - Cell disable(rc-picker useRangeDisabledDate 證實):\n// activeEnd='end' + start 已選 → date < start disabled\n// activeEnd='start' + end 已選 → date > end disabled\n//\n// **date-only Range**(rc-picker `multiplePanel = true`):\n// - **2 calendars 並列**(showTime=false 時)\n// - Full range track 視覺(start / middle / end)\n// - 走原 RDP mode='range' 配對 click 邏輯 + auto-swap\n//\n// **Trigger**:2 input button,active end blue underline 標示\n\nexport interface DatePickerRangeProps\n extends DateFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'value' | 'onChange' | 'placeholder' | 'defaultValue'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮(value 寬 + slot 寬 + gap + 內 padding);\n * chrome 與互動不變。SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */\n width?: FieldWidth\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** 區間值:[start ISO, end ISO]。任一 null 代表尚未選。 */\n value?: [string | null, string | null] | null\n onChange?: (value: [string | null, string | null]) => void\n /** Placeholder:[start placeholder, end placeholder] */\n placeholder?: [string, string]\n className?: string\n disabled?: boolean\n clearable?: boolean\n /** 啟用時間欄位 — value 兩端皆變 ISO datetime */\n showTime?: boolean\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n needConfirm?: boolean\n}\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(\n (\n {\n mode,\n variant: variantProp,\n width,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n formatOptions,\n locale,\n showTime = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n needConfirm: needConfirmProp,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref,\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix:<Field size>/cell surface-size 對 DatePicker 生效\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"view\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const needConfirm = needConfirmProp ?? showTime\n const resolvedPlaceholder: [string, string] = placeholder ?? (\n showTime ? ['Start date time', 'End date time'] : ['Start date', 'End date'] // i18n-allow: DS default; consumer override via placeholder prop\n )\n\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<[string | null, string | null]>(value ?? [null, null])\n const [activeEnd, setActiveEnd] = React.useState<'start' | 'end'>('start')\n\n // 2026-07-05 D4 focus 回復:Range 只用 PopoverAnchor(start/end button 自行 onClick 開啟),\n // Radix triggerRef 恆 null → 內建 onCloseAutoFocus 的 triggerRef.focus() no-op 後又\n // preventDefault 掉 FocusScope fallback → Esc/確定/選完自動關閉後焦點掉到 document.body\n // (WCAG 2.4.3)。手動回焦 active 端 button(對齊 Ant RangePicker);outside-click 關閉\n // 依 Radix non-modal 慣例不搶焦(hasInteractedOutside 鏡射 Radix 自家 guard)。\n const startBtnRef = React.useRef<HTMLButtonElement>(null)\n const endBtnRef = React.useRef<HTMLButtonElement>(null)\n const hasInteractedOutsideRef = React.useRef(false)\n\n // Draft sync canonical(2026-07-14 dim-26 V4 修,升級 2026-05-02 v3):\n // - open=false(關閉期間):controlled value 變更**必**同步 draft — needConfirm 顯示層讀\n // draft(startIso/endIso),不同步會讓 trigger 持續顯示 stale 舊區間(外部 controlled\n // update 被凍結)。關閉期間無編輯進行,直接鏡射無 clobber 風險。\n // - open=true(開啟期間):只在 false→true 轉換時 seed 一次;開啟中 value reference 變更\n // 不覆寫,保護 user 尚未確認的 draft 編輯(原 v3 canonical 保留)。\n const lastOpenRef = React.useRef(open)\n React.useEffect(() => {\n if (!open || !lastOpenRef.current) setDraft(value ?? [null, null])\n lastOpenRef.current = open\n }, [open, value])\n\n const startIso = (needConfirm ? draft[0] : value?.[0]) ?? null\n const endIso = (needConfirm ? draft[1] : value?.[1]) ?? null\n const startDate = React.useMemo(() => isoToDate(startIso), [startIso])\n const endDate = React.useMemo(() => isoToDate(endIso), [endIso])\n const hasValue = !!(value?.[0] || value?.[1])\n const showClear = clearable && hasValue && isEditable\n\n const startText = startIso\n ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale })\n : resolvedPlaceholder[0]\n const endText = endIso\n ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale })\n : resolvedPlaceholder[1]\n\n // 截斷 tooltip(tooltip.spec.md:32 截斷 → 顯完整內容,僅實際截斷時):Range 兩端 edit\n // trigger button 各自截斷、各自量測顯示 — trigger = button 本體(interactive host,hover\n // 直達)、量測內層文字 span,useTruncated 自組 pattern(truncated-text.spec.md「trigger 需\n // 自控」指定解;範本 inline-edit.tsx:251)。popover 開啟時靜默;hook 必在 view / readonly\n // early return 之前(React #310 hook-count 鐵律,同 select.tsx 教訓)。\n const { ref: startTruncationRef, isTruncated: startTruncated } = useTruncated<HTMLSpanElement>({\n deps: [startIso ? startText : ''],\n })\n const { ref: endTruncationRef, isTruncated: endTruncated } = useTruncated<HTMLSpanElement>({\n deps: [endIso ? endText : ''],\n })\n const startTooltipActive = !!startIso && startTruncated && !open\n const endTooltipActive = !!endIso && endTruncated && !open\n\n const activeIso = activeEnd === 'start' ? startIso : endIso\n const activeDate = activeEnd === 'start' ? startDate : endDate\n const activeTime = isoToTimeParts(activeIso) ?? { hours: 0, minutes: 0, seconds: 0 }\n\n // Range visual modifiers(自管,不靠 RDP mode='range'):\n // rangeStart:start 那天 → 圓底白字\n // rangeEnd:end 那天 → 圓底白字\n // rangeMiddle:start+1 ~ end-1 之間的所有天 → 灰底矩形 track\n const rangeModifiers = React.useMemo(() => {\n const mods: Record<string, Date | { from: Date; to: Date } | undefined> = {}\n if (startDate) mods.rangeStart = startDate\n if (endDate) mods.rangeEnd = endDate\n if (startDate && endDate) {\n const middleStart = addDays(startDate, 1)\n const middleEnd = addDays(endDate, -1)\n if (middleEnd >= middleStart) {\n mods.rangeMiddle = { from: middleStart, to: middleEnd }\n }\n }\n return mods\n }, [startDate, endDate])\n\n const commitRange = (next: [string | null, string | null]) => {\n if (needConfirm) setDraft(next)\n else { onChange?.(next); setDraft(next) }\n }\n const setActive = (iso: string | null) => {\n const nextDraft = activeEnd === 'start'\n ? ([iso, draft[1]] as [string | null, string | null])\n : ([draft[0], iso] as [string | null, string | null])\n commitRange(nextDraft)\n }\n /**\n * Click「確定」canonical(2026-05-02 v4,對齊 Ant Design 序列流程):\n * showTime Range:\n * - activeEnd='start' → commit start to draft + switch activeEnd='end' + popup 維持 open\n * - activeEnd='end' → commit final draft to value + close popup\n * date-only Range(沒有 footer,不會走這 path):— N/A\n */\n const handleConfirm = () => {\n if (showTime && activeEnd === 'start' && draft[0]) {\n // Start 已填 → switch to end,popup 維持 open\n setActiveEnd('end')\n } else {\n // End 也填好(or non-showTime needConfirm)→ final commit + close\n onChange?.(draft)\n setOpen(false)\n }\n }\n const handleNow = () => {\n setActive(showTime ? nowIsoDateTime() : dateToIso(new Date()))\n }\n const handleClearRange = (e?: React.MouseEvent) => {\n e?.stopPropagation()\n // Clear = 立刻 commit + 同步 draft(對齊 single mode + user 體感)\n // dual-state 同步,否則 popover 開著時 displayValue=draft 仍顯示舊 [start, end]\n onChange?.([null, null])\n setDraft([null, null])\n }\n const openWithActive = (which: 'start' | 'end') => {\n setActiveEnd(which)\n setOpen(true)\n }\n /**\n * Cell disable(對齊 Ant rc-picker `useRangeDisabledDate`):\n * activeEnd='end' + start 已選 → date < start 被 disable(同日 OK)\n * activeEnd='start' + end 已選 → date > end 被 disable(同日 OK)\n * 防 user 點下違反順序的日期(start > end / end < start)。\n */\n const isOutOfRangeOrder = React.useCallback((date: Date): boolean => {\n // ⚠️ 必先 clone(new Date(...)),否則 setHours 會 mutate useMemo'd date 物件\n if (activeEnd === 'end' && startDate) {\n const startMidnight = new Date(startDate.getTime())\n startMidnight.setHours(0, 0, 0, 0)\n return date.getTime() < startMidnight.getTime()\n }\n if (activeEnd === 'start' && endDate) {\n const endEndOfDay = new Date(endDate.getTime())\n endEndOfDay.setHours(23, 59, 59, 999)\n return date.getTime() > endEndOfDay.getTime()\n }\n return false\n }, [activeEnd, startDate, endDate])\n\n // mode='view'(Phase B2 2026-05-05):純內容輸出 — 無 Field wrapper / 無 Calendar icon。\n if (resolvedMode === 'view') {\n const hasAny = !!(startIso || endIso)\n if (!hasAny) return <span className={cn(fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>\n return (\n <span className={cn('inline-flex items-center min-w-0', nakedCellRowModeAlign, className)}>\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— start / end 各自獨立截斷、各自量測 */}\n <TruncatedText className={cn(!startIso && 'text-fg-muted')}>\n {startIso ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[0]}\n </TruncatedText>\n <ArrowRight size={iconSize} className=\"shrink-0 text-fg-muted mx-2\" aria-hidden />\n <TruncatedText className={cn(!endIso && 'text-fg-muted')}>\n {endIso ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[1]}\n </TruncatedText>\n </span>\n )\n }\n\n // readonly / disabled view — plain wrapper,no popover\n if (!isEditable) {\n return (\n <div\n ref={ref}\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, width, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...props}\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— readonly/disabled 無編輯入口,tooltip 是唯一補救路徑 */}\n <TruncatedText className={cn('flex-1', !startIso && 'text-fg-muted', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {startIso ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[0]}\n </TruncatedText>\n <ArrowRight size={iconSize} className={cn('shrink-0 mx-2', resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted')} aria-hidden />\n <TruncatedText className={cn('flex-1', !endIso && 'text-fg-muted', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {endIso ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[1]}\n </TruncatedText>\n {/* 2026-06-26 類型身份 indicator:readonly 不顯示 / disabled 保留(fg-disabled)— 對齊單一 DatePicker(:488)+ SSOT;Range 無 naked-cell 變體故 gate = resolvedMode==='disabled' */}\n {resolvedMode === 'disabled' && (\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-disabled\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverAnchor asChild>\n <div\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n data-state={open ? 'open' : 'closed'}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', width, size, error }),\n 'cursor-text',\n className,\n )}\n {...props}\n >\n {/* 兩端截斷 tooltip:恆 wrap、未截斷 open={false} 靜默(truncated-text.spec.md\n always-wrap);TooltipTrigger asChild 只合併 handler / data-state 到 button,\n 零多餘 DOM → wrapper flex 佈局不變。內層 span = 量測點(見 startTruncationRef 註解)。 */}\n <Tooltip open={startTooltipActive ? undefined : false}>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n ref={startBtnRef}\n onClick={() => openWithActive('start')}\n data-active-end={open && activeEnd === 'start' ? 'true' : undefined}\n aria-label={resolvedPlaceholder[0]}\n aria-haspopup=\"dialog\"\n aria-expanded={open && activeEnd === 'start'}\n className={cn(\n bareInputStyles,\n // @focus-suppress C — button 不是 input 故無 caret;承擔者:外層欄位邊框 focus-within 轉 primary(field-wrapper.tsx:57);起訖兩顆靠下方主色底線區分(見下一行)\n 'truncate text-left cursor-pointer focus-visible:outline-none',\n // 起訖兩顆共用同一圈欄位邊框,邊框分不出焦點在哪一顆,所以作用端另有一條主色底線。\n // 這條線原本只在面板開著時畫(data-active-end 帶 open 條件),於是「面板關著用 Tab\n // 在起訖之間移動」時兩顆長得一模一樣。補上 focus-visible 這一半 —— 用的是本元件\n // 既有的同一條線,不是第二種指示。對照 Ant Design RangePicker 的 -active-bar\n // (components/date-picker/style/index.ts:`height: lineWidthBold, background: colorPrimary`)。\n 'data-[active-end=true]:underline focus-visible:underline decoration-primary underline-offset-4 decoration-2',\n !startIso && 'text-fg-muted',\n )}\n >\n <span ref={startTruncationRef} className=\"block truncate\">{startText}</span>\n </button>\n </TooltipTrigger>\n <TooltipContent>{startTooltipActive ? startText : null}</TooltipContent>\n </Tooltip>\n <ArrowRight size={iconSize} className=\"shrink-0 text-fg-muted mx-2\" aria-hidden />\n <Tooltip open={endTooltipActive ? undefined : false}>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n ref={endBtnRef}\n onClick={() => openWithActive('end')}\n data-active-end={open && activeEnd === 'end' ? 'true' : undefined}\n aria-label={resolvedPlaceholder[1]}\n aria-haspopup=\"dialog\"\n aria-expanded={open && activeEnd === 'end'}\n className={cn(\n bareInputStyles,\n // @focus-suppress C — button 不是 input 故無 caret;承擔者:外層欄位邊框 focus-within 轉 primary(field-wrapper.tsx:57);起訖兩顆靠下方主色底線區分(見下一行)\n 'truncate text-left cursor-pointer focus-visible:outline-none',\n // 起訖兩顆共用同一圈欄位邊框,邊框分不出焦點在哪一顆,所以作用端另有一條主色底線。\n // 這條線原本只在面板開著時畫(data-active-end 帶 open 條件),於是「面板關著用 Tab\n // 在起訖之間移動」時兩顆長得一模一樣。補上 focus-visible 這一半 —— 用的是本元件\n // 既有的同一條線,不是第二種指示。對照 Ant Design RangePicker 的 -active-bar\n // (components/date-picker/style/index.ts:`height: lineWidthBold, background: colorPrimary`)。\n 'data-[active-end=true]:underline focus-visible:underline decoration-primary underline-offset-4 decoration-2',\n !endIso && 'text-fg-muted',\n )}\n >\n <span ref={endTruncationRef} className=\"block truncate\">{endText}</span>\n </button>\n </TooltipTrigger>\n <TooltipContent>{endTooltipActive ? endText : null}</TooltipContent>\n </Tooltip>\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除日期區間', // i18n-allow: DS default inline-action label\n onClick: handleClearRange,\n }}\n />\n )}\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n </PopoverAnchor>\n <PopoverContent\n className=\"w-auto p-0\"\n align=\"start\"\n aria-label=\"日期區間選擇\" // i18n-allow: DS default dialog label\n // 2026-07-05 D4 focus 回復(機制詳 :758 comment):outside-click 標記不搶焦(鏡射\n // Radix non-modal 自家 hasInteractedOutside guard);其餘關閉路徑(Esc / 確定 /\n // 選完自動關)preventDefault 掉內建 triggerRef.focus()(Anchor-only 恆 null no-op)\n // 後手動回焦 active 端 button — 對齊 Ant RangePicker 關閉後焦點回 active 端輸入格。\n onInteractOutside={() => { hasInteractedOutsideRef.current = true }}\n onCloseAutoFocus={(e) => {\n e.preventDefault()\n if (!hasInteractedOutsideRef.current) {\n ;(activeEnd === 'start' ? startBtnRef : endBtnRef).current?.focus()\n }\n hasInteractedOutsideRef.current = false\n }}\n >\n {/* a11y(2026-07-14 dim-10 修):accessible name 收斂到外層 PopoverContent(Radix 自帶\n role=\"dialog\")— 原內層第二個 role=\"dialog\" 移除,避免 nested dialog 且外層 dialog 無名。\n 2026-05-06 v9.1 M25 chain — 同 single DatePicker 修法,viewport 壓縮 calendar 內滾 + footer 永遠 in-view */}\n <div className=\"flex flex-col flex-1 min-h-0\">\n <ScrollArea className=\"flex-1 min-h-0\">\n <CalendarTimeContainer\n showTime={showTime}\n showSeconds={showSeconds}\n calendar={\n <DateGrid\n // mode='single' + manual modifiers(canonical 2026-05-02 v3):\n // 不用 RDP 內建 mode='range'(它的 click 配對邏輯跟我們的 activeEnd 衝突,\n // 造成「點一次沒反應 / 要點兩次」bug)。改自管 modifiers 控視覺。\n // showTime Range:rangeModifiers 為空(不顯示 range track,對齊 Ant)\n mode=\"single\"\n selected={activeDate}\n onSelect={(date) => {\n if (!date) return\n if (isOutOfRangeOrder(date)) return // 防護:disable 邏輯內 click 已被 RDP 擋,但雙保險\n const preservedTime = isoToTimeParts(activeEnd === 'start' ? draft[0] : draft[1]) ?? activeTime\n const nextIso = showTime\n ? combineDateAndTime(date, preservedTime)\n : dateToIso(date)\n const nextDraft: [string | null, string | null] = activeEnd === 'start'\n ? [nextIso, draft[1]]\n : [draft[0], nextIso]\n commitRange(nextDraft)\n // Auto-advance / close logic:\n if (!showTime) {\n // date-only Range:選完 start 自動切 end;兩端皆填 + 不需確認 → 關閉\n if (activeEnd === 'start') {\n setActiveEnd('end')\n if (!needConfirm && nextDraft[0] && nextDraft[1]) setOpen(false)\n } else if (!needConfirm && nextDraft[0] && nextDraft[1]) {\n setOpen(false)\n }\n }\n // showTime Range:不 auto-advance,讓 user 編 time 後手動按確定 commit\n // (對齊 Ant 序列流程 — 確定 button 切 activeEnd)\n }}\n // showTime Range:不渲 range visualization(對齊 Ant — 整個 popup 等同 single\n // DateTimePicker,沒 range 視覺概念);date-only Range 才顯示\n modifiers={showTime ? {} : rangeModifiers}\n modifiersClassNames={{\n // ── Range visual canonical(2026-05-03 v8 stadium pattern)──\n // v5 修「白色破圖」用 pseudo 蓋全 cell 矩形,但新副作用:button 圓比矩形小,\n // 4 corner triangle 區域 grey 凸出圓外(user 2026-05-03 抓到「凸出去」)。\n // v8 對齊 Ant `cell-range-start::before { border-radius: 9999px 0 0 9999px }`:\n // rangeStart pseudo 加 `rounded-l-full` → pseudo 變「左半圓 + 右矩形」stadium\n // 左半圓 EXACTLY OVERLAY button 圓的左半弧(同 center 同 radius 14)→ 無縫\n // 右側矩形 bridge 2px to middle → 跟 middle pseudo 連續\n // Cell 的 top-left + bottom-left corner triangle:pseudo 不蓋 + button 不蓋 →\n // popover white 顯露(乾淨 breathing)\n rangeStart: cn(\n // 2026-08-11 修偏移:!bg-primary 連 hover 也壓死 → 端點 hover 零回饋,違反\n // date-grid.spec.md:119「選中日 hover 升階 primary→primary-hover」(端點可再點\n // 重選範圍 = 可取消家族,依 user 原則該有回饋)。補顯式 hover 升階(同 !important,\n // hover 變體更特定故 hover 時勝出;ring-0 壓制照舊,spec:118)。\n '[&>button]:!bg-primary [&>button]:hover:!bg-primary-hover [&>button]:!text-on-emphasis [&>button]:hover:!ring-0',\n \"before:content-[''] before:absolute before:inset-y-0\",\n 'before:left-0 before:-right-[2px]',\n 'before:bg-neutral-selected before:pointer-events-none',\n 'before:rounded-l-full', // ← stadium 左半圓 matches button 圓的左半弧\n ),\n rangeEnd: cn(\n // 2026-08-11:同 rangeStart — 補 hover 升階(spec:119),鏡像。\n '[&>button]:!bg-primary [&>button]:hover:!bg-primary-hover [&>button]:!text-on-emphasis [&>button]:hover:!ring-0',\n \"before:content-[''] before:absolute before:inset-y-0\",\n 'before:-left-[2px] before:right-0',\n 'before:bg-neutral-selected before:pointer-events-none',\n 'before:rounded-r-full', // ← 鏡像\n ),\n rangeMiddle: cn(\n \"before:content-[''] before:absolute before:inset-y-0 before:-inset-x-[2px]\",\n 'before:bg-neutral-selected before:pointer-events-none',\n '[&>button]:!bg-transparent [&>button]:!text-foreground',\n ),\n }}\n // Cell disable:防 user 點下違反順序的日期(對齊 Ant useRangeDisabledDate)\n disabled={isOutOfRangeOrder}\n // showTime → 1 cal(對齊 Ant `multiplePanel=false`);date-only → 2 cal(`multiplePanel=true`)\n numberOfMonths={showTime ? 1 : 2}\n defaultMonth={activeDate ?? startDate ?? endDate ?? undefined}\n autoFocus\n />\n }\n timePanel={\n <TimePickerSidePanel\n value={activeTime}\n onChange={(time) => {\n const target = activeDate ?? new Date()\n setActive(combineDateAndTime(target, time))\n }}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n }\n />\n </ScrollArea>\n {(showTime || needConfirm) && (\n // Footer 消費 SurfaceFooter SSOT(border-t + py-tight;左右內距不覆寫,理由見上方 764 行同款註解\n // 與 patterns/overlay-surface/overlay-surface.spec.md「要對齊誰」)。\n // showTime Range 無「此刻」(對齊 Ant `showNow={multiple ? false : showNow}`)→ 只有 確定 走 justify-end。\n // date-only Range needConfirm:左 此刻(mr-auto)+ 右 確定。\n <SurfaceFooter className=\"px-[var(--item-px,var(--field-px))]\">\n {!showTime && (\n <Button variant=\"tertiary\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">此刻</Button>\n )}\n {needConfirm ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={handleConfirm}\n // showTime Range serial flow:start mode 只需 start filled;end mode 兩端皆 filled\n disabled={\n showTime\n ? (activeEnd === 'start' ? !draft[0] : !draft[0] || !draft[1])\n : !draft[0] || !draft[1]\n }\n >\n 確定\n </Button>\n ) : (\n <Button variant=\"tertiary\" size=\"sm\" onClick={() => setOpen(false)}>關閉</Button>\n )}\n </SurfaceFooter>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nDatePickerRange.displayName = 'DatePickerRange'\n\n// Attach Range as namespace:consumer 用 <DatePicker.Range ...>(Ant-style)\n// 走 Object.assign 確保 TS 型別帶上 Range 屬性,而非只做 runtime 附掛\nconst DatePickerWithRange = Object.assign(DatePicker, { Range: DatePickerRange })\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const datePickerMeta = {\n component: 'DatePicker',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = range 帶持續選中(before:bg-neutral-selected,本檔自有);'active' 移除 — 全檔無按壓\n // 視覺(data-active-end 是定位 attr 非按壓 token;2026-07-07 詞彙統一 DS-wide 交叉掃補修)。\n states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport {\n DatePickerWithRange as DatePicker,\n DatePickerRange,\n formatDate,\n}\n"],"names":["CalendarIcon"],"mappings":"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"}
1
+ {"version":3,"file":"date-picker.js","sources":["../../../src/components/DatePicker/date-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\n// code-quality-allow: file-size — foundational composite(DatePicker single + Range + showTime + format/ISO helpers + TimePickerSidePanel sub-components),拆 sub-file 會 (a) 增 cross-file context binding 複雜度 (b) M21 過度抽象(每 helper 1 consumer)。等 inline filter UI 真接入第 2 consumer 再拆。\nimport * as React from 'react'\nimport { X, Calendar as CalendarIcon, ArrowRight } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'\nimport { ScrollArea } from '@/design-system/components/ScrollArea/scroll-area'\nimport { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from '@/design-system/components/Popover/popover'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'\nimport { useTruncated } from '@/design-system/hooks/use-truncated'\nimport { TruncatedText } from '@/design-system/patterns/element-anatomy/truncated-text'\nimport { DateGrid, EMPHASIS_FOCUS_RING_CLASSNAME, RANGE_PREVIEW_ARMED_CLASSNAME, RANGE_PREVIEW_CLASSNAMES, RANGE_TRACK_CLASSNAMES } from '@/design-system/components/DateGrid/date-grid'\nimport { computeRangePreview, rangePreviewModifiers } from './range-preview'\nimport { Button } from '@/design-system/components/Button/button'\nimport { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport {\n TimeColumns,\n isoToTimeParts,\n timePartsToString,\n type TimeParts,\n type TimeStep,\n} from '@/design-system/components/TimePicker/time-columns'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Format ──────────────────────────────────────────────────────────────────\n\nexport interface DateFormatOptions {\n /** Intl.DateTimeFormat options(預設 { year: 'numeric', month: '2-digit', day: '2-digit' }) */\n formatOptions?: Intl.DateTimeFormatOptions\n /** locale(預設 'en-US') */\n locale?: string\n}\n\n/**\n * Default format:**YYYY/MM/DD**(對齊 Ant Design 順序,year-first ISO-like)。\n * 棄 `en-US` `MM/DD/YYYY`(month-first 美式)— 美式順序在 international DS 反直覺\n * (跟 ISO date 視覺對不上,跟 sort 順序也對不上)。Ant / Material X / Apple HIG\n * 一致 year-first。Consumer 想自訂可傳 `formatOptions` + `locale`。\n */\n// 2026-07-06 D3 perf:formatOptions/locale 顯式傳入時走 Intl.DateTimeFormat(貴 constructor)。\n// DataTable date 欄轉發此 config → 每 cell × 每次表格 re-render 重建。module-level cache 重用實例\n// (無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。\nconst dtfCache = new Map<string, Intl.DateTimeFormat>()\nfunction getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + '|' + JSON.stringify(options)\n let fmt = dtfCache.get(key)\n if (!fmt) {\n fmt = new Intl.DateTimeFormat(locale, options)\n dtfCache.set(key, fmt)\n }\n return fmt\n}\n\nfunction formatDate(\n value: string | number | Date,\n options: DateFormatOptions = {},\n): string {\n // TZ-safe:date-only / datetime ISO string 走 isoToDate(local-time 語意,new Date(y,m-1,d))\n // 而非 new Date('YYYY-MM-DD')(ECMAScript 以 UTC 解析 → 負時區 off-by-one,如 LA 顯前一天)。\n // 非 ISO string(RFC 'Mar 12 2025' 等)fallback new Date();number timestamp 直接 new Date()。\n const date =\n value instanceof Date\n ? value\n : typeof value === 'string'\n ? (isoToDate(value) ?? new Date(value))\n : new Date(value)\n if (Number.isNaN(date.getTime())) return String(value)\n // 若 consumer 顯式傳 formatOptions / locale → 走 Intl.DateTimeFormat\n if (options.formatOptions || options.locale) {\n return getDateTimeFormatter(options.locale ?? 'en-US', options.formatOptions ?? { year: 'numeric', month: '2-digit', day: '2-digit' }).format(date)\n }\n // 預設:YYYY/MM/DD(直接組,locale-independent + 視覺穩定)\n const y = date.getFullYear()\n const m = String(date.getMonth() + 1).padStart(2, '0')\n const d = String(date.getDate()).padStart(2, '0')\n return `${y}/${m}/${d}`\n}\n\n/** 顯示用:date 或 datetime,根據 showTime / showSeconds 切換 */\nfunction formatDateOrDateTime(\n iso: string | null | undefined,\n showTime: boolean,\n showSeconds: boolean,\n options: DateFormatOptions = {},\n): string {\n if (!iso) return ''\n const dateText = formatDate(iso, options)\n if (!showTime) return dateText\n const time = isoToTimeParts(iso)\n if (!time) return dateText\n return `${dateText} ${timePartsToString(time, showSeconds)}`\n}\n\n// ── ISO <-> Date conversion ─────────────────────────────────────────────────\n// date-only:'YYYY-MM-DD'(local-time 語意,不帶時區)\n// datetime :'YYYY-MM-DDTHH:MM:SS'(同 local-time 語意)\n\nfunction isoToDate(iso: string | null | undefined): Date | undefined {\n if (!iso) return undefined\n const datePart = iso.slice(0, 10)\n const [y, m, d] = datePart.split('-').map(Number)\n if (!y || !m || !d) return undefined\n const date = new Date(y, m - 1, d)\n const time = isoToTimeParts(iso)\n if (time) {\n date.setHours(time.hours, time.minutes, time.seconds)\n }\n return date\n}\n\n/**\n * Issue 10 typed input parser(2026-05-10):接 user 自由輸入字串,parse 出 ISO date(YYYY-MM-DD)\n * 或 ISO datetime(YYYY-MM-DDTHH:MM[:SS])。\n *\n * 支援 format(per Material X DatePicker / Ant DatePicker typed input idiom):\n * - `YYYY-MM-DD` / `YYYY/MM/DD` / `YYYY.MM.DD`(ISO + 慣例 separator,明確 parse branch)\n * - `YYYY-MM-DD HH:MM[:SS]` / `YYYY-MM-DDTHH:MM[:SS]`(datetime,showTime 才接)\n * - native `Date.parse()` fallback(handle 'Mar 12 2025' 等英文 RFC;US `MM/DD/YYYY` 亦由此判讀,ambiguous)\n * ⚠️ 不支援 EU `DD/MM/YYYY`:無專屬 parser branch;native Date.parse 把 slash 日期當 US MM/DD,\n * `31/12/2026`(月 > 12)直接 Invalid。v1 known-gap,需 EU 請走 formatOptions/locale 或另加 parser branch。\n *\n * Return:`{ iso, valid }`。Invalid → `valid=false` + `iso=null`,UI 顯 aria-invalid。\n * Partial input(打到一半)→ valid=false 但不顯 error UI(consumer 用 onBlur / Enter 才檢驗)。\n *\n * 對齊 Material X-DatePicker `formats` parser + Ant DatePicker `format` array + dayjs.parse。\n */\nfunction parseDateInput(input: string, opts: { allowTime: boolean }): { iso: string | null; valid: boolean } {\n const trimmed = input.trim()\n if (trimmed === '') return { iso: null, valid: true } // empty = no value, OK\n // 1. ISO date YYYY-MM-DD or with separators / . /(可選 time YYYY-MM-DD[T| ]HH:MM[:SS])\n const isoDateMatch = trimmed.match(/^(\\d{4})[-/.](\\d{1,2})[-/.](\\d{1,2})(?:[T\\s](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?)?$/)\n if (isoDateMatch) {\n const [, y, m, d, hh, mm, ss] = isoDateMatch\n const date = new Date(Number(y), Number(m) - 1, Number(d), Number(hh ?? 0), Number(mm ?? 0), Number(ss ?? 0))\n if (isNaN(date.getTime()) || date.getMonth() !== Number(m) - 1) return { iso: null, valid: false }\n // time 分量越界校驗:JS Date 會 silently normalize(25:99:99 → 隔日某時,getTime 仍 valid、\n // getMonth 未必變),但回傳 iso 用的是原始越界字串 → 需在此擋掉 hh>23 / mm>59 / ss>59。\n if (hh != null && (Number(hh) > 23 || Number(mm) > 59 || Number(ss ?? 0) > 59)) return { iso: null, valid: false }\n const datePart = dateToIso(date)\n if (hh != null && opts.allowTime) {\n const h2 = String(Number(hh)).padStart(2, '0')\n const m2 = String(Number(mm)).padStart(2, '0')\n const s2 = String(Number(ss ?? 0)).padStart(2, '0')\n return { iso: `${datePart}T${h2}:${m2}:${s2}`, valid: true }\n }\n return { iso: datePart, valid: true }\n }\n // 2. US / EU locale MM/DD/YYYY or DD/MM/YYYY — ambiguous;v1 fallback Date.parse 並接受結果\n // 3. native Date.parse fallback(RFC-style 'Mar 12 2025')\n const parsed = new Date(trimmed)\n if (!isNaN(parsed.getTime())) {\n const datePart = dateToIso(parsed)\n if (opts.allowTime && (trimmed.includes(':') || trimmed.includes('T'))) {\n const h2 = String(parsed.getHours()).padStart(2, '0')\n const m2 = String(parsed.getMinutes()).padStart(2, '0')\n const s2 = String(parsed.getSeconds()).padStart(2, '0')\n return { iso: `${datePart}T${h2}:${m2}:${s2}`, valid: true }\n }\n return { iso: datePart, valid: true }\n }\n return { iso: null, valid: false }\n}\n\nfunction dateToIso(date: Date | undefined): string {\n if (!date) return ''\n const y = date.getFullYear()\n const m = String(date.getMonth() + 1).padStart(2, '0')\n const d = String(date.getDate()).padStart(2, '0')\n return `${y}-${m}-${d}`\n}\n\nfunction combineDateAndTime(date: Date, time: TimeParts): string {\n const datePart = dateToIso(date)\n const hh = String(time.hours).padStart(2, '0')\n const mi = String(time.minutes).padStart(2, '0')\n const ss = String(time.seconds).padStart(2, '0')\n return `${datePart}T${hh}:${mi}:${ss}`\n}\n\nfunction nowIsoDateTime(): string {\n const d = new Date()\n return combineDateAndTime(d, {\n hours: d.getHours(),\n minutes: d.getMinutes(),\n seconds: d.getSeconds(),\n })\n}\n\nfunction addDays(date: Date, n: number): Date {\n const next = new Date(date)\n next.setDate(next.getDate() + n)\n return next\n}\n\n// ── TimePickerSidePanel ────────────────────────────────────────────────\n//\n// DatePicker showTime / Range showTime 共用的右側時間 panel(canonical 2026-05-03 v8)。\n//\n// ── Caption row alignment canonical(永遠跟 calendar 年月對齊)──\n// 結構必須符合 DateGrid month_caption 同樣的 pt-3 + h-field-xs + mb-3 規格,讓 title\n// 跟 calendar 「April 2026」字 baseline 在同一 Y 座標(垂直對齊)。\n// Y 座標推導:\n// - panel root 的上內距**讀跟 DateGrid 根一樣的運算式**,所以「對齊」是綁定的、不是靠人記得同步\n// - h-field-xs = 24px header,title 純 flex items-center justify-center → 真正水平+垂直置中\n// - mb-3 = 12px gap 對齊 DateGrid month_caption mb-3\n// → title text center Y = 12 + 12 = 24px(from CalendarTimeContainer top)\n// → calendar caption text center Y = 12(p-3 top)+ 12(caption row half)= 24px ✓ 同一 Y\n// 2026-09-18:原本兩邊各寫死 `p-3` / `pt-3`(都是字面 12),靠這行 ⚠️ 註解提醒人「改一邊要記得改另一邊」。\n// DateGrid 的根已改讀 `var(--item-px,var(--field-px))`,這裡跟著讀同一個運算式 ——\n// **任何容器覆寫 `--item-px`,兩邊會一起動**,不再有「其中一邊忘了跟」的空間(先前那種靠註解的雙鎖是紙老虎)。\n// 下方 `mb-3`(caption → 星期列的 12px)兩邊仍是字面值,但它不受根內距影響,維持原樣。\n//\n// ── Header divider canonical(無 border-b)──\n// Header 下方無 divider,對齊 DateGrid month_caption(無 border-b,只 mb-3 gap)。\n// DS internal canonical(M23)優先於 Ant time-picker header divider 慣例 — 兩 panel\n// 同層級 caption 視覺對稱,引入 divider 會破對稱。\n//\n// ── Bottom padding canonical(0)──\n// Root 用 pt-3 而非 py-3:bottom = 0,讓 columns 連續延伸到 SurfaceFooter border-t。\n// Ant / Material time picker idiom — time list 視覺感「continuous scroll」延伸到 footer\n// divider,bottom padding 12px 反而讓 list 看起來「截斷」。\n// 此處與 DateGrid p-3(bottom 12)有意 asymmetric:Calendar cells 不該撞 footer divider\n// (cells 是離散 grid),time list 是 scroll list 撞 divider 反而合理。\n\ninterface TimePickerSidePanelProps {\n value?: TimeParts\n onChange: (next: TimeParts) => void\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n}\n\nfunction TimePickerSidePanel({\n value,\n onChange,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n className,\n}: TimePickerSidePanelProps & { className?: string }) {\n // Dynamic header text — 顯示當前選擇的 HH:MM(對齊 user Q4 + Ant idiom)\n const headerText = value\n ? timePartsToString(value, showSeconds)\n : (showSeconds ? '--:--:--' : '--:--')\n\n return (\n <div className={cn('flex flex-col h-full pt-[var(--item-px,var(--field-px))]', className)}>\n {/* Header 純結構:h-field-xs (24px) + flex 水平+垂直置中 + mb-3 (12px gap) */}\n <div className=\"h-field-xs flex items-center justify-center mb-3\">\n <span className=\"text-body font-medium tabular-nums\">{headerText}</span>\n </div>\n {/* Columns:flex-1 填滿剩餘 height,無 horizontal padding(填滿容器寬度) */}\n <div className=\"flex-1 min-h-0 flex\">\n <TimeColumns\n value={value}\n onChange={onChange}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n </div>\n </div>\n )\n}\n\n/**\n * showTime panel container — 包 DateGrid + TimePicker side panel,DateGrid 主導 row 高度,\n * TimePicker absolute 撐滿同高,不影響 layout。Spacer div 留 layout 寬度給 absolute panel。\n */\nconst TIME_PANEL_WIDTH = (showSeconds: boolean) => showSeconds ? 'w-60' : 'w-40'\n\ninterface CalendarTimeContainerProps {\n showTime: boolean\n showSeconds: boolean\n calendar: React.ReactNode\n timePanel?: React.ReactNode\n}\n\nfunction CalendarTimeContainer({ showTime, showSeconds, calendar, timePanel }: CalendarTimeContainerProps) {\n if (!showTime) return <>{calendar}</>\n return (\n <div className=\"relative\">\n <div className=\"flex flex-row\">\n {calendar}\n {/* Spacer 佔 layout 寬度給 absolute TimePicker;border-l 在這層,不在 absolute 層\n (避免 stacking + border 雙繪) */}\n <div className={cn('shrink-0 border-l border-divider', TIME_PANEL_WIDTH(showSeconds))} />\n </div>\n {/* TimePicker absolute 撐滿 DateGrid 高度(top-0 bottom-0),right-0 對齊 spacer */}\n <div className={cn('absolute top-0 right-0 bottom-0', TIME_PANEL_WIDTH(showSeconds))}>\n {timePanel}\n </div>\n </div>\n )\n}\n\n// ── DatePicker(single)──────────────────────────────────────────────────\n\nexport interface DatePickerProps\n extends DateFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'value' | 'onChange' | 'placeholder' | 'defaultValue'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮(value 寬 + slot 寬 + gap + 內 padding);\n * chrome 與互動不變。SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */\n width?: FieldWidth\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** ISO date(YYYY-MM-DD)或 ISO datetime(YYYY-MM-DDTHH:MM:SS,當 showTime=true) */\n value?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n className?: string\n disabled?: boolean\n /** 允許清空已選值 */\n clearable?: boolean\n /** 啟用時間欄位(時 / 分 [/ 秒]),Ant idiom — value 變 ISO datetime */\n showTime?: boolean\n /** showTime 時是否顯示秒 */\n showSeconds?: boolean\n /** showTime 分鐘步進(會議常用 15) */\n minuteStep?: TimeStep\n /** showTime 秒鐘步進 */\n secondStep?: TimeStep\n /**\n * 是否需 OK 確認才提交,預設 showTime=true 時為 true(對齊 Ant DatePicker showTime)\n * — datetime picker user 習慣編完才 commit,避免 calendar 點到就關。\n */\n needConfirm?: boolean\n /**\n * View 態是否渲 Calendar icon + Field naked wrapper(D-path opt-in,2026-05-08)\n * — DataTable cell view↔edit 像素級對齊用。預設 false(裸 span,backward compat)。\n * 設 true 時 view 走 fieldWrapperStyles(naked variant)+ ItemSuffix CalendarIcon,\n * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。\n */\n showDisplayEndIcon?: boolean\n /** Initial open state(uncontrolled)— DataTable cell-as-input 1-step open canonical */\n defaultOpen?: boolean\n /** open state 變更 callback。DataTable cell-as-input 用:open=false → cell exit edit */\n onOpenChange?: (open: boolean) => void\n /**\n * Issue 10 typed input(2026-05-10):trigger 內渲 real `<input>` 接 user 鍵盤輸入,\n * 同時保持 Calendar icon trigger 開啟 calendar 選擇(對齊 Material X DatePicker / Ant\n * DatePicker / Notion typed-date idiom)。\n *\n * 預設 false(backward-compat)— trigger 仍是 `<div role=\"combobox\">` + `<span>` 文字。\n *\n * Behavior(opt-in `typeable=true`):\n * - `<input>` 取代 `<span>` displayValue,user 可直接打字\n * - Partial input(打到一半,如 \"2025-\")allow,**不**即時驗證\n * - `Enter` / `Blur` → `parseDateInput` 解析 → 合法 commit `onChange`;不合法 set\n * aria-invalid + keep draft visible(user 可繼續修)\n * - `Esc` → reset draft 回 committed value\n * - IME composition 期間不觸發驗證(中日韓輸入法 onCompositionStart/End 攔截)\n * - Calendar pick → 同步 input draft + commit(走原 path)\n * - **點欄位任何地方都開日曆,焦點留在輸入框可繼續打字**(2026-09-07;Ant 官方文件\n * 「By clicking the input box, you can select a date from a popup calendar」+ inputReadOnly:false)\n * - **鍵盤 ArrowDown / Alt+ArrowDown 開啟時焦點進日曆** —— W3C APG date-picker combobox 逐字\n * 「opened by activating the choose date button or by moving keyboard focus to the combobox and\n * pressing Down Arrow or Alt + Down Arrow」;焦點不進去就走不了日期格,那條不能為了打字犧牲\n *\n * **v1 limits**:\n * - format detection ISO YYYY-MM-DD / YYYY/MM/DD / Date.parse fallback(`parseDateInput`)\n * - US `MM/DD/YYYY` vs EU `DD/MM/YYYY` ambiguous → fallback native parser\n * - 未支援 locale-aware format prop(v2 加 `dateFormat?: string`)\n *\n * 對齊 Material X-DatePicker `format` prop / Ant DatePicker `format` array / Notion typed-input。\n */\n typeable?: boolean\n}\n\n// Trigger uses `<div role=\"combobox\" tabIndex={...}>` instead of `<button>` —\n// 對齊 Combobox / Select / TimePicker 同 pattern,避免 ItemInlineAction(內部 button)\n// 構成 nested-interactive(axe serious)。Radix PopoverTrigger 只 compose onClick(非\n// native button → 無 Enter/Space→click),Enter/Space 開 popover 由本檔自建 onKeyDown 補\n// (對齊 select.tsx desktop trigger canonical);typeable 模式交由內層 <input> 自管鍵盤。\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n mode,\n variant: variantProp,\n width,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n formatOptions,\n locale,\n showTime = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n needConfirm: needConfirmProp,\n showDisplayEndIcon = false,\n defaultOpen = false,\n onOpenChange,\n typeable = false,\n id: idProp,\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledByProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix:<Field size>/cell surface-size 對 DatePicker 生效\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"view\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const needConfirm = needConfirmProp ?? showTime // datetime 預設需確認\n const [open, setOpenState] = React.useState(defaultOpen)\n const setOpen = React.useCallback((next: boolean) => { setOpenState(next); onOpenChange?.(next) }, [onOpenChange])\n // Radix Popover 的預設 contentId 在 content 未掛載時仍會留在 trigger。\n // DatePicker 改用穩定 ID,且只在 dialog 已掛載(open)時輸出 aria-controls,\n // 避免 closed state 的懸空 IDREF;typeable 模式則把關聯放到真正的 combobox input。\n const popupId = React.useId()\n const [draft, setDraft] = React.useState<string | null>(value ?? null)\n const resolvedPlaceholder = placeholder ?? (showTime ? 'YYYY/MM/DD HH:MM' : 'YYYY/MM/DD')\n // a11y:role=\"combobox\" 必須有 accessible name(aria-label / labelledby / fieldCtx label)\n const accessibleName = ariaLabelProp ?? (ariaLabelledByProp ? undefined : (fieldCtx?.id ? undefined : resolvedPlaceholder))\n\n // Sync draft from value:closed 期間恆跟隨 + open false→true 同步(避免 popover 開啟期間\n // value 改變 clobber user 的編輯)。\n // 2026-07-14 audit Dim 26(V4 state shadow):needConfirm trigger 讀 draft,原「只在 false→true\n // 同步」使 controlled value 於關閉期間更新後,trigger 仍顯示舊值直到再次開啟 —— 改為\n // 只要 open=false 就同步(與 range picker 分支 L83x 同款 canonical)。\n const lastOpenRef = React.useRef(open)\n React.useEffect(() => {\n if (!open || !lastOpenRef.current) setDraft(value ?? null)\n lastOpenRef.current = open\n }, [open, value])\n\n // Display value canonical(2026-05-02 fix):\n // needConfirm=true(showTime 預設)→ trigger 讀 draft,user 點 calendar 看到 input 即時更新\n // needConfirm=false → trigger 讀 value(committed,符合非確認流程)\n const displayValue = needConfirm ? draft : (value ?? null)\n const displayDate = React.useMemo(() => isoToDate(displayValue), [displayValue])\n const draftDate = React.useMemo(() => isoToDate(draft), [draft])\n const draftTime = isoToTimeParts(draft) ?? { hours: 0, minutes: 0, seconds: 0 }\n const showClear = clearable && (needConfirm ? draft : value) && isEditable\n\n const displayCommitted = formatDateOrDateTime(value, showTime, showSeconds, { formatOptions, locale })\n const displayLive = formatDateOrDateTime(displayValue, showTime, showSeconds, { formatOptions, locale })\n\n // Issue 10 typed input(2026-05-10):draft string + invalid flag + IME composition guard。\n const [inputDraft, setInputDraft] = React.useState<string>(displayLive)\n const [inputInvalid, setInputInvalid] = React.useState(false)\n const composingRef = React.useRef(false)\n // 2026-09-07:記住浮層是**怎麼被打開的**。\n // 指標開啟 → 焦點留在輸入框(使用者正在用滑鼠,可能還想打字;日曆用點的即可)\n // 鍵盤開啟 → 焦點進日曆(否則鍵盤使用者 navigate 不了日期格,那是 W3C APG\n // date-picker combobox 明文要求「focus moves into the dialog」的理由)\n // 兩條路各自成立,不是二選一 —— 這也是 user 2026-09-07 問「同時出現不是更好用嗎」的正解:\n // 好用的是**滑鼠那條**,鍵盤那條不能為了它犧牲可操作性。\n const openedByPointerRef = React.useRef(false)\n const typedInputRef = React.useRef<HTMLInputElement | null>(null)\n // 指標開啟時把焦點送回輸入框。\n // **不用 `onOpenAutoFocus` 攔截**:實測那個事件在本組合下根本沒被派發\n //(探針顯示 handler 從未執行,焦點卻仍被移到日曆內的按鈕),追 Radix 內部只會愈追愈深。\n // 這裡改成「開啟後下一幀把焦點要回來」—— 不依賴任何第三方內部行為,而且驗得到。\n React.useEffect(() => {\n if (!open || !typeable || !openedByPointerRef.current) return\n const id = requestAnimationFrame(() => typedInputRef.current?.focus())\n return () => cancelAnimationFrame(id)\n }, [open, typeable])\n // Sync input draft from committed displayLive(value change from outside)— 不要在 user\n // 打字期間覆寫。透過 ref 比較:committed vs current draft 是否從相同 source。\n const lastDisplayLiveRef = React.useRef(displayLive)\n React.useEffect(() => {\n if (lastDisplayLiveRef.current !== displayLive) {\n setInputDraft(displayLive)\n setInputInvalid(false)\n lastDisplayLiveRef.current = displayLive\n }\n }, [displayLive])\n const handleInputCommit = React.useCallback((raw: string) => {\n const { iso, valid } = parseDateInput(raw, { allowTime: showTime })\n if (!valid) { setInputInvalid(true); return }\n setInputInvalid(false)\n if (iso === null) {\n // empty input = clear value\n onChange?.('')\n setDraft(null)\n } else {\n onChange?.(iso)\n setDraft(iso)\n }\n }, [onChange, showTime])\n\n // 截斷 tooltip(tooltip.spec.md:32 截斷 → 顯完整內容,僅實際截斷時):非 typeable edit\n // trigger 的值 span(bareInputStyles + truncate)是截斷元素;trigger = 整個 field wrapper\n // (interactive host,hover 直達)、量測內層值 span — useTruncated 自組 pattern\n // (truncated-text.spec.md「trigger 需自控」指定解;範本 inline-edit.tsx:251)。\n // typeable 走 <input>(可捲動編輯,無截斷補救需求);popover 開啟時靜默(open 是 hover 之外的 reveal 路徑)。\n // ⚠️ hook 必在下方 view / readonly early return 之前(React #310 hook-count 鐵律,同 select.tsx 教訓)。\n const { ref: triggerTruncationRef, isTruncated: triggerTruncated } = useTruncated<HTMLSpanElement>({\n deps: [!typeable && displayValue ? displayLive : ''],\n })\n const triggerTooltipActive = !typeable && !!displayValue && triggerTruncated && !open\n\n // mode='view'(Phase B2 2026-05-05):純內容輸出 — 對齊原 DatePickerDisplay sub-component(retired)。\n // Default(showDisplayEndIcon=false):無 Field wrapper / 無 Calendar icon — backward compat 裸 span。\n // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix Calendar,\n // 與 edit 同結構消除 cell view↔edit 像素偏移(Layer-B padding mismatch)。\n if (resolvedMode === 'view') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) view typography canonical):bare span 套\n // `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>\n // 截斷必附 tooltip(tooltip.spec.md:32)— 改消費 truncated-text primitive(同 input.tsx:196 同型修)\n return <TruncatedText className={cn(fieldDisplayTextClass(size), className)}>{displayCommitted}</TruncatedText>\n }\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'view', variant, width, size }), className)}\n data-field-mode=\"view\"\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32;data-table.spec.md:183 view 態元件自管 truncation) */}\n <TruncatedText className={cn(bareInputStyles, 'flex-1 min-w-0', !value && fieldEmptyColorClass(resolvedMode))}>\n {value ? displayCommitted : emptyDisplay}\n </TruncatedText>\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n )\n }\n\n // readonly / disabled\n if (!isEditable) {\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, width, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...(props as React.HTMLAttributes<HTMLDivElement>)}\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— readonly/disabled 無編輯入口,tooltip 是唯一補救路徑(同 Range readonly 分支同型) */}\n <TruncatedText className={cn('flex-1', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {value\n ? displayCommitted\n : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>\n }\n </TruncatedText>\n {/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示(純值、不可開) / disabled 保留(fg-disabled,對齊原生 <select disabled>);\n naked cell 依 showDisplayEndIcon=isEditable(維持 2026-05-10 cell canonical「非可編欄不顯」)*/}\n {(variant === 'naked' ? showDisplayEndIcon : resolvedMode === 'disabled') && (\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className={resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'} aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n const triggerText = displayValue\n ? displayLive\n : <span className=\"text-fg-muted\">{resolvedPlaceholder}</span>\n\n const commitDraft = (next: string | null) => {\n if (needConfirm) setDraft(next)\n else onChange?.(next ?? '')\n }\n const handleConfirm = () => { onChange?.(draft ?? ''); setOpen(false) }\n const handleNow = () => {\n const now = showTime ? nowIsoDateTime() : dateToIso(new Date())\n commitDraft(now)\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n {/* 截斷 tooltip:恆 wrap(truncated-text.spec.md always-wrap),未截斷或 popover 開啟時\n open={false} 靜默。TooltipTrigger 放最內側(緊貼 div):PopoverTrigger 注入的\n data-state(Field open 邊框樣式依賴)在 TooltipTrigger 內以 triggerProps 後 spread\n 勝出;反序會被 tooltip 的 data-state 蓋掉。 */}\n <Tooltip open={triggerTooltipActive ? undefined : false}>\n <PopoverTrigger asChild type={undefined}>\n <TooltipTrigger asChild>\n <div\n ref={ref}\n // a11y(2026-07-14 dim-10 修):typeable 模式 combobox 語意(name / state / 鍵盤)\n // 集中在內層 <input>(APG editable-combobox idiom)— 外層 div 移除 role / tabIndex\n // / aria-*,否則形成「關掉鍵盤 handler 的假 combobox tab stop」+ 內層 input 變成\n // 第二個未命名 tab stop(雙重焦點站)。非 typeable 維持原 div-as-combobox canonical。\n id={typeable ? undefined : (idProp ?? fieldCtx?.id)}\n role={typeable ? undefined : 'combobox'}\n tabIndex={typeable ? undefined : (disabled ? -1 : 0)}\n aria-disabled={typeable ? undefined : (disabled || undefined)}\n aria-label={typeable ? undefined : accessibleName}\n aria-labelledby={typeable ? undefined : (ariaLabelledByProp ?? fieldCtx?.labelId)}\n aria-invalid={typeable ? undefined : (error || undefined)}\n aria-required={typeable ? undefined : (fieldCtx?.required || undefined)}\n aria-describedby={typeable ? undefined : (ariaDescribedByProp ?? fieldCtx?.descriptionId)}\n aria-errormessage={typeable ? undefined : (ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined))}\n aria-haspopup={typeable ? undefined : 'dialog'}\n aria-expanded={typeable ? undefined : open}\n aria-controls={typeable || !open ? undefined : popupId}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n // Radix PopoverTrigger 只 compose onClick(onOpenToggle),`<div>` trigger 無\n // native Enter/Space→click → 自建 onKeyDown 開 popover(對齊 select.tsx desktop\n // trigger canonical:Enter/Space → 開;Esc → 關)。typeable 模式不攔 — 內層\n // <input> 自有 Enter/Esc 語意(commit / reset draft),Calendar icon click 開\n // popover(Material/Ant typed-date idiom),避免 div 層 keydown 與 input 衝突。\n onKeyDown={typeable ? undefined : (e) => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(true) }\n if (e.key === 'Escape') setOpen(false)\n }}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, width, size, error }),\n 'text-left cursor-pointer',\n // @focus-suppress B — B Field 家族輸入控件;承擔者:欄位邊框轉 primary(field-wrapper.tsx:57)\n 'focus-visible:outline-none',\n className,\n )}\n {...props}\n >\n {typeable ? (\n // Issue 10 typed input(2026-05-10):real `<input>` 接 user 鍵盤打字。\n // 2026-09-07 訂正:原本這裡寫「Click 在 input 上不 propagate…Calendar icon 點才開\n // popover(Material/**Ant** typed-date idiom)」—— **對 Ant 而言是反的**。\n // Ant Design 官方文件逐字:「By clicking the input box, you can select a date from a\n // popup calendar」,且 `inputReadOnly` 預設 false(可同時打字)。\n // 現行:點欄位任何地方都開,焦點留在輸入框(見上方 openedByPointerRef)。\n // a11y(2026-07-14 dim-10 修):input 持完整 combobox 語意(id/name/state,APG\n // editable-combobox)— 外層 div 已讓位(見上方 role/tabIndex 註解);ArrowDown\n // 開 popover 補鍵盤開啟路徑(原僅 icon click 可開,鍵盤 user 無入口)。\n <input\n ref={typedInputRef}\n type=\"text\"\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n aria-controls={open ? popupId : undefined}\n aria-label={accessibleName}\n aria-labelledby={ariaLabelledByProp ?? fieldCtx?.labelId}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? ((inputInvalid || error) ? fieldCtx?.errorId : undefined)}\n disabled={disabled}\n className={cn(bareInputStyles, 'truncate', !inputDraft && 'placeholder:text-fg-muted')}\n value={inputDraft}\n placeholder={resolvedPlaceholder}\n aria-invalid={inputInvalid || error || undefined}\n onChange={(e) => { setInputDraft(e.target.value); setInputInvalid(false) }}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n if (composingRef.current) return\n if (e.key === 'Enter') { e.preventDefault(); handleInputCommit(inputDraft) }\n if (e.key === 'Escape') { setInputDraft(displayLive); setInputInvalid(false); e.preventDefault() }\n if (e.key === 'ArrowDown' && !open) { e.preventDefault(); openedByPointerRef.current = false; setOpen(true) }\n }}\n onBlur={() => { if (!composingRef.current) handleInputCommit(inputDraft) }}\n // 2026-09-07(user 抓「點下去為何不是直接開日曆同時可以打字」):\n // 原本這裡 `stopPropagation` 把點擊吞掉,所以只有點到圖示那一小塊才會開。\n // Ant Design 官方文件逐字:「By clicking the input box, you can select a date\n // from a popup calendar」,而且 `inputReadOnly` 預設 false(可同時打字)——\n // 我們原本註解寫「Calendar icon 仍 click 開 popover(Material/**Ant** idiom)」\n // 對 Ant 而言是**反的**。現在點欄位任何地方都開,焦點留在輸入框。\n onPointerDown={() => { openedByPointerRef.current = true }}\n />\n ) : (\n // ref = 截斷量測點(值 span 是截斷元素;tooltip trigger 在外層 host div,見 triggerTruncationRef 註解)\n <span ref={triggerTruncationRef} className={cn(bareInputStyles, 'truncate', !displayValue && 'text-fg-muted')}>\n {triggerText}\n </span>\n )}\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除日期', // i18n-allow: DS default inline-action label\n // Clear = 立刻 commit + 同步 draft(對齊 user 體感 / Ant trigger X 慣例)\n // 不走 needConfirm「等確定」語義 — X 在 trigger 上是 standard clear affordance,\n // 應立刻清空。dual-state 必同步:value('') + draft(null),否則 popover 開\n // 著時 displayValue=draft 仍顯示舊值(see line 318: displayValue = needConfirm ? draft : value)。\n onClick: (e) => {\n e?.stopPropagation()\n onChange?.('')\n setDraft(null)\n },\n }}\n />\n )}\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n </TooltipTrigger>\n </PopoverTrigger>\n <TooltipContent>{triggerTooltipActive ? displayLive : null}</TooltipContent>\n </Tooltip>\n <PopoverContent\n id={popupId}\n className=\"w-auto p-0\"\n align=\"start\"\n aria-label=\"日期選擇\" // i18n-allow: DS default dialog label\n >\n {/* a11y(2026-07-14 dim-10 修):Radix PopoverContent 自帶 role=\"dialog\"(popover.tsx:58)\n — 內層 div 原本重複 role=\"dialog\" 形成雙層 nested unnamed dialog,改為純 layout div,\n accessible name 收斂到外層 PopoverContent aria-label(單一 dialog 層)。\n 2026-05-06 v9.1:`flex flex-col flex-1 min-h-0` 完成 M25 chain — viewport\n 壓縮時 dialog 縮 + 內 calendar/footer 排序;原無 chain 致 calendar 末行被\n overflow-hidden 切掉、footer 推出 popover(user 報「位置改變就壞掉」根因)。 */}\n <div className=\"flex flex-col flex-1 min-h-0\">\n {/* Calendar 區包 ScrollArea(2026-08-12 自 raw overflow-y-auto 遷移,跨 OS 一致 overlay 捲軸;M25 chain 由 flex-1 min-h-0 照傳):viewport 壓縮時 calendar 內滾(Material / Carbon\n date picker idiom)。footer 永遠 in-view(SurfaceFooter shrink-0)。 */}\n <ScrollArea className=\"flex-1 min-h-0\">\n <CalendarTimeContainer\n showTime={showTime}\n showSeconds={showSeconds}\n calendar={\n <DateGrid\n mode=\"single\"\n selected={displayDate}\n onSelect={(date) => {\n if (!date) return\n if (showTime) {\n commitDraft(combineDateAndTime(date, draftTime))\n } else {\n commitDraft(dateToIso(date))\n if (!needConfirm) setOpen(false)\n }\n }}\n defaultMonth={displayDate ?? undefined}\n autoFocus\n />\n }\n timePanel={\n <TimePickerSidePanel\n value={draftTime}\n onChange={(time) => {\n const target = draftDate ?? new Date()\n commitDraft(combineDateAndTime(target, time))\n }}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n }\n />\n </ScrollArea>\n {(showTime || needConfirm) && (\n // Footer:消費 SurfaceFooter SSOT(border-t + py-tight;左右內距不覆寫 = 預設 loose 16px,\n // 對齊的是第一個日期格左緣(DateGrid p-3 12 + border-spacing-1 4 = 16),實測差 0。\n // 判準 owner:patterns/overlay-surface/overlay-surface.spec.md「要對齊誰」;\n // ⚠️ 兩個 16 是不相干的字面值剛好相等,改 DateGrid p-3 時 footer 不會跟著動,\n // 不再 hand-coded p-2 / Separator / ml-auto wrapper 三層垃圾)。\n // 「此刻」加 mr-auto 把後面 button 推右(對齊 Ant `marginInlineStart: auto` on OK)。\n // 2026-07-05 D4:gate 由 `showTime` 改 `(showTime || needConfirm)` 對齊 Range(:1068)—\n // date-only `needConfirm` 時 onSelect 只寫 draft 且不關 popover,footer「確定」是唯一\n // commit 路徑,原 gate 漏渲 footer = draft 永遠無法 commit 的死路。\n <SurfaceFooter className=\"px-[var(--item-px,var(--field-px))]\">\n <Button variant=\"tertiary\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">此刻</Button>\n {needConfirm ? (\n <Button variant=\"primary\" size=\"sm\" onClick={handleConfirm} disabled={!draft}>確定</Button>\n ) : (\n <Button variant=\"tertiary\" size=\"sm\" onClick={() => setOpen(false)}>關閉</Button>\n )}\n </SurfaceFooter>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n }\n)\nDatePicker.displayName = 'DatePicker'\n\n// ── DatePickerRange ─────────────────────────────────────────────────────────\n//\n// Canonical 2026-05-02 v4 — 全對齊 Ant Design RangePicker(WebFetch 實證):\n//\n// **showTime Range**(rc-picker `multiplePanel = false` 證實):\n// - **1 calendar + 1 time panel**(等同 single DateTimePicker layout)\n// - 沒 range track 視覺(計算上跟 single 一樣)\n// - footer **無「此刻」按鈕**(rc-picker `showNow={multiple ? false : showNow}` 證實)\n// - Click flow:click input → open popup for activeEnd → 編 → 點「確定」commit activeEnd\n// → if start: switch activeEnd='end' + popup 維持 open;if end: close popup\n// - Cell disable(rc-picker useRangeDisabledDate 證實):\n// activeEnd='end' + start 已選 → date < start disabled\n// activeEnd='start' + end 已選 → date > end disabled\n//\n// **date-only Range**(rc-picker `multiplePanel = true`):\n// - **2 calendars 並列**(showTime=false 時)\n// - Full range track 視覺(start / middle / end)\n// - 走原 RDP mode='range' 配對 click 邏輯 + auto-swap\n//\n// **Trigger**:2 input button,active end blue underline 標示\n\nexport interface DatePickerRangeProps\n extends DateFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'value' | 'onChange' | 'placeholder' | 'defaultValue'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮(value 寬 + slot 寬 + gap + 內 padding);\n * chrome 與互動不變。SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */\n width?: FieldWidth\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** 區間值:[start ISO, end ISO]。任一 null 代表尚未選。 */\n value?: [string | null, string | null] | null\n onChange?: (value: [string | null, string | null]) => void\n /** Placeholder:[start placeholder, end placeholder] */\n placeholder?: [string, string]\n className?: string\n disabled?: boolean\n clearable?: boolean\n /** 啟用時間欄位 — value 兩端皆變 ISO datetime */\n showTime?: boolean\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n needConfirm?: boolean\n}\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(\n (\n {\n mode,\n variant: variantProp,\n width,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n formatOptions,\n locale,\n showTime = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n needConfirm: needConfirmProp,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref,\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix:<Field size>/cell surface-size 對 DatePicker 生效\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"view\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const needConfirm = needConfirmProp ?? showTime\n const resolvedPlaceholder: [string, string] = placeholder ?? (\n showTime ? ['Start date time', 'End date time'] : ['Start date', 'End date'] // i18n-allow: DS default; consumer override via placeholder prop\n )\n\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<[string | null, string | null]>(value ?? [null, null])\n const [activeEnd, setActiveEnd] = React.useState<'start' | 'end'>('start')\n\n // 2026-07-05 D4 focus 回復:Range 只用 PopoverAnchor(start/end button 自行 onClick 開啟),\n // Radix triggerRef 恆 null → 內建 onCloseAutoFocus 的 triggerRef.focus() no-op 後又\n // preventDefault 掉 FocusScope fallback → Esc/確定/選完自動關閉後焦點掉到 document.body\n // (WCAG 2.4.3)。手動回焦 active 端 button(對齊 Ant RangePicker);outside-click 關閉\n // 依 Radix non-modal 慣例不搶焦(hasInteractedOutside 鏡射 Radix 自家 guard)。\n const startBtnRef = React.useRef<HTMLButtonElement>(null)\n const endBtnRef = React.useRef<HTMLButtonElement>(null)\n const hasInteractedOutsideRef = React.useRef(false)\n\n // Draft sync canonical(2026-07-14 dim-26 V4 修,升級 2026-05-02 v3):\n // - open=false(關閉期間):controlled value 變更**必**同步 draft — needConfirm 顯示層讀\n // draft(startIso/endIso),不同步會讓 trigger 持續顯示 stale 舊區間(外部 controlled\n // update 被凍結)。關閉期間無編輯進行,直接鏡射無 clobber 風險。\n // - open=true(開啟期間):只在 false→true 轉換時 seed 一次;開啟中 value reference 變更\n // 不覆寫,保護 user 尚未確認的 draft 編輯(原 v3 canonical 保留)。\n const lastOpenRef = React.useRef(open)\n React.useEffect(() => {\n if (!open || !lastOpenRef.current) setDraft(value ?? [null, null])\n lastOpenRef.current = open\n }, [open, value])\n\n const startIso = (needConfirm ? draft[0] : value?.[0]) ?? null\n const endIso = (needConfirm ? draft[1] : value?.[1]) ?? null\n const startDate = React.useMemo(() => isoToDate(startIso), [startIso])\n const endDate = React.useMemo(() => isoToDate(endIso), [endIso])\n const hasValue = !!(value?.[0] || value?.[1])\n const showClear = clearable && hasValue && isEditable\n\n const startText = startIso\n ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale })\n : resolvedPlaceholder[0]\n const endText = endIso\n ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale })\n : resolvedPlaceholder[1]\n\n // 截斷 tooltip(tooltip.spec.md:32 截斷 → 顯完整內容,僅實際截斷時):Range 兩端 edit\n // trigger button 各自截斷、各自量測顯示 — trigger = button 本體(interactive host,hover\n // 直達)、量測內層文字 span,useTruncated 自組 pattern(truncated-text.spec.md「trigger 需\n // 自控」指定解;範本 inline-edit.tsx:251)。popover 開啟時靜默;hook 必在 view / readonly\n // early return 之前(React #310 hook-count 鐵律,同 select.tsx 教訓)。\n const { ref: startTruncationRef, isTruncated: startTruncated } = useTruncated<HTMLSpanElement>({\n deps: [startIso ? startText : ''],\n })\n const { ref: endTruncationRef, isTruncated: endTruncated } = useTruncated<HTMLSpanElement>({\n deps: [endIso ? endText : ''],\n })\n const startTooltipActive = !!startIso && startTruncated && !open\n const endTooltipActive = !!endIso && endTruncated && !open\n\n const activeIso = activeEnd === 'start' ? startIso : endIso\n const activeDate = activeEnd === 'start' ? startDate : endDate\n const activeTime = isoToTimeParts(activeIso) ?? { hours: 0, minutes: 0, seconds: 0 }\n\n // Range visual modifiers(自管,不靠 RDP mode='range'):\n // rangeStart:start 那天 → 圓底白字\n // rangeEnd:end 那天 → 圓底白字\n // rangeMiddle:start+1 ~ end-1 之間的所有天 → 灰底矩形 track\n const rangeModifiers = React.useMemo(() => {\n const mods: Record<string, Date | { from: Date; to: Date } | undefined> = {}\n if (startDate) mods.rangeStart = startDate\n if (endDate) mods.rangeEnd = endDate\n if (startDate && endDate) {\n const middleStart = addDays(startDate, 1)\n const middleEnd = addDays(endDate, -1)\n if (middleEnd >= middleStart) {\n mods.rangeMiddle = { from: middleStart, to: middleEnd }\n }\n }\n return mods\n }, [startDate, endDate])\n\n // ── 區間預覽(2026-09-23 user 拍板;規則表與原話見 date-picker.spec.md「區間預覽」)──\n // 停留日只有一個,**最後一個輸入贏**:滑鼠移進某格 → 它;鍵盤把看得見的焦點移到某格 → 它。\n // 清除只由同一種來源做(滑鼠離開只清滑鼠設的、blur 只清鍵盤設的):點某一天時 mousedown 會先把前一個焦點格 blur 掉,\n // 若 blur 也能清掉滑鼠剛設的停留日,框會在指標還停在格子上時消失。滑鼠離開格子時不是消失、而是退回仍帶著鍵盤焦點的那一天。\n // 第一版是「滑鼠恆優先」的兩個變數 —— 滑鼠停著不動、用方向鍵移焦點時框不跟著走(user 2026-09-23:\n // 「滑鼠和鍵盤沒有搶走彼此的焦點」)。\n // 焦點只在「看得見的焦點」時算(:focus-visible):浮層開啟時 autoFocus 是程式搬焦點,滑鼠使用者不會先看到整段框;\n // 鍵盤使用者用方向鍵移動時 :focus-visible 為真,框跟著焦點走(Q6)。\n // 判定本身是純函式(range-preview.ts),這裡只接線;showTime 一樣預覽(Q7),track 是否顯示另管。\n type PreviewAnchor = { day: Date; source: 'pointer' | 'keyboard' }\n const [previewAnchor, setPreviewAnchor] = React.useState<PreviewAnchor | null>(null)\n // 目前帶著看得見的鍵盤焦點的那一天:滑鼠離開格子時框退回這裡\n const keyboardDayRef = React.useRef<Date | null>(null)\n React.useEffect(() => {\n if (!open) { setPreviewAnchor(null); keyboardDayRef.current = null }\n }, [open])\n const previewFor = React.useCallback(\n (anchor: Date | null) => computeRangePreview({ activeEnd, start: startDate, end: endDate, anchor }),\n [activeEnd, startDate, endDate],\n )\n const previewModifiers = React.useMemo(\n () => ({\n // 上膛:這一天停留會有框 → 停留前就靜態壓掉它的單格 hover 圈(理由見 DateGrid RANGE_PREVIEW_ARMED_CLASSNAME)\n previewArmed: (day: Date) => previewFor(day) !== null,\n ...rangePreviewModifiers(previewFor(previewAnchor?.day ?? null)),\n }),\n [previewFor, previewAnchor],\n )\n\n const commitRange = (next: [string | null, string | null]) => {\n if (needConfirm) setDraft(next)\n else { onChange?.(next); setDraft(next) }\n }\n const setActive = (iso: string | null) => {\n const nextDraft = activeEnd === 'start'\n ? ([iso, draft[1]] as [string | null, string | null])\n : ([draft[0], iso] as [string | null, string | null])\n commitRange(nextDraft)\n }\n /**\n * Click「確定」canonical(2026-05-02 v4,對齊 Ant Design 序列流程):\n * showTime Range:\n * - activeEnd='start' → commit start to draft + switch activeEnd='end' + popup 維持 open\n * - activeEnd='end' → commit final draft to value + close popup\n * date-only Range(沒有 footer,不會走這 path):— N/A\n */\n const handleConfirm = () => {\n if (showTime && activeEnd === 'start' && draft[0]) {\n // Start 已填 → switch to end,popup 維持 open\n setActiveEnd('end')\n } else {\n // End 也填好(or non-showTime needConfirm)→ final commit + close\n onChange?.(draft)\n setOpen(false)\n }\n }\n const handleNow = () => {\n setActive(showTime ? nowIsoDateTime() : dateToIso(new Date()))\n }\n const handleClearRange = (e?: React.MouseEvent) => {\n e?.stopPropagation()\n // Clear = 立刻 commit + 同步 draft(對齊 single mode + user 體感)\n // dual-state 同步,否則 popover 開著時 displayValue=draft 仍顯示舊 [start, end]\n onChange?.([null, null])\n setDraft([null, null])\n }\n const openWithActive = (which: 'start' | 'end') => {\n setActiveEnd(which)\n setOpen(true)\n }\n /**\n * Cell disable(對齊 Ant rc-picker `useRangeDisabledDate`):\n * activeEnd='end' + start 已選 → date < start 被 disable(同日 OK)\n * activeEnd='start' + end 已選 → date > end 被 disable(同日 OK)\n * 防 user 點下違反順序的日期(start > end / end < start)。\n */\n const isOutOfRangeOrder = React.useCallback((date: Date): boolean => {\n // ⚠️ 必先 clone(new Date(...)),否則 setHours 會 mutate useMemo'd date 物件\n if (activeEnd === 'end' && startDate) {\n const startMidnight = new Date(startDate.getTime())\n startMidnight.setHours(0, 0, 0, 0)\n return date.getTime() < startMidnight.getTime()\n }\n if (activeEnd === 'start' && endDate) {\n const endEndOfDay = new Date(endDate.getTime())\n endEndOfDay.setHours(23, 59, 59, 999)\n return date.getTime() > endEndOfDay.getTime()\n }\n return false\n }, [activeEnd, startDate, endDate])\n\n // mode='view'(Phase B2 2026-05-05):純內容輸出 — 無 Field wrapper / 無 Calendar icon。\n if (resolvedMode === 'view') {\n const hasAny = !!(startIso || endIso)\n if (!hasAny) return <span className={cn(fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>\n return (\n <span className={cn('inline-flex items-center min-w-0', nakedCellRowModeAlign, className)}>\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— start / end 各自獨立截斷、各自量測 */}\n <TruncatedText className={cn(!startIso && 'text-fg-muted')}>\n {startIso ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[0]}\n </TruncatedText>\n <ArrowRight size={iconSize} className=\"shrink-0 text-fg-muted mx-2\" aria-hidden />\n <TruncatedText className={cn(!endIso && 'text-fg-muted')}>\n {endIso ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[1]}\n </TruncatedText>\n </span>\n )\n }\n\n // readonly / disabled view — plain wrapper,no popover\n if (!isEditable) {\n return (\n <div\n ref={ref}\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, width, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...props}\n >\n {/* 截斷必附 tooltip(tooltip.spec.md:32)— readonly/disabled 無編輯入口,tooltip 是唯一補救路徑 */}\n <TruncatedText className={cn('flex-1', !startIso && 'text-fg-muted', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {startIso ? formatDateOrDateTime(startIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[0]}\n </TruncatedText>\n <ArrowRight size={iconSize} className={cn('shrink-0 mx-2', resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted')} aria-hidden />\n <TruncatedText className={cn('flex-1', !endIso && 'text-fg-muted', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {endIso ? formatDateOrDateTime(endIso, showTime, showSeconds, { formatOptions, locale }) : resolvedPlaceholder[1]}\n </TruncatedText>\n {/* 2026-06-26 類型身份 indicator:readonly 不顯示 / disabled 保留(fg-disabled)— 對齊單一 DatePicker(:488)+ SSOT;Range 無 naked-cell 變體故 gate = resolvedMode==='disabled' */}\n {resolvedMode === 'disabled' && (\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-disabled\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverAnchor asChild>\n <div\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n data-state={open ? 'open' : 'closed'}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', width, size, error }),\n 'cursor-text',\n className,\n )}\n {...props}\n >\n {/* 兩端截斷 tooltip:恆 wrap、未截斷 open={false} 靜默(truncated-text.spec.md\n always-wrap);TooltipTrigger asChild 只合併 handler / data-state 到 button,\n 零多餘 DOM → wrapper flex 佈局不變。內層 span = 量測點(見 startTruncationRef 註解)。 */}\n <Tooltip open={startTooltipActive ? undefined : false}>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n ref={startBtnRef}\n onClick={() => openWithActive('start')}\n data-active-end={open && activeEnd === 'start' ? 'true' : undefined}\n aria-label={resolvedPlaceholder[0]}\n aria-haspopup=\"dialog\"\n aria-expanded={open && activeEnd === 'start'}\n className={cn(\n bareInputStyles,\n // @focus-suppress C — button 不是 input 故無 caret;承擔者:外層欄位邊框 focus-within 轉 primary(field-wrapper.tsx:57);起訖兩顆靠下方主色底線區分(見下一行)\n 'truncate text-left cursor-pointer focus-visible:outline-none',\n // 起訖兩顆共用同一圈欄位邊框,邊框分不出焦點在哪一顆,所以作用端另有一條主色底線。\n // 這條線原本只在面板開著時畫(data-active-end 帶 open 條件),於是「面板關著用 Tab\n // 在起訖之間移動」時兩顆長得一模一樣。補上 focus-visible 這一半 —— 用的是本元件\n // 既有的同一條線,不是第二種指示。對照 Ant Design RangePicker 的 -active-bar\n // (components/date-picker/style/index.ts:`height: lineWidthBold, background: colorPrimary`)。\n 'data-[active-end=true]:underline focus-visible:underline decoration-primary underline-offset-4 decoration-2',\n !startIso && 'text-fg-muted',\n )}\n >\n <span ref={startTruncationRef} className=\"block truncate\">{startText}</span>\n </button>\n </TooltipTrigger>\n <TooltipContent>{startTooltipActive ? startText : null}</TooltipContent>\n </Tooltip>\n <ArrowRight size={iconSize} className=\"shrink-0 text-fg-muted mx-2\" aria-hidden />\n <Tooltip open={endTooltipActive ? undefined : false}>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n ref={endBtnRef}\n onClick={() => openWithActive('end')}\n data-active-end={open && activeEnd === 'end' ? 'true' : undefined}\n aria-label={resolvedPlaceholder[1]}\n aria-haspopup=\"dialog\"\n aria-expanded={open && activeEnd === 'end'}\n className={cn(\n bareInputStyles,\n // @focus-suppress C — button 不是 input 故無 caret;承擔者:外層欄位邊框 focus-within 轉 primary(field-wrapper.tsx:57);起訖兩顆靠下方主色底線區分(見下一行)\n 'truncate text-left cursor-pointer focus-visible:outline-none',\n // 起訖兩顆共用同一圈欄位邊框,邊框分不出焦點在哪一顆,所以作用端另有一條主色底線。\n // 這條線原本只在面板開著時畫(data-active-end 帶 open 條件),於是「面板關著用 Tab\n // 在起訖之間移動」時兩顆長得一模一樣。補上 focus-visible 這一半 —— 用的是本元件\n // 既有的同一條線,不是第二種指示。對照 Ant Design RangePicker 的 -active-bar\n // (components/date-picker/style/index.ts:`height: lineWidthBold, background: colorPrimary`)。\n 'data-[active-end=true]:underline focus-visible:underline decoration-primary underline-offset-4 decoration-2',\n !endIso && 'text-fg-muted',\n )}\n >\n <span ref={endTruncationRef} className=\"block truncate\">{endText}</span>\n </button>\n </TooltipTrigger>\n <TooltipContent>{endTooltipActive ? endText : null}</TooltipContent>\n </Tooltip>\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除日期區間', // i18n-allow: DS default inline-action label\n onClick: handleClearRange,\n }}\n />\n )}\n <ItemSuffix className=\"pointer-events-none\">\n <CalendarIcon size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n </div>\n </PopoverAnchor>\n <PopoverContent\n className=\"w-auto p-0\"\n align=\"start\"\n aria-label=\"日期區間選擇\" // i18n-allow: DS default dialog label\n // 2026-07-05 D4 focus 回復(機制詳 :758 comment):outside-click 標記不搶焦(鏡射\n // Radix non-modal 自家 hasInteractedOutside guard);其餘關閉路徑(Esc / 確定 /\n // 選完自動關)preventDefault 掉內建 triggerRef.focus()(Anchor-only 恆 null no-op)\n // 後手動回焦 active 端 button — 對齊 Ant RangePicker 關閉後焦點回 active 端輸入格。\n onInteractOutside={() => { hasInteractedOutsideRef.current = true }}\n onCloseAutoFocus={(e) => {\n e.preventDefault()\n if (!hasInteractedOutsideRef.current) {\n ;(activeEnd === 'start' ? startBtnRef : endBtnRef).current?.focus()\n }\n hasInteractedOutsideRef.current = false\n }}\n >\n {/* a11y(2026-07-14 dim-10 修):accessible name 收斂到外層 PopoverContent(Radix 自帶\n role=\"dialog\")— 原內層第二個 role=\"dialog\" 移除,避免 nested dialog 且外層 dialog 無名。\n 2026-05-06 v9.1 M25 chain — 同 single DatePicker 修法,viewport 壓縮 calendar 內滾 + footer 永遠 in-view */}\n <div className=\"flex flex-col flex-1 min-h-0\">\n <ScrollArea className=\"flex-1 min-h-0\">\n <CalendarTimeContainer\n showTime={showTime}\n showSeconds={showSeconds}\n calendar={\n <DateGrid\n // mode='single' + manual modifiers(canonical 2026-05-02 v3):\n // 不用 RDP 內建 mode='range'(它的 click 配對邏輯跟我們的 activeEnd 衝突,\n // 造成「點一次沒反應 / 要點兩次」bug)。改自管 modifiers 控視覺。\n // showTime Range:rangeModifiers 為空(不顯示 range track,對齊 Ant)\n mode=\"single\"\n selected={activeDate}\n onSelect={(date) => {\n if (!date) return\n if (isOutOfRangeOrder(date)) return // 防護:disable 邏輯內 click 已被 RDP 擋,但雙保險\n const preservedTime = isoToTimeParts(activeEnd === 'start' ? draft[0] : draft[1]) ?? activeTime\n const nextIso = showTime\n ? combineDateAndTime(date, preservedTime)\n : dateToIso(date)\n const nextDraft: [string | null, string | null] = activeEnd === 'start'\n ? [nextIso, draft[1]]\n : [draft[0], nextIso]\n commitRange(nextDraft)\n // Auto-advance / close logic:\n if (!showTime) {\n // date-only Range:選完 start 自動切 end;兩端皆填 + 不需確認 → 關閉\n if (activeEnd === 'start') {\n setActiveEnd('end')\n if (!needConfirm && nextDraft[0] && nextDraft[1]) setOpen(false)\n } else if (!needConfirm && nextDraft[0] && nextDraft[1]) {\n setOpen(false)\n }\n }\n // showTime Range:不 auto-advance,讓 user 編 time 後手動按確定 commit\n // (對齊 Ant 序列流程 — 確定 button 切 activeEnd)\n }}\n // showTime Range:不渲 range track(對齊 Ant — 整個 popup 等同 single\n // DateTimePicker,沒 range 視覺概念);date-only Range 才顯示。\n // 停留預覽框不分 showTime(user 2026-09-23 Q7:「同一套規則」)。\n modifiers={{ ...(showTime ? {} : rangeModifiers), ...previewModifiers }}\n modifiersClassNames={{\n // ── Range visual canonical(2026-05-03 v8 stadium pattern)──\n // v5 修「白色破圖」用 pseudo 蓋全 cell 矩形,但新副作用:button 圓比矩形小,\n // 4 corner triangle 區域 grey 凸出圓外(user 2026-05-03 抓到「凸出去」)。\n // v8 對齊 Ant `cell-range-start::before { border-radius: 9999px 0 0 9999px }`:\n // rangeStart pseudo 加 `rounded-l-full` → pseudo 變「左半圓 + 右矩形」stadium\n // 左半圓 EXACTLY OVERLAY button 圓的左半弧(同 center 同 radius 14)→ 無縫\n // 右側矩形 bridge 2px to middle → 跟 middle pseudo 連續\n // Cell 的 top-left + bottom-left corner triangle:pseudo 不蓋 + button 不蓋 →\n // popover white 顯露(乾淨 breathing)\n rangeStart: cn(\n // 2026-08-11 修偏移:!bg-primary 連 hover 也壓死 → 端點 hover 零回饋,違反\n // date-grid.spec.md:121「選中日 hover 升階 primary→primary-hover」(端點可再點\n // 重選範圍 = 可取消家族,依 user 原則該有回饋)。補顯式 hover 升階(同 !important,\n // hover 變體更特定故 hover 時勝出;ring-0 壓制照舊,spec:120)。\n '[&>button]:!bg-primary [&>button]:hover:!bg-primary-hover [&>button]:!text-on-emphasis [&>button]:hover:!ring-0',\n // 藍底上的鍵盤焦點 = 1px 白線退 3px(同 DateGrid selected;端點不掛 RDP selected,要自己帶)\n EMPHASIS_FOCUS_RING_CLASSNAME,\n // track 的 stadium class 只住在 DateGrid(2026-09-23 收斂;先前這裡各抄一份 = 假 SSOT)\n RANGE_TRACK_CLASSNAMES.start,\n ),\n rangeEnd: cn(\n // 2026-08-11:同 rangeStart — 補 hover 升階(spec:121),鏡像。\n '[&>button]:!bg-primary [&>button]:hover:!bg-primary-hover [&>button]:!text-on-emphasis [&>button]:hover:!ring-0',\n EMPHASIS_FOCUS_RING_CLASSNAME,\n RANGE_TRACK_CLASSNAMES.end,\n ),\n rangeMiddle: cn(\n RANGE_TRACK_CLASSNAMES.middle,\n // 2026-09-23 拿掉 `!text-foreground`:它是 2026-04-21 為了壓過 RDP range 模式中段自帶的 selected 白字而加,\n // 2026-05-03 改成 mode=\"single\" 自管區間時原封抄來 —— 這條路徑的中段不掛 selected,!important 沒有任務,\n // 卻讓鄰月淡字永遠輸給它(全 repo 從沒定義過 outside × range;兩月時鄰月現已不渲染)。字色回到 day_button 預設。\n '[&>button]:!bg-transparent',\n ),\n // ── 停留預覽框(2026-09-23 user 拍板)── 四種格由 range-preview.ts 算出,畫法住在 DateGrid\n previewArmed: RANGE_PREVIEW_ARMED_CLASSNAME,\n previewSingle: RANGE_PREVIEW_CLASSNAMES.single,\n previewStart: RANGE_PREVIEW_CLASSNAMES.start,\n previewMiddle: RANGE_PREVIEW_CLASSNAMES.middle,\n previewEnd: RANGE_PREVIEW_CLASSNAMES.end,\n }}\n // 停留來源:滑鼠與看得見的鍵盤焦點,最後一個輸入贏;各自只清自己設的(理由見上方 previewAnchor 註解)\n onDayMouseEnter={(day) => setPreviewAnchor({ day, source: 'pointer' })}\n onDayMouseLeave={() => setPreviewAnchor((prev) => {\n if (prev?.source !== 'pointer') return prev\n return keyboardDayRef.current ? { day: keyboardDayRef.current, source: 'keyboard' } : null\n })}\n onDayFocus={(day, _modifiers, event) => {\n if (!event.currentTarget.matches(':focus-visible')) return\n keyboardDayRef.current = day\n setPreviewAnchor({ day, source: 'keyboard' })\n }}\n onDayBlur={() => {\n keyboardDayRef.current = null\n setPreviewAnchor((prev) => (prev?.source === 'keyboard' ? null : prev))\n }}\n // Cell disable:防 user 點下違反順序的日期(對齊 Ant useRangeDisabledDate)\n disabled={isOutOfRangeOrder}\n // showTime → 1 cal(對齊 Ant `multiplePanel=false`);date-only → 2 cal(`multiplePanel=true`)\n numberOfMonths={showTime ? 1 : 2}\n defaultMonth={activeDate ?? startDate ?? endDate ?? undefined}\n autoFocus\n />\n }\n timePanel={\n <TimePickerSidePanel\n value={activeTime}\n onChange={(time) => {\n const target = activeDate ?? new Date()\n setActive(combineDateAndTime(target, time))\n }}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n />\n }\n />\n </ScrollArea>\n {(showTime || needConfirm) && (\n // Footer 消費 SurfaceFooter SSOT(border-t + py-tight;左右內距不覆寫,理由見上方 764 行同款註解\n // 與 patterns/overlay-surface/overlay-surface.spec.md「要對齊誰」)。\n // showTime Range 無「此刻」(對齊 Ant `showNow={multiple ? false : showNow}`)→ 只有 確定 走 justify-end。\n // date-only Range needConfirm:左 此刻(mr-auto)+ 右 確定。\n <SurfaceFooter className=\"px-[var(--item-px,var(--field-px))]\">\n {!showTime && (\n <Button variant=\"tertiary\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">此刻</Button>\n )}\n {needConfirm ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={handleConfirm}\n // showTime Range serial flow:start mode 只需 start filled;end mode 兩端皆 filled\n disabled={\n showTime\n ? (activeEnd === 'start' ? !draft[0] : !draft[0] || !draft[1])\n : !draft[0] || !draft[1]\n }\n >\n 確定\n </Button>\n ) : (\n <Button variant=\"tertiary\" size=\"sm\" onClick={() => setOpen(false)}>關閉</Button>\n )}\n </SurfaceFooter>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nDatePickerRange.displayName = 'DatePickerRange'\n\n// Attach Range as namespace:consumer 用 <DatePicker.Range ...>(Ant-style)\n// 走 Object.assign 確保 TS 型別帶上 Range 屬性,而非只做 runtime 附掛\nconst DatePickerWithRange = Object.assign(DatePicker, { Range: DatePickerRange })\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const datePickerMeta = {\n component: 'DatePicker',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = range 帶持續選中(before:bg-neutral-selected,本檔自有);'active' 移除 — 全檔無按壓\n // 視覺(data-active-end 是定位 attr 非按壓 token;2026-07-07 詞彙統一 DS-wide 交叉掃補修)。\n states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport {\n DatePickerWithRange as DatePicker,\n DatePickerRange,\n formatDate,\n}\n"],"names":["CalendarIcon"],"mappings":";;;;;;;;;;;;;;;;;AA6CA,MAAM,+BAAe,IAAA;AACrB,SAAS,qBAAqB,QAAgB,SAA0D;AACtG,QAAM,MAAM,SAAS,MAAM,KAAK,UAAU,OAAO;AACjD,MAAI,MAAM,SAAS,IAAI,GAAG;AAC1B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,KAAK,eAAe,QAAQ,OAAO;AAC7C,aAAS,IAAI,KAAK,GAAG;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,WACP,OACA,UAA6B,IACrB;AAIR,QAAM,OACJ,iBAAiB,OACb,QACA,OAAO,UAAU,WACd,UAAU,KAAK,KAAK,IAAI,KAAK,KAAK,IACnC,IAAI,KAAK,KAAK;AACtB,MAAI,OAAO,MAAM,KAAK,QAAA,CAAS,EAAG,QAAO,OAAO,KAAK;AAErD,MAAI,QAAQ,iBAAiB,QAAQ,QAAQ;AAC3C,WAAO,qBAAqB,QAAQ,UAAU,SAAS,QAAQ,iBAAiB,EAAE,MAAM,WAAW,OAAO,WAAW,KAAK,UAAA,CAAW,EAAE,OAAO,IAAI;AAAA,EACpJ;AAEA,QAAM,IAAI,KAAK,YAAA;AACf,QAAM,IAAI,OAAO,KAAK,SAAA,IAAa,CAAC,EAAE,SAAS,GAAG,GAAG;AACrD,QAAM,IAAI,OAAO,KAAK,QAAA,CAAS,EAAE,SAAS,GAAG,GAAG;AAChD,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB;AAGA,SAAS,qBACP,KACA,UACA,aACA,UAA6B,CAAA,GACrB;AACR,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,WAAW,WAAW,KAAK,OAAO;AACxC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,OAAO,eAAe,GAAG;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,GAAG,QAAQ,IAAI,kBAAkB,MAAM,WAAW,CAAC;AAC5D;AAMA,SAAS,UAAU,KAAkD;AACnE,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,WAAW,IAAI,MAAM,GAAG,EAAE;AAChC,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI,MAAM;AAChD,MAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAG,QAAO;AAC3B,QAAM,OAAO,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC;AACjC,QAAM,OAAO,eAAe,GAAG;AAC/B,MAAI,MAAM;AACR,SAAK,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO;AAAA,EACtD;AACA,SAAO;AACT;AAkBA,SAAS,eAAe,OAAe,MAAsE;AAC3G,QAAM,UAAU,MAAM,KAAA;AACtB,MAAI,YAAY,GAAI,QAAO,EAAE,KAAK,MAAM,OAAO,KAAA;AAE/C,QAAM,eAAe,QAAQ,MAAM,mFAAmF;AACtH,MAAI,cAAc;AAChB,UAAM,CAAA,EAAG,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,IAAI;AAChC,UAAM,OAAO,IAAI,KAAK,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5G,QAAI,MAAM,KAAK,QAAA,CAAS,KAAK,KAAK,eAAe,OAAO,CAAC,IAAI,EAAG,QAAO,EAAE,KAAK,MAAM,OAAO,MAAA;AAG3F,QAAI,MAAM,SAAS,OAAO,EAAE,IAAI,MAAM,OAAO,EAAE,IAAI,MAAM,OAAO,MAAM,CAAC,IAAI,IAAK,QAAO,EAAE,KAAK,MAAM,OAAO,MAAA;AAC3G,UAAM,WAAW,UAAU,IAAI;AAC/B,QAAI,MAAM,QAAQ,KAAK,WAAW;AAChC,YAAM,KAAK,OAAO,OAAO,EAAE,CAAC,EAAE,SAAS,GAAG,GAAG;AAC7C,YAAM,KAAK,OAAO,OAAO,EAAE,CAAC,EAAE,SAAS,GAAG,GAAG;AAC7C,YAAM,KAAK,OAAO,OAAO,MAAM,CAAC,CAAC,EAAE,SAAS,GAAG,GAAG;AAClD,aAAO,EAAE,KAAK,GAAG,QAAQ,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,KAAA;AAAA,IACxD;AACA,WAAO,EAAE,KAAK,UAAU,OAAO,KAAA;AAAA,EACjC;AAGA,QAAM,SAAS,IAAI,KAAK,OAAO;AAC/B,MAAI,CAAC,MAAM,OAAO,QAAA,CAAS,GAAG;AAC5B,UAAM,WAAW,UAAU,MAAM;AACjC,QAAI,KAAK,cAAc,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,IAAI;AACtE,YAAM,KAAK,OAAO,OAAO,SAAA,CAAU,EAAE,SAAS,GAAG,GAAG;AACpD,YAAM,KAAK,OAAO,OAAO,WAAA,CAAY,EAAE,SAAS,GAAG,GAAG;AACtD,YAAM,KAAK,OAAO,OAAO,WAAA,CAAY,EAAE,SAAS,GAAG,GAAG;AACtD,aAAO,EAAE,KAAK,GAAG,QAAQ,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,KAAA;AAAA,IACxD;AACA,WAAO,EAAE,KAAK,UAAU,OAAO,KAAA;AAAA,EACjC;AACA,SAAO,EAAE,KAAK,MAAM,OAAO,MAAA;AAC7B;AAEA,SAAS,UAAU,MAAgC;AACjD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,KAAK,YAAA;AACf,QAAM,IAAI,OAAO,KAAK,SAAA,IAAa,CAAC,EAAE,SAAS,GAAG,GAAG;AACrD,QAAM,IAAI,OAAO,KAAK,QAAA,CAAS,EAAE,SAAS,GAAG,GAAG;AAChD,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB;AAEA,SAAS,mBAAmB,MAAY,MAAyB;AAC/D,QAAM,WAAW,UAAU,IAAI;AAC/B,QAAM,KAAK,OAAO,KAAK,KAAK,EAAE,SAAS,GAAG,GAAG;AAC7C,QAAM,KAAK,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG,GAAG;AAC/C,QAAM,KAAK,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG,GAAG;AAC/C,SAAO,GAAG,QAAQ,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AACtC;AAEA,SAAS,iBAAyB;AAChC,QAAM,wBAAQ,KAAA;AACd,SAAO,mBAAmB,GAAG;AAAA,IAC3B,OAAO,EAAE,SAAA;AAAA,IACT,SAAS,EAAE,WAAA;AAAA,IACX,SAAS,EAAE,WAAA;AAAA,EAAW,CACvB;AACH;AAEA,SAAS,QAAQ,MAAY,GAAiB;AAC5C,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,QAAQ,KAAK,QAAA,IAAY,CAAC;AAC/B,SAAO;AACT;AAwCA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb;AACF,GAAsD;AAEpD,QAAM,aAAa,QACf,kBAAkB,OAAO,WAAW,IACnC,cAAc,aAAa;AAEhC,8BACG,OAAA,EAAI,WAAW,GAAG,4DAA4D,SAAS,GAEtF,UAAA;AAAA,IAAA,oBAAC,OAAA,EAAI,WAAU,oDACb,UAAA,oBAAC,UAAK,WAAU,sCAAsC,sBAAW,EAAA,CACnE;AAAA,IAEA,oBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IAAA,EACF,CACF;AAAA,EAAA,GACF;AAEJ;AAMA,MAAM,mBAAmB,CAAC,gBAAyB,cAAc,SAAS;AAS1E,SAAS,sBAAsB,EAAE,UAAU,aAAa,UAAU,aAAyC;AACzG,MAAI,CAAC,SAAU,QAAO,oBAAA,UAAA,EAAG,UAAA,UAAS;AAClC,SACE,qBAAC,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAI,WAAU,iBACZ,UAAA;AAAA,MAAA;AAAA,MAGD,oBAAC,SAAI,WAAW,GAAG,oCAAoC,iBAAiB,WAAW,CAAC,EAAA,CAAG;AAAA,IAAA,GACzF;AAAA,IAEA,oBAAC,SAAI,WAAW,GAAG,mCAAmC,iBAAiB,WAAW,CAAC,GAChF,UAAA,UAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAuFA,MAAM,aAAa,MAAM;AAAA,EACvB,CACE;AAAA,IACE;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,OAAO,YAAY;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,cAAc;AAAA,IACd,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,qBAAqB;AAAA,IACrB,cAAc;AAAA,IACd;AAAA,IACA,WAAW;AAAA,IACX,IAAI;AAAA,IACJ,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,WAAW,gBAAA;AACjB,UAAM,OAAO,qBAAqB,QAAQ;AAC1C,UAAM,QAAQ,wBAAwB,SAAS;AAC/C,UAAM,WAAW,yBAAyB,YAAY;AAEtD,UAAM,eAAe,qBAAqB,EAAE,MAAM,UAAU;AAC5D,UAAM,eAAe,qBAAA;AACrB,UAAM,UAAgC,wBAAwB,WAAW;AACzE,UAAM,aAAa,iBAAiB;AAEtC,UAAM,WAAW,UAAU,IAA0B;AACnD,UAAM,cAAc,mBAAmB;AACvC,UAAM,CAAC,MAAM,YAAY,IAAI,MAAM,SAAS,WAAW;AACvD,UAAM,UAAU,MAAM,YAAY,CAAC,SAAkB;AAAE,mBAAa,IAAI;AAAG,mDAAe;AAAA,IAAM,GAAG,CAAC,YAAY,CAAC;AAIjH,UAAM,UAAU,MAAM,MAAA;AACtB,UAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,SAAS,IAAI;AACrE,UAAM,sBAAsB,gBAAgB,WAAW,qBAAqB;AAE5E,UAAM,iBAAiB,kBAAkB,qBAAqB,UAAa,qCAAU,MAAK,SAAY;AAOtG,UAAM,cAAc,MAAM,OAAO,IAAI;AACrC,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,QAAQ,CAAC,YAAY,QAAS,UAAS,SAAS,IAAI;AACzD,kBAAY,UAAU;AAAA,IACxB,GAAG,CAAC,MAAM,KAAK,CAAC;AAKhB,UAAM,eAAe,cAAc,QAAS,SAAS;AACrD,UAAM,cAAc,MAAM,QAAQ,MAAM,UAAU,YAAY,GAAG,CAAC,YAAY,CAAC;AAC/E,UAAM,YAAY,MAAM,QAAQ,MAAM,UAAU,KAAK,GAAG,CAAC,KAAK,CAAC;AAC/D,UAAM,YAAY,eAAe,KAAK,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,EAAA;AAC5E,UAAM,YAAY,cAAc,cAAc,QAAQ,UAAU;AAEhE,UAAM,mBAAmB,qBAAqB,OAAO,UAAU,aAAa,EAAE,eAAe,QAAQ;AACrG,UAAM,cAAc,qBAAqB,cAAc,UAAU,aAAa,EAAE,eAAe,QAAQ;AAGvG,UAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAiB,WAAW;AACtE,UAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAC5D,UAAM,eAAe,MAAM,OAAO,KAAK;AAOvC,UAAM,qBAAqB,MAAM,OAAO,KAAK;AAC7C,UAAM,gBAAgB,MAAM,OAAgC,IAAI;AAKhE,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,mBAAmB,QAAS;AACvD,YAAM,KAAK,sBAAsB,MAAA;;AAAM,mCAAc,YAAd,mBAAuB;AAAA,OAAO;AACrE,aAAO,MAAM,qBAAqB,EAAE;AAAA,IACtC,GAAG,CAAC,MAAM,QAAQ,CAAC;AAGnB,UAAM,qBAAqB,MAAM,OAAO,WAAW;AACnD,UAAM,UAAU,MAAM;AACpB,UAAI,mBAAmB,YAAY,aAAa;AAC9C,sBAAc,WAAW;AACzB,wBAAgB,KAAK;AACrB,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF,GAAG,CAAC,WAAW,CAAC;AAChB,UAAM,oBAAoB,MAAM,YAAY,CAAC,QAAgB;AAC3D,YAAM,EAAE,KAAK,UAAU,eAAe,KAAK,EAAE,WAAW,UAAU;AAClE,UAAI,CAAC,OAAO;AAAE,wBAAgB,IAAI;AAAG;AAAA,MAAO;AAC5C,sBAAgB,KAAK;AACrB,UAAI,QAAQ,MAAM;AAEhB,6CAAW;AACX,iBAAS,IAAI;AAAA,MACf,OAAO;AACL,6CAAW;AACX,iBAAS,GAAG;AAAA,MACd;AAAA,IACF,GAAG,CAAC,UAAU,QAAQ,CAAC;AAQvB,UAAM,EAAE,KAAK,sBAAsB,aAAa,iBAAA,IAAqB,aAA8B;AAAA,MACjG,MAAM,CAAC,CAAC,YAAY,eAAe,cAAc,EAAE;AAAA,IAAA,CACpD;AACD,UAAM,uBAAuB,CAAC,YAAY,CAAC,CAAC,gBAAgB,oBAAoB,CAAC;AAMjF,QAAI,iBAAiB,QAAQ;AAC3B,UAAI,CAAC,oBAAoB;AAGvB,YAAI,CAAC,MAAO,QAAO,oBAAC,UAAK,WAAW,GAAG,sBAAsB,IAAI,GAAG,qBAAqB,YAAY,GAAG,SAAS,GAAI,UAAA,cAAa;AAElI,eAAO,oBAAC,iBAAc,WAAW,GAAG,sBAAsB,IAAI,GAAG,SAAS,GAAI,UAAA,iBAAA,CAAiB;AAAA,MACjG;AACA,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW,GAAG,mBAAmB,EAAE,MAAM,QAAQ,SAAS,OAAO,MAAM,GAAG,SAAS;AAAA,UACnF,mBAAgB;AAAA,UAGhB,UAAA;AAAA,YAAA,oBAAC,eAAA,EAAc,WAAW,GAAG,iBAAiB,kBAAkB,CAAC,SAAS,qBAAqB,YAAY,CAAC,GACzG,UAAA,QAAQ,mBAAmB,cAC9B;AAAA,YACA,oBAAC,YAAA,EAAW,WAAU,uBACpB,UAAA,oBAACA,UAAA,EAAa,MAAM,UAAU,WAAU,iBAAgB,eAAW,KAAA,CAAC,EAAA,CACtE;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAGN;AAGA,QAAI,CAAC,YAAY;AACf,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW,GAAG,mBAAmB,EAAE,MAAM,cAAc,SAAkB,OAAO,MAAM,GAAG,SAAS;AAAA,UAClG,mBAAiB;AAAA,UACjB,iBAAe,iBAAiB,aAAa,OAAO;AAAA,UACnD,GAAI;AAAA,UAGL,UAAA;AAAA,YAAA,oBAAC,iBAAc,WAAW,GAAG,UAAU,iBAAiB,cAAc,kBAAkB,GACrF,UAAA,QACG,uCACC,QAAA,EAAK,WAAW,qBAAqB,YAAY,GAAI,wBAAa,GAEzE;AAAA,aAGE,YAAY,UAAU,qBAAqB,iBAAiB,eAC5D,oBAAC,YAAA,EAAW,WAAU,uBACpB,UAAA,oBAACA,YAAa,MAAM,UAAU,WAAW,iBAAiB,aAAa,qBAAqB,iBAAiB,eAAW,MAAC,EAAA,CAC3H;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAIR;AAEA,UAAM,cAAc,eAChB,kCACC,QAAA,EAAK,WAAU,iBAAiB,UAAA,qBAAoB;AAEzD,UAAM,cAAc,CAAC,SAAwB;AAC3C,UAAI,sBAAsB,IAAI;AAAA,UACzB,sCAAW,QAAQ;AAAA,IAC1B;AACA,UAAM,gBAAgB,MAAM;AAAE,2CAAW,SAAS;AAAK,cAAQ,KAAK;AAAA,IAAE;AACtE,UAAM,YAAY,MAAM;AACtB,YAAM,MAAM,WAAW,eAAA,IAAmB,UAAU,oBAAI,MAAM;AAC9D,kBAAY,GAAG;AAAA,IACjB;AAEA,WACE,qBAAC,SAAA,EAAQ,MAAY,cAAc,SAKjC,UAAA;AAAA,MAAA,qBAAC,SAAA,EAAQ,MAAM,uBAAuB,SAAY,OAChD,UAAA;AAAA,QAAA,oBAAC,gBAAA,EAAe,SAAO,MAAC,MAAM,QAC5B,UAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YAKA,IAAI,WAAW,SAAa,WAAU,qCAAU;AAAA,YAChD,MAAM,WAAW,SAAY;AAAA,YAC7B,UAAU,WAAW,SAAa,WAAW,KAAK;AAAA,YAClD,iBAAe,WAAW,SAAa,YAAY;AAAA,YACnD,cAAY,WAAW,SAAY;AAAA,YACnC,mBAAiB,WAAW,SAAa,uBAAsB,qCAAU;AAAA,YACzE,gBAAc,WAAW,SAAa,SAAS;AAAA,YAC/C,iBAAe,WAAW,UAAa,qCAAU,aAAY;AAAA,YAC7D,oBAAkB,WAAW,SAAa,wBAAuB,qCAAU;AAAA,YAC3E,qBAAmB,WAAW,SAAa,yBAAyB,QAAQ,qCAAU,UAAU;AAAA,YAChG,iBAAe,WAAW,SAAY;AAAA,YACtC,iBAAe,WAAW,SAAY;AAAA,YACtC,iBAAe,YAAY,CAAC,OAAO,SAAY;AAAA,YAC/C,mBAAgB;AAAA,YAChB,cAAY,QAAQ,KAAK;AAAA,YAMzB,WAAW,WAAW,SAAY,CAAC,MAAM;AACvC,kBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AAAE,kBAAE,eAAA;AAAkB,wBAAQ,IAAI;AAAA,cAAE;AAC5E,kBAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,YACvC;AAAA,YACA,WAAW;AAAA,cACT,mBAAmB,EAAE,MAAM,QAAQ,SAAkB,OAAO,MAAM,OAAO;AAAA,cACzE;AAAA;AAAA,cAEA;AAAA,cACA;AAAA,YAAA;AAAA,YAED,GAAG;AAAA,YAEH,UAAA;AAAA,cAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAUC;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,KAAK;AAAA,oBACL,MAAK;AAAA,oBACL,IAAI,WAAU,qCAAU;AAAA,oBACxB,MAAK;AAAA,oBACL,iBAAc;AAAA,oBACd,iBAAe;AAAA,oBACf,iBAAe,OAAO,UAAU;AAAA,oBAChC,cAAY;AAAA,oBACZ,mBAAiB,uBAAsB,qCAAU;AAAA,oBACjD,kBAAe,qCAAU,aAAY;AAAA,oBACrC,oBAAkB,wBAAuB,qCAAU;AAAA,oBACnD,qBAAmB,yBAA0B,gBAAgB,QAAS,qCAAU,UAAU;AAAA,oBAC1F;AAAA,oBACA,WAAW,GAAG,iBAAiB,YAAY,CAAC,cAAc,2BAA2B;AAAA,oBACrF,OAAO;AAAA,oBACP,aAAa;AAAA,oBACb,gBAAc,gBAAgB,SAAS;AAAA,oBACvC,UAAU,CAAC,MAAM;AAAE,oCAAc,EAAE,OAAO,KAAK;AAAG,sCAAgB,KAAK;AAAA,oBAAE;AAAA,oBACzE,oBAAoB,MAAM;AAAE,mCAAa,UAAU;AAAA,oBAAK;AAAA,oBACxD,kBAAkB,MAAM;AAAE,mCAAa,UAAU;AAAA,oBAAM;AAAA,oBACvD,WAAW,CAAC,MAAM;AAChB,0BAAI,aAAa,QAAS;AAC1B,0BAAI,EAAE,QAAQ,SAAS;AAAE,0BAAE,eAAA;AAAkB,0CAAkB,UAAU;AAAA,sBAAE;AAC3E,0BAAI,EAAE,QAAQ,UAAU;AAAE,sCAAc,WAAW;AAAG,wCAAgB,KAAK;AAAG,0BAAE,eAAA;AAAA,sBAAiB;AACjG,0BAAI,EAAE,QAAQ,eAAe,CAAC,MAAM;AAAE,0BAAE,eAAA;AAAkB,2CAAmB,UAAU;AAAO,gCAAQ,IAAI;AAAA,sBAAE;AAAA,oBAC9G;AAAA,oBACA,QAAQ,MAAM;AAAE,0BAAI,CAAC,aAAa,QAAS,mBAAkB,UAAU;AAAA,oBAAE;AAAA,oBAOzE,eAAe,MAAM;AAAE,yCAAmB,UAAU;AAAA,oBAAK;AAAA,kBAAA;AAAA,gBAAA;AAAA;AAAA;AAAA,gBAI3D,oBAAC,QAAA,EAAK,KAAK,sBAAsB,WAAW,GAAG,iBAAiB,YAAY,CAAC,gBAAgB,eAAe,GACzG,UAAA,YAAA,CACH;AAAA;AAAA,cAED,aACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAM,QAAQ;AAAA,kBACd,QAAQ;AAAA,oBACN,MAAM;AAAA,oBACN,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKP,SAAS,CAAC,MAAM;AACd,6CAAG;AACH,2DAAW;AACX,+BAAS,IAAI;AAAA,oBACf;AAAA,kBAAA;AAAA,gBACF;AAAA,cAAA;AAAA,cAGJ,oBAAC,YAAA,EAAW,WAAU,uBACpB,UAAA,oBAACA,UAAA,EAAa,MAAM,UAAU,WAAU,iBAAgB,eAAW,KAAA,CAAC,EAAA,CACtE;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,GAEJ,EAAA,CACF;AAAA,QACA,oBAAC,gBAAA,EAAgB,UAAA,uBAAuB,cAAc,KAAA,CAAK;AAAA,MAAA,GAC7D;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAU;AAAA,UACV,OAAM;AAAA,UACN,cAAW;AAAA,UAQX,UAAA,qBAAC,OAAA,EAAI,WAAU,gCAGb,UAAA;AAAA,YAAA,oBAAC,YAAA,EAAW,WAAU,kBACpB,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,UACE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,UAAU;AAAA,oBACV,UAAU,CAAC,SAAS;AAClB,0BAAI,CAAC,KAAM;AACX,0BAAI,UAAU;AACZ,oCAAY,mBAAmB,MAAM,SAAS,CAAC;AAAA,sBACjD,OAAO;AACL,oCAAY,UAAU,IAAI,CAAC;AAC3B,4BAAI,CAAC,YAAa,SAAQ,KAAK;AAAA,sBACjC;AAAA,oBACF;AAAA,oBACA,cAAc,eAAe;AAAA,oBAC7B,WAAS;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGb,WACE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,CAAC,SAAS;AAClB,4BAAM,SAAS,aAAa,oBAAI,KAAA;AAChC,kCAAY,mBAAmB,QAAQ,IAAI,CAAC;AAAA,oBAC9C;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA,GAGN;AAAA,aACE,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAUZ,qBAAC,eAAA,EAAc,WAAU,uCACvB,UAAA;AAAA,cAAA,oBAAC,QAAA,EAAO,SAAQ,YAAW,MAAK,MAAK,SAAS,WAAW,WAAU,WAAU,UAAA,KAAA,CAAE;AAAA,cAC9E,cACC,oBAAC,QAAA,EAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,eAAe,UAAU,CAAC,OAAO,gBAAE,IAEhF,oBAAC,QAAA,EAAO,SAAQ,YAAW,MAAK,MAAK,SAAS,MAAM,QAAQ,KAAK,GAAG,UAAA,KAAA,CAAE;AAAA,YAAA,EAAA,CAE1E;AAAA,UAAA,EAAA,CAEJ;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GACF;AAAA,EAEJ;AACF;AACA,WAAW,cAAc;AAsDzB,MAAM,kBAAkB,MAAM;AAAA,EAC5B,CACE;AAAA,IACE;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,OAAO,YAAY;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,cAAc;AAAA,IACd,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,IAAI;AAAA,IACJ,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,WAAW,gBAAA;AACjB,UAAM,OAAO,qBAAqB,QAAQ;AAC1C,UAAM,QAAQ,wBAAwB,SAAS;AAC/C,UAAM,WAAW,yBAAyB,YAAY;AAEtD,UAAM,eAAe,qBAAqB,EAAE,MAAM,UAAU;AAC5D,UAAM,eAAe,qBAAA;AACrB,UAAM,UAAgC,wBAAwB,WAAW;AACzE,UAAM,aAAa,iBAAiB;AAEtC,UAAM,WAAW,UAAU,IAA0B;AACnD,UAAM,cAAc,mBAAmB;AACvC,UAAM,sBAAwC,gBAC5C,WAAW,CAAC,mBAAmB,eAAe,IAAI,CAAC,cAAc,UAAU;AAG7E,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,UAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAyC,SAAS,CAAC,MAAM,IAAI,CAAC;AAC9F,UAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA0B,OAAO;AAOzE,UAAM,cAAc,MAAM,OAA0B,IAAI;AACxD,UAAM,YAAY,MAAM,OAA0B,IAAI;AACtD,UAAM,0BAA0B,MAAM,OAAO,KAAK;AAQlD,UAAM,cAAc,MAAM,OAAO,IAAI;AACrC,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,QAAQ,CAAC,YAAY,kBAAkB,SAAS,CAAC,MAAM,IAAI,CAAC;AACjE,kBAAY,UAAU;AAAA,IACxB,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,UAAM,YAAY,cAAc,MAAM,CAAC,IAAI,+BAAQ,OAAO;AAC1D,UAAM,UAAU,cAAc,MAAM,CAAC,IAAI,+BAAQ,OAAO;AACxD,UAAM,YAAY,MAAM,QAAQ,MAAM,UAAU,QAAQ,GAAG,CAAC,QAAQ,CAAC;AACrE,UAAM,UAAU,MAAM,QAAQ,MAAM,UAAU,MAAM,GAAG,CAAC,MAAM,CAAC;AAC/D,UAAM,WAAW,CAAC,GAAE,+BAAQ,QAAM,+BAAQ;AAC1C,UAAM,YAAY,aAAa,YAAY;AAE3C,UAAM,YAAY,WACd,qBAAqB,UAAU,UAAU,aAAa,EAAE,eAAe,OAAA,CAAQ,IAC/E,oBAAoB,CAAC;AACzB,UAAM,UAAU,SACZ,qBAAqB,QAAQ,UAAU,aAAa,EAAE,eAAe,OAAA,CAAQ,IAC7E,oBAAoB,CAAC;AAOzB,UAAM,EAAE,KAAK,oBAAoB,aAAa,eAAA,IAAmB,aAA8B;AAAA,MAC7F,MAAM,CAAC,WAAW,YAAY,EAAE;AAAA,IAAA,CACjC;AACD,UAAM,EAAE,KAAK,kBAAkB,aAAa,aAAA,IAAiB,aAA8B;AAAA,MACzF,MAAM,CAAC,SAAS,UAAU,EAAE;AAAA,IAAA,CAC7B;AACD,UAAM,qBAAqB,CAAC,CAAC,YAAY,kBAAkB,CAAC;AAC5D,UAAM,mBAAmB,CAAC,CAAC,UAAU,gBAAgB,CAAC;AAEtD,UAAM,YAAY,cAAc,UAAU,WAAW;AACrD,UAAM,aAAa,cAAc,UAAU,YAAY;AACvD,UAAM,aAAa,eAAe,SAAS,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,EAAA;AAMjF,UAAM,iBAAiB,MAAM,QAAQ,MAAM;AACzC,YAAM,OAAoE,CAAA;AAC1E,UAAI,gBAAgB,aAAa;AACjC,UAAI,cAAc,WAAW;AAC7B,UAAI,aAAa,SAAS;AACxB,cAAM,cAAc,QAAQ,WAAW,CAAC;AACxC,cAAM,YAAY,QAAQ,SAAS,EAAE;AACrC,YAAI,aAAa,aAAa;AAC5B,eAAK,cAAc,EAAE,MAAM,aAAa,IAAI,UAAA;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,CAAC,WAAW,OAAO,CAAC;AAYvB,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA+B,IAAI;AAEnF,UAAM,iBAAiB,MAAM,OAAoB,IAAI;AACrD,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,MAAM;AAAE,yBAAiB,IAAI;AAAG,uBAAe,UAAU;AAAA,MAAK;AAAA,IACrE,GAAG,CAAC,IAAI,CAAC;AACT,UAAM,aAAa,MAAM;AAAA,MACvB,CAAC,WAAwB,oBAAoB,EAAE,WAAW,OAAO,WAAW,KAAK,SAAS,QAAQ;AAAA,MAClG,CAAC,WAAW,WAAW,OAAO;AAAA,IAAA;AAEhC,UAAM,mBAAmB,MAAM;AAAA,MAC7B,OAAO;AAAA;AAAA,QAEL,cAAc,CAAC,QAAc,WAAW,GAAG,MAAM;AAAA,QACjD,GAAG,sBAAsB,YAAW,+CAAe,QAAO,IAAI,CAAC;AAAA,MAAA;AAAA,MAEjE,CAAC,YAAY,aAAa;AAAA,IAAA;AAG5B,UAAM,cAAc,CAAC,SAAyC;AAC5D,UAAI,sBAAsB,IAAI;AAAA,WACzB;AAAE,6CAAW;AAAO,iBAAS,IAAI;AAAA,MAAE;AAAA,IAC1C;AACA,UAAM,YAAY,CAAC,QAAuB;AACxC,YAAM,YAAY,cAAc,UAC3B,CAAC,KAAK,MAAM,CAAC,CAAC,IACd,CAAC,MAAM,CAAC,GAAG,GAAG;AACnB,kBAAY,SAAS;AAAA,IACvB;AAQA,UAAM,gBAAgB,MAAM;AAC1B,UAAI,YAAY,cAAc,WAAW,MAAM,CAAC,GAAG;AAEjD,qBAAa,KAAK;AAAA,MACpB,OAAO;AAEL,6CAAW;AACX,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AACA,UAAM,YAAY,MAAM;AACtB,gBAAU,WAAW,eAAA,IAAmB,UAAU,oBAAI,KAAA,CAAM,CAAC;AAAA,IAC/D;AACA,UAAM,mBAAmB,CAAC,MAAyB;AACjD,6BAAG;AAGH,2CAAW,CAAC,MAAM,IAAI;AACtB,eAAS,CAAC,MAAM,IAAI,CAAC;AAAA,IACvB;AACA,UAAM,iBAAiB,CAAC,UAA2B;AACjD,mBAAa,KAAK;AAClB,cAAQ,IAAI;AAAA,IACd;AAOA,UAAM,oBAAoB,MAAM,YAAY,CAAC,SAAwB;AAEnE,UAAI,cAAc,SAAS,WAAW;AACpC,cAAM,gBAAgB,IAAI,KAAK,UAAU,SAAS;AAClD,sBAAc,SAAS,GAAG,GAAG,GAAG,CAAC;AACjC,eAAO,KAAK,YAAY,cAAc,QAAA;AAAA,MACxC;AACA,UAAI,cAAc,WAAW,SAAS;AACpC,cAAM,cAAc,IAAI,KAAK,QAAQ,SAAS;AAC9C,oBAAY,SAAS,IAAI,IAAI,IAAI,GAAG;AACpC,eAAO,KAAK,YAAY,YAAY,QAAA;AAAA,MACtC;AACA,aAAO;AAAA,IACT,GAAG,CAAC,WAAW,WAAW,OAAO,CAAC;AAGlC,QAAI,iBAAiB,QAAQ;AAC3B,YAAM,SAAS,CAAC,EAAE,YAAY;AAC9B,UAAI,CAAC,OAAQ,QAAO,oBAAC,QAAA,EAAK,WAAW,GAAG,qBAAqB,YAAY,GAAG,SAAS,GAAI,UAAA,aAAA,CAAa;AACtG,kCACG,QAAA,EAAK,WAAW,GAAG,oCAAoC,uBAAuB,SAAS,GAEtF,UAAA;AAAA,QAAA,oBAAC,iBAAc,WAAW,GAAG,CAAC,YAAY,eAAe,GACtD,UAAA,WAAW,qBAAqB,UAAU,UAAU,aAAa,EAAE,eAAe,OAAA,CAAQ,IAAI,oBAAoB,CAAC,GACtH;AAAA,4BACC,YAAA,EAAW,MAAM,UAAU,WAAU,+BAA8B,eAAW,MAAC;AAAA,QAChF,oBAAC,iBAAc,WAAW,GAAG,CAAC,UAAU,eAAe,GACpD,UAAA,SAAS,qBAAqB,QAAQ,UAAU,aAAa,EAAE,eAAe,QAAQ,IAAI,oBAAoB,CAAC,EAAA,CAClH;AAAA,MAAA,GACF;AAAA,IAEJ;AAGA,QAAI,CAAC,YAAY;AACf,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,WAAW,GAAG,mBAAmB,EAAE,MAAM,cAAc,SAAkB,OAAO,MAAM,GAAG,SAAS;AAAA,UAClG,mBAAiB;AAAA,UACjB,iBAAe,iBAAiB,aAAa,OAAO;AAAA,UACnD,GAAG;AAAA,UAGJ,UAAA;AAAA,YAAA,oBAAC,eAAA,EAAc,WAAW,GAAG,UAAU,CAAC,YAAY,iBAAiB,iBAAiB,cAAc,kBAAkB,GACnH,qBAAW,qBAAqB,UAAU,UAAU,aAAa,EAAE,eAAe,QAAQ,IAAI,oBAAoB,CAAC,EAAA,CACtH;AAAA,YACA,oBAAC,YAAA,EAAW,MAAM,UAAU,WAAW,GAAG,iBAAiB,iBAAiB,aAAa,qBAAqB,eAAe,GAAG,eAAW,KAAA,CAAC;AAAA,YAC5I,oBAAC,eAAA,EAAc,WAAW,GAAG,UAAU,CAAC,UAAU,iBAAiB,iBAAiB,cAAc,kBAAkB,GACjH,mBAAS,qBAAqB,QAAQ,UAAU,aAAa,EAAE,eAAe,QAAQ,IAAI,oBAAoB,CAAC,EAAA,CAClH;AAAA,YAEC,iBAAiB,cAChB,oBAAC,YAAA,EAAW,WAAU,uBACpB,UAAA,oBAACA,UAAA,EAAa,MAAM,UAAU,WAAU,oBAAmB,eAAW,MAAC,EAAA,CACzE;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAIR;AAEA,WACE,qBAAC,SAAA,EAAQ,MAAY,cAAc,SACjC,UAAA;AAAA,MAAA,oBAAC,eAAA,EAAc,SAAO,MACpB,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,IAAI,WAAU,qCAAU;AAAA,UACxB,gBAAc,SAAS;AAAA,UACvB,kBAAe,qCAAU,aAAY;AAAA,UACrC,oBAAkB,wBAAuB,qCAAU;AAAA,UACnD,qBAAmB,yBAAyB,QAAQ,qCAAU,UAAU;AAAA,UACxE,mBAAgB;AAAA,UAChB,cAAY,QAAQ,KAAK;AAAA,UACzB,cAAY,OAAO,SAAS;AAAA,UAC5B,WAAW;AAAA,YACT,mBAAmB,EAAE,MAAM,QAAQ,OAAO,MAAM,OAAO;AAAA,YACvD;AAAA,YACA;AAAA,UAAA;AAAA,UAED,GAAG;AAAA,UAKJ,UAAA;AAAA,YAAA,qBAAC,SAAA,EAAQ,MAAM,qBAAqB,SAAY,OAC9C,UAAA;AAAA,cAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,KAAK;AAAA,kBACL,SAAS,MAAM,eAAe,OAAO;AAAA,kBACrC,mBAAiB,QAAQ,cAAc,UAAU,SAAS;AAAA,kBAC1D,cAAY,oBAAoB,CAAC;AAAA,kBACjC,iBAAc;AAAA,kBACd,iBAAe,QAAQ,cAAc;AAAA,kBACrC,WAAW;AAAA,oBACT;AAAA;AAAA,oBAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAMA;AAAA,oBACA,CAAC,YAAY;AAAA,kBAAA;AAAA,kBAGf,8BAAC,QAAA,EAAK,KAAK,oBAAoB,WAAU,kBAAkB,UAAA,UAAA,CAAU;AAAA,gBAAA;AAAA,cAAA,GAEzE;AAAA,cACA,oBAAC,gBAAA,EAAgB,UAAA,qBAAqB,YAAY,KAAA,CAAK;AAAA,YAAA,GACzD;AAAA,gCACC,YAAA,EAAW,MAAM,UAAU,WAAU,+BAA8B,eAAW,MAAC;AAAA,YAChF,qBAAC,SAAA,EAAQ,MAAM,mBAAmB,SAAY,OAC5C,UAAA;AAAA,cAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,KAAK;AAAA,kBACL,SAAS,MAAM,eAAe,KAAK;AAAA,kBACnC,mBAAiB,QAAQ,cAAc,QAAQ,SAAS;AAAA,kBACxD,cAAY,oBAAoB,CAAC;AAAA,kBACjC,iBAAc;AAAA,kBACd,iBAAe,QAAQ,cAAc;AAAA,kBACrC,WAAW;AAAA,oBACT;AAAA;AAAA,oBAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAMA;AAAA,oBACA,CAAC,UAAU;AAAA,kBAAA;AAAA,kBAGb,8BAAC,QAAA,EAAK,KAAK,kBAAkB,WAAU,kBAAkB,UAAA,QAAA,CAAQ;AAAA,gBAAA;AAAA,cAAA,GAErE;AAAA,cACA,oBAAC,gBAAA,EAAgB,UAAA,mBAAmB,UAAU,KAAA,CAAK;AAAA,YAAA,GACrD;AAAA,YACC,aACC;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAM,QAAQ;AAAA,gBACd,QAAQ;AAAA,kBACN,MAAM;AAAA,kBACN,OAAO;AAAA;AAAA,kBACP,SAAS;AAAA,gBAAA;AAAA,cACX;AAAA,YAAA;AAAA,YAGJ,oBAAC,YAAA,EAAW,WAAU,uBACpB,UAAA,oBAACA,UAAA,EAAa,MAAM,UAAU,WAAU,iBAAgB,eAAW,KAAA,CAAC,EAAA,CACtE;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA,GAEJ;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAM;AAAA,UACN,cAAW;AAAA,UAKX,mBAAmB,MAAM;AAAE,oCAAwB,UAAU;AAAA,UAAK;AAAA,UAClE,kBAAkB,CAAC,MAAM;;AACvB,cAAE,eAAA;AACF,gBAAI,CAAC,wBAAwB,SAAS;AACnC,eAAC,oBAAc,UAAU,cAAc,WAAW,YAAjD,mBAA0D;AAAA,YAC9D;AACA,oCAAwB,UAAU;AAAA,UACpC;AAAA,UAKA,UAAA,qBAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,YAAA,oBAAC,YAAA,EAAW,WAAU,kBACtB,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,UACE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAKC,MAAK;AAAA,oBACL,UAAU;AAAA,oBACV,UAAU,CAAC,SAAS;AAClB,0BAAI,CAAC,KAAM;AACX,0BAAI,kBAAkB,IAAI,EAAG;AAC7B,4BAAM,gBAAgB,eAAe,cAAc,UAAU,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK;AACrF,4BAAM,UAAU,WACZ,mBAAmB,MAAM,aAAa,IACtC,UAAU,IAAI;AAClB,4BAAM,YAA4C,cAAc,UAC5D,CAAC,SAAS,MAAM,CAAC,CAAC,IAClB,CAAC,MAAM,CAAC,GAAG,OAAO;AACtB,kCAAY,SAAS;AAErB,0BAAI,CAAC,UAAU;AAEb,4BAAI,cAAc,SAAS;AACzB,uCAAa,KAAK;AAClB,8BAAI,CAAC,eAAe,UAAU,CAAC,KAAK,UAAU,CAAC,EAAG,SAAQ,KAAK;AAAA,wBACjE,WAAW,CAAC,eAAe,UAAU,CAAC,KAAK,UAAU,CAAC,GAAG;AACvD,kCAAQ,KAAK;AAAA,wBACf;AAAA,sBACF;AAAA,oBAGF;AAAA,oBAIA,WAAW,EAAE,GAAI,WAAW,CAAA,IAAK,gBAAiB,GAAG,iBAAA;AAAA,oBACrD,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAUnB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,wBAKV;AAAA;AAAA,wBAEA;AAAA;AAAA,wBAEA,uBAAuB;AAAA,sBAAA;AAAA,sBAEzB,UAAU;AAAA;AAAA,wBAER;AAAA,wBACA;AAAA,wBACA,uBAAuB;AAAA,sBAAA;AAAA,sBAEzB,aAAa;AAAA,wBACX,uBAAuB;AAAA;AAAA;AAAA;AAAA,wBAIvB;AAAA,sBAAA;AAAA;AAAA,sBAGF,cAAc;AAAA,sBACd,eAAe,yBAAyB;AAAA,sBACxC,cAAc,yBAAyB;AAAA,sBACvC,eAAe,yBAAyB;AAAA,sBACxC,YAAY,yBAAyB;AAAA,oBAAA;AAAA,oBAGvC,iBAAiB,CAAC,QAAQ,iBAAiB,EAAE,KAAK,QAAQ,WAAW;AAAA,oBACrE,iBAAiB,MAAM,iBAAiB,CAAC,SAAS;AAChD,2BAAI,6BAAM,YAAW,UAAW,QAAO;AACvC,6BAAO,eAAe,UAAU,EAAE,KAAK,eAAe,SAAS,QAAQ,eAAe;AAAA,oBACxF,CAAC;AAAA,oBACD,YAAY,CAAC,KAAK,YAAY,UAAU;AACtC,0BAAI,CAAC,MAAM,cAAc,QAAQ,gBAAgB,EAAG;AACpD,qCAAe,UAAU;AACzB,uCAAiB,EAAE,KAAK,QAAQ,WAAA,CAAY;AAAA,oBAC9C;AAAA,oBACA,WAAW,MAAM;AACf,qCAAe,UAAU;AACzB,uCAAiB,CAAC,UAAU,6BAAM,YAAW,aAAa,OAAO,IAAK;AAAA,oBACxE;AAAA,oBAEA,UAAU;AAAA,oBAEV,gBAAgB,WAAW,IAAI;AAAA,oBAC/B,cAAc,cAAc,aAAa,WAAW;AAAA,oBACpD,WAAS;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGb,WACE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,CAAC,SAAS;AAClB,4BAAM,SAAS,cAAc,oBAAI,KAAA;AACjC,gCAAU,mBAAmB,QAAQ,IAAI,CAAC;AAAA,oBAC5C;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA,GAGJ;AAAA,aACE,YAAY;AAAA;AAAA;AAAA;AAAA,YAKd,qBAAC,eAAA,EAAc,WAAU,uCACtB,UAAA;AAAA,cAAA,CAAC,YACA,oBAAC,QAAA,EAAO,SAAQ,YAAW,MAAK,MAAK,SAAS,WAAW,WAAU,WAAU,UAAA,KAAA,CAAE;AAAA,cAEhF,cACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS;AAAA,kBAET,UACE,WACK,cAAc,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAC1D,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;AAAA,kBAE5B,UAAA;AAAA,gBAAA;AAAA,cAAA,IAID,oBAAC,QAAA,EAAO,SAAQ,YAAW,MAAK,MAAK,SAAS,MAAM,QAAQ,KAAK,GAAG,UAAA,KAAA,CAAE;AAAA,YAAA,EAAA,CAE1E;AAAA,UAAA,EAAA,CAEF;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GACF;AAAA,EAEJ;AACF;AACA,gBAAgB,cAAc;AAI9B,MAAM,sBAAsB,OAAO,OAAO,YAAY,EAAE,OAAO,iBAAiB;AAIzE,MAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA;AAAA;AAAA,EAKP,QAAQ,CAAC,WAAW,SAAS,YAAY,iBAAiB,UAAU;AAAA,EACpE,QAAQ;AAAA,IACN,IAAI,CAAC,qBAAqB;AAAA,IAC1B,IAAI,CAAC,oBAAoB,eAAe;AAAA,IACxC,MAAM,CAAA;AAAA,EAAC;AAEX;"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @internal — DatePicker.Range 的「區間預覽」判定 —— 純函式,供判定表逐格驗(scripts/test-range-preview.mjs)。
3
+ * 不進 barrel / subpath(consumer 用不到它;只有 DatePicker 內部與測試消費)。
4
+ *
5
+ * SSOT:`date-picker.spec.md`「區間預覽」(規則表 + user 2026-09-23 原話);
6
+ * 畫法 owner:`../DateGrid/date-grid.tsx` `RANGE_PREVIEW_CLASSNAMES`(本檔只算「框哪幾天」,不管怎麼畫)。
7
+ *
8
+ * 要保證的性質(逐字):**停留(滑鼠 / 鍵盤焦點)在某一天時,框出「現在點下去,區間會變成從哪到哪」。**
9
+ * - 正在選結束日、開始日已選:anchor ≥ 開始日 → [開始日, anchor];anchor 在開始日之前是不可點的日子,不預覽
10
+ * - 正在選開始日、結束日已選:anchor ≤ 結束日 → [anchor, 結束日];之後的不可點,不預覽
11
+ * - 對面那一端還空(第一次點之前):沒有另一端可以框 → 不預覽(停留格照舊畫單格圈,由 DateGrid 管)
12
+ * - anchor 與既有端點同一天:單格(完整一圈,兩端都是它)
13
+ *
14
+ * 全部以「日」比較(去掉時分秒):showTime 的端點帶時間,但預覽只關心日曆格。
15
+ */
16
+ export type RangePreviewActiveEnd = 'start' | 'end';
17
+ export interface RangePreviewInput {
18
+ /** 正在選哪一端(DatePicker.Range 的 activeEnd) */
19
+ activeEnd: RangePreviewActiveEnd;
20
+ /** 已選的開始日(可含時間;DatePicker 的 isoToDate 回 undefined,這裡兩種空值都收) */
21
+ start: Date | null | undefined;
22
+ /** 已選的結束日(可含時間) */
23
+ end: Date | null | undefined;
24
+ /** 停留日:滑鼠所在或鍵盤焦點所在的日曆格 */
25
+ anchor: Date | null | undefined;
26
+ }
27
+ export interface RangePreview {
28
+ from: Date;
29
+ to: Date;
30
+ }
31
+ /** react-day-picker `modifiers` 的形狀:單日 = Date,連續多日 = { from, to }(RDP 以「日」比對) */
32
+ export interface RangePreviewModifiers {
33
+ previewSingle?: Date;
34
+ previewStart?: Date;
35
+ previewMiddle?: {
36
+ from: Date;
37
+ to: Date;
38
+ };
39
+ previewEnd?: Date;
40
+ }
41
+ export declare function startOfDay(date: Date): Date;
42
+ /** 停留日 → 「點下去會變成」的區間;不該預覽時回 null。 */
43
+ export declare function computeRangePreview({ activeEnd, start, end, anchor }: RangePreviewInput): RangePreview | null;
44
+ /** 區間 → DateGrid 的四個 preview modifier(單日 / 起點 / 中段 / 終點)。 */
45
+ export declare function rangePreviewModifiers(preview: RangePreview | null): RangePreviewModifiers;
46
+ //# sourceMappingURL=range-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range-preview.d.ts","sourceRoot":"","sources":["../../../src/components/DatePicker/range-preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,KAAK,CAAA;AAEnD,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,SAAS,EAAE,qBAAqB,CAAA;IAChC,+DAA+D;IAC/D,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAA;IAC9B,mBAAmB;IACnB,GAAG,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAA;IAC5B,0BAA0B;IAC1B,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAA;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,EAAE,EAAE,IAAI,CAAA;CACT;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,IAAI,CAAA;IACpB,YAAY,CAAC,EAAE,IAAI,CAAA;IACnB,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAE,CAAA;IACxC,UAAU,CAAC,EAAE,IAAI,CAAA;CAClB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAI3C;AAQD,qCAAqC;AACrC,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI,CAa7G;AAED,6DAA6D;AAC7D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,qBAAqB,CAUzF"}
@@ -0,0 +1,41 @@
1
+ function startOfDay(date) {
2
+ const day = new Date(date.getTime());
3
+ day.setHours(0, 0, 0, 0);
4
+ return day;
5
+ }
6
+ function addDays(date, days) {
7
+ const next = new Date(date.getTime());
8
+ next.setDate(next.getDate() + days);
9
+ return next;
10
+ }
11
+ function computeRangePreview({ activeEnd, start, end, anchor }) {
12
+ if (!anchor) return null;
13
+ const day = startOfDay(anchor);
14
+ if (activeEnd === "end") {
15
+ if (!start) return null;
16
+ const from = startOfDay(start);
17
+ if (day.getTime() < from.getTime()) return null;
18
+ return { from, to: day };
19
+ }
20
+ if (!end) return null;
21
+ const to = startOfDay(end);
22
+ if (day.getTime() > to.getTime()) return null;
23
+ return { from: day, to };
24
+ }
25
+ function rangePreviewModifiers(preview) {
26
+ if (!preview) return {};
27
+ const from = startOfDay(preview.from);
28
+ const to = startOfDay(preview.to);
29
+ if (from.getTime() === to.getTime()) return { previewSingle: from };
30
+ const modifiers = { previewStart: from, previewEnd: to };
31
+ const middleFrom = addDays(from, 1);
32
+ const middleTo = addDays(to, -1);
33
+ if (middleTo.getTime() >= middleFrom.getTime()) modifiers.previewMiddle = { from: middleFrom, to: middleTo };
34
+ return modifiers;
35
+ }
36
+ export {
37
+ computeRangePreview,
38
+ rangePreviewModifiers,
39
+ startOfDay
40
+ };
41
+ //# sourceMappingURL=range-preview.js.map