@oscarpalmer/oui 0.26.1 → 0.27.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 (64) hide show
  1. package/dist/css/floatable.css +0 -2
  2. package/dist/css/message.css +1 -0
  3. package/dist/css/reset.css +1 -0
  4. package/dist/js/dialog.d.mts +2 -0
  5. package/dist/js/dialog.mjs +16 -1
  6. package/dist/js/draggable-zszaW1qS.d.mts +155 -0
  7. package/dist/js/floatable/floatable.embedded.d.mts +2 -0
  8. package/dist/js/floatable/{embedded.mjs → floatable.embedded.mjs} +16 -11
  9. package/dist/js/floatable/floatable.standalone.d.mts +2 -0
  10. package/dist/js/floatable/floatable.standalone.mjs +2 -0
  11. package/dist/js/{embedded-CSOh5njI.d.mts → floatable.embedded-YxGwzgbE.d.mts} +5 -2
  12. package/dist/js/focus-trap/focus-trap.embedded.d.mts +2 -0
  13. package/dist/js/focus-trap/{embedded.mjs → focus-trap.embedded.mjs} +17 -15
  14. package/dist/js/focus-trap/focus-trap.standalone.d.mts +2 -0
  15. package/dist/js/focus-trap/focus-trap.standalone.mjs +2 -0
  16. package/dist/js/{embedded-CHK82Uh5.d.mts → focus-trap.embedded-DR8NpZfr.d.mts} +18 -18
  17. package/dist/js/index.d.mts +4 -3
  18. package/dist/js/index.mjs +8 -7
  19. package/dist/js/internal/draggable.d.mts +2 -2
  20. package/dist/js/internal/draggable.mjs +146 -94
  21. package/dist/js/movable/movable.embedded.d.mts +2 -0
  22. package/dist/js/movable/movable.embedded.mjs +184 -0
  23. package/dist/js/movable/movable.standalone.d.mts +2 -0
  24. package/dist/js/movable/movable.standalone.mjs +2 -0
  25. package/dist/js/popover.d.mts +2 -2
  26. package/dist/js/popover.mjs +4 -3
  27. package/dist/js/sortable/sortable.embedded.d.mts +2 -0
  28. package/dist/js/{sortable.mjs → sortable/sortable.embedded.mjs} +88 -63
  29. package/dist/js/sortable/sortable.standalone.d.mts +2 -0
  30. package/dist/js/sortable/sortable.standalone.mjs +2 -0
  31. package/dist/js/splitter.mjs +5 -5
  32. package/dist/js/toolbar.mjs +6 -6
  33. package/dist/js/tooltip.d.mts +2 -2
  34. package/dist/js/tooltip.mjs +36 -33
  35. package/package.json +11 -11
  36. package/src/css/floatable.scss +0 -2
  37. package/src/css/message.scss +1 -0
  38. package/src/css/reset.scss +1 -0
  39. package/src/js/dialog.ts +31 -1
  40. package/src/js/floatable/{embedded.ts → floatable.embedded.ts} +18 -10
  41. package/src/js/floatable/floatable.standalone.ts +6 -0
  42. package/src/js/focus-trap/{embedded.ts → focus-trap.embedded.ts} +20 -17
  43. package/src/js/focus-trap/focus-trap.standalone.ts +1 -0
  44. package/src/js/index.ts +4 -4
  45. package/src/js/internal/draggable.ts +309 -168
  46. package/src/js/movable/movable.embedded.ts +328 -0
  47. package/src/js/movable/movable.standalone.ts +2 -0
  48. package/src/js/popover.ts +3 -2
  49. package/src/js/{sortable.ts → sortable/sortable.embedded.ts} +170 -103
  50. package/src/js/sortable/sortable.standalone.ts +2 -0
  51. package/src/js/tooltip.ts +49 -43
  52. package/dist/js/floatable/embedded.d.mts +0 -2
  53. package/dist/js/floatable/standalone.d.mts +0 -2
  54. package/dist/js/floatable/standalone.mjs +0 -2
  55. package/dist/js/focus-trap/embedded.d.mts +0 -2
  56. package/dist/js/focus-trap/standalone.d.mts +0 -2
  57. package/dist/js/focus-trap/standalone.mjs +0 -6
  58. package/dist/js/movable-dNqBctx3.d.mts +0 -101
  59. package/dist/js/movable.d.mts +0 -2
  60. package/dist/js/movable.mjs +0 -152
  61. package/dist/js/sortable.d.mts +0 -2
  62. package/src/js/floatable/standalone.ts +0 -1
  63. package/src/js/focus-trap/standalone.ts +0 -6
  64. package/src/js/movable.ts +0 -260
package/src/js/dialog.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import {on} from '@oscarpalmer/toretto/event';
2
2
  import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
3
- import {getOnBeforeToggleListener} from './floatable/embedded';
3
+ import {getOnBeforeToggleListener} from './floatable/floatable.embedded';
4
4
  import {attributable} from './internal/attributable';
5
+ import {ATTRIBUTE_MOVABLE_HANDLE} from './movable/movable.embedded';
6
+ import {createMovable, type OuiMovable} from './movable/movable.standalone';
5
7
 
6
8
  // #region Types
7
9
 
@@ -79,6 +81,7 @@ type OuiDialogState = {
79
81
  element: HTMLDialogElement;
80
82
  listeners: RemovableEventListener[];
81
83
  modal: boolean;
84
+ movable?: OuiMovable;
82
85
  };
83
86
 
84
87
  // #endregion
@@ -120,6 +123,22 @@ function getDialog(element: HTMLDialogElement): OuiDialog | undefined {
120
123
  return states.get(element)?.dialog;
121
124
  }
122
125
 
126
+ function getMovable(element: HTMLDialogElement): OuiMovable | undefined {
127
+ const handles = element.querySelectorAll(SELECTOR_MOVER);
128
+
129
+ if (handles.length === 0) {
130
+ return;
131
+ }
132
+
133
+ for (const handle of handles) {
134
+ handle.setAttribute(ATTRIBUTE_MOVABLE_HANDLE, '');
135
+ }
136
+
137
+ return createMovable(element, {
138
+ container: element.getAttribute(ATTRIBUTE_CONTAINER) ?? undefined,
139
+ });
140
+ }
141
+
123
142
  function getState(element: HTMLDialogElement): OuiDialogState {
124
143
  const state: OuiDialogState = {
125
144
  element,
@@ -136,9 +155,15 @@ function getState(element: HTMLDialogElement): OuiDialogState {
136
155
 
137
156
  element.setAttribute(ARIA_MODAL, String(modal));
138
157
  element.setAttribute(ATTRIBUTE_OPEN, '');
158
+
159
+ element.focus();
139
160
  } else {
140
161
  element.removeAttribute(ATTRIBUTE_OPEN);
141
162
  element.setAttribute(ARIA_MODAL, String(state.modal));
163
+
164
+ element.style.inset = '';
165
+ element.style.position = '';
166
+ element.style.transform = '';
142
167
  }
143
168
  }),
144
169
  ],
@@ -146,6 +171,7 @@ function getState(element: HTMLDialogElement): OuiDialogState {
146
171
  };
147
172
 
148
173
  state.dialog = new OuiDialog(state);
174
+ state.movable = getMovable(element);
149
175
 
150
176
  return state;
151
177
  }
@@ -170,6 +196,8 @@ const ARIA_MODAL = 'aria-modal';
170
196
 
171
197
  const ATTRIBUTE = 'oui-dialog';
172
198
 
199
+ const ATTRIBUTE_CONTAINER = `${ATTRIBUTE}-container`;
200
+
173
201
  const ATTRIBUTE_OPEN = `${ATTRIBUTE}-open`;
174
202
 
175
203
  const COMMAND_SHOW_MODAL = 'show-modal';
@@ -178,6 +206,8 @@ const EVENT_TOGGLE = 'toggle';
178
206
 
179
207
  const MESSAGE = 'The element must be an instance of HTMLDialogElement';
180
208
 
209
+ const SELECTOR_MOVER = `[${ATTRIBUTE}-mover]`;
210
+
181
211
  const SELECTOR_MODAL = ':modal';
182
212
 
183
213
  const TRUE = 'true';
@@ -5,7 +5,7 @@ import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
5
5
 
6
6
  // #region Types
7
7
 
8
- type CreateOuiFloatableOptions = {
8
+ export type CreateOuiFloatableOptions = {
9
9
  /**
10
10
  * Open the floatable when created? _(defaults to `true`)_
11
11
  */
@@ -50,10 +50,8 @@ export class OuiFloatable {
50
50
  content.popover = 'manual';
51
51
  }
52
52
 
53
- const anchorName = getAnchorName(anchor);
54
-
55
- anchor.style.anchorName = anchorName;
56
- content.style.positionAnchor = anchorName;
53
+ anchor.style.anchorName = state.name;
54
+ content.style.positionAnchor = state.name;
57
55
  }
58
56
 
59
57
  /**
@@ -105,6 +103,7 @@ export class OuiFloatable {
105
103
  export type OuiFloatableOptions = {
106
104
  attribute?: string;
107
105
  position: OuiFloatablePosition;
106
+ reusable: boolean;
108
107
  };
109
108
 
110
109
  export type OuiFloatablePosition =
@@ -124,6 +123,7 @@ export type OuiFloatablePosition =
124
123
  type OuiFloatableState = {
125
124
  anchor: HTMLElement;
126
125
  content: HTMLElement;
126
+ name: string;
127
127
  options: OuiFloatableOptions;
128
128
  };
129
129
 
@@ -171,6 +171,7 @@ export function createFloatable(
171
171
  content,
172
172
  {
173
173
  position,
174
+ reusable: false,
174
175
  },
175
176
  true,
176
177
  );
@@ -194,12 +195,14 @@ function createInstance(
194
195
  throw new TypeError(MESSAGE);
195
196
  }
196
197
 
197
- let floatable = instances.get(content);
198
+ let floatable = options.reusable ? undefined : instances.get(content);
198
199
 
199
200
  if (floatable == null) {
200
201
  floatable = new OuiFloatable(getState(anchor, content, options), standalone);
201
202
 
202
- instances.set(content, floatable);
203
+ if (!options.reusable) {
204
+ instances.set(content, floatable);
205
+ }
203
206
  }
204
207
 
205
208
  return floatable;
@@ -212,7 +215,7 @@ export function getOnBeforeToggleListener(element: HTMLElement): RemovableEventL
212
215
  event => {
213
216
  const {newState, source} = event;
214
217
 
215
- if (newState === STATE_OPEN && source?.getAttribute(ARIA_DISABLED) === TRUE) {
218
+ if (newState === FLOATABLE_STATE_OPEN && source?.getAttribute(ARIA_DISABLED) === TRUE) {
216
219
  event.preventDefault();
217
220
  }
218
221
  },
@@ -255,9 +258,12 @@ function getState(
255
258
  content: HTMLElement,
256
259
  options: OuiFloatableOptions,
257
260
  ): OuiFloatableState {
261
+ const name = getAnchorName(anchor);
262
+
258
263
  const state: OuiFloatableState = {
259
264
  anchor,
260
265
  content,
266
+ name,
261
267
  options,
262
268
  };
263
269
 
@@ -266,6 +272,7 @@ function getState(
266
272
 
267
273
  export function removeFloatable(content: HTMLElement): void {
268
274
  instances.get(content)?.destroy();
275
+
269
276
  instances.delete(content);
270
277
  }
271
278
 
@@ -274,6 +281,7 @@ function setPosition(state: OuiFloatableState, override?: OuiFloatablePosition):
274
281
 
275
282
  const area = AREAS[position];
276
283
 
284
+ state.content.style.positionAnchor = state.name;
277
285
  state.content.style.positionArea = area;
278
286
 
279
287
  state.content.setAttribute(ATTRIBUTE_POSITION, POSITIONS[area]);
@@ -306,14 +314,14 @@ const ATTRIBUTE_POSITION = 'oui-position';
306
314
 
307
315
  const EVENT_BEFORE = 'beforetoggle';
308
316
 
317
+ export const FLOATABLE_STATE_OPEN = 'open';
318
+
309
319
  const MESSAGE = 'Anchor and content must be an HTMLElement or SVGElement';
310
320
 
311
321
  const POSITIONS = Object.fromEntries(
312
322
  Object.entries(AREAS).map(([position, area]) => [area, position]),
313
323
  ) as Record<string, OuiFloatablePosition>;
314
324
 
315
- const STATE_OPEN = 'open';
316
-
317
325
  const TRUE = 'true';
318
326
 
319
327
  const instances = new WeakMap<HTMLElement, OuiFloatable>();
@@ -0,0 +1,6 @@
1
+ export {
2
+ createFloatable,
3
+ type CreateOuiFloatableOptions,
4
+ type OuiFloatable,
5
+ type OuiFloatablePosition,
6
+ } from './floatable.embedded';
@@ -4,6 +4,7 @@ import {on} from '@oscarpalmer/toretto/event';
4
4
  import {findAncestor} from '@oscarpalmer/toretto/find';
5
5
  import {getFocusable} from '@oscarpalmer/toretto/focusable';
6
6
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
7
+ import {attributable} from '../internal/attributable';
7
8
 
8
9
  // #region Types
9
10
 
@@ -14,14 +15,14 @@ export class OuiFocusTrap {
14
15
  readonly #state: OuiFocusTrapState;
15
16
 
16
17
  /**
17
- * Is focused maintained within the _FocusTrap_ when clicking outside of it?
18
+ * Is focused maintained within the _focus trap_ when clicking outside of it?
18
19
  */
19
20
  get contain(): boolean {
20
21
  return this.#state.element.hasAttribute(ATTRIBUTE_FOCUSTRAP_CONTAIN);
21
22
  }
22
23
 
23
24
  /**
24
- * Should focus be maintained within the _FocusTrap_ when clicking outside of it?
25
+ * Should focus be maintained within the _focus trap_ when clicking outside of it?
25
26
  */
26
27
  set contain(value: boolean) {
27
28
  if (value === true) {
@@ -32,14 +33,14 @@ export class OuiFocusTrap {
32
33
  }
33
34
 
34
35
  /**
35
- * Is the _FocusTrap_ disabled?
36
+ * Is the _focus trap_ disabled?
36
37
  */
37
38
  get disabled(): boolean {
38
39
  return this.#state.disabled;
39
40
  }
40
41
 
41
42
  /**
42
- * Should the _FocusTrap_ be disabled?
43
+ * Should the _focus trap_ be disabled?
43
44
  */
44
45
  set disabled(value: boolean) {
45
46
  if (value === true) {
@@ -50,14 +51,14 @@ export class OuiFocusTrap {
50
51
  }
51
52
 
52
53
  /**
53
- * Is focus allowed to escape when pressing _Escape_ within the _FocusTrap_?
54
+ * Is focus allowed to escape when pressing _Escape_ within the _focus trap_?
54
55
  */
55
56
  get noescape(): boolean {
56
57
  return this.#state.element.hasAttribute(ATTRIBUTE_FOCUSTRAP_NOESCAPE);
57
58
  }
58
59
 
59
60
  /**
60
- * Should the _FocusTrap_ allow focus to escape when pressing _Escape_?
61
+ * Should the _focus trap_ allow focus to escape when pressing _Escape_?
61
62
  */
62
63
  set noescape(value: boolean) {
63
64
  if (value === true) {
@@ -82,7 +83,7 @@ export class OuiFocusTrap {
82
83
  }
83
84
 
84
85
  /**
85
- * Destroys the _FocusTrap_
86
+ * Destroys the _focus trap_
86
87
  */
87
88
  destroy(): void {
88
89
  if (this.#destroyed) {
@@ -105,7 +106,7 @@ export class OuiFocusTrap {
105
106
  }
106
107
 
107
108
  /**
108
- * Disables the _FocusTrap_
109
+ * Disables the _focus trap_
109
110
  */
110
111
  disable(): void {
111
112
  if (!this.#destroyed && !(this.disabled || this.#state.options.noescape)) {
@@ -114,7 +115,7 @@ export class OuiFocusTrap {
114
115
  }
115
116
 
116
117
  /**
117
- * Enables the _FocusTrap_
118
+ * Enables the _focus trap_
118
119
  */
119
120
  enable(): void {
120
121
  if (!this.#destroyed && this.disabled) {
@@ -123,9 +124,9 @@ export class OuiFocusTrap {
123
124
  }
124
125
 
125
126
  /**
126
- * Focuses the _FocusTrap_
127
+ * Focuses the _focus trap_
127
128
  *
128
- * @param last When `true`, focuses the last focusable element within the _FocusTrap_ instead of the first
129
+ * @param last When `true`, focuses the last focusable element within the _focus trap_ instead of the first
129
130
  */
130
131
  focus(last: boolean): void {
131
132
  if (!this.#destroyed && !this.disabled) {
@@ -134,13 +135,13 @@ export class OuiFocusTrap {
134
135
  }
135
136
  }
136
137
 
137
- type OuiFocusTrapOptions = {
138
+ export type OuiFocusTrapOptions = {
138
139
  /**
139
- * Should focus be maintained within the _FocusTrap_ when clicking outside of it?
140
+ * Should focus be maintained within the _focus trap_ when clicking outside of it?
140
141
  */
141
142
  contain?: boolean;
142
143
  /**
143
- * Should the _FocusTrap_ allow focus to escape when pressing _Escape_?
144
+ * Should the _focus trap_ allow focus to escape when pressing _Escape_?
144
145
  */
145
146
  noescape?: boolean;
146
147
  };
@@ -156,11 +157,11 @@ type OuiFocusTrapState = {
156
157
  // #region Functions
157
158
 
158
159
  /**
159
- * Creates _(or retrieves)_ a _FocusTrap_ for an element
160
+ * Creates _(or retrieves)_ a _focus trap_ for an element
160
161
  *
161
162
  * @param element Element to trap focus within
162
- * @param options _FocusTrap_ options
163
- * @returns _FocusTrap_ instance
163
+ * @param options _Focus trap_ options
164
+ * @returns _Focus trap_ instance
164
165
  */
165
166
  export function createFocusTrap(element: HTMLElement, options?: OuiFocusTrapOptions): OuiFocusTrap {
166
167
  if (!isHTMLOrSVGElement(element)) {
@@ -399,6 +400,8 @@ let lastTarget: HTMLElement | undefined;
399
400
 
400
401
  // #region Initialization
401
402
 
403
+ attributable(ATTRIBUTE_FOCUSTRAP, createFocusTrap, removeFocusTrap);
404
+
402
405
  on(document, 'focusin', onFocusIn, EVENT_OPTIONS);
403
406
  on(document, 'keydown', onKeydown, EVENT_OPTIONS);
404
407
  on(document, 'mousedown', onPointerdown, EVENT_OPTIONS);
@@ -0,0 +1 @@
1
+ export {createFocusTrap, type OuiFocusTrap, type OuiFocusTrapOptions} from './focus-trap.embedded';
package/src/js/index.ts CHANGED
@@ -1,10 +1,10 @@
1
- import './movable';
2
- import './sortable';
1
+ import './sortable/sortable.embedded';
3
2
  import './toolbar';
4
3
 
5
4
  export * from './dialog';
6
- export * from './floatable/standalone';
7
- export * from './focus-trap/standalone';
5
+ export * from './floatable/floatable.standalone';
6
+ export * from './focus-trap/focus-trap.standalone';
7
+ export * from './movable/movable.standalone';
8
8
  export * from './popover';
9
9
  export * from './splitter';
10
10
  export * from './tooltip';