@oscarpalmer/oui 0.27.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/css/floatable.css +9 -0
- package/dist/js/dialog.d.mts +3 -1
- package/dist/js/dialog.mjs +35 -19
- 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 +4 -3
- package/dist/js/index.mjs +6 -2
- package/dist/js/internal/attributable.mjs +3 -4
- package/dist/js/internal/draggable.d.mts +1 -1
- package/dist/js/internal/draggable.mjs +12 -9
- 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.d.mts +2 -0
- package/dist/js/resizable/resizable.embedded.mjs +191 -0
- package/dist/js/resizable/resizable.standalone.d.mts +2 -0
- package/dist/js/resizable/resizable.standalone.mjs +2 -0
- package/dist/js/resizable.embedded-CXrUVPzR.d.mts +35 -0
- 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 +30 -26
- 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 +11 -3
- package/src/css/floatable.scss +9 -0
- package/src/css/form.scss +6 -6
- package/src/js/dialog.ts +50 -24
- 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 +3 -1
- package/src/js/internal/attributable.ts +3 -5
- package/src/js/internal/draggable.ts +13 -10
- 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 +390 -0
- package/src/js/resizable/resizable.standalone.ts +5 -0
- package/src/js/sortable/sortable.embedded.ts +17 -22
- package/src/js/splitter.ts +41 -36
- package/src/js/toolbar.ts +27 -178
- package/src/js/tooltip.ts +18 -30
package/dist/js/splitter.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { setAria } from "@oscarpalmer/toretto/aria";
|
|
1
2
|
import { getPosition, on } from "@oscarpalmer/toretto/event";
|
|
2
3
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
4
|
+
import { getAttribute, setAttribute } from "@oscarpalmer/toretto/attribute";
|
|
5
|
+
import { setStyle, toggleStyles } from "@oscarpalmer/toretto/style";
|
|
3
6
|
import { clamp } from "@oscarpalmer/atoms/number";
|
|
4
7
|
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
5
|
-
import { toggleStyles } from "@oscarpalmer/toretto/style";
|
|
6
8
|
import supportsTouch from "@oscarpalmer/toretto/touch";
|
|
7
9
|
import { round } from "@oscarpalmer/atoms/math";
|
|
8
10
|
//#region src/js/splitter.ts
|
|
@@ -42,8 +44,7 @@ var OuiSplitter = class {
|
|
|
42
44
|
this.types = getTypes(this);
|
|
43
45
|
if (this.types.auto) RESIZE_OBSERVER.observe(this.element);
|
|
44
46
|
else RESIZE_OBSERVER.unobserve(this.element);
|
|
45
|
-
|
|
46
|
-
else this.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
47
|
+
setAttribute(this.element, ATTRIBUTE_HORIZONTAL, this.types.horizontal ? "" : void 0);
|
|
47
48
|
setAriaOrientation(this);
|
|
48
49
|
}
|
|
49
50
|
updateValues() {
|
|
@@ -72,7 +73,7 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
72
73
|
* Maximum size for the first panel
|
|
73
74
|
*/
|
|
74
75
|
set max(value) {
|
|
75
|
-
|
|
76
|
+
if (typeof value === "number") setAttribute(this, ATTRIBUTE_MAX, value.toString());
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
79
|
* Minimum size for the first panel
|
|
@@ -84,7 +85,7 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
84
85
|
* Minimum size for the first panel
|
|
85
86
|
*/
|
|
86
87
|
set min(value) {
|
|
87
|
-
|
|
88
|
+
if (typeof value === "number") setAttribute(this, ATTRIBUTE_MIN, value.toString());
|
|
88
89
|
}
|
|
89
90
|
/**
|
|
90
91
|
* Current size for the first panel
|
|
@@ -96,23 +97,23 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
96
97
|
* Current size for the first panel
|
|
97
98
|
*/
|
|
98
99
|
set size(value) {
|
|
99
|
-
|
|
100
|
+
if (typeof value === "number") setAttribute(this, ATTRIBUTE_SIZE, value.toString());
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
103
|
* Orientation of the splitter
|
|
103
104
|
*/
|
|
104
105
|
get type() {
|
|
105
|
-
return
|
|
106
|
+
return getAttribute(this, ATTRIBUTE_TYPE) ?? "vertical";
|
|
106
107
|
}
|
|
107
108
|
/**
|
|
108
109
|
* Orientation of the splitter
|
|
109
110
|
*/
|
|
110
111
|
set type(value) {
|
|
111
|
-
|
|
112
|
+
setAttribute(this, ATTRIBUTE_TYPE, value);
|
|
112
113
|
}
|
|
113
114
|
constructor() {
|
|
114
115
|
super();
|
|
115
|
-
const panels = [...this.children].filter((child) => child instanceof
|
|
116
|
+
const panels = [...this.children].filter((child) => child instanceof Element);
|
|
116
117
|
if (panels.length !== 2) throw new Error(MESSAGE);
|
|
117
118
|
this.#splitter = new OuiSplitter(this, panels);
|
|
118
119
|
}
|
|
@@ -138,17 +139,19 @@ var OuiSplitterElement = class extends HTMLElement {
|
|
|
138
139
|
};
|
|
139
140
|
function createHandle() {
|
|
140
141
|
const handle = document.createElement("span");
|
|
141
|
-
handle
|
|
142
|
-
|
|
142
|
+
setAria(handle, "hidden", "true");
|
|
143
|
+
setAttribute(handle, ATTRIBUTE_HANDLE, "");
|
|
143
144
|
return handle;
|
|
144
145
|
}
|
|
145
146
|
function createSeparator(splitter) {
|
|
146
147
|
const separator = document.createElement("div");
|
|
147
148
|
separator.role = "separator";
|
|
148
149
|
separator.tabIndex = 0;
|
|
149
|
-
separator
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
setAria(separator, {
|
|
151
|
+
controls: splitter.element.id,
|
|
152
|
+
label: "Resize panels"
|
|
153
|
+
});
|
|
154
|
+
setAttribute(separator, ATTRIBUTE_SEPARATOR, "");
|
|
152
155
|
return separator;
|
|
153
156
|
}
|
|
154
157
|
function getContained(min, value, max) {
|
|
@@ -160,7 +163,7 @@ function getSize(splitter, value) {
|
|
|
160
163
|
return getContained(splitter.values.min, fraction, splitter.values.max);
|
|
161
164
|
}
|
|
162
165
|
function getTypes(splitter, width, height) {
|
|
163
|
-
const type = splitter.element
|
|
166
|
+
const type = getAttribute(splitter.element, "type");
|
|
164
167
|
if (type === "horizontal") return {
|
|
165
168
|
auto: false,
|
|
166
169
|
horizontal: true
|
|
@@ -175,7 +178,7 @@ function getTypes(splitter, width, height) {
|
|
|
175
178
|
};
|
|
176
179
|
}
|
|
177
180
|
function getValue(element, name, defaultValue) {
|
|
178
|
-
const value =
|
|
181
|
+
const value = getAttribute(element, name);
|
|
179
182
|
if (isNullableOrWhitespace(value)) return defaultValue;
|
|
180
183
|
const parsed = Number.parseFloat(value);
|
|
181
184
|
if (Number.isNaN(parsed)) return defaultValue;
|
|
@@ -235,8 +238,7 @@ function onObservation(entries) {
|
|
|
235
238
|
const splitter = MAPPED_ELEMENTS.get(entry.target);
|
|
236
239
|
if (splitter == null) return;
|
|
237
240
|
splitter.types = getTypes(splitter, entry.contentRect.width, entry.contentRect.height);
|
|
238
|
-
|
|
239
|
-
else splitter.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
241
|
+
setAttribute(splitter.element, ATTRIBUTE_HORIZONTAL, splitter.types.horizontal ? "" : void 0);
|
|
240
242
|
setAriaOrientation(splitter);
|
|
241
243
|
}
|
|
242
244
|
frame = void 0;
|
|
@@ -247,8 +249,8 @@ function onPointerdown(event) {
|
|
|
247
249
|
splitter = MAPPED_ELEMENTS.get(findAncestor(event, SELECTOR_HANDLE));
|
|
248
250
|
if (splitter == null) return;
|
|
249
251
|
if (supportsTouch.value) event.preventDefault();
|
|
250
|
-
splitter.handle
|
|
251
|
-
splitter.separator
|
|
252
|
+
setAttribute(splitter.handle, ATTRIBUTE_ACTIVE, "");
|
|
253
|
+
setAttribute(splitter.separator, ATTRIBUTE_ACTIVE, "");
|
|
252
254
|
splitter.rectangle = splitter.element.getBoundingClientRect();
|
|
253
255
|
STYLING_TOGGLER.set();
|
|
254
256
|
}
|
|
@@ -264,17 +266,19 @@ function onPointerup() {
|
|
|
264
266
|
if (splitter != null) onEnd(false);
|
|
265
267
|
}
|
|
266
268
|
function setAriaOrientation(splitter) {
|
|
267
|
-
splitter.separator
|
|
269
|
+
setAria(splitter.separator, "orientation", splitter.types.horizontal ? "horizontal" : "vertical");
|
|
268
270
|
}
|
|
269
271
|
function setAriaValue(splitter) {
|
|
270
|
-
splitter.separator
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
setAria(splitter.separator, {
|
|
273
|
+
valuemax: splitter.values.max,
|
|
274
|
+
valuemin: splitter.values.min,
|
|
275
|
+
valuenow: splitter.values.now.current
|
|
276
|
+
});
|
|
273
277
|
}
|
|
274
278
|
function setSize(splitter, size, previous) {
|
|
275
279
|
splitter.values.now.current = size;
|
|
276
280
|
if (previous) splitter.values.now.previous = size;
|
|
277
|
-
splitter.element
|
|
281
|
+
setStyle(splitter.element, "--oui-splitter-size", `${size}`);
|
|
278
282
|
setAriaValue(splitter);
|
|
279
283
|
}
|
|
280
284
|
const SELECTOR = "oui-splitter";
|
|
@@ -315,7 +319,7 @@ on(document, "keydown", onKeydown);
|
|
|
315
319
|
on(document, "mousedown", onPointerdown);
|
|
316
320
|
on(document, "pointermove", onPointermove);
|
|
317
321
|
on(document, "pointerup", onPointerup);
|
|
318
|
-
on(document, "touchcancel", onPointerup
|
|
322
|
+
on(document, "touchcancel", onPointerup);
|
|
319
323
|
on(document, "touchstart", onPointerdown, { passive: false });
|
|
320
324
|
//#endregion
|
|
321
325
|
export { OuiSplitterElement };
|
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,10 +49,18 @@
|
|
|
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"
|
|
55
59
|
},
|
|
60
|
+
"./resizable": {
|
|
61
|
+
"types": "./dist/js/resizable/resizable.standalone.d.mts",
|
|
62
|
+
"default": "./dist/js/resizable/resizable.standalone.mjs"
|
|
63
|
+
},
|
|
56
64
|
"./sortable": {
|
|
57
65
|
"types": "./dist/js/sortable/sortable.standalone.d.mts",
|
|
58
66
|
"default": "./dist/js/sortable/sortable.standalone.mjs"
|
|
@@ -83,10 +91,10 @@
|
|
|
83
91
|
},
|
|
84
92
|
"dependencies": {
|
|
85
93
|
"@oscarpalmer/atoms": "^0.188.1",
|
|
86
|
-
"@oscarpalmer/toretto": "^0.
|
|
94
|
+
"@oscarpalmer/toretto": "^0.47.3"
|
|
87
95
|
},
|
|
88
96
|
"devDependencies": {
|
|
89
|
-
"@oxlint/plugins": "^1.
|
|
97
|
+
"@oxlint/plugins": "^1.73",
|
|
90
98
|
"@vitest/coverage-istanbul": "^4.1",
|
|
91
99
|
"autoprefixer": "^10.5",
|
|
92
100
|
"concurrently": "^10",
|
package/src/css/floatable.scss
CHANGED
|
@@ -36,6 +36,15 @@
|
|
|
36
36
|
transition-delay: 0.125s;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
:where([oui-dialog]) {
|
|
40
|
+
position: relative;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:where([oui-dialog-resizer]) {
|
|
44
|
+
position: absolute;
|
|
45
|
+
inset: auto 0 0 auto;
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
:where([oui-popover], [oui-tooltip-content]) {
|
|
40
49
|
@include mixins.floatable;
|
|
41
50
|
}
|
package/src/css/form.scss
CHANGED
|
@@ -173,9 +173,9 @@
|
|
|
173
173
|
|
|
174
174
|
:where(
|
|
175
175
|
&.oui-validate:not([disabled], [readonly], [aria-disabled='true' i]):is(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
:user-invalid:not([aria-invalid='false' i]),
|
|
177
|
+
[aria-invalid='true' i]
|
|
178
|
+
)
|
|
179
179
|
) {
|
|
180
180
|
@include invalid;
|
|
181
181
|
|
|
@@ -201,9 +201,9 @@
|
|
|
201
201
|
|
|
202
202
|
:where(
|
|
203
203
|
&.oui-validate:not([disabled], [readonly], [aria-disabled='true' i]):is(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
:user-valid:not([aria-invalid='true' i]),
|
|
205
|
+
[aria-invalid='false' i]
|
|
206
|
+
)
|
|
207
207
|
) {
|
|
208
208
|
@include valid;
|
|
209
209
|
|
package/src/js/dialog.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
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';
|
|
4
5
|
import {attributable} from './internal/attributable';
|
|
5
6
|
import {ATTRIBUTE_MOVABLE_HANDLE} from './movable/movable.embedded';
|
|
6
7
|
import {createMovable, type OuiMovable} from './movable/movable.standalone';
|
|
8
|
+
import {createResizable, RESIZABLE_ATTRIBUTE_HANDLE} from './resizable/resizable.embedded';
|
|
9
|
+
import type {OuiResizable} from './resizable/resizable.standalone';
|
|
10
|
+
import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
11
|
+
import {setStyles} from '@oscarpalmer/toretto/style';
|
|
7
12
|
|
|
8
13
|
// #region Types
|
|
9
14
|
|
|
@@ -66,7 +71,7 @@ class OuiDialog {
|
|
|
66
71
|
|
|
67
72
|
const isModal = modal !== false;
|
|
68
73
|
|
|
69
|
-
element
|
|
74
|
+
setAria(element, 'modal', isModal);
|
|
70
75
|
|
|
71
76
|
if (isModal) {
|
|
72
77
|
element.showModal();
|
|
@@ -82,6 +87,7 @@ type OuiDialogState = {
|
|
|
82
87
|
listeners: RemovableEventListener[];
|
|
83
88
|
modal: boolean;
|
|
84
89
|
movable?: OuiMovable;
|
|
90
|
+
resizable?: OuiResizable;
|
|
85
91
|
};
|
|
86
92
|
|
|
87
93
|
// #endregion
|
|
@@ -101,16 +107,22 @@ function addDialog(element: HTMLElement): void {
|
|
|
101
107
|
function destroyDialog(state: OuiDialogState): void {
|
|
102
108
|
state.element.close();
|
|
103
109
|
|
|
104
|
-
state.element
|
|
110
|
+
setAria(state.element, 'modal', state.modal);
|
|
111
|
+
|
|
105
112
|
state.element.removeAttribute(ATTRIBUTE_OPEN);
|
|
106
113
|
|
|
107
114
|
for (const listener of state.listeners) {
|
|
108
115
|
listener();
|
|
109
116
|
}
|
|
110
117
|
|
|
118
|
+
state.movable?.destroy();
|
|
119
|
+
state.resizable?.destroy();
|
|
120
|
+
|
|
111
121
|
state.dialog = undefined as never;
|
|
112
122
|
state.element = undefined as never;
|
|
113
123
|
state.listeners = undefined as never;
|
|
124
|
+
state.movable = undefined;
|
|
125
|
+
state.resizable = undefined;
|
|
114
126
|
}
|
|
115
127
|
|
|
116
128
|
/**
|
|
@@ -131,47 +143,69 @@ function getMovable(element: HTMLDialogElement): OuiMovable | undefined {
|
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
for (const handle of handles) {
|
|
134
|
-
|
|
146
|
+
setAttribute(handle, ATTRIBUTE_MOVABLE_HANDLE, '');
|
|
135
147
|
}
|
|
136
148
|
|
|
137
149
|
return createMovable(element, {
|
|
138
|
-
container:
|
|
150
|
+
container: getAttribute(element, ATTRIBUTE_CONTAINER),
|
|
139
151
|
});
|
|
140
152
|
}
|
|
141
153
|
|
|
154
|
+
function getResizable(element: HTMLDialogElement): OuiResizable | undefined {
|
|
155
|
+
const handle = element.querySelector(SELECTOR_RESIZER);
|
|
156
|
+
|
|
157
|
+
if (handle == null) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setAttribute(handle, RESIZABLE_ATTRIBUTE_HANDLE, '');
|
|
162
|
+
|
|
163
|
+
return createResizable(element);
|
|
164
|
+
}
|
|
165
|
+
|
|
142
166
|
function getState(element: HTMLDialogElement): OuiDialogState {
|
|
143
167
|
const state: OuiDialogState = {
|
|
144
168
|
element,
|
|
145
169
|
dialog: undefined as never,
|
|
146
170
|
listeners: [
|
|
147
171
|
getOnBeforeToggleListener(element),
|
|
148
|
-
on(element,
|
|
172
|
+
on(element, 'toggle', event => {
|
|
149
173
|
const {source} = event;
|
|
150
174
|
|
|
151
175
|
if (element.open) {
|
|
152
176
|
const modal =
|
|
153
|
-
(source instanceof HTMLButtonElement && source.command ===
|
|
154
|
-
element.matches(
|
|
177
|
+
(source instanceof HTMLButtonElement && source.command === 'show-modal') ||
|
|
178
|
+
element.matches(':modal');
|
|
155
179
|
|
|
156
|
-
element
|
|
157
|
-
|
|
180
|
+
setAria(element, 'modal', modal);
|
|
181
|
+
setAttribute(element, ATTRIBUTE_OPEN, '');
|
|
158
182
|
|
|
159
183
|
element.focus();
|
|
184
|
+
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
state.resizable?.set();
|
|
187
|
+
}, 500);
|
|
160
188
|
} else {
|
|
161
|
-
element
|
|
162
|
-
element
|
|
189
|
+
setAttribute(element, ATTRIBUTE_OPEN);
|
|
190
|
+
setAria(element, 'modal', state.modal);
|
|
163
191
|
|
|
164
|
-
element
|
|
165
|
-
|
|
166
|
-
|
|
192
|
+
setStyles(element, {
|
|
193
|
+
inset: '',
|
|
194
|
+
position: '',
|
|
195
|
+
transform: '',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
state.resizable?.reset();
|
|
167
199
|
}
|
|
168
200
|
}),
|
|
169
201
|
],
|
|
170
|
-
modal: element
|
|
202
|
+
modal: getAria(element, 'modal') === 'true' || element.matches(':modal'),
|
|
171
203
|
};
|
|
172
204
|
|
|
173
205
|
state.dialog = new OuiDialog(state);
|
|
206
|
+
|
|
174
207
|
state.movable = getMovable(element);
|
|
208
|
+
state.resizable = getResizable(element);
|
|
175
209
|
|
|
176
210
|
return state;
|
|
177
211
|
}
|
|
@@ -192,25 +226,17 @@ function removeDialog(element: HTMLElement): void {
|
|
|
192
226
|
|
|
193
227
|
// #region Variables
|
|
194
228
|
|
|
195
|
-
const ARIA_MODAL = 'aria-modal';
|
|
196
|
-
|
|
197
229
|
const ATTRIBUTE = 'oui-dialog';
|
|
198
230
|
|
|
199
231
|
const ATTRIBUTE_CONTAINER = `${ATTRIBUTE}-container`;
|
|
200
232
|
|
|
201
233
|
const ATTRIBUTE_OPEN = `${ATTRIBUTE}-open`;
|
|
202
234
|
|
|
203
|
-
const COMMAND_SHOW_MODAL = 'show-modal';
|
|
204
|
-
|
|
205
|
-
const EVENT_TOGGLE = 'toggle';
|
|
206
|
-
|
|
207
235
|
const MESSAGE = 'The element must be an instance of HTMLDialogElement';
|
|
208
236
|
|
|
209
237
|
const SELECTOR_MOVER = `[${ATTRIBUTE}-mover]`;
|
|
210
238
|
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
const TRUE = 'true';
|
|
239
|
+
const SELECTOR_RESIZER = `[${ATTRIBUTE}-resizer]`;
|
|
214
240
|
|
|
215
241
|
const states = new WeakMap<HTMLElement, OuiDialogState>();
|
|
216
242
|
|