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/element.cjs CHANGED
@@ -1,22 +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
5
  var __export = (target, all) => {
21
6
  for (var name in all)
22
7
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -53,19 +38,27 @@ var DEFAULTS = {
53
38
  // 'auto' | 'off' | 'force'
54
39
  peek: "hover",
55
40
  // 'hover' | 'always' | 'off'
41
+ drag: "fling",
42
+ // 'fling' | 'off' - grab the active folder and throw it (stack mode)
56
43
  defaultActiveIndex: 0,
57
44
  label: "Folder gallery"
58
45
  };
59
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();
60
49
  var MODE_WIDTHS = { stack: 480, grid: 720, carousel: 720 };
61
50
  var SCROLL_THRESHOLD = 30;
62
51
  var SCROLL_COOLDOWN = { stack: 450, carousel: 300 };
63
52
  function hexToRgb(hex) {
64
- 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;
65
56
  return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
66
57
  }
67
58
  function folderColors(hex) {
68
- const [r, g, b] = hexToRgb(hex);
59
+ const rgb = hexToRgb(hex);
60
+ if (!rgb) return null;
61
+ const [r, g, b] = rgb;
69
62
  const dn = (v) => Math.max(v - 30, 0);
70
63
  const up = (v, o) => Math.min(v + o, 255);
71
64
  const fr = up(r, 35);
@@ -79,6 +72,34 @@ function folderColors(hex) {
79
72
  label: yiq >= 128 ? "#1c1c1a" : "#ffffff"
80
73
  };
81
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
+ }
82
103
  function defaultContentRenderer(card, item) {
83
104
  const wrap = document.createElement("div");
84
105
  wrap.className = "fg-content";
@@ -98,12 +119,17 @@ function defaultContentRenderer(card, item) {
98
119
  card.appendChild(wrap);
99
120
  }
100
121
  function createFolderGallery(root, options = {}) {
101
- if (!root || !root.nodeType) throw new Error("createFolderGallery: a root element is required");
102
- 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 };
103
128
  const items = Array.isArray(opts.items) ? opts.items : [];
104
129
  const n = items.length;
105
130
  const renderContent = typeof opts.contentRenderer === "function" ? opts.contentRenderer : defaultContentRenderer;
106
- 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);
107
133
  let mode = MODE_WIDTHS[opts.mode] ? opts.mode : "stack";
108
134
  let active = Math.min(Math.max(opts.defaultActiveIndex | 0, 0), Math.max(n - 1, 0));
109
135
  let scrollCooldown = false;
@@ -114,9 +140,29 @@ function createFolderGallery(root, options = {}) {
114
140
  cleanups.push(() => el.removeEventListener(ev, fn, o));
115
141
  };
116
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
+ };
117
162
  root.classList.add("fg-root");
118
163
  root.setAttribute("data-fg-mode", mode);
119
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");
120
166
  const scene = document.createElement("div");
121
167
  scene.className = "fg-scene";
122
168
  scene.setAttribute("role", "listbox");
@@ -131,26 +177,48 @@ function createFolderGallery(root, options = {}) {
131
177
  live.setAttribute("aria-atomic", "true");
132
178
  root.append(scene, dotsEl, live);
133
179
  function teardown() {
180
+ if (destroyed) return;
181
+ destroyed = true;
182
+ timers.forEach(clearTimeout);
183
+ timers.clear();
184
+ rafs.forEach(cancelAnimationFrame);
185
+ rafs.clear();
134
186
  cleanups.forEach((fn) => fn());
135
187
  cleanups.length = 0;
136
188
  root.replaceChildren();
137
189
  root.classList.remove("fg-root");
138
190
  root.removeAttribute("data-fg-mode");
139
191
  root.removeAttribute("data-fg-peek");
192
+ root.removeAttribute("data-fg-drag");
193
+ INSTANCES.delete(root);
140
194
  }
141
195
  if (n === 0) {
142
- return { next() {
143
- }, prev() {
144
- }, goTo() {
145
- }, setMode() {
146
- }, setPeek() {
147
- }, 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
+ };
148
215
  }
149
216
  function setCardTransform(card, { x, y, z, rx, ry, s, zIndex, opacity, isActive }) {
150
- card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;
151
- 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
+ }
152
221
  card.style.zIndex = String(zIndex);
153
- card.style.opacity = String(opacity);
154
222
  card.setAttribute("aria-selected", isActive ? "true" : "false");
155
223
  card.classList.toggle("is-active", isActive);
156
224
  }
@@ -160,13 +228,9 @@ function createFolderGallery(root, options = {}) {
160
228
  card.tabIndex = i === active ? 0 : -1;
161
229
  card.setAttribute("role", "option");
162
230
  card.setAttribute("aria-label", item.label || `Item ${i + 1}`);
163
- if (item.color) {
164
- const c = folderColors(item.color);
165
- card.style.setProperty("--fg-folder-bg", item.color);
166
- card.style.setProperty("--fg-front", c.front);
167
- card.style.setProperty("--fg-front-solid", c.frontSolid);
168
- card.style.setProperty("--fg-label-on-color", c.label);
169
- }
231
+ card.setAttribute("aria-setsize", String(n));
232
+ card.setAttribute("aria-posinset", String(i + 1));
233
+ card.setAttribute("data-fg-index", String(i));
170
234
  const svg = document.createElementNS(SVG_NS, "svg");
171
235
  svg.setAttribute("class", "fg-folder");
172
236
  svg.setAttribute("viewBox", "0 0 480 342");
@@ -174,7 +238,7 @@ function createFolderGallery(root, options = {}) {
174
238
  svg.setAttribute("aria-hidden", "true");
175
239
  const path = document.createElementNS(SVG_NS, "path");
176
240
  path.setAttribute("d", opts.folderPath);
177
- path.setAttribute("fill", item.color ? folderColors(item.color).back : "var(--fg-folder-bg)");
241
+ path.setAttribute("fill", "var(--fg-folder-bg)");
178
242
  svg.appendChild(path);
179
243
  card.appendChild(svg);
180
244
  renderContent(card, item, i);
@@ -197,6 +261,8 @@ function createFolderGallery(root, options = {}) {
197
261
  front.appendChild(label);
198
262
  }
199
263
  card.appendChild(front);
264
+ if (item.color) paintFolder(card, item.color);
265
+ if (item.gradient) paintGradient(card, item.gradient);
200
266
  scene.appendChild(card);
201
267
  return card;
202
268
  });
@@ -210,9 +276,13 @@ function createFolderGallery(root, options = {}) {
210
276
  dotsEl.appendChild(dot);
211
277
  return dot;
212
278
  });
279
+ const clearGridWidth = (card) => {
280
+ if (card.style.width) card.style.width = "";
281
+ };
213
282
  function layoutStack() {
214
283
  scene.style.height = "";
215
284
  cardEls.forEach((card, i) => {
285
+ clearGridWidth(card);
216
286
  const behind = (i - active + n) % n;
217
287
  if (behind === 0) {
218
288
  setCardTransform(card, { x: 0, y: 100, z: 20, rx: 0, ry: 0, s: 1, zIndex: n + 1, opacity: 1, isActive: true });
@@ -228,33 +298,31 @@ function createFolderGallery(root, options = {}) {
228
298
  }
229
299
  function layoutGrid() {
230
300
  const sceneW = scene.clientWidth || MODE_WIDTHS.grid;
231
- const cols = sceneW < 520 ? 2 : n <= 4 ? 2 : 3;
232
- const gap = 16;
233
- const cardW = sceneW;
234
- const cardH = sceneW * (2 / 3);
235
- const sc = (sceneW - (cols - 1) * gap) / (cols * cardW);
236
- const scaledW = cardW * sc;
237
- const scaledH = cardH * sc;
238
- 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);
239
307
  const rows = Math.ceil(n / cols);
240
308
  cardEls.forEach((card, i) => {
241
309
  const col = i % cols;
242
310
  const row = Math.floor(i / cols);
243
311
  const itemsInRow = Math.min(cols, n - row * cols);
244
- const rowW = itemsInRow * scaledW + (itemsInRow - 1) * gap;
312
+ const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;
245
313
  const rowStartX = (sceneW - rowW) / 2;
246
- const cellLeft = rowStartX + col * (scaledW + gap);
247
- const cellTop = row * (scaledH + tabScaled + gap) + tabScaled;
248
- const tx = cellLeft + scaledW / 2 - cardW / 2;
249
- const ty = cellTop + scaledH - cardH;
250
- 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 });
251
318
  });
252
- scene.style.height = rows * (scaledH + tabScaled + gap) + 20 + "px";
319
+ scene.style.height = rows * cellH + (rows - 1) * gap + "px";
253
320
  }
254
321
  function layoutCarousel() {
255
322
  const sceneW = scene.clientWidth || MODE_WIDTHS.carousel;
256
323
  const cardW = sceneW;
257
324
  const cardH = cardW * (2 / 3);
325
+ cardEls.forEach(clearGridWidth);
258
326
  const activeSc = 0.62;
259
327
  const activeW = cardW * activeSc;
260
328
  const sceneH = Math.max(cardH * activeSc + 40, 180);
@@ -289,29 +357,39 @@ function createFolderGallery(root, options = {}) {
289
357
  live.textContent = cur && cur.label ? `${active + 1} of ${n}: ${cur.label}` : `${active + 1} of ${n}`;
290
358
  }
291
359
  function goTo(i) {
292
- 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);
293
363
  if (next === active) return;
294
364
  active = next;
295
365
  applyLayout();
296
366
  emit("fg-activechange", { index: active, item: items[active] });
297
367
  }
298
368
  function select(i) {
369
+ if (destroyed) return;
299
370
  emit("fg-select", { index: i, item: items[i] });
300
371
  if (typeof opts.onSelect === "function") opts.onSelect(items[i], i);
301
372
  }
302
373
  function setMode(m) {
303
- if (m === mode || !MODE_WIDTHS[m]) return;
374
+ if (destroyed || m === mode || !MODE_WIDTHS[m]) return;
304
375
  mode = m;
305
376
  root.setAttribute("data-fg-mode", mode);
306
377
  applyLayout();
307
378
  emit("fg-modechange", { mode });
308
379
  }
309
380
  function setPeek(p) {
310
- if (!PEEK_MODES.has(p)) return;
381
+ if (destroyed || !PEEK_MODES.has(p) || p === root.getAttribute("data-fg-peek")) return;
311
382
  root.setAttribute("data-fg-peek", p);
383
+ emit("fg-peekchange", { peek: p });
312
384
  }
385
+ const dragEnabled = opts.drag !== "off";
386
+ let suppressClick = false;
313
387
  cardEls.forEach((card, i) => {
314
388
  on(card, "click", () => {
389
+ if (suppressClick) {
390
+ suppressClick = false;
391
+ return;
392
+ }
315
393
  i === active ? select(i) : goTo(i);
316
394
  });
317
395
  on(card, "keydown", (e) => {
@@ -377,7 +455,7 @@ function createFolderGallery(root, options = {}) {
377
455
  scrollAccum = 0;
378
456
  scrollCooldown = true;
379
457
  goTo(active + dir);
380
- setTimeout(() => {
458
+ later(() => {
381
459
  scrollCooldown = false;
382
460
  scrollAccum = 0;
383
461
  }, SCROLL_COOLDOWN[mode] || 300);
@@ -391,6 +469,7 @@ function createFolderGallery(root, options = {}) {
391
469
  touchStartY = e.touches[0].clientY;
392
470
  }, { passive: true });
393
471
  on(scene, "touchend", (e) => {
472
+ if (dragEnabled && mode === "stack") return;
394
473
  const dx = touchStartX - e.changedTouches[0].clientX;
395
474
  const dy = touchStartY - e.changedTouches[0].clientY;
396
475
  const ax = Math.abs(dx);
@@ -399,22 +478,152 @@ function createFolderGallery(root, options = {}) {
399
478
  const dir = ax > ay ? dx > 0 ? 1 : -1 : dy > 0 ? 1 : -1;
400
479
  goTo(active + dir);
401
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
+ }
402
583
  let resizeTimer;
403
584
  on(window, "resize", () => {
404
585
  clearTimeout(resizeTimer);
405
- resizeTimer = setTimeout(applyLayout, 150);
586
+ resizeTimer = later(applyLayout, 150);
406
587
  });
588
+ if (reducedQuery && reducedQuery.addEventListener) {
589
+ on(reducedQuery, "change", (e) => {
590
+ if (!destroyed) {
591
+ reduced = e.matches;
592
+ applyLayout();
593
+ }
594
+ });
595
+ }
407
596
  applyLayout();
408
- return {
597
+ const handle = {
409
598
  next: () => goTo(active + 1),
410
599
  prev: () => goTo(active - 1),
411
600
  goTo,
412
601
  setMode,
413
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
+ },
414
611
  getActiveIndex: () => active,
415
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(),
416
623
  destroy: teardown
417
624
  };
625
+ INSTANCES.set(root, teardown);
626
+ return handle;
418
627
  }
419
628
 
420
629
  // src/folder-gallery-element.js
@@ -423,7 +632,7 @@ var ElementBase = typeof HTMLElement !== "undefined" ? HTMLElement : class {
423
632
  };
424
633
  var FolderGalleryElement = class extends ElementBase {
425
634
  static get observedAttributes() {
426
- return ["mode", "peek", "loop", "scroll-nav", "reduced-motion", "default-active-index", "label", "folder-path"];
635
+ return ["mode", "peek", "drag", "loop", "scroll-nav", "reduced-motion", "default-active-index", "label", "folder-path"];
427
636
  }
428
637
  constructor() {
429
638
  super();
@@ -466,6 +675,7 @@ var FolderGalleryElement = class extends ElementBase {
466
675
  const opts = { items: this._items };
467
676
  if (this.hasAttribute("mode")) opts.mode = this.getAttribute("mode");
468
677
  if (this.hasAttribute("peek")) opts.peek = this.getAttribute("peek");
678
+ if (this.hasAttribute("drag")) opts.drag = this.getAttribute("drag");
469
679
  if (this.hasAttribute("loop")) opts.loop = asBool(this.getAttribute("loop"));
470
680
  if (this.hasAttribute("scroll-nav")) opts.scrollNav = asBool(this.getAttribute("scroll-nav"));
471
681
  if (this.hasAttribute("reduced-motion")) opts.reducedMotion = this.getAttribute("reduced-motion");
@@ -502,12 +712,30 @@ var FolderGalleryElement = class extends ElementBase {
502
712
  setPeek(p) {
503
713
  this.setAttribute("peek", p);
504
714
  }
715
+ setColor(i, hex) {
716
+ this._handle && this._handle.setColor(i, hex);
717
+ }
718
+ setGradient(i, gradient) {
719
+ this._handle && this._handle.setGradient(i, gradient);
720
+ }
505
721
  getActiveIndex() {
506
722
  return this._handle ? this._handle.getActiveIndex() : -1;
507
723
  }
508
724
  getMode() {
509
725
  return this._handle ? this._handle.getMode() : this.getAttribute("mode") || "stack";
510
726
  }
727
+ getPeek() {
728
+ return this._handle ? this._handle.getPeek() : this.getAttribute("peek") || "hover";
729
+ }
730
+ getColor(i) {
731
+ return this._handle ? this._handle.getColor(i) : void 0;
732
+ }
733
+ getGradient(i) {
734
+ return this._handle ? this._handle.getGradient(i) : void 0;
735
+ }
736
+ getItems() {
737
+ return this._handle ? this._handle.getItems() : [];
738
+ }
511
739
  };
512
740
  function defineFolderGallery(tag = "folder-gallery") {
513
741
  if (typeof customElements !== "undefined" && !customElements.get(tag)) {