@oscarpalmer/oui 0.19.0 → 0.21.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/a11y.css +0 -1
- package/dist/css/form.css +0 -1
- package/dist/css/popover.css +22 -0
- package/dist/css/reset.css +8 -8
- package/dist/css/table.css +1 -1
- package/dist/css/tooltip.css +43 -0
- package/dist/js/focus-trap/embedded.d.mts +26 -0
- package/dist/js/focus-trap/{embedded.js → embedded.mjs} +11 -9
- package/dist/js/focus-trap/standalone.d.mts +1 -0
- package/dist/js/focus-trap/{standalone.js → standalone.mjs} +5 -2
- package/dist/js/index.d.mts +4 -0
- package/dist/js/index.mjs +7 -0
- package/dist/js/internal/attributable.d.mts +4 -0
- package/dist/js/{attributable.full.js → internal/attributable.mjs} +8 -1
- package/dist/js/internal/draggable.d.mts +2 -0
- package/dist/js/internal/draggable.mjs +212 -0
- package/dist/js/internal/floatable.d.mts +26 -0
- package/dist/js/{floatable.js → internal/floatable.mjs} +73 -50
- package/dist/js/movable-BEo2LbUI.d.mts +101 -0
- package/dist/js/movable.d.mts +2 -0
- package/dist/js/movable.mjs +152 -0
- package/dist/js/popover.d.mts +20 -0
- package/dist/js/{popover.js → popover.mjs} +10 -8
- package/dist/js/sortable.d.mts +2 -0
- package/dist/js/sortable.mjs +243 -0
- package/dist/js/splitter.d.mts +25 -0
- package/dist/js/{splitter.js → splitter.mjs} +62 -60
- package/dist/js/tooltip.d.mts +4 -0
- package/dist/js/{tooltip.js → tooltip.mjs} +22 -20
- package/package.json +66 -69
- package/src/css/mixins.scss +30 -1
- package/src/css/popover.scss +6 -1
- package/src/css/reset.scss +4 -4
- package/src/css/table.scss +1 -1
- package/src/css/tooltip.scss +55 -1
- package/src/js/focus-trap/embedded.ts +4 -10
- package/src/js/focus-trap/standalone.ts +1 -1
- package/src/js/index.ts +3 -1
- package/src/js/{attributable.ts → internal/attributable.ts} +11 -1
- package/src/js/internal/draggable.ts +515 -0
- package/src/js/{floatable.ts → internal/floatable.ts} +141 -63
- package/src/js/movable.ts +260 -0
- package/src/js/popover.ts +1 -1
- package/src/js/sortable.ts +467 -0
- package/src/js/splitter.ts +3 -9
- package/src/js/tooltip.ts +11 -9
- package/dist/js/attributable.js +0 -36
- package/dist/js/floatable.full.js +0 -398
- package/dist/js/focus-trap/embedded.full.js +0 -408
- package/dist/js/focus-trap/standalone.full.js +0 -447
- package/dist/js/index.js +0 -5
- package/dist/js/oui.full.js +0 -1573
- package/dist/js/popover.full.js +0 -760
- package/dist/js/splitter.full.js +0 -769
- package/dist/js/tooltip.full.js +0 -613
- package/types/attributable.d.ts +0 -1
- package/types/floatable.d.ts +0 -24
- package/types/focus-trap/embedded.d.ts +0 -24
- package/types/focus-trap/standalone.d.ts +0 -1
- package/types/index.d.ts +0 -4
- package/types/popover.d.ts +0 -17
- package/types/splitter.d.ts +0 -23
- package/types/tooltip.d.ts +0 -2
package/dist/css/a11y.css
CHANGED
package/dist/css/form.css
CHANGED
package/dist/css/popover.css
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
|
|
2
2
|
:where([oui-popover-content]) {
|
|
3
3
|
box-shadow: inset 0 1px 0 0 transparent, 0 0 0 1px rgba(0, 0, 0, 0.05), 0 1px 2px -0.5px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 8px 16px -4px rgba(0, 0, 0, 0.05), 0 16px 32px -8px rgba(0, 0, 0, 0.05), 0 32px 64px -16px rgba(0, 0, 0, 0.05), 0 64px 128px -32px rgba(0, 0, 0, 0.05);
|
|
4
|
+
--margin-left: 0;
|
|
5
|
+
--margin-top: 0;
|
|
6
|
+
margin: var(--margin-top, 0) 0 0 var(--margin-left, 0);
|
|
7
|
+
pointer-events: all !important;
|
|
8
|
+
}
|
|
9
|
+
:where([oui-popover-content])[position^=above], :where([oui-popover-content])[position^=top] {
|
|
10
|
+
--margin-top: -0.5rem;
|
|
11
|
+
}
|
|
12
|
+
:where([oui-popover-content])[position^=below], :where([oui-popover-content])[position^=bottom] {
|
|
13
|
+
--margin-top: 0.5rem;
|
|
14
|
+
}
|
|
15
|
+
:where([oui-popover-content])[position^=end], :where([oui-popover-content])[position^=right] {
|
|
16
|
+
--margin-left: 0.5rem;
|
|
17
|
+
}
|
|
18
|
+
:where([oui-popover-content])[position^=left], :where([oui-popover-content])[position^=start] {
|
|
19
|
+
--margin-left: -0.5rem;
|
|
20
|
+
}
|
|
21
|
+
:where([oui-popover-content]) {
|
|
4
22
|
padding: 1rem;
|
|
23
|
+
overscroll-behavior: contain;
|
|
5
24
|
background-color: var(--oui-absolute);
|
|
6
25
|
border-radius: 0.5rem;
|
|
7
26
|
}
|
|
8
27
|
:where([oui-popover-content]):focus {
|
|
9
28
|
outline: none;
|
|
10
29
|
}
|
|
30
|
+
:where([oui-popover-content]) * {
|
|
31
|
+
overscroll-behavior: contain;
|
|
32
|
+
}
|
|
11
33
|
|
|
12
34
|
|
package/dist/css/reset.css
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
-moz-tab-size: 4;
|
|
57
57
|
-o-tab-size: 4;
|
|
58
58
|
tab-size: 4;
|
|
59
|
-
|
|
59
|
+
overflow-wrap: break-word;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
:where(body) {
|
|
@@ -105,9 +105,9 @@ input[type=reset i],
|
|
|
105
105
|
input[type=submit i],
|
|
106
106
|
summary) {
|
|
107
107
|
/** Correct the inability to style clickable types in iOS [HR][RS][SE] */
|
|
108
|
-
-webkit-appearance:
|
|
109
|
-
-moz-appearance:
|
|
110
|
-
appearance:
|
|
108
|
+
-webkit-appearance: auto;
|
|
109
|
+
-moz-appearance: auto;
|
|
110
|
+
appearance: auto;
|
|
111
111
|
/** Ditto */
|
|
112
112
|
cursor: pointer;
|
|
113
113
|
}
|
|
@@ -174,7 +174,7 @@ summary) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
:where(small, sub, sup) {
|
|
177
|
-
font-size: 0.
|
|
177
|
+
font-size: 0.875em;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
:where(sub, sup) {
|
|
@@ -277,9 +277,9 @@ summary) {
|
|
|
277
277
|
|
|
278
278
|
::file-selector-button {
|
|
279
279
|
/** Correct the inability to style upload buttons in iOS and Safari [RS][SE] */
|
|
280
|
-
-webkit-appearance:
|
|
281
|
-
-moz-appearance:
|
|
282
|
-
appearance:
|
|
280
|
+
-webkit-appearance: auto;
|
|
281
|
+
-moz-appearance: auto;
|
|
282
|
+
appearance: auto;
|
|
283
283
|
color: inherit;
|
|
284
284
|
font: inherit;
|
|
285
285
|
line-height: 1.25;
|
package/dist/css/table.css
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
:where(.oui-table_wrapper) {
|
|
7
7
|
max-height: 32em;
|
|
8
8
|
overflow: auto;
|
|
9
|
+
overscroll-behavior: contain;
|
|
9
10
|
border: 1px solid var(--oui-gray-3);
|
|
10
11
|
border-radius: 0.25em;
|
|
11
12
|
position: relative;
|
|
12
|
-
overscroll-behavior-y: none;
|
|
13
13
|
}
|
|
14
14
|
:where(.oui-table caption) {
|
|
15
15
|
height: 2rem;
|
package/dist/css/tooltip.css
CHANGED
|
@@ -27,6 +27,24 @@ var)) {
|
|
|
27
27
|
|
|
28
28
|
:where([oui-tooltip-content]) {
|
|
29
29
|
box-shadow: inset 0 1px 0 0 transparent, 0 0 0 1px rgba(0, 0, 0, 0.05), 0 1px 2px -0.5px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 8px 16px -4px rgba(0, 0, 0, 0.05), 0 16px 32px -8px rgba(0, 0, 0, 0.05), 0 32px 64px -16px rgba(0, 0, 0, 0.05), 0 64px 128px -32px rgba(0, 0, 0, 0.05);
|
|
30
|
+
--margin-left: 0;
|
|
31
|
+
--margin-top: 0;
|
|
32
|
+
margin: var(--margin-top, 0) 0 0 var(--margin-left, 0);
|
|
33
|
+
pointer-events: all !important;
|
|
34
|
+
}
|
|
35
|
+
:where([oui-tooltip-content])[position^=above], :where([oui-tooltip-content])[position^=top] {
|
|
36
|
+
--margin-top: -0.5rem;
|
|
37
|
+
}
|
|
38
|
+
:where([oui-tooltip-content])[position^=below], :where([oui-tooltip-content])[position^=bottom] {
|
|
39
|
+
--margin-top: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
:where([oui-tooltip-content])[position^=end], :where([oui-tooltip-content])[position^=right] {
|
|
42
|
+
--margin-left: 0.5rem;
|
|
43
|
+
}
|
|
44
|
+
:where([oui-tooltip-content])[position^=left], :where([oui-tooltip-content])[position^=start] {
|
|
45
|
+
--margin-left: -0.5rem;
|
|
46
|
+
}
|
|
47
|
+
:where([oui-tooltip-content]) {
|
|
30
48
|
max-width: 48ch;
|
|
31
49
|
padding: 0.75em 1em;
|
|
32
50
|
opacity: 0;
|
|
@@ -36,6 +54,31 @@ var)) {
|
|
|
36
54
|
transition: opacity 0.125s ease-in-out;
|
|
37
55
|
transition-delay: 0s;
|
|
38
56
|
}
|
|
57
|
+
:where([oui-tooltip-content])::after {
|
|
58
|
+
content: "";
|
|
59
|
+
opacity: 0;
|
|
60
|
+
position: absolute;
|
|
61
|
+
}
|
|
62
|
+
:where([oui-tooltip-content])[position^=above]::after, :where([oui-tooltip-content])[position^=below]::after, :where([oui-tooltip-content])[position^=bottom]::after, :where([oui-tooltip-content])[position^=top]::after {
|
|
63
|
+
width: 100%;
|
|
64
|
+
height: 0.5rem;
|
|
65
|
+
}
|
|
66
|
+
:where([oui-tooltip-content])[position^=above]::after, :where([oui-tooltip-content])[position^=top]::after {
|
|
67
|
+
inset: 100% 0 0;
|
|
68
|
+
}
|
|
69
|
+
:where([oui-tooltip-content])[position^=below]::after, :where([oui-tooltip-content])[position^=bottom]::after {
|
|
70
|
+
inset: -5rem 0 0;
|
|
71
|
+
}
|
|
72
|
+
:where([oui-tooltip-content])[position^=end]::after, :where([oui-tooltip-content])[position^=left]::after, :where([oui-tooltip-content])[position^=right]::after, :where([oui-tooltip-content])[position^=start]::after {
|
|
73
|
+
width: 0.5rem;
|
|
74
|
+
height: 100%;
|
|
75
|
+
}
|
|
76
|
+
:where([oui-tooltip-content])[position^=end]::after, :where([oui-tooltip-content])[position^=right]::after {
|
|
77
|
+
inset: 0 0 0 -0.5rem;
|
|
78
|
+
}
|
|
79
|
+
:where([oui-tooltip-content])[position^=left]::after, :where([oui-tooltip-content])[position^=start]::after {
|
|
80
|
+
inset: 0 0 0 100%;
|
|
81
|
+
}
|
|
39
82
|
|
|
40
83
|
:where([oui-tooltip-content][oui-tooltip-content-active]) {
|
|
41
84
|
opacity: 1;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/js/focus-trap/embedded.d.ts
|
|
2
|
+
declare class FocusTrap {
|
|
3
|
+
disabled: boolean;
|
|
4
|
+
connected: boolean;
|
|
5
|
+
element: HTMLElement;
|
|
6
|
+
embedded: boolean;
|
|
7
|
+
get contain(): boolean;
|
|
8
|
+
get noescape(): boolean;
|
|
9
|
+
constructor(element: HTMLElement, options?: Options);
|
|
10
|
+
connect(): void;
|
|
11
|
+
destroy(): void;
|
|
12
|
+
disable(): void;
|
|
13
|
+
disconnect(): void;
|
|
14
|
+
enable(): void;
|
|
15
|
+
focus(last: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
type Options = {
|
|
18
|
+
noescape: boolean;
|
|
19
|
+
};
|
|
20
|
+
declare function createFocusTrap(element: HTMLElement, options?: Options): FocusTrap;
|
|
21
|
+
declare const FOCUS_TRAP_SELECTOR = "oui-focus-trap";
|
|
22
|
+
declare const FOCUS_TRAP_CONTAIN = "oui-focus-trap-contain";
|
|
23
|
+
declare const FOCUS_TRAP_NOESCAPE = "oui-focus-trap-noescape";
|
|
24
|
+
declare const FOCUSTRAPS_ALL: WeakMap<HTMLElement, FocusTrap>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { FOCUSTRAPS_ALL, FOCUS_TRAP_CONTAIN, FOCUS_TRAP_NOESCAPE, FOCUS_TRAP_SELECTOR, FocusTrap, createFocusTrap };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { clamp } from "@oscarpalmer/atoms/number";
|
|
1
2
|
import { on } from "@oscarpalmer/toretto/event";
|
|
2
3
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
3
|
-
import { clamp } from "@oscarpalmer/atoms/number";
|
|
4
4
|
import { getFocusable } from "@oscarpalmer/toretto/focusable";
|
|
5
|
+
//#region src/js/focus-trap/embedded.ts
|
|
5
6
|
var FocusTrap = class {
|
|
6
7
|
disabled = false;
|
|
7
8
|
connected = true;
|
|
@@ -48,7 +49,7 @@ function createFocusTrap(element, options) {
|
|
|
48
49
|
return focusTrap;
|
|
49
50
|
}
|
|
50
51
|
function onEscape(event) {
|
|
51
|
-
let element = findAncestor(event
|
|
52
|
+
let element = findAncestor(event, ATTRIBUTE_SELECTOR);
|
|
52
53
|
let focusTrap = FOCUSTRAPS_ALL.get(element);
|
|
53
54
|
if (element == null || focusTrap == null) return;
|
|
54
55
|
while (focusTrap.disabled) {
|
|
@@ -60,7 +61,7 @@ function onEscape(event) {
|
|
|
60
61
|
focusTrap?.disable();
|
|
61
62
|
}
|
|
62
63
|
function onFocusIn(event) {
|
|
63
|
-
lastTarget = FOCUSTRAPS_ALL.get(findAncestor(event
|
|
64
|
+
lastTarget = FOCUSTRAPS_ALL.get(findAncestor(event, ATTRIBUTE_SELECTOR)) == null ? void 0 : event.target;
|
|
64
65
|
}
|
|
65
66
|
function onKeydown(event) {
|
|
66
67
|
switch (event.key) {
|
|
@@ -75,7 +76,7 @@ function onKeydown(event) {
|
|
|
75
76
|
}
|
|
76
77
|
function onPointerdown(event) {
|
|
77
78
|
const lastFocusTrap = findAncestor(lastTarget, ATTRIBUTE_SELECTOR);
|
|
78
|
-
const nextFocusTrap = findAncestor(event
|
|
79
|
+
const nextFocusTrap = findAncestor(event, ATTRIBUTE_SELECTOR);
|
|
79
80
|
if (lastFocusTrap != null && nextFocusTrap !== lastFocusTrap) {
|
|
80
81
|
if (FOCUSTRAPS_ALL.get(lastFocusTrap)?.contain ?? false) {
|
|
81
82
|
event.preventDefault();
|
|
@@ -86,7 +87,7 @@ function onPointerdown(event) {
|
|
|
86
87
|
for (const focusTrap of FOCUSTRAPS_DISABLED) if (focusTrap.element !== nextFocusTrap) focusTrap.enable();
|
|
87
88
|
}
|
|
88
89
|
function onTab(event) {
|
|
89
|
-
const element = findAncestor(event
|
|
90
|
+
const element = findAncestor(event, ATTRIBUTE_SELECTOR);
|
|
90
91
|
const focusTrap = FOCUSTRAPS_ALL.get(element);
|
|
91
92
|
if (focusTrap == null) return;
|
|
92
93
|
if (!focusTrap.disabled) event.preventDefault();
|
|
@@ -133,18 +134,19 @@ function tabToTrap(focusTrap, elements, element, index) {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
const FOCUS_TRAP_SELECTOR = "oui-focus-trap";
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
const ATTRIBUTE_SELECTOR = `[${FOCUS_TRAP_SELECTOR}]`;
|
|
138
|
+
const EVENT_OPTIONS = {
|
|
138
139
|
capture: true,
|
|
139
140
|
passive: false
|
|
140
141
|
};
|
|
141
142
|
const FOCUS_TRAP_CONTAIN = `${FOCUS_TRAP_SELECTOR}-contain`;
|
|
142
143
|
const FOCUS_TRAP_NOESCAPE = `${FOCUS_TRAP_SELECTOR}-noescape`;
|
|
143
144
|
const FOCUSTRAPS_ALL = /* @__PURE__ */ new WeakMap();
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
const FOCUSTRAPS_DISABLED = /* @__PURE__ */ new Set();
|
|
146
|
+
let lastTarget;
|
|
146
147
|
on(document, "focusin", onFocusIn, EVENT_OPTIONS);
|
|
147
148
|
on(document, "keydown", onKeydown, EVENT_OPTIONS);
|
|
148
149
|
on(document, "mousedown", onPointerdown, EVENT_OPTIONS);
|
|
149
150
|
on(document, "touchstart", onPointerdown, EVENT_OPTIONS);
|
|
151
|
+
//#endregion
|
|
150
152
|
export { FOCUSTRAPS_ALL, FOCUS_TRAP_CONTAIN, FOCUS_TRAP_NOESCAPE, FOCUS_TRAP_SELECTOR, FocusTrap, createFocusTrap };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { attributable } from "../attributable.
|
|
2
|
-
import { FOCUSTRAPS_ALL, FOCUS_TRAP_SELECTOR, createFocusTrap } from "./embedded.
|
|
1
|
+
import { attributable } from "../internal/attributable.mjs";
|
|
2
|
+
import { FOCUSTRAPS_ALL, FOCUS_TRAP_SELECTOR, createFocusTrap } from "./embedded.mjs";
|
|
3
|
+
//#region src/js/focus-trap/standalone.ts
|
|
3
4
|
function removeFocusTraps(element) {
|
|
4
5
|
FOCUSTRAPS_ALL.get(element)?.destroy();
|
|
5
6
|
FOCUSTRAPS_ALL.delete(element);
|
|
6
7
|
}
|
|
7
8
|
attributable(FOCUS_TRAP_SELECTOR, createFocusTrap, removeFocusTraps);
|
|
9
|
+
//#endregion
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./focus-trap/standalone.mjs";
|
|
2
|
+
import "./movable.mjs";
|
|
3
|
+
import { OuiPopoverElement } from "./popover.mjs";
|
|
4
|
+
import "./sortable.mjs";
|
|
5
|
+
import { OuiSplitterElement } from "./splitter.mjs";
|
|
6
|
+
import { setTooltipDelay } from "./tooltip.mjs";
|
|
7
|
+
export { OuiPopoverElement, OuiSplitterElement, setTooltipDelay };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/js/internal/attributable.ts
|
|
1
2
|
function attributable(attribute, add, remove) {
|
|
2
3
|
new MutationObserver((entries) => {
|
|
3
4
|
const { length } = entries;
|
|
@@ -26,11 +27,17 @@ function handleAdded(attribute, nodes, add) {
|
|
|
26
27
|
for (let index = 0; index < length; index += 1) {
|
|
27
28
|
const element = elements[index];
|
|
28
29
|
if (element.hasAttribute(attribute)) add(element);
|
|
30
|
+
if (element.hasChildNodes()) handleAdded(attribute, element.childNodes, add);
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
function handleRemoved(nodes, remove) {
|
|
32
34
|
const elements = [...nodes].filter((node) => node instanceof HTMLElement);
|
|
33
35
|
const { length } = elements;
|
|
34
|
-
for (let index = 0; index < length; index += 1)
|
|
36
|
+
for (let index = 0; index < length; index += 1) {
|
|
37
|
+
const element = elements[index];
|
|
38
|
+
remove(element);
|
|
39
|
+
if (element.hasChildNodes()) handleRemoved(element.childNodes, remove);
|
|
40
|
+
}
|
|
35
41
|
}
|
|
42
|
+
//#endregion
|
|
36
43
|
export { attributable };
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { clamp } from "@oscarpalmer/atoms/number";
|
|
2
|
+
import { getPosition, on } from "@oscarpalmer/toretto/event";
|
|
3
|
+
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
4
|
+
import { toggleStyles } from "@oscarpalmer/toretto/style";
|
|
5
|
+
import supportsTouch from "@oscarpalmer/toretto/touch";
|
|
6
|
+
//#region src/js/internal/draggable.ts
|
|
7
|
+
var Draggable = class {
|
|
8
|
+
container;
|
|
9
|
+
hasHandles;
|
|
10
|
+
horizontal = true;
|
|
11
|
+
vertical = true;
|
|
12
|
+
constructor(prefix, element, options) {
|
|
13
|
+
this.prefix = prefix;
|
|
14
|
+
this.element = element;
|
|
15
|
+
this.options = options;
|
|
16
|
+
setContainer(this, options);
|
|
17
|
+
setDirection(this, options);
|
|
18
|
+
}
|
|
19
|
+
destroy() {
|
|
20
|
+
unsetContainer(this, this.options);
|
|
21
|
+
this.container = void 0;
|
|
22
|
+
this.element = void 0;
|
|
23
|
+
this.options = void 0;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function addDraggable(element) {
|
|
27
|
+
let instances = state.instances.get(element);
|
|
28
|
+
if (instances == null) {
|
|
29
|
+
instances = {};
|
|
30
|
+
state.instances.set(element, instances);
|
|
31
|
+
}
|
|
32
|
+
instances[this.property] ??= new this.constructor(element);
|
|
33
|
+
}
|
|
34
|
+
function getHandle(event, isMovable) {
|
|
35
|
+
return findAncestor(event, isMovable ? SELECTOR_MOVABLE_HANDLE : SELECTOR_SORTABLE_HANDLE);
|
|
36
|
+
}
|
|
37
|
+
function getNextPosition(draggable, position) {
|
|
38
|
+
let x = state.original?.x ?? 0;
|
|
39
|
+
let y = state.original?.y ?? 0;
|
|
40
|
+
if (draggable.horizontal) {
|
|
41
|
+
x = position.x - (state.offset?.x ?? 0);
|
|
42
|
+
if (state.container != null && state.element != null) x = clamp(x, state.container.left, state.container.right - state.element.rectangle.width);
|
|
43
|
+
}
|
|
44
|
+
if (draggable.vertical) {
|
|
45
|
+
y = position.y - (state.offset?.y ?? 0);
|
|
46
|
+
if (state.container != null && state.element != null) y = clamp(y, state.container.top, state.container.bottom - state.element.rectangle.height);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
x,
|
|
50
|
+
y
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function onKeydown(event) {
|
|
54
|
+
if (current == null || event.key !== KEY_ESCAPE) return;
|
|
55
|
+
const { origin } = current;
|
|
56
|
+
current.options.drag.onCancel?.(state, current);
|
|
57
|
+
current.element.dispatchEvent(new CustomEvent(`${current.prefix}:cancel`, { detail: { element: origin } }));
|
|
58
|
+
reset();
|
|
59
|
+
}
|
|
60
|
+
function onPointerdown(event) {
|
|
61
|
+
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || (supportsTouch.value ? event.type === "mousedown" : event.type === "touchstart" || event.button !== 0)) return;
|
|
62
|
+
const container = findAncestor(event, SELECTOR);
|
|
63
|
+
const instances = state.instances.get(container);
|
|
64
|
+
if (instances == null) return;
|
|
65
|
+
const isMovable = container.hasAttribute(ATTRIBUTE_MOVABLE);
|
|
66
|
+
const instance = isMovable ? instances.movable : instances.sortable;
|
|
67
|
+
if (instance == null) return;
|
|
68
|
+
if (supportsTouch.value) event.preventDefault();
|
|
69
|
+
let element = container;
|
|
70
|
+
if (!isMovable) {
|
|
71
|
+
element = findAncestor(event, SELECTOR_SORTABLE_ITEM);
|
|
72
|
+
if (element == null || element.parentElement !== container) return;
|
|
73
|
+
}
|
|
74
|
+
let handle = element;
|
|
75
|
+
instance.hasHandles ??= element.querySelector(SELECTOR_SORTABLE_HANDLE) != null;
|
|
76
|
+
if (instance.hasHandles) handle = getHandle(event, isMovable);
|
|
77
|
+
if (handle == null) return;
|
|
78
|
+
const { dragged } = instance;
|
|
79
|
+
const rectangle = dragged.getBoundingClientRect();
|
|
80
|
+
const position = getPosition(event) ?? {
|
|
81
|
+
x: 0,
|
|
82
|
+
y: 0
|
|
83
|
+
};
|
|
84
|
+
current = instance;
|
|
85
|
+
state.container = instance.container?.getBoundingClientRect();
|
|
86
|
+
state.element = {
|
|
87
|
+
rectangle,
|
|
88
|
+
node: dragged
|
|
89
|
+
};
|
|
90
|
+
state.offset = instance.options.position.getOffset(state, instance, element, position);
|
|
91
|
+
state.original = instance.options.position.getOriginal(state, instance, position);
|
|
92
|
+
const begin = new CustomEvent(`${instance.prefix}:begin`, {
|
|
93
|
+
detail: {
|
|
94
|
+
element,
|
|
95
|
+
position: { ...state.original }
|
|
96
|
+
},
|
|
97
|
+
cancelable: true
|
|
98
|
+
});
|
|
99
|
+
instance.element.dispatchEvent(begin);
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
if (begin.defaultPrevented) {
|
|
102
|
+
current?.options.drag.onCancel(state, instance);
|
|
103
|
+
reset();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const next = getNextPosition(instance, position);
|
|
107
|
+
dragged.style.position = "fixed";
|
|
108
|
+
dragged.style.inset = `${next.y}px auto auto ${next.x}px`;
|
|
109
|
+
instance.options.drag.onBegin(event, state, instance, element, handle, next);
|
|
110
|
+
styleToggler.set();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function onPointermove(event) {
|
|
114
|
+
if (current == null) return;
|
|
115
|
+
const position = getPosition(event);
|
|
116
|
+
if (position == null) return;
|
|
117
|
+
const next = getNextPosition(current, position);
|
|
118
|
+
if (state.element != null) state.element.node.style.inset = `${next.y}px auto auto ${next.x}px`;
|
|
119
|
+
const detail = current.options.drag.onMove(event, state, current, {
|
|
120
|
+
calculated: next,
|
|
121
|
+
original: position
|
|
122
|
+
});
|
|
123
|
+
current.element.dispatchEvent(new CustomEvent(`${current.prefix}:move`, { detail }));
|
|
124
|
+
}
|
|
125
|
+
function onPointerup(event) {
|
|
126
|
+
current?.options.drag.onEnd?.(event, state, current, reset);
|
|
127
|
+
}
|
|
128
|
+
function removeDraggable(element) {
|
|
129
|
+
const { property } = this;
|
|
130
|
+
const instances = state.instances.get(element);
|
|
131
|
+
if (instances == null || instances[property] == null) return;
|
|
132
|
+
instances[property]?.destroy();
|
|
133
|
+
instances[property] = void 0;
|
|
134
|
+
if (instances[properties[property]] == null) state.instances.delete(element);
|
|
135
|
+
}
|
|
136
|
+
function reset() {
|
|
137
|
+
current = void 0;
|
|
138
|
+
state.container = void 0;
|
|
139
|
+
state.element = void 0;
|
|
140
|
+
state.offset = void 0;
|
|
141
|
+
state.original = void 0;
|
|
142
|
+
styleToggler.remove();
|
|
143
|
+
}
|
|
144
|
+
function setContainer(draggable, options) {
|
|
145
|
+
const selector = draggable.element.getAttribute(options.container.attribute);
|
|
146
|
+
if (typeof selector !== "string") return;
|
|
147
|
+
draggable.container = globals.has(selector) ? draggable.element.ownerDocument.body : document.querySelector(selector) ?? void 0;
|
|
148
|
+
if (draggable.container == null) return;
|
|
149
|
+
const set = options.container.map.get(draggable.container);
|
|
150
|
+
if (set == null) options.container.map.set(draggable.container, new Set([draggable]));
|
|
151
|
+
else set.add(draggable);
|
|
152
|
+
options.container.onInitialize?.(draggable);
|
|
153
|
+
}
|
|
154
|
+
function setDirection(draggable, options) {
|
|
155
|
+
const direction = draggable.element.getAttribute(options.direction.attribute);
|
|
156
|
+
if (horizontals.has(direction)) draggable.vertical = false;
|
|
157
|
+
else if (verticals.has(direction)) draggable.horizontal = false;
|
|
158
|
+
options.direction.onAfter?.(draggable);
|
|
159
|
+
}
|
|
160
|
+
function unsetContainer(draggable, options) {
|
|
161
|
+
const { container } = draggable;
|
|
162
|
+
if (container == null) return;
|
|
163
|
+
const set = options.container.map.get(container);
|
|
164
|
+
let empty = false;
|
|
165
|
+
if (set != null) {
|
|
166
|
+
set.delete(draggable);
|
|
167
|
+
if (set.size === 0) {
|
|
168
|
+
empty = true;
|
|
169
|
+
options.container.map.delete(container);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
options.container.onDestroy?.(draggable, container, empty);
|
|
173
|
+
}
|
|
174
|
+
const ATTRIBUTE_MOVABLE = "oui-movable";
|
|
175
|
+
const ATTRIBUTE_SORTABLE = "oui-sortable";
|
|
176
|
+
const KEY_ESCAPE = "Escape";
|
|
177
|
+
const SELECTOR = `[${ATTRIBUTE_MOVABLE}], [${ATTRIBUTE_SORTABLE}]`;
|
|
178
|
+
const SELECTOR_MOVABLE_HANDLE = `[${ATTRIBUTE_MOVABLE}-handle]`;
|
|
179
|
+
const SELECTOR_SORTABLE_HANDLE = `[${ATTRIBUTE_SORTABLE}-handle]`;
|
|
180
|
+
const SELECTOR_SORTABLE_ITEM = `[${ATTRIBUTE_SORTABLE}] > *`;
|
|
181
|
+
const globals = new Set([
|
|
182
|
+
"body",
|
|
183
|
+
"document",
|
|
184
|
+
"window"
|
|
185
|
+
]);
|
|
186
|
+
const horizontals = new Set(["horizontal", "x"]);
|
|
187
|
+
const properties = {
|
|
188
|
+
movable: "sortable",
|
|
189
|
+
sortable: "movable"
|
|
190
|
+
};
|
|
191
|
+
const state = {
|
|
192
|
+
container: void 0,
|
|
193
|
+
element: void 0,
|
|
194
|
+
instances: /* @__PURE__ */ new WeakMap(),
|
|
195
|
+
offset: void 0,
|
|
196
|
+
original: void 0
|
|
197
|
+
};
|
|
198
|
+
const styleToggler = toggleStyles(document.body, {
|
|
199
|
+
touchAction: "none",
|
|
200
|
+
userSelect: "none",
|
|
201
|
+
webkitUserSelect: "none"
|
|
202
|
+
});
|
|
203
|
+
const verticals = new Set(["vertical", "y"]);
|
|
204
|
+
let current;
|
|
205
|
+
on(document, "keydown", onKeydown);
|
|
206
|
+
on(document, "mousedown", onPointerdown);
|
|
207
|
+
on(document, "pointermove", onPointermove);
|
|
208
|
+
on(document, "pointerup", onPointerup);
|
|
209
|
+
on(document, "touchcancel", onPointerup, { passive: false });
|
|
210
|
+
on(document, "touchstart", onPointerdown, { passive: false });
|
|
211
|
+
//#endregion
|
|
212
|
+
export { Draggable, addDraggable, removeDraggable };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/js/internal/floatable.d.ts
|
|
2
|
+
declare class Floatable {
|
|
3
|
+
readonly options: Options;
|
|
4
|
+
active: boolean;
|
|
5
|
+
disabled: boolean;
|
|
6
|
+
frame: DOMHighResTimeStamp | undefined;
|
|
7
|
+
ignoreFocus: boolean;
|
|
8
|
+
constructor(options: Options);
|
|
9
|
+
destroy(): void;
|
|
10
|
+
disable(): void;
|
|
11
|
+
enable(): void;
|
|
12
|
+
toggle(active: boolean, ignoreFocus?: boolean): void;
|
|
13
|
+
}
|
|
14
|
+
type Options = {
|
|
15
|
+
anchor: HTMLElement;
|
|
16
|
+
content: HTMLElement;
|
|
17
|
+
defaultPosition: Position;
|
|
18
|
+
interactive: boolean;
|
|
19
|
+
positionAttribute: string;
|
|
20
|
+
preferAbove: boolean;
|
|
21
|
+
onAfter?: (active: boolean) => void;
|
|
22
|
+
};
|
|
23
|
+
type Position = 'above' | 'above-end' | 'above-start' | 'below' | 'below-end' | 'below-start' | 'end' | 'end-bottom' | 'end-top' | 'horizontal' | 'horizontal-bottom' | 'horizontal-top' | 'start' | 'start-bottom' | 'start-top' | 'vertical' | 'vertical-end' | 'vertical-start';
|
|
24
|
+
declare function deactivate(floatable: Floatable): void;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Floatable, deactivate };
|