flipfolio 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -1,34 +1,7 @@
1
- "use strict";
2
1
  var __defProp = Object.defineProperty;
3
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __spreadValues = (a, b) => {
10
- for (var prop in b || (b = {}))
11
- if (__hasOwnProp.call(b, prop))
12
- __defNormalProp(a, prop, b[prop]);
13
- if (__getOwnPropSymbols)
14
- for (var prop of __getOwnPropSymbols(b)) {
15
- if (__propIsEnum.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- }
18
- return a;
19
- };
20
- var __objRest = (source, exclude) => {
21
- var target = {};
22
- for (var prop in source)
23
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
- target[prop] = source[prop];
25
- if (source != null && __getOwnPropSymbols)
26
- for (var prop of __getOwnPropSymbols(source)) {
27
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
- target[prop] = source[prop];
29
- }
30
- return target;
31
- };
32
5
  var __export = (target, all) => {
33
6
  for (var name in all)
34
7
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -65,19 +38,27 @@ var DEFAULTS = {
65
38
  // 'auto' | 'off' | 'force'
66
39
  peek: "hover",
67
40
  // 'hover' | 'always' | 'off'
41
+ drag: "fling",
42
+ // 'fling' | 'off' - grab the active folder and throw it (stack mode)
68
43
  defaultActiveIndex: 0,
69
44
  label: "Folder gallery"
70
45
  };
71
46
  var PEEK_MODES = /* @__PURE__ */ new Set(["hover", "always", "off"]);
47
+ var DRAG_MODES = /* @__PURE__ */ new Set(["fling", "off"]);
48
+ var INSTANCES = /* @__PURE__ */ new WeakMap();
72
49
  var MODE_WIDTHS = { stack: 480, grid: 720, carousel: 720 };
73
50
  var SCROLL_THRESHOLD = 30;
74
51
  var SCROLL_COOLDOWN = { stack: 450, carousel: 300 };
75
52
  function hexToRgb(hex) {
76
- const h = String(hex).replace("#", "");
53
+ let h = String(hex).trim().replace(/^#/, "");
54
+ if (h.length === 3) h = h.replace(/./g, (c) => c + c);
55
+ if (!/^[0-9a-f]{6}$/i.test(h)) return null;
77
56
  return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
78
57
  }
79
58
  function folderColors(hex) {
80
- const [r, g, b] = hexToRgb(hex);
59
+ const rgb = hexToRgb(hex);
60
+ if (!rgb) return null;
61
+ const [r, g, b] = rgb;
81
62
  const dn = (v) => Math.max(v - 30, 0);
82
63
  const up = (v, o) => Math.min(v + o, 255);
83
64
  const fr = up(r, 35);
@@ -91,6 +72,34 @@ function folderColors(hex) {
91
72
  label: yiq >= 128 ? "#1c1c1a" : "#ffffff"
92
73
  };
93
74
  }
75
+ function paintFolder(card, hex) {
76
+ const c = folderColors(hex);
77
+ if (!c) return;
78
+ card.style.setProperty("--fg-folder-bg", hex);
79
+ card.style.setProperty("--fg-front", c.front);
80
+ card.style.setProperty("--fg-front-solid", c.frontSolid);
81
+ card.style.setProperty("--fg-label-on-color", c.label);
82
+ const path = card.querySelector(".fg-folder path");
83
+ if (path) path.setAttribute("fill", c.back);
84
+ }
85
+ function paintGradient(card, gradient) {
86
+ const front = card.querySelector(".fg-front");
87
+ if (!front) return;
88
+ let layer = front.querySelector(".fg-gradient");
89
+ if (!gradient) {
90
+ if (layer) layer.remove();
91
+ card.classList.remove("fg-card--gradient");
92
+ return;
93
+ }
94
+ if (!layer) {
95
+ layer = document.createElement("div");
96
+ layer.className = "fg-gradient";
97
+ layer.setAttribute("aria-hidden", "true");
98
+ front.insertBefore(layer, front.firstChild);
99
+ }
100
+ layer.style.background = gradient;
101
+ card.classList.add("fg-card--gradient");
102
+ }
94
103
  function defaultContentRenderer(card, item) {
95
104
  const wrap = document.createElement("div");
96
105
  wrap.className = "fg-content";
@@ -110,12 +119,17 @@ function defaultContentRenderer(card, item) {
110
119
  card.appendChild(wrap);
111
120
  }
112
121
  function createFolderGallery(root, options = {}) {
113
- if (!root || !root.nodeType) throw new Error("createFolderGallery: a root element is required");
114
- const opts = __spreadValues(__spreadValues({}, DEFAULTS), options);
122
+ if (!root || root.nodeType !== 1) throw new Error("createFolderGallery: a root element is required");
123
+ const prior = INSTANCES.get(root);
124
+ if (prior) prior();
125
+ const provided = {};
126
+ for (const k in options) if (options[k] !== void 0) provided[k] = options[k];
127
+ const opts = { ...DEFAULTS, ...provided };
115
128
  const items = Array.isArray(opts.items) ? opts.items : [];
116
129
  const n = items.length;
117
130
  const renderContent = typeof opts.contentRenderer === "function" ? opts.contentRenderer : defaultContentRenderer;
118
- const reduced = opts.reducedMotion === "force" || opts.reducedMotion !== "off" && typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
131
+ const reducedQuery = opts.reducedMotion === "auto" && typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : null;
132
+ let reduced = opts.reducedMotion === "force" || (reducedQuery ? reducedQuery.matches : false);
119
133
  let mode = MODE_WIDTHS[opts.mode] ? opts.mode : "stack";
120
134
  let active = Math.min(Math.max(opts.defaultActiveIndex | 0, 0), Math.max(n - 1, 0));
121
135
  let scrollCooldown = false;
@@ -126,9 +140,29 @@ function createFolderGallery(root, options = {}) {
126
140
  cleanups.push(() => el.removeEventListener(ev, fn, o));
127
141
  };
128
142
  const emit = (name, detail) => root.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
143
+ let destroyed = false;
144
+ const timers = /* @__PURE__ */ new Set();
145
+ const rafs = /* @__PURE__ */ new Set();
146
+ const later = (fn, ms) => {
147
+ const id = setTimeout(() => {
148
+ timers.delete(id);
149
+ fn();
150
+ }, ms);
151
+ timers.add(id);
152
+ return id;
153
+ };
154
+ const frame = (fn) => {
155
+ const id = requestAnimationFrame(() => {
156
+ rafs.delete(id);
157
+ fn();
158
+ });
159
+ rafs.add(id);
160
+ return id;
161
+ };
129
162
  root.classList.add("fg-root");
130
163
  root.setAttribute("data-fg-mode", mode);
131
164
  root.setAttribute("data-fg-peek", PEEK_MODES.has(opts.peek) ? opts.peek : "hover");
165
+ root.setAttribute("data-fg-drag", DRAG_MODES.has(opts.drag) ? opts.drag : "fling");
132
166
  const scene = document.createElement("div");
133
167
  scene.className = "fg-scene";
134
168
  scene.setAttribute("role", "listbox");
@@ -143,26 +177,48 @@ function createFolderGallery(root, options = {}) {
143
177
  live.setAttribute("aria-atomic", "true");
144
178
  root.append(scene, dotsEl, live);
145
179
  function teardown() {
180
+ if (destroyed) return;
181
+ destroyed = true;
182
+ timers.forEach(clearTimeout);
183
+ timers.clear();
184
+ rafs.forEach(cancelAnimationFrame);
185
+ rafs.clear();
146
186
  cleanups.forEach((fn) => fn());
147
187
  cleanups.length = 0;
148
188
  root.replaceChildren();
149
189
  root.classList.remove("fg-root");
150
190
  root.removeAttribute("data-fg-mode");
151
191
  root.removeAttribute("data-fg-peek");
192
+ root.removeAttribute("data-fg-drag");
193
+ INSTANCES.delete(root);
152
194
  }
153
195
  if (n === 0) {
154
- return { next() {
155
- }, prev() {
156
- }, goTo() {
157
- }, setMode() {
158
- }, setPeek() {
159
- }, getActiveIndex: () => -1, getMode: () => mode, destroy: teardown };
196
+ const noop = () => {
197
+ };
198
+ INSTANCES.set(root, teardown);
199
+ return {
200
+ next: noop,
201
+ prev: noop,
202
+ goTo: noop,
203
+ setMode: noop,
204
+ setPeek: noop,
205
+ setColor: noop,
206
+ setGradient: noop,
207
+ getActiveIndex: () => -1,
208
+ getMode: () => mode,
209
+ getPeek: () => root.getAttribute("data-fg-peek"),
210
+ getColor: () => void 0,
211
+ getGradient: () => void 0,
212
+ getItems: () => [],
213
+ destroy: teardown
214
+ };
160
215
  }
161
216
  function setCardTransform(card, { x, y, z, rx, ry, s, zIndex, opacity, isActive }) {
162
- card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;
163
- card.style.transformOrigin = "center bottom";
217
+ if (!card.classList.contains("fg-card--flung")) {
218
+ card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;
219
+ card.style.opacity = String(opacity);
220
+ }
164
221
  card.style.zIndex = String(zIndex);
165
- card.style.opacity = String(opacity);
166
222
  card.setAttribute("aria-selected", isActive ? "true" : "false");
167
223
  card.classList.toggle("is-active", isActive);
168
224
  }
@@ -172,13 +228,9 @@ function createFolderGallery(root, options = {}) {
172
228
  card.tabIndex = i === active ? 0 : -1;
173
229
  card.setAttribute("role", "option");
174
230
  card.setAttribute("aria-label", item.label || `Item ${i + 1}`);
175
- if (item.color) {
176
- const c = folderColors(item.color);
177
- card.style.setProperty("--fg-folder-bg", item.color);
178
- card.style.setProperty("--fg-front", c.front);
179
- card.style.setProperty("--fg-front-solid", c.frontSolid);
180
- card.style.setProperty("--fg-label-on-color", c.label);
181
- }
231
+ card.setAttribute("aria-setsize", String(n));
232
+ card.setAttribute("aria-posinset", String(i + 1));
233
+ card.setAttribute("data-fg-index", String(i));
182
234
  const svg = document.createElementNS(SVG_NS, "svg");
183
235
  svg.setAttribute("class", "fg-folder");
184
236
  svg.setAttribute("viewBox", "0 0 480 342");
@@ -186,7 +238,7 @@ function createFolderGallery(root, options = {}) {
186
238
  svg.setAttribute("aria-hidden", "true");
187
239
  const path = document.createElementNS(SVG_NS, "path");
188
240
  path.setAttribute("d", opts.folderPath);
189
- path.setAttribute("fill", item.color ? folderColors(item.color).back : "var(--fg-folder-bg)");
241
+ path.setAttribute("fill", "var(--fg-folder-bg)");
190
242
  svg.appendChild(path);
191
243
  card.appendChild(svg);
192
244
  renderContent(card, item, i);
@@ -209,6 +261,8 @@ function createFolderGallery(root, options = {}) {
209
261
  front.appendChild(label);
210
262
  }
211
263
  card.appendChild(front);
264
+ if (item.color) paintFolder(card, item.color);
265
+ if (item.gradient) paintGradient(card, item.gradient);
212
266
  scene.appendChild(card);
213
267
  return card;
214
268
  });
@@ -222,9 +276,13 @@ function createFolderGallery(root, options = {}) {
222
276
  dotsEl.appendChild(dot);
223
277
  return dot;
224
278
  });
279
+ const clearGridWidth = (card) => {
280
+ if (card.style.width) card.style.width = "";
281
+ };
225
282
  function layoutStack() {
226
283
  scene.style.height = "";
227
284
  cardEls.forEach((card, i) => {
285
+ clearGridWidth(card);
228
286
  const behind = (i - active + n) % n;
229
287
  if (behind === 0) {
230
288
  setCardTransform(card, { x: 0, y: 100, z: 20, rx: 0, ry: 0, s: 1, zIndex: n + 1, opacity: 1, isActive: true });
@@ -240,33 +298,31 @@ function createFolderGallery(root, options = {}) {
240
298
  }
241
299
  function layoutGrid() {
242
300
  const sceneW = scene.clientWidth || MODE_WIDTHS.grid;
243
- const cols = sceneW < 520 ? 2 : n <= 4 ? 2 : 3;
244
- const gap = 16;
245
- const cardW = sceneW;
246
- const cardH = sceneW * (2 / 3);
247
- const sc = (sceneW - (cols - 1) * gap) / (cols * cardW);
248
- const scaledW = cardW * sc;
249
- const scaledH = cardH * sc;
250
- const tabScaled = 22 * sc;
301
+ const gap = Number.isFinite(opts.grid && opts.grid.gap) ? opts.grid.gap : 16;
302
+ const cols = Math.max(1, Math.round(
303
+ opts.grid && opts.grid.columns ? opts.grid.columns : sceneW < 520 ? 2 : n <= 4 ? 2 : 3
304
+ ));
305
+ const cellW = (sceneW - (cols - 1) * gap) / cols;
306
+ const cellH = cellW * (2 / 3);
251
307
  const rows = Math.ceil(n / cols);
252
308
  cardEls.forEach((card, i) => {
253
309
  const col = i % cols;
254
310
  const row = Math.floor(i / cols);
255
311
  const itemsInRow = Math.min(cols, n - row * cols);
256
- const rowW = itemsInRow * scaledW + (itemsInRow - 1) * gap;
312
+ const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;
257
313
  const rowStartX = (sceneW - rowW) / 2;
258
- const cellLeft = rowStartX + col * (scaledW + gap);
259
- const cellTop = row * (scaledH + tabScaled + gap) + tabScaled;
260
- const tx = cellLeft + scaledW / 2 - cardW / 2;
261
- const ty = cellTop + scaledH - cardH;
262
- setCardTransform(card, { x: tx, y: ty, z: 0, rx: 0, ry: 0, s: sc, zIndex: i === active ? 2 : 1, opacity: 1, isActive: i === active });
314
+ const x = rowStartX + col * (cellW + gap);
315
+ const y = row * (cellH + gap);
316
+ card.style.width = cellW + "px";
317
+ setCardTransform(card, { x, y, z: 0, rx: 0, ry: 0, s: 1, zIndex: i === active ? 2 : 1, opacity: 1, isActive: i === active });
263
318
  });
264
- scene.style.height = rows * (scaledH + tabScaled + gap) + 20 + "px";
319
+ scene.style.height = rows * cellH + (rows - 1) * gap + "px";
265
320
  }
266
321
  function layoutCarousel() {
267
322
  const sceneW = scene.clientWidth || MODE_WIDTHS.carousel;
268
323
  const cardW = sceneW;
269
324
  const cardH = cardW * (2 / 3);
325
+ cardEls.forEach(clearGridWidth);
270
326
  const activeSc = 0.62;
271
327
  const activeW = cardW * activeSc;
272
328
  const sceneH = Math.max(cardH * activeSc + 40, 180);
@@ -301,29 +357,39 @@ function createFolderGallery(root, options = {}) {
301
357
  live.textContent = cur && cur.label ? `${active + 1} of ${n}: ${cur.label}` : `${active + 1} of ${n}`;
302
358
  }
303
359
  function goTo(i) {
304
- const next = opts.loop ? (i % n + n) % n : Math.min(Math.max(i, 0), n - 1);
360
+ if (destroyed) return;
361
+ const t = Number.isFinite(+i) ? Math.trunc(+i) : active;
362
+ const next = opts.loop ? (t % n + n) % n : Math.min(Math.max(t, 0), n - 1);
305
363
  if (next === active) return;
306
364
  active = next;
307
365
  applyLayout();
308
366
  emit("fg-activechange", { index: active, item: items[active] });
309
367
  }
310
368
  function select(i) {
369
+ if (destroyed) return;
311
370
  emit("fg-select", { index: i, item: items[i] });
312
371
  if (typeof opts.onSelect === "function") opts.onSelect(items[i], i);
313
372
  }
314
373
  function setMode(m) {
315
- if (m === mode || !MODE_WIDTHS[m]) return;
374
+ if (destroyed || m === mode || !MODE_WIDTHS[m]) return;
316
375
  mode = m;
317
376
  root.setAttribute("data-fg-mode", mode);
318
377
  applyLayout();
319
378
  emit("fg-modechange", { mode });
320
379
  }
321
380
  function setPeek(p) {
322
- if (!PEEK_MODES.has(p)) return;
381
+ if (destroyed || !PEEK_MODES.has(p) || p === root.getAttribute("data-fg-peek")) return;
323
382
  root.setAttribute("data-fg-peek", p);
383
+ emit("fg-peekchange", { peek: p });
324
384
  }
385
+ const dragEnabled = opts.drag !== "off";
386
+ let suppressClick = false;
325
387
  cardEls.forEach((card, i) => {
326
388
  on(card, "click", () => {
389
+ if (suppressClick) {
390
+ suppressClick = false;
391
+ return;
392
+ }
327
393
  i === active ? select(i) : goTo(i);
328
394
  });
329
395
  on(card, "keydown", (e) => {
@@ -389,7 +455,7 @@ function createFolderGallery(root, options = {}) {
389
455
  scrollAccum = 0;
390
456
  scrollCooldown = true;
391
457
  goTo(active + dir);
392
- setTimeout(() => {
458
+ later(() => {
393
459
  scrollCooldown = false;
394
460
  scrollAccum = 0;
395
461
  }, SCROLL_COOLDOWN[mode] || 300);
@@ -403,6 +469,7 @@ function createFolderGallery(root, options = {}) {
403
469
  touchStartY = e.touches[0].clientY;
404
470
  }, { passive: true });
405
471
  on(scene, "touchend", (e) => {
472
+ if (dragEnabled && mode === "stack") return;
406
473
  const dx = touchStartX - e.changedTouches[0].clientX;
407
474
  const dy = touchStartY - e.changedTouches[0].clientY;
408
475
  const ax = Math.abs(dx);
@@ -411,34 +478,168 @@ function createFolderGallery(root, options = {}) {
411
478
  const dir = ax > ay ? dx > 0 ? 1 : -1 : dy > 0 ? 1 : -1;
412
479
  goTo(active + dir);
413
480
  }, { passive: true });
481
+ if (dragEnabled) {
482
+ const DRAG_ROT = 0.04;
483
+ const FLING_DIST = 90;
484
+ const FLING_VEL = 0.5;
485
+ const TAP_SLOP = 6;
486
+ let dragCard = null;
487
+ let dragBase = "";
488
+ let startPX = 0, startPY = 0, dragDX = 0, dragDY = 0;
489
+ let lastX = 0, lastY = 0, lastT = 0, velX = 0, velY = 0;
490
+ on(scene, "pointerdown", (e) => {
491
+ if (mode !== "stack") return;
492
+ if (e.button !== void 0 && e.button !== 0) return;
493
+ const card = e.target && e.target.closest ? e.target.closest(".fg-card") : null;
494
+ if (!card || !card.classList.contains("is-active")) return;
495
+ dragCard = card;
496
+ dragBase = card.style.transform;
497
+ startPX = e.clientX;
498
+ startPY = e.clientY;
499
+ dragDX = 0;
500
+ dragDY = 0;
501
+ lastX = e.clientX;
502
+ lastY = e.clientY;
503
+ lastT = typeof performance !== "undefined" ? performance.now() : Date.now();
504
+ velX = 0;
505
+ velY = 0;
506
+ card.classList.add("fg-card--dragging");
507
+ if (card.setPointerCapture && e.pointerId !== void 0) {
508
+ try {
509
+ card.setPointerCapture(e.pointerId);
510
+ } catch (_) {
511
+ }
512
+ }
513
+ });
514
+ on(window, "pointermove", (e) => {
515
+ if (!dragCard) return;
516
+ dragDX = e.clientX - startPX;
517
+ dragDY = e.clientY - startPY;
518
+ const now = typeof performance !== "undefined" ? performance.now() : Date.now();
519
+ const dt = Math.max(now - lastT, 1);
520
+ velX = (e.clientX - lastX) / dt;
521
+ velY = (e.clientY - lastY) / dt;
522
+ lastX = e.clientX;
523
+ lastY = e.clientY;
524
+ lastT = now;
525
+ dragCard.style.transform = `${dragBase} translate3d(${dragDX}px, ${dragDY}px, 40px) rotate(${(dragDX * DRAG_ROT).toFixed(2)}deg)`;
526
+ });
527
+ const endDrag = () => {
528
+ if (!dragCard) return;
529
+ const card = dragCard;
530
+ dragCard = null;
531
+ card.classList.remove("fg-card--dragging");
532
+ const dist = Math.hypot(dragDX, dragDY);
533
+ if (dist < TAP_SLOP) {
534
+ card.style.transform = dragBase;
535
+ return;
536
+ }
537
+ suppressClick = true;
538
+ const horizontal = Math.abs(dragDX) >= Math.abs(dragDY);
539
+ const flung = dist > FLING_DIST || Math.hypot(velX, velY) > FLING_VEL;
540
+ const dir = horizontal ? dragDX > 0 ? -1 : 1 : dragDY > 0 ? -1 : 1;
541
+ const target = active + dir;
542
+ const blocked = !opts.loop && (target < 0 || target > n - 1);
543
+ if (!flung || blocked) {
544
+ card.style.transform = dragBase;
545
+ return;
546
+ }
547
+ const semantic = dir > 0 ? "next" : "prev";
548
+ emit("fg-flingstart", { index: active, direction: semantic });
549
+ if (reduced) {
550
+ goTo(target);
551
+ emit("fg-flingend", { index: target, direction: semantic });
552
+ return;
553
+ }
554
+ const offX = horizontal ? Math.sign(dragDX) * Math.max(window.innerWidth * 0.7, 480) : dragDX * 3;
555
+ const offY = horizontal ? dragDY * 3 : Math.sign(dragDY) * Math.max(window.innerHeight * 0.7, 480);
556
+ card.classList.add("fg-card--flung");
557
+ card.style.transform = `${dragBase} translate3d(${offX}px, ${offY}px, 40px) rotate(${Math.sign(dragDX || 1) * 18}deg)`;
558
+ card.style.opacity = "0";
559
+ goTo(target);
560
+ later(() => {
561
+ card.classList.add("fg-card--snap");
562
+ card.classList.remove("fg-card--flung");
563
+ applyLayout();
564
+ const slot = card.style.transform;
565
+ const slotOpacity = card.style.opacity;
566
+ card.style.transform = `${slot} translate3d(0, -90px, 0)`;
567
+ card.style.opacity = "0";
568
+ frame(() => frame(() => {
569
+ card.classList.remove("fg-card--snap");
570
+ card.classList.add("fg-card--entering");
571
+ card.style.transform = slot;
572
+ card.style.opacity = slotOpacity;
573
+ later(() => {
574
+ card.classList.remove("fg-card--entering");
575
+ emit("fg-flingend", { index: target, direction: semantic });
576
+ }, 700);
577
+ }));
578
+ }, 400);
579
+ };
580
+ on(window, "pointerup", endDrag);
581
+ on(window, "pointercancel", endDrag);
582
+ }
414
583
  let resizeTimer;
415
584
  on(window, "resize", () => {
416
585
  clearTimeout(resizeTimer);
417
- resizeTimer = setTimeout(applyLayout, 150);
586
+ resizeTimer = later(applyLayout, 150);
418
587
  });
588
+ if (reducedQuery && reducedQuery.addEventListener) {
589
+ on(reducedQuery, "change", (e) => {
590
+ if (!destroyed) {
591
+ reduced = e.matches;
592
+ applyLayout();
593
+ }
594
+ });
595
+ }
419
596
  applyLayout();
420
- return {
597
+ const handle = {
421
598
  next: () => goTo(active + 1),
422
599
  prev: () => goTo(active - 1),
423
600
  goTo,
424
601
  setMode,
425
602
  setPeek,
603
+ setColor: (i, hex) => {
604
+ const card = cardEls[i];
605
+ if (card && !destroyed) paintFolder(card, hex);
606
+ },
607
+ setGradient: (i, gradient) => {
608
+ const card = cardEls[i];
609
+ if (card && !destroyed) paintGradient(card, gradient);
610
+ },
426
611
  getActiveIndex: () => active,
427
612
  getMode: () => mode,
613
+ getPeek: () => root.getAttribute("data-fg-peek"),
614
+ getColor: (i) => {
615
+ const card = cardEls[i];
616
+ return card ? card.style.getPropertyValue("--fg-folder-bg") || void 0 : void 0;
617
+ },
618
+ getGradient: (i) => {
619
+ const layer = cardEls[i] && cardEls[i].querySelector(".fg-gradient");
620
+ return layer ? layer.style.background || void 0 : void 0;
621
+ },
622
+ getItems: () => items.slice(),
428
623
  destroy: teardown
429
624
  };
625
+ INSTANCES.set(root, teardown);
626
+ return handle;
430
627
  }
431
628
 
432
629
  // src/folder-gallery-react.js
433
630
  var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props, ref) {
434
- const _a = props, {
631
+ const {
435
632
  items,
436
633
  mode = "stack",
437
634
  peek = "hover",
635
+ drag = "fling",
438
636
  contentRenderer,
439
637
  onSelect,
440
638
  onActiveChange,
441
639
  onModeChange,
640
+ onPeekChange,
641
+ onFlingStart,
642
+ onFlingEnd,
442
643
  folderPath,
443
644
  loop,
444
645
  scrollNav,
@@ -446,28 +647,13 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
446
647
  defaultActiveIndex,
447
648
  label,
448
649
  className,
449
- style
450
- } = _a, rest = __objRest(_a, [
451
- "items",
452
- "mode",
453
- "peek",
454
- "contentRenderer",
455
- "onSelect",
456
- "onActiveChange",
457
- "onModeChange",
458
- "folderPath",
459
- "loop",
460
- "scrollNav",
461
- "reducedMotion",
462
- "defaultActiveIndex",
463
- "label",
464
- "className",
465
- "style"
466
- ]);
650
+ style,
651
+ ...rest
652
+ } = props;
467
653
  const rootRef = (0, import_react.useRef)(null);
468
654
  const handleRef = (0, import_react.useRef)(null);
469
655
  const callbacksRef = (0, import_react.useRef)({});
470
- callbacksRef.current = { onSelect, onActiveChange, onModeChange, contentRenderer };
656
+ callbacksRef.current = { onSelect, onActiveChange, onModeChange, onPeekChange, onFlingStart, onFlingEnd, contentRenderer };
471
657
  (0, import_react.useEffect)(() => {
472
658
  const root = rootRef.current;
473
659
  if (!root) return void 0;
@@ -475,6 +661,7 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
475
661
  items,
476
662
  mode,
477
663
  peek,
664
+ drag,
478
665
  folderPath,
479
666
  loop,
480
667
  scrollNav,
@@ -496,15 +683,33 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
496
683
  const fn = callbacksRef.current.onModeChange;
497
684
  if (fn) fn(e.detail.mode);
498
685
  };
686
+ const onPeekEv = (e) => {
687
+ const fn = callbacksRef.current.onPeekChange;
688
+ if (fn) fn(e.detail.peek);
689
+ };
690
+ const onFlingStartEv = (e) => {
691
+ const fn = callbacksRef.current.onFlingStart;
692
+ if (fn) fn(e.detail.index, e.detail.direction);
693
+ };
694
+ const onFlingEndEv = (e) => {
695
+ const fn = callbacksRef.current.onFlingEnd;
696
+ if (fn) fn(e.detail.index, e.detail.direction);
697
+ };
499
698
  root.addEventListener("fg-activechange", onActive);
500
699
  root.addEventListener("fg-modechange", onModeEv);
700
+ root.addEventListener("fg-peekchange", onPeekEv);
701
+ root.addEventListener("fg-flingstart", onFlingStartEv);
702
+ root.addEventListener("fg-flingend", onFlingEndEv);
501
703
  return () => {
502
704
  root.removeEventListener("fg-activechange", onActive);
503
705
  root.removeEventListener("fg-modechange", onModeEv);
706
+ root.removeEventListener("fg-peekchange", onPeekEv);
707
+ root.removeEventListener("fg-flingstart", onFlingStartEv);
708
+ root.removeEventListener("fg-flingend", onFlingEndEv);
504
709
  handle.destroy();
505
710
  handleRef.current = null;
506
711
  };
507
- }, [items, folderPath, loop, scrollNav, reducedMotion, defaultActiveIndex, label]);
712
+ }, [items, drag, folderPath, loop, scrollNav, reducedMotion, defaultActiveIndex, label]);
508
713
  (0, import_react.useEffect)(() => {
509
714
  if (handleRef.current && handleRef.current.getMode() !== mode) {
510
715
  handleRef.current.setMode(mode);
@@ -519,10 +724,16 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
519
724
  goTo: (i) => handleRef.current && handleRef.current.goTo(i),
520
725
  setMode: (m) => handleRef.current && handleRef.current.setMode(m),
521
726
  setPeek: (p) => handleRef.current && handleRef.current.setPeek(p),
727
+ setColor: (i, hex) => handleRef.current && handleRef.current.setColor(i, hex),
728
+ setGradient: (i, gradient) => handleRef.current && handleRef.current.setGradient(i, gradient),
522
729
  getActiveIndex: () => handleRef.current ? handleRef.current.getActiveIndex() : -1,
523
- getMode: () => handleRef.current ? handleRef.current.getMode() : mode
524
- }), [mode]);
525
- return (0, import_react.createElement)("div", __spreadValues({ ref: rootRef, className, style }, rest));
730
+ getMode: () => handleRef.current ? handleRef.current.getMode() : mode,
731
+ getPeek: () => handleRef.current ? handleRef.current.getPeek() : peek,
732
+ getColor: (i) => handleRef.current ? handleRef.current.getColor(i) : void 0,
733
+ getGradient: (i) => handleRef.current ? handleRef.current.getGradient(i) : void 0,
734
+ getItems: () => handleRef.current ? handleRef.current.getItems() : []
735
+ }), [mode, peek]);
736
+ return (0, import_react.createElement)("div", { ref: rootRef, className, style, ...rest });
526
737
  });
527
738
  var folder_gallery_react_default = FolderGallery;
528
739
  // Annotate the CommonJS export names for ESM import in node: