@oscarpalmer/oui 0.20.0 → 0.22.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/popover.css +9 -9
- package/dist/css/tooltip.css +20 -20
- package/dist/js/focus-trap/embedded.mjs +1 -1
- package/dist/js/focus-trap/standalone.mjs +1 -1
- package/dist/js/index.d.mts +47 -4
- package/dist/js/index.mjs +2537 -5
- package/dist/js/{attributable.d.mts → internal/attributable.d.mts} +1 -1
- package/dist/js/{attributable.mjs → internal/attributable.mjs} +1 -1
- package/dist/js/internal/draggable.d.mts +2 -0
- package/dist/js/internal/draggable.mjs +215 -0
- package/dist/js/{floatable.d.mts → internal/floatable.d.mts} +3 -3
- package/dist/js/internal/floatable.mjs +187 -0
- package/dist/js/movable-BKFgoaOl.d.mts +101 -0
- package/dist/js/movable.d.mts +2 -0
- package/dist/js/movable.mjs +152 -0
- package/dist/js/popover.mjs +2 -2
- package/dist/js/sortable.d.mts +2 -0
- package/dist/js/sortable.mjs +243 -0
- package/dist/js/splitter.mjs +4 -4
- package/dist/js/tooltip.d.mts +2 -2
- package/dist/js/tooltip.mjs +98 -100
- package/package.json +28 -18
- package/src/css/mixins.scss +8 -12
- package/src/css/popover.scss +1 -1
- package/src/css/tooltip.scss +22 -22
- package/src/js/focus-trap/standalone.ts +1 -1
- package/src/js/index.ts +3 -1
- package/src/js/internal/draggable.ts +515 -0
- package/src/js/internal/floatable.ts +373 -0
- package/src/js/movable.ts +260 -0
- package/src/js/popover.ts +2 -2
- package/src/js/sortable.ts +467 -0
- package/src/js/splitter.ts +1 -4
- package/src/js/tooltip.ts +132 -132
- package/dist/js/floatable.mjs +0 -284
- package/src/js/floatable.ts +0 -590
- /package/src/js/{attributable.ts → internal/attributable.ts} +0 -0
package/dist/js/popover.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Floatable } from "./floatable.mjs";
|
|
2
1
|
import { createFocusTrap } from "./focus-trap/embedded.mjs";
|
|
2
|
+
import { Floatable } from "./internal/floatable.mjs";
|
|
3
3
|
import { getFocusable } from "@oscarpalmer/toretto/focusable";
|
|
4
4
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
5
5
|
//#region src/js/popover.ts
|
|
@@ -25,7 +25,7 @@ var OuiPopoverElement = class extends HTMLElement {
|
|
|
25
25
|
defaultPosition: "below-start",
|
|
26
26
|
interactive: true,
|
|
27
27
|
positionAttribute: "position",
|
|
28
|
-
|
|
28
|
+
reusable: false,
|
|
29
29
|
onAfter: (active) => {
|
|
30
30
|
if (active) focus(content);
|
|
31
31
|
else if (!this.#floatable.ignoreFocus) toggle.focus();
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { attributable } from "./internal/attributable.mjs";
|
|
2
|
+
import { Draggable, addDraggable, removeDraggable } from "./internal/draggable.mjs";
|
|
3
|
+
import { getPosition } from "@oscarpalmer/toretto/event";
|
|
4
|
+
import { findAncestor, getElementFromPosition } from "@oscarpalmer/toretto/find";
|
|
5
|
+
import { createElement } from "@oscarpalmer/toretto/create";
|
|
6
|
+
//#region src/js/sortable.ts
|
|
7
|
+
var Sortable = class extends Draggable {
|
|
8
|
+
connections;
|
|
9
|
+
origin;
|
|
10
|
+
get dragged() {
|
|
11
|
+
return getDragPlaceholder();
|
|
12
|
+
}
|
|
13
|
+
constructor(element) {
|
|
14
|
+
super("sort", element, {
|
|
15
|
+
container: {
|
|
16
|
+
attribute: ATTRIBUTE_CONTAINER,
|
|
17
|
+
map: CONTAINERS
|
|
18
|
+
},
|
|
19
|
+
direction: { attribute: ATTRIBUTE_DIRECTION },
|
|
20
|
+
drag: {
|
|
21
|
+
onBegin,
|
|
22
|
+
onCancel,
|
|
23
|
+
onEnd,
|
|
24
|
+
onMove
|
|
25
|
+
},
|
|
26
|
+
position: {
|
|
27
|
+
getOffset: getOffsetPosition,
|
|
28
|
+
getOriginal: getOriginalPosition
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
this.connections = this.element.getAttribute(ATTRIBUTE_CONNECTIONS);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function afterEnd(insert, from, events, elements, position, valid) {
|
|
35
|
+
elements.origin?.removeAttribute(ATTRIBUTE_ORIGIN);
|
|
36
|
+
from.origin = void 0;
|
|
37
|
+
resetPlaceholders();
|
|
38
|
+
if (!valid || events.some((event) => event.defaultPrevented)) return;
|
|
39
|
+
if (insert !== "insert") {
|
|
40
|
+
elements.target.append(elements.origin);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (position == null) return;
|
|
44
|
+
const { height, top } = elements.target.getBoundingClientRect();
|
|
45
|
+
let insertion;
|
|
46
|
+
if (position.y < top + height / 2) insertion = INSERT_BEFORE;
|
|
47
|
+
else insertion = INSERT_AFTER;
|
|
48
|
+
elements.target?.insertAdjacentElement(insertion, elements.origin);
|
|
49
|
+
document.body.removeAttribute(ATTRIBUTE_ACTIVE);
|
|
50
|
+
}
|
|
51
|
+
function getDragPlaceholder() {
|
|
52
|
+
placeholder.drag.default ??= createElement("div");
|
|
53
|
+
return placeholder.drag.custom ?? placeholder.drag.default;
|
|
54
|
+
}
|
|
55
|
+
function getOffsetPosition(_, draggable, element, position) {
|
|
56
|
+
const rectangle = element.getBoundingClientRect();
|
|
57
|
+
return {
|
|
58
|
+
x: draggable.horizontal ? 0 : position.x - rectangle.left,
|
|
59
|
+
y: draggable.vertical ? 0 : position.y - rectangle.top
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function getOriginalPosition(_, __, position) {
|
|
63
|
+
return position;
|
|
64
|
+
}
|
|
65
|
+
function getPositionPlaceholder(sortable) {
|
|
66
|
+
placeholder.position ??= createElement("div");
|
|
67
|
+
placeholder.position.setAttribute(ATTRIBUTE_PLACEHOLDER_POSITION, "");
|
|
68
|
+
if (sortable.origin != null) placeholder.position.style.setProperty("--placeholder-height", `${sortable.origin.getBoundingClientRect().height}px`);
|
|
69
|
+
return placeholder.position;
|
|
70
|
+
}
|
|
71
|
+
function onBegin(_, state, draggable, element) {
|
|
72
|
+
const sortable = draggable;
|
|
73
|
+
sortable.origin = element;
|
|
74
|
+
setPlaceholder(state, sortable, element);
|
|
75
|
+
element.setAttribute(ATTRIBUTE_ORIGIN, "");
|
|
76
|
+
document.body.setAttribute(ATTRIBUTE_ACTIVE, "");
|
|
77
|
+
}
|
|
78
|
+
function onCancel(_, draggable) {
|
|
79
|
+
const sortable = draggable;
|
|
80
|
+
const { origin } = sortable;
|
|
81
|
+
origin?.removeAttribute(ATTRIBUTE_ORIGIN);
|
|
82
|
+
document.body.removeAttribute(ATTRIBUTE_ACTIVE);
|
|
83
|
+
resetPlaceholders();
|
|
84
|
+
sortable.origin = void 0;
|
|
85
|
+
}
|
|
86
|
+
function onEnd(event, state, draggable, reset) {
|
|
87
|
+
const from = draggable;
|
|
88
|
+
const { origin } = from;
|
|
89
|
+
const position = getPosition(event) ?? {
|
|
90
|
+
x: 0,
|
|
91
|
+
y: 0
|
|
92
|
+
};
|
|
93
|
+
const target = getElementFromPosition(position)[0];
|
|
94
|
+
let element = findAncestor(target, SELECTOR_ITEM);
|
|
95
|
+
if (element?.hasAttribute(ATTRIBUTE_PLACEHOLDER_POSITION)) if (element.getAttribute(ATTRIBUTE_PLACEHOLDER_POSITION) === "after") element = element.previousElementSibling;
|
|
96
|
+
else element = element.nextElementSibling;
|
|
97
|
+
let insert = "insert";
|
|
98
|
+
let to;
|
|
99
|
+
if (element == null) {
|
|
100
|
+
element = findAncestor(target, SELECTOR);
|
|
101
|
+
to = state.instances.get(element)?.sortable;
|
|
102
|
+
insert = element == null ? void 0 : element.children.length === 0 ? "append" : "prepend";
|
|
103
|
+
} else to = state.instances.get(element.parentElement)?.sortable;
|
|
104
|
+
const valid = insert != null && validatePosition(state, from, to, position);
|
|
105
|
+
const options = {
|
|
106
|
+
detail: valid ? {
|
|
107
|
+
from: {
|
|
108
|
+
element: origin,
|
|
109
|
+
position: { ...state.original }
|
|
110
|
+
},
|
|
111
|
+
to: {
|
|
112
|
+
element,
|
|
113
|
+
position: { ...position }
|
|
114
|
+
}
|
|
115
|
+
} : void 0,
|
|
116
|
+
bubbles: false,
|
|
117
|
+
cancelable: true
|
|
118
|
+
};
|
|
119
|
+
const fromEvent = new CustomEvent(EVENT_END, options);
|
|
120
|
+
const toEvent = new CustomEvent(EVENT_END, options);
|
|
121
|
+
from.element.dispatchEvent(fromEvent);
|
|
122
|
+
if (from !== to) to?.element.dispatchEvent(toEvent);
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
afterEnd(insert, from, [fromEvent, toEvent], {
|
|
125
|
+
origin,
|
|
126
|
+
target: element
|
|
127
|
+
}, position, valid);
|
|
128
|
+
reset();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function onMove(event, state, draggable, position) {
|
|
132
|
+
const from = draggable;
|
|
133
|
+
const fromElement = from.origin;
|
|
134
|
+
const toElement = findAncestor(event, SELECTOR_ITEM);
|
|
135
|
+
let to;
|
|
136
|
+
if (toElement == null) to = state.instances.get(findAncestor(event, SELECTOR))?.sortable;
|
|
137
|
+
else to = state.instances.get(toElement.parentElement)?.sortable;
|
|
138
|
+
if (!validatePosition(state, from, to, position.original)) return;
|
|
139
|
+
const placeholder = getPositionPlaceholder(from);
|
|
140
|
+
if (toElement != null && toElement !== fromElement) {
|
|
141
|
+
const { height, top } = toElement.getBoundingClientRect();
|
|
142
|
+
let place;
|
|
143
|
+
if (position.calculated.y < top + height / 2) {
|
|
144
|
+
place = "before";
|
|
145
|
+
toElement.insertAdjacentElement(INSERT_BEFORE, placeholder);
|
|
146
|
+
} else {
|
|
147
|
+
place = "after";
|
|
148
|
+
toElement.insertAdjacentElement(INSERT_AFTER, placeholder);
|
|
149
|
+
}
|
|
150
|
+
placeholder.setAttribute(ATTRIBUTE_PLACEHOLDER_POSITION, place);
|
|
151
|
+
} else placeholder.remove();
|
|
152
|
+
return {
|
|
153
|
+
from: {
|
|
154
|
+
element: fromElement,
|
|
155
|
+
position: { ...state.original }
|
|
156
|
+
},
|
|
157
|
+
to: {
|
|
158
|
+
element: toElement,
|
|
159
|
+
position: { ...position.calculated }
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function resetPlaceholders() {
|
|
164
|
+
placeholder.drag.custom?.remove();
|
|
165
|
+
placeholder.drag.default?.remove();
|
|
166
|
+
placeholder.position?.remove();
|
|
167
|
+
placeholder.drag.custom = void 0;
|
|
168
|
+
}
|
|
169
|
+
function setPlaceholder(state, sortable, origin) {
|
|
170
|
+
const element = getDragPlaceholder();
|
|
171
|
+
placeholder.drag.default.innerHTML = "";
|
|
172
|
+
updatePlaceholder(sortable, element);
|
|
173
|
+
const event = new CustomEvent(EVENT_PLACEHOLDER, {
|
|
174
|
+
cancelable: true,
|
|
175
|
+
detail: {
|
|
176
|
+
origin,
|
|
177
|
+
parent: sortable.element,
|
|
178
|
+
placeholder: element,
|
|
179
|
+
create: (value) => {
|
|
180
|
+
if (!(value instanceof HTMLElement)) return;
|
|
181
|
+
placeholder.drag.custom = value;
|
|
182
|
+
updatePlaceholder(sortable, value);
|
|
183
|
+
state.element = {
|
|
184
|
+
rectangle: value.getBoundingClientRect(),
|
|
185
|
+
node: value
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
sortable.element.dispatchEvent(event);
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
if (!event.defaultPrevented && placeholder.drag.custom == null) placeholder.drag.default?.append(element.cloneNode(true));
|
|
193
|
+
document.body.append(placeholder.drag.custom ?? placeholder.drag.default ?? "");
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function updatePlaceholder(sortable, element) {
|
|
197
|
+
element.style.position = "fixed";
|
|
198
|
+
element.setAttribute(ATTRIBUTE_PLACEHOLDER_DRAG, "");
|
|
199
|
+
if (sortable.horizontal) element.setAttribute(ATTRIBUTE_HORIZONTAL, "");
|
|
200
|
+
else element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
201
|
+
if (sortable.vertical) element.setAttribute(ATTRIBUTE_VERTICAL, "");
|
|
202
|
+
else element.removeAttribute(ATTRIBUTE_VERTICAL);
|
|
203
|
+
}
|
|
204
|
+
function validatePosition(_, from, to, position) {
|
|
205
|
+
let valid = true;
|
|
206
|
+
const rectangle = from.origin?.getBoundingClientRect();
|
|
207
|
+
if (rectangle != null && !from.horizontal) valid = position.x >= rectangle.left && position.x <= rectangle.right;
|
|
208
|
+
if (valid && rectangle != null && !from.vertical) valid = position.y >= rectangle.top && position.y <= rectangle.bottom;
|
|
209
|
+
if (valid && from.connections != null) valid = to?.element.matches(from.connections) ?? true;
|
|
210
|
+
if (valid && to?.connections != null) valid = from.element.matches(to.connections);
|
|
211
|
+
return valid;
|
|
212
|
+
}
|
|
213
|
+
const ATTRIBUTE = "oui-sortable";
|
|
214
|
+
const ATTRIBUTE_ACTIVE = `${ATTRIBUTE}-active`;
|
|
215
|
+
const ATTRIBUTE_CONNECTIONS = `${ATTRIBUTE}-connections`;
|
|
216
|
+
const ATTRIBUTE_CONTAINER = `${ATTRIBUTE}-container`;
|
|
217
|
+
const ATTRIBUTE_DIRECTION = `${ATTRIBUTE}-direction`;
|
|
218
|
+
const ATTRIBUTE_HORIZONTAL = `${ATTRIBUTE}-horizontal`;
|
|
219
|
+
const ATTRIBUTE_ORIGIN = `${ATTRIBUTE}-origin`;
|
|
220
|
+
const ATTRIBUTE_PLACEHOLDER_DRAG = `${ATTRIBUTE}-placeholder-drag`;
|
|
221
|
+
const ATTRIBUTE_PLACEHOLDER_POSITION = `${ATTRIBUTE}-placeholder-position`;
|
|
222
|
+
const ATTRIBUTE_VERTICAL = `${ATTRIBUTE}-vertical`;
|
|
223
|
+
const CONTAINERS = /* @__PURE__ */ new Map();
|
|
224
|
+
const EVENT_END = "sort:end";
|
|
225
|
+
const EVENT_PLACEHOLDER = "sort:placeholder";
|
|
226
|
+
const INSERT_AFTER = "afterend";
|
|
227
|
+
const INSERT_BEFORE = "beforebegin";
|
|
228
|
+
const SELECTOR = `[${ATTRIBUTE}]`;
|
|
229
|
+
const SELECTOR_ITEM = `[${ATTRIBUTE}] > *`;
|
|
230
|
+
const bound = {
|
|
231
|
+
constructor: Sortable,
|
|
232
|
+
property: "sortable"
|
|
233
|
+
};
|
|
234
|
+
const placeholder = {
|
|
235
|
+
drag: {
|
|
236
|
+
custom: void 0,
|
|
237
|
+
default: void 0
|
|
238
|
+
},
|
|
239
|
+
position: void 0
|
|
240
|
+
};
|
|
241
|
+
attributable(ATTRIBUTE, addDraggable.bind(bound), removeDraggable.bind(bound));
|
|
242
|
+
//#endregion
|
|
243
|
+
export { Sortable };
|
package/dist/js/splitter.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { clamp } from "@oscarpalmer/atoms/number";
|
|
1
2
|
import { getPosition, on } from "@oscarpalmer/toretto/event";
|
|
2
3
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
3
|
-
import { clamp } from "@oscarpalmer/atoms/number";
|
|
4
|
-
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
5
|
-
import { round } from "@oscarpalmer/atoms/math";
|
|
6
4
|
import { toggleStyles } from "@oscarpalmer/toretto/style";
|
|
7
5
|
import supportsTouch from "@oscarpalmer/toretto/touch";
|
|
6
|
+
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
7
|
+
import { round } from "@oscarpalmer/atoms/math";
|
|
8
8
|
//#region src/js/splitter.ts
|
|
9
9
|
var OuiSplitterElement = class extends HTMLElement {
|
|
10
10
|
static observedAttributes = [
|
|
@@ -216,7 +216,7 @@ function onObservation(entries) {
|
|
|
216
216
|
});
|
|
217
217
|
}
|
|
218
218
|
function onPointerdown(event) {
|
|
219
|
-
if (event.type === "mousedown"
|
|
219
|
+
if (supportsTouch.value ? event.type === "mousedown" : event.type === "touchstart") return;
|
|
220
220
|
splitter = MAPPED_ELEMENTS.get(findAncestor(event, SELECTOR_HANDLE));
|
|
221
221
|
if (splitter == null) return;
|
|
222
222
|
if (supportsTouch.value) event.preventDefault();
|
package/dist/js/tooltip.d.mts
CHANGED
package/dist/js/tooltip.mjs
CHANGED
|
@@ -1,93 +1,78 @@
|
|
|
1
|
-
import { attributable } from "./attributable.mjs";
|
|
2
|
-
import { Floatable } from "./floatable.mjs";
|
|
1
|
+
import { attributable } from "./internal/attributable.mjs";
|
|
2
|
+
import { Floatable } from "./internal/floatable.mjs";
|
|
3
3
|
import { on } from "@oscarpalmer/toretto/event";
|
|
4
4
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
5
5
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
6
6
|
//#region src/js/tooltip.ts
|
|
7
7
|
var Tooltip = class {
|
|
8
|
-
|
|
8
|
+
elements;
|
|
9
9
|
floatable;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
this.
|
|
10
|
+
constructor(anchor, elements) {
|
|
11
|
+
const content = getContent();
|
|
12
|
+
this.elements = elements;
|
|
13
13
|
this.floatable = new Floatable({
|
|
14
14
|
anchor,
|
|
15
|
-
content
|
|
16
|
-
defaultPosition: "
|
|
15
|
+
content,
|
|
16
|
+
defaultPosition: "above",
|
|
17
17
|
interactive: false,
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
positionAttribute: ATTRIBUTE_POSITION,
|
|
19
|
+
reusable: true,
|
|
20
|
+
onAfter: (active) => {
|
|
21
|
+
if (active) {
|
|
22
|
+
content.innerHTML = "";
|
|
23
|
+
content.append(...this.elements);
|
|
24
|
+
}
|
|
25
|
+
if (active || TOOLTIPS_ACTIVE.size > 0) return;
|
|
26
|
+
content.removeAttribute(ATTRIBUTE_CONTENT_ACTIVE);
|
|
27
|
+
contentTimer = setTimeout(() => {
|
|
28
|
+
content.innerHTML = "";
|
|
29
|
+
content.remove();
|
|
30
|
+
}, delayClose);
|
|
31
|
+
}
|
|
20
32
|
});
|
|
21
|
-
anchor.insertAdjacentElement("afterend", content.element);
|
|
22
|
-
TOOLTIPS_ALL.set(content.element, this);
|
|
23
33
|
}
|
|
24
34
|
destroy() {
|
|
25
|
-
const {
|
|
35
|
+
const { floatable } = this;
|
|
26
36
|
TOOLTIPS_ACTIVE.delete(this);
|
|
37
|
+
TOOLTIPS_PENDING.delete(this);
|
|
27
38
|
TOOLTIPS_ALL.delete(floatable.options.anchor);
|
|
28
39
|
TOOLTIPS_ALL.delete(floatable.options.content);
|
|
29
|
-
reset(floatable.options.anchor, attributes);
|
|
30
|
-
floatable.options.content.remove();
|
|
31
40
|
floatable.destroy();
|
|
32
|
-
this.
|
|
41
|
+
this.elements = [];
|
|
33
42
|
this.floatable = void 0;
|
|
34
43
|
}
|
|
35
44
|
};
|
|
36
45
|
function addTooltip(anchor) {
|
|
37
46
|
if (TOOLTIPS_ALL.has(anchor)) return;
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
47
|
+
const elements = getElements(anchor);
|
|
48
|
+
if (elements != null) TOOLTIPS_ALL.set(anchor, new Tooltip(anchor, elements));
|
|
40
49
|
}
|
|
41
|
-
function closeTooltips(
|
|
42
|
-
const
|
|
43
|
-
for (const
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
active.floatable?.options.content.removeAttribute("oui-tooltip-content-active");
|
|
47
|
-
requestAnimationFrame(() => {
|
|
48
|
-
active.timer = setTimeout(() => {
|
|
49
|
-
TOOLTIPS_ACTIVE.delete(active);
|
|
50
|
-
active.floatable?.toggle(false);
|
|
51
|
-
}, delay);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function getContent(anchor) {
|
|
56
|
-
const attributes = {
|
|
57
|
-
ariaDescribedby: anchor.getAttribute("aria-describedby"),
|
|
58
|
-
ariaLabelledBy: anchor.getAttribute("aria-labelledby")
|
|
59
|
-
};
|
|
60
|
-
const wrapper = getWrapper(anchor);
|
|
61
|
-
const labelledElements = getElements(attributes.ariaLabelledBy);
|
|
62
|
-
if (labelledElements.length > 0) {
|
|
63
|
-
anchor.setAttribute("aria-labelledby", wrapper.id);
|
|
64
|
-
for (const element of labelledElements) wrapper.append(element);
|
|
65
|
-
return {
|
|
66
|
-
attributes,
|
|
67
|
-
element: wrapper
|
|
68
|
-
};
|
|
50
|
+
function closeTooltips(current) {
|
|
51
|
+
const pending = [...TOOLTIPS_PENDING];
|
|
52
|
+
for (const item of pending) {
|
|
53
|
+
if (item === current) continue;
|
|
54
|
+
TOOLTIPS_PENDING.delete(item);
|
|
69
55
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
attributes,
|
|
76
|
-
element: wrapper
|
|
77
|
-
};
|
|
56
|
+
const active = [...TOOLTIPS_ACTIVE];
|
|
57
|
+
for (const item of active) {
|
|
58
|
+
if (item === current) continue;
|
|
59
|
+
TOOLTIPS_ACTIVE.delete(item);
|
|
60
|
+
item.floatable.toggle(false);
|
|
78
61
|
}
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
}
|
|
63
|
+
function getElements(anchor) {
|
|
64
|
+
let elements = findElements(anchor.getAttribute(ARIA_LABELLEDBY));
|
|
65
|
+
if (elements.length > 0) return elements.map((element) => element.cloneNode(true));
|
|
66
|
+
elements = findElements(anchor.getAttribute(ARIA_DESCRIBEDBY));
|
|
67
|
+
if (elements.length > 0) return elements.map((element) => element.cloneNode(true));
|
|
68
|
+
let content = anchor.getAttribute(ARIA_LABEL);
|
|
69
|
+
if (isNullableOrWhitespace(content)) content = anchor.getAttribute(ARIA_DESCRIPTION);
|
|
81
70
|
if (isNullableOrWhitespace(content)) return;
|
|
82
71
|
const paragraph = document.createElement("p");
|
|
83
72
|
paragraph.textContent = content;
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
attributes,
|
|
87
|
-
element: wrapper
|
|
88
|
-
};
|
|
73
|
+
return [paragraph];
|
|
89
74
|
}
|
|
90
|
-
function
|
|
75
|
+
function findElements(attribute) {
|
|
91
76
|
const ids = attribute?.split(EXPRESSION_WHITESPACE) ?? [];
|
|
92
77
|
const elements = [];
|
|
93
78
|
for (const id of ids) {
|
|
@@ -101,19 +86,20 @@ function getElements(attribute) {
|
|
|
101
86
|
}
|
|
102
87
|
return elements;
|
|
103
88
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
wrapper.tabIndex = -1;
|
|
112
|
-
return wrapper;
|
|
89
|
+
function getContent() {
|
|
90
|
+
if (contentElement != null) return contentElement;
|
|
91
|
+
contentElement = document.createElement("div");
|
|
92
|
+
contentElement.setAttribute(ATTRIBUTE_CONTENT, "");
|
|
93
|
+
contentElement.role = "tooltip";
|
|
94
|
+
contentElement.tabIndex = -1;
|
|
95
|
+
return contentElement;
|
|
113
96
|
}
|
|
114
97
|
function onActivate(event) {
|
|
115
98
|
onToggle(event, true);
|
|
116
99
|
}
|
|
100
|
+
function onChange() {
|
|
101
|
+
if (TOOLTIPS_ACTIVE.size === 0) return;
|
|
102
|
+
}
|
|
117
103
|
function onClick(event) {
|
|
118
104
|
if (findAncestor(event, SELECTOR_CONTENT) != null) event.stopPropagation();
|
|
119
105
|
}
|
|
@@ -122,55 +108,67 @@ function onDeactivate(event) {
|
|
|
122
108
|
}
|
|
123
109
|
function onToggle(event, activate) {
|
|
124
110
|
if (toggle != null) cancelAnimationFrame(toggle);
|
|
111
|
+
if (findAncestor(event, SELECTOR_CONTENT) != null) {
|
|
112
|
+
if (contentTimer != null) clearTimeout(contentTimer);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
125
115
|
toggle = requestAnimationFrame(() => {
|
|
126
116
|
toggle = void 0;
|
|
127
|
-
const element = findAncestor(event,
|
|
117
|
+
const element = findAncestor(event, SELECTOR);
|
|
128
118
|
const tooltip = TOOLTIPS_ALL.get(element);
|
|
129
|
-
if (TOOLTIPS_ACTIVE.size > 0) closeTooltips(tooltip);
|
|
130
|
-
if (tooltip == null || tooltip.floatable.
|
|
119
|
+
if (TOOLTIPS_ACTIVE.size > 0 || TOOLTIPS_PENDING.size > 0) closeTooltips(tooltip);
|
|
120
|
+
if (tooltip == null || tooltip.floatable.active === activate) return;
|
|
131
121
|
if (activate) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
122
|
+
clearTimeout(contentTimer);
|
|
123
|
+
TOOLTIPS_PENDING.add(tooltip);
|
|
124
|
+
contentTimer = setTimeout(() => {
|
|
125
|
+
TOOLTIPS_ACTIVE.add(tooltip);
|
|
126
|
+
TOOLTIPS_PENDING.delete(tooltip);
|
|
127
|
+
tooltip.floatable.options.content.style.positionAnchor = tooltip.floatable.options.anchor.style.anchorName;
|
|
128
|
+
tooltip.floatable.toggle(true);
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
tooltip.floatable.options.content.setAttribute(ATTRIBUTE_CONTENT_ACTIVE, "");
|
|
131
|
+
});
|
|
132
|
+
}, delayOpen);
|
|
138
133
|
return;
|
|
139
134
|
}
|
|
140
|
-
|
|
141
|
-
tooltip.
|
|
142
|
-
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
143
|
-
tooltip.floatable.toggle(false);
|
|
144
|
-
}, delay);
|
|
135
|
+
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
136
|
+
tooltip.floatable.toggle(false);
|
|
145
137
|
});
|
|
146
138
|
}
|
|
147
139
|
function removeTooltip(element) {
|
|
148
140
|
if (!element.hasAttribute(ATTRIBUTE_CONTENT)) TOOLTIPS_ALL.get(element)?.destroy();
|
|
149
141
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
function setDelay(value) {
|
|
155
|
-
delay = typeof value === "number" && value >= 0 ? value : delay;
|
|
142
|
+
function setTooltipDelay(open, close) {
|
|
143
|
+
delayClose = typeof close === "number" && close >= 0 ? close : delayClose;
|
|
144
|
+
delayOpen = typeof open === "number" && open >= 0 ? open : delayOpen;
|
|
156
145
|
}
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const
|
|
146
|
+
const ARIA_DESCRIBEDBY = "aria-describedby";
|
|
147
|
+
const ARIA_DESCRIPTION = "aria-description";
|
|
148
|
+
const ARIA_LABEL = "aria-label";
|
|
149
|
+
const ARIA_LABELLEDBY = "aria-labelledby";
|
|
150
|
+
const ATTRIBUTE = "oui-tooltip";
|
|
151
|
+
const ATTRIBUTE_CONTENT = `${ATTRIBUTE}-content`;
|
|
152
|
+
const ATTRIBUTE_CONTENT_ACTIVE = `${ATTRIBUTE_CONTENT}-active`;
|
|
153
|
+
const ATTRIBUTE_POSITION = `${ATTRIBUTE}-position`;
|
|
154
|
+
const SELECTOR = `[${ATTRIBUTE}]`;
|
|
161
155
|
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
162
|
-
const SELECTOR_FULL = `[${SELECTOR}], [${ATTRIBUTE_CONTENT}]`;
|
|
163
156
|
const EXPRESSION_WHITESPACE = /\s+/;
|
|
164
157
|
const TOOLTIPS_ACTIVE = /* @__PURE__ */ new Set();
|
|
165
158
|
const TOOLTIPS_ALL = /* @__PURE__ */ new WeakMap();
|
|
166
|
-
|
|
167
|
-
let
|
|
159
|
+
const TOOLTIPS_PENDING = /* @__PURE__ */ new Set();
|
|
160
|
+
let contentElement;
|
|
161
|
+
let contentTimer;
|
|
162
|
+
let delayClose = 250;
|
|
163
|
+
let delayOpen = 500;
|
|
168
164
|
let toggle;
|
|
169
|
-
attributable(
|
|
165
|
+
attributable(ATTRIBUTE, addTooltip, removeTooltip);
|
|
170
166
|
on(document, "click", onClick, { capture: true });
|
|
171
167
|
on(document, "focusin", onActivate);
|
|
172
168
|
on(document, "focusout", onDeactivate);
|
|
173
169
|
on(document, "mousemove", onActivate);
|
|
170
|
+
on(window, "resize", onChange);
|
|
171
|
+
on(document, "scroll", onChange);
|
|
174
172
|
on(document, "touchstart", onActivate);
|
|
175
173
|
//#endregion
|
|
176
|
-
export {
|
|
174
|
+
export { setTooltipDelay };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/oui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Styles, components, and utilities for building websites and applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -37,10 +37,18 @@
|
|
|
37
37
|
"types": "./dist/js/focus-trap/standalone.d.mts",
|
|
38
38
|
"default": "./dist/js/focus-trap/standalone.mjs"
|
|
39
39
|
},
|
|
40
|
+
"./movable": {
|
|
41
|
+
"types": "./dist/js/movable.d.mts",
|
|
42
|
+
"default": "./dist/js/movable.mjs"
|
|
43
|
+
},
|
|
40
44
|
"./popover": {
|
|
41
45
|
"types": "./dist/js/popover.d.mts",
|
|
42
46
|
"default": "./dist/js/popover.mjs"
|
|
43
47
|
},
|
|
48
|
+
"./sortable": {
|
|
49
|
+
"types": "./dist/js/sortable.d.mts",
|
|
50
|
+
"default": "./dist/js/sortable.mjs"
|
|
51
|
+
},
|
|
44
52
|
"./splitter": {
|
|
45
53
|
"types": "./dist/js/splitter.d.mts",
|
|
46
54
|
"default": "./dist/js/splitter.mjs"
|
|
@@ -51,35 +59,37 @@
|
|
|
51
59
|
}
|
|
52
60
|
},
|
|
53
61
|
"scripts": {
|
|
54
|
-
"build": "vp run
|
|
55
|
-
"
|
|
56
|
-
"build
|
|
57
|
-
"
|
|
62
|
+
"build": "vp run clean && vp pack && vp run css:build",
|
|
63
|
+
"clean": "rm -rf ./dist && rm -rf ./postcss",
|
|
64
|
+
"css:build": "vp run postcss:build && vpx sass ./postcss:./dist/css --no-source-map",
|
|
65
|
+
"css:watch": "vpx sass ./postcss:./dist/css --no-source-map --watch",
|
|
66
|
+
"postcss:build": "vpx postcss ./src/css/*.scss -d ./postcss -u autoprefixer postcss-input-range --parser postcss-scss",
|
|
67
|
+
"postcss:watch": "vpx postcss ./src/css/*.scss -d ./postcss -u autoprefixer postcss-input-range --parser postcss-scss --watch",
|
|
58
68
|
"test": "vpx vitest --coverage",
|
|
59
|
-
"tsdown
|
|
60
|
-
"
|
|
61
|
-
"watch:css": "vpx sass ./postcss:./dist/css --no-source-map --watch",
|
|
62
|
-
"watch:postcss": "vpx postcss ./src/css/*.scss -d ./postcss -u autoprefixer postcss-input-range --parser postcss-scss --watch",
|
|
63
|
-
"watch:js": "vpx vite build --watch"
|
|
69
|
+
"tsdown": "vpx tsdown -c ./tsdown.config.ts --watch",
|
|
70
|
+
"watch": "vp run clean && concurrently \"php -S localhost:3000\" \"vp run postcss:watch\" \"vp run css:watch\" \"vp run tsdown\""
|
|
64
71
|
},
|
|
65
72
|
"dependencies": {
|
|
66
|
-
"@oscarpalmer/atoms": "^0.
|
|
67
|
-
"@oscarpalmer/toretto": "^0.
|
|
73
|
+
"@oscarpalmer/atoms": "^0.187.2",
|
|
74
|
+
"@oscarpalmer/toretto": "^0.46"
|
|
68
75
|
},
|
|
69
76
|
"devDependencies": {
|
|
77
|
+
"@oxlint/plugins": "^1.68",
|
|
70
78
|
"@vitest/coverage-istanbul": "^4.1",
|
|
71
|
-
"autoprefixer": "^10.
|
|
72
|
-
"
|
|
79
|
+
"autoprefixer": "^10.5",
|
|
80
|
+
"concurrently": "^10",
|
|
81
|
+
"jsdom": "^29.1",
|
|
73
82
|
"postcss-cli": "^11",
|
|
74
83
|
"postcss-input-range": "^6",
|
|
75
84
|
"postcss-scss": "^4",
|
|
76
|
-
"sass": "^1.
|
|
77
|
-
"stylelint": "^17.
|
|
85
|
+
"sass": "^1.100",
|
|
86
|
+
"stylelint": "^17.12",
|
|
78
87
|
"stylelint-config-standard-scss": "^17",
|
|
79
|
-
"
|
|
88
|
+
"tsdown": "^0.22",
|
|
89
|
+
"typescript": "^6",
|
|
80
90
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
81
91
|
"vite-plus": "latest",
|
|
82
92
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
|
83
93
|
},
|
|
84
94
|
"packageManager": "npm@11.12.1"
|
|
85
|
-
}
|
|
95
|
+
}
|