@aiquants/resize-panels 2.0.0 → 2.0.1

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 (64) hide show
  1. package/dist/GlobalDebugOverlay.d.ts +4 -0
  2. package/dist/Panel.d.ts +13 -0
  3. package/dist/{src/PanelDebugInfo.d.ts → PanelDebugInfo.d.ts} +2 -14
  4. package/dist/PanelGroup.d.ts +2 -0
  5. package/dist/PanelResizeHandle.d.ts +2 -0
  6. package/dist/allocateLayout.d.ts +12 -0
  7. package/dist/context.d.ts +3 -0
  8. package/dist/debugOverlayStore.d.ts +32 -0
  9. package/dist/{src/hooks.d.ts → hooks.d.ts} +8 -23
  10. package/dist/index.d.ts +13 -2
  11. package/dist/reducer.d.ts +8 -0
  12. package/dist/roundHalfToEven.d.ts +1 -0
  13. package/dist/types.d.ts +183 -0
  14. package/dist/utils/simple-logger.d.ts +37 -0
  15. package/dist/utils.d.ts +16 -0
  16. package/package.json +2 -3
  17. package/dist/src/GlobalDebugOverlay.d.ts +0 -21
  18. package/dist/src/GlobalDebugOverlay.d.ts.map +0 -1
  19. package/dist/src/Panel.d.ts +0 -46
  20. package/dist/src/Panel.d.ts.map +0 -1
  21. package/dist/src/PanelDebugInfo.d.ts.map +0 -1
  22. package/dist/src/PanelGroup.d.ts +0 -7
  23. package/dist/src/PanelGroup.d.ts.map +0 -1
  24. package/dist/src/PanelResizeHandle.d.ts +0 -7
  25. package/dist/src/PanelResizeHandle.d.ts.map +0 -1
  26. package/dist/src/allocateLayout.d.ts +0 -60
  27. package/dist/src/allocateLayout.d.ts.map +0 -1
  28. package/dist/src/context.d.ts +0 -12
  29. package/dist/src/context.d.ts.map +0 -1
  30. package/dist/src/debugOverlayStore.d.ts +0 -69
  31. package/dist/src/debugOverlayStore.d.ts.map +0 -1
  32. package/dist/src/hooks.d.ts.map +0 -1
  33. package/dist/src/index.d.ts +0 -32
  34. package/dist/src/index.d.ts.map +0 -1
  35. package/dist/src/reducer.d.ts +0 -49
  36. package/dist/src/reducer.d.ts.map +0 -1
  37. package/dist/src/roundHalfToEven.d.ts +0 -10
  38. package/dist/src/roundHalfToEven.d.ts.map +0 -1
  39. package/dist/src/types.d.ts +0 -481
  40. package/dist/src/types.d.ts.map +0 -1
  41. package/dist/src/utils/simple-logger.d.ts +0 -111
  42. package/dist/src/utils/simple-logger.d.ts.map +0 -1
  43. package/dist/src/utils.d.ts +0 -164
  44. package/dist/src/utils.d.ts.map +0 -1
  45. package/dist/tests/support/dom-harness.d.ts +0 -89
  46. package/dist/tests/support/dom-harness.d.ts.map +0 -1
  47. package/src/GlobalDebugOverlay.tsx +0 -284
  48. package/src/Panel.tsx +0 -297
  49. package/src/PanelDebugInfo.tsx +0 -94
  50. package/src/PanelGroup.tsx +0 -245
  51. package/src/PanelResizeHandle.tsx +0 -758
  52. package/src/allocateLayout.ts +0 -412
  53. package/src/context.ts +0 -25
  54. package/src/debugOverlayStore.ts +0 -355
  55. package/src/hooks.ts +0 -376
  56. package/src/index.ts +0 -93
  57. package/src/reducer.ts +0 -472
  58. package/src/roundHalfToEven.ts +0 -40
  59. package/src/styles/components.entry.css +0 -10
  60. package/src/styles/resize-panels.css +0 -1
  61. package/src/styles/standalone.entry.css +0 -12
  62. package/src/types.ts +0 -505
  63. package/src/utils/simple-logger.ts +0 -186
  64. package/src/utils.ts +0 -320
package/src/utils.ts DELETED
@@ -1,320 +0,0 @@
1
- /**
2
- * @file Utility functions for custom resizable panels
3
- * カスタムリサイズ可能パネルのためのユーティリティ関数
4
- */
5
-
6
- import { roundHalfToEven } from "./roundHalfToEven"
7
- import { type ContainerAxisMeasurement, type FlexibleSize, PANEL_COLLAPSE_THRESHOLD_MAX, PANEL_COLLAPSE_THRESHOLD_RATIO, PANEL_SNAP_THRESHOLD, type PanelDirection, type PersistedPanelSize, type SizeConfig, type SizeUnit } from "./types"
8
-
9
- /**
10
- * Number of significant fractional digits to preserve when rounding percentage-based sizes.
11
- * パーセンテージサイズの丸めで保持する有効小数桁数。
12
- */
13
- export const PERCENTAGE_DIGITS = 6
14
-
15
- /**
16
- * Convert a pixel measurement to a percentage of the container using banker's rounding.
17
- * ピクセル寸法をバンカーズラウンディングでコンテナ比率のパーセンテージへ変換する。
18
- *
19
- * @param size - Length in pixels / 長さ (px)
20
- * @param base - Container length the percentage is relative to / 基準となるコンテナ長
21
- * @returns Percentage of the container, 0 when the base is not positive / コンテナ比 (%)、基準が非正なら 0
22
- */
23
- export const toRoundedPercentage = (size: number, base: number): number => {
24
- if (base <= 0) {
25
- return 0
26
- }
27
- return roundHalfToEven((size / base) * 100, PERCENTAGE_DIGITS)
28
- }
29
-
30
- /**
31
- * Translate a percentage-based size back into pixels relative to the container.
32
- * パーセンテージ指定のサイズをコンテナ基準のピクセル値へ変換する。
33
- *
34
- * @param percentage - Share of the container in percent / コンテナ比 (%)
35
- * @param base - Container length the percentage is relative to / 基準となるコンテナ長
36
- * @returns Length in pixels / 長さ (px)
37
- */
38
- export const percentageToPixels = (percentage: number, base: number): number => {
39
- if (base <= 0) {
40
- return 0
41
- }
42
- return (percentage / 100) * base
43
- }
44
-
45
- /**
46
- * Normalize a flexible size into a size configuration object.
47
- * 柔軟なサイズ指定をサイズ設定オブジェクトへ正規化する。
48
- *
49
- * @param size - Author-provided size / 利用者が指定したサイズ
50
- * @param defaultUnit - Unit applied to bare numbers / 数値のみの指定に適用する単位
51
- * @returns Normalized configuration, or undefined when nothing was provided / 正規化後の設定、未指定なら undefined
52
- */
53
- export const normalizeSizeConfig = (size: FlexibleSize | undefined, defaultUnit: SizeUnit = "percentage"): SizeConfig | undefined => {
54
- if (size === undefined) {
55
- return undefined
56
- }
57
- if (typeof size === "number") {
58
- return { value: size, unit: defaultUnit }
59
- }
60
- return size
61
- }
62
-
63
- /**
64
- * Convert flexible size constraints into pixel values based on container size.
65
- * 柔軟サイズ制約をコンテナサイズ基準のピクセル値へ変換する。
66
- *
67
- * @param size - Constraint to resolve; bare numbers are percentages / 解決する制約 (数値のみの指定はパーセンテージ)
68
- * @param defaultValue - Value used when the constraint is absent / 制約が無い場合の値
69
- * @param containerSize - Container content-box length / コンテナ内容領域長
70
- * @returns Constraint in pixels / ピクセル換算した制約
71
- */
72
- export const getConstraintInPixels = (size: FlexibleSize | undefined, defaultValue: number, containerSize: number): number => {
73
- if (size === undefined) {
74
- return defaultValue
75
- }
76
- if (typeof size === "number") {
77
- return (size / 100) * containerSize
78
- }
79
- if (size.unit === "pixels") {
80
- return size.value
81
- }
82
- return (size.value / 100) * containerSize
83
- }
84
-
85
- /**
86
- * Clamp a numeric value between the provided bounds.
87
- * 数値を指定した範囲に収めるためのクリッピング処理。
88
- *
89
- * @param value - Value to clamp / 収める値
90
- * @param min - Lower bound / 下限
91
- * @param max - Upper bound / 上限
92
- * @returns Clamped value / 範囲内へ収めた値
93
- */
94
- export const clamp = (value: number, min: number, max: number): number => {
95
- return Math.min(Math.max(value, min), max)
96
- }
97
-
98
- /**
99
- * Derive a snap-to-zero threshold from the container axis length using shared heuristics.
100
- * 共通ヒューリスティクスに基づきコンテナ軸長からゼロスナップ閾値を算出する。
101
- *
102
- * @param axisSize - Container content-box length along the axis / 軸方向のコンテナ内容領域長
103
- * @returns Threshold in pixels below which a panel snaps closed / 閉じたとみなすピクセル閾値
104
- */
105
- export const calculateSnapThreshold = (axisSize: number): number => {
106
- if (!Number.isFinite(axisSize) || axisSize <= 0) {
107
- return 0
108
- }
109
- return Math.max(0, Math.min(PANEL_SNAP_THRESHOLD, PANEL_COLLAPSE_THRESHOLD_MAX, axisSize * PANEL_COLLAPSE_THRESHOLD_RATIO))
110
- }
111
-
112
- /**
113
- * Parse a CSS pixel string and return the numeric portion.
114
- * CSS ピクセル表現から数値を抽出する処理。
115
- */
116
- const parsePixelValue = (value: string | null | undefined): number => {
117
- if (!value) {
118
- return 0
119
- }
120
- const parsed = Number.parseFloat(value)
121
- return Number.isFinite(parsed) ? parsed : 0
122
- }
123
-
124
- /**
125
- * Measure a container along one axis, returning its content-box and border-box lengths.
126
- * 指定軸でコンテナを計測し、内容領域長と境界領域長を返す処理。
127
- *
128
- * The content box is the authority for every layout number in this package: a panel's `flex-basis` resolves
129
- * against it, so the allocator must fill exactly this length for the rendered geometry and the reducer state
130
- * to agree. The measurement is therefore taken in LAYOUT pixels — `getBoundingClientRect` reports the visual
131
- * rect, so a scaled ancestor would otherwise make the group allocate a container it does not have.
132
- * このパッケージのレイアウト数値の基準は内容領域である。パネルの `flex-basis` はこの領域に解決されるため、
133
- * 描画結果とリデューサー状態を一致させるには配分器がこの長さを満たす必要がある。計測は**レイアウトピクセル**で
134
- * 行う: `getBoundingClientRect` は視覚上の矩形を返すため、拡大縮小された祖先の下では存在しない長さを配ってしまう。
135
- *
136
- * @param element - Element to measure / 計測対象の要素
137
- * @param direction - Axis to measure along / 計測する軸
138
- * @param entry - Observation that triggered the measurement, when it came from a ResizeObserver / 計測契機となった観測結果
139
- * @returns Content-box and border-box lengths in pixels / 内容領域長と境界領域長 (px)
140
- */
141
- export const getContainerSize = (element: HTMLElement, direction: PanelDirection, entry?: ResizeObserverEntry): ContainerAxisMeasurement => {
142
- const isHorizontal = direction === "horizontal"
143
- const styles = typeof window !== "undefined" ? window.getComputedStyle(element) : null
144
- const padding = styles ? (isHorizontal ? parsePixelValue(styles.paddingLeft) + parsePixelValue(styles.paddingRight) : parsePixelValue(styles.paddingTop) + parsePixelValue(styles.paddingBottom)) : 0
145
- const borders = styles ? (isHorizontal ? parsePixelValue(styles.borderLeftWidth) + parsePixelValue(styles.borderRightWidth) : parsePixelValue(styles.borderTopWidth) + parsePixelValue(styles.borderBottomWidth)) : 0
146
-
147
- // ResizeObserver の内容ボックス寸法はレイアウトピクセルの厳密値なので、得られるときは最優先で使う
148
- const observedContent = entry?.contentBoxSize?.[0]
149
- const observed = observedContent ? (isHorizontal ? observedContent.inlineSize : observedContent.blockSize) : undefined
150
- // clientWidth / clientHeight もレイアウトピクセルだが整数に丸められる。端数を持つコンテナのために
151
- // 内容ボックス寸法を優先し、無い環境でのみこちらへ落とす
152
- const clientLength = isHorizontal ? element.clientWidth : element.clientHeight
153
- const fallback = clientLength > 0 ? Math.max(0, clientLength - padding) : Math.max(0, (isHorizontal ? element.offsetWidth : element.offsetHeight) - padding - borders)
154
- const content = observed ?? fallback
155
-
156
- return { content: Math.max(0, content), border: Math.max(0, content + padding + borders) }
157
- }
158
-
159
- /**
160
- * Selector that identifies a panel group element.
161
- * パネルグループ要素を特定するセレクタ。
162
- */
163
- const GROUP_SELECTOR = "[data-panel-group-direction]"
164
-
165
- /**
166
- * Selector that matches the elements a group lays out: its panels and its handle anchors.
167
- * グループが配置する要素 (パネルとハンドルのアンカー) に一致するセレクタ。
168
- */
169
- export const LAYOUT_ELEMENT_SELECTOR = "[data-panel-id], [data-resize-handle-anchor]"
170
-
171
- /**
172
- * Selector that matches a panel element.
173
- * パネル要素に一致するセレクタ。
174
- */
175
- const PANEL_SELECTOR = "[data-panel-id]"
176
-
177
- /**
178
- * Resolve the innermost panel group that owns an element.
179
- * ある要素を所有する最も内側のパネルグループを解決する処理。
180
- *
181
- * The owner is never simply the parent: a wrapper element (including `display: contents`, which flex layout
182
- * flattens away) sits between the group and its panels without changing the geometry, and nested groups make
183
- * "the closest group" the only answer that is right in both directions.
184
- * 所有者は単なる親要素ではない。中間要素 (フレックスレイアウトが畳む `display: contents` を含む) は幾何を
185
- * 変えずにグループとパネルの間へ入り込み、入れ子グループもあるため「最も近いグループ」だけが正解となる。
186
- *
187
- * @param element - Element to resolve the owner for / 所有者を解決する対象の要素
188
- * @returns Owning group element, or null when the element is outside every group / 所有するグループ要素、どのグループにも属さないなら null
189
- */
190
- export const findOwningGroup = (element: HTMLElement | null | undefined): HTMLElement | null => element?.closest<HTMLElement>(GROUP_SELECTOR) ?? null
191
-
192
- /**
193
- * Collect the panels and handle anchors a group owns, in document order.
194
- * グループが所有するパネルとハンドルのアンカーを、文書順で収集する処理。
195
- *
196
- * Direct children are not enough: a wrapper (including `display: contents`, which flex layout flattens away)
197
- * leaves the geometry intact while hiding the panels from a sibling scan, and the handle would silently lose
198
- * its neighbours.
199
- * 直下の子だけでは足りない。中間要素 (フレックスレイアウトが畳む `display: contents` を含む) があると幾何は
200
- * 保たれたままパネルが兄弟走査から消え、ハンドルが隣接パネルを見失う。
201
- *
202
- * @param group - Group element / グループ要素
203
- * @returns Owned panel and anchor elements in document order / 所有するパネル・アンカー要素 (文書順)
204
- */
205
- export const getGroupLayoutElements = (group: HTMLElement): HTMLElement[] =>
206
- Array.from(group.querySelectorAll<HTMLElement>(LAYOUT_ELEMENT_SELECTOR)).filter((element) => {
207
- if (findOwningGroup(element) !== group) {
208
- return false
209
- }
210
- // `data-panel-id` は他パッケージも出力する。パネルの中に現れたものを配置要素と見なすと、
211
- // ハンドルが本物の隣を見失って動かなくなる
212
- const enclosingPanel = element.parentElement?.closest<HTMLElement>(PANEL_SELECTOR)
213
- return !(enclosingPanel && group.contains(enclosingPanel))
214
- })
215
-
216
- /**
217
- * Report whether a DOM mutation touched an element the group lays out.
218
- * DOM の変更がグループの配置要素に触れたかどうかを判定する処理。
219
- *
220
- * Content inside a panel changes constantly (a virtualised list recycles rows every frame), so a group-wide
221
- * rescan on every mutation costs time proportional to the whole subtree for changes that can never move a
222
- * panel. Only additions and removals that carry a layout element can.
223
- * パネルの内容は絶え間なく変わる (仮想化リストは毎フレーム行を入れ替える) ため、変更のたびにグループ全体を
224
- * 走査すると、パネルを動かしようのない変更に対して部分木全体に比例した時間を払う。配置要素を含む
225
- * 追加・削除だけがパネルを動かしうる。
226
- *
227
- * @param records - Mutation records delivered by the observer / オブザーバーが届けた変更記録
228
- * @returns True when at least one record added or removed a layout element / 配置要素の追加・削除が 1 件でもあれば true
229
- */
230
- export const touchesLayoutElements = (records: ReadonlyArray<MutationRecord>): boolean => {
231
- const carriesLayoutElement = (nodes: NodeList): boolean => {
232
- for (const node of Array.from(nodes)) {
233
- if (!(node instanceof Element)) {
234
- continue
235
- }
236
- if (node.matches(LAYOUT_ELEMENT_SELECTOR) || node.querySelector(LAYOUT_ELEMENT_SELECTOR) !== null) {
237
- return true
238
- }
239
- }
240
- return false
241
- }
242
- return records.some((record) => carriesLayoutElement(record.addedNodes) || carriesLayoutElement(record.removedNodes))
243
- }
244
-
245
- /**
246
- * Ratio between visual pixels (pointer coordinates) and layout pixels (the lengths the allocator hands out).
247
- * 視覚ピクセル (ポインタ座標) とレイアウトピクセル (配分器が配る長さ) の比。
248
- *
249
- * A transformed ancestor scales pointer coordinates but not the lengths the layout is computed in, so a drag
250
- * delta read straight from the pointer moves the boundary by `travel × scale` instead of `travel` — the grab
251
- * point slides out from under the cursor, and the error grows without bound as the gesture continues.
252
- * 変形された祖先の下ではポインタ座標だけが拡大縮小され、レイアウトが計算される長さは変わらない。
253
- * ポインタの変位をそのまま使うと境界は `移動量 × 倍率` しか動かず、掴んだ点がカーソルから滑り出し、
254
- * その誤差はジェスチャーが続く限り増え続ける。
255
- *
256
- * @param element - Element whose box is compared in both spaces / 双方の空間で矩形を比べる要素
257
- * @param direction - Axis to measure along / 計測する軸
258
- * @returns Visual length divided by layout length, 1 when either is unavailable / 視覚長 ÷ レイアウト長 (いずれか不明なら 1)
259
- */
260
- export const getVisualScale = (element: HTMLElement, direction: PanelDirection): number => {
261
- if (typeof window === "undefined") {
262
- return 1
263
- }
264
- const isHorizontal = direction === "horizontal"
265
- const rect = element.getBoundingClientRect()
266
- const visual = isHorizontal ? rect.width : rect.height
267
- const styles = window.getComputedStyle(element)
268
- // グループは box-sizing: border-box を所有するため、計算値の幅・高さは視覚矩形と同じ境界領域を指す。
269
- // ただし配置されていない要素では指定値 (`100%` など) が返るため、px でなければ換算しない
270
- const declared = isHorizontal ? styles.width : styles.height
271
- if (!declared.endsWith("px")) {
272
- return 1
273
- }
274
- const layout = Number.parseFloat(declared)
275
- return visual > 0 && Number.isFinite(layout) && layout > 0 ? visual / layout : 1
276
- }
277
-
278
- /**
279
- * Persist a panel layout to local storage.
280
- * パネルレイアウトをローカルストレージへ保存する。
281
- *
282
- * @param id - Auto-save identifier / 自動保存の識別子
283
- * @param entries - Panel lengths with their identifiers, in DOM order / DOM 順の識別子つきパネル長
284
- */
285
- export const saveLayout = (id: string, entries: PersistedPanelSize[]): void => {
286
- try {
287
- localStorage.setItem(`panel-layout-${id}`, JSON.stringify(entries))
288
- } catch (error) {
289
- console.warn("Failed to save panel layout:", error)
290
- }
291
- }
292
-
293
- /**
294
- * Restore a panel layout from local storage.
295
- * パネルレイアウトをローカルストレージから復元する。
296
- *
297
- * A stored layout is only usable when every entry names the panel it belongs to, so a payload written by a
298
- * different panel set is rejected instead of being applied by position.
299
- * 保存レイアウトは各項目が所属パネルを名指ししている場合にだけ利用できる。別構成が書いた内容は位置で当てはめず拒否する。
300
- *
301
- * @param id - Auto-save identifier / 自動保存の識別子
302
- * @returns Stored layout, or null when nothing usable is stored / 保存済みレイアウト、利用できなければ null
303
- */
304
- export const loadLayout = (id: string): PersistedPanelSize[] | null => {
305
- try {
306
- const saved = localStorage.getItem(`panel-layout-${id}`)
307
- if (!saved) {
308
- return null
309
- }
310
- const parsed: unknown = JSON.parse(saved)
311
- if (!Array.isArray(parsed)) {
312
- return null
313
- }
314
- const isEntry = (value: unknown): value is PersistedPanelSize => typeof value === "object" && value !== null && typeof (value as PersistedPanelSize).id === "string" && Number.isFinite((value as PersistedPanelSize).size)
315
- return parsed.every(isEntry) ? parsed : null
316
- } catch (error) {
317
- console.warn("Failed to load panel layout:", error)
318
- return null
319
- }
320
- }