@reliability-design/web 1.4.6 → 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 (146) 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 -138
  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 -111
  42. package/dist/components/rel-upload/README.md +220 -0
  43. package/dist/index.d.mts +1147 -340
  44. package/dist/index.d.ts +1147 -340
  45. package/dist/index.js +3451 -3163
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +3416 -3118
  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 +3 -2
  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 -720
  55. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  56. package/dist/components/rel-badge/rel-badge.ts +0 -175
  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 -565
  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 -723
  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 -483
  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/date-parse.test.ts +0 -116
  74. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  75. package/dist/components/rel-date-picker/index.ts +0 -1
  76. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  77. package/dist/components/rel-divider/rel-divider.ts +0 -54
  78. package/dist/components/rel-drawer/index.ts +0 -1
  79. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  80. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  81. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  82. package/dist/components/rel-layout/rel-layout.ts +0 -49
  83. package/dist/components/rel-menu/rel-menu.ts +0 -541
  84. package/dist/components/rel-menubar/index.ts +0 -2
  85. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  86. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  87. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  88. package/dist/components/rel-nav-menu/README.md +0 -289
  89. package/dist/components/rel-nav-menu/index.ts +0 -3
  90. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  91. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  92. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  93. package/dist/components/rel-navbar/README.md +0 -126
  94. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  95. package/dist/components/rel-otp/index.ts +0 -1
  96. package/dist/components/rel-otp/rel-otp.ts +0 -527
  97. package/dist/components/rel-pagination/index.ts +0 -1
  98. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  99. package/dist/components/rel-popover/index.ts +0 -2
  100. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  101. package/dist/components/rel-popover/rel-popover.ts +0 -475
  102. package/dist/components/rel-progress/rel-progress.ts +0 -234
  103. package/dist/components/rel-radio/rel-radio.ts +0 -481
  104. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  105. package/dist/components/rel-resizable/index.ts +0 -3
  106. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  107. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  108. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  109. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  110. package/dist/components/rel-select/rel-select.ts +0 -1416
  111. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  112. package/dist/components/rel-sidebar/README.md +0 -209
  113. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  114. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  115. package/dist/components/rel-slider/rel-slider.ts +0 -673
  116. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  117. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  118. package/dist/components/rel-switch/rel-switch.ts +0 -199
  119. package/dist/components/rel-tab/rel-tab.ts +0 -264
  120. package/dist/components/rel-table/README.md +0 -178
  121. package/dist/components/rel-table/rel-table-body.ts +0 -22
  122. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  123. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  124. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  125. package/dist/components/rel-table/rel-table-head.ts +0 -47
  126. package/dist/components/rel-table/rel-table-header.ts +0 -22
  127. package/dist/components/rel-table/rel-table-row.ts +0 -37
  128. package/dist/components/rel-table/rel-table.ts +0 -62
  129. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  130. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  131. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  132. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  133. package/dist/components/rel-toast/rel-toast.ts +0 -206
  134. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  135. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  136. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  137. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  138. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  139. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  140. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  141. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  142. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  144. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  145. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  146. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -0,0 +1,162 @@
1
+ # rel-range-picker
2
+
3
+ `<rel-range-picker>` is the date-range form field of the Reliability Design System (Figma "Date Range Picker"). It is a text field you can type a "from → to" range into, backed by a two-pane panel: quick-select presets with single-key shortcuts and From / To readouts on the left, a month grid with live range preview on the right, and a Reset / Cancel / Apply footer. The grid and presets edit a draft; only Apply (or typed entry / clear) commits `from`/`to` as ISO `YYYY-MM-DD` strings. Use it for filters and reports that need a start and an end. For a single date use `<rel-date-picker>`, for an always-visible grid use `<rel-calendar>`.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Basic -->
9
+ <rel-range-picker placeholder="Select date range"></rel-range-picker>
10
+
11
+ <!-- Preselected range with a clear icon -->
12
+ <rel-range-picker from="2026-01-05" to="2026-01-09" clearable></rel-range-picker>
13
+
14
+ <!-- Bounded, Monday-first grid -->
15
+ <rel-range-picker min-date="2026-01-01" max-date="2026-12-31" week-start="mon"></rel-range-picker>
16
+
17
+ <!-- Centred popup with a dimmed backdrop instead of an anchored popover -->
18
+ <rel-range-picker mode="popup"></rel-range-picker>
19
+
20
+ <!-- Icon-only trigger (no typed entry, no clear icon) -->
21
+ <rel-range-picker trigger-variant="icon" placeholder="Filter by date"></rel-range-picker>
22
+
23
+ <!-- Sizing the host: explicit width, or fill the parent -->
24
+ <rel-range-picker width="320px" min-width="240px"></rel-range-picker>
25
+ <rel-range-picker fullwidth></rel-range-picker>
26
+
27
+ <!-- Disabled -->
28
+ <rel-range-picker from="2026-01-05" to="2026-01-09" disabled></rel-range-picker>
29
+ ```
30
+
31
+ ```js
32
+ const picker = document.querySelector("rel-range-picker");
33
+
34
+ picker.addEventListener("rel-change", (e) => {
35
+ e.detail.from; // "2026-01-05" ("" when cleared)
36
+ e.detail.to; // "2026-01-09"
37
+ e.detail.label; // "01/05/2026 → 01/09/2026"
38
+ });
39
+ picker.addEventListener("rel-open-change", (e) => e.detail.open);
40
+ picker.addEventListener("rel-blur", () => {/* run validation */});
41
+
42
+ picker.displayLabel; // same formatted label for the committed range
43
+ ```
44
+
45
+ ### Typed entry
46
+
47
+ With the default trigger the field is editable and commits on Enter or when focus leaves the component. Two dates are separated by `→`, `..`, a spaced `-`, or the word `to` (`2026-01-05 to 2026-01-09`, `21/7 .. 28/7`, `t - t+7`); a single date gives a one-day range and a reversed pair is normalised so `from` ≤ `to`. Each side accepts everything `rel-date-picker` parses — ISO, numeric, month names, partial `21/7` / `21`, and the `t` / `today` / `t±N` shortcuts — with ambiguous `a/b` always read as month/day, matching the `MM/DD/YYYY` display. Unparseable text shows "Invalid date range" and a date outside `min-date`/`max-date` shows "Date not available"; the text stays for correction and the committed range is untouched. Escape reverts the text; committing an empty field clears the range like the clear icon.
48
+
49
+ ### Presets and shortcuts
50
+
51
+ The left pane lists Today (`T`), Tomorrow (`M`), Yesterday (`Y`), This Week (`W`), Next Week (`N`), Next 7 Days (`7`) and Next 30 Days (`3`), one per row. Weeks in the presets always run Sunday–Saturday regardless of `week-start`. While the panel is open, pressing the key (no modifier, focus not in an editable field) selects that preset; a preset only fills the draft — Apply still commits it. Following Figma's ".Suggested Range" row, the key is not printed on the row: each preset carries it as `aria-keyshortcuts` and a `title` tooltip instead.
52
+
53
+ > **Changed in this release.** The `Before Today` preset (`B`, `1970-01-01` → yesterday) has been replaced by `Yesterday` (`Y`, yesterday → yesterday) to match Figma. The `B` key no longer selects anything; use typed entry (for example `1970-01-01 to t-1`) if you need the old open-ended range.
54
+
55
+ <!-- readme-gen:start -->
56
+
57
+ <!-- Generated from rel-range-picker.ts — do not edit; run readme-generate.mjs -->
58
+
59
+ ## API
60
+
61
+ ### Attributes / Properties
62
+
63
+ | Property | Attribute | Type | Default | Description |
64
+ | --- | --- | --- | --- | --- |
65
+ | `from` | `from` | `string` | `""` | Committed range start as an ISO `YYYY-MM-DD` string; empty string when no range is set. |
66
+ | `to` | `to` | `string` | `""` | Committed range end as an ISO `YYYY-MM-DD` string; empty string when no range is set. Always ≥ `from`. |
67
+ | `locale` | `locale` | `string` | `"en-US"` | Locale used to match month names in typed input; the visible trigger text and calendar labels are always `en-US` (`MM/DD/YYYY`). |
68
+ | `weekStart` | `week-start` | `"mon" \| "sun"` | `"sun"` | First column of the calendar grid: `"sun"` (default) or `"mon"`. Presets always compute weeks Sunday–Saturday. |
69
+ | `minDate` | `min-date` | `string` | `""` | Earliest selectable date (ISO `YYYY-MM-DD`); earlier days are disabled and typed ranges starting before it are rejected. |
70
+ | `maxDate` | `max-date` | `string` | `""` | Latest selectable date (ISO `YYYY-MM-DD`); later days are disabled and typed ranges ending after it are rejected. |
71
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the trigger, the icons and the panel controls; the host dims to 60% opacity. Reflected. |
72
+ | `open` | `open` | `boolean` | `false` | Whether the panel is open. Setting it directly shows/hides the panel but does not seed the draft from `from`/`to` or fire `rel-open-change` — user interaction does both. Reflected. |
73
+ | `mode` | `mode` | `"popover" \| "popup"` | `"popover"` | `popover` anchors the panel below the trigger; `popup` centres it in the viewport behind a dimmed backdrop (part `overlay`). |
74
+ | `width` | `width` | `string` | `""` | CSS width applied to the host (any CSS length, e.g. `320px`); empty leaves the intrinsic width. |
75
+ | `minWidth` | `min-width` | `string` | `""` | CSS `min-width` applied to the host. |
76
+ | `maxWidth` | `max-width` | `string` | `""` | CSS `max-width` applied to the host. |
77
+ | `height` | `height` | `string` | `""` | CSS height applied to the host. |
78
+ | `minHeight` | `min-height` | `string` | `""` | CSS `min-height` applied to the host. |
79
+ | `maxHeight` | `max-height` | `string` | `""` | CSS `max-height` applied to the host. |
80
+ | `placeholder` | `placeholder` | `string` | `"Select date range"` | Placeholder shown in the empty field; also the accessible name of the input (and of the icon trigger while no range is set). |
81
+ | `triggerVariant` | `trigger-variant` | `"default" \| "icon"` | `"default"` | `default` renders the text field; `icon` renders a single calendar icon button instead (no typed entry, no clear button, parts `trigger`/`value` absent). |
82
+ | `clearable` | `clearable` | `boolean` | `false` | Shows a clear (×) icon while a range is set; clearing fires `rel-change` with empty `from`/`to`. Only with the default trigger. |
83
+ | `fullWidth` | `fullwidth` | `boolean` | `false` | Stretches the host to fill its parent's width (attribute `fullwidth`), mirroring rel-time-picker / rel-date-picker. Off by default so the existing intrinsic-width (`inline-block`) rendering is unchanged. Reflected. |
84
+
85
+ ### Public fields / accessors
86
+
87
+ | Name | Type | Description |
88
+ | --- | --- | --- |
89
+ | `displayLabel` | `string` | Formatted "MM/DD/YYYY → MM/DD/YYYY" (or single date / "") for the current committed `from`/`to`. |
90
+
91
+ ## Events
92
+
93
+ | Event | Detail | Bubbles / composed | Description |
94
+ | --- | --- | --- | --- |
95
+ | `rel-blur` | — | bubbles / composed | Fired when the text input loses focus (before any typed text is committed). No detail. |
96
+ | `rel-change` | `{ from: this.from, to: this.to, label: this.displayLabel } / { from: "", to: "", label: "" }` | bubbles / composed | Fired when the committed range changes — Apply, typed entry, or clear. Detail: `{ from: string, to: string, label: string }` (ISO dates and the formatted `MM/DD/YYYY → MM/DD/YYYY` label; all empty strings when cleared). |
97
+ | `rel-open-change` | `{ open: true } / { open: false }` | bubbles / composed | Fired when the panel opens or closes (open, Cancel, Escape, outside click, Apply). Detail: `{ open: boolean }`. |
98
+
99
+ ## Slots
100
+
101
+ | Slot | Description |
102
+ | --- | --- |
103
+ | — | This element has no slots. |
104
+
105
+ ## CSS Parts
106
+
107
+ | Part | Description |
108
+ | --- | --- |
109
+ | `trigger` | The field box that wraps the text input and the clear/calendar icons (default trigger variant only) |
110
+ | `value` | The editable text input inside the trigger |
111
+ | `overlay` | The full-screen backdrop rendered in `mode="popup"` while open |
112
+ | `panel` | The floating panel (`role="dialog"`) holding presets and calendar |
113
+ | `list` | The preset column and the day grid |
114
+ | `option` | Each quick-select preset button |
115
+ | `action` | The previous-month and next-month buttons |
116
+ | `footer` | The Reset / Cancel / Apply button row |
117
+
118
+ <!-- readme-gen:end -->
119
+
120
+ ## Accessibility
121
+
122
+ - Default trigger: the input is a `role="combobox"` with `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` pointing at the panel, `aria-label` = `placeholder`, `aria-invalid` while typed text is in error and `aria-describedby` pointing at the error line. The icon buttons are labelled "Clear date range" and "Open date range panel" (with `aria-expanded`).
123
+ - Icon trigger (`trigger-variant="icon"`): a `<rel-button>` with `aria-haspopup="dialog"`, `aria-expanded`, and an `aria-label` of the current range label or the `placeholder`.
124
+ - The panel is a `role="dialog"` named "Select date range". Presets sit in a `role="group"` named "Quick date selections"; each preset button carries `aria-pressed` plus an `aria-keyshortcuts` naming its single-key shortcut. The From / To readouts are groups named "From date" / "To date". Focus is not trapped inside the panel.
125
+ - The calendar is a `role="grid"` named "Select date range" with `role="columnheader"` weekday names, `role="row"` weeks and `role="gridcell"` days whose `aria-label` is the full long date plus ", range start" / ", range end" for the endpoints; `aria-selected` is true for the endpoints and every day between them, `aria-disabled` marks unavailable days. Exactly one cell is a Tab stop (last arrowed date → draft start → today → first enabled day).
126
+ - Focus: opening the panel does not move focus; Escape closes it and returns focus to the input. Keyboard navigation in the grid previews the range exactly like mouse hover does.
127
+ - Screen readers hear "<Month> <Year>" after every month change and "From date MM/DD/YYYY" / "To date MM/DD/YYYY" when an endpoint is chosen, through the shared `announce()` region (`utils/announce.ts`).
128
+
129
+ | Key (in the field) | Action |
130
+ | --- | --- |
131
+ | Enter | Nothing typed: open the panel. Text typed: commit it |
132
+ | ArrowDown | Open the panel |
133
+ | Escape | Revert uncommitted text; close the panel and refocus the input |
134
+
135
+ | Key (panel open, focus outside an editable field) | Action |
136
+ | --- | --- |
137
+ | T / M / Y / W / N / 7 / 3 | Select the matching preset into the draft |
138
+ | Escape | Cancel: discard the draft, close, refocus the input |
139
+
140
+ | Key (in the grid) | Action |
141
+ | --- | --- |
142
+ | ArrowLeft / ArrowRight | Previous / next day (switches month at the edge) |
143
+ | ArrowUp / ArrowDown | Same weekday one week earlier / later |
144
+ | Home / End | First / last day of the row, honouring `week-start` |
145
+ | PageUp / PageDown | Same day previous / next month |
146
+ | Shift + PageUp / Shift + PageDown | Same day previous / next year |
147
+ | Enter / Space | Pick the day as range start, then as range end |
148
+
149
+ ## Notes / Edge Cases
150
+
151
+ - Draft vs committed: grid picks and presets only change the draft shown in the From / To readouts. Apply commits it (fires `rel-change`, closes); Cancel, Escape, an outside click, or the popup backdrop discard it; Reset empties the draft. Reset followed by Apply clears the committed range (`rel-change` with empty strings) — Apply is disabled only when both the draft and the committed range are empty.
152
+ - Second-click ordering: picking a day before the current draft start makes it the new start and the old start the end.
153
+ - Setting `open = true` from code shows the panel but neither seeds the draft from `from`/`to` nor fires `rel-open-change`; only user interaction does both.
154
+ - The trigger text, From / To readouts, month names and day labels are always rendered in `en-US` (`MM/DD/YYYY`); `locale` only affects month-name matching in typed input.
155
+ - Leading and trailing cells of adjacent months are visible but never selectable, and render at 40% label opacity (Figma "Day of a week = Other Month"). Today gets a 1 px brand ring; weekend cells render at 70% opacity (Figma "Day of a week = Weekend") unless they are range endpoints.
156
+ - Range styling follows Figma ".Date Range Days": the selected run is an `elevated-1` band across the full cell, rounded at the two ends (a one-day range is a single rounded pill), and the two endpoints are `inverse-2` discs with inverse semibold labels. Days between the endpoints keep their normal label on the band.
157
+ - The panel (and the popup backdrop) is promoted to the browser top layer (`popover="manual"`), so it works inside `rel-modal` / `rel-drawer` and above transformed or `overflow: hidden` ancestors. In `popover` mode it anchors below the host, flips above when there is no room, stays 8 px inside the viewport and repositions on scroll/resize; in `popup` mode it is fixed-centred behind a 40% black backdrop (part `overlay`) whose click cancels. It never exceeds the viewport and scrolls internally.
158
+ - `width`, `min-width`, `max-width`, `height`, `min-height` and `max-height` are copied to the host's inline style on every update, so they override stylesheet rules on the host.
159
+ - The icon trigger has no typed entry, no clear icon and no error line; parts `trigger` and `value` are not rendered in that variant.
160
+ - There is no helper-text or external error API; the only text below the field is the typed-entry error.
161
+ - The footer buttons are `<rel-button>` instances — `ghost` for Reset, `secondary` for Cancel and `primary tone="brand"` for Apply — and the icon trigger uses `<rel-button>` with `<rel-icon name="calendar-range">`, so `@reliability-design/icons` is loaded by this component.
162
+ - The host is `inline-block` with an intrinsic width by default; `fullwidth` switches it to a block that fills the parent.
@@ -1,140 +1,199 @@
1
- # Resizable Components
2
-
3
- A flexible resizable panel system built with Lit web components for the Reliability Design System.
4
-
5
- ## Components
6
-
7
- ### `<rel-resizable-group>`
8
- Container for resizable panels. Manages layout direction and coordinates resizing between panels.
9
-
10
- **Attributes:**
11
- - `direction` (string): Layout direction - `"horizontal"` (default) or `"vertical"`
12
-
13
- **Events:**
14
- - `rel-resize`: Emitted during resize with detail `{ direction, sizes: number[] }`
15
- - `rel-resize-end`: Emitted when resize completes with detail `{ direction, sizes: number[] }`
16
-
17
- ### `<rel-resizable-panel>`
18
- Individual resizable panel within a group.
19
-
20
- **Attributes:**
21
- - `defaultsize` (number): Initial size in percentage (optional)
22
- - `minsize` (number): Minimum size in percentage (default: 10)
23
- - `maxsize` (number): Maximum size in percentage (default: 90)
24
-
25
- ### `<rel-resizable-handle>`
26
- Draggable separator between panels.
27
-
28
- **Features:**
29
- - Pointer drag support (click and drag to resize)
30
- - Keyboard navigation:
31
- - Horizontal: ArrowLeft/ArrowRight (2% step, 5% with Shift)
32
- - Vertical: ArrowUp/ArrowDown (2% step, 5% with Shift)
33
- - Accessible with `role="separator"` and ARIA attributes
34
- - Visual feedback on hover/focus/drag
35
-
36
- ## Usage
37
-
38
- ### Basic Horizontal Layout
39
- ```html
40
- <rel-resizable-group direction="horizontal">
41
- <rel-resizable-panel defaultsize="50">
42
- Left Panel
43
- </rel-resizable-panel>
44
- <rel-resizable-handle></rel-resizable-handle>
45
- <rel-resizable-panel defaultsize="50">
46
- Right Panel
47
- </rel-resizable-panel>
48
- </rel-resizable-group>
49
- ```
50
-
51
- ### Vertical Layout
52
- ```html
53
- <rel-resizable-group direction="vertical">
54
- <rel-resizable-panel defaultsize="40">
55
- Top Panel
56
- </rel-resizable-panel>
57
- <rel-resizable-handle></rel-resizable-handle>
58
- <rel-resizable-panel defaultsize="60">
59
- Bottom Panel
60
- </rel-resizable-panel>
61
- </rel-resizable-group>
62
- ```
63
-
64
- ### Nested Layout
65
- ```html
66
- <rel-resizable-group direction="horizontal">
67
- <rel-resizable-panel defaultsize="33">
68
- Left Sidebar
69
- </rel-resizable-panel>
70
- <rel-resizable-handle></rel-resizable-handle>
71
- <rel-resizable-panel defaultsize="67">
72
- <rel-resizable-group direction="vertical">
73
- <rel-resizable-panel defaultsize="50">
74
- Top Content
75
- </rel-resizable-panel>
76
- <rel-resizable-handle></rel-resizable-handle>
77
- <rel-resizable-panel defaultsize="50">
78
- Bottom Content
79
- </rel-resizable-panel>
80
- </rel-resizable-group>
81
- </rel-resizable-panel>
82
- </rel-resizable-group>
83
- ```
84
-
85
- ### With Constraints
86
- ```html
87
- <rel-resizable-group direction="horizontal">
88
- <rel-resizable-panel defaultsize="30" minsize="20" maxsize="50">
89
- Constrained Panel
90
- </rel-resizable-panel>
91
- <rel-resizable-handle></rel-resizable-handle>
92
- <rel-resizable-panel defaultsize="70">
93
- Main Panel
94
- </rel-resizable-panel>
95
- </rel-resizable-group>
96
- ```
97
-
98
- ## How It Works
99
-
100
- ### Size Calculation
101
- 1. **Initialization**: On first render, the group reads all direct child panels and handles
102
- 2. **Default Sizes**: If panels specify `defaultsize`, those are used; otherwise space is distributed evenly
103
- 3. **Normalization**: All sizes are normalized to total 100% across all panels
104
- 4. **Percentage-Based**: Sizes are always in percentages, ensuring responsive layouts
105
-
106
- ### Resizing Behavior
107
- - **Pointer Drag**: Click and drag a handle to resize adjacent panels
108
- - **Keyboard**: Focus a handle and use arrow keys (direction-aware)
109
- - **Constraints**: Min/max sizes are enforced; total percentage is always preserved
110
- - **Pair Adjustment**: Only the two panels adjacent to a handle are resized
111
-
112
- ### Events
113
- - Use `rel-resize` to track live resizing (useful for updating dependent content)
114
- - Use `rel-resize-end` to react after resize completes (useful for saving preferences)
115
-
116
- ## Accessibility
117
-
118
- - Handles are keyboard focusable with `tabindex="0"`
119
- - `role="separator"` with appropriate `aria-orientation`
120
- - `aria-valuenow`, `aria-valuemin`, `aria-valuemax` reflect current split
121
- - Visual focus indicators using design tokens
122
- - Standard cursor changes (col-resize/row-resize)
123
-
124
- ## Styling
125
-
126
- All styling uses Reliability Design System tokens:
127
- - Borders: `--rel-stroke-sm`
128
- - Colors: `--rel-bg-neutral-elevated-*`, `--rel-bg-info`, `--rel-bg-surface`
129
- - Spacing: `--rel-spacing-*`
130
- - Border radius: `--rel-border-radius-*`
131
-
132
- Panels have `overflow: auto` by default for scrollable content.
133
-
134
- ## Limitations
135
-
136
- 1. **Direct Children Only**: Panels and handles must be direct children of the group
137
- 2. **Handle Placement**: Handles should be placed between panels in the expected order
138
- 3. **No Collapse**: Panels cannot collapse to 0% (use min/max to control)
139
- 4. **Single Direction**: Each group has one direction; nest groups for complex layouts
140
- 5. **Manual Updates**: Group doesn't automatically observe DOM changes after initialization
1
+ # rel-resizable
2
+
3
+ `rel-resizable` is a three-element split-pane system: `<rel-resizable-group>` lays out its direct-child `<rel-resizable-panel>`s along one axis (`direction="horizontal"` side by side, `"vertical"` stacked) and lets the user drag or keyboard-nudge the `<rel-resizable-handle>` between two neighbours to trade percentage size between them, within each panel's `minsize` / `maxsize`. Use it for editor / preview splits, sidebar + content layouts and inspector panes where the user should control the proportions. It has no Figma component of its own; it follows the WAI-ARIA window-splitter pattern. It is not a general grid or an accordion — every panel stays visible and cannot be collapsed below its `minsize`.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Horizontal split (default direction) -->
9
+ <rel-resizable-group direction="horizontal" style="height: 320px;">
10
+ <rel-resizable-panel defaultsize="50">
11
+ <h3>Left</h3>
12
+ </rel-resizable-panel>
13
+ <rel-resizable-handle></rel-resizable-handle>
14
+ <rel-resizable-panel defaultsize="50">
15
+ <h3>Right</h3>
16
+ </rel-resizable-panel>
17
+ </rel-resizable-group>
18
+
19
+ <!-- Vertical split: the group needs an explicit height to divide -->
20
+ <rel-resizable-group direction="vertical" style="height: 480px;">
21
+ <rel-resizable-panel defaultsize="40">Top</rel-resizable-panel>
22
+ <rel-resizable-handle></rel-resizable-handle>
23
+ <rel-resizable-panel defaultsize="60">Bottom</rel-resizable-panel>
24
+ </rel-resizable-group>
25
+
26
+ <!-- Constraints: percentages of the group -->
27
+ <rel-resizable-group style="height: 320px;">
28
+ <rel-resizable-panel defaultsize="30" minsize="20" maxsize="50">Sidebar</rel-resizable-panel>
29
+ <rel-resizable-handle></rel-resizable-handle>
30
+ <rel-resizable-panel defaultsize="70">Main</rel-resizable-panel>
31
+ </rel-resizable-group>
32
+
33
+ <!-- Nested: a vertical group inside the right panel of a horizontal one -->
34
+ <rel-resizable-group direction="horizontal" style="height: 480px;">
35
+ <rel-resizable-panel defaultsize="33">Sidebar</rel-resizable-panel>
36
+ <rel-resizable-handle></rel-resizable-handle>
37
+ <rel-resizable-panel defaultsize="67">
38
+ <rel-resizable-group direction="vertical">
39
+ <rel-resizable-panel defaultsize="50">Editor</rel-resizable-panel>
40
+ <rel-resizable-handle></rel-resizable-handle>
41
+ <rel-resizable-panel defaultsize="50">Preview</rel-resizable-panel>
42
+ </rel-resizable-group>
43
+ </rel-resizable-panel>
44
+ </rel-resizable-group>
45
+
46
+ <!-- Listening for size changes -->
47
+ <rel-resizable-group id="split" style="height: 320px;">
48
+ <rel-resizable-panel>A</rel-resizable-panel>
49
+ <rel-resizable-handle></rel-resizable-handle>
50
+ <rel-resizable-panel>B</rel-resizable-panel>
51
+ </rel-resizable-group>
52
+ <script>
53
+ const split = document.getElementById('split');
54
+ // fires continuously while dragging
55
+ split.addEventListener('rel-resize', (e) => console.log(e.detail.sizes));
56
+ // fires once per drag, and after every keyboard step — good for persisting
57
+ split.addEventListener('rel-resize-end', (e) => localStorage.setItem('split', JSON.stringify(e.detail.sizes)));
58
+ </script>
59
+ ```
60
+
61
+ <!-- readme-gen:start -->
62
+
63
+ <!-- Generated from rel-resizable-group.ts, rel-resizable-handle.ts, rel-resizable-panel.ts — do not edit; run readme-generate.mjs -->
64
+
65
+ ## Elements
66
+
67
+ | Element | Class | Source |
68
+ | --- | --- | --- |
69
+ | `<rel-resizable-group>` | `RelResizableGroup` | `rel-resizable-group.ts` |
70
+ | `<rel-resizable-handle>` | `RelResizableHandle` | `rel-resizable-handle.ts` |
71
+ | `<rel-resizable-panel>` | `RelResizablePanel` | `rel-resizable-panel.ts` |
72
+
73
+ ## API
74
+
75
+ ### `<rel-resizable-group>`
76
+
77
+ #### Attributes / Properties
78
+
79
+ | Property | Attribute | Type | Default | Description |
80
+ | --- | --- | --- | --- | --- |
81
+ | `direction` | `direction` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout axis: `horizontal` places panels side by side and resizes widths, `vertical` stacks them and resizes heights; also pushed to every child handle's `orientation`. Reflected. |
82
+
83
+ ### Events
84
+
85
+ | Event | Detail | Bubbles / composed | Description |
86
+ | --- | --- | --- | --- |
87
+ | `rel-resize` | `{ direction: this.direction, sizes, }` | bubbles / composed | Fired continuously while a handle is dragged. Detail: `{ direction, sizes: number[] }` with one percentage per panel. |
88
+ | `rel-resize-end` | `{ direction: this.direction, sizes, }` | bubbles / composed | Fired once when a drag ends (pointerup) and after every keyboard resize step. Detail: `{ direction, sizes: number[] }` with one percentage per panel. |
89
+
90
+ ### Slots
91
+
92
+ | Slot | Description |
93
+ | --- | --- |
94
+ | (default) | The panels and handles, as direct children in display order (panel, handle, panel, …) |
95
+
96
+ ### CSS Parts
97
+
98
+ | Part | Description |
99
+ | --- | --- |
100
+ | — | None exposed. |
101
+
102
+ ### `<rel-resizable-handle>`
103
+
104
+ #### Attributes / Properties
105
+
106
+ | Property | Attribute | Type | Default | Description |
107
+ | --- | --- | --- | --- | --- |
108
+ | `orientation` | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Axis the handle separates: `horizontal` = 8px-wide vertical bar with `col-resize` cursor, `vertical` = 8px-tall bar with `row-resize`; set automatically from the parent group's `direction` on first render and mirrored to `aria-orientation`. Reflected. |
109
+
110
+ #### Public fields / accessors
111
+
112
+ | Name | Type | Description |
113
+ | --- | --- | --- |
114
+ | `splitValue` | `number` | Current split of the two neighbouring panels as a percentage 0–100 (share held by the leading panel); read-only — the parent group updates it and mirrors it to `aria-valuenow`. |
115
+
116
+ ### Events
117
+
118
+ | Event | Detail | Bubbles / composed | Description |
119
+ | --- | --- | --- | --- |
120
+ | — | — | — | This element emits no custom events. |
121
+
122
+ ### Slots
123
+
124
+ | Slot | Description |
125
+ | --- | --- |
126
+ | — | This element has no slots. |
127
+
128
+ ### CSS Parts
129
+
130
+ | Part | Description |
131
+ | --- | --- |
132
+ | — | None exposed. |
133
+
134
+ ### `<rel-resizable-panel>`
135
+
136
+ #### Attributes / Properties
137
+
138
+ | Property | Attribute | Type | Default | Description |
139
+ | --- | --- | --- | --- | --- |
140
+ | `defaultSize` | `defaultsize` | `number` | `undefined` | Initial size as a percentage of the group; panels without one share whatever is left of 100%, then all sizes are normalised to sum to 100. Attribute is `defaultsize`. |
141
+ | `minSize` | `minsize` | `number` | `10` | Smallest size (percentage) the panel can be resized to. Attribute is `minsize`. |
142
+ | `maxSize` | `maxsize` | `number` | `90` | Largest size (percentage) the panel can be resized to. Attribute is `maxsize`. |
143
+
144
+ #### Public fields / accessors
145
+
146
+ | Name | Type | Description |
147
+ | --- | --- | --- |
148
+ | `currentSize` | `number` | Current size as a percentage of the group (also written to the host's `flex-basis`); read-only — the parent group updates it on every resize. |
149
+
150
+ ### Events
151
+
152
+ | Event | Detail | Bubbles / composed | Description |
153
+ | --- | --- | --- | --- |
154
+ | — | — | — | This element emits no custom events. |
155
+
156
+ ### Slots
157
+
158
+ | Slot | Description |
159
+ | --- | --- |
160
+ | (default) | The panel's content; slotted `h1`–`h4` get the headings-md role |
161
+
162
+ ### CSS Parts
163
+
164
+ | Part | Description |
165
+ | --- | --- |
166
+ | — | None exposed. |
167
+
168
+ <!-- readme-gen:end -->
169
+
170
+ ## Accessibility
171
+
172
+ - Each `<rel-resizable-handle>` makes itself focusable (`tabindex="0"`) and takes `role="separator"` with `aria-orientation` mirroring its `orientation`. The group writes `aria-valuenow` (the leading panel's share of the pair, rounded, 0–100), `aria-valuemin="0"` and `aria-valuemax="100"` on first render and after every resize, so assistive technology can read the current split before and after any interaction.
173
+ - Handles have no accessible name of their own; when several handles are on a page add `aria-label` to each (e.g. `aria-label="Resize sidebar"`).
174
+ - The group and the panels set no roles and are not focusable; panels are `overflow: auto`, so their content scrolls natively and any interactive content inside keeps its own tab order.
175
+ - A focused handle shows a 2px focus ring (`--rel-border-focus`) and a lighter background; hover and drag states change the background and grab-bar colours only.
176
+ - Keyboard resizing (handled by the group, on a focused handle):
177
+
178
+ | Key | Direction | Action |
179
+ | --- | --- | --- |
180
+ | `ArrowLeft` / `ArrowRight` | `horizontal` | Shrink / grow the leading panel by 2% (of the group). |
181
+ | `ArrowUp` / `ArrowDown` | `vertical` | Shrink / grow the leading panel by 2%. |
182
+ | `Shift` + arrow | either | Same, in 5% steps. |
183
+ | `Home` | either | Collapse the leading panel to its `minsize` (as far as the neighbour allows). |
184
+ | `End` | either | Grow the leading panel to its `maxsize` (as far as the neighbour allows). |
185
+
186
+ - Every keyboard step dispatches `rel-resize-end` (not `rel-resize`).
187
+ - Motion: the handle's background transitions are removed under `prefers-reduced-motion: reduce`.
188
+
189
+ ## Notes / Edge Cases
190
+
191
+ - Panels and handles must be **direct children** of the group (`:scope > rel-resizable-panel`), alternating panel / handle / panel. A handle at the start or end, or two handles in a row, has no valid pair and does nothing.
192
+ - Layout is computed once in `firstUpdated`: panels without `defaultsize` share whatever remains of 100%, then all sizes are normalised to sum to exactly 100 (so `defaultsize="50"` + `defaultsize="70"` ends up as 41.7 / 58.3). Panels or handles added or removed later are **not** observed; re-attach the group to re-initialise.
193
+ - Sizes are always percentages of the group, applied as `flex-basis` with `flex-grow: 0; flex-shrink: 0`. The group itself is `display: flex; width: 100%; height: 100%`, so a `vertical` group needs an ancestor with a definite height or it has nothing to divide.
194
+ - Only the two panels adjacent to the dragged handle change; each is clamped to its own `minsize` / `maxsize`, then the pair is rescaled so their combined share stays constant. A panel therefore cannot go below its `minsize` (default 10) and never collapses to 0 unless you set `minsize="0"`.
195
+ - Attribute names are lowercase with no hyphen: `defaultsize`, `minsize`, `maxsize` (properties `defaultSize`, `minSize`, `maxSize`).
196
+ - While a drag is in progress the active handle carries a `data-dragging` attribute (the group sets and removes it) — the only hook for drag-specific styling from outside.
197
+ - Each handle's `orientation` is overwritten from the group's `direction` on first render; set `direction` on the group, not `orientation` on the handles.
198
+ - `rel-resize` fires on every pointer move during a drag; throttle any expensive listener. `rel-resize-end` fires once on pointer-up and after each keyboard step; both carry `{ direction, sizes }` where `sizes[i]` is panel *i*'s current percentage.
199
+ - Nested groups work because the inner group is ordinary panel content; pointer events are handled by the nearest group whose handle is in the event's composed path.