@ds-mo/ui 1.2.0 → 1.4.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 +82 -2
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-badge.js.map +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-action.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/ds-breadcrumb.js +1 -1
- package/dist/components/ds-button-group.js +1 -1
- package/dist/components/ds-button.js +1 -1
- package/dist/components/ds-card.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.d.ts +11 -0
- package/dist/components/ds-chip.js +2 -0
- package/dist/components/ds-chip.js.map +1 -0
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-fade.js +1 -1
- package/dist/components/ds-fade.js.map +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-header.js +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.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-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-sidebar.js +1 -1
- package/dist/components/ds-sidebar.js.map +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-surface.js +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-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-toast-provider.js +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/index.d.ts +3 -4
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/{p-COsKOwZR.js → p--YDw8Q4o.js} +2 -2
- package/dist/components/p-B_enucPQ.js +2 -0
- package/dist/components/p-B_enucPQ.js.map +1 -0
- package/dist/components/{p-Cj00JkJb.js → p-C9za7IDu.js} +2 -2
- package/dist/components/p-DceSoh5V.js +2 -0
- package/dist/components/p-DceSoh5V.js.map +1 -0
- package/dist/components/{p-H89PzqGU.js → p-DqBCQRRd.js} +2 -2
- package/dist/components/p-DznbYg1m.js +2 -0
- package/dist/components/p-DznbYg1m.js.map +1 -0
- package/dist/components/p-_LI2ymhS.js +2 -0
- package/dist/components/p-_LI2ymhS.js.map +1 -0
- package/dist/components/{p-CE5zeV1w.js → p-uMN1Ppw2.js} +2 -2
- package/dist/components/p-vW9EMP8s.js +2 -0
- package/dist/components/p-vW9EMP8s.js.map +1 -0
- package/dist/types/components/Badge/Badge.d.ts +20 -0
- package/dist/types/components/Badge/index.d.ts +2 -0
- package/dist/types/{.stencil → components}/BarNav/BarNav.d.ts +6 -17
- package/dist/types/components/BarNav/bar-nav-types.d.ts +14 -0
- package/dist/types/{.stencil → components}/BarNav/bar-nav-utils.d.ts +1 -1
- package/dist/types/components/BarNav/index.d.ts +3 -0
- package/dist/types/{.stencil → components}/BarNavAction/BarNavAction.d.ts +1 -1
- package/dist/types/components/Chip/Chip.d.ts +30 -0
- package/dist/types/components/Chip/index.d.ts +2 -0
- package/dist/types/components/Fade/Fade.d.ts +25 -0
- package/dist/types/components/Fade/index.d.ts +2 -0
- package/dist/types/{.stencil → components}/PanelNav/PanelNav.d.ts +11 -26
- package/dist/types/components/PanelNav/index.d.ts +4 -0
- package/dist/types/components/PanelNav/panel-nav-types.d.ts +20 -0
- package/dist/types/components/PanelNav/panel-nav-utils.d.ts +26 -0
- package/dist/types/{.stencil → components}/TabGroupNav/TabGroupNav.d.ts +1 -1
- package/dist/types/components/Tag/Tag.d.ts +15 -0
- package/dist/types/components/Tag/index.d.ts +2 -0
- package/dist/types/components.d.ts +256 -108
- package/dist/types/nav/index.d.ts +5 -0
- package/dist/types/nav/nav-chrome.d.ts +14 -0
- package/dist/types/nav/shell-view-transition.d.ts +21 -0
- package/package.json +18 -6
- package/src/angular/index.ts +1 -0
- package/src/angular/proxies.ts +51 -28
- package/src/react/components.ts +1 -0
- package/src/react/ds-chip.ts +35 -0
- package/src/react/ds-tag.ts +3 -12
- package/src/wc/components/Badge/Badge.tsx +58 -8
- package/src/wc/components/Badge/index.ts +1 -1
- package/src/wc/components/Banner/Banner.tsx +2 -2
- package/src/wc/components/BarNav/BarNav.tsx +24 -26
- package/src/wc/components/BarNav/bar-nav-types.ts +15 -0
- package/src/wc/components/BarNav/bar-nav-utils.ts +1 -1
- package/src/wc/components/BarNav/index.ts +2 -1
- package/src/wc/components/BarNavAction/BarNavAction.tsx +11 -2
- package/src/wc/components/Chip/Chip.tsx +127 -0
- package/src/wc/components/Chip/index.ts +1 -0
- package/src/wc/components/Fade/Fade.tsx +86 -6
- package/src/wc/components/Fade/index.ts +1 -1
- package/src/wc/components/PanelNav/PanelNav.tsx +63 -91
- package/src/wc/components/PanelNav/index.ts +9 -4
- package/src/wc/components/PanelNav/panel-nav-types.ts +22 -0
- package/src/wc/components/PanelNav/panel-nav-utils.ts +32 -64
- package/src/wc/components/TabGroup/TabGroup.tsx +9 -1
- package/src/wc/components/TabGroupNav/TabGroupNav.tsx +17 -3
- package/src/wc/components/Tag/Tag.tsx +3 -70
- package/src/wc/components/Tag/index.ts +1 -1
- package/src/wc/components.d.ts +256 -108
- package/src/wc/nav/index.ts +19 -0
- package/src/wc/nav/nav-chrome.ts +45 -0
- package/src/wc/nav/shell-view-transition.ts +102 -0
- package/dist/components/p-CQQe6VNo.js +0 -2
- package/dist/components/p-CQQe6VNo.js.map +0 -1
- package/dist/components/p-Vwlmhl4E.js +0 -2
- package/dist/components/p-Vwlmhl4E.js.map +0 -1
- package/dist/types/.stencil/Badge/Badge.d.ts +0 -8
- package/dist/types/.stencil/Badge/index.d.ts +0 -2
- package/dist/types/.stencil/BarNav/index.d.ts +0 -2
- package/dist/types/.stencil/Fade/Fade.d.ts +0 -10
- package/dist/types/.stencil/Fade/index.d.ts +0 -2
- package/dist/types/.stencil/PanelNav/index.d.ts +0 -3
- package/dist/types/.stencil/PanelNav/panel-nav-utils.d.ts +0 -27
- package/dist/types/.stencil/Tag/Tag.d.ts +0 -32
- package/dist/types/.stencil/Tag/index.d.ts +0 -2
- /package/dist/components/{p-COsKOwZR.js.map → p--YDw8Q4o.js.map} +0 -0
- /package/dist/components/{p-Cj00JkJb.js.map → p-C9za7IDu.js.map} +0 -0
- /package/dist/components/{p-H89PzqGU.js.map → p-DqBCQRRd.js.map} +0 -0
- /package/dist/components/{p-CE5zeV1w.js.map → p-uMN1Ppw2.js.map} +0 -0
- /package/dist/types/{.stencil → components}/Accordion/Accordion.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Banner/Banner.d.ts +0 -0
- /package/dist/types/{.stencil → components}/BarNav/bar-nav-dom-utils.d.ts +0 -0
- /package/dist/types/{.stencil → components}/BarNav/bar-nav-tabs-menu-utils.d.ts +0 -0
- /package/dist/types/{.stencil → components}/BarNavAction/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Breadcrumb/Breadcrumb.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Button/Button.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Button/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/ButtonGroup/ButtonGroup.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Card/Card.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Checkbox/Checkbox.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Checkbox/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Divider/Divider.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Divider/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/EmptyState/EmptyState.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Field/Field.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Header/Header.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Icon/Icon.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Icon/flag-icon-catalog.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Icon/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Icon/system-icon-catalog.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Input/Input.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Input/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Loader/Loader.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Loader/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Menu/Menu.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Modal/Modal.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Pagination/Pagination.d.ts +0 -0
- /package/dist/types/{.stencil → components}/RadioGroup/RadioGroup.d.ts +0 -0
- /package/dist/types/{.stencil → components}/RadioGroup/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Scrollbar/Scrollbar.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Select/Select.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Sidebar/Sidebar.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Skeleton/Skeleton.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Skeleton/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Slider/Slider.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Slider/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Surface/Surface.d.ts +0 -0
- /package/dist/types/{.stencil → components}/TabGroup/TabGroup.d.ts +0 -0
- /package/dist/types/{.stencil → components}/TabGroup/tab-item-utils.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Table/Table.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Text/Text.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Text/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Toast/Toast.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Toast/toast-service.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Toggle/Toggle.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Toggle/index.d.ts +0 -0
- /package/dist/types/{.stencil → components}/ToggleButton/ToggleButton.d.ts +0 -0
- /package/dist/types/{.stencil → components}/ToggleButtonGroup/ToggleButtonGroup.d.ts +0 -0
- /package/dist/types/{.stencil → components}/Tooltip/Tooltip.d.ts +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';
|
|
2
|
+
|
|
3
|
+
export type ChipIntent = 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
|
|
4
|
+
export type ChipContrast = 'strong' | 'bold' | 'medium' | 'faint';
|
|
5
|
+
export type ChipElevation = 'none' | 'flat' | 'elevated';
|
|
6
|
+
export type ChipSize = 'md' | 'sm' | 'xs';
|
|
7
|
+
export type ChipBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';
|
|
8
|
+
|
|
9
|
+
const TEXT_VARIANT: Record<ChipSize, string> = {
|
|
10
|
+
md: 'text-body-medium',
|
|
11
|
+
sm: 'text-body-small',
|
|
12
|
+
xs: 'text-caption',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const ICON_SIZE: Record<ChipSize, number> = { md: 20, sm: 16, xs: 12 };
|
|
16
|
+
|
|
17
|
+
@Component({
|
|
18
|
+
tag: 'ds-chip',
|
|
19
|
+
styleUrl: 'Chip.css',
|
|
20
|
+
scoped: true,
|
|
21
|
+
})
|
|
22
|
+
export class Chip {
|
|
23
|
+
@Prop() label!: string;
|
|
24
|
+
@Prop() intent: ChipIntent = 'neutral';
|
|
25
|
+
@Prop() contrast: ChipContrast = 'faint';
|
|
26
|
+
@Prop() elevation: ChipElevation = 'none';
|
|
27
|
+
@Prop() size: ChipSize = 'md';
|
|
28
|
+
@Prop() rounded: boolean = false;
|
|
29
|
+
@Prop() removable: boolean = false;
|
|
30
|
+
@Prop() maxWidth: string | number | undefined;
|
|
31
|
+
@Prop() inactive: boolean = false;
|
|
32
|
+
@Prop() background: ChipBackground | undefined;
|
|
33
|
+
@Prop({ mutable: true }) pressed: boolean = false;
|
|
34
|
+
|
|
35
|
+
/** Fired when the remove button is clicked. */
|
|
36
|
+
@Event() dsRemove!: EventEmitter<void>;
|
|
37
|
+
/** Fired when an interactive chip is clicked. */
|
|
38
|
+
@Event() dsClick!: EventEmitter<void>;
|
|
39
|
+
/** Fired when the pressed state toggles. */
|
|
40
|
+
@Event() dsPressedChange!: EventEmitter<boolean>;
|
|
41
|
+
|
|
42
|
+
private handleClick = () => {
|
|
43
|
+
if (this.inactive) return;
|
|
44
|
+
this.pressed = !this.pressed;
|
|
45
|
+
this.dsPressedChange.emit(this.pressed);
|
|
46
|
+
this.dsClick.emit();
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
private handleKeyDown = (e: KeyboardEvent) => {
|
|
50
|
+
if (this.inactive) return;
|
|
51
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
this.handleClick();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
private handleRemove = (e: MouseEvent) => {
|
|
58
|
+
e.stopPropagation();
|
|
59
|
+
this.dsRemove.emit();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
render() {
|
|
63
|
+
const sz = this.size.toUpperCase() as 'MD' | 'SM' | 'XS';
|
|
64
|
+
const textVariant = TEXT_VARIANT[this.size];
|
|
65
|
+
const iconSize = ICON_SIZE[this.size];
|
|
66
|
+
|
|
67
|
+
const maxWidthStyle = this.maxWidth != null
|
|
68
|
+
? { maxWidth: typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth }
|
|
69
|
+
: undefined;
|
|
70
|
+
|
|
71
|
+
const hostCls: Record<string, boolean> = {
|
|
72
|
+
'tag': true,
|
|
73
|
+
[`tag--intent-${this.intent}`]: true,
|
|
74
|
+
[`tag--contrast-${this.contrast}`]: true,
|
|
75
|
+
[`tag--elevation-${this.elevation}`]: true,
|
|
76
|
+
[`tag--size-${this.size}`]: true,
|
|
77
|
+
'tag--rounded': this.rounded,
|
|
78
|
+
'tag--rounded-no-icon-left': this.rounded,
|
|
79
|
+
'tag--rounded-no-remove-right': this.rounded && !this.removable,
|
|
80
|
+
[`tag--icon-left-${sz}`]: false,
|
|
81
|
+
[`tag--icon-right-${sz}`]: this.removable,
|
|
82
|
+
'tag--removable': this.removable,
|
|
83
|
+
'tag--interactive': true,
|
|
84
|
+
'tag--inactive': this.inactive,
|
|
85
|
+
'tag--pressed': this.pressed,
|
|
86
|
+
'tag--on-medium': this.background === 'medium',
|
|
87
|
+
'tag--on-bold': this.background === 'bold',
|
|
88
|
+
'tag--on-strong': this.background === 'strong',
|
|
89
|
+
'tag--on-always-dark': this.background === 'always-dark',
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Host
|
|
94
|
+
class={hostCls}
|
|
95
|
+
style={maxWidthStyle}
|
|
96
|
+
onClick={this.handleClick}
|
|
97
|
+
onKeyDown={this.handleKeyDown}
|
|
98
|
+
role="button"
|
|
99
|
+
tabIndex={this.inactive ? -1 : 0}
|
|
100
|
+
aria-pressed={String(this.pressed)}
|
|
101
|
+
aria-disabled={this.inactive || undefined}
|
|
102
|
+
>
|
|
103
|
+
<span class="tag__icon-slot" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
|
|
104
|
+
<slot name="icon" />
|
|
105
|
+
</span>
|
|
106
|
+
<span class={{ 'tag__label': true, [textVariant]: true }}>
|
|
107
|
+
{this.label}
|
|
108
|
+
</span>
|
|
109
|
+
{this.removable && (
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
class="tag__remove"
|
|
113
|
+
onClick={this.handleRemove}
|
|
114
|
+
aria-label={`Remove ${this.label}`}
|
|
115
|
+
tabIndex={-1}
|
|
116
|
+
>
|
|
117
|
+
<slot name="remove-icon">
|
|
118
|
+
<span class="tag__remove-x" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
|
|
119
|
+
✕
|
|
120
|
+
</span>
|
|
121
|
+
</slot>
|
|
122
|
+
</button>
|
|
123
|
+
)}
|
|
124
|
+
</Host>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { ChipIntent, ChipContrast, ChipElevation, ChipSize, ChipBackground } from './Chip';
|
|
@@ -1,30 +1,110 @@
|
|
|
1
1
|
import { Component, Prop, h, Host } from '@stencil/core';
|
|
2
2
|
|
|
3
3
|
export type FadeSide = 'top' | 'bottom' | 'left' | 'right';
|
|
4
|
+
export type FadeSurface =
|
|
5
|
+
| 'default'
|
|
6
|
+
| 'primary'
|
|
7
|
+
| 'secondary'
|
|
8
|
+
| 'navigation'
|
|
9
|
+
| 'media'
|
|
10
|
+
| 'always-dark'
|
|
11
|
+
| 'inverted';
|
|
12
|
+
export type FadeSizeToken =
|
|
13
|
+
| 'size-000'
|
|
14
|
+
| 'size-050'
|
|
15
|
+
| 'size-075'
|
|
16
|
+
| 'size-100'
|
|
17
|
+
| 'size-150'
|
|
18
|
+
| 'size-200'
|
|
19
|
+
| 'size-250'
|
|
20
|
+
| 'size-300'
|
|
21
|
+
| 'size-400'
|
|
22
|
+
| 'size-500'
|
|
23
|
+
| 'size-600'
|
|
24
|
+
| 'size-800';
|
|
25
|
+
export type FadeSize = FadeSizeToken | (string & {});
|
|
26
|
+
|
|
27
|
+
const SIZE_VALUE: Record<FadeSizeToken, string> = {
|
|
28
|
+
'size-000': 'var(--dimension-size-000)',
|
|
29
|
+
'size-050': 'var(--dimension-size-050)',
|
|
30
|
+
'size-075': 'var(--dimension-size-075)',
|
|
31
|
+
'size-100': 'var(--dimension-size-100)',
|
|
32
|
+
'size-150': 'var(--dimension-size-150)',
|
|
33
|
+
'size-200': 'var(--dimension-size-200)',
|
|
34
|
+
'size-250': 'var(--dimension-size-250)',
|
|
35
|
+
'size-300': 'var(--dimension-size-300)',
|
|
36
|
+
'size-400': 'var(--dimension-size-400)',
|
|
37
|
+
'size-500': 'var(--dimension-size-500)',
|
|
38
|
+
'size-600': 'var(--dimension-size-600)',
|
|
39
|
+
'size-800': 'var(--dimension-size-800)',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const SURFACE_BACKGROUND: Record<FadeSurface, string> = {
|
|
43
|
+
default: 'var(--color-background-secondary)',
|
|
44
|
+
primary: 'var(--color-background-primary)',
|
|
45
|
+
secondary: 'var(--color-background-secondary)',
|
|
46
|
+
navigation: 'var(--color-navigation-background)',
|
|
47
|
+
media: 'var(--color-media-background)',
|
|
48
|
+
'always-dark': 'var(--color-always-dark-background)',
|
|
49
|
+
inverted: 'var(--color-inverted-background)',
|
|
50
|
+
};
|
|
4
51
|
|
|
5
52
|
@Component({
|
|
6
53
|
tag: 'ds-fade',
|
|
7
54
|
styleUrl: 'Fade.css',
|
|
8
|
-
|
|
55
|
+
scoped: true,
|
|
9
56
|
})
|
|
10
57
|
export class Fade {
|
|
11
|
-
|
|
12
|
-
|
|
58
|
+
/** Edge where the fade is anchored. */
|
|
59
|
+
@Prop() side: FadeSide = 'bottom';
|
|
60
|
+
|
|
61
|
+
/** Fade depth along the fade axis. Accepts dimension size token names or any CSS length. */
|
|
62
|
+
@Prop() size: FadeSize = 'size-600';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deprecated alias for size. Kept for existing consumers that pass a raw CSS length.
|
|
66
|
+
* Prefer `size`.
|
|
67
|
+
*/
|
|
13
68
|
@Prop() height: string | undefined;
|
|
14
|
-
|
|
69
|
+
|
|
70
|
+
/** Surface context used to choose the fade target background token. */
|
|
71
|
+
@Prop() surface: FadeSurface = 'default';
|
|
72
|
+
|
|
73
|
+
/** Direct background override for contexts that already expose a resolved surface var. */
|
|
15
74
|
@Prop() background: string = 'var(--color-background-secondary)';
|
|
16
75
|
|
|
76
|
+
/** Controls visibility without removing the element from layout/positioning. */
|
|
77
|
+
@Prop() visible: boolean = true;
|
|
78
|
+
|
|
79
|
+
private resolveSize(): string {
|
|
80
|
+
if (this.height) return this.height;
|
|
81
|
+
|
|
82
|
+
return Object.prototype.hasOwnProperty.call(SIZE_VALUE, this.size)
|
|
83
|
+
? SIZE_VALUE[this.size as FadeSizeToken]
|
|
84
|
+
: this.size;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private resolveBackground(): string {
|
|
88
|
+
if (this.background !== 'var(--color-background-secondary)') return this.background;
|
|
89
|
+
return SURFACE_BACKGROUND[this.surface];
|
|
90
|
+
}
|
|
91
|
+
|
|
17
92
|
render() {
|
|
93
|
+
const isVerticalEdge = this.side === 'top' || this.side === 'bottom';
|
|
94
|
+
|
|
18
95
|
return (
|
|
19
96
|
<Host
|
|
20
97
|
aria-hidden="true"
|
|
21
98
|
class={{
|
|
22
99
|
fade: true,
|
|
100
|
+
'fade--hidden': !this.visible,
|
|
101
|
+
'fade--vertical-edge': isVerticalEdge,
|
|
102
|
+
'fade--horizontal-edge': !isVerticalEdge,
|
|
23
103
|
[`fade--${this.side}`]: true,
|
|
24
104
|
}}
|
|
25
105
|
style={{
|
|
26
|
-
|
|
27
|
-
'--ds-fade-bg': this.
|
|
106
|
+
'--ds-fade-size': this.resolveSize(),
|
|
107
|
+
'--ds-fade-bg': this.resolveBackground(),
|
|
28
108
|
} as Record<string, string>}
|
|
29
109
|
/>
|
|
30
110
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { FadeSide } from './Fade';
|
|
1
|
+
export type { FadeSide, FadeSize, FadeSizeToken, FadeSurface } from './Fade';
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { Component, Prop, Event, EventEmitter, Watch, State, Element, h, Host } from '@stencil/core';
|
|
2
|
+
import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
3
|
+
import {
|
|
4
|
+
animateShellNavRadialReveal,
|
|
5
|
+
ensureShellNavVtStyle,
|
|
6
|
+
resolveShellNavRevealOrigin,
|
|
7
|
+
setShellNavRevealOriginVars,
|
|
8
|
+
} from '../../nav/shell-view-transition';
|
|
2
9
|
import {
|
|
3
10
|
deriveActiveIdFromUrl,
|
|
4
|
-
ensurePanelNavVtStyle,
|
|
5
11
|
parsePanelNavGroups,
|
|
6
12
|
resolvePanelNavDisableVt,
|
|
7
|
-
|
|
13
|
+
resolvePanelNavStyle,
|
|
8
14
|
shouldResyncPanelNavGroups,
|
|
9
|
-
|
|
15
|
+
shouldResyncPanelNavStyle,
|
|
10
16
|
} from './panel-nav-utils';
|
|
17
|
+
import type { PanelNavGroup, PanelNavItem, PanelNavRouterMode } from './panel-nav-types';
|
|
11
18
|
|
|
12
|
-
// Module-level WeakMaps
|
|
13
|
-
// class fields avoids Stencil generating getter-only property descriptors on
|
|
14
|
-
// the host element for private fields, which throws in strict environments.
|
|
15
|
-
// Minimal typings for the View Transitions API, which isn't in our TS lib target.
|
|
19
|
+
// Module-level WeakMaps
|
|
16
20
|
interface ViewTransition {
|
|
17
21
|
finished: Promise<void>;
|
|
18
22
|
skipTransition: () => void;
|
|
@@ -24,50 +28,17 @@ type DocumentWithViewTransition = Document & {
|
|
|
24
28
|
const vtTransitions = new WeakMap<object, ViewTransition>();
|
|
25
29
|
const vtResolvers = new WeakMap<object, () => void>();
|
|
26
30
|
|
|
27
|
-
/** Parse a CSS <time> value to milliseconds. Handles `s`, `ms`, and unitless
|
|
28
|
-
* (assumed ms). Critically: `parseFloat('.75s')` is 0.75 — WAAPI treats that as
|
|
29
|
-
* 0.75ms (instant/invisible), so the `s` unit must be scaled to ms. Design
|
|
30
|
-
* tokens (e.g. `--effect-animation-duration-long-1`) are authored in seconds. */
|
|
31
|
-
function parseCssTimeMs(value: string, fallback: number): number {
|
|
32
|
-
const v = value.trim();
|
|
33
|
-
const num = parseFloat(v);
|
|
34
|
-
if (Number.isNaN(num)) return fallback;
|
|
35
|
-
if (/ms\s*$/.test(v)) return num;
|
|
36
|
-
if (/s\s*$/.test(v)) return num * 1000;
|
|
37
|
-
return num;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type PanelNavVariant = 'dashboard' | 'settings';
|
|
41
|
-
export type PanelNavRouterMode = 'anchor' | 'event';
|
|
42
|
-
|
|
43
|
-
export interface PanelNavItem {
|
|
44
|
-
id: string;
|
|
45
|
-
icon: string;
|
|
46
|
-
label: string;
|
|
47
|
-
/** Show a notification dot badge on the item */
|
|
48
|
-
dot?: boolean;
|
|
49
|
-
flag?: boolean;
|
|
50
|
-
/** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.
|
|
51
|
-
* In `event` mode navigation is delegated to the host via `dsNavSelect`. */
|
|
52
|
-
href?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface PanelNavGroup {
|
|
56
|
-
id?: string;
|
|
57
|
-
label?: string;
|
|
58
|
-
items: PanelNavItem[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
31
|
@Component({
|
|
62
32
|
tag: 'ds-panel-nav',
|
|
63
33
|
styleUrl: 'PanelNav.css',
|
|
64
34
|
scoped: true,
|
|
65
35
|
})
|
|
66
36
|
export class PanelNav {
|
|
67
|
-
/**
|
|
68
|
-
|
|
37
|
+
/** Chrome style: `navigation` = navigation tokens, `default` = standard app tokens.
|
|
38
|
+
* Property: `navStyle`. HTML attribute: `nav-style`. */
|
|
39
|
+
@Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'navigation';
|
|
69
40
|
|
|
70
|
-
/** When `true`, the component does not run its own View Transition on
|
|
41
|
+
/** When `true`, the component does not run its own View Transition on style
|
|
71
42
|
* change — it just updates the rendered surface synchronously. Use this when
|
|
72
43
|
* the host app orchestrates the page transition itself (e.g. Angular Router's
|
|
73
44
|
* `withViewTransitions`), so the two don't fight or nest. */
|
|
@@ -122,9 +93,9 @@ export class PanelNav {
|
|
|
122
93
|
|
|
123
94
|
@Element() el!: HTMLElement;
|
|
124
95
|
|
|
125
|
-
/** Mirrors `
|
|
96
|
+
/** Mirrors `style` but is only updated inside the VT callback so the
|
|
126
97
|
* View Transition captures a clean before/after snapshot. */
|
|
127
|
-
@State() private
|
|
98
|
+
@State() private renderedStyle: NavChromeStyle = 'navigation';
|
|
128
99
|
|
|
129
100
|
@State() private parsedGroups: PanelNavGroup[] = [];
|
|
130
101
|
@State() private atBottom = false;
|
|
@@ -193,44 +164,35 @@ export class PanelNav {
|
|
|
193
164
|
this.syncActiveFromUrl();
|
|
194
165
|
}
|
|
195
166
|
|
|
196
|
-
@Watch('
|
|
197
|
-
async
|
|
167
|
+
@Watch('navStyle')
|
|
168
|
+
async onNavStyleChange(newVal: NavChromeStyle) {
|
|
198
169
|
// Host app drives the transition (e.g. Angular Router withViewTransitions):
|
|
199
170
|
// just reflect the new surface so the app's transition captures it.
|
|
200
171
|
if (this.effectiveDisableViewTransition) {
|
|
201
|
-
this.
|
|
172
|
+
this.renderedStyle = newVal;
|
|
202
173
|
return;
|
|
203
174
|
}
|
|
204
175
|
|
|
205
|
-
// Skip any in-progress transition immediately so rapid
|
|
206
|
-
// (e.g. quick dashboard → settings → dashboard) never leave the nav stuck.
|
|
176
|
+
// Skip any in-progress transition immediately so rapid style changes never leave the nav stuck.
|
|
207
177
|
vtTransitions.get(this)?.skipTransition();
|
|
208
178
|
vtTransitions.delete(this);
|
|
209
179
|
|
|
210
180
|
const doc = document as DocumentWithViewTransition;
|
|
211
181
|
if (typeof doc.startViewTransition !== 'function') {
|
|
212
|
-
this.
|
|
182
|
+
this.renderedStyle = newVal;
|
|
213
183
|
return;
|
|
214
184
|
}
|
|
215
185
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const y = rect ? Math.round(rect.top + rect.height / 2) : Math.round(window.innerHeight / 2);
|
|
222
|
-
const maxR = Math.ceil(Math.hypot(
|
|
223
|
-
Math.max(x, window.innerWidth - x),
|
|
224
|
-
Math.max(y, window.innerHeight - y),
|
|
225
|
-
));
|
|
226
|
-
|
|
227
|
-
document.documentElement.style.setProperty('--vt-x', `${x}px`);
|
|
228
|
-
document.documentElement.style.setProperty('--vt-y', `${y}px`);
|
|
186
|
+
ensureShellNavVtStyle();
|
|
187
|
+
const origin = resolveShellNavRevealOrigin(
|
|
188
|
+
this.el.querySelector('.panel-nav__footer-btn') as HTMLElement | null,
|
|
189
|
+
);
|
|
190
|
+
setShellNavRevealOriginVars(origin);
|
|
229
191
|
|
|
230
192
|
const transition = doc.startViewTransition(() =>
|
|
231
193
|
new Promise<void>(resolve => {
|
|
232
194
|
vtResolvers.set(this, resolve);
|
|
233
|
-
this.
|
|
195
|
+
this.renderedStyle = newVal;
|
|
234
196
|
})
|
|
235
197
|
);
|
|
236
198
|
|
|
@@ -240,19 +202,13 @@ export class PanelNav {
|
|
|
240
202
|
vtResolvers.delete(this);
|
|
241
203
|
});
|
|
242
204
|
|
|
243
|
-
transition.ready
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const duration = parseCssTimeMs(durToken, 750);
|
|
247
|
-
document.documentElement.animate(
|
|
248
|
-
{ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxR}px at ${x}px ${y}px)`] },
|
|
249
|
-
{ duration, easing: 'ease-in-out', fill: 'forwards', pseudoElement: '::view-transition-new(root)' },
|
|
250
|
-
);
|
|
251
|
-
}).catch(() => { /* transition was skipped or aborted */ });
|
|
205
|
+
transition.ready
|
|
206
|
+
.then(() => animateShellNavRadialReveal(origin))
|
|
207
|
+
.catch(() => { /* transition was skipped or aborted */ });
|
|
252
208
|
}
|
|
253
209
|
|
|
254
210
|
componentWillLoad() {
|
|
255
|
-
this.
|
|
211
|
+
this.renderedStyle = resolvePanelNavStyle(this.navStyle, this.el.getAttribute('nav-style'));
|
|
256
212
|
if (this.storageKey) {
|
|
257
213
|
try {
|
|
258
214
|
const stored = localStorage.getItem(this.storageKey);
|
|
@@ -331,7 +287,7 @@ export class PanelNav {
|
|
|
331
287
|
|
|
332
288
|
/** Re-parse props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */
|
|
333
289
|
private syncHostPropsIfNeeded() {
|
|
334
|
-
this.
|
|
290
|
+
this.syncRenderedStyleIfNeeded();
|
|
335
291
|
|
|
336
292
|
if (shouldResyncPanelNavGroups(this.parsedGroups, this.groups)) {
|
|
337
293
|
this.onGroupsChange(this.groups);
|
|
@@ -340,16 +296,16 @@ export class PanelNav {
|
|
|
340
296
|
}
|
|
341
297
|
}
|
|
342
298
|
|
|
343
|
-
/** Align `
|
|
344
|
-
private
|
|
345
|
-
if (!
|
|
299
|
+
/** Align `renderedStyle` with `style` when host bindings land after first paint. */
|
|
300
|
+
private syncRenderedStyleIfNeeded() {
|
|
301
|
+
if (!shouldResyncPanelNavStyle(this.renderedStyle, this.navStyle)) return;
|
|
346
302
|
|
|
347
303
|
if (this.effectiveDisableViewTransition) {
|
|
348
304
|
vtTransitions.get(this)?.skipTransition();
|
|
349
305
|
vtTransitions.delete(this);
|
|
350
|
-
this.
|
|
306
|
+
this.renderedStyle = this.navStyle;
|
|
351
307
|
} else {
|
|
352
|
-
void this.
|
|
308
|
+
void this.onNavStyleChange(this.navStyle);
|
|
353
309
|
}
|
|
354
310
|
}
|
|
355
311
|
|
|
@@ -517,7 +473,15 @@ export class PanelNav {
|
|
|
517
473
|
</ds-text>
|
|
518
474
|
</span>
|
|
519
475
|
</span>,
|
|
520
|
-
item.dot &&
|
|
476
|
+
item.dot && (
|
|
477
|
+
<ds-badge
|
|
478
|
+
class="panel-nav__item-dot"
|
|
479
|
+
variant="dot"
|
|
480
|
+
background="var(--_nav-bg)"
|
|
481
|
+
label=""
|
|
482
|
+
aria-hidden="true"
|
|
483
|
+
/>
|
|
484
|
+
),
|
|
521
485
|
];
|
|
522
486
|
|
|
523
487
|
const sharedProps = {
|
|
@@ -538,14 +502,14 @@ export class PanelNav {
|
|
|
538
502
|
}
|
|
539
503
|
|
|
540
504
|
render() {
|
|
541
|
-
const
|
|
505
|
+
const isNavigation = this.renderedStyle === 'navigation';
|
|
542
506
|
const collapsed = this.collapsed || this.viewportNarrow;
|
|
543
507
|
const { userName, userInitial } = this;
|
|
544
508
|
|
|
545
509
|
const navCls: Record<string, boolean> = {
|
|
546
510
|
'panel-nav': true,
|
|
547
|
-
'panel-nav--
|
|
548
|
-
'panel-nav--
|
|
511
|
+
'panel-nav--navigation': isNavigation,
|
|
512
|
+
'panel-nav--default': !isNavigation,
|
|
549
513
|
'panel-nav--collapsed': collapsed,
|
|
550
514
|
'panel-nav--animating': this.isAnimating,
|
|
551
515
|
'panel-nav--at-bottom': this.atBottom,
|
|
@@ -553,7 +517,7 @@ export class PanelNav {
|
|
|
553
517
|
|
|
554
518
|
return (
|
|
555
519
|
<Host style={{ display: 'block', position: 'relative' }}>
|
|
556
|
-
<nav class={navCls} aria-label={
|
|
520
|
+
<nav class={navCls} aria-label={isNavigation ? 'Main navigation' : 'Settings navigation'}>
|
|
557
521
|
|
|
558
522
|
{/* ── Header: Motive logo, reveals collapse toggle on hover ── */}
|
|
559
523
|
<div class="panel-nav__header">
|
|
@@ -604,15 +568,23 @@ export class PanelNav {
|
|
|
604
568
|
|
|
605
569
|
{/* ── Footer ── */}
|
|
606
570
|
<div class="panel-nav__footer">
|
|
607
|
-
|
|
571
|
+
<ds-fade
|
|
572
|
+
class="panel-nav__footer-fade"
|
|
573
|
+
side="bottom"
|
|
574
|
+
size="size-600"
|
|
575
|
+
background="var(--_nav-bg)"
|
|
576
|
+
visible={!this.atBottom}
|
|
577
|
+
/>
|
|
578
|
+
|
|
579
|
+
{/* Left icon button — Gear in navigation style, Dashboard in default style */}
|
|
608
580
|
<button
|
|
609
581
|
type="button"
|
|
610
582
|
class="panel-nav__footer-btn"
|
|
611
|
-
title={
|
|
612
|
-
aria-label={
|
|
583
|
+
title={isNavigation ? 'Settings' : 'Dashboard'}
|
|
584
|
+
aria-label={isNavigation ? 'Open settings' : 'Go to dashboard'}
|
|
613
585
|
onClick={() => this.handleFooterAction()}
|
|
614
586
|
>
|
|
615
|
-
<ds-icon name={
|
|
587
|
+
<ds-icon name={isNavigation ? 'Gear' : 'Dashboard'} size="md" color="inherit" />
|
|
616
588
|
</button>
|
|
617
589
|
|
|
618
590
|
{/* Right user — label fades like nav items; right icon cross-fades chevron ↔ circle+initial */}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { PanelNavRouterMode, PanelNavItem, PanelNavGroup } from './panel-nav-types';
|
|
2
|
+
export type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
2
3
|
export {
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
NAV_STYLE_HINT_ATTR,
|
|
5
|
+
setNavStyleHint,
|
|
6
|
+
clearNavStyleHint,
|
|
7
|
+
resolvePanelNavStyle,
|
|
8
|
+
shouldResyncPanelNavStyle,
|
|
5
9
|
ensurePanelNavVtStyle,
|
|
6
|
-
|
|
10
|
+
deriveActiveIdFromUrl,
|
|
11
|
+
parsePanelNavGroups,
|
|
7
12
|
} from './panel-nav-utils';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
2
|
+
|
|
3
|
+
export type { NavChromeStyle };
|
|
4
|
+
export type PanelNavRouterMode = 'anchor' | 'event';
|
|
5
|
+
|
|
6
|
+
export interface PanelNavItem {
|
|
7
|
+
id: string;
|
|
8
|
+
icon: string;
|
|
9
|
+
label: string;
|
|
10
|
+
/** Show a notification dot badge on the item */
|
|
11
|
+
dot?: boolean;
|
|
12
|
+
flag?: boolean;
|
|
13
|
+
/** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.
|
|
14
|
+
* In `event` mode navigation is delegated to the host via `dsNavSelect`. */
|
|
15
|
+
href?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PanelNavGroup {
|
|
19
|
+
id?: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
items: PanelNavItem[];
|
|
22
|
+
}
|