@ds-mo/ui 13.11.0 → 13.12.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 +6 -3
- package/dist/agent.json +306 -16
- package/dist/angular/ds-mobile-header.d.ts +1 -1
- package/dist/angular/ds-mobile-header.js +3 -3
- package/dist/angular/ds-shell-page.d.ts +1 -1
- package/dist/angular/ds-shell-page.js +3 -3
- package/dist/angular/ds-tab-group.d.ts +1 -1
- package/dist/angular/ds-tab-group.js +3 -3
- package/dist/angular/ds-table.d.ts +28 -0
- package/dist/angular/ds-table.js +54 -0
- package/dist/angular/ds-tag.d.ts +1 -1
- package/dist/angular/ds-tag.js +3 -3
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +1 -0
- package/dist/angular/proxies.d.ts +1 -0
- package/dist/angular/proxies.js +1 -0
- 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-bar-workflow.js.map +1 -1
- package/dist/components/ds-card-overview.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-mobile-header.js +1 -1
- package/dist/components/ds-mobile-section-switcher.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-panel-sub-nav.js +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-scroll-overlay.js +1 -1
- package/dist/components/ds-scroll-overlay.js.map +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-page.js +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-table.d.ts +11 -0
- package/dist/components/ds-table.js +2 -0
- package/dist/components/ds-table.js.map +1 -0
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-tag.js.map +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/index.d.ts +30 -0
- package/dist/components/p-BFXN4ioL.js +2 -0
- package/dist/components/p-BFXN4ioL.js.map +1 -0
- package/dist/components/{p-DgglLVCj.js → p-BYre-ZDg.js} +2 -2
- package/dist/components/p-B_eqa09C.js +2 -0
- package/dist/components/p-B_eqa09C.js.map +1 -0
- package/dist/components/{p-BnGJrBPG.js → p-Bj7KrXzw.js} +2 -2
- package/dist/components/p-BxJurXEn.js +2 -0
- package/dist/components/p-BxJurXEn.js.map +1 -0
- package/dist/components/{p-Dg9qiMDa.js → p-C9StMCWg.js} +2 -2
- package/dist/components/p-CE82aQZr.js +2 -0
- package/dist/components/p-CE82aQZr.js.map +1 -0
- package/dist/components/p-CFNG0K3m.js +2 -0
- package/dist/components/p-CFNG0K3m.js.map +1 -0
- package/dist/components/{p-BQ7Nzx9A.js → p-Ch5yT3UA.js} +2 -2
- package/dist/components/{p-DJGdoPMG.js → p-DKftz0O8.js} +2 -2
- package/dist/components/p-DxygUurH.js +2 -0
- package/dist/components/p-DxygUurH.js.map +1 -0
- package/dist/components/{p-DCN04sgt.js → p-x9jO4lZ7.js} +2 -2
- package/dist/components/{p-DCN04sgt.js.map → p-x9jO4lZ7.js.map} +1 -1
- package/dist/docs/components.json +1567 -74
- package/dist/lib/components/MobileHeader/mobile-header-types.d.ts +3 -2
- package/dist/lib/components/ShellApp/shell-app-types.d.ts +4 -1
- package/dist/lib/components/ShellPage/shell-page-types.d.ts +5 -0
- package/dist/lib/shell/index.d.ts +1 -0
- package/dist/mcp-data/registry/bar-title.json +2 -1
- package/dist/mcp-data/registry/card-overview.json +1 -1
- package/dist/mcp-data/registry/mobile-header.json +48 -10
- package/dist/mcp-data/registry/mobile-sheet-nav.json +2 -2
- package/dist/mcp-data/registry/registry.json +308 -15
- package/dist/mcp-data/registry/shell-app.json +6 -3
- package/dist/mcp-data/registry/shell-page.json +32 -8
- package/dist/mcp-data/registry/shell-tools.json +4 -4
- package/dist/mcp-data/registry/tab-group.json +22 -2
- package/dist/mcp-data/registry/table.json +758 -0
- package/dist/mcp-data/registry/tag.json +22 -3
- package/dist/react/components.d.ts +1 -0
- package/dist/react/components.js +1 -0
- package/dist/react/ds-table.d.ts +16 -0
- package/dist/react/ds-table.js +18 -0
- package/dist/styles/table.css +677 -0
- package/dist/types/components/MobileHeader/MobileHeader.d.ts +8 -3
- package/dist/types/components/MobileHeader/index.d.ts +1 -1
- package/dist/types/components/MobileHeader/mobile-header-types.d.ts +3 -2
- package/dist/types/components/ShellApp/ShellApp.d.ts +8 -0
- package/dist/types/components/ShellApp/shell-app-types.d.ts +4 -1
- package/dist/types/components/ShellPage/ShellPage.d.ts +3 -1
- package/dist/types/components/ShellPage/index.d.ts +1 -1
- package/dist/types/components/ShellPage/shell-page-types.d.ts +1 -0
- package/dist/types/components/TabGroup/TabGroup.d.ts +3 -0
- package/dist/types/components/Table/Table.d.ts +115 -0
- package/dist/types/components/Table/index.d.ts +2 -0
- package/dist/types/components/Table/table-model.d.ts +29 -0
- package/dist/types/components/Table/table-types.d.ts +178 -0
- package/dist/types/components/Tag/Tag.d.ts +2 -0
- package/dist/types/components.d.ts +406 -8
- package/dist/types/shell/index.d.ts +1 -0
- package/package.json +3 -2
- package/dist/components/p-B_nHX-BW.js +0 -2
- package/dist/components/p-B_nHX-BW.js.map +0 -1
- package/dist/components/p-CVmtbj-6.js +0 -2
- package/dist/components/p-CVmtbj-6.js.map +0 -1
- package/dist/components/p-DW9p068S.js +0 -2
- package/dist/components/p-DW9p068S.js.map +0 -1
- package/dist/components/p-Xftomzsv.js +0 -2
- package/dist/components/p-Xftomzsv.js.map +0 -1
- package/dist/components/p-iyiOFsws.js +0 -2
- package/dist/components/p-iyiOFsws.js.map +0 -1
- /package/dist/components/{p-DgglLVCj.js.map → p-BYre-ZDg.js.map} +0 -0
- /package/dist/components/{p-BnGJrBPG.js.map → p-Bj7KrXzw.js.map} +0 -0
- /package/dist/components/{p-Dg9qiMDa.js.map → p-C9StMCWg.js.map} +0 -0
- /package/dist/components/{p-BQ7Nzx9A.js.map → p-Ch5yT3UA.js.map} +0 -0
- /package/dist/components/{p-DJGdoPMG.js.map → p-DKftz0O8.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TabGroupItem } from '../TabGroup/tab-item-utils';
|
|
2
2
|
export type MobileHeaderTone = 'default' | 'brand';
|
|
3
3
|
export type MobileHeaderHeadingLevel = 'h1' | 'h2';
|
|
4
|
-
export type
|
|
4
|
+
export type MobileHeaderSectionsPresentation = 'switcher' | 'segmented';
|
|
5
|
+
export type MobileHeaderSectionItem = TabGroupItem;
|
|
@@ -5,6 +5,7 @@ import type { MobileHeaderTone } from '../MobileHeader/mobile-header-types';
|
|
|
5
5
|
import type { PanelNavGroup, PanelNavItem, PanelNavRouterMode } from '../PanelNav/panel-nav-types';
|
|
6
6
|
import type { PanelToolsHeaders, PanelToolsHeaderAction, PanelToolsItem } from '../PanelTools/panel-tools-types';
|
|
7
7
|
import type { NavChromeStyle } from '../../shell/nav-chrome';
|
|
8
|
+
import type { ShellPageContentInset, ShellPageContentSurface } from '../ShellPage/shell-page-types';
|
|
8
9
|
export type ShellAppComposition = 'managed' | 'slotted';
|
|
9
10
|
/**
|
|
10
11
|
* Router-owned application navigation supplied to managed ShellApp.
|
|
@@ -67,7 +68,9 @@ export interface ShellPageChromeConfig {
|
|
|
67
68
|
mobileActions?: PanelToolsHeaderAction[];
|
|
68
69
|
actionsAriaLabel?: string;
|
|
69
70
|
tone?: MobileHeaderTone;
|
|
70
|
-
contentInset?:
|
|
71
|
+
contentInset?: ShellPageContentInset;
|
|
72
|
+
/** Canvas surface painted by ShellPage around and beneath routed content. */
|
|
73
|
+
contentSurface?: ShellPageContentSurface;
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
76
|
* Product-owned global tools supplied to managed ShellApp. Tool view content
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BarTitleVariant } from '../BarTitle/bar-title-types';
|
|
2
|
+
export type ShellPageHeaderPresentation = 'auto' | BarTitleVariant;
|
|
3
|
+
export type ShellPageContentInset = 'default' | 'none';
|
|
4
|
+
export type ShellPageContentSurface = 'primary' | 'secondary';
|
|
5
|
+
export type ShellPageCapacity = 'roomy' | 'compact' | 'constrained';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { NavChromeStyle } from './nav-chrome';
|
|
2
2
|
export type { PanelNavGroup, PanelNavItem, } from '../components/PanelNav/panel-nav-types';
|
|
3
3
|
export type { BarNavTab } from '../components/BarNav/bar-nav-types';
|
|
4
|
+
export type { ShellPageContentInset, ShellPageContentSurface, } from '../components/ShellPage/shell-page-types';
|
|
4
5
|
export type { ShellAppComposition, ShellNavigationConfig, ShellNavigationSelectDetail, ShellPageChromeConfig, ShellToolsConfig, } from '../components/ShellApp/shell-app-types';
|
|
5
6
|
export type { PanelToolsHeaderAction, PanelToolsHeaderConfig, PanelToolsHeaders, PanelToolsItem, PanelToolsToolId, } from '../components/PanelTools/panel-tools-types';
|
|
6
7
|
export { PANEL_TOOLS_DEFAULT_ITEMS } from '../components/PanelTools/panel-tools-types';
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"responsiveBehavior": [
|
|
75
75
|
"BarTitle does not observe viewport, shell, scroll, or container state. Its owner selects an explicit variant.",
|
|
76
76
|
"ShellPage is the standard owner for automatic expanded, compact, and constrained selection.",
|
|
77
|
+
"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.",
|
|
77
78
|
"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.",
|
|
78
79
|
"A never-collapse primary action remains visible in constrained mode; the title and section label yield space first."
|
|
79
80
|
],
|
|
@@ -473,7 +474,7 @@
|
|
|
473
474
|
"files": [
|
|
474
475
|
{
|
|
475
476
|
"path": "src/wc/components/BarTitle/BarTitle.css",
|
|
476
|
-
"content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-primary);\n}\n\n:host([data-shell-page-syncing]) {\n visibility: hidden;\n}\n\n.bar-title {\n position: relative;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-primary);\n}\n\n.bar-title::after {\n content: '';\n position: absolute;\n inset: auto var(--ds-bar-title-divider-inset, var(--dimension-space-400)) 0;\n height: var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n pointer-events: none;\n}\n\n.bar-title__inner {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-400);\n box-sizing: border-box;\n}\n\n.bar-title__row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--dimension-space-100);\n min-height: var(--dimension-size-400);\n min-width: 0;\n}\n\n:host(.bar-title-host--expanded) .bar-title__row {\n gap: var(--dimension-space-400);\n}\n\n.bar-title__leading {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--dimension-space-100);\n min-width: 0;\n}\n\n.bar-title__title-row {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n min-width: 0;\n min-height: var(--dimension-size-400);\n}\n\n.bar-title__identity {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.bar-title__back,\n.bar-title__section-chevron {\n flex: 0 0 auto;\n}\n\n.bar-title__breadcrumb {\n display: block;\n width: 100%;\n min-width: 0;\n}\n\n.bar-title__heading {\n display: block;\n width: auto;\n min-width: 0;\n user-select: none;\n}\n\n.bar-title__divider {\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-250);\n margin-inline-start: var(--dimension-space-100);\n flex: 0 0 var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n}\n\n.bar-title__section-selector {\n display: flex;\n flex: 0 1 auto;\n min-width: 0;\n max-width: var(--dimension-panel-width-sm);\n}\n\n.bar-title__section-trigger {\n --ds-control-radius: var(--dimension-radius-half);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: 0;\n max-width: 100%;\n padding-inline: var(--ds-control-padding-inline);\n border: none;\n border-radius: var(--ds-control-radius);\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-secondary);\n cursor: default;\n font-family: inherit;\n user-select: none;\n}\n\n.bar-title__section-trigger--expanded {\n color: var(--color-foreground-primary);\n}\n\n.bar-title__section-trigger--expanded::after,\n.bar-title__section-trigger--expanded:hover::after {\n background: var(--ds-interaction-pressed);\n}\n\n.bar-title__section-label {\n min-width: 0;\n padding-inline: var(--ds-control-label-inset);\n}\n\n.bar-title__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n flex: 0 0 auto;\n}\n\n.bar-title__description {\n display: block;\n min-width: 0;\n max-width: 100%;\n}\n\n:host(.bar-title-host--compact) .bar-title {\n height: var(--dimension-size-600);\n}\n\n:host(.bar-title-host--compact) .bar-title::after {\n inset-inline: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__inner {\n flex: 1 1 auto;\n height: 100%;\n min-width: 0;\n padding: 0;\n gap: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__row {\n height: 100%;\n align-items: center;\n}\n\n:host(.bar-title-host--compact) .bar-title__identity {\n height: var(--dimension-size-400);\n}\n\n:host(.bar-title-host--compact) .bar-title__divider {\n margin-inline-start: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n}\n",
|
|
477
|
+
"content": "@import '../../utils/control-density.css';\n@import '../../utils/chrome-header.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n:host([data-shell-page-syncing]) {\n visibility: hidden;\n}\n\n.bar-title {\n position: relative;\n min-width: 0;\n box-sizing: border-box;\n background: var(--_shell-page-header-background, var(--color-background-primary));\n}\n\n.bar-title::after {\n content: '';\n position: absolute;\n inset: auto var(--ds-bar-title-divider-inset, var(--dimension-space-400)) 0;\n height: var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n pointer-events: none;\n}\n\n.bar-title__inner {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--dimension-space-100);\n padding: var(--dimension-space-400);\n box-sizing: border-box;\n}\n\n.bar-title__row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--dimension-space-100);\n min-height: var(--dimension-size-400);\n min-width: 0;\n}\n\n:host(.bar-title-host--expanded) .bar-title__row {\n gap: var(--dimension-space-400);\n}\n\n.bar-title__leading {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--dimension-space-100);\n min-width: 0;\n}\n\n.bar-title__title-row {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n min-width: 0;\n min-height: var(--dimension-size-400);\n}\n\n.bar-title__identity {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.bar-title__back,\n.bar-title__section-chevron {\n flex: 0 0 auto;\n}\n\n.bar-title__breadcrumb {\n display: block;\n width: 100%;\n min-width: 0;\n}\n\n.bar-title__heading {\n display: block;\n width: auto;\n min-width: 0;\n user-select: none;\n}\n\n.bar-title__divider {\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-size-250);\n margin-inline-start: var(--dimension-space-100);\n flex: 0 0 var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n}\n\n.bar-title__section-selector {\n display: flex;\n flex: 0 1 auto;\n min-width: 0;\n max-width: var(--dimension-panel-width-sm);\n}\n\n.bar-title__section-trigger {\n --ds-control-radius: var(--dimension-radius-half);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--ds-control-gap);\n height: var(--ds-control-height);\n min-width: 0;\n max-width: 100%;\n padding-inline: var(--ds-control-padding-inline);\n border: none;\n border-radius: var(--ds-control-radius);\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-secondary);\n cursor: default;\n font-family: inherit;\n user-select: none;\n}\n\n.bar-title__section-trigger--expanded {\n color: var(--color-foreground-primary);\n}\n\n.bar-title__section-trigger--expanded::after,\n.bar-title__section-trigger--expanded:hover::after {\n background: var(--ds-interaction-pressed);\n}\n\n.bar-title__section-label {\n min-width: 0;\n padding-inline: var(--ds-control-label-inset);\n}\n\n.bar-title__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-100);\n flex: 0 0 auto;\n}\n\n.bar-title__description {\n display: block;\n min-width: 0;\n max-width: 100%;\n}\n\n:host(.bar-title-host--compact) .bar-title {\n height: var(--dimension-size-600);\n}\n\n:host(.bar-title-host--compact) .bar-title::after {\n inset-inline: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__inner {\n flex: 1 1 auto;\n height: 100%;\n min-width: 0;\n padding: 0;\n gap: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__row {\n height: 100%;\n align-items: center;\n}\n\n:host(.bar-title-host--compact) .bar-title__identity {\n height: var(--dimension-size-400);\n}\n\n:host(.bar-title-host--compact) .bar-title__divider {\n margin-inline-start: 0;\n}\n\n:host(.bar-title-host--compact) .bar-title__heading {\n display: inline-flex;\n flex: 1 1 auto;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: calc(var(--ds-control-padding-inline) + var(--ds-control-label-inset));\n box-sizing: border-box;\n}\n",
|
|
477
478
|
"type": "registry:ui"
|
|
478
479
|
},
|
|
479
480
|
{
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"Omit score to render a centered Score unavailable cell with a tertiary border and no score fill. Set scoreErrorMessage to replace that copy while the measures remain readable.",
|
|
58
58
|
"Loading replaces the score and measures with skeletons and does not alter the slotted period control. When measures are already supplied, their count preserves the resolved grid shape; otherwise five measure placeholders join the score in the standard six-cell grid.",
|
|
59
59
|
"At most seven measures are shown. Keep larger collections outside the summary bar rather than turning it into a dense dashboard.",
|
|
60
|
-
"The period and filter controls are slotted rather than owned. Use the period slot when the current range is selectable; otherwise periodLabel supplies fixed current copy in the same md control-frame and label-inset anatomy. Use the filter slot for the application-owned comparison-window control. Slotted Selects use hasBorder false so the 48px bar owns their surrounding boundary.",
|
|
60
|
+
"The period and filter controls are slotted rather than owned. Use the period slot when the current range is selectable; otherwise periodLabel supplies fixed current copy in the same md control-frame and label-inset anatomy. Use the filter slot for the application-owned comparison-window control. Slotted Selects use hasBorder false so the 48px bar owns their surrounding boundary and allowClear false because both controls always require one selected reporting period.",
|
|
61
61
|
"A numeric safety score infers fair from 0–50, good from 51–80, and excellent from 81–100. The level can be supplied explicitly when the displayed value is not numeric. The deprecated band field is ignored.",
|
|
62
62
|
"The safety score is the first grid cell and uses the same track, 8px outer inset, 6px inner padding, dividers, and responsive behavior as metric cells. The display value removes 8px from both block edges of its line box so it does not increase the shared row height. Score remains nonselectable and excluded from metric roving focus.",
|
|
63
63
|
"During page-controlled scroll collapse, the component preserves its expanded flow height and stationary reporting bar while the metric grid moves and clips beneath it. Elevation remains fitted to the visible surface.",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"commonlyComposedWith": [
|
|
25
25
|
"component:ds-mobile-section-switcher",
|
|
26
|
+
"component:ds-tab-group",
|
|
26
27
|
"component:ds-shell-page",
|
|
27
28
|
"component:ds-shell-tools"
|
|
28
29
|
],
|
|
@@ -31,19 +32,19 @@
|
|
|
31
32
|
],
|
|
32
33
|
"accessibility": [
|
|
33
34
|
"Static screens render a real h1 or h2 according to headingLevel.",
|
|
34
|
-
"Section-switching screens expose an equivalent semantic heading alongside the accessible popup trigger.",
|
|
35
|
+
"Section-switching screens expose an equivalent semantic heading alongside the accessible popup trigger or named segmented TabGroup.",
|
|
35
36
|
"Subsections use a separately named navigation landmark and never replace the semantic page identity.",
|
|
36
37
|
"Leading and trailing slotted icon actions retain their own accessible names and tooltips."
|
|
37
38
|
],
|
|
38
39
|
"states": [
|
|
39
40
|
"Without multiple sections the center renders heading.",
|
|
40
|
-
"With multiple sections the center renders the controlled MobileSectionSwitcher.",
|
|
41
|
+
"With multiple sections the center renders the controlled MobileSectionSwitcher by default or an explicitly requested segmented TabGroup.",
|
|
41
42
|
"Optional subsections render in a second row and emit dsSubsectionChange without changing the primary section.",
|
|
42
43
|
"Sections represent peer destinations at the current navigation level; subsections represent local views beneath the selected page or detail identity.",
|
|
43
44
|
"Brand tone changes surface and foreground without changing geometry or interaction ownership."
|
|
44
45
|
],
|
|
45
46
|
"responsiveBehavior": [
|
|
46
|
-
"The primary
|
|
47
|
+
"The primary 56px lane matches the Mobile Sheet Nav header and Mobile Bar Nav; optional subsections add one subordinate 48px lane.",
|
|
47
48
|
"Equal flexible side lanes keep the center mathematically aligned when actions are asymmetric."
|
|
48
49
|
],
|
|
49
50
|
"references": [
|
|
@@ -74,8 +75,8 @@
|
|
|
74
75
|
"description": "Semantic heading level for the active mobile screen."
|
|
75
76
|
},
|
|
76
77
|
"sections": {
|
|
77
|
-
"type": "
|
|
78
|
-
"resolvedType": "
|
|
78
|
+
"type": "TabGroupItem[]",
|
|
79
|
+
"resolvedType": "TabGroupItem[]",
|
|
79
80
|
"default": "[]",
|
|
80
81
|
"required": false,
|
|
81
82
|
"mutable": false,
|
|
@@ -90,6 +91,24 @@
|
|
|
90
91
|
"mutable": false,
|
|
91
92
|
"description": "Accessible name for the section chooser."
|
|
92
93
|
},
|
|
94
|
+
"sectionsPresentation": {
|
|
95
|
+
"type": "MobileHeaderSectionsPresentation",
|
|
96
|
+
"resolvedType": "\"segmented\" | \"switcher\"",
|
|
97
|
+
"attribute": "sections-presentation",
|
|
98
|
+
"default": "'switcher'",
|
|
99
|
+
"required": false,
|
|
100
|
+
"mutable": false,
|
|
101
|
+
"description": "Popup switcher or an inline segmented TabGroup for peer sections."
|
|
102
|
+
},
|
|
103
|
+
"sectionsSize": {
|
|
104
|
+
"type": "TabGroupSize",
|
|
105
|
+
"resolvedType": "\"lg\" | \"md\" | \"sm\"",
|
|
106
|
+
"attribute": "sections-size",
|
|
107
|
+
"default": "'md'",
|
|
108
|
+
"required": false,
|
|
109
|
+
"mutable": false,
|
|
110
|
+
"description": "Density for the segmented sections presentation."
|
|
111
|
+
},
|
|
93
112
|
"subsections": {
|
|
94
113
|
"type": "TabItem[]",
|
|
95
114
|
"resolvedType": "TabItem[]",
|
|
@@ -176,8 +195,8 @@
|
|
|
176
195
|
"description": "Semantic heading level for the active mobile screen."
|
|
177
196
|
},
|
|
178
197
|
"sections": {
|
|
179
|
-
"type": "
|
|
180
|
-
"resolvedType": "
|
|
198
|
+
"type": "TabGroupItem[]",
|
|
199
|
+
"resolvedType": "TabGroupItem[]",
|
|
181
200
|
"default": "[]",
|
|
182
201
|
"required": false,
|
|
183
202
|
"mutable": false,
|
|
@@ -192,6 +211,24 @@
|
|
|
192
211
|
"mutable": false,
|
|
193
212
|
"description": "Accessible name for the section chooser."
|
|
194
213
|
},
|
|
214
|
+
"sectionsPresentation": {
|
|
215
|
+
"type": "MobileHeaderSectionsPresentation",
|
|
216
|
+
"resolvedType": "\"segmented\" | \"switcher\"",
|
|
217
|
+
"attribute": "sections-presentation",
|
|
218
|
+
"default": "'switcher'",
|
|
219
|
+
"required": false,
|
|
220
|
+
"mutable": false,
|
|
221
|
+
"description": "Popup switcher or an inline segmented TabGroup for peer sections."
|
|
222
|
+
},
|
|
223
|
+
"sectionsSize": {
|
|
224
|
+
"type": "TabGroupSize",
|
|
225
|
+
"resolvedType": "\"lg\" | \"md\" | \"sm\"",
|
|
226
|
+
"attribute": "sections-size",
|
|
227
|
+
"default": "'md'",
|
|
228
|
+
"required": false,
|
|
229
|
+
"mutable": false,
|
|
230
|
+
"description": "Density for the segmented sections presentation."
|
|
231
|
+
},
|
|
195
232
|
"subsections": {
|
|
196
233
|
"type": "TabItem[]",
|
|
197
234
|
"resolvedType": "TabItem[]",
|
|
@@ -293,22 +330,23 @@
|
|
|
293
330
|
],
|
|
294
331
|
"registryDependencies": [
|
|
295
332
|
"mobile-section-switcher",
|
|
333
|
+
"tab-group",
|
|
296
334
|
"text"
|
|
297
335
|
],
|
|
298
336
|
"files": [
|
|
299
337
|
{
|
|
300
338
|
"path": "src/wc/components/MobileHeader/MobileHeader.css",
|
|
301
|
-
"content": "@import '../../utils/chrome-layout.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-primary);\n color: var(--color-foreground-primary);\n}\n\n:host([tone='brand']) {\n background: var(--color-background-bold-brand);\n color: var(--color-foreground-on-bold);\n}\n\n.mobile-header {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n background: inherit;\n color: inherit;\n}\n\n.mobile-header__primary {\n grid-template-columns: minmax(var(--dimension-size-400), 1fr) minmax(0, auto) minmax(var(--dimension-size-400), 1fr);\n width: 100%;\n height: var(--dimension-size-
|
|
339
|
+
"content": "@import '../../utils/chrome-layout.css';\n\n:host {\n display: block;\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-primary);\n color: var(--color-foreground-primary);\n}\n\n:host([tone='brand']) {\n background: var(--color-background-bold-brand);\n color: var(--color-foreground-on-bold);\n}\n\n.mobile-header {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n background: inherit;\n color: inherit;\n}\n\n.mobile-header__primary {\n grid-template-columns: minmax(var(--dimension-size-400), 1fr) minmax(0, auto) minmax(var(--dimension-size-400), 1fr);\n width: 100%;\n height: calc(var(--dimension-size-500) + var(--dimension-space-200));\n min-width: 0;\n background: inherit;\n color: inherit;\n}\n\n:host(:not([tone='brand'])) .mobile-header::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: 0;\n height: var(--dimension-stroke-width-012);\n background: var(--color-border-tertiary);\n pointer-events: none;\n}\n\n.mobile-header__subsections {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100) var(--dimension-space-200);\n box-sizing: border-box;\n}\n\n.mobile-header__subsections ds-mobile-section-switcher {\n min-width: 0;\n max-width: 100%;\n}\n\n.mobile-header__lane {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n min-width: 0;\n}\n\n.mobile-header__lane--leading {\n justify-content: flex-start;\n}\n\n.mobile-header__lane--trailing {\n justify-content: flex-end;\n}\n\n.mobile-header__center {\n position: relative;\n min-width: 0;\n max-width: 100%;\n justify-self: center;\n}\n\n.mobile-header__heading {\n display: block;\n min-width: 0;\n max-width: 100%;\n text-align: center;\n}\n\n.mobile-header__semantic-heading {\n position: absolute;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-stroke-width-012);\n padding: 0;\n margin: calc(var(--dimension-stroke-width-012) * -1);\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n::slotted([slot='leading']),\n::slotted([slot='trailing']) {\n flex: 0 0 auto;\n}\n",
|
|
302
340
|
"type": "registry:ui"
|
|
303
341
|
},
|
|
304
342
|
{
|
|
305
343
|
"path": "src/wc/components/MobileHeader/MobileHeader.tsx",
|
|
306
|
-
"content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\nimport {
|
|
344
|
+
"content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\nimport {\n getSelectableTabs,\n type TabGroupItem,\n type TabItem,\n} from '../TabGroup/tab-item-utils';\nimport type { TabGroupSize } from '../TabGroup/TabGroup';\nimport type {\n MobileHeaderHeadingLevel,\n MobileHeaderSectionsPresentation,\n MobileHeaderTone,\n} from './mobile-header-types';\n\n@Component({\n tag: 'ds-mobile-header',\n styleUrl: 'MobileHeader.css',\n scoped: true,\n})\nexport class MobileHeader {\n /** Static title used when no peer sections are supplied. */\n @Prop() heading: string = '';\n /** Semantic heading level for the active mobile screen. */\n @Prop() headingLevel: MobileHeaderHeadingLevel = 'h1';\n /** Controlled peer sections. Their selected label replaces the static title. */\n @Prop() sections: TabGroupItem[] = [];\n /** Controlled selected section id. */\n @Prop() value: string = '';\n /** Accessible name for the section chooser. */\n @Prop() sectionsAriaLabel: string = 'Change page section';\n /** Popup switcher or an inline segmented TabGroup for peer sections. */\n @Prop() sectionsPresentation: MobileHeaderSectionsPresentation = 'switcher';\n /** Density for the segmented sections presentation. */\n @Prop() sectionsSize: TabGroupSize = 'md';\n /** Controlled child sections within the selected page or detail screen. */\n @Prop() subsections: TabItem[] = [];\n /** Controlled selected child-section id. */\n @Prop() subvalue: string = '';\n /** Accessible name for the child-section chooser. */\n @Prop() subsectionsAriaLabel: string = 'Change page subsection';\n /** Default page chrome or bold-brand workflow chrome. */\n @Prop({ reflect: true }) tone: MobileHeaderTone = 'default';\n\n /** Section selection intent. */\n @Event() dsSectionChange!: EventEmitter<string>;\n /** Child-section selection intent. */\n @Event() dsSubsectionChange!: EventEmitter<string>;\n\n private get resolvedSections(): TabGroupItem[] {\n return this.sections ?? [];\n }\n\n private get resolvedSubsections(): TabItem[] {\n return this.subsections ?? [];\n }\n\n private get selectedLabel(): string {\n const selectable = getSelectableTabs(this.resolvedSections);\n return selectable.find(section => section.id === this.value)?.label ??\n selectable[0]?.label ??\n this.heading;\n }\n\n private handleSectionChange = (event: CustomEvent<string>) => {\n event.stopPropagation();\n this.dsSectionChange.emit(event.detail);\n };\n\n private handleSubsectionChange = (event: CustomEvent<string>) => {\n event.stopPropagation();\n this.dsSubsectionChange.emit(event.detail);\n };\n\n render() {\n const Heading = this.headingLevel;\n const selectable = getSelectableTabs(this.resolvedSections);\n const hasSwitcher = selectable.length > 1;\n const hasSubsections = getSelectableTabs(this.resolvedSubsections).length > 1;\n const foreground = this.tone === 'brand' ? 'on-bold' : 'primary';\n\n return (\n <Host>\n <header class=\"mobile-header\">\n <div class=\"mobile-header__primary ds-chrome-grid ds-chrome-space--md\">\n <div class=\"mobile-header__lane mobile-header__lane--leading\">\n <slot name=\"leading\" />\n </div>\n <div class=\"mobile-header__center\">\n {hasSwitcher ? (\n [\n <span\n class=\"mobile-header__semantic-heading\"\n role=\"heading\"\n aria-level={this.headingLevel === 'h1' ? '1' : '2'}\n >\n {this.selectedLabel}\n </span>,\n this.sectionsPresentation === 'segmented' ? (\n <ds-tab-group\n tabs={this.resolvedSections}\n value={this.value}\n size={this.sectionsSize}\n ariaLabel={this.sectionsAriaLabel}\n onDsChange={this.handleSectionChange}\n />\n ) : (\n <ds-mobile-section-switcher\n sections={this.resolvedSections}\n value={this.value}\n navigationLabel={this.sectionsAriaLabel}\n onDsChange={this.handleSectionChange}\n />\n ),\n ]\n ) : (\n <ds-text\n class=\"mobile-header__heading\"\n as={Heading}\n variant=\"text-body-medium\"\n emphasis\n color={foreground}\n lineTruncation={1}\n >\n {this.selectedLabel}\n </ds-text>\n )}\n </div>\n <div class=\"mobile-header__lane mobile-header__lane--trailing\">\n <slot name=\"trailing\" />\n </div>\n </div>\n {hasSubsections ? (\n <nav class=\"mobile-header__subsections\" aria-label={this.subsectionsAriaLabel}>\n <ds-mobile-section-switcher\n sections={this.resolvedSubsections}\n value={this.subvalue}\n navigationLabel={this.subsectionsAriaLabel}\n onDsChange={this.handleSubsectionChange}\n />\n </nav>\n ) : null}\n </header>\n </Host>\n );\n }\n}\n",
|
|
307
345
|
"type": "registry:ui"
|
|
308
346
|
},
|
|
309
347
|
{
|
|
310
348
|
"path": "src/wc/components/MobileHeader/mobile-header-types.ts",
|
|
311
|
-
"content": "import type {
|
|
349
|
+
"content": "import type { TabGroupItem } from '../TabGroup/tab-item-utils';\n\nexport type MobileHeaderTone = 'default' | 'brand';\nexport type MobileHeaderHeadingLevel = 'h1' | 'h2';\nexport type MobileHeaderSectionsPresentation = 'switcher' | 'segmented';\nexport type MobileHeaderSectionItem = TabGroupItem;\n",
|
|
312
350
|
"type": "registry:ui"
|
|
313
351
|
}
|
|
314
352
|
]
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
],
|
|
40
40
|
"responsiveBehavior": [
|
|
41
41
|
"The pane fills the mobile content stage while the bottom bar remains available.",
|
|
42
|
-
"The sheet header and scrollable navigation body each use the shared 8px chrome inset;
|
|
42
|
+
"The sheet header and scrollable navigation body each use the shared 8px chrome inset; the context TabGroup and primary controls share a 40px height and 24px icon scale.",
|
|
43
43
|
"Dashboard and Settings preserve their supplied visual groups with an 8px gap between destinations inside a section and a 32px gap between sections."
|
|
44
44
|
],
|
|
45
45
|
"references": [
|
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
},
|
|
313
313
|
{
|
|
314
314
|
"path": "src/wc/components/MobileSheetNav/MobileSheetNav.tsx",
|
|
315
|
-
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Listen,\n Prop,\n Watch,\n} from '@stencil/core';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport { deriveActiveIdFromUrl } from '../PanelNav/panel-nav-utils';\nimport type { PanelNavGroup, PanelNavItem } from '../PanelNav/panel-nav-types';\n\ntype MobileSheetNavHeaderDestinationId = 'account' | 'help';\n\nconst CONTEXT_TABS = [\n { id: 'dashboard', label: 'Dashboard', icon: 'Dashboard', variant: 'icon' as const },\n { id: 'settings', label: 'Settings', icon: 'Gear', variant: 'icon' as const },\n];\n\n@Component({\n tag: 'ds-mobile-sheet-nav',\n styleUrl: 'MobileSheetNav.css',\n scoped: true,\n})\nexport class MobileSheetNav {\n @Element() el!: HTMLElement;\n\n @Prop({ reflect: true }) open: boolean = false;\n @Prop() browseContext: NavChromeStyle = 'dashboard';\n @Prop() dashboardGroups: PanelNavGroup[] = [];\n @Prop() settingsGroups: PanelNavGroup[] = [];\n @Prop() currentUrl: string = '';\n @Prop() navigationLabel: string = 'Application navigation';\n @Prop() dashboardLabel: string = 'Dashboard';\n @Prop() settingsLabel: string = 'Settings';\n @Prop() accountLabel: string = 'Account';\n @Prop() helpLabel: string = 'Help & Support';\n\n @Event() dsAreaSelect!: EventEmitter<string>;\n @Event() dsBrowseContextChange!: EventEmitter<NavChromeStyle>;\n @Event() dsClose!: EventEmitter<void>;\n\n private get groups(): PanelNavGroup[] {\n return this.browseContext === 'settings' ? this.settingsGroups : this.dashboardGroups;\n }\n\n private get activeId(): string {\n return deriveActiveIdFromUrl(this.currentUrl, [\n ...this.dashboardGroups.flatMap(group => group.items),\n ...this.settingsGroups.flatMap(group => group.items),\n ]);\n }\n\n @Watch('open')\n handleOpenChange(open: boolean) {\n if (!open) return;\n requestAnimationFrame(() => {\n const selected = this.el.querySelector<HTMLElement>('[aria-current=\"page\"]');\n const first = this.el.querySelector<HTMLElement>('.mobile-sheet-nav__item');\n (selected ?? first)?.focus({ preventScroll: true });\n });\n }\n\n @Listen('keydown')\n handleKeyDown(event: KeyboardEvent) {\n if (!this.open || event.key !== 'Escape') return;\n event.preventDefault();\n this.dsClose.emit();\n }\n\n private handleContextChange = (event: CustomEvent<string>) => {\n const next = event.detail === 'settings' ? 'settings' : 'dashboard';\n if (next === this.browseContext) return;\n this.dsBrowseContextChange.emit(next);\n };\n\n private renderItem(item: PanelNavItem) {\n const selected = item.id === this.activeId;\n return (\n <button\n type=\"button\"\n class={{\n 'mobile-sheet-nav__item': true,\n 'mobile-sheet-nav__item--selected': selected,\n 'ds-control--lg': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n aria-current={selected ? 'page' : undefined}\n onClick={() => this.dsAreaSelect.emit(item.id)}\n >\n <ds-icon\n class=\"ds-interaction-fill__content\"\n name={item.icon}\n size=\"lg\"\n color=\"inherit\"\n />\n <ds-text\n class=\"mobile-sheet-nav__item-label ds-interaction-fill__content\"\n as=\"span\"\n variant=\"text-body-large\"\n emphasis={selected}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>\n {item.dot && (\n <ds-badge\n class=\"mobile-sheet-nav__dot ds-interaction-fill__content\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n }\n\n private renderLogo() {\n return (\n <span class=\"mobile-sheet-nav__logo\" aria-hidden=\"true\">\n {/* eslint-disable-next-line local/prefer-ds-icon -- Motive brand mark is not part of the IcoMo runtime catalog. */}\n <svg\n class=\"mobile-sheet-nav__logo-mark\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n focusable=\"false\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n );\n }\n\n private renderHeaderDestination(\n id: MobileSheetNavHeaderDestinationId,\n icon: string,\n label: string\n ) {\n return (\n <ds-tooltip label={label} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n size=\"lg\"\n icon={icon}\n aria-label={label}\n activeFill={false}\n hasBorder={false}\n onDsClick={() => this.dsAreaSelect.emit(id)}\n />\n </ds-tooltip>\n );\n }\n\n render() {\n const contextTabs = [\n { ...CONTEXT_TABS[0], label: this.dashboardLabel },\n { ...CONTEXT_TABS[1], label: this.settingsLabel },\n ];\n\n return (\n <Host aria-hidden={this.open ? undefined : 'true'} inert={this.open ? undefined : true}>\n <section\n id=\"ds-mobile-sheet-nav\"\n class=\"mobile-sheet-nav\"\n >\n <header class=\"mobile-sheet-nav__header ds-chrome-grid ds-chrome-space--md\">\n <div class=\"mobile-sheet-nav__brand\">{this.renderLogo()}</div>\n <ds-tab-group\n class=\"mobile-sheet-nav__context\"\n tabs={contextTabs}\n value={this.browseContext}\n aria-label=\"Browse context\"\n onDsChange={this.handleContextChange}\n />\n <div class=\"mobile-sheet-nav__actions\">\n {this.renderHeaderDestination('help', 'CircleQuestion', this.helpLabel)}\n {this.renderHeaderDestination('account', 'Avatar', this.accountLabel)}\n </div>\n </header>\n\n <nav\n class=\"mobile-sheet-nav__body ds-chrome-column ds-chrome-space--md\"\n aria-label={this.navigationLabel}\n >\n <div class=\"mobile-sheet-nav__sections\">\n {this.groups\n .filter(group => group.items.length > 0)\n .map(group => (\n <div class=\"mobile-sheet-nav__items\">\n {group.items.map(item => this.renderItem(item))}\n </div>\n ))}\n </div>\n </nav>\n </section>\n </Host>\n );\n }\n}\n",
|
|
315
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Listen,\n Prop,\n Watch,\n} from '@stencil/core';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport { deriveActiveIdFromUrl } from '../PanelNav/panel-nav-utils';\nimport type { PanelNavGroup, PanelNavItem } from '../PanelNav/panel-nav-types';\n\ntype MobileSheetNavHeaderDestinationId = 'account' | 'help';\n\nconst CONTEXT_TABS = [\n { id: 'dashboard', label: 'Dashboard', icon: 'Dashboard', variant: 'icon' as const },\n { id: 'settings', label: 'Settings', icon: 'Gear', variant: 'icon' as const },\n];\n\n@Component({\n tag: 'ds-mobile-sheet-nav',\n styleUrl: 'MobileSheetNav.css',\n scoped: true,\n})\nexport class MobileSheetNav {\n @Element() el!: HTMLElement;\n\n @Prop({ reflect: true }) open: boolean = false;\n @Prop() browseContext: NavChromeStyle = 'dashboard';\n @Prop() dashboardGroups: PanelNavGroup[] = [];\n @Prop() settingsGroups: PanelNavGroup[] = [];\n @Prop() currentUrl: string = '';\n @Prop() navigationLabel: string = 'Application navigation';\n @Prop() dashboardLabel: string = 'Dashboard';\n @Prop() settingsLabel: string = 'Settings';\n @Prop() accountLabel: string = 'Account';\n @Prop() helpLabel: string = 'Help & Support';\n\n @Event() dsAreaSelect!: EventEmitter<string>;\n @Event() dsBrowseContextChange!: EventEmitter<NavChromeStyle>;\n @Event() dsClose!: EventEmitter<void>;\n\n private get groups(): PanelNavGroup[] {\n return this.browseContext === 'settings' ? this.settingsGroups : this.dashboardGroups;\n }\n\n private get activeId(): string {\n return deriveActiveIdFromUrl(this.currentUrl, [\n ...this.dashboardGroups.flatMap(group => group.items),\n ...this.settingsGroups.flatMap(group => group.items),\n ]);\n }\n\n @Watch('open')\n handleOpenChange(open: boolean) {\n if (!open) return;\n requestAnimationFrame(() => {\n const selected = this.el.querySelector<HTMLElement>('[aria-current=\"page\"]');\n const first = this.el.querySelector<HTMLElement>('.mobile-sheet-nav__item');\n (selected ?? first)?.focus({ preventScroll: true });\n });\n }\n\n @Listen('keydown')\n handleKeyDown(event: KeyboardEvent) {\n if (!this.open || event.key !== 'Escape') return;\n event.preventDefault();\n this.dsClose.emit();\n }\n\n private handleContextChange = (event: CustomEvent<string>) => {\n const next = event.detail === 'settings' ? 'settings' : 'dashboard';\n if (next === this.browseContext) return;\n this.dsBrowseContextChange.emit(next);\n };\n\n private renderItem(item: PanelNavItem) {\n const selected = item.id === this.activeId;\n return (\n <button\n type=\"button\"\n class={{\n 'mobile-sheet-nav__item': true,\n 'mobile-sheet-nav__item--selected': selected,\n 'ds-control--lg': true,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n aria-current={selected ? 'page' : undefined}\n onClick={() => this.dsAreaSelect.emit(item.id)}\n >\n <ds-icon\n class=\"ds-interaction-fill__content\"\n name={item.icon}\n size=\"lg\"\n color=\"inherit\"\n />\n <ds-text\n class=\"mobile-sheet-nav__item-label ds-interaction-fill__content\"\n as=\"span\"\n variant=\"text-body-large\"\n emphasis={selected}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>\n {item.dot && (\n <ds-badge\n class=\"mobile-sheet-nav__dot ds-interaction-fill__content\"\n variant=\"dot\"\n hasRing={false}\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n }\n\n private renderLogo() {\n return (\n <span class=\"mobile-sheet-nav__logo\" aria-hidden=\"true\">\n {/* eslint-disable-next-line local/prefer-ds-icon -- Motive brand mark is not part of the IcoMo runtime catalog. */}\n <svg\n class=\"mobile-sheet-nav__logo-mark\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n focusable=\"false\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n );\n }\n\n private renderHeaderDestination(\n id: MobileSheetNavHeaderDestinationId,\n icon: string,\n label: string\n ) {\n return (\n <ds-tooltip label={label} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n size=\"lg\"\n icon={icon}\n aria-label={label}\n activeFill={false}\n hasBorder={false}\n onDsClick={() => this.dsAreaSelect.emit(id)}\n />\n </ds-tooltip>\n );\n }\n\n render() {\n const contextTabs = [\n { ...CONTEXT_TABS[0], label: this.dashboardLabel },\n { ...CONTEXT_TABS[1], label: this.settingsLabel },\n ];\n\n return (\n <Host aria-hidden={this.open ? undefined : 'true'} inert={this.open ? undefined : true}>\n <section\n id=\"ds-mobile-sheet-nav\"\n class=\"mobile-sheet-nav\"\n >\n <header class=\"mobile-sheet-nav__header ds-chrome-grid ds-chrome-space--md\">\n <div class=\"mobile-sheet-nav__brand\">{this.renderLogo()}</div>\n <ds-tab-group\n class=\"mobile-sheet-nav__context\"\n tabs={contextTabs}\n value={this.browseContext}\n size=\"lg\"\n aria-label=\"Browse context\"\n onDsChange={this.handleContextChange}\n />\n <div class=\"mobile-sheet-nav__actions\">\n {this.renderHeaderDestination('help', 'CircleQuestion', this.helpLabel)}\n {this.renderHeaderDestination('account', 'Avatar', this.accountLabel)}\n </div>\n </header>\n\n <nav\n class=\"mobile-sheet-nav__body ds-chrome-column ds-chrome-space--md\"\n aria-label={this.navigationLabel}\n >\n <div class=\"mobile-sheet-nav__sections\">\n {this.groups\n .filter(group => group.items.length > 0)\n .map(group => (\n <div class=\"mobile-sheet-nav__items\">\n {group.items.map(item => this.renderItem(item))}\n </div>\n ))}\n </div>\n </nav>\n </section>\n </Host>\n );\n }\n}\n",
|
|
316
316
|
"type": "registry:ui"
|
|
317
317
|
}
|
|
318
318
|
]
|