@simhub2/justflipit 3.2.2 → 4.0.1

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +258 -91
  4. package/dist/agent.cjs +63 -0
  5. package/dist/agent.cjs.map +7 -0
  6. package/dist/agent.d.cts +4 -0
  7. package/dist/agent.d.mts +4 -0
  8. package/dist/agent.d.ts +4 -0
  9. package/dist/agent.mjs +43 -0
  10. package/dist/agent.mjs.map +7 -0
  11. package/dist/justFlipIt-combined.js +295 -0
  12. package/dist/justFlipIt-combined.min.js +23 -0
  13. package/dist/justFlipIt-modern.js +271 -476
  14. package/dist/justFlipIt-modern.min.js +23 -1
  15. package/dist/justFlipIt-vanilla.js +272 -343
  16. package/dist/justFlipIt-vanilla.min.js +23 -1
  17. package/dist/justFlipIt.cjs +293 -0
  18. package/dist/justFlipIt.cjs.map +7 -0
  19. package/dist/justFlipIt.d.cts +4 -0
  20. package/dist/justFlipIt.d.mts +38 -0
  21. package/dist/justFlipIt.d.ts +38 -0
  22. package/dist/justFlipIt.js +269 -391
  23. package/dist/justFlipIt.js.map +7 -0
  24. package/dist/justFlipIt.min.js +23 -1
  25. package/dist/justFlipIt.min.js.map +7 -0
  26. package/dist/justFlipIt.mjs +272 -0
  27. package/dist/justFlipIt.mjs.map +7 -0
  28. package/dist/react.cjs +55 -0
  29. package/dist/react.cjs.map +7 -0
  30. package/dist/react.d.cts +3 -0
  31. package/dist/react.d.mts +3 -0
  32. package/dist/react.d.ts +3 -0
  33. package/dist/react.mjs +25 -0
  34. package/dist/react.mjs.map +7 -0
  35. package/dist/svelte.cjs +45 -0
  36. package/dist/svelte.cjs.map +7 -0
  37. package/dist/svelte.d.cts +3 -0
  38. package/dist/svelte.d.mts +3 -0
  39. package/dist/svelte.d.ts +3 -0
  40. package/dist/svelte.mjs +15 -0
  41. package/dist/svelte.mjs.map +7 -0
  42. package/dist/types/justFlipIt.d.ts +36 -59
  43. package/dist/vue.cjs +49 -0
  44. package/dist/vue.cjs.map +7 -0
  45. package/dist/vue.d.cts +3 -0
  46. package/dist/vue.d.mts +3 -0
  47. package/dist/vue.d.ts +3 -0
  48. package/dist/vue.mjs +19 -0
  49. package/dist/vue.mjs.map +7 -0
  50. package/package.json +117 -50
  51. package/src/justFlipIt-combined.js +0 -417
  52. package/src/justFlipIt-combined.min.js +0 -1
  53. package/src/justFlipIt-modern.js +0 -500
  54. package/src/justFlipIt-modern.min.js +0 -1
  55. package/src/justFlipIt-vanilla.js +0 -366
  56. package/src/justFlipIt-vanilla.min.js +0 -1
  57. package/src/justFlipIt.d.ts +0 -61
@@ -1,500 +1,295 @@
1
- /*!
2
- * justFlipIt v3.1.0
3
- * https://github.com/SimHub/justFlipIt
4
- *
5
- * Copyright SimHub
6
- * Released under the MIT license
7
- *
8
- * Date: 2025
9
- */
10
-
11
- (function (root, factory) {
12
- if (typeof define === "function" && define.amd) {
13
- define([], factory);
14
- } else if (typeof module === "object" && module.exports) {
15
- module.exports = factory();
16
- } else {
17
- root.justFlipIt = factory();
18
- }
19
- })(typeof self !== "undefined" ? self : this, function () {
20
- "use strict";
21
-
22
- // Feature detection
23
- const supportsGrid = typeof CSS !== "undefined" &&
24
- CSS.supports &&
25
- CSS.supports("display", "grid");
26
- const supportsWebAnimations = typeof Element !== "undefined" &&
27
- "animate" in Element.prototype;
28
- const supportsCustomProperties = window.CSS && CSS.supports &&
29
- CSS.supports("color", "var(--test)");
30
-
31
- // Style generation based on browser support
32
- function generateStyles() {
33
- // Base styles that work everywhere
34
- let baseStyles =
35
- "._justFlipIt_panel {perspective:1000px;-webkit-perspective:1000px;-moz-perspective:1000px;position:relative;}" +
36
- "._justFlipIt_panel .front{height:inherit;width:inherit;position:absolute;top:0;z-index:900;transform-style:preserve-3d;backface-visibility:hidden;transition:transform .4s ease-in-out;}" +
37
- "._justFlipIt_panel .back, ._justFlipIt_panel .backY{height:inherit;width:inherit;position:absolute;top:0;z-index:1000;transform-style:preserve-3d;backface-visibility:hidden;transition:transform .4s ease-in-out;}";
38
-
39
- // Add prefixed versions for older browsers
40
- let prefixedStyles =
41
- "._justFlipIt_panel .front{-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-webkit-transition:transform .4s ease-in-out;-moz-transition:transform .4s ease-in-out;}" +
42
- "._justFlipIt_panel .back, ._justFlipIt_panel .backY{-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-webkit-transition:transform .4s ease-in-out;-moz-transition:transform .4s ease-in-out;}";
43
-
44
- // X-axis rotations
45
- let xAxisStyles =
46
- "._justFlipIt_panel .back{transform:rotateX(-180deg);-webkit-transform:rotateX(-180deg);-moz-transform:rotateX(-180deg);}" +
47
- "._justFlipIt_panel._flip_ .front{transform:rotateX(180deg);-webkit-transform:rotateX(180deg);-moz-transform:rotateX(180deg);}" +
48
- "._justFlipIt_panel._flip_ .back{transform:rotateX(0);-webkit-transform:rotateX(0);-moz-transform:rotateX(0);}";
49
-
50
- // Y-axis rotations
51
- let yAxisStyles =
52
- "._justFlipIt_panel .backY{transform:rotateY(-180deg);-webkit-transform:rotateY(-180deg);-moz-transform:rotateY(-180deg);}" +
53
- "._justFlipIt_panel._flipY_ .front{transform:rotateY(180deg);-webkit-transform:rotateY(180deg);-moz-transform:rotateY(180deg);}" +
54
- "._justFlipIt_panel._flipY_ .backY{transform:rotateY(0);-webkit-transform:rotateY(0);-moz-transform:rotateY(0);}";
55
-
56
- // PATCH: Transparency-Style
57
- let transparentStyle =
58
- "._justFlipIt_panel {background: transparent !important; box-shadow: none !important;}" +
59
- "._justFlipIt_panel > * {box-sizing: border-box;}";
60
-
61
- return (
62
- baseStyles + prefixedStyles + xAxisStyles + yAxisStyles + transparentStyle
63
- );
64
- }
65
-
66
- // Add styles to document head
67
- function addStyles() {
68
- if (document.querySelector('[data-justflipitstyle="justFlipIt"]')) {
69
- return; // Styles already added
70
- }
71
-
72
- const styleElement = document.createElement("style");
73
- styleElement.setAttribute("data-justflipitstyle", "justFlipIt");
74
- styleElement.textContent = generateStyles();
75
-
76
- document.head.appendChild(styleElement);
77
- }
78
-
79
- // Helper functions to replace jQuery
80
- function getStyle(element, property) {
81
- return window.getComputedStyle(element, null).getPropertyValue(property);
82
- }
83
-
84
- function addClass(element, className) {
85
- if (element.classList) {
86
- element.classList.add(className);
87
- } else {
88
- element.className += " " + className;
89
- }
90
- }
91
-
92
- function removeClass(element, className) {
93
- if (element.classList) {
94
- element.classList.remove(className);
95
- } else {
96
- element.className = element.className.replace(
97
- new RegExp(
98
- "(^|\\b)" + className.split(" ").join("|") + "(\\b|$)",
99
- "gi",
100
- ),
101
- " ",
102
- );
103
- }
104
- }
105
-
106
- function toggleClass(element, className) {
107
- if (element.classList) {
108
- element.classList.toggle(className);
109
- } else {
110
- var classes = element.className.split(" ");
111
- var existingIndex = classes.indexOf(className);
112
-
113
- if (existingIndex >= 0) {
114
- classes.splice(existingIndex, 1);
115
- } else {
116
- classes.push(className);
117
- }
118
-
119
- element.className = classes.join(" ");
120
- }
121
- }
122
-
123
- function hasClass(element, className) {
124
- if (element.classList) {
125
- return element.classList.contains(className);
126
- } else {
127
- return new RegExp("(^| )" + className + "( |$)", "gi").test(
128
- element.className,
129
- );
1
+ var JustFlipItModule = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
130
15
  }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/core.js
21
+ var core_exports = {};
22
+ __export(core_exports, {
23
+ FlipController: () => FlipController,
24
+ default: () => justFlipIt
25
+ });
26
+ var instances = /* @__PURE__ */ new WeakMap();
27
+ var defaults = { Click: false, FlipX: false, Duration: 400, Easing: "cubic-bezier(.22,.61,.36,1)", Tilt: false, Effect: "flip", ReducedMotion: true, preserveClasses: true, preserveStyles: true, Style: [] };
28
+ var css = `
29
+ ._justFlipIt_panel{position:relative;display:grid;perspective:var(--jfi-perspective,1000px);isolation:isolate}
30
+ ._justFlipIt_panel>[data-jfi-face]{grid-area:1/1;min-width:0;backface-visibility:hidden;-webkit-backface-visibility:hidden;transition:transform var(--jfi-duration) var(--jfi-easing),opacity var(--jfi-duration) var(--jfi-easing);transform-origin:center}
31
+ ._justFlipIt_panel>[data-jfi-face=back],._justFlipIt_panel[data-jfi-flipped=true]>[data-jfi-face=front]{pointer-events:none}
32
+ ._justFlipIt_panel[data-jfi-flipped=true]>[data-jfi-face=back]{pointer-events:auto}
33
+ ._justFlipIt_panel[data-jfi-effect=flip]>[data-jfi-face=front]{transform:rotateY(0deg)}
34
+ ._justFlipIt_panel[data-jfi-effect=flip]>[data-jfi-face=back]{transform:rotateY(-180deg)}
35
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-flipped=true]>[data-jfi-face=front]{transform:rotateY(180deg)}
36
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-flipped=true]>[data-jfi-face=back]{transform:rotateY(0deg)}
37
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-axis=x]>[data-jfi-face=front]{transform:rotateX(0deg)}
38
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-axis=x]>[data-jfi-face=back]{transform:rotateX(-180deg)}
39
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-axis=x][data-jfi-flipped=true]>[data-jfi-face=front]{transform:rotateX(180deg)}
40
+ ._justFlipIt_panel[data-jfi-effect=flip][data-jfi-axis=x][data-jfi-flipped=true]>[data-jfi-face=back]{transform:rotateX(0deg)}
41
+ ._justFlipIt_panel[data-jfi-effect=fade]>[data-jfi-face=back],._justFlipIt_panel[data-jfi-effect=scale]>[data-jfi-face=back]{opacity:0}
42
+ ._justFlipIt_panel[data-jfi-effect=fade][data-jfi-flipped=true]>[data-jfi-face=front],._justFlipIt_panel[data-jfi-effect=scale][data-jfi-flipped=true]>[data-jfi-face=front]{opacity:0}
43
+ ._justFlipIt_panel[data-jfi-effect=fade][data-jfi-flipped=true]>[data-jfi-face=back],._justFlipIt_panel[data-jfi-effect=scale][data-jfi-flipped=true]>[data-jfi-face=back]{opacity:1}
44
+ ._justFlipIt_panel[data-jfi-effect=scale]>[data-jfi-face=back],._justFlipIt_panel[data-jfi-effect=scale][data-jfi-flipped=true]>[data-jfi-face=front]{transform:scale(.94)}
45
+ ._justFlipIt_panel[data-jfi-effect=scale][data-jfi-flipped=true]>[data-jfi-face=back]{transform:scale(1)}
46
+ ._justFlipIt_panel[data-jfi-reduced=true]>[data-jfi-face]{transition:none!important}
47
+ @media(prefers-reduced-motion:reduce){._justFlipIt_panel:not([data-jfi-motion=allow])>[data-jfi-face]{transition:none!important}}
48
+ `;
49
+ function styles(doc) {
50
+ if (doc.querySelector('style[data-justflipitstyle="v4"]')) return;
51
+ const style = doc.createElement("style");
52
+ style.dataset.justflipitstyle = "v4";
53
+ style.textContent = css;
54
+ doc.head.append(style);
131
55
  }
132
-
133
- // NEU: Funktion zur Anwendung der benutzerdefinierten Animationseinstellungen
134
- function applyAnimationSettings(element, duration, easing) {
135
- const transitionValue = `transform ${duration / 1000}s ${easing}`;
136
- element.style.transition = transitionValue;
137
- element.style.WebkitTransition = transitionValue;
138
- element.style.MozTransition = transitionValue;
139
- }
140
-
141
- // PATCH: Verbesserte Style-Kopierfunktion
142
- function copyStyles(src, dest) {
143
- const computed = window.getComputedStyle(src);
144
- // Kopiere alle visuellen Eigenschaften
145
- const properties = [
146
- "background",
147
- "backgroundColor",
148
- "backgroundImage",
149
- "backgroundPosition",
150
- "backgroundRepeat",
151
- "backgroundSize",
152
- "borderRadius",
153
- "borderTopLeftRadius",
154
- "borderTopRightRadius",
155
- "borderBottomLeftRadius",
156
- "borderBottomRightRadius",
157
- "border",
158
- "borderTop",
159
- "borderRight",
160
- "borderBottom",
161
- "borderLeft",
162
- "boxShadow",
163
- "color",
164
- "opacity",
165
- ];
166
- properties.forEach((prop) => {
167
- if (
168
- computed[prop] &&
169
- computed[prop] !== "none" &&
170
- computed[prop] !== "initial" &&
171
- computed[prop] !== ""
172
- ) {
173
- dest.style[prop] = computed[prop];
56
+ function validate(options) {
57
+ if (options.Duration !== void 0 && (!Number.isFinite(options.Duration) || options.Duration < 0)) throw new TypeError("Duration must be a non-negative finite number");
58
+ if (options.Effect !== void 0 && !["flip", "fade", "scale"].includes(options.Effect)) throw new TypeError("Unknown Effect");
59
+ if (options.Tilt && typeof options.Tilt === "object") {
60
+ for (const key of ["max", "perspective", "scale"]) {
61
+ if (options.Tilt[key] !== void 0 && (!Number.isFinite(options.Tilt[key]) || options.Tilt[key] < 0)) throw new TypeError(`Invalid Tilt.${key}`);
174
62
  }
175
- });
176
- }
177
-
178
- // Modern animation with fallback
179
- function animateFlip(element, start, end, duration, easing = "ease-in-out") {
180
- if (supportsWebAnimations) {
181
- // Use Web Animations API
182
- const keyframes = [{ transform: start }, { transform: end }];
183
-
184
- const options = {
185
- duration: duration,
186
- easing: easing,
187
- fill: "forwards",
188
- };
189
-
190
- return element.animate(keyframes, options);
191
- } else {
192
- // Fallback to CSS transitions (already set in the CSS)
193
- element.style.transform = end;
194
- element.style.webkitTransform = end;
195
- element.style.mozTransform = end;
196
- return null;
197
63
  }
198
64
  }
199
-
200
- // Store for instances
201
- const instanceMap = new WeakMap();
202
-
203
- // Main justFlipIt class
204
- class JustFlipIt {
65
+ var FlipController = class {
205
66
  constructor(element, options = {}) {
206
- // Make sure styles are added
207
- addStyles();
208
-
67
+ validate(options);
68
+ if (!element || element.nodeType !== 1) throw new TypeError("Expected an HTML element");
209
69
  this.element = element;
210
- this.settings = Object.assign(
211
- {},
212
- {
213
- Template: "", // Template for back side
214
- Click: false, // Click behavior
215
- FlipX: false, // Flip direction
216
- Style: [], // Custom styles
217
- Duration: 400, // Animation duration in ms
218
- Easing: "ease-in-out", // Animation easing function
219
- preserveClasses: true, // NEU
220
- preserveStyles: true, // NEU
221
- },
222
- options,
223
- );
224
-
225
- this.elementWidth = getStyle(element, "width");
226
- this.elementHeight = getStyle(element, "height");
227
- this.elementBackgroundColor = getStyle(element, "background-color");
228
- this.originalClassName = element.className;
229
- this.originalTagName = element.tagName.toLowerCase();
230
- this.originalId = element.id || "";
231
-
232
- // Set flip direction
233
- if (this.settings.FlipX === true) {
234
- this.flipClass = "_flip_";
235
- this.backClass = "back";
236
- this.rotateStart = "rotateX(0deg)";
237
- this.rotateFront = "rotateX(180deg)";
238
- this.rotateBack = "rotateX(-180deg)";
239
- this.rotateReset = "rotateX(0deg)";
70
+ this.settings = { ...defaults, ...options };
71
+ this.flipped = false;
72
+ this.destroyed = false;
73
+ this.cleanups = [];
74
+ this.restores = [];
75
+ this.win = element.ownerDocument.defaultView;
76
+ const doc = element.ownerDocument;
77
+ styles(doc);
78
+ this.managed = !!options.Faces;
79
+ if (this.managed) {
80
+ this.hoverPanel = element;
81
+ this.frontWrapper = element.querySelector(options.Faces.front);
82
+ this.backWrapper = element.querySelector(options.Faces.back);
83
+ if (!this.frontWrapper || !this.backWrapper || this.frontWrapper === this.backWrapper || this.frontWrapper.parentNode !== element || this.backWrapper.parentNode !== element) throw new TypeError("Faces must select two distinct direct children");
240
84
  } else {
241
- this.flipClass = "_flipY_";
242
- this.backClass = "backY";
243
- this.rotateStart = "rotateY(0deg)";
244
- this.rotateFront = "rotateY(180deg)";
245
- this.rotateBack = "rotateY(-180deg)";
246
- this.rotateReset = "rotateY(0deg)";
247
- }
248
-
249
- // Initialize flip
250
- this._init();
251
-
252
- if (this.settings.Tilt) {
253
- this._setupTilt();
254
- }
255
-
256
- // Store instance
257
- instanceMap.set(element, this);
258
- }
259
-
260
- _init() {
261
- const originalElement = this.element;
262
- const originalClassName = this.originalClassName;
263
-
264
- // Panel-Struktur erzeugen
265
- const hoverPanel = document.createElement(this.originalTagName);
266
- addClass(hoverPanel, "hover");
267
- addClass(hoverPanel, "_justFlipIt_panel");
268
- const frontWrapper = document.createElement("div");
269
- addClass(frontWrapper, "front");
270
- const backWrapper = document.createElement("div");
271
- addClass(backWrapper, this.backClass);
272
-
273
- // NEU: Klassen übernehmen (Panel, .front, .backY)
274
- if (this.settings.preserveClasses && originalElement.classList.length) {
275
- originalElement.classList.forEach((cls) => {
276
- if (!hoverPanel.classList.contains(cls)) {
277
- hoverPanel.classList.add(cls);
278
- }
279
- if (frontWrapper && !frontWrapper.classList.contains(cls)) {
280
- frontWrapper.classList.add(cls);
281
- }
282
- if (backWrapper && !backWrapper.classList.contains(cls)) {
283
- backWrapper.classList.add(cls);
284
- }
285
- });
286
- }
287
-
288
- // NEU: Inline-Styles übernehmen (nur auf Panel)
289
- if (
290
- this.settings.preserveStyles && originalElement.getAttribute("style")
291
- ) {
292
- hoverPanel.setAttribute(
293
- "style",
294
- (hoverPanel.getAttribute("style") || "") +
295
- ";" + originalElement.getAttribute("style"),
296
- );
297
- }
298
-
299
- // Nur den INHALT übernehmen!
300
- frontWrapper.innerHTML = originalElement.innerHTML;
301
-
302
- if (this.settings.Template) {
303
- backWrapper.innerHTML = this.settings.Template;
304
- } else {
305
- backWrapper.innerHTML = originalElement.innerHTML;
85
+ if (!element.parentNode) throw new TypeError("Element must have a parent");
86
+ this.hoverPanel = doc.createElement("div");
87
+ this.frontWrapper = doc.createElement("div");
88
+ this.backWrapper = doc.createElement("div");
89
+ if (this.settings.preserveClasses) this.hoverPanel.className = element.className;
90
+ if (this.settings.preserveStyles) this.hoverPanel.style.cssText = element.style.cssText;
91
+ element.before(this.hoverPanel);
92
+ this.hoverPanel.append(this.frontWrapper, this.backWrapper);
93
+ this.frontWrapper.append(element);
94
+ if (options.Template !== void 0) this.backWrapper.innerHTML = options.Template;
95
+ else {
96
+ const clone = element.cloneNode(true);
97
+ clone.removeAttribute("id");
98
+ clone.querySelectorAll("[id]").forEach((node) => node.removeAttribute("id"));
99
+ this.backWrapper.append(clone);
100
+ }
306
101
  }
307
-
308
- // Panel einfügen und Original entfernen
309
- originalElement.parentNode.insertBefore(hoverPanel, originalElement);
310
- hoverPanel.appendChild(frontWrapper);
311
- hoverPanel.appendChild(backWrapper);
312
- originalElement.parentNode.removeChild(originalElement);
313
-
314
- // Panel-Größe und Styles übernehmen
315
- hoverPanel.style.width = this.elementWidth;
316
- hoverPanel.style.height = this.elementHeight;
317
-
318
- // Custom Styles aus Option anwenden
319
- if (this.settings.Style && this.settings.Style.length) {
320
- this.settings.Style.forEach((styleObj) => {
321
- if (styleObj.el) {
322
- if (styleObj.el.toLowerCase() === "self") {
323
- Object.assign(hoverPanel.style, styleObj.style);
324
- } else {
325
- const targetElements = hoverPanel.querySelectorAll(styleObj.el);
326
- targetElements.forEach((el) => {
327
- Object.assign(el.style, styleObj.style);
328
- });
329
- }
102
+ const panel = this.hoverPanel;
103
+ for (const node of [panel, this.frontWrapper, this.backWrapper]) {
104
+ const owned = node === panel ? ["class", "style", "tabindex", "data-jfi-axis", "data-jfi-effect", "data-jfi-motion", "data-jfi-flipped", "data-jfi-reduced"] : ["style", "data-jfi-face", "aria-hidden", "inert"];
105
+ const attrs = owned.map((name) => [name, node.getAttribute(name)]);
106
+ const inert = node.inert || false;
107
+ this.restores.push(() => {
108
+ node.inert = inert;
109
+ for (const [name, value] of attrs) {
110
+ if (value === null) node.removeAttribute(name);
111
+ else node.setAttribute(name, value);
330
112
  }
331
113
  });
332
114
  }
333
-
334
- // Events setzen
335
- this._setupEvents(hoverPanel);
336
-
337
- // Referenzen speichern
338
- this.hoverPanel = hoverPanel;
339
- this.frontWrapper = frontWrapper;
340
- this.backWrapper = backWrapper;
341
- }
342
-
343
- _setupTilt() {
344
- const handleMove = (e) => {
345
- const rect = this.hoverPanel.getBoundingClientRect();
346
- const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
347
- const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
348
- const centerX = rect.width / 2;
349
- const centerY = rect.height / 2;
350
- const rotateX = ((y - centerY) / centerY) * 10;
351
- const rotateY = (-(x - centerX) / centerX) * 10;
352
- this.hoverPanel.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.05, 1.05, 1.05)`;
353
- };
354
-
355
- const handleEnter = () => {
356
- this.hoverPanel.style.transition = "all 0.2s ease-out";
357
- };
358
-
359
- const handleLeave = () => {
360
- this.hoverPanel.style.transform = `perspective(1000px) rotateX(0) rotateY(0) scale3d(1, 1, 1)`;
361
- this.hoverPanel.style.transition = "all 0.5s ease-in-out";
115
+ panel.classList.add("_justFlipIt_panel");
116
+ this.frontWrapper.dataset.jfiFace = "front";
117
+ this.backWrapper.dataset.jfiFace = "back";
118
+ this.motion = this.win.matchMedia?.("(prefers-reduced-motion: reduce)");
119
+ const motionChange = () => {
120
+ this.resetTilt();
121
+ this.render();
362
122
  };
363
-
364
- this.hoverPanel.addEventListener("mousemove", handleMove);
365
- this.hoverPanel.addEventListener("touchmove", handleMove);
366
- this.hoverPanel.addEventListener("mouseenter", handleEnter);
367
- this.hoverPanel.addEventListener("touchstart", handleEnter);
368
- this.hoverPanel.addEventListener("mouseleave", handleLeave);
369
- this.hoverPanel.addEventListener("touchend", handleLeave);
123
+ this.motion?.addEventListener("change", motionChange);
124
+ this.cleanups.push(() => this.motion?.removeEventListener("change", motionChange));
125
+ this.configure();
126
+ this.events();
127
+ this.render();
128
+ instances.set(element, this);
129
+ instances.set(panel, this);
370
130
  }
371
-
372
- _setupEvents(hoverPanel) {
373
- if (this.settings.Tilt === 'only') return;
374
- if (this.settings.Click === true) {
375
- hoverPanel.addEventListener("click", (e) => {
376
- e.preventDefault();
377
- this._toggleFlip();
378
- });
379
- } else if (typeof this.settings.Click === "string") {
380
- const clickElements = hoverPanel.querySelectorAll(this.settings.Click);
381
- clickElements.forEach((el) => {
382
- el.addEventListener("click", () => {
383
- this._toggleFlip();
384
- });
385
- });
386
- } else {
387
- // Default hover behavior
388
- hoverPanel.addEventListener("mouseenter", () => {
389
- this._flip(true);
390
- });
391
- hoverPanel.addEventListener("mouseleave", () => {
392
- this._flip(false);
393
- });
394
- }
131
+ listen(type, handler, options) {
132
+ this.hoverPanel.addEventListener(type, handler, options);
133
+ this.cleanups.push(() => this.hoverPanel.removeEventListener(type, handler, options));
395
134
  }
396
-
397
- _toggleFlip() {
398
- const isFlipped = hasClass(this.hoverPanel, this.flipClass);
399
- this._flip(!isFlipped);
135
+ get reduced() {
136
+ return this.settings.ReducedMotion !== false && !!this.motion?.matches;
400
137
  }
401
-
402
- _flip(shouldFlip) {
403
- if (shouldFlip) {
404
- addClass(this.hoverPanel, this.flipClass);
405
- if (supportsWebAnimations) {
406
- animateFlip(
407
- this.frontWrapper,
408
- this.rotateStart,
409
- this.rotateFront,
410
- this.settings.Duration,
411
- this.settings.Easing,
412
- );
413
- animateFlip(
414
- this.backWrapper,
415
- this.rotateBack,
416
- this.rotateReset,
417
- this.settings.Duration,
418
- this.settings.Easing,
419
- );
420
- }
421
- } else {
422
- removeClass(this.hoverPanel, this.flipClass);
423
- if (supportsWebAnimations) {
424
- animateFlip(
425
- this.frontWrapper,
426
- this.rotateFront,
427
- this.rotateStart,
428
- this.settings.Duration,
429
- this.settings.Easing,
430
- );
431
- animateFlip(
432
- this.backWrapper,
433
- this.rotateReset,
434
- this.rotateBack,
435
- this.settings.Duration,
436
- this.settings.Easing,
437
- );
438
- }
138
+ configure() {
139
+ const s = this.settings, p = this.hoverPanel;
140
+ p.style.setProperty("--jfi-duration", `${s.Duration}ms`);
141
+ p.style.setProperty("--jfi-easing", s.Easing);
142
+ p.dataset.jfiAxis = s.FlipX ? "x" : "y";
143
+ p.dataset.jfiEffect = s.Effect;
144
+ p.dataset.jfiMotion = s.ReducedMotion === false ? "allow" : "respect";
145
+ for (const item of s.Style || []) {
146
+ const targets = item.el === "self" ? [p] : p.querySelectorAll(item.el);
147
+ targets.forEach((node) => Object.assign(node.style, item.style));
439
148
  }
149
+ if (s.Tilt !== "only" && !p.hasAttribute("tabindex")) p.tabIndex = 0;
440
150
  }
441
-
442
- destroy() {
443
- const hoverPanel = this.hoverPanel;
444
- const originalElement = hoverPanel.querySelector(".front > *");
445
-
446
- // Restore original classes
447
- originalElement.className = this.originalClassName;
448
-
449
- // Put the original element back in its place
450
- hoverPanel.parentNode.insertBefore(originalElement, hoverPanel);
451
- hoverPanel.parentNode.removeChild(hoverPanel);
452
-
453
- // Remove from instance map
454
- instanceMap.delete(this.element);
455
- }
456
- }
457
-
458
- // Main function to initialize justFlipIt
459
- function justFlipIt(selector, options = {}) {
460
- if (typeof selector === "string") {
461
- // If selector is a string, find all matching elements
462
- const elements = document.querySelectorAll(selector);
463
- return Array.from(elements).map((el) => {
464
- return new JustFlipIt(el, options);
151
+ events() {
152
+ this.listen("click", (event) => {
153
+ if (this.settings.Tilt === "only") return;
154
+ const trigger = this.settings.Click;
155
+ if (trigger === true || trigger && [].concat(trigger).some((selector) => event.target.closest?.(selector) && this.hoverPanel.contains(event.target.closest(selector)))) this.toggle();
465
156
  });
466
- } else if (selector instanceof Element) {
467
- // If selector is a DOM element
468
- return new JustFlipIt(selector, options);
469
- } else if (selector.length) {
470
- // If selector is array-like (NodeList, HTMLCollection, etc.)
471
- return Array.from(selector).map((el) => {
472
- return new JustFlipIt(el, options);
157
+ this.listen("keydown", (event) => {
158
+ if (event.target !== this.hoverPanel || this.settings.Tilt === "only") return;
159
+ if (event.key === "Enter" || event.key === " ") {
160
+ event.preventDefault();
161
+ this.toggle();
162
+ }
163
+ if (event.key === "Escape") this.flip(false);
473
164
  });
474
- }
475
- }
476
-
477
- // Destroy method
478
- justFlipIt.destroy = function (selector) {
479
- if (typeof selector === "string") {
480
- // If selector is a string, find all matching elements
481
- const elements = document.querySelectorAll(selector);
482
- Array.from(elements).forEach((el) => {
483
- const instance = instanceMap.get(el);
484
- if (instance) instance.destroy();
165
+ this.listen("pointerenter", (event) => {
166
+ if (!this.settings.Click && this.settings.Tilt !== "only" && event.pointerType !== "touch") this.flip(true);
167
+ });
168
+ this.listen("pointerleave", (event) => {
169
+ if (event.pointerType === "touch") return;
170
+ this.resetTilt();
171
+ if (!this.settings.Click && this.settings.Tilt !== "only") this.flip(false);
172
+ });
173
+ this.listen("pointerdown", (event) => {
174
+ if (event.pointerType !== "touch" || event.isPrimary === false || this.touch) return;
175
+ this.resetTilt();
176
+ this.touch = { id: event.pointerId, x: event.clientX, y: event.clientY, moved: false };
177
+ this.queueTilt(event, this.touch);
178
+ }, { passive: true });
179
+ this.listen("pointermove", (event) => {
180
+ if (event.pointerType !== "touch") {
181
+ if (!this.touch) this.queueTilt(event);
182
+ return;
183
+ }
184
+ const touch = this.touch;
185
+ if (!touch || touch.id !== event.pointerId) return;
186
+ touch.moved || (touch.moved = this.touchMoved(event, touch));
187
+ this.queueTilt(event, touch);
188
+ }, { passive: true });
189
+ this.listen("pointerup", (event) => {
190
+ const touch = this.finishTouch(event);
191
+ if (!touch || touch.moved || this.touchMoved(event, touch)) return;
192
+ if (!this.settings.Click && this.settings.Tilt !== "only") this.toggle();
485
193
  });
486
- } else if (selector instanceof Element) {
487
- // If selector is a DOM element
488
- const instance = instanceMap.get(selector);
489
- if (instance) instance.destroy();
490
- } else if (selector.length) {
491
- // If selector is array-like (NodeList, HTMLCollection, etc.)
492
- Array.from(selector).forEach((el) => {
493
- const instance = instanceMap.get(el);
494
- if (instance) instance.destroy();
194
+ this.listen("pointercancel", (event) => {
195
+ if (event.pointerType === "touch") this.finishTouch(event);
196
+ else this.resetTilt();
197
+ });
198
+ this.listen("lostpointercapture", (event) => this.finishTouch(event));
199
+ this.baseTransform = this.hoverPanel.style.transform;
200
+ }
201
+ touchMoved(event, touch) {
202
+ return Math.hypot(event.clientX - touch.x, event.clientY - touch.y) > 10;
203
+ }
204
+ finishTouch(event) {
205
+ const touch = this.touch;
206
+ if (event.pointerType !== "touch" || !touch || touch.id !== event.pointerId) return;
207
+ this.touch = null;
208
+ this.resetTilt();
209
+ return touch;
210
+ }
211
+ queueTilt(event, touch) {
212
+ if (!this.settings.Tilt || this.reduced) return;
213
+ this.point = [event.clientX, event.clientY];
214
+ if (this.frame) return;
215
+ this.frame = this.win.requestAnimationFrame(() => {
216
+ this.frame = 0;
217
+ const rect = touch ? touch.rect ?? (touch.rect = this.hoverPanel.getBoundingClientRect()) : this.hoverPanel.getBoundingClientRect();
218
+ if (!rect.width || !rect.height) return;
219
+ const { max = 10, perspective = 1e3, scale = 1.03 } = this.settings.Tilt;
220
+ const x = Math.max(-1, Math.min(1, (this.point[0] - rect.left) / rect.width * 2 - 1));
221
+ const y = Math.max(-1, Math.min(1, (this.point[1] - rect.top) / rect.height * 2 - 1));
222
+ this.hoverPanel.style.transform = `perspective(${perspective}px) rotateX(${-y * max}deg) rotateY(${x * max}deg) scale(${scale})`;
495
223
  });
496
224
  }
225
+ resetTilt() {
226
+ if (this.frame) this.win.cancelAnimationFrame(this.frame);
227
+ this.frame = 0;
228
+ this.hoverPanel.style.transform = this.baseTransform || "";
229
+ }
230
+ render() {
231
+ this.hoverPanel.dataset.jfiFlipped = String(this.flipped);
232
+ this.hoverPanel.dataset.jfiReduced = String(this.reduced);
233
+ for (const [node, hidden] of [[this.frontWrapper, this.flipped], [this.backWrapper, !this.flipped]]) {
234
+ node.setAttribute("aria-hidden", String(hidden));
235
+ node.inert = hidden;
236
+ }
237
+ }
238
+ flip(value = true) {
239
+ if (this.destroyed || this.flipped === !!value) return this;
240
+ const active = this.element.ownerDocument.activeElement;
241
+ if ((value ? this.frontWrapper : this.backWrapper).contains(active)) this.hoverPanel.focus({ preventScroll: true });
242
+ this.flipped = !!value;
243
+ this.render();
244
+ this.hoverPanel.dispatchEvent(new this.win.CustomEvent("justflipit:change", { bubbles: true, detail: this.getState() }));
245
+ return this;
246
+ }
247
+ toggle() {
248
+ return this.flip(!this.flipped);
249
+ }
250
+ update(options = {}) {
251
+ if (this.destroyed) return this;
252
+ if ("Faces" in options) throw new TypeError("Faces cannot change after initialization");
253
+ validate(options);
254
+ if (this.managed && "Template" in options) throw new TypeError("Render framework face content with the framework");
255
+ this.settings = { ...this.settings, ...options };
256
+ if ("Template" in options) {
257
+ if (this.managed) throw new TypeError("Render framework face content with the framework");
258
+ this.backWrapper.innerHTML = options.Template;
259
+ }
260
+ this.resetTilt();
261
+ this.configure();
262
+ this.render();
263
+ return this;
264
+ }
265
+ getState() {
266
+ return { flipped: this.flipped, destroyed: this.destroyed, effect: this.settings.Effect };
267
+ }
268
+ destroy() {
269
+ if (this.destroyed) return;
270
+ this.resetTilt();
271
+ this.cleanups.forEach((fn) => fn());
272
+ this.restores.forEach((fn) => fn());
273
+ if (!this.managed) {
274
+ this.hoverPanel.replaceWith(this.element);
275
+ }
276
+ instances.delete(this.element);
277
+ instances.delete(this.hoverPanel);
278
+ this.destroyed = true;
279
+ }
497
280
  };
498
-
499
- return justFlipIt;
500
- });
281
+ function elements(target) {
282
+ if (typeof target === "string") return typeof document === "undefined" ? [] : Array.from(document.querySelectorAll(target));
283
+ if (target?.nodeType === 1) return [target];
284
+ return target ? Array.from(target) : [];
285
+ }
286
+ function justFlipIt(target, options) {
287
+ const result = elements(target).map((element) => instances.get(element) || new FlipController(element, options));
288
+ return target?.nodeType === 1 ? result[0] : result;
289
+ }
290
+ justFlipIt.destroy = (target) => elements(target).forEach((element) => instances.get(element)?.destroy());
291
+ justFlipIt.get = (element) => instances.get(element);
292
+ return __toCommonJS(core_exports);
293
+ })();
294
+ globalThis.justFlipIt = JustFlipItModule.default;
295
+ //# sourceMappingURL=justFlipIt.js.map