@reliability-design/web 1.0.3 → 1.1.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/dist/components/rel-accordion/README.md +132 -0
- package/dist/components/rel-accordion/rel-accordion.ts +47 -16
- package/dist/components/rel-alert/README.md +141 -0
- package/dist/components/rel-alert/rel-alert.ts +101 -20
- package/dist/components/rel-avatar/README.md +119 -0
- package/dist/components/rel-avatar/rel-avatar.ts +3 -2
- package/dist/components/rel-badge/README.md +86 -0
- package/dist/components/rel-badge/rel-badge.ts +11 -7
- package/dist/components/rel-breadcrumb/README.md +102 -0
- package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +12 -6
- package/dist/components/rel-button/README.md +130 -0
- package/dist/components/rel-button/rel-button.ts +64 -8
- package/dist/components/rel-calendar/README.md +109 -0
- package/dist/components/rel-calendar/index.ts +1 -0
- package/dist/components/rel-calendar/rel-calendar.ts +467 -0
- package/dist/components/rel-card/README.md +94 -0
- package/dist/components/rel-card/rel-card.ts +12 -3
- package/dist/components/rel-cardmedia/README.md +127 -0
- package/dist/components/rel-cardmedia/rel-cardmedia.ts +14 -4
- package/dist/components/rel-carousel/README.md +166 -0
- package/dist/components/rel-carousel/index.ts +1 -0
- package/dist/components/rel-carousel/rel-carousel.ts +468 -0
- package/dist/components/rel-chart/README.md +286 -0
- package/dist/components/rel-chart/index.ts +1 -0
- package/dist/components/rel-chart/rel-chart.ts +736 -0
- package/dist/components/rel-checkbox/README.md +77 -0
- package/dist/components/rel-checkbox/rel-checkbox.ts +51 -12
- package/dist/components/rel-chip/README.md +114 -0
- package/dist/components/rel-chip/rel-chip.ts +83 -9
- package/dist/components/rel-container/README.md +99 -0
- package/dist/components/rel-date-picker/README.md +253 -0
- package/dist/components/rel-date-picker/index.ts +1 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +916 -0
- package/dist/components/rel-divider/README.md +100 -0
- package/dist/components/rel-divider/rel-divider.ts +9 -0
- package/dist/components/rel-drawer/README.md +200 -0
- package/dist/components/rel-drawer/index.ts +1 -0
- package/dist/components/rel-drawer/rel-drawer-footer.ts +29 -0
- package/dist/components/rel-drawer/rel-drawer.ts +612 -0
- package/dist/components/rel-kbd/README.md +151 -0
- package/dist/components/rel-kbd/rel-kbd.ts +118 -0
- package/dist/components/rel-layout/README.md +88 -0
- package/dist/components/rel-menu/README.md +151 -0
- package/dist/components/rel-menu/rel-menu.ts +64 -24
- package/dist/components/rel-menubar/README.md +329 -0
- package/dist/components/rel-menubar/index.ts +2 -0
- package/dist/components/rel-menubar/rel-menubar-menu.ts +16 -0
- package/dist/components/rel-menubar/rel-menubar.ts +605 -0
- package/dist/components/rel-nav-menu/README.md +289 -0
- package/dist/components/rel-nav-menu/index.ts +3 -0
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +240 -0
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +53 -0
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +227 -0
- package/dist/components/rel-navbar/README.md +126 -0
- package/dist/components/rel-navbar/rel-navbar.ts +104 -0
- package/dist/components/rel-otp/README.md +440 -0
- package/dist/components/rel-otp/index.ts +1 -0
- package/dist/components/rel-otp/rel-otp.ts +504 -0
- package/dist/components/rel-pagination/README.md +410 -0
- package/dist/components/rel-pagination/index.ts +1 -0
- package/dist/components/rel-pagination/rel-pagination.ts +381 -0
- package/dist/components/rel-popover/README.md +568 -0
- package/dist/components/rel-popover/index.ts +2 -0
- package/dist/components/rel-popover/rel-popover-content.ts +55 -0
- package/dist/components/rel-popover/rel-popover.ts +330 -0
- package/dist/components/rel-progress/README.md +110 -0
- package/dist/components/rel-progress/rel-progress.ts +32 -5
- package/dist/components/rel-radio/README.md +120 -0
- package/dist/components/rel-radio/rel-radio.ts +31 -6
- package/dist/components/rel-resizable/README.md +140 -0
- package/dist/components/rel-resizable/index.ts +3 -0
- package/dist/components/rel-resizable/rel-resizable-group.ts +374 -0
- package/dist/components/rel-resizable/rel-resizable-handle.ts +145 -0
- package/dist/components/rel-resizable/rel-resizable-panel.ts +47 -0
- package/dist/components/rel-segmented-button/README.md +141 -0
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +18 -13
- package/dist/components/rel-select/README.md +107 -0
- package/dist/components/rel-select/rel-select.ts +28 -14
- package/dist/components/rel-sheet/README.md +224 -0
- package/dist/components/rel-sheet/rel-sheet.ts +684 -0
- package/dist/components/rel-sidebar/README.md +209 -0
- package/dist/components/rel-sidebar/rel-sidebar.ts +1166 -0
- package/dist/components/rel-slider/README.md +135 -0
- package/dist/components/rel-slider/rel-slider.ts +44 -9
- package/dist/components/rel-snackbar/README.md +134 -0
- package/dist/components/rel-snackbar/rel-snackbar.ts +109 -14
- package/dist/components/rel-stepper/README.md +160 -0
- package/dist/components/rel-stepper/rel-stepper.ts +38 -27
- package/dist/components/rel-switch/README.md +102 -0
- package/dist/components/rel-switch/rel-switch.ts +34 -12
- package/dist/components/rel-tab/README.md +163 -0
- package/dist/components/rel-tab/rel-tab.ts +79 -23
- package/dist/components/rel-table/README.md +178 -0
- package/dist/components/rel-table/rel-table-body.ts +21 -0
- package/dist/components/rel-table/rel-table-caption.ts +27 -0
- package/dist/components/rel-table/rel-table-cell.ts +42 -0
- package/dist/components/rel-table/rel-table-footer.ts +23 -0
- package/dist/components/rel-table/rel-table-head.ts +45 -0
- package/dist/components/rel-table/rel-table-header.ts +21 -0
- package/dist/components/rel-table/rel-table-row.ts +36 -0
- package/dist/components/rel-table/rel-table.ts +53 -0
- package/dist/components/rel-textfield/README.md +90 -0
- package/dist/components/rel-textfield/rel-textfield.ts +55 -39
- package/dist/components/rel-toast/README.md +148 -0
- package/dist/components/rel-toast/rel-toast.ts +63 -24
- package/dist/components/rel-tooltip/README.md +103 -0
- package/dist/components/rel-tooltip/rel-tooltip.ts +123 -80
- package/dist/components/rel-topbar/rel-topbar-actions.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-brand.ts +46 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +33 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +28 -0
- package/dist/components/rel-topbar/rel-topbar-item.ts +315 -0
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-profile.ts +248 -0
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar.ts +329 -0
- package/dist/index.d.mts +831 -7
- package/dist/index.d.ts +831 -7
- package/dist/index.js +9500 -1564
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9240 -1344
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +187 -25
- package/dist/vars-TBB4BMSD.css +346 -0
- package/package.json +2 -1
- package/dist/vars-6ZIUPIU4.css +0 -184
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# rel-stepper
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
A comprehensive stepper component for visualizing multi-step processes. Supports horizontal and vertical orientations, text positioning (below or right of indicator), two size variants, and three step states (done, progress, disabled). Features clickable steps, automatic connector lines, and checkmark icons for completed steps.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
```html
|
|
8
|
+
<!-- Basic horizontal stepper -->
|
|
9
|
+
<rel-stepper id="checkout"></rel-stepper>
|
|
10
|
+
<script>
|
|
11
|
+
const stepper = document.getElementById('checkout');
|
|
12
|
+
stepper.steps = [
|
|
13
|
+
{ title: 'Cart', status: 'done' },
|
|
14
|
+
{ title: 'Shipping', status: 'progress' },
|
|
15
|
+
{ title: 'Payment', status: 'disabled' },
|
|
16
|
+
{ title: 'Confirmation', status: 'disabled' }
|
|
17
|
+
];
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<!-- Vertical stepper with text to the right -->
|
|
21
|
+
<rel-stepper
|
|
22
|
+
id="onboarding"
|
|
23
|
+
orientation="vertical"
|
|
24
|
+
textAlign="right">
|
|
25
|
+
</rel-stepper>
|
|
26
|
+
<script>
|
|
27
|
+
const onboarding = document.getElementById('onboarding');
|
|
28
|
+
onboarding.steps = [
|
|
29
|
+
{ title: 'Create Account', status: 'done' },
|
|
30
|
+
{ title: 'Verify Email', status: 'done' },
|
|
31
|
+
{ title: 'Profile Setup', status: 'progress' },
|
|
32
|
+
{ title: 'Complete', status: 'disabled' }
|
|
33
|
+
];
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<!-- Small size horizontal stepper -->
|
|
37
|
+
<rel-stepper
|
|
38
|
+
id="wizard"
|
|
39
|
+
size="small"
|
|
40
|
+
textAlign="below">
|
|
41
|
+
</rel-stepper>
|
|
42
|
+
<script>
|
|
43
|
+
const wizard = document.getElementById('wizard');
|
|
44
|
+
wizard.steps = [
|
|
45
|
+
{ title: 'Select', status: 'done' },
|
|
46
|
+
{ title: 'Configure', status: 'progress' },
|
|
47
|
+
{ title: 'Review', status: 'disabled' }
|
|
48
|
+
];
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<!-- Listen for step clicks -->
|
|
52
|
+
<rel-stepper id="interactive"></rel-stepper>
|
|
53
|
+
<script>
|
|
54
|
+
const interactive = document.getElementById('interactive');
|
|
55
|
+
interactive.steps = [
|
|
56
|
+
{ title: 'Step 1', status: 'done' },
|
|
57
|
+
{ title: 'Step 2', status: 'progress' },
|
|
58
|
+
{ title: 'Step 3', status: 'disabled' }
|
|
59
|
+
];
|
|
60
|
+
interactive.addEventListener('step-change', (e) => {
|
|
61
|
+
console.log('Clicked step:', e.detail.index, e.detail.step);
|
|
62
|
+
// Update step statuses or navigate to step content
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## API
|
|
68
|
+
|
|
69
|
+
### Attributes/Properties
|
|
70
|
+
| Name | Type | Default | Description |
|
|
71
|
+
|------|------|---------|-------------|
|
|
72
|
+
| `steps` | `Step[]` | `[]` | Array of step items (see Step interface below) |
|
|
73
|
+
| `textAlign` | `"right" \| "below"` | `"below"` | Position of step labels relative to indicators |
|
|
74
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction |
|
|
75
|
+
| `size` | `"small" \| "large"` | `"large"` | Size variant (controls connector line length and spacing) |
|
|
76
|
+
|
|
77
|
+
### Step Interface
|
|
78
|
+
```typescript
|
|
79
|
+
interface Step {
|
|
80
|
+
title: string; // Step label text
|
|
81
|
+
status: "done" | "progress" | "disabled"; // Step state
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Events
|
|
86
|
+
| Event | Detail | Description |
|
|
87
|
+
|-------|--------|-------------|
|
|
88
|
+
| `step-change` | `{ index: number, step: Step }` | Fired when a non-disabled step is clicked |
|
|
89
|
+
|
|
90
|
+
## Slots
|
|
91
|
+
Default slot not used.
|
|
92
|
+
|
|
93
|
+
## CSS Parts
|
|
94
|
+
None exposed.
|
|
95
|
+
|
|
96
|
+
## Tokens / CSS Variables
|
|
97
|
+
|
|
98
|
+
### Semantic Tokens
|
|
99
|
+
- `--rel-fontFamilies-primary` - Font family
|
|
100
|
+
- `--rel-fontSizes-sm` - Label font size
|
|
101
|
+
- `--rel-fontWeights-medium` - Default label font weight
|
|
102
|
+
- `--rel-fontWeights-semibold` - Active/done label font weight
|
|
103
|
+
|
|
104
|
+
### Spacing Tokens
|
|
105
|
+
- `--rel-spacing-space-2xs` - Gap between indicators and labels (text-right mode)
|
|
106
|
+
|
|
107
|
+
### Color Tokens
|
|
108
|
+
- `--rel-colors-white` - Indicator background (progress, disabled states)
|
|
109
|
+
- `--rel-colors-black` - Active/done indicator background, text, and connector color
|
|
110
|
+
- `--rel-colors-gray-border` - Disabled indicator border and inactive connector
|
|
111
|
+
- `--rel-colors-gray-light` - Disabled indicator background
|
|
112
|
+
- `--rel-text-disabled` - Disabled label text color
|
|
113
|
+
- `--rel-text-primary` - Default label text color
|
|
114
|
+
|
|
115
|
+
## Accessibility
|
|
116
|
+
- Steps can be clicked unless in disabled state
|
|
117
|
+
- Checkmark SVG uses `currentColor` for proper theming
|
|
118
|
+
- Focus states could be enhanced with keyboard navigation (not currently implemented)
|
|
119
|
+
|
|
120
|
+
### Keyboard Interactions
|
|
121
|
+
- Click-based interaction only (no keyboard navigation implemented)
|
|
122
|
+
|
|
123
|
+
### Focus Behavior
|
|
124
|
+
- No specific focus handling (steps are div elements, not buttons)
|
|
125
|
+
|
|
126
|
+
## Notes / Edge Cases
|
|
127
|
+
- **Indicator states**:
|
|
128
|
+
- **done**: Black circle with white checkmark SVG icon
|
|
129
|
+
- **progress**: White circle with black border and step number
|
|
130
|
+
- **disabled**: Light gray circle with gray border and step number
|
|
131
|
+
|
|
132
|
+
- **Connector lines**:
|
|
133
|
+
- Positioned absolutely relative to the indicator
|
|
134
|
+
- **Horizontal**: 2px height, extends from indicator to next step
|
|
135
|
+
- **Vertical**: 2px width, extends from indicator to next step
|
|
136
|
+
- **done/progress** connectors are black; **disabled** connectors are gray
|
|
137
|
+
- Last step has no connector
|
|
138
|
+
|
|
139
|
+
- **Size variants** (control connector length and gap):
|
|
140
|
+
- **large horizontal**: 86px connector, 86px gap
|
|
141
|
+
- **small horizontal**: 60px connector, 60px gap
|
|
142
|
+
- **large vertical**: 86px connector, 86px gap
|
|
143
|
+
- **small vertical**: 42px connector, 42px gap
|
|
144
|
+
|
|
145
|
+
- **Text positioning**:
|
|
146
|
+
- **below**: Labels appear centered below indicators
|
|
147
|
+
- **right**: Labels appear to the right of indicators with a small gap
|
|
148
|
+
|
|
149
|
+
- **Layout combinations**:
|
|
150
|
+
- **horizontal + text-below**: Steps arranged in a row, labels centered below
|
|
151
|
+
- **horizontal + text-right**: Steps in a row, labels to the right of each indicator
|
|
152
|
+
- **vertical + text-below**: Steps stacked vertically, labels centered below each indicator
|
|
153
|
+
- **vertical + text-right**: Steps stacked vertically, labels to the right with indicators on the left
|
|
154
|
+
|
|
155
|
+
- The indicator is 22px × 22px with a 2px border
|
|
156
|
+
- Step numbers are 1-indexed (displayed as `index + 1`)
|
|
157
|
+
- Clicking a disabled step does nothing (no event dispatched)
|
|
158
|
+
- All transitions use `0.2s ease`
|
|
159
|
+
- The component uses flexbox extensively for layout management
|
|
160
|
+
- The stepper is `display: block` and `width: 100%`
|
|
@@ -126,8 +126,8 @@ export class RelStepper extends LitElement {
|
|
|
126
126
|
display: flex;
|
|
127
127
|
align-items: center;
|
|
128
128
|
justify-content: center;
|
|
129
|
-
width: 22px;
|
|
130
|
-
height: 22px;
|
|
129
|
+
width: var(--rel-stepper-indicator-size, 22px);
|
|
130
|
+
height: var(--rel-stepper-indicator-size, 22px);
|
|
131
131
|
border: 2px solid var(--rel-colors-gray-border);
|
|
132
132
|
background: var(--rel-colors-white);
|
|
133
133
|
border-radius: 50%;
|
|
@@ -151,7 +151,7 @@ export class RelStepper extends LitElement {
|
|
|
151
151
|
.step-label {
|
|
152
152
|
white-space: nowrap;
|
|
153
153
|
font-size: var(--rel-fontSizes-sm);
|
|
154
|
-
color: var(--rel-
|
|
154
|
+
color: var(--rel-text-primary, #333);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/* Horizontal text-below specific */
|
|
@@ -237,43 +237,54 @@ export class RelStepper extends LitElement {
|
|
|
237
237
|
* Step States
|
|
238
238
|
********************************************/
|
|
239
239
|
.rel-step.done .indicator {
|
|
240
|
-
background: var(--rel-
|
|
241
|
-
border-color: var(--rel-
|
|
242
|
-
color: var(--rel-
|
|
240
|
+
background: var(--rel-color-text-heading);
|
|
241
|
+
border-color: var(--rel-color-text-heading);
|
|
242
|
+
color: var(--rel-color-text-inverse);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
.rel-step.progress .indicator {
|
|
246
|
-
border-color: var(--rel-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
border-color: var(--rel-color-text-heading);
|
|
247
|
+
background: var(--rel-color-bg-primary);
|
|
248
|
+
color: var(--rel-color-text-heading);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
.rel-step.disabled .indicator {
|
|
252
|
-
background: var(--rel-colors-gray-
|
|
253
|
-
border-color: var(--rel-colors-gray-border);
|
|
254
|
-
color: var(--rel-
|
|
252
|
+
background: var(--rel-colors-gray-light, #fafafa);
|
|
253
|
+
border-color: var(--rel-colors-gray-border, #ddd);
|
|
254
|
+
color: var(--rel-text-disabled, #9e9e9e);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
.rel-step.done .connector {
|
|
258
|
-
background: var(--rel-
|
|
258
|
+
background: var(--rel-color-text-heading);
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
.rel-step.progress .connector {
|
|
262
|
-
background: var(--rel-
|
|
262
|
+
background: var(--rel-color-text-heading);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
.rel-step.done .step-label {
|
|
266
|
-
color: var(--rel-
|
|
266
|
+
color: var(--rel-color-text-heading);
|
|
267
267
|
font-weight: var(--rel-fontWeights-semibold);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
.rel-step.progress .step-label {
|
|
271
|
-
color: var(--rel-
|
|
271
|
+
color: var(--rel-color-text-heading);
|
|
272
272
|
font-weight: var(--rel-fontWeights-semibold);
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
.rel-step.disabled .step-label {
|
|
276
|
-
color: var(--rel-
|
|
276
|
+
color: var(--rel-text-disabled, #9e9e9e);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Dark mode: ensure progress/done indicator and labels stay readable */
|
|
280
|
+
:host-context(:root[data-theme="dark"]) .rel-step.progress .step-label,
|
|
281
|
+
:host-context(.dark) .rel-step.progress .step-label {
|
|
282
|
+
color: var(--rel-color-text-primary);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
:host-context(:root[data-theme="dark"]) .rel-step.progress .indicator,
|
|
286
|
+
:host-context(.dark) .rel-step.progress .indicator {
|
|
287
|
+
color: #ffffff;
|
|
277
288
|
}
|
|
278
289
|
|
|
279
290
|
/********************************************
|
|
@@ -281,36 +292,36 @@ export class RelStepper extends LitElement {
|
|
|
281
292
|
********************************************/
|
|
282
293
|
/* Horizontal */
|
|
283
294
|
.rel-stepper.large.horizontal {
|
|
284
|
-
gap: 86px;
|
|
295
|
+
gap: var(--rel-stepper-connector-length-lg, 86px);
|
|
285
296
|
}
|
|
286
297
|
|
|
287
298
|
.rel-stepper.small.horizontal {
|
|
288
|
-
gap: 60px;
|
|
299
|
+
gap: var(--rel-stepper-connector-length-md, 60px);
|
|
289
300
|
}
|
|
290
301
|
|
|
291
302
|
.rel-stepper.large.horizontal .connector {
|
|
292
|
-
width: 86px;
|
|
303
|
+
width: var(--rel-stepper-connector-length-lg, 86px);
|
|
293
304
|
}
|
|
294
305
|
|
|
295
306
|
.rel-stepper.small.horizontal .connector {
|
|
296
|
-
width: 60px;
|
|
307
|
+
width: var(--rel-stepper-connector-length-md, 60px);
|
|
297
308
|
}
|
|
298
309
|
|
|
299
310
|
/* Vertical */
|
|
300
311
|
.rel-stepper.large.vertical .connector {
|
|
301
|
-
height: 86px;
|
|
312
|
+
height: var(--rel-stepper-connector-length-lg, 86px);
|
|
302
313
|
}
|
|
303
314
|
|
|
304
315
|
.rel-stepper.large.vertical {
|
|
305
|
-
gap: 86px;
|
|
316
|
+
gap: var(--rel-stepper-connector-length-lg, 86px);
|
|
306
317
|
}
|
|
307
318
|
|
|
308
319
|
.rel-stepper.small.vertical .connector {
|
|
309
|
-
height: 42px;
|
|
320
|
+
height: var(--rel-stepper-connector-length-sm, 42px);
|
|
310
321
|
}
|
|
311
322
|
|
|
312
323
|
.rel-stepper.small.vertical {
|
|
313
|
-
gap: 42px;
|
|
324
|
+
gap: var(--rel-stepper-connector-length-sm, 42px);
|
|
314
325
|
}
|
|
315
326
|
`;
|
|
316
327
|
|
|
@@ -338,7 +349,7 @@ export class RelStepper extends LitElement {
|
|
|
338
349
|
if (step.status === "disabled") return;
|
|
339
350
|
|
|
340
351
|
this.dispatchEvent(
|
|
341
|
-
new CustomEvent("step-change", {
|
|
352
|
+
new CustomEvent("rel-step-change", {
|
|
342
353
|
detail: {
|
|
343
354
|
index,
|
|
344
355
|
step,
|
|
@@ -362,7 +373,7 @@ export class RelStepper extends LitElement {
|
|
|
362
373
|
>
|
|
363
374
|
<path
|
|
364
375
|
d="M6.44479 14L0.5 7.36383L1.9862 5.70478L6.44479 10.6819L16.0138 0L17.5 1.65904L6.44479 14Z"
|
|
365
|
-
fill="
|
|
376
|
+
fill="currentColor"
|
|
366
377
|
/>
|
|
367
378
|
</svg>
|
|
368
379
|
`;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# rel-switch
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
A toggle switch component for binary on/off states. Features smooth animations, focus halo effects, pressed states, and respects user motion preferences. Built with custom visual styling while maintaining native checkbox accessibility.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
```html
|
|
8
|
+
<!-- Basic switch -->
|
|
9
|
+
<rel-switch></rel-switch>
|
|
10
|
+
|
|
11
|
+
<!-- Pre-checked switch -->
|
|
12
|
+
<rel-switch checked></rel-switch>
|
|
13
|
+
|
|
14
|
+
<!-- Disabled switch -->
|
|
15
|
+
<rel-switch disabled></rel-switch>
|
|
16
|
+
<rel-switch checked disabled></rel-switch>
|
|
17
|
+
|
|
18
|
+
<!-- With event listener -->
|
|
19
|
+
<rel-switch id="notifications"></rel-switch>
|
|
20
|
+
<script>
|
|
21
|
+
const toggle = document.getElementById('notifications');
|
|
22
|
+
toggle.addEventListener('changed', (e) => {
|
|
23
|
+
console.log('Switch state:', e.detail); // boolean: true/false
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<!-- Programmatically control -->
|
|
28
|
+
<rel-switch id="darkMode"></rel-switch>
|
|
29
|
+
<script>
|
|
30
|
+
const darkModeSwitch = document.getElementById('darkMode');
|
|
31
|
+
darkModeSwitch.checked = true;
|
|
32
|
+
darkModeSwitch.disabled = false;
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
### Attributes/Properties
|
|
39
|
+
| Name | Type | Default | Description |
|
|
40
|
+
|------|------|---------|-------------|
|
|
41
|
+
| `checked` | `boolean` | `false` | Whether the switch is in the ON position |
|
|
42
|
+
| `disabled` | `boolean` | `false` | Disables the switch and reduces opacity |
|
|
43
|
+
|
|
44
|
+
## Events
|
|
45
|
+
| Event | Detail | Description |
|
|
46
|
+
|-------|--------|-------------|
|
|
47
|
+
| `changed` | `boolean` (the new checked state) | Fired when the switch is toggled by user interaction |
|
|
48
|
+
|
|
49
|
+
## Slots
|
|
50
|
+
Default slot not used.
|
|
51
|
+
|
|
52
|
+
## CSS Parts
|
|
53
|
+
None exposed.
|
|
54
|
+
|
|
55
|
+
## Tokens / CSS Variables
|
|
56
|
+
|
|
57
|
+
### Component-Level Custom Properties
|
|
58
|
+
The component defines these CSS custom properties on `:host` which can be overridden:
|
|
59
|
+
|
|
60
|
+
- `--switch-track` - Track color when unchecked (default: `--rel-text-placeholder`)
|
|
61
|
+
- `--switch-knob-on` - Knob color when checked (default: `--rel-colors-black`)
|
|
62
|
+
- `--switch-knob-off` - Knob color when unchecked (default: `--rel-colors-white`)
|
|
63
|
+
- `--switch-halo` - Focus halo color (default: `--rel-border-default`)
|
|
64
|
+
- `--switch-disabled` - Color for disabled state (default: `--rel-text-disabled`)
|
|
65
|
+
|
|
66
|
+
### Design Token References
|
|
67
|
+
- `--rel-text-placeholder` - Default track color
|
|
68
|
+
- `--rel-text-dark-placeholder` - Hover track color
|
|
69
|
+
- `--rel-colors-black` - ON knob color
|
|
70
|
+
- `--rel-colors-white` - OFF knob color
|
|
71
|
+
- `--rel-border-default` - Focus halo
|
|
72
|
+
- `--rel-text-disabled` - Disabled state color
|
|
73
|
+
- `--rel-surface-surface` - Used in disabled styling
|
|
74
|
+
|
|
75
|
+
## Accessibility
|
|
76
|
+
- Uses native checkbox input with `role="switch"` and `aria-checked`
|
|
77
|
+
- Visually hidden checkbox maintains keyboard and screen reader support
|
|
78
|
+
- Focus visible state shows prominent halo around knob
|
|
79
|
+
- Disabled state prevents interaction and reduces opacity to 0.5
|
|
80
|
+
- Smooth transitions respect `prefers-reduced-motion` media query
|
|
81
|
+
|
|
82
|
+
### Keyboard Interactions
|
|
83
|
+
- `Space` - Toggle switch on/off
|
|
84
|
+
- `Tab` - Move focus to/from switch
|
|
85
|
+
|
|
86
|
+
### Focus Behavior
|
|
87
|
+
- Focus ring appears as a 6px halo around the knob using `--switch-halo` color
|
|
88
|
+
- Halo uses box-shadow for smooth visual feedback
|
|
89
|
+
|
|
90
|
+
### Motion Support
|
|
91
|
+
- Animations disabled when `prefers-reduced-motion: reduce` is set
|
|
92
|
+
- Transitions apply to track color, knob position, knob color, and box-shadow
|
|
93
|
+
|
|
94
|
+
## Notes / Edge Cases
|
|
95
|
+
- The switch is 42px wide × 22px tall with a 12px tall track and 18px diameter knob
|
|
96
|
+
- Visual knob animates 22px horizontally when toggling from OFF to ON
|
|
97
|
+
- Hover effect only applies when not disabled (track darkens slightly)
|
|
98
|
+
- Pressed state applies a `scale(0.9)` transform to the knob for tactile feedback
|
|
99
|
+
- The knob includes a subtle drop shadow (`0 1px 3px rgba(0,0,0,0.3)`) for depth
|
|
100
|
+
- When disabled, both track and knob use `--switch-disabled` color
|
|
101
|
+
- The component is `display: inline-block` by default
|
|
102
|
+
- Focus halo appears on keyboard focus only (`:focus-visible`), not on mouse click
|
|
@@ -5,17 +5,21 @@ import { customElement, property, state } from "lit/decorators.js";
|
|
|
5
5
|
export class RelSwitch extends LitElement {
|
|
6
6
|
@property({ type: Boolean }) checked = false;
|
|
7
7
|
@property({ type: Boolean }) disabled = false;
|
|
8
|
+
@property({ type: String }) value = "";
|
|
9
|
+
|
|
10
|
+
private _onChange: (value: boolean) => void = () => {};
|
|
11
|
+
private _onTouched: () => void = () => {};
|
|
8
12
|
|
|
9
13
|
@state() private isFocused = false;
|
|
10
14
|
@state() private isPressed = false;
|
|
11
15
|
|
|
12
16
|
static styles = css`
|
|
13
17
|
:host {
|
|
14
|
-
--switch-track:
|
|
15
|
-
--switch-knob-on:
|
|
16
|
-
--switch-knob-off:
|
|
17
|
-
--switch-halo:
|
|
18
|
-
--switch-disabled:
|
|
18
|
+
--switch-track: var(--rel-text-placeholder);
|
|
19
|
+
--switch-knob-on: var(--rel-colors-black);
|
|
20
|
+
--switch-knob-off: var(--rel-colors-white);
|
|
21
|
+
--switch-halo: var(--rel-border-default);
|
|
22
|
+
--switch-disabled: var(--rel-text-disabled);
|
|
19
23
|
display: inline-block;
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -23,8 +27,8 @@ export class RelSwitch extends LitElement {
|
|
|
23
27
|
.switch-wrapper {
|
|
24
28
|
position: relative;
|
|
25
29
|
display: inline-block;
|
|
26
|
-
width: 42px;
|
|
27
|
-
height: 22px;
|
|
30
|
+
width: var(--rel-switch-track-width, 42px);
|
|
31
|
+
height: var(--rel-switch-track-height, 22px);
|
|
28
32
|
cursor: pointer;
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -60,8 +64,8 @@ export class RelSwitch extends LitElement {
|
|
|
60
64
|
top: 50%;
|
|
61
65
|
left: -2px;
|
|
62
66
|
transform: translateY(-50%);
|
|
63
|
-
height: 18px;
|
|
64
|
-
width: 18px;
|
|
67
|
+
height: var(--rel-switch-thumb-size, 18px);
|
|
68
|
+
width: var(--rel-switch-thumb-size, 18px);
|
|
65
69
|
background-color: var(--switch-knob-off);
|
|
66
70
|
border-radius: 50%;
|
|
67
71
|
transition: transform 0.25s ease, background-color 0.25s ease,
|
|
@@ -77,7 +81,7 @@ export class RelSwitch extends LitElement {
|
|
|
77
81
|
|
|
78
82
|
/* Hover */
|
|
79
83
|
.switch-wrapper:hover:not(.switch-disabled) .switch-slider {
|
|
80
|
-
background-color:
|
|
84
|
+
background-color: var(--rel-text-dark-placeholder);
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/* Focus halo */
|
|
@@ -100,6 +104,13 @@ export class RelSwitch extends LitElement {
|
|
|
100
104
|
.switch-disabled .switch-slider::before {
|
|
101
105
|
background-color: var(--switch-disabled);
|
|
102
106
|
}
|
|
107
|
+
|
|
108
|
+
@media (prefers-reduced-motion: reduce) {
|
|
109
|
+
.switch-slider,
|
|
110
|
+
.switch-slider::before {
|
|
111
|
+
transition: none;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
103
114
|
`;
|
|
104
115
|
|
|
105
116
|
private toggle(ev: Event) {
|
|
@@ -109,14 +120,22 @@ export class RelSwitch extends LitElement {
|
|
|
109
120
|
this.checked = input.checked;
|
|
110
121
|
|
|
111
122
|
this.dispatchEvent(
|
|
112
|
-
new CustomEvent
|
|
113
|
-
detail:
|
|
123
|
+
new CustomEvent("rel-change", {
|
|
124
|
+
detail: {
|
|
125
|
+
checked: this.checked,
|
|
126
|
+
value: this.value,
|
|
127
|
+
},
|
|
114
128
|
bubbles: true,
|
|
115
129
|
composed: true,
|
|
116
130
|
})
|
|
117
131
|
);
|
|
132
|
+
this._onChange(this.checked);
|
|
118
133
|
}
|
|
119
134
|
|
|
135
|
+
registerOnChange(fn: (value: boolean) => void): void { this._onChange = fn; }
|
|
136
|
+
registerOnTouched(fn: () => void): void { this._onTouched = fn; }
|
|
137
|
+
writeValue(checked: boolean): void { this.checked = checked; }
|
|
138
|
+
|
|
120
139
|
private handleFocus() {
|
|
121
140
|
this.isFocused = true;
|
|
122
141
|
}
|
|
@@ -124,6 +143,7 @@ export class RelSwitch extends LitElement {
|
|
|
124
143
|
private handleBlur() {
|
|
125
144
|
this.isFocused = false;
|
|
126
145
|
this.isPressed = false;
|
|
146
|
+
this._onTouched();
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
private handleMouseDown() {
|
|
@@ -148,6 +168,8 @@ export class RelSwitch extends LitElement {
|
|
|
148
168
|
<label class="${wrapperClasses}">
|
|
149
169
|
<input
|
|
150
170
|
type="checkbox"
|
|
171
|
+
role="switch"
|
|
172
|
+
aria-checked="${this.checked}"
|
|
151
173
|
?disabled=${this.disabled}
|
|
152
174
|
.checked=${this.checked}
|
|
153
175
|
@change=${(e: Event) => this.toggle(e)}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# rel-tab
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
A tabbed interface component with automatic underline indicator, keyboard navigation, optional badge counts, and disabled states. Features ARIA-compliant navigation with roving tabindex and arrow key support.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
```html
|
|
8
|
+
<!-- Basic tabs -->
|
|
9
|
+
<rel-tab id="basicTabs"></rel-tab>
|
|
10
|
+
<script>
|
|
11
|
+
const tabs = document.getElementById('basicTabs');
|
|
12
|
+
tabs.tabs = [
|
|
13
|
+
{ label: 'Overview' },
|
|
14
|
+
{ label: 'Details' },
|
|
15
|
+
{ label: 'Settings' }
|
|
16
|
+
];
|
|
17
|
+
tabs.activeIndex = 0;
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<!-- With badges (notification counts) -->
|
|
21
|
+
<rel-tab id="badgeTabs"></rel-tab>
|
|
22
|
+
<script>
|
|
23
|
+
const badgeTabs = document.getElementById('badgeTabs');
|
|
24
|
+
badgeTabs.tabs = [
|
|
25
|
+
{ label: 'Inbox', badge: 5 },
|
|
26
|
+
{ label: 'Drafts', badge: 2 },
|
|
27
|
+
{ label: 'Sent' }
|
|
28
|
+
];
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<!-- With disabled tab -->
|
|
32
|
+
<rel-tab id="disabledTabs"></rel-tab>
|
|
33
|
+
<script>
|
|
34
|
+
const disabledTabs = document.getElementById('disabledTabs');
|
|
35
|
+
disabledTabs.tabs = [
|
|
36
|
+
{ label: 'Available' },
|
|
37
|
+
{ label: 'Coming Soon', disabled: true },
|
|
38
|
+
{ label: 'Active' }
|
|
39
|
+
];
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<!-- Listen for tab changes -->
|
|
43
|
+
<rel-tab id="tabsWithListener"></rel-tab>
|
|
44
|
+
<script>
|
|
45
|
+
const listener = document.getElementById('tabsWithListener');
|
|
46
|
+
listener.tabs = [
|
|
47
|
+
{ label: 'Tab 1' },
|
|
48
|
+
{ label: 'Tab 2' },
|
|
49
|
+
{ label: 'Tab 3' }
|
|
50
|
+
];
|
|
51
|
+
listener.addEventListener('tab-change', (e) => {
|
|
52
|
+
console.log('Active tab index:', e.detail);
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<!-- Tab content controlled externally -->
|
|
57
|
+
<rel-tab id="contentTabs" activeIndex="0"></rel-tab>
|
|
58
|
+
<div id="tabContent">
|
|
59
|
+
<div class="panel" data-index="0">Content for Tab 1</div>
|
|
60
|
+
<div class="panel" data-index="1" style="display:none;">Content for Tab 2</div>
|
|
61
|
+
<div class="panel" data-index="2" style="display:none;">Content for Tab 3</div>
|
|
62
|
+
</div>
|
|
63
|
+
<script>
|
|
64
|
+
const contentTabs = document.getElementById('contentTabs');
|
|
65
|
+
contentTabs.tabs = [
|
|
66
|
+
{ label: 'Tab 1' },
|
|
67
|
+
{ label: 'Tab 2' },
|
|
68
|
+
{ label: 'Tab 3' }
|
|
69
|
+
];
|
|
70
|
+
contentTabs.addEventListener('tab-change', (e) => {
|
|
71
|
+
document.querySelectorAll('#tabContent .panel').forEach((panel, i) => {
|
|
72
|
+
panel.style.display = i === e.detail ? 'block' : 'none';
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API
|
|
79
|
+
|
|
80
|
+
### Attributes/Properties
|
|
81
|
+
| Name | Type | Default | Description |
|
|
82
|
+
|------|------|---------|-------------|
|
|
83
|
+
| `tabs` | `TabItem[]` | `[]` | Array of tab items (see TabItem interface below) |
|
|
84
|
+
| `activeIndex` (reflected) | `number` | `0` | Index of the currently active tab |
|
|
85
|
+
|
|
86
|
+
### TabItem Interface
|
|
87
|
+
```typescript
|
|
88
|
+
interface TabItem {
|
|
89
|
+
label: string; // Display text
|
|
90
|
+
badge?: number; // Optional notification count
|
|
91
|
+
disabled?: boolean; // Disables interaction
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Events
|
|
96
|
+
| Event | Detail | Description |
|
|
97
|
+
|-------|--------|-------------|
|
|
98
|
+
| `tab-change` | `number` (the new active index) | Fired when a different tab is selected |
|
|
99
|
+
|
|
100
|
+
## Slots
|
|
101
|
+
| Slot | Description |
|
|
102
|
+
|------|-------------|
|
|
103
|
+
| (default) | Tab panel content (rendered in `.tab-content` wrapper below tabs) |
|
|
104
|
+
|
|
105
|
+
## CSS Parts
|
|
106
|
+
None exposed.
|
|
107
|
+
|
|
108
|
+
## Tokens / CSS Variables
|
|
109
|
+
|
|
110
|
+
### Component-Level Custom Properties
|
|
111
|
+
The component defines CSS custom properties on `:host`:
|
|
112
|
+
|
|
113
|
+
- `--tab-active-color` - Active tab underline color (default: `--rel-colors-green-primary`)
|
|
114
|
+
- `--tab-text-default` - Default tab text color (default: `--rel-text-primary`)
|
|
115
|
+
- `--tab-text-disabled` - Disabled tab text color (default: `--rel-text-disabled`)
|
|
116
|
+
- `--tab-text-hover` - Hover tab text color (default: `--rel-text-heading`)
|
|
117
|
+
- `--tab-underline-height` - Height of active underline (default: `3px`)
|
|
118
|
+
- `--tab-hover-color` - Background color on hover (default: `--rel-surface-surface`)
|
|
119
|
+
|
|
120
|
+
### Design Token References
|
|
121
|
+
- `--rel-fontFamilies-primary` - Font family
|
|
122
|
+
- `--rel-fontSizes-md` - Font size
|
|
123
|
+
- `--rel-fontWeights-medium` - Font weight
|
|
124
|
+
- `--rel-spacing-space-sm`, `--rel-spacing-space-md` - Tab padding
|
|
125
|
+
- `--rel-spacing-space-2xs` - Gap between label and badge
|
|
126
|
+
- `--rel-border-tab` - Bottom border color (default: `#c4c4c4`)
|
|
127
|
+
- `--rel-border-info` - Focus outline color
|
|
128
|
+
- `--rel-borders-radius-border-radius-100` - Focus outline border radius
|
|
129
|
+
- `--rel-colors-red-primary` - Badge background color
|
|
130
|
+
- `--rel-colors-white` - Badge text color
|
|
131
|
+
|
|
132
|
+
## Accessibility
|
|
133
|
+
- Uses `role="tablist"` on container and `role="tab"` on each tab
|
|
134
|
+
- Tabs have `aria-selected` and `aria-disabled` attributes
|
|
135
|
+
- Implements roving tabindex (only active tab is focusable via Tab key)
|
|
136
|
+
- Arrow key navigation between tabs
|
|
137
|
+
- Focus visible states with blue outline
|
|
138
|
+
|
|
139
|
+
### Keyboard Interactions
|
|
140
|
+
- `Tab` - Focus the active tab (or first non-disabled tab)
|
|
141
|
+
- `Arrow Right` - Navigate to next tab (wraps to first, skips disabled)
|
|
142
|
+
- `Arrow Left` - Navigate to previous tab (wraps to last, skips disabled)
|
|
143
|
+
- `Enter / Space` - Activate focused tab
|
|
144
|
+
|
|
145
|
+
### Focus Behavior
|
|
146
|
+
- Focus outline: 2px blue with -2px offset (inside boundary)
|
|
147
|
+
- Border radius applied to focus outline
|
|
148
|
+
- Focus automatically moves when navigating with arrow keys
|
|
149
|
+
|
|
150
|
+
## Notes / Edge Cases
|
|
151
|
+
- The **active tab** has a colored underline (3px high by default) at the bottom
|
|
152
|
+
- **Hover effect** (non-active, non-disabled): light gray background
|
|
153
|
+
- **Badges** are circular (16px height, min 16px width) with red background and white text
|
|
154
|
+
- Clicking a disabled tab does nothing (no event dispatched, no index change)
|
|
155
|
+
- Arrow key navigation wraps around (after last tab, goes to first, and vice versa)
|
|
156
|
+
- Arrow key navigation skips disabled tabs
|
|
157
|
+
- When a tab is activated via arrow keys, focus is programmatically moved to that tab
|
|
158
|
+
- The component includes a `.tab-content` wrapper (with 1rem vertical padding) that renders slotted content
|
|
159
|
+
- The `.tab-content` is always visible; consumers must manually show/hide panel content based on `activeIndex`
|
|
160
|
+
- Tabs have a bottom border (1px solid) via `border-bottom` on `.tabs-wrapper`
|
|
161
|
+
- Active tab underline (`position: absolute`, `bottom: 0`) overlays the border
|
|
162
|
+
- Transitions: color (0.2s), background-color (0.2s), transform (0.2s) for smooth interactions
|
|
163
|
+
- The `activeIndex` property is reflected in HTML as an attribute
|