@aiquants/resize-panels 1.8.1 → 2.0.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.
Files changed (81) hide show
  1. package/README.md +213 -108
  2. package/dist/GlobalDebugOverlay-Cf3GOn2A.cjs +1 -0
  3. package/dist/{GlobalDebugOverlay-DYCR0FIu.js → GlobalDebugOverlay-DQtUdNeF.js} +28 -28
  4. package/dist/debugOverlayStore-D7FT6zPE.js +135 -0
  5. package/dist/debugOverlayStore-DY09saU8.cjs +1 -0
  6. package/dist/index-DZ0itiBD.js +1299 -0
  7. package/dist/index-DqATP_BA.cjs +2 -0
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.d.ts +2 -29
  10. package/dist/index.js +25 -13
  11. package/dist/src/GlobalDebugOverlay.d.ts.map +1 -0
  12. package/dist/src/Panel.d.ts +46 -0
  13. package/dist/src/Panel.d.ts.map +1 -0
  14. package/dist/src/PanelDebugInfo.d.ts +34 -0
  15. package/dist/src/PanelDebugInfo.d.ts.map +1 -0
  16. package/dist/src/PanelGroup.d.ts.map +1 -0
  17. package/dist/{PanelResizeHandle.d.ts → src/PanelResizeHandle.d.ts} +1 -1
  18. package/dist/src/PanelResizeHandle.d.ts.map +1 -0
  19. package/dist/src/allocateLayout.d.ts +60 -0
  20. package/dist/src/allocateLayout.d.ts.map +1 -0
  21. package/dist/src/context.d.ts.map +1 -0
  22. package/dist/{debugOverlayStore.d.ts → src/debugOverlayStore.d.ts} +10 -2
  23. package/dist/src/debugOverlayStore.d.ts.map +1 -0
  24. package/dist/src/hooks.d.ts +48 -0
  25. package/dist/src/hooks.d.ts.map +1 -0
  26. package/dist/src/index.d.ts +32 -0
  27. package/dist/src/index.d.ts.map +1 -0
  28. package/dist/src/reducer.d.ts +49 -0
  29. package/dist/src/reducer.d.ts.map +1 -0
  30. package/dist/src/roundHalfToEven.d.ts.map +1 -0
  31. package/dist/src/types.d.ts +481 -0
  32. package/dist/src/types.d.ts.map +1 -0
  33. package/dist/src/utils/simple-logger.d.ts.map +1 -0
  34. package/dist/src/utils.d.ts +164 -0
  35. package/dist/src/utils.d.ts.map +1 -0
  36. package/dist/styles/resize-panels.standalone.css +1 -1
  37. package/dist/tests/support/dom-harness.d.ts +89 -0
  38. package/dist/tests/support/dom-harness.d.ts.map +1 -0
  39. package/package.json +3 -3
  40. package/src/GlobalDebugOverlay.tsx +3 -3
  41. package/src/Panel.tsx +203 -541
  42. package/src/PanelDebugInfo.tsx +52 -106
  43. package/src/PanelGroup.tsx +27 -42
  44. package/src/PanelResizeHandle.tsx +544 -504
  45. package/src/allocateLayout.ts +412 -0
  46. package/src/debugOverlayStore.ts +19 -15
  47. package/src/hooks.ts +187 -779
  48. package/src/index.ts +37 -6
  49. package/src/reducer.ts +364 -1126
  50. package/src/types.ts +282 -65
  51. package/src/utils.ts +205 -164
  52. package/dist/GlobalDebugOverlay-afs0vF-Q.cjs +0 -1
  53. package/dist/GlobalDebugOverlay.d.ts.map +0 -1
  54. package/dist/Panel.d.ts +0 -7
  55. package/dist/Panel.d.ts.map +0 -1
  56. package/dist/PanelDebugInfo.d.ts +0 -26
  57. package/dist/PanelDebugInfo.d.ts.map +0 -1
  58. package/dist/PanelGroup.d.ts.map +0 -1
  59. package/dist/PanelResizeHandle.d.ts.map +0 -1
  60. package/dist/context.d.ts.map +0 -1
  61. package/dist/debugOverlayStore-Cntyxboe.js +0 -141
  62. package/dist/debugOverlayStore-Dkl-fHoa.cjs +0 -1
  63. package/dist/debugOverlayStore.d.ts.map +0 -1
  64. package/dist/hooks.d.ts +0 -45
  65. package/dist/hooks.d.ts.map +0 -1
  66. package/dist/index-BzgId8aQ.js +0 -1972
  67. package/dist/index-xTV-c0PN.cjs +0 -2
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/reducer.d.ts +0 -39
  70. package/dist/reducer.d.ts.map +0 -1
  71. package/dist/roundHalfToEven.d.ts.map +0 -1
  72. package/dist/types.d.ts +0 -253
  73. package/dist/types.d.ts.map +0 -1
  74. package/dist/utils/simple-logger.d.ts.map +0 -1
  75. package/dist/utils.d.ts +0 -61
  76. package/dist/utils.d.ts.map +0 -1
  77. /package/dist/{GlobalDebugOverlay.d.ts → src/GlobalDebugOverlay.d.ts} +0 -0
  78. /package/dist/{PanelGroup.d.ts → src/PanelGroup.d.ts} +0 -0
  79. /package/dist/{context.d.ts → src/context.d.ts} +0 -0
  80. /package/dist/{roundHalfToEven.d.ts → src/roundHalfToEven.d.ts} +0 -0
  81. /package/dist/{utils → src/utils}/simple-logger.d.ts +0 -0
package/README.md CHANGED
@@ -1,36 +1,39 @@
1
1
  # @aiquants/resize-panels
2
2
 
3
- React 向けのリサイズ可能なパネルレイアウトコンポーネント集です。`~/components/elements/ResizablePanels` で培ったロジックをパッケージ化し、任意のアプリケーションから再利用できるようにしました。
3
+ Resizable panel layout components for React, built on one rule: **there is a single function that decides panel sizes, and the handle is the boundary rather than a thing positioned near it.**
4
4
 
5
- ## 主な特徴
5
+ Every state change — first paint, container resize, drag, collapse, restored layout — runs the same allocator, so the panels always fill the container exactly and the handle can never drift away from the edge it moves.
6
6
 
7
- - `PanelGroup` と `Panel` と `PanelResizeHandle` を中核に、リデューサーでレイアウトと DOM 計測を同期
8
- - `calculateSnapThreshold` とノイズ除去ロジックでリサイズ時の揺らぎを抑え、意図しない折りたたみを防止
9
- - 方向指定付き `collapsible` 設定と `usePanelControls` により、ドラッグ操作と UI 操作の両方で折りたたみ / 展開を制御
10
- - リサイズハンドルはマウス/タッチのドラッグに加え、**矢印キーによるキーボードリサイズ**にも対応(Shift 併用で 5 倍ステップ)。マルチタッチや `pointercancel` の割込みでもドラッグ状態が壊れない
11
- - `dir="rtl"` の **RTL レイアウト**でも、ドラッグ・キーボード・ハンドル位置が視覚方向に一致
12
- - `autoSaveId` を指定するとローカルストレージにパネルサイズを自動保存し、再読み込み時に復元(初期化確定後のサイズのみ保存)
13
- - `showDebugInfo` を使えば `PanelDebugInfo` とグローバルオーバーレイで計測結果や制約違反を可視化(デバッグ UI は遅延読み込みで、未使用時は本番バンドルに含まれない)
7
+ ## Key Features
14
8
 
15
- ## インストール
9
+ - **One allocator, one geometry**: `allocatePanelSizes` distributes the container among the panels (proportional water-filling with per-panel minimum/maximum bounds). `Σ panel sizes == the group's content box` holds after every transition the declared bounds allow, and the reducer logs a breach of that invariant rather than letting it pass silently — a container the minimums overflow, or that the maximums cannot reach, is reported as a constraint violation instead.
10
+ - **A handle that cannot drift**: the resize handle renders a zero-length in-flow flex item at the boundary and draws its hit area symmetrically around it. Nothing computes its position, so it stays on the boundary in LTR and RTL, horizontal and vertical, with group padding, and during every frame of a live container resize. A drag divides the pointer's travel by the group's visual scale, so the grab point stays under the cursor even below a transformed ancestor.
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
+ - **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
+ - **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
+ - **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
+ - **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.
16
16
 
17
- ワークスペース内で利用する場合は `pnpm` を利用します。
17
+ ## Installation
18
18
 
19
- ```bash
20
- pnpm add @aiquants/resize-panels
21
- ```
19
+ Monorepo workspace dependency:
22
20
 
23
- モノレポ内から参照する場合は `package.json` に次のように記載します。
24
-
25
- ```json
21
+ ```jsonc
22
+ // consumer package.json
26
23
  {
27
- "dependencies": {
28
- "@aiquants/resize-panels": "workspace:*"
29
- }
24
+ "dependencies": {
25
+ "@aiquants/resize-panels": "workspace:*"
26
+ }
30
27
  }
31
28
  ```
32
29
 
33
- ## クイックスタート
30
+ Standard package manager installation:
31
+
32
+ ```bash
33
+ pnpm add @aiquants/resize-panels
34
+ ```
35
+
36
+ ## Quick Start
34
37
 
35
38
  ```tsx
36
39
  import { useId } from "react"
@@ -40,38 +43,38 @@ export const Example = () => {
40
43
  const baseId = useId()
41
44
 
42
45
  return (
43
- <PanelGroup id={`${baseId}-group`} direction="horizontal" className="h-96">
46
+ <PanelGroup id={`${baseId}-group`} direction="horizontal" style={{ height: "24rem" }}>
44
47
  <Panel id={`${baseId}-left`} defaultSize={{ value: 200, unit: "pixels" }} className="bg-slate-100">
45
- 左パネル
48
+ Left Panel
46
49
  </Panel>
47
50
  <PanelResizeHandle id={`${baseId}-handle`} />
48
51
  <Panel id={`${baseId}-right`} defaultSize={{ value: 60, unit: "percentage" }} className="bg-slate-50">
49
- 右パネル
52
+ Right Panel
50
53
  </Panel>
51
54
  </PanelGroup>
52
55
  )
53
56
  }
54
57
  ```
55
58
 
56
- ### 折りたたみを有効にする
59
+ ### Enabling Collapsible Panels
57
60
 
58
- `Panel` `collapsible={{ from: "start" }}` `collapsible={{ from: "end" }}` を指定すると、対応する `PanelResizeHandle` を端までドラッグしたタイミングで自動的に折りたたみ・展開が行われます。`defaultCollapsed` を併用すると初期状態を折りたたみ済みに設定できます。方向指定は必須で、各パネルがどちら側から折りたためるかを宣言します。両方向に対応したい場合は `collapsible={{ from: "both" }}` を使用します。
61
+ Specify `collapsible={{ from: "start" }}` or `collapsible={{ from: "end" }}` on a `Panel` to enable automatic collapse/expand when dragging the corresponding handle to container boundaries. Set `defaultCollapsed` to set initial state to collapsed. Use `collapsible={{ from: "both" }}` to allow collapsing from either edge.
59
62
 
60
63
  ```tsx
61
- <PanelGroup direction="horizontal" className="h-96">
64
+ <PanelGroup direction="horizontal" style={{ height: "24rem" }}>
62
65
  <Panel id="sidebar" defaultSize={{ value: 240, unit: "pixels" }} collapsible={{ from: "end" }}>
63
- サイドバー
66
+ Sidebar
64
67
  </Panel>
65
68
  <PanelResizeHandle id="handle" />
66
69
  <Panel id="main" defaultSize={{ value: 60, unit: "percentage" }}>
67
- メインコンテンツ
70
+ Main Content
68
71
  </Panel>
69
72
  </PanelGroup>
70
73
  ```
71
74
 
72
- ### UI から折りたたみ / 展開を切り替える
75
+ ### Programmatic Controls via Hook
73
76
 
74
- `usePanelControls(panelId)` フックを使うと、任意の UI から対象パネルを折りたたみ・展開・トグルできます。ボタンを配置するコンポーネントは `PanelGroup` の配下(コンテキスト内)に置いてください。折りたたみ後もボタンを表示したい場合は、別パネルやヘッダーなど常に可視な場所に設置します。API は方向を必須パラメーターとして受け取り、パネル構成に応じて呼び出し側で指定します。
77
+ Use `usePanelControls(panelId)` inside any child component of `PanelGroup` to trigger collapse, expand, or toggle actions:
75
78
 
76
79
  ```tsx
77
80
  import { PanelGroup, Panel, PanelResizeHandle, usePanelControls } from "@aiquants/resize-panels"
@@ -84,134 +87,236 @@ const PanelToggleButtons = ({ panelId, label }: { panelId: string; label: string
84
87
  <div className="flex items-center gap-2">
85
88
  <span>{label}</span>
86
89
  <button onClick={() => collapse(direction)} disabled={(!canCollapseFromStart && !canCollapseFromEnd) || isCollapsed}>
87
- 折りたたむ
90
+ Collapse
88
91
  </button>
89
92
  <button onClick={() => expand(direction)} disabled={!isCollapsed || (!canCollapseFromStart && !canCollapseFromEnd)}>
90
- 展開する
93
+ Expand
91
94
  </button>
92
95
  <button onClick={() => toggle(direction)} disabled={!canCollapseFromStart && !canCollapseFromEnd}>
93
- {isCollapsed ? "展開に切り替え" : "折りたたみに切り替え"}
96
+ {isCollapsed ? "Switch to Expand" : "Switch to Collapse"}
94
97
  </button>
95
98
  </div>
96
99
  )
97
100
  }
98
101
 
99
102
  export const Example = () => (
100
- <PanelGroup direction="horizontal" className="h-96">
103
+ <PanelGroup direction="horizontal" style={{ height: "24rem" }}>
101
104
  <Panel id="sidebar" defaultSize={{ value: 240, unit: "pixels" }} collapsible={{ from: "end" }}>
102
- <PanelToggleButtons panelId="sidebar" label="サイドバー" />
105
+ <PanelToggleButtons panelId="sidebar" label="Sidebar" />
103
106
  </Panel>
104
107
  <PanelResizeHandle id="split" />
105
108
  <Panel id="main" defaultSize={{ value: 60, unit: "percentage" }}>
106
- <PanelToggleButtons panelId="details" label="詳細パネル" />
109
+ <PanelToggleButtons panelId="details" label="Details Panel" />
107
110
  </Panel>
108
111
  <PanelResizeHandle id="split-right" />
109
112
  <Panel id="details" defaultSize={{ value: 40, unit: "percentage" }} collapsible={{ from: "start" }}>
110
- 詳細
113
+ Details
111
114
  </Panel>
112
115
  </PanelGroup>
113
116
  )
114
117
  ```
115
118
 
116
- `collapse(direction)` は指定方向からパネルを非表示にし、`expand(direction)` はドラッグ操作と同じロジックで直前のサイズや推奨サイズを復元します。`toggle(direction)` を使うと現在の状態に応じて自動的に切り替わります。
119
+ ## Component API
117
120
 
118
- ## コンポーネント API
121
+ ### PanelGroup Properties
119
122
 
120
- ### PanelGroup の主なプロパティ
121
-
122
- | プロパティ | 型 | 説明 |
123
+ | Property | Type | Description |
123
124
  | --- | --- | --- |
124
- | `id` | `string` | `data-panel-group-id` にも反映される識別子。複数グループを並べる場合は指定を推奨 |
125
- | `direction` | `'horizontal' \| 'vertical'` | パネルの配置方向 |
126
- | `className` / `style` | `string` / `React.CSSProperties` | コンテナの見た目を調整するための追加スタイル |
127
- | `children` | `React.ReactNode` | グループ内の `Panel` `PanelResizeHandle` を並べるための子要素 |
128
- | `showDebugInfo` | `boolean` | `PanelDebugInfo` とグローバルオーバーレイを表示して計測結果と制約違反を可視化 |
129
- | `onLayout` | `(sizes: number[]) => void` | リサイズや初期化のたびに最新のピクセルサイズ配列を通知 |
130
- | `autoSaveId` | `string` | 指定すると `localStorage` にパネルサイズを保存・復元 |
125
+ | `id` | `string` | Group identifier (reflected in `data-panel-group-id`). Recommended when mounting multiple groups |
126
+ | `direction` | `'horizontal' \| 'vertical'` | Panel layout orientation |
127
+ | `className` / `style` | `string` / `React.CSSProperties` | Additional styling for the container |
128
+ | `children` | `React.ReactNode` | `Panel` and `PanelResizeHandle` child elements |
129
+ | `showDebugInfo` | `boolean` | Enables the per-panel `PanelDebugInfo` badge and the global debug overlay |
130
+ | `onLayout` | `(sizes: number[]) => void` | Callback emitting updated pixel size arrays on resize/init |
131
+ | `autoSaveId` | `string` | Key used to automatically persist and restore panel sizes in `localStorage` |
131
132
 
132
- ### Panel の主なプロパティ
133
+ ### Panel Properties
133
134
 
134
- | プロパティ | | 説明 |
135
+ | Property | Type | Description |
135
136
  | --- | --- | --- |
136
- | `id` | `string` | パネル識別子。省略時はランダム生成だが、SSR との整合のため明示指定を推奨 |
137
- | `defaultSize` | `{ value: number; unit: 'pixels' \| 'percentage' } \| number` | 初期サイズ。数値のみの場合はパーセンテージとして扱う |
138
- | `minSize` / `maxSize` | `FlexibleSize` | パネルの最小・最大サイズ制約。ピクセル・割合で指定可能 |
139
- | `collapsible` | `{ from: 'start' \| 'end' \| 'both' }` | 折りたたみ方向の宣言。ドラッグや API 呼び出しで利用される必須設定 |
140
- | `defaultCollapsed` | `boolean` | 初期状態を折りたたみ済みにするかどうか |
141
- | `pixelAdjustPriority` | `number` | 余白調整時にピクセル基準パネルへ割り当てる優先度 |
142
- | `className` / `style` | `string` / `React.CSSProperties` | パネル本体の見た目をカスタマイズ |
143
-
144
- ### PanelResizeHandle の主なプロパティ
145
-
146
- | プロパティ | | 説明 |
137
+ | `id` | `string` | Panel identifier (reflected in `data-panel-id`). Optional — a `useId`-derived id is generated when omitted — but fixed at mount: a later change is ignored with a warning |
138
+ | `defaultSize` | `{ value: number; unit: 'pixels' \| 'percentage' } \| number` | Initial panel size (numbers parsed as percentages; `50%` when omitted) |
139
+ | `minSize` / `maxSize` | `FlexibleSize` | Panel size boundaries (pixels or percentages) |
140
+ | `collapsible` | `{ from: 'start' \| 'end' \| 'both' }` | Declarative collapsible edge configuration |
141
+ | `defaultCollapsed` | `boolean` | Whether the panel starts collapsed |
142
+ | `pixelAdjustPriority` | `number` | Order in which pixel panels are served when space is scarce (higher first) |
143
+ | `flexAdjustPriority` | `number` | Serving order among flexible panels (higher first); declaring it also reads `defaultSize` as an absolute claim on that share of the container |
144
+ | `contentOverflow` | `React.CSSProperties["overflow"]` | Overflow of the panel's content wrapper (`auto` by default) |
145
+ | `className` / `style` | `string` / `React.CSSProperties` | Panel container styles |
146
+
147
+ `Panel` and `PanelGroup` forward any other DOM attribute (`data-testid`, `aria-*`, event handlers) to their
148
+ element, except the ones they compute themselves: `Panel` owns `aria-hidden`, which follows the collapsed state,
149
+ and its own `data-panel-*` attributes, while `PanelGroup` owns `data-panel-group-id` and
150
+ `data-panel-group-direction` — the first is what `getPanelGroupElement` looks up, the second is what marks an
151
+ element as a group for `findOwningGroup` and therefore for every handle. `aria-hidden` is removed
152
+ from `PanelProps` to state that ownership, but TypeScript does not type-check JSX attribute names containing a
153
+ hyphen, so neither it nor a `data-panel-*` / `data-panel-group-*` of your own is rejected at compile time: what
154
+ enforces the contract is the runtime override, which applies these attributes after everything you forwarded. A
155
+ `data-panel-group-id` you pass disappears altogether when the group itself has no `id`. Both elements merge your
156
+ `className` with their own (`panel` / `panel-group`) instead of replacing it.
157
+
158
+ ### PanelResizeHandle Properties
159
+
160
+ | Property | Type | Description |
147
161
  | --- | --- | --- |
148
- | `id` | `string` | ハンドル識別子。省略時はランダム生成 |
149
- | `disabled` | `boolean` | `true` にするとドラッグ・キーボード操作を無効化 |
150
- | `onDragging` | `(isDragging: boolean) => void` | ドラッグ開始 / 終了ごとに呼び出されるコールバック(`pointercancel` 等の割込みでも終了時に発火) |
151
- | `className` / `style` | `string` / `React.CSSProperties` | ハンドル見た目を追加カスタマイズ |
152
- | `children` | `React.ReactNode` | 独自のハンドルインジケーターを描画する場合に使用 |
153
-
154
- > ハンドルは `<button>` としてレンダリングされ、フォーカス時に矢印キー(水平なら `←` / `→`、垂直なら `↑` / `↓`)でリサイズできます。`Shift` 併用で 1 押下あたりの移動量が 5 倍になります。`dir="rtl"` のコンテナ内ではドラッグ・キーボードの方向が視覚方向に合わせて反転します。
155
-
156
- ## フックとユーティリティ
157
-
158
- - `useResizablePanels`: `PanelGroup` 内部実装でも利用しているフック。カスタムコンテナを作る場合に役立ちます。
159
- - `usePanelGroup`: コンテキストを直接参照し、パネルリストやレイアウト情報を取得できます。
160
- - `usePanelControls`: 特定パネルに対する折りたたみ / 展開操作を提供します。
161
- - `saveLayout` `loadLayout`: 任意のタイミングでレイアウトを永続化 / 復元できます。
162
- - `getPanelElement`, `getPanelGroupElement`, `getResizeHandleElement`: `data-*` 属性をもとに DOM 要素を取得します。
163
-
164
- ## レイアウト保存と復元
165
-
166
- `PanelGroup` `autoSaveId` を指定すると、`ResizeObserver` が安定した後のパネルサイズを 200ms デバウンス付きで `localStorage` に保存します。ページを再読み込みすると `loadLayout(autoSaveId)` の結果が自動的に反映され、ピクセル基準パネルと割合基準パネルの両方が復元されます。手動で制御したい場合は公開ユーティリティの `saveLayout` / `loadLayout` を直接呼び出してください。
167
-
168
- ## デバッグと可視化
169
-
170
- `PanelGroup` の `showDebugInfo` を有効にすると、各 `Panel` に `PanelDebugInfo` が表示され、折りたたみ状態・計測サイズ・保持中の割合が即座に確認できます。同時にグローバルオーバーレイが開き、コンテナの実測サイズ、制約違反の有無、ハンドル厚み(`PANEL_HANDLE_THICKNESS` で定義された 8px)なども一覧できます。
171
-
172
- グローバルオーバーレイとそのストアは `React.lazy` + dynamic import で遅延読み込みされるため、`showDebugInfo` を一度も使わないアプリの本番バンドルにはデバッグ UI もストア購読も含まれません。
173
-
174
- ## スナップとノイズ対策・最終整合の挙動
175
-
176
- `PanelResizeHandle` `Panel` の両方で `calculateSnapThreshold` を利用し、コンテナサイズに応じたゼロスナップ閾値を共有しています。`Panel` および `PanelGroup` では、サブピクセル単位の微小なノイズ(0.5px 未満の揺れ)を無視するフィルタリングと、動作停止後に正確な真値を反映する **最終整合メカニズム(デバウンス同期)** を導入しています。これにより、スクロールやリサイズ中のチラつきを抑えつつ、最終的な表示状態と内部状態の完全な一致を保証します。
177
-
178
- ## スタイル (CSS)
179
-
180
- 大半のスタイルは JSX ベタ書きの Tailwind ユーティリティで持つため、CSS アーティファクトを **2 種類**配布する。ホストの種類で読み込み方を選ぶ。
181
-
182
- - **Tailwind v4 ホスト**: components-only ビルドを `layer(components)` で読み込み、JSX ユーティリティは自アプリの Tailwind ビルドにパッケージ `src` をスキャンさせて生成する。
162
+ | `id` | `string` | Handle identifier (reflected in `data-resize-handle-id`) |
163
+ | `disabled` | `boolean` | Disables mouse dragging and keyboard resizing when `true` |
164
+ | `thickness` | `number` | Primary axis thickness of the handle's interactive area in pixels (`8px` default) |
165
+ | `indicator` | `boolean \| PanelResizeHandleIndicatorConfig` | Toggle indicator visibility or provide custom pill/bar dimensions and CSS classes |
166
+ | `indicatorThickness` | `number` | Shorthand for indicator pill wrapper thickness in pixels (`6px` default) |
167
+ | `indicatorLength` | `number` | Shorthand for indicator pill wrapper length in pixels (`48px` horizontal / `80px` vertical default) |
168
+ | `indicatorBarThickness` | `number` | Shorthand for indicator inner bar thickness in pixels (`2px` default) |
169
+ | `indicatorBarLength` | `number` | Shorthand for indicator inner bar length in pixels (`32px` horizontal / `64px` vertical default) |
170
+ | `title` | `string` | Custom tooltip text for the handle element |
171
+ | `onDragging` | `(isDragging: boolean) => void` | Callback triggered on drag start and end (including `pointercancel`) |
172
+ | `className` / `style` | `string` / `React.CSSProperties` | Additional styling for the resize handle |
173
+ | `children` | `React.ReactNode` | Custom indicator element rendered inside handle (overrides default indicator) |
174
+
175
+ > Handles render as `<button role="separator">` elements accessible via arrow keys (`←`/`→` for horizontal, `↑`/`↓` for vertical); holding `Shift` increases the step 5x. Under `dir="rtl"` the drag and keyboard directions follow the visual layout. The handle sits inside a zero-length anchor placed by flex layout, so its hit area is centred on the panel boundary without any position arithmetic.
176
+
177
+ `PanelResizeHandle` forwards any other DOM attribute to its `<button>`, and calls your `onPointerDown` /
178
+ `onKeyDown` before its own handling — calling `preventDefault()` in them stops the library's gesture from starting.
179
+ The attributes it computes from the layout (`role`, `aria-orientation`, `aria-valuenow` / `aria-valuemin` /
180
+ `aria-valuemax`, `aria-disabled`, `aria-hidden`) are removed from `PanelResizeHandleProps`, though only `role` is
181
+ actually caught by the compiler — the hyphenated names are not type-checked in JSX, and it is the runtime override
182
+ that keeps them the library's. `aria-label` stays yours and replaces the built-in description, and `title` defaults
183
+ to that same description.
184
+
185
+ Your `style` reaches the button between two library layers. The handle's resolved dimensions are published as CSS
186
+ custom properties `--rp-handle-thickness`, `--rp-indicator-thickness`, `--rp-indicator-length`,
187
+ `--rp-indicator-bar-thickness`, `--rp-indicator-bar-length` — *before* it, so decorations of your own can be sized
188
+ from the same numbers and may redefine them; the declarations that keep the hit area centred on the boundary
189
+ (`position: absolute`, `zIndex: 20`, `touchAction: none`, the logical insets and the thickness / cross-axis `100%`
190
+ box) are applied *after* it and take no override. While the group reports a constraint violation, every handle is
191
+ hidden (`display: none` on its anchor) as well as disabled.
192
+
193
+ ## Hooks & Utilities
194
+
195
+ - `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.
196
+ - `usePanelGroup` / `PanelGroupContext`: context accessor, and the context itself, for the panel list, the container length, the constraint violation and `layoutOrderKey` — the document order of the panels and handle anchors, which changes even when only a handle moved to another boundary.
197
+ - `usePanelControls`: `collapse`, `expand` and `toggle` for a given panel id.
198
+ - `allocatePanelSizes`, `resolvePanelBounds`, `isPixelPanel`: the layout calculation itself, exported for tests and for hosts that drive their own container.
199
+ - `panelReducer`, `initialPanelGroupLayoutState`, `getPanelPreferenceInPixels`, `findLayoutInvariantBreach`: the state machine around the allocator, the length a layout should persist, and the `Σ sizes == container` check.
200
+ - `getContainerSize`: measure one axis of an element in layout pixels; it returns `{ content, border }`, and `content` (padding and borders removed) is the length the allocator fills.
201
+ - `findOwningGroup`, `getGroupLayoutElements`: resolve the group that owns an element, and that group's panels and handle anchors in document order — across wrapper elements, `display: contents` included, while skipping both a nested group's own elements and any `data-panel-id` that belongs to a panel's content rather than to this group.
202
+ - `saveLayout` & `loadLayout`: persist or restore a layout (`{ id, size, collapsed }[]`).
203
+ - `clamp`, `getConstraintInPixels`, `normalizeSizeConfig`, `percentageToPixels`, `toRoundedPercentage`, `calculateSnapThreshold`: the unit conversions and thresholds the components share.
204
+ - `getPanelElement`, `getPanelGroupElement`, `getResizeHandleElement`: DOM element query helpers via `data-*` attributes.
205
+
206
+ Every public type (`PanelProps`, `PanelGroupProps`, `PanelResizeHandleProps`, `PanelLayoutData`, `PanelRegistration`, `PersistedPanelSize`, `PanelGroupLayoutState`, `PanelAction`, `LayoutAllocation`, `PanelBounds`, `ContainerAxisMeasurement`, …) is re-exported from the package root; `docs/specs` (`00-architecture-overview.md` §8) lists the surface in full.
207
+
208
+ ## How sizes are decided
209
+
210
+ | Panel kind | What it declares | What it gets |
211
+ | --- | --- | --- |
212
+ | Flexible (`percentage`, or a bare number) | a weight — only the ratio between such weights matters | its share of what is left after the panels with an absolute target, clamped to its bounds |
213
+ | Flexible with `flexAdjustPriority` | the same number, read as a claim on that share of the container | that claim, served after the pixel panels and clamped to its bounds |
214
+ | Pixel (`pixels`) | an absolute length | that length, clamped to its bounds, once the panels ahead of it in `pixelAdjustPriority` order have been served |
215
+
216
+ Scarcity and surplus are two ends of one order. While space is scarce, targets are served in that order — pixel
217
+ panels by `pixelAdjustPriority`, then flexible panels by `flexAdjustPriority` — so the panel last in line is the
218
+ one that falls short of its target, even when every declared minimum would fit. While space is left over, it is
219
+ offered to the ratio panels by weight, then to the prioritized flexible panels by weight, then to the pixel panels
220
+ in `pixelAdjustPriority` order up to their `maxSize`, and last, in equal parts, to the flexible panels that have a
221
+ weight of 0 and no `flexAdjustPriority`. A sidebar declared in pixels therefore grows past its declared length when
222
+ nothing else can absorb the surplus: give it a `maxSize` to pin it. When a clamp bites, the residue is
223
+ redistributed among the remaining panels of the same stage in their own ratio, and the container ends up exactly
224
+ filled as long as one panel can still take the surplus; when none can, the gap is reported — as a constraint
225
+ violation when the panels' maximums cannot reach the container, and as a logged invariant breach otherwise. The
226
+ full statement, including the proofs of ratio preservation, continuity and the drag fixed point, is in `docs/specs`
227
+ (`11-layout-allocator.md`).
228
+
229
+ ## Layout Persistence
230
+
231
+ When `autoSaveId` is provided to `PanelGroup`, the layout is written to `localStorage` under the key
232
+ `panel-layout-<autoSaveId>` as `{ id, size, collapsed }[]`, 200ms after the layout stops changing: a change arriving
233
+ inside that window restarts the timer rather than forcing a write, so a drag never runs synchronous storage I/O once
234
+ per frame. The one write outside that timer is the flush on unmount, which hands over the entries still pending so
235
+ the last layout is not lost to a navigation. The stored `size` is the panel's preference — the length it occupies
236
+ while open — not its allocated size, so a panel saved while collapsed still knows what to reopen to. On the next
237
+ mount, and again whenever `autoSaveId` changes, the payload is applied only if it names exactly the panels that are
238
+ present; a payload from a different panel set is ignored rather than applied by position.
239
+ Restoring into a container of a different length scales a flexible panel by ratio, because its stored length is
240
+ re-read as a share of the current container, while a pixel panel restores the absolute length it had.
241
+
242
+ ## Debugging & Inspection
243
+
244
+ Enabling `showDebugInfo` on `PanelGroup` renders inspection metrics over each `Panel` (container length, allocated
245
+ size, preference, resolved bounds) along with a global inspector: a header counting the groups, panels and resize
246
+ handles on the page, then one card per group carrying its direction and container box and, for every panel, the
247
+ state size next to the measured size, both percentages, the preference, the unit, the declared bounds and the two
248
+ priorities. Individual handle measurements and constraint violations are not part of that overlay; a violation is
249
+ shown by the placeholder the group draws over its panels, with or without the flag. Debug components are lazily
250
+ imported (`React.lazy`); without the flag no per-panel `ResizeObserver` is created.
251
+
252
+ ## Styling (CSS)
253
+
254
+ The package provides two CSS consumption options:
255
+
256
+ - **Tailwind v4 Host**: Import components-only styles into `layer(components)` and configure Tailwind to scan package source files:
183
257
 
184
258
  ```css
185
259
  /* app tailwind.css */
186
260
  @import "@aiquants/resize-panels/styles/resize-panels.css" layer(components);
187
261
  @source "../node_modules/@aiquants/resize-panels/src/**/*.{ts,tsx}";
188
- /* モノレポ: @source "../../../../packages/resize-panels/src/**/*.{ts,tsx}"; */
262
+ /* monorepo: @source "../../../../packages/resize-panels/src/**/*.{ts,tsx}"; */
189
263
  ```
190
264
 
191
- `resize-panels.css` (Artifact A) は手書きコンポーネントクラスのみで、スタイルの大半が JSX ユーティリティのためほぼ空になる。**preflight・`:root` テーマ変数・Tailwind ユーティリティを含まない**。preflight Tailwind ホストへ流し込むとホストの base 層を破壊するため、Artifact A には同梱しない (preflight は standalone だけが持つ)。standalone ビルドをここで併用してはならない (同名ユーティリティが重複しカスケードが反転する)。
192
-
193
- - **非 Tailwind ホスト**: 自己完結の standalone ビルドを 1 本だけ読み込む。preflight と全ユーティリティを同梱する。
265
+ - **Non-Tailwind Host**: Import the self-contained standalone CSS bundle:
194
266
 
195
267
  ```css
196
268
  @import "@aiquants/resize-panels/styles/resize-panels.standalone.css";
197
269
  ```
198
270
 
199
- ダークは `<html>` `.dark` クラス基準。
271
+ Dark mode activates based on the `.dark` class on `<html>`.
272
+
273
+ ### Style ownership
274
+
275
+ Your `className` and `style` reach all three components, but the declarations the layout itself depends on are
276
+ applied *after* them and do not accept an override:
277
+
278
+ - **Group**: `display: flex`, `flexDirection`, `alignItems: stretch`, `gap: 0px`, `position: relative`,
279
+ `boxSizing: border-box`, `writingMode: horizontal-tb`. The presentation defaults (`width`, `height`, `minWidth`,
280
+ `minHeight`, `overflow`) are yours to change.
281
+ - **Panel**: the flex triple (`flexBasis` / `flexGrow` / `flexShrink`), `order: 0`, `margin: 0`, `display: flex`,
282
+ `flexDirection: column`, `position: relative`, `boxSizing: border-box`, `minWidth` and `minHeight: 0`,
283
+ `maxWidth` and `maxHeight: none`, the cross-axis `100%`, and `display: none` while collapsed. The presentation
284
+ defaults you can override are `overflow: hidden` and `isolation: isolate`.
285
+ - **Handle button**: `position: absolute`, `zIndex: 20`, `touchAction: none`, the logical inset that centres it on
286
+ the boundary, its primary-axis thickness and its cross-axis `100%`. The `--rp-*` custom properties are placed
287
+ before your `style`, so those you may redefine.
288
+
289
+ Both halves of that list are written to the `style` attribute, so a presentation default is overridable by your
290
+ `style` **only** — a utility class loses to the inline declaration it would replace, which is why the examples
291
+ above give the group its height through `style` rather than an `h-*` class. Every property the components do not
292
+ write inline (colour, padding, borders, radius, …) stays entirely yours to set by class.
293
+
294
+ A panel owns `maxWidth` / `maxHeight` because a single utility class (`max-w-*` and friends) would otherwise draw
295
+ it shorter than the length the allocator handed out, leaving state and rendering disagreeing with the handle stuck.
296
+ It defaults to `isolation: isolate` so that a `z-index` used inside a panel cannot rise above the handles; a panel
297
+ whose content must escape its own box — a `position: fixed` maximised overlay, say — opts out with
298
+ `style={{ isolation: "auto" }}` and accepts that the content may then cover a handle. That trade-off is inherent to
299
+ stacking contexts: nothing can both take part in the panel's own stacking order and paint above its siblings.
300
+
301
+ A panel renders its children inside an in-flow `flex: 1 1 auto` wrapper, so padding you set on a panel behaves
302
+ normally; `contentOverflow` chooses how that wrapper scrolls. The group keeps `overflow: hidden` so that a handle
303
+ straddling a boundary cannot raise a scrollbar — at the cost that when an edge panel collapses, the outer half of
304
+ the handle now sitting on the group's own edge is clipped (its centre stays on the boundary).
200
305
 
201
- ## デモ
306
+ ## Demo App
202
307
 
203
- モノレポ内に簡易デモアプリを用意しています。
308
+ Run the included workspace demo app:
204
309
 
205
310
  ```bash
206
311
  pnpm --filter '@aiquants/resize-panels-demo' dev
207
312
  ```
208
313
 
209
- `http://localhost:5175` にアクセスすると複数レイアウトを含むデモを確認できます。
314
+ Open `http://localhost:5175` to test interactive layouts.
210
315
 
211
- ## ビルド
316
+ ## Build
212
317
 
213
318
  ```bash
214
319
  pnpm --filter '@aiquants/resize-panels' build
215
320
  ```
216
321
 
217
- 生成物は `dist` に出力されます。
322
+ Outputs built artifacts to `dist/`.
@@ -0,0 +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-DqATP_BA.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;
@@ -1,9 +1,9 @@
1
1
  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
- import { useDebugOverlayOwnerId as R, useDebugOverlaySnapshot as T } from "./debugOverlayStore-Cntyxboe.js";
5
- import { P as Y } from "./index-BzgId8aQ.js";
6
- const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s < 0 ? 0 : s).toFixed(8)}${n}`, w = {
4
+ import { useDebugOverlayOwnerId as R, useDebugOverlaySnapshot as T } from "./debugOverlayStore-D7FT6zPE.js";
5
+ import { P as Y } from "./index-DZ0itiBD.js";
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%",
9
9
  bottom: "20px",
@@ -75,11 +75,11 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
75
75
  borderRadius: "9999px",
76
76
  fontSize: "10px",
77
77
  textTransform: "uppercase"
78
- }, M = ({ groupId: s }) => R() !== s ? null : /* @__PURE__ */ e(W, {}), W = () => {
78
+ }, J = ({ groupId: a }) => R() !== a ? null : /* @__PURE__ */ e(W, {}), W = () => {
79
79
  const n = T().groups, [o, g] = A(!0);
80
80
  if (n.length === 0 || typeof document > "u")
81
81
  return null;
82
- const u = n.reduce((r, d) => r + d.panels.length, 0), h = n.reduce((r, d) => r + (d.handles?.length ?? 0), 0), m = () => g((r) => !r), f = /* @__PURE__ */ t(
82
+ const u = n.reduce((i, d) => i + d.panels.length, 0), h = n.reduce((i, d) => i + (d.handles?.length ?? 0), 0), m = () => g((i) => !i), f = /* @__PURE__ */ t(
83
83
  "div",
84
84
  {
85
85
  style: {
@@ -145,8 +145,8 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
145
145
  overflowY: "auto",
146
146
  paddingRight: "6px"
147
147
  },
148
- children: n.map((r, d) => {
149
- const l = r.direction === "horizontal" ? r.containerSize.width : r.containerSize.height;
148
+ children: n.map((i, d) => {
149
+ const l = i.direction === "horizontal" ? i.containerSize.width : i.containerSize.height;
150
150
  return /* @__PURE__ */ t("div", { style: k, children: [
151
151
  /* @__PURE__ */ t(
152
152
  "div",
@@ -171,17 +171,17 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
171
171
  "group ",
172
172
  d + 1,
173
173
  ": ",
174
- r.displayName
174
+ i.displayName
175
175
  ]
176
176
  }
177
177
  ),
178
178
  /* @__PURE__ */ t("span", { children: [
179
179
  "direction: ",
180
- r.direction,
180
+ i.direction,
181
181
  " / container: ",
182
- a(r.containerSize.width, "px"),
182
+ s(i.containerSize.width, "px"),
183
183
  " × ",
184
- a(r.containerSize.height, "px")
184
+ s(i.containerSize.height, "px")
185
185
  ] })
186
186
  ]
187
187
  }
@@ -194,18 +194,18 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
194
194
  gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
195
195
  gap: "10px"
196
196
  },
197
- children: r.panels.map((i, b) => {
198
- const c = i.measuredPixelSize ?? i.size, y = i.size, S = a(y, "px"), v = i.percentageSize ?? (l > 0 ? i.size / l * 100 : void 0), E = i.measuredPercentageSize !== void 0 ? i.measuredPercentageSize : l > 0 ? c / l * 100 : void 0, p = i.collapsed || c <= Y, z = i.collapsed ? "collapsed" : p ? "hidden" : "visible", O = a(v, "%"), L = a(E, "%"), _ = a(c, "px");
197
+ children: i.panels.map((r, b) => {
198
+ const p = r.measuredPixelSize ?? r.size, y = r.size, S = s(y, "px"), v = l > 0 ? r.size / l * 100 : void 0, E = r.measuredPercentageSize !== void 0 ? r.measuredPercentageSize : l > 0 ? p / l * 100 : void 0, c = r.collapsed || p <= Y, z = r.collapsed ? "collapsed" : c ? "hidden" : "visible", L = s(v, "%"), O = s(E, "%"), _ = s(p, "px");
199
199
  return /* @__PURE__ */ t("div", { style: I, children: [
200
200
  /* @__PURE__ */ t("div", { style: x, children: [
201
- /* @__PURE__ */ e("span", { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }, children: i.id }),
201
+ /* @__PURE__ */ e("span", { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }, children: r.id }),
202
202
  /* @__PURE__ */ e(
203
203
  "span",
204
204
  {
205
205
  style: {
206
206
  ...H,
207
- backgroundColor: i.collapsed ? "rgba(251, 113, 133, 0.25)" : p ? "rgba(250, 204, 21, 0.25)" : "rgba(74, 222, 128, 0.25)",
208
- color: i.collapsed ? "#fecdd3" : p ? "#fef08a" : "#bbf7d0"
207
+ backgroundColor: r.collapsed ? "rgba(251, 113, 133, 0.25)" : c ? "rgba(250, 204, 21, 0.25)" : "rgba(74, 222, 128, 0.25)",
208
+ color: r.collapsed ? "#fecdd3" : c ? "#fef08a" : "#bbf7d0"
209
209
  },
210
210
  children: z
211
211
  }
@@ -217,27 +217,27 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
217
217
  /* @__PURE__ */ e("span", { children: "size (measured):" }),
218
218
  /* @__PURE__ */ e("span", { children: _ }),
219
219
  /* @__PURE__ */ e("span", { children: "percentage (state):" }),
220
- /* @__PURE__ */ e("span", { children: O }),
221
- /* @__PURE__ */ e("span", { children: "percentage (measured):" }),
222
220
  /* @__PURE__ */ e("span", { children: L }),
221
+ /* @__PURE__ */ e("span", { children: "preference:" }),
222
+ /* @__PURE__ */ e("span", { children: r.sizeUnit === "pixels" ? s(r.preferredPixelSize, "px") : s(r.preferredPercentageSize, "%") }),
223
+ /* @__PURE__ */ e("span", { children: "percentage (measured):" }),
224
+ /* @__PURE__ */ e("span", { children: O }),
223
225
  /* @__PURE__ */ e("span", { children: "unit:" }),
224
- /* @__PURE__ */ e("span", { children: i.sizeUnit }),
226
+ /* @__PURE__ */ e("span", { children: r.sizeUnit }),
225
227
  /* @__PURE__ */ e("span", { children: "min:" }),
226
- /* @__PURE__ */ e("span", { children: i.minSize !== void 0 ? JSON.stringify(i.minSize) : "—" }),
227
- /* @__PURE__ */ e("span", { children: "auto min:" }),
228
- /* @__PURE__ */ e("span", { children: i.autoMinSize !== void 0 ? JSON.stringify(i.autoMinSize) : "—" }),
228
+ /* @__PURE__ */ e("span", { children: r.minSize !== void 0 ? JSON.stringify(r.minSize) : "—" }),
229
229
  /* @__PURE__ */ e("span", { children: "max:" }),
230
- /* @__PURE__ */ e("span", { children: i.maxSize !== void 0 ? JSON.stringify(i.maxSize) : "—" }),
230
+ /* @__PURE__ */ e("span", { children: r.maxSize !== void 0 ? JSON.stringify(r.maxSize) : "—" }),
231
231
  /* @__PURE__ */ e("span", { children: "priority:" }),
232
- /* @__PURE__ */ e("span", { children: i.pixelAdjustPriority ?? "—" }),
232
+ /* @__PURE__ */ e("span", { children: r.pixelAdjustPriority ?? "—" }),
233
233
  /* @__PURE__ */ e("span", { children: "flex priority:" }),
234
- /* @__PURE__ */ e("span", { children: i.flexAdjustPriority ?? "—" })
234
+ /* @__PURE__ */ e("span", { children: r.flexAdjustPriority ?? "—" })
235
235
  ] })
236
- ] }, i.id || b);
236
+ ] }, r.id || b);
237
237
  })
238
238
  }
239
239
  )
240
- ] }, r.groupId);
240
+ ] }, i.groupId);
241
241
  })
242
242
  }
243
243
  )
@@ -248,5 +248,5 @@ const a = (s, n) => typeof s != "number" || !Number.isFinite(s) ? "—" : `${(s
248
248
  };
249
249
  export {
250
250
  W as GlobalDebugOverlay,
251
- M as GlobalDebugOverlayGate
251
+ J as GlobalDebugOverlayGate
252
252
  };