@oscarpalmer/oui 0.16.0 → 0.18.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.
Files changed (62) hide show
  1. package/dist/css/badge.css +3 -3
  2. package/dist/css/content.css +9 -11
  3. package/dist/css/form.css +36 -22
  4. package/dist/css/message.css +4 -0
  5. package/dist/css/popover.css +3 -0
  6. package/dist/css/reset.css +176 -56
  7. package/dist/css/splitter.css +3 -3
  8. package/dist/css/tooltip.css +8 -0
  9. package/dist/js/attributable.full.js +48 -0
  10. package/dist/js/attributable.js +30 -45
  11. package/dist/js/floatable.full.js +413 -0
  12. package/dist/js/floatable.js +259 -132
  13. package/dist/js/focus-trap/embedded.full.js +351 -0
  14. package/dist/js/focus-trap/embedded.js +131 -131
  15. package/dist/js/focus-trap/standalone.full.js +402 -0
  16. package/dist/js/focus-trap/standalone.js +4 -4
  17. package/dist/js/index.js +4 -5
  18. package/dist/js/oui.full.js +1596 -0
  19. package/dist/js/popover.full.js +822 -0
  20. package/dist/js/popover.js +83 -156
  21. package/dist/js/splitter.full.js +605 -0
  22. package/dist/js/splitter.js +279 -200
  23. package/dist/js/tooltip.full.js +679 -0
  24. package/dist/js/tooltip.js +159 -144
  25. package/package.json +31 -59
  26. package/src/css/content.scss +1 -2
  27. package/src/css/form.scss +12 -4
  28. package/src/css/message.scss +3 -0
  29. package/src/css/mixins.scss +7 -0
  30. package/src/css/popover.scss +4 -0
  31. package/src/css/reset.scss +180 -54
  32. package/src/css/splitter.scss +3 -3
  33. package/src/css/tooltip.scss +8 -0
  34. package/src/js/floatable.ts +458 -124
  35. package/src/js/focus-trap/embedded.ts +149 -64
  36. package/src/js/focus-trap/standalone.ts +4 -4
  37. package/src/js/index.ts +1 -0
  38. package/src/js/popover.ts +51 -114
  39. package/src/js/splitter.ts +346 -109
  40. package/src/js/tooltip.ts +157 -63
  41. package/types/floatable.d.ts +19 -7
  42. package/types/focus-trap/embedded.d.ts +18 -5
  43. package/types/index.d.ts +1 -0
  44. package/types/popover.d.ts +2 -0
  45. package/types/splitter.d.ts +13 -1
  46. package/types/tooltip.d.ts +2 -1
  47. package/dist/js/attributable.cjs +0 -51
  48. package/dist/js/floatable.cjs +0 -134
  49. package/dist/js/focus-trap/embedded.cjs +0 -150
  50. package/dist/js/focus-trap/standalone.cjs +0 -8
  51. package/dist/js/index.cjs +0 -6
  52. package/dist/js/popover.cjs +0 -160
  53. package/dist/js/splitter.cjs +0 -211
  54. package/dist/js/tooltip.cjs +0 -160
  55. package/types/attributable.d.cts +0 -5
  56. package/types/floatable.d.cts +0 -16
  57. package/types/focus-trap/embedded.d.cts +0 -15
  58. package/types/focus-trap/standalone.d.cts +0 -3
  59. package/types/index.d.cts +0 -14
  60. package/types/popover.d.cts +0 -14
  61. package/types/splitter.d.cts +0 -10
  62. package/types/tooltip.d.cts +0 -3
@@ -1,134 +1,261 @@
1
- class Floatable {
2
- constructor(anchor, content) {
3
- this.anchor = anchor;
4
- this.content = content;
5
- setStyles(content);
6
- }
7
- activate;
8
- active = false;
9
- deactivate;
10
- frame;
11
- destroy() {
12
- this.activate = void 0;
13
- this.deactivate = void 0;
14
- this.frame = void 0;
15
- this.anchor = void 0;
16
- this.content = void 0;
17
- }
18
- }
19
- function activate(floatable, active, order, time) {
20
- stop(floatable);
21
- if (floatable.active) {
22
- return;
23
- }
24
- if (time == null || time === 0) {
25
- onActivate(floatable, active, order);
26
- } else {
27
- floatable.activate = +setTimeout(() => {
28
- onActivate(floatable, active, order);
29
- }, time);
30
- }
31
- }
32
- function deactivate(floatable, active, order, time) {
33
- stop(floatable);
34
- if (!floatable.active) {
35
- return;
36
- }
37
- if (time == null || time === 0) {
38
- onDeactivate(floatable, active, order);
39
- } else {
40
- floatable.deactivate = +setTimeout(() => {
41
- onDeactivate(floatable, active, order);
42
- }, time);
43
- }
44
- }
45
- function getX(anchor, content) {
46
- if (content.width + 2 * margin >= window.innerWidth) {
47
- return {
48
- left: margin,
49
- right: margin
50
- };
51
- }
52
- const left = anchor.left + (anchor.width - content.width) / 2;
53
- const right = window.innerWidth - margin;
54
- if (left < margin) {
55
- return {
56
- left: margin
57
- };
58
- }
59
- if (left + content.width > right) {
60
- return {
61
- right: margin
62
- };
63
- }
64
- return {
65
- left
66
- };
67
- }
68
- function getY(anchor, content) {
69
- let top = anchor.top - content.height - margin;
70
- if (top - margin >= 0) {
71
- return top;
72
- }
73
- top = anchor.bottom + margin;
74
- if (top + content.height + margin <= window.innerHeight) {
75
- return top;
76
- }
77
- return anchor.top + (anchor.height - content.height) / 2;
78
- }
79
- function onActivate(floatable, active, order) {
80
- floatable.active = true;
81
- active.add(floatable);
82
- if (order != null) {
83
- order.push(floatable);
84
- }
85
- document.body.append(floatable.content);
86
- floatable.content.hidden = false;
87
- update(floatable);
88
- }
89
- function onDeactivate(floatable, active, order) {
90
- floatable.active = false;
91
- active.delete(floatable);
92
- const index = order?.indexOf(floatable) ?? -1;
93
- if (index > -1) {
94
- order?.splice(index, 1);
95
- }
96
- floatable.content.hidden = true;
97
- floatable.anchor.insertAdjacentElement("afterend", floatable.content);
98
- }
99
- function update(floatable) {
100
- function run() {
101
- const anchor = floatable.anchor.getBoundingClientRect();
102
- const content = floatable.content.getBoundingClientRect();
103
- const { left, right } = getX(anchor, content);
104
- const top = getY(anchor, content);
105
- floatable.content.style.inset = `${top}px ${right == null ? "auto" : `${right}px`} auto ${left == null ? "auto" : `${left}px`}`;
106
- floatable.frame = requestAnimationFrame(run);
107
- }
108
- floatable.frame = requestAnimationFrame(run);
109
- }
110
- function setStyles(content) {
111
- content.style.position = "fixed";
112
- content.style.inset = "0 auto auto 0";
113
- content.style.zIndex = "10";
1
+ import { on } from "@oscarpalmer/toretto/event";
2
+ import { findAncestor } from "@oscarpalmer/toretto/find";
3
+ var Floatable = class {
4
+ active = false;
5
+ disabled = false;
6
+ frame;
7
+ ignoreFocus = false;
8
+ constructor(options) {
9
+ this.options = options;
10
+ initialize(this);
11
+ }
12
+ destroy() {
13
+ stop(this);
14
+ const state = getState();
15
+ state?.active.delete(this);
16
+ state?.mapped.delete(this.options.anchor);
17
+ state?.mapped.delete(this.options.content);
18
+ this.options.anchor = void 0;
19
+ this.options.content = void 0;
20
+ }
21
+ disable() {
22
+ if (this.disabled) return;
23
+ this.disabled = true;
24
+ stop(this);
25
+ getState()?.active.delete(this);
26
+ }
27
+ enable() {
28
+ if (!this.disabled) return;
29
+ this.disabled = false;
30
+ initialize(this);
31
+ }
32
+ toggle(active, ignoreFocus) {
33
+ if (this.disabled || this.active === active) return;
34
+ const state = getState();
35
+ if (state == null) return;
36
+ this.ignoreFocus = !active && (ignoreFocus ?? false);
37
+ stop(this);
38
+ if (active) state.active.add(this);
39
+ else state.active.delete(this);
40
+ if (this.options.interactive) if (active) state.order.push(this);
41
+ else {
42
+ const index = state.order.indexOf(this);
43
+ if (index > -1) state.order.splice(index, 1);
44
+ }
45
+ if (active) activate(this);
46
+ else deactivate(this);
47
+ }
48
+ };
49
+ function activate(floatable) {
50
+ floatable.active = true;
51
+ document.body.append(floatable.options.content);
52
+ floatable.options.content.hidden = false;
53
+ updatePosition(floatable);
54
+ }
55
+ function closeAbove(element) {
56
+ const state = getState();
57
+ if (state == null) return;
58
+ const index = state.order.findIndex((floatable) => floatable.options.anchor === element || floatable.options.content === element);
59
+ if (index === -1 || index === state.order.length - 1) return;
60
+ while (index < state.order.length - 1) state.order.pop()?.toggle(false, true);
61
+ }
62
+ function closeAll() {
63
+ const state = getState();
64
+ const floatables = [...state?.active ?? []];
65
+ state?.active.clear();
66
+ for (const floatable of floatables) floatable.toggle(false, true);
67
+ }
68
+ function closeNonInteractive(state) {
69
+ for (const floatable of state.active) if (!floatable.options.interactive) floatable.toggle(false);
70
+ }
71
+ function deactivate(floatable) {
72
+ floatable.active = false;
73
+ floatable.options.content.hidden = true;
74
+ floatable.options.anchor.insertAdjacentElement(INSERT_AFTEREND, floatable.options.content);
75
+ floatable.options.onAfter?.(false);
76
+ }
77
+ function getState() {
78
+ return window._oscarpalmer_oui_floatable;
79
+ }
80
+ function getX(anchor, content, position) {
81
+ if (position.endsWith(POSITION_SUFFIX_END)) {
82
+ const end = anchor.right - content.width;
83
+ return end < 0 ? 0 : end;
84
+ }
85
+ if (position.endsWith(POSITION_SUFFIX_START)) {
86
+ const start = anchor.left;
87
+ return start + content.width > window.innerWidth ? window.innerWidth - content.width : start;
88
+ }
89
+ if (position.startsWith(POSITION_END)) return anchor.right;
90
+ if (position.startsWith(POSITION_START)) return anchor.left - content.width;
91
+ if (position.startsWith(POSITION_HORIZONTAL)) {
92
+ if (anchor.right + content.width <= window.innerWidth) return anchor.right;
93
+ return anchor.left - content.width;
94
+ }
95
+ const left = anchor.left + (anchor.width - content.width) / 2;
96
+ return left < 0 ? 0 : left;
97
+ }
98
+ function getY(anchor, content, position, preferAbove) {
99
+ if (position.startsWith(POSITION_ABOVE)) return anchor.top - content.height;
100
+ if (position.startsWith(POSITION_BELOW)) return anchor.bottom;
101
+ if (position.endsWith(POSITION_BOTTOM)) {
102
+ const bottom = anchor.bottom - content.height;
103
+ return bottom < 0 ? 0 : bottom;
104
+ }
105
+ if (position.endsWith(POSITION_TOP)) {
106
+ const top = anchor.top;
107
+ return top + content.height > window.innerHeight ? window.innerHeight - content.height : top;
108
+ }
109
+ if (position.startsWith(POSITION_VERTICAL)) {
110
+ if (preferAbove && anchor.top - content.height >= 0) return anchor.top - content.height;
111
+ if (anchor.bottom + content.height <= document.body.clientHeight) return anchor.bottom;
112
+ return anchor.top - content.height;
113
+ }
114
+ return anchor.top + (anchor.height - content.height) / 2;
115
+ }
116
+ function initialize(floatable) {
117
+ const { anchor, content, interactive } = floatable.options;
118
+ if (interactive) {
119
+ anchor.setAttribute(ATTRIBUTE_ANCHOR, "");
120
+ content.setAttribute(ATTRIBUTE_CONTENT, "");
121
+ }
122
+ content.style.position = "fixed";
123
+ content.style.inset = "0 auto auto 0";
124
+ content.style.zIndex = "10";
125
+ const state = getState();
126
+ state?.mapped.set(anchor, floatable);
127
+ state?.mapped.set(content, floatable);
128
+ }
129
+ function isDisabled(element) {
130
+ return element.disabled || element.readOnly || element.getAttribute("aria-disabled") === "true";
131
+ }
132
+ function onClick(event) {
133
+ const related = findAncestor(event.target, SELECTOR_ALL);
134
+ if (!(related instanceof HTMLElement)) closeAll();
135
+ else if (related.hasAttribute(ATTRIBUTE_CONTENT)) closeAbove(related);
136
+ else if (!isDisabled(related)) toggle(related);
137
+ }
138
+ function onKeyDown(event) {
139
+ if (event.key !== "Escape") return;
140
+ const state = getState();
141
+ if (state == null || state.active.size === 0) return;
142
+ closeNonInteractive(state);
143
+ const related = findAncestor(event.target, SELECTOR_CONTENT);
144
+ if (!(related instanceof HTMLElement)) return;
145
+ closeAbove(related);
146
+ const floatable = state.mapped.get(related);
147
+ if (floatable != null) floatable.toggle(false);
114
148
  }
115
149
  function stop(floatable) {
116
- if (floatable.frame != null) {
117
- cancelAnimationFrame(floatable.frame);
118
- }
119
- if (floatable.activate != null) {
120
- clearTimeout(floatable.activate);
121
- }
122
- if (floatable.deactivate != null) {
123
- clearTimeout(floatable.deactivate);
124
- }
125
- floatable.activate = void 0;
126
- floatable.deactivate = void 0;
127
- floatable.frame = void 0;
128
- }
129
- const margin = Number.parseInt(getComputedStyle(document.documentElement).fontSize, 10) / 2;
130
- export {
131
- Floatable,
132
- activate,
133
- deactivate
134
- };
150
+ if (floatable.frame != null) cancelAnimationFrame(floatable.frame);
151
+ floatable.frame = void 0;
152
+ }
153
+ function toggle(anchor) {
154
+ const floatable = getState()?.mapped.get(anchor);
155
+ if (floatable == null) {
156
+ closeAll();
157
+ return;
158
+ }
159
+ const active = Boolean(floatable.active);
160
+ const content = findAncestor(floatable.options.anchor, SELECTOR_CONTENT);
161
+ if (content == null) closeAll();
162
+ else closeAbove(content);
163
+ floatable.toggle(!active);
164
+ }
165
+ function updatePosition(floatable) {
166
+ let position = floatable.options.anchor.getAttribute(floatable.options.positionAttribute)?.trim() ?? floatable.options.defaultPosition;
167
+ if (!POSITIONS.has(position)) position = floatable.options.defaultPosition;
168
+ let previousAnchor;
169
+ let previousContent;
170
+ let calculated = false;
171
+ let runOnAfter = false;
172
+ function run() {
173
+ const anchor = floatable.options.anchor.getBoundingClientRect();
174
+ const content = floatable.options.content.getBoundingClientRect();
175
+ if (calculated) {
176
+ if (previousAnchor != null && previousContent != null) {
177
+ if (PROPERTIES.every((property) => anchor[property] === previousAnchor[property]) && PROPERTIES.every((property) => content[property] === previousContent[property])) {
178
+ floatable.frame = requestAnimationFrame(run);
179
+ return;
180
+ }
181
+ }
182
+ } else calculated = true;
183
+ update(anchor, content);
184
+ }
185
+ function update(anchor, content) {
186
+ previousAnchor = anchor;
187
+ previousContent = content;
188
+ const left = getX(anchor, content, position);
189
+ const top = getY(anchor, content, position, floatable.options.preferAbove);
190
+ if (top + content.height > window.innerHeight) floatable.options.content.style.height = `${window.innerHeight - top}px`;
191
+ else floatable.options.content.style.height = "";
192
+ if (left + content.width > window.innerWidth) floatable.options.content.style.width = `${window.innerWidth - left}px`;
193
+ else floatable.options.content.style.width = "";
194
+ floatable.options.content.style.transform = `translate3d(${left}px, ${top}px, 0)`;
195
+ floatable.frame = requestAnimationFrame(run);
196
+ if (!runOnAfter) {
197
+ runOnAfter = true;
198
+ floatable.options.onAfter?.(true);
199
+ }
200
+ }
201
+ floatable.frame = requestAnimationFrame(run);
202
+ }
203
+ var ATTRIBUTE_ANCHOR = "oui-floatable-anchor";
204
+ var ATTRIBUTE_CONTENT = "oui-floatable-content";
205
+ var INSERT_AFTEREND = "afterend";
206
+ var POSITION_ABOVE = "above";
207
+ var POSITION_BELOW = "below";
208
+ var POSITION_BOTTOM = "bottom";
209
+ var POSITION_END = "end";
210
+ var POSITION_HORIZONTAL = "horizontal";
211
+ var POSITION_START = "start";
212
+ var POSITION_SUFFIX_END = "-end";
213
+ var POSITION_SUFFIX_START = "-start";
214
+ var POSITION_TOP = "top";
215
+ var POSITION_VERTICAL = "vertical";
216
+ var POSITIONS = new Set([
217
+ "above",
218
+ "above-end",
219
+ "above-start",
220
+ "below",
221
+ "below-end",
222
+ "below-start",
223
+ "end",
224
+ "end-bottom",
225
+ "end-top",
226
+ "horizontal",
227
+ "horizontal-bottom",
228
+ "horizontal-top",
229
+ "start",
230
+ "start-bottom",
231
+ "start-top",
232
+ "vertical",
233
+ "vertical-end",
234
+ "vertical-start"
235
+ ]);
236
+ var PROPERTIES = [
237
+ "bottom",
238
+ "left",
239
+ "right",
240
+ "top"
241
+ ];
242
+ var SELECTOR_ALL = `[${ATTRIBUTE_ANCHOR}], [${ATTRIBUTE_CONTENT}]`;
243
+ var SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
244
+ if (window._oscarpalmer_oui_floatable == null) {
245
+ Object.defineProperty(window, "_oscarpalmer_oui_floatable", {
246
+ enumerable: false,
247
+ value: {
248
+ active: /* @__PURE__ */ new Set(),
249
+ mapped: /* @__PURE__ */ new Map(),
250
+ order: []
251
+ },
252
+ writable: false
253
+ });
254
+ on(document, "keydown", (event) => {
255
+ onKeyDown(event);
256
+ });
257
+ on(document, "click", (event) => {
258
+ onClick(event);
259
+ });
260
+ }
261
+ export { Floatable, deactivate };