svelte-p5-components 0.4.0 → 0.4.1
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/DraggableWindow.svelte +63 -25
- package/package.json +1 -1
|
@@ -72,36 +72,59 @@
|
|
|
72
72
|
let size = $state(untrack(() => ({ w: typeof width === 'number' ? width : 480, h: 40 })));
|
|
73
73
|
let rootEl: HTMLDivElement | null = $state(null);
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
// Clamp `pos` back inside whatever sandbox `constrained` points at.
|
|
76
|
+
// Runs on drag end (drag might overshoot during pointer-move) AND on
|
|
77
|
+
// any resize of the sandbox (window → viewport mode, parent element →
|
|
78
|
+
// parent mode) so the window snaps back into view when its container
|
|
79
|
+
// shrinks underneath it.
|
|
80
|
+
function clamp() {
|
|
81
|
+
if (typeof window === 'undefined' || !rootEl) return;
|
|
82
|
+
|
|
83
|
+
if (constrained === 'parent') {
|
|
84
|
+
// Position is relative to the parent's padding-box, so bounds
|
|
85
|
+
// are [0, parentSize - windowSize]. Falls through to no-op if
|
|
86
|
+
// the window is somehow larger than the parent (Math.max(0, ...)).
|
|
87
|
+
const parent = rootEl.parentElement;
|
|
88
|
+
if (!parent) return;
|
|
89
|
+
const r = parent.getBoundingClientRect();
|
|
90
|
+
const maxX = Math.max(0, r.width - size.w);
|
|
91
|
+
const maxY = Math.max(0, r.height - size.h);
|
|
92
|
+
pos = {
|
|
93
|
+
x: Math.max(0, Math.min(maxX, pos.x)),
|
|
94
|
+
y: Math.max(0, Math.min(maxY, pos.y))
|
|
95
|
+
};
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (constrained === 'viewport') {
|
|
100
|
+
// Never allow the titlebar above the viewport top - that's the
|
|
101
|
+
// "unreachable" case. Allow partial horizontal overflow so narrow
|
|
102
|
+
// screens don't trap wide windows, but always keep `minVisible`
|
|
103
|
+
// pixels of the window on-screen for a grab handle.
|
|
104
|
+
const vw = window.innerWidth;
|
|
105
|
+
const vh = window.innerHeight;
|
|
106
|
+
const minX = -(size.w - minVisible);
|
|
107
|
+
const maxX = vw - minVisible;
|
|
108
|
+
const minY = 0;
|
|
109
|
+
const maxY = vh - minVisible;
|
|
110
|
+
pos = {
|
|
111
|
+
x: Math.max(minX, Math.min(maxX, pos.x)),
|
|
112
|
+
y: Math.max(minY, Math.min(maxY, pos.y))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// 'none': no clamping.
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
// Keep size fresh (used for clamp math) - ResizeObserver on our own
|
|
98
|
-
// root element catches CSS resize-from-corner too.
|
|
119
|
+
// root element catches CSS resize-from-corner too. We also re-clamp
|
|
120
|
+
// after each size change so corner-resizing past a bound snaps back.
|
|
99
121
|
$effect(() => {
|
|
100
122
|
if (!rootEl) return;
|
|
101
123
|
const el = rootEl;
|
|
102
124
|
const ro = new ResizeObserver(() => {
|
|
103
125
|
const r = el.getBoundingClientRect();
|
|
104
126
|
size = { w: r.width, h: r.height };
|
|
127
|
+
clamp();
|
|
105
128
|
});
|
|
106
129
|
ro.observe(el);
|
|
107
130
|
// seed
|
|
@@ -110,15 +133,30 @@
|
|
|
110
133
|
return () => ro.disconnect();
|
|
111
134
|
});
|
|
112
135
|
|
|
113
|
-
// Re-clamp on window resize -
|
|
114
|
-
//
|
|
136
|
+
// Re-clamp on window resize - the "snap back when the viewport shrinks"
|
|
137
|
+
// case. Harmless in parent mode (clamp() handles parent bounds too), so
|
|
138
|
+
// we keep a single global listener instead of branching.
|
|
115
139
|
$effect(() => {
|
|
116
140
|
if (typeof window === 'undefined') return;
|
|
117
|
-
const onResize = () =>
|
|
141
|
+
const onResize = () => clamp();
|
|
118
142
|
window.addEventListener('resize', onResize);
|
|
119
143
|
return () => window.removeEventListener('resize', onResize);
|
|
120
144
|
});
|
|
121
145
|
|
|
146
|
+
// Parent mode: watch the parent element's size too. A flex/grid row
|
|
147
|
+
// resizing around us wouldn't trigger `window.resize`, so without this
|
|
148
|
+
// the window can end up stranded outside a shrunken container until the
|
|
149
|
+
// user nudges it.
|
|
150
|
+
$effect(() => {
|
|
151
|
+
if (typeof window === 'undefined') return;
|
|
152
|
+
if (constrained !== 'parent' || !rootEl) return;
|
|
153
|
+
const parent = rootEl.parentElement;
|
|
154
|
+
if (!parent) return;
|
|
155
|
+
const ro = new ResizeObserver(() => clamp());
|
|
156
|
+
ro.observe(parent);
|
|
157
|
+
return () => ro.disconnect();
|
|
158
|
+
});
|
|
159
|
+
|
|
122
160
|
// The neodrag `position` plugin takes a `current: {x, y}` and forces
|
|
123
161
|
// the element to that transform. Wrapped in a Compartment so updates
|
|
124
162
|
// to `pos` re-run just this plugin (not the whole plugin list).
|
|
@@ -132,7 +170,7 @@
|
|
|
132
170
|
onDrag: ({ offset }) => {
|
|
133
171
|
pos = { x: offset.x, y: offset.y };
|
|
134
172
|
},
|
|
135
|
-
onDragEnd: () =>
|
|
173
|
+
onDragEnd: () => clamp()
|
|
136
174
|
}),
|
|
137
175
|
...(constrained === 'viewport' ? [bounds(BoundsFrom.viewport())] : []),
|
|
138
176
|
...(constrained === 'parent' ? [bounds(BoundsFrom.parent())] : []),
|
package/package.json
CHANGED