@ds-mo/ui 1.7.4 → 1.8.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/.build-stamp +1 -1
- package/dist/components/ds-app-shell.js +1 -1
- package/dist/components/ds-app-shell.js.map +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-bar-nav-action.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-checkbox.js +1 -1
- package/dist/components/ds-fade.js +1 -1
- package/dist/components/ds-fade.js.map +1 -1
- package/dist/components/ds-field.js +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-tools.d.ts +11 -0
- package/dist/components/ds-panel-tools.js +2 -0
- package/dist/components/ds-panel-tools.js.map +1 -0
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/ds-tab-group-nav.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-toggle.js +1 -1
- package/dist/components/p-B8VZmH43.js +2 -0
- package/dist/components/p-B8VZmH43.js.map +1 -0
- package/dist/components/p-BucV1U6A.js +2 -0
- package/dist/components/p-BucV1U6A.js.map +1 -0
- package/dist/components/p-CX7-_qOW.js +2 -0
- package/dist/components/p-CX7-_qOW.js.map +1 -0
- package/dist/components/{p-D7HuiVTa.js → p-DZDXwEMg.js} +2 -2
- package/dist/components/{p-CiQqF0cR.js → p-DrvpsLK6.js} +2 -2
- package/dist/components/p-TtnqbcnR.js +2 -0
- package/dist/components/p-TtnqbcnR.js.map +1 -0
- package/dist/types/components/BarNav/BarNav.d.ts +9 -2
- package/dist/types/components/Fade/Fade.d.ts +5 -0
- package/dist/types/components/PanelTools/PanelTools.d.ts +27 -0
- package/dist/types/components/PanelTools/index.d.ts +3 -0
- package/dist/types/components/PanelTools/panel-tools-types.d.ts +5 -0
- package/dist/types/components.d.ts +39 -0
- package/dist/types/nav/index.d.ts +1 -1
- package/dist/types/nav/shell-view-transition.d.ts +3 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/scroll-edge-fade.d.ts +13 -0
- package/package.json +2 -2
- package/src/angular/index.ts +1 -0
- package/src/angular/proxies.ts +23 -0
- package/src/react/components.ts +1 -0
- package/src/react/ds-panel-tools.ts +26 -0
- package/src/wc/components/AppShell/AppShell.tsx +7 -2
- package/src/wc/components/BarNav/BarNav.tsx +75 -19
- package/src/wc/components/Fade/Fade.tsx +22 -1
- package/src/wc/components/PanelNav/PanelNav.tsx +10 -9
- package/src/wc/components/PanelTools/PanelTools.tsx +163 -0
- package/src/wc/components/PanelTools/index.ts +2 -0
- package/src/wc/components/PanelTools/panel-tools-types.ts +18 -0
- package/src/wc/components.d.ts +39 -0
- package/src/wc/nav/index.ts +2 -0
- package/src/wc/nav/shell-gradient.ts +2 -2
- package/src/wc/nav/shell-view-transition.ts +24 -5
- package/src/wc/utils/index.ts +7 -0
- package/src/wc/utils/scroll-edge-fade.css +73 -0
- package/src/wc/utils/scroll-edge-fade.ts +38 -0
- package/dist/components/p-CXkDP-ct.js +0 -2
- package/dist/components/p-CXkDP-ct.js.map +0 -1
- package/dist/components/p-DAycHO-h.js +0 -2
- package/dist/components/p-DAycHO-h.js.map +0 -1
- package/dist/components/p-DfL2MOso.js +0 -2
- package/dist/components/p-DfL2MOso.js.map +0 -1
- package/dist/components/p-vW9EMP8s.js +0 -2
- package/dist/components/p-vW9EMP8s.js.map +0 -1
- /package/dist/components/{p-D7HuiVTa.js.map → p-DZDXwEMg.js.map} +0 -0
- /package/dist/components/{p-CiQqF0cR.js.map → p-DrvpsLK6.js.map} +0 -0
package/src/react/components.ts
CHANGED
|
@@ -28,6 +28,7 @@ export { DsMenu } from "./ds-menu.js";
|
|
|
28
28
|
export { DsModal } from "./ds-modal.js";
|
|
29
29
|
export { DsPagination } from "./ds-pagination.js";
|
|
30
30
|
export { DsPanelNav } from "./ds-panel-nav.js";
|
|
31
|
+
export { DsPanelTools } from "./ds-panel-tools.js";
|
|
31
32
|
export { DsRadioGroup } from "./ds-radio-group.js";
|
|
32
33
|
export { DsScrollbar } from "./ds-scrollbar.js";
|
|
33
34
|
export { DsSelect } from "./ds-select.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This file was automatically generated by the Stencil React Output Target.
|
|
5
|
+
* Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* eslint-disable */
|
|
9
|
+
|
|
10
|
+
import type { StencilReactComponent } from '@stencil/react-output-target/runtime';
|
|
11
|
+
import { createComponent } from '@stencil/react-output-target/runtime';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
|
|
14
|
+
import type { Components } from "@ds-mo/ui/dist/components";
|
|
15
|
+
import { DsPanelTools as DsPanelToolsElement, defineCustomElement as defineDsPanelTools } from "@ds-mo/ui/dist/components/ds-panel-tools.js";
|
|
16
|
+
|
|
17
|
+
export type DsPanelToolsEvents = NonNullable<unknown>;
|
|
18
|
+
|
|
19
|
+
export const DsPanelTools: StencilReactComponent<DsPanelToolsElement, DsPanelToolsEvents, Components.DsPanelTools> = /*@__PURE__*/ createComponent<DsPanelToolsElement, DsPanelToolsEvents, Components.DsPanelTools>({
|
|
20
|
+
tagName: 'ds-panel-tools',
|
|
21
|
+
elementClass: DsPanelToolsElement,
|
|
22
|
+
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
|
|
23
|
+
react: React,
|
|
24
|
+
events: {} as DsPanelToolsEvents,
|
|
25
|
+
defineCustomElement: defineDsPanelTools
|
|
26
|
+
});
|
|
@@ -169,8 +169,13 @@ export class AppShell {
|
|
|
169
169
|
<div class="app-shell__bar">
|
|
170
170
|
<slot name="bar" />
|
|
171
171
|
</div>
|
|
172
|
-
<div class="app-
|
|
173
|
-
<
|
|
172
|
+
<div class="app-shell__workspace">
|
|
173
|
+
<div class="app-shell__content">
|
|
174
|
+
<slot />
|
|
175
|
+
</div>
|
|
176
|
+
<div class="app-shell__tools">
|
|
177
|
+
<slot name="tools" />
|
|
178
|
+
</div>
|
|
174
179
|
</div>
|
|
175
180
|
</div>
|
|
176
181
|
</div>
|
|
@@ -105,6 +105,10 @@ export class BarNav {
|
|
|
105
105
|
private resizeObserver: ResizeObserver | null = null;
|
|
106
106
|
private overflowCheckScheduled = false;
|
|
107
107
|
private intrinsicWidthRetryCount = 0;
|
|
108
|
+
/** Matches `basePath` once tabs + URL are reconciled for the active section. */
|
|
109
|
+
@State() private committedSection = '';
|
|
110
|
+
/** Section waiting for URL + tabs to catch up after a cross-area navigation. */
|
|
111
|
+
@State() private pendingSection = '';
|
|
108
112
|
|
|
109
113
|
private get effectiveValue(): string {
|
|
110
114
|
if (this.currentUrl && this.basePath) {
|
|
@@ -125,23 +129,10 @@ export class BarNav {
|
|
|
125
129
|
@Watch('tabsJson')
|
|
126
130
|
@Watch('actions')
|
|
127
131
|
@Watch('actionsJson')
|
|
128
|
-
onPropsChange() {
|
|
129
|
-
this.resolvedTabs = this.tabsJson
|
|
130
|
-
? parseJsonArrayProp(this.tabsJson, [])
|
|
131
|
-
: (this.tabs ?? []);
|
|
132
|
-
this.resolvedActions = this.actionsJson
|
|
133
|
-
? parseJsonArrayProp(this.actionsJson, [])
|
|
134
|
-
: (this.actions ?? []);
|
|
135
|
-
this.syncValueFromUrl();
|
|
136
|
-
this.intrinsicWidthRetryCount = 0;
|
|
137
|
-
this.scheduleOverflowCheck();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
132
|
@Watch('currentUrl')
|
|
141
133
|
@Watch('basePath')
|
|
142
|
-
|
|
143
|
-
this.
|
|
144
|
-
this.scheduleOverflowCheck();
|
|
134
|
+
onHostPropsChange() {
|
|
135
|
+
this.applyHostProps();
|
|
145
136
|
}
|
|
146
137
|
|
|
147
138
|
@Watch('value')
|
|
@@ -179,7 +170,7 @@ export class BarNav {
|
|
|
179
170
|
}
|
|
180
171
|
|
|
181
172
|
componentWillLoad() {
|
|
182
|
-
this.
|
|
173
|
+
this.applyHostProps();
|
|
183
174
|
}
|
|
184
175
|
|
|
185
176
|
componentDidLoad() {
|
|
@@ -215,7 +206,7 @@ export class BarNav {
|
|
|
215
206
|
this.actionsJson,
|
|
216
207
|
)
|
|
217
208
|
) {
|
|
218
|
-
this.
|
|
209
|
+
this.applyHostProps();
|
|
219
210
|
} else if (this.currentUrl && this.basePath) {
|
|
220
211
|
this.syncValueFromUrl();
|
|
221
212
|
}
|
|
@@ -367,22 +358,81 @@ export class BarNav {
|
|
|
367
358
|
this.menuOpen = false;
|
|
368
359
|
}
|
|
369
360
|
|
|
361
|
+
/** Batch tabs/basePath/currentUrl updates so URL derivation never runs with a mixed section. */
|
|
362
|
+
private incomingTabs(): BarNavTab[] {
|
|
363
|
+
return this.tabsJson
|
|
364
|
+
? parseJsonArrayProp(this.tabsJson, [])
|
|
365
|
+
: (this.tabs ?? []);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private incomingActions(): BarNavActionItem[] {
|
|
369
|
+
return this.actionsJson
|
|
370
|
+
? parseJsonArrayProp(this.actionsJson, [])
|
|
371
|
+
: (this.actions ?? []);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private applyHostProps() {
|
|
375
|
+
const nextBasePath = this.basePath;
|
|
376
|
+
const sectionChanged =
|
|
377
|
+
nextBasePath !== '' &&
|
|
378
|
+
this.committedSection !== '' &&
|
|
379
|
+
nextBasePath !== this.committedSection;
|
|
380
|
+
|
|
381
|
+
if (sectionChanged) {
|
|
382
|
+
this.urlDerivedValue = '';
|
|
383
|
+
this.committedSection = '';
|
|
384
|
+
this.pendingSection = nextBasePath;
|
|
385
|
+
this.resolvedTabs = [];
|
|
386
|
+
this.resolvedActions = this.incomingActions();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (this.pendingSection) {
|
|
391
|
+
if (!nextBasePath || nextBasePath !== this.pendingSection) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!this.currentUrl.startsWith(this.pendingSection)) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const tabs = this.incomingTabs();
|
|
398
|
+
if (tabs.length === 0) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
this.pendingSection = '';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
this.resolvedTabs = this.incomingTabs();
|
|
405
|
+
this.resolvedActions = this.incomingActions();
|
|
406
|
+
this.syncValueFromUrl();
|
|
407
|
+
this.committedSection = nextBasePath;
|
|
408
|
+
this.intrinsicWidthRetryCount = 0;
|
|
409
|
+
this.scheduleOverflowCheck();
|
|
410
|
+
}
|
|
411
|
+
|
|
370
412
|
private syncMenuSections() {
|
|
371
413
|
if (!this.menuEl) return;
|
|
372
414
|
this.menuEl.sections = tabsToMenuSections(this.resolvedTabs, this.effectiveValue);
|
|
373
415
|
}
|
|
374
416
|
|
|
375
417
|
private syncValueFromUrl() {
|
|
418
|
+
const tabs = this.incomingTabs();
|
|
419
|
+
|
|
376
420
|
if (!this.currentUrl || !this.basePath) {
|
|
377
421
|
this.urlDerivedValue = '';
|
|
378
422
|
this.hideTabsForDetailRoute = false;
|
|
379
423
|
return;
|
|
380
424
|
}
|
|
381
425
|
|
|
426
|
+
if (!this.currentUrl.startsWith(this.basePath)) {
|
|
427
|
+
this.urlDerivedValue = '';
|
|
428
|
+
this.hideTabsForDetailRoute = false;
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
382
432
|
const { value, hideTabs } = deriveBarNavValueFromUrl(
|
|
383
433
|
this.currentUrl,
|
|
384
434
|
this.basePath,
|
|
385
|
-
|
|
435
|
+
tabs,
|
|
386
436
|
);
|
|
387
437
|
this.urlDerivedValue = value;
|
|
388
438
|
this.hideTabsForDetailRoute = hideTabs;
|
|
@@ -449,8 +499,12 @@ export class BarNav {
|
|
|
449
499
|
}
|
|
450
500
|
|
|
451
501
|
render() {
|
|
452
|
-
const
|
|
502
|
+
const sectionReady =
|
|
503
|
+
this.basePath !== '' && this.committedSection === this.basePath;
|
|
504
|
+
const hasTabs =
|
|
505
|
+
sectionReady && this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute;
|
|
453
506
|
const tabSurface = this.tabSurface;
|
|
507
|
+
const tabGroupKey = this.basePath || 'no-section';
|
|
454
508
|
|
|
455
509
|
return (
|
|
456
510
|
<Host>
|
|
@@ -472,6 +526,7 @@ export class BarNav {
|
|
|
472
526
|
{hasTabs && (
|
|
473
527
|
<div class="bar-nav__tabs-probe" aria-hidden="true" inert>
|
|
474
528
|
<ds-tab-group-nav
|
|
529
|
+
key={`probe-${tabGroupKey}`}
|
|
475
530
|
ref={el => {
|
|
476
531
|
this.probeTabGroupEl = el ?? null;
|
|
477
532
|
if (el) {
|
|
@@ -489,6 +544,7 @@ export class BarNav {
|
|
|
489
544
|
{hasTabs && !this.tabsCollapsed && (
|
|
490
545
|
<div class="bar-nav__left">
|
|
491
546
|
<ds-tab-group-nav
|
|
547
|
+
key={`visible-${tabGroupKey}`}
|
|
492
548
|
class="bar-nav__tabs-visible"
|
|
493
549
|
ref={el => {
|
|
494
550
|
this.visibleTabGroupEl = el ?? null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Component, Prop, h, Host } from '@stencil/core';
|
|
1
|
+
import { Component, Prop, Element, State, h, Host } from '@stencil/core';
|
|
2
|
+
import { isShellGradientActive } from '../../nav/badge-gradient-ring';
|
|
2
3
|
|
|
3
4
|
export type FadeSide = 'top' | 'bottom' | 'left' | 'right';
|
|
4
5
|
export type FadeSurface =
|
|
@@ -55,6 +56,11 @@ const SURFACE_BACKGROUND: Record<FadeSurface, string> = {
|
|
|
55
56
|
scoped: true,
|
|
56
57
|
})
|
|
57
58
|
export class Fade {
|
|
59
|
+
@Element() el!: HTMLElement;
|
|
60
|
+
|
|
61
|
+
/** When under `ds-app-shell[gradient]`, composites the shell wash over the base fade. */
|
|
62
|
+
@State() private shellGradientChrome: 'panel' | 'bar' | null = null;
|
|
63
|
+
|
|
58
64
|
/** Edge where the fade is anchored. */
|
|
59
65
|
@Prop() side: FadeSide = 'bottom';
|
|
60
66
|
|
|
@@ -76,6 +82,18 @@ export class Fade {
|
|
|
76
82
|
/** Controls visibility without removing the element from layout/positioning. */
|
|
77
83
|
@Prop() visible: boolean = true;
|
|
78
84
|
|
|
85
|
+
componentDidLoad() {
|
|
86
|
+
this.syncShellGradient();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private syncShellGradient() {
|
|
90
|
+
if (!isShellGradientActive(this.el)) {
|
|
91
|
+
this.shellGradientChrome = null;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.shellGradientChrome = this.el.closest('ds-bar-nav') ? 'bar' : 'panel';
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
private resolveSize(): string {
|
|
80
98
|
if (this.height) return this.height;
|
|
81
99
|
|
|
@@ -100,6 +118,9 @@ export class Fade {
|
|
|
100
118
|
'fade--hidden': !this.visible,
|
|
101
119
|
'fade--vertical-edge': isVerticalEdge,
|
|
102
120
|
'fade--horizontal-edge': !isVerticalEdge,
|
|
121
|
+
'fade--shell-gradient': this.shellGradientChrome !== null,
|
|
122
|
+
'fade--shell-gradient-panel': this.shellGradientChrome === 'panel',
|
|
123
|
+
'fade--shell-gradient-bar': this.shellGradientChrome === 'bar',
|
|
103
124
|
[`fade--${this.side}`]: true,
|
|
104
125
|
}}
|
|
105
126
|
style={{
|
|
@@ -37,6 +37,7 @@ const vtResolvers = new WeakMap<object, () => void>();
|
|
|
37
37
|
@Component({
|
|
38
38
|
tag: 'ds-panel-nav',
|
|
39
39
|
styleUrl: 'PanelNav.css',
|
|
40
|
+
styleUrls: ['../../utils/scroll-edge-fade.css'],
|
|
40
41
|
scoped: true,
|
|
41
42
|
})
|
|
42
43
|
export class PanelNav {
|
|
@@ -550,7 +551,15 @@ export class PanelNav {
|
|
|
550
551
|
</div>
|
|
551
552
|
|
|
552
553
|
{/* ── Scrollable body ── */}
|
|
553
|
-
<div
|
|
554
|
+
<div
|
|
555
|
+
class={{
|
|
556
|
+
'panel-nav__body': true,
|
|
557
|
+
'scroll-edge-fade': true,
|
|
558
|
+
'scroll-edge-fade--bottom': true,
|
|
559
|
+
'scroll-edge-fade--at-end': this.atBottom,
|
|
560
|
+
}}
|
|
561
|
+
onScroll={e => this.handleBodyScroll(e)}
|
|
562
|
+
>
|
|
554
563
|
{(() => {
|
|
555
564
|
let flatIdx = 0;
|
|
556
565
|
return this.parsedGroups.map(group => (
|
|
@@ -572,14 +581,6 @@ export class PanelNav {
|
|
|
572
581
|
|
|
573
582
|
{/* ── Footer ── */}
|
|
574
583
|
<div class="panel-nav__footer">
|
|
575
|
-
<ds-fade
|
|
576
|
-
class="panel-nav__footer-fade"
|
|
577
|
-
side="bottom"
|
|
578
|
-
size="size-600"
|
|
579
|
-
background="var(--_nav-bg)"
|
|
580
|
-
visible={!this.atBottom}
|
|
581
|
-
/>
|
|
582
|
-
|
|
583
584
|
{/* Left icon button — Gear in navigation style, Dashboard in default style */}
|
|
584
585
|
<button
|
|
585
586
|
type="button"
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Component, Prop, Element, State, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import {
|
|
3
|
+
PANEL_TOOLS_LABELS,
|
|
4
|
+
type PanelToolsToolId,
|
|
5
|
+
} from './panel-tools-types';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
tag: 'ds-panel-tools',
|
|
9
|
+
styleUrl: 'PanelTools.css',
|
|
10
|
+
scoped: true,
|
|
11
|
+
})
|
|
12
|
+
export class PanelTools {
|
|
13
|
+
@Element() el!: HTMLElement;
|
|
14
|
+
|
|
15
|
+
/** When false, width animates to 0. */
|
|
16
|
+
@Prop({ reflect: true }) open: boolean = false;
|
|
17
|
+
|
|
18
|
+
/** Active tool view — `search`, `messages`, `stacks`, `activity`, or `agents`. */
|
|
19
|
+
@Prop({ attribute: 'active-tool', reflect: true }) activeTool: PanelToolsToolId | '' = '';
|
|
20
|
+
|
|
21
|
+
/** Stays true until the close width transition finishes. */
|
|
22
|
+
@State() private expanded = false;
|
|
23
|
+
|
|
24
|
+
/** Suppresses width transition until the host has painted its initial open state. */
|
|
25
|
+
@State() private readyForMotion = false;
|
|
26
|
+
|
|
27
|
+
private motionEnableGeneration = 0;
|
|
28
|
+
|
|
29
|
+
componentWillLoad() {
|
|
30
|
+
if (this.open) this.expanded = true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
componentDidLoad() {
|
|
34
|
+
this.deferMotionEnable();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
disconnectedCallback() {
|
|
38
|
+
this.el.removeEventListener('transitionend', this.handleTransitionEnd);
|
|
39
|
+
this.motionEnableGeneration += 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Two rAFs after the last pre-ready prop change — host bindings may land late on refresh. */
|
|
43
|
+
private deferMotionEnable() {
|
|
44
|
+
if (this.readyForMotion) return;
|
|
45
|
+
const generation = ++this.motionEnableGeneration;
|
|
46
|
+
requestAnimationFrame(() => {
|
|
47
|
+
if (generation !== this.motionEnableGeneration) return;
|
|
48
|
+
requestAnimationFrame(() => {
|
|
49
|
+
if (generation !== this.motionEnableGeneration) return;
|
|
50
|
+
this.readyForMotion = true;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
connectedCallback() {
|
|
56
|
+
this.el.addEventListener('transitionend', this.handleTransitionEnd);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Watch('open')
|
|
60
|
+
openChanged(isOpen: boolean) {
|
|
61
|
+
if (isOpen) this.expanded = true;
|
|
62
|
+
this.deferMotionEnable();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Watch('activeTool')
|
|
66
|
+
activeToolChanged() {
|
|
67
|
+
this.deferMotionEnable();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private handleTransitionEnd = (event: TransitionEvent) => {
|
|
71
|
+
if (event.target !== this.el) return;
|
|
72
|
+
if (event.propertyName !== 'max-width') return;
|
|
73
|
+
if (!this.open) this.expanded = false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
private isShowingContent(): boolean {
|
|
77
|
+
return this.open || this.expanded;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private isActiveTool(id: PanelToolsToolId): boolean {
|
|
81
|
+
return this.isShowingContent() && this.activeTool === id;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private headerLabel(): string {
|
|
85
|
+
if (!this.activeTool) return '';
|
|
86
|
+
return PANEL_TOOLS_LABELS[this.activeTool as PanelToolsToolId] ?? '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
render() {
|
|
90
|
+
const showingContent = this.isShowingContent();
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Host
|
|
94
|
+
class={{
|
|
95
|
+
'panel-tools': true,
|
|
96
|
+
'panel-tools--open': this.open,
|
|
97
|
+
'panel-tools--ready': this.readyForMotion,
|
|
98
|
+
}}
|
|
99
|
+
role="complementary"
|
|
100
|
+
aria-label={this.headerLabel() || 'Tools'}
|
|
101
|
+
aria-hidden={showingContent ? null : 'true'}
|
|
102
|
+
>
|
|
103
|
+
<div class="panel-tools__inner">
|
|
104
|
+
<header class="panel-tools__header">
|
|
105
|
+
<span class="text-body-medium-emphasis">{this.headerLabel()}</span>
|
|
106
|
+
</header>
|
|
107
|
+
<div class="panel-tools__body">
|
|
108
|
+
<div
|
|
109
|
+
class={{
|
|
110
|
+
'panel-tools__view': true,
|
|
111
|
+
'panel-tools__view--active': this.isActiveTool('search'),
|
|
112
|
+
'text-body-medium': true,
|
|
113
|
+
}}
|
|
114
|
+
hidden={!this.isActiveTool('search')}
|
|
115
|
+
>
|
|
116
|
+
<slot name="search" />
|
|
117
|
+
</div>
|
|
118
|
+
<div
|
|
119
|
+
class={{
|
|
120
|
+
'panel-tools__view': true,
|
|
121
|
+
'panel-tools__view--active': this.isActiveTool('messages'),
|
|
122
|
+
'text-body-medium': true,
|
|
123
|
+
}}
|
|
124
|
+
hidden={!this.isActiveTool('messages')}
|
|
125
|
+
>
|
|
126
|
+
<slot name="messages" />
|
|
127
|
+
</div>
|
|
128
|
+
<div
|
|
129
|
+
class={{
|
|
130
|
+
'panel-tools__view': true,
|
|
131
|
+
'panel-tools__view--active': this.isActiveTool('stacks'),
|
|
132
|
+
'text-body-medium': true,
|
|
133
|
+
}}
|
|
134
|
+
hidden={!this.isActiveTool('stacks')}
|
|
135
|
+
>
|
|
136
|
+
<slot name="stacks" />
|
|
137
|
+
</div>
|
|
138
|
+
<div
|
|
139
|
+
class={{
|
|
140
|
+
'panel-tools__view': true,
|
|
141
|
+
'panel-tools__view--active': this.isActiveTool('activity'),
|
|
142
|
+
'text-body-medium': true,
|
|
143
|
+
}}
|
|
144
|
+
hidden={!this.isActiveTool('activity')}
|
|
145
|
+
>
|
|
146
|
+
<slot name="activity" />
|
|
147
|
+
</div>
|
|
148
|
+
<div
|
|
149
|
+
class={{
|
|
150
|
+
'panel-tools__view': true,
|
|
151
|
+
'panel-tools__view--active': this.isActiveTool('agents'),
|
|
152
|
+
'text-body-medium': true,
|
|
153
|
+
}}
|
|
154
|
+
hidden={!this.isActiveTool('agents')}
|
|
155
|
+
>
|
|
156
|
+
<slot name="agents" />
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</Host>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Bar-nav tool ids that map to `ds-panel-tools` views. */
|
|
2
|
+
export type PanelToolsToolId = 'search' | 'messages' | 'stacks' | 'activity' | 'agents';
|
|
3
|
+
|
|
4
|
+
export const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [
|
|
5
|
+
'search',
|
|
6
|
+
'messages',
|
|
7
|
+
'stacks',
|
|
8
|
+
'activity',
|
|
9
|
+
'agents',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {
|
|
13
|
+
search: 'Search',
|
|
14
|
+
messages: 'Messages',
|
|
15
|
+
stacks: 'Stacks',
|
|
16
|
+
activity: 'Activity',
|
|
17
|
+
agents: 'Agents',
|
|
18
|
+
};
|
package/src/wc/components.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { MenuItemData, MenuSection } from "./components/Menu/menu-types";
|
|
|
26
26
|
import { MenuAlign, MenuSide } from "./components/Menu/menu-position";
|
|
27
27
|
import { ModalWidth } from "./components/Modal/Modal";
|
|
28
28
|
import { PanelNavGroup, PanelNavRouterMode, PanelNavUserActionDetail } from "./components/PanelNav/panel-nav-types";
|
|
29
|
+
import { PanelToolsToolId } from "./components/PanelTools/panel-tools-types";
|
|
29
30
|
import { RadioOption } from "./components/RadioGroup/RadioGroup";
|
|
30
31
|
import { ScrollbarVariant } from "./components/Scrollbar/Scrollbar";
|
|
31
32
|
import { SelectOption } from "./components/Select/Select";
|
|
@@ -62,6 +63,7 @@ export { MenuItemData, MenuSection } from "./components/Menu/menu-types";
|
|
|
62
63
|
export { MenuAlign, MenuSide } from "./components/Menu/menu-position";
|
|
63
64
|
export { ModalWidth } from "./components/Modal/Modal";
|
|
64
65
|
export { PanelNavGroup, PanelNavRouterMode, PanelNavUserActionDetail } from "./components/PanelNav/panel-nav-types";
|
|
66
|
+
export { PanelToolsToolId } from "./components/PanelTools/panel-tools-types";
|
|
65
67
|
export { RadioOption } from "./components/RadioGroup/RadioGroup";
|
|
66
68
|
export { ScrollbarVariant } from "./components/Scrollbar/Scrollbar";
|
|
67
69
|
export { SelectOption } from "./components/Select/Select";
|
|
@@ -680,6 +682,18 @@ export namespace Components {
|
|
|
680
682
|
*/
|
|
681
683
|
"userName": string;
|
|
682
684
|
}
|
|
685
|
+
interface DsPanelTools {
|
|
686
|
+
/**
|
|
687
|
+
* Active tool view — `search`, `messages`, `stacks`, `activity`, or `agents`.
|
|
688
|
+
* @default ''
|
|
689
|
+
*/
|
|
690
|
+
"activeTool": PanelToolsToolId | '';
|
|
691
|
+
/**
|
|
692
|
+
* When false, width animates to 0.
|
|
693
|
+
* @default false
|
|
694
|
+
*/
|
|
695
|
+
"open": boolean;
|
|
696
|
+
}
|
|
683
697
|
interface DsRadioGroup {
|
|
684
698
|
"ariaLabel": string | undefined;
|
|
685
699
|
"ariaLabelledby": string | undefined;
|
|
@@ -1465,6 +1479,12 @@ declare global {
|
|
|
1465
1479
|
prototype: HTMLDsPanelNavElement;
|
|
1466
1480
|
new (): HTMLDsPanelNavElement;
|
|
1467
1481
|
};
|
|
1482
|
+
interface HTMLDsPanelToolsElement extends Components.DsPanelTools, HTMLStencilElement {
|
|
1483
|
+
}
|
|
1484
|
+
var HTMLDsPanelToolsElement: {
|
|
1485
|
+
prototype: HTMLDsPanelToolsElement;
|
|
1486
|
+
new (): HTMLDsPanelToolsElement;
|
|
1487
|
+
};
|
|
1468
1488
|
interface HTMLDsRadioGroupElementEventMap {
|
|
1469
1489
|
"dsChange": string;
|
|
1470
1490
|
}
|
|
@@ -1687,6 +1707,7 @@ declare global {
|
|
|
1687
1707
|
"ds-modal": HTMLDsModalElement;
|
|
1688
1708
|
"ds-pagination": HTMLDsPaginationElement;
|
|
1689
1709
|
"ds-panel-nav": HTMLDsPanelNavElement;
|
|
1710
|
+
"ds-panel-tools": HTMLDsPanelToolsElement;
|
|
1690
1711
|
"ds-radio-group": HTMLDsRadioGroupElement;
|
|
1691
1712
|
"ds-scrollbar": HTMLDsScrollbarElement;
|
|
1692
1713
|
"ds-select": HTMLDsSelectElement;
|
|
@@ -2366,6 +2387,18 @@ declare namespace LocalJSX {
|
|
|
2366
2387
|
*/
|
|
2367
2388
|
"userName"?: string;
|
|
2368
2389
|
}
|
|
2390
|
+
interface DsPanelTools {
|
|
2391
|
+
/**
|
|
2392
|
+
* Active tool view — `search`, `messages`, `stacks`, `activity`, or `agents`.
|
|
2393
|
+
* @default ''
|
|
2394
|
+
*/
|
|
2395
|
+
"activeTool"?: PanelToolsToolId | '';
|
|
2396
|
+
/**
|
|
2397
|
+
* When false, width animates to 0.
|
|
2398
|
+
* @default false
|
|
2399
|
+
*/
|
|
2400
|
+
"open"?: boolean;
|
|
2401
|
+
}
|
|
2369
2402
|
interface DsRadioGroup {
|
|
2370
2403
|
"ariaLabel"?: string | undefined;
|
|
2371
2404
|
"ariaLabelledby"?: string | undefined;
|
|
@@ -2953,6 +2986,10 @@ declare namespace LocalJSX {
|
|
|
2953
2986
|
"userName": string;
|
|
2954
2987
|
"userInitial": string;
|
|
2955
2988
|
}
|
|
2989
|
+
interface DsPanelToolsAttributes {
|
|
2990
|
+
"open": boolean;
|
|
2991
|
+
"activeTool": PanelToolsToolId | '';
|
|
2992
|
+
}
|
|
2956
2993
|
interface DsRadioGroupAttributes {
|
|
2957
2994
|
"value": string;
|
|
2958
2995
|
"direction": 'vertical' | 'horizontal';
|
|
@@ -3103,6 +3140,7 @@ declare namespace LocalJSX {
|
|
|
3103
3140
|
"ds-modal": Omit<DsModal, keyof DsModalAttributes> & { [K in keyof DsModal & keyof DsModalAttributes]?: DsModal[K] } & { [K in keyof DsModal & keyof DsModalAttributes as `attr:${K}`]?: DsModalAttributes[K] } & { [K in keyof DsModal & keyof DsModalAttributes as `prop:${K}`]?: DsModal[K] } & OneOf<"heading", DsModal["heading"], DsModalAttributes["heading"]>;
|
|
3104
3141
|
"ds-pagination": Omit<DsPagination, keyof DsPaginationAttributes> & { [K in keyof DsPagination & keyof DsPaginationAttributes]?: DsPagination[K] } & { [K in keyof DsPagination & keyof DsPaginationAttributes as `attr:${K}`]?: DsPaginationAttributes[K] } & { [K in keyof DsPagination & keyof DsPaginationAttributes as `prop:${K}`]?: DsPagination[K] };
|
|
3105
3142
|
"ds-panel-nav": Omit<DsPanelNav, keyof DsPanelNavAttributes> & { [K in keyof DsPanelNav & keyof DsPanelNavAttributes]?: DsPanelNav[K] } & { [K in keyof DsPanelNav & keyof DsPanelNavAttributes as `attr:${K}`]?: DsPanelNavAttributes[K] } & { [K in keyof DsPanelNav & keyof DsPanelNavAttributes as `prop:${K}`]?: DsPanelNav[K] };
|
|
3143
|
+
"ds-panel-tools": Omit<DsPanelTools, keyof DsPanelToolsAttributes> & { [K in keyof DsPanelTools & keyof DsPanelToolsAttributes]?: DsPanelTools[K] } & { [K in keyof DsPanelTools & keyof DsPanelToolsAttributes as `attr:${K}`]?: DsPanelToolsAttributes[K] } & { [K in keyof DsPanelTools & keyof DsPanelToolsAttributes as `prop:${K}`]?: DsPanelTools[K] };
|
|
3106
3144
|
"ds-radio-group": Omit<DsRadioGroup, keyof DsRadioGroupAttributes> & { [K in keyof DsRadioGroup & keyof DsRadioGroupAttributes]?: DsRadioGroup[K] } & { [K in keyof DsRadioGroup & keyof DsRadioGroupAttributes as `attr:${K}`]?: DsRadioGroupAttributes[K] } & { [K in keyof DsRadioGroup & keyof DsRadioGroupAttributes as `prop:${K}`]?: DsRadioGroup[K] };
|
|
3107
3145
|
"ds-scrollbar": Omit<DsScrollbar, keyof DsScrollbarAttributes> & { [K in keyof DsScrollbar & keyof DsScrollbarAttributes]?: DsScrollbar[K] } & { [K in keyof DsScrollbar & keyof DsScrollbarAttributes as `attr:${K}`]?: DsScrollbarAttributes[K] } & { [K in keyof DsScrollbar & keyof DsScrollbarAttributes as `prop:${K}`]?: DsScrollbar[K] };
|
|
3108
3146
|
"ds-select": Omit<DsSelect, keyof DsSelectAttributes> & { [K in keyof DsSelect & keyof DsSelectAttributes]?: DsSelect[K] } & { [K in keyof DsSelect & keyof DsSelectAttributes as `attr:${K}`]?: DsSelectAttributes[K] } & { [K in keyof DsSelect & keyof DsSelectAttributes as `prop:${K}`]?: DsSelect[K] };
|
|
@@ -3149,6 +3187,7 @@ declare module "@stencil/core" {
|
|
|
3149
3187
|
"ds-modal": LocalJSX.IntrinsicElements["ds-modal"] & JSXBase.HTMLAttributes<HTMLDsModalElement>;
|
|
3150
3188
|
"ds-pagination": LocalJSX.IntrinsicElements["ds-pagination"] & JSXBase.HTMLAttributes<HTMLDsPaginationElement>;
|
|
3151
3189
|
"ds-panel-nav": LocalJSX.IntrinsicElements["ds-panel-nav"] & JSXBase.HTMLAttributes<HTMLDsPanelNavElement>;
|
|
3190
|
+
"ds-panel-tools": LocalJSX.IntrinsicElements["ds-panel-tools"] & JSXBase.HTMLAttributes<HTMLDsPanelToolsElement>;
|
|
3152
3191
|
"ds-radio-group": LocalJSX.IntrinsicElements["ds-radio-group"] & JSXBase.HTMLAttributes<HTMLDsRadioGroupElement>;
|
|
3153
3192
|
"ds-scrollbar": LocalJSX.IntrinsicElements["ds-scrollbar"] & JSXBase.HTMLAttributes<HTMLDsScrollbarElement>;
|
|
3154
3193
|
"ds-select": LocalJSX.IntrinsicElements["ds-select"] & JSXBase.HTMLAttributes<HTMLDsSelectElement>;
|
package/src/wc/nav/index.ts
CHANGED
|
@@ -10,14 +10,14 @@ export const SHELL_GRADIENT_OPACITY_VAR = '--ds-shell-gradient-opacity';
|
|
|
10
10
|
/** Layer opacity for the nav gradient wash. */
|
|
11
11
|
export const SHELL_GRADIENT_OPACITY = '0.1';
|
|
12
12
|
|
|
13
|
-
const GRADIENT_GEOMETRY = '
|
|
13
|
+
const GRADIENT_GEOMETRY = '100% 100% at 0% 0%';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Unified shell radial — same wash for all nav chrome styles.
|
|
17
17
|
* Tokens follow `data-theme` (light/dark blue intent).
|
|
18
18
|
*/
|
|
19
19
|
export function buildShellRadialGradient(): string {
|
|
20
|
-
return `radial-gradient(${GRADIENT_GEOMETRY}, var(--color-background-transparent) 0%, var(--color-color-intent-blue-
|
|
20
|
+
return `radial-gradient(${GRADIENT_GEOMETRY}, var(--color-background-transparent) 0%, var(--color-color-intent-blue-strong-background) 100%)`;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Built-in radial image (optional `gradientSrc` on shell overrides). */
|
|
@@ -7,6 +7,19 @@ export { parseCssTimeMs };
|
|
|
7
7
|
|
|
8
8
|
const VT_STYLE_ID = 'ds-shell-nav-vt-style';
|
|
9
9
|
|
|
10
|
+
/** TokoMo tokens for the dashboard ↔ settings radial reveal (WAAPI). */
|
|
11
|
+
export const SHELL_NAV_REVEAL_DURATION_VAR = '--effect-animation-duration-medium-3';
|
|
12
|
+
export const SHELL_NAV_REVEAL_EASING_VAR = '--effect-animation-easing-ease-in-out';
|
|
13
|
+
|
|
14
|
+
const SHELL_NAV_REVEAL_DURATION_FALLBACK_MS = 500;
|
|
15
|
+
const SHELL_NAV_REVEAL_EASING_FALLBACK = 'ease-in-out';
|
|
16
|
+
|
|
17
|
+
function readCssCustomProperty(name: string, fallback: string): string {
|
|
18
|
+
if (typeof document === 'undefined') return fallback;
|
|
19
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
20
|
+
return value || fallback;
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
/** Suppress default cross-fade and pin new snapshots to a 0px circle for WAAPI reveal. */
|
|
11
24
|
export function ensureShellNavVtStyle(): void {
|
|
12
25
|
if (typeof document === 'undefined') return;
|
|
@@ -53,10 +66,16 @@ export function animateShellNavRadialReveal(
|
|
|
53
66
|
origin: ShellNavRevealOrigin,
|
|
54
67
|
durationMs?: number,
|
|
55
68
|
): void {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
const duration =
|
|
70
|
+
durationMs ??
|
|
71
|
+
parseCssTimeMs(
|
|
72
|
+
readCssCustomProperty(SHELL_NAV_REVEAL_DURATION_VAR, '400ms'),
|
|
73
|
+
SHELL_NAV_REVEAL_DURATION_FALLBACK_MS,
|
|
74
|
+
);
|
|
75
|
+
const easing = readCssCustomProperty(
|
|
76
|
+
SHELL_NAV_REVEAL_EASING_VAR,
|
|
77
|
+
SHELL_NAV_REVEAL_EASING_FALLBACK,
|
|
78
|
+
);
|
|
60
79
|
const keyframes = {
|
|
61
80
|
clipPath: [
|
|
62
81
|
`circle(0px at ${origin.x}px ${origin.y}px)`,
|
|
@@ -65,7 +84,7 @@ export function animateShellNavRadialReveal(
|
|
|
65
84
|
};
|
|
66
85
|
const options = {
|
|
67
86
|
duration,
|
|
68
|
-
easing
|
|
87
|
+
easing,
|
|
69
88
|
fill: 'forwards' as const,
|
|
70
89
|
};
|
|
71
90
|
|