@alekstar79/draggable-resizable-container 1.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.
@@ -0,0 +1,298 @@
1
+ import { default as ReactiveEventSystem } from '@alekstar79/reactive-event-system';
2
+ import { StateInterface } from '../utils';
3
+ import { AutoAdjustConfig, Boundaries, ContainerManagerInterface, Plugin, ContainerConfig, ContainerEvent, ContainerState, DirectionMode, MovementMode, PluginMiddleware, ResizeConfig, ResizeDirection } from './types';
4
+
5
+ /**
6
+ * Main container management class for drag and resize operations
7
+ * Implements ContainerManagerInterface for plugin compatibility
8
+ * Now with reactive state management using @alekstar79/reactivity
9
+ */
10
+ export declare class ContainerManager implements ContainerManagerInterface {
11
+ private static MINWIDTH;
12
+ private static MINHEIGHT;
13
+ private dragStream;
14
+ private resizeStream;
15
+ private readonly stateChangeStream;
16
+ private readonly eventEmitter;
17
+ private readonly pluginEventEmitter;
18
+ private readonly config;
19
+ private readonly container;
20
+ private dragHandle;
21
+ private resizeHandles;
22
+ private installedPlugins;
23
+ private reactiveEffects;
24
+ private isDragging;
25
+ private isResizing;
26
+ private resizeDirection;
27
+ private startX;
28
+ private startY;
29
+ private startState;
30
+ private resizeObserver;
31
+ private parentResizeObserver;
32
+ zIndexState: StateInterface;
33
+ private reactiveState;
34
+ private constrainedState;
35
+ private domUpdateEffect;
36
+ /**
37
+ * Create a new container manager instance with reactive state management
38
+ * @param container - HTML element to manage
39
+ * @param config - Configuration options
40
+ */
41
+ constructor(container: HTMLElement, config?: Partial<ContainerConfig>);
42
+ /**
43
+ * Set up ResizeObserver to track viewport and parent size changes
44
+ */
45
+ private setupResizeObservers;
46
+ /**
47
+ * Determine if viewport constraints should be applied
48
+ */
49
+ private shouldConstrainToViewport;
50
+ /**
51
+ * Set up ResizeObserver to track viewport size changes
52
+ */
53
+ private setupViewportResizeObserver;
54
+ /**
55
+ * Handle viewport resize event with reactive state updates
56
+ */
57
+ private handleViewportResize;
58
+ /**
59
+ * Set up ResizeObserver for parent element auto-adjustment
60
+ */
61
+ private setupParentResizeObserver;
62
+ /**
63
+ * Handle parent element resize for auto-adjustment with reactive updates
64
+ */
65
+ private handleParentResize;
66
+ /**
67
+ * Initialize drag and resize handles based on configuration
68
+ */
69
+ private initializeHandles;
70
+ /**
71
+ * Initialize drag handle
72
+ */
73
+ private initializeDragHandle;
74
+ /**
75
+ * Initialize resize handles for all configured directions
76
+ */
77
+ private initializeResizeHandles;
78
+ /**
79
+ * Create individual resize handle for specific direction
80
+ */
81
+ private createResizeHandle;
82
+ /**
83
+ * Apply styles and cursor for resize handle based on direction
84
+ */
85
+ private applyResizeHandleStyles;
86
+ /**
87
+ * Position resize handle based on direction
88
+ */
89
+ private positionResizeHandle;
90
+ /**
91
+ * Bind event listeners to handles
92
+ */
93
+ private bindEvents;
94
+ /**
95
+ * Apply movement mode to coordinates
96
+ */
97
+ private applyMovementMode;
98
+ /**
99
+ * Calculate new state based on resize direction and deltas
100
+ */
101
+ private calculateResizeState;
102
+ /**
103
+ * Constrain container to parent element boundaries (both position and size)
104
+ */
105
+ private constrainToParent;
106
+ /**
107
+ * Get maximum width constraint considering parent and boundaries
108
+ */
109
+ private getMaxWidthConstraint;
110
+ /**
111
+ * Get maximum height constraint considering parent and boundaries
112
+ */
113
+ private getMaxHeightConstraint;
114
+ /**
115
+ * Handle context menu event on drag handle
116
+ */
117
+ private onContextMenu;
118
+ /**
119
+ * Check if snapping plugin is installed
120
+ */
121
+ private hasPluginByName;
122
+ /**
123
+ * Get current container state from DOM
124
+ */
125
+ private getCurrentState;
126
+ /**
127
+ * Setup event middleware for enhanced event processing
128
+ */
129
+ private setupEventMiddleware;
130
+ /**
131
+ * Setup reactive monitoring for container metrics
132
+ */
133
+ private setupReactiveMonitoring;
134
+ /**
135
+ * Subscribe to container events
136
+ * @param event - Event name
137
+ * @param callback - Callback function
138
+ */
139
+ on(event: string, callback: (data: ContainerEvent) => void): void;
140
+ /**
141
+ * Unsubscribe from container events
142
+ * @param event - Event name
143
+ * @param callback - Callback function
144
+ */
145
+ off(event: string, callback: (data: ContainerEvent) => void): void;
146
+ /**
147
+ * Wait for specific container event
148
+ * @example
149
+ * // Wait for drag to complete
150
+ * const dragResult = await manager.waitFor('dragEnd')
151
+ * console.log('Drag completed:', dragResult.state)
152
+ */
153
+ waitFor(event: string, timeout?: number): Promise<ContainerEvent>;
154
+ /**
155
+ * Get reactive stream for specific event type
156
+ * @example
157
+ * // Get state change stream
158
+ * const stateStream = manager.getStream('stateChange')
159
+ * stateStream.subscribe((data) => {
160
+ * console.log('State changed:', data.state)
161
+ * })
162
+ */
163
+ getStream(event: string): ReturnType<ReactiveEventSystem<ContainerEvent>['stream']>;
164
+ /**
165
+ * Pipe container events to another emitter
166
+ * @example
167
+ * // Pipe all events to analytics emitter
168
+ * manager.pipe('*', analyticsEmitter)
169
+ */
170
+ pipe(event: string, targetEmitter: ReactiveEventSystem<ContainerEvent>, targetEvent?: string): () => void;
171
+ /**
172
+ * Get event system metrics for monitoring
173
+ */
174
+ getEventMetrics(): ReturnType<ReactiveEventSystem<ContainerEvent>['getMetrics']>;
175
+ /**
176
+ * Plugin-specific event emission
177
+ */
178
+ emitPluginEvent(event: string, data: any): void;
179
+ /**
180
+ * Listen to plugin-specific events
181
+ */
182
+ onPluginEvent(event: string, listener: (data: any) => void): void;
183
+ /**
184
+ * Remove plugin event listener
185
+ */
186
+ offPluginEvent(event: string, listener: (data: any) => void): void;
187
+ /**
188
+ * Add middleware for plugin events
189
+ */
190
+ usePluginMiddleware(event: string, middleware: PluginMiddleware): () => void;
191
+ /**
192
+ * Handle drag start event
193
+ */
194
+ onDragStart(e: MouseEvent | TouchEvent): void;
195
+ /**
196
+ * Handle drag movement with reactive state updates
197
+ */
198
+ onDragMove(e: MouseEvent | TouchEvent): void;
199
+ /**
200
+ * Handle drag end event
201
+ */
202
+ onDragEnd(): void;
203
+ /**
204
+ * Handle resize start event with direction
205
+ */
206
+ onResizeStart(e: MouseEvent | TouchEvent, direction: ResizeDirection): void;
207
+ /**
208
+ * Handle resize movement with multi-direction support and reactive updates
209
+ */
210
+ onResizeMove(e: MouseEvent | TouchEvent): void;
211
+ /**
212
+ * Handle resize end event
213
+ */
214
+ onResizeEnd(): void;
215
+ /**
216
+ * Set movement direction
217
+ */
218
+ setDirection(direction: DirectionMode): void;
219
+ /**
220
+ * Get current movement direction
221
+ */
222
+ getDirection(): DirectionMode;
223
+ /**
224
+ * Resolve coordinates based on current direction mode
225
+ */
226
+ directionResolver(x: number, y: number): {
227
+ clientX: number;
228
+ clientY: number;
229
+ };
230
+ /**
231
+ * Get current movement mode
232
+ */
233
+ getMode(): MovementMode;
234
+ /**
235
+ * Set movement mode with reactive update
236
+ */
237
+ setMode(mode: MovementMode): void;
238
+ /**
239
+ * Update container boundaries
240
+ */
241
+ setBoundaries(boundaries: Partial<Boundaries>): void;
242
+ /**
243
+ * Get current container state (reactive)
244
+ */
245
+ getState(): ContainerState;
246
+ /**
247
+ * Update container position and size with reactive state
248
+ */
249
+ setState(state: Partial<ContainerState>): void;
250
+ /**
251
+ * Apply current state to DOM immediately for synchronization
252
+ */
253
+ private applyStateToDOM;
254
+ /**
255
+ * Bring container to front programmatically
256
+ */
257
+ bringToFront(): void;
258
+ /**
259
+ * Get container DOM element
260
+ */
261
+ getContainer(): HTMLElement;
262
+ /**
263
+ * Update auto-adjust configuration
264
+ */
265
+ setAutoAdjust(config: AutoAdjustConfig): void;
266
+ /**
267
+ * Update resize configuration
268
+ */
269
+ setResizeConfig(config: ResizeConfig): void;
270
+ /**
271
+ * Set constrain to parent configuration
272
+ */
273
+ setConstrainToParent(enabled: boolean): void;
274
+ /**
275
+ * Set constrain to viewport configuration
276
+ */
277
+ setConstrainToViewport(enabled: boolean): void;
278
+ /**
279
+ * Recalculate container state relative to parent element
280
+ */
281
+ recalculateForParent(): void;
282
+ /**
283
+ * Install plugin on this container manager instance
284
+ */
285
+ use(plugin: Plugin, options?: any): ContainerManagerInterface;
286
+ /**
287
+ * Check if plugin is installed on this instance
288
+ */
289
+ hasPlugin(plugin: Plugin): boolean;
290
+ /**
291
+ * Get all installed plugins on this instance
292
+ */
293
+ getInstalledPlugins(): Plugin[];
294
+ /**
295
+ * Destroy container manager with proper cleanup
296
+ */
297
+ destroy(): void;
298
+ }
@@ -0,0 +1,130 @@
1
+ import { default as ReactiveEventSystem } from '@alekstar79/reactive-event-system';
2
+
3
+ /**
4
+ * Container movement modes
5
+ */
6
+ export type MovementMode = 'smooth' | 'pinned';
7
+ /**
8
+ * Container movement directions
9
+ */
10
+ export type DirectionMode = 'all' | 'horizontal' | 'vertical';
11
+ /**
12
+ * Resize direction types
13
+ */
14
+ export type ResizeDirection = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
15
+ /**
16
+ * Auto-adjustment configuration for parent element
17
+ */
18
+ export interface AutoAdjustConfig {
19
+ enabled?: boolean;
20
+ width?: boolean;
21
+ height?: boolean;
22
+ }
23
+ /**
24
+ * Container boundaries configuration
25
+ */
26
+ export interface Boundaries {
27
+ minWidth?: number;
28
+ minHeight?: number;
29
+ maxWidth?: number;
30
+ maxHeight?: number;
31
+ }
32
+ /**
33
+ * Resize configuration
34
+ */
35
+ export interface ResizeConfig {
36
+ enabled?: boolean;
37
+ directions?: ResizeDirection[];
38
+ }
39
+ /**
40
+ * Configuration options for container manager
41
+ */
42
+ export interface ContainerConfig {
43
+ _uid: string;
44
+ mode: MovementMode;
45
+ boundaries: Boundaries;
46
+ draggingDirection: DirectionMode;
47
+ constrainToViewport: boolean;
48
+ constrainToParent?: boolean;
49
+ autoAdjust?: AutoAdjustConfig;
50
+ resize?: ResizeConfig;
51
+ }
52
+ /**
53
+ * Container position and dimensions
54
+ */
55
+ export interface ContainerState {
56
+ x: number;
57
+ y: number;
58
+ width: number;
59
+ height: number;
60
+ }
61
+ /**
62
+ * Event payload for container changes
63
+ */
64
+ export interface ContainerEvent {
65
+ type: 'drag' | 'resize' | 'modeChange' | 'viewportResize' | 'autoAdjust' | 'parentRecalculated' | 'stateChange';
66
+ state: ContainerState;
67
+ mode: MovementMode;
68
+ direction?: ResizeDirection;
69
+ element: HTMLElement;
70
+ }
71
+ export interface PluginMiddleware {
72
+ (data: any, event: string): any;
73
+ }
74
+ /**
75
+ * Base plugin interface that all plugins must implement
76
+ */
77
+ export interface Plugin {
78
+ pluginId: Symbol;
79
+ /**
80
+ * Install plugin on container manager instance
81
+ * @param instance - ContainerManager instance implementing ContainerManagerInterface
82
+ * @param options - Plugin configuration options
83
+ */
84
+ install(instance: ContainerManagerInterface, options?: any): void;
85
+ destroy?(): void;
86
+ }
87
+ /**
88
+ * Container Manager class interface for plugins
89
+ */
90
+ export interface ContainerManagerInterface {
91
+ getMode(): MovementMode;
92
+ setMode(mode: MovementMode): void;
93
+ getState(): ContainerState;
94
+ setState(state: Partial<ContainerState>): void;
95
+ getDirection(): DirectionMode;
96
+ setDirection(direction: DirectionMode): void;
97
+ setAutoAdjust(config: AutoAdjustConfig): void;
98
+ setResizeConfig(config: ResizeConfig): void;
99
+ setConstrainToParent(enabled: boolean): void;
100
+ setConstrainToViewport(enabled: boolean): void;
101
+ directionResolver(x: number, y: number): {
102
+ clientX: number;
103
+ clientY: number;
104
+ };
105
+ recalculateForParent(): void;
106
+ setBoundaries(boundaries: Partial<Boundaries>): void;
107
+ getContainer(): HTMLElement;
108
+ bringToFront?(): void;
109
+ destroy?(): void;
110
+ on(event: string, callback: (data: ContainerEvent) => void): void;
111
+ off(event: string, callback: (data: ContainerEvent) => void): void;
112
+ waitFor(event: string, timeout?: number): Promise<ContainerEvent>;
113
+ getStream(event: string): ReturnType<ReactiveEventSystem<ContainerEvent>['stream']>;
114
+ pipe(event: string, targetEmitter: ReactiveEventSystem<ContainerEvent>, targetEvent?: string): () => void;
115
+ getEventMetrics(): ReturnType<ReactiveEventSystem<ContainerEvent>['getMetrics']>;
116
+ onDragStart(e: MouseEvent | TouchEvent): void;
117
+ onDragMove(e: MouseEvent | TouchEvent): void;
118
+ onDragEnd(): void;
119
+ onResizeStart(e: MouseEvent | TouchEvent, direction: ResizeDirection): void;
120
+ onResizeMove(e: MouseEvent | TouchEvent): void;
121
+ onResizeEnd(): void;
122
+ emitPluginEvent(event: string, data: any): void;
123
+ onPluginEvent(event: string, listener: Function): void;
124
+ offPluginEvent(event: string, listener: Function): void;
125
+ usePluginMiddleware(event: string, middleware: PluginMiddleware): () => void;
126
+ use(plugin: Plugin, options?: any): ContainerManagerInterface;
127
+ hasPlugin(plugin: Plugin): boolean;
128
+ getInstalledPlugins(): Plugin[];
129
+ [p: string]: any;
130
+ }
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ .container{position:absolute;background:#fff;border:2px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 6px #0000001a;overflow:hidden;resize:none}.drag-handle{position:absolute;top:0;left:0;right:0;height:24px;background:#f7fafc;border-bottom:1px solid #e2e8f0;cursor:move;display:flex;align-items:center;justify-content:center;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.container-title{font-weight:600;font-size:.9rem;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.container-content{position:absolute;top:40px;left:0;right:0;bottom:0;padding:16px;overflow:auto;box-sizing:border-box}.container[data-mode=pinned]{border-color:#e53e3e}.container[data-mode=snap]{border-color:#38a169}.container[data-mode=smooth]{border-color:#3182ce}.resize-n,.resize-s{height:12px;cursor:ns-resize;left:0;right:0}.resize-e,.resize-w{width:12px;cursor:ew-resize;top:0;bottom:0}.resize-ne,.resize-nw,.resize-se,.resize-sw{width:16px;height:16px;background:transparent;border-radius:2px}.resize-n{top:0}.resize-s{bottom:0}.resize-e{right:0}.resize-w{left:0}.resize-ne{top:0;right:0;cursor:nesw-resize}.resize-nw{top:0;left:0;cursor:nwse-resize}.resize-se{bottom:0;right:0;cursor:nwse-resize}.resize-sw{bottom:0;left:0;cursor:nesw-resize}.resize-handle:hover{background:#4299e14d}.resize-ne:hover,.resize-nw:hover,.resize-se:hover,.resize-sw:hover{background:#4299e14d;transform:scale(1.1)}.resize-handle{position:absolute;background:transparent;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;transition:background-color .2s ease}.handle-content{display:flex;justify-content:space-between;align-items:center;width:100%;padding:0 8px;box-sizing:border-box}.mode-controls-container{display:flex;gap:6px;align-items:center;flex-shrink:0}.mode-btn{width:32px;height:32px;border:none;border-radius:8px;cursor:pointer;font-size:14px;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;justify-content:center;background:transparent;color:#6b7280;position:relative;overflow:hidden}.mode-btn:hover{transform:scale(1.2);background:#0000000d}.mode-btn:active{transform:scale(1.1)}.mode-btn:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.container-content::-webkit-scrollbar{width:6px}.container-content::-webkit-scrollbar-track{background:#f3f4f6;border-radius:3px}.container-content::-webkit-scrollbar-thumb{background:#9ca3af;border-radius:3px}.container-content::-webkit-scrollbar-thumb:hover{background:#6b7280}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Main library entry point
3
+ * Exports all public API for the container manager library
4
+ */
5
+ export type { ContainerConfig, ContainerManagerInterface, Plugin, ContainerEvent, ContainerState, Boundaries, MovementMode, } from './core/types';
6
+ export type { StateInterface } from './utils';
7
+ export type { TemplateConfig } from './utils/TemplateLoader';
8
+ export { ContainerInitializer, TemplateLoader, ContentCreator } from './utils';
9
+ export { ContainerManager } from './core/ContainerManager';