@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,289 @@
|
|
|
1
|
+
# rel-nav-menu
|
|
2
|
+
|
|
3
|
+
A horizontal navigation menu component with dropdown panels, perfect for top-level site navigation. Features keyboard navigation, accessible ARIA attributes, and token-driven styling.
|
|
4
|
+
|
|
5
|
+
## What It Is
|
|
6
|
+
|
|
7
|
+
The navigation menu consists of three custom elements:
|
|
8
|
+
- `<rel-nav-menu>` - Root container that manages navigation state and interactions
|
|
9
|
+
- `<rel-nav-menu-item>` - Individual navigation items (can be links or dropdown triggers)
|
|
10
|
+
- `<rel-nav-menu-panel>` - Styled container for dropdown content
|
|
11
|
+
|
|
12
|
+
## Basic Usage
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<rel-nav-menu>
|
|
16
|
+
<rel-nav-menu-item label="Features" href="#features"></rel-nav-menu-item>
|
|
17
|
+
<rel-nav-menu-item label="Pricing" href="#pricing"></rel-nav-menu-item>
|
|
18
|
+
<rel-nav-menu-item label="About" href="#about"></rel-nav-menu-item>
|
|
19
|
+
<rel-nav-menu-item label="Contact" href="#contact"></rel-nav-menu-item>
|
|
20
|
+
</rel-nav-menu>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Dropdown with Content
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<rel-nav-menu>
|
|
27
|
+
<rel-nav-menu-item label="Getting started">
|
|
28
|
+
<rel-nav-menu-panel>
|
|
29
|
+
<a href="#intro" data-nav-item>
|
|
30
|
+
<div data-title>Introduction</div>
|
|
31
|
+
<div data-desc>Re-usable components built with Tailwind CSS.</div>
|
|
32
|
+
</a>
|
|
33
|
+
<a href="#install" data-nav-item>
|
|
34
|
+
<div data-title>Installation</div>
|
|
35
|
+
<div data-desc>How to install dependencies and structure your app.</div>
|
|
36
|
+
</a>
|
|
37
|
+
<a href="#typography" data-nav-item>
|
|
38
|
+
<div data-title>Typography</div>
|
|
39
|
+
<div data-desc>Styles for headings, paragraphs, lists...</div>
|
|
40
|
+
</a>
|
|
41
|
+
</rel-nav-menu-panel>
|
|
42
|
+
</rel-nav-menu-item>
|
|
43
|
+
|
|
44
|
+
<rel-nav-menu-item label="Components">
|
|
45
|
+
<rel-nav-menu-panel>
|
|
46
|
+
<a href="#accordion" data-nav-item>
|
|
47
|
+
<div data-title>Accordion</div>
|
|
48
|
+
<div data-desc>Vertically stacked interactive headings.</div>
|
|
49
|
+
</a>
|
|
50
|
+
<a href="#alert" data-nav-item>
|
|
51
|
+
<div data-title>Alert Dialog</div>
|
|
52
|
+
<div data-desc>Modal dialog that interrupts the user.</div>
|
|
53
|
+
</a>
|
|
54
|
+
</rel-nav-menu-panel>
|
|
55
|
+
</rel-nav-menu-item>
|
|
56
|
+
|
|
57
|
+
<rel-nav-menu-item label="Docs" href="#docs"></rel-nav-menu-item>
|
|
58
|
+
</rel-nav-menu>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Simple Dropdown List
|
|
62
|
+
|
|
63
|
+
For simpler dropdowns without titles and descriptions:
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<rel-nav-menu>
|
|
67
|
+
<rel-nav-menu-item label="Status">
|
|
68
|
+
<rel-nav-menu-panel>
|
|
69
|
+
<a href="#backlog" data-nav-item>Backlog</a>
|
|
70
|
+
<a href="#todo" data-nav-item>To Do</a>
|
|
71
|
+
<a href="#in-progress" data-nav-item>In Progress</a>
|
|
72
|
+
<a href="#done" data-nav-item>Done</a>
|
|
73
|
+
</rel-nav-menu-panel>
|
|
74
|
+
</rel-nav-menu-item>
|
|
75
|
+
</rel-nav-menu>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API Reference
|
|
79
|
+
|
|
80
|
+
### rel-nav-menu
|
|
81
|
+
|
|
82
|
+
Root navigation container.
|
|
83
|
+
|
|
84
|
+
**Attributes:**
|
|
85
|
+
- `open` (string) - ID of currently open item (reflects to attribute)
|
|
86
|
+
- `disabled` (boolean) - Disables all interactions
|
|
87
|
+
- `density` ("comfortable" | "compact") - Spacing density, default: "comfortable"
|
|
88
|
+
|
|
89
|
+
**Events:**
|
|
90
|
+
- `rel-nav-open-change` - Emitted when open state changes
|
|
91
|
+
```typescript
|
|
92
|
+
detail: { openId: string }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Example:**
|
|
96
|
+
```javascript
|
|
97
|
+
const navMenu = document.querySelector('rel-nav-menu');
|
|
98
|
+
navMenu.addEventListener('rel-nav-open-change', (e) => {
|
|
99
|
+
console.log('Open item:', e.detail.openId);
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### rel-nav-menu-item
|
|
104
|
+
|
|
105
|
+
Individual navigation item (link or dropdown trigger).
|
|
106
|
+
|
|
107
|
+
**Attributes:**
|
|
108
|
+
- `label` (string, required) - Text displayed on the trigger
|
|
109
|
+
- `href` (string, optional) - If provided, renders as link (no dropdown)
|
|
110
|
+
- `item-id` (string, optional) - Stable identifier; auto-generated from label if not provided
|
|
111
|
+
- `disabled` (boolean) - Disables this item
|
|
112
|
+
- `icon` (string, optional) - Reserved for future icon support
|
|
113
|
+
|
|
114
|
+
**Behavior:**
|
|
115
|
+
- If `href` is set and no dropdown content → renders as anchor link
|
|
116
|
+
- If dropdown content exists → renders as button with dropdown
|
|
117
|
+
- Auto-generates `item-id` from label if not specified
|
|
118
|
+
|
|
119
|
+
**Slots:**
|
|
120
|
+
- Default slot: dropdown panel content (if any)
|
|
121
|
+
|
|
122
|
+
### rel-nav-menu-panel
|
|
123
|
+
|
|
124
|
+
Styled container for dropdown content.
|
|
125
|
+
|
|
126
|
+
**Usage:**
|
|
127
|
+
Place inside `<rel-nav-menu-item>` to create dropdown content.
|
|
128
|
+
|
|
129
|
+
**Content Structure:**
|
|
130
|
+
Use data attributes for styling:
|
|
131
|
+
- `data-nav-item` - On anchor/link elements for hover styling
|
|
132
|
+
- `data-title` - For the main title text
|
|
133
|
+
- `data-desc` - For the description text
|
|
134
|
+
|
|
135
|
+
## Keyboard Navigation
|
|
136
|
+
|
|
137
|
+
Full keyboard accessibility support:
|
|
138
|
+
|
|
139
|
+
| Key | Action |
|
|
140
|
+
|-----|--------|
|
|
141
|
+
| `Tab` / `Shift+Tab` | Navigate through triggers |
|
|
142
|
+
| `ArrowLeft` | Move focus to previous trigger |
|
|
143
|
+
| `ArrowRight` | Move focus to next trigger |
|
|
144
|
+
| `Enter` / `Space` | Open/close focused dropdown (if it has content) |
|
|
145
|
+
| `ArrowDown` | Move focus into open dropdown panel (first focusable element) |
|
|
146
|
+
| `Escape` | Close open dropdown and return focus to trigger |
|
|
147
|
+
|
|
148
|
+
## Interaction Behavior
|
|
149
|
+
|
|
150
|
+
### Mouse Interactions
|
|
151
|
+
- **Click** a trigger to toggle its dropdown
|
|
152
|
+
- **Click** another trigger to switch dropdowns
|
|
153
|
+
- **Click outside** to close dropdown
|
|
154
|
+
- **Hover switching**: When a dropdown is open, hovering another trigger switches to it
|
|
155
|
+
|
|
156
|
+
### Accessibility
|
|
157
|
+
- Root has `role="navigation"`
|
|
158
|
+
- Dropdown triggers have `aria-haspopup="menu"`, `aria-expanded`, and `aria-controls`
|
|
159
|
+
- Panels have matching `id` attributes
|
|
160
|
+
- Disabled items are marked with `aria-disabled`
|
|
161
|
+
- Focus management with visible focus indicators
|
|
162
|
+
|
|
163
|
+
## Styling Tokens
|
|
164
|
+
|
|
165
|
+
The component uses design tokens from the Reliability Design System:
|
|
166
|
+
|
|
167
|
+
### Colors
|
|
168
|
+
- `--rel-colors-gray-light` - Nav container background
|
|
169
|
+
- `--rel-colors-gray-background` - Trigger hover/active state, panel item hover
|
|
170
|
+
- `--rel-colors-gray-border` - Borders
|
|
171
|
+
- `--rel-colors-gray-heading` - Text color
|
|
172
|
+
- `--rel-colors-gray-placeholder` - Description text
|
|
173
|
+
- `--rel-colors-white` - Panel background
|
|
174
|
+
- `--rel-colors-blue-primary` - Focus outline
|
|
175
|
+
|
|
176
|
+
### Typography
|
|
177
|
+
- `--rel-fontFamilies-primary` - Font family (Poppins)
|
|
178
|
+
- `--rel-fontWeights-medium` - Trigger text weight
|
|
179
|
+
- `--rel-fontWeights-semibold` - Panel item title weight
|
|
180
|
+
- `--rel-fontSizes-sm` - Trigger and title text size
|
|
181
|
+
- `--rel-fontSizes-xs` - Description text size
|
|
182
|
+
- `--rel-lineHeights-xs` - Description line height
|
|
183
|
+
|
|
184
|
+
### Spacing
|
|
185
|
+
- `--rel-spacing-space-3xs` (2px) - Compact spacing
|
|
186
|
+
- `--rel-spacing-space-2xs` (4px) - Container padding, panel gaps
|
|
187
|
+
- `--rel-spacing-space-xs` (8px) - Trigger padding, panel padding
|
|
188
|
+
- `--rel-spacing-space-sm` (12px) - Panel item padding
|
|
189
|
+
- `--rel-spacing-space-md` (16px) - Trigger horizontal padding
|
|
190
|
+
|
|
191
|
+
### Borders & Effects
|
|
192
|
+
- `--rel-borders-width-stroke-100` (1px) - Border width
|
|
193
|
+
- `--rel-borders-radius-border-radius-100` (2px) - Panel item radius
|
|
194
|
+
- `--rel-borders-radius-border-radius-200` (8px) - Container and panel radius
|
|
195
|
+
- `--rel-shadows-shadow-300` - Panel drop shadow
|
|
196
|
+
|
|
197
|
+
### Z-Index
|
|
198
|
+
- Dropdown panels: `z-index: 1000`
|
|
199
|
+
|
|
200
|
+
## Positioning
|
|
201
|
+
|
|
202
|
+
Dropdown panels use fixed positioning with smart collision detection:
|
|
203
|
+
- **Default**: Opens below trigger, start-aligned
|
|
204
|
+
- **Right overflow**: Shifts left to fit viewport
|
|
205
|
+
- **Bottom overflow**: Flips above trigger if more space available
|
|
206
|
+
|
|
207
|
+
## Complete Example
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<rel-nav-menu id="main-nav">
|
|
211
|
+
<rel-nav-menu-item label="Getting started">
|
|
212
|
+
<rel-nav-menu-panel>
|
|
213
|
+
<a href="#intro" data-nav-item>
|
|
214
|
+
<div data-title>Introduction</div>
|
|
215
|
+
<div data-desc>Re-usable components built using Radix UI and Tailwind CSS.</div>
|
|
216
|
+
</a>
|
|
217
|
+
<a href="#installation" data-nav-item>
|
|
218
|
+
<div data-title>Installation</div>
|
|
219
|
+
<div data-desc>How to install dependencies and structure your app.</div>
|
|
220
|
+
</a>
|
|
221
|
+
<a href="#typography" data-nav-item>
|
|
222
|
+
<div data-title>Typography</div>
|
|
223
|
+
<div data-desc>Styles for headings, paragraphs, lists, and more.</div>
|
|
224
|
+
</a>
|
|
225
|
+
</rel-nav-menu-panel>
|
|
226
|
+
</rel-nav-menu-item>
|
|
227
|
+
|
|
228
|
+
<rel-nav-menu-item label="Components">
|
|
229
|
+
<rel-nav-menu-panel>
|
|
230
|
+
<a href="#accordion" data-nav-item>
|
|
231
|
+
<div data-title>Accordion</div>
|
|
232
|
+
<div data-desc>A vertically stacked set of interactive headings.</div>
|
|
233
|
+
</a>
|
|
234
|
+
<a href="#alert-dialog" data-nav-item>
|
|
235
|
+
<div data-title>Alert Dialog</div>
|
|
236
|
+
<div data-desc>A modal dialog that interrupts the user.</div>
|
|
237
|
+
</a>
|
|
238
|
+
<a href="#avatar" data-nav-item>
|
|
239
|
+
<div data-title>Avatar</div>
|
|
240
|
+
<div data-desc>An image element with a fallback.</div>
|
|
241
|
+
</a>
|
|
242
|
+
</rel-nav-menu-panel>
|
|
243
|
+
</rel-nav-menu-item>
|
|
244
|
+
|
|
245
|
+
<rel-nav-menu-item label="Status">
|
|
246
|
+
<rel-nav-menu-panel>
|
|
247
|
+
<a href="#backlog" data-nav-item>Backlog</a>
|
|
248
|
+
<a href="#todo" data-nav-item>To Do</a>
|
|
249
|
+
<a href="#in-progress" data-nav-item>In Progress</a>
|
|
250
|
+
<a href="#done" data-nav-item>Done</a>
|
|
251
|
+
</rel-nav-menu-panel>
|
|
252
|
+
</rel-nav-menu-item>
|
|
253
|
+
|
|
254
|
+
<rel-nav-menu-item label="Documentation" href="#docs"></rel-nav-menu-item>
|
|
255
|
+
</rel-nav-menu>
|
|
256
|
+
|
|
257
|
+
<script>
|
|
258
|
+
const navMenu = document.getElementById('main-nav');
|
|
259
|
+
navMenu.addEventListener('rel-nav-open-change', (e) => {
|
|
260
|
+
console.log('Navigation state:', e.detail.openId);
|
|
261
|
+
});
|
|
262
|
+
</script>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Browser Support
|
|
266
|
+
|
|
267
|
+
Compatible with all modern browsers supporting:
|
|
268
|
+
- Web Components (Custom Elements v1)
|
|
269
|
+
- Shadow DOM
|
|
270
|
+
- ES2020+ features
|
|
271
|
+
|
|
272
|
+
## Framework Integration
|
|
273
|
+
|
|
274
|
+
Works seamlessly with:
|
|
275
|
+
- Vanilla JavaScript
|
|
276
|
+
- Angular
|
|
277
|
+
- React (via wrapper)
|
|
278
|
+
- Vue
|
|
279
|
+
- Any framework supporting Web Components
|
|
280
|
+
|
|
281
|
+
## Comparison with Menubar
|
|
282
|
+
|
|
283
|
+
While `rel-menubar` is designed for application menus (File/Edit/View), `rel-nav-menu` is optimized for:
|
|
284
|
+
- **Website navigation** - Top-level site sections
|
|
285
|
+
- **Content-rich dropdowns** - With titles, descriptions, and icons
|
|
286
|
+
- **Flexible layouts** - Horizontal navigation bars
|
|
287
|
+
- **Marketing sites** - Product features, documentation, etc.
|
|
288
|
+
|
|
289
|
+
Use `rel-menubar` for desktop application UIs, use `rel-nav-menu` for website navigation.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-nav-menu-item")
|
|
5
|
+
export class RelNavMenuItem extends LitElement {
|
|
6
|
+
@property({ type: String }) label = "";
|
|
7
|
+
@property({ type: String }) href = "";
|
|
8
|
+
@property({ type: String, attribute: "item-id" }) itemId = "";
|
|
9
|
+
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
10
|
+
@property({ type: String }) icon = "";
|
|
11
|
+
|
|
12
|
+
@state() private hasContent = false;
|
|
13
|
+
|
|
14
|
+
static styles = css`
|
|
15
|
+
:host {
|
|
16
|
+
display: inline-block;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.trigger {
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: var(--rel-spacing-space-2xs);
|
|
23
|
+
padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-md);
|
|
24
|
+
font-family: var(--rel-fontFamilies-primary);
|
|
25
|
+
font-size: var(--rel-fontSizes-sm);
|
|
26
|
+
font-weight: var(--rel-fontWeights-medium);
|
|
27
|
+
color: var(--rel-colors-gray-heading);
|
|
28
|
+
background: transparent;
|
|
29
|
+
border: none;
|
|
30
|
+
border-radius: var(--rel-borders-radius-border-radius-200);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
transition: all 0.15s ease;
|
|
33
|
+
text-decoration: none;
|
|
34
|
+
outline: none;
|
|
35
|
+
user-select: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.trigger:hover:not(:disabled) {
|
|
39
|
+
background: var(--rel-surface-secondary, #fafafa);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.trigger:focus-visible {
|
|
43
|
+
outline: 2px solid var(--rel-colors-blue-primary);
|
|
44
|
+
outline-offset: 2px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.trigger.active {
|
|
48
|
+
background: var(--rel-surface-secondary, #fafafa);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.trigger:disabled {
|
|
52
|
+
opacity: 0.5;
|
|
53
|
+
cursor: not-allowed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:host([disabled]) .trigger {
|
|
57
|
+
opacity: 0.5;
|
|
58
|
+
cursor: not-allowed;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.content {
|
|
62
|
+
display: none;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
connectedCallback() {
|
|
67
|
+
super.connectedCallback();
|
|
68
|
+
// Generate item-id from label if not provided
|
|
69
|
+
if (!this.itemId && this.label) {
|
|
70
|
+
this.itemId = this.label.toLowerCase().replace(/\s+/g, "-");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
firstUpdated() {
|
|
75
|
+
// Check if there's any slotted content
|
|
76
|
+
const slot = this.shadowRoot?.querySelector("slot");
|
|
77
|
+
if (slot) {
|
|
78
|
+
const nodes = slot.assignedNodes({ flatten: true });
|
|
79
|
+
this.hasContent = nodes.some(
|
|
80
|
+
(node) => node.nodeType === Node.ELEMENT_NODE || node.textContent?.trim()
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private handleSlotChange() {
|
|
86
|
+
const slot = this.shadowRoot?.querySelector("slot");
|
|
87
|
+
if (slot) {
|
|
88
|
+
const nodes = slot.assignedNodes({ flatten: true });
|
|
89
|
+
this.hasContent = nodes.some(
|
|
90
|
+
(node) => node.nodeType === Node.ELEMENT_NODE || node.textContent?.trim()
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get triggerId() {
|
|
96
|
+
return `nav-trigger-${this.itemId}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get panelId() {
|
|
100
|
+
return `nav-panel-${this.itemId}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
render() {
|
|
104
|
+
const isLink = !!this.href && !this.hasContent;
|
|
105
|
+
const tagName = isLink ? "a" : "button";
|
|
106
|
+
const attrs = isLink
|
|
107
|
+
? { href: this.href }
|
|
108
|
+
: {
|
|
109
|
+
type: "button",
|
|
110
|
+
"aria-haspopup": this.hasContent ? "menu" : undefined,
|
|
111
|
+
"aria-controls": this.hasContent ? this.panelId : undefined,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return html`
|
|
115
|
+
${isLink
|
|
116
|
+
? html`
|
|
117
|
+
<a
|
|
118
|
+
class="trigger"
|
|
119
|
+
href="${this.href}"
|
|
120
|
+
id="${this.triggerId}"
|
|
121
|
+
?disabled="${this.disabled}"
|
|
122
|
+
>
|
|
123
|
+
${this.label}
|
|
124
|
+
</a>
|
|
125
|
+
`
|
|
126
|
+
: html`
|
|
127
|
+
<button
|
|
128
|
+
class="trigger"
|
|
129
|
+
id="${this.triggerId}"
|
|
130
|
+
type="button"
|
|
131
|
+
?disabled="${this.disabled}"
|
|
132
|
+
aria-haspopup="${this.hasContent ? "menu" : "false"}"
|
|
133
|
+
aria-controls="${this.hasContent ? this.panelId : ""}"
|
|
134
|
+
>
|
|
135
|
+
${this.label}
|
|
136
|
+
</button>
|
|
137
|
+
`}
|
|
138
|
+
<div class="content">
|
|
139
|
+
<slot @slotchange="${this.handleSlotChange}"></slot>
|
|
140
|
+
</div>
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-nav-menu-panel")
|
|
5
|
+
export class RelNavMenuPanel extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
:host {
|
|
8
|
+
display: block;
|
|
9
|
+
width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.panel-content {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: var(--rel-spacing-space-2xs);
|
|
16
|
+
padding: var(--rel-spacing-space-xs);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
::slotted(a[data-nav-item]) {
|
|
20
|
+
display: block;
|
|
21
|
+
padding: var(--rel-spacing-space-sm);
|
|
22
|
+
border-radius: var(--rel-borders-radius-border-radius-100);
|
|
23
|
+
transition: background 0.15s ease;
|
|
24
|
+
text-decoration: none;
|
|
25
|
+
color: inherit;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
::slotted(a[data-nav-item]:hover) {
|
|
29
|
+
background: var(--rel-surface-secondary, #fafafa);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
::slotted([data-title]) {
|
|
33
|
+
font-size: var(--rel-fontSizes-sm);
|
|
34
|
+
font-weight: var(--rel-fontWeights-semibold);
|
|
35
|
+
color: var(--rel-colors-gray-heading);
|
|
36
|
+
margin-bottom: var(--rel-spacing-space-3xs);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
::slotted([data-desc]) {
|
|
40
|
+
font-size: var(--rel-fontSizes-xs);
|
|
41
|
+
color: var(--rel-colors-gray-placeholder);
|
|
42
|
+
line-height: var(--rel-lineHeights-xs);
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
render() {
|
|
47
|
+
return html`
|
|
48
|
+
<div class="panel-content">
|
|
49
|
+
<slot></slot>
|
|
50
|
+
</div>
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
}
|