@aiquants/resize-panels 1.7.2 → 1.8.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.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Round a number to specified digits using banker's rounding (round half to even).
3
+ * 指定した桁で銀行丸め (最近接偶数への丸め) を行う関数。
4
+ *
5
+ * @param value - 丸め対象の数値
6
+ * @param digits - 丸める桁数 (0: 整数, 1: 小数第1位, -1: 十の位)
7
+ * @returns 丸められた数値
8
+ */
9
+ export const roundHalfToEven = (value: number, digits: number = 0): number => {
10
+ // 非有限値はそのまま返す
11
+ if (!Number.isFinite(value)) {
12
+ return value
13
+ }
14
+
15
+ // 桁を移動させるための乗数を計算
16
+ const multiplier = 10 ** digits
17
+ const shifted = value * multiplier
18
+
19
+ // 整数部分と小数部分を分離
20
+ const floor = Math.floor(shifted)
21
+ const fraction = shifted - floor
22
+
23
+ // 浮動小数点誤差を考慮したイプシロン値
24
+ const epsilon = Number.EPSILON * Math.max(1, Math.abs(shifted))
25
+
26
+ // 0.5 未満の場合は切り捨て
27
+ if (fraction < 0.5 - epsilon) {
28
+ return floor / multiplier
29
+ }
30
+
31
+ // 0.5 より大きい場合は切り上げ
32
+ if (fraction > 0.5 + epsilon) {
33
+ return (floor + 1) / multiplier
34
+ }
35
+
36
+ // ちょうど 0.5 の場合は、最近接偶数に丸める
37
+ // floor が偶数なら floor、奇数なら floor + 1
38
+ const rounded = floor % 2 === 0 ? floor : floor + 1
39
+ return rounded / multiplier
40
+ }
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Artifact A: components-only CSS (Tailwind ホスト向け).
3
+ * 手書きコンポーネントクラスのみ (resize-panels は大半のスタイルを JSX ユーティリティで持つため
4
+ * この成果物はほぼ空になる)。preflight・:root テーマ変数・ユーティリティは含まない
5
+ * (preflight を Tailwind ホストへ流し込むとホストの base 層を破壊するため厳禁)。
6
+ */
7
+ @import "tailwindcss/theme.css" theme(reference);
8
+ @import "./resize-panels.css";
@@ -0,0 +1,3 @@
1
+ @layer components {
2
+ /* nothing */
3
+ }
@@ -0,0 +1,12 @@
1
+ /*! @aiquants/resize-panels standalone CSS — 非 Tailwind ホスト専用。ホストの Tailwind ビルドと混在させないこと */
2
+ /*
3
+ * Artifact B: 自己完結スタンドアロン. 非 Tailwind ホストの利便のため preflight を同梱する
4
+ * (Tailwind ホストはホスト自身の preflight があるので Artifact A を使うこと)。
5
+ */
6
+ @layer theme, base, components, utilities;
7
+ @import "tailwindcss/theme.css" layer(theme);
8
+ @import "tailwindcss/preflight.css" layer(base);
9
+ @import "tailwindcss/utilities.css" layer(utilities) source(none);
10
+ @import "./resize-panels.css" layer(components);
11
+ @source "../**/*.{ts,tsx}";
12
+ @custom-variant dark (&:where(.dark, .dark *));
package/src/types.ts ADDED
@@ -0,0 +1,288 @@
1
+ /**
2
+ * @file Type definitions for the resizable panels components
3
+ * リサイザブルパネルコンポーネントの型定義
4
+ */
5
+
6
+ import type { CSSProperties, ReactNode } from "react"
7
+
8
+ /**
9
+ * Pixel threshold used to decide when panels snap closed or open.
10
+ * パネルのスナップ判定に利用するピクセル閾値。
11
+ */
12
+ export const PANEL_SNAP_THRESHOLD = 12
13
+
14
+ /**
15
+ * Default thickness in pixels for the resize handle interactive area.
16
+ * リサイズハンドルのインタラクティブ領域のデフォルト厚み (px)。
17
+ */
18
+ export const PANEL_HANDLE_THICKNESS = 8
19
+
20
+ /**
21
+ * Spacing in pixels between visual indicator dots in the handle.
22
+ * ハンドル内の視覚インジケーター点同士のピクセル間隔。
23
+ */
24
+ export const PANEL_VISIBLE_INDICATOR_SPACING = 8
25
+
26
+ /**
27
+ * Pointer move throttling interval in milliseconds during dragging.
28
+ * ドラッグ中のポインタ移動を間引くミリ秒単位の間隔。
29
+ */
30
+ export const PANEL_HANDLE_DRAG_THROTTLE_MS = 8
31
+
32
+ /**
33
+ * Pixel step applied per arrow-key press when resizing with the keyboard.
34
+ * キーボード (矢印キー) リサイズで 1 押下ごとに適用するピクセル量。
35
+ */
36
+ export const PANEL_KEYBOARD_RESIZE_STEP = 10
37
+
38
+ /**
39
+ * Ratio of container size used when determining collapse thresholds.
40
+ * 折りたたみ閾値を決定する際に使用するコンテナサイズ比率。
41
+ */
42
+ export const PANEL_COLLAPSE_THRESHOLD_RATIO = 0.05
43
+
44
+ /**
45
+ * Maximum pixel value considered when collapsing panels.
46
+ * パネルを折りたたむ際に考慮するピクセル上限値。
47
+ */
48
+ export const PANEL_COLLAPSE_THRESHOLD_MAX = 10
49
+
50
+ /**
51
+ * Ratio of container size required to expand a collapsed panel.
52
+ * 折りたたまれたパネルを展開するために必要なコンテナサイズ比率。
53
+ */
54
+ export const PANEL_EXPAND_THRESHOLD_RATIO = 0.05
55
+
56
+ /**
57
+ * Maximum pixel threshold used when expanding panels.
58
+ * パネルを展開する際に用いるピクセル閾値の上限。
59
+ */
60
+ export const PANEL_EXPAND_THRESHOLD_MAX = 20
61
+
62
+ /**
63
+ * Minimum thickness required for a handle to be treated as visible.
64
+ * ハンドルが可視と判断されるために必要な最小厚み。
65
+ */
66
+ export const PANEL_HANDLE_VISIBILITY_THRESHOLD = 0.05
67
+
68
+ /**
69
+ * Retry intervals in milliseconds when awaiting layout stabilization.
70
+ * レイアウトの安定化を待機するときに使用するミリ秒単位の再試行間隔。
71
+ */
72
+ export const PANEL_RESIZE_RETRY_INTERVALS = [10, 50, 100, 200, 300, 500, 800] as const
73
+
74
+ /**
75
+ * Tolerance used to decide whether pixel-based panel adjustments are significant.
76
+ * ピクセル基準パネルの補正を有効とみなすための誤差許容値。
77
+ */
78
+ export const PANEL_PIXEL_MUTATION_TOLERANCE = 0.0
79
+
80
+ /**
81
+ * Minimum pixel threshold used when clamping snap calculations.
82
+ * スナップ計算を補正する際に用いる最小ピクセル閾値。
83
+ */
84
+ export const PANEL_MIN_SNAP_THRESHOLD = 0.0
85
+
86
+ /**
87
+ * Layout direction of panels
88
+ * パネルのレイアウト方向
89
+ */
90
+ export type PanelDirection = "horizontal" | "vertical"
91
+
92
+ /**
93
+ * Size unit types for panels
94
+ * パネルのサイズ単位タイプ
95
+ */
96
+ export type SizeUnit = "percentage" | "pixels"
97
+
98
+ /**
99
+ * Size configuration with value and unit
100
+ * 値と単位を持つサイズ設定
101
+ */
102
+ export type SizeConfig = {
103
+ value: number
104
+ unit: SizeUnit
105
+ }
106
+
107
+ /**
108
+ * Flexible size that can be defined in pixels or as a percentage
109
+ * ピクセルまたはパーセンテージで定義できる柔軟なサイズ
110
+ */
111
+ export type FlexibleSize = SizeConfig | number
112
+
113
+ /**
114
+ * Container size dimensions
115
+ * コンテナサイズの寸法
116
+ */
117
+ export type ContainerSize = {
118
+ width: number
119
+ height: number
120
+ }
121
+
122
+ /**
123
+ * Direction from which collapsible panels may close.
124
+ * パネルを折りたためる方向を表す列挙値。
125
+ */
126
+ export type PanelCollapseDirection = "start" | "end"
127
+
128
+ /**
129
+ * Directional options accepted by the collapsible configuration.
130
+ * 折りたたみ設定が受け付ける方向指定値。
131
+ */
132
+ export type CollapsibleDirection = PanelCollapseDirection | "both"
133
+
134
+ /**
135
+ * Configuration object describing how a panel collapses.
136
+ * パネルの折りたたみ方を表す設定オブジェクト。
137
+ */
138
+ export type CollapsibleConfig = {
139
+ from: CollapsibleDirection
140
+ }
141
+
142
+ /**
143
+ * Axis-aligned container measurement including inner (borderless) and outer dimensions.
144
+ * 枠線控除後と控除前の両方の寸法を含む軸方向コンテナ計測値。
145
+ */
146
+ export type ContainerAxisMeasurement = {
147
+ inner: number
148
+ outer: number
149
+ }
150
+
151
+ /**
152
+ * Snapshot describing when panel constraints conflict with the container size.
153
+ * パネル制約とコンテナサイズが矛盾する際のスナップショット。
154
+ */
155
+ export type LayoutConstraintViolationReason = "minimum-exceeded" | "maximum-insufficient"
156
+
157
+ export type LayoutConstraintViolation = {
158
+ reason: LayoutConstraintViolationReason
159
+ totalMinimumSize: number | null
160
+ totalMaximumSize: number | null
161
+ availableContainerSize: number
162
+ }
163
+
164
+ /**
165
+ * Layout data for a single panel
166
+ * 単一パネルのレイアウトデータ
167
+ */
168
+ export interface PanelLayoutData {
169
+ id: string
170
+ size: number
171
+ percentageSize?: number
172
+ preferredPercentageSize?: number
173
+ preferredPixelSize?: number
174
+ sizeUnit: SizeUnit
175
+ originalPixelSize?: number
176
+ minSize?: FlexibleSize
177
+ maxSize?: FlexibleSize
178
+ autoMinSize?: FlexibleSize
179
+ collapseFromStart: boolean
180
+ collapseFromEnd: boolean
181
+ collapsedByDirection?: PanelCollapseDirection | null
182
+ /**
183
+ * Skip automatic growth for flexible panels that were manually reduced to a zero footprint.
184
+ * 手動でゼロ相当まで縮めた柔軟パネルを自動拡張対象から外すフラグ。
185
+ */
186
+ excludeFromAutoGrowth?: boolean
187
+ collapsed?: boolean
188
+ sizeBeforeCollapse?: number
189
+ percentageSizeBeforeCollapse?: number
190
+ preferredPercentageSizeBeforeCollapse?: number
191
+ preferredPixelSizeBeforeCollapse?: number
192
+ pixelAdjustPriority?: number
193
+ flexAdjustPriority?: number
194
+ measuredPixelSize?: number
195
+ measuredPercentageSize?: number
196
+ measuredPixelSizeBeforeCollapse?: number
197
+ }
198
+
199
+ /**
200
+ * Props for the Panel component
201
+ * Panel コンポーネントのプロパティ
202
+ */
203
+ export interface PanelProps {
204
+ id?: string
205
+ defaultSize?: FlexibleSize
206
+ minSize?: FlexibleSize
207
+ maxSize?: FlexibleSize
208
+ autoMinSize?: FlexibleSize
209
+ className?: string
210
+ style?: CSSProperties
211
+ children?: ReactNode
212
+ order?: number
213
+ collapsible?: CollapsibleConfig
214
+ defaultCollapsed?: boolean
215
+ pixelAdjustPriority?: number
216
+ flexAdjustPriority?: number
217
+ contentOverflow?: CSSProperties["overflow"]
218
+ }
219
+
220
+ /**
221
+ * Props for the PanelGroup component
222
+ * PanelGroup コンポーネントのプロパティ
223
+ */
224
+ export interface PanelGroupProps {
225
+ id?: string
226
+ direction: PanelDirection
227
+ storageKey?: string
228
+ className?: string
229
+ style?: React.CSSProperties
230
+ children?: ReactNode
231
+ showDebugInfo?: boolean
232
+ onLayout?: (panelSizes: number[]) => void
233
+ autoSaveId?: string
234
+ }
235
+
236
+ /**
237
+ * Value provided by the PanelGroupContext
238
+ * PanelGroupContext によって提供される値
239
+ */
240
+ export interface PanelGroupContextValue {
241
+ direction: PanelDirection
242
+ registerPanel: (panel: PanelLayoutData) => void
243
+ unregisterPanel: (id: string) => void
244
+ getPanel: (id: string) => PanelLayoutData | undefined
245
+ panels: PanelLayoutData[]
246
+ isContainerReady: boolean
247
+ showDebugInfo: boolean
248
+ resizePanels: (leftId: string, rightId: string, leftSize: number, rightSize: number) => void
249
+ collapsePanel: (id: string, from: PanelCollapseDirection) => void
250
+ expandPanel: (id: string, from: PanelCollapseDirection, targetSize?: number) => void
251
+ containerSize: ContainerSize
252
+ reportPanelMeasurement: (id: string, measurement: PanelMeasurement | null) => void
253
+ panelMeasurements: Record<string, PanelMeasurement>
254
+ reportHandleMeasurement: (id: string, measurement: ResizeHandleLayoutData | null) => void
255
+ handleMeasurements: Record<string, ResizeHandleLayoutData>
256
+ layoutConstraintViolation: LayoutConstraintViolation | null
257
+ }
258
+
259
+ /**
260
+ * Props for the PanelResizeHandle component
261
+ * PanelResizeHandle コンポーネントのプロパティ
262
+ */
263
+ export interface PanelResizeHandleProps {
264
+ id?: string
265
+ disabled?: boolean
266
+ onDragging?: (isDragging: boolean) => void
267
+ className?: string
268
+ style?: React.CSSProperties
269
+ children?: ReactNode
270
+ }
271
+
272
+ export interface PanelMeasurement {
273
+ pixelSize: number
274
+ percentageSize: number
275
+ }
276
+
277
+ /**
278
+ * Layout data for a resize handle (divider) within a panel group.
279
+ * パネルグループ内のリサイズハンドル(ディバイダー)に関するレイアウトデータ。
280
+ */
281
+ export interface ResizeHandleLayoutData {
282
+ id: string
283
+ thickness: number
284
+ direction: PanelDirection
285
+ visible: boolean
286
+ startPanelId?: string | null
287
+ endPanelId?: string | null
288
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * @module utils/logger
3
+ * @description Logger utility for consistent log management across the application.
4
+ * @description アプリケーション全体で一貫したログ管理を行うためのロガーユーティリティ。
5
+ */
6
+
7
+ /**
8
+ * @enum LogLevel
9
+ * @description Log levels for filtering output.
10
+ * @description 出力をフィルタリングするためのログレベル。
11
+ */
12
+ export enum LogLevel {
13
+ DEBUG = 0,
14
+ INFO = 1,
15
+ WARN = 2,
16
+ ERROR = 3,
17
+ NONE = 4,
18
+ }
19
+
20
+ /**
21
+ * @interface ILogger
22
+ * @description Interface for a logger object compatible with Console.
23
+ * @description Console と互換性のあるロガーオブジェクトのインターフェース。
24
+ */
25
+ export interface ILogger {
26
+ debug(message?: unknown, ...optionalParams: unknown[]): void
27
+ info(message?: unknown, ...optionalParams: unknown[]): void
28
+ warn(message?: unknown, ...optionalParams: unknown[]): void
29
+ error(message?: unknown, ...optionalParams: unknown[]): void
30
+ }
31
+
32
+ /**
33
+ * @class Logger
34
+ * @description A wrapper class for handling logging with levels and prefixes.
35
+ * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。
36
+ */
37
+ export class Logger implements ILogger {
38
+ private level: LogLevel
39
+ private prefix: string
40
+ private impl: ILogger
41
+
42
+ /**
43
+ * Registry of all constructed loggers so the static configuration API can reach them.
44
+ * static 設定 API が全ロガーへ到達できるようにするための生成済みインスタンスの登録簿。
45
+ */
46
+ private static readonly instances = new Set<Logger>()
47
+
48
+ /**
49
+ * @constructor
50
+ * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.
51
+ * @param {string} [prefix="[resize-panels]"] - The prefix to add to all log messages.
52
+ * @param {ILogger} [impl=console] - The implementation to use for logging.
53
+ */
54
+ constructor(level: LogLevel = LogLevel.WARN, prefix: string = "[resize-panels]", impl: ILogger = console) {
55
+ this.level = level
56
+ this.prefix = prefix
57
+ this.impl = impl
58
+ Logger.instances.add(this)
59
+ }
60
+
61
+ private static instance: Logger = new Logger(LogLevel.WARN, "[resize-panels]")
62
+
63
+ /**
64
+ * @method setLevel
65
+ * @description Updates the log level for every constructed logger (module-level loggers included).
66
+ * @description 生成済みのすべてのロガー (モジュールレベルのロガーを含む) のログレベルを更新します。
67
+ * @param {LogLevel} level - The new log level.
68
+ */
69
+ static setLevel(level: LogLevel): void {
70
+ for (const instance of Logger.instances) {
71
+ instance.setLevel(level)
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @method setLevel
77
+ * @description Updates the current log level.
78
+ * @description 現在のログレベルを更新します。
79
+ * @param {LogLevel} level - The new log level.
80
+ */
81
+ setLevel(level: LogLevel): void {
82
+ this.level = level
83
+ }
84
+
85
+ /**
86
+ * @method setImplementation
87
+ * @description Updates the logger implementation for every constructed logger.
88
+ * @description 生成済みのすべてのロガーの実装を更新します。
89
+ * @param {ILogger} impl - The new logger implementation.
90
+ */
91
+ static setImplementation(impl: ILogger): void {
92
+ for (const instance of Logger.instances) {
93
+ instance.setImplementation(impl)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @method setImplementation
99
+ * @description Updates the logger implementation.
100
+ * @description ロガーの実装を更新します。
101
+ * @param {ILogger} impl - The new logger implementation.
102
+ */
103
+ setImplementation(impl: ILogger): void {
104
+ this.impl = impl
105
+ }
106
+
107
+ /**
108
+ * @method setPrefix
109
+ * @description Updates the log prefix for every constructed logger.
110
+ * @description 生成済みのすべてのロガーのログのプレフィックスを更新します。
111
+ * @param {string} prefix - The new prefix.
112
+ */
113
+ static setPrefix(prefix: string): void {
114
+ for (const instance of Logger.instances) {
115
+ instance.setPrefix(prefix)
116
+ }
117
+ }
118
+
119
+ /**
120
+ * @method setPrefix
121
+ * @description Updates the log prefix.
122
+ * @description ログのプレフィックスを更新します。
123
+ * @param {string} prefix - The new prefix.
124
+ */
125
+ setPrefix(prefix: string): void {
126
+ this.prefix = prefix
127
+ }
128
+
129
+ /**
130
+ * @method enabledFor
131
+ * @description Returns whether messages at the given level would be emitted.
132
+ * @description 指定レベルのメッセージが出力対象かどうかを返します。
133
+ * 高コストなログ引数 (スナップショット等) の組み立てを抑制中に省略するために使用します。
134
+ * @param {LogLevel} level - The message level to test.
135
+ */
136
+ enabledFor(level: LogLevel): boolean {
137
+ return this.level <= level
138
+ }
139
+
140
+ private formatMessage(message: unknown): unknown[] {
141
+ if (typeof message === "string") {
142
+ return [`${this.prefix} ${message}`]
143
+ }
144
+ return [this.prefix, message]
145
+ }
146
+
147
+ static debug(message?: unknown, ...optionalParams: unknown[]): void {
148
+ Logger.instance.debug(message, ...optionalParams)
149
+ }
150
+
151
+ debug(message?: unknown, ...optionalParams: unknown[]): void {
152
+ if (this.level <= LogLevel.DEBUG) {
153
+ this.impl.debug(...this.formatMessage(message), ...optionalParams)
154
+ }
155
+ }
156
+
157
+ static info(message?: unknown, ...optionalParams: unknown[]): void {
158
+ Logger.instance.info(message, ...optionalParams)
159
+ }
160
+
161
+ info(message?: unknown, ...optionalParams: unknown[]): void {
162
+ if (this.level <= LogLevel.INFO) {
163
+ this.impl.info(...this.formatMessage(message), ...optionalParams)
164
+ }
165
+ }
166
+
167
+ static warn(message?: unknown, ...optionalParams: unknown[]): void {
168
+ Logger.instance.warn(message, ...optionalParams)
169
+ }
170
+
171
+ warn(message?: unknown, ...optionalParams: unknown[]): void {
172
+ if (this.level <= LogLevel.WARN) {
173
+ this.impl.warn(...this.formatMessage(message), ...optionalParams)
174
+ }
175
+ }
176
+
177
+ static error(message?: unknown, ...optionalParams: unknown[]): void {
178
+ Logger.instance.error(message, ...optionalParams)
179
+ }
180
+
181
+ error(message?: unknown, ...optionalParams: unknown[]): void {
182
+ if (this.level <= LogLevel.ERROR) {
183
+ this.impl.error(...this.formatMessage(message), ...optionalParams)
184
+ }
185
+ }
186
+ }