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/README.md
CHANGED
|
@@ -9,9 +9,9 @@ A gallery where every item is a folder: stack them, grid them, spin them.
|
|
|
9
9
|
|
|
10
10
|
Started as a CSS 3D experiment; it now runs the [kirthi.studio](https://kirthi.studio) homepage. No framework, no dependencies.
|
|
11
11
|
|
|
12
|
-
Live demo: [kirthi.studio/demos/flipfolio](https://kirthi.studio/demos/flipfolio/)
|
|
12
|
+
Live demo: [kirthi.studio/demos/flipfolio](https://kirthi.studio/demos/flipfolio/), or edit it on [StackBlitz](https://stackblitz.com/github/kirthi-b/flipfolio/tree/master/examples/vanilla?file=main.js)
|
|
13
13
|
|
|
14
|
-
> v0.
|
|
14
|
+
> v0.3.0. New this release: a crisp grid view, runtime color and gradient theming, and drag hooks.
|
|
15
15
|
|
|
16
16
|

|
|
17
17
|
|
|
@@ -77,6 +77,9 @@ Folders hold your actual content. These are live project images, loaded with `it
|
|
|
77
77
|

|
|
78
78
|
One items array, three layouts. `setMode()` animates between them at runtime.
|
|
79
79
|
|
|
80
|
+

|
|
81
|
+
Grab the front folder and throw it. Stack mode, `drag: 'fling'`; a short drag springs back.
|
|
82
|
+
|
|
80
83
|

|
|
81
84
|
Interiors take images, markup, DOM nodes, or a `contentRenderer` for anything else.
|
|
82
85
|
|
|
@@ -90,29 +93,47 @@ Per-folder tab colors, tokens for radius and blur, and the silhouette itself is
|
|
|
90
93
|
|---|---|---|---|
|
|
91
94
|
| `items` | `Item[]` | `[]` | `{ label?, color?, src?, content?, decal?, ...data }` |
|
|
92
95
|
| `mode` | `'stack'\|'grid'\|'carousel'` | `'stack'` | 3 modes (a 4th "shelf" is planned) |
|
|
96
|
+
| `grid` | `{ columns?, gap? }` | auto | pin the grid's column count and/or gap (px); default is a width/count heuristic |
|
|
93
97
|
| `peek` | `'hover'\|'always'\|'off'` | `'hover'` | contents slide out of the folder |
|
|
94
98
|
| `drag` | `'fling'\|'off'` | `'fling'` | grab the active folder and throw it (stack mode) |
|
|
95
99
|
| `contentRenderer` | `(card, item, i) => void` | built-in | fills the folder interior with whatever you render |
|
|
96
100
|
| `onSelect` | `(item, i) => void` | (none) | fired on click/Enter of the active folder (no built-in navigation) |
|
|
97
|
-
| `folderPath` | `string` | manila default | SVG path `d` (viewBox `0 0 480 342`) |
|
|
101
|
+
| `folderPath` | `string` | manila default | SVG path `d` (viewBox `0 0 480 342`), or a `FOLDER_PATHS` preset |
|
|
98
102
|
| `loop` | `boolean` | `true` | wrap-around |
|
|
99
103
|
| `scrollNav` | `boolean` | `true` | wheel/trackpad cycles the stack/carousel |
|
|
100
104
|
| `reducedMotion` | `'auto'\|'off'\|'force'` | `'auto'` | drops the 3D tilt when reduced |
|
|
101
105
|
| `defaultActiveIndex` | `number` | `0` | |
|
|
102
106
|
| `label` | `string` | `'Folder gallery'` | `aria-label` for the listbox |
|
|
103
107
|
|
|
104
|
-
Handle: `next()`, `prev()`, `goTo(i)`, `setMode(m)`, `setPeek(p)`, `getActiveIndex()`, `getMode()`, `destroy()`.
|
|
108
|
+
Handle: `next()`, `prev()`, `goTo(i)`, `setMode(m)`, `setPeek(p)`, `setColor(i, hex)`, `setGradient(i, gradient)`, `getActiveIndex()`, `getMode()`, `getPeek()`, `getColor(i)`, `getGradient(i)`, `getItems()`, `destroy()`.
|
|
105
109
|
|
|
106
|
-
Events (CustomEvent on the root, bubbling): `fg-select`, `fg-activechange`, `fg-modechange`, each with `event.detail`.
|
|
110
|
+
Events (CustomEvent on the root, bubbling): `fg-select`, `fg-activechange`, `fg-modechange`, `fg-peekchange` (detail `{ peek }`), `fg-flingstart` and `fg-flingend` (detail `{ index, direction }`, direction `'next' | 'prev'`), each with `event.detail`. React props: `onSelect`, `onActiveChange`, `onModeChange`, `onPeekChange`, `onFlingStart`, `onFlingEnd`. Vue emits: `@select`, `@active-change`, `@mode-change`, `@peek-change`, `@fling-start`, `@fling-end`.
|
|
107
111
|
|
|
108
112
|
## Content
|
|
109
113
|
Each folder renders what you give it: `item.src` for an image, `item.content` for an HTML string or a DOM node, or a `contentRenderer(card, item, index)` for anything richer. Set `item.decal` to an image URL and the photo prints on the folder's front panel; the back and tab keep their color. Contents slide out of the folder on hover; the `peek` option makes that always-on or turns it off.
|
|
110
114
|
|
|
115
|
+
## Silhouette
|
|
116
|
+
`folderPath` takes any SVG path `d` in the `0 0 480 342` viewBox. Three presets ship as `FOLDER_PATHS`: `left` (the default tab), `right` (tab mirrored), and `tray` (no tab). Pass one instead of a raw string:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import { createFolderGallery, FOLDER_PATHS } from 'flipfolio';
|
|
120
|
+
createFolderGallery(el, { items, folderPath: FOLDER_PATHS.tray });
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Runtime theming
|
|
124
|
+
Each folder's palette derives from `item.color`; `setColor(index, hex)` re-derives the whole thing (SVG back, frosted + solid fronts, auto-contrast label) at runtime, no rebuild. `item.gradient` paints a CSS gradient across the front over the frosted surface, and `setGradient(index, gradient)` sets or clears it live (pass a falsy value to clear). Together they drive swatch pickers and theme switchers:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
gallery.setColor(0, '#3d2e42'); // recolor one folder
|
|
128
|
+
gallery.setGradient(0, 'linear-gradient(135deg, #236363, #6FCDCD, #8B7FB8)');
|
|
129
|
+
gallery.setGradient(0, null); // back to the flat front
|
|
130
|
+
```
|
|
131
|
+
|
|
111
132
|
## Accessibility
|
|
112
133
|
`role="listbox"`/`option`, roving tabindex, arrow-key + Home/End navigation, `aria-live` position announcements, `:focus-visible` rings, and a `prefers-reduced-motion` fallback that drops the 3D tilt.
|
|
113
134
|
|
|
114
135
|
## Theming
|
|
115
|
-
Override the CSS custom properties on `.fg-root`: `--fg-folder-bg`, `--fg-radius`, `--fg-perspective`, `--fg-ease`, `--fg-front`, `--fg-front-solid`, `--fg-active-blur`, `--fg-label`, `--fg-dot`, `--fg-dot-active`. Light is the default; dark applies automatically under `prefers-color-scheme: dark`, and `data-fg-theme="light"` or `"dark"` on `.fg-root` overrides either way.
|
|
136
|
+
Override the CSS custom properties on `.fg-root`: `--fg-folder-bg`, `--fg-radius`, `--fg-perspective`, `--fg-ease`, `--fg-front`, `--fg-front-solid`, `--fg-active-blur`, `--fg-label`, `--fg-dot`, `--fg-dot-active`, `--fg-scene-height`, `--fg-gap`, `--fg-folder-shadow`, `--fg-gradient-opacity`, `--fg-label-size`, `--fg-label-weight`, `--fg-label-tracking`. Light is the default; dark applies automatically under `prefers-color-scheme: dark`, and `data-fg-theme="light"` or `"dark"` on `.fg-root` overrides either way.
|
|
116
137
|
|
|
117
138
|
## Limits
|
|
118
139
|
Client-rendered only: folders paint after JS runs, there is no SSR of the layout itself. Every folder is a live 3D layer, so the sweet spot is 12 items or fewer, not hundreds. Uses `aspect-ratio` and `backdrop-filter`: evergreen browsers only, no IE fallback.
|
|
@@ -1,35 +1,11 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __spreadValues = (a, b) => {
|
|
7
|
-
for (var prop in b || (b = {}))
|
|
8
|
-
if (__hasOwnProp.call(b, prop))
|
|
9
|
-
__defNormalProp(a, prop, b[prop]);
|
|
10
|
-
if (__getOwnPropSymbols)
|
|
11
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
-
if (__propIsEnum.call(b, prop))
|
|
13
|
-
__defNormalProp(a, prop, b[prop]);
|
|
14
|
-
}
|
|
15
|
-
return a;
|
|
16
|
-
};
|
|
17
|
-
var __objRest = (source, exclude) => {
|
|
18
|
-
var target = {};
|
|
19
|
-
for (var prop in source)
|
|
20
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
21
|
-
target[prop] = source[prop];
|
|
22
|
-
if (source != null && __getOwnPropSymbols)
|
|
23
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
24
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
25
|
-
target[prop] = source[prop];
|
|
26
|
-
}
|
|
27
|
-
return target;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
1
|
// src/folder-gallery.js
|
|
31
2
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
32
3
|
var DEFAULT_FOLDER_PATH = "M 0 22 C 0 10 10 0 22 0 L 155 0 C 168 0 172 6 176 14 C 180 24 188 32 204 32 L 458 32 C 470 32 480 42 480 54 L 480 320 C 480 332 470 342 458 342 L 22 342 C 10 342 0 332 0 320 Z";
|
|
4
|
+
var FOLDER_PATHS = {
|
|
5
|
+
left: DEFAULT_FOLDER_PATH,
|
|
6
|
+
right: "M 480 22 C 480 10 470 0 458 0 L 325 0 C 312 0 308 6 304 14 C 300 24 292 32 276 32 L 22 32 C 10 32 0 42 0 54 L 0 320 C 0 332 10 342 22 342 L 458 342 C 470 342 480 332 480 320 Z",
|
|
7
|
+
tray: "M 0 22 C 0 10 10 0 22 0 L 458 0 C 470 0 480 10 480 22 L 480 320 C 480 332 470 342 458 342 L 22 342 C 10 342 0 332 0 320 Z"
|
|
8
|
+
};
|
|
33
9
|
var DEFAULTS = {
|
|
34
10
|
mode: "stack",
|
|
35
11
|
// 'stack' | 'grid' | 'carousel'
|
|
@@ -47,15 +23,20 @@ var DEFAULTS = {
|
|
|
47
23
|
};
|
|
48
24
|
var PEEK_MODES = /* @__PURE__ */ new Set(["hover", "always", "off"]);
|
|
49
25
|
var DRAG_MODES = /* @__PURE__ */ new Set(["fling", "off"]);
|
|
26
|
+
var INSTANCES = /* @__PURE__ */ new WeakMap();
|
|
50
27
|
var MODE_WIDTHS = { stack: 480, grid: 720, carousel: 720 };
|
|
51
28
|
var SCROLL_THRESHOLD = 30;
|
|
52
29
|
var SCROLL_COOLDOWN = { stack: 450, carousel: 300 };
|
|
53
30
|
function hexToRgb(hex) {
|
|
54
|
-
|
|
31
|
+
let h = String(hex).trim().replace(/^#/, "");
|
|
32
|
+
if (h.length === 3) h = h.replace(/./g, (c) => c + c);
|
|
33
|
+
if (!/^[0-9a-f]{6}$/i.test(h)) return null;
|
|
55
34
|
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
|
|
56
35
|
}
|
|
57
36
|
function folderColors(hex) {
|
|
58
|
-
const
|
|
37
|
+
const rgb = hexToRgb(hex);
|
|
38
|
+
if (!rgb) return null;
|
|
39
|
+
const [r, g, b] = rgb;
|
|
59
40
|
const dn = (v) => Math.max(v - 30, 0);
|
|
60
41
|
const up = (v, o) => Math.min(v + o, 255);
|
|
61
42
|
const fr = up(r, 35);
|
|
@@ -69,6 +50,34 @@ function folderColors(hex) {
|
|
|
69
50
|
label: yiq >= 128 ? "#1c1c1a" : "#ffffff"
|
|
70
51
|
};
|
|
71
52
|
}
|
|
53
|
+
function paintFolder(card, hex) {
|
|
54
|
+
const c = folderColors(hex);
|
|
55
|
+
if (!c) return;
|
|
56
|
+
card.style.setProperty("--fg-folder-bg", hex);
|
|
57
|
+
card.style.setProperty("--fg-front", c.front);
|
|
58
|
+
card.style.setProperty("--fg-front-solid", c.frontSolid);
|
|
59
|
+
card.style.setProperty("--fg-label-on-color", c.label);
|
|
60
|
+
const path = card.querySelector(".fg-folder path");
|
|
61
|
+
if (path) path.setAttribute("fill", c.back);
|
|
62
|
+
}
|
|
63
|
+
function paintGradient(card, gradient) {
|
|
64
|
+
const front = card.querySelector(".fg-front");
|
|
65
|
+
if (!front) return;
|
|
66
|
+
let layer = front.querySelector(".fg-gradient");
|
|
67
|
+
if (!gradient) {
|
|
68
|
+
if (layer) layer.remove();
|
|
69
|
+
card.classList.remove("fg-card--gradient");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!layer) {
|
|
73
|
+
layer = document.createElement("div");
|
|
74
|
+
layer.className = "fg-gradient";
|
|
75
|
+
layer.setAttribute("aria-hidden", "true");
|
|
76
|
+
front.insertBefore(layer, front.firstChild);
|
|
77
|
+
}
|
|
78
|
+
layer.style.background = gradient;
|
|
79
|
+
card.classList.add("fg-card--gradient");
|
|
80
|
+
}
|
|
72
81
|
function defaultContentRenderer(card, item) {
|
|
73
82
|
const wrap = document.createElement("div");
|
|
74
83
|
wrap.className = "fg-content";
|
|
@@ -88,12 +97,17 @@ function defaultContentRenderer(card, item) {
|
|
|
88
97
|
card.appendChild(wrap);
|
|
89
98
|
}
|
|
90
99
|
function createFolderGallery(root, options = {}) {
|
|
91
|
-
if (!root ||
|
|
92
|
-
const
|
|
100
|
+
if (!root || root.nodeType !== 1) throw new Error("createFolderGallery: a root element is required");
|
|
101
|
+
const prior = INSTANCES.get(root);
|
|
102
|
+
if (prior) prior();
|
|
103
|
+
const provided = {};
|
|
104
|
+
for (const k in options) if (options[k] !== void 0) provided[k] = options[k];
|
|
105
|
+
const opts = { ...DEFAULTS, ...provided };
|
|
93
106
|
const items = Array.isArray(opts.items) ? opts.items : [];
|
|
94
107
|
const n = items.length;
|
|
95
108
|
const renderContent = typeof opts.contentRenderer === "function" ? opts.contentRenderer : defaultContentRenderer;
|
|
96
|
-
const
|
|
109
|
+
const reducedQuery = opts.reducedMotion === "auto" && typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
110
|
+
let reduced = opts.reducedMotion === "force" || (reducedQuery ? reducedQuery.matches : false);
|
|
97
111
|
let mode = MODE_WIDTHS[opts.mode] ? opts.mode : "stack";
|
|
98
112
|
let active = Math.min(Math.max(opts.defaultActiveIndex | 0, 0), Math.max(n - 1, 0));
|
|
99
113
|
let scrollCooldown = false;
|
|
@@ -104,6 +118,25 @@ function createFolderGallery(root, options = {}) {
|
|
|
104
118
|
cleanups.push(() => el.removeEventListener(ev, fn, o));
|
|
105
119
|
};
|
|
106
120
|
const emit = (name, detail) => root.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
|
|
121
|
+
let destroyed = false;
|
|
122
|
+
const timers = /* @__PURE__ */ new Set();
|
|
123
|
+
const rafs = /* @__PURE__ */ new Set();
|
|
124
|
+
const later = (fn, ms) => {
|
|
125
|
+
const id = setTimeout(() => {
|
|
126
|
+
timers.delete(id);
|
|
127
|
+
fn();
|
|
128
|
+
}, ms);
|
|
129
|
+
timers.add(id);
|
|
130
|
+
return id;
|
|
131
|
+
};
|
|
132
|
+
const frame = (fn) => {
|
|
133
|
+
const id = requestAnimationFrame(() => {
|
|
134
|
+
rafs.delete(id);
|
|
135
|
+
fn();
|
|
136
|
+
});
|
|
137
|
+
rafs.add(id);
|
|
138
|
+
return id;
|
|
139
|
+
};
|
|
107
140
|
root.classList.add("fg-root");
|
|
108
141
|
root.setAttribute("data-fg-mode", mode);
|
|
109
142
|
root.setAttribute("data-fg-peek", PEEK_MODES.has(opts.peek) ? opts.peek : "hover");
|
|
@@ -122,6 +155,12 @@ function createFolderGallery(root, options = {}) {
|
|
|
122
155
|
live.setAttribute("aria-atomic", "true");
|
|
123
156
|
root.append(scene, dotsEl, live);
|
|
124
157
|
function teardown() {
|
|
158
|
+
if (destroyed) return;
|
|
159
|
+
destroyed = true;
|
|
160
|
+
timers.forEach(clearTimeout);
|
|
161
|
+
timers.clear();
|
|
162
|
+
rafs.forEach(cancelAnimationFrame);
|
|
163
|
+
rafs.clear();
|
|
125
164
|
cleanups.forEach((fn) => fn());
|
|
126
165
|
cleanups.length = 0;
|
|
127
166
|
root.replaceChildren();
|
|
@@ -129,21 +168,34 @@ function createFolderGallery(root, options = {}) {
|
|
|
129
168
|
root.removeAttribute("data-fg-mode");
|
|
130
169
|
root.removeAttribute("data-fg-peek");
|
|
131
170
|
root.removeAttribute("data-fg-drag");
|
|
171
|
+
INSTANCES.delete(root);
|
|
132
172
|
}
|
|
133
173
|
if (n === 0) {
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
174
|
+
const noop = () => {
|
|
175
|
+
};
|
|
176
|
+
INSTANCES.set(root, teardown);
|
|
177
|
+
return {
|
|
178
|
+
next: noop,
|
|
179
|
+
prev: noop,
|
|
180
|
+
goTo: noop,
|
|
181
|
+
setMode: noop,
|
|
182
|
+
setPeek: noop,
|
|
183
|
+
setColor: noop,
|
|
184
|
+
setGradient: noop,
|
|
185
|
+
getActiveIndex: () => -1,
|
|
186
|
+
getMode: () => mode,
|
|
187
|
+
getPeek: () => root.getAttribute("data-fg-peek"),
|
|
188
|
+
getColor: () => void 0,
|
|
189
|
+
getGradient: () => void 0,
|
|
190
|
+
getItems: () => [],
|
|
191
|
+
destroy: teardown
|
|
192
|
+
};
|
|
140
193
|
}
|
|
141
194
|
function setCardTransform(card, { x, y, z, rx, ry, s, zIndex, opacity, isActive }) {
|
|
142
195
|
if (!card.classList.contains("fg-card--flung")) {
|
|
143
196
|
card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;
|
|
144
197
|
card.style.opacity = String(opacity);
|
|
145
198
|
}
|
|
146
|
-
card.style.transformOrigin = "center bottom";
|
|
147
199
|
card.style.zIndex = String(zIndex);
|
|
148
200
|
card.setAttribute("aria-selected", isActive ? "true" : "false");
|
|
149
201
|
card.classList.toggle("is-active", isActive);
|
|
@@ -154,13 +206,9 @@ function createFolderGallery(root, options = {}) {
|
|
|
154
206
|
card.tabIndex = i === active ? 0 : -1;
|
|
155
207
|
card.setAttribute("role", "option");
|
|
156
208
|
card.setAttribute("aria-label", item.label || `Item ${i + 1}`);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
card.style.setProperty("--fg-front", c.front);
|
|
161
|
-
card.style.setProperty("--fg-front-solid", c.frontSolid);
|
|
162
|
-
card.style.setProperty("--fg-label-on-color", c.label);
|
|
163
|
-
}
|
|
209
|
+
card.setAttribute("aria-setsize", String(n));
|
|
210
|
+
card.setAttribute("aria-posinset", String(i + 1));
|
|
211
|
+
card.setAttribute("data-fg-index", String(i));
|
|
164
212
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
165
213
|
svg.setAttribute("class", "fg-folder");
|
|
166
214
|
svg.setAttribute("viewBox", "0 0 480 342");
|
|
@@ -168,7 +216,7 @@ function createFolderGallery(root, options = {}) {
|
|
|
168
216
|
svg.setAttribute("aria-hidden", "true");
|
|
169
217
|
const path = document.createElementNS(SVG_NS, "path");
|
|
170
218
|
path.setAttribute("d", opts.folderPath);
|
|
171
|
-
path.setAttribute("fill",
|
|
219
|
+
path.setAttribute("fill", "var(--fg-folder-bg)");
|
|
172
220
|
svg.appendChild(path);
|
|
173
221
|
card.appendChild(svg);
|
|
174
222
|
renderContent(card, item, i);
|
|
@@ -191,6 +239,8 @@ function createFolderGallery(root, options = {}) {
|
|
|
191
239
|
front.appendChild(label);
|
|
192
240
|
}
|
|
193
241
|
card.appendChild(front);
|
|
242
|
+
if (item.color) paintFolder(card, item.color);
|
|
243
|
+
if (item.gradient) paintGradient(card, item.gradient);
|
|
194
244
|
scene.appendChild(card);
|
|
195
245
|
return card;
|
|
196
246
|
});
|
|
@@ -204,9 +254,13 @@ function createFolderGallery(root, options = {}) {
|
|
|
204
254
|
dotsEl.appendChild(dot);
|
|
205
255
|
return dot;
|
|
206
256
|
});
|
|
257
|
+
const clearGridWidth = (card) => {
|
|
258
|
+
if (card.style.width) card.style.width = "";
|
|
259
|
+
};
|
|
207
260
|
function layoutStack() {
|
|
208
261
|
scene.style.height = "";
|
|
209
262
|
cardEls.forEach((card, i) => {
|
|
263
|
+
clearGridWidth(card);
|
|
210
264
|
const behind = (i - active + n) % n;
|
|
211
265
|
if (behind === 0) {
|
|
212
266
|
setCardTransform(card, { x: 0, y: 100, z: 20, rx: 0, ry: 0, s: 1, zIndex: n + 1, opacity: 1, isActive: true });
|
|
@@ -222,33 +276,31 @@ function createFolderGallery(root, options = {}) {
|
|
|
222
276
|
}
|
|
223
277
|
function layoutGrid() {
|
|
224
278
|
const sceneW = scene.clientWidth || MODE_WIDTHS.grid;
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const scaledH = cardH * sc;
|
|
232
|
-
const tabScaled = 22 * sc;
|
|
279
|
+
const gap = Number.isFinite(opts.grid && opts.grid.gap) ? opts.grid.gap : 16;
|
|
280
|
+
const cols = Math.max(1, Math.round(
|
|
281
|
+
opts.grid && opts.grid.columns ? opts.grid.columns : sceneW < 520 ? 2 : n <= 4 ? 2 : 3
|
|
282
|
+
));
|
|
283
|
+
const cellW = (sceneW - (cols - 1) * gap) / cols;
|
|
284
|
+
const cellH = cellW * (2 / 3);
|
|
233
285
|
const rows = Math.ceil(n / cols);
|
|
234
286
|
cardEls.forEach((card, i) => {
|
|
235
287
|
const col = i % cols;
|
|
236
288
|
const row = Math.floor(i / cols);
|
|
237
289
|
const itemsInRow = Math.min(cols, n - row * cols);
|
|
238
|
-
const rowW = itemsInRow *
|
|
290
|
+
const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;
|
|
239
291
|
const rowStartX = (sceneW - rowW) / 2;
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
setCardTransform(card, { x: tx, y: ty, z: 0, rx: 0, ry: 0, s: sc, zIndex: i === active ? 2 : 1, opacity: 1, isActive: i === active });
|
|
292
|
+
const x = rowStartX + col * (cellW + gap);
|
|
293
|
+
const y = row * (cellH + gap);
|
|
294
|
+
card.style.width = cellW + "px";
|
|
295
|
+
setCardTransform(card, { x, y, z: 0, rx: 0, ry: 0, s: 1, zIndex: i === active ? 2 : 1, opacity: 1, isActive: i === active });
|
|
245
296
|
});
|
|
246
|
-
scene.style.height = rows *
|
|
297
|
+
scene.style.height = rows * cellH + (rows - 1) * gap + "px";
|
|
247
298
|
}
|
|
248
299
|
function layoutCarousel() {
|
|
249
300
|
const sceneW = scene.clientWidth || MODE_WIDTHS.carousel;
|
|
250
301
|
const cardW = sceneW;
|
|
251
302
|
const cardH = cardW * (2 / 3);
|
|
303
|
+
cardEls.forEach(clearGridWidth);
|
|
252
304
|
const activeSc = 0.62;
|
|
253
305
|
const activeW = cardW * activeSc;
|
|
254
306
|
const sceneH = Math.max(cardH * activeSc + 40, 180);
|
|
@@ -283,26 +335,30 @@ function createFolderGallery(root, options = {}) {
|
|
|
283
335
|
live.textContent = cur && cur.label ? `${active + 1} of ${n}: ${cur.label}` : `${active + 1} of ${n}`;
|
|
284
336
|
}
|
|
285
337
|
function goTo(i) {
|
|
286
|
-
|
|
338
|
+
if (destroyed) return;
|
|
339
|
+
const t = Number.isFinite(+i) ? Math.trunc(+i) : active;
|
|
340
|
+
const next = opts.loop ? (t % n + n) % n : Math.min(Math.max(t, 0), n - 1);
|
|
287
341
|
if (next === active) return;
|
|
288
342
|
active = next;
|
|
289
343
|
applyLayout();
|
|
290
344
|
emit("fg-activechange", { index: active, item: items[active] });
|
|
291
345
|
}
|
|
292
346
|
function select(i) {
|
|
347
|
+
if (destroyed) return;
|
|
293
348
|
emit("fg-select", { index: i, item: items[i] });
|
|
294
349
|
if (typeof opts.onSelect === "function") opts.onSelect(items[i], i);
|
|
295
350
|
}
|
|
296
351
|
function setMode(m) {
|
|
297
|
-
if (m === mode || !MODE_WIDTHS[m]) return;
|
|
352
|
+
if (destroyed || m === mode || !MODE_WIDTHS[m]) return;
|
|
298
353
|
mode = m;
|
|
299
354
|
root.setAttribute("data-fg-mode", mode);
|
|
300
355
|
applyLayout();
|
|
301
356
|
emit("fg-modechange", { mode });
|
|
302
357
|
}
|
|
303
358
|
function setPeek(p) {
|
|
304
|
-
if (!PEEK_MODES.has(p)) return;
|
|
359
|
+
if (destroyed || !PEEK_MODES.has(p) || p === root.getAttribute("data-fg-peek")) return;
|
|
305
360
|
root.setAttribute("data-fg-peek", p);
|
|
361
|
+
emit("fg-peekchange", { peek: p });
|
|
306
362
|
}
|
|
307
363
|
const dragEnabled = opts.drag !== "off";
|
|
308
364
|
let suppressClick = false;
|
|
@@ -377,7 +433,7 @@ function createFolderGallery(root, options = {}) {
|
|
|
377
433
|
scrollAccum = 0;
|
|
378
434
|
scrollCooldown = true;
|
|
379
435
|
goTo(active + dir);
|
|
380
|
-
|
|
436
|
+
later(() => {
|
|
381
437
|
scrollCooldown = false;
|
|
382
438
|
scrollAccum = 0;
|
|
383
439
|
}, SCROLL_COOLDOWN[mode] || 300);
|
|
@@ -466,8 +522,11 @@ function createFolderGallery(root, options = {}) {
|
|
|
466
522
|
card.style.transform = dragBase;
|
|
467
523
|
return;
|
|
468
524
|
}
|
|
525
|
+
const semantic = dir > 0 ? "next" : "prev";
|
|
526
|
+
emit("fg-flingstart", { index: active, direction: semantic });
|
|
469
527
|
if (reduced) {
|
|
470
528
|
goTo(target);
|
|
529
|
+
emit("fg-flingend", { index: target, direction: semantic });
|
|
471
530
|
return;
|
|
472
531
|
}
|
|
473
532
|
const offX = horizontal ? Math.sign(dragDX) * Math.max(window.innerWidth * 0.7, 480) : dragDX * 3;
|
|
@@ -476,7 +535,7 @@ function createFolderGallery(root, options = {}) {
|
|
|
476
535
|
card.style.transform = `${dragBase} translate3d(${offX}px, ${offY}px, 40px) rotate(${Math.sign(dragDX || 1) * 18}deg)`;
|
|
477
536
|
card.style.opacity = "0";
|
|
478
537
|
goTo(target);
|
|
479
|
-
|
|
538
|
+
later(() => {
|
|
480
539
|
card.classList.add("fg-card--snap");
|
|
481
540
|
card.classList.remove("fg-card--flung");
|
|
482
541
|
applyLayout();
|
|
@@ -484,12 +543,15 @@ function createFolderGallery(root, options = {}) {
|
|
|
484
543
|
const slotOpacity = card.style.opacity;
|
|
485
544
|
card.style.transform = `${slot} translate3d(0, -90px, 0)`;
|
|
486
545
|
card.style.opacity = "0";
|
|
487
|
-
|
|
546
|
+
frame(() => frame(() => {
|
|
488
547
|
card.classList.remove("fg-card--snap");
|
|
489
548
|
card.classList.add("fg-card--entering");
|
|
490
549
|
card.style.transform = slot;
|
|
491
550
|
card.style.opacity = slotOpacity;
|
|
492
|
-
|
|
551
|
+
later(() => {
|
|
552
|
+
card.classList.remove("fg-card--entering");
|
|
553
|
+
emit("fg-flingend", { index: target, direction: semantic });
|
|
554
|
+
}, 700);
|
|
493
555
|
}));
|
|
494
556
|
}, 400);
|
|
495
557
|
};
|
|
@@ -499,26 +561,53 @@ function createFolderGallery(root, options = {}) {
|
|
|
499
561
|
let resizeTimer;
|
|
500
562
|
on(window, "resize", () => {
|
|
501
563
|
clearTimeout(resizeTimer);
|
|
502
|
-
resizeTimer =
|
|
564
|
+
resizeTimer = later(applyLayout, 150);
|
|
503
565
|
});
|
|
566
|
+
if (reducedQuery && reducedQuery.addEventListener) {
|
|
567
|
+
on(reducedQuery, "change", (e) => {
|
|
568
|
+
if (!destroyed) {
|
|
569
|
+
reduced = e.matches;
|
|
570
|
+
applyLayout();
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
504
574
|
applyLayout();
|
|
505
|
-
|
|
575
|
+
const handle = {
|
|
506
576
|
next: () => goTo(active + 1),
|
|
507
577
|
prev: () => goTo(active - 1),
|
|
508
578
|
goTo,
|
|
509
579
|
setMode,
|
|
510
580
|
setPeek,
|
|
581
|
+
setColor: (i, hex) => {
|
|
582
|
+
const card = cardEls[i];
|
|
583
|
+
if (card && !destroyed) paintFolder(card, hex);
|
|
584
|
+
},
|
|
585
|
+
setGradient: (i, gradient) => {
|
|
586
|
+
const card = cardEls[i];
|
|
587
|
+
if (card && !destroyed) paintGradient(card, gradient);
|
|
588
|
+
},
|
|
511
589
|
getActiveIndex: () => active,
|
|
512
590
|
getMode: () => mode,
|
|
591
|
+
getPeek: () => root.getAttribute("data-fg-peek"),
|
|
592
|
+
getColor: (i) => {
|
|
593
|
+
const card = cardEls[i];
|
|
594
|
+
return card ? card.style.getPropertyValue("--fg-folder-bg") || void 0 : void 0;
|
|
595
|
+
},
|
|
596
|
+
getGradient: (i) => {
|
|
597
|
+
const layer = cardEls[i] && cardEls[i].querySelector(".fg-gradient");
|
|
598
|
+
return layer ? layer.style.background || void 0 : void 0;
|
|
599
|
+
},
|
|
600
|
+
getItems: () => items.slice(),
|
|
513
601
|
destroy: teardown
|
|
514
602
|
};
|
|
603
|
+
INSTANCES.set(root, teardown);
|
|
604
|
+
return handle;
|
|
515
605
|
}
|
|
516
606
|
var folder_gallery_default = createFolderGallery;
|
|
517
607
|
|
|
518
608
|
export {
|
|
519
|
-
|
|
520
|
-
__objRest,
|
|
609
|
+
FOLDER_PATHS,
|
|
521
610
|
createFolderGallery,
|
|
522
611
|
folder_gallery_default
|
|
523
612
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
613
|
+
//# sourceMappingURL=chunk-RFBZ3WPG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/folder-gallery.js"],"sourcesContent":["/* ============================================================================\n * flipfolio - a framework-agnostic 3D folder gallery (core)\n *\n * import { createFolderGallery } from 'flipfolio';\n * import 'flipfolio/styles.css';\n *\n * const gallery = createFolderGallery(rootEl, {\n * items: [{ label, color, src }, ...], // or { content: Node | html }\n * mode: 'stack', // 'stack' | 'grid' | 'carousel'\n * onSelect: (item, index) => { ... },\n * });\n * gallery.next(); gallery.setMode('grid'); gallery.destroy();\n *\n * Ported from a hand-built portfolio engine. No framework, no dependencies.\n * Owns its own DOM and removes every listener on destroy().\n * ========================================================================== */\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/* Default manila-folder-with-tab silhouette (viewBox 0 0 480 342). */\nconst DEFAULT_FOLDER_PATH =\n 'M 0 22 C 0 10 10 0 22 0 L 155 0 C 168 0 172 6 176 14 C 180 24 188 32 204 32 ' +\n 'L 458 32 C 470 32 480 42 480 54 L 480 320 C 480 332 470 342 458 342 ' +\n 'L 22 342 C 10 342 0 332 0 320 Z';\n\n/* A right-side tab is the default mirrored across the viewBox midline; a tabless\n * tray drops the tab notch entirely. Consumers pass one as `folderPath` instead\n * of hand-authoring an SVG path. The default (left tab) stays FOLDER_PATHS.left. */\nexport const FOLDER_PATHS = {\n left: DEFAULT_FOLDER_PATH,\n right:\n 'M 480 22 C 480 10 470 0 458 0 L 325 0 C 312 0 308 6 304 14 C 300 24 292 32 276 32 ' +\n 'L 22 32 C 10 32 0 42 0 54 L 0 320 C 0 332 10 342 22 342 ' +\n 'L 458 342 C 470 342 480 332 480 320 Z',\n tray:\n 'M 0 22 C 0 10 10 0 22 0 L 458 0 C 470 0 480 10 480 22 L 480 320 ' +\n 'C 480 332 470 342 458 342 L 22 342 C 10 342 0 332 0 320 Z',\n};\n\nconst DEFAULTS = {\n mode: 'stack', // 'stack' | 'grid' | 'carousel'\n folderPath: DEFAULT_FOLDER_PATH,\n loop: true,\n scrollNav: true,\n reducedMotion: 'auto', // 'auto' | 'off' | 'force'\n peek: 'hover', // 'hover' | 'always' | 'off'\n drag: 'fling', // 'fling' | 'off' - grab the active folder and throw it (stack mode)\n defaultActiveIndex: 0,\n label: 'Folder gallery',\n};\n\nconst PEEK_MODES = new Set(['hover', 'always', 'off']);\nconst DRAG_MODES = new Set(['fling', 'off']);\n\n/* Track the live instance per root so re-initializing the same element tears the\n * previous one down first (its window/document listeners are otherwise\n * unreachable and would leak, plus its DOM would be duplicated). */\nconst INSTANCES = new WeakMap();\n\nconst MODE_WIDTHS = { stack: 480, grid: 720, carousel: 720 };\nconst SCROLL_THRESHOLD = 30;\nconst SCROLL_COOLDOWN = { stack: 450, carousel: 300 };\n\n/* Parse #rgb or #rrggbb (with or without the hash) to [r,g,b]. Returns null for\n * anything else so callers can no-op instead of painting rgb(NaN,...) surfaces. */\nfunction hexToRgb(hex) {\n let h = String(hex).trim().replace(/^#/, '');\n if (h.length === 3) h = h.replace(/./g, (c) => c + c);\n if (!/^[0-9a-f]{6}$/i.test(h)) return null;\n return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];\n}\n/* Derive the folder's three surfaces from one base color (same offsets as the\n * original engine): back/tab −30, frosted front +40 at 0.55, solid front +35.\n * The solid variant is what keeps NON-active cards fully opaque - only the\n * active card gets the translucent frosted treatment. Also picks a label\n * color (white or ink) by the frontSolid surface's YIQ brightness, so a\n * dark folder color doesn't leave the label unreadable regardless of theme. */\nfunction folderColors(hex) {\n const rgb = hexToRgb(hex);\n if (!rgb) return null;\n const [r, g, b] = rgb;\n const dn = (v) => Math.max(v - 30, 0);\n const up = (v, o) => Math.min(v + o, 255);\n const fr = up(r, 35);\n const fg = up(g, 35);\n const fb = up(b, 35);\n const yiq = (fr * 299 + fg * 587 + fb * 114) / 1000;\n return {\n back: `rgb(${dn(r)},${dn(g)},${dn(b)})`,\n front: `rgba(${up(r, 40)},${up(g, 40)},${up(b, 40)},0.55)`,\n frontSolid: `rgb(${fr},${fg},${fb})`,\n label: yiq >= 128 ? '#1c1c1a' : '#ffffff',\n };\n}\n\n/* Paint a folder's whole palette from a single hex: the SVG back, the frosted\n * and solid front surfaces, and the auto-contrast label color. Shared by the\n * build-time pass and the runtime setColor() handle method so both derive the\n * palette identically. */\nfunction paintFolder(card, hex) {\n const c = folderColors(hex);\n if (!c) return; // ignore a malformed color rather than paint rgb(NaN,...)\n card.style.setProperty('--fg-folder-bg', hex);\n card.style.setProperty('--fg-front', c.front);\n card.style.setProperty('--fg-front-solid', c.frontSolid);\n card.style.setProperty('--fg-label-on-color', c.label);\n const path = card.querySelector('.fg-folder path');\n if (path) path.setAttribute('fill', c.back);\n}\n\n/* Paint a CSS gradient across the folder's front panel, beneath the label and\n * any peek contents. A falsy gradient clears it. */\nfunction paintGradient(card, gradient) {\n const front = card.querySelector('.fg-front');\n if (!front) return;\n let layer = front.querySelector('.fg-gradient');\n if (!gradient) {\n if (layer) layer.remove();\n card.classList.remove('fg-card--gradient');\n return;\n }\n if (!layer) {\n layer = document.createElement('div');\n layer.className = 'fg-gradient';\n layer.setAttribute('aria-hidden', 'true');\n front.insertBefore(layer, front.firstChild);\n }\n layer.style.background = gradient;\n card.classList.add('fg-card--gradient');\n}\n\n/* Built-in default content renderer. Accepts item.content (a Node or HTML\n * string) or item.src (an image). Consumers pass their own contentRenderer\n * (card, item, index) to hold arbitrary content - that's the whole point. */\nfunction defaultContentRenderer(card, item /* , index */) {\n const wrap = document.createElement('div');\n wrap.className = 'fg-content';\n // Duck-type DOM nodes (nodeType) rather than `instanceof Node` - `Node`\n // isn't a global in every embedding (SSR shims, minimal DOM harnesses).\n if (item.content && typeof item.content === 'object' && typeof item.content.nodeType === 'number') {\n wrap.appendChild(item.content);\n } else if (typeof item.content === 'string') {\n wrap.innerHTML = item.content;\n } else if (item.src) {\n const img = document.createElement('img');\n img.className = 'fg-image';\n img.src = item.src;\n img.alt = item.label || '';\n img.loading = 'lazy';\n img.decoding = 'async';\n wrap.appendChild(img);\n }\n card.appendChild(wrap);\n}\n\nexport function createFolderGallery(root, options = {}) {\n if (!root || root.nodeType !== 1) throw new Error('createFolderGallery: a root element is required');\n\n // Re-init on the same root: tear the old instance down so it can't leak.\n const prior = INSTANCES.get(root);\n if (prior) prior();\n\n // Drop undefined values before merging: a wrapper passing an unset prop\n // (loop, scrollNav, ...) as `undefined` must NOT clobber the real default.\n const provided = {};\n for (const k in options) if (options[k] !== undefined) provided[k] = options[k];\n const opts = { ...DEFAULTS, ...provided };\n const items = Array.isArray(opts.items) ? opts.items : [];\n const n = items.length;\n const renderContent =\n typeof opts.contentRenderer === 'function' ? opts.contentRenderer : defaultContentRenderer;\n\n const reducedQuery =\n opts.reducedMotion === 'auto' && typeof matchMedia === 'function'\n ? matchMedia('(prefers-reduced-motion: reduce)')\n : null;\n // `let` so an OS reduced-motion toggle after init re-syncs the JS motion\n // branches (fling short-circuit, stack tilt) with the CSS, which updates live.\n let reduced = opts.reducedMotion === 'force' || (reducedQuery ? reducedQuery.matches : false);\n\n let mode = MODE_WIDTHS[opts.mode] ? opts.mode : 'stack';\n let active = Math.min(Math.max(opts.defaultActiveIndex | 0, 0), Math.max(n - 1, 0));\n let scrollCooldown = false;\n let scrollAccum = 0;\n\n /* ── Listener bookkeeping for destroy() ── */\n const cleanups = [];\n const on = (el, ev, fn, o) => { el.addEventListener(ev, fn, o); cleanups.push(() => el.removeEventListener(ev, fn, o)); };\n const emit = (name, detail) => root.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));\n let destroyed = false;\n\n // Tracked timers/frames so teardown() can cancel anything the fling\n // choreography or the resize debounce has in flight (they mutate layout).\n const timers = new Set();\n const rafs = new Set();\n const later = (fn, ms) => { const id = setTimeout(() => { timers.delete(id); fn(); }, ms); timers.add(id); return id; };\n const frame = (fn) => { const id = requestAnimationFrame(() => { rafs.delete(id); fn(); }); rafs.add(id); return id; };\n\n /* ── Build own DOM (no page scaffold assumed) ── */\n root.classList.add('fg-root');\n root.setAttribute('data-fg-mode', mode);\n root.setAttribute('data-fg-peek', PEEK_MODES.has(opts.peek) ? opts.peek : 'hover');\n root.setAttribute('data-fg-drag', DRAG_MODES.has(opts.drag) ? opts.drag : 'fling');\n const scene = document.createElement('div');\n scene.className = 'fg-scene';\n scene.setAttribute('role', 'listbox');\n scene.setAttribute('aria-label', opts.label);\n scene.setAttribute('aria-roledescription', 'folder gallery');\n const dotsEl = document.createElement('div');\n dotsEl.className = 'fg-dots';\n dotsEl.setAttribute('role', 'tablist');\n const live = document.createElement('div');\n live.className = 'fg-sr-only';\n live.setAttribute('aria-live', 'polite');\n live.setAttribute('aria-atomic', 'true');\n root.append(scene, dotsEl, live);\n\n function teardown() {\n if (destroyed) return;\n destroyed = true;\n timers.forEach(clearTimeout); timers.clear();\n rafs.forEach(cancelAnimationFrame); rafs.clear();\n cleanups.forEach((fn) => fn());\n cleanups.length = 0;\n root.replaceChildren();\n root.classList.remove('fg-root');\n root.removeAttribute('data-fg-mode');\n root.removeAttribute('data-fg-peek');\n root.removeAttribute('data-fg-drag');\n INSTANCES.delete(root);\n }\n\n if (n === 0) {\n // Full no-op surface so an empty gallery matches the handle type: calling\n // any method (setColor, getItems, ...) is safe rather than a TypeError.\n const noop = () => {};\n INSTANCES.set(root, teardown);\n return {\n next: noop, prev: noop, goTo: noop, setMode: noop, setPeek: noop,\n setColor: noop, setGradient: noop,\n getActiveIndex: () => -1, getMode: () => mode,\n getPeek: () => root.getAttribute('data-fg-peek'),\n getColor: () => undefined, getGradient: () => undefined, getItems: () => [],\n destroy: teardown,\n };\n }\n\n /* ── Normalized transform - identical function list for every mode ── */\n function setCardTransform(card, { x, y, z, rx, ry, s, zIndex, opacity, isActive }) {\n /* A card mid-throw owns its own motion: layout updates its role\n (selection, stacking) but leaves transform and opacity to the fling,\n so the pile can shuffle immediately while the thrown folder finishes\n its flight. The fling handler relayouts it once it has faded out. */\n if (!card.classList.contains('fg-card--flung')) {\n card.style.transform = `translate3d(${x}px,${y}px,${z}px) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`;\n card.style.opacity = String(opacity);\n }\n // transform-origin: center bottom is set once in CSS on .fg-card.\n card.style.zIndex = String(zIndex);\n card.setAttribute('aria-selected', isActive ? 'true' : 'false');\n card.classList.toggle('is-active', isActive);\n }\n\n /* ── Build cards ── */\n const cardEls = items.map((item, i) => {\n const card = document.createElement('div');\n card.className = 'fg-card';\n card.tabIndex = i === active ? 0 : -1; // roving tabindex\n card.setAttribute('role', 'option');\n card.setAttribute('aria-label', item.label || `Item ${i + 1}`);\n card.setAttribute('aria-setsize', String(n));\n card.setAttribute('aria-posinset', String(i + 1));\n card.setAttribute('data-fg-index', String(i)); // lets consumers target one folder in CSS/JS\n\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('class', 'fg-folder');\n svg.setAttribute('viewBox', '0 0 480 342');\n svg.setAttribute('preserveAspectRatio', 'none');\n svg.setAttribute('aria-hidden', 'true');\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute('d', opts.folderPath);\n path.setAttribute('fill', 'var(--fg-folder-bg)');\n svg.appendChild(path);\n\n card.appendChild(svg);\n\n renderContent(card, item, i);\n\n const front = document.createElement('div');\n front.className = 'fg-front';\n /* Decal: the photo prints on the folder's FRONT PANEL. The back and tab\n keep their color, so the folder stays a folder: colored tabs in the\n stack, a real material boundary at the front's lip. */\n if (item.decal) {\n card.classList.add('fg-card--decal');\n const photo = document.createElement('img');\n photo.className = 'fg-decal';\n photo.src = item.decal;\n photo.alt = '';\n photo.loading = 'lazy';\n photo.decoding = 'async';\n front.appendChild(photo);\n }\n if (item.label) {\n const label = document.createElement('div');\n label.className = 'fg-label';\n label.textContent = item.label;\n front.appendChild(label);\n }\n card.appendChild(front);\n\n // Palette + optional gradient front (both derive from item; paintFolder\n // needs the SVG path in place, so it runs after the folder is assembled).\n if (item.color) paintFolder(card, item.color);\n if (item.gradient) paintGradient(card, item.gradient);\n\n scene.appendChild(card);\n return card;\n });\n\n /* ── Dots ── */\n const dots = cardEls.map((_, i) => {\n const dot = document.createElement('button');\n dot.type = 'button';\n dot.className = 'fg-dot';\n dot.setAttribute('role', 'tab');\n dot.setAttribute('aria-label', items[i].label || `Go to ${i + 1}`);\n on(dot, 'click', () => goTo(i));\n dotsEl.appendChild(dot);\n return dot;\n });\n\n /* ── Layouts ── */\n // Grid renders each folder at its true cell width (not a full-scene card\n // scaled down), so absolute-unit content stays crisp and legible. Stack and\n // carousel use the full scene width, so they clear any grid width first.\n const clearGridWidth = (card) => { if (card.style.width) card.style.width = ''; };\n\n function layoutStack() {\n scene.style.height = '';\n cardEls.forEach((card, i) => {\n clearGridWidth(card);\n const behind = (i - active + n) % n;\n if (behind === 0) {\n setCardTransform(card, { x: 0, y: 100, z: 20, rx: 0, ry: 0, s: 1, zIndex: n + 1, opacity: 1, isActive: true });\n return;\n }\n const safeN = Math.max(n - 1, 1);\n const tz = -behind * 25 - behind * behind * 3;\n const rx = reduced ? 0 : -behind * 3;\n const ty = 100 - behind * 28 - behind * behind * 2;\n const sc = 1 - behind * 0.06 - (behind / safeN) * (behind / safeN) * 0.04;\n setCardTransform(card, { x: 0, y: ty, z: tz, rx, ry: 0, s: Math.max(sc, 0.5), zIndex: n - behind, opacity: 1 - behind * 0.08, isActive: false });\n });\n }\n function layoutGrid() {\n const sceneW = scene.clientWidth || MODE_WIDTHS.grid;\n const gap = Number.isFinite(opts.grid && opts.grid.gap) ? opts.grid.gap : 16;\n // A pinned column count wins; otherwise two columns on narrow scenes (so\n // folders stay tappable) or for small sets, three otherwise.\n const cols = Math.max(1, Math.round(\n opts.grid && opts.grid.columns ? opts.grid.columns : sceneW < 520 ? 2 : n <= 4 ? 2 : 3\n ));\n // Each card is sized to its cell and rendered at scale 1 (no shrink-to-blur),\n // so the folder art and any content inside read at their true size.\n const cellW = (sceneW - (cols - 1) * gap) / cols;\n const cellH = cellW * (2 / 3);\n const rows = Math.ceil(n / cols);\n cardEls.forEach((card, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const itemsInRow = Math.min(cols, n - row * cols);\n const rowW = itemsInRow * cellW + (itemsInRow - 1) * gap;\n const rowStartX = (sceneW - rowW) / 2;\n const x = rowStartX + col * (cellW + gap);\n const y = row * (cellH + gap);\n card.style.width = cellW + 'px';\n setCardTransform(card, { x, y, z: 0, rx: 0, ry: 0, s: 1, zIndex: i === active ? 2 : 1, opacity: 1, isActive: i === active });\n });\n scene.style.height = rows * cellH + (rows - 1) * gap + 'px';\n }\n function layoutCarousel() {\n const sceneW = scene.clientWidth || MODE_WIDTHS.carousel;\n const cardW = sceneW;\n const cardH = cardW * (2 / 3);\n cardEls.forEach(clearGridWidth);\n const activeSc = 0.62;\n const activeW = cardW * activeSc;\n const sceneH = Math.max(cardH * activeSc + 40, 180);\n scene.style.height = sceneH + 'px';\n const offsetStep = Math.min(130, (sceneW - activeW) / 4);\n cardEls.forEach((card, i) => {\n let offset = i - active;\n if (n > 2 && offset > Math.floor(n / 2)) offset -= n;\n if (n > 2 && offset < -Math.floor(n / 2)) offset += n;\n const sc = Math.max(activeSc - Math.abs(offset) * 0.06, 0.28);\n const z = -Math.abs(offset) * 50;\n const op = Math.max(1 - Math.abs(offset) * 0.25, 0.15);\n const tx = sceneW / 2 + offset * offsetStep - cardW / 2;\n const ty = sceneH - 20 - cardH;\n setCardTransform(card, { x: tx, y: ty, z, rx: 0, ry: 0, s: sc, zIndex: n - Math.abs(offset), opacity: op, isActive: offset === 0 });\n });\n }\n // Scene width tracks the active mode so the layout math (which assumes the\n // mode's logical width) matches the actually-rendered card width. Layouts\n // then read scene.clientWidth, so everything stays self-consistent at any size.\n function sizeScene() {\n scene.style.width = `min(${MODE_WIDTHS[mode]}px, 92vw)`;\n }\n function applyLayout() {\n sizeScene();\n (mode === 'grid' ? layoutGrid : mode === 'carousel' ? layoutCarousel : layoutStack)();\n dots.forEach((d, i) => {\n d.classList.toggle('is-active', i === active);\n d.setAttribute('aria-selected', i === active ? 'true' : 'false');\n });\n cardEls.forEach((c, i) => { c.tabIndex = i === active ? 0 : -1; });\n const cur = items[active];\n live.textContent = cur && cur.label ? `${active + 1} of ${n}: ${cur.label}` : `${active + 1} of ${n}`;\n }\n\n /* ── Navigation ── */\n function goTo(i) {\n if (destroyed) return;\n // Coerce first: an unvalidated NaN/undefined would set active to NaN and\n // blank every card (translate3d(NaNpx,...)) plus emit index: NaN.\n const t = Number.isFinite(+i) ? Math.trunc(+i) : active;\n const next = opts.loop ? ((t % n) + n) % n : Math.min(Math.max(t, 0), n - 1);\n if (next === active) return;\n active = next;\n applyLayout();\n emit('fg-activechange', { index: active, item: items[active] });\n }\n function select(i) {\n if (destroyed) return;\n emit('fg-select', { index: i, item: items[i] });\n if (typeof opts.onSelect === 'function') opts.onSelect(items[i], i);\n }\n function setMode(m) {\n if (destroyed || m === mode || !MODE_WIDTHS[m]) return;\n mode = m;\n root.setAttribute('data-fg-mode', mode);\n applyLayout();\n emit('fg-modechange', { mode });\n }\n function setPeek(p) {\n if (destroyed || !PEEK_MODES.has(p) || p === root.getAttribute('data-fg-peek')) return;\n root.setAttribute('data-fg-peek', p);\n emit('fg-peekchange', { peek: p });\n }\n\n /* ── Interaction ── */\n const dragEnabled = opts.drag !== 'off';\n let suppressClick = false; // a real drag eats the click that follows pointerup\n\n cardEls.forEach((card, i) => {\n on(card, 'click', () => {\n if (suppressClick) { suppressClick = false; return; }\n i === active ? select(i) : goTo(i);\n });\n on(card, 'keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); select(i); }\n });\n });\n /* Arrow keys work two ways, matching the reference implementation:\n 1. Focus already inside the gallery (Tab to a card/dot) - bubbles here\n and re-focuses the new active card, standard roving-tabindex UX.\n 2. Mouse hovering the gallery, focus anywhere else on the page - a\n document-level listener picks it up. Without this route, arrow\n keys only work AFTER a click puts focus on a card - and a click is\n also what fires onSelect, which in real use is often a link. Making\n keyboard nav depend on that first click means the only way to\n \"activate\" arrows is to risk navigating away. Hover is enough. */\n function handleArrowKeys(e) {\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); goTo(active + 1); return true; }\n if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); goTo(active - 1); return true; }\n if (e.key === 'Home') { e.preventDefault(); goTo(0); return true; }\n if (e.key === 'End') { e.preventDefault(); goTo(n - 1); return true; }\n return false;\n }\n on(scene, 'keydown', (e) => { if (handleArrowKeys(e)) cardEls[active].focus(); });\n on(dotsEl, 'keydown', (e) => { if (handleArrowKeys(e)) cardEls[active].focus(); });\n\n let hovered = false;\n on(root, 'mouseenter', () => { hovered = true; });\n on(root, 'mouseleave', () => { hovered = false; });\n on(document, 'keydown', (e) => {\n if (!hovered) return;\n // Don't hijack arrows if focus is on an interactive control outside\n // the gallery (nav link, a form field elsewhere on the page).\n const ae = document.activeElement;\n if (ae && ae !== document.body && !root.contains(ae)) return;\n handleArrowKeys(e); // no forced focus - the user hasn't tabbed in\n });\n\n if (opts.scrollNav) {\n /* Bound to root, not just the scene box: root is the widget's full\n footprint (scene + dots), so wheel events over the dots row or any\n gap the consumer's own layout leaves around the gallery still cycle\n it, instead of requiring the pointer over the exact folder art. */\n on(root, 'wheel', (e) => {\n if (mode === 'grid') return;\n const atStart = !opts.loop && active === 0 && e.deltaY < 0;\n const atEnd = !opts.loop && active === n - 1 && e.deltaY > 0;\n if (atStart || atEnd) return;\n e.preventDefault();\n if (scrollCooldown) return;\n scrollAccum += e.deltaY;\n if (Math.abs(scrollAccum) >= SCROLL_THRESHOLD) {\n const dir = scrollAccum > 0 ? 1 : -1;\n scrollAccum = 0;\n scrollCooldown = true;\n goTo(active + dir);\n later(() => { scrollCooldown = false; scrollAccum = 0; }, SCROLL_COOLDOWN[mode] || 300);\n }\n }, { passive: false });\n }\n\n /* Dominant-axis swipe: up or left advances, down or right goes back.\n Horizontal is the natural phone gesture in carousel; vertical matches\n the wheel direction on the stack. Both work everywhere. */\n let touchStartX = 0;\n let touchStartY = 0;\n on(scene, 'touchstart', (e) => {\n touchStartX = e.touches[0].clientX;\n touchStartY = e.touches[0].clientY;\n }, { passive: true });\n on(scene, 'touchend', (e) => {\n // In stack mode the pointer-based drag below owns the gesture; letting\n // both fire would advance twice per swipe.\n if (dragEnabled && mode === 'stack') return;\n const dx = touchStartX - e.changedTouches[0].clientX;\n const dy = touchStartY - e.changedTouches[0].clientY;\n const ax = Math.abs(dx);\n const ay = Math.abs(dy);\n if (Math.max(ax, ay) < 40) return;\n const dir = ax > ay ? (dx > 0 ? 1 : -1) : (dy > 0 ? 1 : -1);\n goTo(active + dir);\n }, { passive: true });\n\n /* ── Drag: grab the active folder and throw it (stack mode) ──\n Pointer Events give one code path for mouse, touch, and pen. The\n gesture constants come from the folder-deck prototype this feature is\n ported from: rotation follows the drag at 0.04deg/px, a release past\n 90px of travel (or a genuine throw, over 0.5px/ms) flings the folder\n off along the dominant axis and advances; anything shorter springs\n back into the pile on the card's own transition. Direction keeps the\n swipe semantics: left or up is next, right or down is previous. */\n if (dragEnabled) {\n const DRAG_ROT = 0.04;\n const FLING_DIST = 90;\n const FLING_VEL = 0.5;\n const TAP_SLOP = 6;\n let dragCard = null;\n let dragBase = '';\n let startPX = 0, startPY = 0, dragDX = 0, dragDY = 0;\n let lastX = 0, lastY = 0, lastT = 0, velX = 0, velY = 0;\n\n on(scene, 'pointerdown', (e) => {\n if (mode !== 'stack') return;\n if (e.button !== undefined && e.button !== 0) return;\n const card = e.target && e.target.closest ? e.target.closest('.fg-card') : null;\n if (!card || !card.classList.contains('is-active')) return;\n dragCard = card;\n dragBase = card.style.transform;\n startPX = e.clientX; startPY = e.clientY;\n dragDX = 0; dragDY = 0;\n lastX = e.clientX; lastY = e.clientY;\n lastT = (typeof performance !== 'undefined' ? performance.now() : Date.now());\n velX = 0; velY = 0;\n card.classList.add('fg-card--dragging');\n if (card.setPointerCapture && e.pointerId !== undefined) {\n try { card.setPointerCapture(e.pointerId); } catch (_) { /* jsdom / detached */ }\n }\n });\n on(window, 'pointermove', (e) => {\n if (!dragCard) return;\n dragDX = e.clientX - startPX;\n dragDY = e.clientY - startPY;\n const now = (typeof performance !== 'undefined' ? performance.now() : Date.now());\n const dt = Math.max(now - lastT, 1);\n velX = (e.clientX - lastX) / dt;\n velY = (e.clientY - lastY) / dt;\n lastX = e.clientX; lastY = e.clientY; lastT = now;\n // translateZ lifts the grabbed folder clear of every plane in the\n // pile (tilted tops can poke past the active card's resting depth)\n // and reads as physically picking it up.\n dragCard.style.transform = `${dragBase} translate3d(${dragDX}px, ${dragDY}px, 40px) rotate(${(dragDX * DRAG_ROT).toFixed(2)}deg)`;\n });\n const endDrag = () => {\n if (!dragCard) return;\n const card = dragCard;\n dragCard = null;\n card.classList.remove('fg-card--dragging');\n const dist = Math.hypot(dragDX, dragDY);\n if (dist < TAP_SLOP) { card.style.transform = dragBase; return; } // a tap: the click handler owns it\n suppressClick = true;\n const horizontal = Math.abs(dragDX) >= Math.abs(dragDY);\n const flung = dist > FLING_DIST || Math.hypot(velX, velY) > FLING_VEL;\n const dir = horizontal ? (dragDX > 0 ? -1 : 1) : (dragDY > 0 ? -1 : 1);\n const target = active + dir;\n const blocked = !opts.loop && (target < 0 || target > n - 1);\n if (!flung || blocked) { card.style.transform = dragBase; return; } // spring back into the pile\n const semantic = dir > 0 ? 'next' : 'prev';\n emit('fg-flingstart', { index: active, direction: semantic });\n if (reduced) { goTo(target); emit('fg-flingend', { index: target, direction: semantic }); return; }\n const offX = horizontal ? Math.sign(dragDX) * Math.max(window.innerWidth * 0.7, 480) : dragDX * 3;\n const offY = horizontal ? dragDY * 3 : Math.sign(dragDY) * Math.max(window.innerHeight * 0.7, 480);\n /* Choreography: the pile answers the throw IMMEDIATELY (goTo below;\n layout skips the flung card), while the thrown folder finishes its\n own flight - a longer, momentum-eased sail that fades over 340ms.\n Once it is fully invisible, it re-enters by DROPPING IN from above\n its new slot: never straight from off-screen, which would cross\n the other folders' 3D planes and slice them visibly. */\n card.classList.add('fg-card--flung');\n card.style.transform = `${dragBase} translate3d(${offX}px, ${offY}px, 40px) rotate(${Math.sign(dragDX || 1) * 18}deg)`;\n card.style.opacity = '0';\n goTo(target); // shuffle starts now; the flung card is exempt from this relayout\n later(() => {\n card.classList.add('fg-card--snap');\n card.classList.remove('fg-card--flung');\n applyLayout(); // now it takes its slot transform (invisible, transitions off)\n const slot = card.style.transform;\n const slotOpacity = card.style.opacity;\n card.style.transform = `${slot} translate3d(0, -90px, 0)`;\n card.style.opacity = '0';\n /* Phase boundary via double rAF, NOT a synchronous reflow flush:\n WebKit does not treat forced reflows as transition boundaries and\n will compute the transition from the last PAINTED state (the\n fling-off position), which flashes the card across the pile.\n Painting one real frame at the staging point (invisible,\n transitions off) gives every engine the same before-state. */\n frame(() => frame(() => {\n card.classList.remove('fg-card--snap');\n card.classList.add('fg-card--entering'); // quicker settle curve for the drop\n card.style.transform = slot; // descend into the pile\n card.style.opacity = slotOpacity;\n later(() => {\n card.classList.remove('fg-card--entering');\n emit('fg-flingend', { index: target, direction: semantic });\n }, 700);\n }));\n }, 400); // after the 340ms flung fade, with margin: never snap a visible card\n };\n on(window, 'pointerup', endDrag);\n on(window, 'pointercancel', endDrag);\n }\n\n let resizeTimer;\n on(window, 'resize', () => { clearTimeout(resizeTimer); resizeTimer = later(applyLayout, 150); });\n\n // Keep `reduced` in sync if the OS setting changes while mounted (the CSS\n // updates live; this re-runs the JS motion branches to match).\n if (reducedQuery && reducedQuery.addEventListener) {\n on(reducedQuery, 'change', (e) => { if (!destroyed) { reduced = e.matches; applyLayout(); } });\n }\n\n applyLayout();\n\n /* ── Public handle ── */\n const handle = {\n next: () => goTo(active + 1),\n prev: () => goTo(active - 1),\n goTo,\n setMode,\n setPeek,\n setColor: (i, hex) => { const card = cardEls[i]; if (card && !destroyed) paintFolder(card, hex); },\n setGradient: (i, gradient) => { const card = cardEls[i]; if (card && !destroyed) paintGradient(card, gradient); },\n getActiveIndex: () => active,\n getMode: () => mode,\n getPeek: () => root.getAttribute('data-fg-peek'),\n getColor: (i) => { const card = cardEls[i]; return card ? card.style.getPropertyValue('--fg-folder-bg') || undefined : undefined; },\n getGradient: (i) => {\n const layer = cardEls[i] && cardEls[i].querySelector('.fg-gradient');\n return layer ? layer.style.background || undefined : undefined;\n },\n getItems: () => items.slice(),\n destroy: teardown,\n };\n INSTANCES.set(root, teardown);\n return handle;\n}\n\nexport default createFolderGallery;\n"],"mappings":"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","names":[]}
|