diff --git a/ROADMAP.md b/ROADMAP.md index fdceebb3..9ab294b7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -8,6 +8,10 @@ This is a snapshot, not a commitment or a timeline. Items move, get reprioritize - **More blocks** — remaining page templates (`register`, `forgot-password`, `settings-page`, `detail-page`, `wizard`, `pricing-page`) on top of the starter three. +## Component completeness + +Optional leftover sibling gaps, still tracked as P32 in [`TODOLIST.md`](TODOLIST.md): combobox `ariaLabel`, select `required`, and toast `class` docs. + ## Ecosystem / team use - **Registry Directory** — a docs page listing community/third-party registries, so teams can discover each other's component sets. @@ -15,6 +19,7 @@ This is a snapshot, not a commitment or a timeline. Items move, get reprioritize ## Recently shipped +- P32 sibling alignment — sheet built-in close and header `align`, radio `size`, a real dropdown-menu submenu, popover four-way `side` plus aria fallback, transfer root `disabled`/`ariaLabel`, and table sticky backgrounds plus loading/column-visibility recipes - Blocks starter set — `sanring add block/login` (and `dashboard-shell`, `table-page`) installs a page-level template plus its component dependencies - Open in StackBlitz — each component docs previewer can open a minimal Angular 22 + Tailwind project with that example - GitHub registries — `--registry github:owner/repo` (optional `#ref` / `@ref`) expands to the raw `registry.json` at the repo root diff --git a/TODOLIST.md b/TODOLIST.md index 471358a1..5ffbdfef 100644 --- a/TODOLIST.md +++ b/TODOLIST.md @@ -42,3 +42,42 @@ **影響**:shadcn 的 registry 生態是目前採用率的核心驅動之一——開發者能找到、安裝、分享社群元件,讓整個 UI library 不只靠官方維護。Angular 生態目前沒有等價物,這是 Sanring 差異化的機會。 **成本**:高。各子項目可獨立交付。GitHub Registries 與 API Reference 已出貨;下一步是 Directory 和 Auth。 + +--- + +## P32 — 52 元件產品/API 掃描:對齊既有組件,不新增 primitive + +2026-09-22 對 `packages/ui` 52 個正式元件做產品/API 掃描(公開 input/output、兄弟組件對齊、docs 是否超賣能力)。不是重跑 P3/P26/P30 的 a11y 稽核。`check-registry-parity.mjs` 與 `check-registry-sync.mjs` 當日皆綠(52/52)。官方目錄不缺新 primitive;該做的是把兄弟組件對齊,以及把 docs 已經講出去的能力做完。 + +0.25.0 已出(blocks / dialog header align / field textarea projection)。0.25.1 收斂 input/textarea/combobox count、combobox chip wrap、dialog header 獨立底色,以及 P32 sibling API。 + +### 該做 + +- [x] **table sticky 欄背景**:`sticky`/`stickyEnd` 已能傳進 CDK,但 CDK 只加 `position: sticky` + 位移,沒有不透明背景;捲動時文字會透出來。細節與「刻意不做」的 `CdkTextColumn` / CDK flex-layout 見 [packages/ui/src/lib/components/table/todolist.md](packages/ui/src/lib/components/table/todolist.md) +- [x] **table docs 配方**(不寫新元件):loading skeleton rows(既有 `skeleton`);欄位顯隱(dropdown-menu + checkbox + 動態 `sanringRowDefColumns`)。同上 todolist「使用模式」節 +- [x] **sheet 對齊 dialog**:dialog content 已有內建 `showClose` / `closeAriaLabel`,header 已有 `align`;sheet 還要自己放 `[sanringSheetClose]`,`sanring-sheet-header` 沒有 `align` +- [x] **radio `size`**:checkbox / switch / otp-input 有 sm/md/lg;radio 寫死 `RADIO_SIZE_CLASS = 'aspect-square h-4 w-4'`(`radio.styles.ts`) +- [x] **dropdown-menu 真 submenu**:docs 鍵盤表寫了左右鍵開關子選單,官網範例卻是 `mouseenter` 切兩欄,不是巢狀 `menu`。checkbox / radio 範例用打勾圖示組出來即可,不必先做成一等 primitive +- [x] **popover `side` + aria fallback**:tooltip / hover-card 已有四向 `side`;popover 只做上/下,而且沒投影 title 時仍綁死 `aria-labelledby`(`popover-content.component.ts`) +- [x] **transfer 根層 `disabled` + `ariaLabel`**:現在只能 disable 單一 item(`TransferItem.disabled`);雙列表是沒名字的 `role="group"` + +### 可選(有缺口,但已有組合路徑) + +- [ ] **combobox** trigger/input 補 `ariaLabel` / `ariaLabelledBy`(已有 `sanring-combobox-label` 與 field 整合) +- [ ] **select** 補獨立 `required` input(現在只從 `Validators.required` 推導,`aria-required` 吃不到純 template `[required]`) +- [ ] **toast docs** 補 `ToastOptions.class`(型別已有,走 service,不要加元件級 `class` `@Input`) + +### 明確不做(查證後不是缺口,避免下次掃描重開) + +- 新 primitive(`kbd` / `chart` / `toggle-group` / `empty` 等)——官方方向在 blocks 與 registry 生態,不在再堆元件 +- input / textarea 再包一層 `disabled` / `aria-*` input——原生 host 屬性 + `SanringFieldControl` 是刻意的薄 API +- toast 元件級 `class` `@Input`——走 `ToastOptions.class` +- table `CdkTextColumn`、CDK flex-layout ``——見 table todolist,已明示不做 +- dialog 加 `[(isOpen)]`——CDK Dialog 是 service 開啟;sheet / popover 的 `isOpen` model 是另一套 overlay。沒需求不要硬對齊 +- 每個控制項都加 `size`——field 高度契約是共用的 `FIELD_SIZE_CLASS`;radio 是唯一跟 checkbox/switch 並排會明顯不齊的 + +**現況**:52 個元件裡約 44 個判定維持。alert-dialog 沒有自己的 `class` 沒關係(複用 dialog 零件,`showClose` 預設關是對的)。dropdown-menu 的 checkbox/radio docs 範例是組合解法,不是假文件。 + +**影響**:不處理的話,消費者會在「看起來該有的兄弟 API」上卡關(sheet 關閉鈕、radio 尺寸、popover 左右、transfer 整組停用),或照著 dropdown-menu 鍵盤表做出不能用方向鍵開的子選單。 + +**成本**:中。各子項可獨立交付;table docs 配方最低、sheet/radio 次之、dropdown-menu submenu 最重(要接 `@angular/aria/menu` 巢狀,不能沿用現在的兩欄 hover)。 diff --git a/apps/docs/src/app/i18n/locales/en/common.ts b/apps/docs/src/app/i18n/locales/en/common.ts index ef266622..acbc00a8 100644 --- a/apps/docs/src/app/i18n/locales/en/common.ts +++ b/apps/docs/src/app/i18n/locales/en/common.ts @@ -167,4 +167,10 @@ export const commonTranslations = { 'status.maintenance.title': 'Under maintenance', 'status.maintenance.description': 'This component is temporarily under maintenance and hidden from production navigation. It remains reachable directly while we work on it.', + 'combobox.demo.multipleSingleLine': 'Multiple, single line', + 'combobox.demo.multipleMultiLine': 'Multiple, multi-line', + 'combobox.api.chipInputWrap.description': + 'When false, chips and the search input stay on one row instead of wrapping.', + 'combobox.api.chipsWrap.description': + 'When false, selected chips stay on one line and overflow with an ellipsis.', } as const; diff --git a/apps/docs/src/app/i18n/locales/en/components/combobox.ts b/apps/docs/src/app/i18n/locales/en/components/combobox.ts index 7518aced..9b86cd75 100644 --- a/apps/docs/src/app/i18n/locales/en/components/combobox.ts +++ b/apps/docs/src/app/i18n/locales/en/components/combobox.ts @@ -1,3 +1,4 @@ +/** Combobox docs copy. */ export const comboboxTranslations = { 'combobox.description': 'Autocomplete input with a list of suggestions.', 'combobox.examples.basic.description': @@ -36,6 +37,12 @@ export const comboboxTranslations = { 'Selected value controlled by the root. Use a string for single select or string array for multiple select.', 'combobox.api.multiple.description': 'Allows selecting more than one item and pairing the field with chips.', + 'combobox.demo.multipleSingleLine': 'Multiple, single line', + 'combobox.demo.multipleMultiLine': 'Multiple, multi-line', + 'combobox.api.chipInputWrap.description': + 'When false, chips and the search input stay on one row instead of wrapping.', + 'combobox.api.chipsWrap.description': + 'When false, selected chips stay on one line and overflow with an ellipsis.', 'combobox.api.disabled.description': 'Disables the combobox input and prevents selection changes.', 'combobox.api.inputId.description': diff --git a/apps/docs/src/app/i18n/locales/en/components/dialog.ts b/apps/docs/src/app/i18n/locales/en/components/dialog.ts index 5578f571..9d616733 100644 --- a/apps/docs/src/app/i18n/locales/en/components/dialog.ts +++ b/apps/docs/src/app/i18n/locales/en/components/dialog.ts @@ -1,3 +1,4 @@ +/** Dialog docs copy. */ export const dialogTranslations = { 'dialog.description': 'An overlay primitive built on Angular CDK Dialog for modal tasks and focused decisions.', @@ -8,7 +9,7 @@ export const dialogTranslations = { 'dialog.demo.noClose': 'No Close Button', 'dialog.demo.stickyFooter': 'Sticky Footer', 'dialog.demo.scrollable': 'Scrollable Content', - 'dialog.demo.header': 'Header alignment', + 'dialog.demo.header': 'Header alignment and background', 'dialog.examples.description': 'Common dialog patterns for custom actions, hidden close controls, sticky actions, and dense scrollable content.', 'dialog.examples.basic.description': @@ -35,6 +36,8 @@ export const dialogTranslations = { 'dialog.api.mediaClass.description': 'Additional classes merged with the dialog media container.', 'dialog.api.headerAlign.description': 'Header text alignment. start is left, center is centered at every breakpoint. The default stays centered on small screens and left-aligned from sm up.', + 'dialog.api.headerClass.description': + 'Additional classes merged with the header layout. Use this to give the header a different background from sanring-dialog-content, for example bg-[var(--sanring-surface-strong)]. Pair with overflow-hidden p-0 on content so the fill reaches the panel edges.', 'dialog.api.titleClass.description': 'Additional classes merged with the title styles. Use this to change title color, for example text-[var(--sanring-primary-70)].', 'dialog.accessibility.description': diff --git a/apps/docs/src/app/i18n/locales/en/components/dropdown-menu.ts b/apps/docs/src/app/i18n/locales/en/components/dropdown-menu.ts index b23fc6d2..efa1526e 100644 --- a/apps/docs/src/app/i18n/locales/en/components/dropdown-menu.ts +++ b/apps/docs/src/app/i18n/locales/en/components/dropdown-menu.ts @@ -32,6 +32,10 @@ export const dropdownMenuTranslations = { 'Controls item tone. Use destructive for actions that remove data or have serious consequences.', 'dropdownMenu.api.class.description': 'Additional classes merged with the corresponding dropdown menu primitive.', + 'dropdownMenu.api.submenu.description': + 'The nested menu to open, bound to the sub-content export (#ref="sanringDropdownMenuSubContent", then [submenu]="ref.menu"). Arrow Right opens it; Arrow Left closes it.', + 'dropdownMenu.api.subTriggerValue.description': + 'Required by the underlying ARIA menu item. Use a distinct value from sibling items.', 'dropdownMenu.accessibility.description': "Built on @angular/aria/menu. The panel has role='menu'; each item has role='menuitem', role='menuitemcheckbox', or role='menuitemradio' as appropriate. The trigger button has aria-haspopup='menu' and aria-expanded managed by the underlying MenuTrigger directive.", 'dropdownMenu.keyboard.description': 'Keyboard navigation follows the WAI-ARIA menu pattern.', @@ -41,5 +45,5 @@ export const dropdownMenuTranslations = { 'dropdownMenu.keyboard.openSubmenu': 'Open the focused submenu.', 'dropdownMenu.keyboard.closeSubmenu': 'Close the active submenu.', 'dropdownMenu.stateModel.description': - "Stateless. DropdownMenuItemDirective emits (itemSelected) on activation. DropdownMenuCheckboxItemComponent and DropdownMenuRadioGroupComponent manage their own checked/selected state via the checked input and checkedChange output. Open/closed state is managed internally by @angular/aria/menu.", + 'Stateless. DropdownMenuItemDirective emits (itemSelected) on activation. Nest sanring-dropdown-menu-sub with a sub-trigger and sub-content for a real submenu. Checkbox and radio examples compose icons onto ordinary items. Open/closed state is managed internally by @angular/aria/menu.', } as const; diff --git a/apps/docs/src/app/i18n/locales/en/components/popover.ts b/apps/docs/src/app/i18n/locales/en/components/popover.ts index b27a8969..2aa80d2e 100644 --- a/apps/docs/src/app/i18n/locales/en/components/popover.ts +++ b/apps/docs/src/app/i18n/locales/en/components/popover.ts @@ -12,6 +12,14 @@ export const popoverTranslations = { 'popover.examples.description': 'Common popover patterns: alignment, user profile overlay, and form controls.', 'popover.demo.align': 'Align', + 'popover.demo.side': 'Side', + 'popover.api.side.description': + "Preferred side of the trigger: 'top', 'right', 'bottom' (default), or 'left'. The panel flips if it would overflow the viewport.", + 'popover.api.sideOffset.description': 'Distance in pixels between the trigger and the panel.', + 'popover.api.ariaLabel.description': + 'Accessible-name fallback used when no sanring-popover-title is projected and ariaLabelledBy is unset.', + 'popover.api.ariaLabelledBy.description': + 'Ids of external elements that label the panel. Takes precedence over PopoverTitle and ariaLabel.', 'popover.demo.withHeader': 'With Header', 'popover.demo.profile': 'User profile', 'popover.demo.profileEmail': 'jane@example.com', @@ -24,7 +32,7 @@ export const popoverTranslations = { "Alignment relative to the trigger: 'start', 'center' (default), or 'end'.", 'popover.api.class.description': 'Additional classes merged onto the floating panel.', 'popover.accessibility.description': - "The trigger button has aria-haspopup='dialog', aria-expanded, and aria-controls pointing to the panel id. The panel carries role='dialog'. Include sanringPopoverTitle or sanringPopoverDescription to have aria-labelledby and aria-describedby wired automatically.", + "The trigger button has aria-haspopup='dialog', aria-expanded, and aria-controls pointing to the panel id. The panel carries role='dialog'. Project sanring-popover-title to wire aria-labelledby, or set ariaLabel / ariaLabelledBy when the panel has no title.", 'popover.keyboard.description': 'Focus moves into the panel when it opens.', 'popover.keyboard.escape': 'Close the popover panel and return focus to the trigger.', 'popover.keyboard.tab': 'Move focus to the next focusable element inside the panel.', diff --git a/apps/docs/src/app/i18n/locales/en/components/radio.ts b/apps/docs/src/app/i18n/locales/en/components/radio.ts index ae7b26ee..4c706dd8 100644 --- a/apps/docs/src/app/i18n/locales/en/components/radio.ts +++ b/apps/docs/src/app/i18n/locales/en/components/radio.ts @@ -9,6 +9,12 @@ export const radioTranslations = { 'Common patterns including labeled items, horizontal layout, and disabled states.', 'radio.demo.withLabel': 'With Labels', 'radio.demo.horizontal': 'Horizontal', + 'radio.demo.size': 'Size', + 'radio.demo.size.sm': 'Small', + 'radio.demo.size.md': 'Medium', + 'radio.demo.size.lg': 'Large', + 'radio.api.group.size': + 'Control size inherited by every item in the group. Matches checkbox and switch: sm, md (default), or lg.', 'radio.demo.disabled': 'Disabled', 'radio.demo.field': 'With Field', 'radio.demo.planFree': 'Free', diff --git a/apps/docs/src/app/i18n/locales/en/components/sheet.ts b/apps/docs/src/app/i18n/locales/en/components/sheet.ts index c8696af9..25e14fa2 100644 --- a/apps/docs/src/app/i18n/locales/en/components/sheet.ts +++ b/apps/docs/src/app/i18n/locales/en/components/sheet.ts @@ -28,6 +28,11 @@ export const sheetTranslations = { 'sheet.api.isOpen.description': 'Controls the open state. Supports [(isOpen)] two-way binding.', 'sheet.api.side.description': "Edge from which the panel slides in: 'top', 'right' (default), 'bottom', or 'left'.", + 'sheet.api.showClose.description': 'Controls whether the built-in close button is rendered.', + 'sheet.api.closeAriaLabel.description': + 'Accessible name for the built-in close button. Defaults to 關閉面板.', + 'sheet.api.headerAlign.description': + 'Header text alignment. start is left, center is centered at every breakpoint. The default stays centered on small screens and left-aligned from sm up.', 'sheet.demo.customClose': 'Custom Close Button', 'sheet.demo.confirmDelete': 'Confirm deletion', 'sheet.demo.confirmDeleteDescription': 'This action cannot be undone.', diff --git a/apps/docs/src/app/i18n/locales/en/components/table.ts b/apps/docs/src/app/i18n/locales/en/components/table.ts index dcddbc67..9fc06eb4 100644 --- a/apps/docs/src/app/i18n/locales/en/components/table.ts +++ b/apps/docs/src/app/i18n/locales/en/components/table.ts @@ -10,6 +10,8 @@ export const tableTranslations = { 'table.demo.sortable': 'Sortable headers', 'table.demo.columnSizing': 'Column sizing', 'table.demo.sticky': 'Sticky columns', + 'table.demo.loading': 'Loading skeleton', + 'table.demo.columnVisibility': 'Column visibility', 'table.demo.empty': 'Empty state', 'table.demo.selection': 'Row selection', 'table.demo.actions': 'Actions menu', diff --git a/apps/docs/src/app/i18n/locales/en/components/transfer.ts b/apps/docs/src/app/i18n/locales/en/components/transfer.ts index cb49bdae..0679fc36 100644 --- a/apps/docs/src/app/i18n/locales/en/components/transfer.ts +++ b/apps/docs/src/app/i18n/locales/en/components/transfer.ts @@ -16,6 +16,11 @@ export const transferTranslations = { 'transfer.demo.moveToTarget': 'Move to selected', 'transfer.demo.moveToSource': 'Move to available', 'transfer.demo.disabled': 'Disabled items', + 'transfer.demo.disabledGroup': 'Disabled group', + 'transfer.api.disabled.description': + 'Disables the whole control: items cannot be checked and neither panel can move items.', + 'transfer.api.ariaLabel.description': + 'Accessible name for the role="group" root. Use this so the dual list is announced as one control.', 'transfer.demo.headerCount': 'Header with live count', 'transfer.demo.customActions': 'Custom action buttons', 'transfer.demo.oneWay': 'One-way transfer', @@ -57,7 +62,7 @@ export const transferTranslations = { 'transfer.api.pageNav.description': 'Move to the next/previous page. No-ops past either end of the range.', 'transfer.api.interactive.description': - 'False for a target panel in one-way mode; used internally to disable its checkboxes.', + 'False when the root is disabled, or for a target panel in one-way mode; used to disable that panel’s checkboxes.', 'transfer.api.selectableItems.description': 'Non-disabled items across the full filtered list (not limited to the current page). Useful for displaying a count in the header.', 'transfer.api.selectAllChecked.description': diff --git a/apps/docs/src/app/i18n/locales/en/index.ts b/apps/docs/src/app/i18n/locales/en/index.ts index 320d6639..7b68f0cc 100644 --- a/apps/docs/src/app/i18n/locales/en/index.ts +++ b/apps/docs/src/app/i18n/locales/en/index.ts @@ -2,6 +2,7 @@ import { commonTranslations } from './common'; import { componentTranslations } from './components'; import { pageTranslations } from './pages'; +/** Locale catalog for English docs copy. */ export const en = { ...commonTranslations, ...pageTranslations, diff --git a/apps/docs/src/app/i18n/locales/zh/common.ts b/apps/docs/src/app/i18n/locales/zh/common.ts index 580fff73..9ba6a0a5 100644 --- a/apps/docs/src/app/i18n/locales/zh/common.ts +++ b/apps/docs/src/app/i18n/locales/zh/common.ts @@ -162,4 +162,10 @@ export const commonTranslations = { 'status.maintenance.title': '維護中', 'status.maintenance.description': '此元件目前維護中,已從正式環境的導覽中暫時移除;維護期間仍可透過直接連結訪問。', + 'combobox.demo.multipleSingleLine': '多選、單行', + 'combobox.demo.multipleMultiLine': '多選、多行', + 'combobox.api.chipInputWrap.description': + '設為 false 時,chips 與搜尋框維持單行,不再換行。', + 'combobox.api.chipsWrap.description': + '設為 false 時,已選 chips 維持單行,超出輸入框寬度時以刪節號收斂。', } as const; diff --git a/apps/docs/src/app/i18n/locales/zh/components/combobox.ts b/apps/docs/src/app/i18n/locales/zh/components/combobox.ts index c95fb1e6..fb0b2596 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/combobox.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/combobox.ts @@ -1,3 +1,4 @@ +/** Combobox docs copy. */ export const comboboxTranslations = { 'combobox.description': '具備建議清單的自動完成輸入元件。', 'combobox.examples.basic.description': @@ -33,7 +34,14 @@ export const comboboxTranslations = { 'combobox.api.description': 'combobox primitives 支援的 inputs 與 models。', 'combobox.api.value.description': '由 root 控制的選取值。單選使用 string,多選使用 string array。', - 'combobox.api.multiple.description': '允許選取多個項目,並可搭配 chips 呈現。', + 'combobox.demo.multipleSingleLine': '多選、單行', + 'combobox.demo.multipleMultiLine': '多選、多行', + 'combobox.api.chipInputWrap.description': + '設為 false 時,chips 與搜尋框維持單行,不再換行。', + 'combobox.api.chipsWrap.description': + '設為 false 時,已選 chips 維持單行,超出輸入框寬度時以刪節號收斂。', + 'combobox.api.multiple.description': + '允許選取多個項目,並可搭配 chips 呈現。', 'combobox.api.disabled.description': '停用 combobox input,並阻止選取狀態變更。', 'combobox.api.inputId.description': 'input 或自訂 trigger 與其 label 共用的 ID;未提供時自動產生,也可覆寫以串接應用程式內的關聯。', diff --git a/apps/docs/src/app/i18n/locales/zh/components/dialog.ts b/apps/docs/src/app/i18n/locales/zh/components/dialog.ts index bb02f5a1..3c15b2cf 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/dialog.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/dialog.ts @@ -1,3 +1,4 @@ +/** Dialog docs copy. */ export const dialogTranslations = { 'dialog.description': '建立在 Angular CDK Dialog 上的 overlay primitive,適合 modal 任務與聚焦決策。', @@ -8,7 +9,7 @@ export const dialogTranslations = { 'dialog.demo.noClose': '沒有關閉按鈕', 'dialog.demo.stickyFooter': '固定頁尾', 'dialog.demo.scrollable': '可捲動內容', - 'dialog.demo.header': '標題對齊', + 'dialog.demo.header': '標題對齊與背景', 'dialog.examples.description': '常見 Dialog 模式,包含自訂操作、隱藏關閉控制、固定操作區與大量可捲動內容。', 'dialog.examples.basic.description': @@ -34,6 +35,8 @@ export const dialogTranslations = { 'dialog.api.mediaClass.description': '與 dialog media 容器合併的額外 class。', 'dialog.api.headerAlign.description': '標題列文字對齊。start 靠左,center 在所有斷點置中。預設維持小螢幕置中、sm 以上靠左。', + 'dialog.api.headerClass.description': + '與 header 版面樣式合併的額外 class。用來讓標題列背景跟 sanring-dialog-content 不同,例如 bg-[var(--sanring-surface-strong)]。搭配 content 的 overflow-hidden p-0,底色才能貼齊面板邊緣。', 'dialog.api.titleClass.description': '與標題樣式合併的額外 class。用來改標題顏色,例如 text-[var(--sanring-primary-70)]。', 'dialog.accessibility.description': diff --git a/apps/docs/src/app/i18n/locales/zh/components/dropdown-menu.ts b/apps/docs/src/app/i18n/locales/zh/components/dropdown-menu.ts index 66189336..2c66d091 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/dropdown-menu.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/dropdown-menu.ts @@ -26,6 +26,10 @@ export const dropdownMenuTranslations = { 'dropdownMenu.api.variant.description': '控制 item 語氣;移除資料或高風險操作可使用 destructive。', 'dropdownMenu.api.class.description': '與對應 dropdown menu primitive 樣式合併的額外 class。', + 'dropdownMenu.api.submenu.description': + '要開啟的巢狀選單,綁定到 sub-content 匯出的參照(#ref="sanringDropdownMenuSubContent",再用 [submenu]="ref.menu")。右方向鍵開啟,左方向鍵關閉。', + 'dropdownMenu.api.subTriggerValue.description': + '底層 ARIA menu item 要求必填。請與同層其他 item 使用不同的 value。', 'dropdownMenu.accessibility.description': "基於 @angular/aria/menu 構建。面板有 role='menu',每個 item 依情況有 role='menuitem'、role='menuitemcheckbox' 或 role='menuitemradio'。trigger 按鈕的 aria-haspopup='menu' 與 aria-expanded 由底層 MenuTrigger 指令管理。", 'dropdownMenu.keyboard.description': '鍵盤導覽遵循 WAI-ARIA menu 模式。', @@ -35,5 +39,5 @@ export const dropdownMenuTranslations = { 'dropdownMenu.keyboard.openSubmenu': '開啟聚焦的子選單。', 'dropdownMenu.keyboard.closeSubmenu': '關閉目前的子選單。', 'dropdownMenu.stateModel.description': - "無狀態。DropdownMenuItemDirective 啟動時送出 (itemSelected)。DropdownMenuCheckboxItemComponent 和 DropdownMenuRadioGroupComponent 透過 checked input 和 checkedChange output 自行管理選取狀態。開啟/關閉狀態由 @angular/aria/menu 內部管理。", + '無狀態。DropdownMenuItemDirective 啟動時送出 (itemSelected)。巢狀子選單用 sanring-dropdown-menu-sub 搭配 sub-trigger 與 sub-content。Checkbox / radio 範例是在一般 item 上組合圖示。開啟/關閉狀態由 @angular/aria/menu 內部管理。', } as const; diff --git a/apps/docs/src/app/i18n/locales/zh/components/popover.ts b/apps/docs/src/app/i18n/locales/zh/components/popover.ts index 2e65e1b9..05fe5730 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/popover.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/popover.ts @@ -10,6 +10,14 @@ export const popoverTranslations = { 'Popover 由根節點、trigger 指令與內容面板組合,面板可選用 header、title、description 子元件。', 'popover.examples.description': '常見 popover 模式:對齊方式、使用者資料卡片與表單控制項。', 'popover.demo.align': '對齊', + 'popover.demo.side': '方向', + 'popover.api.side.description': + "偏好出現在 trigger 的哪一側:'top'、'right'、'bottom'(預設)或 'left'。會超出視窗時自動翻面。", + 'popover.api.sideOffset.description': 'trigger 與面板之間的間距(像素)。', + 'popover.api.ariaLabel.description': + '未投影 sanring-popover-title、也未設定 ariaLabelledBy 時的 accessible name fallback。', + 'popover.api.ariaLabelledBy.description': + '標示面板的外部元素 id,優先於 PopoverTitle 與 ariaLabel。', 'popover.demo.withHeader': '含 Header', 'popover.demo.profile': '使用者資料', 'popover.demo.profileEmail': 'jane@example.com', @@ -20,7 +28,7 @@ export const popoverTranslations = { 'popover.api.align.description': "相對於 trigger 的對齊方式:'start'、'center'(預設)或 'end'。", 'popover.api.class.description': '合併到浮動面板的額外 class。', 'popover.accessibility.description': - "trigger 按鈕有 aria-haspopup='dialog'、aria-expanded 及指向面板 id 的 aria-controls。面板本身帶有 role='dialog'。加入 sanringPopoverTitle 或 sanringPopoverDescription,即可自動綁定 aria-labelledby 和 aria-describedby。", + "trigger 按鈕有 aria-haspopup='dialog'、aria-expanded 及指向面板 id 的 aria-controls。面板本身帶有 role='dialog'。投影 sanring-popover-title 會綁上 aria-labelledby;沒有 title 時改設 ariaLabel 或 ariaLabelledBy。", 'popover.keyboard.description': 'Popover 開啟時,焦點會移入面板。', 'popover.keyboard.escape': '關閉 popover 面板,焦點返回 trigger。', 'popover.keyboard.tab': '移動焦點到面板內的下一個可聚焦元素。', diff --git a/apps/docs/src/app/i18n/locales/zh/components/radio.ts b/apps/docs/src/app/i18n/locales/zh/components/radio.ts index 47e91664..e0e70480 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/radio.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/radio.ts @@ -8,6 +8,12 @@ export const radioTranslations = { 'radio.examples.description': '常見模式,包含搭配標籤、水平排列與停用狀態。', 'radio.demo.withLabel': '搭配標籤', 'radio.demo.horizontal': '水平排列', + 'radio.demo.size': '尺寸', + 'radio.demo.size.sm': '小', + 'radio.demo.size.md': '中', + 'radio.demo.size.lg': '大', + 'radio.api.group.size': + '套用到群組內每個選項的尺寸,與 checkbox / switch 對齊:sm、md(預設)或 lg。', 'radio.demo.disabled': '停用', 'radio.demo.field': '搭配 Field', 'radio.demo.planFree': '免費方案', diff --git a/apps/docs/src/app/i18n/locales/zh/components/sheet.ts b/apps/docs/src/app/i18n/locales/zh/components/sheet.ts index 1abe0f29..3bcac640 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/sheet.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/sheet.ts @@ -25,6 +25,10 @@ export const sheetTranslations = { 'sheet.api.description': 'sheet primitives 支援的 inputs 與 outputs。', 'sheet.api.isOpen.description': '控制開啟狀態,支援 [(isOpen)] 雙向綁定。', 'sheet.api.side.description': "面板滑入的邊緣:'top'、'right'(預設)、'bottom' 或 'left'。", + 'sheet.api.showClose.description': '是否渲染內建關閉按鈕。', + 'sheet.api.closeAriaLabel.description': '內建關閉按鈕的 accessible name,預設為「關閉面板」。', + 'sheet.api.headerAlign.description': + '標題列對齊。start 靠左,center 在所有斷點置中。預設在小螢幕置中,sm 以上靠左。', 'sheet.demo.customClose': '自訂關閉按鈕', 'sheet.demo.confirmDelete': '確認刪除', 'sheet.demo.confirmDeleteDescription': '此操作無法復原。', diff --git a/apps/docs/src/app/i18n/locales/zh/components/table.ts b/apps/docs/src/app/i18n/locales/zh/components/table.ts index 5d58f0d0..da333c76 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/table.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/table.ts @@ -10,6 +10,8 @@ export const tableTranslations = { 'table.demo.sortable': '可排序表頭', 'table.demo.columnSizing': '欄位比例', 'table.demo.sticky': 'Sticky 欄位', + 'table.demo.loading': '載入骨架列', + 'table.demo.columnVisibility': '欄位顯隱', 'table.demo.empty': '空資料狀態', 'table.demo.selection': '列選取', 'table.demo.actions': '操作選單', diff --git a/apps/docs/src/app/i18n/locales/zh/components/transfer.ts b/apps/docs/src/app/i18n/locales/zh/components/transfer.ts index a11cd2a9..5def5d4e 100644 --- a/apps/docs/src/app/i18n/locales/zh/components/transfer.ts +++ b/apps/docs/src/app/i18n/locales/zh/components/transfer.ts @@ -15,6 +15,11 @@ export const transferTranslations = { 'transfer.demo.moveToTarget': '移到已選項目', 'transfer.demo.moveToSource': '移到可選項目', 'transfer.demo.disabled': '停用項目', + 'transfer.demo.disabledGroup': '整組停用', + 'transfer.api.disabled.description': + '停用整個控制項:項目無法勾選,兩側也都不能移動。', + 'transfer.api.ariaLabel.description': + '給 role="group" 根節點的 accessible name,讓雙列表被讀成同一個控制項。', 'transfer.demo.headerCount': '標題列即時筆數', 'transfer.demo.customActions': '自訂操作按鈕', 'transfer.demo.oneWay': '單向穿梭', @@ -52,7 +57,7 @@ export const transferTranslations = { '面板目前的頁碼(從 0 開始)與依 pageSize、目前篩選條件算出的總頁數。', 'transfer.api.pageNav.description': '切換到下一頁/上一頁,超出範圍時不會有作用。', 'transfer.api.interactive.description': - 'one-way 模式下的 target 面板會是 false,內部用來停用它的 checkbox。', + '根層停用,或 one-way 模式下的 target 面板時為 false,用來停用該面板的 checkbox。', 'transfer.api.selectableItems.description': '過濾後的完整清單中所有非 disabled 的項目(不受分頁限制)。適合在標題列顯示可選數量。', 'transfer.api.selectAllChecked.description': diff --git a/apps/docs/src/app/i18n/locales/zh/index.ts b/apps/docs/src/app/i18n/locales/zh/index.ts index 77162e0b..f36f8af9 100644 --- a/apps/docs/src/app/i18n/locales/zh/index.ts +++ b/apps/docs/src/app/i18n/locales/zh/index.ts @@ -2,6 +2,7 @@ import { commonTranslations } from './common'; import { componentTranslations } from './components'; import { pageTranslations } from './pages'; +/** Locale catalog for Traditional Chinese docs copy. */ export const zh = { ...commonTranslations, ...pageTranslations, diff --git a/apps/docs/src/app/i18n/translations.ts b/apps/docs/src/app/i18n/translations.ts index 5bb67ad3..4abf1dcc 100644 --- a/apps/docs/src/app/i18n/translations.ts +++ b/apps/docs/src/app/i18n/translations.ts @@ -1,6 +1,8 @@ import { en } from './locales/en/index'; import { zh } from './locales/zh/index'; +/** Docs locale catalogs. TranslationKey is keyof typeof en. */ + export const supportedLocales = ['en', 'zh'] as const; export type Locale = (typeof supportedLocales)[number]; diff --git a/apps/docs/src/app/layouts/component-page/component-page-api-table.component.ts b/apps/docs/src/app/layouts/component-page/component-page-api-table.component.ts index bb6575a9..57277c35 100644 --- a/apps/docs/src/app/layouts/component-page/component-page-api-table.component.ts +++ b/apps/docs/src/app/layouts/component-page/component-page-api-table.component.ts @@ -7,12 +7,9 @@ import { I18nService } from '../../i18n/i18n.service'; standalone: true, template: `
- - + {{ i18n.t('docs.api.surface') }}