@oscarpalmer/oui 0.28.0 → 0.29.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/js/dialog.d.mts +1 -1
- package/dist/js/dialog.mjs +19 -20
- package/dist/js/{draggable-zszaW1qS.d.mts → draggable-fIYXd8eO.d.mts} +3 -3
- package/dist/js/floatable/floatable.embedded.d.mts +2 -2
- package/dist/js/floatable/floatable.embedded.mjs +17 -16
- package/dist/js/floatable/floatable.standalone.d.mts +1 -1
- package/dist/js/{floatable.embedded-YxGwzgbE.d.mts → floatable.embedded-DUR8bQBK.d.mts} +1 -2
- package/dist/js/focus-trap/focus-trap.embedded.mjs +6 -5
- package/dist/js/index.d.mts +2 -2
- package/dist/js/index.mjs +1 -0
- package/dist/js/internal/attributable.mjs +3 -4
- package/dist/js/internal/draggable.d.mts +1 -1
- package/dist/js/internal/draggable.mjs +11 -8
- package/dist/js/internal/navigable.d.mts +32 -0
- package/dist/js/internal/navigable.mjs +107 -0
- package/dist/js/menu.d.mts +1 -0
- package/dist/js/menu.mjs +168 -0
- package/dist/js/movable/movable.embedded.d.mts +1 -1
- package/dist/js/movable/movable.embedded.mjs +8 -13
- package/dist/js/movable/movable.standalone.d.mts +1 -1
- package/dist/js/popover.d.mts +1 -1
- package/dist/js/popover.mjs +27 -12
- package/dist/js/resizable/resizable.embedded.mjs +16 -11
- package/dist/js/sortable/sortable.embedded.d.mts +1 -1
- package/dist/js/sortable/sortable.embedded.mjs +15 -13
- package/dist/js/sortable/sortable.standalone.d.mts +1 -1
- package/dist/js/splitter.mjs +29 -25
- package/dist/js/toolbar.mjs +12 -88
- package/dist/js/tooltip.d.mts +1 -1
- package/dist/js/tooltip.mjs +12 -17
- package/package.json +7 -3
- package/src/js/dialog.ts +22 -26
- package/src/js/floatable/floatable.embedded.ts +19 -18
- package/src/js/focus-trap/focus-trap.embedded.ts +6 -5
- package/src/js/index.ts +1 -0
- package/src/js/internal/attributable.ts +3 -5
- package/src/js/internal/draggable.ts +12 -9
- package/src/js/internal/navigable.ts +247 -0
- package/src/js/menu.ts +345 -0
- package/src/js/movable/movable.embedded.ts +10 -13
- package/src/js/popover.ts +41 -15
- package/src/js/resizable/resizable.embedded.ts +17 -10
- package/src/js/sortable/sortable.embedded.ts +17 -22
- package/src/js/splitter.ts +40 -35
- package/src/js/toolbar.ts +27 -178
- package/src/js/tooltip.ts +18 -30
package/dist/js/toolbar.mjs
CHANGED
|
@@ -1,105 +1,29 @@
|
|
|
1
1
|
import { attributable } from "./internal/attributable.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
4
|
-
import { clamp } from "@oscarpalmer/atoms/number";
|
|
5
|
-
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
2
|
+
import { getNavigable, updateNavigableFocus } from "./internal/navigable.mjs";
|
|
6
3
|
//#region src/js/toolbar.ts
|
|
7
4
|
var OuiToolbar = class {
|
|
8
|
-
#
|
|
9
|
-
constructor(
|
|
10
|
-
this.#
|
|
11
|
-
|
|
5
|
+
#navigable;
|
|
6
|
+
constructor(navigable) {
|
|
7
|
+
this.#navigable = navigable;
|
|
8
|
+
updateNavigableFocus(this.#navigable, false);
|
|
12
9
|
}
|
|
13
10
|
destroy() {
|
|
14
|
-
|
|
15
|
-
this.#
|
|
16
|
-
this.#state.element = void 0;
|
|
17
|
-
this.#state.listeners = void 0;
|
|
11
|
+
this.#navigable.destroy();
|
|
12
|
+
this.#navigable = void 0;
|
|
18
13
|
}
|
|
19
14
|
};
|
|
20
15
|
function addToolbar(element) {
|
|
21
|
-
if (!instances.has(element)) instances.set(element, new OuiToolbar(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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);
|
|
16
|
+
if (!instances.has(element)) instances.set(element, new OuiToolbar(getNavigable(element, {
|
|
17
|
+
role: "toolbar",
|
|
18
|
+
selector: SELECTOR,
|
|
19
|
+
vertical: false
|
|
20
|
+
})));
|
|
70
21
|
}
|
|
71
22
|
function removeToolbar(element) {
|
|
72
23
|
instances.get(element)?.destroy();
|
|
73
24
|
instances.delete(element);
|
|
74
25
|
}
|
|
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
26
|
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 = /* @__PURE__ */ new Set([KEY_END, KEY_HOME]);
|
|
92
|
-
const KEYS_HORIZONTAL = /* @__PURE__ */ new Set([KEY_ARROW_RIGHT, "ArrowLeft"]);
|
|
93
|
-
const KEYS_VERTICAL = /* @__PURE__ */ new Set([KEY_ARROW_DOWN, "ArrowUp"]);
|
|
94
|
-
const KEYS_ALL = /* @__PURE__ */ new Set([
|
|
95
|
-
...KEYS_ABSOLUTE,
|
|
96
|
-
...KEYS_HORIZONTAL,
|
|
97
|
-
...KEYS_VERTICAL
|
|
98
|
-
]);
|
|
99
|
-
const KEYS_NEXT = /* @__PURE__ */ new Set([KEY_ARROW_DOWN, KEY_ARROW_RIGHT]);
|
|
100
|
-
const ORIENTATION_HORIZONTAL = "horizontal";
|
|
101
|
-
const ORIENTATIONS = /* @__PURE__ */ new Set([ORIENTATION_HORIZONTAL, "vertical"]);
|
|
102
|
-
const PROPERTY_DISABLED = "disabled";
|
|
103
27
|
const SELECTOR = `[${ATTRIBUTE}-item]`;
|
|
104
28
|
const instances = /* @__PURE__ */ new WeakMap();
|
|
105
29
|
attributable(ATTRIBUTE, addToolbar, removeToolbar);
|
package/dist/js/tooltip.d.mts
CHANGED
package/dist/js/tooltip.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createEmbeddedFloatable } from "./floatable/floatable.embedded.mjs";
|
|
2
2
|
import { attributable } from "./internal/attributable.mjs";
|
|
3
|
+
import { getAria } from "@oscarpalmer/toretto/aria";
|
|
3
4
|
import { on } from "@oscarpalmer/toretto/event";
|
|
4
5
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
5
|
-
import {
|
|
6
|
+
import { setAttribute } from "@oscarpalmer/toretto/attribute";
|
|
6
7
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
7
8
|
import { createElement } from "@oscarpalmer/toretto/create";
|
|
8
9
|
//#region src/js/tooltip.ts
|
|
@@ -87,12 +88,13 @@ function destroyTooltip(state) {
|
|
|
87
88
|
state.tooltip = void 0;
|
|
88
89
|
}
|
|
89
90
|
function findElements(attribute) {
|
|
91
|
+
if (attribute == null) return;
|
|
90
92
|
const ids = attribute?.split(EXPRESSION_WHITESPACE) ?? [];
|
|
91
93
|
const elements = [];
|
|
92
94
|
for (const id of ids) {
|
|
93
95
|
if (isNullableOrWhitespace(id)) continue;
|
|
94
96
|
const element = document.querySelector(`#${id}`);
|
|
95
|
-
if (
|
|
97
|
+
if (element != null) {
|
|
96
98
|
const cloned = element.cloneNode(true);
|
|
97
99
|
cloned.hidden = false;
|
|
98
100
|
elements.push(cloned);
|
|
@@ -101,21 +103,20 @@ function findElements(attribute) {
|
|
|
101
103
|
return elements;
|
|
102
104
|
}
|
|
103
105
|
function getContent() {
|
|
104
|
-
const content = createElement(
|
|
106
|
+
const content = createElement("div", {
|
|
105
107
|
popover: "hint",
|
|
106
108
|
role: "tooltip",
|
|
107
109
|
tabIndex: -1
|
|
108
110
|
});
|
|
109
|
-
|
|
111
|
+
setAttribute(content, ATTRIBUTE_CONTENT, "");
|
|
110
112
|
return content;
|
|
111
113
|
}
|
|
112
114
|
function getElements(anchor) {
|
|
113
|
-
let elements = findElements(anchor
|
|
114
|
-
if (elements.length
|
|
115
|
-
elements
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (isNullableOrWhitespace(text)) text = anchor.getAttribute(ARIA_DESCRIPTION);
|
|
115
|
+
let elements = findElements(getAria(anchor, "labelledby"));
|
|
116
|
+
if (elements == null || elements.length === 0) elements = findElements(getAria(anchor, "describedby"));
|
|
117
|
+
if (elements != null && elements.length > 0) return elements;
|
|
118
|
+
let text = getAria(anchor, "label");
|
|
119
|
+
if (isNullableOrWhitespace(text)) text = getAria(anchor, "description");
|
|
119
120
|
if (isNullableOrWhitespace(text)) return;
|
|
120
121
|
const paragraph = document.createElement("p");
|
|
121
122
|
paragraph.textContent = text;
|
|
@@ -157,8 +158,7 @@ function onPointermove(event) {
|
|
|
157
158
|
activate(event, false);
|
|
158
159
|
}
|
|
159
160
|
function onToggle(event) {
|
|
160
|
-
|
|
161
|
-
else content.removeAttribute(ATTRIBUTE_CONTENT_OPEN);
|
|
161
|
+
setAttribute(content, ATTRIBUTE_CONTENT_OPEN, event.newState === "open" ? "" : void 0);
|
|
162
162
|
}
|
|
163
163
|
function removeTooltip(element) {
|
|
164
164
|
const state = states.get(element);
|
|
@@ -175,11 +175,6 @@ function removeTooltip(element) {
|
|
|
175
175
|
function setTooltipDelay(value) {
|
|
176
176
|
if (typeof value === "number" && !Number.isNaN(value) && value >= 0) delay = value;
|
|
177
177
|
}
|
|
178
|
-
const ARIA_DESCRIBEDBY = "aria-describedby";
|
|
179
|
-
const ARIA_DESCRIPTION = "aria-description";
|
|
180
|
-
const ARIA_LABEL = "aria-label";
|
|
181
|
-
const ARIA_LABELLEDBY = "aria-labelledby";
|
|
182
|
-
const CONTENT_TAGNAME = "div";
|
|
183
178
|
const EXPRESSION_WHITESPACE = /\s+/;
|
|
184
179
|
const ATTRIBUTE = "oui-tooltip";
|
|
185
180
|
const ATTRIBUTE_CONTENT = `${ATTRIBUTE}-content`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/oui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Styles, components, and utilities for building websites and applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
"types": "./dist/js/movable/movable.standalone.d.mts",
|
|
50
50
|
"default": "./dist/js/movable/movable.standalone.mjs"
|
|
51
51
|
},
|
|
52
|
+
"./menu": {
|
|
53
|
+
"types": "./dist/js/menu.d.mts",
|
|
54
|
+
"default": "./dist/js/menu.mjs"
|
|
55
|
+
},
|
|
52
56
|
"./popover": {
|
|
53
57
|
"types": "./dist/js/popover.d.mts",
|
|
54
58
|
"default": "./dist/js/popover.mjs"
|
|
@@ -87,10 +91,10 @@
|
|
|
87
91
|
},
|
|
88
92
|
"dependencies": {
|
|
89
93
|
"@oscarpalmer/atoms": "^0.188.1",
|
|
90
|
-
"@oscarpalmer/toretto": "^0.
|
|
94
|
+
"@oscarpalmer/toretto": "^0.47.3"
|
|
91
95
|
},
|
|
92
96
|
"devDependencies": {
|
|
93
|
-
"@oxlint/plugins": "^1.
|
|
97
|
+
"@oxlint/plugins": "^1.73",
|
|
94
98
|
"@vitest/coverage-istanbul": "^4.1",
|
|
95
99
|
"autoprefixer": "^10.5",
|
|
96
100
|
"concurrently": "^10",
|
package/src/js/dialog.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {getAria, setAria} from '@oscarpalmer/toretto/aria';
|
|
1
2
|
import {on} from '@oscarpalmer/toretto/event';
|
|
2
3
|
import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
|
|
3
4
|
import {getOnBeforeToggleListener} from './floatable/floatable.embedded';
|
|
@@ -6,6 +7,8 @@ import {ATTRIBUTE_MOVABLE_HANDLE} from './movable/movable.embedded';
|
|
|
6
7
|
import {createMovable, type OuiMovable} from './movable/movable.standalone';
|
|
7
8
|
import {createResizable, RESIZABLE_ATTRIBUTE_HANDLE} from './resizable/resizable.embedded';
|
|
8
9
|
import type {OuiResizable} from './resizable/resizable.standalone';
|
|
10
|
+
import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
11
|
+
import {setStyles} from '@oscarpalmer/toretto/style';
|
|
9
12
|
|
|
10
13
|
// #region Types
|
|
11
14
|
|
|
@@ -68,7 +71,7 @@ class OuiDialog {
|
|
|
68
71
|
|
|
69
72
|
const isModal = modal !== false;
|
|
70
73
|
|
|
71
|
-
element
|
|
74
|
+
setAria(element, 'modal', isModal);
|
|
72
75
|
|
|
73
76
|
if (isModal) {
|
|
74
77
|
element.showModal();
|
|
@@ -104,7 +107,8 @@ function addDialog(element: HTMLElement): void {
|
|
|
104
107
|
function destroyDialog(state: OuiDialogState): void {
|
|
105
108
|
state.element.close();
|
|
106
109
|
|
|
107
|
-
state.element
|
|
110
|
+
setAria(state.element, 'modal', state.modal);
|
|
111
|
+
|
|
108
112
|
state.element.removeAttribute(ATTRIBUTE_OPEN);
|
|
109
113
|
|
|
110
114
|
for (const listener of state.listeners) {
|
|
@@ -139,11 +143,11 @@ function getMovable(element: HTMLDialogElement): OuiMovable | undefined {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
for (const handle of handles) {
|
|
142
|
-
|
|
146
|
+
setAttribute(handle, ATTRIBUTE_MOVABLE_HANDLE, '');
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
return createMovable(element, {
|
|
146
|
-
container:
|
|
150
|
+
container: getAttribute(element, ATTRIBUTE_CONTAINER),
|
|
147
151
|
});
|
|
148
152
|
}
|
|
149
153
|
|
|
@@ -154,7 +158,7 @@ function getResizable(element: HTMLDialogElement): OuiResizable | undefined {
|
|
|
154
158
|
return;
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
setAttribute(handle, RESIZABLE_ATTRIBUTE_HANDLE, '');
|
|
158
162
|
|
|
159
163
|
return createResizable(element);
|
|
160
164
|
}
|
|
@@ -165,16 +169,16 @@ function getState(element: HTMLDialogElement): OuiDialogState {
|
|
|
165
169
|
dialog: undefined as never,
|
|
166
170
|
listeners: [
|
|
167
171
|
getOnBeforeToggleListener(element),
|
|
168
|
-
on(element,
|
|
172
|
+
on(element, 'toggle', event => {
|
|
169
173
|
const {source} = event;
|
|
170
174
|
|
|
171
175
|
if (element.open) {
|
|
172
176
|
const modal =
|
|
173
|
-
(source instanceof HTMLButtonElement && source.command ===
|
|
174
|
-
element.matches(
|
|
177
|
+
(source instanceof HTMLButtonElement && source.command === 'show-modal') ||
|
|
178
|
+
element.matches(':modal');
|
|
175
179
|
|
|
176
|
-
element
|
|
177
|
-
|
|
180
|
+
setAria(element, 'modal', modal);
|
|
181
|
+
setAttribute(element, ATTRIBUTE_OPEN, '');
|
|
178
182
|
|
|
179
183
|
element.focus();
|
|
180
184
|
|
|
@@ -182,18 +186,20 @@ function getState(element: HTMLDialogElement): OuiDialogState {
|
|
|
182
186
|
state.resizable?.set();
|
|
183
187
|
}, 500);
|
|
184
188
|
} else {
|
|
185
|
-
element
|
|
186
|
-
element
|
|
189
|
+
setAttribute(element, ATTRIBUTE_OPEN);
|
|
190
|
+
setAria(element, 'modal', state.modal);
|
|
187
191
|
|
|
188
|
-
element
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
setStyles(element, {
|
|
193
|
+
inset: '',
|
|
194
|
+
position: '',
|
|
195
|
+
transform: '',
|
|
196
|
+
});
|
|
191
197
|
|
|
192
198
|
state.resizable?.reset();
|
|
193
199
|
}
|
|
194
200
|
}),
|
|
195
201
|
],
|
|
196
|
-
modal: element
|
|
202
|
+
modal: getAria(element, 'modal') === 'true' || element.matches(':modal'),
|
|
197
203
|
};
|
|
198
204
|
|
|
199
205
|
state.dialog = new OuiDialog(state);
|
|
@@ -220,28 +226,18 @@ function removeDialog(element: HTMLElement): void {
|
|
|
220
226
|
|
|
221
227
|
// #region Variables
|
|
222
228
|
|
|
223
|
-
const ARIA_MODAL = 'aria-modal';
|
|
224
|
-
|
|
225
229
|
const ATTRIBUTE = 'oui-dialog';
|
|
226
230
|
|
|
227
231
|
const ATTRIBUTE_CONTAINER = `${ATTRIBUTE}-container`;
|
|
228
232
|
|
|
229
233
|
const ATTRIBUTE_OPEN = `${ATTRIBUTE}-open`;
|
|
230
234
|
|
|
231
|
-
const COMMAND_SHOW_MODAL = 'show-modal';
|
|
232
|
-
|
|
233
|
-
const EVENT_TOGGLE = 'toggle';
|
|
234
|
-
|
|
235
235
|
const MESSAGE = 'The element must be an instance of HTMLDialogElement';
|
|
236
236
|
|
|
237
237
|
const SELECTOR_MOVER = `[${ATTRIBUTE}-mover]`;
|
|
238
238
|
|
|
239
|
-
const SELECTOR_MODAL = ':modal';
|
|
240
|
-
|
|
241
239
|
const SELECTOR_RESIZER = `[${ATTRIBUTE}-resizer]`;
|
|
242
240
|
|
|
243
|
-
const TRUE = 'true';
|
|
244
|
-
|
|
245
241
|
const states = new WeakMap<HTMLElement, OuiDialogState>();
|
|
246
242
|
|
|
247
243
|
// #endregion
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import {getAria} from '@oscarpalmer/toretto/aria';
|
|
3
|
+
import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
2
4
|
import {on} from '@oscarpalmer/toretto/event';
|
|
3
5
|
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
4
6
|
import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
|
|
7
|
+
import {setStyle, setStyles} from '@oscarpalmer/toretto/style';
|
|
5
8
|
|
|
6
9
|
// #region Types
|
|
7
10
|
|
|
@@ -45,13 +48,15 @@ export class OuiFloatable {
|
|
|
45
48
|
const {anchor, content} = state;
|
|
46
49
|
|
|
47
50
|
if (standalone) {
|
|
48
|
-
|
|
51
|
+
setAttribute(content, ATTRIBUTE_FLOATABLE, '');
|
|
49
52
|
|
|
50
53
|
content.popover = 'manual';
|
|
54
|
+
} else {
|
|
55
|
+
content.popover ??= 'auto';
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
anchor
|
|
54
|
-
content
|
|
58
|
+
setStyle(anchor, 'anchorName', state.name);
|
|
59
|
+
setStyle(content, 'positionAnchor', state.name);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
/**
|
|
@@ -211,11 +216,11 @@ function createInstance(
|
|
|
211
216
|
export function getOnBeforeToggleListener(element: HTMLElement): RemovableEventListener {
|
|
212
217
|
return on(
|
|
213
218
|
element,
|
|
214
|
-
|
|
219
|
+
'beforetoggle',
|
|
215
220
|
event => {
|
|
216
221
|
const {newState, source} = event;
|
|
217
222
|
|
|
218
|
-
if (newState ===
|
|
223
|
+
if (newState === 'open' && source != null && getAria(source, 'disabled') === 'true') {
|
|
219
224
|
event.preventDefault();
|
|
220
225
|
}
|
|
221
226
|
},
|
|
@@ -248,9 +253,11 @@ function getPosition(
|
|
|
248
253
|
return options.position;
|
|
249
254
|
}
|
|
250
255
|
|
|
251
|
-
const attribute =
|
|
256
|
+
const attribute = getAttribute(anchor, options.attribute);
|
|
252
257
|
|
|
253
|
-
return attribute
|
|
258
|
+
return attribute != null && attribute in AREAS
|
|
259
|
+
? (attribute as OuiFloatablePosition)
|
|
260
|
+
: options.position;
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
function getState(
|
|
@@ -281,10 +288,12 @@ function setPosition(state: OuiFloatableState, override?: OuiFloatablePosition):
|
|
|
281
288
|
|
|
282
289
|
const area = AREAS[position];
|
|
283
290
|
|
|
284
|
-
state.content
|
|
285
|
-
state.content.style.positionArea = area;
|
|
291
|
+
setAttribute(state.content, ATTRIBUTE_POSITION, POSITIONS[area]);
|
|
286
292
|
|
|
287
|
-
state.content
|
|
293
|
+
setStyles(state.content, {
|
|
294
|
+
positionAnchor: state.name,
|
|
295
|
+
positionArea: area,
|
|
296
|
+
});
|
|
288
297
|
}
|
|
289
298
|
|
|
290
299
|
// #endregion
|
|
@@ -306,24 +315,16 @@ const AREAS: Record<OuiFloatablePosition, string> = {
|
|
|
306
315
|
'start-top': 'span-end start',
|
|
307
316
|
};
|
|
308
317
|
|
|
309
|
-
const ARIA_DISABLED = 'aria-disabled';
|
|
310
|
-
|
|
311
318
|
export const ATTRIBUTE_FLOATABLE = 'oui-floatable';
|
|
312
319
|
|
|
313
320
|
const ATTRIBUTE_POSITION = 'oui-position';
|
|
314
321
|
|
|
315
|
-
const EVENT_BEFORE = 'beforetoggle';
|
|
316
|
-
|
|
317
|
-
export const FLOATABLE_STATE_OPEN = 'open';
|
|
318
|
-
|
|
319
322
|
const MESSAGE = 'Anchor and content must be an HTMLElement or SVGElement';
|
|
320
323
|
|
|
321
324
|
const POSITIONS = Object.fromEntries(
|
|
322
325
|
Object.entries(AREAS).map(([position, area]) => [area, position]),
|
|
323
326
|
) as Record<string, OuiFloatablePosition>;
|
|
324
327
|
|
|
325
|
-
const TRUE = 'true';
|
|
326
|
-
|
|
327
328
|
const instances = new WeakMap<HTMLElement, OuiFloatable>();
|
|
328
329
|
|
|
329
330
|
let index = 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
3
|
+
import {setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
3
4
|
import {on} from '@oscarpalmer/toretto/event';
|
|
4
5
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
5
6
|
import {getFocusable} from '@oscarpalmer/toretto/focusable';
|
|
@@ -26,9 +27,9 @@ export class OuiFocusTrap {
|
|
|
26
27
|
*/
|
|
27
28
|
set contain(value: boolean) {
|
|
28
29
|
if (value === true) {
|
|
29
|
-
this.#state.element
|
|
30
|
+
setAttribute(this.#state.element, ATTRIBUTE_FOCUSTRAP_CONTAIN, '');
|
|
30
31
|
} else if (value === false) {
|
|
31
|
-
this.#state.element
|
|
32
|
+
setAttribute(this.#state.element, ATTRIBUTE_FOCUSTRAP_CONTAIN, undefined);
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -62,9 +63,9 @@ export class OuiFocusTrap {
|
|
|
62
63
|
*/
|
|
63
64
|
set noescape(value: boolean) {
|
|
64
65
|
if (value === true) {
|
|
65
|
-
this.#state.element
|
|
66
|
+
setAttribute(this.#state.element, ATTRIBUTE_FOCUSTRAP_NOESCAPE, '');
|
|
66
67
|
} else if (value === false) {
|
|
67
|
-
this.#state.element
|
|
68
|
+
setAttribute(this.#state.element, ATTRIBUTE_FOCUSTRAP_NOESCAPE, undefined);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -74,7 +75,7 @@ export class OuiFocusTrap {
|
|
|
74
75
|
|
|
75
76
|
state.element.tabIndex = -1;
|
|
76
77
|
|
|
77
|
-
state.element
|
|
78
|
+
setAttribute(state.element, ATTRIBUTE_FOCUSTRAP, '');
|
|
78
79
|
|
|
79
80
|
this.contain = state.options.contain;
|
|
80
81
|
this.noescape = state.options.noescape;
|
package/src/js/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
2
|
-
|
|
3
1
|
export function attributable(
|
|
4
2
|
attribute: string,
|
|
5
3
|
add: (element: HTMLElement) => void,
|
|
@@ -11,7 +9,7 @@ export function attributable(
|
|
|
11
9
|
for (let index = 0; index < length; index += 1) {
|
|
12
10
|
const entry = entries[index];
|
|
13
11
|
|
|
14
|
-
if (entry.type === 'attributes' &&
|
|
12
|
+
if (entry.type === 'attributes' && entry.target instanceof Element) {
|
|
15
13
|
if (entry.target.hasAttribute(attribute)) {
|
|
16
14
|
handleAdded(attribute, [entry.target], add);
|
|
17
15
|
} else {
|
|
@@ -41,7 +39,7 @@ function handleAdded(
|
|
|
41
39
|
nodes: Node[] | NodeList,
|
|
42
40
|
add: (element: HTMLElement) => void,
|
|
43
41
|
): void {
|
|
44
|
-
const elements = [...nodes].filter(
|
|
42
|
+
const elements = [...nodes].filter(node => node instanceof Element) as HTMLElement[];
|
|
45
43
|
|
|
46
44
|
const {length} = elements;
|
|
47
45
|
|
|
@@ -59,7 +57,7 @@ function handleAdded(
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
function handleRemoved(nodes: Node[] | NodeList, remove: (element: HTMLElement) => void): void {
|
|
62
|
-
const elements = [...nodes].filter(
|
|
60
|
+
const elements = [...nodes].filter(node => node instanceof Element) as HTMLElement[];
|
|
63
61
|
|
|
64
62
|
const {length} = elements;
|
|
65
63
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type {EventPosition, PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
3
|
+
import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
3
4
|
import {getPosition, on} from '@oscarpalmer/toretto/event';
|
|
4
5
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
5
|
-
import {
|
|
6
|
+
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
7
|
+
import {setStyle, setStyles, toggleStyles, type StyleToggler} from '@oscarpalmer/toretto/style';
|
|
6
8
|
import supportsTouch from '@oscarpalmer/toretto/touch';
|
|
7
9
|
import type {OuiMovable} from '../movable/movable.embedded';
|
|
8
10
|
import type {OuiSortable} from '../sortable/sortable.embedded';
|
|
9
|
-
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
10
11
|
|
|
11
12
|
// #region Types
|
|
12
13
|
|
|
@@ -63,7 +64,7 @@ export abstract class OuiDraggable<ExtraState extends PlainObject = PlainObject>
|
|
|
63
64
|
setContainer(item);
|
|
64
65
|
setDirection(item);
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
setAttribute(element, attribute, '');
|
|
67
68
|
|
|
68
69
|
let states = globals.states.get(element);
|
|
69
70
|
|
|
@@ -425,9 +426,11 @@ function onPointerdown(event: MouseEvent | TouchEvent): void {
|
|
|
425
426
|
|
|
426
427
|
const next = getNextPosition(item, position);
|
|
427
428
|
|
|
428
|
-
dragged
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
setStyles(dragged, {
|
|
430
|
+
position: 'fixed',
|
|
431
|
+
inset: `${next.y}px auto auto ${next.x}px`,
|
|
432
|
+
transform: 'translate3d(0, 0, 0)',
|
|
433
|
+
});
|
|
431
434
|
|
|
432
435
|
item.state.options.drag.onBegin?.(event, globals, item, element, handle, next);
|
|
433
436
|
|
|
@@ -450,7 +453,7 @@ function onPointermove(event: MouseEvent | TouchEvent): void {
|
|
|
450
453
|
const next = getNextPosition(current, position);
|
|
451
454
|
|
|
452
455
|
if (globals.element != null) {
|
|
453
|
-
globals.element.node
|
|
456
|
+
setStyle(globals.element.node, 'inset', `${next.y}px auto auto ${next.x}px`);
|
|
454
457
|
}
|
|
455
458
|
|
|
456
459
|
const detail = current.state.options.drag.onMove(event, globals, current, {
|
|
@@ -529,7 +532,7 @@ function setContainer(item: OuiDraggableItem): void {
|
|
|
529
532
|
}
|
|
530
533
|
|
|
531
534
|
const selector =
|
|
532
|
-
container ?? item.instance.element
|
|
535
|
+
container ?? getAttribute(item.instance.element, item.state.options.container.attribute);
|
|
533
536
|
|
|
534
537
|
if (typeof selector !== 'string') {
|
|
535
538
|
return;
|
|
@@ -559,7 +562,7 @@ function setContainerElement(item: OuiDraggableItem, element: HTMLElement): void
|
|
|
559
562
|
}
|
|
560
563
|
|
|
561
564
|
function setDirection(item: OuiDraggableItem): void {
|
|
562
|
-
const direction = item.state.element
|
|
565
|
+
const direction = getAttribute(item.state.element, item.state.options.direction.attribute);
|
|
563
566
|
|
|
564
567
|
if (horizontals.has(direction as OuiDraggableDirection)) {
|
|
565
568
|
item.state.vertical = false;
|