diff --git a/spec/66_component_taxonomy.md b/spec/66_component_taxonomy.md new file mode 100644 index 00000000..421a4ef7 --- /dev/null +++ b/spec/66_component_taxonomy.md @@ -0,0 +1,927 @@ +# Component Taxonomy: 4-Layer Architecture for OMS·WMS·ERP SPA + +**Status**: DRAFT (Phase 0, requires Figma finalization) +**Date**: 2026-07-26 +**Related**: [ADR-001 (Spec 65)](spec/65_adr_001_monolithic_spa_architecture.md), [OpenAPI (Spec 63)](spec/63_oms_wms_erp_api_openapi.yaml) + +--- + +## Overview + +**Component Hierarchy**: 4 layers, 65 total components across OMS/WMS/ERP domains + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Layer 4: Business Composite (11 CRUD Workflows) │ +│ └─ OrderForm, InventoryTransferWizard, VoucherEditor, etc. │ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 3: Domain Fields (12 Domain-Specific Inputs) │ +│ └─ OrderLineField, InventoryField, VoucherLineField, etc. │ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 2: Typed Fields (12 Type-Safe Inputs) │ +│ └─ TextField, DateField, CurrencyField, StatusField, etc. │ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 1: Primitives (30 UI Building Blocks) │ +│ └─ Button, Input, Select, Table, Card, Badge, etc. │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Design System**: Tabler UI (Bootstrap 5) + Storybook 7.0+ + +--- + +## Layer 1: Primitive Components (30) + +### Purpose +Reusable UI elements with **zero business logic**, full accessibility (WCAG 2.1 AA), typed props, consistent behavior. + +### Folder Structure +``` +src/components/primitives/ +├─ Button/ +│ ├─ ButtonBase.vue +│ ├─ ButtonBase.stories.ts +│ └─ ButtonBase.spec.ts +├─ Input/ +│ ├─ InputBase.vue +│ ├─ InputBase.stories.ts +│ └─ InputBase.spec.ts +├─ Select/ +│ ├─ SelectBase.vue +│ ├─ SelectBase.stories.ts +│ └─ SelectBase.spec.ts +├─ Table/ +│ ├─ TableBase.vue +│ ├─ TableBase.stories.ts +│ └─ TableBase.spec.ts +├─ Card/ +│ ├─ CardBase.vue +│ └─ CardBase.stories.ts +├─ Badge/ +├─ Modal/ +├─ Checkbox/ +├─ Radio/ +├─ Textarea/ +├─ Pagination/ +├─ Alert/ +├─ Spinner/ +├─ Tooltip/ +├─ Dropdown/ +├─ Tabs/ +├─ Breadcrumb/ +├─ NavBar/ +├─ Sidebar/ +├─ Icon/ +└─ Link/ +``` + +### Component Specifications + +| Component | Props | Events | A11y | Story | +|-----------|-------|--------|------|-------| +| **ButtonBase** | variant (primary/secondary/danger), size (sm/md/lg), disabled, loading | click | aria-label, focus-visible | 12 stories | +| **InputBase** | type (text/email/number), placeholder, value, disabled, error, required | input, change, blur | label + aria-describedby (error) | 8 stories | +| **SelectBase** | options: Array<{value, label}>, value, disabled, multiple | change | aria-label, aria-expanded | 10 stories | +| **TableBase** | columns: Array<{key, header, sortable}>, data: any[], onSort | row-click, sort | semantic , scope | 6 stories | +| **CardBase** | title, subtitle, footer, clickable | click | semantic
| 5 stories | +| **BadgeBase** | status (success/danger/warning/info), size | — | aria-label | 8 stories | +| **ModalBase** | isOpen, title, onClose | close | role="dialog", focus-trap | 6 stories | +| **CheckboxBase** | value, label, disabled, required | change | aria-label, aria-describedby | 6 stories | +| **RadioBase** | name, options, value, disabled | change | role="radiogroup" | 5 stories | +| **TextareaBase** | value, placeholder, rows, disabled, error | input, change | aria-describedby | 5 stories | +| **PaginationBase** | currentPage, totalPages, onPageChange | page-change | aria-label (next/prev) | 4 stories | +| **AlertBase** | type (success/error/warning), dismissible, onDismiss | dismiss | role="alert" | 8 stories | +| **SpinnerBase** | size, color | — | aria-busy | 4 stories | +| **TooltipBase** | text, position (top/bottom/left/right) | show, hide | aria-describedby | 5 stories | +| **DropdownBase** | trigger, items: Array<{label, action}>, onSelect | select | role="menu", role="menuitem" | 6 stories | +| **TabsBase** | tabs: Array<{id, label, disabled}>, activeId, onTabChange | tab-change | role="tablist", role="tab" | 6 stories | +| **BreadcrumbBase** | items: Array<{label, href}> | navigate | aria-label | 3 stories | +| **NavBarBase** | title, items: Array<{label, href}>, sticky | navigate | semantic