@reliability-design/web 1.4.6 → 1.4.8

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 (148) hide show
  1. package/CHANGELOG.md +246 -49
  2. package/dist/components/rel-accordion/README.md +133 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +172 -61
  5. package/dist/components/rel-avatar/README.md +118 -130
  6. package/dist/components/rel-badge/README.md +97 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +190 -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 +186 -166
  13. package/dist/components/rel-checkbox/README.md +96 -75
  14. package/dist/components/rel-chip/README.md +138 -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 +88 -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-layout/README.md +34 -0
  21. package/dist/components/rel-menu/README.md +180 -151
  22. package/dist/components/rel-menubar/README.md +218 -329
  23. package/dist/components/rel-otp/README.md +171 -440
  24. package/dist/components/rel-pagination/README.md +100 -424
  25. package/dist/components/rel-popover/README.md +203 -568
  26. package/dist/components/rel-progress/README.md +88 -110
  27. package/dist/components/rel-radio/README.md +131 -138
  28. package/dist/components/rel-range-picker/README.md +165 -0
  29. package/dist/components/rel-resizable/README.md +199 -140
  30. package/dist/components/rel-segmented-button/README.md +156 -141
  31. package/dist/components/rel-select/README.md +156 -93
  32. package/dist/components/rel-sheet/README.md +168 -224
  33. package/dist/components/rel-skeleton/README.md +104 -0
  34. package/dist/components/rel-slider/README.md +131 -135
  35. package/dist/components/rel-snackbar/README.md +168 -134
  36. package/dist/components/rel-stepper/README.md +162 -160
  37. package/dist/components/rel-switch/README.md +117 -102
  38. package/dist/components/rel-tab/README.md +173 -163
  39. package/dist/components/rel-textfield/README.md +176 -69
  40. package/dist/components/rel-time-picker/README.md +122 -157
  41. package/dist/components/rel-toast/README.md +165 -148
  42. package/dist/components/rel-tooltip/README.md +119 -111
  43. package/dist/components/rel-upload/README.md +220 -0
  44. package/dist/index.d.mts +1726 -447
  45. package/dist/index.d.ts +1726 -447
  46. package/dist/index.js +13715 -10870
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +14018 -11163
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/styles/focus-ring.ts +84 -0
  51. package/dist/styles/vars.css +892 -866
  52. package/dist/{vars-XNMTSRVN.css → vars-DXX6QOAG.css} +892 -866
  53. package/package.json +3 -2
  54. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  55. package/dist/components/rel-alert/rel-alert.ts +0 -305
  56. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  57. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  58. package/dist/components/rel-badge/rel-badge.ts +0 -175
  59. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  60. package/dist/components/rel-button/rel-button.ts +0 -560
  61. package/dist/components/rel-calendar/index.ts +0 -1
  62. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  63. package/dist/components/rel-card/rel-card.ts +0 -88
  64. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  65. package/dist/components/rel-carousel/index.ts +0 -1
  66. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  67. package/dist/components/rel-chart/README.md +0 -286
  68. package/dist/components/rel-chart/index.ts +0 -1
  69. package/dist/components/rel-chart/rel-chart.ts +0 -754
  70. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  71. package/dist/components/rel-chip/rel-chip.ts +0 -483
  72. package/dist/components/rel-container/rel-container.ts +0 -95
  73. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  74. package/dist/components/rel-date-picker/date-format.ts +0 -47
  75. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  76. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  77. package/dist/components/rel-date-picker/index.ts +0 -1
  78. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  79. package/dist/components/rel-divider/rel-divider.ts +0 -54
  80. package/dist/components/rel-drawer/index.ts +0 -1
  81. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  82. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  83. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  84. package/dist/components/rel-layout/rel-layout.ts +0 -49
  85. package/dist/components/rel-menu/rel-menu.ts +0 -541
  86. package/dist/components/rel-menubar/index.ts +0 -2
  87. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  88. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  89. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  90. package/dist/components/rel-nav-menu/README.md +0 -289
  91. package/dist/components/rel-nav-menu/index.ts +0 -3
  92. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  93. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  94. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  95. package/dist/components/rel-navbar/README.md +0 -126
  96. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  97. package/dist/components/rel-otp/index.ts +0 -1
  98. package/dist/components/rel-otp/rel-otp.ts +0 -527
  99. package/dist/components/rel-pagination/index.ts +0 -1
  100. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  101. package/dist/components/rel-popover/index.ts +0 -2
  102. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  103. package/dist/components/rel-popover/rel-popover.ts +0 -475
  104. package/dist/components/rel-progress/rel-progress.ts +0 -234
  105. package/dist/components/rel-radio/rel-radio.ts +0 -481
  106. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  107. package/dist/components/rel-resizable/index.ts +0 -3
  108. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  109. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  110. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  111. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  112. package/dist/components/rel-select/rel-select.ts +0 -1416
  113. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  114. package/dist/components/rel-sidebar/README.md +0 -209
  115. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  116. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  117. package/dist/components/rel-slider/rel-slider.ts +0 -673
  118. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  119. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  120. package/dist/components/rel-switch/rel-switch.ts +0 -199
  121. package/dist/components/rel-tab/rel-tab.ts +0 -264
  122. package/dist/components/rel-table/README.md +0 -178
  123. package/dist/components/rel-table/rel-table-body.ts +0 -22
  124. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  125. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  126. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  127. package/dist/components/rel-table/rel-table-head.ts +0 -47
  128. package/dist/components/rel-table/rel-table-header.ts +0 -22
  129. package/dist/components/rel-table/rel-table-row.ts +0 -37
  130. package/dist/components/rel-table/rel-table.ts +0 -62
  131. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  132. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  133. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  134. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  135. package/dist/components/rel-toast/rel-toast.ts +0 -206
  136. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  137. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  138. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  139. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  140. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  141. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  142. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  144. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  145. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  146. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  147. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  148. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -0,0 +1,165 @@
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
+ | `size` | `size` | `"small" \| "medium" \| "large" \| "sm" \| "md" \| "lg"` | `"medium"` | Trigger height on the shared field scale — `small` 32 / `medium` 40 / `large` 48px border-box — matching rel-textfield, rel-date-picker and rel-time-picker so the four align in a form row. Also ramps the trigger's text and inline padding. The panel is unaffected: Figma specifies one panel size. `sm` / `md` / `lg` are alternative spellings and stay supported — normalised to the full words, not deprecated. Reflected. |
85
+
86
+ ### Public fields / accessors
87
+
88
+ | Name | Type | Description |
89
+ | --- | --- | --- |
90
+ | `displayLabel` | `string` | Formatted "MM/DD/YYYY → MM/DD/YYYY" (or single date / "") for the current committed `from`/`to`. |
91
+
92
+ ## Events
93
+
94
+ | Event | Detail | Bubbles / composed | Description |
95
+ | --- | --- | --- | --- |
96
+ | `rel-blur` | — | bubbles / composed | Fired when the text input loses focus (before any typed text is committed). No detail. |
97
+ | `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). |
98
+ | `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 }`. |
99
+
100
+ ## Slots
101
+
102
+ | Slot | Description |
103
+ | --- | --- |
104
+ | — | This element has no slots. |
105
+
106
+ ## CSS Parts
107
+
108
+ | Part | Description |
109
+ | --- | --- |
110
+ | `trigger` | The field box that wraps the text input and the clear/calendar icons (default trigger variant only) |
111
+ | `value` | The editable text input inside the trigger |
112
+ | `overlay` | The full-screen backdrop rendered in `mode="popup"` while open |
113
+ | `panel` | The floating panel (`role="dialog"`) holding presets and calendar |
114
+ | `list` | The preset column and the day grid |
115
+ | `option` | Each quick-select preset button |
116
+ | `action` | The header's previous and next buttons; they step a month, a year or a 12-year page depending on which view the panel is showing |
117
+ | `footer` | The Reset / Cancel / Apply button row |
118
+
119
+ <!-- readme-gen:end -->
120
+
121
+ ## Accessibility
122
+
123
+ - 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`).
124
+ - 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`.
125
+ - 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.
126
+ - 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).
127
+ - 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.
128
+ - 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`).
129
+
130
+ | Key (in the field) | Action |
131
+ | --- | --- |
132
+ | Enter | Nothing typed: open the panel. Text typed: commit it |
133
+ | ArrowDown | Open the panel |
134
+ | Escape | Revert uncommitted text; close the panel and refocus the input |
135
+
136
+ | Key (panel open, focus outside an editable field) | Action |
137
+ | --- | --- |
138
+ | T / M / Y / W / N / 7 / 3 | Select the matching preset into the draft |
139
+ | Escape | Cancel: discard the draft, close, refocus the input |
140
+
141
+ | Key (in the grid) | Action |
142
+ | --- | --- |
143
+ | ArrowLeft / ArrowRight | Previous / next day (switches month at the edge) |
144
+ | ArrowUp / ArrowDown | Same weekday one week earlier / later |
145
+ | Home / End | First / last day of the row, honouring `week-start` |
146
+ | PageUp / PageDown | Same day previous / next month |
147
+ | Shift + PageUp / Shift + PageDown | Same day previous / next year |
148
+ | Enter / Space | Pick the day as range start, then as range end |
149
+
150
+ ## Notes / Edge Cases
151
+
152
+ - **The header month and year are buttons.** Clicking the month opens a 12-month grid; clicking the year opens a 12-year page that pages back indefinitely with the chevrons, so a date years in the past is a few clicks rather than a few dozen. Picking a year drills to Months, picking a month returns to the day grid. The chevrons step whatever the current view shows — a month, a year or a whole page — and the panel always reopens on the day grid.
153
+ - **Navigating does not select.** The switcher moves the VIEW only: it writes to the month and year on display and never to the draft or the committed range, so Reset, Cancel and Apply behave exactly as they did, and reopening still seeds the view from the committed start date.
154
+ - 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.
155
+ - Second-click ordering: picking a day before the current draft start makes it the new start and the old start the end.
156
+ - 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.
157
+ - 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.
158
+ - 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.
159
+ - 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.
160
+ - 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.
161
+ - `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.
162
+ - The icon trigger has no typed entry, no clear icon and no error line; parts `trigger` and `value` are not rendered in that variant.
163
+ - There is no helper-text or external error API; the only text below the field is the typed-entry error.
164
+ - 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.
165
+ - 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.