@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.
Files changed (230) hide show
  1. package/package.json +27 -0
  2. package/src/ds-accordion/ds-accordion-item.js +288 -0
  3. package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
  4. package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
  5. package/src/ds-accordion/ds-accordion.js +68 -0
  6. package/src/ds-accordion/ds-accordion.stories.js +118 -0
  7. package/src/ds-accordion/ds-accordion.test.js +146 -0
  8. package/src/ds-accordion/index.js +2 -0
  9. package/src/ds-action-bar/ds-action-bar.js +116 -0
  10. package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
  11. package/src/ds-action-bar/ds-action-bar.test.js +64 -0
  12. package/src/ds-action-bar/index.js +1 -0
  13. package/src/ds-alert/ds-alert.a11y.test.js +151 -0
  14. package/src/ds-alert/ds-alert.js +223 -0
  15. package/src/ds-alert/ds-alert.mdx +142 -0
  16. package/src/ds-alert/ds-alert.stories.js +166 -0
  17. package/src/ds-alert/ds-alert.test.js +256 -0
  18. package/src/ds-alert/index.js +1 -0
  19. package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
  20. package/src/ds-avatar/ds-avatar.js +216 -0
  21. package/src/ds-avatar/ds-avatar.stories.js +120 -0
  22. package/src/ds-avatar/ds-avatar.test.js +83 -0
  23. package/src/ds-avatar/index.js +1 -0
  24. package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
  25. package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
  26. package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
  27. package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
  28. package/src/ds-avatar-extended/index.js +1 -0
  29. package/src/ds-banner/ds-banner.a11y.test.js +51 -0
  30. package/src/ds-banner/ds-banner.js +233 -0
  31. package/src/ds-banner/ds-banner.stories.js +185 -0
  32. package/src/ds-banner/ds-banner.test.js +116 -0
  33. package/src/ds-banner/index.js +1 -0
  34. package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
  35. package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
  36. package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
  37. package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
  38. package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
  39. package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
  40. package/src/ds-button/ds-button.a11y.test.js +49 -0
  41. package/src/ds-button/ds-button.js +205 -0
  42. package/src/ds-button/ds-button.mdx +141 -0
  43. package/src/ds-button/ds-button.stories.js +152 -0
  44. package/src/ds-button/ds-button.test.js +62 -0
  45. package/src/ds-button/index.js +1 -0
  46. package/src/ds-button-group/ds-button-group.js +82 -0
  47. package/src/ds-button-group/ds-button-group.mdx +39 -0
  48. package/src/ds-button-group/ds-button-group.stories.js +47 -0
  49. package/src/ds-button-group/ds-button-group.test.js +47 -0
  50. package/src/ds-button-group/index.js +1 -0
  51. package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
  52. package/src/ds-checkbox/ds-checkbox.js +271 -0
  53. package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
  54. package/src/ds-checkbox/ds-checkbox.test.js +191 -0
  55. package/src/ds-checkbox/index.js +1 -0
  56. package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
  57. package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
  58. package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
  59. package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
  60. package/src/ds-checkbox-group/index.js +1 -0
  61. package/src/ds-dialog/ds-dialog.js +466 -0
  62. package/src/ds-dialog/ds-dialog.stories.js +274 -0
  63. package/src/ds-dialog/ds-dialog.test.js +441 -0
  64. package/src/ds-dialog/index.js +1 -0
  65. package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
  66. package/src/ds-dropdown/ds-dropdown.js +891 -0
  67. package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
  68. package/src/ds-dropdown/ds-dropdown.test.js +268 -0
  69. package/src/ds-dropdown/index.js +1 -0
  70. package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
  71. package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
  72. package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
  73. package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
  74. package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
  75. package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
  76. package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
  77. package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
  78. package/src/ds-file-uploader/ds-file-uploader.js +442 -0
  79. package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
  80. package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
  81. package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
  82. package/src/ds-header/ds-header.a11y.test.js +38 -0
  83. package/src/ds-header/ds-header.js +149 -0
  84. package/src/ds-header/ds-header.stories.js +63 -0
  85. package/src/ds-header/ds-header.test.js +52 -0
  86. package/src/ds-header/index.js +1 -0
  87. package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
  88. package/src/ds-header-nav/ds-header-nav.js +114 -0
  89. package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
  90. package/src/ds-header-nav/ds-header-nav.test.js +93 -0
  91. package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
  92. package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
  93. package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
  94. package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
  95. package/src/ds-icon/ds-icon.a11y.test.js +49 -0
  96. package/src/ds-icon/ds-icon.js +75 -0
  97. package/src/ds-icon/ds-icon.mdx +36 -0
  98. package/src/ds-icon/ds-icon.stories.js +88 -0
  99. package/src/ds-icon/ds-icon.test.js +97 -0
  100. package/src/ds-icon/index.js +1 -0
  101. package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
  102. package/src/ds-icon-button/ds-icon-button.js +224 -0
  103. package/src/ds-icon-button/ds-icon-button.mdx +131 -0
  104. package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
  105. package/src/ds-icon-button/ds-icon-button.test.js +90 -0
  106. package/src/ds-icon-button/index.js +1 -0
  107. package/src/ds-input/ds-input.a11y.test.js +145 -0
  108. package/src/ds-input/ds-input.js +645 -0
  109. package/src/ds-input/ds-input.mdx +251 -0
  110. package/src/ds-input/ds-input.stories.js +298 -0
  111. package/src/ds-input/ds-input.test.js +792 -0
  112. package/src/ds-input/index.js +1 -0
  113. package/src/ds-link/ds-link.js +111 -0
  114. package/src/ds-link/ds-link.stories.js +56 -0
  115. package/src/ds-link/ds-link.test.js +74 -0
  116. package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
  117. package/src/ds-list-item/ds-list-item.js +292 -0
  118. package/src/ds-list-item/ds-list-item.stories.js +101 -0
  119. package/src/ds-list-item/ds-list-item.test.js +63 -0
  120. package/src/ds-menu/ds-menu.js +30 -0
  121. package/src/ds-menu/ds-menu.stories.js +120 -0
  122. package/src/ds-menu/ds-menu.test.js +123 -0
  123. package/src/ds-menu-group/ds-menu-group.js +101 -0
  124. package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
  125. package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
  126. package/src/ds-nav-item/ds-nav-item.js +307 -0
  127. package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
  128. package/src/ds-nav-item/ds-nav-item.test.js +169 -0
  129. package/src/ds-nav-item/index.js +1 -0
  130. package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
  131. package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
  132. package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
  133. package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
  134. package/src/ds-nav-vertical/index.js +1 -0
  135. package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
  136. package/src/ds-pagination/ds-pagination.js +232 -0
  137. package/src/ds-pagination/ds-pagination.stories.js +63 -0
  138. package/src/ds-pagination/ds-pagination.test.js +141 -0
  139. package/src/ds-pagination/index.js +1 -0
  140. package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
  141. package/src/ds-progress-bar/ds-progress-bar.js +81 -0
  142. package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
  143. package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
  144. package/src/ds-radio/ds-radio.a11y.test.js +69 -0
  145. package/src/ds-radio/ds-radio.js +240 -0
  146. package/src/ds-radio/ds-radio.stories.js +102 -0
  147. package/src/ds-radio/ds-radio.test.js +114 -0
  148. package/src/ds-radio/index.js +1 -0
  149. package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
  150. package/src/ds-radio-group/ds-radio-group.js +257 -0
  151. package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
  152. package/src/ds-radio-group/ds-radio-group.test.js +194 -0
  153. package/src/ds-radio-group/index.js +1 -0
  154. package/src/ds-rich-list/ds-rich-list.js +246 -0
  155. package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
  156. package/src/ds-rich-list/ds-rich-list.test.js +293 -0
  157. package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
  158. package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
  159. package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
  160. package/src/ds-slider/ds-slider.js +399 -0
  161. package/src/ds-slider/ds-slider.stories.js +107 -0
  162. package/src/ds-slider/ds-slider.test.js +308 -0
  163. package/src/ds-spinner/ds-spinner.js +173 -0
  164. package/src/ds-spinner/ds-spinner.stories.js +52 -0
  165. package/src/ds-spinner/ds-spinner.test.js +50 -0
  166. package/src/ds-status-border/ds-status-border.js +88 -0
  167. package/src/ds-status-border/ds-status-border.stories.js +242 -0
  168. package/src/ds-status-border/ds-status-border.test.js +168 -0
  169. package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
  170. package/src/ds-stepper/ds-stepper.js +207 -0
  171. package/src/ds-stepper/ds-stepper.stories.js +530 -0
  172. package/src/ds-stepper/ds-stepper.test.js +311 -0
  173. package/src/ds-stepper-item/ds-stepper-item.js +485 -0
  174. package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
  175. package/src/ds-switch/ds-switch.js +348 -0
  176. package/src/ds-switch/ds-switch.stories.js +145 -0
  177. package/src/ds-switch/ds-switch.test.js +226 -0
  178. package/src/ds-switch/index.js +1 -0
  179. package/src/ds-tab-item/ds-tab-item.js +341 -0
  180. package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
  181. package/src/ds-tabs/ds-tab-panel.js +48 -0
  182. package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
  183. package/src/ds-tabs/ds-tabs.js +180 -0
  184. package/src/ds-tabs/ds-tabs.stories.js +152 -0
  185. package/src/ds-tabs/ds-tabs.test.js +306 -0
  186. package/src/ds-tabs/index.js +3 -0
  187. package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
  188. package/src/ds-tag-action/ds-tag-action.js +185 -0
  189. package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
  190. package/src/ds-tag-action/ds-tag-action.test.js +44 -0
  191. package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
  192. package/src/ds-tag-removable/ds-tag-removable.js +146 -0
  193. package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
  194. package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
  195. package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
  196. package/src/ds-tag-status/ds-tag-status.js +164 -0
  197. package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
  198. package/src/ds-tag-status/ds-tag-status.test.js +140 -0
  199. package/src/ds-tag-status/index.js +1 -0
  200. package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
  201. package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
  202. package/src/ds-textarea/ds-textarea.js +505 -0
  203. package/src/ds-textarea/ds-textarea.stories.js +335 -0
  204. package/src/ds-textarea/ds-textarea.test.js +218 -0
  205. package/src/ds-textarea/index.js +1 -0
  206. package/src/ds-thumbnail/ds-thumbnail.js +207 -0
  207. package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
  208. package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
  209. package/src/ds-toast/ds-toast-provider.js +110 -0
  210. package/src/ds-toast/ds-toast.a11y.test.js +34 -0
  211. package/src/ds-toast/ds-toast.js +243 -0
  212. package/src/ds-toast/ds-toast.stories.js +143 -0
  213. package/src/ds-toast/ds-toast.test.js +93 -0
  214. package/src/ds-toast/index.js +2 -0
  215. package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
  216. package/src/ds-tooltip/ds-tooltip.js +217 -0
  217. package/src/ds-tooltip/ds-tooltip.mdx +75 -0
  218. package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
  219. package/src/ds-tooltip/ds-tooltip.test.js +191 -0
  220. package/src/ds-tooltip/index.js +1 -0
  221. package/src/ds-tooltip/positioner.js +117 -0
  222. package/src/index.js +50 -0
  223. package/src/mixins/field-label.mixin.js +113 -0
  224. package/src/mixins/field-message.mixin.js +66 -0
  225. package/src/token-provider/index.js +1 -0
  226. package/src/token-provider/token-provider.a11y.test.js +44 -0
  227. package/src/token-provider/token-provider.js +85 -0
  228. package/src/token-provider/token-provider.stories.js +105 -0
  229. package/src/token-provider/token-provider.test.js +134 -0
  230. package/src/utils/number-input.utils.js +42 -0
@@ -0,0 +1,485 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import '../ds-icon/ds-icon.js';
3
+
4
+ /**
5
+ * Stepper item component representing a single step in a stepper.
6
+ *
7
+ * @element ds-stepper-item
8
+ *
9
+ * @prop {boolean} completed - Whether this step is completed
10
+ * @prop {boolean} current - Whether this is the current active step
11
+ * @prop {boolean} disabled - Whether this step is disabled
12
+ * @prop {boolean} error - Whether this step has an error state
13
+ *
14
+ * @slot - Default slot for step label content
15
+ * @slot content - Slot for expandable content (vertical wizard mode)
16
+ *
17
+ * @fires ds-step-click - Fired when a step is clicked
18
+ *
19
+ * @csspart indicator - The step indicator button
20
+ * @csspart connector-before - The connector line before the indicator
21
+ * @csspart connector-after - The connector line after the indicator
22
+ * @csspart label - The step label container
23
+ */
24
+ export class DsStepperItem extends LitElement {
25
+ static properties = {
26
+ completed: { type: Boolean, reflect: true },
27
+ current: { type: Boolean, reflect: true },
28
+ disabled: { type: Boolean, reflect: true },
29
+ error: { type: Boolean, reflect: true },
30
+ // Internal properties set by parent stepper
31
+ _direction: { type: String, state: true },
32
+ _position: { type: String, state: true }, // 'first' | 'middle' | 'last'
33
+ _prevCompleted: { type: Boolean, state: true },
34
+ _prevError: { type: Boolean, state: true },
35
+ _step: { type: Number, state: true },
36
+ _totalSteps: { type: Number, state: true },
37
+ _hasContent: { type: Boolean, state: true },
38
+ _linear: { type: Boolean, state: true }
39
+ };
40
+
41
+ static styles = css`
42
+ /* ===== HOST LAYOUT ===== */
43
+ :host {
44
+ display: flex;
45
+ flex: 1;
46
+ min-width: 0;
47
+ }
48
+
49
+ /* Horizontal layout */
50
+ :host(:not([data-direction="vertical"])) {
51
+ flex-direction: column;
52
+ align-items: center;
53
+ }
54
+
55
+ /* Vertical layout */
56
+ :host([data-direction="vertical"]) {
57
+ flex-direction: column;
58
+ align-items: stretch;
59
+ }
60
+
61
+ /* ===== DISABLED STATE ===== */
62
+ :host([disabled]) {
63
+ pointer-events: none;
64
+ }
65
+
66
+ :host([disabled]) .indicator ds-icon {
67
+ color: var(--ds-color-icon-disabled);
68
+ }
69
+
70
+ :host([disabled]) .label {
71
+ color: var(--ds-color-text-disabled);
72
+ }
73
+
74
+ :host([disabled]) .content-wrapper {
75
+ color: var(--ds-color-text-disabled);
76
+ }
77
+
78
+ /* ===== LINEAR NON-INTERACTIVE STATE ===== */
79
+ :host([data-linear-locked]) {
80
+ pointer-events: none;
81
+ }
82
+
83
+ :host([data-linear-locked]) .indicator {
84
+ cursor: default;
85
+ }
86
+
87
+ /* ===== STEP VISUAL (connector + indicator row) ===== */
88
+ .step-visual {
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ width: 100%;
93
+ position: relative;
94
+ }
95
+
96
+ :host([data-direction="vertical"]) .step-visual {
97
+ flex-direction: column;
98
+ width: auto;
99
+ height: 100%;
100
+ min-height: var(--ds-stepper-indicator-size, 32px);
101
+ }
102
+
103
+ /* ===== CONNECTORS ===== */
104
+ .connector {
105
+ flex: 1;
106
+ background-color: var(--ds-color-border-strong);
107
+ min-width: 1px;
108
+ min-height: 1px;
109
+ transition: background-color 0.2s, width 0.15s, height 0.15s;
110
+ }
111
+
112
+ /* Horizontal connectors */
113
+ :host(:not([data-direction="vertical"])) .connector {
114
+ height: 1px;
115
+ position: absolute;
116
+ top: 50%;
117
+ transform: translateY(-50%);
118
+ width: 50%;
119
+ z-index: 0;
120
+ }
121
+
122
+ :host(:not([data-direction="vertical"])) .connector.connector-before {
123
+ left: 0;
124
+ }
125
+
126
+ :host(:not([data-direction="vertical"])) .connector.connector-after {
127
+ right: 0;
128
+ }
129
+
130
+ /* Vertical connectors */
131
+ :host([data-direction="vertical"]) .connector {
132
+ position: absolute;
133
+ left: 50%;
134
+ transform: translateX(-50%);
135
+ width: 2px;
136
+ z-index: 0;
137
+ flex: none;
138
+ }
139
+
140
+ :host([data-direction="vertical"]) .connector.connector-before {
141
+ top: 0;
142
+ height: calc(var(--ds-stepper-indicator-size, 32px) / 2);
143
+ }
144
+
145
+ :host([data-direction="vertical"]) .connector.connector-after {
146
+ left: calc(var(--ds-stepper-indicator-size, 32px) / 2);
147
+ top: calc(var(--ds-stepper-indicator-size, 32px) / 2);
148
+ height: auto;
149
+ bottom: 0;
150
+ }
151
+
152
+ /* Hide first/last connectors */
153
+ :host([data-position="first"]) .connector-before {
154
+ visibility: hidden;
155
+ }
156
+
157
+ :host([data-position="last"]) .connector-after {
158
+ visibility: hidden;
159
+ }
160
+
161
+ /* Completed connector */
162
+ .connector.completed {
163
+ background-color: var(--ds-color-border-brand);
164
+ }
165
+
166
+ :host(:not([data-direction="vertical"])) .connector.completed {
167
+ height: 2px;
168
+ }
169
+
170
+ :host([data-direction="vertical"]) .connector.completed {
171
+ width: 2px;
172
+ }
173
+
174
+ :host([data-direction="vertical"]) .connector:not(.completed):not(.error) {
175
+ width: 1px;
176
+ background-color: var(--ds-color-border-strong);
177
+ }
178
+
179
+ /* Error connector */
180
+ .connector.error {
181
+ background-color: var(--ds-color-border-error);
182
+ }
183
+
184
+ :host(:not([data-direction="vertical"])) .connector.error {
185
+ height: 2px;
186
+ }
187
+
188
+ :host([data-direction="vertical"]) .connector.error {
189
+ width: 2px;
190
+ }
191
+
192
+ /* ===== INDICATOR ===== */
193
+ .indicator {
194
+ display: flex;
195
+ align-items: center;
196
+ justify-content: center;
197
+ width: var(--ds-stepper-indicator-size, 32px);
198
+ height: var(--ds-stepper-indicator-size, 32px);
199
+ border-radius: var(--ds-radius-action, 999px);
200
+ background: transparent;
201
+ border: 2px solid transparent;
202
+ cursor: default;
203
+ flex-shrink: 0;
204
+ box-sizing: border-box;
205
+ transition: background-color 0.2s, border-color 0.2s;
206
+ position: relative;
207
+ z-index: 1;
208
+ }
209
+
210
+ /* Icon color by state */
211
+ .indicator ds-icon {
212
+ color: var(--ds-color-icon-secondary);
213
+ }
214
+
215
+ :host([completed]) .indicator ds-icon,
216
+ :host([current]) .indicator ds-icon {
217
+ color: var(--ds-color-icon-brand);
218
+ }
219
+
220
+ :host([error]) .indicator ds-icon {
221
+ color: var(--ds-color-icon-error);
222
+ }
223
+
224
+ /* Clickable steps (non-disabled, non-linear-locked) */
225
+ :host(:not([disabled]):not([data-linear-locked])) .indicator {
226
+ cursor: pointer;
227
+ }
228
+
229
+ /* Hover */
230
+ :host(:not([disabled]):not([data-linear-locked])) .indicator:hover {
231
+ background-color: var(--ds-color-bg-hover);
232
+ }
233
+
234
+ /* Focus */
235
+ .indicator:focus {
236
+ outline: none;
237
+ }
238
+
239
+ .indicator:focus-visible {
240
+ border-color: var(--ds-color-border-focus);
241
+ }
242
+
243
+ /* ===== LABEL ===== */
244
+ .label {
245
+ font: var(--ds-typo-content-body-bold);
246
+ color: var(--ds-color-text-default);
247
+ margin-top: var(--ds-space-sm, 8px);
248
+ text-align: center;
249
+ white-space: nowrap;
250
+ }
251
+
252
+ :host([data-direction="vertical"]) .label {
253
+ margin-top: 0;
254
+ margin-left: var(--ds-space-sm, 8px);
255
+ text-align: left;
256
+ align-self: center;
257
+ }
258
+
259
+ /* ===== VERTICAL CONTENT ===== */
260
+ .content-wrapper {
261
+ display: none;
262
+ padding-top: var(--ds-space-sm, 8px);
263
+ padding-bottom: var(--ds-space-lg, 24px);
264
+ }
265
+
266
+ :host([data-direction="vertical"][current]) .content-wrapper:not([hidden]) {
267
+ display: block;
268
+ margin-left: 0;
269
+ padding-left: calc(var(--ds-stepper-indicator-size, 32px) + var(--ds-space-sm, 8px));
270
+ border-left: 2px solid transparent;
271
+ }
272
+
273
+ /* ===== VERTICAL STRUCTURE ===== */
274
+ :host([data-direction="vertical"]) .step-header {
275
+ display: flex;
276
+ align-items: center;
277
+ }
278
+
279
+ :host([data-direction="vertical"]) .indicator-column {
280
+ display: flex;
281
+ flex-direction: column;
282
+ align-items: center;
283
+ min-height: var(--ds-stepper-indicator-size, 32px);
284
+ position: relative;
285
+ width: var(--ds-stepper-indicator-size, 32px);
286
+ flex-shrink: 0;
287
+ }
288
+
289
+ :host([data-direction="vertical"]) .indicator {
290
+ z-index: 1;
291
+ position: relative;
292
+ }
293
+
294
+ .step-container {
295
+ display: flex;
296
+ flex-direction: column;
297
+ width: 100%;
298
+ position: relative;
299
+ padding-bottom: var(--ds-space-sm, 8px);
300
+ }
301
+ `;
302
+
303
+ constructor() {
304
+ super();
305
+ this.completed = false;
306
+ this.current = false;
307
+ this.disabled = false;
308
+ this.error = false;
309
+ this._direction = 'horizontal';
310
+ this._position = 'middle';
311
+ this._prevCompleted = false;
312
+ this._prevError = false;
313
+ this._step = 1;
314
+ this._totalSteps = 1;
315
+ this._hasContent = false;
316
+ this._linear = false;
317
+ }
318
+
319
+ updated(changedProperties) {
320
+ super.updated(changedProperties);
321
+
322
+ // Data attributes for CSS selectors
323
+ if (changedProperties.has('_direction')) {
324
+ if (this._direction === 'vertical') {
325
+ this.setAttribute('data-direction', 'vertical');
326
+ } else {
327
+ this.removeAttribute('data-direction');
328
+ }
329
+ }
330
+
331
+ if (changedProperties.has('_position')) {
332
+ this.setAttribute('data-position', this._position);
333
+ }
334
+
335
+ // ARIA: aria-current for the active step
336
+ if (changedProperties.has('current')) {
337
+ if (this.current) {
338
+ this.setAttribute('aria-current', 'step');
339
+ } else {
340
+ this.removeAttribute('aria-current');
341
+ }
342
+ }
343
+
344
+ // Linear lock: non-completed, non-current steps become non-interactive
345
+ if (changedProperties.has('_linear') || changedProperties.has('completed') || changedProperties.has('current')) {
346
+ if (this._linear && !this.completed && !this.current) {
347
+ this.setAttribute('data-linear-locked', '');
348
+ } else {
349
+ this.removeAttribute('data-linear-locked');
350
+ }
351
+ }
352
+
353
+ // Propagate state changes to parent
354
+ if (changedProperties.has('completed') || changedProperties.has('error')) {
355
+ this.dispatchEvent(new CustomEvent('ds-step-update', { bubbles: true, composed: true }));
356
+ }
357
+ }
358
+
359
+ _getIconName() {
360
+ return this.current ? 'circle-filled' : 'dot-filled-stepper';
361
+ }
362
+
363
+ _isInteractive() {
364
+ if (this.disabled) return false;
365
+ if (this._linear && !this.completed && !this.current) return false;
366
+ return true;
367
+ }
368
+
369
+ _handleClick() {
370
+ if (this._isInteractive()) {
371
+ this.dispatchEvent(new CustomEvent('ds-step-click', {
372
+ bubbles: true,
373
+ composed: true,
374
+ detail: { step: this._step }
375
+ }));
376
+ }
377
+ }
378
+
379
+ _handleKeyDown(e) {
380
+ if ((e.key === 'Enter' || e.key === ' ') && this._isInteractive()) {
381
+ e.preventDefault();
382
+ this._handleClick();
383
+ }
384
+ }
385
+
386
+ _getConnectorBeforeClass() {
387
+ let cls = 'connector connector-before';
388
+ if (this._prevError) {
389
+ cls += ' error';
390
+ } else if (this._prevCompleted) {
391
+ cls += ' completed';
392
+ }
393
+ return cls;
394
+ }
395
+
396
+ _getConnectorAfterClass() {
397
+ let cls = 'connector connector-after';
398
+ if (this.error) {
399
+ cls += ' error';
400
+ } else if (this.completed) {
401
+ cls += ' completed';
402
+ }
403
+ return cls;
404
+ }
405
+
406
+ render() {
407
+ const interactive = this._isInteractive();
408
+ const connectorBeforeClass = this._getConnectorBeforeClass();
409
+ const connectorAfterClass = this._getConnectorAfterClass();
410
+ const isVertical = this._direction === 'vertical';
411
+ const ariaLabel = interactive ? `Step ${this._step} of ${this._totalSteps}` : '';
412
+
413
+ if (isVertical) {
414
+ return html`
415
+ <div class="step-container">
416
+ <div class="${connectorAfterClass}" part="connector-after"></div>
417
+
418
+ <div class="step-header">
419
+ <div class="indicator-column">
420
+ <div class="${connectorBeforeClass}" part="connector-before"></div>
421
+ <div
422
+ class="indicator"
423
+ part="indicator"
424
+ role="${interactive ? 'button' : 'presentation'}"
425
+ tabindex="${interactive ? '0' : '-1'}"
426
+ aria-label="${ariaLabel}"
427
+ aria-disabled="${this.disabled ? 'true' : 'false'}"
428
+ @click=${this._handleClick}
429
+ @keydown=${this._handleKeyDown}
430
+ >
431
+ <ds-icon name="${this._getIconName()}" size="sm"></ds-icon>
432
+ </div>
433
+ </div>
434
+
435
+ <div class="label" part="label">
436
+ <slot></slot>
437
+ </div>
438
+ </div>
439
+
440
+ <div class="content-wrapper" ?hidden=${!this._hasContent}>
441
+ <slot name="content" @slotchange=${this._handleContentSlotChange}></slot>
442
+ </div>
443
+ </div>
444
+ `;
445
+ }
446
+
447
+ return html`
448
+ <div class="step-visual">
449
+ <div class="${connectorBeforeClass}" part="connector-before"></div>
450
+
451
+ <div
452
+ class="indicator"
453
+ part="indicator"
454
+ role="${interactive ? 'button' : 'presentation'}"
455
+ tabindex="${interactive ? '0' : '-1'}"
456
+ aria-label="${ariaLabel}"
457
+ aria-disabled="${this.disabled ? 'true' : 'false'}"
458
+ @click=${this._handleClick}
459
+ @keydown=${this._handleKeyDown}
460
+ >
461
+ <ds-icon name="${this._getIconName()}" size="sm"></ds-icon>
462
+ </div>
463
+
464
+ <div class="${connectorAfterClass}" part="connector-after"></div>
465
+ </div>
466
+
467
+ <div class="label" part="label">
468
+ <slot></slot>
469
+ </div>
470
+ `;
471
+ }
472
+
473
+ _handleContentSlotChange(e) {
474
+ const slot = e.target;
475
+ const nodes = slot.assignedNodes({ flatten: true });
476
+ this._hasContent = nodes.some(node =>
477
+ node.nodeType === Node.ELEMENT_NODE ||
478
+ (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '')
479
+ );
480
+ }
481
+ }
482
+
483
+ if (!customElements.get('ds-stepper-item')) {
484
+ customElements.define('ds-stepper-item', DsStepperItem);
485
+ }