@ds-mo/ui 11.1.0 → 12.1.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/README.md +1 -1
- package/dist/.build-stamp +1 -1
- package/dist/agent-patterns.json +150 -9
- package/dist/agent.json +488 -174
- package/dist/angular/ds-bar-title.d.ts +28 -0
- package/dist/angular/ds-bar-title.js +48 -0
- package/dist/angular/ds-panel-nav.d.ts +1 -1
- package/dist/angular/{ds-app-shell.d.ts → ds-shell-app.d.ts} +5 -5
- package/dist/angular/{ds-app-shell.js → ds-shell-app.js} +10 -10
- package/dist/angular/ds-shell-page.d.ts +12 -0
- package/dist/angular/ds-shell-page.js +38 -0
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +3 -1
- package/dist/angular/proxies.d.ts +3 -1
- package/dist/angular/proxies.js +3 -1
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-response.js.map +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-avatar.js +1 -1
- package/dist/components/ds-avatar.js.map +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/{ds-app-shell.d.ts → ds-bar-title.d.ts} +4 -4
- package/dist/components/ds-bar-title.js +2 -0
- package/dist/components/ds-bar-title.js.map +1 -0
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-data-viz-donut.js +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-shell-data-viz.js +1 -1
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-donut.js +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-line.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-code-block.js +1 -1
- package/dist/components/ds-conversation-list-item.js +1 -1
- package/dist/components/ds-conversation-list-item.js.map +1 -1
- package/dist/components/ds-conversation-list-section.js +1 -1
- package/dist/components/ds-conversation-list.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-composer.js.map +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-message-scroller.js.map +1 -1
- package/dist/components/ds-message.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-modal.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-sub-nav.js +1 -1
- package/dist/components/ds-panel-sub-nav.js.map +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-tool-search.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-select-multi.js +1 -1
- package/dist/components/ds-select-multi.js.map +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +1 -1
- package/dist/components/ds-shell-app.d.ts +11 -0
- package/dist/components/ds-shell-app.js +2 -0
- package/dist/components/ds-shell-app.js.map +1 -0
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js.map +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/ds-shell-page.d.ts +11 -0
- package/dist/components/ds-shell-page.js +2 -0
- package/dist/components/ds-shell-page.js.map +1 -0
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/{p-DJ78wdNv.js → p-B9jvSGid.js} +2 -2
- package/dist/components/{p-DuNZGFkV.js → p-BAfG3M8z.js} +2 -2
- package/dist/components/p-BAfG3M8z.js.map +1 -0
- package/dist/components/{p-Dh4q5TSS.js → p-BfEjWe8U.js} +2 -2
- package/dist/components/{p-CZZYGqKz.js → p-Bs6SS34m.js} +2 -2
- package/dist/components/p-BzsITTZa.js +2 -0
- package/dist/components/p-BzsITTZa.js.map +1 -0
- package/dist/components/p-C3Zfn7hz.js +2 -0
- package/dist/components/p-C3Zfn7hz.js.map +1 -0
- package/dist/components/{p-bNpFhP5L.js → p-CR7yMitN.js} +2 -2
- package/dist/components/p-CTV_QWC5.js +2 -0
- package/dist/components/p-CTV_QWC5.js.map +1 -0
- package/dist/components/p-CUik7PCi.js +2 -0
- package/dist/components/p-CUik7PCi.js.map +1 -0
- package/dist/components/{p-BKhG2xrW.js → p-D2x17ei-.js} +1 -1
- package/dist/components/{p-BKhG2xrW.js.map → p-D2x17ei-.js.map} +1 -1
- package/dist/components/{p-CjP8sYlo.js → p-DABj3qTX.js} +2 -2
- package/dist/components/p-DfIch2Dk.js +2 -0
- package/dist/components/{p-BCCt2eT_.js.map → p-DfIch2Dk.js.map} +1 -1
- package/dist/components/{p-Bet_UjML.js → p-Dfxnxx7L.js} +2 -2
- package/dist/components/{p-CrwU83-g.js → p-Dg3xLna0.js} +2 -2
- package/dist/components/{p-BaZRP4Oo.js → p-DpyTrZa2.js} +2 -2
- package/dist/components/{p-P_4vE1ub.js → p-Dqwcw1-m.js} +2 -2
- package/dist/components/{p-CyoHRqy6.js → p-DszI94sZ.js} +2 -2
- package/dist/components/{p-DrDHC7V6.js → p-DxeHimqk.js} +2 -2
- package/dist/components/{p-DrDHC7V6.js.map → p-DxeHimqk.js.map} +1 -1
- package/dist/components/p-Dy3Skua8.js +2 -0
- package/dist/components/{p-B_qVPsEU.js.map → p-Dy3Skua8.js.map} +1 -1
- package/dist/components/{p-CLn7QnE0.js → p-Dy_sZ-dI.js} +2 -2
- package/dist/components/{p-BEWtqFX7.js → p-LFGbdi7Y.js} +2 -2
- package/dist/components/{p-0kEg-Z4f.js → p-liMPkhUP.js} +2 -2
- package/dist/components/{p-CPPAndX2.js → p-tDXHTAJh.js} +2 -2
- package/dist/components/{p-DTqCBjUj.js → p-webARv8d.js} +2 -2
- package/dist/docs/components.json +1122 -330
- package/dist/lib/shell/chrome-transition.d.ts +1 -1
- package/dist/lib/shell/index.js +1 -1
- package/dist/lib/shell/index.js.map +1 -1
- package/dist/lib/shell/shell-gradient.d.ts +2 -2
- package/dist/mcp-data/registry/agent-activity.json +1 -1
- package/dist/mcp-data/registry/agent-response.json +1 -1
- package/dist/mcp-data/registry/agent-source-list.json +1 -1
- package/dist/mcp-data/registry/agent-tool-call.json +1 -1
- package/dist/mcp-data/registry/attachment-list.json +1 -1
- package/dist/mcp-data/registry/avatar.json +2 -2
- package/dist/mcp-data/registry/badge.json +5 -5
- package/dist/mcp-data/registry/bar-nav.json +9 -9
- package/dist/mcp-data/registry/bar-title.json +451 -0
- package/dist/mcp-data/registry/button-filled.json +1 -1
- package/dist/mcp-data/registry/button-unfilled.json +1 -1
- package/dist/mcp-data/registry/card-data-viz-donut.json +1 -1
- package/dist/mcp-data/registry/card-setting.json +1 -1
- package/dist/mcp-data/registry/card-shell-data-viz.json +1 -1
- package/dist/mcp-data/registry/chart-bar.json +1 -1
- package/dist/mcp-data/registry/chart-donut.json +1 -1
- package/dist/mcp-data/registry/chart-legend.json +1 -1
- package/dist/mcp-data/registry/chart-line.json +1 -1
- package/dist/mcp-data/registry/checkbox.json +1 -1
- package/dist/mcp-data/registry/chip.json +1 -1
- package/dist/mcp-data/registry/code-block.json +5 -4
- package/dist/mcp-data/registry/conversation-list-item.json +1 -1
- package/dist/mcp-data/registry/conversation-list-section.json +1 -1
- package/dist/mcp-data/registry/conversation-list.json +1 -1
- package/dist/mcp-data/registry/divider.json +1 -1
- package/dist/mcp-data/registry/empty-state.json +1 -1
- package/dist/mcp-data/registry/field.json +1 -1
- package/dist/mcp-data/registry/icon.json +2 -2
- package/dist/mcp-data/registry/input.json +1 -1
- package/dist/mcp-data/registry/loader.json +1 -1
- package/dist/mcp-data/registry/markdown.json +1 -1
- package/dist/mcp-data/registry/menu.json +2 -2
- package/dist/mcp-data/registry/message-bubble.json +1 -1
- package/dist/mcp-data/registry/message-composer.json +8 -6
- package/dist/mcp-data/registry/message-scroller.json +5 -4
- package/dist/mcp-data/registry/message.json +1 -1
- package/dist/mcp-data/registry/modal.json +6 -5
- package/dist/mcp-data/registry/panel-nav.json +9 -9
- package/dist/mcp-data/registry/panel-sub-nav.json +2 -2
- package/dist/mcp-data/registry/panel-tool-header.json +5 -4
- package/dist/mcp-data/registry/panel-tool-search.json +7 -6
- package/dist/mcp-data/registry/panel-tools.json +14 -14
- package/dist/mcp-data/registry/radio.json +1 -1
- package/dist/mcp-data/registry/registry.json +497 -177
- package/dist/mcp-data/registry/select-multi.json +3 -2
- package/dist/mcp-data/registry/select.json +3 -2
- package/dist/mcp-data/registry/shell-app.json +213 -0
- package/dist/mcp-data/registry/shell-gradient-picker.json +9 -9
- package/dist/mcp-data/registry/shell-gradient-swatch.json +3 -3
- package/dist/mcp-data/registry/shell-page.json +200 -0
- package/dist/mcp-data/registry/skeleton.json +1 -1
- package/dist/mcp-data/registry/slider.json +1 -1
- package/dist/mcp-data/registry/swatch-picker.json +2 -2
- package/dist/mcp-data/registry/switch.json +1 -1
- package/dist/mcp-data/registry/tab-group.json +1 -1
- package/dist/mcp-data/registry/tag.json +1 -1
- package/dist/mcp-data/registry/text.json +1 -1
- package/dist/mcp-data/registry/toast.json +1 -1
- package/dist/mcp-data/registry/tooltip-data-viz.json +1 -1
- package/dist/mcp-data/registry/tooltip.json +1 -1
- package/dist/mcp-data/registry/typing-indicator.json +1 -1
- package/dist/react/components.d.ts +3 -1
- package/dist/react/components.js +3 -1
- package/dist/react/ds-bar-title.d.ts +14 -0
- package/dist/react/ds-bar-title.js +16 -0
- package/dist/react/{ds-app-shell.d.ts → ds-shell-app.d.ts} +3 -3
- package/dist/react/ds-shell-app.js +12 -0
- package/dist/react/ds-shell-page.d.ts +9 -0
- package/dist/react/ds-shell-page.js +12 -0
- package/dist/types/components/Badge/Badge.d.ts +1 -1
- package/dist/types/components/BarTitle/BarTitle.d.ts +76 -0
- package/dist/types/components/BarTitle/bar-title-types.d.ts +30 -0
- package/dist/types/components/BarTitle/index.d.ts +2 -0
- package/dist/types/components/Menu/Menu.d.ts +5 -0
- package/dist/types/components/PanelNav/PanelNav.d.ts +1 -1
- package/dist/types/components/{AppShell/AppShell.d.ts → ShellApp/ShellApp.d.ts} +3 -3
- package/dist/types/components/ShellPage/ShellPage.d.ts +42 -0
- package/dist/types/components/ShellPage/index.d.ts +3 -0
- package/dist/types/components/ShellPage/shell-page-responsive.d.ts +12 -0
- package/dist/types/components/ShellPage/shell-page-types.d.ts +5 -0
- package/dist/types/components.d.ts +278 -55
- package/dist/types/shell/chrome-transition.d.ts +1 -1
- package/dist/types/shell/shell-gradient.d.ts +2 -2
- package/dist/types/shell/shell-shortcuts.d.ts +1 -1
- package/package.json +3 -3
- package/dist/components/ds-app-shell.js +0 -2
- package/dist/components/ds-app-shell.js.map +0 -1
- package/dist/components/p-BCCt2eT_.js +0 -2
- package/dist/components/p-B_qVPsEU.js +0 -2
- package/dist/components/p-BsnezUUC.js +0 -2
- package/dist/components/p-BsnezUUC.js.map +0 -1
- package/dist/components/p-C1K5QGUZ.js +0 -2
- package/dist/components/p-C1K5QGUZ.js.map +0 -1
- package/dist/components/p-CKaJx0IX.js +0 -2
- package/dist/components/p-CKaJx0IX.js.map +0 -1
- package/dist/components/p-DuNZGFkV.js.map +0 -1
- package/dist/components/p-ZTugT4as.js +0 -2
- package/dist/components/p-ZTugT4as.js.map +0 -1
- package/dist/mcp-data/registry/app-shell.json +0 -210
- package/dist/react/ds-app-shell.js +0 -12
- /package/dist/components/{p-DJ78wdNv.js.map → p-B9jvSGid.js.map} +0 -0
- /package/dist/components/{p-Dh4q5TSS.js.map → p-BfEjWe8U.js.map} +0 -0
- /package/dist/components/{p-CZZYGqKz.js.map → p-Bs6SS34m.js.map} +0 -0
- /package/dist/components/{p-bNpFhP5L.js.map → p-CR7yMitN.js.map} +0 -0
- /package/dist/components/{p-CjP8sYlo.js.map → p-DABj3qTX.js.map} +0 -0
- /package/dist/components/{p-Bet_UjML.js.map → p-Dfxnxx7L.js.map} +0 -0
- /package/dist/components/{p-CrwU83-g.js.map → p-Dg3xLna0.js.map} +0 -0
- /package/dist/components/{p-BaZRP4Oo.js.map → p-DpyTrZa2.js.map} +0 -0
- /package/dist/components/{p-P_4vE1ub.js.map → p-Dqwcw1-m.js.map} +0 -0
- /package/dist/components/{p-CyoHRqy6.js.map → p-DszI94sZ.js.map} +0 -0
- /package/dist/components/{p-CLn7QnE0.js.map → p-Dy_sZ-dI.js.map} +0 -0
- /package/dist/components/{p-BEWtqFX7.js.map → p-LFGbdi7Y.js.map} +0 -0
- /package/dist/components/{p-0kEg-Z4f.js.map → p-liMPkhUP.js.map} +0 -0
- /package/dist/components/{p-CPPAndX2.js.map → p-tDXHTAJh.js.map} +0 -0
- /package/dist/components/{p-DTqCBjUj.js.map → p-webARv8d.js.map} +0 -0
|
@@ -721,7 +721,7 @@
|
|
|
721
721
|
"peerDependencies": {
|
|
722
722
|
"required": [
|
|
723
723
|
"@ds-mo/tokens ^3.1.1",
|
|
724
|
-
"@ds-mo/icons ^6.
|
|
724
|
+
"@ds-mo/icons ^6.4.0"
|
|
725
725
|
],
|
|
726
726
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
727
727
|
}
|
|
@@ -738,7 +738,8 @@
|
|
|
738
738
|
"empty-state",
|
|
739
739
|
"icon",
|
|
740
740
|
"loader",
|
|
741
|
-
"text"
|
|
741
|
+
"text",
|
|
742
|
+
"tooltip"
|
|
742
743
|
],
|
|
743
744
|
"files": [
|
|
744
745
|
{
|
|
@@ -707,7 +707,7 @@
|
|
|
707
707
|
"peerDependencies": {
|
|
708
708
|
"required": [
|
|
709
709
|
"@ds-mo/tokens ^3.1.1",
|
|
710
|
-
"@ds-mo/icons ^6.
|
|
710
|
+
"@ds-mo/icons ^6.4.0"
|
|
711
711
|
],
|
|
712
712
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
713
713
|
}
|
|
@@ -723,7 +723,8 @@
|
|
|
723
723
|
"empty-state",
|
|
724
724
|
"icon",
|
|
725
725
|
"loader",
|
|
726
|
-
"text"
|
|
726
|
+
"text",
|
|
727
|
+
"tooltip"
|
|
727
728
|
],
|
|
728
729
|
"files": [
|
|
729
730
|
{
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "shell-app",
|
|
4
|
+
"title": "ShellApp",
|
|
5
|
+
"description": "The single required workspace root for standard authenticated application pages, coordinating primary and secondary navigation, tools chrome, responsive composition, content geometry, shared styling, and shell behavior.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/shell-app",
|
|
11
|
+
"source": "src/wc/components/ShellApp/ShellApp.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "specialized",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "The single required workspace root for standard authenticated application pages, coordinating primary and secondary navigation, tools chrome, responsive composition, content geometry, shared styling, and shell behavior.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"Building any standard authenticated product or workspace page with CompoMo.",
|
|
19
|
+
"PanelNav, BarNav, PanelTools, page content, shared chrome, responsive behavior, and shell shortcuts must be composed as one application frame.",
|
|
20
|
+
"Future application templates need the supported base-page composition boundary."
|
|
21
|
+
],
|
|
22
|
+
"avoidWhen": [
|
|
23
|
+
"Building authentication, onboarding, public, marketing, print, error, or intentionally focused full-screen experiences that require a different layout.",
|
|
24
|
+
"Another ShellApp already exists in the page; applications must render exactly one and must never nest ShellApps.",
|
|
25
|
+
"Building content inside an existing ShellApp; render the page content rather than another shell."
|
|
26
|
+
],
|
|
27
|
+
"commonlyComposedWith": [
|
|
28
|
+
"component:ds-panel-nav",
|
|
29
|
+
"component:ds-bar-nav",
|
|
30
|
+
"component:ds-panel-tools",
|
|
31
|
+
"component:ds-shell-page",
|
|
32
|
+
"component:ds-swatch-picker"
|
|
33
|
+
],
|
|
34
|
+
"patterns": [
|
|
35
|
+
"pattern:application-shell",
|
|
36
|
+
"pattern:shell-page"
|
|
37
|
+
],
|
|
38
|
+
"accessibility": [
|
|
39
|
+
"Keep primary destinations in PanelNav and secondary route sections in BarNav.",
|
|
40
|
+
"Ensure the default content region contains the page's single main landmark.",
|
|
41
|
+
"Keep PanelNav, BarNav, and PanelTools chrome available in the shell's supported desktop and tablet compositions.",
|
|
42
|
+
"Do not override browser keyboard chords when enabling shell shortcuts.",
|
|
43
|
+
"Require Search and Help as the minimum PanelTools actions and provide accessible labels for every tool.",
|
|
44
|
+
"Preserve focus and accessible names across navigation, collapse, overflow, and responsive presentation changes."
|
|
45
|
+
],
|
|
46
|
+
"states": [
|
|
47
|
+
"Render exactly one ShellApp as the workspace root and never nest it.",
|
|
48
|
+
"PanelNav, BarNav, and PanelTools are required shell regions; PanelTools contains at least Search and Help even when no additional product tools apply.",
|
|
49
|
+
"The application router owns the current route, authorization filtering, Dashboard or Settings context, destination collections, section collections, and localized content labels.",
|
|
50
|
+
"ShellApp propagates navStyle and coordinates layout, gradient alignment, transitions, metrics, shortcuts, and responsive composition.",
|
|
51
|
+
"Set gradientPreset to the single resolved global chrome preference: neutral by default, none for solid chrome, or cool, neutral, warm, and fresh for token-based washes.",
|
|
52
|
+
"The application owns gradient preference persistence, cross-tab synchronization, and server or account precedence; ShellApp owns rendering the resolved preset across authenticated routes.",
|
|
53
|
+
"Dashboard represents day-to-day operational work while Settings represents administrative and configuration work; route state selects the context.",
|
|
54
|
+
"Update PanelNav groups, BarNav tabs and heading, navStyle, basePath, and currentUrl atomically from one resolved route state.",
|
|
55
|
+
"BarNav remains present: it shows route-section tabs when available and the active PanelNav destination label as heading otherwise.",
|
|
56
|
+
"Render one ShellPage as the default-slot child for each standard routed page; ShellApp owns the content scroller while ShellPage owns page header stickiness and content inset.",
|
|
57
|
+
"The application filters unauthorized destinations, sections, and tools before passing them into shell components."
|
|
58
|
+
],
|
|
59
|
+
"responsiveBehavior": [
|
|
60
|
+
"ShellApp owns the fixed system breakpoints and resolves desktop at 1200px and wider, tablet from 768px through 1199px, and mobile below 768px.",
|
|
61
|
+
"On desktop, PanelNav defaults expanded, permits user collapse or expansion, and respects the locally persisted desktop preference.",
|
|
62
|
+
"On tablet, ShellApp keeps PanelNav permanently collapsed and prevents user expansion without overwriting the desktop preference.",
|
|
63
|
+
"On mobile, ShellApp remains the page root but replaces PanelNav and desktop navigation composition with a future dedicated mobile navigation design.",
|
|
64
|
+
"Keep PanelNav width, BarNav height, PanelTools chrome, and main-content origin stable except during intentional responsive or collapse transitions.",
|
|
65
|
+
"The current runtime does not yet implement the complete desktop, tablet, and mobile composition contract; consuming applications must enforce missing breakpoint behavior until ShellApp provides it."
|
|
66
|
+
],
|
|
67
|
+
"frameworkCaveats": {
|
|
68
|
+
"customElements": [
|
|
69
|
+
"Assign complex navigation and tool collections as JavaScript properties on the slotted components.",
|
|
70
|
+
"Stamp route-derived nav-style before components connect when deterministic first paint is required."
|
|
71
|
+
],
|
|
72
|
+
"react": [
|
|
73
|
+
"Use generated wrappers and preserve native slot names for PanelNav, BarNav, PanelTools, and main content.",
|
|
74
|
+
"Resolve all shell inputs from one router state before rendering the composition."
|
|
75
|
+
],
|
|
76
|
+
"angular": [
|
|
77
|
+
"Use generated standalone adapters and preserve slot attributes in templates.",
|
|
78
|
+
"Bind complex inputs as properties and update navigation inputs atomically from one resolved router state.",
|
|
79
|
+
"Bind both nav-style attributes and properties when deterministic first paint is required."
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"references": [
|
|
83
|
+
{
|
|
84
|
+
"label": "Component guide",
|
|
85
|
+
"path": "src/wc/components/ShellApp/ShellApp.mdx"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"label": "Executable Storybook composition",
|
|
89
|
+
"path": "src/wc/components/ShellApp/ShellApp.stories.ts"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"label": "Framework integration",
|
|
93
|
+
"path": "docs/framework-integration.md"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"label": "Application shell contract",
|
|
97
|
+
"path": "agent/patterns/application-shell/pattern.agent.json"
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"api": {
|
|
102
|
+
"props": {
|
|
103
|
+
"gradientPreset": {
|
|
104
|
+
"type": "ShellGradientPreset",
|
|
105
|
+
"resolvedType": "\"cool\" | \"fresh\" | \"neutral\" | \"none\" | \"warm\"",
|
|
106
|
+
"attribute": "gradient-preset",
|
|
107
|
+
"default": "'neutral'",
|
|
108
|
+
"required": false,
|
|
109
|
+
"mutable": false,
|
|
110
|
+
"description": "Shell chrome wash preset. `none` renders solid chrome; the remaining\npresets use token-based washes that adapt to the active color theme."
|
|
111
|
+
},
|
|
112
|
+
"navStyle": {
|
|
113
|
+
"type": "NavChromeStyle",
|
|
114
|
+
"resolvedType": "\"dashboard\" | \"settings\"",
|
|
115
|
+
"attribute": "nav-style",
|
|
116
|
+
"default": "'dashboard'",
|
|
117
|
+
"required": false,
|
|
118
|
+
"mutable": false,
|
|
119
|
+
"description": "Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`."
|
|
120
|
+
},
|
|
121
|
+
"shortcutsEnabled": {
|
|
122
|
+
"type": "boolean",
|
|
123
|
+
"resolvedType": "boolean",
|
|
124
|
+
"attribute": "shortcuts-enabled",
|
|
125
|
+
"default": "true",
|
|
126
|
+
"required": false,
|
|
127
|
+
"mutable": false,
|
|
128
|
+
"description": "When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native."
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"events": [],
|
|
132
|
+
"methods": [],
|
|
133
|
+
"slots": []
|
|
134
|
+
},
|
|
135
|
+
"props": {
|
|
136
|
+
"gradientPreset": {
|
|
137
|
+
"type": "ShellGradientPreset",
|
|
138
|
+
"resolvedType": "\"cool\" | \"fresh\" | \"neutral\" | \"none\" | \"warm\"",
|
|
139
|
+
"attribute": "gradient-preset",
|
|
140
|
+
"default": "'neutral'",
|
|
141
|
+
"required": false,
|
|
142
|
+
"mutable": false,
|
|
143
|
+
"description": "Shell chrome wash preset. `none` renders solid chrome; the remaining\npresets use token-based washes that adapt to the active color theme."
|
|
144
|
+
},
|
|
145
|
+
"navStyle": {
|
|
146
|
+
"type": "NavChromeStyle",
|
|
147
|
+
"resolvedType": "\"dashboard\" | \"settings\"",
|
|
148
|
+
"attribute": "nav-style",
|
|
149
|
+
"default": "'dashboard'",
|
|
150
|
+
"required": false,
|
|
151
|
+
"mutable": false,
|
|
152
|
+
"description": "Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`."
|
|
153
|
+
},
|
|
154
|
+
"shortcutsEnabled": {
|
|
155
|
+
"type": "boolean",
|
|
156
|
+
"resolvedType": "boolean",
|
|
157
|
+
"attribute": "shortcuts-enabled",
|
|
158
|
+
"default": "true",
|
|
159
|
+
"required": false,
|
|
160
|
+
"mutable": false,
|
|
161
|
+
"description": "When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"events": [],
|
|
165
|
+
"methods": [],
|
|
166
|
+
"slots": [],
|
|
167
|
+
"exports": {
|
|
168
|
+
"customElement": "ds-shell-app",
|
|
169
|
+
"react": "DsShellApp",
|
|
170
|
+
"angular": "DsShellApp"
|
|
171
|
+
},
|
|
172
|
+
"consumption": {
|
|
173
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
174
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
175
|
+
"customElements": {
|
|
176
|
+
"import": "import '@ds-mo/ui/dist/components/ds-shell-app.js';",
|
|
177
|
+
"example": "<ds-shell-app></ds-shell-app>"
|
|
178
|
+
},
|
|
179
|
+
"react": {
|
|
180
|
+
"import": "import { DsShellApp } from '@ds-mo/ui/react';",
|
|
181
|
+
"example": "<DsShellApp />"
|
|
182
|
+
},
|
|
183
|
+
"angular": {
|
|
184
|
+
"import": "import { DsShellApp } from '@ds-mo/ui/angular/ds-shell-app';",
|
|
185
|
+
"example": "<ds-shell-app></ds-shell-app>"
|
|
186
|
+
},
|
|
187
|
+
"peerDependencies": {
|
|
188
|
+
"required": [
|
|
189
|
+
"@ds-mo/tokens ^3.1.1",
|
|
190
|
+
"@ds-mo/icons ^6.4.0"
|
|
191
|
+
],
|
|
192
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"dependencies": [
|
|
197
|
+
"@ds-mo/ui",
|
|
198
|
+
"@ds-mo/tokens"
|
|
199
|
+
],
|
|
200
|
+
"registryDependencies": [],
|
|
201
|
+
"files": [
|
|
202
|
+
{
|
|
203
|
+
"path": "src/wc/components/ShellApp/ShellApp.css",
|
|
204
|
+
"content": ":host {\n display: block;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n overflow: hidden;\n overscroll-behavior: none;\n --ds-shell-panel-tools-width: var(--dimension-panel-width-xs);\n}\n\n.shell-app {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n overflow: hidden;\n overscroll-behavior: none;\n font-family: var(--typography-font-family, system-ui);\n}\n\n:host {\n --_shell-chrome-bg: var(--color-background-secondary);\n}\n\n.shell-app__row {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n height: 100%;\n position: relative;\n}\n\n/*\n * One chrome layer behind the shell row. Wash uses background-attachment: fixed\n * so the pattern is locked to the viewport — panel/tools collapse only changes the\n * transparent window, not the pattern phase. JS sets wash background-size from\n * readShellViewportDimensions(), not the shell element box.\n */\n.shell-app__chrome {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background-color: var(--_shell-chrome-bg);\n}\n\n:host(.shell-app--gradient) .shell-app__chrome::before {\n content: '';\n position: absolute;\n inset: 0;\n pointer-events: none;\n background-position: 0 0;\n background-attachment: fixed;\n z-index: 1;\n opacity: var(--ds-shell-gradient-opacity, 1);\n background-image: var(--ds-shell-gradient-image, none);\n background-size: var(--ds-shell-gradient-size, auto);\n background-repeat: no-repeat;\n}\n\n.shell-app__panel {\n flex: 0 0 auto;\n min-height: 0;\n height: 100%;\n position: relative;\n /* Above main so resize handle line can paint over the workspace edge */\n z-index: 2;\n}\n\n.shell-app__panel ::slotted(ds-panel-nav) {\n display: block;\n height: 100%;\n}\n\n.shell-app__main {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n grid-template-rows: auto minmax(0, 1fr);\n grid-template-areas:\n 'bar tools'\n 'content tools';\n position: relative;\n z-index: 1;\n}\n\n.shell-app__bar {\n grid-area: bar;\n flex: 0 0 auto;\n min-width: 0;\n}\n\n.shell-app__bar ::slotted(ds-bar-nav) {\n display: block;\n}\n\n.shell-app__content {\n grid-area: content;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: none;\n /* Limit reflow to the page column — not on __main (breaks tools flex shrink). */\n contain: layout;\n}\n\n/* Opaque page surface — masks the fixed chrome layer in the content column. */\n.shell-app__content {\n position: relative;\n z-index: 1;\n background-color: var(--color-background-primary);\n}\n\n.shell-app__tools {\n grid-area: tools;\n flex: 0 0 auto;\n min-width: 0;\n min-height: 0;\n height: 100%;\n display: flex;\n position: relative;\n z-index: 0;\n overflow: hidden;\n}\n\n.shell-app__tools ::slotted(ds-panel-tools) {\n display: block;\n height: 100%;\n min-height: 0;\n}\n\n:host(.shell-app--tools-fullscreen) .shell-app__panel,\n:host(.shell-app--tools-fullscreen) .shell-app__bar,\n:host(.shell-app--tools-fullscreen) .shell-app__content {\n visibility: hidden;\n}\n",
|
|
205
|
+
"type": "registry:ui"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"path": "src/wc/components/ShellApp/ShellApp.tsx",
|
|
209
|
+
"content": "import { Component, Prop, Element, State, Watch, Listen, h, Host } from '@stencil/core';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport {\n isEditableShortcutTarget,\n resolveShellShortcut,\n} from '../../shell/shell-shortcuts';\nimport type { PanelToolsToolId } from '../PanelTools/panel-tools-types';\nimport {\n DEFAULT_SHELL_GRADIENT_PRESET,\n normalizeShellGradientPreset,\n type ShellGradientPreset,\n} from '../../shell/shell-gradient-presets';\nimport {\n CHROME_TRANSITION_END,\n CHROME_TRANSITION_START,\n ChromeTransitionDepth,\n createRafCoalescer,\n readChromeTransitionSource,\n} from '../../shell/chrome-transition';\nimport {\n isPanelNavCollapsed,\n panelWidthPxFromTokens,\n readPanelNavWidthTokens,\n type PanelNavWidthTokens,\n} from '../../shell/shell-chrome-metrics';\nimport {\n SHELL_GRADIENT_IMAGE_VAR,\n SHELL_GRADIENT_OPACITY_VAR,\n SHELL_GRADIENT_POSITION_BAR_VAR,\n SHELL_GRADIENT_POSITION_PANEL_VAR,\n SHELL_GRADIENT_SIZE_VAR,\n buildShellRadialGradient,\n readShellViewportDimensions,\n shellGradientPositionBar,\n shellGradientPositionPanel,\n shellGradientPresetOpacity,\n shellGradientSize,\n} from '../../shell/shell-gradient';\n\n@Component({\n tag: 'ds-shell-app',\n styleUrl: 'ShellApp.css',\n scoped: true,\n})\nexport class ShellApp {\n /** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n /**\n * Shell chrome wash preset. `none` renders solid chrome; the remaining\n * presets use token-based washes that adapt to the active color theme.\n */\n @Prop({ attribute: 'gradient-preset', reflect: true }) gradientPreset: ShellGradientPreset =\n DEFAULT_SHELL_GRADIENT_PRESET;\n\n /** When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native. */\n @Prop({ attribute: 'shortcuts-enabled' }) shortcutsEnabled: boolean = true;\n\n @Element() el!: HTMLElement;\n @State() private toolsFullscreen = false;\n\n private resizeObserver: ResizeObserver | null = null;\n private readonly panelNavTransition = new ChromeTransitionDepth();\n private readonly chromeSyncCoalescer = createRafCoalescer(() => this.syncChrome());\n private panelWidthTokens: PanelNavWidthTokens = { expandedPx: 0, collapsedPx: 0 };\n private cachedViewportWidth = 0;\n private cachedViewportHeight = 0;\n private onWindowResize = () => {\n if (this.panelNavTransition.isActive) return;\n this.scheduleChromeSync();\n };\n private onVisualViewportChange = () => {\n if (this.panelNavTransition.isActive) return;\n this.scheduleChromeSync();\n };\n\n componentDidLoad() {\n this.syncSlottedNavStyle();\n this.connectMetricsObserver();\n this.connectViewportListeners();\n this.el.addEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n this.el.addEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n requestAnimationFrame(() => {\n this.cachePanelWidthTokens();\n this.scheduleChromeSync();\n });\n const tools = this.el.querySelector('ds-panel-tools');\n this.toolsFullscreen = tools?.getAttribute('presentation') === 'fullscreen';\n }\n\n @Listen('dsPresentationChange')\n handleToolsPresentation(event: CustomEvent<{ presentation?: string }>) {\n this.toolsFullscreen = event.detail?.presentation === 'fullscreen';\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.disconnectViewportListeners();\n this.chromeSyncCoalescer.cancel();\n this.el.removeEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n this.el.removeEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n }\n\n @Watch('navStyle')\n @Watch('gradientPreset')\n onShellPropsChange() {\n this.syncSlottedNavStyle();\n this.scheduleChromeSync();\n }\n\n private onChromeTransitionStart = (event: Event) => {\n if (readChromeTransitionSource(event) !== 'panel-nav') return;\n\n this.panelNavTransition.enter();\n const viewport = readShellViewportDimensions();\n this.cachedViewportWidth = viewport.width;\n this.cachedViewportHeight = viewport.height;\n this.syncChrome();\n };\n\n private onChromeTransitionEnd = (event: Event) => {\n if (readChromeTransitionSource(event) !== 'panel-nav') return;\n\n this.panelNavTransition.exit();\n if (!this.panelNavTransition.isActive) {\n this.scheduleChromeSync();\n }\n };\n\n /** Coalesce ResizeObserver bursts to one layout read per frame. */\n private scheduleChromeSync() {\n this.chromeSyncCoalescer.schedule();\n }\n\n private syncSlottedNavStyle() {\n const panel = this.el.querySelector('ds-panel-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;\n const bar = this.el.querySelector('ds-bar-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;\n if (panel) {\n panel.setAttribute('nav-style', this.navStyle);\n panel.navStyle = this.navStyle;\n }\n if (bar) {\n bar.setAttribute('nav-style', this.navStyle);\n bar.navStyle = this.navStyle;\n }\n }\n\n private connectMetricsObserver() {\n this.resizeObserver = new ResizeObserver(() => {\n if (this.panelNavTransition.isActive) return;\n this.scheduleChromeSync();\n });\n this.resizeObserver.observe(this.el);\n const panelWrap = this.el.querySelector('.shell-app__panel');\n if (panelWrap) this.resizeObserver.observe(panelWrap);\n }\n\n private connectViewportListeners() {\n if (typeof window === 'undefined') return;\n\n window.addEventListener('resize', this.onWindowResize, { passive: true });\n\n const visual = window.visualViewport;\n if (visual) {\n visual.addEventListener('resize', this.onVisualViewportChange, { passive: true });\n visual.addEventListener('scroll', this.onVisualViewportChange, { passive: true });\n }\n }\n\n private disconnectViewportListeners() {\n if (typeof window === 'undefined') return;\n\n window.removeEventListener('resize', this.onWindowResize);\n\n const visual = window.visualViewport;\n if (visual) {\n visual.removeEventListener('resize', this.onVisualViewportChange);\n visual.removeEventListener('scroll', this.onVisualViewportChange);\n }\n }\n\n private cachePanelWidthTokens() {\n const navRoot = this.el.querySelector('ds-panel-nav .panel-nav') as HTMLElement | null;\n if (!navRoot) return;\n this.panelWidthTokens = readPanelNavWidthTokens(navRoot);\n }\n\n private applyGradientVars(target: HTMLElement, vars: Record<string, string | null>) {\n const style = target.style;\n for (const [name, value] of Object.entries(vars)) {\n if (value === null) style.removeProperty(name);\n else style.setProperty(name, value);\n }\n }\n\n private clearGradientPaintVars(targets: HTMLElement[]) {\n const keys = [\n SHELL_GRADIENT_IMAGE_VAR,\n SHELL_GRADIENT_SIZE_VAR,\n SHELL_GRADIENT_OPACITY_VAR,\n ];\n for (const target of targets) {\n for (const key of keys) target.style.removeProperty(key);\n }\n }\n\n /** Panel/bar wash offsets for scroll fades and badge rings. */\n private syncChromeLayoutVars(\n panel: HTMLElement | null,\n bar: HTMLElement | null,\n panelPosition: string,\n barPosition: string,\n ) {\n if (panel) {\n panel.style.setProperty(SHELL_GRADIENT_POSITION_PANEL_VAR, panelPosition);\n }\n if (bar) {\n bar.style.setProperty(SHELL_GRADIENT_POSITION_BAR_VAR, barPosition);\n }\n }\n\n private resolvePanelWidthPx(panelNav: HTMLElement | null): number {\n const navRoot = panelNav?.querySelector('.panel-nav') as HTMLElement | null;\n\n if (this.panelNavTransition.isActive && this.panelWidthTokens.expandedPx > 0) {\n const collapsed = isPanelNavCollapsed(panelNav, navRoot);\n return panelWidthPxFromTokens(this.panelWidthTokens, collapsed);\n }\n\n const panelWrap = this.el.querySelector('.shell-app__panel') as HTMLElement | null;\n const measured = panelWrap?.getBoundingClientRect().width ?? 0;\n if (measured > 0) return measured;\n\n if (this.panelWidthTokens.expandedPx > 0) {\n const collapsed = isPanelNavCollapsed(panelNav, navRoot);\n return panelWidthPxFromTokens(this.panelWidthTokens, collapsed);\n }\n\n return 0;\n }\n\n /** Fixed-attachment wash/grid size — always the viewport, never the shell element box. */\n private resolveViewportDimensions(): { width: number; height: number } {\n if (this.panelNavTransition.isActive && this.cachedViewportWidth > 0) {\n return {\n width: this.cachedViewportWidth,\n height: this.cachedViewportHeight,\n };\n }\n\n return readShellViewportDimensions();\n }\n\n private chromeLayerActive(): boolean {\n return normalizeShellGradientPreset(this.gradientPreset) !== 'none';\n }\n\n @Listen('keydown', { target: 'window', capture: true })\n handleWindowKeyDown(e: KeyboardEvent) {\n if (!this.shortcutsEnabled) return;\n if (isEditableShortcutTarget(e.target)) return;\n\n const action = resolveShellShortcut(e);\n if (!action) return;\n\n e.preventDefault();\n\n const panel = this.el.querySelector('ds-panel-nav') as HTMLDsPanelNavElement | null;\n const tools = this.el.querySelector('ds-panel-tools') as HTMLDsPanelToolsElement | null;\n\n if (action === 'toggle-panel-nav') {\n void panel?.toggleCollapsed();\n return;\n }\n\n if (action === 'close-panel-tools') {\n void tools?.closeDrawer();\n return;\n }\n\n if (action.startsWith('open-tool:') && tools) {\n const toolId = action.slice('open-tool:'.length) as PanelToolsToolId;\n void tools.activateTool(toolId);\n }\n }\n\n private syncChrome() {\n const panelNav = this.el.querySelector('ds-panel-nav') as HTMLElement | null;\n const bar = this.el.querySelector('ds-bar-nav') as HTMLElement | null;\n const targets = [this.el, panelNav, bar].filter((el): el is HTMLElement => el !== null);\n\n const clearLayoutVars = () => {\n if (panelNav) panelNav.style.removeProperty(SHELL_GRADIENT_POSITION_PANEL_VAR);\n if (bar) bar.style.removeProperty(SHELL_GRADIENT_POSITION_BAR_VAR);\n };\n\n const preset = normalizeShellGradientPreset(this.gradientPreset);\n if (preset === 'none') {\n this.clearGradientPaintVars(targets);\n clearLayoutVars();\n return;\n }\n\n const viewport = this.resolveViewportDimensions();\n const panelWidth = this.resolvePanelWidthPx(panelNav);\n\n const panelPosition = shellGradientPositionPanel();\n const barPosition = shellGradientPositionBar(panelWidth);\n\n this.syncChromeLayoutVars(panelNav, bar, panelPosition, barPosition);\n\n const image = buildShellRadialGradient(preset);\n\n const size = shellGradientSize({\n width: viewport.width,\n height: viewport.height,\n });\n const opacity = shellGradientPresetOpacity(preset);\n\n for (const target of targets) {\n if (target === this.el) {\n this.applyGradientVars(target, {\n [SHELL_GRADIENT_IMAGE_VAR]: image,\n [SHELL_GRADIENT_SIZE_VAR]: size,\n [SHELL_GRADIENT_POSITION_PANEL_VAR]: panelPosition,\n [SHELL_GRADIENT_POSITION_BAR_VAR]: barPosition,\n [SHELL_GRADIENT_OPACITY_VAR]: opacity,\n });\n continue;\n }\n\n const isBar = target.tagName.toLowerCase() === 'ds-bar-nav';\n this.applyGradientVars(target, {\n [SHELL_GRADIENT_IMAGE_VAR]: image,\n [SHELL_GRADIENT_SIZE_VAR]: size,\n [SHELL_GRADIENT_OPACITY_VAR]: opacity,\n [SHELL_GRADIENT_POSITION_PANEL_VAR]: isBar ? null : panelPosition,\n [SHELL_GRADIENT_POSITION_BAR_VAR]: isBar ? barPosition : null,\n });\n }\n }\n\n render() {\n const chromeActive = this.chromeLayerActive();\n const shellCls: Record<string, boolean> = {\n 'shell-app': true,\n 'shell-app--gradient': chromeActive,\n [`shell-app--${this.navStyle}`]: true,\n 'shell-app--tools-fullscreen': this.toolsFullscreen,\n };\n\n return (\n <Host class={shellCls}>\n <div class=\"shell-app__row\">\n <div class=\"shell-app__chrome\" aria-hidden=\"true\" />\n <div class=\"shell-app__panel\" aria-hidden={this.toolsFullscreen ? 'true' : undefined} inert={this.toolsFullscreen ? true : undefined}>\n <slot name=\"panel\" />\n </div>\n <div class=\"shell-app__main\">\n <div class=\"shell-app__bar\" aria-hidden={this.toolsFullscreen ? 'true' : undefined} inert={this.toolsFullscreen ? true : undefined}>\n <slot name=\"bar\" />\n </div>\n <div class=\"shell-app__tools\">\n <slot name=\"tools\" />\n </div>\n <div class=\"shell-app__content\" aria-hidden={this.toolsFullscreen ? 'true' : undefined} inert={this.toolsFullscreen ? true : undefined}>\n <slot />\n </div>\n </div>\n </div>\n </Host>\n );\n }\n}\n",
|
|
210
|
+
"type": "registry:ui"
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
3
|
"name": "shell-gradient-picker",
|
|
4
4
|
"title": "ShellGradientPicker",
|
|
5
|
-
"description": "Deprecated compatibility wrapper that maps the fixed
|
|
5
|
+
"description": "Deprecated compatibility wrapper that maps the fixed ShellApp wash presets into SwatchPicker.",
|
|
6
6
|
"type": "registry:ui",
|
|
7
7
|
"meta": {
|
|
8
8
|
"library": "@ds-mo/ui",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"status": "deprecated",
|
|
16
16
|
"replacedBy": "component:ds-swatch-picker",
|
|
17
17
|
"replacementReason": "SwatchPicker now owns the same accessible interaction with a generic option model that supports both flat colors and gradients.",
|
|
18
|
-
"summary": "Deprecated compatibility wrapper that maps the fixed
|
|
18
|
+
"summary": "Deprecated compatibility wrapper that maps the fixed ShellApp wash presets into SwatchPicker.",
|
|
19
19
|
"useWhen": [
|
|
20
20
|
"Adding the Appearance or Theme section to the application's user or account menu.",
|
|
21
|
-
"Users must choose the global
|
|
21
|
+
"Users must choose the global ShellApp chrome treatment from none, cool, neutral, warm, or fresh."
|
|
22
22
|
],
|
|
23
23
|
"avoidWhen": [
|
|
24
24
|
"Choosing semantic intent, page background, chart, content, or brand colors.",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
],
|
|
36
36
|
"commonlyComposedWith": [
|
|
37
|
-
"component:ds-app
|
|
37
|
+
"component:ds-shell-app",
|
|
38
38
|
"component:ds-menu"
|
|
39
39
|
],
|
|
40
40
|
"patterns": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"states": [
|
|
49
49
|
"Neutral is the default preset; none renders solid shell chrome without a wash.",
|
|
50
50
|
"Cool, neutral, warm, and fresh use design tokens and automatically adapt to the active light or dark color theme.",
|
|
51
|
-
"Apply dsChange immediately to
|
|
51
|
+
"Apply dsChange immediately to ShellApp.gradientPreset and keep the containing account menu open.",
|
|
52
52
|
"The application owns local persistence, cross-tab synchronization, and server or account preference precedence; the picker owns only selection UI and events.",
|
|
53
53
|
"Treat the fixed preset union as the only supported values and do not construct custom gradients."
|
|
54
54
|
],
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
],
|
|
59
59
|
"frameworkCaveats": {
|
|
60
60
|
"customElements": [
|
|
61
|
-
"Listen for dsChange, resolve application preference precedence, and assign the result to the single
|
|
61
|
+
"Listen for dsChange, resolve application preference precedence, and assign the result to the single ShellApp gradientPreset property."
|
|
62
62
|
],
|
|
63
63
|
"react": [
|
|
64
64
|
"Use the generated wrapper and handle onDsChange without closing the containing account menu."
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"path": "src/wc/components/SwatchPicker/SwatchPicker.stories.ts"
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
|
-
"label": "
|
|
77
|
-
"path": "src/wc/components/
|
|
76
|
+
"label": "ShellApp gradient contract",
|
|
77
|
+
"path": "src/wc/components/ShellApp/ShellApp.mdx"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"label": "Application shell composition",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"peerDependencies": {
|
|
168
168
|
"required": [
|
|
169
169
|
"@ds-mo/tokens ^3.1.1",
|
|
170
|
-
"@ds-mo/icons ^6.
|
|
170
|
+
"@ds-mo/icons ^6.4.0"
|
|
171
171
|
],
|
|
172
172
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
173
173
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
3
|
"name": "shell-gradient-swatch",
|
|
4
4
|
"title": "ShellGradientSwatch",
|
|
5
|
-
"description": "Deprecated internal compatibility component for one legacy
|
|
5
|
+
"description": "Deprecated internal compatibility component for one legacy ShellApp gradient swatch.",
|
|
6
6
|
"type": "registry:ui",
|
|
7
7
|
"meta": {
|
|
8
8
|
"library": "@ds-mo/ui",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"status": "deprecated",
|
|
16
16
|
"replacedBy": "component:ds-swatch-picker",
|
|
17
17
|
"replacementReason": "SwatchPicker renders its option buttons internally so an individually published radio swatch is no longer needed.",
|
|
18
|
-
"summary": "Deprecated internal compatibility component for one legacy
|
|
18
|
+
"summary": "Deprecated internal compatibility component for one legacy ShellApp gradient swatch.",
|
|
19
19
|
"useWhen": [
|
|
20
20
|
"Maintaining or extending ShellGradientPicker inside CompoMo."
|
|
21
21
|
],
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
"peerDependencies": {
|
|
185
185
|
"required": [
|
|
186
186
|
"@ds-mo/tokens ^3.1.1",
|
|
187
|
-
"@ds-mo/icons ^6.
|
|
187
|
+
"@ds-mo/icons ^6.4.0"
|
|
188
188
|
],
|
|
189
189
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
190
190
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "shell-page",
|
|
4
|
+
"title": "ShellPage",
|
|
5
|
+
"description": "The standard routed-page boundary inside ShellApp, owning sticky BarTitle placement, container-aware header variants, stable scroll compaction, and optional page-content inset without creating a nested scroll container.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/shell-page",
|
|
11
|
+
"source": "src/wc/components/ShellPage/ShellPage.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "The standard routed-page boundary inside ShellApp, owning sticky BarTitle placement, container-aware header variants, stable scroll compaction, and optional page-content inset without creating a nested scroll container.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"Rendering a standard routed page inside ShellApp's default content region.",
|
|
19
|
+
"BarTitle should expand at a roomy page top, compact after scroll, or use constrained action layout when the page column narrows.",
|
|
20
|
+
"Pages need consistent content gutters with an explicit full-bleed option."
|
|
21
|
+
],
|
|
22
|
+
"avoidWhen": [
|
|
23
|
+
"Building authentication, onboarding, public, modal, drawer, or intentionally focused fullscreen layouts outside the standard ShellApp page region.",
|
|
24
|
+
"Another ShellPage already owns the routed page; never nest ShellPages.",
|
|
25
|
+
"A bounded card or pane needs its own header rather than a page-level main landmark."
|
|
26
|
+
],
|
|
27
|
+
"alternatives": [
|
|
28
|
+
{
|
|
29
|
+
"when": "The composition is a persistent application-wide workspace frame.",
|
|
30
|
+
"component": "component:ds-shell-app",
|
|
31
|
+
"reason": "ShellApp owns global navigation, tools, and the content scroll region; ShellPage belongs inside it."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"when": "Only static page title chrome is required without sticky or responsive orchestration.",
|
|
35
|
+
"component": "component:ds-bar-title",
|
|
36
|
+
"reason": "BarTitle can be used standalone with an explicit variant."
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"commonlyComposedWith": [
|
|
40
|
+
"component:ds-shell-app",
|
|
41
|
+
"component:ds-bar-title",
|
|
42
|
+
"component:ds-tab-group"
|
|
43
|
+
],
|
|
44
|
+
"patterns": [
|
|
45
|
+
"pattern:shell-page"
|
|
46
|
+
],
|
|
47
|
+
"accessibility": [
|
|
48
|
+
"ShellPage exposes the routed page as the main landmark; render exactly one active ShellPage inside ShellApp.",
|
|
49
|
+
"Provide exactly one BarTitle through the header slot so the page has one h1.",
|
|
50
|
+
"Keep loading, empty, validation, error, progress, success, and failure communication in the default product-content slot.",
|
|
51
|
+
"Responsive variant changes preserve the same heading, controls, accessible names, and keyboard focus targets."
|
|
52
|
+
],
|
|
53
|
+
"states": [
|
|
54
|
+
"headerPresentation=auto selects expanded, compact, or constrained from ShellPage's own inline capacity and scroll position.",
|
|
55
|
+
"expanded, compact, and constrained are explicit overrides for exceptional layouts and deterministic examples.",
|
|
56
|
+
"contentInset=default supplies standard page gutters; none permits maps, canvases, and other full-bleed content.",
|
|
57
|
+
"The required header slot accepts one BarTitle. The default slot owns router output and all page product content."
|
|
58
|
+
],
|
|
59
|
+
"responsiveBehavior": [
|
|
60
|
+
"ShellPage observes its own rendered width instead of viewport media queries or ShellApp desktop/tablet modes.",
|
|
61
|
+
"Roomy pages expand at the top and compact after scroll; medium-capacity pages start compact; narrow pages use the constrained header variant.",
|
|
62
|
+
"A newly navigated header resolves current page capacity before it is revealed, so persistent tool or navigation chrome cannot flash the expanded presentation first.",
|
|
63
|
+
"Token-based capacity bands use hysteresis so opening tools or resizing navigation cannot oscillate near a boundary.",
|
|
64
|
+
"Scroll compaction preserves the expanded page flow offset so content never jumps beneath the sticky header.",
|
|
65
|
+
"ShellPage relies on ShellApp's content scroller and never introduces nested scrolling."
|
|
66
|
+
],
|
|
67
|
+
"frameworkCaveats": {
|
|
68
|
+
"customElements": [
|
|
69
|
+
"Import ShellPage and BarTitle, place one ds-bar-title in slot=header, and render route content as default children.",
|
|
70
|
+
"Do not set BarTitle.variant while ShellPage owns headerPresentation."
|
|
71
|
+
],
|
|
72
|
+
"react": [
|
|
73
|
+
"Render DsBarTitle with slot=header inside DsShellPage and keep routed content as the remaining children.",
|
|
74
|
+
"Use contentInset none only for deliberately full-bleed page products."
|
|
75
|
+
],
|
|
76
|
+
"angular": [
|
|
77
|
+
"Import the generated standalone DsShellPage and DsBarTitle adapters, preserve slot=header, and place router-outlet in the ShellPage default slot.",
|
|
78
|
+
"Do not add another overflow auto wrapper around the routed page."
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"references": [
|
|
82
|
+
{
|
|
83
|
+
"label": "Page-family and responsive examples",
|
|
84
|
+
"path": "src/wc/components/ShellPage/ShellPage.stories.ts"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"label": "Cross-browser geometry and interaction coverage",
|
|
88
|
+
"path": "tests/e2e/bar-title.spec.ts"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"label": "Reusable framework composition",
|
|
92
|
+
"path": "agent/patterns/shell-page/pattern.agent.json"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"api": {
|
|
97
|
+
"props": {
|
|
98
|
+
"contentInset": {
|
|
99
|
+
"type": "ShellPageContentInset",
|
|
100
|
+
"resolvedType": "\"default\" | \"none\"",
|
|
101
|
+
"attribute": "content-inset",
|
|
102
|
+
"default": "'default'",
|
|
103
|
+
"required": false,
|
|
104
|
+
"mutable": false,
|
|
105
|
+
"description": "Standard page gutters, or no inset for full-bleed page content."
|
|
106
|
+
},
|
|
107
|
+
"headerPresentation": {
|
|
108
|
+
"type": "ShellPageHeaderPresentation",
|
|
109
|
+
"resolvedType": "\"auto\" | \"compact\" | \"constrained\" | \"expanded\"",
|
|
110
|
+
"attribute": "header-presentation",
|
|
111
|
+
"default": "'auto'",
|
|
112
|
+
"required": false,
|
|
113
|
+
"mutable": false,
|
|
114
|
+
"description": "Automatic container/scroll behavior, or an explicit header variant override."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"events": [],
|
|
118
|
+
"methods": [],
|
|
119
|
+
"slots": []
|
|
120
|
+
},
|
|
121
|
+
"props": {
|
|
122
|
+
"contentInset": {
|
|
123
|
+
"type": "ShellPageContentInset",
|
|
124
|
+
"resolvedType": "\"default\" | \"none\"",
|
|
125
|
+
"attribute": "content-inset",
|
|
126
|
+
"default": "'default'",
|
|
127
|
+
"required": false,
|
|
128
|
+
"mutable": false,
|
|
129
|
+
"description": "Standard page gutters, or no inset for full-bleed page content."
|
|
130
|
+
},
|
|
131
|
+
"headerPresentation": {
|
|
132
|
+
"type": "ShellPageHeaderPresentation",
|
|
133
|
+
"resolvedType": "\"auto\" | \"compact\" | \"constrained\" | \"expanded\"",
|
|
134
|
+
"attribute": "header-presentation",
|
|
135
|
+
"default": "'auto'",
|
|
136
|
+
"required": false,
|
|
137
|
+
"mutable": false,
|
|
138
|
+
"description": "Automatic container/scroll behavior, or an explicit header variant override."
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"events": [],
|
|
142
|
+
"methods": [],
|
|
143
|
+
"slots": [],
|
|
144
|
+
"exports": {
|
|
145
|
+
"customElement": "ds-shell-page",
|
|
146
|
+
"react": "DsShellPage",
|
|
147
|
+
"angular": "DsShellPage"
|
|
148
|
+
},
|
|
149
|
+
"consumption": {
|
|
150
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
151
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
152
|
+
"customElements": {
|
|
153
|
+
"import": "import '@ds-mo/ui/dist/components/ds-shell-page.js';",
|
|
154
|
+
"example": "<ds-shell-page></ds-shell-page>"
|
|
155
|
+
},
|
|
156
|
+
"react": {
|
|
157
|
+
"import": "import { DsShellPage } from '@ds-mo/ui/react';",
|
|
158
|
+
"example": "<DsShellPage />"
|
|
159
|
+
},
|
|
160
|
+
"angular": {
|
|
161
|
+
"import": "import { DsShellPage } from '@ds-mo/ui/angular/ds-shell-page';",
|
|
162
|
+
"example": "<ds-shell-page></ds-shell-page>"
|
|
163
|
+
},
|
|
164
|
+
"peerDependencies": {
|
|
165
|
+
"required": [
|
|
166
|
+
"@ds-mo/tokens ^3.1.1",
|
|
167
|
+
"@ds-mo/icons ^6.4.0"
|
|
168
|
+
],
|
|
169
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"dependencies": [
|
|
174
|
+
"@ds-mo/ui",
|
|
175
|
+
"@ds-mo/tokens"
|
|
176
|
+
],
|
|
177
|
+
"registryDependencies": [],
|
|
178
|
+
"files": [
|
|
179
|
+
{
|
|
180
|
+
"path": "src/wc/components/ShellPage/ShellPage.css",
|
|
181
|
+
"content": ":host {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 100%;\n box-sizing: border-box;\n background: var(--color-background-primary);\n}\n\n.shell-page__scroll-sentinel {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-stroke-width-012);\n pointer-events: none;\n}\n\n.shell-page__sticky-header {\n position: sticky;\n inset-block-start: 0;\n z-index: var(--dimension-z-index-raised);\n min-width: 0;\n background: var(--color-background-primary);\n overflow: visible;\n overflow-anchor: none;\n}\n\n.shell-page__sticky-header ::slotted(ds-bar-title) {\n display: block;\n min-width: 0;\n}\n\n.shell-page__flow-spacer {\n height: var(--ds-shell-page-flow-spacer, 0);\n background: var(--color-background-primary);\n}\n\n.shell-page__content {\n min-width: 0;\n box-sizing: border-box;\n background: var(--color-background-primary);\n}\n\n:host(.shell-page-host--inset-default) .shell-page__content {\n padding: var(--dimension-space-400) var(--dimension-space-400) var(--dimension-space-800);\n}\n\n:host(.shell-page-host--inset-none) .shell-page__content {\n padding: 0;\n}\n",
|
|
182
|
+
"type": "registry:ui"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"path": "src/wc/components/ShellPage/ShellPage.tsx",
|
|
186
|
+
"content": "import { Component, Element, h, Host, Prop, State } from '@stencil/core';\nimport { createRafCoalescer } from '../../shell/chrome-transition';\nimport { resolveCssLengthPx } from '../../utils/resolve-css-length-px';\nimport type { BarTitleVariant } from '../BarTitle/bar-title-types';\nimport { resolveShellPageCapacity, resolveShellPageHeaderVariant } from './shell-page-responsive';\nimport type {\n ShellPageCapacity,\n ShellPageContentInset,\n ShellPageHeaderPresentation,\n} from './shell-page-types';\n\ntype BarTitleElement = HTMLElement & { variant: BarTitleVariant };\n\n@Component({\n tag: 'ds-shell-page',\n styleUrl: 'ShellPage.css',\n scoped: true,\n})\nexport class ShellPage {\n private static readonly MAX_HEADER_REVEAL_FRAMES = 12;\n\n @Element() el!: HTMLElement;\n\n /** Automatic container/scroll behavior, or an explicit header variant override. */\n @Prop() headerPresentation: ShellPageHeaderPresentation = 'auto';\n\n /** Standard page gutters, or no inset for full-bleed page content. */\n @Prop() contentInset: ShellPageContentInset = 'default';\n\n @State() private capacity: ShellPageCapacity | null = null;\n @State() private pageTopVisible = true;\n\n private sentinelEl: HTMLElement | null = null;\n private spacerEl: HTMLElement | null = null;\n private headerEl: BarTitleElement | null = null;\n private expandedHeaderHeight = 0;\n private resizeObserver: ResizeObserver | null = null;\n private headerResizeObserver: ResizeObserver | null = null;\n private headerRevealFrame: number | null = null;\n private scrollRoot: HTMLElement | Window | null = null;\n private readonly layoutCoalescer = createRafCoalescer(() => this.syncCapacity());\n\n componentDidLoad() {\n // Resolve the already-laid-out page width before adopting a slotted header.\n // Waiting for the first ResizeObserver/RAF lets BarTitle paint its default\n // expanded variant for one frame when persistent shell chrome narrows a\n // newly navigated page.\n this.syncCapacity();\n this.observeHeader(this.findHeader());\n this.connectObservers();\n this.connectScrollRoot();\n this.layoutCoalescer.schedule();\n }\n\n componentDidRender() {\n this.syncHeaderVariant();\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.cancelHeaderReveal();\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = null;\n this.layoutCoalescer.cancel();\n }\n\n private get effectiveVariant(): BarTitleVariant {\n return resolveShellPageHeaderVariant(\n this.headerPresentation,\n this.capacity ?? 'roomy',\n this.pageTopVisible\n );\n }\n\n private get isScrollCompacted(): boolean {\n return this.headerPresentation === 'auto' && this.capacity === 'roomy' && !this.pageTopVisible;\n }\n\n private connectObservers() {\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.layoutCoalescer.schedule());\n this.resizeObserver.observe(this.el);\n }\n }\n\n private composedParent(element: Element): Element | null {\n return (element as HTMLElement).assignedSlot?.parentElement ?? element.parentElement;\n }\n\n private findScrollRoot(): HTMLElement | Window {\n let ancestor = this.composedParent(this.el);\n while (ancestor && ancestor !== document.documentElement) {\n const overflow = getComputedStyle(ancestor).overflowY;\n if (overflow === 'auto' || overflow === 'scroll') return ancestor as HTMLElement;\n ancestor = this.composedParent(ancestor);\n }\n return window;\n }\n\n private connectScrollRoot() {\n this.scrollRoot?.removeEventListener('scroll', this.handleScroll);\n this.scrollRoot = this.findScrollRoot();\n this.scrollRoot.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n\n private handleScroll = () => {\n if (!this.sentinelEl || !this.scrollRoot) return;\n const rootTop =\n this.scrollRoot === window ? 0 : (this.scrollRoot as HTMLElement).getBoundingClientRect().top;\n this.pageTopVisible = this.sentinelEl.getBoundingClientRect().top >= rootTop;\n };\n\n private findHeader(): BarTitleElement | null {\n return this.el.querySelector<BarTitleElement>('ds-bar-title');\n }\n\n private syncCapacity() {\n const inlineSize = this.el.getBoundingClientRect().width;\n if (inlineSize <= 0) return;\n\n const compactAt = resolveCssLengthPx('--dimension-panel-width-2xl', 0);\n const constrainedAt = resolveCssLengthPx('--dimension-panel-width-lg', 0);\n const hysteresis = resolveCssLengthPx('--dimension-space-100', 0);\n if (compactAt <= 0 || constrainedAt <= 0) return;\n\n const next = resolveShellPageCapacity({\n inlineSize,\n compactAt,\n constrainedAt,\n hysteresis,\n previous: this.capacity,\n });\n if (next !== this.capacity) this.capacity = next;\n else this.syncHeaderVariant();\n }\n\n private get compactHeaderHeight(): number {\n return resolveCssLengthPx('--dimension-size-600', 0);\n }\n\n private setSpacerHeight(height: number) {\n if (!this.spacerEl) return;\n this.spacerEl.style.setProperty('--ds-shell-page-flow-spacer', `${Math.max(0, height)}px`);\n }\n\n private variantClass(variant: BarTitleVariant): string {\n return `bar-title-host--${variant}`;\n }\n\n private cancelHeaderReveal() {\n if (this.headerRevealFrame !== null) cancelAnimationFrame(this.headerRevealFrame);\n this.headerRevealFrame = null;\n this.headerEl?.removeAttribute('data-shell-page-syncing');\n }\n\n private revealHeaderWhenSynced(header: BarTitleElement, variant: BarTitleVariant) {\n // Stencil re-renders within a frame or two of a prop change, but this cap\n // guarantees the header can never stay hidden indefinitely if that\n // assumption ever breaks.\n let framesRemaining = ShellPage.MAX_HEADER_REVEAL_FRAMES;\n const reveal = () => {\n if (this.headerEl !== header) {\n header.removeAttribute('data-shell-page-syncing');\n return;\n }\n framesRemaining -= 1;\n if (framesRemaining <= 0 || header.classList.contains(this.variantClass(variant))) {\n header.removeAttribute('data-shell-page-syncing');\n this.headerRevealFrame = null;\n return;\n }\n this.headerRevealFrame = requestAnimationFrame(reveal);\n };\n this.headerRevealFrame = requestAnimationFrame(reveal);\n }\n\n private syncHeaderVariant(concealUntilSynced = false) {\n const header = this.headerEl;\n if (!header) return;\n\n const next = this.effectiveVariant;\n if (header.variant === 'expanded') {\n this.expandedHeaderHeight = header.getBoundingClientRect().height;\n }\n\n this.setSpacerHeight(\n this.isScrollCompacted ? this.expandedHeaderHeight - this.compactHeaderHeight : 0\n );\n\n const renderedVariantIsStale = !header.classList.contains(this.variantClass(next));\n if (concealUntilSynced && renderedVariantIsStale) {\n header.setAttribute('data-shell-page-syncing', '');\n }\n if (header.variant !== next) header.variant = next;\n if (concealUntilSynced && renderedVariantIsStale) {\n this.revealHeaderWhenSynced(header, next);\n }\n }\n\n private observeHeader(header: BarTitleElement | null) {\n this.cancelHeaderReveal();\n this.headerResizeObserver?.disconnect();\n this.headerResizeObserver = null;\n this.headerEl = header;\n this.expandedHeaderHeight = 0;\n this.setSpacerHeight(0);\n\n if (!header) return;\n if (typeof ResizeObserver !== 'undefined') {\n this.headerResizeObserver = new ResizeObserver(() => {\n if (header.variant === 'expanded') {\n this.expandedHeaderHeight = header.getBoundingClientRect().height;\n }\n });\n this.headerResizeObserver.observe(header);\n }\n this.syncHeaderVariant(true);\n }\n\n private handleHeaderSlotChange = (event: Event) => {\n const slot = event.target as HTMLSlotElement;\n const header = slot\n .assignedElements({ flatten: true })\n .find(element => element.tagName.toLowerCase() === 'ds-bar-title');\n this.observeHeader((header as BarTitleElement | undefined) ?? null);\n };\n\n render() {\n return (\n <Host\n role=\"main\"\n class={{\n 'shell-page-host--inset-default': this.contentInset === 'default',\n 'shell-page-host--inset-none': this.contentInset === 'none',\n [`shell-page-host--header-${this.effectiveVariant}`]: true,\n }}\n >\n <div\n ref={el => {\n this.sentinelEl = el ?? null;\n }}\n class=\"shell-page__scroll-sentinel\"\n aria-hidden=\"true\"\n />\n <div class=\"shell-page__sticky-header\">\n <slot name=\"header\" onSlotchange={this.handleHeaderSlotChange} />\n </div>\n <div\n ref={el => {\n this.spacerEl = el ?? null;\n }}\n class=\"shell-page__flow-spacer\"\n aria-hidden=\"true\"\n />\n <div class=\"shell-page__content\">\n <slot />\n </div>\n </Host>\n );\n }\n}\n",
|
|
187
|
+
"type": "registry:ui"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"path": "src/wc/components/ShellPage/shell-page-responsive.ts",
|
|
191
|
+
"content": "import type { ShellPageCapacity, ShellPageHeaderPresentation } from './shell-page-types';\nimport type { BarTitleVariant } from '../BarTitle/bar-title-types';\n\nexport interface ShellPageCapacityOptions {\n inlineSize: number;\n compactAt: number;\n constrainedAt: number;\n hysteresis: number;\n previous: ShellPageCapacity | null;\n}\n\nexport function resolveShellPageCapacity({\n inlineSize,\n compactAt,\n constrainedAt,\n hysteresis,\n previous,\n}: ShellPageCapacityOptions): ShellPageCapacity {\n if (previous === null) {\n if (inlineSize <= constrainedAt) return 'constrained';\n if (inlineSize <= compactAt) return 'compact';\n return 'roomy';\n }\n\n if (previous === 'constrained') {\n if (inlineSize <= constrainedAt + hysteresis) return 'constrained';\n return inlineSize <= compactAt ? 'compact' : 'roomy';\n }\n\n if (inlineSize <= constrainedAt) return 'constrained';\n\n if (previous === 'compact') {\n return inlineSize <= compactAt + hysteresis ? 'compact' : 'roomy';\n }\n\n return inlineSize <= compactAt ? 'compact' : 'roomy';\n}\n\nexport function resolveShellPageHeaderVariant(\n presentation: ShellPageHeaderPresentation,\n capacity: ShellPageCapacity,\n pageTopVisible: boolean\n): BarTitleVariant {\n if (presentation !== 'auto') return presentation;\n if (capacity === 'constrained') return 'constrained';\n if (capacity === 'compact' || !pageTopVisible) return 'compact';\n return 'expanded';\n}\n",
|
|
192
|
+
"type": "registry:ui"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"path": "src/wc/components/ShellPage/shell-page-types.ts",
|
|
196
|
+
"content": "import type { BarTitleVariant } from '../BarTitle/bar-title-types';\n\nexport type ShellPageHeaderPresentation = 'auto' | BarTitleVariant;\n\nexport type ShellPageContentInset = 'default' | 'none';\n\nexport type ShellPageCapacity = 'roomy' | 'compact' | 'constrained';\n",
|
|
197
|
+
"type": "registry:ui"
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
}
|