@ds-mo/ui 14.23.0 → 14.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +10 -7
- package/dist/agent.json +57 -14
- package/dist/angular/ds-bar-title.d.ts +1 -1
- package/dist/angular/ds-bar-title.js +3 -3
- package/dist/angular/ds-button-filled.d.ts +3 -1
- package/dist/angular/ds-button-filled.js +7 -4
- package/dist/angular/ds-button-unfilled.d.ts +3 -1
- package/dist/angular/ds-button-unfilled.js +7 -4
- package/dist/components/ds-agent-questionnaire.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-title.js +1 -1
- package/dist/components/ds-bar-workflow.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-chart.js +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-code-block.js +1 -1
- package/dist/components/ds-filter-menu.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-message-actions.js +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-mobile-sheet-nav.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-pagination.js +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-tool-header.js +1 -1
- package/dist/components/ds-panel-tool-search.js +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-shell-app.js +1 -1
- package/dist/components/ds-shell-app.js.map +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-table-filter.js +1 -1
- package/dist/components/ds-table-group.js +1 -1
- package/dist/components/ds-table-saved-views.js +1 -1
- package/dist/components/ds-table-search.js +1 -1
- package/dist/components/ds-table-sort.js +1 -1
- package/dist/components/ds-table.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/p-BR1ayqMU.js +2 -0
- package/dist/components/p-BR1ayqMU.js.map +1 -0
- package/dist/components/{p-DzVYGJJv.js → p-BSRIF4m8.js} +2 -2
- package/dist/components/{p-FitB53eM.js → p-BZlhBJZL.js} +2 -2
- package/dist/components/{p-BmqnrfTf.js → p-Bo6uW_8T.js} +2 -2
- package/dist/components/{p-BIaHDBfP.js → p-Bq8svcPs.js} +2 -2
- package/dist/components/{p-DAFR4U9P.js → p-BqbdABo3.js} +2 -2
- package/dist/components/{p-nVv71P3f.js → p-CEXchU5N.js} +2 -2
- package/dist/components/p-CEfxLsbX.js +2 -0
- package/dist/components/p-CEfxLsbX.js.map +1 -0
- package/dist/components/{p-DXbryJHz.js → p-CYMKmJ2s.js} +2 -2
- package/dist/components/{p-C13fWp2x.js → p-CZHIkVgj.js} +2 -2
- package/dist/components/{p-CL0tECXf.js → p-CZjjDhUl.js} +2 -2
- package/dist/components/{p-Bd-Kpekh.js → p-C_FFU6W3.js} +2 -2
- package/dist/components/{p-CilwchJH.js → p-CglHiyY2.js} +2 -2
- package/dist/components/{p-9M0ak2q_.js → p-DLaN9BPq.js} +2 -2
- package/dist/components/{p-CrYJnDev.js → p-DiS5BgLA.js} +2 -2
- package/dist/components/{p-DiyO47xX.js → p-PXdvBMXB.js} +2 -2
- package/dist/components/{p-7MWaWl8w.js → p-kslioYhf.js} +2 -2
- package/dist/components/p-kslioYhf.js.map +1 -0
- package/dist/docs/components.json +257 -30
- package/dist/lib/components/BarTitle/bar-title-types.d.ts +54 -1
- package/dist/lib/components/ButtonFilled/ButtonFilled.d.ts +12 -1
- package/dist/lib/components/ShellApp/shell-app-types.d.ts +3 -1
- package/dist/lib/shell/index.d.ts +1 -0
- package/dist/mcp-data/registry/bar-title.json +34 -12
- package/dist/mcp-data/registry/button-filled.json +60 -6
- package/dist/mcp-data/registry/button-unfilled.json +60 -6
- package/dist/mcp-data/registry/registry.json +56 -13
- package/dist/mcp-data/registry/shell-app.json +6 -2
- package/dist/react/ds-button-filled.d.ts +1 -0
- package/dist/react/ds-button-filled.js +4 -1
- package/dist/react/ds-button-unfilled.d.ts +1 -0
- package/dist/react/ds-button-unfilled.js +1 -0
- package/dist/types/components/BarTitle/BarTitle.d.ts +19 -6
- package/dist/types/components/BarTitle/bar-title-actions.d.ts +11 -0
- package/dist/types/components/BarTitle/bar-title-types.d.ts +54 -1
- package/dist/types/components/BarTitle/index.d.ts +1 -1
- package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +12 -1
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +12 -1
- package/dist/types/components/ShellApp/ShellApp.d.ts +12 -0
- package/dist/types/components/ShellApp/shell-app-types.d.ts +3 -1
- package/dist/types/components.d.ts +60 -4
- package/dist/types/shell/index.d.ts +1 -0
- package/dist/types/utils/button-story-foundation.d.ts +2 -0
- package/dist/vue/ds-bar-title.js +1 -0
- package/dist/vue/ds-button-filled.js +6 -2
- package/dist/vue/ds-button-unfilled.js +4 -0
- package/package.json +1 -1
- package/dist/components/p--68f7nIx.js +0 -2
- package/dist/components/p--68f7nIx.js.map +0 -1
- package/dist/components/p-7MWaWl8w.js.map +0 -1
- package/dist/components/p-BnTPuV3x.js +0 -2
- package/dist/components/p-BnTPuV3x.js.map +0 -1
- /package/dist/components/{p-DzVYGJJv.js.map → p-BSRIF4m8.js.map} +0 -0
- /package/dist/components/{p-FitB53eM.js.map → p-BZlhBJZL.js.map} +0 -0
- /package/dist/components/{p-BmqnrfTf.js.map → p-Bo6uW_8T.js.map} +0 -0
- /package/dist/components/{p-BIaHDBfP.js.map → p-Bq8svcPs.js.map} +0 -0
- /package/dist/components/{p-DAFR4U9P.js.map → p-BqbdABo3.js.map} +0 -0
- /package/dist/components/{p-nVv71P3f.js.map → p-CEXchU5N.js.map} +0 -0
- /package/dist/components/{p-DXbryJHz.js.map → p-CYMKmJ2s.js.map} +0 -0
- /package/dist/components/{p-C13fWp2x.js.map → p-CZHIkVgj.js.map} +0 -0
- /package/dist/components/{p-CL0tECXf.js.map → p-CZjjDhUl.js.map} +0 -0
- /package/dist/components/{p-Bd-Kpekh.js.map → p-C_FFU6W3.js.map} +0 -0
- /package/dist/components/{p-CilwchJH.js.map → p-CglHiyY2.js.map} +0 -0
- /package/dist/components/{p-9M0ak2q_.js.map → p-DLaN9BPq.js.map} +0 -0
- /package/dist/components/{p-CrYJnDev.js.map → p-DiS5BgLA.js.map} +0 -0
- /package/dist/components/{p-DiyO47xX.js.map → p-PXdvBMXB.js.map} +0 -0
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"A secondary command accompanies the single filled primary action in a local decision area.",
|
|
19
19
|
"A toolbar, tool rail, overflow area, or shell region needs a compact icon action.",
|
|
20
20
|
"A command represents a persistent toggle or mode whose active state remains meaningful after activation.",
|
|
21
|
-
"A row, card, or toolbar needs an overflow or more-options menu; this is the only component that carries that role."
|
|
21
|
+
"A row, card, or toolbar needs an overflow or more-options menu; this is the only component that carries that role.",
|
|
22
|
+
"A secondary command has closely related alternatives and needs split mode without losing its border, surface, active, variant, or loading treatment."
|
|
22
23
|
],
|
|
23
24
|
"avoidWhen": [
|
|
24
25
|
"The command is the single primary action in a local decision area; use ButtonFilled instead.",
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
],
|
|
54
55
|
"accessibility": [
|
|
55
56
|
"Provide an explicit accessible name for every icon-only action.",
|
|
57
|
+
"In split mode, provide menuAriaLabel for the appended ChevronDown segment, pass controls with the Menu id, and synchronize expanded with the Menu open state.",
|
|
56
58
|
"Set hasMenu when the button triggers a Menu; it implies aria-haspopup of menu. Pass controls with the Menu id and expanded from the same open state given to Menu. Expanded popup triggers hold only the pressed wash while open, without changing their resting foreground; expanded disclosure controls without popup semantics retain the normal resting appearance.",
|
|
57
59
|
"Give an icon-only menu button an accessible name that states the role, such as More options or Customize table, because no visible label or chevron carries it.",
|
|
58
60
|
"Use haspopup directly for a non-menu popup such as a listbox or dialog; those cases get no chevron.",
|
|
@@ -78,7 +80,9 @@
|
|
|
78
80
|
"Icon and icon-label loading replace the icon in place. Label-only loading centers the loader while preserving the button's measured width.",
|
|
79
81
|
"hasMenu covers two distinct menu-button shapes and the variant selects between them. With label or icon-label the action has a menu, and a trailing chevron carries the affordance. With icon the button is a menu and no chevron is added.",
|
|
80
82
|
"Because the icon-only shape has no chevron, its glyph must convey the menu on its own. Use Ellipses for a generic more-options or overflow control. Use a specific icon when the menu has a named purpose, such as Table for Customize table.",
|
|
81
|
-
"An open popup is a transient pressed state rather than a selected one. While expanded is true and haspopup or hasMenu identifies a popup, the pressed wash is held for the popup's complete rendered lifecycle and survives hover without promoting foreground or selected fill, including when chrome opts out of selected fills with activeFill set to false. Expanded disclosure buttons without popup semantics remain visually neutral."
|
|
83
|
+
"An open popup is a transient pressed state rather than a selected one. While expanded is true and haspopup or hasMenu identifies a popup, the pressed wash is held for the popup's complete rendered lifecycle and survives hover without promoting foreground or selected fill, including when chrome opts out of selected fills with activeFill set to false. Expanded disclosure buttons without popup semantics remain visually neutral.",
|
|
84
|
+
"Split mode supports label, icon, and icon-label primary segments. The primary segment emits dsClick and the appended menu segment emits dsMenuClick.",
|
|
85
|
+
"Split mode keeps both segments flush and uses a tertiary surface-aware separator. Bordered mode also preserves the shared outer border; borderless mode limits the separator to the content line height."
|
|
82
86
|
],
|
|
83
87
|
"responsiveBehavior": [
|
|
84
88
|
"The consuming layout decides when an icon-label action becomes icon-only and must provide an explicit accessible name after changing variants. Table caption Customize is one such layout: below 900px it uses the icon shape while keeping the same neutral resting foreground regardless of the controlled column configuration.",
|
|
@@ -270,6 +274,15 @@
|
|
|
270
274
|
"mutable": false,
|
|
271
275
|
"description": "Whether the visible label uses the emphasized weight."
|
|
272
276
|
},
|
|
277
|
+
"menuAriaLabel": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"resolvedType": "string",
|
|
280
|
+
"attribute": "menu-aria-label",
|
|
281
|
+
"default": "'More options'",
|
|
282
|
+
"required": false,
|
|
283
|
+
"mutable": false,
|
|
284
|
+
"description": "Accessible name for the appended menu segment in split mode."
|
|
285
|
+
},
|
|
273
286
|
"pressScale": {
|
|
274
287
|
"type": "boolean",
|
|
275
288
|
"resolvedType": "boolean",
|
|
@@ -305,6 +318,15 @@
|
|
|
305
318
|
"mutable": false,
|
|
306
319
|
"description": "Control density (height, padding, icon, type)."
|
|
307
320
|
},
|
|
321
|
+
"split": {
|
|
322
|
+
"type": "boolean",
|
|
323
|
+
"resolvedType": "boolean",
|
|
324
|
+
"attribute": "split",
|
|
325
|
+
"default": "false",
|
|
326
|
+
"required": false,
|
|
327
|
+
"mutable": false,
|
|
328
|
+
"description": "Append a separate ChevronDown menu segment while preserving this button's\nvariant, size, surface, active, loading, border, and width treatment."
|
|
329
|
+
},
|
|
308
330
|
"type": {
|
|
309
331
|
"type": "'button' | 'submit' | 'reset'",
|
|
310
332
|
"resolvedType": "\"button\" | \"reset\" | \"submit\"",
|
|
@@ -347,12 +369,19 @@
|
|
|
347
369
|
"bubbles": true,
|
|
348
370
|
"cancelable": true,
|
|
349
371
|
"composed": true
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"name": "dsMenuClick",
|
|
375
|
+
"detail": "MouseEvent",
|
|
376
|
+
"bubbles": true,
|
|
377
|
+
"cancelable": true,
|
|
378
|
+
"composed": true
|
|
350
379
|
}
|
|
351
380
|
],
|
|
352
381
|
"methods": [
|
|
353
382
|
{
|
|
354
383
|
"name": "setFocus",
|
|
355
|
-
"signature": "setFocus() => Promise<void>"
|
|
384
|
+
"signature": "setFocus(segment?: \"primary\" | \"menu\") => Promise<void>"
|
|
356
385
|
}
|
|
357
386
|
],
|
|
358
387
|
"slots": []
|
|
@@ -522,6 +551,15 @@
|
|
|
522
551
|
"mutable": false,
|
|
523
552
|
"description": "Whether the visible label uses the emphasized weight."
|
|
524
553
|
},
|
|
554
|
+
"menuAriaLabel": {
|
|
555
|
+
"type": "string",
|
|
556
|
+
"resolvedType": "string",
|
|
557
|
+
"attribute": "menu-aria-label",
|
|
558
|
+
"default": "'More options'",
|
|
559
|
+
"required": false,
|
|
560
|
+
"mutable": false,
|
|
561
|
+
"description": "Accessible name for the appended menu segment in split mode."
|
|
562
|
+
},
|
|
525
563
|
"pressScale": {
|
|
526
564
|
"type": "boolean",
|
|
527
565
|
"resolvedType": "boolean",
|
|
@@ -557,6 +595,15 @@
|
|
|
557
595
|
"mutable": false,
|
|
558
596
|
"description": "Control density (height, padding, icon, type)."
|
|
559
597
|
},
|
|
598
|
+
"split": {
|
|
599
|
+
"type": "boolean",
|
|
600
|
+
"resolvedType": "boolean",
|
|
601
|
+
"attribute": "split",
|
|
602
|
+
"default": "false",
|
|
603
|
+
"required": false,
|
|
604
|
+
"mutable": false,
|
|
605
|
+
"description": "Append a separate ChevronDown menu segment while preserving this button's\nvariant, size, surface, active, loading, border, and width treatment."
|
|
606
|
+
},
|
|
560
607
|
"type": {
|
|
561
608
|
"type": "'button' | 'submit' | 'reset'",
|
|
562
609
|
"resolvedType": "\"button\" | \"reset\" | \"submit\"",
|
|
@@ -599,12 +646,19 @@
|
|
|
599
646
|
"bubbles": true,
|
|
600
647
|
"cancelable": true,
|
|
601
648
|
"composed": true
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
"name": "dsMenuClick",
|
|
652
|
+
"detail": "MouseEvent",
|
|
653
|
+
"bubbles": true,
|
|
654
|
+
"cancelable": true,
|
|
655
|
+
"composed": true
|
|
602
656
|
}
|
|
603
657
|
],
|
|
604
658
|
"methods": [
|
|
605
659
|
{
|
|
606
660
|
"name": "setFocus",
|
|
607
|
-
"signature": "setFocus() => Promise<void>"
|
|
661
|
+
"signature": "setFocus(segment?: \"primary\" | \"menu\") => Promise<void>"
|
|
608
662
|
}
|
|
609
663
|
],
|
|
610
664
|
"slots": [],
|
|
@@ -657,12 +711,12 @@
|
|
|
657
711
|
"files": [
|
|
658
712
|
{
|
|
659
713
|
"path": "src/wc/components/ButtonUnfilled/ButtonUnfilled.css",
|
|
660
|
-
"content": "@import '../../utils/button-base.css';\n@import '../../utils/table-caption-control.css';\n\n:host {\n --ds-button-unfilled-fg: var(--color-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-secondary);\n --ds-button-unfilled-border: var(--color-border-secondary);\n}\n\n.button-unfilled {\n background: transparent;\n color: var(--ds-button-unfilled-fg);\n /* Hover/press/selected fill: `.ds-interaction-fill` (+ `--selected` / `--on-*`).\n Active foreground changes remain independent from popup-open pressed paint. */\n}\n\n.button-unfilled--active {\n color: var(--ds-button-unfilled-fg-active);\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. Keep the\n * pressed wash visible for the popup's complete rendered lifecycle without\n * promoting the resting foreground or selected fill.\n */\n/* Inset stroke via interaction-fill ::after (above selected / hover washes). */\n.button-unfilled--bordered {\n --ds-interaction-border-color: var(--ds-button-unfilled-border);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* Foreground / chrome tokens per surface — interaction tokens come from util `--on-*`. */\n.button-unfilled--background-medium {\n --ds-button-unfilled-fg: var(--color-foreground-on-medium-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-medium-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-medium-neutral);\n --ds-button-unfilled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-unfilled--background-bold {\n --ds-button-unfilled-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-bold-neutral);\n --ds-button-unfilled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-unfilled--background-strong {\n --ds-button-unfilled-fg: var(--color-foreground-on-strong-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-strong-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-strong-neutral);\n --ds-button-unfilled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-unfilled--background-translucent {\n --ds-button-unfilled-fg: var(--color-translucent-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-translucent-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-translucent-translucent);\n --ds-button-unfilled-border: var(--color-translucent-border-secondary);\n}\n\n.button-unfilled--background-inverted {\n --ds-button-unfilled-fg: var(--color-inverted-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-inverted-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-inverted-background);\n --ds-button-unfilled-border: var(--color-inverted-border-secondary);\n}\n\n.button-unfilled--background-media {\n --ds-button-unfilled-fg: var(--color-media-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-media-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-media-background);\n --ds-button-unfilled-border: var(--color-media-border-secondary);\n}\n\n.button-unfilled--on-always-dark {\n --ds-button-unfilled-fg: var(--color-always-dark-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-always-dark-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-always-dark-background);\n --ds-button-unfilled-border: var(--color-always-dark-border-secondary);\n}\n\n/*\n * After surface ring defaults: selected fill remaps the knock-out to the active\n * wash so the badge halo matches the selected background.\n */\n.button-unfilled.ds-interaction-fill--selected {\n --ds-button-unfilled-dot-ring: var(--ds-interaction-active);\n}\n",
|
|
714
|
+
"content": "@import '../../utils/button-base.css';\n@import '../../utils/button-split.css';\n@import '../../utils/table-caption-control.css';\n\n:host {\n --ds-button-unfilled-fg: var(--color-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-secondary);\n --ds-button-unfilled-border: var(--color-border-secondary);\n --ds-button-split-border: var(--color-border-secondary);\n --ds-button-split-divider: var(--color-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-medium) {\n --ds-button-split-border: var(--color-border-on-medium-background-secondary);\n --ds-button-split-divider: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-bold) {\n --ds-button-split-border: var(--color-border-on-bold-background-secondary);\n --ds-button-split-divider: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-strong) {\n --ds-button-split-border: var(--color-border-on-strong-background-secondary);\n --ds-button-split-divider: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.button-unfilled-host--background-translucent) {\n --ds-button-split-border: var(--color-translucent-border-secondary);\n --ds-button-split-divider: var(--color-translucent-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-inverted) {\n --ds-button-split-border: var(--color-inverted-border-secondary);\n --ds-button-split-divider: var(--color-inverted-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-media) {\n --ds-button-split-border: var(--color-media-border-secondary);\n --ds-button-split-divider: var(--color-media-border-tertiary);\n}\n\n:host(.button-unfilled-host--background-always-dark) {\n --ds-button-split-border: var(--color-always-dark-border-secondary);\n --ds-button-split-divider: var(--color-always-dark-border-tertiary);\n}\n\n.button-unfilled {\n background: transparent;\n color: var(--ds-button-unfilled-fg);\n /* Hover/press/selected fill: `.ds-interaction-fill` (+ `--selected` / `--on-*`).\n Active foreground changes remain independent from popup-open pressed paint. */\n}\n\n.button-unfilled--active {\n color: var(--ds-button-unfilled-fg-active);\n}\n\n/*\n * An open popup is a transient pressed state, not a selected state. Keep the\n * pressed wash visible for the popup's complete rendered lifecycle without\n * promoting the resting foreground or selected fill.\n */\n/* Inset stroke via interaction-fill ::after (above selected / hover washes). */\n.button-unfilled--bordered {\n --ds-interaction-border-color: var(--ds-button-unfilled-border);\n}\n\n/* Inactive: `.ds-control-inactive` (50% opacity). */\n\n/* Foreground / chrome tokens per surface — interaction tokens come from util `--on-*`. */\n.button-unfilled--background-medium {\n --ds-button-unfilled-fg: var(--color-foreground-on-medium-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-medium-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-medium-neutral);\n --ds-button-unfilled-border: var(--color-border-on-medium-background-secondary);\n}\n\n.button-unfilled--background-bold {\n --ds-button-unfilled-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-bold-neutral);\n --ds-button-unfilled-border: var(--color-border-on-bold-background-secondary);\n}\n\n.button-unfilled--background-strong {\n --ds-button-unfilled-fg: var(--color-foreground-on-strong-background-secondary);\n --ds-button-unfilled-fg-active: var(--color-foreground-on-strong-background-primary);\n --ds-button-unfilled-dot-ring: var(--color-background-strong-neutral);\n --ds-button-unfilled-border: var(--color-border-on-strong-background-secondary);\n}\n\n.button-unfilled--background-translucent {\n --ds-button-unfilled-fg: var(--color-translucent-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-translucent-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-translucent-translucent);\n --ds-button-unfilled-border: var(--color-translucent-border-secondary);\n}\n\n.button-unfilled--background-inverted {\n --ds-button-unfilled-fg: var(--color-inverted-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-inverted-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-inverted-background);\n --ds-button-unfilled-border: var(--color-inverted-border-secondary);\n}\n\n.button-unfilled--background-media {\n --ds-button-unfilled-fg: var(--color-media-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-media-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-media-background);\n --ds-button-unfilled-border: var(--color-media-border-secondary);\n}\n\n.button-unfilled--on-always-dark {\n --ds-button-unfilled-fg: var(--color-always-dark-foreground-secondary);\n --ds-button-unfilled-fg-active: var(--color-always-dark-foreground-primary);\n --ds-button-unfilled-dot-ring: var(--color-always-dark-background);\n --ds-button-unfilled-border: var(--color-always-dark-border-secondary);\n}\n\n/*\n * After surface ring defaults: selected fill remaps the knock-out to the active\n * wash so the badge halo matches the selected background.\n */\n.button-unfilled.ds-interaction-fill--selected {\n --ds-button-unfilled-dot-ring: var(--ds-interaction-active);\n}\n",
|
|
661
715
|
"type": "registry:ui"
|
|
662
716
|
},
|
|
663
717
|
{
|
|
664
718
|
"path": "src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx",
|
|
665
|
-
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { controlWidthClass } from '../../utils';\nimport { beginElevatedControlPress } from '../../utils/control-press';\nimport { renderButtonContent } from '../../utils/button-render';\nimport type { ControlInsetDepth } from '../../utils/control-text';\nimport { observeTableCaptionCompact } from '../../utils/table-caption-compact';\nimport type { ButtonPopup, ButtonSize, ButtonVariant, ButtonWidth } from '../../utils/button-types';\n\nexport type ButtonUnfilledBackground =\n | 'faint'\n | 'medium'\n | 'bold'\n | 'strong'\n | 'translucent'\n | 'inverted'\n | 'media'\n | 'always-dark';\n\nexport type ButtonUnfilledVariant = ButtonVariant;\nexport type ButtonUnfilledSize = ButtonSize;\nexport type ButtonUnfilledWidth = ButtonWidth;\nexport type ButtonUnfilledPopup = ButtonPopup;\n\n@Component({\n tag: 'ds-button-unfilled',\n styleUrl: 'ButtonUnfilled.css',\n scoped: true,\n})\nexport class ButtonUnfilled {\n @Element() el!: HTMLElement;\n\n /**\n * Content layout. Default is label-only; pass `icon` for icon-only chrome\n * (nav / tool rails) or `icon-label` for leading icon + text.\n */\n @Prop() variant: ButtonUnfilledVariant = 'label';\n\n /** Control density (height, padding, icon, type). */\n @Prop() size: ButtonUnfilledSize = 'md';\n\n /** Use reduced outer geometry when nested inside a control of the same size. */\n @Prop() isInset: boolean = false;\n\n /** Single removes 4px overall; double removes 8px overall (xs stays single). */\n @Prop() insetDepth: ControlInsetDepth = 'single';\n\n /** Width fit — hug content (default) or fill the parent. */\n @Prop() width: ButtonUnfilledWidth = 'hug';\n\n /** Visible text for `label` / `icon-label` variants. */\n @Prop() label: string = '';\n\n /** Whether the visible label uses the emphasized weight. */\n @Prop() labelEmphasis: boolean = true;\n\n /** Icon name passed to <ds-icon> for `icon` / `icon-label` variants. */\n @Prop() icon: string = '';\n\n /**\n * Owner-controlled visual emphasis inside a composite. This does not add\n * toggle semantics and does not emit dsChange.\n */\n @Prop() isActive: boolean = false;\n\n /**\n * When active, render the selected interaction fill.\n * Default `true` for general UI. Shell chrome (nav / tool rails) should pass\n * `false` so selection is foreground-only (primary color, no fill).\n */\n @Prop() activeFill: boolean = true;\n\n /** Show a 1px secondary inset border. Default on; shell chrome can pass `false`. */\n @Prop() hasBorder: boolean = true;\n\n /** Use the half-radius treatment instead of the default control radius. */\n @Prop() rounded: boolean = false;\n\n /**\n * Scale down during a physical pointer press.\n * Disable when an owning composite requires fixed child or background geometry.\n */\n @Prop() pressScale: boolean = true;\n\n /** Show a notification dot at the top-right of the icon zone (icon variant only). */\n @Prop() dot: boolean = false;\n\n /** Disables interaction. */\n @Prop() isInactive: boolean = false;\n\n /** Shows an inline loader and prevents interaction without applying inactive opacity. */\n @Prop() isLoading: boolean = false;\n\n /** Native button type. */\n @Prop() type: 'button' | 'submit' | 'reset' = 'button';\n\n /** Actual parent surface context. Omit on primary and secondary surfaces. */\n @Prop() background: ButtonUnfilledBackground | undefined;\n\n /** Accessible name override. Required for icon-only buttons. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop() controls: string | undefined;\n /**\n * Controlled disclosure or popup state forwarded to `aria-expanded`.\n * Popup triggers hold only the pressed wash while open; their resting\n * foreground is unchanged. Ordinary disclosures remain visually neutral.\n */\n @Prop() expanded: boolean | undefined;\n @Prop() haspopup: ButtonUnfilledPopup | undefined;\n /**\n * Controlled state for a genuine toggle button. Adds aria-pressed, promotes\n * active styling, and makes activation emit dsChange with the requested state.\n */\n @Prop() pressed: boolean | undefined;\n\n /**\n * Collapse the visible label and chevron to an icon-only control when the\n * owning `ds-table` caption is narrower than 900px. The trigger omits those\n * parts rather than clipping them.\n */\n @Prop({ reflect: true }) collapseLabel: boolean = false;\n\n /**\n * Mark the button as a Menu trigger. Implies `aria-haspopup=\"menu\"` and covers\n * both menu-button shapes:\n *\n * - `label` / `icon-label` — the action *has* a menu; a trailing chevron carries\n * the affordance.\n * - `icon` — the button *is* a menu. No chevron is added, so the glyph must\n * convey the menu on its own. Use `Ellipses` for generic more-options;\n * use a specific icon when the menu has a named purpose, such as\n * `Table` for Customize table.\n *\n * Use `haspopup` directly for non-menu popups.\n */\n @Prop() hasMenu: boolean = false;\n\n /**\n * Native `tabindex` for roving keyboard groups in shell chrome.\n * Omit for the default button tab stop (`0`).\n */\n @Prop({ attribute: 'tab-index' }) focusTabIndex?: number;\n\n @Event() dsClick!: EventEmitter<MouseEvent>;\n @Event() dsChange!: EventEmitter<boolean>;\n\n @State() private captionCompact = false;\n\n private buttonEl: HTMLButtonElement | null = null;\n private captionCompactDisconnect: (() => void) | undefined;\n private hasLoaded = false;\n\n componentDidLoad(): void {\n this.hasLoaded = true;\n this.syncCaptionCompactObserver();\n }\n\n connectedCallback(): void {\n if (this.hasLoaded) this.syncCaptionCompactObserver();\n }\n\n disconnectedCallback(): void {\n this.disconnectCaptionCompactObserver();\n }\n\n @Watch('collapseLabel')\n onCollapseLabelChange() {\n this.syncCaptionCompactObserver();\n }\n\n @Method()\n async setFocus() {\n this.buttonEl?.focus();\n }\n\n private handleClick = (event: MouseEvent) => {\n if (this.isInactive || this.isLoading) {\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n this.dsClick.emit(event);\n if (this.pressed !== undefined) this.dsChange.emit(!this.pressed);\n };\n\n private get captionIconOnly(): boolean {\n return (\n this.collapseLabel &&\n this.captionCompact &&\n this.variant === 'icon-label' &&\n Boolean(this.icon)\n );\n }\n\n private get visualVariant(): ButtonUnfilledVariant {\n return this.captionIconOnly ? 'icon' : this.variant;\n }\n\n private get showDot(): boolean {\n return this.variant === 'icon' && this.dot && !this.isLoading;\n }\n\n private syncCaptionCompactObserver(): void {\n this.disconnectCaptionCompactObserver();\n if (!this.collapseLabel) {\n if (this.captionCompact) this.captionCompact = false;\n return;\n }\n this.captionCompactDisconnect = observeTableCaptionCompact(this.el, compact => {\n if (this.captionCompact !== compact) this.captionCompact = compact;\n });\n }\n\n private disconnectCaptionCompactObserver(): void {\n this.captionCompactDisconnect?.();\n this.captionCompactDisconnect = undefined;\n }\n\n private get resolvedHaspopup(): ButtonUnfilledPopup | undefined {\n return this.haspopup ?? (this.hasMenu ? 'menu' : undefined);\n }\n\n private get doubleInset(): boolean {\n return this.isInset && this.insetDepth === 'double' && this.size !== 'xs';\n }\n\n /** Only popup triggers hold a pressed visual while expanded. */\n private get expandedPopup(): boolean {\n return this.expanded === true && this.resolvedHaspopup !== undefined && !this.isInactive;\n }\n\n private get visuallyActive(): boolean {\n return this.isActive || this.pressed === true;\n }\n\n /** Knock-out ring: selected fill → active wash; otherwise surface token. */\n private get dotRing(): string {\n if ((this.isActive || this.pressed === true) && this.activeFill) {\n return 'var(--ds-interaction-active)';\n }\n return 'var(--ds-button-unfilled-dot-ring)';\n }\n\n private get accessibleName(): string | undefined {\n if (this.ariaLabel) return this.ariaLabel;\n if (this.isLoading && this.variant === 'label' && this.label) return this.label;\n return undefined;\n }\n\n render() {\n const bg = this.background;\n const cls: Record<string, boolean> = {\n 'button-unfilled': true,\n 'ds-button': true,\n 'ds-focus-ring-inset': true,\n 'ds-control-press-scale': this.pressScale,\n 'ds-interaction-fill': true,\n 'ds-interaction-fill--selected':\n (this.isActive || this.pressed === true) && this.activeFill && !this.isInactive,\n 'ds-interaction-fill--on-faint': bg === 'faint',\n 'ds-interaction-fill--on-medium': bg === 'medium',\n 'ds-interaction-fill--on-bold': bg === 'bold',\n 'ds-interaction-fill--on-strong': bg === 'strong',\n 'ds-interaction-fill--on-translucent': bg === 'translucent',\n 'ds-interaction-fill--on-inverted': bg === 'inverted',\n 'ds-interaction-fill--on-media': bg === 'media',\n 'ds-interaction-fill--on-always-dark': bg === 'always-dark',\n 'button-unfilled--active': this.visuallyActive,\n 'button-unfilled--expanded': this.expandedPopup,\n 'ds-button--expanded': this.expandedPopup,\n 'button-unfilled--bordered': this.hasBorder,\n 'ds-button--bordered': this.hasBorder,\n 'button-unfilled--rounded': this.rounded,\n 'ds-button--rounded': this.rounded,\n 'ds-control-inactive': this.isInactive,\n 'ds-control--lg': this.size === 'lg',\n 'ds-control--md': this.size === 'md',\n 'ds-control--sm': this.size === 'sm',\n 'ds-control--xs': this.size === 'xs',\n 'ds-control--inset': this.isInset && !this.doubleInset,\n 'ds-control--inset-double': this.doubleInset,\n 'ds-control-frame': true,\n 'button-unfilled--icon': this.visualVariant === 'icon',\n 'ds-button--icon': this.visualVariant === 'icon',\n 'button-unfilled--label': this.visualVariant === 'label',\n 'button-unfilled--icon-label': this.visualVariant === 'icon-label',\n 'button-unfilled--background-faint': bg === 'faint',\n 'button-unfilled--background-medium': bg === 'medium',\n 'button-unfilled--background-bold': bg === 'bold',\n 'button-unfilled--background-strong': bg === 'strong',\n 'button-unfilled--background-translucent': bg === 'translucent',\n 'button-unfilled--background-inverted': bg === 'inverted',\n 'button-unfilled--background-media': bg === 'media',\n 'button-unfilled--on-always-dark': bg === 'always-dark',\n };\n\n const button = (\n <button\n ref={el => {\n this.buttonEl = el ?? null;\n }}\n type={this.type}\n class={cls}\n disabled={this.isInactive}\n tabIndex={this.focusTabIndex ?? 0}\n aria-label={this.accessibleName}\n aria-busy={this.isLoading ? 'true' : undefined}\n aria-disabled={this.isLoading ? 'true' : undefined}\n aria-controls={this.controls}\n aria-expanded={this.expanded === undefined ? undefined : String(this.expanded)}\n aria-haspopup={this.resolvedHaspopup}\n aria-pressed={this.pressed === undefined ? undefined : String(this.pressed)}\n onPointerDown={event =>\n beginElevatedControlPress(event, this.pressScale && !this.isInactive && !this.isLoading)\n }\n onClick={this.handleClick}\n >\n {renderButtonContent({\n namespace: 'button-unfilled',\n variant: this.visualVariant,\n size: this.size,\n label: this.label,\n labelEmphasis: this.labelEmphasis,\n icon: this.icon,\n hasMenu: this.hasMenu,\n isLoading: this.isLoading,\n dot: {\n visible: this.showDot,\n background: this.dotRing,\n },\n })}\n </button>\n );\n\n return (\n <Host\n class={{\n 'button-unfilled-host': true,\n 'button-unfilled-host--icon': this.visualVariant === 'icon',\n 'ds-button-host--icon': this.visualVariant === 'icon',\n 'ds-control--lg': this.size === 'lg',\n 'ds-control--md': this.size === 'md',\n 'ds-control--sm': this.size === 'sm',\n 'ds-control--xs': this.size === 'xs',\n 'ds-control--inset': this.isInset && !this.doubleInset,\n 'ds-control--inset-double': this.doubleInset,\n 'ds-table-caption-control': this.collapseLabel,\n 'ds-table-caption-control--compact': this.captionIconOnly,\n ...controlWidthClass(this.width),\n }}\n tabIndex={-1}\n >\n {this.collapseLabel ? (\n <ds-tooltip\n label={this.captionIconOnly ? this.label || this.accessibleName || '' : ''}\n side=\"top\"\n size=\"sm\"\n >\n {button}\n </ds-tooltip>\n ) : (\n button\n )}\n </Host>\n );\n }\n}\n",
|
|
719
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n Watch,\n} from '@stencil/core';\nimport { controlWidthClass } from '../../utils';\nimport { beginElevatedControlPress } from '../../utils/control-press';\nimport { renderButtonContent } from '../../utils/button-render';\nimport type { ControlInsetDepth } from '../../utils/control-text';\nimport { observeTableCaptionCompact } from '../../utils/table-caption-compact';\nimport type { ButtonPopup, ButtonSize, ButtonVariant, ButtonWidth } from '../../utils/button-types';\n\nexport type ButtonUnfilledBackground =\n | 'faint'\n | 'medium'\n | 'bold'\n | 'strong'\n | 'translucent'\n | 'inverted'\n | 'media'\n | 'always-dark';\n\nexport type ButtonUnfilledVariant = ButtonVariant;\nexport type ButtonUnfilledSize = ButtonSize;\nexport type ButtonUnfilledWidth = ButtonWidth;\nexport type ButtonUnfilledPopup = ButtonPopup;\n\n@Component({\n tag: 'ds-button-unfilled',\n styleUrl: 'ButtonUnfilled.css',\n scoped: true,\n})\nexport class ButtonUnfilled {\n @Element() el!: HTMLElement;\n\n /**\n * Content layout. Default is label-only; pass `icon` for icon-only chrome\n * (nav / tool rails) or `icon-label` for leading icon + text.\n */\n @Prop() variant: ButtonUnfilledVariant = 'label';\n\n /** Control density (height, padding, icon, type). */\n @Prop() size: ButtonUnfilledSize = 'md';\n\n /** Use reduced outer geometry when nested inside a control of the same size. */\n @Prop() isInset: boolean = false;\n\n /** Single removes 4px overall; double removes 8px overall (xs stays single). */\n @Prop() insetDepth: ControlInsetDepth = 'single';\n\n /** Width fit — hug content (default) or fill the parent. */\n @Prop() width: ButtonUnfilledWidth = 'hug';\n\n /** Visible text for `label` / `icon-label` variants. */\n @Prop() label: string = '';\n\n /** Whether the visible label uses the emphasized weight. */\n @Prop() labelEmphasis: boolean = true;\n\n /** Icon name passed to <ds-icon> for `icon` / `icon-label` variants. */\n @Prop() icon: string = '';\n\n /**\n * Owner-controlled visual emphasis inside a composite. This does not add\n * toggle semantics and does not emit dsChange.\n */\n @Prop() isActive: boolean = false;\n\n /**\n * When active, render the selected interaction fill.\n * Default `true` for general UI. Shell chrome (nav / tool rails) should pass\n * `false` so selection is foreground-only (primary color, no fill).\n */\n @Prop() activeFill: boolean = true;\n\n /** Show a 1px secondary inset border. Default on; shell chrome can pass `false`. */\n @Prop() hasBorder: boolean = true;\n\n /** Use the half-radius treatment instead of the default control radius. */\n @Prop() rounded: boolean = false;\n\n /**\n * Scale down during a physical pointer press.\n * Disable when an owning composite requires fixed child or background geometry.\n */\n @Prop() pressScale: boolean = true;\n\n /** Show a notification dot at the top-right of the icon zone (icon variant only). */\n @Prop() dot: boolean = false;\n\n /** Disables interaction. */\n @Prop() isInactive: boolean = false;\n\n /** Shows an inline loader and prevents interaction without applying inactive opacity. */\n @Prop() isLoading: boolean = false;\n\n /** Native button type. */\n @Prop() type: 'button' | 'submit' | 'reset' = 'button';\n\n /** Actual parent surface context. Omit on primary and secondary surfaces. */\n @Prop() background: ButtonUnfilledBackground | undefined;\n\n /** Accessible name override. Required for icon-only buttons. */\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop() controls: string | undefined;\n /**\n * Controlled disclosure or popup state forwarded to `aria-expanded`.\n * Popup triggers hold only the pressed wash while open; their resting\n * foreground is unchanged. Ordinary disclosures remain visually neutral.\n */\n @Prop() expanded: boolean | undefined;\n @Prop() haspopup: ButtonUnfilledPopup | undefined;\n\n /**\n * Append a separate ChevronDown menu segment while preserving this button's\n * variant, size, surface, active, loading, border, and width treatment.\n */\n @Prop() split: boolean = false;\n\n /** Accessible name for the appended menu segment in split mode. */\n @Prop() menuAriaLabel: string = 'More options';\n\n /**\n * Controlled state for a genuine toggle button. Adds aria-pressed, promotes\n * active styling, and makes activation emit dsChange with the requested state.\n */\n @Prop() pressed: boolean | undefined;\n\n /**\n * Collapse the visible label and chevron to an icon-only control when the\n * owning `ds-table` caption is narrower than 900px. The trigger omits those\n * parts rather than clipping them.\n */\n @Prop({ reflect: true }) collapseLabel: boolean = false;\n\n /**\n * Mark the button as a Menu trigger. Implies `aria-haspopup=\"menu\"` and covers\n * both menu-button shapes:\n *\n * - `label` / `icon-label` — the action *has* a menu; a trailing chevron carries\n * the affordance.\n * - `icon` — the button *is* a menu. No chevron is added, so the glyph must\n * convey the menu on its own. Use `Ellipses` for generic more-options;\n * use a specific icon when the menu has a named purpose, such as\n * `Table` for Customize table.\n *\n * Use `haspopup` directly for non-menu popups.\n */\n @Prop() hasMenu: boolean = false;\n\n /**\n * Native `tabindex` for roving keyboard groups in shell chrome.\n * Omit for the default button tab stop (`0`).\n */\n @Prop({ attribute: 'tab-index' }) focusTabIndex?: number;\n\n @Event() dsClick!: EventEmitter<MouseEvent>;\n @Event() dsMenuClick!: EventEmitter<MouseEvent>;\n @Event() dsChange!: EventEmitter<boolean>;\n\n @State() private captionCompact = false;\n\n private buttonEl: HTMLButtonElement | null = null;\n private menuButtonEl: HTMLButtonElement | null = null;\n private captionCompactDisconnect: (() => void) | undefined;\n private hasLoaded = false;\n\n componentDidLoad(): void {\n this.hasLoaded = true;\n this.syncCaptionCompactObserver();\n }\n\n connectedCallback(): void {\n if (this.hasLoaded) this.syncCaptionCompactObserver();\n }\n\n disconnectedCallback(): void {\n this.disconnectCaptionCompactObserver();\n }\n\n @Watch('collapseLabel')\n onCollapseLabelChange() {\n this.syncCaptionCompactObserver();\n }\n\n @Method()\n async setFocus(segment: 'primary' | 'menu' = 'primary') {\n (segment === 'menu' ? this.menuButtonEl : this.buttonEl)?.focus();\n }\n\n private handleClick = (event: MouseEvent) => {\n if (this.isInactive || this.isLoading) {\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n this.dsClick.emit(event);\n if (this.pressed !== undefined) this.dsChange.emit(!this.pressed);\n };\n\n private handleMenuClick = (event: MouseEvent) => {\n if (this.isInactive || this.isLoading) {\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n this.dsMenuClick.emit(event);\n };\n\n private get captionIconOnly(): boolean {\n return (\n this.collapseLabel &&\n this.captionCompact &&\n this.variant === 'icon-label' &&\n Boolean(this.icon)\n );\n }\n\n private get visualVariant(): ButtonUnfilledVariant {\n return this.captionIconOnly ? 'icon' : this.variant;\n }\n\n private get showDot(): boolean {\n return this.variant === 'icon' && this.dot && !this.isLoading;\n }\n\n private syncCaptionCompactObserver(): void {\n this.disconnectCaptionCompactObserver();\n if (!this.collapseLabel) {\n if (this.captionCompact) this.captionCompact = false;\n return;\n }\n this.captionCompactDisconnect = observeTableCaptionCompact(this.el, compact => {\n if (this.captionCompact !== compact) this.captionCompact = compact;\n });\n }\n\n private disconnectCaptionCompactObserver(): void {\n this.captionCompactDisconnect?.();\n this.captionCompactDisconnect = undefined;\n }\n\n private get resolvedHaspopup(): ButtonUnfilledPopup | undefined {\n return this.haspopup ?? (this.hasMenu ? 'menu' : undefined);\n }\n\n private get doubleInset(): boolean {\n return this.isInset && this.insetDepth === 'double' && this.size !== 'xs';\n }\n\n /** Only popup triggers hold a pressed visual while expanded. */\n private get expandedPopup(): boolean {\n return this.expanded === true && this.resolvedHaspopup !== undefined && !this.isInactive;\n }\n\n private get visuallyActive(): boolean {\n return this.isActive || this.pressed === true;\n }\n\n /** Knock-out ring: selected fill → active wash; otherwise surface token. */\n private get dotRing(): string {\n if ((this.isActive || this.pressed === true) && this.activeFill) {\n return 'var(--ds-interaction-active)';\n }\n return 'var(--ds-button-unfilled-dot-ring)';\n }\n\n private get accessibleName(): string | undefined {\n if (this.ariaLabel) return this.ariaLabel;\n if (this.isLoading && this.variant === 'label' && this.label) return this.label;\n return undefined;\n }\n\n private buttonClass(\n variant: ButtonUnfilledVariant,\n expanded: boolean,\n bordered: boolean\n ): Record<string, boolean> {\n const bg = this.background;\n const cls: Record<string, boolean> = {\n 'button-unfilled': true,\n 'ds-button': true,\n 'ds-focus-ring-inset': true,\n 'ds-control-press-scale': this.pressScale && !this.split,\n 'ds-interaction-fill': true,\n 'ds-interaction-fill--selected':\n (this.isActive || this.pressed === true) && this.activeFill && !this.isInactive,\n 'ds-interaction-fill--on-faint': bg === 'faint',\n 'ds-interaction-fill--on-medium': bg === 'medium',\n 'ds-interaction-fill--on-bold': bg === 'bold',\n 'ds-interaction-fill--on-strong': bg === 'strong',\n 'ds-interaction-fill--on-translucent': bg === 'translucent',\n 'ds-interaction-fill--on-inverted': bg === 'inverted',\n 'ds-interaction-fill--on-media': bg === 'media',\n 'ds-interaction-fill--on-always-dark': bg === 'always-dark',\n 'button-unfilled--active': this.visuallyActive,\n 'button-unfilled--expanded': expanded,\n 'ds-button--expanded': expanded,\n 'button-unfilled--bordered': bordered,\n 'ds-button--bordered': bordered,\n 'button-unfilled--rounded': this.rounded,\n 'ds-button--rounded': this.rounded,\n 'ds-control-inactive': this.isInactive,\n 'ds-control--lg': this.size === 'lg',\n 'ds-control--md': this.size === 'md',\n 'ds-control--sm': this.size === 'sm',\n 'ds-control--xs': this.size === 'xs',\n 'ds-control--inset': this.isInset && !this.doubleInset,\n 'ds-control--inset-double': this.doubleInset,\n 'ds-control-frame': true,\n 'button-unfilled--icon': variant === 'icon',\n 'ds-button--icon': variant === 'icon',\n 'button-unfilled--label': variant === 'label',\n 'button-unfilled--icon-label': variant === 'icon-label',\n 'button-unfilled--background-faint': bg === 'faint',\n 'button-unfilled--background-medium': bg === 'medium',\n 'button-unfilled--background-bold': bg === 'bold',\n 'button-unfilled--background-strong': bg === 'strong',\n 'button-unfilled--background-translucent': bg === 'translucent',\n 'button-unfilled--background-inverted': bg === 'inverted',\n 'button-unfilled--background-media': bg === 'media',\n 'button-unfilled--on-always-dark': bg === 'always-dark',\n };\n return cls;\n }\n\n render() {\n const primaryButton = (\n <button\n ref={el => {\n this.buttonEl = el ?? null;\n }}\n type={this.type}\n class={{\n ...this.buttonClass(\n this.visualVariant,\n !this.split && this.expandedPopup,\n this.hasBorder && !this.split\n ),\n 'ds-button-split__primary': this.split,\n }}\n disabled={this.isInactive}\n tabIndex={this.focusTabIndex ?? 0}\n aria-label={this.accessibleName}\n aria-busy={this.isLoading ? 'true' : undefined}\n aria-disabled={this.isLoading ? 'true' : undefined}\n aria-controls={this.split ? undefined : this.controls}\n aria-expanded={\n this.split || this.expanded === undefined ? undefined : String(this.expanded)\n }\n aria-haspopup={this.split ? undefined : this.resolvedHaspopup}\n aria-pressed={this.pressed === undefined ? undefined : String(this.pressed)}\n onPointerDown={event =>\n beginElevatedControlPress(\n event,\n this.pressScale && !this.split && !this.isInactive && !this.isLoading\n )\n }\n onClick={this.handleClick}\n >\n {renderButtonContent({\n namespace: 'button-unfilled',\n variant: this.visualVariant,\n size: this.size,\n label: this.label,\n labelEmphasis: this.labelEmphasis,\n icon: this.icon,\n hasMenu: !this.split && this.hasMenu,\n isLoading: this.isLoading,\n dot: {\n visible: this.showDot,\n background: this.dotRing,\n },\n })}\n </button>\n );\n\n const control = this.split ? (\n <div\n class={{\n 'ds-button-split': true,\n 'ds-button-split--rounded': this.rounded,\n }}\n >\n {primaryButton}\n <span\n class={{\n 'ds-button-split__divider': true,\n 'ds-button-split__divider--content-height': !this.hasBorder,\n }}\n aria-hidden=\"true\"\n />\n <button\n ref={el => {\n this.menuButtonEl = el ?? null;\n }}\n class={{\n ...this.buttonClass('icon', this.expanded === true && !this.isInactive, false),\n 'ds-button-split__menu': true,\n }}\n type=\"button\"\n disabled={this.isInactive || this.isLoading}\n tabIndex={this.focusTabIndex ?? 0}\n aria-label={this.menuAriaLabel}\n aria-controls={this.controls}\n aria-expanded={String(this.expanded === true)}\n aria-haspopup=\"menu\"\n onClick={this.handleMenuClick}\n >\n {renderButtonContent({\n namespace: 'button-unfilled',\n variant: 'icon',\n size: this.size,\n label: '',\n labelEmphasis: true,\n icon: 'ChevronDown',\n hasMenu: false,\n isLoading: false,\n })}\n </button>\n {this.hasBorder ? <span class=\"ds-button-split__outline\" aria-hidden=\"true\" /> : null}\n </div>\n ) : (\n primaryButton\n );\n\n return (\n <Host\n class={{\n 'button-unfilled-host': true,\n 'button-unfilled-host--icon': this.visualVariant === 'icon' && !this.split,\n 'ds-button-host--icon': this.visualVariant === 'icon' && !this.split,\n 'ds-button-split-host': this.split,\n 'ds-button-split-host--primary-icon': this.split && this.visualVariant === 'icon',\n [`button-unfilled-host--background-${this.background}`]:\n this.split && this.background !== undefined,\n 'ds-control--lg': this.size === 'lg',\n 'ds-control--md': this.size === 'md',\n 'ds-control--sm': this.size === 'sm',\n 'ds-control--xs': this.size === 'xs',\n 'ds-control--inset': this.isInset && !this.doubleInset,\n 'ds-control--inset-double': this.doubleInset,\n 'ds-table-caption-control': this.collapseLabel,\n 'ds-table-caption-control--compact': this.captionIconOnly,\n ...controlWidthClass(this.width),\n }}\n tabIndex={-1}\n >\n {this.collapseLabel ? (\n <ds-tooltip\n label={this.captionIconOnly ? this.label || this.accessibleName || '' : ''}\n side=\"top\"\n size=\"sm\"\n >\n {control}\n </ds-tooltip>\n ) : (\n control\n )}\n </Host>\n );\n }\n}\n",
|
|
666
720
|
"type": "registry:ui"
|
|
667
721
|
}
|
|
668
722
|
]
|
|
@@ -1265,7 +1265,7 @@
|
|
|
1265
1265
|
{
|
|
1266
1266
|
"name": "bar-title",
|
|
1267
1267
|
"title": "BarTitle",
|
|
1268
|
-
"description": "Page-level title chrome with
|
|
1268
|
+
"description": "Page-level title chrome with route identity, responsive ordered button, icon, menu, split, and overflow actions, plus explicit expanded, compact, or constrained variants.",
|
|
1269
1269
|
"type": "registry:ui",
|
|
1270
1270
|
"meta": {
|
|
1271
1271
|
"library": "@ds-mo/ui",
|
|
@@ -1276,12 +1276,12 @@
|
|
|
1276
1276
|
"intent": {
|
|
1277
1277
|
"audience": "general",
|
|
1278
1278
|
"status": "stable",
|
|
1279
|
-
"summary": "Page-level title chrome with
|
|
1279
|
+
"summary": "Page-level title chrome with route identity, responsive ordered button, icon, menu, split, and overflow actions, plus explicit expanded, compact, or constrained variants.",
|
|
1280
1280
|
"useWhen": [
|
|
1281
1281
|
"A top-level page needs one h1 above its content.",
|
|
1282
1282
|
"A list or detail page has multiple route-level sections that belong in the inline active-section menu.",
|
|
1283
1283
|
"A detail page needs route ancestors in expanded presentation and a page-level Back action in compact chrome.",
|
|
1284
|
-
"A page needs
|
|
1284
|
+
"A page needs an ordered set of primary, secondary, icon, menu, split, or overflow commands."
|
|
1285
1285
|
],
|
|
1286
1286
|
"avoidWhen": [
|
|
1287
1287
|
"The title belongs to a PanelTools drawer or fullscreen pane; use PanelToolHeader through PanelTools instead.",
|
|
@@ -1325,15 +1325,17 @@
|
|
|
1325
1325
|
"Provide ordered breadcrumb items for deeper expanded paths and handle dsBreadcrumbSelect when native href navigation is not used; backLabel remains the single-parent expanded fallback.",
|
|
1326
1326
|
"Keep value synchronized with the current child route; dsSectionChange returns the selected section id.",
|
|
1327
1327
|
"Section and overflow triggers expose menu relationships and restore focus after selection or dismissal.",
|
|
1328
|
-
"Use one filled primary action in the page decision area and mark destructive overflow actions explicitly."
|
|
1328
|
+
"Use one filled primary action in the page decision area, give icon and menu triggers specific accessible names, and mark destructive overflow actions explicitly.",
|
|
1329
|
+
"Keep every top-level action and nested menu-choice id non-empty and unique; dsAction emits the selected id without adding a parent prefix."
|
|
1329
1330
|
],
|
|
1330
1331
|
"states": [
|
|
1331
1332
|
"Omitting Back, sections, primaryAction, and actions produces the top-level title-only header.",
|
|
1332
1333
|
"Two or more selectable sections add the inline section trigger; one section does not render a redundant menu.",
|
|
1333
1334
|
"Dividers in sections or actions create separate Menu groups without becoming selectable rows.",
|
|
1334
|
-
"The overflow trigger uses its bordered treatment whenever
|
|
1335
|
+
"The overflow trigger uses its bordered treatment whenever any visible action sits beside it; overflow-only presentations keep the trigger borderless.",
|
|
1336
|
+
"Ordered actions preserve authored order, but only the first eligible filled button, unfilled button, icon button, labeled menu button, or split button renders beside the title. Remaining commands use the shared overflow, whose rows are text-only. Menu choices and overflowed split commands become separate rows and share the same flat action event.",
|
|
1335
1337
|
"expanded renders the larger heading, optional description, inset divider, and a full-width caption breadcrumb row above the title. The breadcrumb begins after 32px top padding; the following title and right-side actions share one top alignment. Compact renders 48px chrome with the existing icon Back action; constrained uses compact chrome and moves an auto-collapse primary action into overflow.",
|
|
1336
|
-
"
|
|
1338
|
+
"The one visible ordered action collapses by default in constrained presentation. Use never for an exceptional action whose hierarchy must remain visible; explicit overflow actions never leave the shared menu.",
|
|
1337
1339
|
"showDivider=false removes the bottom divider when the surrounding page composition supplies the boundary; showCompactDivider can independently restore or remove that boundary in compact and constrained variants.",
|
|
1338
1340
|
"placement=shell-bar is reserved for ShellApp panel section navigation. It remains compact, omits description and breadcrumb presentation, and preserves applicable Back, section, primary, and overflow controls."
|
|
1339
1341
|
],
|
|
@@ -1343,15 +1345,16 @@
|
|
|
1343
1345
|
"ShellApp owns shell-bar placement and always selects compact presentation there; mobile continues to use MobileHeader.",
|
|
1344
1346
|
"When ShellPage owns BarTitle on a secondary desktop or tablet page, its expanded, compact, and constrained presentations all match that canvas; the separate MobileHeader component remains primary.",
|
|
1345
1347
|
"Compact titles use the shared compact-header geometry and md control inset so top-level headings align with BarNav and inline Back geometry aligns with PanelToolHeader. Long headings and section labels truncate independently while Back and action controls retain complete hit targets and accessible names.",
|
|
1346
|
-
"A never-collapse
|
|
1348
|
+
"A never-collapse action remains visible in constrained mode; the title and section label yield space first.",
|
|
1349
|
+
"On mobile, icon commands remain visible by default while labeled buttons, labeled menus, split buttons, and explicit overflow commands become one flat overflow menu derived by ShellApp."
|
|
1347
1350
|
],
|
|
1348
1351
|
"frameworkCaveats": {
|
|
1349
1352
|
"customElements": [
|
|
1350
|
-
"Assign breadcrumbs, sections, primaryAction, and actions as JavaScript properties and replace array or object identities when controlled state changes.",
|
|
1353
|
+
"Assign breadcrumbs, sections, actionItems, primaryAction, and actions as JavaScript properties and replace array or object identities when controlled state changes.",
|
|
1351
1354
|
"Handle dsBack for compact navigation and dsBreadcrumbSelect for application-owned expanded breadcrumb navigation, update value after dsSectionChange, and execute product commands from dsAction."
|
|
1352
1355
|
],
|
|
1353
1356
|
"react": [
|
|
1354
|
-
"Pass breadcrumb, section, and action arrays directly to DsBarTitle and keep value controlled from route state.",
|
|
1357
|
+
"Pass breadcrumb, section, and ordered action arrays directly to DsBarTitle and keep value controlled from route state.",
|
|
1355
1358
|
"Place DsBarTitle in DsShellPage with slot=header when automatic page behavior is required."
|
|
1356
1359
|
],
|
|
1357
1360
|
"angular": [
|
|
@@ -1375,6 +1378,10 @@
|
|
|
1375
1378
|
]
|
|
1376
1379
|
},
|
|
1377
1380
|
"props": {
|
|
1381
|
+
"actionItems": {
|
|
1382
|
+
"type": "BarTitleActionConfigItem[]",
|
|
1383
|
+
"required": false
|
|
1384
|
+
},
|
|
1378
1385
|
"actions": {
|
|
1379
1386
|
"type": "BarTitleActionItem[]",
|
|
1380
1387
|
"required": false
|
|
@@ -1789,7 +1796,8 @@
|
|
|
1789
1796
|
"useWhen": [
|
|
1790
1797
|
"The user initiates the single primary command in a local decision area.",
|
|
1791
1798
|
"An action's consequence requires a matching semantic treatment.",
|
|
1792
|
-
"A standalone compact primary action needs an icon-only presentation with an explicit accessible name."
|
|
1799
|
+
"A standalone compact primary action needs an icon-only presentation with an explicit accessible name.",
|
|
1800
|
+
"The primary command has closely related alternatives and needs split mode without losing its filled intent, contrast, variant, or loading treatment."
|
|
1793
1801
|
],
|
|
1794
1802
|
"avoidWhen": [
|
|
1795
1803
|
"Another filled action already exists in the same local decision area.",
|
|
@@ -1826,6 +1834,7 @@
|
|
|
1826
1834
|
],
|
|
1827
1835
|
"accessibility": [
|
|
1828
1836
|
"Provide an explicit accessible name for icon-only buttons.",
|
|
1837
|
+
"In split mode, provide menuAriaLabel for the appended ChevronDown segment, pass controls with the Menu id, and synchronize expanded with the Menu open state.",
|
|
1829
1838
|
"Set hasMenu when the action opens a Menu; it implies aria-haspopup of menu and renders the trailing chevron. Pass controls with the Menu id and expanded from the same open state given to Menu.",
|
|
1830
1839
|
"Use haspopup directly for a non-menu popup such as a dialog; that case gets no chevron.",
|
|
1831
1840
|
"Use inactive state only when the action must remain visible but unavailable.",
|
|
@@ -1849,7 +1858,9 @@
|
|
|
1849
1858
|
"Visible labels are emphasized by default. Set labelEmphasis to false when a composed surface deliberately needs the button label to match adjacent regular-weight control labels; this does not change the action hierarchy, foreground, icon, or interaction states.",
|
|
1850
1859
|
"ButtonFilled has no active, pressed, or selected state; a filled action is a command, not a toggle or a persistent selection.",
|
|
1851
1860
|
"An open popup is a transient pressed state instead. While expanded is true the pressed wash is held for the popup's complete rendered lifecycle and survives hover, so the trigger reads as held open without introducing a selected treatment.",
|
|
1852
|
-
"hasMenu supports only the label and icon-label variants. The chevron is what communicates that a menu will open, so an icon-only filled menu button would leave the affordance unstated."
|
|
1861
|
+
"hasMenu supports only the label and icon-label variants. The chevron is what communicates that a menu will open, so an icon-only filled menu button would leave the affordance unstated.",
|
|
1862
|
+
"Split mode supports label, icon, and icon-label primary segments. The primary segment emits dsClick and the appended menu segment emits dsMenuClick.",
|
|
1863
|
+
"Split mode keeps both segments flush and uses a tertiary on-background separator. Rounded changes the shared outer radius without separating the segments."
|
|
1853
1864
|
],
|
|
1854
1865
|
"responsiveBehavior": [
|
|
1855
1866
|
"Use fill width only when the parent layout calls for a full-width action, commonly on narrow form layouts."
|
|
@@ -1937,6 +1948,10 @@
|
|
|
1937
1948
|
"type": "boolean",
|
|
1938
1949
|
"required": false
|
|
1939
1950
|
},
|
|
1951
|
+
"menuAriaLabel": {
|
|
1952
|
+
"type": "string",
|
|
1953
|
+
"required": false
|
|
1954
|
+
},
|
|
1940
1955
|
"pressScale": {
|
|
1941
1956
|
"type": "boolean",
|
|
1942
1957
|
"required": false
|
|
@@ -1949,6 +1964,10 @@
|
|
|
1949
1964
|
"type": "ButtonFilledSize",
|
|
1950
1965
|
"required": false
|
|
1951
1966
|
},
|
|
1967
|
+
"split": {
|
|
1968
|
+
"type": "boolean",
|
|
1969
|
+
"required": false
|
|
1970
|
+
},
|
|
1952
1971
|
"type": {
|
|
1953
1972
|
"type": "'button' | 'submit' | 'reset'",
|
|
1954
1973
|
"required": false
|
|
@@ -1966,6 +1985,10 @@
|
|
|
1966
1985
|
{
|
|
1967
1986
|
"name": "dsClick",
|
|
1968
1987
|
"detail": "MouseEvent"
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
"name": "dsMenuClick",
|
|
1991
|
+
"detail": "MouseEvent"
|
|
1969
1992
|
}
|
|
1970
1993
|
],
|
|
1971
1994
|
"slots": [],
|
|
@@ -2007,7 +2030,8 @@
|
|
|
2007
2030
|
"A secondary command accompanies the single filled primary action in a local decision area.",
|
|
2008
2031
|
"A toolbar, tool rail, overflow area, or shell region needs a compact icon action.",
|
|
2009
2032
|
"A command represents a persistent toggle or mode whose active state remains meaningful after activation.",
|
|
2010
|
-
"A row, card, or toolbar needs an overflow or more-options menu; this is the only component that carries that role."
|
|
2033
|
+
"A row, card, or toolbar needs an overflow or more-options menu; this is the only component that carries that role.",
|
|
2034
|
+
"A secondary command has closely related alternatives and needs split mode without losing its border, surface, active, variant, or loading treatment."
|
|
2011
2035
|
],
|
|
2012
2036
|
"avoidWhen": [
|
|
2013
2037
|
"The command is the single primary action in a local decision area; use ButtonFilled instead.",
|
|
@@ -2042,6 +2066,7 @@
|
|
|
2042
2066
|
],
|
|
2043
2067
|
"accessibility": [
|
|
2044
2068
|
"Provide an explicit accessible name for every icon-only action.",
|
|
2069
|
+
"In split mode, provide menuAriaLabel for the appended ChevronDown segment, pass controls with the Menu id, and synchronize expanded with the Menu open state.",
|
|
2045
2070
|
"Set hasMenu when the button triggers a Menu; it implies aria-haspopup of menu. Pass controls with the Menu id and expanded from the same open state given to Menu. Expanded popup triggers hold only the pressed wash while open, without changing their resting foreground; expanded disclosure controls without popup semantics retain the normal resting appearance.",
|
|
2046
2071
|
"Give an icon-only menu button an accessible name that states the role, such as More options or Customize table, because no visible label or chevron carries it.",
|
|
2047
2072
|
"Use haspopup directly for a non-menu popup such as a listbox or dialog; those cases get no chevron.",
|
|
@@ -2067,7 +2092,9 @@
|
|
|
2067
2092
|
"Icon and icon-label loading replace the icon in place. Label-only loading centers the loader while preserving the button's measured width.",
|
|
2068
2093
|
"hasMenu covers two distinct menu-button shapes and the variant selects between them. With label or icon-label the action has a menu, and a trailing chevron carries the affordance. With icon the button is a menu and no chevron is added.",
|
|
2069
2094
|
"Because the icon-only shape has no chevron, its glyph must convey the menu on its own. Use Ellipses for a generic more-options or overflow control. Use a specific icon when the menu has a named purpose, such as Table for Customize table.",
|
|
2070
|
-
"An open popup is a transient pressed state rather than a selected one. While expanded is true and haspopup or hasMenu identifies a popup, the pressed wash is held for the popup's complete rendered lifecycle and survives hover without promoting foreground or selected fill, including when chrome opts out of selected fills with activeFill set to false. Expanded disclosure buttons without popup semantics remain visually neutral."
|
|
2095
|
+
"An open popup is a transient pressed state rather than a selected one. While expanded is true and haspopup or hasMenu identifies a popup, the pressed wash is held for the popup's complete rendered lifecycle and survives hover without promoting foreground or selected fill, including when chrome opts out of selected fills with activeFill set to false. Expanded disclosure buttons without popup semantics remain visually neutral.",
|
|
2096
|
+
"Split mode supports label, icon, and icon-label primary segments. The primary segment emits dsClick and the appended menu segment emits dsMenuClick.",
|
|
2097
|
+
"Split mode keeps both segments flush and uses a tertiary surface-aware separator. Bordered mode also preserves the shared outer border; borderless mode limits the separator to the content line height."
|
|
2071
2098
|
],
|
|
2072
2099
|
"responsiveBehavior": [
|
|
2073
2100
|
"The consuming layout decides when an icon-label action becomes icon-only and must provide an explicit accessible name after changing variants. Table caption Customize is one such layout: below 900px it uses the icon shape while keeping the same neutral resting foreground regardless of the controlled column configuration.",
|
|
@@ -2170,6 +2197,10 @@
|
|
|
2170
2197
|
"type": "boolean",
|
|
2171
2198
|
"required": false
|
|
2172
2199
|
},
|
|
2200
|
+
"menuAriaLabel": {
|
|
2201
|
+
"type": "string",
|
|
2202
|
+
"required": false
|
|
2203
|
+
},
|
|
2173
2204
|
"pressScale": {
|
|
2174
2205
|
"type": "boolean",
|
|
2175
2206
|
"required": false
|
|
@@ -2186,6 +2217,10 @@
|
|
|
2186
2217
|
"type": "ButtonUnfilledSize",
|
|
2187
2218
|
"required": false
|
|
2188
2219
|
},
|
|
2220
|
+
"split": {
|
|
2221
|
+
"type": "boolean",
|
|
2222
|
+
"required": false
|
|
2223
|
+
},
|
|
2189
2224
|
"type": {
|
|
2190
2225
|
"type": "'button' | 'submit' | 'reset'",
|
|
2191
2226
|
"required": false
|
|
@@ -2207,6 +2242,10 @@
|
|
|
2207
2242
|
{
|
|
2208
2243
|
"name": "dsClick",
|
|
2209
2244
|
"detail": "MouseEvent"
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
"name": "dsMenuClick",
|
|
2248
|
+
"detail": "MouseEvent"
|
|
2210
2249
|
}
|
|
2211
2250
|
],
|
|
2212
2251
|
"slots": [],
|
|
@@ -7899,6 +7938,7 @@
|
|
|
7899
7938
|
"Dashboard represents day-to-day operational work while Settings represents administrative and configuration work; route state selects the context.",
|
|
7900
7939
|
"Replace navigation and pageChrome together from one completed route state.",
|
|
7901
7940
|
"When pageChrome.showBack marks a detail depth, mobile chrome keeps the page heading as its identity and presents only local subsections; peer route tabs remain in desktop BarNav rather than replacing the detail heading.",
|
|
7941
|
+
"Route-owned ordered page actions drive both BarTitle and MobileHeader. ShellApp forwards one flat page-action id and owns the derived mobile overflow menu so routes do not duplicate menu state or choices.",
|
|
7902
7942
|
"Managed ShellApp keeps the selected desktop section-navigation presentation and ShellPage present and owns page header stickiness and the content scroller.",
|
|
7903
7943
|
"Route-owned pageChrome independently selects ShellPage contentInset, page-header seams, scroll compaction, expanded and compact header-divider visibility, and contentSurface. ShellApp forwards them while resolving whether desktop title geometry is page-local or shell-bar-owned.",
|
|
7904
7944
|
"The application filters unauthorized destinations, sections, and tools before passing them into shell components."
|
|
@@ -7923,6 +7963,7 @@
|
|
|
7923
7963
|
"The routed content cell and tools cell expose separate anchored-overlay collision boundaries, so top-layer menus and tooltips remain above local clipping without crossing into neighboring shell lanes. Full-screen modal overlays remain viewport-owned.",
|
|
7924
7964
|
"Paper texture chrome is desktop/tablet-only and follows the fixed viewport phase of the shell chrome; mobile remains a solid primary stage.",
|
|
7925
7965
|
"Managed page-header capacity follows only the fixed shell breakpoint: roomy on desktop and compact on tablet. Opening PanelTools does not compact or constrain BarTitle.",
|
|
7966
|
+
"Mobile keeps at most the first eligible ordered icon command visible and moves labeled buttons, menu choices, split actions, and explicit overflow commands into one shell-owned text-only page-actions menu.",
|
|
7926
7967
|
"The bottom bar remains visible at all route and tool depths in the foundation release."
|
|
7927
7968
|
],
|
|
7928
7969
|
"frameworkCaveats": {
|
|
@@ -8089,7 +8130,9 @@
|
|
|
8089
8130
|
"registryDependencies": [
|
|
8090
8131
|
"bar-nav",
|
|
8091
8132
|
"bar-title",
|
|
8133
|
+
"button-filled",
|
|
8092
8134
|
"button-unfilled",
|
|
8135
|
+
"menu",
|
|
8093
8136
|
"mobile-bar-nav",
|
|
8094
8137
|
"mobile-header",
|
|
8095
8138
|
"mobile-sheet-nav",
|