@aiquants/resize-panels 2.1.0 → 2.3.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/README.md +153 -15
- package/dist/{GlobalDebugOverlay-CIiVujEL.js → GlobalDebugOverlay-CbqXJA1A.js} +1 -1
- package/dist/{GlobalDebugOverlay-DA1__b7e.cjs → GlobalDebugOverlay-R4lLTee4.cjs} +1 -1
- package/dist/index-BLe_thse.js +1500 -0
- package/dist/index-DMDH6fMm.cjs +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +29 -27
- package/dist/labels.d.ts +33 -0
- package/dist/placeholderFocus.d.ts +6 -0
- package/dist/styles/resize-panels.standalone.css +1 -1
- package/dist/types.d.ts +4 -0
- package/package.json +5 -3
- package/dist/index-BplkuFjn.cjs +0 -2
- package/dist/index-DlQDkOjq.js +0 -1332
package/README.md
CHANGED
|
@@ -11,9 +11,10 @@ Every state change — first paint, container resize, drag, collapse, restored l
|
|
|
11
11
|
- **Predictable proportions**: a flexible panel's size is a weight — only the ratio between weights matters, unless the panel declares `flexAdjustPriority`, which reads the same number as a claim on that share of the container. A dragged layout becomes a fixed point of the allocator, so it survives the next resize as a ratio instead of snapping back.
|
|
12
12
|
- **Directional Collapsible Control**: declarative `collapsible` settings (`from: 'start' | 'end' | 'both'`) and the `usePanelControls` hook support collapsing/expanding from drags and from UI controls; expanding restores the layout the panel had before it closed.
|
|
13
13
|
- **Keyboard & Touch Accessibility**: handles are `role="separator"` widgets whose `aria-valuenow` / `aria-valuemin` / `aria-valuemax` publish the boundary and the travel *this* handle can reach, collapsing included; a collapsed neighbour is reported as length 0 rather than making the values disappear. Arrow keys resize (5x step with Shift), reopen a collapsed neighbour from the side it closed towards and collapse a panel the pair's travel cannot shrink further, while a handle with nowhere to go leaves the key to the page. A drag needs the primary button and survives multi-touch and `pointercancel`.
|
|
14
|
+
- **A click instead of a drag**: give a handle `activationSizes` and a click or tap moves the boundary through those preset sizes — the single-pointer alternative to dragging that WCAG 2.5.7 asks for (see [Clicking instead of dragging](#clicking-instead-of-dragging)).
|
|
14
15
|
- **Auto-Persistence**: set `autoSaveId` to store each panel's open length and collapsed flag in `localStorage` next to its panel id, so a layout saved by a different set of panels is rejected instead of being applied by position.
|
|
15
16
|
- **Layout notifications with a cause**: `onLayout` receives the allocated lengths together with why they changed — a drag, a key, a host call, a container resize, a panel change or a restored layout — so a host can keep what the reader chose apart from what the container forced.
|
|
16
|
-
- **Debug & Visualization**: enable `showDebugInfo` to inspect allocations through the per-panel `PanelDebugInfo` badge and a global overlay (lazily imported; production bundles without it create no per-panel observers at all). A constraint violation needs no flag: the group draws its own placeholder over the panels whenever the allocator reports one
|
|
17
|
+
- **Debug & Visualization**: enable `showDebugInfo` to inspect allocations through the per-panel `PanelDebugInfo` badge and a global overlay (lazily imported; production bundles without it create no per-panel observers at all). A constraint violation needs no flag: the group draws its own [placeholder](#constraint-violation-placeholder) over the panels whenever the allocator reports one, with texts you can replace through `labels`.
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -130,6 +131,7 @@ export const Example = () => (
|
|
|
130
131
|
| `showDebugInfo` | `boolean` | Enables the per-panel `PanelDebugInfo` badge and the global debug overlay |
|
|
131
132
|
| `onLayout` | `(sizes: number[], change: PanelLayoutChange) => void` | Called with the panels' allocated pixel lengths (DOM order) each time they change once the container is measured; `change.source` says why (see [Layout Notifications](#layout-notifications)) |
|
|
132
133
|
| `autoSaveId` | `string` | Key used to automatically persist and restore panel sizes in `localStorage` |
|
|
134
|
+
| `labels` | `PanelGroupLabels` | Texts of the constraint-violation placeholder, in pairs: a reason's summary and detail go together, a reason left out keeps its built-in English pair, and an invalid value or half a pair throws when the group renders (see [Constraint-violation placeholder](#constraint-violation-placeholder)) |
|
|
133
135
|
|
|
134
136
|
### Panel Properties
|
|
135
137
|
|
|
@@ -154,14 +156,17 @@ from `PanelProps` to state that ownership, but TypeScript does not type-check JS
|
|
|
154
156
|
hyphen, so neither it nor a `data-panel-*` / `data-panel-group-*` of your own is rejected at compile time: what
|
|
155
157
|
enforces the contract is the runtime override, which applies these attributes after everything you forwarded. A
|
|
156
158
|
`data-panel-group-id` you pass disappears altogether when the group itself has no `id`. Both elements merge your
|
|
157
|
-
`className` with their own (`panel` / `panel-group`) instead of replacing it.
|
|
159
|
+
`className` with their own (`panel` / `panel-group`) instead of replacing it. While the group reports a constraint
|
|
160
|
+
violation, `Panel` also renders `inert`, whatever you passed: an `inert` of your own applies the rest of the time and
|
|
161
|
+
comes back once the violation ends.
|
|
158
162
|
|
|
159
163
|
### PanelResizeHandle Properties
|
|
160
164
|
|
|
161
165
|
| Property | Type | Description |
|
|
162
166
|
| --- | --- | --- |
|
|
163
167
|
| `id` | `string` | Handle identifier (reflected in `data-resize-handle-id`) |
|
|
164
|
-
| `disabled` | `boolean` | Disables
|
|
168
|
+
| `disabled` | `boolean` | Disables dragging, clicking and keyboard resizing when `true` |
|
|
169
|
+
| `activationSizes` | `readonly FlexibleSize[]` | Lengths of the panel before the handle that a click or tap moves the boundary through, in the units of a panel's `defaultSize` / `minSize` / `maxSize` (see [Clicking instead of dragging](#clicking-instead-of-dragging)); without it a click does nothing |
|
|
165
170
|
| `thickness` | `number` | Primary axis thickness of the handle's interactive area in pixels (`8px` default) |
|
|
166
171
|
| `indicator` | `boolean \| PanelResizeHandleIndicatorConfig` | Toggle indicator visibility or provide custom pill/bar dimensions and CSS classes |
|
|
167
172
|
| `indicatorThickness` | `number` | Shorthand for indicator pill wrapper thickness in pixels (`6px` default) |
|
|
@@ -169,7 +174,7 @@ enforces the contract is the runtime override, which applies these attributes af
|
|
|
169
174
|
| `indicatorBarThickness` | `number` | Shorthand for indicator inner bar thickness in pixels (`2px` default) |
|
|
170
175
|
| `indicatorBarLength` | `number` | Shorthand for indicator inner bar length in pixels (`32px` horizontal / `64px` vertical default) |
|
|
171
176
|
| `title` | `string` | Custom tooltip text for the handle element |
|
|
172
|
-
| `onDragging` | `(isDragging: boolean) => void` | Callback triggered
|
|
177
|
+
| `onDragging` | `(isDragging: boolean) => void` | Callback triggered when the primary button presses the handle and when the press ends (including `pointercancel`) — a drag, or a click on a handle with `activationSizes`; `false` comes only after every `'pointer'` change of the press has been reported to `onLayout` |
|
|
173
178
|
| `className` / `style` | `string` / `React.CSSProperties` | Additional styling for the resize handle |
|
|
174
179
|
| `children` | `React.ReactNode` | Custom indicator element rendered inside handle (overrides default indicator) |
|
|
175
180
|
|
|
@@ -191,6 +196,61 @@ from the same numbers and may redefine them; the declarations that keep the hit
|
|
|
191
196
|
box) are applied *after* it and take no override. While the group reports a constraint violation, every handle is
|
|
192
197
|
hidden (`display: none` on its anchor) as well as disabled.
|
|
193
198
|
|
|
199
|
+
### Clicking instead of dragging
|
|
200
|
+
|
|
201
|
+
WCAG 2.2 Success Criterion 2.5.7 (Dragging Movements, level AA) asks that anything done by dragging can also be done
|
|
202
|
+
with a single pointer and no drag; the arrow keys do not cover it, because a reader with a pointer may have no
|
|
203
|
+
keyboard. `activationSizes` is that alternative for a handle, in the form of technique G219: a click or a tap moves the
|
|
204
|
+
boundary through a list of preset lengths of the panel before the handle.
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { type FlexibleSize, Panel, PanelGroup, PanelResizeHandle } from "@aiquants/resize-panels"
|
|
208
|
+
|
|
209
|
+
// Keep the stops in one module-level constant: a new array on every render defeats the handle's memo.
|
|
210
|
+
const LIST_STOPS: readonly FlexibleSize[] = [
|
|
211
|
+
{ value: 200, unit: "pixels" },
|
|
212
|
+
{ value: 320, unit: "pixels" },
|
|
213
|
+
{ value: 480, unit: "pixels" },
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
<PanelGroup direction="horizontal">
|
|
217
|
+
<Panel id="list" defaultSize={{ value: 320, unit: "pixels" }} minSize={{ value: 200, unit: "pixels" }} maxSize={{ value: 480, unit: "pixels" }} />
|
|
218
|
+
<PanelResizeHandle activationSizes={LIST_STOPS} />
|
|
219
|
+
<Panel id="detail" />
|
|
220
|
+
</PanelGroup>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
- **Stops**: each entry is a length of the panel *before* the handle, written like a panel's `defaultSize` /
|
|
224
|
+
`minSize` / `maxSize` — `{ value, unit: "pixels" }`, `{ value, unit: "percentage" }`, or a bare number for a
|
|
225
|
+
percentage of the group's content box, resolved against the container's length at the moment of the click. The
|
|
226
|
+
order of the array does not matter.
|
|
227
|
+
- **Click or drag**: a press of the primary button (a mouse's main button, a touch or a pen) is a click when the
|
|
228
|
+
pointer is released before it travels **4 CSS px** from where it went down. The distance is the straight line in
|
|
229
|
+
viewport coordinates — the coordinates pointer events carry — taken before the drag converts the travel into the
|
|
230
|
+
group's layout pixels, so neither right-to-left text nor a scaled ancestor changes what counts as a click, and a
|
|
231
|
+
press that slides along the handle is not one. Until the pointer travels that far the boundary does not move at
|
|
232
|
+
all, so a click never nudges it; once it does, the press is an ordinary drag that catches up with the pointer, the
|
|
233
|
+
travel so far included. Only the pointer that pressed counts: other buttons, other pointers, and a press that ends
|
|
234
|
+
in `pointercancel` or lost pointer capture never click.
|
|
235
|
+
- **Cycling**: a click moves the boundary to the shortest stop longer than the panel's current length; from the
|
|
236
|
+
longest stop it returns to the shortest.
|
|
237
|
+
- **Clamping**: every stop is first clamped into the range a plain resize of the two panels can reach (both panels'
|
|
238
|
+
`minSize` / `maxSize` applied to their combined length), so a click never collapses a panel. A stop that lands
|
|
239
|
+
where the boundary already is gets skipped, and when every stop lands there the click changes nothing.
|
|
240
|
+
- **Collapsed neighbours**: as with the arrow keys, only the handle a panel collapsed towards can reopen it. A click on
|
|
241
|
+
that handle reopens the panel and moves the boundary to a stop in the same change; with either neighbour collapsed
|
|
242
|
+
the boundary sits at an end of the pair, so that stop is the shortest. A click on a handle that cannot reopen its
|
|
243
|
+
collapsed neighbour does nothing.
|
|
244
|
+
- **Notifications**: a click is a press like a drag. `onDragging(true)` comes when the button goes down, the click's
|
|
245
|
+
one change reaches `onLayout` as `'pointer'`, and `onDragging(false)` comes last, so a host that saves when a drag
|
|
246
|
+
ends saves a click the same way. `aria-valuenow` follows the boundary as always.
|
|
247
|
+
- **Name**: the built-in `aria-label` and `title` add `click to cycle through preset sizes` when stops are given; an
|
|
248
|
+
`aria-label` or `title` of your own still replaces them.
|
|
249
|
+
- **No stops**: without `activationSizes` (or with an empty array) a click does nothing, and a drag applies every move
|
|
250
|
+
as it comes, with no threshold.
|
|
251
|
+
- **Invalid stops**: a value that is not an array, or an entry that is not a non-negative finite size (`NaN`, a
|
|
252
|
+
negative length, an unknown unit, …), throws a `RangeError` when the handle renders instead of being guessed at.
|
|
253
|
+
|
|
194
254
|
## Hooks & Utilities
|
|
195
255
|
|
|
196
256
|
- `useResizablePanels`: the hook behind `PanelGroup`. It measures the container, keeps the panel order equal to DOM order, notifies `onLayout`, persists the layout and returns the group's base style — it never computes a panel size.
|
|
@@ -203,8 +263,9 @@ hidden (`display: none` on its anchor) as well as disabled.
|
|
|
203
263
|
- `saveLayout` & `loadLayout`: persist or restore a layout (`{ id, size, collapsed }[]`).
|
|
204
264
|
- `clamp`, `getConstraintInPixels`, `normalizeSizeConfig`, `percentageToPixels`, `toRoundedPercentage`, `calculateSnapThreshold`: the unit conversions and thresholds the components share.
|
|
205
265
|
- `getPanelElement`, `getPanelGroupElement`, `getResizeHandleElement`: DOM element query helpers via `data-*` attributes.
|
|
266
|
+
- `DEFAULT_PANEL_GROUP_LABELS`, `PANEL_GROUP_LABEL_PLACEHOLDERS`: the built-in English texts of the [constraint-violation placeholder](#constraint-violation-placeholder) and the placeholders each text accepts, both frozen.
|
|
206
267
|
|
|
207
|
-
Every public type (`PanelProps`, `PanelGroupProps`, `PanelResizeHandleProps`, `PanelLayoutData`, `PanelRegistration`, `PersistedPanelSize`, `PanelGroupLayoutState`, `PanelAction`, `PanelLayoutChange`, `PanelLayoutSource`, `PanelRequestSource`, `LayoutAllocation`, `PanelBounds`, `ContainerAxisMeasurement`, …) is re-exported from the package root; `docs/specs` (`00-architecture-overview.md` §8) lists the surface in full.
|
|
268
|
+
Every public type (`PanelProps`, `PanelGroupProps`, `PanelResizeHandleProps`, `PanelGroupLabels`, `PanelGroupLabelKey`, `PanelLayoutData`, `PanelRegistration`, `PersistedPanelSize`, `PanelGroupLayoutState`, `PanelAction`, `PanelLayoutChange`, `PanelLayoutSource`, `PanelRequestSource`, `LayoutAllocation`, `PanelBounds`, `ContainerAxisMeasurement`, …) is re-exported from the package root; `docs/specs` (`00-architecture-overview.md` §8) lists the surface in full.
|
|
208
269
|
|
|
209
270
|
## How sizes are decided
|
|
210
271
|
|
|
@@ -227,6 +288,81 @@ violation when the panels' maximums cannot reach the container, and as a logged
|
|
|
227
288
|
full statement, including the proofs of ratio preservation, continuity and the drag fixed point, is in `docs/specs`
|
|
228
289
|
(`11-layout-allocator.md`).
|
|
229
290
|
|
|
291
|
+
## Constraint-violation placeholder
|
|
292
|
+
|
|
293
|
+
When the panels' size constraints cannot fit the container — their minimums add up to more than it, or their
|
|
294
|
+
maximums cannot fill it — the allocator keeps the bounds and reports a violation, and the group draws a placeholder
|
|
295
|
+
over its panels until the container or the panels change enough to resolve it.
|
|
296
|
+
|
|
297
|
+
| Reason | Built-in summary | Built-in detail |
|
|
298
|
+
| --- | --- | --- |
|
|
299
|
+
| The minimums exceed the container | `Layout unavailable: minimum panel sizes exceed the container.` | `Min required: {required}px \| Available: {available}px` |
|
|
300
|
+
| The maximums cannot fill the container | `Layout unavailable: panels cannot fill the container with their maximum sizes.` | `Max total: {total}px \| Needed: {needed}px` |
|
|
301
|
+
|
|
302
|
+
- **Numbers**: `{required}` is the minimums' total rounded up and `{available}` the container's length rounded down;
|
|
303
|
+
`{total}` is the maximums' total rounded down and `{needed}` the container's length rounded up. What the panels
|
|
304
|
+
need is rounded up and what the other side offers is rounded down, so the two numbers shown always keep the strict
|
|
305
|
+
inequality behind the violation.
|
|
306
|
+
- **Labels**: pass `labels` (`PanelGroupLabels`) to replace a reason's texts as a pair — `minimumExceededSummary`
|
|
307
|
+
with `minimumExceededDetail`, and `maximumInsufficientSummary` with `maximumInsufficientDetail`. Pass both texts of
|
|
308
|
+
a pair or neither: a pair you leave out (both keys absent or `undefined`) keeps its built-in English texts from
|
|
309
|
+
`DEFAULT_PANEL_GROUP_LABELS`, and half a pair is rejected, by the type and when the group renders. A detail may use
|
|
310
|
+
only its own placeholders (`PANEL_GROUP_LABEL_PLACEHOLDERS`), may omit or repeat them, and may hold braces only as
|
|
311
|
+
those placeholders; the summaries take none, and braces in a summary are literal text.
|
|
312
|
+
- **Language**: the box carries the language of the pair it shows: `lang="en"` with a built-in pair, and no `lang`
|
|
313
|
+
with a pair you supply, which is read in the page's language even when its texts equal the built-in ones. The
|
|
314
|
+
summary and the detail carry no `lang` of their own and inherit the box's, so the region's name, which comes from
|
|
315
|
+
the summary, is read in the same voice as both lines. To keep a reason in English with `lang="en"`, leave its pair
|
|
316
|
+
out.
|
|
317
|
+
- **Validation**: `labels` is checked when the group renders, whether or not a violation is shown, once per `labels`
|
|
318
|
+
object identity. A value that is not a plain object, or a text that is not a string, throws a `TypeError`; an
|
|
319
|
+
unknown key, a blank text (`text.trim() === ""`), a placeholder the key does not accept, a brace outside a
|
|
320
|
+
placeholder in a detail (`{ required }`, `{{required}}`, a lone `{` or `}`, `{}`; the message gives its index), or
|
|
321
|
+
half a pair (the message names both keys and the one passed) throws a `RangeError`. Every text is checked before
|
|
322
|
+
the pairs are, so a malformed text is reported even when its partner is missing.
|
|
323
|
+
Every message starts with `[resize-panels]`, and your error boundary receives the error.
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { type PanelGroupLabels, PanelGroup } from "@aiquants/resize-panels"
|
|
327
|
+
|
|
328
|
+
// A module-level constant keeps the identity stable, so the labels are validated once. The maximums' pair is left
|
|
329
|
+
// out, so that reason keeps its built-in English pair.
|
|
330
|
+
const LAYOUT_LABELS: PanelGroupLabels = {
|
|
331
|
+
minimumExceededSummary: "パネルの最小幅の合計が領域に収まりません。",
|
|
332
|
+
minimumExceededDetail: "必要: {required}px / 利用可能: {available}px",
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
<PanelGroup direction="horizontal" labels={LAYOUT_LABELS}>
|
|
336
|
+
{/* ... */}
|
|
337
|
+
</PanelGroup>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
- **Structure**: a translucent veil (`position: absolute`, `inset: 0`, `z-index: 60`, `backdrop-filter: blur(2px)`,
|
|
341
|
+
with the classes `bg-slate-50/82 dark:bg-slate-950/82`) covers the group's padding box and takes the pointer. Inside
|
|
342
|
+
it, a `<section>` named by its summary (implicit role `region`) holds the summary and the detail; hook into it with
|
|
343
|
+
`data-panel-group-placeholder="constraint-violation"`. The `z-index` 60 is above the handles' 20, and above the
|
|
344
|
+
content of a panel that opted out of `isolation: isolate` as long as that content stays below 60.
|
|
345
|
+
- **Layout**: the box's layout is written inline, like every declaration the package's layout depends on, so it holds
|
|
346
|
+
whatever CSS you build. The box fills the veil, scrolls instead of clipping (the group hides overflow), centres its
|
|
347
|
+
text only while the text fits (`safe center`, so overflowing text starts at the scroll origin) and breaks a word
|
|
348
|
+
wider than itself (`overflow-wrap: anywhere`).
|
|
349
|
+
- **Contrast**: the box's surface is opaque, so its text does not depend on what lies under the veil. Computed from
|
|
350
|
+
Tailwind v4's theme, the summary and the detail reach 13.37:1 and 9.44:1 in the light scheme and 11.90:1 and
|
|
351
|
+
9.87:1 in the dark one (at least 7:1, WCAG 1.4.6 AAA); the focus outline reaches at least 6.90:1 against the
|
|
352
|
+
surface and the border (at least 3:1, 1.4.11). Lines are 1.5 high and at most `40ic` long (1.4.8). A Tailwind host
|
|
353
|
+
decides `dark:` with its own dark variant; the standalone CSS follows a `.dark` class on an ancestor.
|
|
354
|
+
- **Focus**: while the placeholder is shown, every `Panel` is `inert` and every handle is hidden and disabled, so
|
|
355
|
+
neither focus nor assistive technology reaches what lies under the veil. If the focus was inside the group when the
|
|
356
|
+
placeholder appeared — in a panel (an SVG element, or an element inside an open shadow root there, included) or on
|
|
357
|
+
a handle, and also when the group itself lives in a shadow root — the box takes it and keeps the element it came
|
|
358
|
+
from; the box is in the tab order only while it exists. When the box goes away still holding the focus and nothing
|
|
359
|
+
else holds it, the focus returns to that element. Focus that left the box during the violation — moved by you or the
|
|
360
|
+
user, or dropped to the body by a click on nothing — stays where it went, and focus that was outside the group is
|
|
361
|
+
never moved. Nested groups share the record: whatever order their violations start and end in, the focus sits on
|
|
362
|
+
the box of the outermost group still broken and returns to the element it came from once every group has mended.
|
|
363
|
+
If that element can no longer take it (removed, disabled), it goes to the box of an enclosing group still broken,
|
|
364
|
+
or nowhere.
|
|
365
|
+
|
|
230
366
|
## Layout Notifications
|
|
231
367
|
|
|
232
368
|
`onLayout(sizes, change)` runs after every commit that replaces the panels' sizes once the container has been
|
|
@@ -235,7 +371,7 @@ and `change.source` (`PanelLayoutSource`) says what caused the change:
|
|
|
235
371
|
|
|
236
372
|
| `change.source` | Cause |
|
|
237
373
|
| --- | --- |
|
|
238
|
-
| `'pointer'` | A drag on a `PanelResizeHandle`, including a collapse or an expand at either end of it |
|
|
374
|
+
| `'pointer'` | A drag on a `PanelResizeHandle`, including a collapse or an expand at either end of it, or a click that moves the handle to one of its `activationSizes` |
|
|
239
375
|
| `'keyboard'` | The arrow keys on a `PanelResizeHandle`, including the collapse and expand they trigger |
|
|
240
376
|
| `'api'` | The host: `usePanelControls`, or `resizePanels` / `collapsePanel` / `expandPanel` from `usePanelGroup()` called without a `source` |
|
|
241
377
|
| `'container'` | The group's content box changed length along its axis, or the group measured it afresh — its first measurement and the one after a `direction` change included, so a group rendered with its panels reports `'container'` first |
|
|
@@ -245,16 +381,18 @@ and `change.source` (`PanelLayoutSource`) says what caused the change:
|
|
|
245
381
|
When several changes are committed in one render, `source` is the cause of the last of them. React's StrictMode
|
|
246
382
|
does not change the notifications: when a group stops observing its container it drops the measurement, so the
|
|
247
383
|
development-only remount measures again after the panels have registered again, as the first mount did. A pointer
|
|
248
|
-
press and release without movement
|
|
249
|
-
cross axis change nothing and are not reported; a commit can, however, repeat
|
|
250
|
-
re-registering, a stored layout equal to the current one, a `direction` change), so
|
|
251
|
-
sizes twice. A handler that declares only `sizes` keeps working.
|
|
384
|
+
press and release without movement on a handle without `activationSizes`, a key that pushes a boundary already at the
|
|
385
|
+
end of its range and a resize of the cross axis change nothing and are not reported; a commit can, however, repeat
|
|
386
|
+
the previous sizes (a panel re-registering, a stored layout equal to the current one, a `direction` change), so
|
|
387
|
+
handlers should accept the same sizes twice. A handler that declares only `sizes` keeps working.
|
|
252
388
|
|
|
253
389
|
A drag reports each pointer move it applies, and commits each one before it handles the next pointer event. Every
|
|
254
390
|
`'pointer'` notification of a drag — the last move held back by the 8 ms throttle included — therefore reaches
|
|
255
391
|
`onLayout` before the handle calls `onDragging(false)`, however the drag ends (release, `pointercancel`, lost pointer
|
|
256
|
-
capture or unmount).
|
|
257
|
-
|
|
392
|
+
capture or unmount). A click on a handle with `activationSizes` is a press too: its one change is committed before
|
|
393
|
+
the press ends, so it also arrives between `onDragging(true)` and `onDragging(false)`. Pair `'pointer'` with
|
|
394
|
+
`onDragging(false)` to act once when the press ends; a key press is a complete change of its own and never calls
|
|
395
|
+
`onDragging`.
|
|
258
396
|
|
|
259
397
|
```tsx
|
|
260
398
|
const saveWidth = (width: number) => localStorage.setItem("tree-width", String(width))
|
|
@@ -304,8 +442,8 @@ size, preference, resolved bounds) along with a global inspector: a header count
|
|
|
304
442
|
handles on the page, then one card per group carrying its direction and container box and, for every panel, the
|
|
305
443
|
state size next to the measured size, both percentages, the preference, the unit, the declared bounds and the two
|
|
306
444
|
priorities. Individual handle measurements and constraint violations are not part of that overlay; a violation is
|
|
307
|
-
shown by the placeholder the group draws over its panels, with or without the
|
|
308
|
-
imported (`React.lazy`); without the flag no per-panel `ResizeObserver` is created.
|
|
445
|
+
shown by the [placeholder](#constraint-violation-placeholder) the group draws over its panels, with or without the
|
|
446
|
+
flag. Debug components are lazily imported (`React.lazy`); without the flag no per-panel `ResizeObserver` is created.
|
|
309
447
|
|
|
310
448
|
## Styling (CSS)
|
|
311
449
|
|
|
@@ -326,7 +464,7 @@ The package provides two CSS consumption options:
|
|
|
326
464
|
@import "@aiquants/resize-panels/styles/resize-panels.standalone.css";
|
|
327
465
|
```
|
|
328
466
|
|
|
329
|
-
Dark mode
|
|
467
|
+
Dark mode applies inside any element carrying the `.dark` class, whether `<html>` or another ancestor.
|
|
330
468
|
|
|
331
469
|
### Style ownership
|
|
332
470
|
|
|
@@ -2,7 +2,7 @@ import { jsx as e, jsxs as t } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState as A } from "react";
|
|
3
3
|
import { createPortal as P } from "react-dom";
|
|
4
4
|
import { useDebugOverlayOwnerId as R, useDebugOverlaySnapshot as T } from "./debugOverlayStore-D7FT6zPE.js";
|
|
5
|
-
import { P as Y } from "./index-
|
|
5
|
+
import { P as Y } from "./index-BLe_thse.js";
|
|
6
6
|
const s = (a, n) => typeof a != "number" || !Number.isFinite(a) ? "—" : `${(a < 0 ? 0 : a).toFixed(8)}${n}`, w = {
|
|
7
7
|
position: "fixed",
|
|
8
8
|
left: "50%",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),A=require("react"),P=require("react-dom"),x=require("./debugOverlayStore-DY09saU8.cjs"),R=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),A=require("react"),P=require("react-dom"),x=require("./debugOverlayStore-DY09saU8.cjs"),R=require("./index-DMDH6fMm.cjs"),n=(o,i)=>typeof o!="number"||!Number.isFinite(o)?"—":`${(o<0?0:o).toFixed(8)}${i}`,T={position:"fixed",left:"50%",bottom:"20px",transform:"translate(-50%, 0)",zIndex:100,maxWidth:"min(90vw, 1120px)",width:"100%",borderRadius:"14px",backgroundColor:"rgba(15, 23, 42, 0.72)",color:"#e2e8f0",fontFamily:"monospace",fontSize:"11px",lineHeight:1.6,boxShadow:"0 22px 45px rgba(15,23,42,0.45)",border:"1px solid rgba(148, 163, 184, 0.22)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",pointerEvents:"auto",overflow:"auto"},Y={display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:"12px"},w={display:"flex",flexDirection:"column",gap:"4px"},D={padding:"6px 10px",borderRadius:"9999px",border:"1px solid rgba(148, 163, 184, 0.35)",color:"#e2e8f0",fontWeight:600,fontSize:"10px",letterSpacing:"0.03em",textTransform:"uppercase",cursor:"pointer",transition:"background-color 0.18s ease, border-color 0.18s ease"},C={padding:"10px 12px",borderRadius:"10px",backgroundColor:"rgba(30, 41, 59, 0.55)",border:"1px solid rgba(148, 163, 184, 0.18)",display:"flex",flexDirection:"column",gap:"10px"},N={padding:"8px 10px",borderRadius:"8px",backgroundColor:"rgba(15, 23, 42, 0.55)",border:"1px solid rgba(148, 163, 184, 0.16)",display:"flex",flexDirection:"column",gap:"4px"},p={display:"flex",justifyContent:"space-between",alignItems:"center",gap:"6px"},k={display:"grid",gridTemplateColumns:"auto 1fr",gap:"2px 10px",color:"#e2e8f0"},G={padding:"2px 6px",borderRadius:"9999px",fontSize:"10px",textTransform:"uppercase"},I=({groupId:o})=>x.useDebugOverlayOwnerId()!==o?null:e.jsx(u,{}),u=()=>{const i=x.useDebugOverlaySnapshot().groups,[t,g]=A.useState(!0);if(i.length===0||typeof document>"u")return null;const b=i.reduce((s,a)=>s+a.panels.length,0),h=i.reduce((s,a)=>s+(a.handles?.length??0),0),f=()=>g(s=>!s),m=e.jsxs("div",{style:{...T,padding:t?"10px 18px":"14px 18px",maxHeight:t?"52px":"min(65vh, 520px)",transition:"max-height 0.24s ease, padding 0.24s ease"},children:[e.jsxs("div",{style:{...Y,marginBottom:t?0:"12px"},children:[e.jsxs("div",{style:w,children:[e.jsx("span",{style:{fontWeight:700,fontSize:"12px",letterSpacing:"0.03em"},children:"Panel Overview"}),e.jsxs("span",{style:{color:"#cbd5f5"},children:["groups: ",i.length," / panels: ",b," / resizers: ",h]})]}),e.jsx("button",{type:"button",onClick:f,style:{...D,backgroundColor:t?"rgba(59, 130, 246, 0.2)":"rgba(71, 85, 105, 0.45)"},title:t?"Panel Overview を開く":"Panel Overview を閉じる",children:t?"開く":"閉じる"})]}),!t&&e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"12px",maxHeight:"45vh",overflowY:"auto",paddingRight:"6px"},children:i.map((s,a)=>{const l=s.direction==="horizontal"?s.containerSize.width:s.containerSize.height;return e.jsxs("div",{style:C,children:[e.jsxs("div",{style:{...p,flexWrap:"wrap",gap:"10px",color:"#dbeafe"},children:[e.jsxs("span",{style:{fontWeight:600,fontSize:"11px",letterSpacing:"0.02em",textTransform:"uppercase"},children:["group ",a+1,": ",s.displayName]}),e.jsxs("span",{children:["direction: ",s.direction," / container: ",n(s.containerSize.width,"px")," × ",n(s.containerSize.height,"px")]})]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"10px"},children:s.panels.map((r,y)=>{const d=r.measuredPixelSize??r.size,S=r.size,j=n(S,"px"),v=l>0?r.size/l*100:void 0,E=r.measuredPercentageSize!==void 0?r.measuredPercentageSize:l>0?d/l*100:void 0,c=r.collapsed||d<=R.PANEL_SNAP_THRESHOLD,O=r.collapsed?"collapsed":c?"hidden":"visible",z=n(v,"%"),L=n(E,"%"),_=n(d,"px");return e.jsxs("div",{style:N,children:[e.jsxs("div",{style:p,children:[e.jsx("span",{style:{flex:1,minWidth:0,overflow:"hidden",textOverflow:"ellipsis"},children:r.id}),e.jsx("span",{style:{...G,backgroundColor:r.collapsed?"rgba(251, 113, 133, 0.25)":c?"rgba(250, 204, 21, 0.25)":"rgba(74, 222, 128, 0.25)",color:r.collapsed?"#fecdd3":c?"#fef08a":"#bbf7d0"},children:O})]}),e.jsxs("div",{style:k,children:[e.jsx("span",{children:"size (state):"}),e.jsx("span",{children:j}),e.jsx("span",{children:"size (measured):"}),e.jsx("span",{children:_}),e.jsx("span",{children:"percentage (state):"}),e.jsx("span",{children:z}),e.jsx("span",{children:"preference:"}),e.jsx("span",{children:r.sizeUnit==="pixels"?n(r.preferredPixelSize,"px"):n(r.preferredPercentageSize,"%")}),e.jsx("span",{children:"percentage (measured):"}),e.jsx("span",{children:L}),e.jsx("span",{children:"unit:"}),e.jsx("span",{children:r.sizeUnit}),e.jsx("span",{children:"min:"}),e.jsx("span",{children:r.minSize!==void 0?JSON.stringify(r.minSize):"—"}),e.jsx("span",{children:"max:"}),e.jsx("span",{children:r.maxSize!==void 0?JSON.stringify(r.maxSize):"—"}),e.jsx("span",{children:"priority:"}),e.jsx("span",{children:r.pixelAdjustPriority??"—"}),e.jsx("span",{children:"flex priority:"}),e.jsx("span",{children:r.flexAdjustPriority??"—"})]})]},r.id||y)})})]},s.groupId)})})]});return P.createPortal(m,document.body)};exports.GlobalDebugOverlay=u;exports.GlobalDebugOverlayGate=I;
|