@ds-mo/ui 1.4.0 → 1.6.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 -0
- package/dist/components/{ds-sidebar.d.ts → ds-app-shell.d.ts} +4 -4
- package/dist/components/ds-app-shell.js +2 -0
- package/dist/components/ds-app-shell.js.map +1 -0
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-banner.js.map +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-button-group.js +1 -1
- package/dist/components/ds-button.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-loader.js +1 -1
- package/dist/components/ds-menu.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-select.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-toast-provider.js +1 -1
- package/dist/components/ds-toast-provider.js.map +1 -1
- package/dist/components/ds-toggle-button-group.js +1 -1
- package/dist/components/ds-toggle-button.js +1 -1
- package/dist/components/ds-toggle.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-tooltip.js.map +1 -1
- package/dist/components/p-BRju0sEC.js +2 -0
- package/dist/components/p-BRju0sEC.js.map +1 -0
- package/dist/components/p-BvmooLHV.js +2 -0
- package/dist/components/p-BvmooLHV.js.map +1 -0
- package/dist/components/p-C4WbCXBj.js +2 -0
- package/dist/components/p-C4WbCXBj.js.map +1 -0
- package/dist/components/p-CYJ-de0u.js +2 -0
- package/dist/components/p-CYJ-de0u.js.map +1 -0
- package/dist/components/{p--YDw8Q4o.js → p-CsO880L-.js} +2 -2
- package/dist/components/p-DTjpgYJV.js +2 -0
- package/dist/components/p-DTjpgYJV.js.map +1 -0
- package/dist/components/p-DfL2MOso.js +2 -0
- package/dist/components/p-DfL2MOso.js.map +1 -0
- package/dist/components/p-DiWQFROF.js +2 -0
- package/dist/components/p-DiWQFROF.js.map +1 -0
- package/dist/types/components/AppShell/AppShell.d.ts +25 -0
- package/dist/types/components/Banner/Banner.d.ts +2 -0
- package/dist/types/components/Loader/Loader.d.ts +3 -2
- package/dist/types/components/Menu/Menu.d.ts +11 -2
- package/dist/types/components/Modal/Modal.d.ts +1 -0
- package/dist/types/components/PanelNav/PanelNav.d.ts +3 -6
- package/dist/types/components/Toast/Toast.d.ts +1 -0
- package/dist/types/components/Tooltip/Tooltip.d.ts +14 -3
- package/dist/types/components.d.ts +92 -105
- package/dist/types/nav/index.d.ts +2 -0
- package/dist/types/nav/shell-gradient.d.ts +25 -0
- package/dist/types/nav/shell-view-transition.d.ts +2 -2
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/resolve-css-length-px.d.ts +8 -0
- package/dist/types/utils/resolve-css-time-ms.d.ts +8 -0
- package/dist/types/utils/token-defaults.d.ts +27 -0
- package/package.json +4 -4
- package/src/angular/index.ts +1 -1
- package/src/angular/proxies.ts +23 -33
- package/src/react/components.ts +1 -1
- package/src/react/ds-app-shell.ts +26 -0
- package/src/wc/components/AppShell/AppShell.tsx +180 -0
- package/src/wc/components/Banner/Banner.tsx +11 -5
- package/src/wc/components/Loader/Loader.tsx +8 -3
- package/src/wc/components/Menu/Menu.tsx +61 -26
- package/src/wc/components/Modal/Modal.tsx +10 -7
- package/src/wc/components/PanelNav/PanelNav.tsx +5 -8
- package/src/wc/components/Toast/Toast.tsx +7 -2
- package/src/wc/components/Toast/toast-service.ts +8 -1
- package/src/wc/components/ToggleButton/ToggleButton.tsx +7 -3
- package/src/wc/components/Tooltip/Tooltip.tsx +70 -31
- package/src/wc/components.d.ts +92 -105
- package/src/wc/nav/index.ts +14 -0
- package/src/wc/nav/shell-gradient.ts +44 -0
- package/src/wc/nav/shell-view-transition.ts +4 -10
- package/dist/components/ds-sidebar.js +0 -2
- package/dist/components/ds-sidebar.js.map +0 -1
- package/dist/components/p-BcuUR5Ao.js +0 -2
- package/dist/components/p-BcuUR5Ao.js.map +0 -1
- package/dist/components/p-CpbnmEu1.js +0 -2
- package/dist/components/p-CpbnmEu1.js.map +0 -1
- package/dist/components/p-DznbYg1m.js +0 -2
- package/dist/components/p-DznbYg1m.js.map +0 -1
- package/dist/components/p-uMN1Ppw2.js +0 -2
- package/dist/components/p-uMN1Ppw2.js.map +0 -1
- package/dist/types/components/Sidebar/Sidebar.d.ts +0 -29
- package/src/react/ds-sidebar.ts +0 -33
- package/src/wc/components/Sidebar/Sidebar.tsx +0 -245
- /package/dist/components/{p--YDw8Q4o.js.map → p-CsO880L-.js.map} +0 -0
package/src/angular/proxies.ts
CHANGED
|
@@ -35,6 +35,29 @@ export declare interface DsAccordion extends Components.DsAccordion {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
@ProxyCmp({
|
|
39
|
+
inputs: ['gradient', 'gradientSrc', 'navStyle']
|
|
40
|
+
})
|
|
41
|
+
@Component({
|
|
42
|
+
selector: 'ds-app-shell',
|
|
43
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
44
|
+
template: '<ng-content></ng-content>',
|
|
45
|
+
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
46
|
+
inputs: ['gradient', 'gradientSrc', 'navStyle'],
|
|
47
|
+
standalone: false
|
|
48
|
+
})
|
|
49
|
+
export class DsAppShell {
|
|
50
|
+
protected el: HTMLDsAppShellElement;
|
|
51
|
+
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
52
|
+
c.detach();
|
|
53
|
+
this.el = r.nativeElement;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
export declare interface DsAppShell extends Components.DsAppShell {}
|
|
59
|
+
|
|
60
|
+
|
|
38
61
|
@ProxyCmp({
|
|
39
62
|
inputs: ['background', 'count', 'isSelected', 'label', 'max', 'surface', 'variant']
|
|
40
63
|
})
|
|
@@ -744,39 +767,6 @@ export declare interface DsSelect extends Components.DsSelect {
|
|
|
744
767
|
}
|
|
745
768
|
|
|
746
769
|
|
|
747
|
-
@ProxyCmp({
|
|
748
|
-
inputs: ['collapsed', 'mobile', 'resizable', 'width']
|
|
749
|
-
})
|
|
750
|
-
@Component({
|
|
751
|
-
selector: 'ds-sidebar',
|
|
752
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
753
|
-
template: '<ng-content></ng-content>',
|
|
754
|
-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
755
|
-
inputs: ['collapsed', 'mobile', 'resizable', 'width'],
|
|
756
|
-
outputs: ['dsToggle', 'dsWidthChange'],
|
|
757
|
-
standalone: false
|
|
758
|
-
})
|
|
759
|
-
export class DsSidebar {
|
|
760
|
-
protected el: HTMLDsSidebarElement;
|
|
761
|
-
@Output() dsToggle = new EventEmitter<CustomEvent<void>>();
|
|
762
|
-
@Output() dsWidthChange = new EventEmitter<CustomEvent<IDsSidebarSidebarWidth>>();
|
|
763
|
-
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
764
|
-
c.detach();
|
|
765
|
-
this.el = r.nativeElement;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
import type { SidebarWidth as IDsSidebarSidebarWidth } from '@ds-mo/ui';
|
|
771
|
-
|
|
772
|
-
export declare interface DsSidebar extends Components.DsSidebar {
|
|
773
|
-
|
|
774
|
-
dsToggle: EventEmitter<CustomEvent<void>>;
|
|
775
|
-
|
|
776
|
-
dsWidthChange: EventEmitter<CustomEvent<IDsSidebarSidebarWidth>>;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
|
|
780
770
|
@ProxyCmp({
|
|
781
771
|
inputs: ['height', 'lines', 'shimmer', 'variant', 'width']
|
|
782
772
|
})
|
package/src/react/components.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
/* eslint-disable */
|
|
7
7
|
export { DsAccordion } from "./ds-accordion.js";
|
|
8
|
+
export { DsAppShell } from "./ds-app-shell.js";
|
|
8
9
|
export { DsBadge } from "./ds-badge.js";
|
|
9
10
|
export { DsBanner } from "./ds-banner.js";
|
|
10
11
|
export { DsBarNavAction } from "./ds-bar-nav-action.js";
|
|
@@ -30,7 +31,6 @@ export { DsPanelNav } from "./ds-panel-nav.js";
|
|
|
30
31
|
export { DsRadioGroup } from "./ds-radio-group.js";
|
|
31
32
|
export { DsScrollbar } from "./ds-scrollbar.js";
|
|
32
33
|
export { DsSelect } from "./ds-select.js";
|
|
33
|
-
export { DsSidebar } from "./ds-sidebar.js";
|
|
34
34
|
export { DsSkeleton } from "./ds-skeleton.js";
|
|
35
35
|
export { DsSlider } from "./ds-slider.js";
|
|
36
36
|
export { DsSurface } from "./ds-surface.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 { DsAppShell as DsAppShellElement, defineCustomElement as defineDsAppShell } from "@ds-mo/ui/dist/components/ds-app-shell.js";
|
|
16
|
+
|
|
17
|
+
export type DsAppShellEvents = NonNullable<unknown>;
|
|
18
|
+
|
|
19
|
+
export const DsAppShell: StencilReactComponent<DsAppShellElement, DsAppShellEvents, Components.DsAppShell> = /*@__PURE__*/ createComponent<DsAppShellElement, DsAppShellEvents, Components.DsAppShell>({
|
|
20
|
+
tagName: 'ds-app-shell',
|
|
21
|
+
elementClass: DsAppShellElement,
|
|
22
|
+
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
|
|
23
|
+
react: React,
|
|
24
|
+
events: {} as DsAppShellEvents,
|
|
25
|
+
defineCustomElement: defineDsAppShell
|
|
26
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
3
|
+
import {
|
|
4
|
+
SHELL_GRADIENT_IMAGE_VAR,
|
|
5
|
+
SHELL_GRADIENT_OPACITY,
|
|
6
|
+
SHELL_GRADIENT_OPACITY_VAR,
|
|
7
|
+
SHELL_GRADIENT_POSITION_BAR_VAR,
|
|
8
|
+
SHELL_GRADIENT_POSITION_PANEL_VAR,
|
|
9
|
+
SHELL_GRADIENT_SIZE_VAR,
|
|
10
|
+
shellGradientImageForStyle,
|
|
11
|
+
shellGradientPositionBar,
|
|
12
|
+
shellGradientPositionPanel,
|
|
13
|
+
shellGradientSize,
|
|
14
|
+
} from '../../nav/shell-gradient';
|
|
15
|
+
|
|
16
|
+
@Component({
|
|
17
|
+
tag: 'ds-app-shell',
|
|
18
|
+
styleUrl: 'AppShell.css',
|
|
19
|
+
scoped: true,
|
|
20
|
+
})
|
|
21
|
+
export class AppShell {
|
|
22
|
+
/** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */
|
|
23
|
+
@Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'navigation';
|
|
24
|
+
|
|
25
|
+
/** When `true`, paints the shared L-shaped radial wash on panel + bar backgrounds. */
|
|
26
|
+
@Prop({ reflect: true }) gradient: boolean = false;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Optional custom gradient for `background-image` (e.g. SVG URL).
|
|
30
|
+
* When set, overrides the built-in radial wash.
|
|
31
|
+
*/
|
|
32
|
+
@Prop() gradientSrc: string = '';
|
|
33
|
+
|
|
34
|
+
@Element() el!: HTMLElement;
|
|
35
|
+
|
|
36
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
37
|
+
|
|
38
|
+
componentDidLoad() {
|
|
39
|
+
this.syncSlottedNavStyle();
|
|
40
|
+
this.connectMetricsObserver();
|
|
41
|
+
this.scheduleGradientVars();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private scheduleGradientVars() {
|
|
45
|
+
this.updateGradientVars();
|
|
46
|
+
requestAnimationFrame(() => this.updateGradientVars());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
disconnectedCallback() {
|
|
50
|
+
this.resizeObserver?.disconnect();
|
|
51
|
+
this.resizeObserver = null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Watch('navStyle')
|
|
55
|
+
@Watch('gradient')
|
|
56
|
+
@Watch('gradientSrc')
|
|
57
|
+
onShellPropsChange() {
|
|
58
|
+
this.syncSlottedNavStyle();
|
|
59
|
+
this.scheduleGradientVars();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private connectMetricsObserver() {
|
|
63
|
+
this.resizeObserver = new ResizeObserver(() => this.scheduleGradientVars());
|
|
64
|
+
this.resizeObserver.observe(this.el);
|
|
65
|
+
const panelWrap = this.el.querySelector('.app-shell__panel');
|
|
66
|
+
const barWrap = this.el.querySelector('.app-shell__bar');
|
|
67
|
+
if (panelWrap) this.resizeObserver.observe(panelWrap);
|
|
68
|
+
if (barWrap) this.resizeObserver.observe(barWrap);
|
|
69
|
+
this.scheduleGradientVars();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private applyGradientVars(target: HTMLElement, vars: Record<string, string | null>) {
|
|
73
|
+
const style = target.style;
|
|
74
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
75
|
+
if (value === null) style.removeProperty(name);
|
|
76
|
+
else style.setProperty(name, value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private clearGradientVars(targets: HTMLElement[]) {
|
|
81
|
+
const keys = [
|
|
82
|
+
SHELL_GRADIENT_IMAGE_VAR,
|
|
83
|
+
SHELL_GRADIENT_SIZE_VAR,
|
|
84
|
+
SHELL_GRADIENT_POSITION_PANEL_VAR,
|
|
85
|
+
SHELL_GRADIENT_POSITION_BAR_VAR,
|
|
86
|
+
SHELL_GRADIENT_OPACITY_VAR,
|
|
87
|
+
];
|
|
88
|
+
for (const target of targets) {
|
|
89
|
+
for (const key of keys) target.style.removeProperty(key);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private syncSlottedNavStyle() {
|
|
94
|
+
const panel = this.el.querySelector('ds-panel-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;
|
|
95
|
+
const bar = this.el.querySelector('ds-bar-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;
|
|
96
|
+
if (panel) {
|
|
97
|
+
panel.setAttribute('nav-style', this.navStyle);
|
|
98
|
+
panel.navStyle = this.navStyle;
|
|
99
|
+
}
|
|
100
|
+
if (bar) {
|
|
101
|
+
bar.setAttribute('nav-style', this.navStyle);
|
|
102
|
+
bar.navStyle = this.navStyle;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private updateGradientVars() {
|
|
107
|
+
const panel = this.el.querySelector('ds-panel-nav') as HTMLElement | null;
|
|
108
|
+
const bar = this.el.querySelector('ds-bar-nav') as HTMLElement | null;
|
|
109
|
+
const targets = [this.el, panel, bar].filter((el): el is HTMLElement => el !== null);
|
|
110
|
+
|
|
111
|
+
if (!this.gradient) {
|
|
112
|
+
this.clearGradientVars(targets);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const image = this.gradientSrc.trim()
|
|
117
|
+
? `url(${this.gradientSrc.trim()})`
|
|
118
|
+
: shellGradientImageForStyle(this.navStyle);
|
|
119
|
+
|
|
120
|
+
const shellRect = this.el.getBoundingClientRect();
|
|
121
|
+
const panelWrap = this.el.querySelector('.app-shell__panel') as HTMLElement | null;
|
|
122
|
+
const panelWidth = panelWrap?.getBoundingClientRect().width ?? 0;
|
|
123
|
+
|
|
124
|
+
const size = shellGradientSize({
|
|
125
|
+
width: shellRect.width,
|
|
126
|
+
height: shellRect.height,
|
|
127
|
+
panelWidth,
|
|
128
|
+
});
|
|
129
|
+
const panelPosition = shellGradientPositionPanel();
|
|
130
|
+
const barPosition = shellGradientPositionBar(panelWidth);
|
|
131
|
+
const opacity = SHELL_GRADIENT_OPACITY;
|
|
132
|
+
|
|
133
|
+
for (const target of targets) {
|
|
134
|
+
if (target === this.el) {
|
|
135
|
+
this.applyGradientVars(target, {
|
|
136
|
+
[SHELL_GRADIENT_IMAGE_VAR]: image,
|
|
137
|
+
[SHELL_GRADIENT_SIZE_VAR]: size,
|
|
138
|
+
[SHELL_GRADIENT_POSITION_PANEL_VAR]: panelPosition,
|
|
139
|
+
[SHELL_GRADIENT_POSITION_BAR_VAR]: barPosition,
|
|
140
|
+
[SHELL_GRADIENT_OPACITY_VAR]: opacity,
|
|
141
|
+
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const isBar = target.tagName.toLowerCase() === 'ds-bar-nav';
|
|
146
|
+
this.applyGradientVars(target, {
|
|
147
|
+
[SHELL_GRADIENT_IMAGE_VAR]: image,
|
|
148
|
+
[SHELL_GRADIENT_SIZE_VAR]: size,
|
|
149
|
+
[SHELL_GRADIENT_OPACITY_VAR]: opacity,
|
|
150
|
+
[SHELL_GRADIENT_POSITION_PANEL_VAR]: isBar ? null : panelPosition,
|
|
151
|
+
[SHELL_GRADIENT_POSITION_BAR_VAR]: isBar ? barPosition : null,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
render() {
|
|
157
|
+
const shellCls: Record<string, boolean> = {
|
|
158
|
+
'app-shell': true,
|
|
159
|
+
'app-shell--gradient': this.gradient,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<Host class={shellCls}>
|
|
164
|
+
<div class="app-shell__row">
|
|
165
|
+
<div class="app-shell__panel">
|
|
166
|
+
<slot name="panel" />
|
|
167
|
+
</div>
|
|
168
|
+
<div class="app-shell__main">
|
|
169
|
+
<div class="app-shell__bar">
|
|
170
|
+
<slot name="bar" />
|
|
171
|
+
</div>
|
|
172
|
+
<div class="app-shell__content">
|
|
173
|
+
<slot />
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</Host>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Prop, State, Event, EventEmitter, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
export type BannerIntent =
|
|
4
5
|
| 'brand' | 'positive' | 'negative' | 'warning' | 'caution'
|
|
@@ -6,9 +7,6 @@ export type BannerIntent =
|
|
|
6
7
|
|
|
7
8
|
export type BannerContrast = 'faint' | 'medium' | 'bold' | 'strong';
|
|
8
9
|
|
|
9
|
-
const FADE_OUT_MS = 200;
|
|
10
|
-
const AUTO_DISMISS_MS = 4000;
|
|
11
|
-
|
|
12
10
|
@Component({
|
|
13
11
|
tag: 'ds-banner',
|
|
14
12
|
styleUrl: 'Banner.css',
|
|
@@ -32,6 +30,14 @@ export class Banner {
|
|
|
32
30
|
private dismissTimer: ReturnType<typeof setTimeout> | null = null;
|
|
33
31
|
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
34
32
|
|
|
33
|
+
private get fadeOutMs(): number {
|
|
34
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private get autoDismissMs(): number {
|
|
38
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.animationDelayLong2, TOKEN_DEFAULTS.animationDelayLong2);
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
componentDidLoad() {
|
|
36
42
|
if (this.floating && this.message) this.scheduleDismiss();
|
|
37
43
|
}
|
|
@@ -58,7 +64,7 @@ export class Banner {
|
|
|
58
64
|
this.dismissTimer = setTimeout(() => {
|
|
59
65
|
this.dismissTimer = null;
|
|
60
66
|
this.beginClose();
|
|
61
|
-
},
|
|
67
|
+
}, this.autoDismissMs);
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
private beginClose() {
|
|
@@ -69,7 +75,7 @@ export class Banner {
|
|
|
69
75
|
this.closing = false;
|
|
70
76
|
this.closeTimer = null;
|
|
71
77
|
this.dsDismiss.emit();
|
|
72
|
-
},
|
|
78
|
+
}, this.fadeOutMs);
|
|
73
79
|
} else {
|
|
74
80
|
this.dsDismiss.emit();
|
|
75
81
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Prop, h } from '@stencil/core';
|
|
2
|
+
import { resolveCssLengthPx, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
@Component({
|
|
4
5
|
tag: 'ds-loader',
|
|
@@ -6,8 +7,8 @@ import { Component, Prop, h } from '@stencil/core';
|
|
|
6
7
|
shadow: true,
|
|
7
8
|
})
|
|
8
9
|
export class Loader {
|
|
9
|
-
/** Render size
|
|
10
|
-
@Prop() size: number =
|
|
10
|
+
/** Render size — number (px) or TokoMo length (`var(--dimension-iconography-md)`, etc.). */
|
|
11
|
+
@Prop() size: number | string = TOKEN_DEFAULTS.iconographyMd;
|
|
11
12
|
/**
|
|
12
13
|
* Accessible label for standalone usage. Wraps the spinner in a live region
|
|
13
14
|
* and renders the label visually-hidden. Omit when the host element already
|
|
@@ -15,8 +16,12 @@ export class Loader {
|
|
|
15
16
|
*/
|
|
16
17
|
@Prop() label: string | undefined;
|
|
17
18
|
|
|
19
|
+
private get sizePx(): number {
|
|
20
|
+
return resolveCssLengthPx(this.size, TOKEN_DEFAULTS.iconographyMd);
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
render() {
|
|
19
|
-
const px = `${this.
|
|
24
|
+
const px = `${this.sizePx}px`;
|
|
20
25
|
const svg = (
|
|
21
26
|
<svg
|
|
22
27
|
style={{ width: px, height: px }}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Prop, State, Event, EventEmitter, Element, Watch, Listen, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssLengthPx, resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
export type MenuSide = 'top' | 'right' | 'bottom' | 'left';
|
|
4
5
|
export type MenuAlign = 'start' | 'center' | 'end';
|
|
@@ -19,9 +20,6 @@ export interface MenuSection {
|
|
|
19
20
|
items: MenuItemData[];
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
const CLOSE_ANIMATION_MS = 220;
|
|
23
|
-
const VP_PAD = 4;
|
|
24
|
-
|
|
25
23
|
@Component({
|
|
26
24
|
tag: 'ds-menu',
|
|
27
25
|
styleUrl: 'Menu.css',
|
|
@@ -35,8 +33,10 @@ export class Menu {
|
|
|
35
33
|
@Prop() sections: MenuSection[] = [];
|
|
36
34
|
@Prop() side: MenuSide = 'bottom';
|
|
37
35
|
@Prop() align: MenuAlign = 'start';
|
|
38
|
-
|
|
39
|
-
@Prop()
|
|
36
|
+
/** Gap between anchor and menu — number (px) or TokoMo length (`var(--dimension-space-050)`, etc.). */
|
|
37
|
+
@Prop() sideOffset: number | string = TOKEN_DEFAULTS.space050;
|
|
38
|
+
/** Cross-axis offset — number (px) or TokoMo length. */
|
|
39
|
+
@Prop() alignOffset: number | string = 0;
|
|
40
40
|
@Prop() menuWidth: string | undefined;
|
|
41
41
|
@Prop() minWidth: string | undefined;
|
|
42
42
|
/** External trigger element to position against. Set via JS: menuEl.anchor = buttonEl */
|
|
@@ -85,10 +85,42 @@ export class Menu {
|
|
|
85
85
|
this.shouldRender = false;
|
|
86
86
|
this.closing = false;
|
|
87
87
|
this.closeTimer = null;
|
|
88
|
-
},
|
|
88
|
+
}, this.closeAnimationMs);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
@Watch('side')
|
|
93
|
+
@Watch('align')
|
|
94
|
+
@Watch('sideOffset')
|
|
95
|
+
@Watch('alignOffset')
|
|
96
|
+
onPositionPropsChange() {
|
|
97
|
+
if (this.open) this.calculatePosition();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private get viewportPadPx(): number {
|
|
101
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private get sideOffsetPx(): number {
|
|
105
|
+
return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private get alignOffsetPx(): number {
|
|
109
|
+
return resolveCssLengthPx(this.alignOffset, 0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private get popupFallbackWidthPx(): number {
|
|
113
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.menuWidthXs, TOKEN_DEFAULTS.menuWidthXs);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private get popupFallbackHeightPx(): number {
|
|
117
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.menuFallbackHeight, TOKEN_DEFAULTS.menuFallbackHeight);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private get closeAnimationMs(): number {
|
|
121
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);
|
|
122
|
+
}
|
|
123
|
+
|
|
92
124
|
private get resolvedAnchor(): HTMLElement | null {
|
|
93
125
|
if (this.anchor) return this.anchor;
|
|
94
126
|
if (this.anchorId) return document.getElementById(this.anchorId);
|
|
@@ -112,40 +144,43 @@ export class Menu {
|
|
|
112
144
|
if (!popup) return;
|
|
113
145
|
|
|
114
146
|
const a = anchorEl.getBoundingClientRect();
|
|
115
|
-
const pw = popup.offsetWidth ||
|
|
116
|
-
const ph = popup.offsetHeight ||
|
|
147
|
+
const pw = popup.offsetWidth || this.popupFallbackWidthPx;
|
|
148
|
+
const ph = popup.offsetHeight || this.popupFallbackHeightPx;
|
|
149
|
+
const sideOffset = this.sideOffsetPx;
|
|
150
|
+
const alignOffset = this.alignOffsetPx;
|
|
151
|
+
const vpPad = this.viewportPadPx;
|
|
117
152
|
let x = 0, y = 0;
|
|
118
153
|
|
|
119
154
|
switch (this.side) {
|
|
120
155
|
case 'top':
|
|
121
|
-
y = a.top - ph -
|
|
122
|
-
x = this.align === 'start' ? a.left +
|
|
123
|
-
: this.align === 'end' ? a.right - pw +
|
|
124
|
-
: a.left + a.width / 2 - pw / 2 +
|
|
156
|
+
y = a.top - ph - sideOffset;
|
|
157
|
+
x = this.align === 'start' ? a.left + alignOffset
|
|
158
|
+
: this.align === 'end' ? a.right - pw + alignOffset
|
|
159
|
+
: a.left + a.width / 2 - pw / 2 + alignOffset;
|
|
125
160
|
break;
|
|
126
161
|
case 'bottom':
|
|
127
|
-
y = a.bottom +
|
|
128
|
-
x = this.align === 'start' ? a.left +
|
|
129
|
-
: this.align === 'end' ? a.right - pw +
|
|
130
|
-
: a.left + a.width / 2 - pw / 2 +
|
|
162
|
+
y = a.bottom + sideOffset;
|
|
163
|
+
x = this.align === 'start' ? a.left + alignOffset
|
|
164
|
+
: this.align === 'end' ? a.right - pw + alignOffset
|
|
165
|
+
: a.left + a.width / 2 - pw / 2 + alignOffset;
|
|
131
166
|
break;
|
|
132
167
|
case 'left':
|
|
133
|
-
x = a.left - pw -
|
|
134
|
-
y = this.align === 'start' ? a.top +
|
|
135
|
-
: this.align === 'end' ? a.bottom - ph +
|
|
136
|
-
: a.top + a.height / 2 - ph / 2 +
|
|
168
|
+
x = a.left - pw - sideOffset;
|
|
169
|
+
y = this.align === 'start' ? a.top + alignOffset
|
|
170
|
+
: this.align === 'end' ? a.bottom - ph + alignOffset
|
|
171
|
+
: a.top + a.height / 2 - ph / 2 + alignOffset;
|
|
137
172
|
break;
|
|
138
173
|
case 'right':
|
|
139
|
-
x = a.right +
|
|
140
|
-
y = this.align === 'start' ? a.top +
|
|
141
|
-
: this.align === 'end' ? a.bottom - ph +
|
|
142
|
-
: a.top + a.height / 2 - ph / 2 +
|
|
174
|
+
x = a.right + sideOffset;
|
|
175
|
+
y = this.align === 'start' ? a.top + alignOffset
|
|
176
|
+
: this.align === 'end' ? a.bottom - ph + alignOffset
|
|
177
|
+
: a.top + a.height / 2 - ph / 2 + alignOffset;
|
|
143
178
|
break;
|
|
144
179
|
}
|
|
145
180
|
|
|
146
181
|
this.pos = {
|
|
147
|
-
x: Math.min(Math.max(x,
|
|
148
|
-
y: Math.min(Math.max(y,
|
|
182
|
+
x: Math.min(Math.max(x, vpPad), window.innerWidth - pw - vpPad),
|
|
183
|
+
y: Math.min(Math.max(y, vpPad), window.innerHeight - ph - vpPad),
|
|
149
184
|
};
|
|
150
185
|
}
|
|
151
186
|
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Component, Prop, State, Event, EventEmitter, Element, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
export type ModalWidth = 'sm' | 'md' | 'lg';
|
|
4
5
|
|
|
5
6
|
const WIDTH_MAP: Record<ModalWidth, string> = {
|
|
6
|
-
sm: 'var(--dimension-modal-width-sm
|
|
7
|
-
md: 'var(--dimension-modal-width-md
|
|
8
|
-
lg: 'var(--dimension-modal-width-lg
|
|
7
|
+
sm: 'var(--dimension-modal-width-sm)',
|
|
8
|
+
md: 'var(--dimension-modal-width-md)',
|
|
9
|
+
lg: 'var(--dimension-modal-width-lg)',
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
const CLOSE_ANIMATION_MS = 220;
|
|
12
|
-
|
|
13
12
|
const FOCUSABLE_SEL = [
|
|
14
13
|
'a[href]',
|
|
15
14
|
'area[href]',
|
|
@@ -77,7 +76,7 @@ export class Modal {
|
|
|
77
76
|
this.closeTimer = null;
|
|
78
77
|
this.previousFocus?.focus?.();
|
|
79
78
|
this.previousFocus = null;
|
|
80
|
-
},
|
|
79
|
+
}, this.closeAnimationMs);
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
|
|
@@ -154,6 +153,10 @@ export class Modal {
|
|
|
154
153
|
return this.modalWidth as string;
|
|
155
154
|
}
|
|
156
155
|
|
|
156
|
+
private get closeAnimationMs(): number {
|
|
157
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);
|
|
158
|
+
}
|
|
159
|
+
|
|
157
160
|
render() {
|
|
158
161
|
// Always render slots so scoped-mode slot distribution doesn't leak content.
|
|
159
162
|
// The backdrop is hidden via CSS when not active.
|
|
@@ -173,7 +176,7 @@ export class Modal {
|
|
|
173
176
|
aria-labelledby={this.titleId}
|
|
174
177
|
aria-describedby={this.subtitle ? this.subtitleId : undefined}
|
|
175
178
|
tabIndex={-1}
|
|
176
|
-
style={{ width: `min(${this.resolvedWidth}, calc(100vw -
|
|
179
|
+
style={{ width: `min(${this.resolvedWidth}, calc(100vw - 2 * var(--dimension-space-200)))` }}
|
|
177
180
|
>
|
|
178
181
|
<div class="modal-header">
|
|
179
182
|
<h2 id={this.titleId} class="text-title-small modal-title">{this.heading}</h2>
|
|
@@ -56,16 +56,14 @@ export class PanelNav {
|
|
|
56
56
|
@Prop() activeId: string = '';
|
|
57
57
|
|
|
58
58
|
/** Whether the nav is in collapsed (icon-only) state.
|
|
59
|
-
*
|
|
60
|
-
* do not also bind it from outside in that mode. */
|
|
59
|
+
* Set `storageKey` to persist across reloads. `dsNavToggle` still fires on change. */
|
|
61
60
|
@Prop({ mutable: true }) collapsed: boolean = false;
|
|
62
61
|
|
|
63
62
|
/** Viewport width (px) below which the nav auto-collapses to icon-only mode. 0 = disabled. */
|
|
64
63
|
@Prop() breakpoint: number = 0;
|
|
65
64
|
|
|
66
65
|
/** `localStorage` key used to persist the collapsed state across page loads.
|
|
67
|
-
* When set
|
|
68
|
-
* still fires for consumers that want to observe the change. */
|
|
66
|
+
* When set, collapsed state is restored on mount and written on each toggle. */
|
|
69
67
|
@Prop() storageKey: string = '';
|
|
70
68
|
|
|
71
69
|
/** Current route URL (e.g. `window.location.pathname` or the router's active URL).
|
|
@@ -321,14 +319,13 @@ export class PanelNav {
|
|
|
321
319
|
queueMicrotask(tick);
|
|
322
320
|
}
|
|
323
321
|
|
|
324
|
-
/** Centralised toggle:
|
|
325
|
-
* also mutates `collapsed` directly and persists to `localStorage`. */
|
|
322
|
+
/** Centralised toggle: updates `collapsed`, emits `dsNavToggle`, optionally persists. */
|
|
326
323
|
private applyToggle(next: boolean) {
|
|
327
|
-
this.
|
|
324
|
+
this.collapsed = next;
|
|
328
325
|
if (this.storageKey) {
|
|
329
|
-
this.collapsed = next;
|
|
330
326
|
try { localStorage.setItem(this.storageKey, String(next)); } catch { /* unavailable */ }
|
|
331
327
|
}
|
|
328
|
+
this.dsNavToggle.emit(next);
|
|
332
329
|
}
|
|
333
330
|
|
|
334
331
|
private getAllItems(): PanelNavItem[] {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Prop, State, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
import { subscribeToToasts, getToasts, toast as toastService, type ToastData, type ToastIntent } from './toast-service';
|
|
3
4
|
|
|
4
5
|
export type ToastPosition = 'top-center' | 'top-right' | 'bottom-center' | 'bottom-right';
|
|
@@ -33,6 +34,10 @@ export class ToastProvider {
|
|
|
33
34
|
|
|
34
35
|
private unsubscribe: (() => void) | null = null;
|
|
35
36
|
|
|
37
|
+
private get fadeOutMs(): number {
|
|
38
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
connectedCallback() {
|
|
37
42
|
this.unsubscribe = subscribeToToasts(() => this.syncFromStore());
|
|
38
43
|
this.syncFromStore();
|
|
@@ -80,7 +85,7 @@ export class ToastProvider {
|
|
|
80
85
|
setTimeout(() => {
|
|
81
86
|
this.items = this.items.filter(i => i.data.id !== id);
|
|
82
87
|
toastService.dismiss(id);
|
|
83
|
-
},
|
|
88
|
+
}, this.fadeOutMs);
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
private pauseItem(id: string) {
|
|
@@ -109,7 +114,7 @@ export class ToastProvider {
|
|
|
109
114
|
gap: 'var(--dimension-space-075)',
|
|
110
115
|
pointerEvents: 'none',
|
|
111
116
|
padding: 'var(--dimension-space-100)',
|
|
112
|
-
maxWidth:
|
|
117
|
+
maxWidth: `var(${TOKEN_DEFAULTS.panelWidthMd})`,
|
|
113
118
|
width: '100%',
|
|
114
119
|
boxSizing: 'border-box',
|
|
115
120
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
|
+
|
|
1
3
|
export type ToastIntent = 'neutral' | 'brand' | 'positive' | 'negative' | 'warning' | 'caution';
|
|
2
4
|
|
|
3
5
|
export interface ToastData {
|
|
@@ -30,10 +32,15 @@ export function getToasts(): ToastData[] {
|
|
|
30
32
|
return toasts;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function defaultToastDurationMs(): number {
|
|
36
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.animationDelayLong2, TOKEN_DEFAULTS.animationDelayLong2);
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
export const toast = {
|
|
34
40
|
show(options: ToastOptions): string {
|
|
35
41
|
const id = `ds-toast-${++idCounter}`;
|
|
36
|
-
|
|
42
|
+
const duration = options.duration ?? defaultToastDurationMs();
|
|
43
|
+
toasts = [...toasts, { id, intent: 'neutral', ...options, duration }];
|
|
37
44
|
emit();
|
|
38
45
|
return id;
|
|
39
46
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';
|
|
2
2
|
import type { IconSize } from '../Icon/Icon';
|
|
3
|
+
import { resolveCssLengthPx, TOKEN_DEFAULTS } from '../../utils';
|
|
3
4
|
|
|
4
5
|
export type ToggleButtonElevation = 'none' | 'flat' | 'elevated' | 'floating';
|
|
5
6
|
export type ToggleButtonSize = 'md' | 'sm' | 'xs';
|
|
@@ -7,8 +8,11 @@ export type ToggleButtonBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'a
|
|
|
7
8
|
|
|
8
9
|
const ICON_SIZE: Record<ToggleButtonSize, IconSize> = { md: 'md', sm: 'sm', xs: 'xs' };
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const ICON_TOKEN: Record<ToggleButtonSize, string> = {
|
|
12
|
+
md: TOKEN_DEFAULTS.iconographyMd,
|
|
13
|
+
sm: TOKEN_DEFAULTS.iconographySm,
|
|
14
|
+
xs: TOKEN_DEFAULTS.iconographyXs,
|
|
15
|
+
};
|
|
12
16
|
|
|
13
17
|
const TEXT_CLASS: Record<ToggleButtonSize, string> = {
|
|
14
18
|
md: 'text-body-medium-emphasis',
|
|
@@ -68,7 +72,7 @@ export class ToggleButton {
|
|
|
68
72
|
render() {
|
|
69
73
|
const size = this.size;
|
|
70
74
|
const iconSize = ICON_SIZE[size];
|
|
71
|
-
const iconPx =
|
|
75
|
+
const iconPx = resolveCssLengthPx(ICON_TOKEN[size], ICON_TOKEN[size]);
|
|
72
76
|
const textClass = TEXT_CLASS[size];
|
|
73
77
|
const hasIcon = !!this.icon;
|
|
74
78
|
const hasLabel = !!this.label;
|