@aiquants/drag-drop-panels 0.7.2 → 0.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 +32 -10
- package/dist/drag-drop/hooks/useCustomDragHandlers.d.ts.map +1 -1
- package/dist/index.es.js +88 -88
- package/dist/index.umd.js +2 -2
- package/dist/styles/drag-drop-panels.standalone.css +3 -0
- package/package.json +11 -4
- package/src/DragDropLayout.tsx +751 -0
- package/src/drag-drop/ColumnControlPanel.tsx +123 -0
- package/src/drag-drop/ColumnInsertPanel.tsx +106 -0
- package/src/drag-drop/DebugComponents.tsx +222 -0
- package/src/drag-drop/DebugControlPanel.tsx +200 -0
- package/src/drag-drop/DebugOverlay.tsx +250 -0
- package/src/drag-drop/DragDropLayout.css +130 -0
- package/src/drag-drop/ResizeHandle.tsx +208 -0
- package/src/drag-drop/ResponsiveControl.tsx +33 -0
- package/src/drag-drop/hooks/dragGhostScale.spec.tsx +153 -0
- package/src/drag-drop/hooks/useColumnLayout.ts +457 -0
- package/src/drag-drop/hooks/useCustomDragHandlers.tsx +333 -0
- package/src/drag-drop/hooks/useDragDropColumns.tsx +446 -0
- package/src/drag-drop/hooks/useDragDropState.tsx +200 -0
- package/src/drag-drop/hooks/useFitScale.ts +48 -0
- package/src/drag-drop/hooks/useMouseTracking.tsx +102 -0
- package/src/drag-drop/hooks/useNormalDragHandlers.tsx +427 -0
- package/src/drag-drop/hooks/usePanelManagement.tsx +238 -0
- package/src/drag-drop/hooks/useResponsiveColumns.ts +210 -0
- package/src/drag-drop/resize-handle.css +71 -0
- package/src/drag-drop/types.ts +248 -0
- package/src/drag-drop/utils/columnIdUtils.ts +104 -0
- package/src/drag-drop/utils/edgeScrollUtils.ts +124 -0
- package/src/drag-drop/utils/simple-logger.ts +162 -0
- package/src/index.ts +60 -0
- package/src/styles/standalone.entry.css +27 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the ColumnControlPanel component.
|
|
3
|
+
* This component provides UI controls for managing the number of columns,
|
|
4
|
+
* auto-balancing panels, and resetting column widths in a drag-and-drop layout.
|
|
5
|
+
*
|
|
6
|
+
* ColumnControlPanel コンポーネントを定義します。
|
|
7
|
+
* このコンポーネントは、ドラッグ&ドロップレイアウトにおいて、カラム数の管理、
|
|
8
|
+
* パネルの自動バランス調整、およびカラム幅のリセットを行うためのUIコントロールを提供します。
|
|
9
|
+
*/
|
|
10
|
+
import { Grid3X3, Minus, Plus, RotateCcw } from "lucide-react"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the ColumnControlPanel component.
|
|
14
|
+
*
|
|
15
|
+
* ColumnControlPanel コンポーネントのプロパティ。
|
|
16
|
+
*/
|
|
17
|
+
export type ColumnControlPanelProps = {
|
|
18
|
+
/** The current number of columns. / 現在のカラム数。 */
|
|
19
|
+
columnCount: number
|
|
20
|
+
/** Callback function when the column count changes. / カラム数が変更されたときのコールバック関数。 */
|
|
21
|
+
onColumnCountChange: (count: number) => void
|
|
22
|
+
/** Callback function to auto-balance panels across columns. / パネルをカラム間で自動的にバランス調整するためのコールバック関数。 */
|
|
23
|
+
onAutoBalance: () => void
|
|
24
|
+
/** Callback function to reset column widths to their initial state. / カラム幅を初期状態にリセットするためのコールバック関数。 */
|
|
25
|
+
onResetWidths: () => void
|
|
26
|
+
/** The minimum number of columns allowed. Defaults to 2. / 許可される最小カラム数。デフォルトは2。 */
|
|
27
|
+
minColumns?: number
|
|
28
|
+
/** The maximum number of columns allowed. Defaults to 6. / 許可される最大カラム数。デフォルトは6。 */
|
|
29
|
+
maxColumns?: number
|
|
30
|
+
/** A flag indicating if a resize operation is in progress. / リサイズ操作が進行中かどうかを示すフラグ。 */
|
|
31
|
+
isResizing?: boolean
|
|
32
|
+
/** A flag indicating if responsive mode is active (restricts manual column adjustments). / レスポンシブモードが有効かどうか(手動カラム調整を制限)。 */
|
|
33
|
+
isResponsiveMode?: boolean
|
|
34
|
+
/** The responsive column count when responsive mode is active. / レスポンシブモード有効時のレスポンシブカラム数。 */
|
|
35
|
+
responsiveColumnCount?: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A UI component that provides controls for managing layout columns.
|
|
40
|
+
* It allows users to add or remove columns, balance panels, and reset widths.
|
|
41
|
+
*
|
|
42
|
+
* レイアウトカラムを管理するためのコントロールを提供するUIコンポーネント。
|
|
43
|
+
* ユーザーはカラムの追加・削除、パネルのバランス調整、幅のリセットができます。
|
|
44
|
+
* @param {ColumnControlPanelProps} props - The props for the component.
|
|
45
|
+
* @returns {JSX.Element} The rendered column control panel.
|
|
46
|
+
*/
|
|
47
|
+
export const ColumnControlPanel = ({ columnCount, onColumnCountChange, onAutoBalance, onResetWidths, minColumns = 2, maxColumns = 6, isResizing = false, isResponsiveMode = false, responsiveColumnCount }: ColumnControlPanelProps) => {
|
|
48
|
+
// カラムを減らせるかどうかのフラグ(レスポンシブモード時は無効)
|
|
49
|
+
const canDecreaseColumns = columnCount > minColumns && !isResponsiveMode
|
|
50
|
+
// カラムを増やせるかどうかのフラグ(レスポンシブモード時は無効)
|
|
51
|
+
const canIncreaseColumns = columnCount < maxColumns && !isResponsiveMode
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="flex items-center gap-3 rounded-lg border border-slate-200 bg-white/80 px-4 py-2 shadow-sm backdrop-blur-lg">
|
|
55
|
+
{/* カラム数制御 */}
|
|
56
|
+
<div className="flex items-center gap-2">
|
|
57
|
+
<span className="font-medium text-slate-700 text-sm">カラム数:</span>
|
|
58
|
+
|
|
59
|
+
{/* カラム減少ボタン */}
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
onClick={() => canDecreaseColumns && onColumnCountChange(columnCount - 1)}
|
|
63
|
+
disabled={!canDecreaseColumns || isResizing}
|
|
64
|
+
className={`rounded p-1 text-xs transition-colors ${canDecreaseColumns && !isResizing ? "border border-slate-300 text-slate-600 hover:border-red-300 hover:bg-red-50 hover:text-red-600" : "cursor-not-allowed border border-slate-200 bg-slate-50 text-slate-300"}`}
|
|
65
|
+
title={isResponsiveMode ? "レスポンシブモード中:カラム数は自動調整されます" : `カラムを削除 (最小: ${minColumns}列)`}>
|
|
66
|
+
<Minus className="h-3 w-3" />
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
{/* 現在のカラム数表示 */}
|
|
70
|
+
<div className={`min-w-[2rem] rounded border px-3 py-1 text-center font-mono text-sm ${isResponsiveMode ? "border-blue-300 bg-blue-50 text-blue-700" : "border-slate-300 bg-slate-100 text-slate-700"}`}>
|
|
71
|
+
{isResponsiveMode ? (responsiveColumnCount ?? columnCount) : columnCount}
|
|
72
|
+
{isResponsiveMode && <span className="ml-1 text-xs">自動</span>}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{/* カラム増加ボタン */}
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
onClick={() => canIncreaseColumns && onColumnCountChange(columnCount + 1)}
|
|
79
|
+
disabled={!canIncreaseColumns || isResizing}
|
|
80
|
+
className={`rounded p-1 text-xs transition-colors ${canIncreaseColumns && !isResizing ? "border border-slate-300 text-slate-600 hover:border-blue-300 hover:bg-blue-50 hover:text-blue-600" : "cursor-not-allowed border border-slate-200 bg-slate-50 text-slate-300"}`}
|
|
81
|
+
title={isResponsiveMode ? "レスポンシブモード中:カラム数は自動調整されます" : `カラムを追加 (最大: ${maxColumns}列)`}>
|
|
82
|
+
<Plus className="h-3 w-3" />
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{/* 区切り線 */}
|
|
87
|
+
<div className="h-6 w-px bg-slate-300" />
|
|
88
|
+
|
|
89
|
+
{/* レイアウト制御ボタン */}
|
|
90
|
+
<div className="flex items-center gap-2">
|
|
91
|
+
{/* 自動バランス調整 */}
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
onClick={onAutoBalance}
|
|
95
|
+
disabled={isResizing}
|
|
96
|
+
className={`flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors ${!isResizing ? "border border-slate-300 text-slate-600 hover:border-green-300 hover:bg-green-50 hover:text-green-600" : "cursor-not-allowed border border-slate-200 bg-slate-50 text-slate-300"}`}
|
|
97
|
+
title="パネルを全カラムに均等分散">
|
|
98
|
+
<Grid3X3 className="h-3 w-3" />
|
|
99
|
+
<span>バランス</span>
|
|
100
|
+
</button>
|
|
101
|
+
|
|
102
|
+
{/* 幅リセット */}
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onClick={onResetWidths}
|
|
106
|
+
disabled={isResizing}
|
|
107
|
+
className={`flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors ${!isResizing ? "border border-slate-300 text-slate-600 hover:border-orange-300 hover:bg-orange-50 hover:text-orange-600" : "cursor-not-allowed border border-slate-200 bg-slate-50 text-slate-300"}`}
|
|
108
|
+
title="カラム幅を初期値にリセット">
|
|
109
|
+
<RotateCcw className="h-3 w-3" />
|
|
110
|
+
<span>幅リセット</span>
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{/* リサイズ中インジケーター */}
|
|
115
|
+
{isResizing && (
|
|
116
|
+
<div className="flex items-center gap-1 text-blue-600 text-xs">
|
|
117
|
+
<div className="h-2 w-2 animate-pulse rounded-full bg-blue-500" />
|
|
118
|
+
<span>リサイズ中...</span>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the ColumnInsertPanel component.
|
|
3
|
+
* This component provides UI controls for inserting and deleting columns at specific positions
|
|
4
|
+
* in a drag-and-drop layout.
|
|
5
|
+
*
|
|
6
|
+
* ColumnInsertPanel コンポーネントを定義します。
|
|
7
|
+
* このコンポーネントは、ドラッグ&ドロップレイアウトにおいて、
|
|
8
|
+
* 特定の位置にカラムを挿入・削除するためのUIコントロールを提供します。
|
|
9
|
+
*/
|
|
10
|
+
import { Plus, X } from "lucide-react"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the ColumnInsertPanel component。
|
|
14
|
+
*
|
|
15
|
+
* ColumnInsertPanel コンポーネントのプロパティ。
|
|
16
|
+
*/
|
|
17
|
+
export type ColumnInsertPanelProps = {
|
|
18
|
+
/** The current number of columns. / 現在のカラム数。 */
|
|
19
|
+
columnCount: number
|
|
20
|
+
/** Callback function to insert a column at a specific position. / 特定の位置にカラムを挿入するためのコールバック関数。 */
|
|
21
|
+
onInsertColumn: (position: number) => void
|
|
22
|
+
/** Callback function to delete a column at a specific position. / 特定の位置のカラムを削除するためのコールバック関数。 */
|
|
23
|
+
onDeleteColumn: (position: number) => void
|
|
24
|
+
/** The minimum number of columns allowed. Defaults to 1. / 許可される最小カラム数。デフォルトは1。 */
|
|
25
|
+
minColumns?: number
|
|
26
|
+
/** The maximum number of columns allowed. Defaults to 6. / 許可される最大カラム数。デフォルトは6。 */
|
|
27
|
+
maxColumns?: number
|
|
28
|
+
/** A flag indicating if a resize operation is in progress. / リサイズ操作が進行中かどうかを示すフラグ。 */
|
|
29
|
+
isResizing?: boolean
|
|
30
|
+
/** A flag to disable the entire panel (e.g., during responsive mode). / パネル全体を無効化するフラグ(レスポンシブモード時など)。 */
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A UI component that provides controls for inserting and deleting columns at specific positions.
|
|
36
|
+
* It shows visual indicators between columns where new columns can be inserted,
|
|
37
|
+
* and delete buttons on each column.
|
|
38
|
+
*
|
|
39
|
+
* 特定の位置でカラムの挿入・削除を行うためのコントロールを提供するUIコンポーネント。
|
|
40
|
+
* 新しいカラムを挿入できる位置にビジュアルインジケーターを表示し、
|
|
41
|
+
* 各カラムに削除ボタンを表示します。
|
|
42
|
+
* @param {ColumnInsertPanelProps} props - The props for the component.
|
|
43
|
+
* @returns {JSX.Element} The rendered column insert panel。
|
|
44
|
+
*/
|
|
45
|
+
export const ColumnInsertPanel = ({ columnCount, onInsertColumn, onDeleteColumn, minColumns = 1, maxColumns = 6, isResizing = false, disabled = false }: ColumnInsertPanelProps) => {
|
|
46
|
+
// カラムを増やせるかどうかのフラグ
|
|
47
|
+
const canInsertColumns = columnCount < maxColumns && !disabled
|
|
48
|
+
// カラムを減らせるかどうかのフラグ
|
|
49
|
+
const canDeleteColumns = columnCount > minColumns && !disabled
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className={`flex items-center justify-center gap-1 rounded-lg border border-slate-200 px-4 py-2 shadow-sm backdrop-blur-lg ${disabled ? "bg-slate-100/80 opacity-60" : "bg-white/80"}`}>
|
|
53
|
+
{disabled && (
|
|
54
|
+
<div className="flex items-center gap-2 text-slate-500 text-xs">
|
|
55
|
+
<span>レスポンシブモード有効中:カラム数は自動調整されます</span>
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
{!disabled && (
|
|
60
|
+
<>
|
|
61
|
+
{/* 最初の位置に挿入ボタン */}
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
onClick={() => canInsertColumns && onInsertColumn(0)}
|
|
65
|
+
disabled={!canInsertColumns || isResizing}
|
|
66
|
+
className={`flex h-8 w-8 items-center justify-center rounded border-2 border-dashed text-xs transition-colors ${
|
|
67
|
+
canInsertColumns && !isResizing ? "border-blue-300 text-blue-600 hover:border-blue-500 hover:bg-blue-50" : "cursor-not-allowed border-slate-200 text-slate-300"
|
|
68
|
+
}`}
|
|
69
|
+
title="この位置に新しいカラムを挿入">
|
|
70
|
+
<Plus className="h-4 w-4" />
|
|
71
|
+
</button>
|
|
72
|
+
|
|
73
|
+
{Array.from({ length: columnCount }, (_, index) => (
|
|
74
|
+
<div key={`column-${index}`} className="flex items-center gap-1">
|
|
75
|
+
{/* カラム表示と削除ボタン */}
|
|
76
|
+
<div className="relative flex h-8 w-12 items-center justify-center rounded border border-slate-300 bg-slate-100">
|
|
77
|
+
<span className="font-medium font-mono text-slate-700 text-xs">{index + 1}</span>
|
|
78
|
+
{canDeleteColumns && !isResizing && (
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={() => onDeleteColumn(index)}
|
|
82
|
+
className="absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full border border-red-300 bg-red-100 text-red-600 hover:bg-red-200"
|
|
83
|
+
title={`カラム ${index + 1} を削除`}>
|
|
84
|
+
<X className="h-2.5 w-2.5" />
|
|
85
|
+
</button>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{/* カラムの後に挿入ボタン */}
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={() => canInsertColumns && onInsertColumn(index + 1)}
|
|
93
|
+
disabled={!canInsertColumns || isResizing}
|
|
94
|
+
className={`flex h-8 w-8 items-center justify-center rounded border-2 border-dashed text-xs transition-colors ${
|
|
95
|
+
canInsertColumns && !isResizing ? "border-blue-300 text-blue-600 hover:border-blue-500 hover:bg-blue-50" : "cursor-not-allowed border-slate-200 text-slate-300"
|
|
96
|
+
}`}
|
|
97
|
+
title="この位置に新しいカラムを挿入">
|
|
98
|
+
<Plus className="h-4 w-4" />
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
))}
|
|
102
|
+
</>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines debug components and helper functions for the drag-and-drop layout.
|
|
3
|
+
* These components provide visual feedback and logging for development and troubleshooting purposes.
|
|
4
|
+
*
|
|
5
|
+
* ドラッグ&ドロップレイアウト用のデバッグコンポーネントとヘルパー関数を定義します。
|
|
6
|
+
* これらのコンポーネントは、開発およびトラブルシューティング目的で、視覚的なフィードバックとロギングを提供します。
|
|
7
|
+
*/
|
|
8
|
+
import type { CSSProperties, MouseEvent } from "react"
|
|
9
|
+
import type { CustomDragState, DebugConfig, DragOverPosition, DragState, MousePosition, OriginalPanelPosition, TouchDragState } from "./types"
|
|
10
|
+
import { Logger } from "./utils/simple-logger"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the DebugPanel component.
|
|
14
|
+
*
|
|
15
|
+
* DebugPanel コンポーネントのプロパティ。
|
|
16
|
+
*/
|
|
17
|
+
interface DebugPanelProps {
|
|
18
|
+
/** Debug configuration object. / デバッグ設定オブジェクト。 */
|
|
19
|
+
config: DebugConfig
|
|
20
|
+
/** The current state of the drag operation. / ドラッグ操作の現在の状態。 */
|
|
21
|
+
dragState: DragState
|
|
22
|
+
/** The position where the dragged panel is hovering over. / ドラッグ中のパネルがホバーしている位置。 */
|
|
23
|
+
dragOverPosition: DragOverPosition | null
|
|
24
|
+
/** The original position of the panel being dragged. / ドラッグされているパネルの元の位置。 */
|
|
25
|
+
originalPanelPosition: OriginalPanelPosition | null
|
|
26
|
+
/** The current mouse coordinates. / 現在のマウス座標。 */
|
|
27
|
+
mousePosition: MousePosition
|
|
28
|
+
/** The state related to touch-based dragging. / タッチベースのドラッグに関連する状態。 */
|
|
29
|
+
touchDragState: TouchDragState
|
|
30
|
+
/** The state for custom drag handling. / カスタムドラッグ処理の状態。 */
|
|
31
|
+
customDrag: CustomDragState
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A panel that displays various debug information related to the drag-and-drop state.
|
|
36
|
+
*
|
|
37
|
+
* ドラッグ&ドロップの状態に関連する様々なデバッグ情報を表示するパネル。
|
|
38
|
+
* @param {DebugPanelProps} props - The props for the component.
|
|
39
|
+
* @returns {JSX.Element | null} The rendered debug panel or null if not enabled.
|
|
40
|
+
*/
|
|
41
|
+
export const DebugPanel = ({ config, dragState, dragOverPosition, originalPanelPosition, mousePosition, touchDragState, customDrag }: DebugPanelProps) => {
|
|
42
|
+
// デバッグパネルが表示設定でない場合は何もレンダリングしない
|
|
43
|
+
if (!config.showDebugPanel) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="fixed top-4 right-4 z-40 max-w-xs rounded-lg bg-black/80 px-3 py-2 font-mono text-white text-xs leading-tight shadow-xl">
|
|
49
|
+
<div className="mb-2 font-bold text-sm text-yellow-300">🐛 Debug Panel</div>
|
|
50
|
+
|
|
51
|
+
<div className="space-y-1">
|
|
52
|
+
{/* ドラッグ状態 */}
|
|
53
|
+
<div className="text-blue-300">Drag State:</div>
|
|
54
|
+
<div className="pl-2">
|
|
55
|
+
<div>isDragging: {dragState.isDragging ? "✅" : "❌"}</div>
|
|
56
|
+
<div>draggedPanel: {dragState.draggedPanel || "none"}</div>
|
|
57
|
+
<div>dragOverColumn: {dragState.dragOverColumn ?? "null"}</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{/* 挿入位置 */}
|
|
61
|
+
<div className="mt-2 text-green-300">Insert Position:</div>
|
|
62
|
+
<div className="pl-2">
|
|
63
|
+
<div>column: {dragOverPosition?.columnIndex ?? "null"}</div>
|
|
64
|
+
<div>index: {dragOverPosition?.insertIndex ?? "null"}</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{/* 元の位置 */}
|
|
68
|
+
<div className="mt-2 text-purple-300">Original Position:</div>
|
|
69
|
+
<div className="pl-2">
|
|
70
|
+
<div>column: {originalPanelPosition?.columnIndex ?? "null"}</div>
|
|
71
|
+
<div>index: {originalPanelPosition?.panelIndex ?? "null"}</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* マウス位置 */}
|
|
75
|
+
<div className="mt-2 text-orange-300">Mouse Position:</div>
|
|
76
|
+
<div className="pl-2">
|
|
77
|
+
<div>
|
|
78
|
+
X: {mousePosition.x}, Y: {mousePosition.y}
|
|
79
|
+
</div>
|
|
80
|
+
<div>visible: {mousePosition.isVisible ? "👁️" : "🙈"}</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{/* タッチドラッグ状態 */}
|
|
84
|
+
<div className="mt-2 text-red-300">Touch Drag:</div>
|
|
85
|
+
<div className="pl-2">
|
|
86
|
+
<div>active: {touchDragState.isTouchDragging ? "✅" : "❌"}</div>
|
|
87
|
+
<div>element: {touchDragState.draggedElement ? "📱" : "none"}</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{/* カスタムドラッグ状態 */}
|
|
91
|
+
<div className="mt-2 text-cyan-300">Custom Drag:</div>
|
|
92
|
+
<div className="pl-2">
|
|
93
|
+
<div>active: {customDrag.isActive ? "✅" : "❌"}</div>
|
|
94
|
+
<div>panelId: {customDrag.panelId || "none"}</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Props for the MousePositionDisplay component.
|
|
103
|
+
*
|
|
104
|
+
* MousePositionDisplay コンポーネントのプロパティ。
|
|
105
|
+
*/
|
|
106
|
+
interface MousePositionDisplayProps {
|
|
107
|
+
/** Debug configuration object. / デバッグ設定オブジェクト。 */
|
|
108
|
+
config: DebugConfig
|
|
109
|
+
/** The current mouse coordinates. / 現在のマウス座標。 */
|
|
110
|
+
mousePosition: MousePosition
|
|
111
|
+
/** The current state of the drag operation. / ドラッグ操作の現在の状態。 */
|
|
112
|
+
dragState: DragState
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Displays the current mouse coordinates on the screen during a drag operation.
|
|
117
|
+
*
|
|
118
|
+
* ドラッグ操作中に画面上に現在のマウス座標を表示します。
|
|
119
|
+
* @param {MousePositionDisplayProps} props - The props for the component。
|
|
120
|
+
* @returns {JSX.Element | null} The rendered mouse position display or null if not enabled。
|
|
121
|
+
*/
|
|
122
|
+
export const MousePositionDisplay = ({ config, mousePosition, dragState }: MousePositionDisplayProps) => {
|
|
123
|
+
// マウス位置表示が無効、またはドラッグ中でない場合は何も表示しない
|
|
124
|
+
if (!(config.showMousePosition && dragState.isDragging)) {
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
className="pointer-events-none fixed z-50 rounded-md bg-yellow-500/90 px-2 py-1 font-mono text-black text-xs leading-tight shadow-lg"
|
|
131
|
+
style={
|
|
132
|
+
{
|
|
133
|
+
left: `${mousePosition.x + 10}px`,
|
|
134
|
+
top: `${mousePosition.y - 30}px`,
|
|
135
|
+
} as CSSProperties
|
|
136
|
+
}>
|
|
137
|
+
<div>
|
|
138
|
+
Mouse: {mousePosition.x}, {mousePosition.y}
|
|
139
|
+
</div>
|
|
140
|
+
<div className="text-[10px] opacity-80">Dragging: {dragState.draggedPanel || "none"}</div>
|
|
141
|
+
</div>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Props for the PanelSizeDisplay component。
|
|
147
|
+
*
|
|
148
|
+
* PanelSizeDisplay コンポーネントのプロパティ。
|
|
149
|
+
*/
|
|
150
|
+
interface PanelSizeDisplayProps {
|
|
151
|
+
/** Debug configuration object. / デバッグ設定オブジェクト。 */
|
|
152
|
+
config: DebugConfig
|
|
153
|
+
panelId: string
|
|
154
|
+
/** The size and position information of the panel. / パネルのサイズと位置情報。 */
|
|
155
|
+
sizeInfo:
|
|
156
|
+
| {
|
|
157
|
+
width: number
|
|
158
|
+
height: number
|
|
159
|
+
x: number
|
|
160
|
+
y: number
|
|
161
|
+
minHeight: string
|
|
162
|
+
maxHeight: string
|
|
163
|
+
}
|
|
164
|
+
| undefined
|
|
165
|
+
/** Callback function for click events on the size info display. / サイズ情報表示のクリックイベントのコールバック関数。 */
|
|
166
|
+
onSizeInfoClick: (e: MouseEvent | TouchEvent) => void
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Displays the size and position of a panel.
|
|
171
|
+
*
|
|
172
|
+
* パネルのサイズと位置を表示します。
|
|
173
|
+
* @param {PanelSizeDisplayProps} props - The props for the component。
|
|
174
|
+
* @returns {JSX.Element | null} The rendered panel size display or null if not enabled。
|
|
175
|
+
*/
|
|
176
|
+
export const PanelSizeDisplay = ({ config, panelId, sizeInfo, onSizeInfoClick }: PanelSizeDisplayProps) => {
|
|
177
|
+
// パネルサイズ表示が無効、またはサイズ情報がない場合は何も表示しない
|
|
178
|
+
if (!(config.showPanelSizes && sizeInfo)) {
|
|
179
|
+
return null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: クリックイベントを持つ静的要素。デバッグ目的のため、インタラクティブ性は限定的。
|
|
184
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: キーボード操作はデバッグ機能では優先度が低い。
|
|
185
|
+
<div
|
|
186
|
+
className="absolute top-1 right-24 z-10 cursor-default rounded-md bg-red-500/90 px-2 py-1 font-mono text-white text-xs leading-tight shadow-lg"
|
|
187
|
+
onClick={onSizeInfoClick}
|
|
188
|
+
onMouseDown={onSizeInfoClick}
|
|
189
|
+
onTouchStart={(e) => e instanceof TouchEvent && onSizeInfoClick(e)}
|
|
190
|
+
onDragStart={onSizeInfoClick}>
|
|
191
|
+
{/* パネル識別子 */}
|
|
192
|
+
<div className="text-[10px] opacity-80">ID:{panelId}</div>
|
|
193
|
+
{/* 幅と高さ */}
|
|
194
|
+
<div>
|
|
195
|
+
{sizeInfo.width}×{sizeInfo.height}
|
|
196
|
+
</div>
|
|
197
|
+
{/* X, Y座標 */}
|
|
198
|
+
<div>
|
|
199
|
+
X:{sizeInfo.x} Y:{sizeInfo.y}
|
|
200
|
+
</div>
|
|
201
|
+
{/* 最小・最大高さ */}
|
|
202
|
+
<div className="text-[10px] opacity-80">
|
|
203
|
+
min:{sizeInfo.minHeight} max:{sizeInfo.maxHeight}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A helper function for logging debug messages to the console based on the configured log level.
|
|
211
|
+
*
|
|
212
|
+
* 設定されたログレベルに基づいてデバッグメッセージをコンソールに出力するためのヘルパー関数。
|
|
213
|
+
* @param {DebugConfig} config - The debug configuration object。
|
|
214
|
+
* @param {1 | 2} level - The log level of the message (1 for basic, 2 for verbose)。
|
|
215
|
+
* @param {...any[]} args - The arguments to log to the console。
|
|
216
|
+
*/
|
|
217
|
+
export const debugLog = (config: DebugConfig, level: 1 | 2, ...args: unknown[]) => {
|
|
218
|
+
// 設定されたログレベルがメッセージのレベル以上の場合にのみログ出力
|
|
219
|
+
if ((config.logLevel ?? 0) >= level) {
|
|
220
|
+
Logger.info(...args)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the DebugControlPanel component.
|
|
3
|
+
* This component provides a UI for toggling various debug visualizations and setting the log level
|
|
4
|
+
* for the drag-and-drop layout system.
|
|
5
|
+
*
|
|
6
|
+
* DebugControlPanel コンポーネントを定義します。
|
|
7
|
+
* このコンポーネントは、ドラッグ&ドロップレイアウトシステムの様々なデバッグ表示を切り替えたり、
|
|
8
|
+
* ログレベルを設定したりするためのUIを提供します。
|
|
9
|
+
*/
|
|
10
|
+
import type { DebugConfig } from "./types"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the DebugControlPanel component.
|
|
14
|
+
*
|
|
15
|
+
* DebugControlPanel コンポーネントのプロパティ。
|
|
16
|
+
*/
|
|
17
|
+
type DebugControlPanelProps = {
|
|
18
|
+
/** The current debug configuration object. / 現在のデバッグ設定オブジェクト。 */
|
|
19
|
+
config: DebugConfig
|
|
20
|
+
/** Callback function to update the debug configuration. / デバッグ設定を更新するためのコールバック関数。 */
|
|
21
|
+
onConfigChange: (newConfig: DebugConfig) => void
|
|
22
|
+
/** A flag indicating if the control panel is currently visible. / コントロールパネルが現在表示されているかどうかを示すフラグ。 */
|
|
23
|
+
isVisible: boolean
|
|
24
|
+
/** Callback function to toggle the visibility of the control panel. / コントロールパネルの表示を切り替えるためのコールバック関数。 */
|
|
25
|
+
onToggleVisibility: () => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A floating panel with controls to manage debug settings.
|
|
30
|
+
* It can be toggled to show/hide and provides checkboxes and a slider
|
|
31
|
+
* to dynamically change the debug configuration.
|
|
32
|
+
*
|
|
33
|
+
* デバッグ設定を管理するためのコントロールを持つフローティングパネル。
|
|
34
|
+
* 表示・非表示を切り替えることができ、チェックボックスとスライダーで
|
|
35
|
+
* デバッグ設定を動的に変更できます。
|
|
36
|
+
* @param {DebugControlPanelProps} props - The props for the component.
|
|
37
|
+
* @returns {JSX.Element} The rendered debug control panel.
|
|
38
|
+
*/
|
|
39
|
+
export const DebugControlPanel = ({ config, onConfigChange, isVisible, onToggleVisibility }: DebugControlPanelProps) => {
|
|
40
|
+
// パネルが非表示の場合、表示を切り替えるための小さなボタンのみをレンダリング
|
|
41
|
+
if (!isVisible) {
|
|
42
|
+
return (
|
|
43
|
+
<button type="button" onClick={onToggleVisibility} className="fixed right-4 bottom-4 z-50 rounded-lg bg-gray-800 p-2 text-white shadow-lg transition-colors hover:bg-gray-700" title="デバッグ設定を開く">
|
|
44
|
+
🛠️
|
|
45
|
+
</button>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// パネルが表示されている場合、すべての設定コントロールをレンダリング
|
|
50
|
+
return (
|
|
51
|
+
<div className="fixed right-4 bottom-4 z-50 max-w-sm rounded-lg border border-gray-300 bg-white p-4 shadow-xl">
|
|
52
|
+
{/* パネルヘッダー */}
|
|
53
|
+
<div className="mb-3 flex items-center justify-between">
|
|
54
|
+
<h3 className="font-bold text-gray-800 text-sm">🛠️ デバッグ設定</h3>
|
|
55
|
+
<button type="button" onClick={onToggleVisibility} className="text-gray-500 text-lg hover:text-gray-700" title="閉じる">
|
|
56
|
+
✕
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div className="space-y-2">
|
|
61
|
+
{/* 各種デバッグ表示のトグルスイッチ */}
|
|
62
|
+
<label className="flex items-center space-x-2 text-sm">
|
|
63
|
+
<input
|
|
64
|
+
type="checkbox"
|
|
65
|
+
checked={config.showDebugPanel ?? false}
|
|
66
|
+
onChange={(e) =>
|
|
67
|
+
onConfigChange({
|
|
68
|
+
...config,
|
|
69
|
+
showDebugPanel: e.target.checked,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
className="rounded"
|
|
73
|
+
/>
|
|
74
|
+
<span>デバッグパネル表示</span>
|
|
75
|
+
</label>
|
|
76
|
+
|
|
77
|
+
<label className="flex items-center space-x-2 text-sm">
|
|
78
|
+
<input
|
|
79
|
+
type="checkbox"
|
|
80
|
+
checked={config.showMousePosition ?? false}
|
|
81
|
+
onChange={(e) =>
|
|
82
|
+
onConfigChange({
|
|
83
|
+
...config,
|
|
84
|
+
showMousePosition: e.target.checked,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
className="rounded"
|
|
88
|
+
/>
|
|
89
|
+
<span>マウス位置表示</span>
|
|
90
|
+
</label>
|
|
91
|
+
|
|
92
|
+
<label className="flex items-center space-x-2 text-sm">
|
|
93
|
+
<input
|
|
94
|
+
type="checkbox"
|
|
95
|
+
checked={config.showPanelSizes ?? false}
|
|
96
|
+
onChange={(e) =>
|
|
97
|
+
onConfigChange({
|
|
98
|
+
...config,
|
|
99
|
+
showPanelSizes: e.target.checked,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
className="rounded"
|
|
103
|
+
/>
|
|
104
|
+
<span>パネルサイズ情報</span>
|
|
105
|
+
</label>
|
|
106
|
+
|
|
107
|
+
<label className="flex items-center space-x-2 text-sm">
|
|
108
|
+
<input
|
|
109
|
+
type="checkbox"
|
|
110
|
+
checked={config.showPlaceholderInfo ?? false}
|
|
111
|
+
onChange={(e) =>
|
|
112
|
+
onConfigChange({
|
|
113
|
+
...config,
|
|
114
|
+
showPlaceholderInfo: e.target.checked,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
className="rounded"
|
|
118
|
+
/>
|
|
119
|
+
<span>プレースホルダー詳細</span>
|
|
120
|
+
</label>
|
|
121
|
+
|
|
122
|
+
<label className="flex items-center space-x-2 text-sm">
|
|
123
|
+
<input
|
|
124
|
+
type="checkbox"
|
|
125
|
+
checked={config.showDropZoneOutlines ?? false}
|
|
126
|
+
onChange={(e) =>
|
|
127
|
+
onConfigChange({
|
|
128
|
+
...config,
|
|
129
|
+
showDropZoneOutlines: e.target.checked,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
className="rounded"
|
|
133
|
+
/>
|
|
134
|
+
<span>ドロップゾーン境界</span>
|
|
135
|
+
</label>
|
|
136
|
+
|
|
137
|
+
{/* ログレベル設定スライダー */}
|
|
138
|
+
<div className="border-gray-200 border-t pt-2">
|
|
139
|
+
<label htmlFor="log-level-slider" className="mb-1 block text-sm">
|
|
140
|
+
ログレベル: {config.logLevel ?? 0}
|
|
141
|
+
</label>
|
|
142
|
+
<input
|
|
143
|
+
id="log-level-slider"
|
|
144
|
+
type="range"
|
|
145
|
+
min="0"
|
|
146
|
+
max="2"
|
|
147
|
+
value={config.logLevel ?? 0}
|
|
148
|
+
onChange={(e) =>
|
|
149
|
+
onConfigChange({
|
|
150
|
+
...config,
|
|
151
|
+
logLevel: parseInt(e.target.value, 10) as 0 | 1 | 2,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
className="w-full"
|
|
155
|
+
aria-label="ログレベル設定"
|
|
156
|
+
/>
|
|
157
|
+
<div className="mt-1 flex justify-between text-gray-500 text-xs">
|
|
158
|
+
<span>無効</span>
|
|
159
|
+
<span>基本</span>
|
|
160
|
+
<span>詳細</span>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* 一括設定ボタン */}
|
|
165
|
+
<div className="space-y-2 pt-2">
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
onClick={() =>
|
|
169
|
+
onConfigChange({
|
|
170
|
+
showDebugPanel: true,
|
|
171
|
+
showMousePosition: true,
|
|
172
|
+
showPanelSizes: true,
|
|
173
|
+
showPlaceholderInfo: true,
|
|
174
|
+
showDropZoneOutlines: true,
|
|
175
|
+
logLevel: 2,
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
className="w-full rounded bg-blue-100 px-2 py-1 text-blue-700 text-sm transition-colors hover:bg-blue-200">
|
|
179
|
+
すべて有効
|
|
180
|
+
</button>
|
|
181
|
+
<button
|
|
182
|
+
type="button"
|
|
183
|
+
onClick={() =>
|
|
184
|
+
onConfigChange({
|
|
185
|
+
showDebugPanel: false,
|
|
186
|
+
showMousePosition: false,
|
|
187
|
+
showPanelSizes: false,
|
|
188
|
+
showPlaceholderInfo: false,
|
|
189
|
+
showDropZoneOutlines: false,
|
|
190
|
+
logLevel: 0,
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
className="w-full rounded bg-gray-100 px-2 py-1 text-gray-700 text-sm transition-colors hover:bg-gray-200">
|
|
194
|
+
すべて無効
|
|
195
|
+
</button>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
200
|
+
}
|