@fr0st/ui-sortable 3.0.0 → 3.0.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.
package/README.md CHANGED
@@ -19,85 +19,76 @@ Drag-and-drop sorting for Frost UI with handles, connected containers, axis cont
19
19
  - Configurable, direction-aware item swap threshold
20
20
  - Normalized item, container, and index data on lifecycle events
21
21
  - Native `Sortable` class and `sortable` fQuery plugin
22
+ - Existing-instance reuse with frozen resolved options
22
23
  - Prebuilt ESM and UMD bundles with source maps
23
24
  - No component-specific CSS or Sass
24
25
  - JSDoc-powered IntelliSense
25
26
 
27
+ Explore [the demo](./demo/index.html) for interactive examples.
28
+
26
29
  ## Installation
27
30
 
28
31
  ### Browser projects / bundlers
29
32
 
30
- Install Sortable with its Frost UI v4 and fQuery v5 peers:
31
-
32
33
  ```bash
33
- npm i @fr0st/ui-sortable@3 @fr0st/ui@4 @fr0st/query@5
34
+ npm i @fr0st/ui-sortable
34
35
  ```
35
36
 
36
- The package root resolves to the compiled ESM bundle. Import the Frost UI stylesheet and the default component export:
37
+ Frost UI Sortable's package entry point is ESM-only and requires a browser DOM. Import the default `Sortable` export and the stylesheets in browser projects and bundlers.
37
38
 
38
39
  ```js
39
40
  import '@fr0st/ui/dist/frost-ui.min.css';
40
41
  import Sortable from '@fr0st/ui-sortable';
41
-
42
- const sortable = Sortable.init(
43
- document.querySelector('#tasks'),
44
- {
45
- cursor: 'grab',
46
- handle: '.drag-handle',
47
- },
48
- );
49
42
  ```
50
43
 
51
- `@fr0st/ui` and `@fr0st/query` are peer dependencies so the component shares the application's UI and fQuery instances. The package root, `dist/*`, and `src/*` are available through package exports.
52
-
53
- Sortable requires a browser DOM or a compatible DOM environment configured through fQuery. Server-rendered applications should load the component on the client.
44
+ `@fr0st/ui` and `@fr0st/query` are peer dependencies so the component shares the application's instances.
54
45
 
55
46
  ### Browser (ESM)
56
47
 
57
- The ESM bundle imports `@fr0st/ui` and `@fr0st/query`. Frost UI and fQuery also require `@fr0st/core`, so map all three dependencies when loading the bundle directly in a browser:
48
+ The ESM bundle imports `@fr0st/ui` and `@fr0st/query`. fQuery also imports `@fr0st/core`, so map all three dependencies when loading the bundle directly in a browser:
58
49
 
59
50
  ```html
60
51
  <link
61
52
  rel="stylesheet"
62
- href="https://cdn.jsdelivr.net/npm/@fr0st/ui@4/dist/frost-ui.min.css">
63
-
53
+ href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
64
54
  <script type="importmap">
65
55
  {
66
56
  "imports": {
67
- "@fr0st/core": "https://cdn.jsdelivr.net/npm/@fr0st/core@4/dist/frost-core.esm.min.js",
68
- "@fr0st/query": "https://cdn.jsdelivr.net/npm/@fr0st/query@5/dist/fquery.esm.min.js",
69
- "@fr0st/ui": "https://cdn.jsdelivr.net/npm/@fr0st/ui@4/dist/frost-ui.esm.min.js"
57
+ "@fr0st/core": "https://cdn.jsdelivr.net/npm/@fr0st/core@latest/dist/frost-core.esm.min.js",
58
+ "@fr0st/query": "https://cdn.jsdelivr.net/npm/@fr0st/query@latest/dist/fquery.esm.min.js",
59
+ "@fr0st/ui": "https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.esm.min.js"
70
60
  }
71
61
  }
72
62
  </script>
73
63
  <script type="module">
74
- import Sortable from 'https://cdn.jsdelivr.net/npm/@fr0st/ui-sortable@3/dist/frost-ui-sortable.esm.min.js';
75
-
76
- Sortable.init(document.querySelector('#tasks'));
64
+ import Sortable from 'https://cdn.jsdelivr.net/npm/@fr0st/ui-sortable@latest/dist/frost-ui-sortable.esm.min.js';
77
65
  </script>
78
66
  ```
79
67
 
80
68
  ### Browser (UMD)
81
69
 
82
- Load Frost UI's all-in-one bundle before Sortable. The UI bundle supplies both the `UI` and `fQuery` globals expected by the component:
70
+ Load the bundles from your own copy or a CDN:
83
71
 
84
72
  ```html
85
73
  <link
86
74
  rel="stylesheet"
87
- href="https://cdn.jsdelivr.net/npm/@fr0st/ui@4/dist/frost-ui.min.css">
88
-
89
- <script src="https://cdn.jsdelivr.net/npm/@fr0st/ui@4/dist/frost-ui-bundle.min.js"></script>
90
- <script src="https://cdn.jsdelivr.net/npm/@fr0st/ui-sortable@3/dist/frost-ui-sortable.min.js"></script>
75
+ href="/path/to/dist/frost-ui.min.css">
76
+ <script src="/path/to/dist/frost-ui-bundle.min.js"></script>
77
+ <script src="/path/to/dist/frost-ui-sortable.min.js"></script>
78
+ <!-- or -->
79
+ <link
80
+ rel="stylesheet"
81
+ href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
82
+ <script src="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui-bundle.min.js"></script>
83
+ <script src="https://cdn.jsdelivr.net/npm/@fr0st/ui-sortable@latest/dist/frost-ui-sortable.min.js"></script>
91
84
  <script>
92
- const sortable = UI.Sortable.init(
93
- document.querySelector('#tasks'),
94
- );
85
+ const { Sortable } = globalThis.UI;
95
86
  </script>
96
87
  ```
97
88
 
98
- The UMD bundle adds `Sortable` to the existing `globalThis.UI` object. It expects `globalThis.UI` and `globalThis.fQuery` to exist before it loads. If the non-bundled Frost UI build is used instead, load fQuery, Frost UI, and Sortable in that order.
89
+ The UMD bundle adds `Sortable` to the existing `globalThis.UI` object. Load Frost UI's all-in-one bundle first; it supplies the `UI` and `fQuery` globals.
99
90
 
100
- Do not load the separate fQuery script when using `frost-ui-bundle.js` or `frost-ui-bundle.min.js`.
91
+ The package root resolves to the prebuilt ESM bundle. Published files under `dist/` and `src/` are also available through matching package subpaths.
101
92
 
102
93
  ## Usage
103
94
 
@@ -112,10 +103,11 @@ Sortable targets direct `<li>` children by default:
112
103
  ```
113
104
 
114
105
  ```js
106
+ import $ from '@fr0st/query';
115
107
  import Sortable from '@fr0st/ui-sortable';
116
108
 
117
109
  const sortable = Sortable.init(
118
- document.querySelector('#tasks'),
110
+ $.findOne('#tasks'),
119
111
  { cursor: 'grab' },
120
112
  );
121
113
  ```
@@ -139,7 +131,7 @@ Cancel checks include ancestors of the handle up to, but not including, the sort
139
131
  ```
140
132
 
141
133
  ```js
142
- Sortable.init(document.querySelector('#tasks'), {
134
+ Sortable.init($.findOne('#tasks'), {
143
135
  cursor: 'grab',
144
136
  handle: '.drag-handle',
145
137
  });
@@ -166,7 +158,7 @@ Initialize each container with a shared `connectWith` selector. Give empty conta
166
158
  ```
167
159
 
168
160
  ```js
169
- document.querySelectorAll('.connected').forEach((node) => {
161
+ $.find('.connected').forEach((node) => {
170
162
  Sortable.init(node, {
171
163
  connectWith: '.connected',
172
164
  cursor: 'grab',
@@ -183,10 +175,10 @@ Containers can use different `cursor` and `handle` settings. On transfer, Sortab
183
175
  Options are resolved in this order:
184
176
 
185
177
  1. Component defaults
186
- 2. The container's `data-ui-*` attributes
178
+ 2. The element's `data-ui-*` attributes
187
179
  3. Options passed to `Sortable.init()`
188
180
 
189
- Resolved `instance.options` are frozen.
181
+ Resolved `instance.options` are shallow-frozen.
190
182
 
191
183
  | Option | Type | Default | Description |
192
184
  | --- | --- | --- | --- |
@@ -226,7 +218,7 @@ Auto-scrolling updates item placement and sorting events even while the pointer
226
218
 
227
219
  ## Data attributes
228
220
 
229
- Every option can be supplied through a `data-ui-*` attribute. fQuery normalizes boolean, numeric, array, object, and `null` values.
221
+ Use kebab-case `data-ui-*` attributes for serializable options. fQuery normalizes boolean, numeric, array, object, and `null` values.
230
222
 
231
223
  ```html
232
224
  <ul
@@ -246,58 +238,51 @@ Every option can be supplied through a `data-ui-*` attribute. fQuery normalizes
246
238
  </ul>
247
239
  ```
248
240
 
249
- Data attributes do not initialize the component automatically. Initialize matching elements directly or through the fQuery plugin:
250
-
251
241
  ```js
242
+ import $ from '@fr0st/query';
243
+ import '@fr0st/ui-sortable';
244
+
252
245
  $('[data-ui-toggle="sortable"]').sortable();
253
246
  ```
254
247
 
255
- ## Methods
256
-
257
- ### `Sortable.init(node, options?)`
258
-
259
- Return the existing instance for `node`, or create one with the supplied options.
248
+ Data attributes configure options; they do not initialize Sortable by themselves. Initialize the component through the class or fQuery plugin. Changing an option's data attribute after initialization does not reconfigure the existing instance.
260
249
 
261
- ### `items()`
250
+ ## Methods
262
251
 
263
- Return a fresh array of elements matching the configured `items` selector.
252
+ | Method | Returns | Description |
253
+ | --- | --- | --- |
254
+ | `Sortable.init(node, options?)` | `Sortable` | Return the existing instance for an element or create one. |
255
+ | `disable()` | `void` | Stop the instance from starting sorts or receiving connected items. |
256
+ | `dispose()` | `void` | Remove owned events and registered state, release cursor locks, and cancel scrolling and active drag state. |
257
+ | `enable()` | `void` | Allow the instance to start and receive sorts. |
258
+ | `items()` | `HTMLElement[]` | Return a fresh array of elements matching the configured `items` selector. |
264
259
 
265
260
  ```js
266
261
  const items = sortable.items();
262
+ sortable.disable();
263
+ sortable.enable();
264
+ sortable.dispose();
267
265
  ```
268
266
 
269
- ### `disable()`
270
-
271
- Stop the instance from starting sorts or receiving connected items.
267
+ Disposal restores original inline cursor declarations, including `!important` priority, and releases cursor style locks.
272
268
 
273
- ### `enable()`
269
+ ## Lifecycle
274
270
 
275
- Allow the instance to start and receive sorts.
271
+ Calling `Sortable.init()` again for the same element returns its existing instance. Dispose the current instance before reinitializing with different options.
276
272
 
277
- ### `dispose()`
278
-
279
- Detach Sortable events, restore original inline cursor declarations (including `!important` priority), release cursor style locks, cancel active auto-scrolling, clear active drag state, remove stored component data, and set `node` and `options` to `null`. Calling `dispose()` again has no effect.
280
-
281
- ## fQuery plugin
282
-
283
- Registration adds `sortable` to `fQuery.QuerySet.prototype`:
284
-
285
- ```js
286
- const sortable = $('#tasks').sortable({ cursor: 'grab' });
273
+ An instance exposes its original element as `instance.node` and its shallow-frozen resolved configuration as `instance.options`. Both become `null` after disposal.
287
274
 
288
- $('#tasks').sortable('disable');
289
- $('#tasks').sortable('enable');
290
- const items = $('#tasks').sortable('items');
291
- $('#tasks').sortable('dispose');
292
- ```
275
+ `dispose()` releases resources owned by the component and removes its registered instance. Repeated disposal is safe and does not affect a new instance initialized on the same element. Use a new instance before calling other methods after disposal.
293
276
 
294
- Calling the plugin on multiple elements initializes every valid element and returns the first result.
277
+ If initialization fails, the component releases resources it created and removes its registered instance before rethrowing the error. The element can then be initialized again.
295
278
 
296
279
  ## Events
297
280
 
298
281
  Listen with fQuery's namespaced event API:
299
282
 
300
283
  ```js
284
+ import $ from '@fr0st/query';
285
+
301
286
  $.addEvent(
302
287
  '#tasks',
303
288
  'sort.ui.sortable sorting.ui.sortable sorted.ui.sortable update.ui.sortable',
@@ -313,14 +298,14 @@ $.addEvent(
313
298
  );
314
299
  ```
315
300
 
316
- | Event | Target | Description |
317
- | --- | --- | --- |
318
- | `sort.ui.sortable` | Source | The first movement begins an active sort. |
319
- | `sorting.ui.sortable` | Current container | The item changes index within the same container. |
320
- | `send.ui.sortable` | Previous container | The item moves out to a connected container. |
321
- | `receive.ui.sortable` | New container | The item enters from a connected container. |
322
- | `sorted.ui.sortable` | Final container | The pointer or touch is released, or the touch sequence is cancelled, after an active sort. |
323
- | `update.ui.sortable` | Changed container(s) | The final order changes. Cross-container moves update both source and destination. |
301
+ | Event | Description |
302
+ | --- | --- |
303
+ | `sort.ui.sortable` | The first movement begins an active sort. Emitted on the source container. |
304
+ | `sorting.ui.sortable` | The item changes index within the same container. Emitted on the current container. |
305
+ | `send.ui.sortable` | The item moves out to a connected container. Emitted on the previous container. |
306
+ | `receive.ui.sortable` | The item enters from a connected container. Emitted on the new container. |
307
+ | `sorted.ui.sortable` | The pointer or touch is released, or the touch sequence is cancelled, after an active sort. Emitted on the final container. |
308
+ | `update.ui.sortable` | The final order changes. Cross-container moves update both source and destination. Emitted on each container whose order changed. |
324
309
 
325
310
  Every event exposes the same normalized fields directly and through `event.detail`:
326
311
 
@@ -335,12 +320,32 @@ Every event exposes the same normalized fields directly and through `event.detai
335
320
 
336
321
  Indices are calculated among elements matching the container's configured `items` selector; unrelated child elements are not counted.
337
322
 
338
- ## Styling and accessibility
323
+ ## fQuery API
339
324
 
340
- Sortable does not ship component-specific CSS or Sass. It composes existing Frost UI classes and leaves collection layout to the application. The default helper class, `bg-body-tertiary`, comes from Frost UI and can be replaced or disabled with `helperClass: null`.
325
+ Importing Sortable registers `sortable` on `fQuery.QuerySet`:
326
+
327
+ ```js
328
+ import $ from '@fr0st/query';
329
+ import '@fr0st/ui-sortable';
330
+
331
+ const sortable = $('#tasks').sortable({ cursor: 'grab' });
332
+
333
+ $('#tasks').sortable('disable');
334
+ $('#tasks').sortable('enable');
335
+ const items = $('#tasks').sortable('items');
336
+ $('#tasks').sortable('dispose');
337
+ ```
338
+
339
+ Pass an options object to initialize every matched element, or pass a public method name followed by its arguments. The first component or method result is returned.
340
+
341
+ ## Accessibility
341
342
 
342
343
  Pointer sorting does not replace keyboard-accessible ordering controls. For workflows where order is essential, provide buttons or another keyboard mechanism that performs the same move and communicates the updated position to assistive technology. A visible drag handle should have an accessible label that describes its purpose.
343
344
 
345
+ ## Customization
346
+
347
+ Sortable does not ship component-specific CSS or Sass. It composes existing Frost UI classes and leaves collection layout to the application. The default helper class, `bg-body-tertiary`, comes from Frost UI and can be replaced or disabled with `helperClass: null`.
348
+
344
349
  For touch interfaces, applying `touch-action: none` to sortable items or handles prevents browser gestures from competing with dragging:
345
350
 
346
351
  ```css
@@ -350,13 +355,19 @@ For touch interfaces, applying `touch-action: none` to sortable items or handles
350
355
  }
351
356
  ```
352
357
 
358
+ ## Themes and RTL
359
+
360
+ Frost UI follows the user's preferred color scheme by default. Set `data-ui-theme="light"` or `data-ui-theme="dark"` on the document or an ancestor to select a theme explicitly.
361
+
362
+ Sortable uses Frost UI utilities for helper styling. The application's CSS controls list layout and item presentation; sorting follows the rendered item geometry.
363
+
353
364
  ## Browser support
354
365
 
355
366
  Bundles are built with Vite's `baseline-widely-available` target. The automated browser suite is configured for the Chromium, Firefox, and WebKit versions supplied by the installed Playwright release.
356
367
 
357
368
  ## Development
358
369
 
359
- Use Node.js matching `^20.19.0 || ^22.13.0 || >=24`. Install dependencies with `npm ci`, then install Playwright browsers with `npx playwright install --with-deps`.
370
+ Install dependencies with `npm ci`, then install Playwright browsers with `npx playwright install --with-deps`.
360
371
 
361
372
  ```bash
362
373
  npm test
@@ -364,12 +375,14 @@ npm run lint
364
375
  npm run build
365
376
  ```
366
377
 
367
- `npm test` rebuilds JavaScript, then runs the Playwright suite in Chromium, Firefox, and WebKit. `npm run test:browser` runs the suite against the existing bundles, so rebuild after changing source files.
378
+ `npm test` rebuilds the bundles, then runs the Playwright suite in Chromium, Firefox, and WebKit. `npm run test:browser` runs the suite against the existing bundles, so rebuild after changing source files.
368
379
 
369
- After building, `npm run test:coverage` runs Chromium tests and writes coverage reports to `coverage/` and `test-results/coverage/`.
380
+ After building, `npm run test:coverage` runs Chromium tests and writes coverage reports to `coverage/`.
370
381
 
371
382
  `npm run test:headed` and `npm run test:ui` also use the existing bundles and open headed browsers or the Playwright UI.
372
383
 
384
+ To view the demo, open `demo/index.html` in your browser after building.
385
+
373
386
  ## License
374
387
 
375
388
  Frost UI Sortable is released under the [MIT License](./LICENSE).
@@ -95,6 +95,7 @@ var Sortable = class extends BaseComponent {
95
95
  #currentSortable;
96
96
  #dragging;
97
97
  #enabled;
98
+ #initialCursorNodes = [];
98
99
  #pos;
99
100
  #scrollFrame;
100
101
  #selector;
@@ -107,11 +108,18 @@ var Sortable = class extends BaseComponent {
107
108
  */
108
109
  constructor(node, options) {
109
110
  super(node, options);
110
- this.#selector = this.options.handle ? `:is(${this.options.items}) :is(${this.options.handle})` : this.options.items;
111
- this.#dragging = false;
112
- this.#enabled = !this.options.disabled;
113
- setCursor($.find(this.#selector, this.node), this.options.cursor);
114
- this.#events();
111
+ try {
112
+ this.#selector = this.options.handle ? `:is(${this.options.items}) :is(${this.options.handle})` : this.options.items;
113
+ this.#dragging = false;
114
+ this.#enabled = !this.options.disabled;
115
+ this.#initialCursorNodes = $.find(this.#selector, this.node);
116
+ setCursor(this.#initialCursorNodes, this.options.cursor);
117
+ this.#events();
118
+ this.#initialCursorNodes = null;
119
+ } catch (error) {
120
+ this.dispose();
121
+ throw error;
122
+ }
115
123
  }
116
124
  /**
117
125
  * Disables the Sortable.
@@ -124,9 +132,10 @@ var Sortable = class extends BaseComponent {
124
132
  if (!this.node) return;
125
133
  $.removeEventDelegate(this.node, "mousedown.ui.sortable touchstart.ui.sortable");
126
134
  $.removeEvent(this.node, "receive.ui.sortable");
127
- resetCursor($.find(this.#selector, this.node));
135
+ resetCursor(this.#initialCursorNodes ?? $.find(this.#selector, this.node));
128
136
  if (this.#target && this.options.helperClass) $.removeClass(this.#target, this.options.helperClass);
129
137
  this.#resetDrag();
138
+ this.#initialCursorNodes = null;
130
139
  super.dispose();
131
140
  }
132
141
  /**
@@ -183,16 +192,16 @@ var Sortable = class extends BaseComponent {
183
192
  * Attaches events for the Sortable.
184
193
  */
185
194
  #events() {
186
- const downEvent = (e) => {
187
- const cancelled = this.options.cancel && ($.is(e.target, this.options.cancel) || $.is(e.currentTarget, this.options.cancel) || $.closest(e.target, this.options.cancel, this.node).length);
188
- if (e.button || !this.#enabled || cancelled || $.getDataset(this.node, "uiDragging")) return false;
189
- this.#target = this.options.handle ? this.items().find((node) => $.hasDescendent(node, e.currentTarget)) : e.currentTarget;
195
+ const downEvent = (event) => {
196
+ const cancelled = this.options.cancel && ($.is(event.target, this.options.cancel) || $.is(event.currentTarget, this.options.cancel) || $.closest(event.target, this.options.cancel, this.node).length);
197
+ if (event.button || !this.#enabled || cancelled || $.getDataset(this.node, "uiDragging")) return false;
198
+ this.#target = this.options.handle ? this.items().find((node) => $.hasDescendent(node, event.currentTarget)) : event.currentTarget;
190
199
  if (!this.#target) return false;
191
200
  };
192
- const moveEvent = (e) => {
201
+ const moveEvent = (event) => {
193
202
  if (!this.node || !this.#target) return;
194
203
  const previousPos = this.#pos;
195
- this.#pos = getPosition(e);
204
+ this.#pos = getPosition(event);
196
205
  const deltaX = previousPos ? this.#pos.x - previousPos.x : 0;
197
206
  const deltaY = previousPos ? this.#pos.y - previousPos.y : 0;
198
207
  if (!this.#dragging) {
@@ -216,13 +225,13 @@ var Sortable = class extends BaseComponent {
216
225
  this.#updatePlacement(deltaX, deltaY);
217
226
  this.#scheduleScroll();
218
227
  };
219
- const upEvent = (e) => {
228
+ const upEvent = (event) => {
220
229
  if (!this.node) return;
221
230
  if (!this.#dragging) {
222
231
  this.#target = null;
223
232
  return;
224
233
  }
225
- if (e.type === "mouseup") e.preventDefault();
234
+ if (event.type === "mouseup") event.preventDefault();
226
235
  const source = this.node;
227
236
  const destination = this.#currentSortable;
228
237
  /** @type {SortableEventData} */
@@ -244,7 +253,7 @@ var Sortable = class extends BaseComponent {
244
253
  };
245
254
  const dragEvent = $.mouseDragFactory(downEvent, moveEvent, upEvent);
246
255
  $.addEventDelegate(this.node, "mousedown.ui.sortable touchstart.ui.sortable", this.#selector, dragEvent);
247
- $.addEvent(this.node, "receive.ui.sortable", (_) => {
256
+ $.addEvent(this.node, "receive.ui.sortable", () => {
248
257
  setCursor($.find(this.#selector, this.node), this.options.cursor);
249
258
  });
250
259
  }
@@ -267,7 +276,7 @@ var Sortable = class extends BaseComponent {
267
276
  */
268
277
  #scheduleScroll() {
269
278
  if (!this.node || !this.options.scroll || !this.#dragging || this.#scrollFrame !== null && this.#scrollFrame !== void 0) return;
270
- this.#scrollFrame = window.requestAnimationFrame((_) => {
279
+ this.#scrollFrame = window.requestAnimationFrame(() => {
271
280
  this.#checkScroll();
272
281
  });
273
282
  }
@@ -1 +1 @@
1
- {"version":3,"file":"frost-ui-sortable.esm.js","names":[],"sources":["../src/helpers.js","../src/sortable.js","../src/index.js"],"sourcesContent":["/** @import { SortableEventData } from './sortable.js'; */\n\nimport $ from '@fr0st/query';\n\nconst cursorLocks = new WeakMap();\n\n/**\n * Resets the cursor style for a collection of elements.\n * @param {Iterable<HTMLElement>} nodes The elements to restore.\n */\nexport function resetCursor(nodes) {\n for (const node of nodes) {\n const release = cursorLocks.get(node);\n\n if (!release) {\n continue;\n }\n\n release();\n cursorLocks.delete(node);\n }\n};\n\n/**\n * Sets the cursor style for a collection of elements.\n * @param {Iterable<HTMLElement>} nodes The elements to update.\n * @param {string} cursor The cursor style.\n */\nexport function setCursor(nodes, cursor) {\n for (const node of nodes) {\n if (cursorLocks.has(node)) {\n $.setStyle(node, { cursor });\n } else {\n cursorLocks.set(node, $.setStyleLock(node, 'cursor', cursor));\n }\n }\n};\n\n/**\n * Triggers a Sortable event with its data and detail payloads.\n * @param {HTMLElement} node The event target.\n * @param {string} name The event name without its namespace.\n * @param {SortableEventData} data The sorting event data.\n */\nexport function triggerSortableEvent(node, name, data) {\n $.triggerEvent(node, `${name}.ui.sortable`, {\n data,\n detail: {\n target: data.item,\n ...data,\n },\n });\n};\n","import $ from '@fr0st/query';\nimport { BaseComponent, getPosition } from '@fr0st/ui';\nimport { resetCursor, setCursor, triggerSortableEvent } from './helpers.js';\n\nconst document = $.getContext();\nconst window = $.getWindow();\n\n/** @typedef {'x'|'y'} SortableAxis */\n\n/**\n * @typedef {object} SortableOptions\n * @property {SortableAxis|null} [axis=null] The axis used to determine item placement.\n * @property {string} [cancel='input, textarea, button, select, option'] The selector for elements that cannot start a sort.\n * @property {string|null} [connectWith=null] The selector for connected Sortable containers.\n * @property {string} [cursor='auto'] The cursor applied to sortable items or handles.\n * @property {boolean} [disabled=false] Whether sorting is initially disabled.\n * @property {boolean} [dropOnEmpty=true] Whether connected empty containers accept items.\n * @property {string|null} [handle=null] The handle selector within each sortable item.\n * @property {string|null} [helperClass='bg-body-tertiary'] The class applied to an item while it is being sorted.\n * @property {string} [items=':scope > li'] The selector for sortable items.\n * @property {boolean} [scroll=true] Whether to scroll the window while sorting near an edge.\n * @property {number} [scrollSensitivity=20] The distance from a window edge that starts scrolling, in pixels.\n * @property {number} [scrollSpeed=5] The scroll distance applied per animation frame, in pixels.\n * @property {number} [swapThreshold=0.25] The fraction of an item crossed before swapping, from `0` to `1`.\n */\n\n/**\n * @typedef {object} SortableEventData\n * @property {HTMLElement} item The item being sorted.\n * @property {HTMLElement} from The previous or original Sortable container.\n * @property {HTMLElement} to The next or final Sortable container.\n * @property {number} oldIndex The previous or original item index.\n * @property {number} newIndex The next or final item index.\n */\n\n/**\n * Controls a sortable collection of elements.\n * @augments {BaseComponent<SortableOptions>}\n */\nexport default class Sortable extends BaseComponent {\n /** @type {SortableOptions} */\n static defaults = {\n axis: null,\n cancel: 'input, textarea, button, select, option',\n connectWith: null,\n cursor: 'auto',\n disabled: false,\n dropOnEmpty: true,\n handle: null,\n helperClass: 'bg-body-tertiary',\n items: ':scope > li',\n scroll: true,\n scrollSensitivity: 20,\n scrollSpeed: 5,\n swapThreshold: 0.25,\n };\n\n #currentIndex;\n #currentSortable;\n #dragging;\n #enabled;\n #pos;\n #scrollFrame;\n #selector;\n #startIndex;\n #target;\n\n /**\n * Creates a Sortable.\n * @param {HTMLElement} node The sortable container.\n * @param {SortableOptions} [options] The Sortable options.\n */\n constructor(node, options) {\n super(node, options);\n\n this.#selector = this.options.handle ?\n `:is(${this.options.items}) :is(${this.options.handle})` :\n this.options.items;\n\n this.#dragging = false;\n this.#enabled = !this.options.disabled;\n\n setCursor($.find(this.#selector, this.node), this.options.cursor);\n this.#events();\n }\n\n /**\n * Disables the Sortable.\n */\n disable() {\n this.#enabled = false;\n }\n\n /** @inheritdoc */\n dispose() {\n if (!this.node) {\n return;\n }\n\n $.removeEventDelegate(this.node, 'mousedown.ui.sortable touchstart.ui.sortable');\n $.removeEvent(this.node, 'receive.ui.sortable');\n resetCursor($.find(this.#selector, this.node));\n\n if (this.#target && this.options.helperClass) {\n $.removeClass(this.#target, this.options.helperClass);\n }\n\n this.#resetDrag();\n\n super.dispose();\n }\n\n /**\n * Enables the Sortable.\n */\n enable() {\n this.#enabled = true;\n }\n\n /**\n * Gets the Sortable item elements.\n * @returns {HTMLElement[]} The Sortable item elements.\n */\n items() {\n return $.find(this.options.items, this.node);\n }\n\n /**\n * Checks whether the window should scroll while sorting.\n */\n #checkScroll() {\n this.#scrollFrame = null;\n\n if (!this.node || !this.#dragging || !this.#target || !this.#pos) {\n return;\n }\n\n const originalX = $.getScrollX(window);\n const originalY = $.getScrollY(window);\n const windowHeight = $.height(window);\n const windowWidth = $.width(window);\n\n let offsetX = originalX;\n let offsetY = originalY;\n\n if (this.#pos.y < originalY + this.options.scrollSensitivity) {\n offsetY = Math.max(0, originalY - this.options.scrollSpeed);\n } else if (this.#pos.y > originalY + windowHeight - this.options.scrollSensitivity) {\n const docHeight = $.height(document, { boxSize: $.SCROLL_BOX });\n const maxY = Math.max(0, docHeight - windowHeight);\n offsetY = Math.min(maxY, originalY + this.options.scrollSpeed);\n }\n\n if (this.#pos.x < originalX + this.options.scrollSensitivity) {\n offsetX = Math.max(0, originalX - this.options.scrollSpeed);\n } else if (this.#pos.x > originalX + windowWidth - this.options.scrollSensitivity) {\n const docWidth = $.width(document, { boxSize: $.SCROLL_BOX });\n const maxX = Math.max(0, docWidth - windowWidth);\n offsetX = Math.min(maxX, originalX + this.options.scrollSpeed);\n }\n\n if (offsetX === originalX && offsetY === originalY) {\n return;\n }\n\n window.scrollTo({\n left: offsetX,\n top: offsetY,\n behavior: 'instant',\n });\n\n const deltaX = offsetX - originalX;\n const deltaY = offsetY - originalY;\n\n this.#pos.x += deltaX;\n this.#pos.y += deltaY;\n\n this.#updatePlacement(deltaX, deltaY);\n this.#scheduleScroll();\n }\n\n /**\n * Attaches events for the Sortable.\n */\n #events() {\n const downEvent = (e) => {\n const cancelled = this.options.cancel &&\n (\n $.is(e.target, this.options.cancel) ||\n $.is(e.currentTarget, this.options.cancel) ||\n $.closest(e.target, this.options.cancel, this.node).length\n );\n\n if (\n e.button ||\n !this.#enabled ||\n cancelled ||\n $.getDataset(this.node, 'uiDragging')\n ) {\n return false;\n }\n\n this.#target = this.options.handle ?\n this.items().find((node) => $.hasDescendent(node, e.currentTarget)) :\n e.currentTarget;\n\n if (!this.#target) {\n return false;\n }\n };\n\n const moveEvent = (e) => {\n if (!this.node || !this.#target) {\n return;\n }\n\n const previousPos = this.#pos;\n this.#pos = getPosition(e);\n const deltaX = previousPos ?\n this.#pos.x - previousPos.x :\n 0;\n const deltaY = previousPos ?\n this.#pos.y - previousPos.y :\n 0;\n\n if (!this.#dragging) {\n this.#dragging = true;\n this.#currentSortable = this.node;\n this.#startIndex = this.items().indexOf(this.#target);\n this.#currentIndex = this.#startIndex;\n\n $.setDataset(this.node, { uiDragging: true });\n\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: this.node,\n to: this.node,\n oldIndex: this.#startIndex,\n newIndex: this.#startIndex,\n };\n\n triggerSortableEvent(this.node, 'sort', eventData);\n\n if (!this.node) {\n return;\n }\n\n if (this.options.helperClass) {\n $.addClass(this.#target, this.options.helperClass);\n }\n }\n\n this.#updatePlacement(deltaX, deltaY);\n this.#scheduleScroll();\n };\n\n const upEvent = (e) => {\n if (!this.node) {\n return;\n }\n\n if (!this.#dragging) {\n this.#target = null;\n return;\n }\n\n if (e.type === 'mouseup') {\n e.preventDefault();\n }\n\n const source = this.node;\n const destination = this.#currentSortable;\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: source,\n to: destination,\n oldIndex: this.#startIndex,\n newIndex: this.#currentIndex,\n };\n\n triggerSortableEvent(destination, 'sorted', eventData);\n\n if (!this.node) {\n return;\n }\n\n if (this.options.helperClass) {\n $.removeClass(this.#target, this.options.helperClass);\n }\n\n if (source !== destination) {\n triggerSortableEvent(source, 'update', eventData);\n\n if (this.node) {\n triggerSortableEvent(destination, 'update', eventData);\n }\n } else if (this.#currentIndex !== this.#startIndex) {\n triggerSortableEvent(destination, 'update', eventData);\n }\n\n if (this.node) {\n this.#resetDrag();\n }\n };\n\n const dragEvent = $.mouseDragFactory(downEvent, moveEvent, upEvent);\n\n $.addEventDelegate(\n this.node,\n 'mousedown.ui.sortable touchstart.ui.sortable',\n this.#selector,\n dragEvent,\n );\n\n $.addEvent(this.node, 'receive.ui.sortable', (_) => {\n setCursor($.find(this.#selector, this.node), this.options.cursor);\n });\n }\n\n /**\n * Resets the active drag state.\n */\n #resetDrag() {\n if (this.#scrollFrame !== null && this.#scrollFrame !== undefined) {\n window.cancelAnimationFrame(this.#scrollFrame);\n }\n\n if (this.#dragging && this.node) {\n $.removeDataset(this.node, 'uiDragging');\n }\n\n this.#currentIndex = null;\n this.#currentSortable = null;\n this.#dragging = false;\n this.#pos = null;\n this.#scrollFrame = null;\n this.#startIndex = null;\n this.#target = null;\n }\n\n /**\n * Schedules the next auto-scroll update.\n */\n #scheduleScroll() {\n if (\n !this.node ||\n !this.options.scroll ||\n !this.#dragging ||\n this.#scrollFrame !== null && this.#scrollFrame !== undefined\n ) {\n return;\n }\n\n this.#scrollFrame = window.requestAnimationFrame((_) => {\n this.#checkScroll();\n });\n }\n\n /**\n * Updates item placement and sorting events for the current pointer position.\n * @param {number} deltaX The horizontal pointer movement.\n * @param {number} deltaY The vertical pointer movement.\n */\n #updatePlacement(deltaX, deltaY) {\n let nearestSortable = this.node;\n\n if (this.options.connectWith) {\n const connected = $.find(this.options.connectWith)\n .filter((node) => node !== this.node)\n .filter((node) => {\n const sortable = this.constructor.init(node);\n\n return sortable.#enabled &&\n (sortable.options.dropOnEmpty || sortable.items().length);\n });\n\n if (connected.length) {\n nearestSortable = $.nearestTo(\n [this.node, ...connected],\n this.#pos.x,\n this.#pos.y,\n { offset: true },\n );\n }\n }\n\n const nearestInstance = this.constructor.init(nearestSortable);\n const nearestItems = nearestInstance.items()\n .filter((node) => node !== this.#target);\n const nearestItem = $.nearestTo(\n nearestItems,\n this.#pos.x,\n this.#pos.y,\n { offset: true },\n );\n\n if (nearestItem) {\n this.#updateTarget(\n nearestItem,\n this.#pos.x,\n this.#pos.y,\n deltaX,\n deltaY,\n );\n } else if (nearestSortable !== this.#currentSortable) {\n $.append(nearestSortable, this.#target);\n } else {\n return;\n }\n\n const newIndex = nearestInstance.items().indexOf(this.#target);\n\n if (nearestSortable !== this.#currentSortable) {\n const previousSortable = this.#currentSortable;\n const previousInstance = this.constructor.init(previousSortable);\n const { handle } = previousInstance.options;\n\n resetCursor(handle ?\n $.find(handle, this.#target) :\n [this.#target]);\n\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: previousSortable,\n to: nearestSortable,\n oldIndex: this.#currentIndex,\n newIndex,\n };\n\n triggerSortableEvent(previousSortable, 'send', eventData);\n\n if (!this.node) {\n return;\n }\n\n triggerSortableEvent(nearestSortable, 'receive', eventData);\n } else if (this.#currentIndex !== newIndex) {\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: nearestSortable,\n to: nearestSortable,\n oldIndex: this.#currentIndex,\n newIndex,\n };\n\n triggerSortableEvent(nearestSortable, 'sorting', eventData);\n }\n\n if (!this.node) {\n return;\n }\n\n this.#currentIndex = newIndex;\n this.#currentSortable = nearestSortable;\n }\n\n /**\n * Moves the target relative to the nearest item.\n * @param {HTMLElement} nearestItem The nearest item.\n * @param {number} x The mouse X position.\n * @param {number} y The mouse Y position.\n * @param {number} deltaX The horizontal pointer movement.\n * @param {number} deltaY The vertical pointer movement.\n */\n #updateTarget(nearestItem, x, y, deltaX, deltaY) {\n const nearestCenter = $.center(nearestItem, { offset: true });\n const nearestRect = $.rect(nearestItem, { offset: true });\n const targetCenter = $.center(this.#target, { offset: true });\n const threshold = Math.min(1, Math.max(0, this.options.swapThreshold));\n let thresholdX = 0.5;\n let thresholdY = 0.5;\n\n if (deltaX) {\n thresholdX = deltaX > 0 ?\n threshold :\n 1 - threshold;\n }\n\n if (deltaY) {\n thresholdY = deltaY > 0 ?\n threshold :\n 1 - threshold;\n }\n\n let method;\n if (this.options.axis === 'y' || nearestCenter.x === targetCenter.x) {\n const swapY = nearestRect.top + nearestRect.height * thresholdY;\n method = y < swapY ?\n 'before' :\n 'after';\n } else if (this.options.axis === 'x' || nearestCenter.y === targetCenter.y) {\n const swapX = nearestRect.left + nearestRect.width * thresholdX;\n method = x < swapX ?\n 'before' :\n 'after';\n } else {\n const swapX = nearestRect.left + nearestRect.width * thresholdX;\n const swapY = nearestRect.top + nearestRect.height * thresholdY;\n method = x < swapX && y < swapY ?\n 'before' :\n 'after';\n }\n\n $[method](nearestItem, this.#target);\n }\n}\n","import { initComponent } from '@fr0st/ui';\nimport Sortable from './sortable.js';\n\ninitComponent('sortable', Sortable);\n\nexport default Sortable;\n"],"mappings":"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"}
1
+ {"version":3,"file":"frost-ui-sortable.esm.js","names":[],"sources":["../src/helpers.js","../src/sortable.js","../src/index.js"],"sourcesContent":["/** @import { SortableEventData } from './sortable.js'; */\n\nimport $ from '@fr0st/query';\n\nconst cursorLocks = new WeakMap();\n\n/**\n * Resets the cursor style for a collection of elements.\n * @param {Iterable<HTMLElement>} nodes The elements to restore.\n */\nexport function resetCursor(nodes) {\n for (const node of nodes) {\n const release = cursorLocks.get(node);\n\n if (!release) {\n continue;\n }\n\n release();\n cursorLocks.delete(node);\n }\n}\n\n/**\n * Sets the cursor style for a collection of elements.\n * @param {Iterable<HTMLElement>} nodes The elements to update.\n * @param {string} cursor The cursor style.\n */\nexport function setCursor(nodes, cursor) {\n for (const node of nodes) {\n if (cursorLocks.has(node)) {\n $.setStyle(node, { cursor });\n } else {\n cursorLocks.set(node, $.setStyleLock(node, 'cursor', cursor));\n }\n }\n}\n\n/**\n * Triggers a Sortable event with its data and detail payloads.\n * @param {HTMLElement} node The event target.\n * @param {string} name The event name without its namespace.\n * @param {SortableEventData} data The sorting event data.\n */\nexport function triggerSortableEvent(node, name, data) {\n $.triggerEvent(node, `${name}.ui.sortable`, {\n data,\n detail: {\n target: data.item,\n ...data,\n },\n });\n}\n","import $ from '@fr0st/query';\nimport { BaseComponent, getPosition } from '@fr0st/ui';\nimport { resetCursor, setCursor, triggerSortableEvent } from './helpers.js';\n\nconst document = $.getContext();\nconst window = $.getWindow();\n\n/** @typedef {'x'|'y'} SortableAxis */\n\n/**\n * @typedef {object} SortableOptions\n * @property {SortableAxis|null} [axis=null] The axis used to determine item placement.\n * @property {string} [cancel='input, textarea, button, select, option'] The selector for elements that cannot start a sort.\n * @property {string|null} [connectWith=null] The selector for connected Sortable containers.\n * @property {string} [cursor='auto'] The cursor applied to sortable items or handles.\n * @property {boolean} [disabled=false] Whether sorting is initially disabled.\n * @property {boolean} [dropOnEmpty=true] Whether connected empty containers accept items.\n * @property {string|null} [handle=null] The handle selector within each sortable item.\n * @property {string|null} [helperClass='bg-body-tertiary'] The class applied to an item while it is being sorted.\n * @property {string} [items=':scope > li'] The selector for sortable items.\n * @property {boolean} [scroll=true] Whether to scroll the window while sorting near an edge.\n * @property {number} [scrollSensitivity=20] The distance from a window edge that starts scrolling, in pixels.\n * @property {number} [scrollSpeed=5] The scroll distance applied per animation frame, in pixels.\n * @property {number} [swapThreshold=0.25] The fraction of an item crossed before swapping, from `0` to `1`.\n */\n\n/**\n * @typedef {object} SortableEventData\n * @property {HTMLElement} item The item being sorted.\n * @property {HTMLElement} from The previous or original Sortable container.\n * @property {HTMLElement} to The next or final Sortable container.\n * @property {number} oldIndex The previous or original item index.\n * @property {number} newIndex The next or final item index.\n */\n\n/**\n * Controls a sortable collection of elements.\n * @augments {BaseComponent<SortableOptions>}\n */\nexport default class Sortable extends BaseComponent {\n /** @type {SortableOptions} */\n static defaults = {\n axis: null,\n cancel: 'input, textarea, button, select, option',\n connectWith: null,\n cursor: 'auto',\n disabled: false,\n dropOnEmpty: true,\n handle: null,\n helperClass: 'bg-body-tertiary',\n items: ':scope > li',\n scroll: true,\n scrollSensitivity: 20,\n scrollSpeed: 5,\n swapThreshold: 0.25,\n };\n\n #currentIndex;\n #currentSortable;\n #dragging;\n #enabled;\n #initialCursorNodes = [];\n #pos;\n #scrollFrame;\n #selector;\n #startIndex;\n #target;\n\n /**\n * Creates a Sortable.\n * @param {HTMLElement} node The sortable container.\n * @param {SortableOptions} [options] The Sortable options.\n */\n constructor(node, options) {\n super(node, options);\n\n try {\n this.#selector = this.options.handle ?\n `:is(${this.options.items}) :is(${this.options.handle})` :\n this.options.items;\n\n this.#dragging = false;\n this.#enabled = !this.options.disabled;\n\n this.#initialCursorNodes = $.find(this.#selector, this.node);\n setCursor(this.#initialCursorNodes, this.options.cursor);\n this.#events();\n this.#initialCursorNodes = null;\n } catch (error) {\n this.dispose();\n throw error;\n }\n }\n\n /**\n * Disables the Sortable.\n */\n disable() {\n this.#enabled = false;\n }\n\n /** @inheritdoc */\n dispose() {\n if (!this.node) {\n return;\n }\n\n $.removeEventDelegate(this.node, 'mousedown.ui.sortable touchstart.ui.sortable');\n $.removeEvent(this.node, 'receive.ui.sortable');\n resetCursor(this.#initialCursorNodes ?? $.find(this.#selector, this.node));\n\n if (this.#target && this.options.helperClass) {\n $.removeClass(this.#target, this.options.helperClass);\n }\n\n this.#resetDrag();\n\n this.#initialCursorNodes = null;\n\n super.dispose();\n }\n\n /**\n * Enables the Sortable.\n */\n enable() {\n this.#enabled = true;\n }\n\n /**\n * Gets the Sortable item elements.\n * @returns {HTMLElement[]} The Sortable item elements.\n */\n items() {\n return $.find(this.options.items, this.node);\n }\n\n /**\n * Checks whether the window should scroll while sorting.\n */\n #checkScroll() {\n this.#scrollFrame = null;\n\n if (!this.node || !this.#dragging || !this.#target || !this.#pos) {\n return;\n }\n\n const originalX = $.getScrollX(window);\n const originalY = $.getScrollY(window);\n const windowHeight = $.height(window);\n const windowWidth = $.width(window);\n\n let offsetX = originalX;\n let offsetY = originalY;\n\n if (this.#pos.y < originalY + this.options.scrollSensitivity) {\n offsetY = Math.max(0, originalY - this.options.scrollSpeed);\n } else if (this.#pos.y > originalY + windowHeight - this.options.scrollSensitivity) {\n const docHeight = $.height(document, { boxSize: $.SCROLL_BOX });\n const maxY = Math.max(0, docHeight - windowHeight);\n offsetY = Math.min(maxY, originalY + this.options.scrollSpeed);\n }\n\n if (this.#pos.x < originalX + this.options.scrollSensitivity) {\n offsetX = Math.max(0, originalX - this.options.scrollSpeed);\n } else if (this.#pos.x > originalX + windowWidth - this.options.scrollSensitivity) {\n const docWidth = $.width(document, { boxSize: $.SCROLL_BOX });\n const maxX = Math.max(0, docWidth - windowWidth);\n offsetX = Math.min(maxX, originalX + this.options.scrollSpeed);\n }\n\n if (offsetX === originalX && offsetY === originalY) {\n return;\n }\n\n window.scrollTo({\n left: offsetX,\n top: offsetY,\n behavior: 'instant',\n });\n\n const deltaX = offsetX - originalX;\n const deltaY = offsetY - originalY;\n\n this.#pos.x += deltaX;\n this.#pos.y += deltaY;\n\n this.#updatePlacement(deltaX, deltaY);\n this.#scheduleScroll();\n }\n\n /**\n * Attaches events for the Sortable.\n */\n #events() {\n const downEvent = (event) => {\n const cancelled = this.options.cancel &&\n (\n $.is(event.target, this.options.cancel) ||\n $.is(event.currentTarget, this.options.cancel) ||\n $.closest(event.target, this.options.cancel, this.node).length\n );\n\n if (\n event.button ||\n !this.#enabled ||\n cancelled ||\n $.getDataset(this.node, 'uiDragging')\n ) {\n return false;\n }\n\n this.#target = this.options.handle ?\n this.items().find((node) => $.hasDescendent(node, event.currentTarget)) :\n event.currentTarget;\n\n if (!this.#target) {\n return false;\n }\n };\n\n const moveEvent = (event) => {\n if (!this.node || !this.#target) {\n return;\n }\n\n const previousPos = this.#pos;\n this.#pos = getPosition(event);\n const deltaX = previousPos ?\n this.#pos.x - previousPos.x :\n 0;\n const deltaY = previousPos ?\n this.#pos.y - previousPos.y :\n 0;\n\n if (!this.#dragging) {\n this.#dragging = true;\n this.#currentSortable = this.node;\n this.#startIndex = this.items().indexOf(this.#target);\n this.#currentIndex = this.#startIndex;\n\n $.setDataset(this.node, { uiDragging: true });\n\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: this.node,\n to: this.node,\n oldIndex: this.#startIndex,\n newIndex: this.#startIndex,\n };\n\n triggerSortableEvent(this.node, 'sort', eventData);\n\n if (!this.node) {\n return;\n }\n\n if (this.options.helperClass) {\n $.addClass(this.#target, this.options.helperClass);\n }\n }\n\n this.#updatePlacement(deltaX, deltaY);\n this.#scheduleScroll();\n };\n\n const upEvent = (event) => {\n if (!this.node) {\n return;\n }\n\n if (!this.#dragging) {\n this.#target = null;\n return;\n }\n\n if (event.type === 'mouseup') {\n event.preventDefault();\n }\n\n const source = this.node;\n const destination = this.#currentSortable;\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: source,\n to: destination,\n oldIndex: this.#startIndex,\n newIndex: this.#currentIndex,\n };\n\n triggerSortableEvent(destination, 'sorted', eventData);\n\n if (!this.node) {\n return;\n }\n\n if (this.options.helperClass) {\n $.removeClass(this.#target, this.options.helperClass);\n }\n\n if (source !== destination) {\n triggerSortableEvent(source, 'update', eventData);\n\n if (this.node) {\n triggerSortableEvent(destination, 'update', eventData);\n }\n } else if (this.#currentIndex !== this.#startIndex) {\n triggerSortableEvent(destination, 'update', eventData);\n }\n\n if (this.node) {\n this.#resetDrag();\n }\n };\n\n const dragEvent = $.mouseDragFactory(downEvent, moveEvent, upEvent);\n\n $.addEventDelegate(\n this.node,\n 'mousedown.ui.sortable touchstart.ui.sortable',\n this.#selector,\n dragEvent,\n );\n\n $.addEvent(this.node, 'receive.ui.sortable', () => {\n setCursor($.find(this.#selector, this.node), this.options.cursor);\n });\n }\n\n /**\n * Resets the active drag state.\n */\n #resetDrag() {\n if (this.#scrollFrame !== null && this.#scrollFrame !== undefined) {\n window.cancelAnimationFrame(this.#scrollFrame);\n }\n\n if (this.#dragging && this.node) {\n $.removeDataset(this.node, 'uiDragging');\n }\n\n this.#currentIndex = null;\n this.#currentSortable = null;\n this.#dragging = false;\n this.#pos = null;\n this.#scrollFrame = null;\n this.#startIndex = null;\n this.#target = null;\n }\n\n /**\n * Schedules the next auto-scroll update.\n */\n #scheduleScroll() {\n if (\n !this.node ||\n !this.options.scroll ||\n !this.#dragging ||\n this.#scrollFrame !== null && this.#scrollFrame !== undefined\n ) {\n return;\n }\n\n this.#scrollFrame = window.requestAnimationFrame(() => {\n this.#checkScroll();\n });\n }\n\n /**\n * Updates item placement and sorting events for the current pointer position.\n * @param {number} deltaX The horizontal pointer movement.\n * @param {number} deltaY The vertical pointer movement.\n */\n #updatePlacement(deltaX, deltaY) {\n let nearestSortable = this.node;\n\n if (this.options.connectWith) {\n const connected = $.find(this.options.connectWith)\n .filter((node) => node !== this.node)\n .filter((node) => {\n const sortable = this.constructor.init(node);\n\n return sortable.#enabled &&\n (sortable.options.dropOnEmpty || sortable.items().length);\n });\n\n if (connected.length) {\n nearestSortable = $.nearestTo(\n [this.node, ...connected],\n this.#pos.x,\n this.#pos.y,\n { offset: true },\n );\n }\n }\n\n const nearestInstance = this.constructor.init(nearestSortable);\n const nearestItems = nearestInstance.items()\n .filter((node) => node !== this.#target);\n const nearestItem = $.nearestTo(\n nearestItems,\n this.#pos.x,\n this.#pos.y,\n { offset: true },\n );\n\n if (nearestItem) {\n this.#updateTarget(\n nearestItem,\n this.#pos.x,\n this.#pos.y,\n deltaX,\n deltaY,\n );\n } else if (nearestSortable !== this.#currentSortable) {\n $.append(nearestSortable, this.#target);\n } else {\n return;\n }\n\n const newIndex = nearestInstance.items().indexOf(this.#target);\n\n if (nearestSortable !== this.#currentSortable) {\n const previousSortable = this.#currentSortable;\n const previousInstance = this.constructor.init(previousSortable);\n const { handle } = previousInstance.options;\n\n resetCursor(handle ?\n $.find(handle, this.#target) :\n [this.#target]);\n\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: previousSortable,\n to: nearestSortable,\n oldIndex: this.#currentIndex,\n newIndex,\n };\n\n triggerSortableEvent(previousSortable, 'send', eventData);\n\n if (!this.node) {\n return;\n }\n\n triggerSortableEvent(nearestSortable, 'receive', eventData);\n } else if (this.#currentIndex !== newIndex) {\n /** @type {SortableEventData} */\n const eventData = {\n item: this.#target,\n from: nearestSortable,\n to: nearestSortable,\n oldIndex: this.#currentIndex,\n newIndex,\n };\n\n triggerSortableEvent(nearestSortable, 'sorting', eventData);\n }\n\n if (!this.node) {\n return;\n }\n\n this.#currentIndex = newIndex;\n this.#currentSortable = nearestSortable;\n }\n\n /**\n * Moves the target relative to the nearest item.\n * @param {HTMLElement} nearestItem The nearest item.\n * @param {number} x The mouse X position.\n * @param {number} y The mouse Y position.\n * @param {number} deltaX The horizontal pointer movement.\n * @param {number} deltaY The vertical pointer movement.\n */\n #updateTarget(nearestItem, x, y, deltaX, deltaY) {\n const nearestCenter = $.center(nearestItem, { offset: true });\n const nearestRect = $.rect(nearestItem, { offset: true });\n const targetCenter = $.center(this.#target, { offset: true });\n const threshold = Math.min(1, Math.max(0, this.options.swapThreshold));\n let thresholdX = 0.5;\n let thresholdY = 0.5;\n\n if (deltaX) {\n thresholdX = deltaX > 0 ?\n threshold :\n 1 - threshold;\n }\n\n if (deltaY) {\n thresholdY = deltaY > 0 ?\n threshold :\n 1 - threshold;\n }\n\n let method;\n if (this.options.axis === 'y' || nearestCenter.x === targetCenter.x) {\n const swapY = nearestRect.top + nearestRect.height * thresholdY;\n method = y < swapY ?\n 'before' :\n 'after';\n } else if (this.options.axis === 'x' || nearestCenter.y === targetCenter.y) {\n const swapX = nearestRect.left + nearestRect.width * thresholdX;\n method = x < swapX ?\n 'before' :\n 'after';\n } else {\n const swapX = nearestRect.left + nearestRect.width * thresholdX;\n const swapY = nearestRect.top + nearestRect.height * thresholdY;\n method = x < swapX && y < swapY ?\n 'before' :\n 'after';\n }\n\n $[method](nearestItem, this.#target);\n }\n}\n","import { initComponent } from '@fr0st/ui';\nimport Sortable from './sortable.js';\n\ninitComponent('sortable', Sortable);\n\nexport default Sortable;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{BaseComponent as e,getPosition as t,initComponent as n}from"@fr0st/ui";import r from"@fr0st/query";var i=new WeakMap;function a(e){for(let t of e){let e=i.get(t);e&&(e(),i.delete(t))}}function o(e,t){for(let n of e)i.has(n)?r.setStyle(n,{cursor:t}):i.set(n,r.setStyleLock(n,`cursor`,t))}function s(e,t,n){r.triggerEvent(e,`${t}.ui.sortable`,{data:n,detail:{target:n.item,...n}})}var c=r.getContext(),l=r.getWindow(),u=class extends e{static defaults={axis:null,cancel:`input, textarea, button, select, option`,connectWith:null,cursor:`auto`,disabled:!1,dropOnEmpty:!0,handle:null,helperClass:`bg-body-tertiary`,items:`:scope > li`,scroll:!0,scrollSensitivity:20,scrollSpeed:5,swapThreshold:.25};#e;#t;#n;#r;#i;#a;#o;#s;#c;constructor(e,t){super(e,t),this.#o=this.options.handle?`:is(${this.options.items}) :is(${this.options.handle})`:this.options.items,this.#n=!1,this.#r=!this.options.disabled,o(r.find(this.#o,this.node),this.options.cursor),this.#u()}disable(){this.#r=!1}dispose(){this.node&&(r.removeEventDelegate(this.node,`mousedown.ui.sortable touchstart.ui.sortable`),r.removeEvent(this.node,`receive.ui.sortable`),a(r.find(this.#o,this.node)),this.#c&&this.options.helperClass&&r.removeClass(this.#c,this.options.helperClass),this.#d(),super.dispose())}enable(){this.#r=!0}items(){return r.find(this.options.items,this.node)}#l(){if(this.#a=null,!this.node||!this.#n||!this.#c||!this.#i)return;let e=r.getScrollX(l),t=r.getScrollY(l),n=r.height(l),i=r.width(l),a=e,o=t;if(this.#i.y<t+this.options.scrollSensitivity)o=Math.max(0,t-this.options.scrollSpeed);else if(this.#i.y>t+n-this.options.scrollSensitivity){let e=r.height(c,{boxSize:r.SCROLL_BOX}),i=Math.max(0,e-n);o=Math.min(i,t+this.options.scrollSpeed)}if(this.#i.x<e+this.options.scrollSensitivity)a=Math.max(0,e-this.options.scrollSpeed);else if(this.#i.x>e+i-this.options.scrollSensitivity){let t=r.width(c,{boxSize:r.SCROLL_BOX}),n=Math.max(0,t-i);a=Math.min(n,e+this.options.scrollSpeed)}if(a===e&&o===t)return;l.scrollTo({left:a,top:o,behavior:`instant`});let s=a-e,u=o-t;this.#i.x+=s,this.#i.y+=u,this.#p(s,u),this.#f()}#u(){let e=r.mouseDragFactory(e=>{let t=this.options.cancel&&(r.is(e.target,this.options.cancel)||r.is(e.currentTarget,this.options.cancel)||r.closest(e.target,this.options.cancel,this.node).length);if(e.button||!this.#r||t||r.getDataset(this.node,`uiDragging`)||(this.#c=this.options.handle?this.items().find(t=>r.hasDescendent(t,e.currentTarget)):e.currentTarget,!this.#c))return!1},e=>{if(!this.node||!this.#c)return;let n=this.#i;this.#i=t(e);let i=n?this.#i.x-n.x:0,a=n?this.#i.y-n.y:0;if(!this.#n){this.#n=!0,this.#t=this.node,this.#s=this.items().indexOf(this.#c),this.#e=this.#s,r.setDataset(this.node,{uiDragging:!0});let e={item:this.#c,from:this.node,to:this.node,oldIndex:this.#s,newIndex:this.#s};if(s(this.node,`sort`,e),!this.node)return;this.options.helperClass&&r.addClass(this.#c,this.options.helperClass)}this.#p(i,a),this.#f()},e=>{if(!this.node)return;if(!this.#n){this.#c=null;return}e.type===`mouseup`&&e.preventDefault();let t=this.node,n=this.#t,i={item:this.#c,from:t,to:n,oldIndex:this.#s,newIndex:this.#e};s(n,`sorted`,i),this.node&&(this.options.helperClass&&r.removeClass(this.#c,this.options.helperClass),t===n?this.#e!==this.#s&&s(n,`update`,i):(s(t,`update`,i),this.node&&s(n,`update`,i)),this.node&&this.#d())});r.addEventDelegate(this.node,`mousedown.ui.sortable touchstart.ui.sortable`,this.#o,e),r.addEvent(this.node,`receive.ui.sortable`,e=>{o(r.find(this.#o,this.node),this.options.cursor)})}#d(){this.#a!==null&&this.#a!==void 0&&l.cancelAnimationFrame(this.#a),this.#n&&this.node&&r.removeDataset(this.node,`uiDragging`),this.#e=null,this.#t=null,this.#n=!1,this.#i=null,this.#a=null,this.#s=null,this.#c=null}#f(){!this.node||!this.options.scroll||!this.#n||this.#a!==null&&this.#a!==void 0||(this.#a=l.requestAnimationFrame(e=>{this.#l()}))}#p(e,t){let n=this.node;if(this.options.connectWith){let e=r.find(this.options.connectWith).filter(e=>e!==this.node).filter(e=>{let t=this.constructor.init(e);return t.#r&&(t.options.dropOnEmpty||t.items().length)});e.length&&(n=r.nearestTo([this.node,...e],this.#i.x,this.#i.y,{offset:!0}))}let i=this.constructor.init(n),o=i.items().filter(e=>e!==this.#c),c=r.nearestTo(o,this.#i.x,this.#i.y,{offset:!0});if(c)this.#m(c,this.#i.x,this.#i.y,e,t);else if(n!==this.#t)r.append(n,this.#c);else return;let l=i.items().indexOf(this.#c);if(n!==this.#t){let e=this.#t,{handle:t}=this.constructor.init(e).options;a(t?r.find(t,this.#c):[this.#c]);let i={item:this.#c,from:e,to:n,oldIndex:this.#e,newIndex:l};if(s(e,`send`,i),!this.node)return;s(n,`receive`,i)}else if(this.#e!==l){let e={item:this.#c,from:n,to:n,oldIndex:this.#e,newIndex:l};s(n,`sorting`,e)}this.node&&(this.#e=l,this.#t=n)}#m(e,t,n,i,a){let o=r.center(e,{offset:!0}),s=r.rect(e,{offset:!0}),c=r.center(this.#c,{offset:!0}),l=Math.min(1,Math.max(0,this.options.swapThreshold)),u=.5,d=.5;i&&(u=i>0?l:1-l),a&&(d=a>0?l:1-l);let f;if(this.options.axis===`y`||o.x===c.x)f=n<s.top+s.height*d?`before`:`after`;else if(this.options.axis===`x`||o.y===c.y)f=t<s.left+s.width*u?`before`:`after`;else{let e=s.left+s.width*u,r=s.top+s.height*d;f=t<e&&n<r?`before`:`after`}r[f](e,this.#c)}};n(`sortable`,u);var d=u;export{d as default};
1
+ import{BaseComponent as e,getPosition as t,initComponent as n}from"@fr0st/ui";import r from"@fr0st/query";var i=/* @__PURE__ */ new WeakMap;function a(e){for(let t of e){let e=i.get(t);e&&(e(),i.delete(t))}}function o(e,t){for(let n of e)i.has(n)?r.setStyle(n,{cursor:t}):i.set(n,r.setStyleLock(n,`cursor`,t))}function s(e,t,n){r.triggerEvent(e,`${t}.ui.sortable`,{data:n,detail:{target:n.item,...n}})}var c=r.getContext(),l=r.getWindow(),u=class extends e{static defaults={axis:null,cancel:`input, textarea, button, select, option`,connectWith:null,cursor:`auto`,disabled:!1,dropOnEmpty:!0,handle:null,helperClass:`bg-body-tertiary`,items:`:scope > li`,scroll:!0,scrollSensitivity:20,scrollSpeed:5,swapThreshold:.25};#e;#t;#n;#r;#i=[];#a;#o;#s;#c;#l;constructor(e,t){super(e,t);try{this.#s=this.options.handle?`:is(${this.options.items}) :is(${this.options.handle})`:this.options.items,this.#n=!1,this.#r=!this.options.disabled,this.#i=r.find(this.#s,this.node),o(this.#i,this.options.cursor),this.#d(),this.#i=null}catch(e){throw this.dispose(),e}}disable(){this.#r=!1}dispose(){this.node&&(r.removeEventDelegate(this.node,`mousedown.ui.sortable touchstart.ui.sortable`),r.removeEvent(this.node,`receive.ui.sortable`),a(this.#i??r.find(this.#s,this.node)),this.#l&&this.options.helperClass&&r.removeClass(this.#l,this.options.helperClass),this.#f(),this.#i=null,super.dispose())}enable(){this.#r=!0}items(){return r.find(this.options.items,this.node)}#u(){if(this.#o=null,!this.node||!this.#n||!this.#l||!this.#a)return;let e=r.getScrollX(l),t=r.getScrollY(l),n=r.height(l),i=r.width(l),a=e,o=t;if(this.#a.y<t+this.options.scrollSensitivity)o=Math.max(0,t-this.options.scrollSpeed);else if(this.#a.y>t+n-this.options.scrollSensitivity){let e=r.height(c,{boxSize:r.SCROLL_BOX}),i=Math.max(0,e-n);o=Math.min(i,t+this.options.scrollSpeed)}if(this.#a.x<e+this.options.scrollSensitivity)a=Math.max(0,e-this.options.scrollSpeed);else if(this.#a.x>e+i-this.options.scrollSensitivity){let t=r.width(c,{boxSize:r.SCROLL_BOX}),n=Math.max(0,t-i);a=Math.min(n,e+this.options.scrollSpeed)}if(a===e&&o===t)return;l.scrollTo({left:a,top:o,behavior:`instant`});let s=a-e,u=o-t;this.#a.x+=s,this.#a.y+=u,this.#m(s,u),this.#p()}#d(){let e=r.mouseDragFactory(e=>{let t=this.options.cancel&&(r.is(e.target,this.options.cancel)||r.is(e.currentTarget,this.options.cancel)||r.closest(e.target,this.options.cancel,this.node).length);if(e.button||!this.#r||t||r.getDataset(this.node,`uiDragging`)||(this.#l=this.options.handle?this.items().find(t=>r.hasDescendent(t,e.currentTarget)):e.currentTarget,!this.#l))return!1},e=>{if(!this.node||!this.#l)return;let n=this.#a;this.#a=t(e);let i=n?this.#a.x-n.x:0,a=n?this.#a.y-n.y:0;if(!this.#n){this.#n=!0,this.#t=this.node,this.#c=this.items().indexOf(this.#l),this.#e=this.#c,r.setDataset(this.node,{uiDragging:!0});let e={item:this.#l,from:this.node,to:this.node,oldIndex:this.#c,newIndex:this.#c};if(s(this.node,`sort`,e),!this.node)return;this.options.helperClass&&r.addClass(this.#l,this.options.helperClass)}this.#m(i,a),this.#p()},e=>{if(!this.node)return;if(!this.#n){this.#l=null;return}e.type===`mouseup`&&e.preventDefault();let t=this.node,n=this.#t,i={item:this.#l,from:t,to:n,oldIndex:this.#c,newIndex:this.#e};s(n,`sorted`,i),this.node&&(this.options.helperClass&&r.removeClass(this.#l,this.options.helperClass),t===n?this.#e!==this.#c&&s(n,`update`,i):(s(t,`update`,i),this.node&&s(n,`update`,i)),this.node&&this.#f())});r.addEventDelegate(this.node,`mousedown.ui.sortable touchstart.ui.sortable`,this.#s,e),r.addEvent(this.node,`receive.ui.sortable`,()=>{o(r.find(this.#s,this.node),this.options.cursor)})}#f(){this.#o!==null&&this.#o!==void 0&&l.cancelAnimationFrame(this.#o),this.#n&&this.node&&r.removeDataset(this.node,`uiDragging`),this.#e=null,this.#t=null,this.#n=!1,this.#a=null,this.#o=null,this.#c=null,this.#l=null}#p(){this.node&&this.options.scroll&&this.#n&&(this.#o===null||this.#o===void 0)&&(this.#o=l.requestAnimationFrame(()=>{this.#u()}))}#m(e,t){let n=this.node;if(this.options.connectWith){let e=r.find(this.options.connectWith).filter(e=>e!==this.node).filter(e=>{let t=this.constructor.init(e);return t.#r&&(t.options.dropOnEmpty||t.items().length)});e.length&&(n=r.nearestTo([this.node,...e],this.#a.x,this.#a.y,{offset:!0}))}let i=this.constructor.init(n),o=i.items().filter(e=>e!==this.#l),c=r.nearestTo(o,this.#a.x,this.#a.y,{offset:!0});if(c)this.#h(c,this.#a.x,this.#a.y,e,t);else if(n!==this.#t)r.append(n,this.#l);else return;let l=i.items().indexOf(this.#l);if(n!==this.#t){let e=this.#t,{handle:t}=this.constructor.init(e).options;a(t?r.find(t,this.#l):[this.#l]);let i={item:this.#l,from:e,to:n,oldIndex:this.#e,newIndex:l};if(s(e,`send`,i),!this.node)return;s(n,`receive`,i)}else if(this.#e!==l){let e={item:this.#l,from:n,to:n,oldIndex:this.#e,newIndex:l};s(n,`sorting`,e)}this.node&&(this.#e=l,this.#t=n)}#h(e,t,n,i,a){let o=r.center(e,{offset:!0}),s=r.rect(e,{offset:!0}),c=r.center(this.#l,{offset:!0}),l=Math.min(1,Math.max(0,this.options.swapThreshold)),u=.5,d=.5;i&&(u=i>0?l:1-l),a&&(d=a>0?l:1-l);let f;if(this.options.axis===`y`||o.x===c.x)f=n<s.top+s.height*d?`before`:`after`;else if(this.options.axis===`x`||o.y===c.y)f=t<s.left+s.width*u?`before`:`after`;else{let e=s.left+s.width*u,r=s.top+s.height*d;f=t<e&&n<r?`before`:`after`}r[f](e,this.#l)}};n(`sortable`,u);var d=u;export{d as default};
2
2
  //# sourceMappingURL=frost-ui-sortable.esm.min.js.map