@oscarpalmer/oui 0.26.0 → 0.27.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/dist/css/floatable.css +0 -2
- package/dist/css/message.css +1 -0
- package/dist/css/reset.css +1 -0
- package/dist/js/dialog.d.mts +6 -0
- package/dist/js/dialog.mjs +23 -1
- package/dist/js/draggable-zszaW1qS.d.mts +155 -0
- package/dist/js/floatable/floatable.embedded.d.mts +2 -0
- package/dist/js/floatable/{embedded.mjs → floatable.embedded.mjs} +22 -17
- package/dist/js/floatable/floatable.standalone.d.mts +2 -0
- package/dist/js/floatable/floatable.standalone.mjs +2 -0
- package/dist/js/{embedded-ai6cqURc.d.mts → floatable.embedded-YxGwzgbE.d.mts} +13 -10
- package/dist/js/focus-trap/focus-trap.embedded.d.mts +2 -0
- package/dist/js/focus-trap/{embedded.mjs → focus-trap.embedded.mjs} +17 -15
- package/dist/js/focus-trap/focus-trap.standalone.d.mts +2 -0
- package/dist/js/focus-trap/focus-trap.standalone.mjs +2 -0
- package/dist/js/{embedded-CHK82Uh5.d.mts → focus-trap.embedded-DR8NpZfr.d.mts} +18 -18
- package/dist/js/index.d.mts +4 -3
- package/dist/js/index.mjs +8 -7
- package/dist/js/internal/draggable.d.mts +2 -2
- package/dist/js/internal/draggable.mjs +146 -94
- package/dist/js/movable/movable.embedded.d.mts +2 -0
- package/dist/js/movable/movable.embedded.mjs +184 -0
- package/dist/js/movable/movable.standalone.d.mts +2 -0
- package/dist/js/movable/movable.standalone.mjs +2 -0
- package/dist/js/popover.d.mts +3 -2
- package/dist/js/popover.mjs +8 -3
- package/dist/js/sortable/sortable.embedded.d.mts +2 -0
- package/dist/js/{sortable.mjs → sortable/sortable.embedded.mjs} +88 -63
- package/dist/js/sortable/sortable.standalone.d.mts +2 -0
- package/dist/js/sortable/sortable.standalone.mjs +2 -0
- package/dist/js/splitter.d.mts +24 -0
- package/dist/js/splitter.mjs +33 -9
- package/dist/js/toolbar.mjs +6 -6
- package/dist/js/tooltip.d.mts +12 -2
- package/dist/js/tooltip.mjs +49 -33
- package/package.json +11 -11
- package/src/css/floatable.scss +0 -2
- package/src/css/message.scss +1 -0
- package/src/css/reset.scss +1 -0
- package/src/js/dialog.ts +42 -1
- package/src/js/floatable/{embedded.ts → floatable.embedded.ts} +26 -18
- package/src/js/floatable/floatable.standalone.ts +6 -0
- package/src/js/focus-trap/{embedded.ts → focus-trap.embedded.ts} +20 -17
- package/src/js/focus-trap/focus-trap.standalone.ts +1 -0
- package/src/js/index.ts +4 -4
- package/src/js/internal/draggable.ts +309 -168
- package/src/js/movable/movable.embedded.ts +328 -0
- package/src/js/movable/movable.standalone.ts +2 -0
- package/src/js/popover.ts +11 -2
- package/src/js/{sortable.ts → sortable/sortable.embedded.ts} +170 -103
- package/src/js/sortable/sortable.standalone.ts +2 -0
- package/src/js/splitter.ts +28 -4
- package/src/js/tooltip.ts +66 -43
- package/dist/js/floatable/embedded.d.mts +0 -2
- package/dist/js/floatable/standalone.d.mts +0 -2
- package/dist/js/floatable/standalone.mjs +0 -2
- package/dist/js/focus-trap/embedded.d.mts +0 -2
- package/dist/js/focus-trap/standalone.d.mts +0 -2
- package/dist/js/focus-trap/standalone.mjs +0 -6
- package/dist/js/movable-dNqBctx3.d.mts +0 -101
- package/dist/js/movable.d.mts +0 -2
- package/dist/js/movable.mjs +0 -152
- package/dist/js/sortable.d.mts +0 -2
- package/src/js/floatable/standalone.ts +0 -1
- package/src/js/focus-trap/standalone.ts +0 -6
- package/src/js/movable.ts +0 -260
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import type {EventPosition, PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {getPosition, on} from '@oscarpalmer/toretto/event';
|
|
3
|
+
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
4
|
+
import {attributable} from '../internal/attributable';
|
|
5
|
+
import {
|
|
6
|
+
addDraggable,
|
|
7
|
+
getDraggableStates,
|
|
8
|
+
OuiDraggable,
|
|
9
|
+
removeDraggable,
|
|
10
|
+
type CreateOuiDraggableOptions,
|
|
11
|
+
type OuiDraggableBound,
|
|
12
|
+
type OuiDraggableGlobals,
|
|
13
|
+
type OuiDraggableItem,
|
|
14
|
+
type OuiDraggableState,
|
|
15
|
+
type OuiDragMovePosition,
|
|
16
|
+
} from '../internal/draggable';
|
|
17
|
+
|
|
18
|
+
// #region Types
|
|
19
|
+
|
|
20
|
+
export class OuiMovable extends OuiDraggable<OuiMovableState> {
|
|
21
|
+
#element: HTMLElement;
|
|
22
|
+
|
|
23
|
+
#style: OuiMovableStyle;
|
|
24
|
+
|
|
25
|
+
constructor(element: HTMLElement, options?: CreateOuiDraggableOptions) {
|
|
26
|
+
const style = {
|
|
27
|
+
inset: element.style.inset,
|
|
28
|
+
position: element.style.position,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
super(
|
|
32
|
+
'movable',
|
|
33
|
+
'move',
|
|
34
|
+
ATTRIBUTE_MOVABLE,
|
|
35
|
+
element,
|
|
36
|
+
{
|
|
37
|
+
container: {
|
|
38
|
+
attribute: ATTRIBUTE_CONTAINER,
|
|
39
|
+
map: CONTAINERS,
|
|
40
|
+
onDestroy: destroyContainer,
|
|
41
|
+
onInitialize: initializeContainer,
|
|
42
|
+
},
|
|
43
|
+
direction: {
|
|
44
|
+
attribute: ATTRIBUTE_DIRECTION,
|
|
45
|
+
onAfter: afterDirection,
|
|
46
|
+
},
|
|
47
|
+
drag: {
|
|
48
|
+
onBegin,
|
|
49
|
+
onCancel,
|
|
50
|
+
onEnd,
|
|
51
|
+
onMove,
|
|
52
|
+
},
|
|
53
|
+
input: options,
|
|
54
|
+
position: {
|
|
55
|
+
getOffset: getOffsetPosition,
|
|
56
|
+
getOriginal: getOriginalPosition,
|
|
57
|
+
},
|
|
58
|
+
getDragged() {
|
|
59
|
+
return element;
|
|
60
|
+
},
|
|
61
|
+
getOrigin() {
|
|
62
|
+
return element;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
style,
|
|
67
|
+
hasHandles: element.querySelector(SELECTOR_HANDLE) != null,
|
|
68
|
+
moved: false,
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
this.#element = element;
|
|
73
|
+
|
|
74
|
+
this.#style = style;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Destroys the _Movable_
|
|
79
|
+
*/
|
|
80
|
+
override destroy(): void {
|
|
81
|
+
MOVED.delete(this);
|
|
82
|
+
|
|
83
|
+
this.#element.style.inset = this.#style.inset;
|
|
84
|
+
this.#element.style.position = this.#style.position;
|
|
85
|
+
|
|
86
|
+
this.#element = undefined as never;
|
|
87
|
+
this.#style = undefined as never;
|
|
88
|
+
|
|
89
|
+
super.destroy();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type OuiMovableState = {
|
|
94
|
+
hasHandles?: boolean;
|
|
95
|
+
moved: boolean;
|
|
96
|
+
offset?: Partial<EventPosition>;
|
|
97
|
+
style: OuiMovableStyle;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
type OuiMovableStyle = {
|
|
101
|
+
inset: string;
|
|
102
|
+
position: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// #endregion
|
|
106
|
+
|
|
107
|
+
// #region Functions
|
|
108
|
+
|
|
109
|
+
function afterDirection(item: OuiDraggableItem): void {
|
|
110
|
+
const container = item.state.container?.getBoundingClientRect();
|
|
111
|
+
|
|
112
|
+
if (container != null) {
|
|
113
|
+
setOffset(item.state as OuiDraggableState & OuiMovableState, container);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Creates _(or retrieves)_ a _OuiMovable_ instance for an element
|
|
119
|
+
*
|
|
120
|
+
* @param element Element to make movable
|
|
121
|
+
* @returns _OuiMovable_ instance
|
|
122
|
+
*/
|
|
123
|
+
export function createMovable(
|
|
124
|
+
element: HTMLElement,
|
|
125
|
+
options?: CreateOuiDraggableOptions,
|
|
126
|
+
): OuiMovable {
|
|
127
|
+
if (!isHTMLOrSVGElement(element)) {
|
|
128
|
+
throw new TypeError(MESSAGE);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return addMovable(element, options) as OuiMovable;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function destroyContainer(_: unknown, container: HTMLElement, empty: boolean): void {
|
|
135
|
+
if (empty) {
|
|
136
|
+
resizer.unobserve(container);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getOffsetPosition(
|
|
141
|
+
globals: OuiDraggableGlobals,
|
|
142
|
+
_: OuiDraggableItem,
|
|
143
|
+
__: HTMLElement,
|
|
144
|
+
position: EventPosition,
|
|
145
|
+
): EventPosition {
|
|
146
|
+
return {
|
|
147
|
+
x: position.x - (globals.element?.rectangle.left ?? 0),
|
|
148
|
+
y: position.y - (globals.element?.rectangle.top ?? 0),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getOriginalPosition(globals: OuiDraggableGlobals): EventPosition {
|
|
153
|
+
return {
|
|
154
|
+
x: globals.element?.rectangle.left ?? 0,
|
|
155
|
+
y: globals.element?.rectangle.top ?? 0,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function initializeContainer(item: OuiDraggableItem): void {
|
|
160
|
+
resizer.observe(item.state.container!);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function onBegin(): void {}
|
|
164
|
+
|
|
165
|
+
function onCancel(globals: OuiDraggableGlobals, item: OuiDraggableItem): void {
|
|
166
|
+
const {element, moved, style} = item.state as OuiDraggableState & OuiMovableState;
|
|
167
|
+
|
|
168
|
+
const {x, y} = globals.original ?? {x: 0, y: 0};
|
|
169
|
+
|
|
170
|
+
if (moved) {
|
|
171
|
+
element.style.inset = `${y}px auto auto ${x}px`;
|
|
172
|
+
} else {
|
|
173
|
+
element.style.position = style.position;
|
|
174
|
+
element.style.inset = style.inset;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
element.removeAttribute(ATTRIBUTE_MOVING);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onEnd(
|
|
181
|
+
event: MouseEvent | TouchEvent,
|
|
182
|
+
globals: OuiDraggableGlobals,
|
|
183
|
+
item: OuiDraggableItem,
|
|
184
|
+
reset: () => void,
|
|
185
|
+
): void {
|
|
186
|
+
const movable = item.instance as OuiMovable;
|
|
187
|
+
const state = item.state as OuiDraggableState & OuiMovableState;
|
|
188
|
+
|
|
189
|
+
state.element.removeAttribute(ATTRIBUTE_MOVING);
|
|
190
|
+
|
|
191
|
+
if (!state.moved) {
|
|
192
|
+
state.moved = true;
|
|
193
|
+
|
|
194
|
+
if (state.container != null) {
|
|
195
|
+
MOVED.add(movable);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const container = state.container?.getBoundingClientRect();
|
|
200
|
+
|
|
201
|
+
if (container != null) {
|
|
202
|
+
setOffset(state, container);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const end = new CustomEvent(EVENT_END, {
|
|
206
|
+
detail: {
|
|
207
|
+
from: {...globals.original},
|
|
208
|
+
to: getPosition(event),
|
|
209
|
+
},
|
|
210
|
+
cancelable: true,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
state.element.dispatchEvent(end);
|
|
214
|
+
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
if (end.defaultPrevented) {
|
|
217
|
+
onCancel(globals, item);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
reset();
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function onMove(
|
|
225
|
+
_: MouseEvent | TouchEvent,
|
|
226
|
+
globals: OuiDraggableGlobals,
|
|
227
|
+
__: OuiDraggableItem,
|
|
228
|
+
position: OuiDragMovePosition,
|
|
229
|
+
): PlainObject | undefined {
|
|
230
|
+
globals.element?.node.setAttribute(ATTRIBUTE_MOVING, '');
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
from: {...globals.original},
|
|
234
|
+
to: {...position.calculated},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function onResize(entries: ResizeObserverEntry[]): void {
|
|
239
|
+
const {length} = entries;
|
|
240
|
+
|
|
241
|
+
for (let index = 0; index < length; index += 1) {
|
|
242
|
+
const entry = entries[index];
|
|
243
|
+
const set = CONTAINERS.get(entry.target as HTMLElement);
|
|
244
|
+
|
|
245
|
+
if (set == null) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const movable of set) {
|
|
250
|
+
reposition(movable);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function onScroll(): void {
|
|
256
|
+
for (const moved of MOVED) {
|
|
257
|
+
reposition(moved);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function reposition(movable: OuiMovable): void {
|
|
262
|
+
const state = getDraggableStates(movable.element)?.movable as OuiDraggableState & OuiMovableState;
|
|
263
|
+
|
|
264
|
+
if (state == null || !state.moved || state.container == null) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const container = state.container.getBoundingClientRect();
|
|
269
|
+
|
|
270
|
+
const x = container.left + (state.offset?.x ?? 0);
|
|
271
|
+
const y = container.top + (state.offset?.y ?? 0);
|
|
272
|
+
|
|
273
|
+
state.element.style.inset = `${y}px auto auto ${x}px`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function setOffset(state: OuiDraggableState & OuiMovableState, container: DOMRect): void {
|
|
277
|
+
state.offset = {};
|
|
278
|
+
|
|
279
|
+
const element = state.element.getBoundingClientRect();
|
|
280
|
+
|
|
281
|
+
state.offset.y = element.top - container.top;
|
|
282
|
+
state.offset.x = element.left - container.left;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// #endregion
|
|
286
|
+
|
|
287
|
+
// #region Variables
|
|
288
|
+
|
|
289
|
+
export const ATTRIBUTE_MOVABLE = 'oui-movable';
|
|
290
|
+
|
|
291
|
+
const ATTRIBUTE_CONTAINER = `${ATTRIBUTE_MOVABLE}-container`;
|
|
292
|
+
|
|
293
|
+
const ATTRIBUTE_DIRECTION = `${ATTRIBUTE_MOVABLE}-direction`;
|
|
294
|
+
|
|
295
|
+
export const ATTRIBUTE_MOVABLE_HANDLE = `${ATTRIBUTE_MOVABLE}-handle`;
|
|
296
|
+
|
|
297
|
+
const ATTRIBUTE_MOVING = `${ATTRIBUTE_MOVABLE}-moving`;
|
|
298
|
+
|
|
299
|
+
const CONTAINERS = new Map<HTMLElement, Set<OuiMovable>>();
|
|
300
|
+
|
|
301
|
+
const EVENT_END = 'move:end';
|
|
302
|
+
|
|
303
|
+
const MESSAGE = 'The element must be an instance of HTMLElement or SVGElement';
|
|
304
|
+
|
|
305
|
+
const MOVED = new Set<OuiMovable>();
|
|
306
|
+
|
|
307
|
+
const SELECTOR_HANDLE = `[${ATTRIBUTE_MOVABLE_HANDLE}]`;
|
|
308
|
+
|
|
309
|
+
const bound: OuiDraggableBound = {
|
|
310
|
+
constructor: OuiMovable,
|
|
311
|
+
property: 'movable',
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const resizer = new ResizeObserver(onResize);
|
|
315
|
+
|
|
316
|
+
const addMovable = addDraggable.bind(bound);
|
|
317
|
+
|
|
318
|
+
const removeMovable = removeDraggable.bind(bound);
|
|
319
|
+
|
|
320
|
+
// #endregion
|
|
321
|
+
|
|
322
|
+
// #region Initialization
|
|
323
|
+
|
|
324
|
+
on(document, 'scroll', onScroll);
|
|
325
|
+
|
|
326
|
+
attributable(ATTRIBUTE_MOVABLE, addMovable, removeMovable);
|
|
327
|
+
|
|
328
|
+
// #endregion
|
package/src/js/popover.ts
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
getOnBeforeToggleListener,
|
|
8
8
|
type OuiFloatable,
|
|
9
9
|
type OuiFloatableOptions,
|
|
10
|
-
} from './floatable/embedded';
|
|
11
|
-
import {createFocusTrap, type OuiFocusTrap} from './focus-trap/embedded';
|
|
10
|
+
} from './floatable/floatable.embedded';
|
|
11
|
+
import {createFocusTrap, type OuiFocusTrap} from './focus-trap/focus-trap.embedded';
|
|
12
12
|
import {attributable} from './internal/attributable';
|
|
13
13
|
|
|
14
14
|
// #region Types
|
|
@@ -23,6 +23,14 @@ class OuiPopover {
|
|
|
23
23
|
return this.#state.content.checkVisibility();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
set open(value: boolean) {
|
|
27
|
+
if (value === true) {
|
|
28
|
+
this.show();
|
|
29
|
+
} else if (value === false) {
|
|
30
|
+
this.hide();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
constructor(state: OuiPopoverState) {
|
|
27
35
|
this.#state = state;
|
|
28
36
|
|
|
@@ -169,6 +177,7 @@ const TRUE = 'true';
|
|
|
169
177
|
const options: OuiFloatableOptions = {
|
|
170
178
|
attribute: `${ATTRIBUTE}position`,
|
|
171
179
|
position: 'below-start',
|
|
180
|
+
reusable: false,
|
|
172
181
|
};
|
|
173
182
|
|
|
174
183
|
const states = new WeakMap<HTMLElement, OuiPopoverState>();
|