panelset 1.0.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +213 -0
- package/dist/panelset-core.js +1119 -0
- package/dist/panelset-core.js.map +1 -0
- package/dist/panelset.css +2 -1
- package/dist/panelset.esm.js +2 -0
- package/dist/panelset.js +1 -432
- package/dist/register.js +6 -0
- package/dist/register.js.map +1 -0
- package/dist/types/functions/config.d.ts +18 -0
- package/dist/types/functions/core.d.ts +11 -0
- package/dist/types/functions/focus.d.ts +16 -0
- package/dist/types/functions/persist.d.ts +7 -0
- package/dist/types/functions/pinning.d.ts +6 -0
- package/dist/types/functions/utils.d.ts +42 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.iife.d.ts +1 -0
- package/dist/types/panel.d.ts +56 -0
- package/dist/types/panel.element.d.ts +4 -0
- package/dist/types/panel.types.d.ts +28 -0
- package/dist/types/panelcontrol.d.ts +64 -0
- package/dist/types/panelcontrol.element.d.ts +9 -0
- package/dist/types/panelcontrol.types.d.ts +14 -0
- package/dist/types/panelset.d.ts +149 -0
- package/dist/types/panelset.element.d.ts +4 -0
- package/dist/types/panelset.esm.d.ts +6 -0
- package/dist/types/panelset.types.d.ts +89 -0
- package/dist/types/register.d.ts +1 -0
- package/package.json +30 -18
- package/dist/index.d.ts +0 -119
- package/dist/panelset.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-06-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- PanelControl: a tablist controller with keyboard navigation
|
|
7
|
+
- Web component versions
|
|
8
|
+
- PNavigation on PanelSet: `next()`, `prev()` etc
|
|
9
|
+
- `addPanel()` and `removePanel()`
|
|
10
|
+
- `refresh()`
|
|
11
|
+
- Verb buttons wired by delegation: `data-ps-next`, `data-ps-prev`, `data-ps-close`.
|
|
12
|
+
- Lifecycle events
|
|
13
|
+
- Async content loading
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [1.0.9] - 2026-04-10
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Added Panel
|
|
20
|
+
- New documentation
|
|
21
|
+
- Added destroy() method
|
|
22
|
+
- Added data-panel-trigger implicit wiring (auto ID assignment)
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Fixed mid-close reversal bug (smooth re-open from mid-animation)
|
|
26
|
+
- Fixed focus jump when closeSiblings closes a sibling
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
- Removed Core.measure()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## [1.0.8] - 2026-02-18
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Removed aria-selected.
|
|
37
|
+
|
|
38
|
+
|
|
3
39
|
## [1.0.7] - 2026-02-18
|
|
4
40
|
|
|
5
41
|
### Changed
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# PanelSet
|
|
2
|
+
|
|
3
|
+
**Flexible panel management with smooth transitions.**
|
|
4
|
+
|
|
5
|
+
A TypeScript + SCSS library for animating elements between sizes. Three classes share one animation core (a lock / measure / animate / unlock cycle): the browser does the animating, JavaScript only measures and sets pixel values. Accessible by default (managed ARIA and focus), interrupt-safe, and it respects `prefers-reduced-motion`.
|
|
6
|
+
|
|
7
|
+
> **Documentation:** this README is a quick reference. The full guides, every option, and live examples live in the HTML docs. Source and issues: <https://github.com/martinomagnifico/panelset>.
|
|
8
|
+
|
|
9
|
+
## The three classes
|
|
10
|
+
|
|
11
|
+
| Class | What it does |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **`Panel`** | A single element that opens and closes (accordions, show-more, sidebars, drawers). Animates `height` or `width`. |
|
|
14
|
+
| **`PanelSet`** | A container that switches between mutually exclusive panels (tabs, wizards, steppers). Animates its own height to fit the incoming panel. |
|
|
15
|
+
| **`PanelControl`** | Optional. Makes a tab strip or sidebar drive a `PanelSet`: keyboard navigation, roving `tabindex`, `aria-selected`, and tab locking through `setTabState()`. |
|
|
16
|
+
|
|
17
|
+
`PanelControl` is side-effect-free, so it tree-shakes out of the ESM build when you don’t import it.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install panelset
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { Panel, PanelSet, PanelControl } from 'panelset';
|
|
27
|
+
import 'panelset/style.css';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or as a script tag (IIFE bundle):
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<link rel="stylesheet" href="panelset.css">
|
|
34
|
+
<script src="panelset.js"></script>
|
|
35
|
+
<!-- exposes window.Panel / PanelSet / PanelControl and registers
|
|
36
|
+
<ps-panel> / <ps-panelset> / <ps-panelcontrol> -->
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Panel
|
|
40
|
+
|
|
41
|
+
Panels are **closed by default** (no class needed). Add `is-open` to start open.
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<button aria-controls="my-panel" aria-expanded="false">Toggle</button>
|
|
45
|
+
|
|
46
|
+
<div id="my-panel" data-panel>
|
|
47
|
+
<div class="panel-wrapper">Content here</div>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { Panel } from 'panelset';
|
|
53
|
+
Panel.init();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Any `[aria-controls]` element is a trigger. As a shortcut, a `[data-panel-trigger]` button placed next to a panel (or as the direct child of a heading next to it) gets its `id` and ARIA set up automatically.
|
|
57
|
+
|
|
58
|
+
### Accordion (group)
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<div data-panel-group data-panel-close-siblings>
|
|
62
|
+
<button aria-controls="acc-1" aria-expanded="false">Item 1</button>
|
|
63
|
+
<div id="acc-1" data-panel><div class="panel-wrapper">Answer 1</div></div>
|
|
64
|
+
|
|
65
|
+
<button aria-controls="acc-2" aria-expanded="false">Item 2</button>
|
|
66
|
+
<div id="acc-2" data-panel><div class="panel-wrapper">Answer 2</div></div>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`data-panel-close-siblings` (or `closeSiblings: true`) makes opening one panel close the others.
|
|
71
|
+
|
|
72
|
+
### Panel options
|
|
73
|
+
|
|
74
|
+
| Option | Type | Default | Description |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `axis` | `'vertical' \| 'horizontal'` | `'vertical'` | Which dimension animates (height or width) |
|
|
77
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Content alignment within the clipped container |
|
|
78
|
+
| `transitions` | `boolean` | `true` | Enable/disable CSS transitions |
|
|
79
|
+
| `autoFocus` | `false \| true \| 'heading' \| 'first' \| 'input'` | `false` | Move focus into the panel on open |
|
|
80
|
+
| `returnFocus` | `boolean` | `true` | Return focus to the trigger on close |
|
|
81
|
+
| `closeSiblings` | `boolean` | `false` | Close other open panels in the same group |
|
|
82
|
+
| `closeOnResize` | `boolean` | `false` | Close the panel when the window is resized |
|
|
83
|
+
| `interruptible` | `boolean` | `true` | Allow a new open/close to interrupt one in progress |
|
|
84
|
+
| `persist` | `boolean` | `false` | Save open/closed state to `localStorage` |
|
|
85
|
+
| `deepLink` | `boolean` | `false` | Reflect open state in the `?panel=` URL |
|
|
86
|
+
| `loadingDelay` | `number` | `320` | ms before the spinner appears during async loading |
|
|
87
|
+
| `loadingHeight` | `number` | `150` | px reserved while async content loads |
|
|
88
|
+
| `debug` | `boolean` | `false` | Log events to the console |
|
|
89
|
+
|
|
90
|
+
### Async content
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
const panel = new Panel('#my-panel');
|
|
94
|
+
|
|
95
|
+
panel.onBeforeOpen((el, signal) => {
|
|
96
|
+
return fetch('/api/content', { signal })
|
|
97
|
+
.then(r => r.text())
|
|
98
|
+
.then(html => { el.querySelector('.panel-wrapper').innerHTML = html; });
|
|
99
|
+
}, { once: true });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or listen for the `panel:beforeopen` event and call `e.detail.waitUntil(promise)` to hold the open until it resolves.
|
|
103
|
+
|
|
104
|
+
## PanelSet
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<nav role="tablist" data-panelcontrol>
|
|
108
|
+
<button role="tab" aria-controls="panel-1" aria-selected="true">Tab 1</button>
|
|
109
|
+
<button role="tab" aria-controls="panel-2">Tab 2</button>
|
|
110
|
+
</nav>
|
|
111
|
+
|
|
112
|
+
<div data-panelset>
|
|
113
|
+
<div class="panel-wrapper">
|
|
114
|
+
<div id="panel-1" role="tabpanel" class="active">Content 1</div>
|
|
115
|
+
<div id="panel-2" role="tabpanel" hidden>Content 2</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import { PanelSet, PanelControl } from 'panelset';
|
|
122
|
+
PanelSet.init();
|
|
123
|
+
PanelControl.init(); // keyboard navigation + state for the tab strip
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Mark the starting panel with `class="active"` and every other panel `hidden`.
|
|
127
|
+
|
|
128
|
+
### PanelSet options
|
|
129
|
+
|
|
130
|
+
| Option | Type | Default | Description |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Alignment while opening/closing |
|
|
133
|
+
| `transitions` | `boolean \| { panels?: boolean, height?: boolean }` | `true` | Enable/disable transitions (or per axis) |
|
|
134
|
+
| `levels` | `boolean` | `false` | Give panels a depth order from DOM position; forward/back slide opposite ways |
|
|
135
|
+
| `loop` | `boolean` | `false` | `next()` / `prev()` wrap around the ends |
|
|
136
|
+
| `closable` | `boolean` | `false` | Allow the whole container to open and close |
|
|
137
|
+
| `closeOnTab` | `boolean` | `false` | Clicking the active tab closes the container (needs `closable`) |
|
|
138
|
+
| `disabledMode` | `'aria' \| 'native'` | `'aria'` | How `data-ps-next` / `-prev` buttons are disabled at the ends |
|
|
139
|
+
| `autoFocus` | `false \| true \| 'heading' \| 'first' \| 'input'` | `false` | Move focus into the panel on activation |
|
|
140
|
+
| `returnFocus` | `boolean` | `false` | Return focus to the trigger on close |
|
|
141
|
+
| `interruptible` | `boolean` | `true` | Allow a new activation to interrupt one in progress |
|
|
142
|
+
| `manageTriggers` | `boolean` | `true` | Reflect `aria-selected` / activating state onto `[aria-controls]` triggers |
|
|
143
|
+
| `manageLabels` | `boolean` | `true` | Link each panel to its tab via `aria-labelledby` (auto-generates a tab id if needed) |
|
|
144
|
+
| `persist` | `boolean` | `false` | Save the active panel id to `localStorage` |
|
|
145
|
+
| `deepLink` | `boolean` | `false` | Reflect the active panel in the `?panel=` URL |
|
|
146
|
+
| `loadingDelay` | `number` | `320` | ms before the spinner appears during async loading |
|
|
147
|
+
| `loadingHeight` | `number` | `150` | px reserved while async content loads |
|
|
148
|
+
| `debug` | `boolean` | `false` | Log events to the console |
|
|
149
|
+
|
|
150
|
+
### Async content
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
const ps = new PanelSet('#my-panelset');
|
|
154
|
+
|
|
155
|
+
ps.onBeforeOpen((targetPanel, signal) => {
|
|
156
|
+
return fetch(`/api/${targetPanel.id}`, { signal })
|
|
157
|
+
.then(r => r.json())
|
|
158
|
+
.then(data => { targetPanel.innerHTML = render(data); });
|
|
159
|
+
}, { once: true });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## PanelControl
|
|
163
|
+
|
|
164
|
+
Drives one `PanelSet` from its trigger elements, so you do not hand-write the tab interaction. Put `data-panelcontrol` on the container; add `role="tablist"` (with `role="tab"` buttons) to switch on the keyboard model (arrow keys, `Home` / `End`, roving `tabindex`). It finds its `PanelSet` through the panels the triggers point at, so the control can live anywhere in the DOM.
|
|
165
|
+
|
|
166
|
+
Lock or unlock a tab from your own code:
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
control.setTabState('panel-3', 'disabled'); // lock
|
|
170
|
+
control.setTabState('panel-3', 'enabled'); // unlock
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
| Option | Type | Default | Description |
|
|
174
|
+
|---|---|---|---|
|
|
175
|
+
| `activation` | `'manual' \| 'auto'` | `'manual'` | `manual`: arrows move focus, Enter/Space/click activates. `auto`: arrows activate too. |
|
|
176
|
+
| `debug` | `boolean` | `false` | Log to the console |
|
|
177
|
+
|
|
178
|
+
## Configuration sources
|
|
179
|
+
|
|
180
|
+
Every option can also be set as a **data attribute** in the markup, or as a plain attribute on the **web component**. When the same option is set more than once, the most specific wins: **defaults -> JS options -> data attribute**. The exact attribute names are listed per option in the docs.
|
|
181
|
+
|
|
182
|
+
## Web components
|
|
183
|
+
|
|
184
|
+
`register()` defines `<ps-panel>`, `<ps-panelset>`, and `<ps-panelcontrol>` (the script-tag build calls it for you). On the elements, options are plain attributes, no `data-` prefix.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
import { register } from 'panelset';
|
|
188
|
+
register(); // default 'ps' prefix
|
|
189
|
+
register('acme'); // <acme-panel>, <acme-panelset>, <acme-panelcontrol>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## CSS
|
|
193
|
+
|
|
194
|
+
Timing and sizing are CSS custom properties, set on the element or any ancestor (all timing values need a unit, e.g. `0.25s` not `0`). For example, on a Panel:
|
|
195
|
+
|
|
196
|
+
```css
|
|
197
|
+
[data-panel] {
|
|
198
|
+
--ps-open-speed: 0.25s;
|
|
199
|
+
--ps-open-timing: ease-in-out;
|
|
200
|
+
--ps-close-speed: 0.25s;
|
|
201
|
+
--ps-close-timing: ease-in-out;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`PanelSet` exposes a similar set for its fade and height timing. See the docs for the full list.
|
|
206
|
+
|
|
207
|
+
## Support
|
|
208
|
+
|
|
209
|
+
PanelSet is free and open source. If it saves you time, consider [sponsoring my work](https://ko-fi.com/martinomagnifico).
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT © [Martinomagnifico](https://github.com/martinomagnifico)
|