@reliability-design/web 1.4.5 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/CHANGELOG.md +211 -49
  2. package/dist/components/rel-accordion/README.md +132 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +156 -61
  5. package/dist/components/rel-avatar/README.md +116 -130
  6. package/dist/components/rel-badge/README.md +98 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +160 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +184 -166
  13. package/dist/components/rel-checkbox/README.md +93 -75
  14. package/dist/components/rel-chip/README.md +132 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +78 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-menu/README.md +178 -151
  21. package/dist/components/rel-menubar/README.md +218 -329
  22. package/dist/components/rel-otp/README.md +170 -440
  23. package/dist/components/rel-pagination/README.md +100 -424
  24. package/dist/components/rel-popover/README.md +203 -568
  25. package/dist/components/rel-progress/README.md +88 -110
  26. package/dist/components/rel-radio/README.md +131 -135
  27. package/dist/components/rel-range-picker/README.md +162 -0
  28. package/dist/components/rel-resizable/README.md +199 -140
  29. package/dist/components/rel-segmented-button/README.md +155 -141
  30. package/dist/components/rel-select/README.md +154 -93
  31. package/dist/components/rel-sheet/README.md +168 -224
  32. package/dist/components/rel-skeleton/README.md +103 -0
  33. package/dist/components/rel-slider/README.md +130 -135
  34. package/dist/components/rel-snackbar/README.md +162 -134
  35. package/dist/components/rel-stepper/README.md +158 -160
  36. package/dist/components/rel-switch/README.md +108 -102
  37. package/dist/components/rel-tab/README.md +171 -163
  38. package/dist/components/rel-textfield/README.md +159 -69
  39. package/dist/components/rel-time-picker/README.md +122 -157
  40. package/dist/components/rel-toast/README.md +157 -148
  41. package/dist/components/rel-tooltip/README.md +108 -103
  42. package/dist/components/rel-upload/README.md +220 -0
  43. package/dist/index.d.mts +1387 -350
  44. package/dist/index.d.ts +1387 -350
  45. package/dist/index.js +5863 -3600
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +5864 -3592
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/styles/vars.css +877 -866
  50. package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
  51. package/package.json +4 -3
  52. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  53. package/dist/components/rel-alert/rel-alert.ts +0 -305
  54. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -689
  55. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  56. package/dist/components/rel-badge/rel-badge.ts +0 -170
  57. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  58. package/dist/components/rel-button/rel-button.ts +0 -560
  59. package/dist/components/rel-calendar/index.ts +0 -1
  60. package/dist/components/rel-calendar/rel-calendar.ts +0 -519
  61. package/dist/components/rel-card/rel-card.ts +0 -88
  62. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  63. package/dist/components/rel-carousel/index.ts +0 -1
  64. package/dist/components/rel-carousel/rel-carousel.ts +0 -700
  65. package/dist/components/rel-chart/README.md +0 -286
  66. package/dist/components/rel-chart/index.ts +0 -1
  67. package/dist/components/rel-chart/rel-chart.ts +0 -754
  68. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  69. package/dist/components/rel-chip/rel-chip.ts +0 -472
  70. package/dist/components/rel-container/rel-container.ts +0 -95
  71. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  72. package/dist/components/rel-date-picker/date-format.ts +0 -47
  73. package/dist/components/rel-date-picker/index.ts +0 -1
  74. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1237
  75. package/dist/components/rel-divider/rel-divider.ts +0 -54
  76. package/dist/components/rel-drawer/index.ts +0 -1
  77. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  78. package/dist/components/rel-drawer/rel-drawer.ts +0 -604
  79. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  80. package/dist/components/rel-layout/rel-layout.ts +0 -49
  81. package/dist/components/rel-menu/rel-menu.ts +0 -535
  82. package/dist/components/rel-menubar/index.ts +0 -2
  83. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  84. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  85. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  86. package/dist/components/rel-nav-menu/README.md +0 -289
  87. package/dist/components/rel-nav-menu/index.ts +0 -3
  88. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  89. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  90. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  91. package/dist/components/rel-navbar/README.md +0 -126
  92. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  93. package/dist/components/rel-otp/index.ts +0 -1
  94. package/dist/components/rel-otp/rel-otp.ts +0 -517
  95. package/dist/components/rel-pagination/index.ts +0 -1
  96. package/dist/components/rel-pagination/rel-pagination.ts +0 -509
  97. package/dist/components/rel-popover/index.ts +0 -2
  98. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  99. package/dist/components/rel-popover/rel-popover.ts +0 -444
  100. package/dist/components/rel-progress/rel-progress.ts +0 -225
  101. package/dist/components/rel-radio/rel-radio.ts +0 -305
  102. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1331
  103. package/dist/components/rel-resizable/index.ts +0 -3
  104. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  105. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  106. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  107. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -213
  108. package/dist/components/rel-select/rel-select.ts +0 -1385
  109. package/dist/components/rel-sheet/rel-sheet.ts +0 -465
  110. package/dist/components/rel-sidebar/README.md +0 -209
  111. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1185
  112. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -134
  113. package/dist/components/rel-slider/rel-slider.ts +0 -670
  114. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -325
  115. package/dist/components/rel-stepper/rel-stepper.ts +0 -565
  116. package/dist/components/rel-switch/rel-switch.ts +0 -197
  117. package/dist/components/rel-tab/rel-tab.ts +0 -264
  118. package/dist/components/rel-table/README.md +0 -178
  119. package/dist/components/rel-table/rel-table-body.ts +0 -22
  120. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  121. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  122. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  123. package/dist/components/rel-table/rel-table-head.ts +0 -47
  124. package/dist/components/rel-table/rel-table-header.ts +0 -22
  125. package/dist/components/rel-table/rel-table-row.ts +0 -37
  126. package/dist/components/rel-table/rel-table.ts +0 -62
  127. package/dist/components/rel-textfield/rel-textfield.ts +0 -866
  128. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1126
  129. package/dist/components/rel-toast/rel-toast.ts +0 -204
  130. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -190
  131. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  132. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  133. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  134. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  135. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  136. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  137. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  138. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  139. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  140. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  141. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  142. package/dist/components/rel-upload/rel-upload.ts +0 -943
@@ -1,465 +0,0 @@
1
- import { LitElement, html, css } from "lit";
2
- import { customElement, property, query } from "lit/decorators.js";
3
- import "@reliability-design/icons/rel-icon";
4
- import { getDeepActiveElement, getFocusableElements, trapTabKey } from "../../utils/focus.js";
5
-
6
- export type ModalSide = "top" | "bottom" | "center";
7
-
8
- @customElement("rel-modal")
9
- export class RelModal extends LitElement {
10
- @property({ type: Boolean, reflect: true }) open = false;
11
- @property({ type: String }) side: ModalSide = "bottom";
12
- @property({ type: String }) title = "";
13
- @property({ type: String }) description = "";
14
- @property({ type: Boolean }) dismissible = true;
15
- @property({ type: Boolean }) modal = true;
16
-
17
- /**
18
- * Figma "Breakpoint" axis. responsive (default) collapses to a full-width
19
- * bottom sheet below 480px; web keeps the fixed desktop panel at every size.
20
- */
21
- @property({ type: String, reflect: true })
22
- breakpoint: "responsive" | "web" = "responsive";
23
-
24
- @query(".overlay") private overlayElement!: HTMLElement;
25
- @query(".panel") private panelElement!: HTMLElement;
26
-
27
- private previousFocus: HTMLElement | null = null;
28
- private originalBodyOverflow = "";
29
- private originalBodyPaddingRight = "";
30
-
31
- static styles = css`
32
- :host {
33
- color: var(--rel-text-default);
34
- --modal-transition-duration: 0.3s;
35
- --modal-transition-timing: cubic-bezier(0.32, 0.72, 0, 1);
36
- }
37
-
38
- .modal-container {
39
- position: fixed;
40
- top: 0;
41
- left: 0;
42
- right: 0;
43
- bottom: 0;
44
- z-index: 1000;
45
- pointer-events: none;
46
- /* pointer-events:none only blocks the mouse — the closed modal's
47
- close button and slotted content would otherwise stay in the Tab
48
- order and swallow keyboard focus into invisible UI. visibility
49
- also removes them from the tab order; the transition delays the
50
- flip to hidden until the close animation has played. */
51
- visibility: hidden;
52
- transition: visibility 0s linear var(--modal-transition-duration);
53
- }
54
-
55
- .modal-container.open {
56
- pointer-events: auto;
57
- visibility: visible;
58
- transition-delay: 0s;
59
- }
60
-
61
- .overlay {
62
- position: absolute;
63
- top: 0;
64
- left: 0;
65
- right: 0;
66
- bottom: 0;
67
- background: var(--rel-overlay-scrim);
68
- opacity: 0;
69
- transition: opacity var(--modal-transition-duration) var(--modal-transition-timing);
70
- }
71
-
72
- .modal-container.open .overlay {
73
- opacity: 1;
74
- }
75
-
76
- .overlay.no-modal {
77
- display: none !important;
78
- }
79
-
80
- .panel {
81
- position: absolute;
82
- background: var(--rel-bg-surface);
83
- box-shadow: var(--rel-shadows-shadow-500);
84
- display: flex;
85
- flex-direction: column;
86
- overflow: hidden;
87
- outline: none;
88
- font-family: var(--rel-fonts-font-family);
89
- width: clamp(360px, 49vw, 708px);
90
- left: 50%;
91
- transition: transform var(--modal-transition-duration) var(--modal-transition-timing),
92
- opacity var(--modal-transition-duration) var(--modal-transition-timing);
93
- }
94
-
95
- .panel[data-side="bottom"] {
96
- bottom: 48px;
97
- left: 50%;
98
- width: clamp(360px, 49vw, 708px);
99
- max-height: calc(100vh - 96px);
100
- border-radius: var(--rel-border-radius-sm);
101
- transform: translateX(-50%) translateY(calc(100% + 48px));
102
- }
103
-
104
- .modal-container.open .panel[data-side="bottom"] {
105
- transform: translateX(-50%) translateY(0);
106
- }
107
-
108
- .panel[data-side="top"] {
109
- top: 48px;
110
- left: 50%;
111
- width: clamp(360px, 49vw, 708px);
112
- max-height: calc(100vh - 96px);
113
- border-radius: var(--rel-border-radius-sm);
114
- transform: translateX(-50%) translateY(calc(-100% - 48px));
115
- }
116
-
117
- .modal-container.open .panel[data-side="top"] {
118
- transform: translateX(-50%) translateY(0);
119
- }
120
-
121
- .panel[data-side="center"] {
122
- top: 50%;
123
- left: 50%;
124
- width: clamp(360px, 49vw, 708px);
125
- max-height: calc(100vh - 96px);
126
- border-radius: var(--rel-border-radius-sm);
127
- transform: translate(-50%, -50%) scale(0.96);
128
- opacity: 0;
129
- }
130
-
131
- .modal-container.open .panel[data-side="center"] {
132
- transform: translate(-50%, -50%) scale(1);
133
- opacity: 1;
134
- }
135
-
136
- /* Figma Breakpoint=Responsive: below 480px every side collapses to a
137
- full-width bottom sheet. Breakpoint=Web keeps the desktop panel. */
138
- @media (max-width: 480px) {
139
- :host([breakpoint="responsive"]) .panel,
140
- :host([breakpoint="responsive"]) .panel[data-side="top"],
141
- :host([breakpoint="responsive"]) .panel[data-side="center"],
142
- :host([breakpoint="responsive"]) .panel[data-side="bottom"] {
143
- width: 100%;
144
- left: 0;
145
- right: 0;
146
- top: auto;
147
- bottom: 0;
148
- max-height: 90vh;
149
- border-radius: var(--rel-border-radius-md) var(--rel-border-radius-md) 0 0;
150
- opacity: 1;
151
- transform: translateY(100%);
152
- }
153
-
154
- :host([breakpoint="responsive"]) .modal-container.open .panel,
155
- :host([breakpoint="responsive"]) .modal-container.open .panel[data-side="top"],
156
- :host([breakpoint="responsive"]) .modal-container.open .panel[data-side="center"],
157
- :host([breakpoint="responsive"]) .modal-container.open .panel[data-side="bottom"] {
158
- transform: translateY(0);
159
- }
160
- }
161
-
162
- .header {
163
- display: flex;
164
- align-items: flex-start;
165
- justify-content: space-between;
166
- padding: var(--rel-spacing-md) var(--rel-spacing-lg);
167
- border-bottom: var(--rel-stroke-sm) solid var(--rel-border-default);
168
- flex-shrink: 0;
169
- }
170
-
171
- .header.empty {
172
- display: none;
173
- }
174
-
175
- .header-text {
176
- gap: var(--rel-spacing-2xs);
177
- display: flex;
178
- flex-direction: column;
179
- gap: var(--rel-spacing-2xs);
180
- }
181
-
182
- .title {
183
- color: var(--rel-text-label);
184
- margin: 0;
185
-
186
- font-family: var(--rel-headings-lg-font-family);
187
-
188
- font-weight: var(--rel-headings-lg-font-weight);
189
-
190
- font-size: var(--rel-headings-lg-font-size);
191
-
192
- line-height: var(--rel-headings-lg-line-height);
193
-
194
- letter-spacing: var(--rel-headings-lg-letter-spacing);
195
-
196
- }
197
-
198
- .description {
199
- color: var(--rel-text-body);
200
- margin: 0;
201
-
202
- font-family: var(--rel-body-lg-regular-font-family);
203
-
204
- font-weight: var(--rel-body-lg-regular-font-weight);
205
-
206
- font-size: var(--rel-body-lg-regular-font-size);
207
-
208
- line-height: var(--rel-body-lg-regular-line-height);
209
-
210
- letter-spacing: var(--rel-body-lg-regular-letter-spacing);
211
-
212
- }
213
-
214
- /* Emphasised inline text in slotted content (Figma body-lg-medium) */
215
- .content ::slotted(strong),
216
- .content ::slotted(b) {
217
- font-weight: var(--rel-body-lg-medium-font-weight);
218
- }
219
-
220
- .close-button {
221
- background: transparent;
222
- border: none;
223
- cursor: pointer;
224
- padding: var(--rel-spacing-2xs);
225
- display: flex;
226
- align-items: center;
227
- justify-content: center;
228
- align-self: flex-start;
229
- flex-shrink: 0;
230
- border-radius: var(--rel-border-radius-xs);
231
- color: var(--rel-text-default);
232
- transition: background 0.15s ease;
233
- }
234
-
235
- .close-button:hover {
236
- background: var(--rel-bg-neutral-elevated-1);
237
- }
238
-
239
- .close-button:focus-visible {
240
- outline: 2px solid var(--rel-border-focus);
241
- outline-offset: 2px;
242
- }
243
-
244
- .content {
245
- flex: 1;
246
- overflow-y: auto;
247
- padding: var(--rel-spacing-xl);
248
- }
249
-
250
- .footer {
251
- display: none;
252
- }
253
-
254
- :host([has-footer]) .footer {
255
- display: flex;
256
- flex-shrink: 0;
257
- padding: var(--rel-spacing-md) var(--rel-spacing-lg);
258
- border-top: var(--rel-stroke-sm) solid var(--rel-border-default);
259
- justify-content: flex-end;
260
- gap: var(--rel-spacing-xs);
261
- }
262
-
263
- @media (prefers-reduced-motion: reduce) {
264
- .overlay,
265
- .panel {
266
- transition: none;
267
- }
268
-
269
- .modal-container .panel {
270
- transform: none;
271
- }
272
-
273
- .modal-container:not(.open) .panel {
274
- display: none;
275
- }
276
- }
277
- `;
278
-
279
- connectedCallback() {
280
- super.connectedCallback();
281
- if (this.open) {
282
- this.lockScroll();
283
- }
284
- this.updateComplete.then(() => {
285
- const footerSlot = this.shadowRoot?.querySelector(
286
- 'slot[name="footer"]'
287
- ) as HTMLSlotElement | null;
288
- if (footerSlot) {
289
- const sync = () => {
290
- const hasContent = footerSlot.assignedElements({ flatten: true }).length > 0;
291
- this.toggleAttribute('has-footer', hasContent);
292
- };
293
- footerSlot.addEventListener('slotchange', sync);
294
- sync();
295
- }
296
- });
297
- }
298
-
299
- disconnectedCallback() {
300
- super.disconnectedCallback();
301
- this.unlockScroll();
302
- document.removeEventListener("keydown", this.handleKeydown);
303
- }
304
-
305
- protected willUpdate() {
306
- // `title` doubles as HTML's global tooltip attribute: leaving it on the
307
- // host shows a native tooltip anywhere over the modal, including the
308
- // full-viewport backdrop. Sync it into the property, then strip it.
309
- if (this.hasAttribute("title")) {
310
- const value = this.getAttribute("title") ?? "";
311
- this.removeAttribute("title");
312
- this.title = value;
313
- }
314
- }
315
-
316
- updated(changed: Map<string, unknown>) {
317
- if (changed.has("open")) {
318
- if (this.open) {
319
- this.handleOpen();
320
- } else {
321
- this.handleClose();
322
- }
323
- }
324
- }
325
-
326
- private handleOpen() {
327
- // Deep-resolved — document.activeElement retargets to shadow hosts whose
328
- // .focus() may be a no-op, silently dropping the restore.
329
- this.previousFocus = getDeepActiveElement();
330
- this.lockScroll();
331
- document.addEventListener("keydown", this.handleKeydown);
332
-
333
- // Move focus into the dialog — before this, a keyboard user had to Tab
334
- // through the entire page behind the overlay to reach the sheet.
335
- if (this.modal) {
336
- this.updateComplete.then(() => {
337
- const panel = this.panelElement;
338
- if (!panel) return;
339
- const focusTarget = getFocusableElements(panel)[0];
340
- if (focusTarget) focusTarget.focus();
341
- else panel.focus();
342
- });
343
- }
344
- this.dispatchEvent(
345
- new CustomEvent("rel-open-change", {
346
- detail: true,
347
- bubbles: true,
348
- composed: true,
349
- })
350
- );
351
- }
352
-
353
- private handleClose() {
354
- this.unlockScroll();
355
- document.removeEventListener("keydown", this.handleKeydown);
356
- if (this.previousFocus?.focus) {
357
- this.updateComplete.then(() => {
358
- this.previousFocus?.focus();
359
- this.previousFocus = null;
360
- });
361
- }
362
- this.dispatchEvent(
363
- new CustomEvent("rel-open-change", {
364
- detail: false,
365
- bubbles: true,
366
- composed: true,
367
- })
368
- );
369
- }
370
-
371
- private lockScroll() {
372
- if (typeof document === "undefined") return;
373
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
374
- this.originalBodyOverflow = document.body.style.overflow;
375
- this.originalBodyPaddingRight = document.body.style.paddingRight;
376
- document.body.style.overflow = "hidden";
377
- if (scrollbarWidth > 0) {
378
- document.body.style.paddingRight = `${scrollbarWidth}px`;
379
- }
380
- }
381
-
382
- private unlockScroll() {
383
- if (typeof document === "undefined") return;
384
- document.body.style.overflow = this.originalBodyOverflow;
385
- document.body.style.paddingRight = this.originalBodyPaddingRight;
386
- }
387
-
388
- private handleKeydown = (e: KeyboardEvent) => {
389
- if (!this.open) return;
390
- if (e.key === "Escape" && this.dismissible) {
391
- e.preventDefault();
392
- this.open = false;
393
- return;
394
- }
395
- // aria-modal promised containment; actually deliver it.
396
- if (e.key === "Tab" && this.modal && this.panelElement) {
397
- trapTabKey(e, this.panelElement);
398
- }
399
- };
400
-
401
- private handleOverlayClick = (e: MouseEvent) => {
402
- if (!this.dismissible) return;
403
- if (e.target === this.overlayElement) {
404
- this.open = false;
405
- }
406
- };
407
-
408
- private handleCloseButtonClick() {
409
- this.open = false;
410
- }
411
-
412
- render() {
413
- const hasTitle = !!this.title;
414
- const hasDescription = !!this.description;
415
- const showHeader = hasTitle || hasDescription || this.dismissible;
416
-
417
- return html`
418
- <div class="modal-container ${this.open ? "open" : ""}">
419
- <div
420
- part="overlay"
421
- class="overlay ${!this.modal ? 'no-modal' : ''}"
422
- @click="${this.handleOverlayClick}"></div>
423
-
424
- <div
425
- part="panel"
426
- class="panel"
427
- data-side="${this.side}"
428
- role="${this.modal ? "dialog" : "region"}"
429
- aria-modal="${this.modal}"
430
- tabindex="-1">
431
-
432
- <div part="header" class="header ${!showHeader ? "empty" : ""}">
433
- <div class="header-text">
434
- ${hasTitle ? html`<h2 class="title">${this.title}</h2>` : ""}
435
- ${hasDescription ? html`<p class="description">${this.description}</p>` : ""}
436
- </div>
437
- ${this.dismissible
438
- ? html`<button
439
- part="close-button"
440
- class="close-button"
441
- @click="${this.handleCloseButtonClick}"
442
- aria-label="Close">
443
- <rel-icon name="close" size="sm"></rel-icon>
444
- </button>`
445
- : ""}
446
- </div>
447
-
448
- <div part="content" class="content">
449
- <slot></slot>
450
- </div>
451
-
452
- <div part="footer" class="footer">
453
- <slot name="footer"></slot>
454
- </div>
455
- </div>
456
- </div>
457
- `;
458
- }
459
- }
460
-
461
- declare global {
462
- interface HTMLElementTagNameMap {
463
- "rel-modal": RelModal;
464
- }
465
- }
@@ -1,209 +0,0 @@
1
- # rel-sidebar
2
-
3
- ## Overview
4
- A sophisticated sidebar navigation system with multiple components: `rel-sidebar-provider` (state manager), `rel-sidebar` (visual container), `rel-sidebar-trigger` (toggle button), and layout helpers (`rel-sidebar-header`, `rel-sidebar-footer`, `rel-sidebar-content`, `rel-sidebar-rail`). Supports three variants (sidebar, floating, inset), two collapsible modes (offcanvas, icon), mobile detection, localStorage persistence, keyboard shortcuts, and focus trapping.
5
-
6
- ## Components
7
- This sidebar system consists of multiple components that work together:
8
- - **`<rel-sidebar-provider>`**: Root wrapper that manages state
9
- - **`<rel-sidebar>`**: Visual sidebar container
10
- - **`<rel-sidebar-trigger>`**: Toggle button
11
- - **`<rel-sidebar-header>`**: Header layout helper
12
- - **`<rel-sidebar-footer>`**: Footer layout helper
13
- - **`<rel-sidebar-content>`**: Main content wrapper
14
- - **`<rel-sidebar-rail>`**: Collapsed (icon-only) state content
15
-
16
- ## Usage
17
- ```html
18
- <!-- Basic sidebar with provider -->
19
- <rel-sidebar-provider>
20
- <rel-sidebar>
21
- <rel-sidebar-header>
22
- <h2>App Name</h2>
23
- <rel-sidebar-trigger>
24
- <button>☰</button>
25
- </rel-sidebar-trigger>
26
- </rel-sidebar-header>
27
-
28
- <rel-sidebar-content>
29
- <nav>
30
- <a href="#home">Home</a>
31
- <a href="#about">About</a>
32
- <a href="#contact">Contact</a>
33
- </nav>
34
- </rel-sidebar-content>
35
-
36
- <rel-sidebar-footer>
37
- <p>© 2024 Company</p>
38
- </rel-sidebar-footer>
39
- </rel-sidebar>
40
-
41
- <main>
42
- <h1>Main Content</h1>
43
- <p>Page content goes here</p>
44
- </main>
45
- </rel-sidebar-provider>
46
-
47
- <!-- Controlled open state -->
48
- <rel-sidebar-provider open="true">
49
- <rel-sidebar>
50
- <nav>Navigation items</nav>
51
- </rel-sidebar>
52
- <main>Content</main>
53
- </rel-sidebar-provider>
54
-
55
- <!-- Right-side floating variant -->
56
- <rel-sidebar-provider side="right" variant="floating" width="lg">
57
- <rel-sidebar>
58
- <h3>Right Sidebar</h3>
59
- </rel-sidebar>
60
- <main>Content</main>
61
- </rel-sidebar-provider>
62
-
63
- <!-- Offcanvas mode (overlay on all screen sizes) -->
64
- <rel-sidebar-provider collapsible="offcanvas">
65
- <rel-sidebar>
66
- <nav>Menu</nav>
67
- </rel-sidebar>
68
- <main>Content</main>
69
- </rel-sidebar-provider>
70
-
71
- <!-- Listen for state changes -->
72
- <rel-sidebar-provider id="sidebarProvider">
73
- <rel-sidebar>Nav</rel-sidebar>
74
- <main>Content</main>
75
- </rel-sidebar-provider>
76
- <script>
77
- const provider = document.getElementById('sidebarProvider');
78
- provider.addEventListener('rel-sidebar-change', (e) => {
79
- console.log('Open:', e.detail.open);
80
- console.log('Collapsed:', e.detail.collapsed);
81
- console.log('Reason:', e.detail.reason);
82
- });
83
- </script>
84
- ```
85
-
86
- ## API
87
-
88
- ### rel-sidebar-provider Properties
89
- | Name | Type | Default | Description |
90
- |------|------|---------|-------------|
91
- | `open` (reflected) | `boolean?` | `undefined` | Controlled open state (undefined = uncontrolled) |
92
- | `defaultopen` | `boolean` | `true` | Default open state (uncontrolled mode) |
93
- | `collapsed` (reflected) | `boolean?` | `undefined` | Controlled collapsed state |
94
- | `defaultcollapsed` | `boolean` | `false` | Default collapsed state (uncontrolled mode) |
95
- | `variant` | `"sidebar" \| "floating" \| "inset"` | `"sidebar"` | Visual variant |
96
- | `side` | `"left" \| "right"` | `"left"` | Which side the sidebar appears on |
97
- | `collapsible` | `"offcanvas" \| "icon" \| "none"` | `"icon"` | Collapsible behavior mode |
98
- | `width` | `"sm" \| "md" \| "lg"` | `"md"` | Sidebar width (240/280/320px) |
99
- | `persist-key` | `string` | `"rel-sidebar"` | localStorage key for persistence |
100
- | `keyboard-shortcut` | `boolean` | `true` | Enable Ctrl/Cmd+B shortcut |
101
- | `disable-persist` | `boolean` | `false` | Disable localStorage persistence |
102
- | `disable-overlay-close` | `boolean` | `false` | Prevent closing on overlay click |
103
-
104
- ### rel-sidebar Properties
105
- | Name | Type | Default | Description |
106
- |------|------|---------|-------------|
107
- | `inset` | `boolean` | `false` | Use inset variant styling (overrides provider variant) |
108
-
109
- ## Events
110
- | Event | Detail | Description |
111
- |-------|--------|-------------|
112
- | `rel-sidebar-change` | `{ open, collapsed, reason }` | Fired when sidebar state changes |
113
- | `rel-sidebar-state` | `{ isOpen, isCollapsed, isMobile, side, variant, collapsible, width }` | Internal state sync event |
114
-
115
- ### Change Reasons
116
- - `"trigger"`: Toggled via trigger button
117
- - `"rail"`: Toggled collapsed state
118
- - `"overlay"`: Closed by clicking overlay
119
- - `"escape"`: Closed via Escape key
120
- - `"shortcut"`: Toggled via keyboard shortcut (Ctrl/Cmd+B)
121
- - `"api"`: Changed programmatically via `setOpen()` or `setCollapsed()`
122
-
123
- ## Methods
124
-
125
- ### Provider Methods
126
- ```typescript
127
- provider.setOpen(nextOpen: boolean, reason?: string): void
128
- provider.setCollapsed(nextCollapsed: boolean, reason?: string): void
129
- ```
130
-
131
- ##Slots
132
- | Slot | Component | Description |
133
- |------|-----------|-------------|
134
- | (default) | All | Main content area |
135
-
136
- ## CSS Parts
137
- None exposed.
138
-
139
- ## Tokens / CSS Variables
140
-
141
- ### Width Tokens (defined on `<rel-sidebar>`)
142
- - `--sidebar-width-sm`: `240px` (small width)
143
- - `--sidebar-width-md`: `280px` (medium width)
144
- - `--sidebar-width-lg`: `320px` (large width)
145
- - `--sidebar-width-collapsed`: `56px` (collapsed/icon-only width)
146
-
147
- ### Transition Tokens
148
- - `--sidebar-transition-duration`: `0.2s`
149
- - `--sidebar-transition-timing`: `cubic-bezier(0.4, 0, 0.2, 1)`
150
-
151
- ### Color Tokens
152
- - `--rel-bg-surface-elevated` - Sidebar background (`#fafafa`)
153
- - `--rel-text-default` - Text color
154
- - `--rel-border-default` - Border color
155
- - `--rel-overlay-scrim` - Overlay background (`rgba(0, 0, 0, 0.5)`)
156
-
157
- ### Shadow Tokens (floating variant)
158
- - `--rel-shadows-shadow-400` - Floating variant shadow
159
-
160
- ### Other Tokens
161
- - `--rel-border-radius-md` - Floating variant border radius
162
- - `--rel-spacing-md` - Floating variant inset, header/footer padding
163
- - `--rel-spacing-2xs` - Used in various layouts
164
-
165
- ## Accessibility
166
- - Sidebar uses `role="navigation"`
167
- - Focus trap active in offcanvas mode
168
- - Escape key closes offcanvas
169
- - Keyboard shortcut (Ctrl/Cmd+B) for toggling
170
- - Restores focus to trigger when closing
171
-
172
- ### Keyboard Interactions
173
- | Key | Action |
174
- |-----|--------|
175
- | `Ctrl/Cmd + B` | Toggle sidebar (open/collapsed depending on mode) |
176
- | `Escape` | Close sidebar (offcanvas mode only) |
177
- | `Tab` | Focus trap within sidebar when open in offcanvas mode |
178
-
179
- ### Focus Behavior
180
- - In offcanvas mode, focus moves to first focusable element when opened
181
- - Focus is trapped within sidebar in offcanvas mode
182
- - Focus restores to trigger button when closed
183
-
184
- ## Notes / Edge Cases
185
- - **Mobile detection**: Automatically detects `max-width: 768px` breakpoint and switches to offcanvas mode
186
- - **Collapsible modes**:
187
- - **offcanvas**: Sidebar slides in/out with overlay (like a drawer)
188
- - **icon**: Sidebar collapses to icon-only width (56px)
189
- - **none**: Sidebar cannot be collapsed (always full width)
190
-
191
- - **Variants**:
192
- - **sidebar**: Fixed position, full height
193
- - **floating**: Inset with margins, rounded corners, shadow
194
- - **inset**: Relative position, no shadow
195
-
196
- - **State persistence**: By default, sidebar state is saved to localStorage and restored on reload (disable with `disable-persist`)
197
- - **Controlled vs uncontrolled**: If `open` or `collapsed` props are set, the component is controlled and won't manage its own state
198
- - **Mobile behavior**: On mobile, sidebar starts closed and ignores persisted state
199
- - **Scroll locking**: In offcanvas mode, body scroll is locked when sidebar is open
200
- - **Transitions disabled**: When `prefers-reduced-motion: reduce`
201
- - The provider uses `display: contents` to avoid affecting layout
202
- - Sidebar is overlaid with `z-index: 1000` in offcanvas mode
203
- - Overlay is `z-index: 999`
204
-
205
- ### Width Values
206
- - **sm**: 240px
207
- - **md**: 280px
208
- - **lg**: 320px
209
- - **collapsed**: 56px