@ds-mo/ui 2.11.1 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/.build-stamp +1 -1
- package/dist/components/ds-app-shell.js +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-bar-nav.js.map +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/{ds-accordion.d.ts → ds-button-unfilled.d.ts} +4 -4
- package/dist/components/ds-button-unfilled.js +2 -0
- package/dist/components/ds-card-data-viz.js +1 -1
- package/dist/components/ds-card-data-viz.js.map +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-setting.js.map +1 -1
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-bar.js.map +1 -1
- package/dist/components/ds-chart-donut.js +1 -1
- package/dist/components/ds-chart-donut.js.map +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-legend.js.map +1 -1
- package/dist/components/ds-chart-line.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-checkbox.js.map +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-chip.js.map +1 -1
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-empty-state.js.map +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-field.js.map +1 -1
- package/dist/components/ds-header.js +1 -1
- package/dist/components/ds-header.js.map +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-input.js.map +1 -1
- package/dist/components/ds-loader.js +1 -1
- package/dist/components/ds-loader.js.map +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-pagination.js +1 -1
- package/dist/components/ds-pagination.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/ds-radio-group.js +1 -1
- package/dist/components/ds-radio-group.js.map +1 -1
- package/dist/components/ds-scrollbar.js +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-skeleton.js.map +1 -1
- 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-tab-group.js.map +1 -1
- package/dist/components/ds-table.js +1 -1
- package/dist/components/ds-table.js.map +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-tag.js.map +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toggle.js +1 -1
- package/dist/components/ds-toggle.js.map +1 -1
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip-data-viz.js.map +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-tooltip.js.map +1 -1
- package/dist/components/p-B9dHCgY-.js +2 -0
- package/dist/components/p-B9dHCgY-.js.map +1 -0
- package/dist/components/p-BPK76IsW.js +2 -0
- package/dist/components/p-BPK76IsW.js.map +1 -0
- package/dist/components/p-BQq26pt9.js.map +1 -1
- package/dist/components/p-BTC9Njj6.js +2 -0
- package/dist/components/p-BTC9Njj6.js.map +1 -0
- package/dist/components/p-BZIOTcnM.js +2 -0
- package/dist/components/p-BZIOTcnM.js.map +1 -0
- package/dist/components/{p-DdFUTZ3y.js → p-C3ORQUOp.js} +2 -2
- package/dist/components/{p-8Nt92j1-.js → p-CyFVn2Sc.js} +2 -2
- package/dist/components/p-DKdI8T8v.js +2 -0
- package/dist/components/p-DKdI8T8v.js.map +1 -0
- package/dist/components/{p-DOU4cbpc.js → p-DRqxVEye.js} +2 -2
- package/dist/components/p-DkcH8mRl.js +2 -0
- package/dist/components/p-DkcH8mRl.js.map +1 -0
- package/dist/components/p-DusYu-if.js +2 -0
- package/dist/components/p-DusYu-if.js.map +1 -0
- package/dist/components/p-EK1hOP19.js.map +1 -1
- package/dist/components/p-UbKTMQjv.js +2 -0
- package/dist/components/p-UbKTMQjv.js.map +1 -0
- package/dist/lib/nav/index.js +3 -0
- package/dist/lib/nav/index.js.map +2 -2
- package/dist/lib/utils/index.js +6 -0
- package/dist/lib/utils/index.js.map +2 -2
- package/dist/lib/utils/token-defaults.d.ts +3 -0
- package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +17 -3
- package/dist/types/components/ButtonFilled/index.d.ts +1 -1
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +66 -0
- package/dist/types/components/ButtonUnfilled/index.d.ts +2 -0
- package/dist/types/components/CardDataViz/CardDataViz.d.ts +5 -1
- package/dist/types/components/CardSetting/CardSetting.d.ts +1 -1
- package/dist/types/components/ChartDonut/ChartDonut.d.ts +7 -0
- package/dist/types/components/Checkbox/Checkbox.d.ts +1 -1
- package/dist/types/components/Chip/Chip.d.ts +18 -14
- package/dist/types/components/Chip/index.d.ts +1 -1
- package/dist/types/components/Input/Input.d.ts +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +1 -1
- package/dist/types/components/PanelNav/PanelNav.d.ts +2 -0
- package/dist/types/components/PanelTools/panel-tools-types.d.ts +1 -1
- package/dist/types/components/RadioGroup/RadioGroup.d.ts +2 -2
- package/dist/types/components/Select/Select.d.ts +2 -2
- package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -1
- package/dist/types/components/Slider/Slider.d.ts +1 -1
- package/dist/types/components/TabGroup/TabGroup.d.ts +1 -1
- package/dist/types/components/TabGroup/tab-item-utils.d.ts +2 -1
- package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +1 -1
- package/dist/types/components/Tag/Tag.d.ts +0 -2
- package/dist/types/components/Tag/index.d.ts +1 -1
- package/dist/types/components/Toggle/Toggle.d.ts +1 -1
- package/dist/types/components/Tooltip/Tooltip.d.ts +25 -6
- package/dist/types/components/TooltipDataViz/TooltipDataViz.d.ts +18 -0
- package/dist/types/components.d.ts +277 -476
- package/dist/types/utils/token-defaults.d.ts +3 -0
- package/package.json +6 -3
- package/src/angular/index.ts +1 -6
- package/src/angular/proxies.ts +33 -170
- package/src/react/components.ts +1 -6
- package/src/react/ds-button-unfilled.ts +33 -0
- package/src/react/ds-chip.ts +2 -10
- package/src/wc/components/BarNav/BarNav.tsx +3 -2
- package/src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts +1 -1
- package/src/wc/components/ButtonFilled/ButtonFilled.tsx +90 -11
- package/src/wc/components/ButtonFilled/index.ts +6 -1
- package/src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx +223 -0
- package/src/wc/components/ButtonUnfilled/index.ts +6 -0
- package/src/wc/components/CardDataViz/CardDataViz.tsx +36 -2
- package/src/wc/components/CardSetting/CardSetting.tsx +4 -6
- package/src/wc/components/ChartDonut/ChartDonut.tsx +50 -3
- package/src/wc/components/ChartLegend/ChartLegend.tsx +3 -0
- package/src/wc/components/Checkbox/Checkbox.tsx +5 -5
- package/src/wc/components/Chip/Chip.tsx +50 -67
- package/src/wc/components/Chip/index.ts +1 -1
- package/src/wc/components/Input/Input.tsx +4 -4
- package/src/wc/components/Menu/Menu.tsx +7 -4
- package/src/wc/components/Pagination/Pagination.tsx +6 -6
- package/src/wc/components/PanelNav/PanelNav.tsx +77 -48
- package/src/wc/components/PanelTools/PanelTools.tsx +19 -16
- package/src/wc/components/PanelTools/panel-tools-types.ts +1 -1
- package/src/wc/components/PanelTools/panel-tools-utils.ts +1 -1
- package/src/wc/components/RadioGroup/RadioGroup.tsx +7 -7
- package/src/wc/components/Select/Select.tsx +5 -5
- package/src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx +4 -3
- package/src/wc/components/Slider/Slider.tsx +7 -3
- package/src/wc/components/TabGroup/TabGroup.tsx +13 -7
- package/src/wc/components/TabGroup/tab-item-utils.ts +2 -1
- package/src/wc/components/TabGroupNav/TabGroupNav.tsx +14 -13
- package/src/wc/components/Tag/Tag.tsx +25 -24
- package/src/wc/components/Tag/index.ts +1 -1
- package/src/wc/components/Toggle/Toggle.tsx +6 -6
- package/src/wc/components/Tooltip/Tooltip.tsx +166 -64
- package/src/wc/components/TooltipDataViz/TooltipDataViz.tsx +57 -2
- package/src/wc/components.d.ts +277 -476
- package/src/wc/utils/control-density.css +42 -0
- package/src/wc/utils/control-inactive.css +20 -0
- package/src/wc/utils/interaction-fill.css +162 -0
- package/src/wc/utils/scroll-edge-fade.css +5 -0
- package/src/wc/utils/token-defaults.ts +6 -0
- package/dist/components/ds-accordion.js +0 -2
- package/dist/components/ds-accordion.js.map +0 -1
- package/dist/components/ds-breadcrumb.d.ts +0 -11
- package/dist/components/ds-breadcrumb.js +0 -2
- package/dist/components/ds-breadcrumb.js.map +0 -1
- package/dist/components/ds-button-unfilled-icon.d.ts +0 -11
- package/dist/components/ds-button-unfilled-icon.js +0 -2
- package/dist/components/ds-fade.d.ts +0 -11
- package/dist/components/ds-fade.js +0 -2
- package/dist/components/ds-fade.js.map +0 -1
- package/dist/components/ds-surface.d.ts +0 -11
- package/dist/components/ds-surface.js +0 -2
- package/dist/components/ds-surface.js.map +0 -1
- package/dist/components/ds-toast-provider.d.ts +0 -11
- package/dist/components/ds-toast-provider.js +0 -2
- package/dist/components/ds-toast-provider.js.map +0 -1
- package/dist/components/p-BMQbEIrQ.js +0 -2
- package/dist/components/p-BMQbEIrQ.js.map +0 -1
- package/dist/components/p-BSFgy5r6.js +0 -2
- package/dist/components/p-BSFgy5r6.js.map +0 -1
- package/dist/components/p-CBUIn8dk.js +0 -2
- package/dist/components/p-CBUIn8dk.js.map +0 -1
- package/dist/components/p-ChVwCVHy.js +0 -2
- package/dist/components/p-ChVwCVHy.js.map +0 -1
- package/dist/components/p-CpNIQXz6.js +0 -2
- package/dist/components/p-CpNIQXz6.js.map +0 -1
- package/dist/components/p-CvgHQfvT.js +0 -2
- package/dist/components/p-CvgHQfvT.js.map +0 -1
- package/dist/components/p-bDf6yBQd.js +0 -2
- package/dist/components/p-bDf6yBQd.js.map +0 -1
- package/dist/types/components/Accordion/Accordion.d.ts +0 -28
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +0 -16
- package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +0 -45
- package/dist/types/components/ButtonUnfilledIcon/index.d.ts +0 -2
- package/dist/types/components/Fade/Fade.d.ts +0 -22
- package/dist/types/components/Fade/index.d.ts +0 -2
- package/dist/types/components/Surface/Surface.d.ts +0 -22
- package/dist/types/components/Toast/Toast.d.ts +0 -16
- package/dist/types/components/Toast/toast-service.d.ts +0 -24
- package/src/react/ds-accordion.ts +0 -27
- package/src/react/ds-breadcrumb.ts +0 -27
- package/src/react/ds-button-unfilled-icon.ts +0 -33
- package/src/react/ds-fade.ts +0 -26
- package/src/react/ds-surface.ts +0 -26
- package/src/react/ds-toast-provider.ts +0 -26
- package/src/wc/components/Accordion/Accordion.tsx +0 -144
- package/src/wc/components/Breadcrumb/Breadcrumb.tsx +0 -61
- package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +0 -129
- package/src/wc/components/ButtonUnfilledIcon/index.ts +0 -1
- package/src/wc/components/Fade/Fade.tsx +0 -58
- package/src/wc/components/Fade/index.ts +0 -1
- package/src/wc/components/Surface/Surface.tsx +0 -81
- package/src/wc/components/Toast/Toast.tsx +0 -179
- package/src/wc/components/Toast/toast-service.ts +0 -67
- /package/dist/components/{ds-button-unfilled-icon.js.map → ds-button-unfilled.js.map} +0 -0
- /package/dist/components/{p-DdFUTZ3y.js.map → p-C3ORQUOp.js.map} +0 -0
- /package/dist/components/{p-8Nt92j1-.js.map → p-CyFVn2Sc.js.map} +0 -0
- /package/dist/components/{p-DOU4cbpc.js.map → p-DRqxVEye.js.map} +0 -0
|
@@ -15,7 +15,7 @@ export class Slider {
|
|
|
15
15
|
@Prop() max: number = 100;
|
|
16
16
|
@Prop() step: number = 1;
|
|
17
17
|
@Prop() label!: string;
|
|
18
|
-
@Prop()
|
|
18
|
+
@Prop() isInactive: boolean = false;
|
|
19
19
|
@Prop() valueText: string | undefined;
|
|
20
20
|
/** Associates with an external <label>. Defaults to an auto-generated ID. */
|
|
21
21
|
@Prop() inputId: string | undefined;
|
|
@@ -42,7 +42,11 @@ export class Slider {
|
|
|
42
42
|
<span class="text-body-small-emphasis">{this.value}</span>
|
|
43
43
|
</div>
|
|
44
44
|
<div
|
|
45
|
-
class={{
|
|
45
|
+
class={{
|
|
46
|
+
track: true,
|
|
47
|
+
'ds-control-inactive': this.isInactive,
|
|
48
|
+
'track--at-min': pct === 0,
|
|
49
|
+
}}
|
|
46
50
|
style={{ '--slider-pct': String(pct) } as Record<string, string>}
|
|
47
51
|
>
|
|
48
52
|
<div class="track-inner">
|
|
@@ -56,7 +60,7 @@ export class Slider {
|
|
|
56
60
|
max={this.max}
|
|
57
61
|
step={this.step}
|
|
58
62
|
value={this.value}
|
|
59
|
-
disabled={this.
|
|
63
|
+
disabled={this.isInactive}
|
|
60
64
|
class="range-input"
|
|
61
65
|
aria-valuetext={this.valueText}
|
|
62
66
|
onInput={this.handleInput}
|
|
@@ -64,7 +64,7 @@ export class TabGroup {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Find the next non-
|
|
67
|
+
* Find the next non-inactive tab index relative to `from`, stepping by `step`.
|
|
68
68
|
* Returns `from` unchanged if no other enabled tab exists.
|
|
69
69
|
*/
|
|
70
70
|
private findEnabled(from: number, step: 1 | -1): number {
|
|
@@ -72,7 +72,7 @@ export class TabGroup {
|
|
|
72
72
|
const len = tabs.length;
|
|
73
73
|
for (let n = 0; n < len; n++) {
|
|
74
74
|
const i = (from + step * (n + 1) + len * (n + 1)) % len;
|
|
75
|
-
if (!tabs[i]?.
|
|
75
|
+
if (!tabs[i]?.isInactive) return i;
|
|
76
76
|
}
|
|
77
77
|
return from;
|
|
78
78
|
}
|
|
@@ -102,10 +102,10 @@ export class TabGroup {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (e.key === 'Home') {
|
|
105
|
-
const first = tabs.findIndex(t => !t.
|
|
105
|
+
const first = tabs.findIndex(t => !t.isInactive);
|
|
106
106
|
nextIdx = first === -1 ? null : first;
|
|
107
107
|
} else if (e.key === 'End') {
|
|
108
|
-
const lastEnabled = [...tabs].reverse().findIndex(t => !t.
|
|
108
|
+
const lastEnabled = [...tabs].reverse().findIndex(t => !t.isInactive);
|
|
109
109
|
nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -162,14 +162,20 @@ export class TabGroup {
|
|
|
162
162
|
tab: true,
|
|
163
163
|
'tab--selected': isSelected,
|
|
164
164
|
'ds-focus-ring-inset': true,
|
|
165
|
+
'ds-interaction-fill': !tab.isInactive,
|
|
166
|
+
'ds-interaction-fill--on-medium': bgClass === 'on-medium',
|
|
167
|
+
'ds-interaction-fill--on-bold': bgClass === 'on-bold',
|
|
168
|
+
'ds-interaction-fill--on-strong': bgClass === 'on-strong',
|
|
169
|
+
'ds-interaction-fill--on-always-dark': bgClass === 'on-always-dark',
|
|
170
|
+
'ds-control-inactive': !!tab.isInactive,
|
|
165
171
|
[bgClass]: !!bgClass,
|
|
166
172
|
}}
|
|
167
173
|
aria-selected={isSelected}
|
|
168
|
-
aria-disabled={tab.
|
|
174
|
+
aria-disabled={tab.isInactive || undefined}
|
|
169
175
|
aria-controls={tab.panelId ?? undefined}
|
|
170
|
-
disabled={tab.
|
|
176
|
+
disabled={tab.isInactive}
|
|
171
177
|
tabIndex={isSelected ? 0 : -1}
|
|
172
|
-
onClick={() => !tab.
|
|
178
|
+
onClick={() => !tab.isInactive && this.selectTab(tab.id)}
|
|
173
179
|
>
|
|
174
180
|
<span class={{
|
|
175
181
|
tab__label: true,
|
|
@@ -5,7 +5,8 @@ export interface TabItemDivider {
|
|
|
5
5
|
export interface TabItemTab {
|
|
6
6
|
id: string;
|
|
7
7
|
label: string;
|
|
8
|
-
|
|
8
|
+
/** Disables the tab (same meaning as control `isInactive`). */
|
|
9
|
+
isInactive?: boolean;
|
|
9
10
|
/** id of the tabpanel this tab controls */
|
|
10
11
|
panelId?: string;
|
|
11
12
|
/** Show a notification dot (brand) on the tab — no count, matching panel-nav. */
|
|
@@ -91,23 +91,23 @@ export class TabGroupNav {
|
|
|
91
91
|
@Method()
|
|
92
92
|
async focusLastTab() {
|
|
93
93
|
const tabs = this.selectableTabs;
|
|
94
|
-
const last = [...tabs].reverse().find(tab => !tab.
|
|
94
|
+
const last = [...tabs].reverse().find(tab => !tab.isInactive);
|
|
95
95
|
if (last) await this.focusTab(last.id);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
@Method()
|
|
99
99
|
async focusFirstTab() {
|
|
100
|
-
const first = this.selectableTabs.find(tab => !tab.
|
|
100
|
+
const first = this.selectableTabs.find(tab => !tab.isInactive);
|
|
101
101
|
if (first) await this.focusTab(first.id);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
private syncFocusedTabId(preferred: string) {
|
|
105
105
|
const tabs = this.selectableTabs;
|
|
106
|
-
if (tabs.some(tab => tab.id === preferred && !tab.
|
|
106
|
+
if (tabs.some(tab => tab.id === preferred && !tab.isInactive)) {
|
|
107
107
|
this.focusedTabId = preferred;
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
|
-
const first = tabs.find(tab => !tab.
|
|
110
|
+
const first = tabs.find(tab => !tab.isInactive);
|
|
111
111
|
this.focusedTabId = first?.id ?? '';
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -127,13 +127,13 @@ export class TabGroupNav {
|
|
|
127
127
|
private findEnabledLinear(from: number, step: 1 | -1): number | null {
|
|
128
128
|
const tabs = this.selectableTabs;
|
|
129
129
|
for (let i = from + step; i >= 0 && i < tabs.length; i += step) {
|
|
130
|
-
if (!tabs[i]?.
|
|
130
|
+
if (!tabs[i]?.isInactive) return i;
|
|
131
131
|
}
|
|
132
132
|
return null;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* Find the next non-
|
|
136
|
+
* Find the next non-inactive tab index relative to `from`, stepping by `step`.
|
|
137
137
|
* Returns `from` unchanged if no other enabled tab exists.
|
|
138
138
|
*/
|
|
139
139
|
private findEnabled(from: number, step: 1 | -1): number {
|
|
@@ -141,7 +141,7 @@ export class TabGroupNav {
|
|
|
141
141
|
const len = tabs.length;
|
|
142
142
|
for (let n = 0; n < len; n++) {
|
|
143
143
|
const i = (from + step * (n + 1) + len * (n + 1)) % len;
|
|
144
|
-
if (!tabs[i]?.
|
|
144
|
+
if (!tabs[i]?.isInactive) return i;
|
|
145
145
|
}
|
|
146
146
|
return from;
|
|
147
147
|
}
|
|
@@ -156,7 +156,7 @@ export class TabGroupNav {
|
|
|
156
156
|
private activateFocusedTab(e: KeyboardEvent) {
|
|
157
157
|
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
158
158
|
const tab = this.selectableTabs.find(item => item.id === this.focusedTabId);
|
|
159
|
-
if (!tab || tab.
|
|
159
|
+
if (!tab || tab.isInactive) return;
|
|
160
160
|
e.preventDefault();
|
|
161
161
|
this.selectTab(tab.id, { focus: true });
|
|
162
162
|
}
|
|
@@ -221,10 +221,10 @@ export class TabGroupNav {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
if (e.key === 'Home') {
|
|
224
|
-
const first = tabs.findIndex(t => !t.
|
|
224
|
+
const first = tabs.findIndex(t => !t.isInactive);
|
|
225
225
|
nextIdx = first === -1 ? null : first;
|
|
226
226
|
} else if (e.key === 'End') {
|
|
227
|
-
const lastEnabled = [...tabs].reverse().findIndex(t => !t.
|
|
227
|
+
const lastEnabled = [...tabs].reverse().findIndex(t => !t.isInactive);
|
|
228
228
|
nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -305,14 +305,15 @@ export class TabGroupNav {
|
|
|
305
305
|
tab: true,
|
|
306
306
|
'tab--selected': isSelected,
|
|
307
307
|
'ds-focus-ring-inset': true,
|
|
308
|
+
'ds-control-inactive': !!tab.isInactive,
|
|
308
309
|
[bgClass]: !!bgClass,
|
|
309
310
|
}}
|
|
310
311
|
aria-selected={isSelected}
|
|
311
|
-
aria-disabled={tab.
|
|
312
|
+
aria-disabled={tab.isInactive || undefined}
|
|
312
313
|
aria-controls={tab.panelId ?? undefined}
|
|
313
|
-
disabled={tab.
|
|
314
|
+
disabled={tab.isInactive}
|
|
314
315
|
tabIndex={this.tabIndexForTab(tab)}
|
|
315
|
-
onClick={() => !tab.
|
|
316
|
+
onClick={() => !tab.isInactive && this.selectTab(tab.id)}
|
|
316
317
|
onFocus={() => {
|
|
317
318
|
if (!this.selectionFollowsFocus) {
|
|
318
319
|
this.focusedTabId = tab.id;
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { Component, Prop, h, Host } from '@stencil/core';
|
|
2
2
|
|
|
3
|
-
export type TagIntent
|
|
4
|
-
export type TagContrast
|
|
5
|
-
export type
|
|
6
|
-
export type TagSize = 'md' | 'sm' | 'xs';
|
|
3
|
+
export type TagIntent = 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
|
|
4
|
+
export type TagContrast = 'strong' | 'bold' | 'medium' | 'faint';
|
|
5
|
+
export type TagSize = 'md' | 'sm' | 'xs';
|
|
7
6
|
|
|
7
|
+
/** Text variant per control-density recipe (md / sm / xs). */
|
|
8
8
|
const TEXT_VARIANT: Record<TagSize, string> = {
|
|
9
9
|
md: 'text-body-medium',
|
|
10
10
|
sm: 'text-body-small',
|
|
11
11
|
xs: 'text-caption',
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* `ds-icon` size prop matching control-density icon metrics
|
|
16
|
+
* (md→20 / sm→16 / xs→12 via `--dimension-iconography-*`).
|
|
17
|
+
*/
|
|
18
|
+
const ICON_SIZE: Record<TagSize, 'md' | 'sm' | 'xs'> = {
|
|
19
|
+
md: 'md',
|
|
20
|
+
sm: 'sm',
|
|
21
|
+
xs: 'xs',
|
|
22
|
+
};
|
|
15
23
|
|
|
16
24
|
@Component({
|
|
17
25
|
tag: 'ds-tag',
|
|
@@ -22,13 +30,11 @@ export class Tag {
|
|
|
22
30
|
@Prop() label!: string;
|
|
23
31
|
@Prop() intent: TagIntent = 'neutral';
|
|
24
32
|
@Prop() contrast: TagContrast = 'faint';
|
|
25
|
-
@Prop() elevation: TagElevation = 'none';
|
|
26
33
|
@Prop() size: TagSize = 'md';
|
|
27
34
|
@Prop() rounded: boolean = false;
|
|
28
35
|
@Prop() maxWidth: string | number | undefined;
|
|
29
36
|
|
|
30
37
|
render() {
|
|
31
|
-
const sz = this.size.toUpperCase() as 'MD' | 'SM' | 'XS';
|
|
32
38
|
const textVariant = TEXT_VARIANT[this.size];
|
|
33
39
|
const iconSize = ICON_SIZE[this.size];
|
|
34
40
|
|
|
@@ -36,29 +42,24 @@ export class Tag {
|
|
|
36
42
|
? { maxWidth: typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth }
|
|
37
43
|
: undefined;
|
|
38
44
|
|
|
39
|
-
const hostCls: Record<string, boolean> = {
|
|
40
|
-
'tag': true,
|
|
41
|
-
[`tag--intent-${this.intent}`]: true,
|
|
42
|
-
[`tag--contrast-${this.contrast}`]: true,
|
|
43
|
-
[`tag--elevation-${this.elevation}`]: true,
|
|
44
|
-
[`tag--size-${this.size}`]: true,
|
|
45
|
-
'tag--rounded': this.rounded,
|
|
46
|
-
'tag--rounded-no-icon-left': this.rounded, // consumer overrides via slot presence hack
|
|
47
|
-
'tag--rounded-no-remove-right': this.rounded,
|
|
48
|
-
[`tag--icon-left-${sz}`]: false, // toggled by slotchange
|
|
49
|
-
[`tag--icon-right-${sz}`]: false,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
45
|
return (
|
|
53
46
|
<Host
|
|
54
|
-
class={
|
|
47
|
+
class={{
|
|
48
|
+
'tag': true,
|
|
49
|
+
[`tag--intent-${this.intent}`]: true,
|
|
50
|
+
[`tag--contrast-${this.contrast}`]: true,
|
|
51
|
+
[`tag--size-${this.size}`]: true,
|
|
52
|
+
'ds-control--md': this.size === 'md',
|
|
53
|
+
'ds-control--sm': this.size === 'sm',
|
|
54
|
+
'ds-control--xs': this.size === 'xs',
|
|
55
|
+
'tag--rounded': this.rounded,
|
|
56
|
+
}}
|
|
55
57
|
style={maxWidthStyle}
|
|
56
58
|
>
|
|
57
|
-
{/* Leading icon
|
|
58
|
-
<span class="tag__icon-slot"
|
|
59
|
+
{/* Leading icon — consumer provides ds-icon (or any SVG); prefer size matching tag size. */}
|
|
60
|
+
<span class="tag__icon-slot" data-icon-size={iconSize}>
|
|
59
61
|
<slot name="icon" />
|
|
60
62
|
</span>
|
|
61
|
-
{/* Label */}
|
|
62
63
|
<span class={{ 'tag__label': true, [textVariant]: true }}>
|
|
63
64
|
{this.label}
|
|
64
65
|
</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { TagIntent, TagContrast,
|
|
1
|
+
export type { TagIntent, TagContrast, TagSize } from './Tag';
|
|
@@ -7,18 +7,18 @@ import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';
|
|
|
7
7
|
})
|
|
8
8
|
export class Toggle {
|
|
9
9
|
@Prop({ mutable: true }) checked: boolean = false;
|
|
10
|
-
@Prop()
|
|
10
|
+
@Prop() isInactive: boolean = false;
|
|
11
11
|
|
|
12
12
|
@Event() dsChange!: EventEmitter<boolean>;
|
|
13
13
|
|
|
14
14
|
private handleClick = () => {
|
|
15
|
-
if (this.
|
|
15
|
+
if (this.isInactive) return;
|
|
16
16
|
this.checked = !this.checked;
|
|
17
17
|
this.dsChange.emit(this.checked);
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
private handleKeyDown = (e: KeyboardEvent) => {
|
|
21
|
-
if (this.
|
|
21
|
+
if (this.isInactive) return;
|
|
22
22
|
if (e.key === ' ' || e.key === 'Enter') {
|
|
23
23
|
e.preventDefault();
|
|
24
24
|
this.handleClick();
|
|
@@ -30,9 +30,9 @@ export class Toggle {
|
|
|
30
30
|
<Host
|
|
31
31
|
role="switch"
|
|
32
32
|
aria-checked={String(this.checked)}
|
|
33
|
-
aria-disabled={this.
|
|
34
|
-
tabIndex={this.
|
|
35
|
-
class={{ toggle: true, checked: this.checked, inactive: this.
|
|
33
|
+
aria-disabled={this.isInactive || undefined}
|
|
34
|
+
tabIndex={this.isInactive ? -1 : 0}
|
|
35
|
+
class={{ toggle: true, checked: this.checked, 'ds-control-inactive': this.isInactive }}
|
|
36
36
|
onClick={this.handleClick}
|
|
37
37
|
onKeyDown={this.handleKeyDown}
|
|
38
38
|
>
|
|
@@ -1,12 +1,39 @@
|
|
|
1
|
-
import { Component, Prop,
|
|
1
|
+
import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';
|
|
2
2
|
import { resolveCssLengthPx, resolveCssTimeMs, TOKEN_CSS_LENGTHS, TOKEN_DEFAULTS } from '../../utils';
|
|
3
3
|
|
|
4
4
|
export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
|
|
5
5
|
export type TooltipAlign = 'start' | 'center' | 'end';
|
|
6
|
+
export type TooltipSize = 'md' | 'sm' | 'xs';
|
|
7
|
+
|
|
8
|
+
/** Body text per control-density size (regular weight — not emphasis). */
|
|
9
|
+
const TEXT_VARIANT: Record<TooltipSize, string> = {
|
|
10
|
+
md: 'text-body-medium',
|
|
11
|
+
sm: 'text-body-small',
|
|
12
|
+
xs: 'text-caption',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const LINE_HEIGHT: Record<TooltipSize, string> = {
|
|
16
|
+
md: 'var(--typography-lineheight-md)',
|
|
17
|
+
sm: 'var(--typography-lineheight-sm)',
|
|
18
|
+
xs: 'var(--typography-lineheight-xs)',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const FALLBACK_HEIGHT: Record<TooltipSize, string> = {
|
|
22
|
+
md: TOKEN_DEFAULTS.size400,
|
|
23
|
+
sm: TOKEN_DEFAULTS.size300,
|
|
24
|
+
xs: TOKEN_DEFAULTS.size200,
|
|
25
|
+
};
|
|
6
26
|
|
|
7
27
|
let lastDismissedAt = 0;
|
|
8
28
|
let tooltipIdCounter = 0;
|
|
29
|
+
/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */
|
|
30
|
+
let activeTooltip: Tooltip | null = null;
|
|
9
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Imperative body portal for the popup.
|
|
34
|
+
* Stencil must not own the portaled node — moving a VDOM child to `document.body`
|
|
35
|
+
* causes recreate → re-portal → @State loops that freeze the page.
|
|
36
|
+
*/
|
|
10
37
|
@Component({
|
|
11
38
|
tag: 'ds-tooltip',
|
|
12
39
|
styleUrl: 'Tooltip.css',
|
|
@@ -16,47 +43,67 @@ export class Tooltip {
|
|
|
16
43
|
@Element() el!: HTMLElement;
|
|
17
44
|
|
|
18
45
|
@Prop() label!: string;
|
|
46
|
+
|
|
47
|
+
/** Control density (height, padding, type). */
|
|
48
|
+
@Prop() size: TooltipSize = 'md';
|
|
49
|
+
|
|
19
50
|
@Prop() side: TooltipSide = 'top';
|
|
20
51
|
@Prop() align: TooltipAlign = 'center';
|
|
21
52
|
/** Gap between anchor and tooltip — number (px) or TokoMo length. */
|
|
22
53
|
@Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;
|
|
23
54
|
/** Cross-axis offset — number (px) or TokoMo length. */
|
|
24
55
|
@Prop() alignOffset: number | string = 0;
|
|
25
|
-
/**
|
|
26
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Hover show delay before the tooltip appears.
|
|
58
|
+
* Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)
|
|
59
|
+
* or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the
|
|
60
|
+
* initial hover/focus show only — after a recent dismiss, reopen is instant.
|
|
61
|
+
* Prefer the default; override only for denser chrome or rare/destructive actions.
|
|
62
|
+
*/
|
|
63
|
+
@Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;
|
|
27
64
|
@Prop() shortcutKey: string | undefined;
|
|
28
65
|
@Prop() shortcutKeyPosition: 'start' | 'end' = 'end';
|
|
29
66
|
|
|
30
|
-
@State() private visible: boolean = false;
|
|
31
|
-
@State() private closing: boolean = false;
|
|
32
|
-
@State() private pos: { x: number; y: number } = { x: 0, y: 0 };
|
|
33
|
-
|
|
34
67
|
private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;
|
|
35
68
|
private delayTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
69
|
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
37
70
|
private anchor: HTMLElement | null = null;
|
|
38
|
-
private
|
|
71
|
+
private popupEl: HTMLElement | null = null;
|
|
72
|
+
private skipEnterAnimation = false;
|
|
73
|
+
private isOpen = false;
|
|
39
74
|
private mouseEnterHandler: () => void = () => this.show();
|
|
40
75
|
private mouseLeaveHandler: () => void = () => this.hide();
|
|
76
|
+
/** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */
|
|
41
77
|
private focusHandler: () => void = () => this.show();
|
|
42
|
-
private blurHandler: () => void = () =>
|
|
78
|
+
private blurHandler: (e: FocusEvent) => void = (e) => {
|
|
79
|
+
const next = e.relatedTarget as Node | null;
|
|
80
|
+
if (next && this.anchor?.contains(next)) return;
|
|
81
|
+
this.hide();
|
|
82
|
+
};
|
|
43
83
|
|
|
44
84
|
componentDidLoad() {
|
|
45
85
|
this.bindAnchor();
|
|
46
86
|
}
|
|
47
87
|
|
|
48
88
|
disconnectedCallback() {
|
|
89
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
49
90
|
this.unbindAnchor();
|
|
50
91
|
this.clearTimers();
|
|
92
|
+
this.destroyPopup();
|
|
51
93
|
}
|
|
52
94
|
|
|
53
95
|
@Watch('label')
|
|
96
|
+
@Watch('size')
|
|
54
97
|
@Watch('side')
|
|
55
98
|
@Watch('align')
|
|
56
99
|
@Watch('sideOffset')
|
|
57
100
|
@Watch('alignOffset')
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
@Watch('shortcutKey')
|
|
102
|
+
@Watch('shortcutKeyPosition')
|
|
103
|
+
onContentOrPositionChange() {
|
|
104
|
+
if (!this.popupEl || !this.isOpen) return;
|
|
105
|
+
this.renderPopupContent();
|
|
106
|
+
this.calculatePosition();
|
|
60
107
|
}
|
|
61
108
|
|
|
62
109
|
private get viewportPadPx(): number {
|
|
@@ -72,7 +119,7 @@ export class Tooltip {
|
|
|
72
119
|
}
|
|
73
120
|
|
|
74
121
|
private get hoverDelayMs(): number {
|
|
75
|
-
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.
|
|
122
|
+
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);
|
|
76
123
|
}
|
|
77
124
|
|
|
78
125
|
private get instantReopenMs(): number {
|
|
@@ -91,7 +138,8 @@ export class Tooltip {
|
|
|
91
138
|
}
|
|
92
139
|
|
|
93
140
|
private get tooltipFallbackHeightPx(): number {
|
|
94
|
-
|
|
141
|
+
const token = FALLBACK_HEIGHT[this.size];
|
|
142
|
+
return resolveCssLengthPx(token, token);
|
|
95
143
|
}
|
|
96
144
|
|
|
97
145
|
private bindAnchor() {
|
|
@@ -103,8 +151,8 @@ export class Tooltip {
|
|
|
103
151
|
child.setAttribute('aria-describedby', this.tooltipId);
|
|
104
152
|
child.addEventListener('mouseenter', this.mouseEnterHandler);
|
|
105
153
|
child.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
106
|
-
child.addEventListener('
|
|
107
|
-
child.addEventListener('
|
|
154
|
+
child.addEventListener('focusin', this.focusHandler);
|
|
155
|
+
child.addEventListener('focusout', this.blurHandler);
|
|
108
156
|
}
|
|
109
157
|
|
|
110
158
|
private unbindAnchor() {
|
|
@@ -112,8 +160,8 @@ export class Tooltip {
|
|
|
112
160
|
this.anchor.removeAttribute('aria-describedby');
|
|
113
161
|
this.anchor.removeEventListener('mouseenter', this.mouseEnterHandler);
|
|
114
162
|
this.anchor.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
115
|
-
this.anchor.removeEventListener('
|
|
116
|
-
this.anchor.removeEventListener('
|
|
163
|
+
this.anchor.removeEventListener('focusin', this.focusHandler);
|
|
164
|
+
this.anchor.removeEventListener('focusout', this.blurHandler);
|
|
117
165
|
this.anchor = null;
|
|
118
166
|
}
|
|
119
167
|
|
|
@@ -122,39 +170,114 @@ export class Tooltip {
|
|
|
122
170
|
if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }
|
|
123
171
|
}
|
|
124
172
|
|
|
173
|
+
private destroyPopup() {
|
|
174
|
+
if (!this.popupEl) return;
|
|
175
|
+
this.popupEl.remove();
|
|
176
|
+
this.popupEl = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Drop the open tip immediately (warm handoff to another tooltip). */
|
|
180
|
+
private hideInstant() {
|
|
181
|
+
this.clearTimers();
|
|
182
|
+
lastDismissedAt = Date.now();
|
|
183
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
184
|
+
this.destroyPopup();
|
|
185
|
+
this.isOpen = false;
|
|
186
|
+
this.skipEnterAnimation = false;
|
|
187
|
+
}
|
|
188
|
+
|
|
125
189
|
private show() {
|
|
126
190
|
this.clearTimers();
|
|
127
|
-
|
|
191
|
+
let instant = Date.now() - lastDismissedAt < this.instantReopenMs;
|
|
192
|
+
if (activeTooltip && activeTooltip !== this) {
|
|
193
|
+
activeTooltip.hideInstant();
|
|
194
|
+
instant = true;
|
|
195
|
+
}
|
|
196
|
+
activeTooltip = this;
|
|
197
|
+
this.skipEnterAnimation = instant;
|
|
128
198
|
if (instant) {
|
|
129
|
-
this.
|
|
130
|
-
this.visible = true;
|
|
131
|
-
requestAnimationFrame(() => this.calculatePosition());
|
|
199
|
+
this.mountPopup();
|
|
132
200
|
return;
|
|
133
201
|
}
|
|
134
202
|
this.delayTimer = setTimeout(() => {
|
|
135
203
|
this.delayTimer = null;
|
|
136
|
-
this.
|
|
137
|
-
this.visible = true;
|
|
138
|
-
requestAnimationFrame(() => this.calculatePosition());
|
|
204
|
+
this.mountPopup();
|
|
139
205
|
}, this.hoverDelayMs);
|
|
140
206
|
}
|
|
141
207
|
|
|
142
208
|
private hide() {
|
|
143
209
|
this.clearTimers();
|
|
144
|
-
if (!this.
|
|
210
|
+
if (!this.popupEl && !this.isOpen) return;
|
|
145
211
|
lastDismissedAt = Date.now();
|
|
146
|
-
this
|
|
212
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
213
|
+
if (!this.popupEl) {
|
|
214
|
+
this.isOpen = false;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.popupEl.classList.remove('tooltip-popup--instant');
|
|
218
|
+
this.popupEl.classList.add('tooltip-popup--closing');
|
|
147
219
|
this.closeTimer = setTimeout(() => {
|
|
148
220
|
this.closeTimer = null;
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
221
|
+
this.destroyPopup();
|
|
222
|
+
this.isOpen = false;
|
|
223
|
+
this.skipEnterAnimation = false;
|
|
151
224
|
}, this.fadeOutMs);
|
|
152
225
|
}
|
|
153
226
|
|
|
227
|
+
private mountPopup() {
|
|
228
|
+
if (!this.popupEl) {
|
|
229
|
+
const tip = document.createElement('div');
|
|
230
|
+
tip.id = this.tooltipId;
|
|
231
|
+
tip.setAttribute('role', 'tooltip');
|
|
232
|
+
// `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.
|
|
233
|
+
tip.className = 'tooltip-popup sc-ds-tooltip';
|
|
234
|
+
Object.assign(tip.style, {
|
|
235
|
+
position: 'fixed',
|
|
236
|
+
left: '0',
|
|
237
|
+
top: '0',
|
|
238
|
+
zIndex: 'var(--dimension-z-index-tooltip)',
|
|
239
|
+
pointerEvents: 'none',
|
|
240
|
+
});
|
|
241
|
+
document.body.appendChild(tip);
|
|
242
|
+
this.popupEl = tip;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);
|
|
246
|
+
this.popupEl.classList.remove('tooltip-popup--closing');
|
|
247
|
+
this.renderPopupContent();
|
|
248
|
+
this.calculatePosition();
|
|
249
|
+
this.isOpen = true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private renderPopupContent() {
|
|
253
|
+
if (!this.popupEl) return;
|
|
254
|
+
const textVariant = TEXT_VARIANT[this.size];
|
|
255
|
+
const lineHeight = LINE_HEIGHT[this.size];
|
|
256
|
+
const density =
|
|
257
|
+
this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';
|
|
258
|
+
// Every node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.
|
|
259
|
+
const sc = 'sc-ds-tooltip';
|
|
260
|
+
|
|
261
|
+
const startKey =
|
|
262
|
+
this.shortcutKey && this.shortcutKeyPosition === 'start'
|
|
263
|
+
? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
|
|
264
|
+
: '';
|
|
265
|
+
const endKey =
|
|
266
|
+
this.shortcutKey && this.shortcutKeyPosition === 'end'
|
|
267
|
+
? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
|
|
268
|
+
: '';
|
|
269
|
+
|
|
270
|
+
this.popupEl.innerHTML =
|
|
271
|
+
`<div class="tooltip-inner ${density} ${sc}">` +
|
|
272
|
+
startKey +
|
|
273
|
+
`<span class="tooltip-label ${textVariant} ${sc}" style="line-height:${lineHeight}">${escapeHtml(this.label)}</span>` +
|
|
274
|
+
endKey +
|
|
275
|
+
`</div>`;
|
|
276
|
+
}
|
|
277
|
+
|
|
154
278
|
private calculatePosition() {
|
|
155
|
-
if (!this.anchor) return;
|
|
156
|
-
const tip = this.
|
|
157
|
-
if (!tip) return;
|
|
279
|
+
if (!this.anchor || !this.popupEl) return;
|
|
280
|
+
const tip = this.popupEl;
|
|
158
281
|
const a = this.anchor.getBoundingClientRect();
|
|
159
282
|
const tw = tip.offsetWidth || this.tooltipFallbackWidthPx;
|
|
160
283
|
const th = tip.offsetHeight || this.tooltipFallbackHeightPx;
|
|
@@ -190,46 +313,25 @@ export class Tooltip {
|
|
|
190
313
|
break;
|
|
191
314
|
}
|
|
192
315
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
};
|
|
316
|
+
const nextX = Math.min(Math.max(x, vpPad), window.innerWidth - tw - vpPad);
|
|
317
|
+
const nextY = Math.min(Math.max(y, vpPad), window.innerHeight - th - vpPad);
|
|
318
|
+
tip.style.transform = `translate(${Math.round(nextX)}px, ${Math.round(nextY)}px)`;
|
|
197
319
|
}
|
|
198
320
|
|
|
199
321
|
render() {
|
|
200
322
|
return (
|
|
201
323
|
<Host style={{ display: 'contents' }}>
|
|
202
324
|
<slot />
|
|
203
|
-
{(this.visible || this.closing) && (
|
|
204
|
-
<div
|
|
205
|
-
id={this.tooltipId}
|
|
206
|
-
class={{ 'tooltip-popup': true, 'tooltip-popup--closing': this.closing }}
|
|
207
|
-
role="tooltip"
|
|
208
|
-
style={{
|
|
209
|
-
position: 'fixed',
|
|
210
|
-
left: '0',
|
|
211
|
-
top: '0',
|
|
212
|
-
transform: `translate(${Math.round(this.pos.x)}px, ${Math.round(this.pos.y)}px)`,
|
|
213
|
-
zIndex: '10000',
|
|
214
|
-
pointerEvents: 'none',
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
<div class="tooltip-inner">
|
|
218
|
-
{this.shortcutKey && this.shortcutKeyPosition === 'start' && (
|
|
219
|
-
<div class="key-hint" aria-hidden="true">
|
|
220
|
-
<span class="text-caption-emphasis">{this.shortcutKey}</span>
|
|
221
|
-
</div>
|
|
222
|
-
)}
|
|
223
|
-
<span class="text-body-small">{this.label}</span>
|
|
224
|
-
{this.shortcutKey && this.shortcutKeyPosition === 'end' && (
|
|
225
|
-
<div class="key-hint" aria-hidden="true">
|
|
226
|
-
<span class="text-caption-emphasis">{this.shortcutKey}</span>
|
|
227
|
-
</div>
|
|
228
|
-
)}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
)}
|
|
232
325
|
</Host>
|
|
233
326
|
);
|
|
234
327
|
}
|
|
235
328
|
}
|
|
329
|
+
|
|
330
|
+
function escapeHtml(value: string): string {
|
|
331
|
+
return value
|
|
332
|
+
.replace(/&/g, '&')
|
|
333
|
+
.replace(/</g, '<')
|
|
334
|
+
.replace(/>/g, '>')
|
|
335
|
+
.replace(/"/g, '"')
|
|
336
|
+
.replace(/'/g, ''');
|
|
337
|
+
}
|