@qijenchen/design-system 0.1.0-beta.10 → 0.1.0-beta.13
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/CLAUDE.md +201 -0
- package/README.md +7 -15
- package/cli-init.mjs +90 -0
- package/ds-canonical/commands/README.md +26 -0
- package/ds-canonical/commands/gov-status.md +79 -0
- package/ds-canonical/hooks/README.md +145 -0
- package/ds-canonical/hooks/_log-fire.sh +44 -0
- package/ds-canonical/hooks/block_prototype_imports.py +111 -0
- package/ds-canonical/hooks/check_app_shell_primary_header_consistency.sh +68 -0
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +88 -0
- package/ds-canonical/hooks/check_audit_sample_escape.sh +73 -0
- package/ds-canonical/hooks/check_benchmark_citation.sh +106 -0
- package/ds-canonical/hooks/check_canonical_propagation.sh +189 -0
- package/ds-canonical/hooks/check_chrome_header_handcraft.sh +70 -0
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +83 -0
- package/ds-canonical/hooks/check_codex_collab_5step.sh +108 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +117 -0
- package/ds-canonical/hooks/check_dim_count_drift.sh +72 -0
- package/ds-canonical/hooks/check_field_controls_contracts.sh +110 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +205 -0
- package/ds-canonical/hooks/check_file_size_budget.sh +60 -0
- package/ds-canonical/hooks/check_header_with_tabs_border.sh +87 -0
- package/ds-canonical/hooks/check_main_branch_workbench.sh +93 -0
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +165 -0
- package/ds-canonical/hooks/check_opacity_token_usage.sh +149 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +194 -0
- package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +56 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +53 -0
- package/ds-canonical/hooks/check_propose_plain_chinese.sh +74 -0
- package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +70 -0
- package/ds-canonical/hooks/check_select_all_canonical.sh +58 -0
- package/ds-canonical/hooks/check_solo_workflow.sh +258 -0
- package/ds-canonical/hooks/check_spec_class_drift.sh +88 -0
- package/ds-canonical/hooks/check_story_invariants.sh +612 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +105 -0
- package/ds-canonical/hooks/check_tab_lg_chrome_header_equal.sh +66 -0
- package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +104 -0
- package/ds-canonical/hooks/enforce_home_charter.sh +44 -0
- package/ds-canonical/hooks/inject_pending_self_audit.sh +204 -0
- package/ds-canonical/hooks/lib/_approval_re.sh +33 -0
- package/ds-canonical/hooks/lib/_code_quality.sh +73 -0
- package/ds-canonical/hooks/lib/_cva_default_sync.sh +69 -0
- package/ds-canonical/hooks/lib/_governance_coverage_check.sh +49 -0
- package/ds-canonical/hooks/lib/_hardcoded_strings.sh +163 -0
- package/ds-canonical/hooks/lib/_layout_space_canonical.sh +56 -0
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +141 -0
- package/ds-canonical/hooks/lib/_person_data_richness.sh +42 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +48 -0
- package/ds-canonical/hooks/lib/_token_hygiene.sh +95 -0
- package/ds-canonical/hooks/log_governance_fires.sh +50 -0
- package/ds-canonical/hooks/log_skill_invokes.sh +41 -0
- package/ds-canonical/hooks/post_edit_dispatcher.sh +62 -0
- package/ds-canonical/hooks/retired/check_anatomy_section_numbering.sh +106 -0
- package/ds-canonical/hooks/retired/check_avatar_hovercard.sh +90 -0
- package/ds-canonical/hooks/retired/check_button_icon_literal.sh.retired-2026-04-28 +38 -0
- package/ds-canonical/hooks/retired/check_container_breathing.sh +142 -0
- package/ds-canonical/hooks/retired/check_governance_compliance.sh +61 -0
- package/ds-canonical/hooks/retired/check_icon_only_padding_formula.sh +104 -0
- package/ds-canonical/hooks/retired/check_item_content_primitive.sh +150 -0
- package/ds-canonical/hooks/retired/check_item_list_gap.sh +153 -0
- package/ds-canonical/hooks/retired/check_sideoffset_canonical.sh +65 -0
- package/ds-canonical/hooks/retired/check_spec_iteration_tag.sh +87 -0
- package/ds-canonical/hooks/retired/check_ssot_consultation.sh +88 -0
- package/ds-canonical/hooks/retired/check_sync_update.sh +20 -0
- package/ds-canonical/hooks/retired/check_third_party_dom_verified.sh +95 -0
- package/ds-canonical/hooks/retired/enforce_home_charter.sh +125 -0
- package/ds-canonical/hooks/retired/post_edit_canonical_interrogate.sh +109 -0
- package/ds-canonical/hooks/retired/pre_edit_spec_check.sh +68 -0
- package/ds-canonical/hooks/retired/pre_new_component_spec.sh +39 -0
- package/ds-canonical/hooks/retired/pre_write_subsumption_check.sh +112 -0
- package/ds-canonical/hooks/retired/stop_meta_self_audit.sh.retired-2026-05-13 +76 -0
- package/ds-canonical/hooks/retired/tests/test_check_anatomy_section_numbering.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_avatar_hovercard.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_container_breathing.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_governance_compliance.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_icon_only_padding_formula.sh +79 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_content_primitive.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_item_list_gap.sh +163 -0
- package/ds-canonical/hooks/retired/tests/test_check_sideoffset_canonical.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_spec_iteration_tag.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_ssot_consultation.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_check_sync_update.sh +14 -0
- package/ds-canonical/hooks/retired/tests/test_check_third_party_dom_verified.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_enforce_home_charter.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_edit_spec_check.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_new_component_spec.sh +15 -0
- package/ds-canonical/hooks/retired/tests/test_pre_write_subsumption_check.sh +63 -0
- package/ds-canonical/hooks/session_start_governance_check.sh +263 -0
- package/ds-canonical/hooks/stop_passive_logging.sh +322 -0
- package/ds-canonical/hooks/stop_self_audit.sh +450 -0
- package/ds-canonical/hooks/tests/KNOWN-BROKEN.md +15 -0
- package/ds-canonical/hooks/tests/run-all.sh +76 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +143 -0
- package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +140 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +93 -0
- package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +115 -0
- package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +133 -0
- package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +123 -0
- package/ds-canonical/hooks/tests/test_check_code_quality.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +96 -0
- package/ds-canonical/hooks/tests/test_check_cva_default_sync.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +122 -0
- package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +98 -0
- package/ds-canonical/hooks/tests/test_check_field_controls_contracts.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +194 -0
- package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +32 -0
- package/ds-canonical/hooks/tests/test_check_hardcoded_strings.sh +14 -0
- package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_layout_space_canonical.sh +73 -0
- package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +147 -0
- package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +136 -0
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +110 -0
- package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +148 -0
- package/ds-canonical/hooks/tests/test_check_peoplepicker_ssot_drift.sh +108 -0
- package/ds-canonical/hooks/tests/test_check_person_data_richness.sh +58 -0
- package/ds-canonical/hooks/tests/test_check_pixel_quantified_audit.sh +142 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +126 -0
- package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +117 -0
- package/ds-canonical/hooks/tests/test_check_select_all_canonical.sh +125 -0
- package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +201 -0
- package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +135 -0
- package/ds-canonical/hooks/tests/test_check_story_anatomy.sh.broken +197 -0
- package/ds-canonical/hooks/tests/test_check_story_category.sh.broken +187 -0
- package/ds-canonical/hooks/tests/test_check_story_compile_drift.sh +15 -0
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +209 -0
- package/ds-canonical/hooks/tests/test_check_story_name_jargon.sh.broken +53 -0
- package/ds-canonical/hooks/tests/test_check_story_slot_split.sh +156 -0
- package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +176 -0
- package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +138 -0
- package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +21 -0
- package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +169 -0
- package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +77 -0
- package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +125 -0
- package/ds-canonical/hooks/tests/test_log_governance_fires.sh +10 -0
- package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +7 -0
- package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +108 -0
- package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +143 -0
- package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +95 -0
- package/ds-canonical/hooks/tests/test_stop_governance_drift_check.sh.broken +125 -0
- package/ds-canonical/hooks/tests/test_stop_harvest_corrections.sh +10 -0
- package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +100 -0
- package/ds-canonical/hooks/tests/test_stop_self_audit.sh +76 -0
- package/ds-canonical/hooks/tests/test_stop_tsc_sanity.sh +10 -0
- package/ds-canonical/references/README.md +43 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +74 -0
- package/ds-canonical/references/build-ui-canonicals.md +69 -0
- package/ds-canonical/references/cva-patterns.md +41 -0
- package/ds-canonical/references/drag-canonical.md +331 -0
- package/ds-canonical/references/item-anatomy-recipe.md +225 -0
- package/ds-canonical/references/naming-conventions.md +56 -0
- package/ds-canonical/references/principle-dim-map.json +515 -0
- package/ds-canonical/references/props-naming.md +45 -0
- package/ds-canonical/references/spec-rules.md +58 -0
- package/ds-canonical/references/ssot-consultation.md +63 -0
- package/ds-canonical/references/ssot-index.md +40 -0
- package/ds-canonical/references/story-baseline-registry.json +79 -0
- package/ds-canonical/references/structural-token-retention.md +42 -0
- package/ds-canonical/references/tailwind-gotchas.md +87 -0
- package/ds-canonical/references/ui-dev-rules.md +60 -0
- package/ds-canonical/rules/README.md +34 -0
- package/ds-canonical/rules/meta-patterns.md +87 -0
- package/ds-canonical/rules/self-verify.md +53 -0
- package/ds-canonical/rules/spec-rules.md +25 -0
- package/ds-canonical/rules/story-rules.md +56 -0
- package/ds-canonical/rules/ui-development.md +87 -0
- package/ds-canonical/skills/README.md +88 -0
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +181 -0
- package/ds-canonical/skills/code-quality-audit/SKILL.md +63 -0
- package/ds-canonical/skills/codex-collab/SKILL.md +249 -0
- package/ds-canonical/skills/codex-collab/references/brief-template.md +48 -0
- package/ds-canonical/skills/codex-collab/references/transport.md +58 -0
- package/ds-canonical/skills/codify-corrections/SKILL.md +184 -0
- package/ds-canonical/skills/codify-principle/SKILL.md +151 -0
- package/ds-canonical/skills/component-quality-gate/SKILL.md +102 -0
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +79 -0
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +247 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +123 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +165 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +91 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +229 -0
- package/ds-canonical/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +343 -0
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1260 -0
- package/ds-canonical/skills/design-system-audit/references/checkpoints.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +240 -0
- package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +364 -0
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +175 -0
- package/ds-canonical/skills/design-system-audit/references/spec-template.md +66 -0
- package/ds-canonical/skills/ensure-canonical/SKILL.md +196 -0
- package/ds-canonical/skills/governance-health/SKILL.md +146 -0
- package/ds-canonical/skills/knowledge-prune/SKILL.md +303 -0
- package/ds-canonical/skills/new-component/SKILL.md +170 -0
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +85 -0
- package/ds-canonical/skills/performance-audit/SKILL.md +107 -0
- package/ds-canonical/skills/product-ui-audit/SKILL.md +230 -0
- package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +246 -0
- package/ds-canonical/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/skills/product-ui-audit/references/report-template.md +159 -0
- package/ds-canonical/skills/propose-options/SKILL.md +177 -0
- package/ds-canonical/skills/prototype/SKILL.md +244 -0
- package/ds-canonical/skills/prototype/references/audit-checks.md +37 -0
- package/ds-canonical/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/skills/prototype/references/checkpoints.md +191 -0
- package/ds-canonical/skills/prototype/references/evaluation-matrix.md +141 -0
- package/ds-canonical/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/skills/prototype/references/proposal-template.md +229 -0
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +198 -0
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +159 -0
- package/ds-canonical/skills/story-writing/SKILL.md +122 -0
- package/ds-canonical/skills/story-writing/references/anatomy-standard.md +217 -0
- package/ds-canonical/skills/story-writing/references/category-templates.md +174 -0
- package/ds-canonical/skills/story-writing/references/example-selection.md +70 -0
- package/ds-canonical/skills/story-writing/references/self-check.md +20 -0
- package/ds-canonical/skills/ux-audit/SKILL.md +130 -0
- package/ds-canonical/skills/visual-audit/SKILL.md +245 -0
- package/ds-canonical/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/skills/visual-audit/references/audit-architecture.md +100 -0
- package/ds-canonical/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/package.json +9 -5
- package/src/components/Accordion/accordion.spec.md +114 -0
- package/src/components/Alert/alert.spec.md +197 -0
- package/src/components/AppShell/app-shell.spec.md +331 -0
- package/src/components/AspectRatio/aspect-ratio.spec.md +134 -0
- package/src/components/Avatar/avatar.spec.md +329 -0
- package/src/components/Badge/badge.spec.md +380 -0
- package/src/components/Breadcrumb/breadcrumb.spec.md +257 -0
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +210 -0
- package/src/components/Button/button.spec.md +460 -0
- package/src/components/Calendar/calendar.spec.md +242 -0
- package/src/components/Carousel/carousel.spec.md +253 -0
- package/src/components/Chart/chart.spec.md +155 -0
- package/src/components/Checkbox/checkbox.spec.md +344 -0
- package/src/components/Chip/chip.spec.md +237 -0
- package/src/components/CircularProgress/circular-progress.spec.md +268 -0
- package/src/components/Coachmark/coachmark.spec.md +230 -0
- package/src/components/Combobox/combobox.spec.md +180 -0
- package/src/components/Command/command.spec.md +171 -0
- package/src/components/DataTable/data-table.spec.md +525 -0
- package/src/components/DateGrid/date-grid.spec.md +215 -0
- package/src/components/DatePicker/date-picker.spec.md +334 -0
- package/src/components/DescriptionList/description-list.spec.md +214 -0
- package/src/components/Dialog/dialog.spec.md +202 -0
- package/src/components/DropdownMenu/dropdown-menu.spec.md +250 -0
- package/src/components/Empty/empty.spec.md +214 -0
- package/src/components/Field/field-controls.spec.md +338 -0
- package/src/components/Field/field.spec.md +438 -0
- package/src/components/Field/form-validation.spec.md +152 -0
- package/src/components/FieldControlGroup/field-control-group.spec.md +176 -0
- package/src/components/FileItem/file-item.spec.md +467 -0
- package/src/components/FileUpload/file-upload.spec.md +123 -0
- package/src/components/FileViewer/file-viewer.spec.md +373 -0
- package/src/components/HoverCard/hover-card.spec.md +157 -0
- package/src/components/Input/input.spec.md +193 -0
- package/src/components/LinkInput/link-input.spec.md +130 -0
- package/src/components/Menu/menu-item.spec.md +290 -0
- package/src/components/NameCard/name-card.spec.md +171 -0
- package/src/components/Notice/notice.spec.md +149 -0
- package/src/components/NumberInput/number-input.spec.md +126 -0
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +120 -0
- package/src/components/PeoplePicker/people-picker.spec.md +263 -0
- package/src/components/Popover/popover.spec.md +198 -0
- package/src/components/ProgressBar/progress-bar.spec.md +232 -0
- package/src/components/RadioGroup/radio-group.spec.md +141 -0
- package/src/components/Rating/rating.spec.md +208 -0
- package/src/components/ScrollArea/scroll-area.spec.md +145 -0
- package/src/components/SegmentedControl/segmented-control.spec.md +295 -0
- package/src/components/Select/select.spec.md +299 -0
- package/src/components/SelectMenu/select-menu.spec.md +220 -0
- package/src/components/SelectionControl/selection-item.spec.md +128 -0
- package/src/components/Separator/separator.spec.md +109 -0
- package/src/components/Sheet/sheet.spec.md +148 -0
- package/src/components/Sidebar/sidebar.spec.md +713 -0
- package/src/components/Skeleton/skeleton.spec.md +104 -0
- package/src/components/Slider/slider.spec.md +353 -0
- package/src/components/Steps/steps.spec.md +465 -0
- package/src/components/Switch/switch.spec.md +215 -0
- package/src/components/Tabs/tabs.spec.md +314 -0
- package/src/components/Tag/tag.spec.md +282 -0
- package/src/components/Textarea/textarea.spec.md +151 -0
- package/src/components/TimePicker/time-picker.spec.md +279 -0
- package/src/components/Toast/toast.spec.md +177 -0
- package/src/components/Tooltip/tooltip.spec.md +139 -0
- package/src/components/TreeView/tree-view.spec.md +374 -0
- package/src/patterns/action-bar/action-bar.spec.md +458 -0
- package/src/patterns/element-anatomy/element-anatomy.spec.md +215 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +315 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +1042 -0
- package/src/patterns/header-canonical/header-canonical.spec.md +285 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +191 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +428 -0
- package/src/patterns/resize-handle/resize-handle.spec.md +109 -0
- package/src/tokens/color/color.spec.md +804 -0
- package/src/tokens/density/density.spec.md +127 -0
- package/src/tokens/elevation/elevation.spec.md +81 -0
- package/src/tokens/layoutSpace/layoutSpace.spec.md +314 -0
- package/src/tokens/motion/motion.spec.md +97 -0
- package/src/tokens/opacity/opacity.spec.md +78 -0
- package/src/tokens/orphan-tokens.spec.md +117 -0
- package/src/tokens/radius/radius.spec.md +123 -0
- package/src/tokens/typography/typography.spec.md +202 -0
- package/src/tokens/uiSize/uiSize.spec.md +438 -0
- package/src/styles/preset.css +0 -31
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: PeoplePicker
|
|
3
|
+
family: 4
|
|
4
|
+
variants: {}
|
|
5
|
+
sizes: {}
|
|
6
|
+
traits:
|
|
7
|
+
- isInputLike
|
|
8
|
+
benchmark:
|
|
9
|
+
- Ant Design Mentions: github.com/ant-design/ant-design/tree/master/components/mentions
|
|
10
|
+
- MUI Autocomplete: github.com/mui/material-ui/tree/master/packages/mui-material/src/Autocomplete
|
|
11
|
+
- Polaris Combobox: github.com/Shopify/polaris/tree/main/polaris-react/src/components/Combobox
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
|
|
15
|
+
|
|
16
|
+
# PeoplePicker 設計原則
|
|
17
|
+
|
|
18
|
+
## 定位
|
|
19
|
+
|
|
20
|
+
PeoplePicker 是**人員選擇器**——專為「從人員清單中選一個或多個」設計的複合元件。外觀對齊 Select / Combobox,差異在選項前綴有 Avatar 視覺。
|
|
21
|
+
|
|
22
|
+
**實作基礎**:組合元件——Popover + Command(cmdk)搜尋 + SelectMenu 浮層 + PersonDisplay render。無直接 external primitive base(底層靠 shadcn passthrough 的 Popover / Command)。
|
|
23
|
+
|
|
24
|
+
共用規則見 `../Field/field-controls.spec.md`。本文件只記錄 PeoplePicker 特有的原則。
|
|
25
|
+
|
|
26
|
+
**Layout Family**:CLAUDE.md 4-Family Model **Family 4(Field control layout)** 消費者。結構繼承 `components/Field/field-controls.spec.md` 的 `fieldWrapperStyles + [startIcon?] [<editable>] [endAction?]` 規格,視覺對齊 Family 1(Menu item)讓 SelectMenu trigger + options 連續一致。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 何時用
|
|
31
|
+
|
|
32
|
+
- **指派人員**:指派 task、assignee、reviewer 選擇
|
|
33
|
+
- **加入成員**:team / channel / project 加人
|
|
34
|
+
- **@提及選擇器**:文章 / 留言 / chat 內插入 @user
|
|
35
|
+
- **多人協作選擇**:訂單協作人員、共同擁有者
|
|
36
|
+
- **需要 avatar 視覺識別**:人員清單視覺辨識高於純文字 label
|
|
37
|
+
|
|
38
|
+
## 何時不用
|
|
39
|
+
|
|
40
|
+
| 場景 | 改用 | 原因 |
|
|
41
|
+
|------|------|------|
|
|
42
|
+
| 一般分類選擇(非人員)| `Select` / `Combobox` | PeoplePicker 專為人員設計,有 Avatar prefix |
|
|
43
|
+
| 單選純文字選項(狀態 / 類別)| `Select` | PeoplePicker 開銷較大,靠 Avatar 識別;非人員用 Select |
|
|
44
|
+
| 多選非人員 tag | `Combobox` | Combobox 是通用多選,無 Avatar 假設 |
|
|
45
|
+
| 只顯示單一人員(不需選擇)| `NameCard` + `Avatar` | PeoplePicker 是選擇器,純展示用 NameCard |
|
|
46
|
+
| 顯示整個 team 成員列表 | `Avatar.Group` / 自訂 list | PeoplePicker 是「選人」介面,不是「展示 team」 |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 單選 vs 多選
|
|
51
|
+
|
|
52
|
+
透過 `value` prop 的類型決定:
|
|
53
|
+
|
|
54
|
+
| 模式 | value 類型 | 視覺 |
|
|
55
|
+
|------|-----------|------|
|
|
56
|
+
| 單選 | `PersonValue \| null` | Field 顯示單一 Avatar + Name |
|
|
57
|
+
| 多選 | `PersonValue[]` | Field 顯示 Avatar 陣列(與 Combobox 多選同模式)|
|
|
58
|
+
|
|
59
|
+
**判斷**:
|
|
60
|
+
- 「這個 task 指派給誰」→ 單選(assignee 通常一個)
|
|
61
|
+
- 「選擇 reviewers」→ 多選(reviewer 可多人)
|
|
62
|
+
- 「加入 channel 的成員」→ 多選
|
|
63
|
+
- 「文章作者」→ 單選
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 搜尋
|
|
68
|
+
|
|
69
|
+
PeoplePicker 永遠支援搜尋(內部使用 `Command` / cmdk)——因為人員清單通常規模較大且使用者記得人名關鍵字(符合 Select spec「Searchable 開啟判斷」的「label 性質」主判準:人名是獨特關鍵字)。
|
|
70
|
+
|
|
71
|
+
- **搜尋 placeholder**:預設「搜尋人員…」,可透過 `searchPlaceholder` 覆寫
|
|
72
|
+
- **空狀態**:預設「沒有符合的人員」,透過 `emptyText` 覆寫
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 與 Select / Combobox 的分界
|
|
77
|
+
|
|
78
|
+
| | PeoplePicker | Select | Combobox |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| 資料類型 | **人員**(含 Avatar) | 任意(純文字)| 任意(純文字)|
|
|
81
|
+
| 單選 / 多選 | 兩者皆支援(value 類型決定)| 單選 | 多選 |
|
|
82
|
+
| 視覺 prefix | Avatar(必備) | 可選 startIcon | 無 |
|
|
83
|
+
| 底層實作 | Popover + Command + SelectMenu | native `<select>` | native `<select>` + Tag overlay |
|
|
84
|
+
| 搜尋 | 永遠啟用(人名本質)— 走 inline-trigger | 可選 `searchable`(單選短列表 → inline-trigger only)| 可選 `searchable` + `searchIn='menu' \| 'trigger'`(多選 → 兩種模式)|
|
|
85
|
+
|
|
86
|
+
**搜尋型態 SSOT canonical**(A1-A5 spec,2026-05-11;2026-05-12 補 PeoplePicker multi `searchIn` opt-in):
|
|
87
|
+
|
|
88
|
+
| 型態 | 使用情境 | 元件 API |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| **none** | 短列表 (< 7 選項) / 自然語言 label 可用 native type-ahead | 不傳 `searchable` |
|
|
91
|
+
| **inline-trigger** | 單選 + 需快速 type-and-pick;trigger 變 input | `<Select searchable />` / `<Combobox searchable searchIn='trigger' />` / `<PeoplePicker />` single / `<PeoplePicker multi searchIn='trigger' />` |
|
|
92
|
+
| **panel-top** | 多選 + 連續 type-and-select(關鍵字保留在 menu 不被 chip 切走) | `<Combobox searchable searchIn='menu' />` / `<PeoplePicker multi />`(default menu) |
|
|
93
|
+
|
|
94
|
+
### Trigger display SSOT canonical table(2026-05-15 v3 user verbatim 整理)
|
|
95
|
+
|
|
96
|
+
**PeoplePicker 是獨立元件,有自己的另一層 SSOT,並非完全繼承 Select / Combobox**。下表標出哪些繼承、哪些 PeoplePicker 自己定義 — **改一處先看本表,違反該層 SSOT = 漂移**。
|
|
97
|
+
|
|
98
|
+
#### A. 元件本質繼承表
|
|
99
|
+
|
|
100
|
+
| 面向 | 來源 SSOT |
|
|
101
|
+
|---|---|
|
|
102
|
+
| 單人互動邏輯(open/close/keyboard/搜尋)| **繼承 Select**(只多套一層 Avatar 視覺包裝)|
|
|
103
|
+
| 多人互動邏輯(N tag / overflow / inline-search)| **繼承 Combobox**(但 tag 和溢出**換成圓形 avatar 視覺**)|
|
|
104
|
+
| Avatar 在 field 內 padding(12px 固定)| **PeoplePicker 自己**(不繼承 Combobox density-dependent 公式)|
|
|
105
|
+
| +N 溢出 chip 形狀(**圓形**)| **PeoplePicker 自己**(不繼承 Combobox 矩形 tag) |
|
|
106
|
+
| 多人 tag 視覺(**圓形 avatar overlap `-ml-0.5`**) | **PeoplePicker 自己**(不繼承 Combobox 矩形 Tag chip)|
|
|
107
|
+
| 多人 length=1 視覺(降階為 avatar+人名,跟單人視覺一致)| **PeoplePicker 自己**(Combobox 無此特殊)|
|
|
108
|
+
|
|
109
|
+
#### B. 單人完整 trigger SSOT
|
|
110
|
+
|
|
111
|
+
| state | trigger 顯示 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| 空、closed | placeholder「請選擇人員」+ 按空間 ellipsis |
|
|
114
|
+
| 選 1 人、closed | avatar + 人名 + 按空間 ellipsis(`PersonDisplay`,`person-display.tsx:131-147`)|
|
|
115
|
+
| open + inline-search、未選 | input cursor + placeholder「請選擇人員」(trigger empty placeholder,**不**用「搜尋…」)|
|
|
116
|
+
| open + inline-search、選 1 人 | input cursor + placeholder = **該人名**(本人名字當搜尋預設值,按空間 ellipsis;`select.tsx:185` `selectedLabel \|\| placeholder` empty-state fallback canonical)|
|
|
117
|
+
| open + panel-search、選 1 人 | avatar + 人名 + ellipsis(搜尋框在 panel 內,trigger 視覺不變)|
|
|
118
|
+
|
|
119
|
+
#### C. 多人 length=1 trigger SSOT(降階為單人視覺)
|
|
120
|
+
|
|
121
|
+
| state | trigger 顯示 |
|
|
122
|
+
|---|---|
|
|
123
|
+
| closed | avatar + 人名 + ellipsis(= 單人 closed,共享 `PersonDisplay`)|
|
|
124
|
+
| open + panel-search | avatar + 人名 + ellipsis(panel 內搜尋,trigger 視覺不變)|
|
|
125
|
+
| open + inline-search | avatar + **input cursor**(原本人名位置被輸入區取代;**因 avatar 仍可見,placeholder 永遠空,只剩 cursor** — 對齊 §E 「avatar 存在 → placeholder 空」rule,避免視覺重複造成混亂)|
|
|
126
|
+
|
|
127
|
+
#### D. 多人 length≥2 trigger SSOT(avatar stack 視覺)
|
|
128
|
+
|
|
129
|
+
| state | trigger 顯示 |
|
|
130
|
+
|---|---|
|
|
131
|
+
| closed | **圓形 avatar stack overlap `-ml-0.5` + 圓形 +N 溢出 chip**(本來放人名的地方不放人名)|
|
|
132
|
+
| open + inline-search、**未選**(length=0)| input cursor + placeholder「請選擇人員」(trigger empty placeholder,**不**用「搜尋…」)|
|
|
133
|
+
| open + inline-search、**已選**(length≥2)| **avatar stack + 圓形 +N + input cursor 在 +N 的右側**(stack 跟 +N 不動;cursor 接在 +N 後;empty search 不顯 placeholder,純 cursor。對齊 Combobox `trailing` slot placement)|
|
|
134
|
+
| open + panel-search、已選 | avatar stack + 圓形 +N(searchbox 在 panel 內,trigger 視覺不變)|
|
|
135
|
+
|
|
136
|
+
#### E. 共享 SSOT(單人 ↔ 多人 length=1 必對齊)
|
|
137
|
+
|
|
138
|
+
| 共享項 | SSOT owner |
|
|
139
|
+
|---|---|
|
|
140
|
+
| Avatar 左 inset 距 trigger.left = 12px 固定(不隨 size/density 漂移)| 本 spec L94 + `people-picker.tsx:316` `!px-3` inject(form context)/ `!px-[var(--table-cell-px)]`(naked variant table cell)|
|
|
141
|
+
| Avatar+人名視覺 | `person-display.tsx:131-147` `PersonDisplay`(共享 renderer)|
|
|
142
|
+
| 人名按空間 ellipsis | `person-display.tsx:144` `truncate flex-1 min-w-0` |
|
|
143
|
+
| Placeholder ellipsis(inline-search active 時)| `field-wrapper.tsx:244` `bareInputStyles` 含 `truncate` |
|
|
144
|
+
| Empty-state inline-search placeholder(未選時 → trigger empty placeholder) | `select.tsx:185` `selectedLabel \|\| placeholder` / `combobox.tsx:554` `items.length === 0 ? placeholder : ''`(2026-05-15 Drift A fix 對齊 PeoplePicker SSOT)|
|
|
145
|
+
|
|
146
|
+
**Avatar-presence → placeholder 規則(2026-05-15 user verbatim「只要有 avatar 存在,placeholder 都是空的,只會出現 cursor,這樣才不會造成混亂」)**:
|
|
147
|
+
|
|
148
|
+
| 場景 | avatar 是否可見 | placeholder 行為 |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| 單人 inline-search、已選、open | ❌(input **取代** avatar/name,avatar 消失)| placeholder = **selectedLabel(該人名,memory aid)** — `select.tsx:185` |
|
|
151
|
+
| 單人 inline-search、未選、open | ❌(從來沒 avatar)| placeholder = **trigger empty placeholder「請選擇人員」** |
|
|
152
|
+
| 多人 inline-search、length≥1、open | ✅(avatar/stack 仍在,input 在 trailing)| **placeholder = '' 純 cursor**(避免跟 avatar 重複)|
|
|
153
|
+
| 多人 inline-search、length=0、open | ❌(empty)| placeholder = **trigger empty placeholder「請選擇人員」** — `combobox.tsx:554`(Drift A fix)|
|
|
154
|
+
|
|
155
|
+
**規則一句話**:`placeholder = (visible_avatar_exists) ? '' : (selectedLabel ?? triggerEmptyPlaceholder)`。
|
|
156
|
+
|
|
157
|
+
**搜尋預設模式(2026-05-15 user 拍板)**:多選系列 default `searchIn='menu'`(panel-top search)。`searchIn='trigger'` 為 opt-in。Single mode 永遠 inline-search(Select 慣例)。
|
|
158
|
+
|
|
159
|
+
#### F. Cell-edit ↔ Field-edit 一致性 invariant
|
|
160
|
+
|
|
161
|
+
`MultiPersonCell` / `PersonCell`(`cell-registry.tsx:381-391` cell-as-input)+ form context PeoplePicker **包同一個元件**,所以 §B/§C/§D 規則對兩 surface 都成立。差別只在 padding 機制(共 §E)— 兩 surface 都產出 12px inset,**禁分歧**。
|
|
162
|
+
|
|
163
|
+
**改 PeoplePicker 視覺前必查本表,動 trigger render / placeholder / overflowShape / tagWrapperClassName 前先 cite 對應 row。** Hook `check_peoplepicker_ssot_drift.sh` 機械強制。
|
|
164
|
+
|
|
165
|
+
**Avatar 左 inset SSOT(2026-05-13 v2 — user 拍 Path a + density-drift root-cause,GitHub people picker idiom)**:single trigger / multi stack 第一個 avatar 距 trigger.left = **12px 固定**(不隨 size / density 漂移)。實作:PeoplePicker form context + 有 tag → `cn(className, '!px-3')` inject 12px override 到 Combobox Field wrapper,**覆蓋** Combobox `tagPadding[size]` density-dependent calc 公式;`tagAreaPaddingLeftPx={undefined}` 不再 +8 magic。Cell context naked variant `!px-[var(--table-cell-px)]` 已是 12px 不 inject。**為何 v1「4+8=12」公式報廢**(per 2026-05-13 codex V2 verdict correction):`tagPadding[size]` 是 `(field-height - icon-size) / 2`,真值表:default sm/md=4px → 4+8=12 ✓;default lg=6px → 14px ✗;comfortable sm/md=6px → 14px ✗;comfortable lg=8px → 16px ✗。**default sm + default md 兩個 size 也 = 12px**(我前 too narrow claim「只 md」 codex 修正);**真正漂的 = default lg + 全 comfortable 共 4 個組合**。整體 stale 結論不變(4 個漂 / 2 個對),故 `!px-3` 固定 12px 是正解。對齊 GitHub picker / Material Autocomplete renderTags / Ant Select tagRender 共享 fixed-inset canonical(不 scale with size)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
166
|
+
|
|
167
|
+
**判準**:
|
|
168
|
+
- **選人 → PeoplePicker**(不用 Select / Combobox 代替,失去 Avatar 視覺)
|
|
169
|
+
- **非人員單選 → Select**
|
|
170
|
+
- **非人員多選 → Combobox**
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## PersonValue 型別
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
type PersonValue = string | { name: string; avatarUrl?: string; description?: string }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- **簡單用 string**:只有名字(fallback 顯示 initials)
|
|
181
|
+
- **完整物件**:包含 avatar URL 和 description(顯示 Avatar + 次要資訊如 role / email)
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## readonly / disabled
|
|
186
|
+
|
|
187
|
+
與其他 Field 一致:
|
|
188
|
+
- **readonly**:顯示選中人員(Avatar + Name),無 dropdown trigger、無搜尋
|
|
189
|
+
- **disabled**:灰化整個 field,不可互動
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 禁止事項
|
|
194
|
+
|
|
195
|
+
- ❌ 用 PeoplePicker 選非人員(部門 / 專案 / 類別)——那些用 Select / Combobox
|
|
196
|
+
- ❌ 在 readonly 模式顯示搜尋或 dropdown——readonly 是純展示
|
|
197
|
+
- ❌ 多選情境強制單選(限制 value.length === 1)——改用 Select(如果真的單選)
|
|
198
|
+
- ❌ 把 PeoplePicker 當 Team Member 展示元件用(不選人、只看名單)——用 Avatar.Group 或自訂 list
|
|
199
|
+
- ❌ 不搭配 Avatar 視覺(純文字名字)——失去 PeoplePicker 存在的理由,直接用 Select / Combobox
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 為何無 Inspector
|
|
204
|
+
|
|
205
|
+
PeoplePicker 是 **composite Field Control**(Field shell + Avatar + SelectMenu),關鍵決策維度是 `mode`(single / multi)× `size` × search behavior × value type——已由 `ModeMatrix` / `SizeMatrix` / `ColorMatrix` / `StateBehavior` / `PersonValueType` 五張矩陣完整覆蓋。互動 Inspector 對 composite 元件較弱:「傳 PersonValue 陣列」需要真實資料 fixture 展示,單組合試玩無法呈現——改以 `StateBehavior` 的真實 Assignee picker 場景取代。
|
|
206
|
+
|
|
207
|
+
對應 anatomy story:保留 `Overview` + 元件特有 `ModeMatrix` / `PersonValueType` + `SizeMatrix` + `ColorMatrix` + `StateBehavior`。
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## shadcn passthrough 例外說明
|
|
212
|
+
|
|
213
|
+
PeoplePicker 是 **composite 元件**(Popover + Command + SelectMenu + Avatar 內部組裝),其 API 為完全 declarative(value / onChange / size / mode / options...)。**不套 `forwardRef` / `...props` spread**:
|
|
214
|
+
|
|
215
|
+
- **沒有單一 DOM root 可 ref**:consumer「對誰 forward」含糊——指 Popover trigger?Command search input?SelectMenu content?任一選擇都會誤導 consumer
|
|
216
|
+
- **`...props` spread 會撒到何處?** composite 元件無 identity DOM,spread 若到 root wrapper 多半無意義(wrapper 只是 flex container);到 trigger 則語義不清
|
|
217
|
+
- **API 邊界明確才是 composite 價值**:PeoplePicker 暴露的是「選人」語意,不是「彈出選單 + 搜尋 + 渲染頭像」的 DOM 細節。若 consumer 需要 DOM-level 控制,該用底層 Popover + Combobox 自組,不該透過 PeoplePicker
|
|
218
|
+
|
|
219
|
+
`displayName = 'PeoplePicker'` 保留,無 `asChild`(composite 不是 Slot-compat)。
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 邊界案例
|
|
224
|
+
|
|
225
|
+
- **Disabled**:Field SSOT own;trigger / dismiss / inline-search input 全 disable,已選 avatar tag dismiss X 自動隱藏(對齊 Combobox readonly/disabled tag 規則)。token 走 M24 state precedence。
|
|
226
|
+
- **Loading(async people fetch)**:`loading?: boolean` forward 給 SelectMenu(SelectMenu 端「Loading」段 SSOT);dropdown body 替換為 `<Empty icon={<CircularProgress size={48}/>}/>` + `aria-busy`。trigger 不變,user 隨時可開。
|
|
227
|
+
- **Empty(no search results)**:`emptyText` 預設「沒有符合的人員」(本 spec L72 已 codify);無 creatable mode(人員不可建立)。
|
|
228
|
+
- **Empty(no value selected)**:single mode → trigger 顯 placeholder「請選擇人員」;multi mode `value=[]` → trigger 同 placeholder(無 avatar stack 渲);詳「Trigger display SSOT canonical table」B-D 段。
|
|
229
|
+
- **Dark mode / density**:Avatar 12px inset 固定不隨 density 漂移(PeoplePicker own,本 spec L94 + L140 codify);其他走 Field + SelectMenu SSOT。
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 相關
|
|
234
|
+
|
|
235
|
+
- `./person-display.tsx` — PersonValue 的顯示元件(Avatar + Name,readonly / DataTable cell 用)
|
|
236
|
+
- `../Select/select.spec.md` — 非人員單選的對應元件
|
|
237
|
+
- `../Combobox/combobox.spec.md` — 非人員多選的對應元件
|
|
238
|
+
- `../SelectMenu/select-menu.tsx` — PeoplePicker edit mode 的浮層選單(內部消費)
|
|
239
|
+
- `../Avatar/avatar.spec.md` — 選項與已選值的 Avatar 視覺
|
|
240
|
+
- `../NameCard/name-card.spec.md` — 純顯示人員資訊的對應元件(非選擇器)
|
|
241
|
+
- `../Field/field-controls.spec.md` — Field Control 共用規則
|
|
242
|
+
|
|
243
|
+
## A11y 預設
|
|
244
|
+
|
|
245
|
+
**ARIA / Pattern**:對齊 [W3C ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/) 對應 pattern。
|
|
246
|
+
|
|
247
|
+
**Keyboard 行為**:
|
|
248
|
+
|
|
249
|
+
- Tab — focus trigger
|
|
250
|
+
- Enter / Space — 開啟 picker
|
|
251
|
+
- 字母鍵 — type-ahead 搜尋
|
|
252
|
+
- ↑/↓ — 導覽 people
|
|
253
|
+
- Enter — 選擇 / 取消選擇
|
|
254
|
+
|
|
255
|
+
**Focus**:focus-visible ring 對齊 DS canonical(`outline: 2px solid var(--ring)`);focus management 由元件 own。
|
|
256
|
+
|
|
257
|
+
**驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
|
|
258
|
+
|
|
259
|
+
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
260
|
+
|
|
261
|
+
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
262
|
+
|
|
263
|
+
- `select-menu.spec.md`
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: Popover
|
|
3
|
+
family: composite
|
|
4
|
+
variants: {}
|
|
5
|
+
sizes: {}
|
|
6
|
+
traits:
|
|
7
|
+
- isOverlay
|
|
8
|
+
benchmark:
|
|
9
|
+
- Radix Popover primitive: github.com/radix-ui/primitives/tree/main/packages/react/popover
|
|
10
|
+
- MUI Popover: github.com/mui/material-ui/tree/master/packages/mui-material/src/Popover
|
|
11
|
+
- Polaris Popover: github.com/Shopify/polaris/tree/main/polaris-react/src/components/Popover
|
|
12
|
+
- Ant Design Popover: github.com/ant-design/ant-design/tree/master/components/popover
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
|
|
16
|
+
|
|
17
|
+
# Popover 設計原則
|
|
18
|
+
|
|
19
|
+
## 定位
|
|
20
|
+
|
|
21
|
+
Popover 是**點擊觸發的浮層容器**——提供定位、動畫、焦點管理,內容由 consumer 決定。
|
|
22
|
+
|
|
23
|
+
**實作基礎**:shadcn passthrough——基於 Radix Popover。本 DS 保留 shadcn 原結構 + 橋接 DS token(elevation / radius / border)。
|
|
24
|
+
|
|
25
|
+
**Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
|
|
26
|
+
|
|
27
|
+
**視覺對齊 Dialog**:外殼 token 與 Dialog 完全一致(`bg-surface-raised` / `border-border` / `rounded-lg` / `elevation-200`)。差別僅兩點:(1) Popover 是 non-modal 輕量浮層不阻斷背景,(2) **density 永遠鎖 `md`** — Popover 不隨頁面 density 放大(lightweight 浮層保持緊湊)。
|
|
28
|
+
|
|
29
|
+
**Content size canonical(2026-04-29)**:density lock md → **fields(Input / Select / Checkbox)預設 `size="md"`**(field-height-md 32px,對齊 density)。其餘細節走 SSOT pointer:
|
|
30
|
+
- Chrome corner buttons(close X / refresh / dismiss)→ `patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」(sm native + v5 trick)
|
|
31
|
+
- Row inline action(panel list row 內 Eye / drag / suffix toggle)→ `patterns/element-anatomy/inline-action.spec.md`「尺寸對照」table(md = 16+18 hover bg)
|
|
32
|
+
- Footer action button → `action-bar.spec.md`(輕量 chrome,sm canonical)
|
|
33
|
+
|
|
34
|
+
**觸發距離 canonical**:`sideOffset = 8`(px)—— trigger 到 Popover 邊緣的垂直/水平間距。8px 是世界級浮層 idiom(Notion / Linear / Figma / Stripe 皆約 6-8px);< 4px 會讓浮層貼死 trigger 失去「另一層」感,> 12px 會拉斷 trigger ↔ content 的視覺關聯。Radix/shadcn 預設 4px 太緊,本 DS 改為 8 對齊 overlay primitive 的呼吸感。Dialog 不適用(居中或 full-screen)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
35
|
+
|
|
36
|
+
**Viewport 邊距 canonical**:`collisionPadding = 8`(px)——浮層與 viewport 邊緣的最小間距。當 popover 原本要貼近 viewport 邊(trigger 在頁面最右側、或視窗很窄),Radix 會自動推開 8px 讓浮層不貼死邊界。世界級對照:macOS / iOS / Notion / Figma 的 overflow 選單永遠留視覺呼吸距。`sideOffset` 管 trigger↔popover,`collisionPadding` 管 popover↔viewport,兩者互補。Consumer 不需傳 — 預設值是 canonical。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
37
|
+
|
|
38
|
+
**Align 對齊 canonical(跨浮層 SSOT)**:`align` 必須跟隨 trigger 在容器中的位置,**不是自由選擇**:
|
|
39
|
+
|
|
40
|
+
| Trigger 在容器 | `align` | 對齊行為 |
|
|
41
|
+
|---------------|---------|----------|
|
|
42
|
+
| **左側 / 左上**(page header left、sidebar filter button) | `"start"` | Popover 與 trigger 左邊緣對齊,向右展開 |
|
|
43
|
+
| **置中**(toolbar 中央、modal 內置中按鈕) | `"center"`(default)| Popover 與 trigger 中心對齊 |
|
|
44
|
+
| **右側 / 右上**(page header right、設定按鈕、overflow menu) | `"end"` | Popover 與 trigger 右邊緣對齊,向左展開 |
|
|
45
|
+
|
|
46
|
+
**為什麼**:Popover 通常比 trigger 寬。align 錯方向會 (1)溢出容器邊緣 / 被 viewport 裁切、(2)視覺上 popover「沒黏在 trigger 那一側」產生 disconnect,使用者以為兩者無關。世界級對照:Figma / Notion / Linear / Stripe / Material / Ant Design 一律遵循(trigger 靠哪邊、浮層跟著靠哪邊)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
47
|
+
|
|
48
|
+
**SSOT 適用範圍(2026-04-20 精緻化)**:本規則是**「結構化浮層」的 canonical** —— Popover / DropdownMenu / Coachmark / SelectMenu 嚴格遵守(這些浮層寬度通常比 trigger 寬、且有結構化內容,錯方向必視覺 disconnect)。
|
|
49
|
+
|
|
50
|
+
**輕量浮層例外**:Tooltip / HoverCard 不適用此 canonical —— 兩者 predominantly hover 觸發、純展示(不互動或極少互動)、寬度隨內容自適應,Radix 預設 `align="center"` 即可涵蓋絕大多數情境。強行套 trigger-position alignment 反而破壞 Tooltip「貼合指標」的輕量感。這兩個 primitive 走自己的 spec canonical(通常 center + 小 sideOffset)。
|
|
51
|
+
|
|
52
|
+
**SelectMenu 特例**:SelectMenu 是 input-triggered,width 在 min/max 範圍內自動跟 input 同寬 → `align="start"`(對齊 input 左邊緣)在視覺上與「popover 跟 input 同寬」重疊,consumer 感知不到 alignment 差異。仍採用 canonical 以保 code 一致性,但視覺上是透明的。
|
|
53
|
+
|
|
54
|
+
其他結構化浮層 spec 若討論 align 應 pointer 指回本節。
|
|
55
|
+
|
|
56
|
+
**結構化組合(可選)**:PopoverContent 外殼無內距,consumer 可選擇:
|
|
57
|
+
- **單一 body**:包 `<PopoverBody>` 取得 Dialog body 同 padding (`px-loose py-tight`)
|
|
58
|
+
- **含標題**:組合 `<PopoverHeader>` + `<PopoverBody>`(下分隔線由 Header 提供)
|
|
59
|
+
- **含操作按鈕列**:再加 `<PopoverFooter>`(上分隔線由 Footer 提供)
|
|
60
|
+
|
|
61
|
+
**Dismiss 預設**(與 Dialog 一致):Popover **預設 dismissible** —— 點擊 trigger 外 / 按 Esc / focus 離開 content 樹,三者任一皆關閉。由 Radix 內建 `onPointerDownOutside` / `onEscapeKeyDown` 行為提供,consumer 無需額外 wire。如需強制 modal(必須按 Close 才能關),傳 `modal={true}` 並在 Header 放 Close 按鈕;但這是例外,不是預設——Popover 的本質是「使用者可以忽略、繼續主流程」。
|
|
62
|
+
|
|
63
|
+
> **跨家族 SSOT pointer**:PopoverHeader 屬 **Padding-based overlay header 家族**;border / padding / dismiss size / withTabs(tabs 進 PopoverHeader 時 border auto-suppress + tabs size sm)的跨家族視覺契約 SSOT 詳 `patterns/header-canonical/header-canonical.spec.md`。本節僅 codify Popover 特有 close X v5 unbounded canonical。
|
|
64
|
+
|
|
65
|
+
**Close X 按鈕(2026-04-22 v5 unbounded canonical)**:所有 `PopoverHeader` 內建右上 X 按鈕,`<Button data-dismiss iconOnly dismiss size="sm" />` native sm(28 md / 32 lg)。透過 SurfaceHeader 的 v5 CSS rule 自動套負 margin 讓 layout 佔位 = 24(chrome-header-height 幾何)。`hideClose` prop 可讓 composition 元件(如 Coachmark)選擇隱藏。詳 `patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」。
|
|
66
|
+
|
|
67
|
+
**Header / Footer 高度 canonical**:padding-based(繼承 SurfaceHeader / SurfaceFooter),高度 = max(child layout) + 2×tight。unbounded 透過 v5 trick layout 佔位 24 → header 48 / footer 48(若只有 unbounded)或自然長(若有 bounded)。詳 `tokens/uiSize/uiSize.spec.md`「Chrome header 選型 canonical」。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Title typography canonical(non-modal 特化,2026-04-22 v4)
|
|
72
|
+
|
|
73
|
+
**PopoverTitle 用 `text-body font-medium`(14px),不是 Dialog / Sheet 的 `text-body-lg`(16px)**。這是針對 **non-modal 輕量浮層** 的視覺考量,跟 density 鎖 md 同源的輕量 rationale。
|
|
74
|
+
|
|
75
|
+
**重量分級(world-class 共通 pattern)**:
|
|
76
|
+
|
|
77
|
+
| Overlay 類型 | Title typography | Rationale |
|
|
78
|
+
|------------|------------------|-----------|
|
|
79
|
+
| **Modal**(Dialog / Sheet)| `text-body-lg`(16px)| 重量級決策 surface,title 是決策 anchor,需視覺重量 |
|
|
80
|
+
| **Non-modal**(Popover / Coachmark / Tooltip / HoverCard)| `text-body`(14px)| 輕量浮層,可忽略、非阻斷,title 是輔助標籤不搶視覺重心 |
|
|
81
|
+
|
|
82
|
+
**世界級對照**(7 家 DS 同 split):
|
|
83
|
+
| DS | Modal title | Non-modal popover title |
|
|
84
|
+
|----|-------------|------------------------|
|
|
85
|
+
| Material M3 | Headline(24px large / 16px body 分 variant)| Smaller(usually 14-16px) |
|
|
86
|
+
| Polaris | Modal title 大號 | Popover 較小 |
|
|
87
|
+
| Atlassian | Modal 大 | InlineDialog 小 |
|
|
88
|
+
| Notion | Modal 16px+ | Popover 14px |
|
|
89
|
+
| Linear | Modal 16px | Popover / HoverCard 14px |
|
|
90
|
+
| Figma | Modal 16px | Inline panel / popover 13-14px |
|
|
91
|
+
| GitHub Primer | Dialog 16px | Overlay 14px |
|
|
92
|
+
|
|
93
|
+
**共通原則**:non-modal overlay 的 title 比 modal 小一級。我方採 16 → 14 分級,在 world-class 範圍內。
|
|
94
|
+
|
|
95
|
+
**Coachmark 的 PopoverTitle 消費**:Coachmark 的 header 小標籤(如 "新功能介紹" / "Tip 1 of 3")繼承 PopoverTitle 14px。Coachmark **body 內容的主 title**(長敘述型 "建立你的第一個 Workspace")另走 `text-body-lg`(見 `coachmark.tsx:L178`)— 那是 body content 不是 chrome title,分離 concerns。
|
|
96
|
+
|
|
97
|
+
**禁止**:consumer 自己在 PopoverContent 裡手刻 `<h2 className="text-body-lg">` 當 title(繞 PopoverTitle 的 canonical)。若需要大 title,視為該用 Dialog 而非 Popover(選對元件)。
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 何時用
|
|
102
|
+
|
|
103
|
+
- **點擊觸發的輕量浮層**:filter panel、date picker 展開、設定 mini panel
|
|
104
|
+
- **需要放互動元素的浮層**:按鈕、輸入框、checkbox 群組
|
|
105
|
+
- **非 modal 的補充 UI**:使用者可以忽略並繼續主流程,不阻斷背景互動
|
|
106
|
+
|
|
107
|
+
## 何時不用
|
|
108
|
+
|
|
109
|
+
| 場景 | 改用 | 原因 |
|
|
110
|
+
|------|------|------|
|
|
111
|
+
| hover 觸發(非點擊)| `HoverCard` | HoverCard 觸發是 hover,Popover 是點擊 |
|
|
112
|
+
| 純文字提示 | `Tooltip` | Tooltip 更輕量,適合純文字 |
|
|
113
|
+
| 需要阻斷背景的流程 | `Dialog` | Dialog 是 modal,Popover 非 modal |
|
|
114
|
+
| 操作選單(複製 / 刪除)| `DropdownMenu` | DropdownMenu 有 menu 語意 + 鍵盤導覽 |
|
|
115
|
+
| 選值下拉 | `Select` / `Combobox` | 下拉選單用專用元件,不自組 Popover + list |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 與 Dialog 的分界
|
|
120
|
+
|
|
121
|
+
- **Popover**:non-modal 輕量浮層(filter / settings panel / mini 操作面板)——背景仍可互動
|
|
122
|
+
- **Dialog**:modal 阻斷互動(confirmation / form wizard / 需要完成才能繼續的流程)
|
|
123
|
+
|
|
124
|
+
**判斷法**:問「使用者可以 ignore 這個浮層、繼續使用主介面嗎?」可以 → Popover;不可以、必須處理才能繼續 → Dialog。
|
|
125
|
+
|
|
126
|
+
## 與 DropdownMenu 的分界
|
|
127
|
+
|
|
128
|
+
- **DropdownMenu**:有預設 item 結構(MenuItem list),語意是「從清單中選一個動作」— click 單一 item 即觸發 action,無 save CTA
|
|
129
|
+
- **Popover**:內容自由(任意 React 元素),可放按鈕、輸入框、圖表、form;支援「暫存選中狀態 + 明確 save CTA」
|
|
130
|
+
|
|
131
|
+
選單類(click 單項即動作,無 save)用 DropdownMenu;自由組合 UI 面板(多欄表單、filter 控制群、「選完按套用」的多選工作流)用 Popover。
|
|
132
|
+
|
|
133
|
+
### 合法但少見:Popover 內含可選 item 列 + footer save CTA
|
|
134
|
+
|
|
135
|
+
Popover 可以在 body 放 checkbox / radio 列表 / 可選 chip / 可選 menu-item-like elements,**但必在 footer 有 CTA 按鈕(套用 / 儲存 / 完成)觸發狀態提交**。這個 pattern 跟 DropdownMenu 的差別是:**DropdownMenu 一 click 即觸發動作,Popover 這類是「暫存選擇 → 按 CTA 才 commit」**。典型情境:
|
|
136
|
+
|
|
137
|
+
- **多選 filter panel**:checkbox 列表 + 底部 `清除 / 套用`(見 `FilterPanel` story)
|
|
138
|
+
- **Bulk edit**:選多個 menu-item-like options + 底部 `儲存變更`
|
|
139
|
+
- **Custom toolbar 設定**:可選 toggle 列 + 底部 `套用偏好`
|
|
140
|
+
|
|
141
|
+
canonical 判斷:「使用者 click 單項是否立即改變系統狀態?」是 → DropdownMenu;否(要按 footer CTA 才 commit)→ Popover。
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 禁止事項
|
|
146
|
+
|
|
147
|
+
- ❌ **不把 Popover 當 Tooltip**:Tooltip 是 hover 觸發的純文字輔助,Popover 是 click 觸發的互動面板,語意與觸發模型不同
|
|
148
|
+
- ❌ **不用 Popover 做 confirmation dialog**:確認框必須阻斷背景互動,使用者不能忽略——那是 Dialog 的職責
|
|
149
|
+
- ❌ **不在 Popover 內放 nested Popover**:嵌套層級混亂、焦點管理崩壞,複雜互動改用 Dialog 或拆成多步驟
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## A11y 預設
|
|
154
|
+
|
|
155
|
+
Radix Popover 自動處理:
|
|
156
|
+
|
|
157
|
+
- **焦點管理**:開啟時移動焦點進入 content;關閉時 focus return to trigger
|
|
158
|
+
- **Esc 關閉**:按 Esc 自動關閉並返回焦點
|
|
159
|
+
- **Focus trap**:`modal={true}` 時焦點鎖在 content 內;預設 non-modal 下焦點離開 content 樹會自動關閉
|
|
160
|
+
- **ARIA**:trigger 自動 `aria-expanded` / `aria-controls`,content `role="dialog"`
|
|
161
|
+
|
|
162
|
+
Consumer 無需額外處理 a11y,保留 Radix `data-state` 屬性即可。
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 為何無 Inspector
|
|
167
|
+
|
|
168
|
+
Popover 是**浮層容器 primitive**——關鍵決策是 `side` × `align` × `sideOffset`,以及內部 Header / Body / Footer 結構。`PlacementMatrix` 是完整的 side × align 12-cell 矩陣,比互動 Inspector(切單一 side/align)更能呈現所有定位組合。Header / Body / Footer padding 屬 `overlay-surface` SSOT,已由 `ColorMatrix` 的 token 表 + 該 pattern spec 覆蓋。
|
|
169
|
+
|
|
170
|
+
Popover 內容完全由 consumer 決定,無自己的 variant/size/disabled 等可試玩 prop——Inspector 對 container 類元件價值低。
|
|
171
|
+
|
|
172
|
+
對應 anatomy story:保留 `Overview` + 元件特有 `PlacementMatrix`(12-cell side × align 矩陣) + `ColorMatrix` + `SizeMatrix` + `StateBehavior`(open / close / animation)。
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 相關
|
|
177
|
+
|
|
178
|
+
- `../HoverCard/hover-card.spec.md` — hover 觸發的對應浮層
|
|
179
|
+
- `../Tooltip/tooltip.spec.md` — 純文字提示
|
|
180
|
+
- `../Dialog/dialog.spec.md` — 需要阻斷的 modal
|
|
181
|
+
- `../DropdownMenu/dropdown-menu.spec.md` — 有 menu 語意的操作選單
|
|
182
|
+
- `../SelectMenu/select-menu.spec.md` — SelectMenu 消費 Popover 作為浮層容器
|
|
183
|
+
- `../Coachmark/coachmark.spec.md` — Coachmark 為 Popover 的 composition pattern(無 header / 有 media / footer justify-between),共用所有 overlay-surface SSOT
|
|
184
|
+
- Radix Popover primitive — `@radix-ui/react-popover`
|
|
185
|
+
- `../../patterns/overlay-surface/overlay-surface.spec.md` — Header / Body / Footer padding SSOT(Dialog + Popover 共用)
|
|
186
|
+
|
|
187
|
+
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
188
|
+
|
|
189
|
+
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
190
|
+
|
|
191
|
+
- `command.spec.md`
|
|
192
|
+
- `file-viewer.spec.md`
|
|
193
|
+
|
|
194
|
+
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
195
|
+
|
|
196
|
+
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
197
|
+
|
|
198
|
+
- `density.spec.md`
|