ywana-core8 0.2.6 → 0.2.9
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/dist/index.css +726 -92
- package/dist/index.js +481 -202
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +481 -202
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +481 -202
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/desktop/AppManager.js +179 -115
- package/src/desktop/ApplicationMenu.css +425 -87
- package/src/desktop/ApplicationMenu.js +122 -76
- package/src/desktop/Desktop.stories.jsx +148 -509
- package/src/desktop/WindowContext.js +3 -0
- package/src/desktop/WindowManager.js +32 -10
- package/src/desktop/desktop-linux.css +55 -0
- package/src/desktop/desktop-macos.css +57 -0
- package/src/desktop/desktop-windows.css +54 -3
- package/src/desktop/desktop.css +30 -0
- package/src/desktop/desktop.js +135 -81
- package/src/desktop/window.css +98 -0
- package/src/desktop/window.js +163 -2
- package/src/html/button.css +5 -0
- package/src/html/button.js +2 -1
- package/src/desktop.backup/desktop.css +0 -6
- package/src/desktop.backup/desktop.js +0 -13
- package/src/desktop.backup/window.css +0 -58
- package/src/desktop.backup/window.js +0 -27
package/src/desktop/desktop.js
CHANGED
@@ -2,7 +2,9 @@ import React, { useRef, useEffect, useState, createContext, useContext } from 'r
|
|
2
2
|
import { WindowProvider, useWindows } from './WindowContext'
|
3
3
|
import { Window } from './window'
|
4
4
|
import { ApplicationMenu } from './ApplicationMenu'
|
5
|
-
import {
|
5
|
+
import { AppManager, AppLoader, defaultDesktopConfig } from './AppManager'
|
6
|
+
import { ToggleButton } from '../html/selector'
|
7
|
+
import { Icon } from '../html/icon'
|
6
8
|
import './desktop.css'
|
7
9
|
import './desktop-windows.css'
|
8
10
|
import './desktop-linux.css'
|
@@ -32,9 +34,22 @@ export const useAppManager = () => {
|
|
32
34
|
/**
|
33
35
|
* AppProvider - Provides Application state and AppManager to React components
|
34
36
|
*/
|
35
|
-
export const AppProvider = ({
|
37
|
+
export const AppProvider = ({
|
38
|
+
children,
|
39
|
+
desktopConfig = defaultDesktopConfig
|
40
|
+
}) => {
|
41
|
+
// Create a new AppManager instance if none provided
|
42
|
+
const appManager = new AppManager()
|
36
43
|
const [isApplicationMenuOpen, setIsApplicationMenuOpen] = useState(false)
|
37
44
|
|
45
|
+
// Load configuration on mount
|
46
|
+
useEffect(() => {
|
47
|
+
// Only load if the appManager is empty (no apps registered)
|
48
|
+
if (appManager.getAllApps().length === 0 && desktopConfig) {
|
49
|
+
AppLoader.load(appManager, desktopConfig)
|
50
|
+
}
|
51
|
+
}, [appManager, desktopConfig])
|
52
|
+
|
38
53
|
const value = {
|
39
54
|
// Application Menu state
|
40
55
|
applicationMenu: {
|
@@ -44,7 +59,7 @@ export const AppProvider = ({ children, appManager = defaultAppManager }) => {
|
|
44
59
|
toggle: () => setIsApplicationMenuOpen(!isApplicationMenuOpen)
|
45
60
|
},
|
46
61
|
// App Manager instance
|
47
|
-
appManager
|
62
|
+
appManager
|
48
63
|
}
|
49
64
|
|
50
65
|
return (
|
@@ -57,17 +72,17 @@ export const AppProvider = ({ children, appManager = defaultAppManager }) => {
|
|
57
72
|
/**
|
58
73
|
* Desktop layout component - manages overall desktop structure and sizing
|
59
74
|
*/
|
60
|
-
const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }) => {
|
75
|
+
const DesktopLayout = ({ children, className = '', theme = 'windows', workspaceRef, ...props }) => {
|
61
76
|
const desktopRef = useRef(null)
|
62
77
|
const { windowManager } = useWindows()
|
63
78
|
|
64
|
-
// Measure
|
79
|
+
// Measure workspace size on mount and resize
|
65
80
|
useEffect(() => {
|
66
|
-
let currentSize = { width: 1200, height:
|
81
|
+
let currentSize = { width: 1200, height: 750 } // Default workspace size (desktop - taskbar)
|
67
82
|
|
68
|
-
const
|
69
|
-
if (
|
70
|
-
const rect =
|
83
|
+
const measureWorkspace = () => {
|
84
|
+
if (workspaceRef.current) {
|
85
|
+
const rect = workspaceRef.current.getBoundingClientRect()
|
71
86
|
const newSize = {
|
72
87
|
width: rect.width,
|
73
88
|
height: rect.height
|
@@ -82,12 +97,12 @@ const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }
|
|
82
97
|
}
|
83
98
|
|
84
99
|
// Initial measurement
|
85
|
-
|
100
|
+
measureWorkspace()
|
86
101
|
|
87
102
|
// Listen for resize
|
88
|
-
const resizeObserver = new ResizeObserver(
|
89
|
-
if (
|
90
|
-
resizeObserver.observe(
|
103
|
+
const resizeObserver = new ResizeObserver(measureWorkspace)
|
104
|
+
if (workspaceRef.current) {
|
105
|
+
resizeObserver.observe(workspaceRef.current)
|
91
106
|
}
|
92
107
|
|
93
108
|
return () => {
|
@@ -124,13 +139,13 @@ const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }
|
|
124
139
|
}
|
125
140
|
|
126
141
|
/**
|
127
|
-
* Workspace -
|
142
|
+
* Workspace - container for windows with proper boundaries
|
128
143
|
*/
|
129
144
|
export const Workspace = ({ children }) => {
|
130
145
|
const { windows } = useWindows()
|
131
146
|
|
132
147
|
return (
|
133
|
-
|
148
|
+
<div className="workspace">
|
134
149
|
{children}
|
135
150
|
|
136
151
|
{/* Render windows using Window component */}
|
@@ -148,7 +163,7 @@ export const Workspace = ({ children }) => {
|
|
148
163
|
</div>
|
149
164
|
</Window>
|
150
165
|
))}
|
151
|
-
|
166
|
+
</div>
|
152
167
|
)
|
153
168
|
}
|
154
169
|
|
@@ -156,6 +171,11 @@ export const Workspace = ({ children }) => {
|
|
156
171
|
* DesktopTaskbar - handles window creation and management
|
157
172
|
*/
|
158
173
|
export const DesktopTaskbar = () => {
|
174
|
+
console.log('DesktopTaskbar render')
|
175
|
+
|
176
|
+
const windowsContext = useWindows()
|
177
|
+
console.log('useWindows result:', windowsContext)
|
178
|
+
|
159
179
|
const {
|
160
180
|
createWindow,
|
161
181
|
windows,
|
@@ -163,12 +183,20 @@ export const DesktopTaskbar = () => {
|
|
163
183
|
activeWindowId,
|
164
184
|
focusWindow,
|
165
185
|
minimizeWindow,
|
166
|
-
closeWindow
|
167
|
-
|
186
|
+
closeWindow,
|
187
|
+
cascadeWindows,
|
188
|
+
tileWindows,
|
189
|
+
centerAllWindows,
|
190
|
+
clearAllWindows
|
191
|
+
} = windowsContext
|
168
192
|
|
169
|
-
const {
|
193
|
+
const { isOpen, toggle } = useApplicationMenu()
|
194
|
+
console.log('DesktopTaskbar - isOpen:', isOpen, 'toggle:', toggle)
|
170
195
|
|
171
196
|
const handleCreateWindow = () => {
|
197
|
+
console.log('handleCreateWindow called!')
|
198
|
+
console.log('createWindow function:', createWindow)
|
199
|
+
|
172
200
|
const windowTypes = [
|
173
201
|
{ title: 'File Explorer', icon: '📁', size: { width: 600, height: 400 } },
|
174
202
|
{ title: 'Text Editor', icon: '📝', size: { width: 500, height: 350 } },
|
@@ -178,6 +206,7 @@ export const DesktopTaskbar = () => {
|
|
178
206
|
]
|
179
207
|
|
180
208
|
const randomType = windowTypes[Math.floor(Math.random() * windowTypes.length)]
|
209
|
+
console.log('Creating window:', randomType)
|
181
210
|
|
182
211
|
createWindow({
|
183
212
|
title: randomType.title,
|
@@ -226,58 +255,25 @@ export const DesktopTaskbar = () => {
|
|
226
255
|
}
|
227
256
|
|
228
257
|
return (
|
229
|
-
<div style={{
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
border: 'none',
|
249
|
-
borderRadius: '4px',
|
250
|
-
cursor: 'pointer',
|
251
|
-
fontSize: '14px',
|
252
|
-
fontWeight: 'bold',
|
253
|
-
flexShrink: 0,
|
254
|
-
display: 'flex',
|
255
|
-
alignItems: 'center',
|
256
|
-
gap: '6px'
|
257
|
-
}}
|
258
|
-
title="Open application menu"
|
259
|
-
>
|
260
|
-
<span style={{ fontSize: '16px' }}>🚀</span>
|
261
|
-
Start
|
262
|
-
</button>
|
263
|
-
|
264
|
-
{/* Create window button (for testing) */}
|
265
|
-
<button
|
266
|
-
onClick={handleCreateWindow}
|
267
|
-
style={{
|
268
|
-
padding: '8px 12px',
|
269
|
-
background: '#666',
|
270
|
-
color: 'white',
|
271
|
-
border: 'none',
|
272
|
-
borderRadius: '4px',
|
273
|
-
cursor: 'pointer',
|
274
|
-
fontSize: '12px',
|
275
|
-
flexShrink: 0
|
276
|
-
}}
|
277
|
-
title="Create random window (for testing)"
|
278
|
-
>
|
279
|
-
+
|
280
|
-
</button>
|
258
|
+
<div className='desktop-taskbar' style={{
|
259
|
+
height: '50px',
|
260
|
+
background: 'rgba(0,0,0,0.8)',
|
261
|
+
display: 'flex',
|
262
|
+
alignItems: 'center',
|
263
|
+
padding: '0 16px',
|
264
|
+
gap: '8px'
|
265
|
+
}}>
|
266
|
+
{/* Start button */}
|
267
|
+
<ToggleButton
|
268
|
+
checked={isOpen}
|
269
|
+
onToggle={() => {
|
270
|
+
console.log('Start button toggled, current state:', isOpen)
|
271
|
+
toggle()
|
272
|
+
}}
|
273
|
+
icon="start"
|
274
|
+
label="Start"
|
275
|
+
>
|
276
|
+
</ToggleButton>
|
281
277
|
|
282
278
|
{/* Separator */}
|
283
279
|
<div style={{
|
@@ -356,6 +352,49 @@ export const DesktopTaskbar = () => {
|
|
356
352
|
))}
|
357
353
|
</div>
|
358
354
|
|
355
|
+
{/* Window Layout Controls */}
|
356
|
+
<div style={{
|
357
|
+
display: 'flex',
|
358
|
+
gap: '4px',
|
359
|
+
alignItems: 'center',
|
360
|
+
marginLeft: 'auto',
|
361
|
+
marginRight: '8px'
|
362
|
+
}}>
|
363
|
+
|
364
|
+
<Icon clickable size="small"
|
365
|
+
action={() => {
|
366
|
+
console.log('Cascade windows clicked!')
|
367
|
+
cascadeWindows()
|
368
|
+
}}
|
369
|
+
icon="view_stream"
|
370
|
+
label="Cascade"
|
371
|
+
/>
|
372
|
+
<Icon clickable size="small"
|
373
|
+
action={() => {
|
374
|
+
console.log('Tile windows clicked!')
|
375
|
+
tileWindows()
|
376
|
+
}}
|
377
|
+
icon="view_module"
|
378
|
+
label="Tile"
|
379
|
+
/>
|
380
|
+
<Icon clickable size="small"
|
381
|
+
action={() => {
|
382
|
+
console.log('Center all windows clicked!')
|
383
|
+
centerAllWindows()
|
384
|
+
}}
|
385
|
+
icon="center_focus_strong"
|
386
|
+
label="Center"
|
387
|
+
/>
|
388
|
+
<Icon clickable size="small"
|
389
|
+
action={() => {
|
390
|
+
console.log('Clear all windows clicked!')
|
391
|
+
clearAllWindows()
|
392
|
+
}}
|
393
|
+
icon="clear_all"
|
394
|
+
label="Clear"
|
395
|
+
/>
|
396
|
+
</div>
|
397
|
+
|
359
398
|
{/* Debug info */}
|
360
399
|
<div style={{
|
361
400
|
color: 'rgba(255,255,255,0.7)',
|
@@ -375,18 +414,26 @@ export const DesktopTaskbar = () => {
|
|
375
414
|
*/
|
376
415
|
const DesktopInternal = ({ desktopSize, children, ...props }) => {
|
377
416
|
const { isOpen, close } = useApplicationMenu()
|
417
|
+
const workspaceRef = useRef(null)
|
378
418
|
|
379
419
|
return (
|
380
420
|
<WindowProvider desktopSize={desktopSize}>
|
381
|
-
<DesktopLayout {...props}>
|
382
|
-
|
383
|
-
|
384
|
-
|
421
|
+
<DesktopLayout workspaceRef={workspaceRef} {...props}>
|
422
|
+
{/* Workspace container - has its own boundaries */}
|
423
|
+
<div ref={workspaceRef} className="desktop__workspace-container">
|
424
|
+
<Workspace>
|
425
|
+
{children}
|
426
|
+
</Workspace>
|
427
|
+
|
428
|
+
{/* ApplicationMenu positioned over workspace */}
|
429
|
+
<ApplicationMenu
|
430
|
+
isOpen={isOpen}
|
431
|
+
onClose={close}
|
432
|
+
/>
|
433
|
+
</div>
|
434
|
+
|
435
|
+
{/* Taskbar at bottom - separate from workspace */}
|
385
436
|
<DesktopTaskbar />
|
386
|
-
<ApplicationMenu
|
387
|
-
isOpen={isOpen}
|
388
|
-
onClose={close}
|
389
|
-
/>
|
390
437
|
</DesktopLayout>
|
391
438
|
</WindowProvider>
|
392
439
|
)
|
@@ -395,9 +442,16 @@ const DesktopInternal = ({ desktopSize, children, ...props }) => {
|
|
395
442
|
/**
|
396
443
|
* Main Desktop component with AppProvider wrapper
|
397
444
|
*/
|
398
|
-
export const Desktop = ({
|
445
|
+
export const Desktop = ({
|
446
|
+
desktopSize,
|
447
|
+
children,
|
448
|
+
desktopConfig,
|
449
|
+
...props
|
450
|
+
}) => {
|
399
451
|
return (
|
400
|
-
<AppProvider
|
452
|
+
<AppProvider
|
453
|
+
desktopConfig={desktopConfig}
|
454
|
+
>
|
401
455
|
<DesktopInternal desktopSize={desktopSize} {...props}>
|
402
456
|
{children}
|
403
457
|
</DesktopInternal>
|
package/src/desktop/window.css
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
11
11
|
overflow: hidden;
|
12
12
|
user-select: none;
|
13
|
+
pointer-events: auto; /* Ensure windows are clickable */
|
13
14
|
}
|
14
15
|
|
15
16
|
.window:focus-within {
|
@@ -249,3 +250,100 @@
|
|
249
250
|
.window--dragging * {
|
250
251
|
user-select: none !important;
|
251
252
|
}
|
253
|
+
|
254
|
+
/* Window Resize Handles */
|
255
|
+
.window__resize-handle {
|
256
|
+
position: absolute;
|
257
|
+
background: transparent;
|
258
|
+
z-index: 10;
|
259
|
+
}
|
260
|
+
|
261
|
+
/* Edge handles */
|
262
|
+
.window__resize-handle--n {
|
263
|
+
top: -3px;
|
264
|
+
left: 8px;
|
265
|
+
right: 8px;
|
266
|
+
height: 6px;
|
267
|
+
cursor: n-resize;
|
268
|
+
}
|
269
|
+
|
270
|
+
.window__resize-handle--s {
|
271
|
+
bottom: -3px;
|
272
|
+
left: 8px;
|
273
|
+
right: 8px;
|
274
|
+
height: 6px;
|
275
|
+
cursor: s-resize;
|
276
|
+
}
|
277
|
+
|
278
|
+
.window__resize-handle--e {
|
279
|
+
top: 8px;
|
280
|
+
bottom: 8px;
|
281
|
+
right: -3px;
|
282
|
+
width: 6px;
|
283
|
+
cursor: e-resize;
|
284
|
+
}
|
285
|
+
|
286
|
+
.window__resize-handle--w {
|
287
|
+
top: 8px;
|
288
|
+
bottom: 8px;
|
289
|
+
left: -3px;
|
290
|
+
width: 6px;
|
291
|
+
cursor: w-resize;
|
292
|
+
}
|
293
|
+
|
294
|
+
/* Corner handles */
|
295
|
+
.window__resize-handle--ne {
|
296
|
+
top: -3px;
|
297
|
+
right: -3px;
|
298
|
+
width: 12px;
|
299
|
+
height: 12px;
|
300
|
+
cursor: ne-resize;
|
301
|
+
}
|
302
|
+
|
303
|
+
.window__resize-handle--nw {
|
304
|
+
top: -3px;
|
305
|
+
left: -3px;
|
306
|
+
width: 12px;
|
307
|
+
height: 12px;
|
308
|
+
cursor: nw-resize;
|
309
|
+
}
|
310
|
+
|
311
|
+
.window__resize-handle--se {
|
312
|
+
bottom: -3px;
|
313
|
+
right: -3px;
|
314
|
+
width: 12px;
|
315
|
+
height: 12px;
|
316
|
+
cursor: se-resize;
|
317
|
+
}
|
318
|
+
|
319
|
+
.window__resize-handle--sw {
|
320
|
+
bottom: -3px;
|
321
|
+
left: -3px;
|
322
|
+
width: 12px;
|
323
|
+
height: 12px;
|
324
|
+
cursor: sw-resize;
|
325
|
+
}
|
326
|
+
|
327
|
+
/* Resize handle hover effects */
|
328
|
+
.window__resize-handle:hover {
|
329
|
+
background: rgba(25, 118, 210, 0.2);
|
330
|
+
}
|
331
|
+
|
332
|
+
/* Window resizing state */
|
333
|
+
.window--resizing {
|
334
|
+
pointer-events: none;
|
335
|
+
}
|
336
|
+
|
337
|
+
.window--resizing * {
|
338
|
+
pointer-events: none !important;
|
339
|
+
}
|
340
|
+
|
341
|
+
/* Allow resize handles to work during resize */
|
342
|
+
.window--resizing .window__resize-handle {
|
343
|
+
pointer-events: auto !important;
|
344
|
+
}
|
345
|
+
|
346
|
+
/* Disable text selection during resize */
|
347
|
+
.window--resizing * {
|
348
|
+
user-select: none !important;
|
349
|
+
}
|
package/src/desktop/window.js
CHANGED
@@ -130,14 +130,119 @@ export const Window = ({
|
|
130
130
|
updateWindowPosition(id, finalPosition)
|
131
131
|
}
|
132
132
|
|
133
|
-
//
|
133
|
+
// Handle resize start
|
134
|
+
const handleResizeStart = (e, direction) => {
|
135
|
+
if (!resizable || maximized) return
|
136
|
+
|
137
|
+
e.preventDefault()
|
138
|
+
e.stopPropagation()
|
139
|
+
|
140
|
+
setIsResizing(true)
|
141
|
+
setResizeDirection(direction)
|
142
|
+
setResizeStartSize(size)
|
143
|
+
setResizeStartPosition(position)
|
144
|
+
setResizeStartMouse({ x: e.clientX, y: e.clientY })
|
145
|
+
|
146
|
+
// Focus window
|
147
|
+
focusWindow(id)
|
148
|
+
}
|
149
|
+
|
150
|
+
// Handle resize move
|
151
|
+
const handleResizeMove = (e) => {
|
152
|
+
if (!isResizing) return
|
153
|
+
|
154
|
+
e.preventDefault()
|
155
|
+
|
156
|
+
const deltaX = e.clientX - resizeStartMouse.x
|
157
|
+
const deltaY = e.clientY - resizeStartMouse.y
|
158
|
+
|
159
|
+
let newSize = { ...resizeStartSize }
|
160
|
+
let newPosition = { ...resizeStartPosition }
|
161
|
+
|
162
|
+
// Calculate new size and position based on resize direction
|
163
|
+
switch (resizeDirection) {
|
164
|
+
case 'n': // North
|
165
|
+
newSize.height = Math.max(150, resizeStartSize.height - deltaY)
|
166
|
+
newPosition.y = resizeStartPosition.y + (resizeStartSize.height - newSize.height)
|
167
|
+
break
|
168
|
+
case 's': // South
|
169
|
+
newSize.height = Math.max(150, resizeStartSize.height + deltaY)
|
170
|
+
break
|
171
|
+
case 'e': // East
|
172
|
+
newSize.width = Math.max(200, resizeStartSize.width + deltaX)
|
173
|
+
break
|
174
|
+
case 'w': // West
|
175
|
+
newSize.width = Math.max(200, resizeStartSize.width - deltaX)
|
176
|
+
newPosition.x = resizeStartPosition.x + (resizeStartSize.width - newSize.width)
|
177
|
+
break
|
178
|
+
case 'ne': // Northeast
|
179
|
+
newSize.width = Math.max(200, resizeStartSize.width + deltaX)
|
180
|
+
newSize.height = Math.max(150, resizeStartSize.height - deltaY)
|
181
|
+
newPosition.y = resizeStartPosition.y + (resizeStartSize.height - newSize.height)
|
182
|
+
break
|
183
|
+
case 'nw': // Northwest
|
184
|
+
newSize.width = Math.max(200, resizeStartSize.width - deltaX)
|
185
|
+
newSize.height = Math.max(150, resizeStartSize.height - deltaY)
|
186
|
+
newPosition.x = resizeStartPosition.x + (resizeStartSize.width - newSize.width)
|
187
|
+
newPosition.y = resizeStartPosition.y + (resizeStartSize.height - newSize.height)
|
188
|
+
break
|
189
|
+
case 'se': // Southeast
|
190
|
+
newSize.width = Math.max(200, resizeStartSize.width + deltaX)
|
191
|
+
newSize.height = Math.max(150, resizeStartSize.height + deltaY)
|
192
|
+
break
|
193
|
+
case 'sw': // Southwest
|
194
|
+
newSize.width = Math.max(200, resizeStartSize.width - deltaX)
|
195
|
+
newSize.height = Math.max(150, resizeStartSize.height + deltaY)
|
196
|
+
newPosition.x = resizeStartPosition.x + (resizeStartSize.width - newSize.width)
|
197
|
+
break
|
198
|
+
}
|
199
|
+
|
200
|
+
// Apply changes visually
|
201
|
+
if (windowRef.current) {
|
202
|
+
windowRef.current.style.width = `${newSize.width}px`
|
203
|
+
windowRef.current.style.height = `${newSize.height}px`
|
204
|
+
windowRef.current.style.left = `${newPosition.x}px`
|
205
|
+
windowRef.current.style.top = `${newPosition.y}px`
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
// Handle resize end
|
210
|
+
const handleResizeEnd = () => {
|
211
|
+
if (!isResizing) return
|
212
|
+
|
213
|
+
setIsResizing(false)
|
214
|
+
setResizeDirection('')
|
215
|
+
|
216
|
+
// Get final size and position
|
217
|
+
const desktopContainer = windowRef.current?.parentElement
|
218
|
+
if (!desktopContainer) return
|
219
|
+
|
220
|
+
const windowRect = windowRef.current.getBoundingClientRect()
|
221
|
+
const desktopRect = desktopContainer.getBoundingClientRect()
|
222
|
+
|
223
|
+
const finalSize = {
|
224
|
+
width: windowRect.width,
|
225
|
+
height: windowRect.height
|
226
|
+
}
|
227
|
+
|
228
|
+
const finalPosition = {
|
229
|
+
x: windowRect.left - desktopRect.left,
|
230
|
+
y: windowRect.top - desktopRect.top
|
231
|
+
}
|
232
|
+
|
233
|
+
// Update WindowManager
|
234
|
+
updateWindowSize(id, finalSize)
|
235
|
+
updateWindowPosition(id, finalPosition)
|
236
|
+
}
|
237
|
+
|
238
|
+
// Setup drag and resize event listeners
|
134
239
|
useEffect(() => {
|
135
240
|
if (isDragging) {
|
136
241
|
document.addEventListener('mousemove', handleMouseMove)
|
137
242
|
document.addEventListener('mouseup', handleMouseUp)
|
138
243
|
document.body.style.userSelect = 'none'
|
139
244
|
document.body.style.cursor = 'move'
|
140
|
-
|
245
|
+
|
141
246
|
return () => {
|
142
247
|
document.removeEventListener('mousemove', handleMouseMove)
|
143
248
|
document.removeEventListener('mouseup', handleMouseUp)
|
@@ -147,6 +252,20 @@ export const Window = ({
|
|
147
252
|
}
|
148
253
|
}, [isDragging])
|
149
254
|
|
255
|
+
useEffect(() => {
|
256
|
+
if (isResizing) {
|
257
|
+
document.addEventListener('mousemove', handleResizeMove)
|
258
|
+
document.addEventListener('mouseup', handleResizeEnd)
|
259
|
+
document.body.style.userSelect = 'none'
|
260
|
+
|
261
|
+
return () => {
|
262
|
+
document.removeEventListener('mousemove', handleResizeMove)
|
263
|
+
document.removeEventListener('mouseup', handleResizeEnd)
|
264
|
+
document.body.style.userSelect = ''
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}, [isResizing])
|
268
|
+
|
150
269
|
// Window control handlers
|
151
270
|
const handleMinimize = (e) => {
|
152
271
|
e.stopPropagation()
|
@@ -189,6 +308,7 @@ export const Window = ({
|
|
189
308
|
'window',
|
190
309
|
maximized && 'window--maximized',
|
191
310
|
isDragging && 'window--dragging',
|
311
|
+
isResizing && 'window--resizing',
|
192
312
|
className
|
193
313
|
].filter(Boolean).join(' ')
|
194
314
|
|
@@ -264,6 +384,47 @@ export const Window = ({
|
|
264
384
|
{statusBar}
|
265
385
|
</div>
|
266
386
|
)}
|
387
|
+
|
388
|
+
{/* Resize Handles */}
|
389
|
+
{resizable && !maximized && (
|
390
|
+
<>
|
391
|
+
{/* Edge handles */}
|
392
|
+
<div
|
393
|
+
className="window__resize-handle window__resize-handle--n"
|
394
|
+
onMouseDown={(e) => handleResizeStart(e, 'n')}
|
395
|
+
/>
|
396
|
+
<div
|
397
|
+
className="window__resize-handle window__resize-handle--s"
|
398
|
+
onMouseDown={(e) => handleResizeStart(e, 's')}
|
399
|
+
/>
|
400
|
+
<div
|
401
|
+
className="window__resize-handle window__resize-handle--e"
|
402
|
+
onMouseDown={(e) => handleResizeStart(e, 'e')}
|
403
|
+
/>
|
404
|
+
<div
|
405
|
+
className="window__resize-handle window__resize-handle--w"
|
406
|
+
onMouseDown={(e) => handleResizeStart(e, 'w')}
|
407
|
+
/>
|
408
|
+
|
409
|
+
{/* Corner handles */}
|
410
|
+
<div
|
411
|
+
className="window__resize-handle window__resize-handle--ne"
|
412
|
+
onMouseDown={(e) => handleResizeStart(e, 'ne')}
|
413
|
+
/>
|
414
|
+
<div
|
415
|
+
className="window__resize-handle window__resize-handle--nw"
|
416
|
+
onMouseDown={(e) => handleResizeStart(e, 'nw')}
|
417
|
+
/>
|
418
|
+
<div
|
419
|
+
className="window__resize-handle window__resize-handle--se"
|
420
|
+
onMouseDown={(e) => handleResizeStart(e, 'se')}
|
421
|
+
/>
|
422
|
+
<div
|
423
|
+
className="window__resize-handle window__resize-handle--sw"
|
424
|
+
onMouseDown={(e) => handleResizeStart(e, 'sw')}
|
425
|
+
/>
|
426
|
+
</>
|
427
|
+
)}
|
267
428
|
</div>
|
268
429
|
)
|
269
430
|
}
|
package/src/html/button.css
CHANGED
package/src/html/button.js
CHANGED
@@ -111,7 +111,8 @@ export const Button = (props) => {
|
|
111
111
|
icon: loading ? 'hourglass_empty' : icon,
|
112
112
|
size: size === 'small' ? 'small' : size === 'large' ? 'normal' : 'small',
|
113
113
|
disabled: disabled || loading,
|
114
|
-
className: loading ? 'loading-icon' : ''
|
114
|
+
className: loading ? 'loading-icon' : '',
|
115
|
+
eventPropagation: true, // Allow click events to bubble up to button
|
115
116
|
}
|
116
117
|
|
117
118
|
return (
|