@ds-mo/ui 1.5.0 → 1.6.1
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/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-button-group.js +1 -1
- package/dist/components/ds-button.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-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-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/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 +42 -103
- 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 +8 -2
- package/src/angular/index.ts +0 -1
- package/src/angular/proxies.ts +0 -33
- package/src/react/components.ts +0 -1
- 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 +42 -103
- package/src/wc/nav/shell-view-transition.ts +4 -10
- package/src/wc/utils/index.ts +4 -0
- package/src/wc/utils/resolve-css-length-px.ts +64 -0
- package/src/wc/utils/resolve-css-time-ms.ts +46 -0
- package/src/wc/utils/token-defaults.ts +48 -0
- package/dist/components/ds-sidebar.d.ts +0 -11
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ds-mo/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "CompoMo — composable web components styled with TokoMo design tokens",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-components",
|
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
"types": "./src/wc/nav/index.ts",
|
|
46
46
|
"import": "./src/wc/nav/index.ts"
|
|
47
47
|
},
|
|
48
|
+
"./utils": {
|
|
49
|
+
"types": "./src/wc/utils/index.ts",
|
|
50
|
+
"import": "./src/wc/utils/index.ts"
|
|
51
|
+
},
|
|
48
52
|
"./dist/components/*": "./dist/components/*"
|
|
49
53
|
},
|
|
50
54
|
"files": [
|
|
@@ -55,6 +59,7 @@
|
|
|
55
59
|
"!src/wc/components/**/*.stories.ts",
|
|
56
60
|
"!src/wc/components/**/*.stories.tsx",
|
|
57
61
|
"src/wc/nav",
|
|
62
|
+
"src/wc/utils",
|
|
58
63
|
"src/angular",
|
|
59
64
|
"src/react"
|
|
60
65
|
],
|
|
@@ -63,7 +68,8 @@
|
|
|
63
68
|
"pretypecheck": "node scripts/generate-icon-catalog.mjs",
|
|
64
69
|
"pretest": "node scripts/generate-icon-catalog.mjs",
|
|
65
70
|
"build": "stencil build && node scripts/patch-index-types.mjs && node scripts/verify-icons-externalized.mjs && node scripts/write-build-stamp.mjs",
|
|
66
|
-
"
|
|
71
|
+
"verify:pack": "node scripts/verify-npm-pack.mjs && node scripts/verify-nav-import.mjs",
|
|
72
|
+
"test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/shell-view-transition.test.ts tests/shell-gradient.test.ts tests/resolve-css-length-px.test.ts tests/resolve-css-time-ms.test.ts tests/overlay-positioning.test.ts tests/bar-nav-utils.test.ts tests/bar-nav-tabs-menu-utils.test.ts tests/bar-nav-dom-utils.test.ts tests/icon-catalog.test.ts",
|
|
67
73
|
"test:e2e": "npm run build && playwright test",
|
|
68
74
|
"test:e2e:install": "playwright install chromium",
|
|
69
75
|
"build:docs": "stencil build --docs",
|
package/src/angular/index.ts
CHANGED
package/src/angular/proxies.ts
CHANGED
|
@@ -767,39 +767,6 @@ export declare interface DsSelect extends Components.DsSelect {
|
|
|
767
767
|
}
|
|
768
768
|
|
|
769
769
|
|
|
770
|
-
@ProxyCmp({
|
|
771
|
-
inputs: ['collapsed', 'mobile', 'resizable', 'width']
|
|
772
|
-
})
|
|
773
|
-
@Component({
|
|
774
|
-
selector: 'ds-sidebar',
|
|
775
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
776
|
-
template: '<ng-content></ng-content>',
|
|
777
|
-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
778
|
-
inputs: ['collapsed', 'mobile', 'resizable', 'width'],
|
|
779
|
-
outputs: ['dsToggle', 'dsWidthChange'],
|
|
780
|
-
standalone: false
|
|
781
|
-
})
|
|
782
|
-
export class DsSidebar {
|
|
783
|
-
protected el: HTMLDsSidebarElement;
|
|
784
|
-
@Output() dsToggle = new EventEmitter<CustomEvent<void>>();
|
|
785
|
-
@Output() dsWidthChange = new EventEmitter<CustomEvent<IDsSidebarSidebarWidth>>();
|
|
786
|
-
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
787
|
-
c.detach();
|
|
788
|
-
this.el = r.nativeElement;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
import type { SidebarWidth as IDsSidebarSidebarWidth } from '@ds-mo/ui';
|
|
794
|
-
|
|
795
|
-
export declare interface DsSidebar extends Components.DsSidebar {
|
|
796
|
-
|
|
797
|
-
dsToggle: EventEmitter<CustomEvent<void>>;
|
|
798
|
-
|
|
799
|
-
dsWidthChange: EventEmitter<CustomEvent<IDsSidebarSidebarWidth>>;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
|
|
803
770
|
@ProxyCmp({
|
|
804
771
|
inputs: ['height', 'lines', 'shimmer', 'variant', 'width']
|
|
805
772
|
})
|
package/src/react/components.ts
CHANGED
|
@@ -31,7 +31,6 @@ export { DsPanelNav } from "./ds-panel-nav.js";
|
|
|
31
31
|
export { DsRadioGroup } from "./ds-radio-group.js";
|
|
32
32
|
export { DsScrollbar } from "./ds-scrollbar.js";
|
|
33
33
|
export { DsSelect } from "./ds-select.js";
|
|
34
|
-
export { DsSidebar } from "./ds-sidebar.js";
|
|
35
34
|
export { DsSkeleton } from "./ds-skeleton.js";
|
|
36
35
|
export { DsSlider } from "./ds-slider.js";
|
|
37
36
|
export { DsSurface } from "./ds-surface.js";
|
|
@@ -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;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { Component, Prop, State, Element, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssLengthPx, resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
|
|
4
5
|
export type TooltipAlign = 'start' | 'center' | 'end';
|
|
5
6
|
|
|
6
|
-
const HOVER_DELAY_MS = 600;
|
|
7
|
-
const INSTANT_REOPEN_MS = 300;
|
|
8
|
-
const FADE_OUT_MS = 200;
|
|
9
|
-
const VP_PAD = 4;
|
|
10
|
-
|
|
11
7
|
let lastDismissedAt = 0;
|
|
12
8
|
let tooltipIdCounter = 0;
|
|
13
9
|
|
|
@@ -22,9 +18,12 @@ export class Tooltip {
|
|
|
22
18
|
@Prop() label!: string;
|
|
23
19
|
@Prop() side: TooltipSide = 'top';
|
|
24
20
|
@Prop() align: TooltipAlign = 'center';
|
|
25
|
-
|
|
26
|
-
@Prop()
|
|
27
|
-
|
|
21
|
+
/** Gap between anchor and tooltip — number (px) or TokoMo length. */
|
|
22
|
+
@Prop() sideOffset: number | string = TOKEN_DEFAULTS.space050;
|
|
23
|
+
/** Cross-axis offset — number (px) or TokoMo length. */
|
|
24
|
+
@Prop() alignOffset: number | string = 0;
|
|
25
|
+
/** Show delay — number (ms) or TokoMo time token / `var(--effect-animation-delay-*)`. */
|
|
26
|
+
@Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium1;
|
|
28
27
|
@Prop() shortcutKey: string | undefined;
|
|
29
28
|
@Prop() shortcutKeyPosition: 'start' | 'end' = 'end';
|
|
30
29
|
|
|
@@ -54,10 +53,47 @@ export class Tooltip {
|
|
|
54
53
|
@Watch('label')
|
|
55
54
|
@Watch('side')
|
|
56
55
|
@Watch('align')
|
|
56
|
+
@Watch('sideOffset')
|
|
57
|
+
@Watch('alignOffset')
|
|
57
58
|
onPositionPropsChange() {
|
|
58
59
|
if (this.visible) this.calculatePosition();
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
private get viewportPadPx(): number {
|
|
63
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private get sideOffsetPx(): number {
|
|
67
|
+
return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private get alignOffsetPx(): number {
|
|
71
|
+
return resolveCssLengthPx(this.alignOffset, 0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private get hoverDelayMs(): number {
|
|
75
|
+
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private get instantReopenMs(): number {
|
|
79
|
+
return resolveCssTimeMs(
|
|
80
|
+
TOKEN_DEFAULTS.animationDurationMedium1,
|
|
81
|
+
TOKEN_DEFAULTS.animationDurationMedium1,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private get fadeOutMs(): number {
|
|
86
|
+
return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private get tooltipFallbackWidthPx(): number {
|
|
90
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.tooltipFallbackWidth, TOKEN_DEFAULTS.tooltipFallbackWidth);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private get tooltipFallbackHeightPx(): number {
|
|
94
|
+
return resolveCssLengthPx(TOKEN_DEFAULTS.size300, TOKEN_DEFAULTS.size300);
|
|
95
|
+
}
|
|
96
|
+
|
|
61
97
|
private bindAnchor() {
|
|
62
98
|
const slot = this.el.querySelector('slot') as HTMLSlotElement | null;
|
|
63
99
|
const slotted = slot?.assignedElements?.() ?? [];
|
|
@@ -88,7 +124,7 @@ export class Tooltip {
|
|
|
88
124
|
|
|
89
125
|
private show() {
|
|
90
126
|
this.clearTimers();
|
|
91
|
-
const instant = Date.now() - lastDismissedAt <
|
|
127
|
+
const instant = Date.now() - lastDismissedAt < this.instantReopenMs;
|
|
92
128
|
if (instant) {
|
|
93
129
|
this.closing = false;
|
|
94
130
|
this.visible = true;
|
|
@@ -100,7 +136,7 @@ export class Tooltip {
|
|
|
100
136
|
this.closing = false;
|
|
101
137
|
this.visible = true;
|
|
102
138
|
requestAnimationFrame(() => this.calculatePosition());
|
|
103
|
-
}, this.
|
|
139
|
+
}, this.hoverDelayMs);
|
|
104
140
|
}
|
|
105
141
|
|
|
106
142
|
private hide() {
|
|
@@ -112,7 +148,7 @@ export class Tooltip {
|
|
|
112
148
|
this.closeTimer = null;
|
|
113
149
|
this.visible = false;
|
|
114
150
|
this.closing = false;
|
|
115
|
-
},
|
|
151
|
+
}, this.fadeOutMs);
|
|
116
152
|
}
|
|
117
153
|
|
|
118
154
|
private calculatePosition() {
|
|
@@ -120,40 +156,43 @@ export class Tooltip {
|
|
|
120
156
|
const tip = this.el.querySelector('.tooltip-popup') as HTMLElement | null;
|
|
121
157
|
if (!tip) return;
|
|
122
158
|
const a = this.anchor.getBoundingClientRect();
|
|
123
|
-
const tw = tip.offsetWidth ||
|
|
124
|
-
const th = tip.offsetHeight ||
|
|
159
|
+
const tw = tip.offsetWidth || this.tooltipFallbackWidthPx;
|
|
160
|
+
const th = tip.offsetHeight || this.tooltipFallbackHeightPx;
|
|
161
|
+
const sideOffset = this.sideOffsetPx;
|
|
162
|
+
const alignOffset = this.alignOffsetPx;
|
|
163
|
+
const vpPad = this.viewportPadPx;
|
|
125
164
|
let x = 0, y = 0;
|
|
126
165
|
|
|
127
166
|
switch (this.side) {
|
|
128
167
|
case 'top':
|
|
129
|
-
y = a.top - th -
|
|
130
|
-
x = this.align === 'start' ? a.left +
|
|
131
|
-
: this.align === 'end' ? a.right - tw +
|
|
132
|
-
: a.left + a.width / 2 - tw / 2 +
|
|
168
|
+
y = a.top - th - sideOffset;
|
|
169
|
+
x = this.align === 'start' ? a.left + alignOffset
|
|
170
|
+
: this.align === 'end' ? a.right - tw + alignOffset
|
|
171
|
+
: a.left + a.width / 2 - tw / 2 + alignOffset;
|
|
133
172
|
break;
|
|
134
173
|
case 'bottom':
|
|
135
|
-
y = a.bottom +
|
|
136
|
-
x = this.align === 'start' ? a.left +
|
|
137
|
-
: this.align === 'end' ? a.right - tw +
|
|
138
|
-
: a.left + a.width / 2 - tw / 2 +
|
|
174
|
+
y = a.bottom + sideOffset;
|
|
175
|
+
x = this.align === 'start' ? a.left + alignOffset
|
|
176
|
+
: this.align === 'end' ? a.right - tw + alignOffset
|
|
177
|
+
: a.left + a.width / 2 - tw / 2 + alignOffset;
|
|
139
178
|
break;
|
|
140
179
|
case 'left':
|
|
141
|
-
x = a.left - tw -
|
|
142
|
-
y = this.align === 'start' ? a.top +
|
|
143
|
-
: this.align === 'end' ? a.bottom - th +
|
|
144
|
-
: a.top + a.height / 2 - th / 2 +
|
|
180
|
+
x = a.left - tw - sideOffset;
|
|
181
|
+
y = this.align === 'start' ? a.top + alignOffset
|
|
182
|
+
: this.align === 'end' ? a.bottom - th + alignOffset
|
|
183
|
+
: a.top + a.height / 2 - th / 2 + alignOffset;
|
|
145
184
|
break;
|
|
146
185
|
case 'right':
|
|
147
|
-
x = a.right +
|
|
148
|
-
y = this.align === 'start' ? a.top +
|
|
149
|
-
: this.align === 'end' ? a.bottom - th +
|
|
150
|
-
: a.top + a.height / 2 - th / 2 +
|
|
186
|
+
x = a.right + sideOffset;
|
|
187
|
+
y = this.align === 'start' ? a.top + alignOffset
|
|
188
|
+
: this.align === 'end' ? a.bottom - th + alignOffset
|
|
189
|
+
: a.top + a.height / 2 - th / 2 + alignOffset;
|
|
151
190
|
break;
|
|
152
191
|
}
|
|
153
192
|
|
|
154
193
|
this.pos = {
|
|
155
|
-
x: Math.min(Math.max(x,
|
|
156
|
-
y: Math.min(Math.max(y,
|
|
194
|
+
x: Math.min(Math.max(x, vpPad), window.innerWidth - tw - vpPad),
|
|
195
|
+
y: Math.min(Math.max(y, vpPad), window.innerHeight - th - vpPad),
|
|
157
196
|
};
|
|
158
197
|
}
|
|
159
198
|
|