@oscarpalmer/oui 0.21.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.
@@ -1,2 +1,2 @@
1
- import { a as DraggableState, i as DraggableBound, n as DragMovePosition, o as addDraggable, r as Draggable, s as removeDraggable } from "../movable-BEo2LbUI.mjs";
1
+ import { a as DraggableState, i as DraggableBound, n as DragMovePosition, o as addDraggable, r as Draggable, s as removeDraggable } from "../movable-BKFgoaOl.mjs";
2
2
  export { DragMovePosition, Draggable, DraggableBound, DraggableState, addDraggable, removeDraggable };
@@ -5,6 +5,9 @@ import { toggleStyles } from "@oscarpalmer/toretto/style";
5
5
  import supportsTouch from "@oscarpalmer/toretto/touch";
6
6
  //#region src/js/internal/draggable.ts
7
7
  var Draggable = class {
8
+ prefix;
9
+ element;
10
+ options;
8
11
  container;
9
12
  hasHandles;
10
13
  horizontal = true;
@@ -17,10 +17,10 @@ type Options = {
17
17
  defaultPosition: Position;
18
18
  interactive: boolean;
19
19
  positionAttribute: string;
20
- preferAbove: boolean;
20
+ reusable: boolean;
21
21
  onAfter?: (active: boolean) => void;
22
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';
23
+ type Position = 'above' | 'above-end' | 'above-start' | 'below' | 'below-end' | 'below-start' | 'end' | 'end-bottom' | 'end-top' | 'start' | 'start-bottom' | 'start-top';
24
24
  declare function deactivate(floatable: Floatable): void;
25
25
  //#endregion
26
26
  export { Floatable, deactivate };
@@ -2,6 +2,7 @@ import { on } from "@oscarpalmer/toretto/event";
2
2
  import { findAncestor } from "@oscarpalmer/toretto/find";
3
3
  //#region src/js/internal/floatable.ts
4
4
  var Floatable = class {
5
+ options;
5
6
  active = false;
6
7
  disabled = false;
7
8
  frame;
@@ -51,7 +52,7 @@ function activate(floatable) {
51
52
  floatable.active = true;
52
53
  document.body.append(floatable.options.content);
53
54
  floatable.options.content.hidden = false;
54
- updatePosition(floatable);
55
+ setPosition(floatable);
55
56
  }
56
57
  function closeAbove(element) {
57
58
  const state = getState();
@@ -71,82 +72,30 @@ function closeNonInteractive(state) {
71
72
  }
72
73
  function deactivate(floatable) {
73
74
  floatable.active = false;
74
- floatable.options.content.hidden = true;
75
- floatable.options.anchor.insertAdjacentElement(INSERT_AFTEREND, floatable.options.content);
76
- floatable.options.onAfter?.(false);
77
- }
78
- function getAttribute(position, anchor, top, left) {
79
- switch (position) {
80
- case "horizontal":
81
- case "horizontal-bottom":
82
- case "horizontal-top": return position.replace("horizontal", left >= anchor.right ? "right" : "left");
83
- case "vertical":
84
- case "vertical-end":
85
- case "vertical-start": return position.replace("vertical", top >= anchor.bottom ? "bottom" : "top");
86
- default: break;
75
+ if (!floatable.options.reusable) {
76
+ floatable.options.content.hidden = true;
77
+ floatable.options.anchor.insertAdjacentElement(INSERT_AFTEREND, floatable.options.content);
87
78
  }
79
+ floatable.options.onAfter?.(false);
88
80
  }
89
- function getMargin() {
90
- try {
91
- const fontsize = parseFloat(getComputedStyle(document.documentElement).fontSize);
92
- if (Number.isNaN(fontsize)) return 5;
93
- const margin = fontsize / 2;
94
- return margin < 5 ? 5 : margin;
95
- } catch {
96
- return 5;
97
- }
81
+ function getAnchorName() {
82
+ index += 1;
83
+ return `--oui-floatable-anchor-${index}`;
98
84
  }
99
85
  function getState() {
100
86
  return window._oscarpalmer_oui_floatable;
101
87
  }
102
- function getX(anchor, content, position, margin) {
103
- if (position === "above" || position === "below" || position === "vertical") {
104
- const center = anchor.left + anchor.width / 2;
105
- const width = content.width / 2;
106
- if (center + width + margin >= document.body.clientWidth) return document.body.clientWidth - content.width - margin / 2;
107
- if (center - width - margin <= 0) return margin / 2;
108
- return center - width;
109
- }
110
- if (position.startsWith("end")) return anchor.right;
111
- if (position.startsWith("start")) return anchor.left - content.width;
112
- if (position.endsWith("end")) return anchor.right - content.width - margin <= 0 ? margin / 2 : anchor.right - content.width;
113
- if (position.endsWith("start")) {
114
- if (anchor.left + content.width + margin >= document.body.clientWidth) return document.body.clientWidth - content.width - margin / 2;
115
- return anchor.left - margin <= 0 ? margin / 2 : anchor.left;
116
- }
117
- if (!position.startsWith("horizontal")) return 0;
118
- if (anchor.right + content.width + margin >= document.body.clientWidth) return anchor.left - content.width;
119
- return anchor.right;
120
- }
121
- function getY(anchor, content, position, margin, preferAbove) {
122
- if (position === "end" || position === "start" || position === "horizontal") {
123
- const center = anchor.top + anchor.height / 2;
124
- const height = content.height / 2;
125
- if (center + height + margin >= document.body.clientHeight) return document.body.clientHeight - content.height - margin / 2;
126
- if (center - height - margin <= 0) return margin / 2;
127
- return center - height;
128
- }
129
- if (position.startsWith("above")) return anchor.top - content.height;
130
- if (position.startsWith("below")) return anchor.bottom;
131
- if (position.endsWith("bottom")) return anchor.bottom - content.height - margin <= 0 ? margin / 2 : anchor.bottom - content.height;
132
- if (position.endsWith("top")) return anchor.top + content.height + margin >= document.body.clientHeight ? document.body.clientHeight - content.height - margin : anchor.top;
133
- if (!position.startsWith("vertical")) return 0;
134
- if (preferAbove) {
135
- if (anchor.top - content.height - margin <= 0 && anchor.bottom + content.height + margin <= document.body.clientHeight) return anchor.bottom;
136
- return anchor.top - content.height;
137
- }
138
- if (anchor.bottom + content.height + margin >= document.body.clientHeight) return anchor.top - content.height;
139
- return anchor.bottom;
140
- }
141
88
  function initialize(floatable) {
142
89
  const { anchor, content, interactive } = floatable.options;
143
90
  if (interactive) {
144
91
  anchor.setAttribute(ATTRIBUTE_ANCHOR, "");
145
92
  content.setAttribute(ATTRIBUTE_CONTENT, "");
146
93
  }
147
- content.style.position = "fixed";
148
- content.style.inset = "0 auto auto 0";
149
- content.style.zIndex = "10";
94
+ const anchorName = getAnchorName();
95
+ anchor.style.anchorName = anchorName;
96
+ content.style.position = "absolute";
97
+ content.style.positionAnchor = anchorName;
98
+ content.style.positionTry = "normal flip-start";
150
99
  const state = getState();
151
100
  state?.mapped.set(anchor, floatable);
152
101
  state?.mapped.set(content, floatable);
@@ -171,10 +120,13 @@ function onKeyDown(event) {
171
120
  const floatable = state.mapped.get(related);
172
121
  if (floatable != null) floatable.toggle(false);
173
122
  }
174
- function setAttribute(position, anchor, floater, top, left) {
175
- let actual;
176
- if (/^(horizontal|vertical)/.test(position)) actual = getAttribute(position, anchor, top, left);
177
- floater.setAttribute("position", actual ?? position);
123
+ function setPosition(floatable) {
124
+ let position = floatable.options.anchor.getAttribute(floatable.options.positionAttribute) ?? floatable.options.defaultPosition;
125
+ if (AREAS[position] == null) position = floatable.options.defaultPosition;
126
+ const area = AREAS[position];
127
+ floatable.options.content.style.positionArea = area;
128
+ floatable.options.content.setAttribute(ATTRIBUTE_POSITION, POSITIONS[area]);
129
+ floatable.options.onAfter?.(true);
178
130
  }
179
131
  function stop(floatable) {
180
132
  if (floatable.frame != null) cancelAnimationFrame(floatable.frame);
@@ -192,77 +144,28 @@ function toggle(anchor) {
192
144
  else closeAbove(content);
193
145
  floatable.toggle(!active);
194
146
  }
195
- function updatePosition(floatable) {
196
- let position = floatable.options.anchor.getAttribute(floatable.options.positionAttribute)?.trim() ?? floatable.options.defaultPosition;
197
- if (!POSITIONS.has(position)) position = floatable.options.defaultPosition;
198
- const margin = getMargin();
199
- let previousAnchor;
200
- let previousContent;
201
- let calculated = false;
202
- let runOnAfter = false;
203
- function run() {
204
- const anchor = floatable.options.anchor.getBoundingClientRect();
205
- const content = floatable.options.content.getBoundingClientRect();
206
- if (calculated) {
207
- if (previousAnchor != null && previousContent != null) {
208
- if (PROPERTIES.every((property) => anchor[property] === previousAnchor[property]) && PROPERTIES.every((property) => content[property] === previousContent[property])) {
209
- floatable.frame = requestAnimationFrame(run);
210
- return;
211
- }
212
- }
213
- } else calculated = true;
214
- update(anchor, content);
215
- }
216
- function update(anchor, content) {
217
- previousAnchor = anchor;
218
- previousContent = content;
219
- const left = getX(anchor, content, position, margin);
220
- const top = getY(anchor, content, position, margin, floatable.options.preferAbove);
221
- setAttribute(position, anchor, floatable.options.content, top, left);
222
- if (top + content.height > window.innerHeight) floatable.options.content.style.height = `${window.innerHeight - top}px`;
223
- else floatable.options.content.style.height = "";
224
- if (left + content.width > window.innerWidth) floatable.options.content.style.width = `${window.innerWidth - left}px`;
225
- else floatable.options.content.style.width = "";
226
- floatable.options.content.style.transform = `translate3d(${left}px, ${top}px, 0)`;
227
- floatable.frame = requestAnimationFrame(run);
228
- if (!runOnAfter) {
229
- runOnAfter = true;
230
- floatable.options.onAfter?.(true);
231
- }
232
- }
233
- floatable.frame = requestAnimationFrame(run);
234
- }
147
+ const AREAS = {
148
+ above: "block-start",
149
+ "above-end": "start span-start",
150
+ "above-start": "start span-end",
151
+ below: "block-end",
152
+ "below-end": "end span-start",
153
+ "below-start": "end span-end",
154
+ end: "center end",
155
+ "end-bottom": "span-start end",
156
+ "end-top": "span-end end",
157
+ start: "center start",
158
+ "start-bottom": "span-start start",
159
+ "start-top": "span-end start"
160
+ };
235
161
  const ATTRIBUTE_ANCHOR = "oui-floatable-anchor";
236
162
  const ATTRIBUTE_CONTENT = "oui-floatable-content";
163
+ const ATTRIBUTE_POSITION = "oui-position";
237
164
  const INSERT_AFTEREND = "afterend";
238
- const POSITIONS = new Set([
239
- "above",
240
- "above-end",
241
- "above-start",
242
- "below",
243
- "below-end",
244
- "below-start",
245
- "end",
246
- "end-bottom",
247
- "end-top",
248
- "horizontal",
249
- "horizontal-bottom",
250
- "horizontal-top",
251
- "start",
252
- "start-bottom",
253
- "start-top",
254
- "vertical",
255
- "vertical-end",
256
- "vertical-start"
257
- ]);
258
- const PROPERTIES = [
259
- "bottom",
260
- "left",
261
- "right",
262
- "top"
263
- ];
165
+ const POSITIONS = Object.fromEntries(Object.entries(AREAS).map(([position, area]) => [area, position]));
264
166
  const SELECTOR_ALL = `[${ATTRIBUTE_ANCHOR}], [${ATTRIBUTE_CONTENT}]`;
265
167
  const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
168
+ let index = 0;
266
169
  if (window._oscarpalmer_oui_floatable == null) {
267
170
  Object.defineProperty(window, "_oscarpalmer_oui_floatable", {
268
171
  enumerable: false,
@@ -1,2 +1,2 @@
1
- import { t as Movable } from "./movable-BEo2LbUI.mjs";
1
+ import { t as Movable } from "./movable-BKFgoaOl.mjs";
2
2
  export { Movable };
@@ -25,7 +25,7 @@ var OuiPopoverElement = class extends HTMLElement {
25
25
  defaultPosition: "below-start",
26
26
  interactive: true,
27
27
  positionAttribute: "position",
28
- preferAbove: false,
28
+ reusable: false,
29
29
  onAfter: (active) => {
30
30
  if (active) focus(content);
31
31
  else if (!this.#floatable.ignoreFocus) toggle.focus();
@@ -1,2 +1,2 @@
1
- import { c as Sortable } from "./movable-BEo2LbUI.mjs";
1
+ import { c as Sortable } from "./movable-BKFgoaOl.mjs";
2
2
  export { Sortable };
@@ -1,4 +1,4 @@
1
1
  //#region src/js/tooltip.d.ts
2
- declare function setTooltipDelay(value: number): void;
2
+ declare function setTooltipDelay(open?: number, close?: number): void;
3
3
  //#endregion
4
4
  export { setTooltipDelay };
@@ -5,89 +5,74 @@ 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
- attributes;
8
+ elements;
9
9
  floatable;
10
- timer;
11
- constructor(anchor, content) {
12
- this.attributes = content.attributes;
10
+ constructor(anchor, elements) {
11
+ const content = getContent();
12
+ this.elements = elements;
13
13
  this.floatable = new Floatable({
14
14
  anchor,
15
- content: content.element,
16
- defaultPosition: "vertical",
15
+ content,
16
+ defaultPosition: "above",
17
17
  interactive: false,
18
- preferAbove: true,
19
- positionAttribute: ATTRIBUTE_POSITION
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 { attributes, floatable } = this;
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.attributes = void 0;
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 content = getContent(anchor);
39
- if (content != null) TOOLTIPS_ALL.set(anchor, new Tooltip(anchor, content));
47
+ const elements = getElements(anchor);
48
+ if (elements != null) TOOLTIPS_ALL.set(anchor, new Tooltip(anchor, elements));
40
49
  }
41
- function closeTooltips(next) {
42
- const tooltips = [...TOOLTIPS_ACTIVE];
43
- for (const active of tooltips) {
44
- if (active === next) continue;
45
- if (active.timer != null) clearTimeout(active.timer);
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 describedElements = getElements(attributes.ariaDescribedby);
71
- if (describedElements.length > 0) {
72
- anchor.setAttribute("aria-describedby", wrapper.id);
73
- for (const element of describedElements) wrapper.append(element);
74
- return {
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
- let content = anchor.getAttribute("aria-label");
80
- if (isNullableOrWhitespace(content)) content = anchor.getAttribute("aria-description");
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
- wrapper.append(paragraph);
85
- return {
86
- attributes,
87
- element: wrapper
88
- };
73
+ return [paragraph];
89
74
  }
90
- function getElements(attribute) {
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 getWrapper(anchor) {
105
- const wrapper = document.createElement("div");
106
- wrapper.setAttribute(ATTRIBUTE_CONTENT, "");
107
- wrapper.className = anchor.getAttribute(ATTRIBUTE_CLASS) ?? "";
108
- wrapper.hidden = true;
109
- wrapper.id = `${SELECTOR}-${++index}`;
110
- wrapper.role = "tooltip";
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, SELECTOR_FULL);
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.options.content.hasAttribute("oui-tooltip-content-active") === activate) return;
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
- if (tooltip.timer != null) clearTimeout(tooltip.timer);
133
- TOOLTIPS_ACTIVE.add(tooltip);
134
- tooltip.floatable.toggle(true);
135
- requestAnimationFrame(() => {
136
- tooltip.floatable.options.content.setAttribute("oui-tooltip-content-active", "");
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
- tooltip.floatable.options.content.removeAttribute("oui-tooltip-content-active");
141
- tooltip.timer ??= setTimeout(() => {
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 reset(anchor, attributes) {
151
- if (attributes.ariaDescribedby == null) anchor.removeAttribute("aria-describedby");
152
- else anchor.setAttribute("aria-describedby", attributes.ariaDescribedby);
153
- }
154
- function setTooltipDelay(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 SELECTOR = "oui-tooltip";
158
- const ATTRIBUTE_CLASS = `${SELECTOR}-class`;
159
- const ATTRIBUTE_CONTENT = `${SELECTOR}-content`;
160
- const ATTRIBUTE_POSITION = `${SELECTOR}-position`;
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
- let delay = 500;
167
- let index = 0;
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(SELECTOR, addTooltip, removeTooltip);
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
174
  export { setTooltipDelay };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/oui",
3
- "version": "0.21.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"
@@ -66,16 +74,16 @@
66
74
  "@oscarpalmer/toretto": "^0.46"
67
75
  },
68
76
  "devDependencies": {
69
- "@oxlint/plugins": "^1.67",
77
+ "@oxlint/plugins": "^1.68",
70
78
  "@vitest/coverage-istanbul": "^4.1",
71
79
  "autoprefixer": "^10.5",
72
- "concurrently": "^9.2",
80
+ "concurrently": "^10",
73
81
  "jsdom": "^29.1",
74
82
  "postcss-cli": "^11",
75
83
  "postcss-input-range": "^6",
76
84
  "postcss-scss": "^4",
77
85
  "sass": "^1.100",
78
- "stylelint": "^17.11",
86
+ "stylelint": "^17.12",
79
87
  "stylelint-config-standard-scss": "^17",
80
88
  "tsdown": "^0.22",
81
89
  "typescript": "^6",
@@ -84,4 +92,4 @@
84
92
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
85
93
  },
86
94
  "packageManager": "npm@11.12.1"
87
- }
95
+ }
@@ -60,24 +60,20 @@
60
60
  margin: var(--margin-top, 0) 0 0 var(--margin-left, 0);
61
61
  pointer-events: all !important;
62
62
 
63
- &[position^='above'],
64
- &[position^='top'] {
65
- --margin-top: -0.5rem;
63
+ &[oui-position^='above'] {
64
+ margin: 0 0 0.5em;
66
65
  }
67
66
 
68
- &[position^='below'],
69
- &[position^='bottom'] {
70
- --margin-top: 0.5rem;
67
+ &[oui-position^='below'] {
68
+ margin: 0.5em 0 0;
71
69
  }
72
70
 
73
- &[position^='end'],
74
- &[position^='right'] {
75
- --margin-left: 0.5rem;
71
+ &[oui-position^='end'] {
72
+ margin: 0 0 0 0.5em;
76
73
  }
77
74
 
78
- &[position^='left'],
79
- &[position^='start'] {
80
- --margin-left: -0.5rem;
75
+ &[oui-position^='start'] {
76
+ margin: 0 0.5em 0 0;
81
77
  }
82
78
  }
83
79
 
@@ -6,7 +6,7 @@
6
6
  padding: 1rem;
7
7
  overscroll-behavior: contain;
8
8
  background-color: var(--oui-absolute);
9
- border-radius: 0.5rem;
9
+ border-radius: 0.25rem;
10
10
 
11
11
  &:focus {
12
12
  outline: none;