@reliability-design/web 1.0.3 → 1.0.6
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 +43 -16
- package/dist/components/rel-alert/README.md +141 -0
- package/dist/components/rel-alert/rel-alert.ts +18 -7
- package/dist/components/rel-avatar/README.md +119 -0
- package/dist/components/rel-badge/README.md +86 -0
- package/dist/components/rel-badge/rel-badge.ts +6 -6
- 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-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 +469 -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 +18 -10
- package/dist/components/rel-chip/README.md +114 -0
- package/dist/components/rel-chip/rel-chip.ts +20 -4
- 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-drawer/README.md +200 -0
- package/dist/components/rel-drawer/index.ts +1 -0
- package/dist/components/rel-drawer/rel-drawer.ts +621 -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 +18 -10
- 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 +576 -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 +143 -0
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +53 -0
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +331 -0
- package/dist/components/rel-navbar/README.md +126 -0
- package/dist/components/rel-navbar/rel-navbar.ts +91 -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 +376 -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 +327 -0
- package/dist/components/rel-progress/README.md +110 -0
- package/dist/components/rel-progress/rel-progress.ts +32 -5
- package/dist/components/rel-quote-page/README.md +166 -0
- package/dist/components/rel-quote-page/rel-quote-page.ts +816 -0
- package/dist/components/rel-radio/README.md +120 -0
- package/dist/components/rel-radio/rel-radio.ts +13 -5
- 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 +149 -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 +14 -14
- package/dist/components/rel-sheet/README.md +224 -0
- package/dist/components/rel-sheet/rel-sheet.ts +683 -0
- package/dist/components/rel-sidebar/README.md +209 -0
- package/dist/components/rel-sidebar/rel-sidebar.ts +1158 -0
- package/dist/components/rel-slider/README.md +135 -0
- package/dist/components/rel-slider/rel-slider.ts +19 -7
- package/dist/components/rel-snackbar/README.md +134 -0
- package/dist/components/rel-snackbar/rel-snackbar.ts +67 -2
- package/dist/components/rel-stepper/README.md +160 -0
- package/dist/components/rel-stepper/rel-stepper.ts +6 -6
- package/dist/components/rel-switch/README.md +102 -0
- package/dist/components/rel-switch/rel-switch.ts +15 -6
- package/dist/components/rel-tab/README.md +163 -0
- package/dist/components/rel-tab/rel-tab.ts +36 -14
- package/dist/components/rel-table/README.md +178 -0
- package/dist/components/rel-table/rel-table-body.ts +16 -0
- package/dist/components/rel-table/rel-table-caption.ts +22 -0
- package/dist/components/rel-table/rel-table-cell.ts +49 -0
- package/dist/components/rel-table/rel-table-footer.ts +18 -0
- package/dist/components/rel-table/rel-table-head.ts +33 -0
- package/dist/components/rel-table/rel-table-header.ts +16 -0
- package/dist/components/rel-table/rel-table-row.ts +32 -0
- package/dist/components/rel-table/rel-table.ts +47 -0
- package/dist/components/rel-textfield/README.md +90 -0
- package/dist/components/rel-textfield/rel-textfield.ts +35 -19
- package/dist/components/rel-toast/README.md +148 -0
- package/dist/components/rel-toast/rel-toast.ts +32 -14
- package/dist/components/rel-tooltip/README.md +103 -0
- package/dist/components/rel-tooltip/rel-tooltip.ts +9 -9
- package/dist/index.d.mts +836 -1
- package/dist/index.d.ts +836 -1
- package/dist/index.js +10658 -1493
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10616 -1499
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +293 -183
- package/dist/{vars-6ZIUPIU4.css → vars-ITL2K3KO.css} +293 -183
- package/package.json +1 -1
|
@@ -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
|
|
@@ -11,11 +11,11 @@ export class RelSwitch extends LitElement {
|
|
|
11
11
|
|
|
12
12
|
static styles = css`
|
|
13
13
|
:host {
|
|
14
|
-
--switch-track:
|
|
15
|
-
--switch-knob-on:
|
|
16
|
-
--switch-knob-off:
|
|
17
|
-
--switch-halo:
|
|
18
|
-
--switch-disabled:
|
|
14
|
+
--switch-track: var(--rel-text-placeholder);
|
|
15
|
+
--switch-knob-on: var(--rel-colors-black);
|
|
16
|
+
--switch-knob-off: var(--rel-colors-white);
|
|
17
|
+
--switch-halo: var(--rel-border-default);
|
|
18
|
+
--switch-disabled: var(--rel-text-disabled);
|
|
19
19
|
display: inline-block;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -77,7 +77,7 @@ export class RelSwitch extends LitElement {
|
|
|
77
77
|
|
|
78
78
|
/* Hover */
|
|
79
79
|
.switch-wrapper:hover:not(.switch-disabled) .switch-slider {
|
|
80
|
-
background-color:
|
|
80
|
+
background-color: var(--rel-text-dark-placeholder);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/* Focus halo */
|
|
@@ -100,6 +100,13 @@ export class RelSwitch extends LitElement {
|
|
|
100
100
|
.switch-disabled .switch-slider::before {
|
|
101
101
|
background-color: var(--switch-disabled);
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce) {
|
|
105
|
+
.switch-slider,
|
|
106
|
+
.switch-slider::before {
|
|
107
|
+
transition: none;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
103
110
|
`;
|
|
104
111
|
|
|
105
112
|
private toggle(ev: Event) {
|
|
@@ -148,6 +155,8 @@ export class RelSwitch extends LitElement {
|
|
|
148
155
|
<label class="${wrapperClasses}">
|
|
149
156
|
<input
|
|
150
157
|
type="checkbox"
|
|
158
|
+
role="switch"
|
|
159
|
+
aria-checked="${this.checked}"
|
|
151
160
|
?disabled=${this.disabled}
|
|
152
161
|
.checked=${this.checked}
|
|
153
162
|
@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
|
|
@@ -14,34 +14,40 @@ export class RelTab extends LitElement {
|
|
|
14
14
|
|
|
15
15
|
static styles = css`
|
|
16
16
|
:host {
|
|
17
|
-
--tab-active-color:
|
|
18
|
-
--tab-text-default:
|
|
19
|
-
--tab-text-disabled:
|
|
20
|
-
--tab-text-hover:
|
|
17
|
+
--tab-active-color: var(--rel-colors-green-primary);
|
|
18
|
+
--tab-text-default: var(--rel-text-primary);
|
|
19
|
+
--tab-text-disabled: var(--rel-text-disabled);
|
|
20
|
+
--tab-text-hover: var(--rel-text-heading);
|
|
21
21
|
--tab-underline-height: 3px;
|
|
22
|
-
--tab-hover-color:
|
|
22
|
+
--tab-hover-color: var(--rel-surface-surface);
|
|
23
23
|
display: block;
|
|
24
24
|
font-family: var(--rel-fontFamilies-primary);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
.tabs-wrapper {
|
|
28
28
|
display: flex;
|
|
29
|
-
border-bottom: 1px solid #
|
|
30
|
-
font-family: var(--rel-fontFamilies-primary);
|
|
29
|
+
border-bottom: 1px solid var(--rel-border-tab, #c4c4c4);
|
|
30
|
+
font-family: var(--rel-fontFamilies-primary, sans-serif);
|
|
31
31
|
user-select: none;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.tab {
|
|
35
35
|
position: relative;
|
|
36
|
-
padding:
|
|
36
|
+
padding: var(--rel-spacing-space-sm, 12px) var(--rel-spacing-space-md, 16px);
|
|
37
37
|
cursor: pointer;
|
|
38
38
|
color: var(--tab-text-default);
|
|
39
|
-
font-size:
|
|
40
|
-
font-weight: 500;
|
|
39
|
+
font-size: var(--rel-fontSizes-md, 14px);
|
|
40
|
+
font-weight: var(--rel-fontWeights-medium, 500);
|
|
41
41
|
transition: color 0.2s ease;
|
|
42
42
|
display: flex;
|
|
43
43
|
align-items: center;
|
|
44
|
-
gap:
|
|
44
|
+
gap: var(--rel-spacing-space-2xs, 4px);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tab:focus-visible {
|
|
48
|
+
outline: 2px solid var(--rel-border-info, #0091ea);
|
|
49
|
+
outline-offset: -2px;
|
|
50
|
+
border-radius: var(--rel-borders-radius-border-radius-100, 2px);
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
.tab:hover:not(.disabled) .tab-label {
|
|
@@ -77,8 +83,8 @@ export class RelTab extends LitElement {
|
|
|
77
83
|
display: inline-flex;
|
|
78
84
|
justify-content: center;
|
|
79
85
|
align-items: center;
|
|
80
|
-
background:
|
|
81
|
-
color:
|
|
86
|
+
background: var(--rel-colors-red-primary);
|
|
87
|
+
color: var(--rel-colors-white);
|
|
82
88
|
border-radius: 50%;
|
|
83
89
|
font-size: 11px;
|
|
84
90
|
font-weight: 600;
|
|
@@ -107,10 +113,26 @@ export class RelTab extends LitElement {
|
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
private onKeyDown(event: KeyboardEvent, index: number) {
|
|
110
|
-
if (this.tabs[index]?.disabled) return;
|
|
111
116
|
if (event.key === "Enter" || event.key === " ") {
|
|
117
|
+
if (this.tabs[index]?.disabled) return;
|
|
112
118
|
event.preventDefault();
|
|
113
119
|
this.selectTab(index);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (event.key === "ArrowRight" || event.key === "ArrowLeft") {
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
const dir = event.key === "ArrowRight" ? 1 : -1;
|
|
126
|
+
let next = index;
|
|
127
|
+
for (let i = 0; i < this.tabs.length; i++) {
|
|
128
|
+
next = (next + dir + this.tabs.length) % this.tabs.length;
|
|
129
|
+
if (!this.tabs[next]?.disabled) break;
|
|
130
|
+
}
|
|
131
|
+
this.selectTab(next);
|
|
132
|
+
this.updateComplete.then(() => {
|
|
133
|
+
const tabEls = this.shadowRoot?.querySelectorAll("[role=tab]");
|
|
134
|
+
(tabEls?.[next] as HTMLElement)?.focus();
|
|
135
|
+
});
|
|
114
136
|
}
|
|
115
137
|
}
|
|
116
138
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# rel-table
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
A simple table wrapper component providing optional border and compact mode styling. Uses slots to accept standard HTML table content (thead, tbody, tfoot). Features horizontal scrolling for overflow and semantic table structure.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
```html
|
|
8
|
+
<!-- Basic table -->
|
|
9
|
+
<rel-table>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th>Name</th>
|
|
13
|
+
<th>Email</th>
|
|
14
|
+
<th>Role</th>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody>
|
|
18
|
+
<tr>
|
|
19
|
+
<td>John Doe</td>
|
|
20
|
+
<td>john@example.com</td>
|
|
21
|
+
<td>Admin</td>
|
|
22
|
+
</tr>
|
|
23
|
+
<tr>
|
|
24
|
+
<td>Jane Smith</td>
|
|
25
|
+
<td>jane@example.com</td>
|
|
26
|
+
<td>User</td>
|
|
27
|
+
</tr>
|
|
28
|
+
</tbody>
|
|
29
|
+
</rel-table>
|
|
30
|
+
|
|
31
|
+
<!-- Bordered table -->
|
|
32
|
+
<rel-table bordered>
|
|
33
|
+
<thead>
|
|
34
|
+
<tr>
|
|
35
|
+
<th>Product</th>
|
|
36
|
+
<th>Price</th>
|
|
37
|
+
<th>Quantity</th>
|
|
38
|
+
</tr>
|
|
39
|
+
</thead>
|
|
40
|
+
<tbody>
|
|
41
|
+
<tr>
|
|
42
|
+
<td>Widget</td>
|
|
43
|
+
<td>$10.00</td>
|
|
44
|
+
<td>5</td>
|
|
45
|
+
</tr>
|
|
46
|
+
<tr>
|
|
47
|
+
<td>Gadget</td>
|
|
48
|
+
<td>$25.00</td>
|
|
49
|
+
<td>2</td>
|
|
50
|
+
</tr>
|
|
51
|
+
</tbody>
|
|
52
|
+
</rel-table>
|
|
53
|
+
|
|
54
|
+
<!-- Compact table -->
|
|
55
|
+
<rel-table compact>
|
|
56
|
+
<thead>
|
|
57
|
+
<tr>
|
|
58
|
+
<th>ID</th>
|
|
59
|
+
<th>Status</th>
|
|
60
|
+
</tr>
|
|
61
|
+
</thead>
|
|
62
|
+
<tbody>
|
|
63
|
+
<tr>
|
|
64
|
+
<td>001</td>
|
|
65
|
+
<td>Active</td>
|
|
66
|
+
</tr>
|
|
67
|
+
<tr>
|
|
68
|
+
<td>002</td>
|
|
69
|
+
<td>Pending</td>
|
|
70
|
+
</tr>
|
|
71
|
+
</tbody>
|
|
72
|
+
</rel-table>
|
|
73
|
+
|
|
74
|
+
<!-- Bordered and compact -->
|
|
75
|
+
<rel-table bordered compact>
|
|
76
|
+
<thead>
|
|
77
|
+
<tr>
|
|
78
|
+
<th>Date</th>
|
|
79
|
+
<th>Event</th>
|
|
80
|
+
</tr>
|
|
81
|
+
</thead>
|
|
82
|
+
<tbody>
|
|
83
|
+
<tr>
|
|
84
|
+
<td>2024-01-15</td>
|
|
85
|
+
<td>Launch</td>
|
|
86
|
+
</tr>
|
|
87
|
+
<tr>
|
|
88
|
+
<td>2024-02-20</td>
|
|
89
|
+
<td>Update</td>
|
|
90
|
+
</tr>
|
|
91
|
+
</tbody>
|
|
92
|
+
</rel-table>
|
|
93
|
+
|
|
94
|
+
<!-- Responsive table with horizontal scroll -->
|
|
95
|
+
<rel-table bordered>
|
|
96
|
+
<thead>
|
|
97
|
+
<tr>
|
|
98
|
+
<th>Column 1</th>
|
|
99
|
+
<th>Column 2</th>
|
|
100
|
+
<th>Column 3</th>
|
|
101
|
+
<th>Column 4</th>
|
|
102
|
+
<th>Column 5</th>
|
|
103
|
+
<th>Column 6</th>
|
|
104
|
+
<th>Column 7</th>
|
|
105
|
+
<th>Column 8</th>
|
|
106
|
+
</tr>
|
|
107
|
+
</thead>
|
|
108
|
+
<tbody>
|
|
109
|
+
<tr>
|
|
110
|
+
<td>Data 1</td>
|
|
111
|
+
<td>Data 2</td>
|
|
112
|
+
<td>Data 3</td>
|
|
113
|
+
<td>Data 4</td>
|
|
114
|
+
<td>Data 5</td>
|
|
115
|
+
<td>Data 6</td>
|
|
116
|
+
<td>Data 7</td>
|
|
117
|
+
<td>Data 8</td>
|
|
118
|
+
</tr>
|
|
119
|
+
</tbody>
|
|
120
|
+
</rel-table>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
### Attributes/Properties
|
|
126
|
+
| Name | Type | Default | Description |
|
|
127
|
+
|------|------|---------|-------------|
|
|
128
|
+
| `bordered` (reflected) | `boolean` | `false` | Adds border and border-radius to the table wrapper |
|
|
129
|
+
| `compact` (reflected) | `boolean` | `false` | Applies compact spacing (currently defined but not styled in component) |
|
|
130
|
+
|
|
131
|
+
## Events
|
|
132
|
+
None.
|
|
133
|
+
|
|
134
|
+
## Slots
|
|
135
|
+
| Slot | Description |
|
|
136
|
+
|------|-------------|
|
|
137
|
+
| (default) | Standard HTML table content (`<thead>`, `<tbody>`, `<tfoot>`, `<tr>`, `<th>`, `<td>`, etc.) |
|
|
138
|
+
|
|
139
|
+
## CSS Parts
|
|
140
|
+
None exposed.
|
|
141
|
+
|
|
142
|
+
## Tokens / CSS Variables
|
|
143
|
+
|
|
144
|
+
### Semantic Tokens
|
|
145
|
+
- `--rel-fontFamilies-primary` - Font family
|
|
146
|
+
- `--rel-fontSizes-md` - Table font size
|
|
147
|
+
|
|
148
|
+
### Color Tokens
|
|
149
|
+
- `--rel-colors-gray-primary` - Table text color
|
|
150
|
+
- `--rel-colors-gray-border` - Border color (when bordered is true)
|
|
151
|
+
|
|
152
|
+
### Border Tokens
|
|
153
|
+
- `--rel-borders-width-stroke-100` - Border width
|
|
154
|
+
- `--rel-borders-radius-border-radius-200` - Table wrapper border radius
|
|
155
|
+
|
|
156
|
+
## Accessibility
|
|
157
|
+
- Uses semantic native `<table>` element for proper screen reader support
|
|
158
|
+
- Ensure slotted table content includes proper `<thead>`, `<th>`, and `scope` attributes
|
|
159
|
+
- `caption-side: bottom` allows captions to be read after table content
|
|
160
|
+
|
|
161
|
+
### Keyboard Interactions
|
|
162
|
+
- Standard browser table navigation
|
|
163
|
+
- Focusable elements within cells can be tabbed to
|
|
164
|
+
|
|
165
|
+
### Focus Behavior
|
|
166
|
+
- No specific focus handling (standard table behavior)
|
|
167
|
+
|
|
168
|
+
## Notes / Edge Cases
|
|
169
|
+
- The component wraps the table in a `.table-wrapper` div with `overflow-x: auto` for horizontal scrolling
|
|
170
|
+
- The inner `<table>` element uses `border-collapse: collapse` for standard table rendering
|
|
171
|
+
- **bordered mode**: Applies a border with `--rel-borders-width-stroke-100` and rounded corners to the wrapper
|
|
172
|
+
- The `bordered` and `compact` attributes are reflected in HTML (appear as actual attributes when set)
|
|
173
|
+
- **compact mode**: Currently reflected but no styling is applied in the component (may be intended for future use or external styling)
|
|
174
|
+
- The table is `width: 100%` by default
|
|
175
|
+
- The component is `display: block` and takes full width of its container
|
|
176
|
+
- The table uses `caption-side: bottom` for any `<caption>` elements
|
|
177
|
+
- You must provide your own cell padding, row styling, and hover effects via global CSS or inline styles
|
|
178
|
+
- The component provides minimal structural styling; most table appearance is controlled by the browser and your custom CSS
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-table-body")
|
|
5
|
+
export class RelTableBody extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
:host {
|
|
8
|
+
display: table-row-group;
|
|
9
|
+
font-family: var(--rel-fontFamilies-primary);
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
render() {
|
|
14
|
+
return html`<slot></slot>`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-table-caption")
|
|
5
|
+
export class RelTableCaption extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
:host {
|
|
8
|
+
display: table-caption;
|
|
9
|
+
caption-side: bottom;
|
|
10
|
+
font-family: var(--rel-fontFamilies-primary);
|
|
11
|
+
margin-top: var(--rel-spacing-space-md);
|
|
12
|
+
padding-top: var(--rel-spacing-space-sm);
|
|
13
|
+
font-size: var(--rel-fontSizes-sm);
|
|
14
|
+
color: var(--rel-colors-gray-placeholder);
|
|
15
|
+
text-align: center;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
return html`<slot></slot>`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-table-cell")
|
|
5
|
+
export class RelTableCell extends LitElement {
|
|
6
|
+
@property({ type: String, reflect: true }) align: "left" | "center" | "right" = "left";
|
|
7
|
+
@property({ type: Number, reflect: true }) colspan?: number;
|
|
8
|
+
|
|
9
|
+
static styles = css`
|
|
10
|
+
:host {
|
|
11
|
+
display: table-cell;
|
|
12
|
+
font-family: var(--rel-fontFamilies-primary);
|
|
13
|
+
padding: var(--rel-spacing-space-sm) var(--rel-spacing-space-md);
|
|
14
|
+
font-size: var(--rel-fontSizes-md);
|
|
15
|
+
color: var(--rel-colors-gray-primary);
|
|
16
|
+
vertical-align: middle;
|
|
17
|
+
text-align: left;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:host([align="center"]) {
|
|
21
|
+
text-align: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:host([align="right"]) {
|
|
25
|
+
text-align: right;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
render() {
|
|
30
|
+
return html`<slot></slot>`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
updated(changedProperties: Map<string, any>) {
|
|
34
|
+
super.updated(changedProperties);
|
|
35
|
+
// Handle colspan attribute
|
|
36
|
+
if (changedProperties.has("colspan") && this.colspan) {
|
|
37
|
+
// Apply colspan to the host element since it's display: table-cell
|
|
38
|
+
this.style.setProperty("--colspan", this.colspan.toString());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
connectedCallback() {
|
|
43
|
+
super.connectedCallback();
|
|
44
|
+
// Set colspan attribute on the host element for table layout
|
|
45
|
+
if (this.colspan) {
|
|
46
|
+
this.setAttribute("colspan", this.colspan.toString());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-table-footer")
|
|
5
|
+
export class RelTableFooter extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
:host {
|
|
8
|
+
display: table-footer-group;
|
|
9
|
+
font-family: var(--rel-fontFamilies-primary);
|
|
10
|
+
font-weight: var(--rel-fontWeights-medium, 500);
|
|
11
|
+
background-color: var(--rel-colors-gray-light);
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
return html`<slot></slot>`;
|
|
17
|
+
}
|
|
18
|
+
}
|