PrimeNG Dock 组件完全指南:从基础导航到模拟桌面 UI 实战
【免费下载链接】primengThe Most Complete Angular UI Component Library项目地址: https://gitcode.com/GitHub_Trending/pr/primeng
Dock 是 PrimeNG 提供的一种导航组件,由一组菜单项(MenuItem)构成,形态类似于 macOS 的 Dock 栏,适合用作应用内的快捷导航或模拟桌面级操作界面。本文将以 apps/showcase/public/llms/components/dock.md 为核心骨架,结合 组件源码、样式定义、单元测试 与类型定义,完整讲解 Dock 的导入方式、基础与高级用法、全部 Props/Emits/Templates API、无障碍键盘支持、Pass Through 定制以及主题设计令牌,读完即可在 Angular 应用中落地一个可用的 Dock 导航。
Dock 是什么
Dock 是一个由菜单项(menuitems)组成的导航组件。它不依赖额外的第三方依赖,直接基于 PrimeNG 的MenuItem模型定义导航项,每个菜单项可以包含图标、标签、跳转链接或命令回调。从源码注释与官方文档描述(见 dock.ts 与 dockstyle.ts)可以确认,其核心定位是"navigation component consisting of menuitems"。
安装与导入
Dock 属于 PrimeNG 标准组件,无需单独安装额外包。在模块式应用中使用DockModule:
import { DockModule } from 'primeng/dock';该导入方式与 showcase 中的 import-doc.ts 完全一致。在 Angular 17+ 的 standalone 场景下,也可以直接导入Dock组件类(advanced 示例中import { Dock } from 'primeng/dock'的写法即是如此)。
快速上手的最小示例
import { Component, OnInit } from '@angular/core'; import { DockModule } from 'primeng/dock'; import { MenuItem } from 'primeng/api'; @Component({ selector: 'app-dock-demo', standalone: true, imports: [DockModule], template: ` <div class="dock-window"> <p-dock [model]="items"></p-dock> </div> ` }) export class DockDemo implements OnInit { items: MenuItem[] | undefined; ngOnInit() { this.items = [ { label: 'Finder', icon: 'https://primefaces.org/cdn/primeng/images/dock/finder.svg' }, { label: 'App Store', icon: 'https://primefaces.org/cdn/primeng/images/dock/appstore.svg' }, { label: 'Photos', icon: 'https://primefaces.org/cdn/primeng/images/dock/photos.svg' }, { label: 'Trash', icon: 'https://primefaces.org/cdn/primeng/images/dock/trash.png' } ]; } }默认情况下 Dock 渲染在底部(position默认为bottom),并会根据模型自动生成菜单项。
基础用法:模型与定位
Dock 需要一个MenuItem[]集合作为其模型(model)。默认位置是底部(bottom),通过position属性还可以设置为其他三个方向:top、left、right。Dock 的内容由item模板定义。
下面的 Basic 示例来自官方文档,通过单选按钮动态切换 Dock 的停靠方向,同时展示了item模板的用法:
import { Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { DockModule } from 'primeng/dock'; import { RadioButtonModule } from 'primeng/radiobutton'; import { TooltipModule } from 'primeng/tooltip'; import { MenuItem } from 'primeng/api'; @Component({ template: ` <div class="card"> <div class="flex flex-wrap gap-4 mb-8"> <div *ngFor="let pos of positionOptions" class="flex items-center"> <p-radiobutton name="dock" [value]="pos.value" [label]="pos.label" [(ngModel)]="position" [inputId]="pos.label" /> <label [for]="pos.label" class="ml-2"> {{ pos.label }} </label> </div> </div> <div class="dock-window"> <p-dock [model]="items" [position]="position"> <ng-template #item let-item> <img [pTooltip]="item.label" tooltipPosition="top" [src]="item.icon" [alt]="item.label" width="100%" /> </ng-template> </p-dock> </div> </div> `, standalone: true, imports: [DockModule, RadioButtonModule, TooltipModule, FormsModule] }) export class DockBasicDemo implements OnInit { items: MenuItem[] | undefined; positionOptions: any[]; ngOnInit() { this.items = [ { label: 'Finder', icon: 'https://primefaces.org/cdn/primeng/images/dock/finder.svg' }, { label: 'App Store', icon: 'https://primefaces.org/cdn/primeng/images/dock/appstore.svg' }, { label: 'Photos', icon: 'https://primefaces.org/cdn/primeng/images/dock/photos.svg' }, { label: 'Trash', icon: 'https://primefaces.org/cdn/primeng/images/dock/trash.png' } ]; } }关键知识点
model属性:MenuItem[]类型,默认值为null。菜单项支持的字段在 dock.ts 模板中有完整映射,包括label、icon、url、routerLink、command、disabled、visible、badge、styleClass、tooltipOptions等。position属性:可选值为"right" | "left" | "top" | "bottom",默认bottom。从 dockstyle.ts 可以看到,根节点会根据当前 position 追加p-dock-bottom/p-dock-top/p-dock-left/p-dock-right类名。item模板:通过<ng-template #item let-item>定义,item即当前MenuItem,模板上下文定义在 dock.types.ts 中($implicit: MenuItem)。不提供模板时,组件会默认渲染图标span(见 dock.ts)。
高级用法:用 Dock 组装一个模拟桌面 UI
Dock 的经典实战场景是模拟桌面操作系统界面。官方 Advanced 示例将 Dock 与 Menubar、Dialog、Tree、Terminal、Galleria、Toast、Tooltip 组合在一起,构建了一个带 Finder、Terminal、App Store、Safari、Photos、GitHub、Trash 图标的 macOS 风格 Dock。
import { Component, OnInit, inject } from '@angular/core'; import { DialogModule } from 'primeng/dialog'; import { DockModule } from 'primeng/dock'; import { GalleriaModule } from 'primeng/galleria'; import { MenubarModule } from 'primeng/menubar'; import { TerminalModule } from 'primeng/terminal'; import { ToastModule } from 'primeng/toast'; import { TreeModule } from 'primeng/tree'; import { TooltipModule } from 'primeng/tooltip'; import { NodeService } from '@/service/nodeservice'; import { PhotoService } from '@/service/photoservice'; import { MenuItem, MessageService } from 'primeng/api'; @Component({ template: ` <div class="card dock-demo"> <p-menubar [model]="menubarItems"> <ng-template #start> <i class="pi pi-apple px-2"></i> </ng-template> <ng-template #end> <i class="pi pi-video px-2"></i> <i class="pi pi-wifi px-2"></i> <i class="pi pi-volume-up px-2"></i> <span class="px-2">Fri 13:07</span> <i class="pi pi-search px-2"></i> <i class="pi pi-bars px-2"></i> </ng-template> </p-menubar> <div class="dock-window"> <p-dock [model]="dockItems" position="bottom"> <ng-template #item let-item> <a [pTooltip]="item.label" tooltipPosition="top" class="p-dock-item-link"> <img [alt]="item.label" [src]="item.icon" style="width: 100%" /> </a> </ng-template> </p-dock> <p-toast position="top-center" key="tc" /> <p-dialog [(visible)]="displayFinder" [breakpoints]="{ '960px': '50vw' }" [style]="{ width: '30vw', height: '18rem' }" [draggable]="false" [resizable]="false" header="Finder"> <p-tree [value]="nodes" /> </p-dialog> <p-dialog [maximizable]="true" [(visible)]="displayTerminal" [breakpoints]="{ '960px': '50vw' }" [style]="{ width: '30vw' }" [draggable]="false" [resizable]="false" header="Terminal"> <p-terminal welcomeMessage="Welcome to PrimeNG (cmd: 'date', 'greet {0}', 'random')" prompt="primeng $" /> </p-dialog> <p-galleria [(value)]="images" [showThumbnails]="false" [showThumbnailNavigators]="false" [showItemNavigators]="true" [(visible)]="displayGalleria" [circular]="true" [responsiveOptions]="responsiveOptions" [fullScreen]="true" [containerStyle]="{ width: '400px' }" > <ng-template #item let-item> <img [src]="item.itemImageSrc" style="width: 100%; display: block;" /> </ng-template> </p-galleria> </div> </div> `, standalone: true, imports: [DialogModule, DockModule, GalleriaModule, MenubarModule, TerminalModule, ToastModule, TreeModule, TooltipModule], providers: [NodeService, PhotoService, MessageService] }) export class DockAdvancedDemo implements OnInit { private nodeService = inject(NodeService); private photoService = inject(PhotoService); private messageService = inject(MessageService); displayTerminal: boolean | undefined; displayFinder: boolean | undefined; displayGalleria: boolean | undefined; dockItems: MenuItem[] | undefined; menubarItems: any[] | undefined; responsiveOptions: any[] | undefined; images: any[] | undefined; nodes: any[] | undefined; subscription: Subscription | undefined; ngOnInit() { this.dockItems = [ { label: 'Finder', tooltipOptions: { tooltipLabel: 'Finder', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/finder.svg', command: () => { this.displayFinder = true; } }, { label: 'Terminal', tooltipOptions: { tooltipLabel: 'Terminal', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/terminal.svg', command: () => { this.displayTerminal = true; } }, { label: 'App Store', tooltipOptions: { tooltipLabel: 'App Store', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/appstore.svg', url: 'https://www.apple.com/app-store/' }, { label: 'Safari', tooltipOptions: { tooltipLabel: 'Safari', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/safari.svg' }, { label: 'Photos', tooltipOptions: { tooltipLabel: 'Photos', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/photos.svg', command: () => { this.displayGalleria = true; } }, { label: 'GitHub', tooltipOptions: { tooltipLabel: 'GitHub', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/github.svg', url: 'https://github.com/primefaces/primeng' }, { label: 'Trash', tooltipOptions: { tooltipLabel: 'Trash', tooltipPosition: 'top', positionTop: -15, positionLeft: 15, showDelay: 1000 }, icon: 'https://primefaces.org/cdn/primeng/images/dock/trash.png', command: () => { this.messageService.add({ severity: 'info', summary: 'Trash is empty', key: 'tc' }); } } ]; this.menubarItems = [ { label: 'Finder', styleClass: 'menubar-root' }, { label: 'File', items: [ { label: 'New', icon: 'pi pi-fw pi-plus', items: [ { label: 'Bookmark', icon: 'pi pi-fw pi-bookmark' }, { label: 'Video', icon: 'pi pi-fw pi-video' } ] }, { label: 'Delete', icon: 'pi pi-fw pi-trash' }, { separator: true }, { label: 'Export', icon: 'pi pi-fw pi-external-link' } ] }, { label: 'Edit', items: [ { label: 'Left', icon: 'pi pi-fw pi-align-left' }, { label: 'Right', icon: 'pi pi-fw pi-align-right' }, { label: 'Center', icon: 'pi pi-fw pi-align-center' }, { label: 'Justify', icon: 'pi pi-fw pi-align-justify' } ] }, { label: 'Users', items: [ { label: 'New', icon: 'pi pi-fw pi-user-plus' }, { label: 'Delete', icon: 'pi pi-fw pi-user-minus' }, { label: 'Search', icon: 'pi pi-fw pi-users', items: [ { label: 'Filter', icon: 'pi pi-fw pi-filter', items: [ { label: 'Print', icon: 'pi pi-fw pi-print' } ] }, { icon: 'pi pi-fw pi-bars', label: 'List' } ] } ] }, { label: 'Events', items: [ { label: 'Edit', icon: 'pi pi-fw pi-pencil', items: [ { label: 'Save', icon: 'pi pi-fw pi-calendar-plus' }, { label: 'Delete', icon: 'pi pi-fw pi-calendar-minus' } ] }, { label: 'Archieve', icon: 'pi pi-fw pi-calendar-times', items: [ { label: 'Remove', icon: 'pi pi-fw pi-calendar-minus' } ] } ] }, { label: 'Quit' } ]; this.responsiveOptions = [ { breakpoint: '1024px', numVisible: 3 }, { breakpoint: '768px', numVisible: 2 }, { breakpoint: '560px', numVisible: 1 } ]; this.subscription = this.terminalService.commandHandler.subscribe((command) => this.commandHandler(command)); this.galleriaService.getImages().then((data) => (this.images = data)); this.nodeService.getFiles().then((data) => (this.nodes = data)); } commandHandler(text: any) { let response; let argsIndex = text.indexOf(' '); let command = argsIndex !== -1 ? text.substring(0, argsIndex) : text; switch (command) { case 'date': response = 'Today is ' + new Date().toDateString(); break; case 'greet': response = 'Hola ' + text.substring(argsIndex + 1) + '!'; break; case 'random': response = Math.floor(Math.random() * 100); break; default: response = 'Unknown command: ' + command; break; } if (response) { this.terminalService.sendResponse(response as string); } } ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } } }示例拆解与实战要点
- 菜单项三种行为模型:Dock 的
MenuItem支持三类点击行为——command回调(如 Finder 打开对话框、Trash 弹出 Toast 提示)、url外链跳转(如 App Store、GitHub 项)、routerLink路由跳转(组件源码 dock.ts 中对routerLink的渲染分支可作佐证)。命令回调通过onItemClick触发,参数为{ originalEvent, item }(见 dock.ts)。 - Tooltip 集成:每个菜单项可通过
tooltipOptions配置tooltipLabel、tooltipPosition、positionTop、positionLeft、showDelay等参数;模板中的[pTooltip]指令与菜单项的tooltipOptions是两种等价写法。 item模板的应用:示例用自定义模板把图标渲染为<img>,并给链接追加p-dock-item-link类,这与组件默认渲染的span图标(dock.ts)形成对比——当你需要自定义图标内容时就用模板覆盖。- 配合其他组件:Dock 本身只负责导航触发,真正的"窗口"由 Dialog(Finder/Terminal)、Galleria(Photos)等组件呈现,Toast 用于反馈(Trash 点击提示)。
breakpoints配合numVisible可以控制 Galleria 在不同视口下显示的图片数量。 - 终端交互:
TerminalService.commandHandler订阅用户输入,commandHandler解析并响应date、greet {0}、random命令。注意:示例中使用了this.terminalService与this.galleriaService,实际使用时需要通过构造函数或inject()注入TerminalService与PhotoService(GalleriaService由PhotoService提供),并在ngOnDestroy中退订subscription以防内存泄漏。
API 参考:Props、Emits 与 Templates
以下三张表来自官方文档,与 dock.ts 中@Input/@Output声明一一对应。
Props
| Name | Type | Default | Description |
|---|---|---|---|
| dt | InputSignal<Object> | undefined | Defines scoped design tokens of the component. |
| unstyled | InputSignal<boolean> | undefined | Indicates whether the component should be rendered without styles. |
| pt | InputSignal<DockPassThrough> | undefined | Used to pass attributes to DOM elements inside the component. |
| ptOptions | InputSignal<PassThroughOptions> | undefined | Used to configure passthrough(pt) options of the component. |
| id | string | - | Current id state as a string. |
| styleClass | string | - | Class of the element.(Deprecated) |
| model | MenuItem[] | null | MenuModel instance to define the action items. |
| position | "right" | "left" | "top" | "bottom" | bottom | Position of element. |
| ariaLabel | string | - | Defines a string that labels the input for accessibility. |
| breakpoint | string | 960px | The breakpoint to define the maximum width boundary. |
| ariaLabelledBy | string | - | Defines a string that labels the dropdown button for accessibility. |
Emits
| Name | Parameters | Description |
|---|---|---|
| onFocus | event: FocusEvent | Callback to execute when button is focused. |
| onBlur | event: FocusEvent | Callback to invoke when the component loses focus. |
Templates
| Name | Type | Description |
|---|---|---|
| item | TemplateRef<DockItemTemplateContext> | Custom item template. |
属性细节补充
id自动生成:未显式传入id时,组件在初始化阶段会通过uuid('pn_id_')生成形如pn_id_xxx的字符串 id(见 dock.ts),并作为ul列表元素的 id 与aria-activedescendant的定位依据。styleClass已废弃:源码标注@deprecated since v20.0.0, use class instead(dock.ts),新代码应直接使用 Angular 的class绑定。breakpoint响应式边界:默认960px,组件内部用window.matchMedia((max-width: ${this.breakpoint}))监听视口宽度,当命中时根节点会追加p-dock-mobile类(见 dock.ts 与 dockstyle.ts),移动端可借此切换为不同的布局样式。源码在组件销毁时会通过unbindMatchMediaListener解绑监听,避免内存泄漏(dock.ts)。onFocus/onBlur:在列表获得/失去焦点时触发,焦点进入时内部还会把focusedOptionIndex重置为 0(见 dock.ts)。
无障碍与键盘导航
Screen Reader 下,Dock 组件使用menurole,并通过aria-orientation声明菜单方向;菜单的描述文字既可以通过aria-labelledby属性指定,也可以直接用aria-label提供。每个列表项(li)具有presentationrole,而其中的锚点(a)元素则具有menuitemrole,其aria-label指向菜单项的 label;当菜单项被禁用时,会设置aria-disabled。
以上 ARIA 结构在 dock.ts 的模板中有直接实现:ul上绑定role="menu"、aria-orientation、aria-label/aria-labelledby、aria-activedescendant与tabindex;li上绑定role="menuitem"、aria-label、aria-disabled;锚点绑定aria-hidden="true"。单元测试 dock.spec.ts 也验证了这些 ARIA 属性在四种 position 下aria-orientation的正确取值。
键盘支持
| Key | Function |
|---|---|
| tab | 焦点进入菜单时,将焦点加到第一个菜单项;若焦点已在菜单内,则移到页面 tab 序列的下一个可聚焦元素。 |
| shift + tab | 焦点进入菜单时,将焦点加到最后一个菜单项;若焦点已在菜单内,则移到页面 tab 序列的上一个可聚焦元素。 |
| enter | 激活当前聚焦的 menuitem。 |
| space | 激活当前聚焦的 menuitem。 |
| down arrow | 在垂直布局中,将焦点移到下一个 menuitem。 |
| up arrow | 在垂直布局中,将焦点移到上一个 menuitem。 |
| home | 在水平布局中,将焦点移到第一个 menuitem。 |
| end | 在水平布局中,将焦点移到最后一个 menuitem。 |
键盘逻辑的源码实现
组件在onListKeyDown中根据event.code分派按键(dock.ts):
- 方向键与布局联动:
position为left/right(垂直布局)时,ArrowDown/ArrowUp生效;position为top/bottom(水平布局)时,ArrowRight/ArrowLeft生效。 Home跳转到索引 0,End通过查找最后一个data-p-disabled="false"的菜单项确定末尾索引。Enter与Space均调用onSpaceKey:内部找到focusedOptionIndex对应的li,再查找其中的a,button元素并触发原生click()(dock.ts)。- 焦点移动只遍历
data-p-disabled="false"的菜单项,即禁用的菜单项会被键盘导航自动跳过(findNextOptionIndex/findPrevOptionIndex,见 dock.ts)。 aria-activedescendant会在获得焦点后指向当前聚焦菜单项的 id,方便读屏软件播报。
这些行为在 dock.spec.ts 的"Keyboard Navigation Tests"中均有对应的单元测试覆盖。
Pass Through Options:细粒度定制 DOM
Pass Through(简称 pt)是 PrimeNG 提供的无样式定制机制,用于把属性、类名或事件直接传给组件内部的 DOM 元素。Dock 的 pt 选项定义在 dock.types.ts,与官方文档表完全一致:
| Name | Type | Description |
|---|---|---|
| root | PassThroughOption<HTMLElement, I> | Used to pass attributes to the root's DOM element. |
| listContainer | PassThroughOption<HTMLDivElement, I> | Used to pass attributes to the list container's DOM element. |
| list | PassThroughOption<HTMLUListElement, I> | Used to pass attributes to the list's DOM element. |
| item | PassThroughOption<HTMLLIElement, I> | Used to pass attributes to the item's DOM element. |
| itemContent | PassThroughOption<HTMLDivElement, I> | Used to pass attributes to the item content's DOM element. |
| itemLink | PassThroughOption<HTMLAnchorElement, I> | Used to pass attributes to the item link's DOM element. |
| itemIcon | PassThroughOption<HTMLSpanElement, I> | Used to pass attributes to the item icon's DOM element. |
组件在模板中通过ptm('listContainer')、ptm('list')、getPTOptions(item, i, 'item')等调用把 pt 选项应用到对应元素(dock.ts),其中 item 级别的 pt 还会携带{ item, index }上下文(getPTOptions,见 dock.ts),允许你按菜单项动态返回不同属性。配合unstyled属性使用,可以完全脱离预设样式,实现 headless 定制。
Theming:CSS 类与设计令牌
CSS Classes
组件 DOM 结构与样式类的对应关系定义在 dockstyle.ts,与官方文档表一致:
| Class | Description |
|---|---|
| p-dock | Class name of the root element |
| p-dock-list-container | Class name of the list container element |
| p-dock-list | Class name of the list element |
| p-dock-item | Class name of the item element |
| p-dock-item-content | Class name of the item content element |
| p-dock-item-link | Class name of the item link element |
| p-dock-item-icon | Class name of the item icon element |
此外还有两个随状态追加的类:根节点根据 position 追加p-dock-bottom等方向类、命中 breakpoint 时追加p-dock-mobile;菜单项获得焦点或禁用时追加p-focus/p-disabled。data-pc-name="dock"属性会挂在组件宿主元素上,供自动化测试与 pt 选择器使用(测试见 dock.spec.ts)。
Design Tokens
| Token | CSS Variable | Description |
|---|---|---|
| dock.background | --p-dock-background | Background of root |
| dock.border.color | --p-dock-border-color | Border color of root |
| dock.padding | --p-dock-padding | Padding of root |
| dock.border.radius | --p-dock-border-radius | Border radius of root |
| dock.item.border.radius | --p-dock-item-border-radius | Border radius of item |
| dock.item.padding | --p-dock-item-padding | Padding of item |
| dock.item.size | --p-dock-item-size | Size of item |
| dock.item.focus.ring.width | --p-dock-item-focus-ring-width | Focus ring width of item |
| dock.item.focus.ring.style | --p-dock-item-focus-ring-style | Focus ring style of item |
| dock.item.focus.ring.color | --p-dock-item-focus-ring-color | Focus ring color of item |
| dock.item.focus.ring.offset | --p-dock-item-focus-ring-offset | Focus ring offset of item |
| dock.item.focus.ring.shadow | --p-dock-item-focus-ring-shadow | Focus ring shadow of item |
设计令牌统一以--p-dock-*CSS 变量形式暴露(实际样式值由@primeuix/styles/dock提供,见 dockstyle.ts)。你可以通过覆盖这些变量实现主题定制:例如修改--p-dock-background改变 Dock 底色、修改--p-dock-item-size调整图标尺寸、修改--p-dock-item-focus-ring-color定制键盘焦点高亮颜色。
从源码看 Dock 的内部机制
渲染分支:链接 vs 命令
组件对每个菜单项渲染为两种锚点分支(dock.ts):
- 当菜单项定义了
routerLink且未禁用时,渲染为 Angular Router 链接(routerLinkActive="router-link-active",支持queryParams、fragment、queryParamsHandling、preserveFragment、skipLocationChange、replaceUrl、state等路由属性); - 否则渲染为普通
href链接(对应url字段)或纯点击项(对应command)。
isClickableRouterLink方法决定走哪个分支(dock.ts),对应测试见 dock.spec.ts。
菜单项渲染细节
visible: false隐藏:@for循环内通过*ngIf="item.visible !== false"过滤隐藏项(dock.ts),测试见 dock.spec.ts。disabled支持布尔与函数:disabled(item)会先判断是否为函数,是则调用求值(dock.ts),禁用项同时带aria-disabled与data-p-disabled属性。badge徽标:菜单项可配置badge、badgeStyleClass,组件内置p-badge渲染角标(dock.ts)。- Ripple 水波纹:锚点应用了
pRipple指令,点击时有水波纹反馈。 - 动态模型:模型是响应式的,运行时增删菜单项会实时反映到 DOM,测试覆盖了 add/remove/clear 三种变更(dock.spec.ts)。
生命周期与平台适配
组件继承自BaseComponent,在onInit中生成 id 并绑定媒体查询监听,在onDestroy中解绑;onAfterViewChecked中把ptms(['host', 'root'])的 pt 属性同步到宿主元素(dock.ts)。整份 dock.spec.ts 共覆盖组件初始化、输入属性、位置与方向、交互、模板、键盘导航、焦点管理、样式、无障碍、路由集成、禁用项、动态模型、边界情况、媒体查询与公共方法等十余个测试分组,可作为自定义扩展时的参考基线。
总结
Dock 组件以MenuItem[]为唯一核心输入,通过position实现四向停靠,通过item模板实现完全自定义的图标内容,通过command/url/routerLink三种方式承载导航行为,并通过完整的 ARIA 结构与键盘导航(Enter/Space/方向键/Home/End)保证无障碍体验。配合breakpoint响应式切换、Pass Through 无样式定制以及--p-dock-*设计令牌,Dock 既可以直接落地为产品导航,也可以像 Advanced 示例那样与 Dialog、Galleria、Terminal、Toast 组合,快速搭建富有表现力的类桌面应用界面。想深入了解实现细节,可以继续阅读 dock.ts、dockstyle.ts、dock.types.ts 与 dock.spec.ts。
【免费下载链接】primengThe Most Complete Angular UI Component Library项目地址: https://gitcode.com/GitHub_Trending/pr/primeng
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考