@qijenchen/design-system 0.1.0-beta.84 → 0.1.0-beta.85
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.
- package/dist/components/DataTable/data-table-filter-group.d.ts +47 -0
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-group.js +154 -0
- package/dist/components/DataTable/data-table-filter-group.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +2 -324
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +29 -0
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js +187 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -0
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/Field/index.d.ts +1 -0
- package/dist/components/Field/index.d.ts.map +1 -1
- package/dist/components/Field/index.js +2 -0
- package/dist/components/Field/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/ds-canonical/hooks/check_datatable_invariants.sh +6 -3
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/components/Checkbox/checkbox.spec.md +2 -0
- package/src/components/DataTable/data-table-filter-group.tsx +231 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +13 -430
- package/src/components/DataTable/data-table-filter-value-picker.tsx +237 -0
- package/src/components/DataTable/data-table.spec.md +1 -1
- package/src/components/DataTable/data-table.tsx +4 -1
- package/src/components/DataTable/filter-operators.spec.md +3 -3
- package/src/components/Field/index.ts +1 -0
- package/src/components/Select/select.spec.md +2 -0
- package/src/index.ts +4 -0
- package/src/tokens/color/color.spec.md +1 -1
- package/src/tokens/uiSize/uiSize.css +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// @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(2026-07-14 自 data-table-filter-panel.tsx 拆檔搬移,原檔級 escape 隨段落帶入)。
|
|
2
|
+
// @internal — DS-internal 單元(FilterValuePicker value-picker switcher,只被
|
|
3
|
+
// data-table-filter-group.tsx FilterRow 消費);不隨 index.ts re-export 進 npm public surface。
|
|
4
|
+
// ────────────────────────────── 消費的 SSOT ──────────────────────────────
|
|
5
|
+
// - Field controls(Family 4,統一 size="sm"):Input / NumberInput / Select / Combobox /
|
|
6
|
+
// DatePicker + DatePickerRange(showTime)/ PeoplePicker — 按 ValueShape dispatch
|
|
7
|
+
// (ValueShape ↔ picker 對照 canonical:./data-table.spec.md「三、Operator × ValueShape SSOT」)
|
|
8
|
+
// - className 直接 forward 給 inner Field control(不另開 wrapper div)—
|
|
9
|
+
// field-control-group.spec.md「forward className」canonical(2026-05-04 #2 fix)
|
|
10
|
+
// - Operator / ValueShape SSOT:./filter-operators.ts(DATE_RELATIVE_OPTIONS / DATE_RELATIVE_GROUPS)
|
|
11
|
+
// - Person pool:PeoplePicker PersonValue(對齊 cell-registry meta.people SSOT)
|
|
12
|
+
// 設計規則:./filter-operators.spec.md「ValueShape canonical」+ ./data-table.spec.md「進階篩選」段
|
|
13
|
+
|
|
14
|
+
import { Select, type SelectOption } from '@/design-system/components/Select/select'
|
|
15
|
+
import { Combobox } from '@/design-system/components/Combobox/combobox'
|
|
16
|
+
import { Input } from '@/design-system/components/Input/input'
|
|
17
|
+
import { NumberInput } from '@/design-system/components/NumberInput/number-input'
|
|
18
|
+
import { DatePicker, DatePickerRange } from '@/design-system/components/DatePicker/date-picker'
|
|
19
|
+
import { PeoplePicker } from '@/design-system/components/PeoplePicker/people-picker'
|
|
20
|
+
import type { PersonValue } from '@/design-system/components/PeoplePicker/person-display'
|
|
21
|
+
import {
|
|
22
|
+
DATE_RELATIVE_OPTIONS,
|
|
23
|
+
DATE_RELATIVE_GROUPS,
|
|
24
|
+
type ValueShape,
|
|
25
|
+
} from './filter-operators'
|
|
26
|
+
|
|
27
|
+
// ── FilterValuePicker(value-picker switcher per ValueShape)────────────────
|
|
28
|
+
//
|
|
29
|
+
// 2026-05-03 M21 retract:本 helper 原獨立檔 filter-value-picker.tsx(187 行 / 1 consumer),
|
|
30
|
+
// claim「未來 inline filter UI 共用」= premature abstraction → inline 回 panel。
|
|
31
|
+
// 2026-07-14 拆檔 refactor:panel 822 行過 800 hard cap(file-size escalation)→ 重新抽出為
|
|
32
|
+
// @internal 私有模組(非 public API 抽象,與 M21 不衝突;consumer 仍只見 DataTableFilterPanel)。
|
|
33
|
+
|
|
34
|
+
export interface FilterValuePickerColInfo {
|
|
35
|
+
id: string
|
|
36
|
+
label: string
|
|
37
|
+
options?: Array<{ value: string; label: string }>
|
|
38
|
+
/** Person pool — person/multiPerson filter picker 用(2026-05-07 升級,SSOT 對齊 cell-registry) */
|
|
39
|
+
people?: Array<{ name: string; avatarUrl?: string; description?: string }>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface FilterValuePickerProps {
|
|
43
|
+
shape: ValueShape | null
|
|
44
|
+
value: unknown
|
|
45
|
+
onChange: (v: unknown) => void
|
|
46
|
+
colInfo?: FilterValuePickerColInfo
|
|
47
|
+
disabled?: boolean
|
|
48
|
+
/** 用 column.label 組「{label} 篩選值」(panel 每 row 不顯式 label,a11y 必填) */
|
|
49
|
+
ariaLabel?: string
|
|
50
|
+
/** Forward 給內部 Field control 的 className(2026-05-04 #2 fix)
|
|
51
|
+
* 避免外層包 wrapper div 破壞 FieldControlGroup CSS variants(rounded radii / margin overlap) */
|
|
52
|
+
className?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function FilterValuePicker({
|
|
56
|
+
shape,
|
|
57
|
+
value,
|
|
58
|
+
onChange,
|
|
59
|
+
colInfo,
|
|
60
|
+
disabled,
|
|
61
|
+
ariaLabel,
|
|
62
|
+
className,
|
|
63
|
+
}: FilterValuePickerProps) {
|
|
64
|
+
if (!shape || disabled) {
|
|
65
|
+
return <Input size="sm" value="" onChange={() => {}} placeholder="輸入值…" disabled aria-label={ariaLabel} className={className} />
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
switch (shape) {
|
|
69
|
+
case 'none':
|
|
70
|
+
return null
|
|
71
|
+
|
|
72
|
+
case 'text':
|
|
73
|
+
return (
|
|
74
|
+
<Input
|
|
75
|
+
size="sm"
|
|
76
|
+
value={String(value ?? '')}
|
|
77
|
+
onChange={(e) => onChange(e.target.value)}
|
|
78
|
+
placeholder="輸入值…"
|
|
79
|
+
aria-label={ariaLabel}
|
|
80
|
+
className={className}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
case 'number':
|
|
85
|
+
return (
|
|
86
|
+
<NumberInput
|
|
87
|
+
size="sm"
|
|
88
|
+
value={typeof value === 'number' ? value : null}
|
|
89
|
+
onChange={(v) => onChange(v ?? '')}
|
|
90
|
+
placeholder="輸入數字…"
|
|
91
|
+
aria-label={ariaLabel}
|
|
92
|
+
className={className}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
case 'date_single':
|
|
97
|
+
return (
|
|
98
|
+
<DatePicker
|
|
99
|
+
size="sm"
|
|
100
|
+
value={typeof value === 'string' ? value : null}
|
|
101
|
+
onChange={(v) => onChange(v ?? '')}
|
|
102
|
+
aria-label={ariaLabel}
|
|
103
|
+
className={className}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
case 'date_range':
|
|
108
|
+
return (
|
|
109
|
+
<DatePickerRange
|
|
110
|
+
size="sm"
|
|
111
|
+
value={Array.isArray(value) && value.length === 2
|
|
112
|
+
? (value as [string | null, string | null])
|
|
113
|
+
: null}
|
|
114
|
+
onChange={(v) => onChange(v)}
|
|
115
|
+
aria-label={ariaLabel}
|
|
116
|
+
className={className}
|
|
117
|
+
/>
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
case 'date_relative': {
|
|
121
|
+
// 群組分類:Past / Current / Future(對齊 Linear / Notion idiom),走 Select.groups → SelectMenu
|
|
122
|
+
const opts: SelectOption[] = DATE_RELATIVE_OPTIONS.map((o) => ({
|
|
123
|
+
value: o.value,
|
|
124
|
+
label: o.label,
|
|
125
|
+
group: o.group,
|
|
126
|
+
}))
|
|
127
|
+
return (
|
|
128
|
+
<Select
|
|
129
|
+
size="sm"
|
|
130
|
+
options={opts}
|
|
131
|
+
groups={DATE_RELATIVE_GROUPS as unknown as Array<{ key: string; label: string }>}
|
|
132
|
+
value={String(value ?? '')}
|
|
133
|
+
onChange={(v) => onChange(v)}
|
|
134
|
+
placeholder="選擇相對日期"
|
|
135
|
+
aria-label={ariaLabel}
|
|
136
|
+
className={className}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case 'select_single': {
|
|
142
|
+
const opts: SelectOption[] = (colInfo?.options ?? []).map((o) => ({
|
|
143
|
+
value: o.value,
|
|
144
|
+
label: o.label,
|
|
145
|
+
}))
|
|
146
|
+
return (
|
|
147
|
+
<Select
|
|
148
|
+
size="sm"
|
|
149
|
+
options={opts}
|
|
150
|
+
value={String(value ?? '')}
|
|
151
|
+
onChange={(v) => onChange(v)}
|
|
152
|
+
placeholder="選擇值"
|
|
153
|
+
aria-label={ariaLabel}
|
|
154
|
+
className={className}
|
|
155
|
+
/>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'select_multi': {
|
|
160
|
+
const opts = (colInfo?.options ?? []).map((o) => ({
|
|
161
|
+
value: o.value,
|
|
162
|
+
label: o.label,
|
|
163
|
+
}))
|
|
164
|
+
const arr = Array.isArray(value) ? (value as string[]) : []
|
|
165
|
+
return (
|
|
166
|
+
<Combobox
|
|
167
|
+
size="sm"
|
|
168
|
+
options={opts}
|
|
169
|
+
value={arr}
|
|
170
|
+
onChange={(v) => onChange(v)}
|
|
171
|
+
placeholder="選擇值…"
|
|
172
|
+
aria-label={ariaLabel}
|
|
173
|
+
className={className}
|
|
174
|
+
/>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'datetime_single':
|
|
179
|
+
return (
|
|
180
|
+
<DatePicker
|
|
181
|
+
size="sm"
|
|
182
|
+
showTime
|
|
183
|
+
value={typeof value === 'string' ? value : null}
|
|
184
|
+
onChange={(v) => onChange(v ?? '')}
|
|
185
|
+
aria-label={ariaLabel}
|
|
186
|
+
className={className}
|
|
187
|
+
/>
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
case 'datetime_range':
|
|
191
|
+
return (
|
|
192
|
+
<DatePickerRange
|
|
193
|
+
size="sm"
|
|
194
|
+
showTime
|
|
195
|
+
value={Array.isArray(value) && value.length === 2
|
|
196
|
+
? (value as [string | null, string | null])
|
|
197
|
+
: null}
|
|
198
|
+
onChange={(v) => onChange(v)}
|
|
199
|
+
aria-label={ariaLabel}
|
|
200
|
+
className={className}
|
|
201
|
+
/>
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
// person_single / person_multi — 走 PeoplePicker(2026-05-07 升級,對齊 cell-registry SSOT)。
|
|
205
|
+
// colInfo.people 來自 column meta.people。Filter value:
|
|
206
|
+
// - person_single:存 PersonValue | null(picker emit array,我們取 [0])
|
|
207
|
+
// - person_multi:存 PersonValue[]
|
|
208
|
+
case 'person_single': {
|
|
209
|
+
const v = value as PersonValue | null | undefined
|
|
210
|
+
return (
|
|
211
|
+
<PeoplePicker
|
|
212
|
+
size="sm"
|
|
213
|
+
value={v ?? null}
|
|
214
|
+
people={colInfo?.people ?? []}
|
|
215
|
+
onChange={(next) => onChange(next[0] ?? null)}
|
|
216
|
+
aria-label={ariaLabel}
|
|
217
|
+
className={className}
|
|
218
|
+
/>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
case 'person_multi': {
|
|
222
|
+
const v = Array.isArray(value) ? (value as PersonValue[]) : []
|
|
223
|
+
return (
|
|
224
|
+
<PeoplePicker
|
|
225
|
+
size="sm"
|
|
226
|
+
value={v}
|
|
227
|
+
people={colInfo?.people ?? []}
|
|
228
|
+
onChange={(next) => onChange(next)}
|
|
229
|
+
aria-label={ariaLabel}
|
|
230
|
+
className={className}
|
|
231
|
+
/>
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
default:
|
|
235
|
+
return null
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -305,7 +305,7 @@ type FilterTreeNested = { mode: 'nested'; conjunction: 'and'|'or'; children: Fil
|
|
|
305
305
|
|
|
306
306
|
### 三、Operator × ValueShape SSOT
|
|
307
307
|
|
|
308
|
-
`filter-operators.ts` 的 `OPERATOR_REGISTRY: Record<ColumnType, OperatorSpec[]>` 是唯一 truth。Panel 完全 data-driven:field 選 → load op set → 選 op → 由 `valueShape` dispatch picker(`filter-value-picker.tsx
|
|
308
|
+
`filter-operators.ts` 的 `OPERATOR_REGISTRY: Record<ColumnType, OperatorSpec[]>` 是唯一 truth。Panel 完全 data-driven:field 選 → load op set → 選 op → 由 `valueShape` dispatch picker(`data-table-filter-value-picker.tsx`,@internal;2026-07-14 file-size 拆檔自 panel)。`is_set` / `is_not_set` / `is_true` / `is_false`(`ValueShape='none'`)不渲 picker。
|
|
309
309
|
|
|
310
310
|
ValueShape ↔ DS picker 對照(canonical 2026-05-02):
|
|
311
311
|
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
// 每個檔過 M21 / M17 / Rule-of-3 三 test:
|
|
6
6
|
// - data-table.tsx(主,foundational)
|
|
7
7
|
// - data-table-filter-panel.tsx + data-table-sort-manager.tsx + data-table-column-visibility-panel.tsx(panel state 隔離)
|
|
8
|
+
// - data-table-filter-group.tsx + data-table-filter-value-picker.tsx(@internal;2026-07-14 filter panel
|
|
9
|
+
// 過 800 hard cap 拆檔 — row/nested-group renderer + ValueShape picker,只被 panel 消費)
|
|
8
10
|
// - cell-registry.tsx(column type → cell display / edit 解析 SSOT)
|
|
9
11
|
// - data-table-interaction-layer.tsx + active-editor-controller.ts(spreadsheet overlay + portal editor)
|
|
10
12
|
// - column-types.ts + filter-operators.ts(✓ Rule-of-3 SSOT,3+ consumer)
|
|
11
13
|
// - filter-tree.ts(pure data + eval,test isolation)
|
|
12
14
|
// - lib/column-meta.ts(Internal SSOT,消 5 處 `(col as any)`)
|
|
13
15
|
// - + stories / spec / css
|
|
14
|
-
// M21 retract:filter-value-picker.tsx 1 consumer →
|
|
16
|
+
// M21 retract:filter-value-picker.tsx 1 consumer → 2026-05-03 inline 回 panel(上列 2026-07-14
|
|
17
|
+
// @internal 拆檔非 M21 迴轉:非 public 抽象,file-size hard cap 驅動,consumer 仍只見 panel)。
|
|
15
18
|
import * as React from 'react'
|
|
16
19
|
import { createPortal } from 'react-dom'
|
|
17
20
|
import { Empty } from '@/design-system/components/Empty/empty'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Advanced Filter 設計原則(Operator × ColumnType × ValueShape SSOT,v3)
|
|
2
2
|
|
|
3
|
-
> **狀態**:已落地 — registry 寫成 `filter-operators.ts`,`filter-tree.ts` + `data-table-filter-panel.tsx`
|
|
3
|
+
> **狀態**:已落地 — registry 寫成 `filter-operators.ts`,`filter-tree.ts` + filter panel 家族消費(`data-table-filter-panel.tsx`;row/picker 直接消費點在 2026-07-14 拆檔子模組 `data-table-filter-group.tsx` / `data-table-filter-value-picker.tsx`)(原 draft 兩個升級條件皆已達成,2026-06-11 對齊)。
|
|
4
4
|
> **M8 benchmark**:對照 ClickUp(CU)/ Airtable(AT)/ Notion(NT)三家。**v3 加 datetime / includeTime + 砍 number.between(2/3 業界共識)**。
|
|
5
5
|
> **設計路線**:**ClickUp 為 baseline**,合理 + 不衝突 + 真實用的擴充採納。
|
|
6
6
|
> **檔位**:暫放 DataTable/,若決定抽 `patterns/advanced-filter/` 再搬。
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
| 1-to-1 比對 | ✓ `is` / `is_not`(picker 多選 OR-語意) | ✗ 不適用(陣列比單值 ambiguous) |
|
|
134
134
|
| 集合運算 | ✗ 不適用 | ✓ `has_any_of` / `has_all_of` / `has_none_of` |
|
|
135
135
|
|
|
136
|
-
差異**根源於 cell 資料形狀**,不是 picker 不同。Value picker 兩者都共用 `<Combobox>` DS 既有元件(code 真值 `data-table-filter-
|
|
136
|
+
差異**根源於 cell 資料形狀**,不是 picker 不同。Value picker 兩者都共用 `<Combobox>` DS 既有元件(code 真值 `data-table-filter-value-picker.tsx` select_multi case;2026-07-14 拆檔自 panel)。
|
|
137
137
|
|
|
138
138
|
### `person`(4 ops — 跟 select 同邏輯)
|
|
139
139
|
| op key | 中 | en | valueShape | CU | AT | NT |
|
|
@@ -238,7 +238,7 @@
|
|
|
238
238
|
|
|
239
239
|
**技術坑(信心 85%,Phase B spec.md 詳)**:TanStack `ColumnFiltersState` per-column 一筆 filter value,N 條同 column 會 AND-chain 不能 OR。**業界標準解法**:棄用 `columnFilters`,改自管 FilterTree state + 用 `globalFilter` + 自訂 `globalFilterFn` 走樹求 boolean。
|
|
240
240
|
|
|
241
|
-
## 8. 邊界案例(code 真值 — `filter-tree.ts` /
|
|
241
|
+
## 8. 邊界案例(code 真值 — `filter-tree.ts` / filter panel 家族:`data-table-filter-panel.tsx` + `data-table-filter-group.tsx`)
|
|
242
242
|
|
|
243
243
|
- **Condition 值空 / 未選 field**:視為 incomplete,求值 **pass-through**(不過濾任何 row);空值 ≠ `is_not_set`(後者是顯式 op)— `isConditionComplete` 守門
|
|
244
244
|
- **群組刪光 condition**:空群組保留並 pass-through(true),panel 顯示「移除空群組」按鈕由 user 顯式刪除,不自動消失
|
|
@@ -13,6 +13,8 @@ benchmark:
|
|
|
13
13
|
- Polaris Select: github.com/Shopify/polaris/tree/main/polaris-react/src/components/Select
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
<!-- @spec-budget-exception: SSOT-heavy 例外(CLAUDE.md 行數預算「foundational SSOT 例外 ≤800-1200」)— 本檔持有跨元件 SSOT:「與 RadioGroup 的分界」(~40 行)+ 3 筆拍板記錄(2026-07-13 D2 hidden-input mirror / 2026-05-21 D3 dual-mode / 2026-06-12 R2 open-pair 例外);2026-07-14 knowledge-prune P2 結論「殘餘超標內容全是 canonical,再砍必動 meaning」(.claude/logs/prune-p2-candidates.md)。 -->
|
|
17
|
+
|
|
16
18
|
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
|
|
17
19
|
|
|
18
20
|
# Select 設計原則
|
package/src/index.ts
CHANGED
|
@@ -246,12 +246,16 @@ export {
|
|
|
246
246
|
FieldError,
|
|
247
247
|
FieldGroup,
|
|
248
248
|
FieldLabel,
|
|
249
|
+
getMenuListMinHeight,
|
|
249
250
|
useFormValidation,
|
|
250
251
|
} from './components/Field/index'
|
|
251
252
|
export type {
|
|
252
253
|
FieldGroupProps,
|
|
253
254
|
FieldLabelProps,
|
|
255
|
+
FieldMode,
|
|
254
256
|
FieldProps,
|
|
257
|
+
FieldVariant,
|
|
258
|
+
FieldWidth,
|
|
255
259
|
FormFieldInputProps,
|
|
256
260
|
UseFormValidationOptions,
|
|
257
261
|
UseFormValidationReturn,
|
|
@@ -649,7 +649,7 @@ Dark mode 覆寫:hover/active 方向反轉(hover → step-7,active → ste
|
|
|
649
649
|
|
|
650
650
|
| Token | 答 | 語意 | 典型場景(real consumer grep verified 2026-05-20) |
|
|
651
651
|
|---|---|---|---|
|
|
652
|
-
| `bg-muted`(neutral-2) | **是** | **靜態非互動 surface** — 退化 / placeholder / locked 視覺 | Skeleton(`skeleton.tsx:10`) / DataTable table header(`data-table.tsx:312 HEADER_BG`) / Calendar 非當月日格(`calendar.tsx:316`)/ Alert neutral(`alert.tsx:30`)/ DataTable filter-panel inner group container(`data-table-filter-
|
|
652
|
+
| `bg-muted`(neutral-2) | **是** | **靜態非互動 surface** — 退化 / placeholder / locked 視覺 | Skeleton(`skeleton.tsx:10`) / DataTable table header(`data-table.tsx:312 HEADER_BG`) / Calendar 非當月日格(`calendar.tsx:316`)/ Alert neutral(`alert.tsx:30`)/ DataTable filter-panel inner group container(`data-table-filter-group.tsx:202`,2026-07-14 拆檔自 filter-panel)/ tab 容器 / code block / scrollbar track(`semantic.css:367`)/ anatomy `<th>` |
|
|
653
653
|
| `bg-secondary`(neutral-3) | **否,只是視覺退後一級** | **存在且微淡可辨** — 元素是正常狀態,但需要退後一級 | Tag neutral(`tag.tsx`)/ Slider rest track(`slider.tsx`)/ FileItem compact-B progress track(`file-item.tsx`)/ Badge low(`badge.tsx:37`)/ CircularProgress track(`circular-progress.tsx:150`)/ Steps fillBg(`steps.tsx:698,715`)/ ProgressBar track(`progress-bar.tsx`)|
|
|
654
654
|
|
|
655
655
|
**判斷法**:「這個元素是『還沒準備好 / 不可操作』嗎?」
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
--tree-indent-lg: 1.75rem; /* 28px */
|
|
76
76
|
|
|
77
77
|
/* DataTable Panel Widths — Sort manager / Filter panel cells(SSOT,2026-05-23 deep-audit Phase A.4 Decision 2 codify per user verbatim「決策二照你建議做」+「確保最SSOT最簡潔」)
|
|
78
|
-
Consumer:data-table-sort-manager.tsx / data-table-filter-
|
|
78
|
+
Consumer:data-table-sort-manager.tsx / data-table-filter-group.tsx(FilterRow;2026-07-14 拆檔自 filter-panel)
|
|
79
79
|
不 density-scale(panels 是 floating layout,不該跟密度走)
|
|
80
80
|
Naming aligned to family `--<component>-<part>-<size>`(per --field-height-* / --table-row-* / --tab-height-* precedent)*/
|
|
81
81
|
--data-table-sort-panel-width: 30rem; /* 480px — sort manager root container width */
|