@oscarpalmer/oui 0.17.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 (58) 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/reset.css +176 -56
  6. package/dist/css/tooltip.css +8 -0
  7. package/dist/js/attributable.full.js +48 -0
  8. package/dist/js/attributable.js +30 -45
  9. package/dist/js/floatable.full.js +413 -0
  10. package/dist/js/floatable.js +254 -169
  11. package/dist/js/focus-trap/embedded.full.js +351 -0
  12. package/dist/js/focus-trap/embedded.js +131 -131
  13. package/dist/js/focus-trap/standalone.full.js +402 -0
  14. package/dist/js/focus-trap/standalone.js +4 -4
  15. package/dist/js/index.js +4 -5
  16. package/dist/js/oui.full.js +1596 -0
  17. package/dist/js/popover.full.js +822 -0
  18. package/dist/js/popover.js +83 -162
  19. package/dist/js/splitter.full.js +605 -0
  20. package/dist/js/splitter.js +263 -245
  21. package/dist/js/tooltip.full.js +679 -0
  22. package/dist/js/tooltip.js +159 -150
  23. package/package.json +31 -59
  24. package/src/css/content.scss +1 -2
  25. package/src/css/form.scss +12 -4
  26. package/src/css/message.scss +3 -0
  27. package/src/css/mixins.scss +7 -0
  28. package/src/css/reset.scss +180 -54
  29. package/src/css/tooltip.scss +8 -0
  30. package/src/js/floatable.ts +380 -133
  31. package/src/js/focus-trap/embedded.ts +149 -64
  32. package/src/js/focus-trap/standalone.ts +4 -4
  33. package/src/js/index.ts +1 -0
  34. package/src/js/popover.ts +50 -120
  35. package/src/js/splitter.ts +250 -103
  36. package/src/js/tooltip.ts +155 -68
  37. package/types/floatable.d.ts +17 -10
  38. package/types/focus-trap/embedded.d.ts +18 -5
  39. package/types/index.d.ts +1 -0
  40. package/types/popover.d.ts +2 -0
  41. package/types/splitter.d.ts +13 -1
  42. package/types/tooltip.d.ts +2 -1
  43. package/dist/js/attributable.cjs +0 -51
  44. package/dist/js/floatable.cjs +0 -176
  45. package/dist/js/focus-trap/embedded.cjs +0 -150
  46. package/dist/js/focus-trap/standalone.cjs +0 -8
  47. package/dist/js/index.cjs +0 -6
  48. package/dist/js/popover.cjs +0 -166
  49. package/dist/js/splitter.cjs +0 -272
  50. package/dist/js/tooltip.cjs +0 -166
  51. package/types/attributable.d.cts +0 -5
  52. package/types/floatable.d.cts +0 -20
  53. package/types/focus-trap/embedded.d.cts +0 -15
  54. package/types/focus-trap/standalone.d.cts +0 -3
  55. package/types/index.d.cts +0 -14
  56. package/types/popover.d.cts +0 -14
  57. package/types/splitter.d.cts +0 -10
  58. package/types/tooltip.d.cts +0 -3
@@ -1,176 +1,261 @@
1
- class Floatable {
2
- constructor(anchor, content, attribute, position, preferAbove) {
3
- this.anchor = anchor;
4
- this.content = content;
5
- this.attribute = attribute;
6
- this.position = position;
7
- this.preferAbove = preferAbove;
8
- setStyles(content);
9
- }
10
- activate;
11
- active = false;
12
- deactivate;
13
- frame;
14
- destroy() {
15
- this.activate = void 0;
16
- this.deactivate = void 0;
17
- this.frame = void 0;
18
- this.anchor = void 0;
19
- this.content = void 0;
20
- }
21
- }
22
- function activate(floatable, active, order, time) {
23
- stop(floatable);
24
- if (floatable.active) {
25
- return;
26
- }
27
- if (time == null || time === 0) {
28
- onActivate(floatable, active, order);
29
- } else {
30
- floatable.activate = +setTimeout(() => {
31
- onActivate(floatable, active, order);
32
- }, time);
33
- }
34
- }
35
- function deactivate(floatable, active, order, time) {
36
- stop(floatable);
37
- if (!floatable.active) {
38
- return;
39
- }
40
- if (time == null || time === 0) {
41
- onDeactivate(floatable, active, order);
42
- } else {
43
- floatable.deactivate = +setTimeout(() => {
44
- onDeactivate(floatable, active, order);
45
- }, time);
46
- }
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;
47
79
  }
48
80
  function getX(anchor, content, position) {
49
- if (position.endsWith("-end")) {
50
- const end = anchor.right - content.width;
51
- return end - margin < 0 ? margin : end;
52
- }
53
- if (position.endsWith("-start")) {
54
- const start = anchor.left;
55
- return start + content.width + margin > window.innerWidth ? window.innerWidth - content.width - margin : start;
56
- }
57
- if (position.startsWith("end")) {
58
- return anchor.right + margin;
59
- }
60
- if (position.startsWith("start")) {
61
- return anchor.left - content.width - margin;
62
- }
63
- if (position.startsWith("horizontal")) {
64
- if (anchor.right + content.width + margin <= window.innerWidth) {
65
- return anchor.right + margin;
66
- }
67
- return anchor.left - content.width - margin;
68
- }
69
- return anchor.left + (anchor.width - content.width) / 2;
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;
70
97
  }
71
98
  function getY(anchor, content, position, preferAbove) {
72
- if (position.startsWith("above")) {
73
- return anchor.top - content.height - margin;
74
- }
75
- if (position.startsWith("below")) {
76
- return anchor.bottom + margin;
77
- }
78
- if (position.endsWith("bottom")) {
79
- const bottom = anchor.bottom - content.height;
80
- return bottom + margin < 0 ? margin : bottom;
81
- }
82
- if (position.endsWith("top")) {
83
- const top = anchor.top;
84
- return top + content.height + margin > window.innerHeight ? window.innerHeight - content.height - margin : top;
85
- }
86
- if (position.startsWith("vertical")) {
87
- if (preferAbove && anchor.top - content.height - margin >= 0) {
88
- return anchor.top - content.height - margin;
89
- }
90
- if (anchor.bottom + content.height + margin <= document.body.clientHeight) {
91
- return anchor.bottom + margin;
92
- }
93
- return anchor.top - content.height - margin;
94
- }
95
- return anchor.top + (anchor.height - content.height) / 2;
96
- }
97
- function onActivate(floatable, active, order) {
98
- floatable.active = true;
99
- active.add(floatable);
100
- if (order != null) {
101
- order.push(floatable);
102
- }
103
- document.body.append(floatable.content);
104
- floatable.content.hidden = false;
105
- update(floatable);
106
- }
107
- function onDeactivate(floatable, active, order) {
108
- floatable.active = false;
109
- active.delete(floatable);
110
- const index = order?.indexOf(floatable) ?? -1;
111
- if (index > -1) {
112
- order?.splice(index, 1);
113
- }
114
- floatable.content.hidden = true;
115
- floatable.anchor.insertAdjacentElement("afterend", floatable.content);
116
- }
117
- function update(floatable) {
118
- let position = floatable.anchor.getAttribute(floatable.attribute)?.trim() ?? floatable.position;
119
- if (!positions.has(position)) {
120
- position = floatable.position;
121
- }
122
- function run() {
123
- const anchor = floatable.anchor.getBoundingClientRect();
124
- const content = floatable.content.getBoundingClientRect();
125
- const left = getX(anchor, content, position);
126
- const top = getY(anchor, content, position, floatable.preferAbove);
127
- floatable.content.style.inset = `${top}px auto auto ${left}px`;
128
- floatable.frame = requestAnimationFrame(run);
129
- }
130
- floatable.frame = requestAnimationFrame(run);
131
- }
132
- function setStyles(content) {
133
- content.style.position = "fixed";
134
- content.style.inset = "0 auto auto 0";
135
- content.style.zIndex = "10";
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);
136
148
  }
137
149
  function stop(floatable) {
138
- if (floatable.frame != null) {
139
- cancelAnimationFrame(floatable.frame);
140
- }
141
- if (floatable.activate != null) {
142
- clearTimeout(floatable.activate);
143
- }
144
- if (floatable.deactivate != null) {
145
- clearTimeout(floatable.deactivate);
146
- }
147
- floatable.activate = void 0;
148
- floatable.deactivate = void 0;
149
- floatable.frame = void 0;
150
- }
151
- const margin = Number.parseInt(getComputedStyle(document.documentElement).fontSize, 10) / 2;
152
- const positions = /* @__PURE__ */ new Set([
153
- "above",
154
- "above-end",
155
- "above-start",
156
- "below",
157
- "below-end",
158
- "below-start",
159
- "end",
160
- "end-bottom",
161
- "end-top",
162
- "horizontal",
163
- "horizontal-bottom",
164
- "horizontal-top",
165
- "start",
166
- "start-bottom",
167
- "start-top",
168
- "vertical",
169
- "vertical-end",
170
- "vertical-start"
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"
171
235
  ]);
172
- export {
173
- Floatable,
174
- activate,
175
- deactivate
176
- };
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 };