@oscarpalmer/oui 0.28.0 → 0.29.1

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 (46) hide show
  1. package/dist/js/dialog.d.mts +1 -1
  2. package/dist/js/dialog.mjs +19 -20
  3. package/dist/js/{draggable-zszaW1qS.d.mts → draggable-fIYXd8eO.d.mts} +3 -3
  4. package/dist/js/floatable/floatable.embedded.d.mts +2 -2
  5. package/dist/js/floatable/floatable.embedded.mjs +17 -16
  6. package/dist/js/floatable/floatable.standalone.d.mts +1 -1
  7. package/dist/js/{floatable.embedded-YxGwzgbE.d.mts → floatable.embedded-DUR8bQBK.d.mts} +1 -2
  8. package/dist/js/focus-trap/focus-trap.embedded.mjs +6 -5
  9. package/dist/js/index.d.mts +2 -2
  10. package/dist/js/index.mjs +1 -0
  11. package/dist/js/internal/attributable.mjs +3 -4
  12. package/dist/js/internal/draggable.d.mts +1 -1
  13. package/dist/js/internal/draggable.mjs +11 -8
  14. package/dist/js/internal/navigable.d.mts +32 -0
  15. package/dist/js/internal/navigable.mjs +107 -0
  16. package/dist/js/menu.d.mts +1 -0
  17. package/dist/js/menu.mjs +168 -0
  18. package/dist/js/movable/movable.embedded.d.mts +1 -1
  19. package/dist/js/movable/movable.embedded.mjs +8 -13
  20. package/dist/js/movable/movable.standalone.d.mts +1 -1
  21. package/dist/js/popover.d.mts +1 -1
  22. package/dist/js/popover.mjs +28 -13
  23. package/dist/js/resizable/resizable.embedded.mjs +16 -11
  24. package/dist/js/sortable/sortable.embedded.d.mts +1 -1
  25. package/dist/js/sortable/sortable.embedded.mjs +15 -13
  26. package/dist/js/sortable/sortable.standalone.d.mts +1 -1
  27. package/dist/js/splitter.mjs +29 -25
  28. package/dist/js/toolbar.mjs +12 -88
  29. package/dist/js/tooltip.d.mts +1 -1
  30. package/dist/js/tooltip.mjs +12 -17
  31. package/package.json +7 -3
  32. package/src/js/dialog.ts +22 -26
  33. package/src/js/floatable/floatable.embedded.ts +19 -18
  34. package/src/js/focus-trap/focus-trap.embedded.ts +6 -5
  35. package/src/js/index.ts +1 -0
  36. package/src/js/internal/attributable.ts +3 -5
  37. package/src/js/internal/draggable.ts +12 -9
  38. package/src/js/internal/navigable.ts +247 -0
  39. package/src/js/menu.ts +345 -0
  40. package/src/js/movable/movable.embedded.ts +10 -13
  41. package/src/js/popover.ts +42 -16
  42. package/src/js/resizable/resizable.embedded.ts +17 -10
  43. package/src/js/sortable/sortable.embedded.ts +17 -22
  44. package/src/js/splitter.ts +40 -35
  45. package/src/js/toolbar.ts +27 -178
  46. package/src/js/tooltip.ts +18 -30
@@ -0,0 +1,247 @@
1
+ import {clamp} from '@oscarpalmer/atoms/number';
2
+ import {getAria, setRole} from '@oscarpalmer/toretto/aria';
3
+ import {on} from '@oscarpalmer/toretto/event';
4
+ import {findAncestor} from '@oscarpalmer/toretto/find';
5
+ import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
6
+ import type {AriaRole, RemovableEventListener} from '@oscarpalmer/toretto/models';
7
+
8
+ // #region Types
9
+
10
+ export class OuiNavigable {
11
+ active: OuiNavigableActive = {
12
+ index: 0,
13
+ };
14
+
15
+ element: HTMLElement;
16
+
17
+ listeners: RemovableEventListener[];
18
+
19
+ options: OuiNavigableOptions;
20
+
21
+ orientation: OuiNavigableOrientation;
22
+
23
+ constructor(element: HTMLElement, options: OuiNavigableOptions) {
24
+ this.element = element;
25
+ this.options = options;
26
+
27
+ this.listeners = [
28
+ on(element, 'focusin', event => {
29
+ onFocusin(this, event);
30
+ }),
31
+ on(element, 'keydown', event => {
32
+ onKeydown(this, event);
33
+ }),
34
+ ];
35
+
36
+ this.orientation = getOrientation(element, options);
37
+
38
+ if (options.role != null) {
39
+ setRole(element, options.role);
40
+ }
41
+ }
42
+
43
+ destroy(): void {
44
+ const listeners = this.listeners.splice(0);
45
+
46
+ for (const listener of listeners) {
47
+ listener();
48
+ }
49
+
50
+ this.active.element = undefined;
51
+ this.element = undefined as never;
52
+ }
53
+ }
54
+
55
+ type OuiNavigableActive = {
56
+ element?: HTMLElement;
57
+ index: number;
58
+ };
59
+
60
+ export type OuiNavigableOptions = {
61
+ role?: AriaRole;
62
+ selector: string;
63
+ vertical: boolean;
64
+ onDeactivate?: (event: KeyboardEvent, navigable: OuiNavigable) => void;
65
+ onPreventNavigation?: (event: KeyboardEvent, navigable: OuiNavigable) => boolean;
66
+ onShouldActivate?: (event: KeyboardEvent, navigable: OuiNavigable) => boolean;
67
+ onShouldDeactivate?: (event: KeyboardEvent, navigable: OuiNavigable) => boolean | undefined;
68
+ };
69
+
70
+ type OuiNavigableOrientation = 'horizontal' | 'vertical';
71
+
72
+ // #endregion
73
+
74
+ // #region Functions
75
+
76
+ function getItems(container: HTMLElement, selector: string): HTMLElement[] {
77
+ return [...container.querySelectorAll(selector)].filter(isValidItem);
78
+ }
79
+
80
+ export function getNavigable(element: HTMLElement, options: OuiNavigableOptions): OuiNavigable {
81
+ return new OuiNavigable(element, options);
82
+ }
83
+
84
+ function getOrientation(
85
+ element: HTMLElement,
86
+ options: OuiNavigableOptions,
87
+ ): OuiNavigableOrientation {
88
+ const attribute = getAria(element, 'orientation');
89
+
90
+ return ORIENTATIONS.has(attribute as OuiNavigableOrientation)
91
+ ? (attribute as OuiNavigableOrientation)
92
+ : options.vertical
93
+ ? ORIENTATION_VERTICAL
94
+ : ORIENTATION_HORIZONTAL;
95
+ }
96
+
97
+ function isValidItem(element: Element): element is HTMLElement {
98
+ return 'disabled' in element ? element.disabled !== true : true;
99
+ }
100
+
101
+ function onFocusin(navigable: OuiNavigable, event: FocusEvent): void {
102
+ const item = findAncestor(event, navigable.options.selector);
103
+
104
+ if (!isHTMLOrSVGElement(item) || !isValidItem(item)) {
105
+ return;
106
+ }
107
+
108
+ const items = getItems(navigable.element, navigable.options.selector);
109
+ const index = items.indexOf(item);
110
+
111
+ if (index === -1) {
112
+ return;
113
+ }
114
+
115
+ navigable.active.element = item;
116
+ navigable.active.index = index;
117
+
118
+ updateNavigableFocus(navigable, false, items);
119
+ }
120
+
121
+ function onKeydown(navigable: OuiNavigable, event: KeyboardEvent): void {
122
+ const escaped = event.key === 'Escape';
123
+ const stopPropagation = navigable.options.onShouldDeactivate?.(event, navigable);
124
+
125
+ if (escaped || stopPropagation != null) {
126
+ if (escaped || stopPropagation) {
127
+ event.stopPropagation();
128
+ }
129
+
130
+ navigable.options.onDeactivate?.(event, navigable);
131
+
132
+ return;
133
+ }
134
+
135
+ if (!KEYS_ALL.has(event.key)) {
136
+ return;
137
+ }
138
+
139
+ const mustActivate = KEYS_ACTIVATE.has(event.key);
140
+ const shouldActivate = navigable.options.onShouldActivate?.(event, navigable) === true;
141
+
142
+ if (
143
+ mustActivate ||
144
+ shouldActivate ||
145
+ navigable.options.onPreventNavigation?.(event, navigable) === true
146
+ ) {
147
+ event.stopPropagation();
148
+ }
149
+
150
+ if (mustActivate || shouldActivate) {
151
+ return;
152
+ }
153
+
154
+ const items = getItems(navigable.element, navigable.options.selector);
155
+ const {length} = items;
156
+
157
+ if (length === 0) {
158
+ return;
159
+ }
160
+
161
+ let index: number | undefined;
162
+
163
+ if (KEYS_ABSOLUTE.has(event.key)) {
164
+ index = event.key === 'Home' ? 0 : items.length - 1;
165
+ } else {
166
+ const direction = NAVIGABLE_KEYS_HORIZONTAL.has(event.key)
167
+ ? ORIENTATION_HORIZONTAL
168
+ : ORIENTATION_VERTICAL;
169
+
170
+ if (navigable.orientation === direction) {
171
+ if (navigable.active.element instanceof HTMLSelectElement) {
172
+ event.preventDefault();
173
+ }
174
+
175
+ index = clamp(
176
+ navigable.active.index + (KEYS_NEXT.has(event.key) ? 1 : -1),
177
+ 0,
178
+ length - 1,
179
+ true,
180
+ );
181
+ }
182
+ }
183
+
184
+ if (index == null || index === navigable.active.index) {
185
+ return;
186
+ }
187
+
188
+ navigable.active.element = items[index];
189
+ navigable.active.index = index;
190
+
191
+ updateNavigableFocus(navigable, true, items);
192
+ }
193
+
194
+ export function updateNavigableFocus(
195
+ navigable: OuiNavigable,
196
+ focus: boolean,
197
+ items?: HTMLElement[],
198
+ ): void {
199
+ const updated = items ?? getItems(navigable.element, navigable.options.selector);
200
+ const {length} = updated;
201
+
202
+ if (length === 0) {
203
+ return;
204
+ }
205
+
206
+ for (let index = 0; index < length; index += 1) {
207
+ updated[index].tabIndex = index === navigable.active.index ? 0 : -1;
208
+ }
209
+
210
+ navigable.active.element ??= updated[navigable.active.index];
211
+
212
+ if (focus) {
213
+ navigable.active.element?.focus();
214
+ }
215
+ }
216
+
217
+ // #endregion
218
+
219
+ // #region Variables
220
+
221
+ const KEYS_ABSOLUTE = new Set(['End', 'Home']);
222
+
223
+ const KEYS_ACTIVATE = new Set(['Enter', ' ']);
224
+
225
+ export const NAVIGABLE_KEYS_HORIZONTAL = new Set(['ArrowRight', 'ArrowLeft']);
226
+
227
+ export const NAVIGABLE_KEYS_VERTICAL = new Set(['ArrowDown', 'ArrowUp']);
228
+
229
+ const KEYS_ALL = new Set([
230
+ ...KEYS_ABSOLUTE,
231
+ ...KEYS_ACTIVATE,
232
+ ...NAVIGABLE_KEYS_HORIZONTAL,
233
+ ...NAVIGABLE_KEYS_VERTICAL,
234
+ ]);
235
+
236
+ const KEYS_NEXT = new Set(['ArrowDown', 'ArrowRight']);
237
+
238
+ const ORIENTATION_HORIZONTAL: OuiNavigableOrientation = 'horizontal';
239
+
240
+ const ORIENTATION_VERTICAL: OuiNavigableOrientation = 'vertical';
241
+
242
+ const ORIENTATIONS = new Set<OuiNavigableOrientation>([
243
+ ORIENTATION_HORIZONTAL,
244
+ ORIENTATION_VERTICAL,
245
+ ]);
246
+
247
+ // #endregion
package/src/js/menu.ts ADDED
@@ -0,0 +1,345 @@
1
+ import {setAria, setRole} from '@oscarpalmer/toretto/aria';
2
+ import {setAttribute} from '@oscarpalmer/toretto/attribute';
3
+ import {on} from '@oscarpalmer/toretto/event';
4
+ import {findAncestor} from '@oscarpalmer/toretto/find';
5
+ import {createEmbeddedFloatable, getOnBeforeToggleListener} from './floatable/floatable.embedded';
6
+ import type {OuiFloatable} from './floatable/floatable.standalone';
7
+ import {attributable} from './internal/attributable';
8
+ import {
9
+ NAVIGABLE_KEYS_HORIZONTAL,
10
+ NAVIGABLE_KEYS_VERTICAL,
11
+ type OuiNavigable,
12
+ type OuiNavigableOptions,
13
+ getNavigable,
14
+ updateNavigableFocus,
15
+ } from './internal/navigable';
16
+
17
+ // #region Types
18
+
19
+ type OuiBasicMenu = {
20
+ destroy(): void;
21
+ };
22
+
23
+ type OuiBasicMenuOptions = Pick<OuiNavigableOptions, 'role' | 'selector' | 'vertical'>;
24
+
25
+ class OuiMenu {
26
+ content: HTMLElement;
27
+
28
+ element: HTMLElement;
29
+
30
+ floatable: OuiFloatable;
31
+
32
+ navigable: OuiNavigable;
33
+
34
+ toggle: HTMLElement;
35
+
36
+ get open(): boolean {
37
+ return this.floatable.open;
38
+ }
39
+
40
+ constructor(element: HTMLElement, navigable: OuiNavigable) {
41
+ this.element = element;
42
+ this.navigable = navigable;
43
+
44
+ this.content = navigable.element;
45
+
46
+ this.toggle = element.querySelector(SELECTOR_MENU_TOGGLE)!;
47
+
48
+ this.floatable = createEmbeddedFloatable(this.toggle, this.content, {
49
+ attribute: ATTRIBUTE_MENU_POSITION,
50
+ position:
51
+ findAncestor(element.parentElement!, SELECTOR_MENU_CONTENT) == null
52
+ ? 'below-start'
53
+ : 'end-top',
54
+ reusable: false,
55
+ });
56
+
57
+ this.navigable.listeners.push(
58
+ getOnBeforeToggleListener(this.content),
59
+ on(this.content, 'click', event => {
60
+ event.stopPropagation();
61
+
62
+ const toggle = findAncestor(event, SELECTOR_MENU_TOGGLE);
63
+
64
+ if (toggle != null) {
65
+ return;
66
+ }
67
+
68
+ const elements = document.querySelectorAll(SELECTOR_MENU_CONTENT);
69
+
70
+ for (const element of elements) {
71
+ menus.get(element as HTMLElement)?.floatable.hide();
72
+ }
73
+
74
+ const menu = menus.get(elements[0] as HTMLElement);
75
+
76
+ if (menu != null && event.pointerId === -1) {
77
+ updateNavigableFocus(menu.navigable, true);
78
+ }
79
+ }),
80
+ on(this.content, 'toggle', event => {
81
+ const expanded = event.newState === 'open';
82
+
83
+ if (expanded) {
84
+ this.floatable?.update();
85
+
86
+ updateNavigableFocus(this.navigable, true);
87
+ } else {
88
+ }
89
+
90
+ setAria(this.toggle, 'expanded', expanded);
91
+ }),
92
+ );
93
+
94
+ this.floatable.update();
95
+
96
+ initializeMenu(this);
97
+
98
+ updateNavigableFocus(this.navigable, false);
99
+ }
100
+
101
+ destroy(): void {
102
+ this.floatable.destroy();
103
+ this.navigable.destroy();
104
+
105
+ this.floatable = undefined as never;
106
+ this.navigable = undefined as never;
107
+ }
108
+ }
109
+
110
+ class OuiMenubar {
111
+ element: HTMLElement;
112
+
113
+ navigable: OuiNavigable;
114
+
115
+ constructor(element: HTMLElement, navigable: OuiNavigable) {
116
+ this.element = element;
117
+ this.navigable = navigable;
118
+
119
+ updateNavigableFocus(this.navigable, false);
120
+ }
121
+
122
+ destroy(): void {
123
+ this.navigable.destroy();
124
+
125
+ this.navigable = undefined as never;
126
+ }
127
+ }
128
+
129
+ // #endregion
130
+
131
+ // #region Functions
132
+
133
+ function addInstance<Instance>(
134
+ instances: WeakMap<HTMLElement, Instance>,
135
+ ctor: new (element: HTMLElement, navigable: OuiNavigable) => Instance,
136
+ element: HTMLElement,
137
+ content: HTMLElement,
138
+ options: OuiBasicMenuOptions,
139
+ ): void {
140
+ if (!instances.has(content)) {
141
+ instances.set(
142
+ content,
143
+ new ctor(
144
+ element,
145
+ getNavigable(content, {
146
+ ...options,
147
+ onDeactivate,
148
+ onPreventNavigation,
149
+ onShouldActivate,
150
+ onShouldDeactivate,
151
+ }),
152
+ ),
153
+ );
154
+ }
155
+ }
156
+
157
+ function addMenu(element: HTMLElement): void {
158
+ const content = element.querySelector(SELECTOR_MENU_CONTENT);
159
+ const toggle = element.querySelector(SELECTOR_MENU_TOGGLE);
160
+
161
+ if (content != null && toggle != null) {
162
+ addInstance(menus, OuiMenu, element, content as never, {
163
+ selector: SELECTOR_MENU_ITEM,
164
+ vertical: true,
165
+ });
166
+ }
167
+ }
168
+
169
+ function addMenubar(element: HTMLElement): void {
170
+ addInstance(menubars, OuiMenubar, element, element, {
171
+ role: 'menubar',
172
+ selector: SELECTOR_MENUBAR_ITEM,
173
+ vertical: false,
174
+ });
175
+ }
176
+
177
+ function getId(element: HTMLElement, value?: string): string {
178
+ let {id} = element;
179
+
180
+ if (id == null || id.trim().length === 0) {
181
+ id = value == null ? `oui_menu_${++index}` : `${value}_toggle`;
182
+
183
+ element.id = id;
184
+ }
185
+
186
+ return id;
187
+ }
188
+
189
+ function initializeMenu(menu: OuiMenu): void {
190
+ const id = getId(menu.content);
191
+
192
+ setAttribute(menu.toggle, 'popovertarget', id);
193
+
194
+ setAria(menu.toggle, {
195
+ controls: id,
196
+ expanded: 'false',
197
+ haspopup: 'menu',
198
+ });
199
+
200
+ setAttribute(menu.content, 'tabindex', '-1');
201
+
202
+ setRole(menu.content, 'menu');
203
+ }
204
+
205
+ function onDeactivate(_: KeyboardEvent, navigable: OuiNavigable): void {
206
+ const menu = menus.get(navigable.element);
207
+
208
+ if (menu == null) {
209
+ return;
210
+ }
211
+
212
+ menu.floatable.hide();
213
+
214
+ const element = findAncestor(menu.element.parentElement!, SELECTORS) as HTMLElement | null;
215
+
216
+ if (element == null) {
217
+ return;
218
+ }
219
+
220
+ const instance = menubars.get(element) ?? menus.get(element);
221
+
222
+ if (instance != null) {
223
+ updateNavigableFocus(instance.navigable, true);
224
+ }
225
+ }
226
+
227
+ function onPreventNavigation(_: KeyboardEvent, navigable: OuiNavigable): boolean {
228
+ const instance = menubars.get(navigable.element) ?? menus.get(navigable.element);
229
+
230
+ return findAncestor(instance?.element.parentElement!, SELECTORS) != null;
231
+ }
232
+
233
+ function onShouldActivate(event: KeyboardEvent, navigable: OuiNavigable): boolean {
234
+ let activate = false;
235
+
236
+ if (menubars.has(navigable.element)) {
237
+ activate =
238
+ navigable.orientation === 'horizontal'
239
+ ? NAVIGABLE_KEYS_VERTICAL.has(event.key)
240
+ : NAVIGABLE_KEYS_HORIZONTAL.has(event.key);
241
+ } else if (menus.has(navigable.element)) {
242
+ activate =
243
+ navigable.orientation === 'horizontal'
244
+ ? NAVIGABLE_KEYS_VERTICAL.has(event.key)
245
+ : event.key === 'ArrowRight';
246
+ }
247
+
248
+ if (activate) {
249
+ const target =
250
+ event.target instanceof HTMLElement && event.target.matches(SELECTOR_MENU_TOGGLE)
251
+ ? event.target
252
+ : undefined;
253
+
254
+ if (target == null) {
255
+ activate = false;
256
+ } else {
257
+ target.click();
258
+ }
259
+ }
260
+
261
+ return activate;
262
+ }
263
+
264
+ function onShouldDeactivate(event: KeyboardEvent, navigable: OuiNavigable): boolean | undefined {
265
+ if (menubars.has(navigable.element)) {
266
+ return;
267
+ }
268
+
269
+ const menu = menus.get(navigable.element);
270
+
271
+ if (menu == null) {
272
+ return;
273
+ }
274
+
275
+ const horizontal = navigable.orientation === 'horizontal';
276
+
277
+ const altKey = horizontal ? 'ArrowDown' : 'ArrowRight';
278
+ const baseKey = horizontal ? 'ArrowUp' : 'ArrowLeft';
279
+
280
+ if (event.key === baseKey) {
281
+ return findAncestor(menu.element.parentElement!, SELECTOR_MENU_CONTENT) != null;
282
+ }
283
+
284
+ if (
285
+ event.key === altKey &&
286
+ event.target instanceof HTMLElement &&
287
+ !event.target.hasAttribute('popovertarget')
288
+ ) {
289
+ return false;
290
+ }
291
+ }
292
+
293
+ function removeInstance<Instance extends OuiBasicMenu>(
294
+ instances: WeakMap<HTMLElement, Instance>,
295
+ element: HTMLElement,
296
+ ): void {
297
+ instances.get(element)?.destroy();
298
+
299
+ instances.delete(element);
300
+ }
301
+
302
+ function removeMenu(element: HTMLElement): void {
303
+ removeInstance(menus, element);
304
+ }
305
+
306
+ function removeMenubar(element: HTMLElement): void {
307
+ removeInstance(menubars, element);
308
+ }
309
+
310
+ // #endregion
311
+
312
+ // #region Variables
313
+
314
+ const ATTRIBUTE_MENU = 'oui-menu';
315
+
316
+ const ATTRIBUTE_MENU_POSITION = 'oui-menu-position';
317
+
318
+ const ATTRIBUTE_MENUBAR = 'oui-menubar';
319
+
320
+ const SELECTOR_MENUBAR = `[${ATTRIBUTE_MENUBAR}]`;
321
+
322
+ const SELECTOR_MENUBAR_ITEM = `:scope > [${ATTRIBUTE_MENUBAR}-item], :scope > * > [${ATTRIBUTE_MENUBAR}-item]`;
323
+
324
+ const SELECTOR_MENU_CONTENT = `[${ATTRIBUTE_MENU}-content]`;
325
+
326
+ const SELECTOR_MENU_ITEM = `:scope > [${ATTRIBUTE_MENU}-item], :scope > * > [${ATTRIBUTE_MENU}-item]`;
327
+
328
+ const SELECTOR_MENU_TOGGLE = `[${ATTRIBUTE_MENU}-toggle]`;
329
+
330
+ const SELECTORS = `${SELECTOR_MENUBAR}, ${SELECTOR_MENU_CONTENT}`;
331
+
332
+ const menubars = new WeakMap<HTMLElement, OuiMenubar>();
333
+
334
+ const menus = new WeakMap<HTMLElement, OuiMenu>();
335
+
336
+ let index = 0;
337
+
338
+ // #endregion
339
+
340
+ // #region Initialization
341
+
342
+ attributable(ATTRIBUTE_MENU, addMenu, removeMenu);
343
+ attributable(ATTRIBUTE_MENUBAR, addMenubar, removeMenubar);
344
+
345
+ // #endregion
@@ -1,6 +1,8 @@
1
1
  import type {EventPosition, PlainObject} from '@oscarpalmer/atoms/models';
2
+ import {setAttribute} from '@oscarpalmer/toretto/attribute';
2
3
  import {getPosition, on} from '@oscarpalmer/toretto/event';
3
4
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
5
+ import {getStyles, setStyle, setStyles} from '@oscarpalmer/toretto/style';
4
6
  import {attributable} from '../internal/attributable';
5
7
  import {
6
8
  addDraggable,
@@ -23,10 +25,7 @@ export class OuiMovable extends OuiDraggable<OuiMovableState> {
23
25
  #style: OuiMovableStyle;
24
26
 
25
27
  constructor(element: HTMLElement, options?: CreateOuiDraggableOptions) {
26
- const style = {
27
- inset: element.style.inset,
28
- position: element.style.position,
29
- };
28
+ const style = getStyles(element, ['inset', 'position']);
30
29
 
31
30
  super(
32
31
  'movable',
@@ -80,8 +79,7 @@ export class OuiMovable extends OuiDraggable<OuiMovableState> {
80
79
  override destroy(): void {
81
80
  MOVED.delete(this);
82
81
 
83
- this.#element.style.inset = this.#style.inset;
84
- this.#element.style.position = this.#style.position;
82
+ setStyles(this.#element, this.#style);
85
83
 
86
84
  this.#element = undefined as never;
87
85
  this.#style = undefined as never;
@@ -98,8 +96,8 @@ type OuiMovableState = {
98
96
  };
99
97
 
100
98
  type OuiMovableStyle = {
101
- inset: string;
102
- position: string;
99
+ inset: string | undefined;
100
+ position: string | undefined;
103
101
  };
104
102
 
105
103
  // #endregion
@@ -168,10 +166,9 @@ function onCancel(globals: OuiDraggableGlobals, item: OuiDraggableItem): void {
168
166
  const {x, y} = globals.original ?? {x: 0, y: 0};
169
167
 
170
168
  if (moved) {
171
- element.style.inset = `${y}px auto auto ${x}px`;
169
+ setStyle(element, 'inset', `${y}px auto auto ${x}px`);
172
170
  } else {
173
- element.style.position = style.position;
174
- element.style.inset = style.inset;
171
+ setStyles(element, style);
175
172
  }
176
173
 
177
174
  element.removeAttribute(ATTRIBUTE_MOVING);
@@ -227,7 +224,7 @@ function onMove(
227
224
  __: OuiDraggableItem,
228
225
  position: OuiDragMovePosition,
229
226
  ): PlainObject | undefined {
230
- globals.element?.node.setAttribute(ATTRIBUTE_MOVING, '');
227
+ setAttribute(globals.element?.node as never, ATTRIBUTE_MOVING, '');
231
228
 
232
229
  return {
233
230
  from: {...globals.original},
@@ -270,7 +267,7 @@ function reposition(movable: OuiMovable): void {
270
267
  const x = container.left + (state.offset?.x ?? 0);
271
268
  const y = container.top + (state.offset?.y ?? 0);
272
269
 
273
- state.element.style.inset = `${y}px auto auto ${x}px`;
270
+ setStyle(state.element, 'inset', `${y}px auto auto ${x}px`);
274
271
  }
275
272
 
276
273
  function setOffset(state: OuiDraggableState & OuiMovableState, container: DOMRect): void {