@oscarpalmer/oui 0.24.0 → 0.26.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/{tooltip.css → floatable.css} +59 -24
- package/dist/css/reset.css +17 -0
- package/dist/js/dialog.d.mts +38 -0
- package/dist/js/dialog.mjs +92 -0
- package/dist/js/embedded-CHK82Uh5.d.mts +76 -0
- package/dist/js/embedded-ai6cqURc.d.mts +68 -0
- package/dist/js/floatable/embedded.d.mts +2 -0
- package/dist/js/floatable/embedded.mjs +157 -0
- package/dist/js/floatable/standalone.d.mts +2 -0
- package/dist/js/floatable/standalone.mjs +2 -0
- package/dist/js/focus-trap/embedded.d.mts +2 -26
- package/dist/js/focus-trap/embedded.mjs +158 -80
- package/dist/js/focus-trap/standalone.d.mts +2 -1
- package/dist/js/focus-trap/standalone.mjs +3 -3
- package/dist/js/index.d.mts +6 -3
- package/dist/js/index.mjs +9 -5
- package/dist/js/internal/draggable.d.mts +2 -2
- package/dist/js/internal/draggable.mjs +3 -3
- package/dist/js/{movable-BKFgoaOl.d.mts → movable-dNqBctx3.d.mts} +16 -16
- package/dist/js/movable.d.mts +2 -2
- package/dist/js/movable.mjs +4 -4
- package/dist/js/popover.d.mts +38 -1
- package/dist/js/popover.mjs +83 -44
- package/dist/js/sortable.d.mts +2 -2
- package/dist/js/sortable.mjs +5 -5
- package/dist/js/splitter.mjs +49 -48
- package/dist/js/toolbar.d.mts +1 -0
- package/dist/js/toolbar.mjs +107 -0
- package/dist/js/tooltip.d.mts +27 -1
- package/dist/js/tooltip.mjs +85 -49
- package/package.json +14 -2
- package/src/css/{tooltip.scss → floatable.scss} +47 -5
- package/src/css/mixins.scss +0 -2
- package/src/css/reset.scss +17 -0
- package/src/js/dialog.ts +188 -0
- package/src/js/floatable/embedded.ts +323 -0
- package/src/js/floatable/standalone.ts +1 -0
- package/src/js/focus-trap/embedded.ts +219 -93
- package/src/js/focus-trap/standalone.ts +4 -2
- package/src/js/index.ts +6 -4
- package/src/js/internal/draggable.ts +20 -20
- package/src/js/movable.ts +17 -17
- package/src/js/popover.ts +143 -61
- package/src/js/sortable.ts +26 -26
- package/src/js/splitter.ts +98 -96
- package/src/js/toolbar.ts +214 -0
- package/src/js/tooltip.ts +138 -54
- package/dist/css/popover.css +0 -30
- package/dist/js/floatable.d.mts +0 -59
- package/dist/js/floatable.mjs +0 -130
- package/src/css/popover.scss +0 -10
- package/src/js/floatable.ts +0 -237
package/dist/js/popover.mjs
CHANGED
|
@@ -1,64 +1,103 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEmbeddedFloatable, getOnBeforeToggleListener } from "./floatable/embedded.mjs";
|
|
2
2
|
import { attributable } from "./internal/attributable.mjs";
|
|
3
|
-
import { createFocusTrap
|
|
4
|
-
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
3
|
+
import { createFocusTrap } from "./focus-trap/embedded.mjs";
|
|
5
4
|
import { on } from "@oscarpalmer/toretto/event";
|
|
5
|
+
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
6
6
|
//#region src/js/popover.ts
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.floatable?.update();
|
|
25
|
-
element.focus();
|
|
26
|
-
} else if (!ignoreFocus) this.anchor?.focus();
|
|
27
|
-
});
|
|
28
|
-
this.element.setAttribute(ATTRIBUTE_CONTENT, "");
|
|
29
|
-
this.floatable?.update();
|
|
7
|
+
var OuiPopover = class {
|
|
8
|
+
#state;
|
|
9
|
+
/**
|
|
10
|
+
* Is the popover open?
|
|
11
|
+
*/
|
|
12
|
+
get open() {
|
|
13
|
+
return this.#state.content.checkVisibility();
|
|
14
|
+
}
|
|
15
|
+
constructor(state) {
|
|
16
|
+
this.#state = state;
|
|
17
|
+
this.#state.floatable.update();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Closes the popover
|
|
21
|
+
*/
|
|
22
|
+
hide() {
|
|
23
|
+
this.#state.floatable.hide();
|
|
30
24
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.
|
|
36
|
-
this.anchor = null;
|
|
37
|
-
this.element = null;
|
|
25
|
+
/**
|
|
26
|
+
* Opens the popover
|
|
27
|
+
*/
|
|
28
|
+
show() {
|
|
29
|
+
this.#state.floatable.show();
|
|
38
30
|
}
|
|
39
31
|
};
|
|
40
|
-
function
|
|
41
|
-
if (
|
|
32
|
+
function addPopover(element) {
|
|
33
|
+
if (states.has(element) || element.hasAttribute("oui-floatable")) return;
|
|
34
|
+
const anchor = element.ownerDocument.querySelector(`[popovertarget="${element.id}"]`);
|
|
35
|
+
if (isHTMLOrSVGElement(anchor)) states.set(element, getState(anchor, element));
|
|
36
|
+
}
|
|
37
|
+
function destroyPopover(state) {
|
|
38
|
+
state.floatable.destroy();
|
|
39
|
+
state.focusTrap?.destroy();
|
|
40
|
+
for (const listener of state.listeners) listener();
|
|
41
|
+
state.anchor = void 0;
|
|
42
|
+
state.content = void 0;
|
|
43
|
+
state.floatable = void 0;
|
|
44
|
+
state.focusTrap = void 0;
|
|
45
|
+
state.listeners = void 0;
|
|
46
|
+
state.popover = void 0;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the _OuiPopover_ for an element
|
|
50
|
+
*
|
|
51
|
+
* @param element Element to get _OuiPopover_ for
|
|
52
|
+
* @returns _OuiPopover_ instance
|
|
53
|
+
*/
|
|
54
|
+
function getPopover(element) {
|
|
55
|
+
return states.get(element)?.popover;
|
|
56
|
+
}
|
|
57
|
+
function getState(anchor, content) {
|
|
58
|
+
const state = {
|
|
59
|
+
anchor,
|
|
60
|
+
content,
|
|
61
|
+
floatable: createEmbeddedFloatable(anchor, content, options),
|
|
62
|
+
focusTrap: content.getAttribute(ARIA_MODAL) === TRUE ? createFocusTrap(content, { noescape: true }) : void 0,
|
|
63
|
+
listeners: [getOnBeforeToggleListener(content), on(content, EVENT_TOGGLE, (event) => {
|
|
64
|
+
if (event.newState === STATE_OPEN) {
|
|
65
|
+
ignoreFocus = false;
|
|
66
|
+
state.floatable?.update();
|
|
67
|
+
content.setAttribute(ATTRIBUTE_OPEN, "");
|
|
68
|
+
content.focus();
|
|
69
|
+
} else {
|
|
70
|
+
content.removeAttribute(ATTRIBUTE_OPEN);
|
|
71
|
+
if (!ignoreFocus) anchor?.focus();
|
|
72
|
+
}
|
|
73
|
+
})],
|
|
74
|
+
popover: void 0
|
|
75
|
+
};
|
|
76
|
+
state.popover = new OuiPopover(state);
|
|
77
|
+
return state;
|
|
42
78
|
}
|
|
43
79
|
function onPointerdown() {
|
|
44
80
|
ignoreFocus = true;
|
|
45
81
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
function removePopover(element) {
|
|
83
|
+
const state = states.get(element);
|
|
84
|
+
if (state == null) return;
|
|
85
|
+
destroyPopover(state);
|
|
86
|
+
states.delete(element);
|
|
49
87
|
}
|
|
50
|
-
const ATTRIBUTE = "popover";
|
|
51
|
-
const
|
|
88
|
+
const ATTRIBUTE = "oui-popover";
|
|
89
|
+
const ARIA_MODAL = "aria-modal";
|
|
90
|
+
const ATTRIBUTE_OPEN = `${ATTRIBUTE}-open`;
|
|
52
91
|
const EVENT_TOGGLE = "toggle";
|
|
53
|
-
const POPOVER_HINT = "hint";
|
|
54
92
|
const STATE_OPEN = "open";
|
|
55
|
-
const
|
|
93
|
+
const TRUE = "true";
|
|
56
94
|
const options = {
|
|
57
95
|
attribute: `${ATTRIBUTE}position`,
|
|
58
96
|
position: "below-start"
|
|
59
97
|
};
|
|
98
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
60
99
|
let ignoreFocus = false;
|
|
61
100
|
on(document, "pointerdown", onPointerdown, { capture: true });
|
|
62
|
-
attributable(ATTRIBUTE,
|
|
101
|
+
attributable(ATTRIBUTE, addPopover, removePopover);
|
|
63
102
|
//#endregion
|
|
64
|
-
export {};
|
|
103
|
+
export { getPopover };
|
package/dist/js/sortable.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
export {
|
|
1
|
+
import { c as OuiSortable } from "./movable-dNqBctx3.mjs";
|
|
2
|
+
export { OuiSortable };
|
package/dist/js/sortable.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { attributable } from "./internal/attributable.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
2
|
+
import { OuiDraggable, addDraggable, removeDraggable } from "./internal/draggable.mjs";
|
|
4
3
|
import { getPosition } from "@oscarpalmer/toretto/event";
|
|
4
|
+
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
5
5
|
import { findAncestor, getElementFromPosition } from "@oscarpalmer/toretto/find";
|
|
6
6
|
import { createElement } from "@oscarpalmer/toretto/create";
|
|
7
7
|
//#region src/js/sortable.ts
|
|
8
|
-
var
|
|
8
|
+
var OuiSortable = class extends OuiDraggable {
|
|
9
9
|
connections;
|
|
10
10
|
origin;
|
|
11
11
|
get dragged() {
|
|
@@ -230,7 +230,7 @@ const INSERT_BEFORE = "beforebegin";
|
|
|
230
230
|
const SELECTOR = `[${ATTRIBUTE}]`;
|
|
231
231
|
const SELECTOR_ITEM = `[${ATTRIBUTE}] > *`;
|
|
232
232
|
const bound = {
|
|
233
|
-
constructor:
|
|
233
|
+
constructor: OuiSortable,
|
|
234
234
|
property: "sortable"
|
|
235
235
|
};
|
|
236
236
|
const placeholder = {
|
|
@@ -242,4 +242,4 @@ const placeholder = {
|
|
|
242
242
|
};
|
|
243
243
|
attributable(ATTRIBUTE, addDraggable.bind(bound), removeDraggable.bind(bound));
|
|
244
244
|
//#endregion
|
|
245
|
-
export {
|
|
245
|
+
export { OuiSortable };
|
package/dist/js/splitter.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { getPosition, on } from "@oscarpalmer/toretto/event";
|
|
1
2
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
2
3
|
import { clamp } from "@oscarpalmer/atoms/number";
|
|
3
|
-
import { getPosition, on } from "@oscarpalmer/toretto/event";
|
|
4
4
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
5
5
|
import { toggleStyles } from "@oscarpalmer/toretto/style";
|
|
6
6
|
import supportsTouch from "@oscarpalmer/toretto/touch";
|
|
@@ -10,6 +10,50 @@ const ATTRIBUTE_MAX = "max";
|
|
|
10
10
|
const ATTRIBUTE_MIN = "min";
|
|
11
11
|
const ATTRIBUTE_SIZE = "size";
|
|
12
12
|
const ATTRIBUTE_TYPE = "type";
|
|
13
|
+
var OuiSplitter = class {
|
|
14
|
+
element;
|
|
15
|
+
handle;
|
|
16
|
+
panels;
|
|
17
|
+
rectangle;
|
|
18
|
+
separator;
|
|
19
|
+
types;
|
|
20
|
+
values;
|
|
21
|
+
constructor(element, panels) {
|
|
22
|
+
this.element = element;
|
|
23
|
+
this.panels = panels;
|
|
24
|
+
if (isNullableOrWhitespace(element.id)) element.id = `oui_splitter_${++index}`;
|
|
25
|
+
this.values = getValues(element);
|
|
26
|
+
this.handle = createHandle();
|
|
27
|
+
this.rectangle = element.getBoundingClientRect();
|
|
28
|
+
this.separator = createSeparator(this);
|
|
29
|
+
this.separator.append(this.handle);
|
|
30
|
+
panels[0].insertAdjacentElement("afterend", this.separator);
|
|
31
|
+
this.updateOrientation();
|
|
32
|
+
this.updateValues();
|
|
33
|
+
}
|
|
34
|
+
updateConnections(add) {
|
|
35
|
+
const method = METHODS_CONNECT[add];
|
|
36
|
+
MAPPED_ELEMENTS[method](this.element, this);
|
|
37
|
+
MAPPED_ELEMENTS[method](this.handle, this);
|
|
38
|
+
MAPPED_ELEMENTS[method](this.separator, this);
|
|
39
|
+
if (this.types.auto) RESIZE_OBSERVER[METHODS_OBSERVE[add]](this.element);
|
|
40
|
+
}
|
|
41
|
+
updateOrientation() {
|
|
42
|
+
this.types = getTypes(this);
|
|
43
|
+
if (this.types.auto) RESIZE_OBSERVER.observe(this.element);
|
|
44
|
+
else RESIZE_OBSERVER.unobserve(this.element);
|
|
45
|
+
if (this.types.horizontal) this.element.setAttribute(ATTRIBUTE_HORIZONTAL, "");
|
|
46
|
+
else this.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
47
|
+
setAriaOrientation(this);
|
|
48
|
+
}
|
|
49
|
+
updateValues() {
|
|
50
|
+
this.values = getValues(this.element);
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
setSize(this, this.values.now.current);
|
|
53
|
+
setAriaValue(this);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
13
57
|
var OuiSplitterElement = class extends HTMLElement {
|
|
14
58
|
static observedAttributes = [
|
|
15
59
|
ATTRIBUTE_MAX,
|
|
@@ -45,8 +89,8 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
45
89
|
constructor() {
|
|
46
90
|
super();
|
|
47
91
|
const panels = [...this.children].filter((child) => child instanceof HTMLElement);
|
|
48
|
-
if (panels.length !== 2) throw new Error(
|
|
49
|
-
this.#splitter = new
|
|
92
|
+
if (panels.length !== 2) throw new Error(MESSAGE);
|
|
93
|
+
this.#splitter = new OuiSplitter(this, panels);
|
|
50
94
|
}
|
|
51
95
|
connectedCallback() {
|
|
52
96
|
this.#splitter.updateConnections(1);
|
|
@@ -68,51 +112,6 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
68
112
|
}
|
|
69
113
|
}
|
|
70
114
|
};
|
|
71
|
-
let index = 0;
|
|
72
|
-
var Splitter = class {
|
|
73
|
-
element;
|
|
74
|
-
handle;
|
|
75
|
-
panels;
|
|
76
|
-
rectangle;
|
|
77
|
-
separator;
|
|
78
|
-
types;
|
|
79
|
-
values;
|
|
80
|
-
constructor(element, panels) {
|
|
81
|
-
this.element = element;
|
|
82
|
-
this.panels = panels;
|
|
83
|
-
if (isNullableOrWhitespace(element.id)) element.id = `oui_splitter_${++index}`;
|
|
84
|
-
this.values = getValues(element);
|
|
85
|
-
this.handle = createHandle();
|
|
86
|
-
this.rectangle = element.getBoundingClientRect();
|
|
87
|
-
this.separator = createSeparator(this);
|
|
88
|
-
this.separator.append(this.handle);
|
|
89
|
-
panels[0].insertAdjacentElement("afterend", this.separator);
|
|
90
|
-
this.updateOrientation();
|
|
91
|
-
this.updateValues();
|
|
92
|
-
}
|
|
93
|
-
updateConnections(add) {
|
|
94
|
-
const method = METHODS_CONNECT[add];
|
|
95
|
-
MAPPED_ELEMENTS[method](this.element, this);
|
|
96
|
-
MAPPED_ELEMENTS[method](this.handle, this);
|
|
97
|
-
MAPPED_ELEMENTS[method](this.separator, this);
|
|
98
|
-
if (this.types.auto) RESIZE_OBSERVER[METHODS_OBSERVE[add]](this.element);
|
|
99
|
-
}
|
|
100
|
-
updateOrientation() {
|
|
101
|
-
this.types = getTypes(this);
|
|
102
|
-
if (this.types.auto) RESIZE_OBSERVER.observe(this.element);
|
|
103
|
-
else RESIZE_OBSERVER.unobserve(this.element);
|
|
104
|
-
if (this.types.horizontal) this.element.setAttribute(ATTRIBUTE_HORIZONTAL, "");
|
|
105
|
-
else this.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
106
|
-
setAriaOrientation(this);
|
|
107
|
-
}
|
|
108
|
-
updateValues() {
|
|
109
|
-
this.values = getValues(this.element);
|
|
110
|
-
requestAnimationFrame(() => {
|
|
111
|
-
setSize(this, this.values.now.current);
|
|
112
|
-
setAriaValue(this);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
115
|
function createHandle() {
|
|
117
116
|
const handle = document.createElement("span");
|
|
118
117
|
handle.setAttribute("aria-hidden", "true");
|
|
@@ -269,6 +268,7 @@ const KEYS_ALL = new Set([
|
|
|
269
268
|
...KEYS_VERTICAL
|
|
270
269
|
]);
|
|
271
270
|
const MAPPED_ELEMENTS = /* @__PURE__ */ new WeakMap();
|
|
271
|
+
const MESSAGE = `<${SELECTOR}> must have exactly two panels.`;
|
|
272
272
|
const METHODS_CONNECT = ["delete", "set"];
|
|
273
273
|
const METHODS_OBSERVE = ["unobserve", "observe"];
|
|
274
274
|
const NAVIGATION_MAXIMUM_PERCENTAGE = .9;
|
|
@@ -283,6 +283,7 @@ const STYLING_TOGGLER = toggleStyles(document.body, {
|
|
|
283
283
|
webkitUserSelect: "none"
|
|
284
284
|
});
|
|
285
285
|
const VALUE_DECIMALS = 6;
|
|
286
|
+
let index = 0;
|
|
286
287
|
let frame;
|
|
287
288
|
let splitter;
|
|
288
289
|
customElements.define(SELECTOR, OuiSplitterElement);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { attributable } from "./internal/attributable.mjs";
|
|
2
|
+
import { on } from "@oscarpalmer/toretto/event";
|
|
3
|
+
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
4
|
+
import { clamp } from "@oscarpalmer/atoms/number";
|
|
5
|
+
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
6
|
+
//#region src/js/toolbar.ts
|
|
7
|
+
var OuiToolbar = class {
|
|
8
|
+
#state;
|
|
9
|
+
constructor(state) {
|
|
10
|
+
this.#state = state;
|
|
11
|
+
updateFocus(this.#state, false);
|
|
12
|
+
}
|
|
13
|
+
destroy() {
|
|
14
|
+
for (const listener of this.#state.listeners) listener();
|
|
15
|
+
this.#state.active = void 0;
|
|
16
|
+
this.#state.element = void 0;
|
|
17
|
+
this.#state.listeners = void 0;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function addToolbar(element) {
|
|
21
|
+
if (!instances.has(element)) instances.set(element, new OuiToolbar(getState(element)));
|
|
22
|
+
}
|
|
23
|
+
function getItems(element) {
|
|
24
|
+
return [...element.querySelectorAll(SELECTOR)].filter(isValidItem);
|
|
25
|
+
}
|
|
26
|
+
function getOrientation(element) {
|
|
27
|
+
const attribute = element.getAttribute(ARIA_ORIENTATION);
|
|
28
|
+
return ORIENTATIONS.has(attribute) ? attribute : ORIENTATION_HORIZONTAL;
|
|
29
|
+
}
|
|
30
|
+
function getState(element) {
|
|
31
|
+
const state = {
|
|
32
|
+
element,
|
|
33
|
+
active: { index: 0 },
|
|
34
|
+
listeners: [on(element, EVENT_FOCUSIN, (event) => {
|
|
35
|
+
onFocusin(state, event);
|
|
36
|
+
}), on(element, EVENT_KEYDOWN, (event) => {
|
|
37
|
+
onKeydown(state, event);
|
|
38
|
+
}, { passive: false })],
|
|
39
|
+
orientation: getOrientation(element)
|
|
40
|
+
};
|
|
41
|
+
return state;
|
|
42
|
+
}
|
|
43
|
+
function isValidItem(element) {
|
|
44
|
+
return PROPERTY_DISABLED in element ? element.disabled !== true : true;
|
|
45
|
+
}
|
|
46
|
+
function onKeydown(state, event) {
|
|
47
|
+
if (!KEYS_ALL.has(event.key)) return;
|
|
48
|
+
const items = getItems(state.element);
|
|
49
|
+
if (items.length === 0) return;
|
|
50
|
+
let index;
|
|
51
|
+
if (KEYS_ABSOLUTE.has(event.key)) index = event.key === KEY_HOME ? 0 : items.length - 1;
|
|
52
|
+
else if (state.orientation === ORIENTATION_HORIZONTAL ? KEYS_HORIZONTAL.has(event.key) : KEYS_VERTICAL.has(event.key)) {
|
|
53
|
+
if (state.active.element instanceof HTMLSelectElement) event.preventDefault();
|
|
54
|
+
index = clamp(state.active.index + (KEYS_NEXT.has(event.key) ? 1 : -1), 0, items.length - 1, true);
|
|
55
|
+
}
|
|
56
|
+
if (index == null || index === state.active.index) return;
|
|
57
|
+
state.active.element = items[index];
|
|
58
|
+
state.active.index = index;
|
|
59
|
+
updateFocus(state, true, items);
|
|
60
|
+
}
|
|
61
|
+
function onFocusin(state, event) {
|
|
62
|
+
const item = findAncestor(event, SELECTOR);
|
|
63
|
+
if (!isHTMLOrSVGElement(item) || !isValidItem(item)) return;
|
|
64
|
+
const items = getItems(state.element);
|
|
65
|
+
const index = items.indexOf(item);
|
|
66
|
+
if (index === -1) return;
|
|
67
|
+
state.active.element = item;
|
|
68
|
+
state.active.index = index;
|
|
69
|
+
updateFocus(state, false, items);
|
|
70
|
+
}
|
|
71
|
+
function removeToolbar(element) {
|
|
72
|
+
instances.get(element)?.destroy();
|
|
73
|
+
instances.delete(element);
|
|
74
|
+
}
|
|
75
|
+
function updateFocus(state, focus, items) {
|
|
76
|
+
const updated = items ?? getItems(state.element);
|
|
77
|
+
const { length } = updated;
|
|
78
|
+
if (length === 0) return;
|
|
79
|
+
for (let index = 0; index < length; index += 1) updated[index].tabIndex = index === state.active.index ? 0 : -1;
|
|
80
|
+
state.active.element ??= updated[state.active.index];
|
|
81
|
+
if (focus) state.active.element?.focus();
|
|
82
|
+
}
|
|
83
|
+
const ARIA_ORIENTATION = "aria-orientation";
|
|
84
|
+
const ATTRIBUTE = "oui-toolbar";
|
|
85
|
+
const EVENT_KEYDOWN = "keydown";
|
|
86
|
+
const EVENT_FOCUSIN = "focusin";
|
|
87
|
+
const KEY_ARROW_DOWN = "ArrowDown";
|
|
88
|
+
const KEY_ARROW_RIGHT = "ArrowRight";
|
|
89
|
+
const KEY_END = "End";
|
|
90
|
+
const KEY_HOME = "Home";
|
|
91
|
+
const KEYS_ABSOLUTE = new Set([KEY_END, KEY_HOME]);
|
|
92
|
+
const KEYS_HORIZONTAL = new Set([KEY_ARROW_RIGHT, "ArrowLeft"]);
|
|
93
|
+
const KEYS_VERTICAL = new Set([KEY_ARROW_DOWN, "ArrowUp"]);
|
|
94
|
+
const KEYS_ALL = new Set([
|
|
95
|
+
...KEYS_ABSOLUTE,
|
|
96
|
+
...KEYS_HORIZONTAL,
|
|
97
|
+
...KEYS_VERTICAL
|
|
98
|
+
]);
|
|
99
|
+
const KEYS_NEXT = new Set([KEY_ARROW_DOWN, KEY_ARROW_RIGHT]);
|
|
100
|
+
const ORIENTATION_HORIZONTAL = "horizontal";
|
|
101
|
+
const ORIENTATIONS = new Set([ORIENTATION_HORIZONTAL, "vertical"]);
|
|
102
|
+
const PROPERTY_DISABLED = "disabled";
|
|
103
|
+
const SELECTOR = `[${ATTRIBUTE}-item]`;
|
|
104
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
105
|
+
attributable(ATTRIBUTE, addToolbar, removeToolbar);
|
|
106
|
+
//#endregion
|
|
107
|
+
export {};
|
package/dist/js/tooltip.d.mts
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
|
+
import { n as OuiFloatable } from "./embedded-ai6cqURc.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/js/tooltip.d.ts
|
|
4
|
+
declare class OuiTooltip {
|
|
5
|
+
#private;
|
|
6
|
+
/**
|
|
7
|
+
* Is the tooltip open?
|
|
8
|
+
*/
|
|
9
|
+
get open(): boolean;
|
|
10
|
+
constructor(state: OuiTooltipState);
|
|
11
|
+
/**
|
|
12
|
+
* Closes the tooltip
|
|
13
|
+
*/
|
|
14
|
+
hide(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Opens the tooltip
|
|
17
|
+
*/
|
|
18
|
+
show(): void;
|
|
19
|
+
}
|
|
20
|
+
type OuiTooltipState = {
|
|
21
|
+
anchor: HTMLElement;
|
|
22
|
+
content: HTMLElement;
|
|
23
|
+
floatable: OuiFloatable;
|
|
24
|
+
timer?: number;
|
|
25
|
+
tooltip: OuiTooltip;
|
|
26
|
+
};
|
|
27
|
+
declare function getTooltip(element: HTMLElement): OuiTooltip | undefined;
|
|
2
28
|
/**
|
|
3
29
|
* Set the delay before showing the tooltip, in milliseconds
|
|
4
30
|
*
|
|
@@ -7,4 +33,4 @@
|
|
|
7
33
|
*/
|
|
8
34
|
declare function setTooltipDelay(value: number): void;
|
|
9
35
|
//#endregion
|
|
10
|
-
export { setTooltipDelay };
|
|
36
|
+
export { type OuiTooltip, getTooltip, setTooltipDelay };
|
package/dist/js/tooltip.mjs
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEmbeddedFloatable } from "./floatable/embedded.mjs";
|
|
2
2
|
import { attributable } from "./internal/attributable.mjs";
|
|
3
|
+
import { on } from "@oscarpalmer/toretto/event";
|
|
3
4
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
4
5
|
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
5
|
-
import { on } from "@oscarpalmer/toretto/event";
|
|
6
6
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
7
7
|
import { createElement } from "@oscarpalmer/toretto/create";
|
|
8
8
|
//#region src/js/tooltip.ts
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
var OuiTooltip = class {
|
|
10
|
+
#state;
|
|
11
|
+
/**
|
|
12
|
+
* Is the tooltip open?
|
|
13
|
+
*/
|
|
14
|
+
get open() {
|
|
15
|
+
return this.#state.content.checkVisibility();
|
|
16
|
+
}
|
|
17
|
+
constructor(state) {
|
|
18
|
+
this.#state = state;
|
|
19
|
+
state.floatable.update();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Closes the tooltip
|
|
23
|
+
*/
|
|
24
|
+
hide() {
|
|
25
|
+
this.#state.floatable.hide();
|
|
21
26
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Opens the tooltip
|
|
29
|
+
*/
|
|
30
|
+
show() {
|
|
31
|
+
this.#state.floatable.show();
|
|
26
32
|
}
|
|
27
33
|
};
|
|
28
34
|
function activate(event, focus) {
|
|
@@ -30,34 +36,50 @@ function activate(event, focus) {
|
|
|
30
36
|
const content = findAncestor(event, SELECTOR_CONTENT);
|
|
31
37
|
if (anchor == null && content == null) {
|
|
32
38
|
if (!focused) {
|
|
33
|
-
closeTooltip(
|
|
34
|
-
|
|
39
|
+
closeTooltip(activeState);
|
|
40
|
+
activeState = void 0;
|
|
41
|
+
activeTooltip = void 0;
|
|
35
42
|
}
|
|
36
43
|
return;
|
|
37
44
|
}
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
const shouldDelay =
|
|
41
|
-
closeTooltip(
|
|
42
|
-
|
|
45
|
+
const state = states.get(anchor ?? content);
|
|
46
|
+
if (state == null || state.tooltip === activeTooltip) return;
|
|
47
|
+
const shouldDelay = activeTooltip == null;
|
|
48
|
+
closeTooltip(activeState);
|
|
49
|
+
activeState = state;
|
|
50
|
+
activeTooltip = state.tooltip;
|
|
43
51
|
focused = focus;
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
state.timer = setTimeout(() => {
|
|
53
|
+
state.content.showPopover({ source: state.anchor });
|
|
46
54
|
setTimeout(() => {
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
state.floatable.update();
|
|
56
|
+
state.content.setAttribute(ATTRIBUTE_CONTENT_OPEN, "");
|
|
49
57
|
});
|
|
50
58
|
}, shouldDelay ? delay : 0);
|
|
51
59
|
}
|
|
52
|
-
function
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
function addTooltip(element) {
|
|
61
|
+
if (!states.has(element)) createTooltip(element);
|
|
62
|
+
}
|
|
63
|
+
function closeTooltip(state) {
|
|
64
|
+
if (state == null) return;
|
|
65
|
+
if (state.timer != null) clearTimeout(state.timer);
|
|
66
|
+
state.content?.hidePopover();
|
|
67
|
+
state.content?.removeAttribute(ATTRIBUTE_CONTENT_OPEN);
|
|
57
68
|
}
|
|
58
69
|
function createTooltip(anchor) {
|
|
59
70
|
const content = getContent(anchor);
|
|
60
|
-
if (content
|
|
71
|
+
if (content == null) return;
|
|
72
|
+
anchor.insertAdjacentElement("afterend", content);
|
|
73
|
+
states.set(anchor, getState(anchor, content));
|
|
74
|
+
}
|
|
75
|
+
function destroyTooltip(state) {
|
|
76
|
+
clearTimeout(state.timer);
|
|
77
|
+
state.content.remove();
|
|
78
|
+
state.floatable.destroy();
|
|
79
|
+
state.anchor = void 0;
|
|
80
|
+
state.content = void 0;
|
|
81
|
+
state.floatable = void 0;
|
|
82
|
+
state.tooltip = void 0;
|
|
61
83
|
}
|
|
62
84
|
function findElements(attribute) {
|
|
63
85
|
const ids = attribute?.split(EXPRESSION_WHITESPACE) ?? [];
|
|
@@ -97,27 +119,40 @@ function getElements(anchor) {
|
|
|
97
119
|
paragraph.textContent = content;
|
|
98
120
|
return [paragraph];
|
|
99
121
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
122
|
+
function getState(anchor, content) {
|
|
123
|
+
const state = {
|
|
124
|
+
anchor,
|
|
125
|
+
content,
|
|
126
|
+
floatable: createEmbeddedFloatable(anchor, content, options),
|
|
127
|
+
tooltip: null
|
|
128
|
+
};
|
|
129
|
+
state.tooltip = new OuiTooltip(state);
|
|
130
|
+
return state;
|
|
131
|
+
}
|
|
132
|
+
function getTooltip(element) {
|
|
133
|
+
return states.get(element)?.tooltip;
|
|
102
134
|
}
|
|
103
135
|
function onFocusin(event) {
|
|
104
136
|
activate(event, true);
|
|
105
137
|
}
|
|
106
138
|
function onFocusout() {
|
|
107
|
-
if (
|
|
139
|
+
if (activeTooltip == null) return;
|
|
108
140
|
if (focusTimer != null) clearTimeout(focusTimer);
|
|
109
141
|
focusTimer = setTimeout(() => {
|
|
110
|
-
if (
|
|
111
|
-
closeTooltip(
|
|
112
|
-
|
|
142
|
+
if (activeTooltip == null || document.activeElement == null || findAncestor(document.activeElement, SELECTOR) != null) return;
|
|
143
|
+
closeTooltip(activeState);
|
|
144
|
+
activeState = void 0;
|
|
145
|
+
activeTooltip = void 0;
|
|
113
146
|
});
|
|
114
147
|
}
|
|
115
148
|
function onPointermove(event) {
|
|
116
149
|
activate(event, false);
|
|
117
150
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
function removeTooltip(element) {
|
|
152
|
+
const state = states.get(element);
|
|
153
|
+
if (state == null) return;
|
|
154
|
+
destroyTooltip(state);
|
|
155
|
+
states.delete(element);
|
|
121
156
|
}
|
|
122
157
|
/**
|
|
123
158
|
* Set the delay before showing the tooltip, in milliseconds
|
|
@@ -136,21 +171,22 @@ const CONTENT_TAGNAME = "div";
|
|
|
136
171
|
const EXPRESSION_WHITESPACE = /\s+/;
|
|
137
172
|
const ATTRIBUTE = "oui-tooltip";
|
|
138
173
|
const ATTRIBUTE_CONTENT = `${ATTRIBUTE}-content`;
|
|
139
|
-
const
|
|
174
|
+
const ATTRIBUTE_CONTENT_OPEN = `${ATTRIBUTE_CONTENT}-open`;
|
|
140
175
|
const SELECTOR = `[${ATTRIBUTE}]`;
|
|
141
176
|
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
142
|
-
const
|
|
177
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
143
178
|
const options = {
|
|
144
179
|
attribute: `${ATTRIBUTE}-position`,
|
|
145
180
|
position: "above"
|
|
146
181
|
};
|
|
147
182
|
let delay = 500;
|
|
148
183
|
let focused = false;
|
|
149
|
-
let
|
|
184
|
+
let activeState;
|
|
185
|
+
let activeTooltip;
|
|
150
186
|
let focusTimer;
|
|
151
187
|
on(document, "focusin", onFocusin);
|
|
152
188
|
on(document, "focusout", onFocusout);
|
|
153
189
|
on(document, "pointermove", onPointermove);
|
|
154
|
-
attributable(ATTRIBUTE,
|
|
190
|
+
attributable(ATTRIBUTE, addTooltip, removeTooltip);
|
|
155
191
|
//#endregion
|
|
156
|
-
export { setTooltipDelay };
|
|
192
|
+
export { getTooltip, setTooltipDelay };
|