flipfolio 0.2.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/index.d.ts CHANGED
@@ -9,6 +9,9 @@ export interface FolderItem {
9
9
  content?: string | Node;
10
10
  /** Image URL printed on the folder's front panel; back and tab keep their color. */
11
11
  decal?: string;
12
+ /** CSS gradient painted across the front panel (e.g. `linear-gradient(...)`),
13
+ * over the frosted surface but under the label. */
14
+ gradient?: string;
12
15
  /** Any extra data - passed through to onSelect. */
13
16
  [key: string]: unknown;
14
17
  }
@@ -20,7 +23,7 @@ export type DragMode = 'fling' | 'off';
20
23
  export interface FolderGalleryOptions {
21
24
  items?: FolderItem[];
22
25
  mode?: GalleryMode;
23
- /** Fills the folder interior. Overrides the built-in polaroid/image preset. */
26
+ /** Fills the folder interior. Overrides the built-in image/content renderer. */
24
27
  contentRenderer?: (card: HTMLElement, item: FolderItem, index: number) => void;
25
28
  /** Fired on click/Enter of the active folder. No built-in navigation. */
26
29
  onSelect?: (item: FolderItem, index: number) => void;
@@ -36,6 +39,9 @@ export interface FolderGalleryOptions {
36
39
  defaultActiveIndex?: number;
37
40
  /** aria-label for the listbox. */
38
41
  label?: string;
42
+ /** Pin the grid layout's column count and/or gap (px). Both default to the
43
+ * built-in heuristic (2 or 3 columns by scene width and item count, 16px gap). */
44
+ grid?: { columns?: number; gap?: number };
39
45
  }
40
46
 
41
47
  export interface FolderGalleryHandle {
@@ -44,12 +50,44 @@ export interface FolderGalleryHandle {
44
50
  goTo(index: number): void;
45
51
  setMode(mode: GalleryMode): void;
46
52
  setPeek(peek: PeekMode): void;
53
+ /** Recolor folder `index` from a single hex; re-derives the full palette
54
+ * (back, frosted + solid fronts, auto-contrast label) like build time. */
55
+ setColor(index: number, hex: string): void;
56
+ /** Paint (or, with a falsy value, clear) a CSS gradient on folder `index`'s front. */
57
+ setGradient(index: number, gradient: string | null): void;
47
58
  getActiveIndex(): number;
48
59
  getMode(): GalleryMode;
60
+ /** Current peek mode (reads the root's `data-fg-peek`). */
61
+ getPeek(): PeekMode;
62
+ /** The hex color folder `index` is painted with, or `undefined` if unset. */
63
+ getColor(index: number): string | undefined;
64
+ /** The CSS gradient on folder `index`'s front, or `undefined` if none. */
65
+ getGradient(index: number): string | undefined;
66
+ /** A shallow copy of the items the gallery was built with. */
67
+ getItems(): FolderItem[];
49
68
  /** Removes every listener and empties the root element. */
50
69
  destroy(): void;
51
70
  }
52
71
 
72
+ /**
73
+ * DOM CustomEvents the gallery dispatches on its root element. They bubble, so
74
+ * a wrapper or ancestor can listen too. Use with `addEventListener` for typed
75
+ * `event.detail` (augment `HTMLElementEventMap` in your app to wire it up).
76
+ */
77
+ export interface FolderGalleryEventMap {
78
+ 'fg-select': CustomEvent<{ index: number; item: FolderItem }>;
79
+ 'fg-activechange': CustomEvent<{ index: number; item: FolderItem }>;
80
+ 'fg-modechange': CustomEvent<{ mode: GalleryMode }>;
81
+ 'fg-peekchange': CustomEvent<{ peek: PeekMode }>;
82
+ 'fg-flingstart': CustomEvent<{ index: number; direction: 'next' | 'prev' }>;
83
+ 'fg-flingend': CustomEvent<{ index: number; direction: 'next' | 'prev' }>;
84
+ }
85
+
86
+ /** Preset folder silhouette path strings for the `folderPath` option
87
+ * (viewBox `0 0 480 342`): a left tab (default), a mirrored right tab, or a
88
+ * tabless tray. */
89
+ export const FOLDER_PATHS: { left: string; right: string; tray: string };
90
+
53
91
  /**
54
92
  * Create a 3D folder gallery inside `root`. The gallery builds and owns its
55
93
  * own DOM; call `handle.destroy()` to tear it down.
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import {
2
+ FOLDER_PATHS,
2
3
  createFolderGallery,
3
4
  folder_gallery_default
4
- } from "./chunk-Q2NKZMCC.js";
5
+ } from "./chunk-RFBZ3WPG.js";
5
6
  export {
7
+ FOLDER_PATHS,
6
8
  createFolderGallery,
7
9
  folder_gallery_default as default
8
10
  };
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 });
@@ -72,15 +45,20 @@ var DEFAULTS = {
72
45
  };
73
46
  var PEEK_MODES = /* @__PURE__ */ new Set(["hover", "always", "off"]);
74
47
  var DRAG_MODES = /* @__PURE__ */ new Set(["fling", "off"]);
48
+ var INSTANCES = /* @__PURE__ */ new WeakMap();
75
49
  var MODE_WIDTHS = { stack: 480, grid: 720, carousel: 720 };
76
50
  var SCROLL_THRESHOLD = 30;
77
51
  var SCROLL_COOLDOWN = { stack: 450, carousel: 300 };
78
52
  function hexToRgb(hex) {
79
- 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;
80
56
  return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
81
57
  }
82
58
  function folderColors(hex) {
83
- const [r, g, b] = hexToRgb(hex);
59
+ const rgb = hexToRgb(hex);
60
+ if (!rgb) return null;
61
+ const [r, g, b] = rgb;
84
62
  const dn = (v) => Math.max(v - 30, 0);
85
63
  const up = (v, o) => Math.min(v + o, 255);
86
64
  const fr = up(r, 35);
@@ -94,6 +72,34 @@ function folderColors(hex) {
94
72
  label: yiq >= 128 ? "#1c1c1a" : "#ffffff"
95
73
  };
96
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
+ }
97
103
  function defaultContentRenderer(card, item) {
98
104
  const wrap = document.createElement("div");
99
105
  wrap.className = "fg-content";
@@ -113,12 +119,17 @@ function defaultContentRenderer(card, item) {
113
119
  card.appendChild(wrap);
114
120
  }
115
121
  function createFolderGallery(root, options = {}) {
116
- if (!root || !root.nodeType) throw new Error("createFolderGallery: a root element is required");
117
- 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 };
118
128
  const items = Array.isArray(opts.items) ? opts.items : [];
119
129
  const n = items.length;
120
130
  const renderContent = typeof opts.contentRenderer === "function" ? opts.contentRenderer : defaultContentRenderer;
121
- 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);
122
133
  let mode = MODE_WIDTHS[opts.mode] ? opts.mode : "stack";
123
134
  let active = Math.min(Math.max(opts.defaultActiveIndex | 0, 0), Math.max(n - 1, 0));
124
135
  let scrollCooldown = false;
@@ -129,6 +140,25 @@ function createFolderGallery(root, options = {}) {
129
140
  cleanups.push(() => el.removeEventListener(ev, fn, o));
130
141
  };
131
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
+ };
132
162
  root.classList.add("fg-root");
133
163
  root.setAttribute("data-fg-mode", mode);
134
164
  root.setAttribute("data-fg-peek", PEEK_MODES.has(opts.peek) ? opts.peek : "hover");
@@ -147,6 +177,12 @@ function createFolderGallery(root, options = {}) {
147
177
  live.setAttribute("aria-atomic", "true");
148
178
  root.append(scene, dotsEl, live);
149
179
  function teardown() {
180
+ if (destroyed) return;
181
+ destroyed = true;
182
+ timers.forEach(clearTimeout);
183
+ timers.clear();
184
+ rafs.forEach(cancelAnimationFrame);
185
+ rafs.clear();
150
186
  cleanups.forEach((fn) => fn());
151
187
  cleanups.length = 0;
152
188
  root.replaceChildren();
@@ -154,21 +190,34 @@ function createFolderGallery(root, options = {}) {
154
190
  root.removeAttribute("data-fg-mode");
155
191
  root.removeAttribute("data-fg-peek");
156
192
  root.removeAttribute("data-fg-drag");
193
+ INSTANCES.delete(root);
157
194
  }
158
195
  if (n === 0) {
159
- return { next() {
160
- }, prev() {
161
- }, goTo() {
162
- }, setMode() {
163
- }, setPeek() {
164
- }, 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
+ };
165
215
  }
166
216
  function setCardTransform(card, { x, y, z, rx, ry, s, zIndex, opacity, isActive }) {
167
217
  if (!card.classList.contains("fg-card--flung")) {
168
218
  card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;
169
219
  card.style.opacity = String(opacity);
170
220
  }
171
- card.style.transformOrigin = "center bottom";
172
221
  card.style.zIndex = String(zIndex);
173
222
  card.setAttribute("aria-selected", isActive ? "true" : "false");
174
223
  card.classList.toggle("is-active", isActive);
@@ -179,13 +228,9 @@ function createFolderGallery(root, options = {}) {
179
228
  card.tabIndex = i === active ? 0 : -1;
180
229
  card.setAttribute("role", "option");
181
230
  card.setAttribute("aria-label", item.label || `Item ${i + 1}`);
182
- if (item.color) {
183
- const c = folderColors(item.color);
184
- card.style.setProperty("--fg-folder-bg", item.color);
185
- card.style.setProperty("--fg-front", c.front);
186
- card.style.setProperty("--fg-front-solid", c.frontSolid);
187
- card.style.setProperty("--fg-label-on-color", c.label);
188
- }
231
+ card.setAttribute("aria-setsize", String(n));
232
+ card.setAttribute("aria-posinset", String(i + 1));
233
+ card.setAttribute("data-fg-index", String(i));
189
234
  const svg = document.createElementNS(SVG_NS, "svg");
190
235
  svg.setAttribute("class", "fg-folder");
191
236
  svg.setAttribute("viewBox", "0 0 480 342");
@@ -193,7 +238,7 @@ function createFolderGallery(root, options = {}) {
193
238
  svg.setAttribute("aria-hidden", "true");
194
239
  const path = document.createElementNS(SVG_NS, "path");
195
240
  path.setAttribute("d", opts.folderPath);
196
- path.setAttribute("fill", item.color ? folderColors(item.color).back : "var(--fg-folder-bg)");
241
+ path.setAttribute("fill", "var(--fg-folder-bg)");
197
242
  svg.appendChild(path);
198
243
  card.appendChild(svg);
199
244
  renderContent(card, item, i);
@@ -216,6 +261,8 @@ function createFolderGallery(root, options = {}) {
216
261
  front.appendChild(label);
217
262
  }
218
263
  card.appendChild(front);
264
+ if (item.color) paintFolder(card, item.color);
265
+ if (item.gradient) paintGradient(card, item.gradient);
219
266
  scene.appendChild(card);
220
267
  return card;
221
268
  });
@@ -229,9 +276,13 @@ function createFolderGallery(root, options = {}) {
229
276
  dotsEl.appendChild(dot);
230
277
  return dot;
231
278
  });
279
+ const clearGridWidth = (card) => {
280
+ if (card.style.width) card.style.width = "";
281
+ };
232
282
  function layoutStack() {
233
283
  scene.style.height = "";
234
284
  cardEls.forEach((card, i) => {
285
+ clearGridWidth(card);
235
286
  const behind = (i - active + n) % n;
236
287
  if (behind === 0) {
237
288
  setCardTransform(card, { x: 0, y: 100, z: 20, rx: 0, ry: 0, s: 1, zIndex: n + 1, opacity: 1, isActive: true });
@@ -247,33 +298,31 @@ function createFolderGallery(root, options = {}) {
247
298
  }
248
299
  function layoutGrid() {
249
300
  const sceneW = scene.clientWidth || MODE_WIDTHS.grid;
250
- const cols = sceneW < 520 ? 2 : n <= 4 ? 2 : 3;
251
- const gap = 16;
252
- const cardW = sceneW;
253
- const cardH = sceneW * (2 / 3);
254
- const sc = (sceneW - (cols - 1) * gap) / (cols * cardW);
255
- const scaledW = cardW * sc;
256
- const scaledH = cardH * sc;
257
- 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);
258
307
  const rows = Math.ceil(n / cols);
259
308
  cardEls.forEach((card, i) => {
260
309
  const col = i % cols;
261
310
  const row = Math.floor(i / cols);
262
311
  const itemsInRow = Math.min(cols, n - row * cols);
263
- const rowW = itemsInRow * scaledW + (itemsInRow - 1) * gap;
312
+ const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;
264
313
  const rowStartX = (sceneW - rowW) / 2;
265
- const cellLeft = rowStartX + col * (scaledW + gap);
266
- const cellTop = row * (scaledH + tabScaled + gap) + tabScaled;
267
- const tx = cellLeft + scaledW / 2 - cardW / 2;
268
- const ty = cellTop + scaledH - cardH;
269
- 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 });
270
318
  });
271
- scene.style.height = rows * (scaledH + tabScaled + gap) + 20 + "px";
319
+ scene.style.height = rows * cellH + (rows - 1) * gap + "px";
272
320
  }
273
321
  function layoutCarousel() {
274
322
  const sceneW = scene.clientWidth || MODE_WIDTHS.carousel;
275
323
  const cardW = sceneW;
276
324
  const cardH = cardW * (2 / 3);
325
+ cardEls.forEach(clearGridWidth);
277
326
  const activeSc = 0.62;
278
327
  const activeW = cardW * activeSc;
279
328
  const sceneH = Math.max(cardH * activeSc + 40, 180);
@@ -308,26 +357,30 @@ function createFolderGallery(root, options = {}) {
308
357
  live.textContent = cur && cur.label ? `${active + 1} of ${n}: ${cur.label}` : `${active + 1} of ${n}`;
309
358
  }
310
359
  function goTo(i) {
311
- 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);
312
363
  if (next === active) return;
313
364
  active = next;
314
365
  applyLayout();
315
366
  emit("fg-activechange", { index: active, item: items[active] });
316
367
  }
317
368
  function select(i) {
369
+ if (destroyed) return;
318
370
  emit("fg-select", { index: i, item: items[i] });
319
371
  if (typeof opts.onSelect === "function") opts.onSelect(items[i], i);
320
372
  }
321
373
  function setMode(m) {
322
- if (m === mode || !MODE_WIDTHS[m]) return;
374
+ if (destroyed || m === mode || !MODE_WIDTHS[m]) return;
323
375
  mode = m;
324
376
  root.setAttribute("data-fg-mode", mode);
325
377
  applyLayout();
326
378
  emit("fg-modechange", { mode });
327
379
  }
328
380
  function setPeek(p) {
329
- if (!PEEK_MODES.has(p)) return;
381
+ if (destroyed || !PEEK_MODES.has(p) || p === root.getAttribute("data-fg-peek")) return;
330
382
  root.setAttribute("data-fg-peek", p);
383
+ emit("fg-peekchange", { peek: p });
331
384
  }
332
385
  const dragEnabled = opts.drag !== "off";
333
386
  let suppressClick = false;
@@ -402,7 +455,7 @@ function createFolderGallery(root, options = {}) {
402
455
  scrollAccum = 0;
403
456
  scrollCooldown = true;
404
457
  goTo(active + dir);
405
- setTimeout(() => {
458
+ later(() => {
406
459
  scrollCooldown = false;
407
460
  scrollAccum = 0;
408
461
  }, SCROLL_COOLDOWN[mode] || 300);
@@ -491,8 +544,11 @@ function createFolderGallery(root, options = {}) {
491
544
  card.style.transform = dragBase;
492
545
  return;
493
546
  }
547
+ const semantic = dir > 0 ? "next" : "prev";
548
+ emit("fg-flingstart", { index: active, direction: semantic });
494
549
  if (reduced) {
495
550
  goTo(target);
551
+ emit("fg-flingend", { index: target, direction: semantic });
496
552
  return;
497
553
  }
498
554
  const offX = horizontal ? Math.sign(dragDX) * Math.max(window.innerWidth * 0.7, 480) : dragDX * 3;
@@ -501,7 +557,7 @@ function createFolderGallery(root, options = {}) {
501
557
  card.style.transform = `${dragBase} translate3d(${offX}px, ${offY}px, 40px) rotate(${Math.sign(dragDX || 1) * 18}deg)`;
502
558
  card.style.opacity = "0";
503
559
  goTo(target);
504
- setTimeout(() => {
560
+ later(() => {
505
561
  card.classList.add("fg-card--snap");
506
562
  card.classList.remove("fg-card--flung");
507
563
  applyLayout();
@@ -509,12 +565,15 @@ function createFolderGallery(root, options = {}) {
509
565
  const slotOpacity = card.style.opacity;
510
566
  card.style.transform = `${slot} translate3d(0, -90px, 0)`;
511
567
  card.style.opacity = "0";
512
- requestAnimationFrame(() => requestAnimationFrame(() => {
568
+ frame(() => frame(() => {
513
569
  card.classList.remove("fg-card--snap");
514
570
  card.classList.add("fg-card--entering");
515
571
  card.style.transform = slot;
516
572
  card.style.opacity = slotOpacity;
517
- setTimeout(() => card.classList.remove("fg-card--entering"), 700);
573
+ later(() => {
574
+ card.classList.remove("fg-card--entering");
575
+ emit("fg-flingend", { index: target, direction: semantic });
576
+ }, 700);
518
577
  }));
519
578
  }, 400);
520
579
  };
@@ -524,24 +583,52 @@ function createFolderGallery(root, options = {}) {
524
583
  let resizeTimer;
525
584
  on(window, "resize", () => {
526
585
  clearTimeout(resizeTimer);
527
- resizeTimer = setTimeout(applyLayout, 150);
586
+ resizeTimer = later(applyLayout, 150);
528
587
  });
588
+ if (reducedQuery && reducedQuery.addEventListener) {
589
+ on(reducedQuery, "change", (e) => {
590
+ if (!destroyed) {
591
+ reduced = e.matches;
592
+ applyLayout();
593
+ }
594
+ });
595
+ }
529
596
  applyLayout();
530
- return {
597
+ const handle = {
531
598
  next: () => goTo(active + 1),
532
599
  prev: () => goTo(active - 1),
533
600
  goTo,
534
601
  setMode,
535
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
+ },
536
611
  getActiveIndex: () => active,
537
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(),
538
623
  destroy: teardown
539
624
  };
625
+ INSTANCES.set(root, teardown);
626
+ return handle;
540
627
  }
541
628
 
542
629
  // src/folder-gallery-react.js
543
630
  var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props, ref) {
544
- const _a = props, {
631
+ const {
545
632
  items,
546
633
  mode = "stack",
547
634
  peek = "hover",
@@ -550,6 +637,9 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
550
637
  onSelect,
551
638
  onActiveChange,
552
639
  onModeChange,
640
+ onPeekChange,
641
+ onFlingStart,
642
+ onFlingEnd,
553
643
  folderPath,
554
644
  loop,
555
645
  scrollNav,
@@ -557,29 +647,13 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
557
647
  defaultActiveIndex,
558
648
  label,
559
649
  className,
560
- style
561
- } = _a, rest = __objRest(_a, [
562
- "items",
563
- "mode",
564
- "peek",
565
- "drag",
566
- "contentRenderer",
567
- "onSelect",
568
- "onActiveChange",
569
- "onModeChange",
570
- "folderPath",
571
- "loop",
572
- "scrollNav",
573
- "reducedMotion",
574
- "defaultActiveIndex",
575
- "label",
576
- "className",
577
- "style"
578
- ]);
650
+ style,
651
+ ...rest
652
+ } = props;
579
653
  const rootRef = (0, import_react.useRef)(null);
580
654
  const handleRef = (0, import_react.useRef)(null);
581
655
  const callbacksRef = (0, import_react.useRef)({});
582
- callbacksRef.current = { onSelect, onActiveChange, onModeChange, contentRenderer };
656
+ callbacksRef.current = { onSelect, onActiveChange, onModeChange, onPeekChange, onFlingStart, onFlingEnd, contentRenderer };
583
657
  (0, import_react.useEffect)(() => {
584
658
  const root = rootRef.current;
585
659
  if (!root) return void 0;
@@ -609,11 +683,29 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
609
683
  const fn = callbacksRef.current.onModeChange;
610
684
  if (fn) fn(e.detail.mode);
611
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
+ };
612
698
  root.addEventListener("fg-activechange", onActive);
613
699
  root.addEventListener("fg-modechange", onModeEv);
700
+ root.addEventListener("fg-peekchange", onPeekEv);
701
+ root.addEventListener("fg-flingstart", onFlingStartEv);
702
+ root.addEventListener("fg-flingend", onFlingEndEv);
614
703
  return () => {
615
704
  root.removeEventListener("fg-activechange", onActive);
616
705
  root.removeEventListener("fg-modechange", onModeEv);
706
+ root.removeEventListener("fg-peekchange", onPeekEv);
707
+ root.removeEventListener("fg-flingstart", onFlingStartEv);
708
+ root.removeEventListener("fg-flingend", onFlingEndEv);
617
709
  handle.destroy();
618
710
  handleRef.current = null;
619
711
  };
@@ -632,10 +724,16 @@ var FolderGallery = (0, import_react.forwardRef)(function FolderGallery2(props,
632
724
  goTo: (i) => handleRef.current && handleRef.current.goTo(i),
633
725
  setMode: (m) => handleRef.current && handleRef.current.setMode(m),
634
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),
635
729
  getActiveIndex: () => handleRef.current ? handleRef.current.getActiveIndex() : -1,
636
- getMode: () => handleRef.current ? handleRef.current.getMode() : mode
637
- }), [mode]);
638
- 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 });
639
737
  });
640
738
  var folder_gallery_react_default = FolderGallery;
641
739
  // Annotate the CommonJS export names for ESM import in node: