@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.
- package/LICENSE +21 -0
- package/README.md +21 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/styles/resize-panels.css +1 -1
- package/dist/styles/resize-panels.standalone.css +3 -0
- package/package.json +10 -7
- package/src/GlobalDebugOverlay.tsx +284 -0
- package/src/Panel.tsx +635 -0
- package/src/PanelDebugInfo.tsx +148 -0
- package/src/PanelGroup.tsx +260 -0
- package/src/PanelResizeHandle.tsx +718 -0
- package/src/context.ts +25 -0
- package/src/debugOverlayStore.ts +351 -0
- package/src/hooks.ts +968 -0
- package/src/index.ts +62 -0
- package/src/reducer.ts +1234 -0
- package/src/roundHalfToEven.ts +40 -0
- package/src/styles/components.entry.css +8 -0
- package/src/styles/resize-panels.css +3 -0
- package/src/styles/standalone.entry.css +12 -0
- package/src/types.ts +288 -0
- package/src/utils/simple-logger.ts +186 -0
- package/src/utils.ts +279 -0
- package/dist/resize-panels.css +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Debug info overlay component for panels
|
|
3
|
+
* パネル用デバッグ情報オーバーレイコンポーネント
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type CSSProperties, memo, useMemo } from "react"
|
|
7
|
+
import type { PanelDirection, PanelLayoutData } from "./types"
|
|
8
|
+
|
|
9
|
+
type PanelDebugInfoProps = {
|
|
10
|
+
panel: PanelLayoutData | undefined
|
|
11
|
+
measuredPixelSize: number
|
|
12
|
+
measuredPercentageSize: number
|
|
13
|
+
containerAxisSize: number
|
|
14
|
+
direction: PanelDirection
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type FormatPanelDebugInfoArgs = {
|
|
18
|
+
panel: PanelLayoutData | undefined
|
|
19
|
+
measuredPixelSize: number
|
|
20
|
+
measuredPercentageSize: number
|
|
21
|
+
containerAxisSize: number
|
|
22
|
+
containerLabel: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type FormattedPanelDebugInfo = {
|
|
26
|
+
stateValueDisplay: string
|
|
27
|
+
measuredValueDisplay: string
|
|
28
|
+
containerDisplay: string
|
|
29
|
+
headlineLabel: string
|
|
30
|
+
titleText: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const formatPanelDebugInfo = ({ panel, measuredPixelSize, measuredPercentageSize, containerAxisSize, containerLabel }: FormatPanelDebugInfoArgs): FormattedPanelDebugInfo => {
|
|
34
|
+
const rawStatePixelSize = panel?.size
|
|
35
|
+
const statePixelDisplay = typeof rawStatePixelSize === "number" && Number.isFinite(rawStatePixelSize) ? `${rawStatePixelSize.toFixed(3)}px` : "-"
|
|
36
|
+
const statePercentageDisplay = panel?.percentageSize !== undefined ? `${panel.percentageSize.toFixed(3)}%` : "-"
|
|
37
|
+
const measuredPixelDisplay = Number.isFinite(measuredPixelSize) ? `${measuredPixelSize.toFixed(3)}px` : "-"
|
|
38
|
+
const measuredPercentageDisplay = Number.isFinite(measuredPercentageSize) ? `${measuredPercentageSize.toFixed(3)}%` : "-"
|
|
39
|
+
const containerDisplay = Number.isFinite(containerAxisSize) ? `${containerAxisSize.toFixed(3)}px` : "-"
|
|
40
|
+
const stateValueDisplay = `${statePixelDisplay} / ${statePercentageDisplay}`
|
|
41
|
+
const measuredValueDisplay = `${measuredPixelDisplay} / ${measuredPercentageDisplay}`
|
|
42
|
+
const headlineLabel = panel?.sizeUnit === "pixels" ? "Fixed" : "Flexible"
|
|
43
|
+
const titleLines = [headlineLabel, `${containerLabel}: ${containerDisplay}`, `size (state): ${stateValueDisplay}`, `size (measured): ${measuredValueDisplay}`]
|
|
44
|
+
return {
|
|
45
|
+
stateValueDisplay,
|
|
46
|
+
measuredValueDisplay,
|
|
47
|
+
containerDisplay,
|
|
48
|
+
headlineLabel,
|
|
49
|
+
titleText: titleLines.join("\n"),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const PanelDebugInfo = memo(({ panel, measuredPixelSize, measuredPercentageSize, containerAxisSize, direction }: PanelDebugInfoProps) => {
|
|
54
|
+
const containerLabel = direction === "horizontal" ? "container width" : "container height"
|
|
55
|
+
const { stateValueDisplay, measuredValueDisplay, containerDisplay, headlineLabel, titleText } = useMemo(
|
|
56
|
+
() =>
|
|
57
|
+
formatPanelDebugInfo({
|
|
58
|
+
panel,
|
|
59
|
+
measuredPixelSize,
|
|
60
|
+
measuredPercentageSize,
|
|
61
|
+
containerAxisSize,
|
|
62
|
+
containerLabel,
|
|
63
|
+
}),
|
|
64
|
+
[panel, measuredPixelSize, measuredPercentageSize, containerAxisSize, containerLabel],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const containerStyle: CSSProperties = {
|
|
68
|
+
position: "absolute",
|
|
69
|
+
top: "2px",
|
|
70
|
+
right: "4px",
|
|
71
|
+
backgroundColor: "rgba(30, 41, 59, 0.6)",
|
|
72
|
+
color: "#fff",
|
|
73
|
+
padding: "4px 8px",
|
|
74
|
+
borderRadius: "6px",
|
|
75
|
+
fontSize: "11px",
|
|
76
|
+
fontFamily: "monospace",
|
|
77
|
+
pointerEvents: "auto",
|
|
78
|
+
zIndex: 10,
|
|
79
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.3)",
|
|
80
|
+
border: "1px solid rgba(255,255,255,0.1)",
|
|
81
|
+
maxWidth: "calc(100% - 12px)",
|
|
82
|
+
minWidth: "60px",
|
|
83
|
+
width: "fit-content",
|
|
84
|
+
overflow: "hidden",
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const wrapperStyle: CSSProperties = {
|
|
88
|
+
display: "flex",
|
|
89
|
+
flexDirection: "column",
|
|
90
|
+
gap: "1px",
|
|
91
|
+
minWidth: "0",
|
|
92
|
+
width: "100%",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const headlineStyle: CSSProperties = {
|
|
96
|
+
display: "flex",
|
|
97
|
+
justifyContent: "flex-start",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
gap: "6px",
|
|
100
|
+
fontSize: "10px",
|
|
101
|
+
color: "#ccc",
|
|
102
|
+
whiteSpace: "nowrap",
|
|
103
|
+
overflow: "hidden",
|
|
104
|
+
textOverflow: "ellipsis",
|
|
105
|
+
minWidth: "0",
|
|
106
|
+
width: "100%",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const debugLineStyle: CSSProperties = {
|
|
110
|
+
display: "flex",
|
|
111
|
+
justifyContent: "space-between",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
gap: "6px",
|
|
114
|
+
whiteSpace: "nowrap",
|
|
115
|
+
overflow: "hidden",
|
|
116
|
+
textOverflow: "ellipsis",
|
|
117
|
+
minWidth: "0",
|
|
118
|
+
width: "100%",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const debugValueStyle: CSSProperties = {
|
|
122
|
+
marginLeft: "auto",
|
|
123
|
+
fontVariantNumeric: "tabular-nums",
|
|
124
|
+
whiteSpace: "nowrap",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div style={containerStyle} title={titleText}>
|
|
129
|
+
<div style={wrapperStyle}>
|
|
130
|
+
<div style={headlineStyle}>
|
|
131
|
+
<span>{headlineLabel}</span>
|
|
132
|
+
</div>
|
|
133
|
+
<div style={debugLineStyle}>
|
|
134
|
+
<span>{containerLabel}</span>
|
|
135
|
+
<span style={debugValueStyle}>{containerDisplay}</span>
|
|
136
|
+
</div>
|
|
137
|
+
<div style={debugLineStyle}>
|
|
138
|
+
<span>size (state)</span>
|
|
139
|
+
<span style={debugValueStyle}>{stateValueDisplay}</span>
|
|
140
|
+
</div>
|
|
141
|
+
<div style={debugLineStyle}>
|
|
142
|
+
<span>size (measured)</span>
|
|
143
|
+
<span style={debugValueStyle}>{measuredValueDisplay}</span>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
})
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file PanelGroup component for custom resizable panels
|
|
3
|
+
* カスタムリサイズ可能パネルのための PanelGroup コンポーネント
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type CSSProperties, lazy, Suspense, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
|
|
7
|
+
import { twMerge } from "tailwind-merge"
|
|
8
|
+
import { PanelGroupContext } from "./context"
|
|
9
|
+
import { useResizablePanels } from "./hooks"
|
|
10
|
+
import type { PanelGroupProps } from "./types"
|
|
11
|
+
import { getContainerSize } from "./utils"
|
|
12
|
+
|
|
13
|
+
// デバッグオーバーレイ (UI とストア) は dynamic import で遅延読み込みする。
|
|
14
|
+
// showDebugInfo を使わないアプリのバンドルへオーバーレイ一式を含めず、ストア購読も発生させないための分離
|
|
15
|
+
const LazyDebugOverlayGate = lazy(() => import("./GlobalDebugOverlay").then((module) => ({ default: module.GlobalDebugOverlayGate })))
|
|
16
|
+
|
|
17
|
+
type DebugOverlayStoreModule = typeof import("./debugOverlayStore")
|
|
18
|
+
|
|
19
|
+
let debugGroupIdCounter = 0
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate unique debug group identifier for panel groups without explicit IDs.
|
|
23
|
+
* 明示的な ID を持たないパネルグループ用の一意なデバッググループ識別子を生成。
|
|
24
|
+
*/
|
|
25
|
+
const generateDebugGroupId = () => {
|
|
26
|
+
debugGroupIdCounter += 1
|
|
27
|
+
return `panel-group-${debugGroupIdCounter}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ResizablePanelsContextValue = ReturnType<typeof useResizablePanels>["contextValue"]
|
|
31
|
+
type LayoutConstraintViolation = NonNullable<ResizablePanelsContextValue["layoutConstraintViolation"]>
|
|
32
|
+
type DebugPanel = ResizablePanelsContextValue["panels"][number]
|
|
33
|
+
type DebugPanelMeasurementMap = ResizablePanelsContextValue["panelMeasurements"]
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Builds summary and detail labels for layout constraint placeholders.
|
|
37
|
+
* レイアウト制約プレースホルダー用のサマリーと詳細ラベルを生成。
|
|
38
|
+
*/
|
|
39
|
+
const buildConstraintCopy = (violation: LayoutConstraintViolation) => {
|
|
40
|
+
const isMinimum = violation.reason === "minimum-exceeded"
|
|
41
|
+
const summary = isMinimum ? "Layout unavailable: minimum panel sizes exceed the container." : "Layout unavailable: panels cannot fill the container with their maximum sizes."
|
|
42
|
+
const detail = isMinimum
|
|
43
|
+
? `Min required: ${Math.ceil(violation.totalMinimumSize ?? 0)}px | Available: ${Math.floor(violation.availableContainerSize)}px`
|
|
44
|
+
: `Max total: ${Math.floor(violation.totalMaximumSize ?? 0)}px | Needed: ${Math.floor(violation.availableContainerSize)}px`
|
|
45
|
+
return { summary, detail }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Synchronizes measured panel dimensions into overlay panel descriptors.
|
|
50
|
+
* 測定済みパネル寸法をオーバーレイ用パネル情報へ同期。
|
|
51
|
+
*/
|
|
52
|
+
const mergePanelMeasurements = (panels: ReadonlyArray<DebugPanel>, measurementMap: DebugPanelMeasurementMap): DebugPanel[] => {
|
|
53
|
+
let changed = false
|
|
54
|
+
const nextPanels = panels.map((panel) => {
|
|
55
|
+
const measurement = measurementMap[panel.id]
|
|
56
|
+
if (!measurement) {
|
|
57
|
+
return panel
|
|
58
|
+
}
|
|
59
|
+
if (panel.measuredPixelSize === measurement.pixelSize && panel.measuredPercentageSize === measurement.percentageSize) {
|
|
60
|
+
return panel
|
|
61
|
+
}
|
|
62
|
+
changed = true
|
|
63
|
+
return {
|
|
64
|
+
...panel,
|
|
65
|
+
measuredPixelSize: measurement.pixelSize,
|
|
66
|
+
measuredPercentageSize: measurement.percentageSize,
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
return changed ? nextPanels : (panels as DebugPanel[])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Measures overlay container dimensions with graceful fallbacks.
|
|
74
|
+
* フォールバックを考慮しつつオーバーレイ用コンテナ寸法を計測。
|
|
75
|
+
*/
|
|
76
|
+
const measureDebugOverlayContainerSize = (element: HTMLElement, fallbackWidth?: number, fallbackHeight?: number) => {
|
|
77
|
+
const horizontal = getContainerSize(element, "horizontal")
|
|
78
|
+
const vertical = getContainerSize(element, "vertical")
|
|
79
|
+
const rect = element.getBoundingClientRect()
|
|
80
|
+
const width = horizontal.inner > 0 ? horizontal.inner : (fallbackWidth ?? rect.width)
|
|
81
|
+
const height = vertical.inner > 0 ? vertical.inner : (fallbackHeight ?? rect.height)
|
|
82
|
+
return { width, height }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Provide layout context and container for coordinated panels.
|
|
87
|
+
* 連動するパネルのためのレイアウトコンテキストとコンテナを提供するコンポーネント。
|
|
88
|
+
*/
|
|
89
|
+
export const PanelGroup = (props: PanelGroupProps) => {
|
|
90
|
+
const { id, className, style, children, direction } = props
|
|
91
|
+
const { groupRef, contextValue, groupStyle } = useResizablePanels(props)
|
|
92
|
+
|
|
93
|
+
const [debugOverlayContainerSize, setDebugOverlayContainerSize] = useState({ width: 0, height: 0 })
|
|
94
|
+
|
|
95
|
+
const constraintViolation = contextValue.layoutConstraintViolation
|
|
96
|
+
const shouldRenderPlaceholder = Boolean(constraintViolation)
|
|
97
|
+
const placeholderInner = useMemo(() => {
|
|
98
|
+
if (!constraintViolation) {
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
const { summary, detail } = buildConstraintCopy(constraintViolation)
|
|
102
|
+
return (
|
|
103
|
+
<div
|
|
104
|
+
className="flex w-full flex-1 flex-col items-center justify-center gap-2 rounded-lg border border-slate-300 border-dashed bg-slate-100/70 px-6 py-8 text-center text-slate-600 text-sm dark:border-slate-600 dark:bg-slate-800/50 dark:text-slate-200"
|
|
105
|
+
data-panel-group-placeholder="constraint-violation">
|
|
106
|
+
<span>{summary}</span>
|
|
107
|
+
<span className="text-slate-500 text-xs dark:text-slate-400">{detail}</span>
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
}, [constraintViolation])
|
|
111
|
+
|
|
112
|
+
const debugGroupIdRef = useRef(id ?? generateDebugGroupId())
|
|
113
|
+
const debugGroupId = debugGroupIdRef.current
|
|
114
|
+
const debugDisplayName = id ?? debugGroupId
|
|
115
|
+
|
|
116
|
+
const shouldShowDebugOverlay = contextValue.showDebugInfo && contextValue.panels.length > 0
|
|
117
|
+
const debugOverlayPanels = shouldShowDebugOverlay ? contextValue.panels : []
|
|
118
|
+
const debugOverlayMeasurementMap = shouldShowDebugOverlay ? contextValue.panelMeasurements : {}
|
|
119
|
+
const debugOverlayHandles = useMemo(() => {
|
|
120
|
+
if (!shouldShowDebugOverlay) {
|
|
121
|
+
return []
|
|
122
|
+
}
|
|
123
|
+
const handles = Object.values(contextValue.handleMeasurements)
|
|
124
|
+
if (handles.length <= 1) {
|
|
125
|
+
return handles
|
|
126
|
+
}
|
|
127
|
+
return handles.slice().sort((a, b) => a.id.localeCompare(b.id))
|
|
128
|
+
}, [contextValue.handleMeasurements, shouldShowDebugOverlay])
|
|
129
|
+
const containerSize = contextValue.containerSize
|
|
130
|
+
const debugOverlayContainerWidth = debugOverlayContainerSize.width > 0 ? debugOverlayContainerSize.width : containerSize.width
|
|
131
|
+
const debugOverlayContainerHeight = debugOverlayContainerSize.height > 0 ? debugOverlayContainerSize.height : containerSize.height
|
|
132
|
+
|
|
133
|
+
useLayoutEffect(() => {
|
|
134
|
+
// shouldShowDebugOverlay と groupRef に応じてデバッグオーバーレイ用コンテナ寸法を監視
|
|
135
|
+
if (!shouldShowDebugOverlay) {
|
|
136
|
+
setDebugOverlayContainerSize({ width: 0, height: 0 })
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
if (typeof window === "undefined" || typeof ResizeObserver === "undefined") {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
const element = groupRef.current
|
|
143
|
+
if (!element) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
const updateSize = (nextSize: { width: number; height: number }) => {
|
|
147
|
+
setDebugOverlayContainerSize((previous) => (previous.width === nextSize.width && previous.height === nextSize.height ? previous : nextSize))
|
|
148
|
+
}
|
|
149
|
+
const observer = new ResizeObserver((entries) => {
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (entry.target !== element) {
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
const nextSize = measureDebugOverlayContainerSize(element, entry.contentRect.width, entry.contentRect.height)
|
|
155
|
+
updateSize(nextSize)
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
observer.observe(element)
|
|
159
|
+
updateSize(measureDebugOverlayContainerSize(element))
|
|
160
|
+
return () => {
|
|
161
|
+
observer.disconnect()
|
|
162
|
+
}
|
|
163
|
+
}, [groupRef, shouldShowDebugOverlay])
|
|
164
|
+
|
|
165
|
+
const debugOverlayPanelsWithMeasurements = useMemo(() => mergePanelMeasurements(debugOverlayPanels, debugOverlayMeasurementMap), [debugOverlayPanels, debugOverlayMeasurementMap])
|
|
166
|
+
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
// shouldShowDebugOverlay と debugGroupId に応じてデバッグオーバーレイ有効状態を同期
|
|
169
|
+
// ストアは遅延読み込みのため、有効化されるまで import しない (未読込なら解除処理も不要)
|
|
170
|
+
if (!shouldShowDebugOverlay) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let cancelled = false
|
|
175
|
+
let loadedStore: DebugOverlayStoreModule | null = null
|
|
176
|
+
import("./debugOverlayStore").then((storeModule) => {
|
|
177
|
+
if (cancelled) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
loadedStore = storeModule
|
|
181
|
+
storeModule.enableDebugOverlayGroup(debugGroupId)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
return () => {
|
|
185
|
+
cancelled = true
|
|
186
|
+
if (loadedStore) {
|
|
187
|
+
loadedStore.disableDebugOverlayGroup(debugGroupId)
|
|
188
|
+
loadedStore.removeDebugOverlayGroup(debugGroupId)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}, [shouldShowDebugOverlay, debugGroupId])
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
// 依存配列の変化ごとに最新のオーバーレイ集計をストアへ送出
|
|
195
|
+
if (!shouldShowDebugOverlay) {
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let cancelled = false
|
|
200
|
+
import("./debugOverlayStore").then((storeModule) => {
|
|
201
|
+
if (cancelled) {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
storeModule.updateDebugOverlayGroup(debugGroupId, {
|
|
205
|
+
displayName: debugDisplayName,
|
|
206
|
+
direction,
|
|
207
|
+
containerSize: {
|
|
208
|
+
width: debugOverlayContainerWidth,
|
|
209
|
+
height: debugOverlayContainerHeight,
|
|
210
|
+
},
|
|
211
|
+
panels: debugOverlayPanelsWithMeasurements,
|
|
212
|
+
handles: debugOverlayHandles,
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
return () => {
|
|
216
|
+
cancelled = true
|
|
217
|
+
}
|
|
218
|
+
}, [shouldShowDebugOverlay, debugGroupId, debugDisplayName, direction, debugOverlayContainerWidth, debugOverlayContainerHeight, debugOverlayPanelsWithMeasurements, debugOverlayHandles])
|
|
219
|
+
|
|
220
|
+
const mergedStyle = useMemo(() => {
|
|
221
|
+
const baseStyle: CSSProperties = {
|
|
222
|
+
...groupStyle,
|
|
223
|
+
...style,
|
|
224
|
+
}
|
|
225
|
+
if (!baseStyle.position) {
|
|
226
|
+
baseStyle.position = "relative"
|
|
227
|
+
}
|
|
228
|
+
return baseStyle
|
|
229
|
+
}, [groupStyle, style])
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<PanelGroupContext.Provider value={contextValue}>
|
|
233
|
+
<div ref={groupRef} className={twMerge("panel-group", direction === "horizontal" ? "flex-row" : "flex-col", className)} style={mergedStyle} data-panel-group-id={id} data-panel-group-direction={direction}>
|
|
234
|
+
{children}
|
|
235
|
+
{shouldRenderPlaceholder && placeholderInner ? (
|
|
236
|
+
<div
|
|
237
|
+
className="pointer-events-auto"
|
|
238
|
+
style={{
|
|
239
|
+
position: "absolute",
|
|
240
|
+
inset: 0,
|
|
241
|
+
zIndex: 40,
|
|
242
|
+
display: "flex",
|
|
243
|
+
flexDirection: "column",
|
|
244
|
+
alignItems: "center",
|
|
245
|
+
justifyContent: "center",
|
|
246
|
+
backgroundColor: "rgba(248, 250, 252, 0.82)",
|
|
247
|
+
backdropFilter: "blur(2px)",
|
|
248
|
+
}}>
|
|
249
|
+
{placeholderInner}
|
|
250
|
+
</div>
|
|
251
|
+
) : null}
|
|
252
|
+
</div>
|
|
253
|
+
{shouldShowDebugOverlay && (
|
|
254
|
+
<Suspense fallback={null}>
|
|
255
|
+
<LazyDebugOverlayGate groupId={debugGroupId} />
|
|
256
|
+
</Suspense>
|
|
257
|
+
)}
|
|
258
|
+
</PanelGroupContext.Provider>
|
|
259
|
+
)
|
|
260
|
+
}
|