@nuvia-ui/components 4.0.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/package.json +27 -0
- package/src/ds-accordion/ds-accordion-item.js +288 -0
- package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
- package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
- package/src/ds-accordion/ds-accordion.js +68 -0
- package/src/ds-accordion/ds-accordion.stories.js +118 -0
- package/src/ds-accordion/ds-accordion.test.js +146 -0
- package/src/ds-accordion/index.js +2 -0
- package/src/ds-action-bar/ds-action-bar.js +116 -0
- package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
- package/src/ds-action-bar/ds-action-bar.test.js +64 -0
- package/src/ds-action-bar/index.js +1 -0
- package/src/ds-alert/ds-alert.a11y.test.js +151 -0
- package/src/ds-alert/ds-alert.js +223 -0
- package/src/ds-alert/ds-alert.mdx +142 -0
- package/src/ds-alert/ds-alert.stories.js +166 -0
- package/src/ds-alert/ds-alert.test.js +256 -0
- package/src/ds-alert/index.js +1 -0
- package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
- package/src/ds-avatar/ds-avatar.js +216 -0
- package/src/ds-avatar/ds-avatar.stories.js +120 -0
- package/src/ds-avatar/ds-avatar.test.js +83 -0
- package/src/ds-avatar/index.js +1 -0
- package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
- package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
- package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
- package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
- package/src/ds-avatar-extended/index.js +1 -0
- package/src/ds-banner/ds-banner.a11y.test.js +51 -0
- package/src/ds-banner/ds-banner.js +233 -0
- package/src/ds-banner/ds-banner.stories.js +185 -0
- package/src/ds-banner/ds-banner.test.js +116 -0
- package/src/ds-banner/index.js +1 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
- package/src/ds-button/ds-button.a11y.test.js +49 -0
- package/src/ds-button/ds-button.js +205 -0
- package/src/ds-button/ds-button.mdx +141 -0
- package/src/ds-button/ds-button.stories.js +152 -0
- package/src/ds-button/ds-button.test.js +62 -0
- package/src/ds-button/index.js +1 -0
- package/src/ds-button-group/ds-button-group.js +82 -0
- package/src/ds-button-group/ds-button-group.mdx +39 -0
- package/src/ds-button-group/ds-button-group.stories.js +47 -0
- package/src/ds-button-group/ds-button-group.test.js +47 -0
- package/src/ds-button-group/index.js +1 -0
- package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
- package/src/ds-checkbox/ds-checkbox.js +271 -0
- package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
- package/src/ds-checkbox/ds-checkbox.test.js +191 -0
- package/src/ds-checkbox/index.js +1 -0
- package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
- package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
- package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
- package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
- package/src/ds-checkbox-group/index.js +1 -0
- package/src/ds-dialog/ds-dialog.js +466 -0
- package/src/ds-dialog/ds-dialog.stories.js +274 -0
- package/src/ds-dialog/ds-dialog.test.js +441 -0
- package/src/ds-dialog/index.js +1 -0
- package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
- package/src/ds-dropdown/ds-dropdown.js +891 -0
- package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
- package/src/ds-dropdown/ds-dropdown.test.js +268 -0
- package/src/ds-dropdown/index.js +1 -0
- package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
- package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
- package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
- package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
- package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
- package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
- package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
- package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
- package/src/ds-file-uploader/ds-file-uploader.js +442 -0
- package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
- package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
- package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
- package/src/ds-header/ds-header.a11y.test.js +38 -0
- package/src/ds-header/ds-header.js +149 -0
- package/src/ds-header/ds-header.stories.js +63 -0
- package/src/ds-header/ds-header.test.js +52 -0
- package/src/ds-header/index.js +1 -0
- package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
- package/src/ds-header-nav/ds-header-nav.js +114 -0
- package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
- package/src/ds-header-nav/ds-header-nav.test.js +93 -0
- package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
- package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
- package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
- package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
- package/src/ds-icon/ds-icon.a11y.test.js +49 -0
- package/src/ds-icon/ds-icon.js +75 -0
- package/src/ds-icon/ds-icon.mdx +36 -0
- package/src/ds-icon/ds-icon.stories.js +88 -0
- package/src/ds-icon/ds-icon.test.js +97 -0
- package/src/ds-icon/index.js +1 -0
- package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
- package/src/ds-icon-button/ds-icon-button.js +224 -0
- package/src/ds-icon-button/ds-icon-button.mdx +131 -0
- package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
- package/src/ds-icon-button/ds-icon-button.test.js +90 -0
- package/src/ds-icon-button/index.js +1 -0
- package/src/ds-input/ds-input.a11y.test.js +145 -0
- package/src/ds-input/ds-input.js +645 -0
- package/src/ds-input/ds-input.mdx +251 -0
- package/src/ds-input/ds-input.stories.js +298 -0
- package/src/ds-input/ds-input.test.js +792 -0
- package/src/ds-input/index.js +1 -0
- package/src/ds-link/ds-link.js +111 -0
- package/src/ds-link/ds-link.stories.js +56 -0
- package/src/ds-link/ds-link.test.js +74 -0
- package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
- package/src/ds-list-item/ds-list-item.js +292 -0
- package/src/ds-list-item/ds-list-item.stories.js +101 -0
- package/src/ds-list-item/ds-list-item.test.js +63 -0
- package/src/ds-menu/ds-menu.js +30 -0
- package/src/ds-menu/ds-menu.stories.js +120 -0
- package/src/ds-menu/ds-menu.test.js +123 -0
- package/src/ds-menu-group/ds-menu-group.js +101 -0
- package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
- package/src/ds-nav-item/ds-nav-item.js +307 -0
- package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.test.js +169 -0
- package/src/ds-nav-item/index.js +1 -0
- package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
- package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
- package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
- package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
- package/src/ds-nav-vertical/index.js +1 -0
- package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
- package/src/ds-pagination/ds-pagination.js +232 -0
- package/src/ds-pagination/ds-pagination.stories.js +63 -0
- package/src/ds-pagination/ds-pagination.test.js +141 -0
- package/src/ds-pagination/index.js +1 -0
- package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
- package/src/ds-progress-bar/ds-progress-bar.js +81 -0
- package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
- package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
- package/src/ds-radio/ds-radio.a11y.test.js +69 -0
- package/src/ds-radio/ds-radio.js +240 -0
- package/src/ds-radio/ds-radio.stories.js +102 -0
- package/src/ds-radio/ds-radio.test.js +114 -0
- package/src/ds-radio/index.js +1 -0
- package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
- package/src/ds-radio-group/ds-radio-group.js +257 -0
- package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
- package/src/ds-radio-group/ds-radio-group.test.js +194 -0
- package/src/ds-radio-group/index.js +1 -0
- package/src/ds-rich-list/ds-rich-list.js +246 -0
- package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
- package/src/ds-rich-list/ds-rich-list.test.js +293 -0
- package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
- package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
- package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
- package/src/ds-slider/ds-slider.js +399 -0
- package/src/ds-slider/ds-slider.stories.js +107 -0
- package/src/ds-slider/ds-slider.test.js +308 -0
- package/src/ds-spinner/ds-spinner.js +173 -0
- package/src/ds-spinner/ds-spinner.stories.js +52 -0
- package/src/ds-spinner/ds-spinner.test.js +50 -0
- package/src/ds-status-border/ds-status-border.js +88 -0
- package/src/ds-status-border/ds-status-border.stories.js +242 -0
- package/src/ds-status-border/ds-status-border.test.js +168 -0
- package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
- package/src/ds-stepper/ds-stepper.js +207 -0
- package/src/ds-stepper/ds-stepper.stories.js +530 -0
- package/src/ds-stepper/ds-stepper.test.js +311 -0
- package/src/ds-stepper-item/ds-stepper-item.js +485 -0
- package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
- package/src/ds-switch/ds-switch.js +348 -0
- package/src/ds-switch/ds-switch.stories.js +145 -0
- package/src/ds-switch/ds-switch.test.js +226 -0
- package/src/ds-switch/index.js +1 -0
- package/src/ds-tab-item/ds-tab-item.js +341 -0
- package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
- package/src/ds-tabs/ds-tab-panel.js +48 -0
- package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
- package/src/ds-tabs/ds-tabs.js +180 -0
- package/src/ds-tabs/ds-tabs.stories.js +152 -0
- package/src/ds-tabs/ds-tabs.test.js +306 -0
- package/src/ds-tabs/index.js +3 -0
- package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
- package/src/ds-tag-action/ds-tag-action.js +185 -0
- package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
- package/src/ds-tag-action/ds-tag-action.test.js +44 -0
- package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
- package/src/ds-tag-removable/ds-tag-removable.js +146 -0
- package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
- package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
- package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
- package/src/ds-tag-status/ds-tag-status.js +164 -0
- package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
- package/src/ds-tag-status/ds-tag-status.test.js +140 -0
- package/src/ds-tag-status/index.js +1 -0
- package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
- package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
- package/src/ds-textarea/ds-textarea.js +505 -0
- package/src/ds-textarea/ds-textarea.stories.js +335 -0
- package/src/ds-textarea/ds-textarea.test.js +218 -0
- package/src/ds-textarea/index.js +1 -0
- package/src/ds-thumbnail/ds-thumbnail.js +207 -0
- package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
- package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
- package/src/ds-toast/ds-toast-provider.js +110 -0
- package/src/ds-toast/ds-toast.a11y.test.js +34 -0
- package/src/ds-toast/ds-toast.js +243 -0
- package/src/ds-toast/ds-toast.stories.js +143 -0
- package/src/ds-toast/ds-toast.test.js +93 -0
- package/src/ds-toast/index.js +2 -0
- package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
- package/src/ds-tooltip/ds-tooltip.js +217 -0
- package/src/ds-tooltip/ds-tooltip.mdx +75 -0
- package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
- package/src/ds-tooltip/ds-tooltip.test.js +191 -0
- package/src/ds-tooltip/index.js +1 -0
- package/src/ds-tooltip/positioner.js +117 -0
- package/src/index.js +50 -0
- package/src/mixins/field-label.mixin.js +113 -0
- package/src/mixins/field-message.mixin.js +66 -0
- package/src/token-provider/index.js +1 -0
- package/src/token-provider/token-provider.a11y.test.js +44 -0
- package/src/token-provider/token-provider.js +85 -0
- package/src/token-provider/token-provider.stories.js +105 -0
- package/src/token-provider/token-provider.test.js +134 -0
- package/src/utils/number-input.utils.js +42 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { computePosition, autoUpdate, flip, shift, offset, arrow, autoPlacement } from '@floating-ui/dom';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A Reactive Controller to handle floating-ui positioning
|
|
5
|
+
*/
|
|
6
|
+
export class PositionerController {
|
|
7
|
+
constructor(host) {
|
|
8
|
+
this.host = host;
|
|
9
|
+
this.host.addController(this);
|
|
10
|
+
this._cleanup = null;
|
|
11
|
+
this._reference = null;
|
|
12
|
+
this._floating = null;
|
|
13
|
+
this._placement = 'top';
|
|
14
|
+
this._strategy = 'absolute';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_update() {
|
|
18
|
+
if (!this.host.isConnected || !this._reference || !this._floating) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this._stop();
|
|
23
|
+
|
|
24
|
+
this._cleanup = autoUpdate(
|
|
25
|
+
this._reference,
|
|
26
|
+
this._floating,
|
|
27
|
+
() => {
|
|
28
|
+
// Calculate the offset dynamically from token
|
|
29
|
+
const offsetAmount = this._offsetValue;
|
|
30
|
+
|
|
31
|
+
// Handle 'auto' placement
|
|
32
|
+
const isAuto = this._placement.startsWith('auto');
|
|
33
|
+
|
|
34
|
+
// Use placement as-is for standard values
|
|
35
|
+
const targetPlacement = this._placement;
|
|
36
|
+
|
|
37
|
+
const middleware = [
|
|
38
|
+
offset(offsetAmount),
|
|
39
|
+
shift({ padding: 8 })
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
if (isAuto) {
|
|
43
|
+
middleware.push(autoPlacement());
|
|
44
|
+
} else {
|
|
45
|
+
middleware.push(flip());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
computePosition(this._reference, this._floating, {
|
|
49
|
+
strategy: this._strategy,
|
|
50
|
+
placement: isAuto ? undefined : targetPlacement,
|
|
51
|
+
middleware,
|
|
52
|
+
}).then(({ x, y }) => {
|
|
53
|
+
Object.assign(this._floating.style, {
|
|
54
|
+
left: `${x}px`,
|
|
55
|
+
top: `${y}px`,
|
|
56
|
+
position: this._strategy // Ensure CSS position matches strategy
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
// Critical options for dynamic repositioning
|
|
62
|
+
ancestorScroll: true, // Reposition when ancestor scrolls
|
|
63
|
+
ancestorResize: true, // Reposition when ancestor resizes
|
|
64
|
+
elementResize: true, // Reposition when elements resize
|
|
65
|
+
layoutShift: true // Reposition on layout shifts
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
set target(referenceElement) {
|
|
71
|
+
this._reference = referenceElement;
|
|
72
|
+
this._update();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
set floating(floatingElement) {
|
|
76
|
+
this._floating = floatingElement;
|
|
77
|
+
this._update();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
set placement(value) {
|
|
81
|
+
this._placement = value;
|
|
82
|
+
this._update();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
set strategy(value) {
|
|
86
|
+
this._strategy = value;
|
|
87
|
+
this._update();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
hostConnected() {
|
|
91
|
+
this._update();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
hostDisconnected() {
|
|
95
|
+
this._stop();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
_stop() {
|
|
99
|
+
if (this._cleanup) {
|
|
100
|
+
this._cleanup();
|
|
101
|
+
this._cleanup = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Reads the --ds-space-sm token from the computed styles of the host
|
|
107
|
+
* to determine the offset value in pixels.
|
|
108
|
+
*/
|
|
109
|
+
get _offsetValue() {
|
|
110
|
+
if (!this.host) return 8; // Fallback
|
|
111
|
+
const styles = getComputedStyle(this.host);
|
|
112
|
+
const tokenValue = styles.getPropertyValue('--ds-space-sm').trim();
|
|
113
|
+
// Parse '8px' -> 8
|
|
114
|
+
const pixels = parseFloat(tokenValue);
|
|
115
|
+
return isNaN(pixels) ? 8 : pixels;
|
|
116
|
+
}
|
|
117
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Export all components
|
|
2
|
+
export * from './ds-accordion/index.js';
|
|
3
|
+
export { DsTagAction } from './ds-tag-action/ds-tag-action.js';
|
|
4
|
+
export { DsTagRemovable } from './ds-tag-removable/ds-tag-removable.js';
|
|
5
|
+
export { DsTabItem } from './ds-tab-item/ds-tab-item.js';
|
|
6
|
+
export { DsTabs } from './ds-tabs/ds-tabs.js';
|
|
7
|
+
export { DsTabPanel } from './ds-tabs/ds-tab-panel.js';
|
|
8
|
+
export { DsIcon } from './ds-icon/index.js';
|
|
9
|
+
export { TokenProvider } from './token-provider/index.js';
|
|
10
|
+
export { DsButton } from './ds-button/index.js';
|
|
11
|
+
export { DsButtonGroup } from './ds-button-group/index.js';
|
|
12
|
+
export { DsActionBar } from './ds-action-bar/index.js';
|
|
13
|
+
export { DsIconButton } from './ds-icon-button/index.js';
|
|
14
|
+
export { DsInput } from './ds-input/index.js';
|
|
15
|
+
export { DsTextarea } from './ds-textarea/index.js';
|
|
16
|
+
export { DsDropdown } from './ds-dropdown/index.js';
|
|
17
|
+
export { DsCheckbox } from './ds-checkbox/index.js';
|
|
18
|
+
export { DsCheckboxGroup } from './ds-checkbox-group/index.js';
|
|
19
|
+
export { DsRadio } from './ds-radio/index.js';
|
|
20
|
+
export { DsRadioGroup } from './ds-radio-group/index.js';
|
|
21
|
+
export { DsTooltip } from './ds-tooltip/index.js';
|
|
22
|
+
export { DsTagStatus } from './ds-tag-status/index.js';
|
|
23
|
+
export { DsProgressBar } from './ds-progress-bar/ds-progress-bar.js';
|
|
24
|
+
export { DsSpinner } from './ds-spinner/ds-spinner.js';
|
|
25
|
+
export { DsLink } from './ds-link/ds-link.js';
|
|
26
|
+
export { DsNavItem } from './ds-nav-item/index.js';
|
|
27
|
+
export { DsNavVertical } from './ds-nav-vertical/index.js';
|
|
28
|
+
export { DsHeader } from './ds-header/index.js';
|
|
29
|
+
export { DsHeaderNav } from './ds-header-nav/ds-header-nav.js';
|
|
30
|
+
export { DsHeaderNavItem } from './ds-header-nav-item/ds-header-nav-item.js';
|
|
31
|
+
export { DsAvatar } from './ds-avatar/index.js';
|
|
32
|
+
export { DsAvatarExtended } from './ds-avatar-extended/index.js';
|
|
33
|
+
export { DsListItem } from './ds-list-item/ds-list-item.js';
|
|
34
|
+
export { DsMenu } from './ds-menu/ds-menu.js';
|
|
35
|
+
export { DsMenuGroup } from './ds-menu-group/ds-menu-group.js';
|
|
36
|
+
export { DsAlert } from './ds-alert/index.js';
|
|
37
|
+
export { DsBanner } from './ds-banner/index.js';
|
|
38
|
+
export { DsToast, DsToastProvider } from './ds-toast/index.js';
|
|
39
|
+
export { DsDialog } from './ds-dialog/index.js';
|
|
40
|
+
export { DsPagination } from './ds-pagination/index.js';
|
|
41
|
+
export { DsSwitch } from './ds-switch/index.js';
|
|
42
|
+
export { DsSlider } from './ds-slider/ds-slider.js';
|
|
43
|
+
export { DsStepper } from './ds-stepper/ds-stepper.js';
|
|
44
|
+
export { DsStepperItem } from './ds-stepper-item/ds-stepper-item.js';
|
|
45
|
+
export { DsRichList } from './ds-rich-list/ds-rich-list.js';
|
|
46
|
+
export { DsRichListItem } from './ds-rich-list-item/ds-rich-list-item.js';
|
|
47
|
+
export { DsThumbnail } from './ds-thumbnail/ds-thumbnail.js';
|
|
48
|
+
export { DsStatusBorder } from './ds-status-border/ds-status-border.js';
|
|
49
|
+
export { DsFileUploaded } from './ds-file-uploaded/ds-file-uploaded.js';
|
|
50
|
+
export { DsFileUploader } from './ds-file-uploader/ds-file-uploader.js';
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { html, css } from 'lit';
|
|
2
|
+
import '../ds-tooltip/ds-tooltip.js';
|
|
3
|
+
import '../ds-icon-button/ds-icon-button.js';
|
|
4
|
+
|
|
5
|
+
export const fieldLabelStyles = css`
|
|
6
|
+
.label-row {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: flex-start; /* Align to top for text wrap edge case */
|
|
9
|
+
gap: var(--ds-space-xs);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.label-row label {
|
|
13
|
+
font: var(--ds-typo-content-body-bold);
|
|
14
|
+
color: var(--ds-color-text-default);
|
|
15
|
+
line-height: 24px; /* Match info button height for alignment */
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.label-row ds-icon-button {
|
|
20
|
+
flex-shrink: 0; /* Prevent button from shrinking */
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Inline layout styles (used when labelPosition="inline-start") */
|
|
24
|
+
.field-wrapper.inline-label {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: row;
|
|
27
|
+
align-items: flex-start;
|
|
28
|
+
gap: var(--ds-space-sm, 8px);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.field-wrapper.inline-label .label-row {
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
padding-top: 4px; /* Align label text (24px) with 32px input: (32 - 24) / 2 = 4px */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.field-wrapper.inline-label .field-content {
|
|
37
|
+
flex: 1;
|
|
38
|
+
min-width: 0; /* Allow shrinking */
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
gap: var(--ds-space-xs);
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Mixin for rendering field labels with optional info button
|
|
48
|
+
*
|
|
49
|
+
* @param {LitElement} superClass - The class to extend
|
|
50
|
+
* @returns {LitElement} Extended class with label rendering capabilities
|
|
51
|
+
*/
|
|
52
|
+
export const FieldLabelMixin = (superClass) => class extends superClass {
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Renders a field label with optional info button
|
|
56
|
+
* Info button is now OUTSIDE the label element for accessibility
|
|
57
|
+
*
|
|
58
|
+
* @param {string} label - Label text
|
|
59
|
+
* @param {string} info - Info tooltip text (shows info button if provided)
|
|
60
|
+
* @param {string} inputId - ID of the input to associate with label
|
|
61
|
+
* @returns {TemplateResult|string} Lit template or empty string
|
|
62
|
+
*/
|
|
63
|
+
renderFieldLabel(label, info = '', inputId = '', labelId = '') {
|
|
64
|
+
if (!label) return '';
|
|
65
|
+
|
|
66
|
+
return html`
|
|
67
|
+
<div class="label-row" part="label-row">
|
|
68
|
+
<label id="${labelId}" for="${inputId}">${label}</label>
|
|
69
|
+
${info ? html`
|
|
70
|
+
<ds-tooltip content="${info}" placement="top">
|
|
71
|
+
<ds-icon-button
|
|
72
|
+
icon="info"
|
|
73
|
+
variant="action"
|
|
74
|
+
size="s"
|
|
75
|
+
aria-label="${info}"
|
|
76
|
+
@click=${this._handleInfoClick}
|
|
77
|
+
></ds-icon-button>
|
|
78
|
+
</ds-tooltip>
|
|
79
|
+
` : ''}
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handles info button click
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
_handleInfoClick(e) {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
e.stopImmediatePropagation(); // Prevent any other handlers
|
|
92
|
+
|
|
93
|
+
// Prevent focus from moving to the field
|
|
94
|
+
const activeElement = document.activeElement;
|
|
95
|
+
if (activeElement && activeElement !== e.target) {
|
|
96
|
+
e.target.blur();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.dispatchEvent(new CustomEvent('info-click', {
|
|
100
|
+
bubbles: true,
|
|
101
|
+
composed: true,
|
|
102
|
+
detail: { info: this.info }
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns CSS for field label styling
|
|
108
|
+
* @returns {CSSResult} CSS result
|
|
109
|
+
*/
|
|
110
|
+
static get fieldLabelStyles() {
|
|
111
|
+
return fieldLabelStyles;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { html, css } from 'lit';
|
|
2
|
+
import '../ds-icon/ds-icon.js';
|
|
3
|
+
|
|
4
|
+
export const fieldMessageStyles = css`
|
|
5
|
+
.field-message {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: var(--ds-space-xs);
|
|
9
|
+
font: var(--ds-typo-content-body-regular);
|
|
10
|
+
color: var(--ds-color-text-secondary);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.field-message--error {
|
|
14
|
+
color: var(--ds-color-text-error);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.field-message--error .field-message__icon {
|
|
18
|
+
color: var(--ds-color-icon-error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.field-message__icon {
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.field-message__text {
|
|
26
|
+
color: inherit;
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mixin for rendering field messages (help text or error text)
|
|
32
|
+
*
|
|
33
|
+
* @param {LitElement} superClass - The class to extend
|
|
34
|
+
* @returns {LitElement} Extended class with message rendering capabilities
|
|
35
|
+
*/
|
|
36
|
+
export const FieldMessageMixin = (superClass) => class extends superClass {
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Renders a field message (help text or error text with icon)
|
|
40
|
+
*
|
|
41
|
+
* @param {string} helpText - Help text to display
|
|
42
|
+
* @param {string} errorText - Error text to display (takes precedence over helpText)
|
|
43
|
+
* @returns {TemplateResult|string} Lit template or empty string
|
|
44
|
+
*/
|
|
45
|
+
renderFieldMessage(helpText = '', errorText = '') {
|
|
46
|
+
const hasError = !!errorText;
|
|
47
|
+
const message = hasError ? errorText : helpText;
|
|
48
|
+
|
|
49
|
+
if (!message) return '';
|
|
50
|
+
|
|
51
|
+
return html`
|
|
52
|
+
<div class="field-message ${hasError ? 'field-message--error' : ''}" part="message">
|
|
53
|
+
${hasError ? html`<ds-icon name="warning" size="sm" class="field-message__icon"></ds-icon>` : ''}
|
|
54
|
+
<span class="field-message__text">${message}</span>
|
|
55
|
+
</div>
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns CSS for field message styling
|
|
61
|
+
* @returns {CSSResult} CSS result
|
|
62
|
+
*/
|
|
63
|
+
static get fieldMessageStyles() {
|
|
64
|
+
return fieldMessageStyles;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TokenProvider } from './token-provider.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import axe from 'axe-core';
|
|
3
|
+
import './token-provider.js';
|
|
4
|
+
|
|
5
|
+
describe('token-provider accessibility', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
container = document.createElement('div');
|
|
10
|
+
document.body.appendChild(container);
|
|
11
|
+
document.documentElement.removeAttribute('data-theme');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
container.remove();
|
|
16
|
+
document.documentElement.removeAttribute('data-theme');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should pass axe accessibility checks', async () => {
|
|
20
|
+
container.innerHTML = `
|
|
21
|
+
<token-provider theme="light">
|
|
22
|
+
<div>Content inside provider</div>
|
|
23
|
+
</token-provider>
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
27
|
+
|
|
28
|
+
const results = await axe.run(container);
|
|
29
|
+
expect(results.violations).toHaveLength(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should not interfere with document accessibility', async () => {
|
|
33
|
+
container.innerHTML = '<token-provider theme="light"></token-provider>';
|
|
34
|
+
|
|
35
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
36
|
+
|
|
37
|
+
// token-provider should be invisible to screen readers
|
|
38
|
+
const provider = container.querySelector('token-provider');
|
|
39
|
+
expect(provider).toBeTruthy();
|
|
40
|
+
|
|
41
|
+
// Verify it doesn't add any problematic ARIA attributes
|
|
42
|
+
expect(provider.getAttribute('role')).toBeFalsy();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Provider Web Component
|
|
3
|
+
* Manages theme context and injects design tokens
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* <token-provider theme="light">
|
|
7
|
+
* <your-app></your-app>
|
|
8
|
+
* </token-provider>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export class TokenProvider extends HTMLElement {
|
|
12
|
+
static get observedAttributes() {
|
|
13
|
+
return ['theme'];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
this._theme = 'light';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
// Apply theme to document root for global token access
|
|
23
|
+
this._applyTheme();
|
|
24
|
+
|
|
25
|
+
// Dispatch ready event
|
|
26
|
+
this.dispatchEvent(new CustomEvent('tokens-ready', {
|
|
27
|
+
bubbles: true,
|
|
28
|
+
composed: true,
|
|
29
|
+
detail: { theme: this._theme }
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
34
|
+
if (name === 'theme' && oldValue !== newValue) {
|
|
35
|
+
this._theme = newValue || 'light';
|
|
36
|
+
this._applyTheme();
|
|
37
|
+
|
|
38
|
+
this.dispatchEvent(new CustomEvent('theme-change', {
|
|
39
|
+
bubbles: true,
|
|
40
|
+
composed: true,
|
|
41
|
+
detail: { theme: this._theme, previousTheme: oldValue }
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get theme() {
|
|
47
|
+
return this._theme;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
set theme(value) {
|
|
51
|
+
this.setAttribute('theme', value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_applyTheme() {
|
|
55
|
+
document.documentElement.setAttribute('data-theme', this._theme);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Toggle between light and dark themes
|
|
60
|
+
* @returns {string} The new theme
|
|
61
|
+
*/
|
|
62
|
+
toggleTheme() {
|
|
63
|
+
const newTheme = this._theme === 'light' ? 'dark' : 'light';
|
|
64
|
+
this.theme = newTheme;
|
|
65
|
+
return newTheme;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get a token value from CSS custom properties
|
|
70
|
+
* @param {string} tokenName - Token name without -- prefix
|
|
71
|
+
* @returns {string} The computed token value
|
|
72
|
+
*/
|
|
73
|
+
getToken(tokenName) {
|
|
74
|
+
return getComputedStyle(document.documentElement)
|
|
75
|
+
.getPropertyValue(`--${tokenName}`)
|
|
76
|
+
.trim();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Register the custom element
|
|
81
|
+
if (!customElements.get('token-provider')) {
|
|
82
|
+
customElements.define('token-provider', TokenProvider);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default TokenProvider;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import './token-provider.js';
|
|
2
|
+
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Token Provider',
|
|
7
|
+
component: 'token-provider',
|
|
8
|
+
argTypes: {
|
|
9
|
+
theme: {
|
|
10
|
+
control: 'radio',
|
|
11
|
+
options: ['light'],
|
|
12
|
+
description: 'Theme mode (dark coming soon)',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Default = {
|
|
18
|
+
args: {
|
|
19
|
+
theme: 'light',
|
|
20
|
+
},
|
|
21
|
+
render: ({ theme }) => {
|
|
22
|
+
const provider = document.createElement('token-provider');
|
|
23
|
+
provider.setAttribute('theme', theme);
|
|
24
|
+
|
|
25
|
+
const content = document.createElement('div');
|
|
26
|
+
content.style.cssText = `
|
|
27
|
+
padding: 2rem;
|
|
28
|
+
background: var(--ds-color-bg-secondary);
|
|
29
|
+
border: 1px solid var(--ds-color-border-default);
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
`;
|
|
32
|
+
content.innerHTML = `
|
|
33
|
+
<h2 style="font: var(--ds-typo-heading-lg); color: var(--ds-color-text-default); margin-bottom: 1rem;">
|
|
34
|
+
Sample Content
|
|
35
|
+
</h2>
|
|
36
|
+
<p style="font: var(--ds-typo-content-body-regular); color: var(--ds-color-text-default); margin-bottom: 1rem;">
|
|
37
|
+
This content is wrapped in a token-provider which manages theme context and injects design tokens.
|
|
38
|
+
</p>
|
|
39
|
+
<button style="
|
|
40
|
+
background: var(--ds-color-bg-brand);
|
|
41
|
+
color: var(--ds-color-text-inverse);
|
|
42
|
+
padding: var(--ds-space-sm) var(--ds-space-md);
|
|
43
|
+
border: none;
|
|
44
|
+
border-radius: var(--ds-radius-action);
|
|
45
|
+
font: var(--ds-typo-content-body-bold);
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
">
|
|
48
|
+
Brand Button
|
|
49
|
+
</button>
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
provider.appendChild(content);
|
|
53
|
+
return provider;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const Usage = {
|
|
58
|
+
render: () => {
|
|
59
|
+
const container = document.createElement('div');
|
|
60
|
+
container.innerHTML = `
|
|
61
|
+
<div style="font: var(--ds-typo-content-body-regular); color: var(--ds-color-text-default); max-width: 600px;">
|
|
62
|
+
<h2 style="font: var(--ds-typo-heading-lg); margin-bottom: 1rem;">
|
|
63
|
+
How to use Token Provider
|
|
64
|
+
</h2>
|
|
65
|
+
|
|
66
|
+
<h3 style="font: var(--ds-typo-heading-sm); margin-top: 2rem; margin-bottom: 0.5rem;">
|
|
67
|
+
HTML
|
|
68
|
+
</h3>
|
|
69
|
+
<pre style="
|
|
70
|
+
background: var(--ds-color-bg-secondary);
|
|
71
|
+
padding: var(--ds-space-md);
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
overflow-x: auto;
|
|
74
|
+
font-family: monospace;
|
|
75
|
+
font-size: 12px;
|
|
76
|
+
"><code><token-provider theme="light">
|
|
77
|
+
<your-app></your-app>
|
|
78
|
+
</token-provider></code></pre>
|
|
79
|
+
|
|
80
|
+
<h3 style="font: var(--ds-typo-heading-sm); margin-top: 2rem; margin-bottom: 0.5rem;">
|
|
81
|
+
JavaScript
|
|
82
|
+
</h3>
|
|
83
|
+
<pre style="
|
|
84
|
+
background: var(--ds-color-bg-secondary);
|
|
85
|
+
padding: var(--ds-space-md);
|
|
86
|
+
border-radius: 4px;
|
|
87
|
+
overflow-x: auto;
|
|
88
|
+
font-family: monospace;
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
"><code>// Get token value
|
|
91
|
+
const provider = document.querySelector('token-provider');
|
|
92
|
+
const tokenValue = provider.getToken('ds-space-md');
|
|
93
|
+
|
|
94
|
+
// Toggle theme
|
|
95
|
+
provider.toggleTheme();
|
|
96
|
+
|
|
97
|
+
// Listen to theme changes
|
|
98
|
+
provider.addEventListener('theme-change', (e) => {
|
|
99
|
+
console.log('New theme:', e.detail.theme);
|
|
100
|
+
});</code></pre>
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
return container;
|
|
104
|
+
},
|
|
105
|
+
};
|