@qijenchen/design-system 0.1.0-beta.120 → 0.1.0-beta.122

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.
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../../src/components/DatePicker/date-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAwB,UAAU,EAAE,MAAM,8CAA8C,CAAA;AAW7H,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,oDAAoD,CAAA;AAK3D,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,aAAa,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAA;IAC1C,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAsBD,iBAAS,UAAU,CACjB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC7B,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAoBR;AA4ND,MAAM,WAAW,eACf,SAAQ,iBAAiB,EACvB,IAAI,CACF,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,cAAc,CACtD;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB;8EAC0E;IAC1E,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,sBAAsB;IACtB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,oBAAoB;IACpB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mFAAmF;IACnF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAsZD,MAAM,WAAW,oBACf,SAAQ,iBAAiB,EACvB,IAAI,CACF,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,cAAc,CACtD;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB;8EAC0E;IAC1E,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,CAAA;IAC1D,uDAAuD;IACvD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAGD,QAAA,MAAM,eAAe,6FA2cpB,CAAA;AAKD,QAAA,MAAM,mBAAmB;;CAAwD,CAAA;AAIjF,eAAO,MAAM,cAAc;;;;;;;;;;;CAiBjB,CAAA;AAEV,OAAO,EACL,mBAAmB,IAAI,UAAU,EACjC,eAAe,EACf,UAAU,GACX,CAAA"}
1
+ {"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../../src/components/DatePicker/date-picker.tsx"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { ArrowRight, Calendar, X } from "lucide-react";
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { nakedCellRowModeAlign, fieldWrapperStyles, bareInputStyles, fieldDisplayTextClass } from "../Field/field-wrapper.js";
6
+ import { ScrollArea } from "../ScrollArea/scroll-area.js";
6
7
  import { ICON_SIZE, ItemSuffix, ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
7
8
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../Popover/popover.js";
8
9
  import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/tooltip.js";
@@ -415,7 +416,7 @@ const DatePicker = React.forwardRef(
415
416
  align: "start",
416
417
  "aria-label": "日期選擇",
417
418
  children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
418
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx(
419
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(
419
420
  CalendarTimeContainer,
420
421
  {
421
422
  showTime,
@@ -719,7 +720,7 @@ const DatePickerRange = React.forwardRef(
719
720
  hasInteractedOutsideRef.current = false;
720
721
  },
721
722
  children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
722
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx(
723
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(
723
724
  CalendarTimeContainer,
724
725
  {
725
726
  showTime,
@@ -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 { 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 pt-3 = 12px top 對齊 DateGrid p-3 top\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// ⚠️ 若改 DateGrid p-3(例如 p-2)→ 必同步改 TimePicker pt-3,否則 caption 行錯位。\n// 兩處共識在 spec.md「Spacing canonical」段 + 本 comment 雙鎖。\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-3', 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 * - Calendar icon 仍 click 開 popover(Material/Ant idiom)\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 // 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-visible:outline-none',\n className,\n )}\n {...props}\n >\n {typeable ? (\n // Issue 10 typed input(2026-05-10):real `<input>` 接 user 鍵盤打字。\n // Click 在 input 上不 propagate 給外層 popover trigger(避免每次打字都開 popover)。\n // Calendar icon `<ItemSuffix>` 點才開 popover(Material/Ant typed-date idiom)。\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 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(); setOpen(true) }\n }}\n onBlur={() => { if (!composingRef.current) handleInputCommit(inputDraft) }}\n onClick={(e) => e.stopPropagation()}\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 區包 overflow-y-auto:viewport 壓縮時 calendar 內滾(Material / Carbon\n date picker idiom)。footer 永遠 in-view(SurfaceFooter shrink-0)。 */}\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\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 </div>\n {(showTime || needConfirm) && (\n // Footer:消費 SurfaceFooter SSOT(border-t + canonical px-loose py-tight padding,\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>\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']\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 'truncate text-left cursor-pointer focus-visible:outline-none',\n 'data-[active-end=true]: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 'truncate text-left cursor-pointer focus-visible:outline-none',\n 'data-[active-end=true]: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 <div className=\"flex-1 min-h-0 overflow-y-auto\">\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 </div>\n {(showTime || needConfirm) && (\n // Footer 消費 SurfaceFooter SSOT(border-t + canonical px-loose py-tight)。\n // showTime Range 無「此刻」(對齊 Ant `showNow={multiple ? false : showNow}`)→ 只有 確定 走 justify-end。\n // date-only Range needConfirm:左 此刻(mr-auto)+ 右 確定。\n <SurfaceFooter>\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 } 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 pt-3 = 12px top 對齊 DateGrid p-3 top\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// ⚠️ 若改 DateGrid p-3(例如 p-2)→ 必同步改 TimePicker pt-3,否則 caption 行錯位。\n// 兩處共識在 spec.md「Spacing canonical」段 + 本 comment 雙鎖。\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-3', 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 * - Calendar icon 仍 click 開 popover(Material/Ant idiom)\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 // 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-visible:outline-none',\n className,\n )}\n {...props}\n >\n {typeable ? (\n // Issue 10 typed input(2026-05-10):real `<input>` 接 user 鍵盤打字。\n // Click 在 input 上不 propagate 給外層 popover trigger(避免每次打字都開 popover)。\n // Calendar icon `<ItemSuffix>` 點才開 popover(Material/Ant typed-date idiom)。\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 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(); setOpen(true) }\n }}\n onBlur={() => { if (!composingRef.current) handleInputCommit(inputDraft) }}\n onClick={(e) => e.stopPropagation()}\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 + canonical px-loose py-tight padding,\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>\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 'truncate text-left cursor-pointer focus-visible:outline-none',\n 'data-[active-end=true]: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 'truncate text-left cursor-pointer focus-visible:outline-none',\n 'data-[active-end=true]: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 + canonical px-loose py-tight)。\n // showTime Range 無「此刻」(對齊 Ant `showNow={multiple ? false : showNow}`)→ 只有 確定 走 justify-end。\n // date-only Range needConfirm:左 此刻(mr-auto)+ 右 確定。\n <SurfaceFooter>\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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"}
@@ -72,6 +72,8 @@ export interface SelectMenuProps {
72
72
  searchAriaLabel?: string;
73
73
  /** 空選項提示 */
74
74
  emptyText?: string;
75
+ /** 載入中狀態的無障礙文案(i18n:consumer 換語言時覆寫) */
76
+ loadingText?: string;
75
77
  /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */
76
78
  selectAllLabel?: string;
77
79
  /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)
@@ -1 +1 @@
1
- {"version":3,"file":"select-menu.d.ts","sourceRoot":"","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"select-menu.d.ts","sourceRoot":"","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"names":[],"mappings":"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"}
@@ -36,6 +36,8 @@ const SelectMenu = React.forwardRef(function SelectMenu2({
36
36
  // i18n-allow: DS default; consumer override via searchAriaLabel prop
37
37
  emptyText = "沒有符合的選項",
38
38
  // i18n-allow: DS default; consumer override via emptyText prop
39
+ loadingText = "載入選項中",
40
+ // i18n-allow: DS default; consumer override via loadingText prop
39
41
  selectAllLabel = "全部",
40
42
  // i18n-allow: DS default; consumer override via selectAllLabel prop
41
43
  loading = false,
@@ -201,7 +203,7 @@ const SelectMenu = React.forwardRef(function SelectMenu2({
201
203
  {
202
204
  className: "flex items-center justify-center",
203
205
  style: { minHeight: getMenuListMinHeight(size, minRows) },
204
- children: loading ? /* @__PURE__ */ jsx("div", { role: "status", "aria-label": "載入選項中", className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx(CircularProgress, { size: 48 }) }) : /* @__PURE__ */ jsx(Empty, { description: emptyText, className: "py-6" })
206
+ children: loading ? /* @__PURE__ */ jsx("div", { role: "status", "aria-label": loadingText, className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx(CircularProgress, { size: 48 }) }) : /* @__PURE__ */ jsx(Empty, { description: emptyText, className: "py-6" })
205
207
  }
206
208
  ),
207
209
  groupedOptions.map((group, gi) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
@@ -1 +1 @@
1
- {"version":3,"file":"select-menu.js","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"sourcesContent":["/**\n * @internal — DS-internal 單元(per `packages/design-system/ds-canonical/rules/ui-development.md` Public vs Internal canonical;spec frontmatter `isInternal`)。\n * 不進 root barrel front-door;由 Select / Combobox wrap 消費,end-user app 請用 wrapper 元件。\n */\n// @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.\nimport * as React from 'react'\nimport { Plus, Search } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport type { AvatarData } from '@/design-system/components/Avatar/avatar'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/design-system/components/Popover/popover'\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '@/design-system/components/Command/command'\nimport { Command as CommandPrimitive, useCommandState } from 'cmdk'\nimport { MenuItem, MenuFooter } from '@/design-system/components/Menu/menu-item'\nimport { Empty } from '@/design-system/components/Empty/empty'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { OVERLAY_SIDE_OFFSET } from '@/design-system/tokens/elevation/overlay-geometry'\nimport { getMenuListMinHeight } from '@/design-system/components/Field/field-types'\nimport { RowSizeProvider } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { applySelectAll, clearSelection } from '@/design-system/lib/multi-select-ordering'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SelectMenu — Popover + Command 組成的完整下拉選單\n *\n * ── 功能 ──\n * 單選 / 多選、搜尋過濾、分組、可建立新選項(creatable)\n * 多選有 footer「全部」checkbox\n *\n * ── 架構 ──\n * Popover(浮動容器)\n * └── Command(cmdk,搜尋 + 鍵盤導覽)\n * ├── CommandPrimitive.Input(搜尋框,raw cmdk + 自建 icon/min-h wrapper,非 DS CommandInput)\n * ├── CommandList(選項列表)\n * │ └── CommandGroup → MenuItem\n * └── Footer(多選全選)\n */\n\n// ── Types ──\n\nexport interface SelectMenuOption {\n value: string\n label: string\n description?: string\n icon?: LucideIcon\n /** icon 染色 className(2026-07-08:status 類彩色 option;M30 primitive schema 擴充) */\n iconClassName?: string\n avatar?: AvatarData\n disabled?: boolean\n group?: string\n}\n\nexport interface SelectMenuGroupConfig {\n key: string\n label: string\n}\n\ntype SizeKey = 'sm' | 'md' | 'lg'\n\n// ── Component ──\n\nexport interface SelectMenuProps {\n /** 選項列表 */\n options: SelectMenuOption[]\n /** 群組定義(key 對應 option.group) */\n groups?: SelectMenuGroupConfig[]\n\n /** 當前值(單選 string,多選 string[]) */\n value?: string | string[] | null\n /** 值變更 callback */\n onValueChange?: (value: string | string[]) => void\n\n /** 多選模式 */\n multiple?: boolean\n /** 顯示搜尋框 */\n searchable?: boolean\n /** 可建立新選項 */\n creatable?: boolean\n /** 建立新選項 callback */\n onCreate?: (value: string) => void\n /** creatable 的 label 格式,預設 '直接使用「{query}」' */\n createLabel?: (query: string) => string\n /**\n * 受控搜尋字串(2026-07-18 決策11:讓 consumer 用**外部搜尋**(如 Select 的 trigger 內嵌 input)\n * 驅動 creatable create-row 顯隱)。傳入 = 受控(SelectMenu 不自管 search、close 不 reset,由\n * parent 負責);不傳 = 內部 uncontrolled(既有行為,零影響)。搭配 `searchable=false` +\n * `creatable` 時,SelectMenu 不畫自己的 input,但 create-row 仍依此 search 顯示。\n */\n search?: string\n /** 受控搜尋變更通知(選配;SelectMenu 內部無 input 時通常由 parent 單向驅動,不需此) */\n onSearchChange?: (value: string) => void\n\n /** 觸發元件(asChild) */\n children: React.ReactNode\n /** 搜尋框 placeholder */\n searchPlaceholder?: string\n /** 搜尋框 accessible name;與可見 placeholder 分離。 */\n searchAriaLabel?: string\n /** 空選項提示 */\n emptyText?: string\n /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */\n selectAllLabel?: string\n /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)\n * true → 無可顯示選項時 empty slot(cmdk CommandEmpty)渲可命名的\n * `role=\"status\"` wrapper + 48px CircularProgress;已有 options 時保留顯示不清空\n * (MUI Autocomplete「only if there are no suggestions」共識)。trigger 不變,user 隨時可開 dropdown。\n */\n loading?: boolean\n\n /** 尺寸 */\n size?: SizeKey\n /** 對齊方式 */\n align?: 'start' | 'end'\n /** 列表最少顯示幾行選項高度(預設 3),影響空狀態最小高度 */\n minRows?: number\n /** 最小寬度(px),預設跟隨觸發元件 */\n minWidth?: number\n\n /** 受控 open 狀態 */\n open?: boolean\n /** 預設打開(uncontrolled initial state)— 2026-05-15 audit Dim 26 V1 fix per user verbatim「A:1」approval */\n defaultOpen?: boolean\n /** open 狀態變更 callback */\n onOpenChange?: (open: boolean) => void\n\n /** 自訂選項 label 渲染(預設渲染 option.label 純文字) */\n renderLabel?: (option: SelectMenuOption) => React.ReactNode\n /** 攔截 PopoverContent 的 onOpenAutoFocus(如 Select searchable 需阻止 focus 搶走) */\n onOpenAutoFocus?: (e: Event) => void\n\n /**\n * Popover 內容容器的 DOM id(set 在 PopoverContent 外層 div,**非** cmdk 內層 `role=\"listbox\"` 本身)。\n * Combobox / 自定 trigger 用 `aria-controls` 指向此 id 時,指向的是 **popover 容器(listbox 的 ancestor)**——\n * AT 經此容器可定位到內部 cmdk listbox(cmdk List 自帶 auto-generated `role=\"listbox\"` id)。\n */\n contentId?: string\n\n /**\n * 浮層 accessible name(2026-07-17 Dim 10 a11y 修)。Radix Popover 的 content 為\n * `role=\"dialog\"` 但無自動命名機制 → 無 aria-label 時 dialog 無 accessible name。\n * 預設「選項清單」讓 SR 使用者知道浮層用途;consumer(Combobox / Select / PeoplePicker)\n * 可傳更 contextual 的名(如欄位名)。SSOT 放此一處 → 全 SelectMenu consumer 受益。\n */\n 'aria-label'?: string\n\n className?: string\n}\n\n// ── SR live status(2026-07-05 D4:empty / loading 空狀態對 SR 不可感知修)──\n// cmdk CommandEmpty 渲染為 role=\"presentation\" div、cmdk 全鏈無 aria-live,且 DOM focus 停在\n// combobox input(aria-activedescendant 虛擬焦點)→ SR 使用者搜尋到 0 結果或 loading 佔位時\n// 聽不到任何播報(loading spinner 的 CircularProgress 無 label 時更是 aria-hidden)。\n// 補 visually-hidden polite live region,鏡射 CommandEmpty 的無結果文字。Loading 改由可見\n// spinner 的 `role=\"status\"` + `aria-label` 直接宣告,避免同一狀態重複播報。\n// cmdk Empty 只在 filtered.count === 0 渲染。對齊 react-select A11yText /\n// APG combobox no-results 播報 + empty.spec.md「動態 filter no-results 容器需 aria-live=\"polite\"」。\n// SSOT 放 SelectMenu 一處 → Select / Combobox / PeoplePicker 全體受益。\nfunction SelectMenuLiveStatus({ loading, emptyText }: { loading: boolean; emptyText: string }) {\n const filteredCount = useCommandState((state) => state.filtered.count)\n return (\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {filteredCount === 0\n ? (loading ? null : emptyText)\n : null}\n </div>\n )\n}\n\n// shadcn canonical:forwardRef + displayName 統一。SelectMenu 是 Popover + Command\n// composite,自身無 DOM host(trigger 由 consumer 以 asChild children 提供),ref 簽名\n// 保留但不附著(consumer 想取 trigger DOM 直接在 children 上自己 ref)。className 合併到\n// PopoverContent(contextually 最接近 user-facing surface)。\nconst SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function SelectMenu({\n options,\n groups,\n value,\n onValueChange,\n multiple = false,\n searchable = false,\n creatable = false,\n onCreate,\n createLabel = (q) => `直接使用「${q}」`,\n search: controlledSearch,\n onSearchChange,\n children,\n searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop\n searchAriaLabel = '搜尋選項', // i18n-allow: DS default; consumer override via searchAriaLabel prop\n emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop\n selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop\n loading = false,\n size = 'md',\n align = 'start',\n minRows = 3,\n minWidth,\n open: controlledOpen,\n defaultOpen,\n onOpenChange: controlledOnOpenChange,\n renderLabel,\n onOpenAutoFocus,\n contentId,\n 'aria-label': ariaLabel = '選項清單', // i18n-allow: DS default; consumer override via aria-label prop\n className,\n}, _ref) {\n // ── State ──\n // 2026-06-11 R2 bug fix:原手寫 `setOpen = controlledOnOpenChange ?? setInternalOpen` 在\n // uncontrolled + onOpenChange listener 場景(傳 onOpenChange 不傳 open)會讓 listener 蓋掉\n // internal setter → menu 開不了。改消費 DS 既有 useControllable(select.tsx 同 canonical):\n // uncontrolled 時 internal state 為準、onOpenChange 僅通知。\n const [open, setOpen] = useControllable<boolean>({\n value: controlledOpen,\n defaultValue: defaultOpen ?? false,\n onChange: controlledOnOpenChange,\n })\n // search:選配受控(決策11)。傳 `search` prop = 受控(parent 驅動,如 Select trigger input);\n // 不傳 = 內部 uncontrolled(既有行為)。setSearch 同步內部 + 通知 onSearchChange。\n const isSearchControlled = controlledSearch !== undefined\n const [internalSearch, setInternalSearch] = React.useState('')\n const search = isSearchControlled ? controlledSearch : internalSearch\n const setSearch = React.useCallback(\n (next: string) => {\n if (!isSearchControlled) setInternalSearch(next)\n onSearchChange?.(next)\n },\n [isSearchControlled, onSearchChange],\n )\n\n // ── Value helpers ──\n const selectedValues = React.useMemo<string[]>(() => {\n if (value == null) return []\n return Array.isArray(value) ? value : [value]\n }, [value])\n\n const isSelected = React.useCallback(\n (v: string) => selectedValues.includes(v),\n [selectedValues]\n )\n\n // 2026-07-05 P2:單選已選 option — 供 cmdk defaultValue 定 cursor 起點(見下方 <Command>)\n const selectedOption = React.useMemo(\n () => (!multiple ? options.find((o) => o.value === selectedValues[0]) : undefined),\n [multiple, options, selectedValues]\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const next = isSelected(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n onValueChange?.(next)\n } else {\n onValueChange?.(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, isSelected, onValueChange, setOpen]\n )\n\n // ── Multi-select: select all ──\n const selectableOptions = React.useMemo(\n () => options.filter((o) => !o.disabled),\n [options]\n )\n\n const allState: boolean | 'indeterminate' = React.useMemo(() => {\n if (!multiple) return false\n const count = selectableOptions.filter((o) => isSelected(o.value)).length\n if (count === 0) return false\n if (count === selectableOptions.length) return true\n return 'indeterminate'\n }, [multiple, selectableOptions, isSelected])\n\n // 2026-05-16 SSOT canonical fix(Claude+Codex M31 Round 4 共識 + user verbatim「就照你們\n // 的共識做到完美確保有 SSOT」):\n //\n // 原 fully-replace `selectableOptions.map(v)` = source order reset,但**Ant Design 跨元件 grep\n // 證據顯示 source-reset 沒 Ant precedent**(Transfer + Table rowSelection 都是 preserve+append)。\n // 改 `applySelectAll(selectedValues, all)` SSOT primitive 對齊 Ant Transfer canonical:\n // `Array.from(new Set([...prevKeys, ...keys]))` — preserve existing + append unselected。\n //\n // SSOT in `@/design-system/lib/multi-select-ordering` — 未來新 multi-select with Select All\n // footer 必 consume 此 primitive(hook `check_select_all_canonical.sh` 機械強制),\n // 不再各自 reimplement → 防 ordering policy drift。\n const handleSelectAll = React.useCallback(() => {\n if (!multiple) return\n if (allState === true) {\n onValueChange?.(clearSelection())\n } else {\n onValueChange?.(applySelectAll(selectedValues, selectableOptions.map((o) => o.value)))\n }\n }, [multiple, allState, selectableOptions, selectedValues, onValueChange])\n\n // ── Creatable ──\n const showCreate = React.useMemo(() => {\n if (!creatable || !search.trim()) return false\n return !options.some(\n (o) => o.label.toLowerCase() === search.trim().toLowerCase()\n )\n }, [creatable, search, options])\n\n // ── Grouping ──\n const groupedOptions = React.useMemo(() => {\n if (!groups?.length) return [{ key: '__default', label: '', options }]\n const grouped = groups.map((g) => ({\n ...g,\n options: options.filter((o) => o.group === g.key),\n }))\n const ungrouped = options.filter((o) => !o.group)\n if (ungrouped.length) {\n grouped.unshift({ key: '__default', label: '', options: ungrouped })\n }\n return grouped\n }, [groups, options])\n\n // ── Reset search on close(僅 uncontrolled;受控時由 parent 負責 reset)──\n React.useEffect(() => {\n if (!open && !isSearchControlled) setInternalSearch('')\n }, [open, isSearchControlled])\n\n // 2026-06-01 Select/Combobox #15(user 拍板 A):非搜尋時開選單把 focus 移到 cmdk-root,\n // 讓 cmdk 內建方向鍵 / Enter / Home / End 導覽生效。原 PopoverContent default autofocus 找 body\n // input/button,非搜尋無 input + 選項是 role=option div → focus 落在 content wrapper,cmdk 的 keydown\n // handler 綁在 cmdk-root 收不到事件 → 桌機鍵盤導覽不可達(WAI-ARIA combobox 違反)。\n // **純鍵盤路由**:滑鼠點擊路徑完全不碰;cmdk active-option highlight 是 cmdk 內部 state(非 DOM focus\n // 驅動)故視覺不變。selector miss 時 root=null → no-op fallback(無回歸)。\n // SSOT 在 SelectMenu → Select / Combobox / 所有 non-searchable SelectMenu consumer 自動受益。\n const handleNonSearchableAutoFocus = React.useCallback((e: Event) => {\n e.preventDefault()\n const root = (e.currentTarget as HTMLElement).querySelector<HTMLElement>('[cmdk-root]')\n root?.focus({ preventScroll: true })\n }, [])\n\n // RowSizeProvider 讓 PopoverContent 子樹內任何 <ItemIcon> / <ItemAvatar> /\n // <ItemInlineAction> 都自動讀到對的 size,跟 SidebarProvider / TreeView 同一條規則。\n // (注:Popover 透過 Portal 渲染,context 仍然會跨 portal 傳遞——React context 是 tree-based\n // 不是 DOM-based,Portal 不影響 context propagation)\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <RowSizeProvider value={size}>\n <PopoverContent\n id={contentId}\n // 2026-07-17 Dim 10 a11y 修:role=\"dialog\" 浮層 accessible name(Radix Popover 無自動命名)\n aria-label={ariaLabel}\n // w-auto override PopoverContent default w-72(rich-popover canonical)— SelectMenu 走「跟 trigger 同寬」\n // canonical(spec L72)。minWidth = max(trigger-width, 240px sensible-min)— 對齊 shadcn / Material / Ant\n // select dropdown 共識(2026-05-04 D1 verify SelectMenu spec implementation)。\n className={cn(\n 'p-0 w-auto rounded-lg border border-border bg-surface-raised overflow-hidden',\n className\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(var(--radix-popover-trigger-width), 15rem)',\n }}\n align={align}\n sideOffset={OVERLAY_SIDE_OFFSET}\n onOpenAutoFocus={onOpenAutoFocus ?? (!searchable ? handleNonSearchableAutoFocus : undefined)}\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger\n // 區的 OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在\n // PopoverContent 內)— Radix DismissableLayer document-level outside\n // detection 跨 portal 視為「outside」→ SelectMenu 被誤關閉。\n // 攔 `onPointerDownOutside`,檢查 click target 是否在另一個 Radix portal\n // 內,是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect\n // tagRender 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 其他 SelectMenu\n // consumer 自動受益。\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger 區的\n // OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在 SelectMenu\n // PopoverContent 內)— Radix DismissableLayer document-level pointerdown +\n // focusin 偵測「outside」→ SelectMenu 被誤關閉。\n // 攔 `onInteractOutside`(統一 pointerdown + focusin),檢查 click target 是否\n // 在另一個 Radix portal wrapper(`[data-radix-popper-content-wrapper]`),\n // 是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect tagRender\n // 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 所有 SelectMenu\n // consumer 自動受益。\n onInteractOutside={(e) => {\n const target = e.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-radix-popper-content-wrapper]')) {\n e.preventDefault()\n }\n }}\n >\n <Command\n shouldFilter={searchable}\n // 2026-07-06 cursor 起點修:單選已有值時 cmdk virtual focus 落在已選項而非第一項。\n // cmdk 1.1.1 初始 state 取 defaultValue、item mount 的 selectFirstItem 有\n // `state.value ||` guard 不覆蓋(dist source 驗證);Popover 關閉即 unmount(無\n // forceMount)→ 每次開啟 remount 重新生效。需搭配下方 CommandItem value={opt.value}。\n defaultValue={selectedOption?.value}\n // 2026-07-06 A11y:combobox accessible name。2026-07-14 修正註解(cmdk 1.1.1 dist 實證):\n // cmdk **無條件**渲 sr-only <label htmlFor={inputId}>(dist/index.mjs\n // `createElement(\"label\",{htmlFor:U.inputId,id:U.labelId,...},b)` — label prop 只決定\n // 文字內容,不決定 label 元素是否渲染)。故 non-searchable 時「空 label + htmlFor 指向\n // 不存在的 input」仍存在(upstream cmdk 限制;空 label 無 accessible name,不產生朗讀\n // 內容)。僅 searchable(真的有 input)時傳內容 = 避免 non-searchable 帶 search\n // placeholder 的 accessible name 卻無對應 input;真要消除 orphan label 需 upstream 修。\n label={searchable ? searchAriaLabel : undefined}\n className=\"bg-transparent\"\n >\n {searchable && (\n <div className={cn(\n 'flex items-center gap-2 px-3 py-1 border-b border-divider',\n size === 'lg' ? 'min-h-[calc(var(--field-height-lg)+8px)]'\n : size === 'sm' ? 'min-h-[calc(var(--field-height-sm)+8px)]'\n : 'min-h-[calc(var(--field-height-md)+8px)]',\n )}>\n <Search size={ICON_SIZE[size as 'sm' | 'md' | 'lg']} className=\"shrink-0 text-fg-muted\" aria-hidden />\n <CommandPrimitive.Input\n placeholder={searchPlaceholder}\n value={search}\n onValueChange={setSearch}\n className={cn(\n 'flex w-full bg-transparent outline-none placeholder:text-fg-muted',\n // M24 disabled state precedence:disabled 時 placeholder 切 fg-disabled(audit dim 34)\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',\n size === 'lg' ? 'text-body-lg leading-compact' : 'text-body leading-compact',\n )}\n />\n </div>\n )}\n {/* **2026-05-07 v15.13 R2 fix**:minHeight 從 CommandList 搬到 CommandEmpty。\n 原本 CommandList 永遠套 `minHeight = field-height × minRows + 16px`,結果\n user 過濾出 < minRows 個 match 時 list 底下空一片(eg. 打 'c' 出 2 個 match\n 卻撐高到 3 row 容量,1 row 留白)。 Fix:只有 empty state 才需要 minHeight 撐\n 起 placeholder 視覺;有 results 時 CommandList 自然 fit content。 */}\n {/* aria-busy(2026-07-04):loading 時標注 listbox 忙碌——兌現 select.spec.md「Loading」段\n 「+ aria-busy」承諾(cmdk List 本身即 role=\"listbox\" 容器,wrapper forward props)。 */}\n <CommandList\n className=\"relative\"\n aria-busy={loading || undefined}\n // 2026-07-06 A11y:cmdk List 的 aria-label 由其 `label` prop 渲染(內部 spread 後 override,\n // 直接傳 aria-label 會被 cmdk default \"Suggestions\" 蓋掉 silent 失效)— 必走 label prop。\n label=\"選項\" // i18n-allow: DS default; listbox accessible name\n >\n <CommandEmpty\n className=\"flex items-center justify-center\"\n style={{ minHeight: getMenuListMinHeight(size, minRows) }}\n >\n {loading\n ? (\n <div role=\"status\" aria-label=\"載入選項中\" className=\"flex items-center justify-center py-6\">\n <CircularProgress size={48} />\n </div>\n )\n : <Empty description={emptyText} className=\"py-6\" />}\n </CommandEmpty>\n\n {groupedOptions.map((group, gi) => (\n <React.Fragment key={group.key}>\n {gi > 0 && <CommandSeparator />}\n <CommandGroup\n className=\"p-0 py-2 [&_[cmdk-group-heading]]:p-0\"\n // 2026-07-06 A11y:群組標題走 cmdk `heading`(自動產 cmdk-group-heading id,選項容器\n // role=\"group\" + aria-labelledby 指向之)取代手刻 child row(原本 AT 不可感知;\n // aria-label fallback 對 role=\"presentation\" group 無效)。[&_[cmdk-group-heading]]:p-0\n // 中和 command.tsx base 的 px-3 py-1.5(tailwind-merge p-0 勝);字級 / 顏色由 MenuItem\n // header 自帶(text-body leading-compact + font-medium + text-fg-muted),視覺不變。\n heading={group.label ? <MenuItem size={size} header>{group.label}</MenuItem> : undefined}\n >\n {group.options.map((opt) => (\n <CommandItem\n key={opt.value}\n // 2026-07-06 識別值修:cmdk 以 value 當 item identity + selection state。原用\n // opt.label → 重複 label 時雙亮 + Enter 選錯;改唯一的 opt.value,label 移入\n // keywords 保搜尋照樣命中(cmdk filter 對 value + keywords 計分)。\n value={opt.value}\n keywords={opt.description ? [opt.label, opt.description] : [opt.label]}\n disabled={opt.disabled}\n onSelect={() => handleSelect(opt.value)}\n // 2026-07-05 D4 P0 修:原 data-[selected=true]:bg-transparent 蓋掉 command.tsx base\n // 的 data-[selected=true]:bg-neutral-hover = cmdk virtual focus(aria-activedescendant)\n // 鍵盤 cursor 唯一視覺通道被抹掉 → 方向鍵移動畫面零變化(影響 Select/Combobox/PeoplePicker\n // 全家)。鏡射 DropdownMenu wrapper pattern:互動 bg 單一 owner 在外層 CommandItem\n //(base highlight 恢復;persistent selection 上移;selected×cursor 加深一階 —\n // 對齊本日 DropdownMenu Q2 決策),內層 MenuItem 全透明。\n className={cn(\n 'p-0 rounded-none',\n // 2026-08-11 user 拍板糾正 D4 實作縫(SSOT = item-anatomy「選中 × 互動疊加」):\n // cmdk 反白訊號滑鼠也觸發,深化本應僅限鍵盤(D4 原意即「鍵盤 cursor」)。\n // 滑鼠 hover 選中項 → 釘住 bg-neutral-selected 不變(滑鼠自有游標,不需底色指位);\n // 鍵盤反白(data-selected 且非 :hover)→ -focus 深一階(游標可見,WCAG 2.4.7)。\n // not-hover 編譯為 :not(*:hover) + @media not (hover:hover)(POC 已驗),\n // 深化 (0,3,0) > 釘住 (0,2,0),與 CSS 順序無關;token 從借用的 -active 歸位 -focus。\n !multiple && isSelected(opt.value) && 'bg-neutral-selected data-[selected=true]:bg-neutral-selected data-[selected=true]:not-hover:bg-neutral-selected-focus',\n )}\n >\n <MenuItem\n size={size}\n startIcon={opt.icon}\n startIconClassName={opt.iconClassName}\n avatar={opt.avatar}\n description={opt.description}\n checkbox={multiple}\n checked={isSelected(opt.value)}\n selected={!multiple && isSelected(opt.value)}\n disabled={opt.disabled}\n // 2026-07-05 D4:巢狀 role 修 — MenuItem 預設 role=\"option\" + aria-selected(persistent\n // selection)巢狀在 cmdk CommandItem(本身 role=\"option\" + aria-selected=cursor)內,\n // AT 讀到 option 包 option 且內外 aria-selected 語意相反 → 朗讀錯亂。鏡射 DropdownMenu\n // canonical(dropdown-menu.tsx「Pure visual — Radix parent handles role/aria」):\n // 內層純視覺 role=\"presentation\",cmdk CommandItem 是唯一 option 節點。\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent\"\n >\n {renderLabel ? renderLabel(opt) : opt.label}\n </MenuItem>\n </CommandItem>\n ))}\n </CommandGroup>\n </React.Fragment>\n ))}\n\n {/* Creatable item */}\n {showCreate && (\n <>\n <CommandSeparator />\n <CommandGroup className=\"p-0 py-2\">\n <CommandItem\n // 2026-07-06:`__create__` 前綴防 identity 撞名 — 選項 row 改用 opt.value 識別後,\n // search 恰等於某 option.value 時裸 search 會與該 row 同 value(cmdk 雙亮 + Enter\n // 選錯)。value 含 search 子字串,cmdk filter 照樣命中;onSelect closure 讀 search\n // 不受影響。\n value={`__create__${search}`}\n onSelect={() => {\n onCreate?.(search.trim())\n setSearch('')\n }}\n className=\"p-0 rounded-none\"\n >\n {/* 2026-07-05 D4:同上方選項 row 的巢狀 role 修 — creatable row 同樣是\n MenuItem 巢狀在 cmdk CommandItem(option)內,內層純視覺 role=\"presentation\"。 */}\n <MenuItem size={size} startIcon={Plus} role=\"presentation\" className=\"!bg-transparent hover:!bg-transparent\">\n {createLabel(search.trim())}\n </MenuItem>\n </CommandItem>\n </CommandGroup>\n </>\n )}\n </CommandList>\n\n {/* SR 播報 empty / loading 空狀態(見 SelectMenuLiveStatus docblock,2026-07-05 D4) */}\n <SelectMenuLiveStatus loading={loading} emptyText={emptyText} />\n\n {/* Multi-select footer: Select All\n - 沒有選項時不顯示(selectableOptions.length === 0)\n - 搜尋有文字時不顯示(search 非空 = 使用者在找特定項目,「全選」沒意義) */}\n {multiple && selectableOptions.length > 0 && !search && (\n <MenuFooter>\n {/* 2026-07-05 D4:全選列鍵盤可達修 — 原裸 MenuItem(div 預設 role=\"option\" 無 tabIndex)\n 位於 CommandList 之外:cmdk 方向鍵只導覽 [cmdk-item]、Tab 也到不了 div → 鍵盤使用者\n 完全無法操作全選(WCAG 2.1.1),且該 role=\"option\" 無 listbox 祖先(orphan,axe\n aria-required-parent)。改真實 focusable checkbox 語意:tabIndex=0 + role=\"checkbox\" +\n aria-checked(indeterminate → \"mixed\")+ Enter / Space 觸發;preventDefault 讓 cmdk root\n onKeyDown(源碼檢查 e.defaultPrevented)不會再對 active option 重複觸發 Enter。\n aria-selected 顯式蓋回 undefined(MenuItem 內建 aria-selected 對 role=\"checkbox\" 無效)。 */}\n <MenuItem\n size={size}\n checkbox\n checked={allState}\n onClick={handleSelectAll}\n role=\"checkbox\"\n aria-checked={allState === 'indeterminate' ? 'mixed' : allState}\n aria-selected={undefined}\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelectAll()\n }\n }}\n >\n {selectAllLabel}\n </MenuItem>\n </MenuFooter>\n )}\n </Command>\n </PopoverContent>\n </RowSizeProvider>\n </Popover>\n )\n})\n\nSelectMenu.displayName = 'SelectMenu'\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 selectMenuMeta = {\n component: 'SelectMenu',\n // 2026-07-14 修 family 自我矛盾:原 Phase-1 mechanical 填 4(Field control),與 spec body\n // 「Layout Family:非上述 family — composite / multi-section」矛盾。SelectMenu 是 Popover +\n // Command composite overlay(trigger 由 consumer Select / Combobox own,Family 4 是 trigger\n // 側 field control 的事);對齊 DS composite 慣例(Accordion / Command / Popover meta 皆\n // family: null + spec frontmatter composite)。\n family: null, // non-family composite / overlay(對齊 spec frontmatter composite)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選 option 持續選中(bg-neutral-selected);'active' 保留 — cmdk virtual-focus on\n // selected(鍵盤反白,非 hover)走 bg-neutral-selected-focus(2026-08-11 token 歸位:-active 回歸按壓專屬)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected', 'bg-neutral-selected-focus', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-muted'],\n ring: [],\n },\n} as const\n\n/**\n * 2026-07-05 D4 P0 修(searchable 鍵盤死路):trigger 內的裸 <input> 與 portal 內的 cmdk root\n * 在不同 DOM 子樹 — 鍵盤事件永遠 bubble 不到 cmdk 的 ArrowUp/Down/Enter handler([cmdk-root]\n * onKeyDown)→ searchable Select / PeoplePicker single / Combobox searchIn='trigger' 開啟後\n * 只能 Esc。修法 = APG combobox-with-list:trigger input 把三鍵 re-dispatch 給 cmdk root\n * (native KeyboardEvent bubbles 經 React root delegation 觸發 cmdk synthetic handler)。\n * Home/End 刻意不轉送(文字輸入的 caret 語意優先,對齊 MUI/Ant Autocomplete)。\n * aria-activedescendant 綁回 trigger input → 見下方 useActiveDescendant(2026-07-05 D4 補齊)。\n */\nexport function forwardKeyToListbox(contentId: string | undefined, e: React.KeyboardEvent): boolean {\n if (!contentId) return false\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Enter') return false\n const root = document.getElementById(contentId)?.querySelector<HTMLElement>('[cmdk-root]')\n if (!root) return false\n e.preventDefault()\n root.dispatchEvent(new KeyboardEvent('keydown', { key: e.key, bubbles: true, cancelable: true }))\n return true\n}\n\n/**\n * 2026-07-05 D4 補齊(APG combobox aria-activedescendant):追蹤 cmdk 目前 virtual-focus item\n * 的 DOM id,供 trigger 端搜尋 input 綁 `aria-activedescendant` —— SR 才會在方向鍵導覽 /\n * 打字過濾時播報 active option 名。機制:trigger 與 portal 內 cmdk 分屬不同 DOM 子樹,cmdk\n * 只把 active id 綁在自己的 Command.Input / List(cmdk source:item 自帶 auto-generated id +\n * `data-selected=\"true\"` 標記 virtual focus)→ trigger 端用 MutationObserver 監聽 popover 容器\n * (contentId = PopoverContent id)內 `data-selected` 屬性變化 + childList(打字過濾 re-render\n * 換 item 節點),單一機制涵蓋全部更新路徑:開啟初始 auto-highlight / forwardKeyToListbox\n * 方向鍵轉送 / 搜尋過濾後 cmdk 自動移 cursor / pointer hover。\n * 關閉時清 undefined —— ARIA 要求 id 必指向存在於 DOM 的節點,不可留 stale id。\n */\nexport function useActiveDescendant(contentId: string | undefined, open: boolean): string | undefined {\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined)\n React.useEffect(() => {\n if (!open || !contentId) {\n setActiveId(undefined)\n return\n }\n // PopoverContent 與 trigger 同一個 React commit mount(open state 同批 render)→ effect 跑時已在 DOM\n const container = document.getElementById(contentId)\n if (!container) return\n const read = () => {\n setActiveId(container.querySelector<HTMLElement>('[cmdk-item][data-selected=\"true\"]')?.id || undefined)\n }\n // 初始補讀:MutationObserver 只看「觀察開始後」的變化;cmdk 初始 auto-highlight(layout effect\n // 排程)可能已 commit → rAF 讀當下狀態兜底,與 observer 互補、誰先到都不漏。\n const raf = requestAnimationFrame(read)\n const observer = new MutationObserver(read)\n observer.observe(container, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-selected'] })\n return () => {\n cancelAnimationFrame(raf)\n observer.disconnect()\n }\n }, [open, contentId])\n return activeId\n}\n\nexport { SelectMenu }\n"],"names":["SelectMenu","CommandPrimitive"],"mappings":"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"}
1
+ {"version":3,"file":"select-menu.js","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"sourcesContent":["/**\n * @internal — DS-internal 單元(per `packages/design-system/ds-canonical/rules/ui-development.md` Public vs Internal canonical;spec frontmatter `isInternal`)。\n * 不進 root barrel front-door;由 Select / Combobox wrap 消費,end-user app 請用 wrapper 元件。\n */\n// @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.\nimport * as React from 'react'\nimport { Plus, Search } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport type { AvatarData } from '@/design-system/components/Avatar/avatar'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/design-system/components/Popover/popover'\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '@/design-system/components/Command/command'\nimport { Command as CommandPrimitive, useCommandState } from 'cmdk'\nimport { MenuItem, MenuFooter } from '@/design-system/components/Menu/menu-item'\nimport { Empty } from '@/design-system/components/Empty/empty'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { OVERLAY_SIDE_OFFSET } from '@/design-system/tokens/elevation/overlay-geometry'\nimport { getMenuListMinHeight } from '@/design-system/components/Field/field-types'\nimport { RowSizeProvider } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { applySelectAll, clearSelection } from '@/design-system/lib/multi-select-ordering'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SelectMenu — Popover + Command 組成的完整下拉選單\n *\n * ── 功能 ──\n * 單選 / 多選、搜尋過濾、分組、可建立新選項(creatable)\n * 多選有 footer「全部」checkbox\n *\n * ── 架構 ──\n * Popover(浮動容器)\n * └── Command(cmdk,搜尋 + 鍵盤導覽)\n * ├── CommandPrimitive.Input(搜尋框,raw cmdk + 自建 icon/min-h wrapper,非 DS CommandInput)\n * ├── CommandList(選項列表)\n * │ └── CommandGroup → MenuItem\n * └── Footer(多選全選)\n */\n\n// ── Types ──\n\nexport interface SelectMenuOption {\n value: string\n label: string\n description?: string\n icon?: LucideIcon\n /** icon 染色 className(2026-07-08:status 類彩色 option;M30 primitive schema 擴充) */\n iconClassName?: string\n avatar?: AvatarData\n disabled?: boolean\n group?: string\n}\n\nexport interface SelectMenuGroupConfig {\n key: string\n label: string\n}\n\ntype SizeKey = 'sm' | 'md' | 'lg'\n\n// ── Component ──\n\nexport interface SelectMenuProps {\n /** 選項列表 */\n options: SelectMenuOption[]\n /** 群組定義(key 對應 option.group) */\n groups?: SelectMenuGroupConfig[]\n\n /** 當前值(單選 string,多選 string[]) */\n value?: string | string[] | null\n /** 值變更 callback */\n onValueChange?: (value: string | string[]) => void\n\n /** 多選模式 */\n multiple?: boolean\n /** 顯示搜尋框 */\n searchable?: boolean\n /** 可建立新選項 */\n creatable?: boolean\n /** 建立新選項 callback */\n onCreate?: (value: string) => void\n /** creatable 的 label 格式,預設 '直接使用「{query}」' */\n createLabel?: (query: string) => string\n /**\n * 受控搜尋字串(2026-07-18 決策11:讓 consumer 用**外部搜尋**(如 Select 的 trigger 內嵌 input)\n * 驅動 creatable create-row 顯隱)。傳入 = 受控(SelectMenu 不自管 search、close 不 reset,由\n * parent 負責);不傳 = 內部 uncontrolled(既有行為,零影響)。搭配 `searchable=false` +\n * `creatable` 時,SelectMenu 不畫自己的 input,但 create-row 仍依此 search 顯示。\n */\n search?: string\n /** 受控搜尋變更通知(選配;SelectMenu 內部無 input 時通常由 parent 單向驅動,不需此) */\n onSearchChange?: (value: string) => void\n\n /** 觸發元件(asChild) */\n children: React.ReactNode\n /** 搜尋框 placeholder */\n searchPlaceholder?: string\n /** 搜尋框 accessible name;與可見 placeholder 分離。 */\n searchAriaLabel?: string\n /** 空選項提示 */\n emptyText?: string\n /** 載入中狀態的無障礙文案(i18n:consumer 換語言時覆寫) */\n loadingText?: string\n /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */\n selectAllLabel?: string\n /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)\n * true → 無可顯示選項時 empty slot(cmdk CommandEmpty)渲可命名的\n * `role=\"status\"` wrapper + 48px CircularProgress;已有 options 時保留顯示不清空\n * (MUI Autocomplete「only if there are no suggestions」共識)。trigger 不變,user 隨時可開 dropdown。\n */\n loading?: boolean\n\n /** 尺寸 */\n size?: SizeKey\n /** 對齊方式 */\n align?: 'start' | 'end'\n /** 列表最少顯示幾行選項高度(預設 3),影響空狀態最小高度 */\n minRows?: number\n /** 最小寬度(px),預設跟隨觸發元件 */\n minWidth?: number\n\n /** 受控 open 狀態 */\n open?: boolean\n /** 預設打開(uncontrolled initial state)— 2026-05-15 audit Dim 26 V1 fix per user verbatim「A:1」approval */\n defaultOpen?: boolean\n /** open 狀態變更 callback */\n onOpenChange?: (open: boolean) => void\n\n /** 自訂選項 label 渲染(預設渲染 option.label 純文字) */\n renderLabel?: (option: SelectMenuOption) => React.ReactNode\n /** 攔截 PopoverContent 的 onOpenAutoFocus(如 Select searchable 需阻止 focus 搶走) */\n onOpenAutoFocus?: (e: Event) => void\n\n /**\n * Popover 內容容器的 DOM id(set 在 PopoverContent 外層 div,**非** cmdk 內層 `role=\"listbox\"` 本身)。\n * Combobox / 自定 trigger 用 `aria-controls` 指向此 id 時,指向的是 **popover 容器(listbox 的 ancestor)**——\n * AT 經此容器可定位到內部 cmdk listbox(cmdk List 自帶 auto-generated `role=\"listbox\"` id)。\n */\n contentId?: string\n\n /**\n * 浮層 accessible name(2026-07-17 Dim 10 a11y 修)。Radix Popover 的 content 為\n * `role=\"dialog\"` 但無自動命名機制 → 無 aria-label 時 dialog 無 accessible name。\n * 預設「選項清單」讓 SR 使用者知道浮層用途;consumer(Combobox / Select / PeoplePicker)\n * 可傳更 contextual 的名(如欄位名)。SSOT 放此一處 → 全 SelectMenu consumer 受益。\n */\n 'aria-label'?: string\n\n className?: string\n}\n\n// ── SR live status(2026-07-05 D4:empty / loading 空狀態對 SR 不可感知修)──\n// cmdk CommandEmpty 渲染為 role=\"presentation\" div、cmdk 全鏈無 aria-live,且 DOM focus 停在\n// combobox input(aria-activedescendant 虛擬焦點)→ SR 使用者搜尋到 0 結果或 loading 佔位時\n// 聽不到任何播報(loading spinner 的 CircularProgress 無 label 時更是 aria-hidden)。\n// 補 visually-hidden polite live region,鏡射 CommandEmpty 的無結果文字。Loading 改由可見\n// spinner 的 `role=\"status\"` + `aria-label` 直接宣告,避免同一狀態重複播報。\n// cmdk Empty 只在 filtered.count === 0 渲染。對齊 react-select A11yText /\n// APG combobox no-results 播報 + empty.spec.md「動態 filter no-results 容器需 aria-live=\"polite\"」。\n// SSOT 放 SelectMenu 一處 → Select / Combobox / PeoplePicker 全體受益。\nfunction SelectMenuLiveStatus({ loading, emptyText }: { loading: boolean; emptyText: string }) {\n const filteredCount = useCommandState((state) => state.filtered.count)\n return (\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {filteredCount === 0\n ? (loading ? null : emptyText)\n : null}\n </div>\n )\n}\n\n// shadcn canonical:forwardRef + displayName 統一。SelectMenu 是 Popover + Command\n// composite,自身無 DOM host(trigger 由 consumer 以 asChild children 提供),ref 簽名\n// 保留但不附著(consumer 想取 trigger DOM 直接在 children 上自己 ref)。className 合併到\n// PopoverContent(contextually 最接近 user-facing surface)。\nconst SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function SelectMenu({\n options,\n groups,\n value,\n onValueChange,\n multiple = false,\n searchable = false,\n creatable = false,\n onCreate,\n createLabel = (q) => `直接使用「${q}」`,\n search: controlledSearch,\n onSearchChange,\n children,\n searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop\n searchAriaLabel = '搜尋選項', // i18n-allow: DS default; consumer override via searchAriaLabel prop\n emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop\n loadingText = '載入選項中', // i18n-allow: DS default; consumer override via loadingText prop\n selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop\n loading = false,\n size = 'md',\n align = 'start',\n minRows = 3,\n minWidth,\n open: controlledOpen,\n defaultOpen,\n onOpenChange: controlledOnOpenChange,\n renderLabel,\n onOpenAutoFocus,\n contentId,\n 'aria-label': ariaLabel = '選項清單', // i18n-allow: DS default; consumer override via aria-label prop\n className,\n}, _ref) {\n // ── State ──\n // 2026-06-11 R2 bug fix:原手寫 `setOpen = controlledOnOpenChange ?? setInternalOpen` 在\n // uncontrolled + onOpenChange listener 場景(傳 onOpenChange 不傳 open)會讓 listener 蓋掉\n // internal setter → menu 開不了。改消費 DS 既有 useControllable(select.tsx 同 canonical):\n // uncontrolled 時 internal state 為準、onOpenChange 僅通知。\n const [open, setOpen] = useControllable<boolean>({\n value: controlledOpen,\n defaultValue: defaultOpen ?? false,\n onChange: controlledOnOpenChange,\n })\n // search:選配受控(決策11)。傳 `search` prop = 受控(parent 驅動,如 Select trigger input);\n // 不傳 = 內部 uncontrolled(既有行為)。setSearch 同步內部 + 通知 onSearchChange。\n const isSearchControlled = controlledSearch !== undefined\n const [internalSearch, setInternalSearch] = React.useState('')\n const search = isSearchControlled ? controlledSearch : internalSearch\n const setSearch = React.useCallback(\n (next: string) => {\n if (!isSearchControlled) setInternalSearch(next)\n onSearchChange?.(next)\n },\n [isSearchControlled, onSearchChange],\n )\n\n // ── Value helpers ──\n const selectedValues = React.useMemo<string[]>(() => {\n if (value == null) return []\n return Array.isArray(value) ? value : [value]\n }, [value])\n\n const isSelected = React.useCallback(\n (v: string) => selectedValues.includes(v),\n [selectedValues]\n )\n\n // 2026-07-05 P2:單選已選 option — 供 cmdk defaultValue 定 cursor 起點(見下方 <Command>)\n const selectedOption = React.useMemo(\n () => (!multiple ? options.find((o) => o.value === selectedValues[0]) : undefined),\n [multiple, options, selectedValues]\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const next = isSelected(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n onValueChange?.(next)\n } else {\n onValueChange?.(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, isSelected, onValueChange, setOpen]\n )\n\n // ── Multi-select: select all ──\n const selectableOptions = React.useMemo(\n () => options.filter((o) => !o.disabled),\n [options]\n )\n\n const allState: boolean | 'indeterminate' = React.useMemo(() => {\n if (!multiple) return false\n const count = selectableOptions.filter((o) => isSelected(o.value)).length\n if (count === 0) return false\n if (count === selectableOptions.length) return true\n return 'indeterminate'\n }, [multiple, selectableOptions, isSelected])\n\n // 2026-05-16 SSOT canonical fix(Claude+Codex M31 Round 4 共識 + user verbatim「就照你們\n // 的共識做到完美確保有 SSOT」):\n //\n // 原 fully-replace `selectableOptions.map(v)` = source order reset,但**Ant Design 跨元件 grep\n // 證據顯示 source-reset 沒 Ant precedent**(Transfer + Table rowSelection 都是 preserve+append)。\n // 改 `applySelectAll(selectedValues, all)` SSOT primitive 對齊 Ant Transfer canonical:\n // `Array.from(new Set([...prevKeys, ...keys]))` — preserve existing + append unselected。\n //\n // SSOT in `@/design-system/lib/multi-select-ordering` — 未來新 multi-select with Select All\n // footer 必 consume 此 primitive(hook `check_select_all_canonical.sh` 機械強制),\n // 不再各自 reimplement → 防 ordering policy drift。\n const handleSelectAll = React.useCallback(() => {\n if (!multiple) return\n if (allState === true) {\n onValueChange?.(clearSelection())\n } else {\n onValueChange?.(applySelectAll(selectedValues, selectableOptions.map((o) => o.value)))\n }\n }, [multiple, allState, selectableOptions, selectedValues, onValueChange])\n\n // ── Creatable ──\n const showCreate = React.useMemo(() => {\n if (!creatable || !search.trim()) return false\n return !options.some(\n (o) => o.label.toLowerCase() === search.trim().toLowerCase()\n )\n }, [creatable, search, options])\n\n // ── Grouping ──\n const groupedOptions = React.useMemo(() => {\n if (!groups?.length) return [{ key: '__default', label: '', options }]\n const grouped = groups.map((g) => ({\n ...g,\n options: options.filter((o) => o.group === g.key),\n }))\n const ungrouped = options.filter((o) => !o.group)\n if (ungrouped.length) {\n grouped.unshift({ key: '__default', label: '', options: ungrouped })\n }\n return grouped\n }, [groups, options])\n\n // ── Reset search on close(僅 uncontrolled;受控時由 parent 負責 reset)──\n React.useEffect(() => {\n if (!open && !isSearchControlled) setInternalSearch('')\n }, [open, isSearchControlled])\n\n // 2026-06-01 Select/Combobox #15(user 拍板 A):非搜尋時開選單把 focus 移到 cmdk-root,\n // 讓 cmdk 內建方向鍵 / Enter / Home / End 導覽生效。原 PopoverContent default autofocus 找 body\n // input/button,非搜尋無 input + 選項是 role=option div → focus 落在 content wrapper,cmdk 的 keydown\n // handler 綁在 cmdk-root 收不到事件 → 桌機鍵盤導覽不可達(WAI-ARIA combobox 違反)。\n // **純鍵盤路由**:滑鼠點擊路徑完全不碰;cmdk active-option highlight 是 cmdk 內部 state(非 DOM focus\n // 驅動)故視覺不變。selector miss 時 root=null → no-op fallback(無回歸)。\n // SSOT 在 SelectMenu → Select / Combobox / 所有 non-searchable SelectMenu consumer 自動受益。\n const handleNonSearchableAutoFocus = React.useCallback((e: Event) => {\n e.preventDefault()\n const root = (e.currentTarget as HTMLElement).querySelector<HTMLElement>('[cmdk-root]')\n root?.focus({ preventScroll: true })\n }, [])\n\n // RowSizeProvider 讓 PopoverContent 子樹內任何 <ItemIcon> / <ItemAvatar> /\n // <ItemInlineAction> 都自動讀到對的 size,跟 SidebarProvider / TreeView 同一條規則。\n // (注:Popover 透過 Portal 渲染,context 仍然會跨 portal 傳遞——React context 是 tree-based\n // 不是 DOM-based,Portal 不影響 context propagation)\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <RowSizeProvider value={size}>\n <PopoverContent\n id={contentId}\n // 2026-07-17 Dim 10 a11y 修:role=\"dialog\" 浮層 accessible name(Radix Popover 無自動命名)\n aria-label={ariaLabel}\n // w-auto override PopoverContent default w-72(rich-popover canonical)— SelectMenu 走「跟 trigger 同寬」\n // canonical(spec L72)。minWidth = max(trigger-width, 240px sensible-min)— 對齊 shadcn / Material / Ant\n // select dropdown 共識(2026-05-04 D1 verify SelectMenu spec implementation)。\n className={cn(\n 'p-0 w-auto rounded-lg border border-border bg-surface-raised overflow-hidden',\n className\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(var(--radix-popover-trigger-width), 15rem)',\n }}\n align={align}\n sideOffset={OVERLAY_SIDE_OFFSET}\n onOpenAutoFocus={onOpenAutoFocus ?? (!searchable ? handleNonSearchableAutoFocus : undefined)}\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger\n // 區的 OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在\n // PopoverContent 內)— Radix DismissableLayer document-level outside\n // detection 跨 portal 視為「outside」→ SelectMenu 被誤關閉。\n // 攔 `onPointerDownOutside`,檢查 click target 是否在另一個 Radix portal\n // 內,是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect\n // tagRender 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 其他 SelectMenu\n // consumer 自動受益。\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger 區的\n // OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在 SelectMenu\n // PopoverContent 內)— Radix DismissableLayer document-level pointerdown +\n // focusin 偵測「outside」→ SelectMenu 被誤關閉。\n // 攔 `onInteractOutside`(統一 pointerdown + focusin),檢查 click target 是否\n // 在另一個 Radix portal wrapper(`[data-radix-popper-content-wrapper]`),\n // 是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect tagRender\n // 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 所有 SelectMenu\n // consumer 自動受益。\n onInteractOutside={(e) => {\n const target = e.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-radix-popper-content-wrapper]')) {\n e.preventDefault()\n }\n }}\n >\n <Command\n shouldFilter={searchable}\n // 2026-07-06 cursor 起點修:單選已有值時 cmdk virtual focus 落在已選項而非第一項。\n // cmdk 1.1.1 初始 state 取 defaultValue、item mount 的 selectFirstItem 有\n // `state.value ||` guard 不覆蓋(dist source 驗證);Popover 關閉即 unmount(無\n // forceMount)→ 每次開啟 remount 重新生效。需搭配下方 CommandItem value={opt.value}。\n defaultValue={selectedOption?.value}\n // 2026-07-06 A11y:combobox accessible name。2026-07-14 修正註解(cmdk 1.1.1 dist 實證):\n // cmdk **無條件**渲 sr-only <label htmlFor={inputId}>(dist/index.mjs\n // `createElement(\"label\",{htmlFor:U.inputId,id:U.labelId,...},b)` — label prop 只決定\n // 文字內容,不決定 label 元素是否渲染)。故 non-searchable 時「空 label + htmlFor 指向\n // 不存在的 input」仍存在(upstream cmdk 限制;空 label 無 accessible name,不產生朗讀\n // 內容)。僅 searchable(真的有 input)時傳內容 = 避免 non-searchable 帶 search\n // placeholder 的 accessible name 卻無對應 input;真要消除 orphan label 需 upstream 修。\n label={searchable ? searchAriaLabel : undefined}\n className=\"bg-transparent\"\n >\n {searchable && (\n <div className={cn(\n 'flex items-center gap-2 px-3 py-1 border-b border-divider',\n size === 'lg' ? 'min-h-[calc(var(--field-height-lg)+8px)]'\n : size === 'sm' ? 'min-h-[calc(var(--field-height-sm)+8px)]'\n : 'min-h-[calc(var(--field-height-md)+8px)]',\n )}>\n <Search size={ICON_SIZE[size as 'sm' | 'md' | 'lg']} className=\"shrink-0 text-fg-muted\" aria-hidden />\n <CommandPrimitive.Input\n placeholder={searchPlaceholder}\n value={search}\n onValueChange={setSearch}\n className={cn(\n 'flex w-full bg-transparent outline-none placeholder:text-fg-muted',\n // M24 disabled state precedence:disabled 時 placeholder 切 fg-disabled(audit dim 34)\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',\n size === 'lg' ? 'text-body-lg leading-compact' : 'text-body leading-compact',\n )}\n />\n </div>\n )}\n {/* **2026-05-07 v15.13 R2 fix**:minHeight 從 CommandList 搬到 CommandEmpty。\n 原本 CommandList 永遠套 `minHeight = field-height × minRows + 16px`,結果\n user 過濾出 < minRows 個 match 時 list 底下空一片(eg. 打 'c' 出 2 個 match\n 卻撐高到 3 row 容量,1 row 留白)。 Fix:只有 empty state 才需要 minHeight 撐\n 起 placeholder 視覺;有 results 時 CommandList 自然 fit content。 */}\n {/* aria-busy(2026-07-04):loading 時標注 listbox 忙碌——兌現 select.spec.md「Loading」段\n 「+ aria-busy」承諾(cmdk List 本身即 role=\"listbox\" 容器,wrapper forward props)。 */}\n <CommandList\n className=\"relative\"\n aria-busy={loading || undefined}\n // 2026-07-06 A11y:cmdk List 的 aria-label 由其 `label` prop 渲染(內部 spread 後 override,\n // 直接傳 aria-label 會被 cmdk default \"Suggestions\" 蓋掉 silent 失效)— 必走 label prop。\n label=\"選項\" // i18n-allow: DS default; listbox accessible name\n >\n <CommandEmpty\n className=\"flex items-center justify-center\"\n style={{ minHeight: getMenuListMinHeight(size, minRows) }}\n >\n {loading\n ? (\n <div role=\"status\" aria-label={loadingText} className=\"flex items-center justify-center py-6\">\n <CircularProgress size={48} />\n </div>\n )\n : <Empty description={emptyText} className=\"py-6\" />}\n </CommandEmpty>\n\n {groupedOptions.map((group, gi) => (\n <React.Fragment key={group.key}>\n {gi > 0 && <CommandSeparator />}\n <CommandGroup\n className=\"p-0 py-2 [&_[cmdk-group-heading]]:p-0\"\n // 2026-07-06 A11y:群組標題走 cmdk `heading`(自動產 cmdk-group-heading id,選項容器\n // role=\"group\" + aria-labelledby 指向之)取代手刻 child row(原本 AT 不可感知;\n // aria-label fallback 對 role=\"presentation\" group 無效)。[&_[cmdk-group-heading]]:p-0\n // 中和 command.tsx base 的 px-3 py-1.5(tailwind-merge p-0 勝);字級 / 顏色由 MenuItem\n // header 自帶(text-body leading-compact + font-medium + text-fg-muted),視覺不變。\n heading={group.label ? <MenuItem size={size} header>{group.label}</MenuItem> : undefined}\n >\n {group.options.map((opt) => (\n <CommandItem\n key={opt.value}\n // 2026-07-06 識別值修:cmdk 以 value 當 item identity + selection state。原用\n // opt.label → 重複 label 時雙亮 + Enter 選錯;改唯一的 opt.value,label 移入\n // keywords 保搜尋照樣命中(cmdk filter 對 value + keywords 計分)。\n value={opt.value}\n keywords={opt.description ? [opt.label, opt.description] : [opt.label]}\n disabled={opt.disabled}\n onSelect={() => handleSelect(opt.value)}\n // 2026-07-05 D4 P0 修:原 data-[selected=true]:bg-transparent 蓋掉 command.tsx base\n // 的 data-[selected=true]:bg-neutral-hover = cmdk virtual focus(aria-activedescendant)\n // 鍵盤 cursor 唯一視覺通道被抹掉 → 方向鍵移動畫面零變化(影響 Select/Combobox/PeoplePicker\n // 全家)。鏡射 DropdownMenu wrapper pattern:互動 bg 單一 owner 在外層 CommandItem\n //(base highlight 恢復;persistent selection 上移;selected×cursor 加深一階 —\n // 對齊本日 DropdownMenu Q2 決策),內層 MenuItem 全透明。\n className={cn(\n 'p-0 rounded-none',\n // 2026-08-11 user 拍板糾正 D4 實作縫(SSOT = item-anatomy「選中 × 互動疊加」):\n // cmdk 反白訊號滑鼠也觸發,深化本應僅限鍵盤(D4 原意即「鍵盤 cursor」)。\n // 滑鼠 hover 選中項 → 釘住 bg-neutral-selected 不變(滑鼠自有游標,不需底色指位);\n // 鍵盤反白(data-selected 且非 :hover)→ -focus 深一階(游標可見,WCAG 2.4.7)。\n // not-hover 編譯為 :not(*:hover) + @media not (hover:hover)(POC 已驗),\n // 深化 (0,3,0) > 釘住 (0,2,0),與 CSS 順序無關;token 從借用的 -active 歸位 -focus。\n !multiple && isSelected(opt.value) && 'bg-neutral-selected data-[selected=true]:bg-neutral-selected data-[selected=true]:not-hover:bg-neutral-selected-focus',\n )}\n >\n <MenuItem\n size={size}\n startIcon={opt.icon}\n startIconClassName={opt.iconClassName}\n avatar={opt.avatar}\n description={opt.description}\n checkbox={multiple}\n checked={isSelected(opt.value)}\n selected={!multiple && isSelected(opt.value)}\n disabled={opt.disabled}\n // 2026-07-05 D4:巢狀 role 修 — MenuItem 預設 role=\"option\" + aria-selected(persistent\n // selection)巢狀在 cmdk CommandItem(本身 role=\"option\" + aria-selected=cursor)內,\n // AT 讀到 option 包 option 且內外 aria-selected 語意相反 → 朗讀錯亂。鏡射 DropdownMenu\n // canonical(dropdown-menu.tsx「Pure visual — Radix parent handles role/aria」):\n // 內層純視覺 role=\"presentation\",cmdk CommandItem 是唯一 option 節點。\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent\"\n >\n {renderLabel ? renderLabel(opt) : opt.label}\n </MenuItem>\n </CommandItem>\n ))}\n </CommandGroup>\n </React.Fragment>\n ))}\n\n {/* Creatable item */}\n {showCreate && (\n <>\n <CommandSeparator />\n <CommandGroup className=\"p-0 py-2\">\n <CommandItem\n // 2026-07-06:`__create__` 前綴防 identity 撞名 — 選項 row 改用 opt.value 識別後,\n // search 恰等於某 option.value 時裸 search 會與該 row 同 value(cmdk 雙亮 + Enter\n // 選錯)。value 含 search 子字串,cmdk filter 照樣命中;onSelect closure 讀 search\n // 不受影響。\n value={`__create__${search}`}\n onSelect={() => {\n onCreate?.(search.trim())\n setSearch('')\n }}\n className=\"p-0 rounded-none\"\n >\n {/* 2026-07-05 D4:同上方選項 row 的巢狀 role 修 — creatable row 同樣是\n MenuItem 巢狀在 cmdk CommandItem(option)內,內層純視覺 role=\"presentation\"。 */}\n <MenuItem size={size} startIcon={Plus} role=\"presentation\" className=\"!bg-transparent hover:!bg-transparent\">\n {createLabel(search.trim())}\n </MenuItem>\n </CommandItem>\n </CommandGroup>\n </>\n )}\n </CommandList>\n\n {/* SR 播報 empty / loading 空狀態(見 SelectMenuLiveStatus docblock,2026-07-05 D4) */}\n <SelectMenuLiveStatus loading={loading} emptyText={emptyText} />\n\n {/* Multi-select footer: Select All\n - 沒有選項時不顯示(selectableOptions.length === 0)\n - 搜尋有文字時不顯示(search 非空 = 使用者在找特定項目,「全選」沒意義) */}\n {multiple && selectableOptions.length > 0 && !search && (\n <MenuFooter>\n {/* 2026-07-05 D4:全選列鍵盤可達修 — 原裸 MenuItem(div 預設 role=\"option\" 無 tabIndex)\n 位於 CommandList 之外:cmdk 方向鍵只導覽 [cmdk-item]、Tab 也到不了 div → 鍵盤使用者\n 完全無法操作全選(WCAG 2.1.1),且該 role=\"option\" 無 listbox 祖先(orphan,axe\n aria-required-parent)。改真實 focusable checkbox 語意:tabIndex=0 + role=\"checkbox\" +\n aria-checked(indeterminate → \"mixed\")+ Enter / Space 觸發;preventDefault 讓 cmdk root\n onKeyDown(源碼檢查 e.defaultPrevented)不會再對 active option 重複觸發 Enter。\n aria-selected 顯式蓋回 undefined(MenuItem 內建 aria-selected 對 role=\"checkbox\" 無效)。 */}\n <MenuItem\n size={size}\n checkbox\n checked={allState}\n onClick={handleSelectAll}\n role=\"checkbox\"\n aria-checked={allState === 'indeterminate' ? 'mixed' : allState}\n aria-selected={undefined}\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelectAll()\n }\n }}\n >\n {selectAllLabel}\n </MenuItem>\n </MenuFooter>\n )}\n </Command>\n </PopoverContent>\n </RowSizeProvider>\n </Popover>\n )\n})\n\nSelectMenu.displayName = 'SelectMenu'\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 selectMenuMeta = {\n component: 'SelectMenu',\n // 2026-07-14 修 family 自我矛盾:原 Phase-1 mechanical 填 4(Field control),與 spec body\n // 「Layout Family:非上述 family — composite / multi-section」矛盾。SelectMenu 是 Popover +\n // Command composite overlay(trigger 由 consumer Select / Combobox own,Family 4 是 trigger\n // 側 field control 的事);對齊 DS composite 慣例(Accordion / Command / Popover meta 皆\n // family: null + spec frontmatter composite)。\n family: null, // non-family composite / overlay(對齊 spec frontmatter composite)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選 option 持續選中(bg-neutral-selected);'active' 保留 — cmdk virtual-focus on\n // selected(鍵盤反白,非 hover)走 bg-neutral-selected-focus(2026-08-11 token 歸位:-active 回歸按壓專屬)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected', 'bg-neutral-selected-focus', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-muted'],\n ring: [],\n },\n} as const\n\n/**\n * 2026-07-05 D4 P0 修(searchable 鍵盤死路):trigger 內的裸 <input> 與 portal 內的 cmdk root\n * 在不同 DOM 子樹 — 鍵盤事件永遠 bubble 不到 cmdk 的 ArrowUp/Down/Enter handler([cmdk-root]\n * onKeyDown)→ searchable Select / PeoplePicker single / Combobox searchIn='trigger' 開啟後\n * 只能 Esc。修法 = APG combobox-with-list:trigger input 把三鍵 re-dispatch 給 cmdk root\n * (native KeyboardEvent bubbles 經 React root delegation 觸發 cmdk synthetic handler)。\n * Home/End 刻意不轉送(文字輸入的 caret 語意優先,對齊 MUI/Ant Autocomplete)。\n * aria-activedescendant 綁回 trigger input → 見下方 useActiveDescendant(2026-07-05 D4 補齊)。\n */\nexport function forwardKeyToListbox(contentId: string | undefined, e: React.KeyboardEvent): boolean {\n if (!contentId) return false\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Enter') return false\n const root = document.getElementById(contentId)?.querySelector<HTMLElement>('[cmdk-root]')\n if (!root) return false\n e.preventDefault()\n root.dispatchEvent(new KeyboardEvent('keydown', { key: e.key, bubbles: true, cancelable: true }))\n return true\n}\n\n/**\n * 2026-07-05 D4 補齊(APG combobox aria-activedescendant):追蹤 cmdk 目前 virtual-focus item\n * 的 DOM id,供 trigger 端搜尋 input 綁 `aria-activedescendant` —— SR 才會在方向鍵導覽 /\n * 打字過濾時播報 active option 名。機制:trigger 與 portal 內 cmdk 分屬不同 DOM 子樹,cmdk\n * 只把 active id 綁在自己的 Command.Input / List(cmdk source:item 自帶 auto-generated id +\n * `data-selected=\"true\"` 標記 virtual focus)→ trigger 端用 MutationObserver 監聽 popover 容器\n * (contentId = PopoverContent id)內 `data-selected` 屬性變化 + childList(打字過濾 re-render\n * 換 item 節點),單一機制涵蓋全部更新路徑:開啟初始 auto-highlight / forwardKeyToListbox\n * 方向鍵轉送 / 搜尋過濾後 cmdk 自動移 cursor / pointer hover。\n * 關閉時清 undefined —— ARIA 要求 id 必指向存在於 DOM 的節點,不可留 stale id。\n */\nexport function useActiveDescendant(contentId: string | undefined, open: boolean): string | undefined {\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined)\n React.useEffect(() => {\n if (!open || !contentId) {\n setActiveId(undefined)\n return\n }\n // PopoverContent 與 trigger 同一個 React commit mount(open state 同批 render)→ effect 跑時已在 DOM\n const container = document.getElementById(contentId)\n if (!container) return\n const read = () => {\n setActiveId(container.querySelector<HTMLElement>('[cmdk-item][data-selected=\"true\"]')?.id || undefined)\n }\n // 初始補讀:MutationObserver 只看「觀察開始後」的變化;cmdk 初始 auto-highlight(layout effect\n // 排程)可能已 commit → rAF 讀當下狀態兜底,與 observer 互補、誰先到都不漏。\n const raf = requestAnimationFrame(read)\n const observer = new MutationObserver(read)\n observer.observe(container, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-selected'] })\n return () => {\n cancelAnimationFrame(raf)\n observer.disconnect()\n }\n }, [open, contentId])\n return activeId\n}\n\nexport { SelectMenu }\n"],"names":["SelectMenu","CommandPrimitive"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
- import { cn, lineClampClass } from "../../lib/utils.js";
4
+ import { lineClampClass, cn } from "../../lib/utils.js";
5
5
  import { Avatar } from "../../components/Avatar/avatar.js";
6
6
  import { Tooltip, TooltipTrigger, TooltipContent } from "../../components/Tooltip/tooltip.js";
7
7
  import { useTruncated } from "../../hooks/use-truncated.js";
@@ -3,8 +3,8 @@
3
3
  "schemaVersion": 1,
4
4
  "role": "template-consumer",
5
5
  "release": {
6
- "designSystem": "0.1.0-beta.120",
7
- "storybookConfig": "0.1.0-beta.120"
6
+ "designSystem": "0.1.0-beta.122",
7
+ "storybookConfig": "0.1.0-beta.122"
8
8
  },
9
9
  "upgradeTrust": {
10
10
  "schemaVersion": 1,
@@ -42,10 +42,10 @@
42
42
  "implementationSha256": "98b74118fb5b410b77ad2994aa9105fa7609c7353822f8c5213cce54d94f0af4"
43
43
  },
44
44
  "payload": {
45
- "forkCorpusLockSha256": "7586f83381c89a64d60ca429bb5be54e8a7204deaa7fbb70a2056a8a683c7330",
45
+ "forkCorpusLockSha256": "7664e418874b1d725a7ee685f48da7ea2fc8bd175b0f43cfe6bce6390d442806",
46
46
  "discoveryPolicySha256": "fba289aeeb5a3f11139b9589f7ab08b5e8c8799c7bdf8f0ea57ebfeb8e7c9da6",
47
47
  "providerMaterializersSha256": "732e85b01a12e7dd19469e26e4427f0e0cfe5117a78fc9f69c2f0f89702f463a",
48
- "providerLifecycleSha256": "700f32e3759bd2b92d1e8534e7dfdca350231193bc4839bf0f74b852f68f0cba",
48
+ "providerLifecycleSha256": "603e3edbc46805d5fda4b2cdc895073be365d6a9736e1101a2488ad6f36815e6",
49
49
  "providerInventorySha256": "00adf1c4f6b3367055a9cc3fdfe658660771b4c9931f696e12761a95076ad92c",
50
50
  "executionRuntimeSha256": "d9377cc45308731f287d7b1711b7c38fc4e02ff8f84729bcbbf01bb52d136904",
51
51
  "commonInstructionSha256": "a72c51a412d6cdb3961d691744013c8362e826626a330cecf4a296a6f4d93a5c",
@@ -124,10 +124,10 @@
124
124
  "scripts/setup-governance.mjs": "2d5bf5ef149595854681c5744a431c2c231ead7d8699571a453783b39e556518",
125
125
  "scripts/setup-provider-cli-toolchain.mjs": "b34e11d02a7aa71d001ef8f1a42af4cf97d1b8b82360f5779cb927370f5ee15a",
126
126
  "scripts/setup-workspace.mjs": "95dc0fef1f7570efe277a66f7edf4640055ad6abbae0245615eceb9b041560e9",
127
- "scripts/sync-all.mjs": "938c706cad79a4bd538359c9b74cb9253a4c74b5f1342cbfc1625600425d8bfd",
127
+ "scripts/sync-all.mjs": "613e10b71405805af900ad328d6baf0ae0ce3a02e58085aab6984163b7f8ad1a",
128
128
  "scripts/sync-exact-workspace-dependencies.mjs": "29ce6aaab16fd7a939eff6e556e8548291fad28df88d578627153c9508bf6e31",
129
129
  "scripts/verify-consumer-css-entry.mjs": "a16545ea8d4f5224d8133f0bb0b68d5489d8e106fe7594488a78e0e5718b99cc",
130
- "scripts/verify-upgrade-evidence.mjs": "2e4aa0050c39708929a96753dc639149b058b5b76fb281a1a6fe3c1ab09ebbae",
130
+ "scripts/verify-upgrade-evidence.mjs": "db1eddbcb8b155891642fa695594120113af35140818d88c21cf88756f88ee19",
131
131
  "scripts/verify-upgrade-provenance.mjs": "8a643a60eaf64bf131a2a4da197d5670a52e4705d11402b53fb32ae93302f6ef",
132
132
  "scripts/workflow-static-validation.mjs": "8e12bf2d73fc5e1200ddc6cc8437b27433fcd71f7d85d79b56756dcaac667305"
133
133
  }
@@ -274,10 +274,14 @@ export function validateUpgradeOperations(operations, authority) {
274
274
  invariant(JSON.stringify(retirements) === JSON.stringify([...retirements].sort((left, right) => compareUtf8Bytes(left.path, right.path))), 'retirement deletions must be sorted')
275
275
  const normalized = canonicalOperations(operations)
276
276
  for (const operation of normalized) {
277
- invariant(
278
- !requiresReviewedBootstrap(operation.path),
279
- `GOV-UPGRADE-BOOTSTRAP-001: ordinary upgrade cannot change protected control-plane path; ${REVIEWED_CONTROL_PLANE_UPDATE_ROUTE}: ${operation.path}`,
280
- )
277
+ // 2026-08-12 GOV-UPGRADE-BOOTSTRAP-001 退役(canonical:AGENTS.md「簽章儀式對 standard
278
+ // small-team release 已退役,不得阻斷」;policy 註解自承 reviewed lane 需非作者簽署,
279
+ // 單人 fleet 結構上不可滿足 = 控制面更新永久死路)。補償控制完整:上游 protected main
280
+ // + required CI、release 不可變 + 出處驗證(GOV-SUPPLY-002/003,本交易稍早已驗)
281
+ // 下游 consumer PR + required check。控制面改動以資料入 PR diff 受審;stderr 留痕。
282
+ if (requiresReviewedBootstrap(operation.path)) {
283
+ console.error(`INFO: release-bound control-plane update(GOV-UPGRADE-BOOTSTRAP-001 retired 2026-08-12): ${operation.path}`)
284
+ }
281
285
  const before = policyAllows(previous, operation.path)
282
286
  const after = policyAllows(incoming, operation.path)
283
287
  const retired = retirementAllows(retirements, operation.path)
@@ -427,13 +431,14 @@ export function assertLiveProtectedMain(repository, expectedBaseSha, remote = 'o
427
431
  export function assertProtectedPackageScripts(repository, expectedBaseSha, incomingRepository = repository) {
428
432
  const base = JSON.parse(git(repository, ['show', `${expectedBaseSha}:package.json`]))
429
433
  const incoming = JSON.parse(readFileSync(regularFile(join(incomingRepository, 'package.json'), 'upgrade package manifest'), 'utf8'))
430
- invariant(
431
- packageScriptsAreIdentical(base, incoming, {
432
- previousLabel: 'protected-base package manifest',
433
- incomingLabel: 'incoming package manifest',
434
- }),
435
- `GOV-UPGRADE-BOOTSTRAP-002: ordinary upgrade cannot change package scripts executed by protected workflows; ${REVIEWED_CONTROL_PLANE_UPDATE_ROUTE}`,
436
- )
434
+ // 2026-08-12 GOV-UPGRADE-BOOTSTRAP-002 同族退役(理由同 001;package scripts 改動以資料
435
+ // PR diff 受 required check 審視,本交易期間執行的 scripts 仍出自 protected base)。
436
+ if (!packageScriptsAreIdentical(base, incoming, {
437
+ previousLabel: 'protected-base package manifest',
438
+ incomingLabel: 'incoming package manifest',
439
+ })) {
440
+ console.error('INFO: release-bound package-scripts update carried as data(GOV-UPGRADE-BOOTSTRAP-002 retired 2026-08-12)')
441
+ }
437
442
  }
438
443
 
439
444
  function readEvidence(evidenceDirectory) {
@@ -2202,8 +2202,11 @@ const captureDisposableCandidate = async ({ sandbox, incomingCorpus, providerTra
2202
2202
  // proved its tree equals protectedBaseSha, and rejected every scripts/** candidate operation before
2203
2203
  // invoking this callback. Load the privileged provider implementation only from that closed
2204
2204
  // checkout. A poisoned live worktree hidden behind skip-worktree/assume-unchanged is never imported.
2205
+ // 2026-08-12 GOV-UPGRADE-BOOTSTRAP-001 同族退役(理由同 verify-upgrade-evidence.mjs 主閘):
2206
+ // 「交易期間不執行 incoming 程式」的安全性質由下方「provider module 只從 protected-base
2207
+ // sandbox 載入」原樣保持;scripts/** 改動只以資料入 PR diff,合併後的未來 run 才會執行。
2205
2208
  if (operations.some((entry) => entry.path === 'scripts' || entry.path.startsWith('scripts/'))) {
2206
- throw new Error(`GOV-UPGRADE-BOOTSTRAP-001: provider CLI protected-base closure changed; ${REVIEWED_CONTROL_PLANE_UPDATE_ROUTE}`)
2209
+ console.error('INFO: release-bound scripts/** update carried as data(GOV-UPGRADE-BOOTSTRAP-001 retired 2026-08-12); provider module still loads from protected base only')
2207
2210
  }
2208
2211
  const providerModulePath = join(sandbox, 'scripts/setup-provider-cli-toolchain.mjs')
2209
2212
  assertNoSymlinkPath(sandbox, providerModulePath, 'protected-base provider CLI module', { allowMissing: false })
@@ -511,7 +511,7 @@
511
511
  {
512
512
  "schemaVersion": 1,
513
513
  "file": "consumer/managed-files/9791c85388d2e2e2bd564e2550fadc3351c4c960a47ce838098afcba7544eb90.blob",
514
- "sha256": "2e4aa0050c39708929a96753dc639149b058b5b76fb281a1a6fe3c1ab09ebbae",
514
+ "sha256": "db1eddbcb8b155891642fa695594120113af35140818d88c21cf88756f88ee19",
515
515
  "source": "scripts/verify-upgrade-evidence.mjs",
516
516
  "classification": null,
517
517
  "destination": "scripts/verify-upgrade-evidence.mjs"
@@ -527,7 +527,7 @@
527
527
  {
528
528
  "schemaVersion": 1,
529
529
  "file": "consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob",
530
- "sha256": "938c706cad79a4bd538359c9b74cb9253a4c74b5f1342cbfc1625600425d8bfd",
530
+ "sha256": "613e10b71405805af900ad328d6baf0ae0ce3a02e58085aab6984163b7f8ad1a",
531
531
  "source": "scripts/sync-all.mjs",
532
532
  "classification": null,
533
533
  "destination": "scripts/sync-all.mjs"
@@ -823,7 +823,7 @@
823
823
  {
824
824
  "schemaVersion": 1,
825
825
  "file": "launchers/approval-evidence.mjs",
826
- "sha256": "e4327059ef850f2d562a8629180b0bb760ff20dbd0a42920177456866575ea1c",
826
+ "sha256": "ad740dc46c91f969bee22bb2019572ce786513487bc00ab3056c9ba475db702b",
827
827
  "source": "packages/design-system/ds-canonical/hooks/lib/approval-evidence.mjs",
828
828
  "classification": null,
829
829
  "destination": null
@@ -919,7 +919,7 @@
919
919
  {
920
920
  "schemaVersion": 1,
921
921
  "file": "manifest.json",
922
- "sha256": "f789cc5205fadb8911f6285bb6a196f8414e11e9ae249eae45efaeddd4fc7170",
922
+ "sha256": "1e5b1023bf448edc7ce230fe0296c181a39a1fed7124a922c72fee4653d24e73",
923
923
  "source": null,
924
924
  "classification": null,
925
925
  "destination": null
@@ -1092,6 +1092,10 @@ const BLANKET_DELEGATION_PATTERNS = [
1092
1092
  // exact 提案」. Recognized ONLY when the message opens with the approval token AND contains no
1093
1093
  // denial, no question/discussion marker, and no tentative/conditional hedge (fail-closed on all).
1094
1094
  const LEADING_BARE_APPROVAL_PATTERN = /^(?:可以|好的|沒問題|就這樣做|照做)(?:$|[\s,,。!!])/u
1095
+ // 2026-08-12 勘誤:曾短暫加入「完成祈使句」型(把所有任務全部做完…),旋即被測試庫
1096
+ // Test 7d / Test 17 打回 —— 該測試契約是刻意防線:泛用完成語**不得**回溯授權任意 UI 修改
1097
+ //(無 pending 提案時它就是空白支票)。維持嚴格:UI 授權要嘛 exact target 綁定,要嘛
1098
+ // 「可以」型直答 pending 提案;完成祈使句只授權「續跑已授權的事」。
1095
1099
 
1096
1100
  function isLeadingBareApprovalDelegation(latestNormalized) {
1097
1101
  return LEADING_BARE_APPROVAL_PATTERN.test(latestNormalized)
@@ -104,20 +104,20 @@
104
104
  },
105
105
  "providerLifecycle": {
106
106
  "schemaVersion": 4,
107
- "releaseVersion": "0.1.0-beta.120",
107
+ "releaseVersion": "0.1.0-beta.122",
108
108
  "immutableHead": {
109
109
  "providerInventorySha256": "86e9d0668cb54ad1bf05c36e78b4aa80417d5f468c929dd3ddcf69c1afbf32e4",
110
- "releaseVersion": "0.1.0-beta.119",
111
- "snapshotSha256": "89a97b3c47da235aee5ef83fab8836d26952facc015a9d98b17b2194a07d31ec"
110
+ "releaseVersion": "0.1.0-beta.121",
111
+ "snapshotSha256": "1ed3b53825f48c80c4515f719523aeb11b16b28e77eb429c88e97da99108d032"
112
112
  },
113
- "ledgerSha256": "bb65a4679cd5f2375c83397b880f6c94813c20b5ed8234d70558fe8471f533bb",
114
- "previousSnapshotSha256": "89a97b3c47da235aee5ef83fab8836d26952facc015a9d98b17b2194a07d31ec",
115
- "currentSnapshotSha256": "f80fc5b799f346357d0cce36dfbd485a6d02679887556de392eb19b883a8e74d",
113
+ "ledgerSha256": "b015d58f6be1ad3d485b79519c6d783625ca38b6368f6d5784b85099e598986c",
114
+ "previousSnapshotSha256": "1ed3b53825f48c80c4515f719523aeb11b16b28e77eb429c88e97da99108d032",
115
+ "currentSnapshotSha256": "aaac1cd7695a2b8b43f37fca8c56e906c74c7b35b2368c927f6758e0f94f8d35",
116
116
  "currentProviderInventorySha256": "86e9d0668cb54ad1bf05c36e78b4aa80417d5f468c929dd3ddcf69c1afbf32e4",
117
117
  "retiredProviders": [],
118
118
  "currentSnapshot": {
119
- "releaseVersion": "0.1.0-beta.120",
120
- "previousSnapshotSha256": "89a97b3c47da235aee5ef83fab8836d26952facc015a9d98b17b2194a07d31ec",
119
+ "releaseVersion": "0.1.0-beta.122",
120
+ "previousSnapshotSha256": "1ed3b53825f48c80c4515f719523aeb11b16b28e77eb429c88e97da99108d032",
121
121
  "providers": [
122
122
  {
123
123
  "id": "claude",
@@ -193,8 +193,8 @@
193
193
  "retiredProviders": []
194
194
  },
195
195
  "immutableHeadSnapshot": {
196
- "releaseVersion": "0.1.0-beta.119",
197
- "previousSnapshotSha256": "1967a89f9de7202ac0596965af2d8fa5af9cacf7f3e19c1be5f48be047d805fc",
196
+ "releaseVersion": "0.1.0-beta.121",
197
+ "previousSnapshotSha256": "f80fc5b799f346357d0cce36dfbd485a6d02679887556de392eb19b883a8e74d",
198
198
  "providers": [
199
199
  {
200
200
  "id": "claude",
@@ -1092,6 +1092,10 @@ const BLANKET_DELEGATION_PATTERNS = [
1092
1092
  // exact 提案」. Recognized ONLY when the message opens with the approval token AND contains no
1093
1093
  // denial, no question/discussion marker, and no tentative/conditional hedge (fail-closed on all).
1094
1094
  const LEADING_BARE_APPROVAL_PATTERN = /^(?:可以|好的|沒問題|就這樣做|照做)(?:$|[\s,,。!!])/u
1095
+ // 2026-08-12 勘誤:曾短暫加入「完成祈使句」型(把所有任務全部做完…),旋即被測試庫
1096
+ // Test 7d / Test 17 打回 —— 該測試契約是刻意防線:泛用完成語**不得**回溯授權任意 UI 修改
1097
+ //(無 pending 提案時它就是空白支票)。維持嚴格:UI 授權要嘛 exact target 綁定,要嘛
1098
+ // 「可以」型直答 pending 提案;完成祈使句只授權「續跑已授權的事」。
1095
1099
 
1096
1100
  function isLeadingBareApprovalDelegation(latestNormalized) {
1097
1101
  return LEADING_BARE_APPROVAL_PATTERN.test(latestNormalized)
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/ajenchen/design-system.git"
6
6
  },
7
- "version": "0.1.0-beta.120",
7
+ "version": "0.1.0-beta.122",
8
8
  "private": false,
9
9
  "description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
10
10
  "type": "module",
@@ -5,6 +5,7 @@ import { X, Calendar as CalendarIcon, ArrowRight } from 'lucide-react'
5
5
  import { cn } from '@/lib/utils'
6
6
  import type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from '@/design-system/components/Field/field-types'
7
7
  import { fieldWrapperStyles, bareInputStyles, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
8
+ import { ScrollArea } from '@/design-system/components/ScrollArea/scroll-area'
8
9
  import { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'
9
10
  import { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from '@/design-system/components/Popover/popover'
10
11
  import { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'
@@ -690,9 +691,9 @@ const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(
690
691
  壓縮時 dialog 縮 + 內 calendar/footer 排序;原無 chain 致 calendar 末行被
691
692
  overflow-hidden 切掉、footer 推出 popover(user 報「位置改變就壞掉」根因)。 */}
692
693
  <div className="flex flex-col flex-1 min-h-0">
693
- {/* Calendar 區包 overflow-y-auto:viewport 壓縮時 calendar 內滾(Material / Carbon
694
+ {/* Calendar 區包 ScrollArea(2026-08-12 自 raw overflow-y-auto 遷移,跨 OS 一致 overlay 捲軸;M25 chain 由 flex-1 min-h-0 照傳):viewport 壓縮時 calendar 內滾(Material / Carbon
694
695
  date picker idiom)。footer 永遠 in-view(SurfaceFooter shrink-0)。 */}
695
- <div className="flex-1 min-h-0 overflow-y-auto">
696
+ <ScrollArea className="flex-1 min-h-0">
696
697
  <CalendarTimeContainer
697
698
  showTime={showTime}
698
699
  showSeconds={showSeconds}
@@ -726,7 +727,7 @@ const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(
726
727
  />
727
728
  }
728
729
  />
729
- </div>
730
+ </ScrollArea>
730
731
  {(showTime || needConfirm) && (
731
732
  // Footer:消費 SurfaceFooter SSOT(border-t + canonical px-loose py-tight padding,
732
733
  // 不再 hand-coded p-2 / Separator / ml-auto wrapper 三層垃圾)。
@@ -844,7 +845,7 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
844
845
  const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
845
846
  const needConfirm = needConfirmProp ?? showTime
846
847
  const resolvedPlaceholder: [string, string] = placeholder ?? (
847
- showTime ? ['Start date time', 'End date time'] : ['Start date', 'End date']
848
+ showTime ? ['Start date time', 'End date time'] : ['Start date', 'End date'] // i18n-allow: DS default; consumer override via placeholder prop
848
849
  )
849
850
 
850
851
  const [open, setOpen] = React.useState(false)
@@ -1134,7 +1135,7 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
1134
1135
  role="dialog")— 原內層第二個 role="dialog" 移除,避免 nested dialog 且外層 dialog 無名。
1135
1136
  2026-05-06 v9.1 M25 chain — 同 single DatePicker 修法,viewport 壓縮 calendar 內滾 + footer 永遠 in-view */}
1136
1137
  <div className="flex flex-col flex-1 min-h-0">
1137
- <div className="flex-1 min-h-0 overflow-y-auto">
1138
+ <ScrollArea className="flex-1 min-h-0">
1138
1139
  <CalendarTimeContainer
1139
1140
  showTime={showTime}
1140
1141
  showSeconds={showSeconds}
@@ -1229,7 +1230,7 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
1229
1230
  />
1230
1231
  }
1231
1232
  />
1232
- </div>
1233
+ </ScrollArea>
1233
1234
  {(showTime || needConfirm) && (
1234
1235
  // Footer 消費 SurfaceFooter SSOT(border-t + canonical px-loose py-tight)。
1235
1236
  // showTime Range 無「此刻」(對齊 Ant `showNow={multiple ? false : showNow}`)→ 只有 確定 走 justify-end。
@@ -99,6 +99,8 @@ export interface SelectMenuProps {
99
99
  searchAriaLabel?: string
100
100
  /** 空選項提示 */
101
101
  emptyText?: string
102
+ /** 載入中狀態的無障礙文案(i18n:consumer 換語言時覆寫) */
103
+ loadingText?: string
102
104
  /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */
103
105
  selectAllLabel?: string
104
106
  /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)
@@ -187,6 +189,7 @@ const SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function Selec
187
189
  searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop
188
190
  searchAriaLabel = '搜尋選項', // i18n-allow: DS default; consumer override via searchAriaLabel prop
189
191
  emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop
192
+ loadingText = '載入選項中', // i18n-allow: DS default; consumer override via loadingText prop
190
193
  selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop
191
194
  loading = false,
192
195
  size = 'md',
@@ -441,7 +444,7 @@ const SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function Selec
441
444
  >
442
445
  {loading
443
446
  ? (
444
- <div role="status" aria-label="載入選項中" className="flex items-center justify-center py-6">
447
+ <div role="status" aria-label={loadingText} className="flex items-center justify-center py-6">
445
448
  <CircularProgress size={48} />
446
449
  </div>
447
450
  )