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/README.md +27 -6
- package/dist/{chunk-Q2NKZMCC.js → chunk-RFBZ3WPG.js} +165 -76
- package/dist/chunk-RFBZ3WPG.js.map +1 -0
- package/dist/element.cjs +176 -59
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.ts +6 -0
- package/dist/element.js +19 -1
- package/dist/element.js.map +1 -1
- package/dist/folder-gallery.css +34 -24
- package/dist/index.cjs +165 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.js +3 -1
- package/dist/react.cjs +193 -95
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +3 -0
- package/dist/react.js +36 -27
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +187 -63
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.ts +18 -3
- package/dist/vue.js +30 -5
- package/dist/vue.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-Q2NKZMCC.js.map +0 -1
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
|
|
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
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
|
-
|
|
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
|
|
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 ||
|
|
117
|
-
const
|
|
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
|
|
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
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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",
|
|
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
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
const
|
|
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 *
|
|
312
|
+
const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;
|
|
264
313
|
const rowStartX = (sceneW - rowW) / 2;
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
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 *
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
562
|
-
|
|
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
|
-
|
|
638
|
-
|
|
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:
|