@qijenchen/design-system 0.1.0-beta.73 → 0.1.0-beta.75
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/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +27 -6
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +57 -34
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/ds-canonical/fork/governance.lock +7 -7
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +2 -0
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +2 -0
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +2 -0
- package/ds-canonical/fork/skills/prototype/references/audit-checks.md +1 -0
- package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -0
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -0
- package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +1 -0
- package/ds-canonical/hooks/check_plugin_fork_health.sh +2 -2
- package/ds-canonical/hooks/check_story_invariants.sh +26 -0
- package/ds-canonical/hooks/lib/_app_shell_primary_header_consistency.sh +31 -4
- package/ds-canonical/hooks/session_start_governance_check.sh +1 -1
- package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +46 -2
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +30 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/accordion.principles.stories.tsx +3 -3
- package/src/components/Alert/alert.principles.stories.tsx +5 -5
- package/src/components/AppShell/_demo-helpers.tsx +23 -6
- package/src/components/AppShell/app-shell.principles.stories.tsx +9 -8
- package/src/components/AppShell/app-shell.spec.md +9 -8
- package/src/components/AppShell/app-shell.stories.tsx +5 -3
- package/src/components/AspectRatio/aspect-ratio.principles.stories.tsx +1 -1
- package/src/components/Avatar/avatar.principles.stories.tsx +3 -3
- package/src/components/Badge/badge.principles.stories.tsx +3 -3
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +3 -3
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +3 -3
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +2 -2
- package/src/components/BulkActionBar/bulk-action-bar.tsx +3 -2
- package/src/components/Button/button.principles.stories.tsx +3 -3
- package/src/components/Calendar/calendar.principles.stories.tsx +3 -3
- package/src/components/Carousel/carousel.principles.stories.tsx +2 -2
- package/src/components/Chart/chart.principles.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
- package/src/components/Chip/chip.principles.stories.tsx +3 -3
- package/src/components/Coachmark/coachmark.principles.stories.tsx +3 -3
- package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
- package/src/components/Combobox/combobox.principles.stories.tsx +5 -5
- package/src/components/Command/command.principles.stories.tsx +7 -7
- package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
- package/src/components/DataTable/data-table.spec.md +23 -15
- package/src/components/DataTable/data-table.stories.tsx +29 -25
- package/src/components/DataTable/data-table.tsx +92 -44
- package/src/components/DateGrid/date-grid.principles.stories.tsx +4 -4
- package/src/components/DatePicker/date-picker.principles.stories.tsx +5 -5
- package/src/components/DescriptionList/description-list.principles.stories.tsx +5 -5
- package/src/components/Dialog/dialog.principles.stories.tsx +4 -4
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +5 -5
- package/src/components/Empty/empty.principles.stories.tsx +2 -2
- package/src/components/Field/field.principles.stories.tsx +4 -4
- package/src/components/FileItem/file-item.principles.stories.tsx +5 -5
- package/src/components/FileUpload/file-upload.principles.stories.tsx +4 -4
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +5 -5
- package/src/components/HoverCard/hover-card.principles.stories.tsx +5 -5
- package/src/components/Input/input.principles.stories.tsx +4 -4
- package/src/components/LinkInput/link-input.principles.stories.tsx +5 -5
- package/src/components/Menu/menu-item.principles.stories.tsx +7 -7
- package/src/components/Notice/notice.principles.stories.tsx +7 -7
- package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +5 -5
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +3 -3
- package/src/components/Popover/popover.principles.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.principles.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +2 -2
- package/src/components/RadioGroup/radio-group.principles.stories.tsx +2 -2
- package/src/components/Rating/rating.principles.stories.tsx +3 -3
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +4 -4
- package/src/components/Select/select.principles.stories.tsx +5 -5
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +8 -8
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +7 -7
- package/src/components/Separator/separator.principles.stories.tsx +4 -4
- package/src/components/Sheet/sheet.principles.stories.tsx +2 -2
- package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
- package/src/components/Sidebar/sidebar.spec.md +1 -1
- package/src/components/Skeleton/skeleton.principles.stories.tsx +5 -5
- package/src/components/Slider/slider.principles.stories.tsx +3 -3
- package/src/components/Steps/steps.principles.stories.tsx +4 -4
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Tabs/tabs.principles.stories.tsx +3 -3
- package/src/components/Tag/tag.principles.stories.tsx +3 -3
- package/src/components/Textarea/textarea.principles.stories.tsx +2 -2
- package/src/components/TimePicker/time-picker.principles.stories.tsx +5 -5
- package/src/components/Toast/toast.principles.stories.tsx +2 -2
- package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
- package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
- package/src/components/TreeView/tree-view.stories.tsx +1 -1
- package/src/tokens/color/color.spec.md +2 -0
|
@@ -4,7 +4,7 @@ import React from 'react'
|
|
|
4
4
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
5
5
|
import { createColumnHelper, type ColumnDef } from '@tanstack/react-table'
|
|
6
6
|
import { Pencil, Trash2, MoreVertical, Search, Filter, Eye, Download, Plus, ArrowUpDown } from 'lucide-react'
|
|
7
|
-
import { DataTable } from './data-table'
|
|
7
|
+
import { DataTable, type DataTableSelection } from './data-table'
|
|
8
8
|
import { DataTableSortManager } from './data-table-sort-manager'
|
|
9
9
|
import { DataTableColumnVisibilityPanel } from './data-table-column-visibility-panel'
|
|
10
10
|
import { DataTableFilterPanel, evaluateTree, createEmptyFilterTree, isFilterTreeActive, type FilterTree } from './data-table-filter-panel'
|
|
@@ -948,8 +948,7 @@ export const WithBulkActions: Story = {
|
|
|
948
948
|
name: '選取 + 批次操作',
|
|
949
949
|
parameters: { layout: 'fullscreen' },
|
|
950
950
|
render: () => {
|
|
951
|
-
const [selection, setSelection] = React.useState<
|
|
952
|
-
const [allSelected, setAllSelected] = React.useState(false)
|
|
951
|
+
const [selection, setSelection] = React.useState<DataTableSelection>({ mode: 'include', ids: [] })
|
|
953
952
|
const [search, setSearch] = React.useState('')
|
|
954
953
|
const [columnVisibility, setColumnVisibility] = React.useState<Record<string, boolean>>({})
|
|
955
954
|
// Issue 3(2026-05-10):columnSearch 移到 `<DataTableColumnVisibilityPanel>` 內 own,
|
|
@@ -968,13 +967,16 @@ export const WithBulkActions: Story = {
|
|
|
968
967
|
[search]
|
|
969
968
|
)
|
|
970
969
|
const VISIBLE = filteredData.length
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
970
|
+
// 反向選取(inverted)showcase:include 全可見已選且 dataset 更大 → offer「選取全部 M」;
|
|
971
|
+
// all 模式 → 顯示「已選取全部 M(排除 K)」。count = M − excluded(consumer 端計算)。
|
|
972
|
+
const isAll = selection.mode === 'all'
|
|
973
|
+
const visibleIds = filteredData.map((p) => p.sku)
|
|
974
|
+
const selectedCount = isAll ? TOTAL - selection.excluded.length : selection.ids.length
|
|
975
|
+
const visibleSelectedIds = isAll
|
|
976
|
+
? visibleIds.filter((id) => !selection.excluded.includes(id))
|
|
977
|
+
: selection.ids.filter((id) => visibleIds.includes(id))
|
|
978
|
+
const allVisibleSelected = VISIBLE > 0 && visibleSelectedIds.length === VISIBLE
|
|
979
|
+
const showHint = isAll || (allVisibleSelected && TOTAL > VISIBLE)
|
|
978
980
|
|
|
979
981
|
return (
|
|
980
982
|
// 撐滿 parent(layout=fullscreen);
|
|
@@ -1068,6 +1070,7 @@ export const WithBulkActions: Story = {
|
|
|
1068
1070
|
selectable
|
|
1069
1071
|
selection={selection}
|
|
1070
1072
|
onSelectionChange={setSelection}
|
|
1073
|
+
totalCount={TOTAL}
|
|
1071
1074
|
columnVisibility={columnVisibility}
|
|
1072
1075
|
onColumnVisibilityChange={setColumnVisibility}
|
|
1073
1076
|
getRowId={(row) => row.sku}
|
|
@@ -1092,7 +1095,7 @@ export const WithBulkActions: Story = {
|
|
|
1092
1095
|
|
|
1093
1096
|
{/* 底部 chrome group(撤回前一版 absolute overlay,2026-05-04 user 抓 BulkActionBar 沒底色 + 蓋表底列 regression):
|
|
1094
1097
|
回 flex flow 自然推 table。Q7 mount-time growth 真因 = virtualizer estimateRowHeight ≠ token,已在 DataTable 內修(estimate size-aware) */}
|
|
1095
|
-
{(showHint ||
|
|
1098
|
+
{(showHint || selectedCount > 0) && (
|
|
1096
1099
|
<div className="flex flex-col">
|
|
1097
1100
|
{showHint && (
|
|
1098
1101
|
<Alert
|
|
@@ -1100,24 +1103,24 @@ export const WithBulkActions: Story = {
|
|
|
1100
1103
|
placement="fixed"
|
|
1101
1104
|
dismissible={false}
|
|
1102
1105
|
title={
|
|
1103
|
-
|
|
1106
|
+
isAll ? (
|
|
1104
1107
|
<>
|
|
1105
|
-
已選取全部 {TOTAL}
|
|
1108
|
+
已選取全部 {TOTAL} 個項目{selection.excluded.length > 0 ? `(排除 ${selection.excluded.length} 個)` : ''}。{' '}
|
|
1106
1109
|
<button
|
|
1107
1110
|
type="button"
|
|
1108
|
-
onClick={() => {
|
|
1109
|
-
className="text-primary hover:
|
|
1111
|
+
onClick={() => setSelection({ mode: 'include', ids: [] })}
|
|
1112
|
+
className="text-primary hover:text-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
1110
1113
|
>
|
|
1111
1114
|
清除選取項目
|
|
1112
1115
|
</button>
|
|
1113
1116
|
</>
|
|
1114
1117
|
) : (
|
|
1115
1118
|
<>
|
|
1116
|
-
已選取本頁全部 {
|
|
1119
|
+
已選取本頁全部 {selectedCount} 個。{' '}
|
|
1117
1120
|
<button
|
|
1118
1121
|
type="button"
|
|
1119
|
-
onClick={() =>
|
|
1120
|
-
className="text-primary hover:
|
|
1122
|
+
onClick={() => setSelection({ mode: 'all', excluded: [] })}
|
|
1123
|
+
className="text-primary hover:text-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
1121
1124
|
>
|
|
1122
1125
|
點此選取全部 {TOTAL} 個項目
|
|
1123
1126
|
</button>
|
|
@@ -1126,10 +1129,11 @@ export const WithBulkActions: Story = {
|
|
|
1126
1129
|
}
|
|
1127
1130
|
/>
|
|
1128
1131
|
)}
|
|
1129
|
-
{
|
|
1132
|
+
{selectedCount > 0 && (
|
|
1130
1133
|
<BulkActionBar
|
|
1131
|
-
selection={
|
|
1132
|
-
|
|
1134
|
+
selection={visibleSelectedIds}
|
|
1135
|
+
totalSelected={selectedCount}
|
|
1136
|
+
onClear={() => setSelection({ mode: 'include', ids: [] })}
|
|
1133
1137
|
actions={
|
|
1134
1138
|
<>
|
|
1135
1139
|
<Button variant="tertiary" size="md" startIcon={Download}>下載</Button>
|
|
@@ -1165,7 +1169,7 @@ export const SelectionKeyboardAndShift: Story = {
|
|
|
1165
1169
|
height="400px"
|
|
1166
1170
|
selectable
|
|
1167
1171
|
selection={selection}
|
|
1168
|
-
onSelectionChange={setSelection}
|
|
1172
|
+
onSelectionChange={(s) => setSelection(s.mode === 'include' ? s.ids : [])}
|
|
1169
1173
|
getRowId={(row) => row.sku}
|
|
1170
1174
|
/>
|
|
1171
1175
|
</div>
|
|
@@ -1192,7 +1196,7 @@ export const SelectionSingleMode: Story = {
|
|
|
1192
1196
|
height="auto"
|
|
1193
1197
|
selectable="single"
|
|
1194
1198
|
selection={selection}
|
|
1195
|
-
onSelectionChange={setSelection}
|
|
1199
|
+
onSelectionChange={(s) => setSelection(s.mode === 'include' ? s.ids : [])}
|
|
1196
1200
|
getRowId={(row) => row.sku}
|
|
1197
1201
|
/>
|
|
1198
1202
|
</div>
|
|
@@ -1216,7 +1220,7 @@ export const SelectionDisabledRows: Story = {
|
|
|
1216
1220
|
height="auto"
|
|
1217
1221
|
selectable
|
|
1218
1222
|
selection={selection}
|
|
1219
|
-
onSelectionChange={setSelection}
|
|
1223
|
+
onSelectionChange={(s) => setSelection(s.mode === 'include' ? s.ids : [])}
|
|
1220
1224
|
getRowId={(row) => row.sku}
|
|
1221
1225
|
isRowSelectable={(row) => row.stock !== 'Out of stock'}
|
|
1222
1226
|
/>
|
|
@@ -1819,7 +1823,7 @@ export const RoadmapAllInOne: Story = {
|
|
|
1819
1823
|
inlineEdit
|
|
1820
1824
|
selectable
|
|
1821
1825
|
selection={selection}
|
|
1822
|
-
onSelectionChange={setSelection}
|
|
1826
|
+
onSelectionChange={(s) => setSelection(s.mode === 'include' ? s.ids : [])}
|
|
1823
1827
|
columnVisibility={columnVisibility}
|
|
1824
1828
|
onColumnVisibilityChange={setColumnVisibility}
|
|
1825
1829
|
pinnedLeftColumns={['id']}
|
|
@@ -134,12 +134,14 @@ export interface DataTableProps<TData>
|
|
|
134
134
|
spreadsheetMode?: boolean
|
|
135
135
|
|
|
136
136
|
// ── L2 Selection(see data-table.spec.md「L2 選取」)──
|
|
137
|
-
/**
|
|
138
|
-
selection?: string[]
|
|
139
|
-
/** 預設選取(uncontrolled) */
|
|
140
|
-
defaultSelection?: string[]
|
|
141
|
-
/** Selection 變更 callback */
|
|
142
|
-
onSelectionChange?: (next:
|
|
137
|
+
/** 已選列(controlled)。傳 string[] = include shorthand;傳 DataTableSelection 支援反向選取(all + excluded) */
|
|
138
|
+
selection?: string[] | DataTableSelection
|
|
139
|
+
/** 預設選取(uncontrolled);同上接受 string[] 或 DataTableSelection */
|
|
140
|
+
defaultSelection?: string[] | DataTableSelection
|
|
141
|
+
/** Selection 變更 callback(emit DataTableSelection union;include / all 兩模型) */
|
|
142
|
+
onSelectionChange?: (next: DataTableSelection) => void
|
|
143
|
+
/** 全資料集筆數 M(server-side / filter 後);all 模式 count = totalCount − excluded.length(consumer 計算) */
|
|
144
|
+
totalCount?: number
|
|
143
145
|
/** 是否啟用 selection / 模式;true 等同 'multi' */
|
|
144
146
|
selectable?: boolean | 'single' | 'multi'
|
|
145
147
|
/** Row 是否可選(disabled rows 只 disable checkbox,row 內容正常 render) */
|
|
@@ -264,6 +266,48 @@ const cellEditId = (rowId: string, colId: string) => `${rowId}__${colId}`
|
|
|
264
266
|
// (詳 ./data-table.css)— consumer 可走 CSS override 改值,不再 hard-code in TS。
|
|
265
267
|
// L2 selection 內部 column id(避免 magic string 重複)
|
|
266
268
|
const SELECT_COL_ID = '__select__'
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* L2 選取模型(discriminated union,2026-06-22 對齊 MUI X DataGrid v8
|
|
272
|
+
* rowSelectionModel { type: include | exclude, ids } + AG Grid selectAll + toggledNodes)。
|
|
273
|
+
*
|
|
274
|
+
* - mode==='include' (ids):只選 ids 列(預設;= 載入/可見列逐一選取)。
|
|
275
|
+
* - mode==='all' (excluded):全資料集(filter 後)選取,扣掉 excluded —— 反向選取(inverted)。
|
|
276
|
+
* 解決「全選 10k 筆只載 50 筆 → 無法列舉其餘 ID」:all 模式「選取 = 全集 − excluded」,
|
|
277
|
+
* 任何 toggle 都只是 excluded 的 add/remove,對任意順序封閉、O(1)、不需列舉未載入 ID。
|
|
278
|
+
*
|
|
279
|
+
* 計數(consumer):mode==='all' ? totalCount − excluded.length : ids.length(需傳 totalCount)。
|
|
280
|
+
* 進入 all 模式:consumer 在「選取全部 M」hint 點擊時 setSelection({ mode: 'all', excluded: [] })。
|
|
281
|
+
*/
|
|
282
|
+
export type DataTableSelection =
|
|
283
|
+
| { mode: 'include'; ids: string[] }
|
|
284
|
+
| { mode: 'all'; excluded: string[] }
|
|
285
|
+
|
|
286
|
+
// 正規化:string[] shorthand → { mode:'include' }(向後相容既有 consumer 傳 ID 陣列)
|
|
287
|
+
function normalizeSelection(
|
|
288
|
+
input: string[] | DataTableSelection | undefined,
|
|
289
|
+
): DataTableSelection | undefined {
|
|
290
|
+
if (input === undefined) return undefined
|
|
291
|
+
if (Array.isArray(input)) return { mode: 'include', ids: input }
|
|
292
|
+
return input
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// union-aware「把 ids 設成 willSelect 狀態」純函式(對任意 toggle 順序封閉)
|
|
296
|
+
function applySelectIds(
|
|
297
|
+
sel: DataTableSelection,
|
|
298
|
+
ids: string[],
|
|
299
|
+
willSelect: boolean,
|
|
300
|
+
): DataTableSelection {
|
|
301
|
+
if (sel.mode === 'include') {
|
|
302
|
+
const set = new Set(sel.ids)
|
|
303
|
+
ids.forEach((id) => { if (willSelect) set.add(id); else set.delete(id) })
|
|
304
|
+
return { mode: 'include', ids: Array.from(set) }
|
|
305
|
+
}
|
|
306
|
+
// all 模式:選取 = 全集 − excluded → willSelect 移出 excluded;取消選取 加進 excluded
|
|
307
|
+
const set = new Set(sel.excluded)
|
|
308
|
+
ids.forEach((id) => { if (willSelect) set.delete(id); else set.add(id) })
|
|
309
|
+
return { mode: 'all', excluded: Array.from(set) }
|
|
310
|
+
}
|
|
267
311
|
const cellPadding: React.CSSProperties = { paddingBlock: 'var(--table-cell-py)', paddingInline: 'var(--table-cell-px)' }
|
|
268
312
|
const HEADER_BG = 'bg-muted'
|
|
269
313
|
|
|
@@ -774,6 +818,7 @@ function DataTableInner<TData>(
|
|
|
774
818
|
estimateRowHeight, tableOptions, rowActions, cellErrors,
|
|
775
819
|
pinnedLeftColumns, pinnedRightColumns, inlineEdit = false,
|
|
776
820
|
selection: selectionProp, defaultSelection, onSelectionChange,
|
|
821
|
+
totalCount,
|
|
777
822
|
selectable = false, isRowSelectable, getRowId, getRowAriaLabel,
|
|
778
823
|
preserveSelectionOnFilter = false,
|
|
779
824
|
columnVisibility: columnVisibilityProp, defaultColumnVisibility, onColumnVisibilityChange,
|
|
@@ -887,9 +932,9 @@ function DataTableInner<TData>(
|
|
|
887
932
|
// ── L2 Selection state ──
|
|
888
933
|
const enabled = selectable !== false
|
|
889
934
|
const mode = selectable === 'single' ? 'single' : 'multi'
|
|
890
|
-
const [selection, setSelection] = useControllable<
|
|
891
|
-
value: selectionProp,
|
|
892
|
-
defaultValue: defaultSelection ?? [],
|
|
935
|
+
const [selection, setSelection] = useControllable<DataTableSelection>({
|
|
936
|
+
value: normalizeSelection(selectionProp),
|
|
937
|
+
defaultValue: normalizeSelection(defaultSelection) ?? { mode: 'include', ids: [] },
|
|
893
938
|
onChange: onSelectionChange,
|
|
894
939
|
})
|
|
895
940
|
// Shift-click anchor:存最後一次「單擊」的 row id,shift-click 時做區間選
|
|
@@ -1381,7 +1426,7 @@ function DataTableInner<TData>(
|
|
|
1381
1426
|
// 內部 checkbox/radio 用 stopPropagation 避免 double-fire
|
|
1382
1427
|
const onCellClick = isDisabled ? undefined : (e: React.MouseEvent) => {
|
|
1383
1428
|
e.stopPropagation()
|
|
1384
|
-
if (mode === 'single') setSelection([rowId])
|
|
1429
|
+
if (mode === 'single') setSelection({ mode: 'include', ids: [rowId] })
|
|
1385
1430
|
else toggleRow(rowId, rowOriginal, { shiftKey: e.shiftKey })
|
|
1386
1431
|
}
|
|
1387
1432
|
return (
|
|
@@ -1408,7 +1453,7 @@ function DataTableInner<TData>(
|
|
|
1408
1453
|
) : (
|
|
1409
1454
|
<Checkbox
|
|
1410
1455
|
size={checkboxSize}
|
|
1411
|
-
checked={
|
|
1456
|
+
checked={isSelectedId(rowId)}
|
|
1412
1457
|
disabled={isDisabled}
|
|
1413
1458
|
aria-label={ariaLabel}
|
|
1414
1459
|
onClick={(e) => {
|
|
@@ -1679,8 +1724,10 @@ function DataTableInner<TData>(
|
|
|
1679
1724
|
React.useEffect(() => {
|
|
1680
1725
|
if (!enabled || preserveSelectionOnFilter) return
|
|
1681
1726
|
setSelection(prev => {
|
|
1682
|
-
|
|
1683
|
-
|
|
1727
|
+
// all 模式 = 「全部符合當前 filter」→ 不清(excluded 留著,被 filter 掉的 excluded 列無害)
|
|
1728
|
+
if (prev.mode === 'all') return prev
|
|
1729
|
+
const filtered = prev.ids.filter(id => visibleRowIdsSet.has(id))
|
|
1730
|
+
return filtered.length === prev.ids.length ? prev : { mode: 'include', ids: filtered }
|
|
1684
1731
|
})
|
|
1685
1732
|
}, [visibleRowIdsKey, enabled, preserveSelectionOnFilter, visibleRowIdsSet, setSelection])
|
|
1686
1733
|
|
|
@@ -1692,9 +1739,22 @@ function DataTableInner<TData>(
|
|
|
1692
1739
|
.map(r => r.id)
|
|
1693
1740
|
}, [rows, enabled, isRowSelectable])
|
|
1694
1741
|
|
|
1742
|
+
// Union-aware「某列是否選取」+ 計數(include = ids 內;all = 不在 excluded 內)
|
|
1743
|
+
const includeSet = React.useMemo(
|
|
1744
|
+
() => (selection.mode === 'include' ? new Set(selection.ids) : new Set<string>()),
|
|
1745
|
+
[selection],
|
|
1746
|
+
)
|
|
1747
|
+
const excludeSet = React.useMemo(
|
|
1748
|
+
() => (selection.mode === 'all' ? new Set(selection.excluded) : new Set<string>()),
|
|
1749
|
+
[selection],
|
|
1750
|
+
)
|
|
1751
|
+
const isSelectedId = React.useCallback(
|
|
1752
|
+
(id: string) => (selection.mode === 'include' ? includeSet.has(id) : !excludeSet.has(id)),
|
|
1753
|
+
[selection.mode, includeSet, excludeSet],
|
|
1754
|
+
)
|
|
1755
|
+
const hasAnySelection = selection.mode === 'all' || includeSet.size > 0
|
|
1695
1756
|
// Header tri-state checkbox value
|
|
1696
|
-
const
|
|
1697
|
-
const visibleSelectedCount = selectableVisibleIds.filter(id => selectionSet.has(id)).length
|
|
1757
|
+
const visibleSelectedCount = selectableVisibleIds.filter(id => isSelectedId(id)).length
|
|
1698
1758
|
const headerCheckedState: boolean | 'indeterminate' =
|
|
1699
1759
|
selectableVisibleIds.length === 0 ? false
|
|
1700
1760
|
: visibleSelectedCount === 0 ? false
|
|
@@ -1708,20 +1768,16 @@ function DataTableInner<TData>(
|
|
|
1708
1768
|
)
|
|
1709
1769
|
|
|
1710
1770
|
const toggleHeaderCheckbox = React.useCallback(() => {
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
} else {
|
|
1716
|
-
// 選全可見(扣除 disabled);保留可見外的既有 selection
|
|
1717
|
-
setSelection(prev => Array.from(new Set([...prev, ...selectableVisibleIds])))
|
|
1718
|
-
}
|
|
1771
|
+
// header tri-state visible-scoped:全可見已選 → 取消可見;否則 → 選全可見。
|
|
1772
|
+
// include / all 兩模型由 applySelectIds 處理(all 模式 toggle 改寫 excluded)。
|
|
1773
|
+
const willSelect = headerCheckedState !== true
|
|
1774
|
+
setSelection(prev => applySelectIds(prev, selectableVisibleIds, willSelect))
|
|
1719
1775
|
}, [headerCheckedState, selectableVisibleIds, setSelection])
|
|
1720
1776
|
|
|
1721
1777
|
const toggleRow = React.useCallback((rowId: string, rowOriginal: TData, opts?: { shiftKey?: boolean }) => {
|
|
1722
1778
|
if (isRowSelectable && !isRowSelectable(rowOriginal)) return
|
|
1723
1779
|
if (mode === 'single') {
|
|
1724
|
-
setSelection(
|
|
1780
|
+
setSelection(isSelectedId(rowId) ? { mode: 'include', ids: [] } : { mode: 'include', ids: [rowId] })
|
|
1725
1781
|
anchorRowIdRef.current = rowId
|
|
1726
1782
|
return
|
|
1727
1783
|
}
|
|
@@ -1739,24 +1795,16 @@ function DataTableInner<TData>(
|
|
|
1739
1795
|
return row && (!isRowSelectable || isRowSelectable(row.original))
|
|
1740
1796
|
})
|
|
1741
1797
|
// Mail / GitHub 慣例:shift-click 把 range 全變「rowId 點擊後該變的狀態」
|
|
1742
|
-
const willCheck = !
|
|
1743
|
-
setSelection(prev =>
|
|
1744
|
-
const set = new Set(prev)
|
|
1745
|
-
rangeIds.forEach(id => willCheck ? set.add(id) : set.delete(id))
|
|
1746
|
-
return Array.from(set)
|
|
1747
|
-
})
|
|
1798
|
+
const willCheck = !isSelectedId(rowId)
|
|
1799
|
+
setSelection(prev => applySelectIds(prev, rangeIds, willCheck))
|
|
1748
1800
|
return
|
|
1749
1801
|
}
|
|
1750
1802
|
}
|
|
1751
|
-
// 一般 toggle + 更新 anchor
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
if (set.has(rowId)) set.delete(rowId)
|
|
1755
|
-
else set.add(rowId)
|
|
1756
|
-
return Array.from(set)
|
|
1757
|
-
})
|
|
1803
|
+
// 一般 toggle + 更新 anchor(include / all 由 applySelectIds 處理)
|
|
1804
|
+
const willCheck = !isSelectedId(rowId)
|
|
1805
|
+
setSelection(prev => applySelectIds(prev, [rowId], willCheck))
|
|
1758
1806
|
anchorRowIdRef.current = rowId
|
|
1759
|
-
}, [isRowSelectable, mode,
|
|
1807
|
+
}, [isRowSelectable, mode, isSelectedId, rows, visibleIdToRow, setSelection])
|
|
1760
1808
|
|
|
1761
1809
|
// ── Cmd+A / Esc / Arrow keys 鍵盤 handler(table-level)──
|
|
1762
1810
|
// code-quality-allow: long-function — single keyboard dispatch covering Cmd+A / Esc / Arrow / Space + selection state mutations,拆 sub-handler 會切散 keyboard mode coherence
|
|
@@ -1823,18 +1871,18 @@ function DataTableInner<TData>(
|
|
|
1823
1871
|
// Cmd/Ctrl+A:選全可見(扣 disabled)— 對齊 Mail / GitHub / Linear 慣例
|
|
1824
1872
|
if ((e.metaKey || e.ctrlKey) && e.key === 'a' && mode === 'multi') {
|
|
1825
1873
|
e.preventDefault()
|
|
1826
|
-
setSelection(prev =>
|
|
1874
|
+
setSelection(prev => applySelectIds(prev, selectableVisibleIds, true))
|
|
1827
1875
|
return
|
|
1828
1876
|
}
|
|
1829
1877
|
// Esc:clear selection
|
|
1830
|
-
if (e.key === 'Escape' &&
|
|
1878
|
+
if (e.key === 'Escape' && hasAnySelection) {
|
|
1831
1879
|
e.preventDefault()
|
|
1832
|
-
setSelection([])
|
|
1880
|
+
setSelection({ mode: 'include', ids: [] })
|
|
1833
1881
|
anchorRowIdRef.current = null
|
|
1834
1882
|
return
|
|
1835
1883
|
}
|
|
1836
1884
|
},
|
|
1837
|
-
[enabled, mode,
|
|
1885
|
+
[enabled, mode, hasAnySelection, selectableVisibleIds, setSelection,
|
|
1838
1886
|
spreadsheetMode, selectedCellId, editingCellId, table, isCellEditable]
|
|
1839
1887
|
)
|
|
1840
1888
|
|
|
@@ -2906,8 +2954,8 @@ function DataTableInner<TData>(
|
|
|
2906
2954
|
if (enabled && mode === 'single') {
|
|
2907
2955
|
return (
|
|
2908
2956
|
<RadioGroupPrimitive.Root
|
|
2909
|
-
value={selection[0] ?? ''}
|
|
2910
|
-
onValueChange={(v) => v && setSelection([v])}
|
|
2957
|
+
value={selection.mode === 'include' ? (selection.ids[0] ?? '') : ''}
|
|
2958
|
+
onValueChange={(v) => v && setSelection({ mode: 'include', ids: [v] })}
|
|
2911
2959
|
>
|
|
2912
2960
|
{wrapWithDnd(tableContent)}
|
|
2913
2961
|
</RadioGroupPrimitive.Root>
|
|
@@ -71,16 +71,16 @@ export const UsageGuidance: Story = {
|
|
|
71
71
|
<p>適合 DateGrid 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
72
72
|
<ul className="space-y-1">
|
|
73
73
|
<li>
|
|
74
|
-
<LinkTo kind="Design System/Internal/DateGrid/展示" name="單日 — 生日 / 到期日"><span className="text-primary hover:
|
|
74
|
+
<LinkTo kind="Design System/Internal/DateGrid/展示" name="單日 — 生日 / 到期日"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Single — 生日 / 到期日</span></LinkTo>
|
|
75
75
|
</li>
|
|
76
76
|
<li>
|
|
77
|
-
<LinkTo kind="Design System/Internal/DateGrid/展示" name="多日 — 活動可參加日期"><span className="text-primary hover:
|
|
77
|
+
<LinkTo kind="Design System/Internal/DateGrid/展示" name="多日 — 活動可參加日期"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Multiple — 活動可參加日期</span></LinkTo>
|
|
78
78
|
</li>
|
|
79
79
|
<li>
|
|
80
|
-
<LinkTo kind="Design System/Internal/DateGrid/展示" name="範圍 — 分析時段 / 訂單範圍"><span className="text-primary hover:
|
|
80
|
+
<LinkTo kind="Design System/Internal/DateGrid/展示" name="範圍 — 分析時段 / 訂單範圍"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Range — 分析時段 / 訂單範圍</span></LinkTo>
|
|
81
81
|
</li>
|
|
82
82
|
<li>
|
|
83
|
-
<LinkTo kind="Design System/Internal/DateGrid/展示" name="行內 — 儀表板小卡"><span className="text-primary hover:
|
|
83
|
+
<LinkTo kind="Design System/Internal/DateGrid/展示" name="行內 — 儀表板小卡"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">行內 — Linear 專案截止日小卡</span></LinkTo>
|
|
84
84
|
</li>
|
|
85
85
|
</ul>
|
|
86
86
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親」)。</p>
|
|
@@ -47,11 +47,11 @@ export const UsageGuidance: Story = {
|
|
|
47
47
|
<div className="prose prose-sm max-w-prose mb-8">
|
|
48
48
|
<p>適合 DatePicker 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
49
49
|
<ul className="space-y-1">
|
|
50
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="四模式"><span className="text-primary hover:
|
|
51
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="可清除"><span className="text-primary hover:
|
|
52
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="尺寸"><span className="text-primary hover:
|
|
53
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="範圍模式:訂房 / 訂機票情境"><span className="text-primary hover:
|
|
54
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="展示樣式"><span className="text-primary hover:
|
|
50
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">請假單送審後日期欄位從可編輯轉唯讀/純展示(四模式)</span></LinkTo></li>
|
|
51
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="可清除"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">篩選器的選填截止日,填錯一鍵清空(可清除)</span></LinkTo></li>
|
|
52
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">緊湊工具列與標準表單的尺寸對應(尺寸)</span></LinkTo></li>
|
|
53
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="範圍模式:訂房 / 訂機票情境"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Range:訂房 / 訂機票情境</span></LinkTo></li>
|
|
54
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="展示樣式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">審批詳情頁唯讀展示申請日期(展示樣式)</span></LinkTo></li>
|
|
55
55
|
</ul>
|
|
56
56
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
|
|
57
57
|
</div>
|
|
@@ -53,19 +53,19 @@ export const UsageGuidance: Story = {
|
|
|
53
53
|
<p>適合 DescriptionList 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
54
54
|
<ul className="space-y-1">
|
|
55
55
|
<li>
|
|
56
|
-
<LinkTo kind="Design System/Components/DescriptionList/展示" name="使用者個資"><span className="text-primary hover:
|
|
56
|
+
<LinkTo kind="Design System/Components/DescriptionList/展示" name="使用者個資"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">使用者個資</span></LinkTo>
|
|
57
57
|
</li>
|
|
58
58
|
<li>
|
|
59
|
-
<LinkTo kind="Design System/Components/DescriptionList/展示" name="產品規格"><span className="text-primary hover:
|
|
59
|
+
<LinkTo kind="Design System/Components/DescriptionList/展示" name="產品規格"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">產品規格</span></LinkTo>
|
|
60
60
|
</li>
|
|
61
61
|
<li>
|
|
62
|
-
<LinkTo kind="Design System/Components/DescriptionList/展示" name="訂單明細"><span className="text-primary hover:
|
|
62
|
+
<LinkTo kind="Design System/Components/DescriptionList/展示" name="訂單明細"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">訂單明細</span></LinkTo>
|
|
63
63
|
</li>
|
|
64
64
|
<li>
|
|
65
|
-
<LinkTo kind="Design System/Components/DescriptionList/展示" name="詳情面板"><span className="text-primary hover:
|
|
65
|
+
<LinkTo kind="Design System/Components/DescriptionList/展示" name="詳情面板"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">詳情面板</span></LinkTo>
|
|
66
66
|
</li>
|
|
67
67
|
<li>
|
|
68
|
-
<LinkTo kind="Design System/Components/DescriptionList/展示" name="水平佈局"><span className="text-primary hover:
|
|
68
|
+
<LinkTo kind="Design System/Components/DescriptionList/展示" name="水平佈局"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">水平佈局</span></LinkTo>
|
|
69
69
|
</li>
|
|
70
70
|
</ul>
|
|
71
71
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
|
|
@@ -52,16 +52,16 @@ export const UsageGuidance: Story = {
|
|
|
52
52
|
<p>適合 Dialog 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
53
53
|
<ul className="space-y-1">
|
|
54
54
|
<li>
|
|
55
|
-
<LinkTo kind="Design System/Components/Dialog/展示" name="表單"><span className="text-primary hover:
|
|
55
|
+
<LinkTo kind="Design System/Components/Dialog/展示" name="表單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">表單</span></LinkTo>
|
|
56
56
|
</li>
|
|
57
57
|
<li>
|
|
58
|
-
<LinkTo kind="Design System/Components/Dialog/展示" name="長內容"><span className="text-primary hover:
|
|
58
|
+
<LinkTo kind="Design System/Components/Dialog/展示" name="長內容"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">長內容</span></LinkTo>
|
|
59
59
|
</li>
|
|
60
60
|
<li>
|
|
61
|
-
<LinkTo kind="Design System/Components/Dialog/展示" name="危險操作"><span className="text-primary hover:
|
|
61
|
+
<LinkTo kind="Design System/Components/Dialog/展示" name="危險操作"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">危險操作</span></LinkTo>
|
|
62
62
|
</li>
|
|
63
63
|
<li>
|
|
64
|
-
<LinkTo kind="Design System/Components/Dialog/展示" name="主體放清單"><span className="text-primary hover:
|
|
64
|
+
<LinkTo kind="Design System/Components/Dialog/展示" name="主體放清單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">主體放清單</span></LinkTo>
|
|
65
65
|
</li>
|
|
66
66
|
</ul>
|
|
67
67
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
|
|
@@ -963,7 +963,7 @@ export const Accessibility = {
|
|
|
963
963
|
<div className="max-w-3xl text-body text-fg-secondary">
|
|
964
964
|
<h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
|
|
965
965
|
<p className="whitespace-pre-line">{"無障礙能力沿用 Radix 選單元件的內建支援,涵蓋語意角色、選單導覽與焦點管理,設計師與工程師不需額外設定。\n\n鍵盤操作:\n\n- Tab — 將焦點移到觸發按鈕\n- Enter / Space / 向下鍵 — 開啟選單\n- 向上鍵 / 向下鍵 — 在選項之間移動\n- Enter — 選擇目前的選項\n- Esc — 關閉整個選單(含子選單;逐層收合用左方向鍵)\n\n焦點管理:選單開啟時焦點會進入選單,關閉後自動回到原本的觸發按鈕;目前導覽到的選項會以明顯的 highlight 底色標示,鍵盤使用者隨時看得到自己在哪一項。\n\n驗證:鍵盤可完整操作(不需滑鼠),文字對比達 WCAG AA;在 Storybook 無障礙檢查面板上應為零項嚴重問題。"}</p>
|
|
966
|
-
<p className="mt-4 text-fg-muted">完整 ARIA 細節參考 <a className="text-primary hover:
|
|
966
|
+
<p className="mt-4 text-fg-muted">完整 ARIA 細節參考 <a className="text-primary hover:text-primary-hover" href="https://www.radix-ui.com/primitives/docs/components/dropdown-menu#accessibility" target="_blank" rel="noreferrer">Radix Accessibility 文件</a>。</p>
|
|
967
967
|
</div>
|
|
968
968
|
),
|
|
969
969
|
}
|
|
@@ -58,19 +58,19 @@ export const UsageGuidance: Story = {
|
|
|
58
58
|
<p>適合 DropdownMenu 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
59
59
|
<ul className="space-y-1">
|
|
60
60
|
<li>
|
|
61
|
-
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="群組"><span className="text-primary hover:
|
|
61
|
+
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="群組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">群組 — 帳號選單(個人資料 / 設定 / 登出分組)</span></LinkTo>
|
|
62
62
|
</li>
|
|
63
63
|
<li>
|
|
64
|
-
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="後綴"><span className="text-primary hover:
|
|
64
|
+
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="後綴"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">後綴 — 收件匣未讀數 badge、外開連結提示</span></LinkTo>
|
|
65
65
|
</li>
|
|
66
66
|
<li>
|
|
67
|
-
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="子選單"><span className="text-primary hover:
|
|
67
|
+
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="子選單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">子選單 — 主題切換(淺色 / 深色 / 跟隨系統)</span></LinkTo>
|
|
68
68
|
</li>
|
|
69
69
|
<li>
|
|
70
|
-
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="勾選項"><span className="text-primary hover:
|
|
70
|
+
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="勾選項"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">勾選項 — 表格顯示欄位切換</span></LinkTo>
|
|
71
71
|
</li>
|
|
72
72
|
<li>
|
|
73
|
-
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="單選"><span className="text-primary hover:
|
|
73
|
+
<LinkTo kind="Design System/Components/DropdownMenu/展示" name="單選"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">單選 — 檔案清單排序方式</span></LinkTo>
|
|
74
74
|
</li>
|
|
75
75
|
</ul>
|
|
76
76
|
<p className="text-fg-muted mt-3">判斷不確定時:先看「選完之後畫面是否需要保留選中狀態」——需要就改用 Select / SelectMenu。下方的「DropdownMenu vs 選值元件」與「群組 vs 分隔線」範例提供更完整的對照。</p>
|
|
@@ -59,10 +59,10 @@ export const UsageGuidance: Story = {
|
|
|
59
59
|
<p>適合 Empty 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
60
60
|
<ul className="space-y-1">
|
|
61
61
|
<li>
|
|
62
|
-
<LinkTo kind="Design System/Components/Empty/展示" name="搜尋無結果"><span className="text-primary hover:
|
|
62
|
+
<LinkTo kind="Design System/Components/Empty/展示" name="搜尋無結果"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">搜尋無結果</span></LinkTo>
|
|
63
63
|
</li>
|
|
64
64
|
<li>
|
|
65
|
-
<LinkTo kind="Design System/Components/Empty/展示" name="空清單"><span className="text-primary hover:
|
|
65
|
+
<LinkTo kind="Design System/Components/Empty/展示" name="空清單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">空清單</span></LinkTo>
|
|
66
66
|
</li>
|
|
67
67
|
</ul>
|
|
68
68
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親」)。</p>
|
|
@@ -49,10 +49,10 @@ export const UsageGuidance: Story = {
|
|
|
49
49
|
<div className="prose prose-sm max-w-prose mb-8">
|
|
50
50
|
<p>適合 Field 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
51
51
|
<ul className="space-y-1">
|
|
52
|
-
<li><LinkTo kind="Design System/Components/Field/展示" name="垂直"><span className="text-primary hover:
|
|
53
|
-
<li><LinkTo kind="Design System/Components/Field/展示" name="水平"><span className="text-primary hover:
|
|
54
|
-
<li><LinkTo kind="Design System/Components/Field/展示" name="混合控制元件的高度對齊"><span className="text-primary hover:
|
|
55
|
-
<li><LinkTo kind="Design System/Components/Field/展示" name="SegmentedControl 作為 Field 控制元件"><span className="text-primary hover:
|
|
52
|
+
<li><LinkTo kind="Design System/Components/Field/展示" name="垂直"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">註冊 / 建立專案表單 — 引導式逐欄輸入(垂直佈局)</span></LinkTo></li>
|
|
53
|
+
<li><LinkTo kind="Design System/Components/Field/展示" name="水平"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">帳號設定 / 偏好設定頁 — 修改已知屬性(水平佈局)</span></LinkTo></li>
|
|
54
|
+
<li><LinkTo kind="Design System/Components/Field/展示" name="混合控制元件的高度對齊"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">同一表單混用 Input / Select / DatePicker 等控制元件</span></LinkTo></li>
|
|
55
|
+
<li><LinkTo kind="Design System/Components/Field/展示" name="SegmentedControl 作為 Field 控制元件"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">少量互斥選項(計費週期、版面密度)用 SegmentedControl 當控制元件</span></LinkTo></li>
|
|
56
56
|
</ul>
|
|
57
57
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
|
|
58
58
|
</div>
|
|
@@ -43,19 +43,19 @@ export const UsageGuidance: Story = {
|
|
|
43
43
|
<p>適合 FileItem 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
44
44
|
<ul className="space-y-1">
|
|
45
45
|
<li>
|
|
46
|
-
<LinkTo kind="Design System/Components/FileItem/展示" name="豐富樣式"><span className="text-primary hover:
|
|
46
|
+
<LinkTo kind="Design System/Components/FileItem/展示" name="豐富樣式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">圖片 / 設計稿上傳 — 需要縮圖預覽(豐富樣式)</span></LinkTo>
|
|
47
47
|
</li>
|
|
48
48
|
<li>
|
|
49
|
-
<LinkTo kind="Design System/Components/FileItem/展示" name="緊湊樣式"><span className="text-primary hover:
|
|
49
|
+
<LinkTo kind="Design System/Components/FileItem/展示" name="緊湊樣式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">CSV / JSON 批次匯入的密集清單(緊湊樣式)</span></LinkTo>
|
|
50
50
|
</li>
|
|
51
51
|
<li>
|
|
52
|
-
<LinkTo kind="Design System/Components/FileItem/展示" name="懸停替換"><span className="text-primary hover:
|
|
52
|
+
<LinkTo kind="Design System/Components/FileItem/展示" name="懸停替換"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">上傳完成後滑入整列、狀態 icon 換成下載鈕(懸停替換)</span></LinkTo>
|
|
53
53
|
</li>
|
|
54
54
|
<li>
|
|
55
|
-
<LinkTo kind="Design System/Components/FileItem/展示" name="已上傳"><span className="text-primary hover:
|
|
55
|
+
<LinkTo kind="Design System/Components/FileItem/展示" name="已上傳"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">工單 / 訊息附件的已上傳靜態檔案</span></LinkTo>
|
|
56
56
|
</li>
|
|
57
57
|
<li>
|
|
58
|
-
<LinkTo kind="Design System/Components/FileItem/展示" name="緊湊 混合"><span className="text-primary hover:
|
|
58
|
+
<LinkTo kind="Design System/Components/FileItem/展示" name="緊湊 混合"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">同一批次混合上傳中 / 完成 / 失敗狀態的清單</span></LinkTo>
|
|
59
59
|
</li>
|
|
60
60
|
</ul>
|
|
61
61
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
|
|
@@ -64,16 +64,16 @@ export const UsageGuidance: Story = {
|
|
|
64
64
|
<p>適合 FileUpload 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
65
65
|
<ul className="space-y-1">
|
|
66
66
|
<li>
|
|
67
|
-
<LinkTo kind="Design System/Components/FileUpload/展示" name="單檔上傳"><span className="text-primary hover:
|
|
67
|
+
<LinkTo kind="Design System/Components/FileUpload/展示" name="單檔上傳"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">履歷 / 大頭貼等單檔上傳</span></LinkTo>
|
|
68
68
|
</li>
|
|
69
69
|
<li>
|
|
70
|
-
<LinkTo kind="Design System/Components/FileUpload/展示" name="批次上傳"><span className="text-primary hover:
|
|
70
|
+
<LinkTo kind="Design System/Components/FileUpload/展示" name="批次上傳"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">相簿照片 / 多檔附件批次上傳</span></LinkTo>
|
|
71
71
|
</li>
|
|
72
72
|
<li>
|
|
73
|
-
<LinkTo kind="Design System/Components/FileUpload/展示" name="內建 files 屬性"><span className="text-primary hover:
|
|
73
|
+
<LinkTo kind="Design System/Components/FileUpload/展示" name="內建 files 屬性"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">上傳後直接顯示進度清單(內建 files 屬性)</span></LinkTo>
|
|
74
74
|
</li>
|
|
75
75
|
<li>
|
|
76
|
-
<LinkTo kind="Design System/Components/FileUpload/展示" name="自訂內容"><span className="text-primary hover:
|
|
76
|
+
<LinkTo kind="Design System/Components/FileUpload/展示" name="自訂內容"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">品牌化上傳區 — 自訂 dropzone 內容</span></LinkTo>
|
|
77
77
|
</li>
|
|
78
78
|
</ul>
|
|
79
79
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
|