@ixfx/components 0.8.1 → 0.8.3

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 (120) hide show
  1. package/bundle/index.d.ts +326 -103
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +2840 -1285
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +3 -3
  6. package/dist/ac-token.js +3 -3
  7. package/dist/checkbox.js +1 -1
  8. package/dist/{colour-picker-Ddsk-7E_.js → colour-picker-CSxwUmtE.js} +2 -2
  9. package/dist/{colour-picker-Ddsk-7E_.js.map → colour-picker-CSxwUmtE.js.map} +1 -1
  10. package/dist/colour-picker.js +1 -1
  11. package/dist/crumbs.js +4 -4
  12. package/dist/data-display.js +1 -1
  13. package/dist/data-grid-D-z7bx5-.d.ts.map +1 -1
  14. package/dist/data-grid.js +9 -6
  15. package/dist/data-grid.js.map +1 -1
  16. package/dist/detail-list-_ZJHYEBr.d.ts.map +1 -1
  17. package/dist/detail-list.js +68 -5
  18. package/dist/detail-list.js.map +1 -1
  19. package/dist/editable-label.js +1 -1
  20. package/dist/{fallbacks-DNI9xt0F.js → fallbacks-BcKGq-2F.js} +17 -1
  21. package/dist/{fallbacks-DNI9xt0F.js.map → fallbacks-BcKGq-2F.js.map} +1 -1
  22. package/dist/{grid-list-nRUQOcEL.js → grid-list-BXfY3XXX.js} +531 -29
  23. package/dist/grid-list-BXfY3XXX.js.map +1 -0
  24. package/dist/{grid-list-u33vmKzb.d.ts → grid-list-x550wJ0X.d.ts} +115 -3
  25. package/dist/grid-list-x550wJ0X.d.ts.map +1 -0
  26. package/dist/grid-list.d.ts +2 -2
  27. package/dist/grid-list.js +1 -1
  28. package/dist/handle-BPOxBOKf.js +219 -0
  29. package/dist/handle-BPOxBOKf.js.map +1 -0
  30. package/dist/{icon-DcEEGQa_.js → icon-Dy33gu4Z.js} +2 -2
  31. package/dist/{icon-DcEEGQa_.js.map → icon-Dy33gu4Z.js.map} +1 -1
  32. package/dist/icons.js +1 -1
  33. package/dist/{index-BeuCXtiO.d.ts → index-DV3iaUBW.d.ts} +2 -2
  34. package/dist/{index-BeuCXtiO.d.ts.map → index-DV3iaUBW.d.ts.map} +1 -1
  35. package/dist/index.d.ts +132 -50
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +554 -206
  38. package/dist/index.js.map +1 -1
  39. package/dist/{interaction-Cr13azbM.js → interaction-B9AAlsFO.js} +1 -1
  40. package/dist/labelled-radial-input.js +2 -2
  41. package/dist/{labelled-slider-input-BInB5GKL.js → labelled-slider-input-BCH9968c.js} +3 -3
  42. package/dist/{labelled-slider-input-BInB5GKL.js.map → labelled-slider-input-BCH9968c.js.map} +1 -1
  43. package/dist/labelled-slider-input.js +1 -1
  44. package/dist/list-selection-controller-SA4piSBi.js +589 -0
  45. package/dist/list-selection-controller-SA4piSBi.js.map +1 -0
  46. package/dist/{menu-Bg90Qr0B.js → menu-HMoxCPY2.js} +5 -5
  47. package/dist/{menu-Bg90Qr0B.js.map → menu-HMoxCPY2.js.map} +1 -1
  48. package/dist/{menu-item-Bx-Fs7jq.js → menu-item-BQDYxk5A.js} +5 -5
  49. package/dist/{menu-item-Bx-Fs7jq.js.map → menu-item-BQDYxk5A.js.map} +1 -1
  50. package/dist/menu.js +2 -2
  51. package/dist/miller.d.ts +1 -1
  52. package/dist/miller.js +3 -3
  53. package/dist/narrowed-text.js +3 -3
  54. package/dist/notifier.js +1 -1
  55. package/dist/panel.d.ts +1 -1
  56. package/dist/panel.js +1 -1
  57. package/dist/plots.d.ts +2 -2
  58. package/dist/plots.js +1 -1
  59. package/dist/polar-pad.js +1 -1
  60. package/dist/{radial-input-BfA3GO0H.js → radial-input-DyxfbNzD.js} +2 -2
  61. package/dist/{radial-input-BfA3GO0H.js.map → radial-input-DyxfbNzD.js.map} +1 -1
  62. package/dist/radial-input.js +1 -1
  63. package/dist/range-input.js +1 -1
  64. package/dist/range.js +1 -1
  65. package/dist/select-horiz.js +3 -3
  66. package/dist/{slider-input-Bl1hFE89.js → slider-input-WR8_RwHg.js} +2 -2
  67. package/dist/{slider-input-Bl1hFE89.js.map → slider-input-WR8_RwHg.js.map} +1 -1
  68. package/dist/slider-input.js +1 -1
  69. package/dist/snackbar.js +1 -1
  70. package/dist/{split-layout-BFSbUZQv.js → split-layout-CA5jbNbG.js} +22 -33
  71. package/dist/split-layout-CA5jbNbG.js.map +1 -0
  72. package/dist/split-layout-JK3xWVX2.d.ts.map +1 -1
  73. package/dist/split-layout.js +1 -1
  74. package/dist/swipe.js +1 -1
  75. package/dist/tabs.js +1 -1
  76. package/dist/{tickled-controller-Bn3YdQsM.d.ts → tickled-controller-C1ZsHpyg.d.ts} +10 -1
  77. package/dist/tickled-controller-C1ZsHpyg.d.ts.map +1 -0
  78. package/dist/{tickled-controller-C66DgJHl.js → tickled-controller-Dh1Uh_vG.js} +4 -1
  79. package/dist/tickled-controller-Dh1Uh_vG.js.map +1 -0
  80. package/dist/{timeline-B2fgDL4U.js → timeline-THyvH8xy.js} +14 -8
  81. package/dist/timeline-THyvH8xy.js.map +1 -0
  82. package/dist/timeline.d.ts +1 -1
  83. package/dist/timeline.js +1 -1
  84. package/dist/{titlebar-YQ-jVQoS.d.ts → titlebar-Tqu-VvMU.d.ts} +5 -1
  85. package/dist/titlebar-Tqu-VvMU.d.ts.map +1 -0
  86. package/dist/titlebar.d.ts +1 -1
  87. package/dist/titlebar.js +5 -3
  88. package/dist/titlebar.js.map +1 -1
  89. package/dist/{tree-Chptss7I.js → tree-DTQon838.js} +4 -4
  90. package/dist/{tree-Chptss7I.js.map → tree-DTQon838.js.map} +1 -1
  91. package/dist/tree.d.ts +1 -1
  92. package/dist/tree.js +1 -1
  93. package/dist/vertical-list-CFuFDygi.d.ts.map +1 -1
  94. package/dist/vertical-list.js +68 -4
  95. package/dist/vertical-list.js.map +1 -1
  96. package/dist/{xy-axis-DzM1brBZ.js → xy-axis-CrrVdDFe.js} +28 -3
  97. package/dist/xy-axis-CrrVdDFe.js.map +1 -0
  98. package/dist/{xy-axis-BfP9hKLj.d.ts → xy-axis-DQqd2yXJ.d.ts} +28 -12
  99. package/dist/{xy-axis-BfP9hKLj.d.ts.map → xy-axis-DQqd2yXJ.d.ts.map} +1 -1
  100. package/dist/xy-pad.js +1 -1
  101. package/docs-user/data-grid.md +1 -1
  102. package/docs-user/detail-list.md +4 -0
  103. package/docs-user/dock.md +6 -3
  104. package/docs-user/grid-list.md +49 -0
  105. package/docs-user/index.json +2 -2
  106. package/docs-user/infra-tickled.md +2 -0
  107. package/docs-user/split-layout.md +2 -4
  108. package/docs-user/twosplit-layout.md +14 -16
  109. package/docs-user/vertical-list.md +4 -0
  110. package/package.json +1 -1
  111. package/dist/grid-list-nRUQOcEL.js.map +0 -1
  112. package/dist/grid-list-u33vmKzb.d.ts.map +0 -1
  113. package/dist/list-selection-controller-DTFI40li.js +0 -264
  114. package/dist/list-selection-controller-DTFI40li.js.map +0 -1
  115. package/dist/split-layout-BFSbUZQv.js.map +0 -1
  116. package/dist/tickled-controller-Bn3YdQsM.d.ts.map +0 -1
  117. package/dist/tickled-controller-C66DgJHl.js.map +0 -1
  118. package/dist/timeline-B2fgDL4U.js.map +0 -1
  119. package/dist/titlebar-YQ-jVQoS.d.ts.map +0 -1
  120. package/dist/xy-axis-DzM1brBZ.js.map +0 -1
@@ -1,17 +1,18 @@
1
- import { t as TickledController } from "./tickled-controller-C66DgJHl.js";
1
+ import { t as TickledController } from "./tickled-controller-Dh1Uh_vG.js";
2
2
  import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
3
- import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
3
+ import { t as themeFallbacks } from "./fallbacks-BcKGq-2F.js";
4
4
  import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
5
5
  import { c as createElementListSearchAdapter, d as createDataListSearchAdapter, i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES, u as keyForElement } from "./incr-search-C05WjbZh.js";
6
+ import { r as ListDragSelectController, t as ListSelectionController } from "./list-selection-controller-SA4piSBi.js";
6
7
  import { i as listItemFadeKeyframes } from "./list-item-animation-DuiwgN3l.js";
7
- import { t as ListSelectionController } from "./list-selection-controller-DTFI40li.js";
8
- import "./interaction-Cr13azbM.js";
8
+ import "./interaction-B9AAlsFO.js";
9
9
  import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
10
10
  import { LitElement, css, html } from "lit";
11
- import { property, state } from "lit/decorators.js";
11
+ import { property, query, state } from "lit/decorators.js";
12
12
  import { createCommandRegistry } from "@clinth/ui-commands";
13
13
  import { repeat } from "lit/directives/repeat.js";
14
14
  import { createRef, ref } from "lit/directives/ref.js";
15
+ import { classMap } from "lit/directives/class-map.js";
15
16
  //#region src/grid-list/grid-list-source.ts
16
17
  const ITEM_SELECTOR$1 = `:scope > *`;
17
18
  const SYNTHETIC_PREFIX = `gl-auto-`;
@@ -232,6 +233,233 @@ var DataGridListSource = class {
232
233
  }
233
234
  };
234
235
  //#endregion
236
+ //#region src/pan-text/pan-text.ts
237
+ const SPEED = 45;
238
+ const END_PADDING = 4;
239
+ const START_DELAY = 300;
240
+ const END_DWELL = 2e3;
241
+ const RESET_DURATION = 400;
242
+ const PAN_EASING = `cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
243
+ let PanTextElement = class PanTextElement extends LitElement {
244
+ constructor(..._args) {
245
+ super(..._args);
246
+ this.direction = `end`;
247
+ this.disabled = false;
248
+ this.mode = `hover`;
249
+ this._isOverflowing = false;
250
+ this._isHovering = false;
251
+ this._loopActive = false;
252
+ this._loopAtEnd = false;
253
+ this._overflow = 0;
254
+ this._resizeObserver = null;
255
+ this._loopTimer = 0;
256
+ this._onMouseEnter = () => {
257
+ this._isHovering = true;
258
+ };
259
+ this._onMouseLeave = () => {
260
+ this._isHovering = false;
261
+ };
262
+ this._measure = () => {
263
+ if (this._isHovering || this._loopAtEnd) return;
264
+ const el = this._textEl;
265
+ if (!el) return;
266
+ this._overflow = Math.max(0, el.scrollWidth - el.clientWidth);
267
+ const overflowing = this._overflow > 0;
268
+ this.classList.toggle(`is-overflowing`, overflowing);
269
+ if (overflowing !== this._isOverflowing) this._isOverflowing = overflowing;
270
+ if (this._loopActive && this.mode === `loop` && !this.disabled && overflowing && this._loopTimer === 0) this._scheduleLoopPan(START_DELAY);
271
+ if (!overflowing && this._loopAtEnd) {
272
+ this._loopAtEnd = false;
273
+ this.requestUpdate();
274
+ }
275
+ };
276
+ }
277
+ connectedCallback() {
278
+ super.connectedCallback();
279
+ this._resizeObserver = new ResizeObserver(() => this._measure());
280
+ this.addEventListener(`mouseenter`, this._onMouseEnter);
281
+ this.addEventListener(`mouseleave`, this._onMouseLeave);
282
+ }
283
+ disconnectedCallback() {
284
+ super.disconnectedCallback();
285
+ this._clearLoopTimer();
286
+ this._loopActive = false;
287
+ this._loopAtEnd = false;
288
+ this._resizeObserver?.disconnect();
289
+ this._resizeObserver = null;
290
+ this.removeEventListener(`mouseenter`, this._onMouseEnter);
291
+ this.removeEventListener(`mouseleave`, this._onMouseLeave);
292
+ }
293
+ firstUpdated() {
294
+ this._measure();
295
+ this._resizeObserver?.observe(this);
296
+ document.fonts?.ready.then(() => this._measure());
297
+ if (this.mode === `loop`) this.startLoop();
298
+ }
299
+ updated(changed) {
300
+ if (changed.has(`mode`)) {
301
+ if (this.mode === `loop`) this.startLoop();
302
+ else this.stopLoop();
303
+ }
304
+ if (changed.has(`disabled`) && this.disabled) this.stopLoop();
305
+ this._measure();
306
+ }
307
+ /**
308
+ * Whether a loop pan is currently active (between `startLoop()` and `stopLoop()`).
309
+ */
310
+ get looping() {
311
+ return this._loopActive;
312
+ }
313
+ /**
314
+ * Begin looping: pan to the end, dwell there for 2 seconds, reset to the
315
+ * start and pan again, repeating until `stopLoop()` is called. A no-op when
316
+ * `disabled`, when there is nothing to reveal, or under reduced-motion.
317
+ */
318
+ startLoop() {
319
+ if (this.disabled || this._prefersReducedMotion()) return;
320
+ this._loopActive = true;
321
+ this._loopAtEnd = false;
322
+ this.requestUpdate();
323
+ if (this._isOverflowing && this._loopTimer === 0) this._scheduleLoopPan(START_DELAY);
324
+ }
325
+ /**
326
+ * Stop a loop started with `startLoop()` and snap back to the rest position.
327
+ */
328
+ stopLoop() {
329
+ if (!this._loopActive && !this._loopAtEnd && this._loopTimer === 0) return;
330
+ this._clearLoopTimer();
331
+ this._loopActive = false;
332
+ this._loopAtEnd = false;
333
+ this.requestUpdate();
334
+ }
335
+ _prefersReducedMotion() {
336
+ return globalThis.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches ?? false;
337
+ }
338
+ _panDuration() {
339
+ return Math.max(1.5, (this._overflow + END_PADDING) / SPEED);
340
+ }
341
+ _clearLoopTimer() {
342
+ if (this._loopTimer !== 0) {
343
+ clearTimeout(this._loopTimer);
344
+ this._loopTimer = 0;
345
+ }
346
+ }
347
+ _scheduleLoopPan(delay) {
348
+ this._clearLoopTimer();
349
+ this._loopTimer = window.setTimeout(() => {
350
+ this._loopTimer = 0;
351
+ if (!this._loopActive || this.disabled || !this._isOverflowing) return;
352
+ this._loopAtEnd = true;
353
+ this.requestUpdate();
354
+ const dwell = this._panDuration() * 1e3 + END_DWELL;
355
+ this._loopTimer = window.setTimeout(() => {
356
+ this._loopTimer = 0;
357
+ if (!this._loopActive) return;
358
+ this._loopAtEnd = false;
359
+ this.requestUpdate();
360
+ this._scheduleLoopPan(700);
361
+ }, dwell);
362
+ }, delay);
363
+ }
364
+ _textStyle() {
365
+ if (!this._isOverflowing) return ``;
366
+ const distance = this._overflow + END_PADDING;
367
+ const duration = Math.max(1.5, distance / SPEED);
368
+ const revealEnd = this.direction === `end`;
369
+ const restX = revealEnd ? 0 : -distance;
370
+ const hoverX = revealEnd ? -distance : 0;
371
+ if (this._loopActive && this.mode === `loop`) {
372
+ if (this._loopAtEnd) return [
373
+ `transform: translateX(${hoverX}px)`,
374
+ `transition: transform ${duration}s ${PAN_EASING}`,
375
+ `overflow: visible`,
376
+ `text-overflow: clip`
377
+ ].join(`; `);
378
+ return [
379
+ `transform: translateX(${restX}px)`,
380
+ `transition: transform ${RESET_DURATION}ms ${PAN_EASING}`,
381
+ revealEnd ? `` : `overflow: visible; text-overflow: clip`
382
+ ].filter(Boolean).join(`; `);
383
+ }
384
+ if (this._isHovering) return [
385
+ `transform: translateX(${hoverX}px)`,
386
+ `transition: transform ${duration}s ${PAN_EASING} ${START_DELAY}ms`,
387
+ `overflow: visible`,
388
+ `text-overflow: clip`
389
+ ].join(`; `);
390
+ return [
391
+ `transform: translateX(${restX}px)`,
392
+ `transition: transform 400ms ${PAN_EASING}`,
393
+ revealEnd ? `` : `overflow: visible; text-overflow: clip`
394
+ ].filter(Boolean).join(`; `);
395
+ }
396
+ render() {
397
+ const textClasses = {
398
+ "is-overflowing": this._isOverflowing,
399
+ "is-hovering": this._isHovering
400
+ };
401
+ return html`
402
+ <span class="text ${classMap(textClasses)}" part="text" style=${this._textStyle()}>
403
+ <slot @slotchange=${this._measure}></slot>
404
+ </span>
405
+ `;
406
+ }
407
+ static {
408
+ this.styles = [themeFallbacks, css`
409
+ /* Renders like plain inline text: no colours, border, padding or margin
410
+ of its own — it only adds the clipping needed to pan on hover. */
411
+ :host {
412
+ display: inline-block;
413
+ overflow: hidden;
414
+ white-space: nowrap;
415
+ max-width: 100%;
416
+ box-sizing: border-box;
417
+ font: inherit;
418
+ color: inherit;
419
+ }
420
+
421
+ :host([disabled]) {
422
+ opacity: var(--disabled-opacity, 0.5);
423
+ pointer-events: none;
424
+ }
425
+
426
+ .text {
427
+ display: block;
428
+ overflow: hidden;
429
+ text-overflow: ellipsis;
430
+ white-space: nowrap;
431
+ will-change: transform;
432
+ }
433
+
434
+ @media (prefers-reduced-motion: reduce) {
435
+ .text {
436
+ transition: none !important;
437
+ transform: none !important;
438
+ }
439
+ }
440
+ `];
441
+ }
442
+ };
443
+ __decorate([property({
444
+ type: String,
445
+ attribute: `direction`
446
+ })], PanTextElement.prototype, "direction", void 0);
447
+ __decorate([property({
448
+ type: Boolean,
449
+ attribute: `disabled`
450
+ })], PanTextElement.prototype, "disabled", void 0);
451
+ __decorate([property({
452
+ type: String,
453
+ attribute: `mode`,
454
+ reflect: true
455
+ })], PanTextElement.prototype, "mode", void 0);
456
+ __decorate([state()], PanTextElement.prototype, "_isOverflowing", void 0);
457
+ __decorate([state()], PanTextElement.prototype, "_isHovering", void 0);
458
+ __decorate([state()], PanTextElement.prototype, "_loopActive", void 0);
459
+ __decorate([state()], PanTextElement.prototype, "_loopAtEnd", void 0);
460
+ __decorate([query(`.text`)], PanTextElement.prototype, "_textEl", void 0);
461
+ PanTextElement = __decorate([safeCustomElement(`ixfx-pan-text`)], PanTextElement);
462
+ //#endregion
235
463
  //#region src/grid-list/grid-list.ts
236
464
  const ITEM_SELECTOR = `:scope > *`;
237
465
  const MIN_ITEM_SIZE = 16;
@@ -317,6 +545,7 @@ let GridListElement = class GridListElement extends LitElement {
317
545
  super(..._args);
318
546
  this.selectionMode = `single`;
319
547
  this.interactionMode = `standard`;
548
+ this.disableHoverTickle = false;
320
549
  this.itemSize = 160;
321
550
  this.gap = 8;
322
551
  this.orientation = `grid`;
@@ -326,6 +555,7 @@ let GridListElement = class GridListElement extends LitElement {
326
555
  this.captionBackdrop = `scrim`;
327
556
  this.captionHeight = 24;
328
557
  this.itemWrap = `stop`;
558
+ this.itemsDraggable = false;
329
559
  this.items = [];
330
560
  this._scrollTop = 0;
331
561
  this._scrollLeft = 0;
@@ -362,10 +592,56 @@ let GridListElement = class GridListElement extends LitElement {
362
592
  enabled: this.#source.isSelectable(i)
363
593
  })),
364
594
  onTickle: (k) => {
595
+ this.#stopActiveCaptionPan();
365
596
  if (k !== void 0) this.#emitItemEvent(`list-tickle`, k);
366
597
  },
367
598
  onStateChange: () => this._syncItemStates(),
368
- itemWrap: this.#itemWrap
599
+ itemWrap: this.#itemWrap,
600
+ hoverDisabled: () => this.disableHoverTickle || this.#dragSelect.isDragging
601
+ });
602
+ this.#marqueePreview = /* @__PURE__ */ new Set();
603
+ this.#nativeDragging = false;
604
+ this.#dragOriginKey = void 0;
605
+ this.#postDragSuppressTimer = 0;
606
+ this.#activeCaptionPan = null;
607
+ this.#dragSelect = new ListDragSelectController(this, {
608
+ getSurface: () => this.#viewportRef.value,
609
+ isEnabled: () => this.selection.canDragSelect() && this.#source.count > 0 && !this.hasAttribute(`disable-drag-select`),
610
+ hitTest: (rect) => this.#marqueeHitTest(rect),
611
+ getBaseSelection: () => this.selection.selected,
612
+ onPreview: (preview, rect) => {
613
+ this.#marqueePreview = preview;
614
+ this.#marqueeRect = rect;
615
+ this.requestUpdate();
616
+ },
617
+ onCommit: (finalSet) => {
618
+ this.selection.applyDragSelection(finalSet);
619
+ this.#marqueePreview = /* @__PURE__ */ new Set();
620
+ this.#marqueeRect = void 0;
621
+ this.requestUpdate();
622
+ },
623
+ autoScroll: (dx, dy) => {
624
+ const vp = this.#viewportRef.value;
625
+ if (!vp) return;
626
+ if (dx !== 0) {
627
+ vp.scrollLeft += dx;
628
+ this._scrollLeft = vp.scrollLeft;
629
+ }
630
+ if (dy !== 0) {
631
+ vp.scrollTop += dy;
632
+ this._scrollTop = vp.scrollTop;
633
+ }
634
+ },
635
+ shouldStart: (event) => {
636
+ if (!this.itemsDraggable) return true;
637
+ const cell = event.composedPath()[0]?.closest?.(`.cell`);
638
+ if (!cell) return true;
639
+ const key = cell.dataset.key;
640
+ if (!key) return true;
641
+ const index = this.#source.indexOfKey(key);
642
+ if (index < 0) return true;
643
+ return !this.#source.isSelectable(index);
644
+ }
369
645
  });
370
646
  this.#selectedSnapshot = /* @__PURE__ */ new Set();
371
647
  this.#onScroll = (e) => {
@@ -378,6 +654,32 @@ let GridListElement = class GridListElement extends LitElement {
378
654
  this.#emitViewportChange();
379
655
  });
380
656
  };
657
+ this.#onDragStart = (e, key) => {
658
+ if (!this.itemsDraggable) return;
659
+ const index = this.#source.indexOfKey(key);
660
+ if (index < 0 || !this.#source.isSelectable(index)) return;
661
+ if (!this.selection.selectedKeys.has(key)) this.selection.replace(key);
662
+ const selectedKeys = this.#source.keys().filter((k) => !isSyntheticKey(k)).filter((k) => this.selection.selectedKeys.has(k));
663
+ const detail = {
664
+ key,
665
+ index: this.#source.indexOfKey(key),
666
+ item: this.#source.resolveKeyToData(key) ?? this.#source.resolveKeyToElement(key),
667
+ selectedKeys
668
+ };
669
+ if (!this._dispatch(`grid-list-native-drag-start`, detail, true)) {
670
+ e.preventDefault();
671
+ return;
672
+ }
673
+ this.#nativeDragging = true;
674
+ this.#dragOriginKey = key;
675
+ this.toggleAttribute(`data-native-dragging`, true);
676
+ const count = selectedKeys.length;
677
+ this.setAttribute(`aria-label`, count === 1 ? `Dragging 1 item` : `Dragging ${count} items`);
678
+ };
679
+ this.#onDragEnd = () => {
680
+ this.#clearNativeDragState();
681
+ this.#startPostDragSuppressTimer();
682
+ };
381
683
  this._onKeyDown = (e) => {
382
684
  if ((e.ctrlKey || e.metaKey) && e.key === `f`) return;
383
685
  if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
@@ -447,6 +749,7 @@ let GridListElement = class GridListElement extends LitElement {
447
749
  break;
448
750
  }
449
751
  case `Escape`:
752
+ if (this.#nativeDragging) this.#clearNativeDragState();
450
753
  this.selection.clear();
451
754
  break;
452
755
  default: return;
@@ -514,7 +817,18 @@ let GridListElement = class GridListElement extends LitElement {
514
817
  border-radius: var(--grid-list-radius);
515
818
  }
516
819
 
517
- /* The square that holds the slotted content. */
820
+ /* The square that holds the slotted content — the "image well". Its
821
+ background is a set of consumer-settable variables so a Finder-style
822
+ look is possible: on selection the caption takes the accent colouring
823
+ while the well takes a tint of the surface colour. Each variable is
824
+ unset by default, so the fallback (today's look) applies until a
825
+ consumer opts in.
826
+ --grid-list-thumb-bg idle well background
827
+ --grid-list-thumb-bg-tickled under the cursor
828
+ --grid-list-thumb-bg-selected when the cell is selected
829
+ --grid-list-thumb-bg-selected-tickled selected + under the cursor
830
+ --grid-list-thumb-selection-wash colour of the box-mode wash (default: --accent)
831
+ --grid-list-thumb-selection-wash-opacity strength of that wash (default: 0.18; set 0 to drop it) */
518
832
  .thumb {
519
833
  position: relative;
520
834
  flex: 1;
@@ -524,7 +838,7 @@ let GridListElement = class GridListElement extends LitElement {
524
838
  box-sizing: border-box;
525
839
  overflow: hidden;
526
840
  border-radius: 0;
527
- background: transparent;
841
+ background: var(--grid-list-thumb-bg, transparent);
528
842
  border: 1px solid transparent;
529
843
  transition: background var(--transition), border-color var(--transition);
530
844
  }
@@ -537,6 +851,7 @@ let GridListElement = class GridListElement extends LitElement {
537
851
  align-items: center;
538
852
  justify-content: center;
539
853
  overflow: hidden;
854
+ draggable: false;
540
855
  }
541
856
  .thumb ::slotted(*:empty)::after {
542
857
  content: "Empty";
@@ -548,19 +863,27 @@ let GridListElement = class GridListElement extends LitElement {
548
863
  }
549
864
 
550
865
  /* Selection / cursor wash — sits over the slotted content so it reads
551
- on photos and arbitrary DOM alike. */
866
+ on photos and arbitrary DOM alike. Set
867
+ --grid-list-thumb-selection-wash-opacity: 0 to drop it in favour of a
868
+ --grid-list-thumb-bg-selected tint. */
552
869
  .thumb::after {
553
870
  content: "";
554
871
  position: absolute;
555
872
  inset: 0;
556
873
  pointer-events: none;
557
874
  border-radius: inherit;
558
- background: var(--accent);
875
+ background: var(--grid-list-thumb-selection-wash, var(--accent));
559
876
  opacity: 0;
560
877
  transition: opacity var(--transition, 0.1s ease-out);
561
878
  }
562
879
  .cell.selected .thumb::after {
563
- opacity: 0.18;
880
+ opacity: var(--grid-list-thumb-selection-wash-opacity, 0.18);
881
+ }
882
+ :host(:not([caption-style="separate"])) .cell.selected .thumb {
883
+ background: var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, transparent));
884
+ }
885
+ :host(:not([caption-style="separate"])) .cell.selected.tickled .thumb {
886
+ background: var(--grid-list-thumb-bg-selected-tickled, var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, transparent)));
564
887
  }
565
888
 
566
889
  /* ---- Caption (base) ---- */
@@ -578,6 +901,18 @@ let GridListElement = class GridListElement extends LitElement {
578
901
  }
579
902
  :host([caption-align="left"]) .caption { text-align: left; }
580
903
  :host([caption-align="right"]) .caption { text-align: right; }
904
+ /* Tickled captions swap their plain label for an ixfx-pan-text in loop
905
+ mode so a truncated label scrolls instead of staying an ellipsis. The
906
+ pan fills the caption band (flex in 'separate', block otherwise) and
907
+ inherits its alignment. */
908
+ .caption ixfx-pan-text {
909
+ display: block;
910
+ flex: 1;
911
+ min-width: 0;
912
+ max-width: 100%;
913
+ font: inherit;
914
+ color: inherit;
915
+ }
581
916
 
582
917
  /* ---- Box modes (hidden / under / overlaid): the cell is one box with a
583
918
  single selection outline ---- */
@@ -587,8 +922,8 @@ let GridListElement = class GridListElement extends LitElement {
587
922
  overflow: hidden;
588
923
  transition: background var(--transition), border-color var(--transition);
589
924
  }
590
- :host(:not([caption-style="separate"])) .cell:hover,
591
- :host(:not([caption-style="separate"])) .cell.tickled {
925
+ :host(:not([caption-style="separate"])) .cell.tickled,
926
+ :host(:not([caption-style="separate"]):not([disable-hover-tickle])) .cell:hover {
592
927
  background: var(--item-bg-tickled);
593
928
  border: var(--item-border-tickled);
594
929
  }
@@ -655,12 +990,12 @@ let GridListElement = class GridListElement extends LitElement {
655
990
  :host([caption-style="separate"]) .thumb {
656
991
  flex: none;
657
992
  border-radius: var(--grid-list-radius);
658
- background: var(--grid-list-item-bg);
993
+ background: var(--grid-list-thumb-bg, var(--grid-list-item-bg));
659
994
  border: 1px solid transparent;
660
995
  }
661
- :host([caption-style="separate"]) .cell:hover .thumb,
662
- :host([caption-style="separate"]) .cell.tickled .thumb {
663
- background: var(--item-bg-tickled);
996
+ :host([caption-style="separate"]) .cell.tickled .thumb,
997
+ :host([caption-style="separate"]:not([disable-hover-tickle])) .cell:hover .thumb {
998
+ background: var(--grid-list-thumb-bg-tickled, var(--item-bg-tickled));
664
999
  border: var(--item-border-tickled);
665
1000
  }
666
1001
  :host([caption-style="separate"]) .caption {
@@ -673,14 +1008,17 @@ let GridListElement = class GridListElement extends LitElement {
673
1008
  }
674
1009
  :host([caption-style="separate"][caption-align="left"]) .caption { align-self: flex-start; }
675
1010
  :host([caption-style="separate"][caption-align="right"]) .caption { align-self: flex-end; }
1011
+ /* Inset the selection ring (negative offset) so it is never clipped by the
1012
+ scroll viewport's overflow when the cell sits against an edge. */
676
1013
  :host([caption-style="separate"]) .cell.selected .thumb {
1014
+ background: var(--grid-list-thumb-bg-selected, var(--grid-list-thumb-bg, var(--grid-list-item-bg)));
677
1015
  outline: 2px solid var(--accent);
678
- outline-offset: 2px;
1016
+ outline-offset: -2px;
679
1017
  }
680
1018
  :host([caption-style="separate"]) .cell.selected.tickled .thumb {
681
- background: var(--item-bg-selected-tickled);
1019
+ background: var(--grid-list-thumb-bg-selected-tickled, var(--item-bg-selected-tickled));
682
1020
  outline: 2px dashed var(--surface-3);
683
- outline-offset: 2px;
1021
+ outline-offset: -2px;
684
1022
  }
685
1023
  :host([caption-style="separate"]) .cell.selected .caption {
686
1024
  background: var(--accent);
@@ -718,6 +1056,26 @@ let GridListElement = class GridListElement extends LitElement {
718
1056
  :host([reflowing]) .cell { transition: background var(--transition), border-color var(--transition); }
719
1057
  .cell.entering { animation: none; }
720
1058
  }
1059
+
1060
+ /* Drag-select (marquee) rectangle + covered-cell preview. The preview
1061
+ reuses the selection wash so covered cells read as "about to be
1062
+ selected". Style hooks: part="marquee", --grid-list-marquee-border,
1063
+ --grid-list-marquee-fill. */
1064
+ .marquee {
1065
+ position: absolute;
1066
+ pointer-events: none;
1067
+ z-index: 5;
1068
+ border: 1px solid var(--grid-list-marquee-border, var(--accent));
1069
+ background: var(--grid-list-marquee-fill, color-mix(in srgb, var(--accent) 15%, transparent));
1070
+ }
1071
+ .cell.marquee .thumb::after {
1072
+ opacity: var(--grid-list-thumb-selection-wash-opacity, 0.18);
1073
+ }
1074
+
1075
+ /* Native item drag origin cell. */
1076
+ .cell.dragging-origin {
1077
+ opacity: 0.6;
1078
+ }
721
1079
  `
722
1080
  ];
723
1081
  }
@@ -750,6 +1108,62 @@ let GridListElement = class GridListElement extends LitElement {
750
1108
  */
751
1109
  #sourceElement;
752
1110
  #source;
1111
+ /** Live drag-select preview: covered keys + the surface-relative rect. */
1112
+ #marqueePreview;
1113
+ #marqueeRect;
1114
+ /** Native item drag state: true while a drag is active. */
1115
+ #nativeDragging;
1116
+ /** Key of the cell that originated the native drag. */
1117
+ #dragOriginKey;
1118
+ /** Timer handle for post-drag click suppression. */
1119
+ #postDragSuppressTimer;
1120
+ /**
1121
+ * The single caption pan-text currently scrolling. At most one caption is
1122
+ * a pan-text in loop mode at a time — when tickle moves, the old one is
1123
+ * stopped before the new one starts (see `#activateTickledPan`).
1124
+ */
1125
+ #activeCaptionPan;
1126
+ #dragSelect;
1127
+ /**
1128
+ * Map a surface-relative (viewport-visible) rect to covered item keys using
1129
+ * the same `computeGridWindow` geometry the renderer uses — pure arithmetic,
1130
+ * so virtualized off-screen items are covered without DOM measurement.
1131
+ */
1132
+ #marqueeHitTest(rect) {
1133
+ const count = this.#source.count;
1134
+ if (count === 0) return [];
1135
+ const horizontal = this.orientation === `row`;
1136
+ const win = computeGridWindow(count, this.itemSize, this.#rowHeight(), this.gap, Math.max(1, this._viewportWidth), Math.max(1, this._viewportHeight), horizontal ? this._scrollLeft : this._scrollTop, this.orientation, 0);
1137
+ const out = [];
1138
+ if (horizontal) {
1139
+ const x0 = rect.x + this._scrollLeft;
1140
+ const x1 = x0 + rect.width;
1141
+ const lo = Math.max(0, Math.floor(x0 / win.colStride));
1142
+ const hi = Math.min(count - 1, Math.floor(x1 / win.colStride));
1143
+ for (let i = lo; i <= hi; i++) out.push(i);
1144
+ } else {
1145
+ const x0 = rect.x + this._scrollLeft;
1146
+ const x1 = x0 + rect.width;
1147
+ const y0 = rect.y + this._scrollTop;
1148
+ const y1 = y0 + rect.height;
1149
+ const colLo = Math.max(0, Math.floor(x0 / win.colStride));
1150
+ const colHi = Math.min(win.cols - 1, Math.floor(x1 / win.colStride));
1151
+ const rowLo = Math.max(0, Math.floor(y0 / win.rowStride));
1152
+ const rowHi = Math.floor(y1 / win.rowStride);
1153
+ for (let row = rowLo; row <= rowHi; row++) for (let col = colLo; col <= colHi; col++) {
1154
+ const index = row * win.cols + col;
1155
+ if (index >= 0 && index < count) out.push(index);
1156
+ }
1157
+ }
1158
+ const keys = [];
1159
+ for (const index of out) {
1160
+ const key = this.#source.keyAt(index);
1161
+ if (!key || isSyntheticKey(key)) continue;
1162
+ if (!this.#source.isSelectable(index)) continue;
1163
+ keys.push(key);
1164
+ }
1165
+ return keys;
1166
+ }
753
1167
  #searchMode;
754
1168
  #searchFilterKeys;
755
1169
  /** (Re)build {@link search} for the current mode; a no-op if it already matches. */
@@ -822,6 +1236,7 @@ let GridListElement = class GridListElement extends LitElement {
822
1236
  }
823
1237
  disconnectedCallback() {
824
1238
  super.disconnectedCallback();
1239
+ this.#stopActiveCaptionPan();
825
1240
  this.removeEventListener(`keydown`, this._onKeyDown);
826
1241
  this.#resizeObserver?.disconnect();
827
1242
  this.#resizeObserver = void 0;
@@ -834,6 +1249,7 @@ let GridListElement = class GridListElement extends LitElement {
834
1249
  clearTimeout(this.#reflowTimer);
835
1250
  this.#reflowTimer = 0;
836
1251
  }
1252
+ if (this.#nativeDragging) this.#clearNativeDragState();
837
1253
  }
838
1254
  #onChildrenChanged(records) {
839
1255
  const previousLength = this.#items.length;
@@ -1021,6 +1437,7 @@ let GridListElement = class GridListElement extends LitElement {
1021
1437
  if (Math.abs(vp.scrollLeft - this._scrollLeft) > .5) vp.scrollLeft = this._scrollLeft;
1022
1438
  }
1023
1439
  this.#applyZoomAnchor();
1440
+ this.#activateTickledPan();
1024
1441
  }
1025
1442
  /** Re-anchor the scroll so the item captured in `willUpdate` stays centred. */
1026
1443
  #applyZoomAnchor() {
@@ -1351,15 +1768,19 @@ let GridListElement = class GridListElement extends LitElement {
1351
1768
  element
1352
1769
  });
1353
1770
  }
1354
- _dispatch(name, detail) {
1355
- this.dispatchEvent(new CustomEvent(name, {
1771
+ _dispatch(name, detail, cancelable = false) {
1772
+ return this.dispatchEvent(new CustomEvent(name, {
1356
1773
  detail,
1357
1774
  bubbles: true,
1358
- composed: true
1775
+ composed: true,
1776
+ cancelable
1359
1777
  }));
1360
1778
  }
1361
1779
  /** Selected keys, snapshotted once per render so the cell renderers don't each rebuild the set. */
1362
1780
  #selectedSnapshot;
1781
+ #marqueeStyle(rect) {
1782
+ return `left:${rect.x}px;top:${rect.y}px;width:${Math.max(0, rect.width)}px;height:${Math.max(0, rect.height)}px`;
1783
+ }
1363
1784
  render() {
1364
1785
  const win = this.#currentWindow();
1365
1786
  this.#selectedSnapshot = this.selection.selectedKeys;
@@ -1376,6 +1797,7 @@ let GridListElement = class GridListElement extends LitElement {
1376
1797
  <div class="spacer" style=${spacerStyle}>
1377
1798
  ${this.#source instanceof DataGridListSource ? this.#renderDataCells(win) : this.#renderElementCells(win)}
1378
1799
  </div>
1800
+ ${this.#marqueeRect ? html`<div class="marquee" part="marquee" style=${this.#marqueeStyle(this.#marqueeRect)}></div>` : ``}
1379
1801
  </div>
1380
1802
  ${this.search?.renderField()}
1381
1803
  `;
@@ -1387,7 +1809,10 @@ let GridListElement = class GridListElement extends LitElement {
1387
1809
  #renderDataCell(index, win) {
1388
1810
  const key = this.#source.keyAt(index);
1389
1811
  const selected = this.#selectedSnapshot.has(key);
1812
+ const marquee = this.#marqueePreview.has(key);
1390
1813
  const tickled = this.tickled.isTickled(key);
1814
+ const selectable = this.#source.isSelectable(index);
1815
+ const draggable = this.itemsDraggable && selectable;
1391
1816
  const style = this.captionStyle;
1392
1817
  const separate = style === `separate`;
1393
1818
  const caption = style === `hidden` ? `` : this.#source.labelAt(index);
@@ -1398,24 +1823,69 @@ let GridListElement = class GridListElement extends LitElement {
1398
1823
  const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
1399
1824
  return html`
1400
1825
  <div
1401
- class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
1826
+ class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${marquee ? ` marquee` : ``}${this.#nativeDragging && key === this.#dragOriginKey ? ` dragging-origin` : ``}"
1402
1827
  part="item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
1403
1828
  role="option"
1404
1829
  aria-selected=${selected ? `true` : `false`}
1405
1830
  style="width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)"
1406
1831
  data-index=${index}
1407
1832
  data-key=${key}
1833
+ draggable=${draggable ? `true` : `false`}
1408
1834
  ?data-selected=${selected}
1409
1835
  ?data-tickled=${tickled}
1836
+ ?data-marquee=${marquee}
1410
1837
  @click=${(e) => this.#onCellClick(e, key)}
1411
1838
  @dblclick=${() => this.#onCellActivate(key)}
1412
1839
  @pointerenter=${() => this.tickled.handlePointerEnter(key)}
1840
+ @dragstart=${(e) => this.#onDragStart(e, key)}
1841
+ @dragend=${() => this.#onDragEnd()}
1413
1842
  >
1414
1843
  <div class="content" part="content">${this.#source.renderCellContent(index)}</div>
1415
- ${showCaption ? html`<div class="caption" part="caption" style=${captionCss} title=${caption}>${caption}</div>` : ``}
1844
+ ${showCaption ? this.#renderCaption(caption, captionCss, tickled) : ``}
1416
1845
  </div>
1417
1846
  `;
1418
1847
  }
1848
+ /**
1849
+ * A tickled cell swaps its plain label for an `ixfx-pan-text` in loop mode
1850
+ * so a truncated label scrolls instead of staying an ellipsis. Unticked
1851
+ * cells keep the cheap plain-text caption.
1852
+ */
1853
+ #renderCaption(caption, captionCss, tickled) {
1854
+ if (tickled) return html`<div class="caption" part="caption" style=${captionCss} title=${caption}><ixfx-pan-text mode="loop">${caption}</ixfx-pan-text></div>`;
1855
+ return html`<div class="caption" part="caption" style=${captionCss} title=${caption}>${caption}</div>`;
1856
+ }
1857
+ /** Stop the active caption pan, if any, and forget it. */
1858
+ #stopActiveCaptionPan() {
1859
+ const active = this.#activeCaptionPan;
1860
+ this.#activeCaptionPan = null;
1861
+ try {
1862
+ active?.stopLoop?.();
1863
+ } catch {}
1864
+ }
1865
+ /**
1866
+ * Enforce a single scrolling caption: the tickled cell's pan-text loops,
1867
+ * every other pan-text is stopped. The pan-text also auto-starts when
1868
+ * `mode="loop"` is set; this covers the case where the element was already
1869
+ * connected before overflow was measured.
1870
+ */
1871
+ #activateTickledPan() {
1872
+ const tickledCell = this.renderRoot.querySelector(`.cell[data-tickled]`);
1873
+ const pans = [...this.renderRoot.querySelectorAll(`ixfx-pan-text[mode="loop"]`)];
1874
+ const current = tickledCell?.querySelector(`ixfx-pan-text[mode="loop"]`) ?? null;
1875
+ for (const pan of pans) if (pan !== current) try {
1876
+ pan.stopLoop?.();
1877
+ } catch {}
1878
+ const active = this.#activeCaptionPan;
1879
+ if (active && active !== current) try {
1880
+ active.stopLoop?.();
1881
+ } catch {}
1882
+ if (!current) {
1883
+ this.#activeCaptionPan = null;
1884
+ return;
1885
+ }
1886
+ this.#activeCaptionPan = current;
1887
+ current.startLoop?.();
1888
+ }
1419
1889
  #renderElementCells(win) {
1420
1890
  const slice = this.#items.slice(win.startIndex, win.endIndex);
1421
1891
  return repeat(slice, (_el, i) => this.#source.keyAt(win.startIndex + i), (el, i) => this.#renderCell(el, win.startIndex + i, win));
@@ -1423,8 +1893,11 @@ let GridListElement = class GridListElement extends LitElement {
1423
1893
  #renderCell(el, index, win) {
1424
1894
  const key = this.#source.keyAt(index);
1425
1895
  const selected = this.#selectedSnapshot.has(key);
1896
+ const marquee = this.#marqueePreview.has(key);
1426
1897
  const tickled = this.tickled.isTickled(key);
1427
1898
  const entering = this.#entering.has(el);
1899
+ const selectable = this.#source.isSelectable(index);
1900
+ const draggable = this.itemsDraggable && selectable;
1428
1901
  const style = this.captionStyle;
1429
1902
  const separate = style === `separate`;
1430
1903
  const caption = style === `hidden` ? `` : this.#captionFor(el);
@@ -1436,23 +1909,27 @@ let GridListElement = class GridListElement extends LitElement {
1436
1909
  const captionCss = separate ? `height:${Math.max(0, this.captionHeight - 4)}px` : style === `under` ? `height:${this.captionHeight}px` : ``;
1437
1910
  return html`
1438
1911
  <div
1439
- class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${entering ? ` entering` : ``}"
1912
+ class="cell${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}${entering ? ` entering` : ``}${marquee ? ` marquee` : ``}${this.#nativeDragging && key === this.#dragOriginKey ? ` dragging-origin` : ``}"
1440
1913
  part="item${selected ? ` selected` : ``}${tickled ? ` tickled` : ``}"
1441
1914
  role="option"
1442
1915
  aria-selected=${selected ? `true` : `false`}
1443
1916
  style="width:${this.itemSize}px;height:${cellHeight}px;transform:translate(${x}px,${y}px)"
1444
1917
  data-index=${index}
1445
1918
  data-key=${key}
1919
+ draggable=${draggable ? `true` : `false`}
1446
1920
  ?data-selected=${selected}
1447
1921
  ?data-tickled=${tickled}
1922
+ ?data-marquee=${marquee}
1448
1923
  @click=${(e) => this.#onCellClick(e, key)}
1449
1924
  @dblclick=${() => this.#onCellActivate(key)}
1450
1925
  @pointerenter=${() => this.tickled.handlePointerEnter(key)}
1926
+ @dragstart=${(e) => this.#onDragStart(e, key)}
1927
+ @dragend=${() => this.#onDragEnd()}
1451
1928
  >
1452
1929
  <div class="thumb" part="thumb" style=${thumbStyle}>
1453
1930
  ${this.#source.renderCellContent(index)}
1454
1931
  </div>
1455
- ${showCaption ? html`<div class="caption" part="caption" style=${captionCss} title=${caption}>${caption}</div>` : ``}
1932
+ ${showCaption ? this.#renderCaption(caption, captionCss, tickled) : ``}
1456
1933
  </div>
1457
1934
  `;
1458
1935
  }
@@ -1464,9 +1941,24 @@ let GridListElement = class GridListElement extends LitElement {
1464
1941
  }
1465
1942
  }
1466
1943
  #onCellClick(e, key) {
1944
+ if (this.#dragSelect.isDragging || this.#nativeDragging || this.#postDragSuppressTimer !== 0) return;
1467
1945
  this.selection.handleClick(key, e);
1468
1946
  this.#emitItemEvent(`list-item-click`, key);
1469
1947
  }
1948
+ #onDragStart;
1949
+ #onDragEnd;
1950
+ #startPostDragSuppressTimer() {
1951
+ if (this.#postDragSuppressTimer) clearTimeout(this.#postDragSuppressTimer);
1952
+ this.#postDragSuppressTimer = window.setTimeout(() => {
1953
+ this.#postDragSuppressTimer = 0;
1954
+ }, 0);
1955
+ }
1956
+ #clearNativeDragState() {
1957
+ this.#nativeDragging = false;
1958
+ this.#dragOriginKey = void 0;
1959
+ this.toggleAttribute(`data-native-dragging`, false);
1960
+ this.removeAttribute(`aria-label`);
1961
+ }
1470
1962
  #onCellActivate(key) {
1471
1963
  if (this.interactionMode !== `manual`) this.selection.replace(key);
1472
1964
  this.#emitItemEvent(`list-activate`, key);
@@ -1480,6 +1972,11 @@ __decorate([property({
1480
1972
  type: String,
1481
1973
  attribute: `interaction-mode`
1482
1974
  })], GridListElement.prototype, "interactionMode", void 0);
1975
+ __decorate([property({
1976
+ type: Boolean,
1977
+ attribute: `disable-hover-tickle`,
1978
+ reflect: true
1979
+ })], GridListElement.prototype, "disableHoverTickle", void 0);
1483
1980
  __decorate([property({
1484
1981
  type: Number,
1485
1982
  attribute: `item-size`
@@ -1519,6 +2016,11 @@ __decorate([property({
1519
2016
  })], GridListElement.prototype, "itemWrap", void 0);
1520
2017
  __decorate([property({ attribute: false })], GridListElement.prototype, "filterPredicate", void 0);
1521
2018
  __decorate([property({ attribute: false })], GridListElement.prototype, "itemFilterPredicate", void 0);
2019
+ __decorate([property({
2020
+ type: Boolean,
2021
+ attribute: `items-draggable`,
2022
+ reflect: true
2023
+ })], GridListElement.prototype, "itemsDraggable", void 0);
1522
2024
  __decorate([property({
1523
2025
  type: String,
1524
2026
  attribute: `search-modes`,
@@ -1532,6 +2034,6 @@ __decorate([state()], GridListElement.prototype, "_viewportHeight", void 0);
1532
2034
  __decorate([state()], GridListElement.prototype, "_viewportWidth", void 0);
1533
2035
  GridListElement = __decorate([safeCustomElement(`ixfx-grid-list`)], GridListElement);
1534
2036
  //#endregion
1535
- export { GridListElement as t };
2037
+ export { PanTextElement as n, GridListElement as t };
1536
2038
 
1537
- //# sourceMappingURL=grid-list-nRUQOcEL.js.map
2039
+ //# sourceMappingURL=grid-list-BXfY3XXX.js.map