@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.
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +258 -91
- package/dist/agent.cjs +63 -0
- package/dist/agent.cjs.map +7 -0
- package/dist/agent.d.cts +4 -0
- package/dist/agent.d.mts +4 -0
- package/dist/agent.d.ts +4 -0
- package/dist/agent.mjs +43 -0
- package/dist/agent.mjs.map +7 -0
- package/dist/justFlipIt-combined.js +295 -0
- package/dist/justFlipIt-combined.min.js +23 -0
- package/dist/justFlipIt-modern.js +271 -476
- package/dist/justFlipIt-modern.min.js +23 -1
- package/dist/justFlipIt-vanilla.js +272 -343
- package/dist/justFlipIt-vanilla.min.js +23 -1
- package/dist/justFlipIt.cjs +293 -0
- package/dist/justFlipIt.cjs.map +7 -0
- package/dist/justFlipIt.d.cts +4 -0
- package/dist/justFlipIt.d.mts +38 -0
- package/dist/justFlipIt.d.ts +38 -0
- package/dist/justFlipIt.js +269 -391
- package/dist/justFlipIt.js.map +7 -0
- package/dist/justFlipIt.min.js +23 -1
- package/dist/justFlipIt.min.js.map +7 -0
- package/dist/justFlipIt.mjs +272 -0
- package/dist/justFlipIt.mjs.map +7 -0
- package/dist/react.cjs +55 -0
- package/dist/react.cjs.map +7 -0
- package/dist/react.d.cts +3 -0
- package/dist/react.d.mts +3 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.mjs +25 -0
- package/dist/react.mjs.map +7 -0
- package/dist/svelte.cjs +45 -0
- package/dist/svelte.cjs.map +7 -0
- package/dist/svelte.d.cts +3 -0
- package/dist/svelte.d.mts +3 -0
- package/dist/svelte.d.ts +3 -0
- package/dist/svelte.mjs +15 -0
- package/dist/svelte.mjs.map +7 -0
- package/dist/types/justFlipIt.d.ts +36 -59
- package/dist/vue.cjs +49 -0
- package/dist/vue.cjs.map +7 -0
- package/dist/vue.d.cts +3 -0
- package/dist/vue.d.mts +3 -0
- package/dist/vue.d.ts +3 -0
- package/dist/vue.mjs +19 -0
- package/dist/vue.mjs.map +7 -0
- package/package.json +117 -50
- package/src/justFlipIt-combined.js +0 -417
- package/src/justFlipIt-combined.min.js +0 -1
- package/src/justFlipIt-modern.js +0 -500
- package/src/justFlipIt-modern.min.js +0 -1
- package/src/justFlipIt-vanilla.js +0 -366
- package/src/justFlipIt-vanilla.min.js +0 -1
- package/src/justFlipIt.d.ts +0 -61
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
- Enable 3D tilt on touch and reset it when the finger lifts or scrolling takes over.
|
|
6
|
+
- Keep tap-to-flip active across flip, fade and scale without immediately flipping back on touch release.
|
|
7
|
+
- Restrict axis rotation to the flip effect so fade also works with `FlipX: true`.
|
|
8
|
+
|
|
9
|
+
## 4.0.0
|
|
10
|
+
|
|
11
|
+
- One SSR-safe, dependency-free core with real ESM/CommonJS/browser exports.
|
|
12
|
+
- Preserve original DOM nodes and event handlers; idempotent initialization and teardown.
|
|
13
|
+
- Responsive grid faces, reversible transform transitions, fade and scale effects.
|
|
14
|
+
- Frame-batched pointer tilt, touch tap, keyboard controls, inert hidden faces and live reduced motion.
|
|
15
|
+
- Typed flip/toggle/update/state methods and change events.
|
|
16
|
+
- React hook, Vue 3 directive and Svelte action using existing framework-owned faces.
|
|
17
|
+
- Model-independent agent tool schema and allowlisted command executor with plain-text content.
|
|
18
|
+
- Reproducible build, automated lifecycle tests, type checks, CI and npm release workflow.
|
|
19
|
+
|
|
20
|
+
Breaking: jQuery registration removed; generated face classes replaced by data attributes;
|
|
21
|
+
framework integrations use existing direct-child faces; npm exports now distinguish ESM and CJS.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) SimHub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,145 +1,312 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="img/readme-banner.svg" alt="justFlipIt v4 — A little motion. A new perspective." width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@simhub2/justflipit"><img src="https://img.shields.io/npm/v/%40simhub2%2Fjustflipit?style=flat-square&color=b4df83" alt="npm version"></a>
|
|
7
|
+
<a href="https://github.com/SimHub/justFlipIt/actions/workflows/ci.yml"><img src="https://github.com/SimHub/justFlipIt/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-d9ccff?style=flat-square" alt="MIT license"></a>
|
|
9
|
+
<a href="#frameworks"><img src="https://img.shields.io/badge/TypeScript-included-93c5fd?style=flat-square" alt="TypeScript declarations included"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
Responsive flip and tilt animations for your UI.<br>
|
|
14
|
+
One dependency-free core. Native framework lifecycles. Model-independent agent tools.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="#quick-start">Quick start</a> ·
|
|
19
|
+
<a href="#frameworks">React / Vue / Svelte</a> ·
|
|
20
|
+
<a href="#agent-tools">Agent tools</a> ·
|
|
21
|
+
<a href="#options">Options</a> ·
|
|
22
|
+
<a href="#upgrading-from-v3">Upgrade from v3</a>
|
|
23
|
+
</p>
|
|
8
24
|
|
|
9
25
|
---
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
## A card with more to say
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
> The interactive [Live Demo](https://SimHub.github.io/justFlipIt/) shows all features and use cases.
|
|
29
|
+
Build product cards, reveal details, or let an agent control a card through explicit tool calls. Choose a 3D flip, a soft fade, or a scale transition, then add pointer-driven tilt.
|
|
15
30
|
|
|
16
|
-
|
|
31
|
+

|
|
17
32
|
|
|
18
|
-
|
|
33
|
+
**[Browse the demo source →](example/index.html)** · Run locally with `npm run build` and `python3 -m http.server 4173`, then open [localhost:4173/example/](http://localhost:4173/example/).
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- **Flexible**: Use via CDN, NPM, or ES modules.
|
|
35
|
+
| Motion that responds | Fits your application | Control when you need it |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Flip on either axis, fade, and scale | React hook, Vue directive, Svelte action | `flip`, `toggle`, `update`, and state inspection |
|
|
38
|
+
| Pointer tilt batched per animation frame | Existing framework-owned faces stay in place | Model-independent JSON-schema tool definition |
|
|
39
|
+
| Touch, keyboard, and reduced-motion support | Responsive layout and SSR-safe imports | Typed API and change events |
|
|
26
40
|
|
|
27
|
-
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npm install @simhub2/justflipit
|
|
45
|
+
```
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
Add a container to your page, then initialize it after the element exists:
|
|
30
48
|
|
|
31
|
-
**CDN / Local:**
|
|
32
49
|
```html
|
|
33
|
-
<
|
|
50
|
+
<div class="product-card">
|
|
51
|
+
<h2>A different perspective</h2>
|
|
52
|
+
<p>Click to discover the other side.</p>
|
|
53
|
+
</div>
|
|
34
54
|
```
|
|
35
55
|
|
|
36
|
-
**NPM:**
|
|
37
|
-
```bash
|
|
38
|
-
npm install @simhub2/justflipit
|
|
39
|
-
```
|
|
40
56
|
```js
|
|
41
|
-
import justFlipIt from
|
|
57
|
+
import justFlipIt from '@simhub2/justflipit';
|
|
58
|
+
|
|
59
|
+
const card = justFlipIt(document.querySelector('.product-card'), {
|
|
60
|
+
Click: true,
|
|
61
|
+
Template: '<h2>There is more to see.</h2><p>Your details go here.</p>',
|
|
62
|
+
Tilt: { max: 10, perspective: 1000, scale: 1.03 },
|
|
63
|
+
Duration: 450,
|
|
64
|
+
});
|
|
42
65
|
```
|
|
43
66
|
|
|
44
|
-
|
|
67
|
+
The original node and its event listeners are preserved. Both faces participate in a responsive grid, so the card adapts to its content. Style the faces to match your design; the core supplies the motion and layout.
|
|
45
68
|
|
|
46
|
-
|
|
69
|
+
> `Template` accepts trusted application HTML. Use the agent bridge's `setText` action for model-generated text. With a framework, render content through the framework instead.
|
|
47
70
|
|
|
48
|
-
|
|
71
|
+
<details>
|
|
72
|
+
<summary><strong>Using a browser script or CommonJS?</strong></summary>
|
|
49
73
|
|
|
50
|
-
**
|
|
51
|
-
```html
|
|
52
|
-
<div class="card">Hover me!</div>
|
|
74
|
+
**Browser script — no bundler:**
|
|
53
75
|
|
|
76
|
+
```html
|
|
77
|
+
<div class="card">Front of the card</div>
|
|
78
|
+
<script src="https://cdn.jsdelivr.net/npm/@simhub2/justflipit@4.0.1/dist/justFlipIt.min.js"></script>
|
|
54
79
|
<script>
|
|
55
|
-
justFlipIt('.card');
|
|
80
|
+
justFlipIt('.card', { Click: true, Template: '<p>Back of the card</p>' });
|
|
56
81
|
</script>
|
|
57
82
|
```
|
|
58
83
|
|
|
59
|
-
**
|
|
84
|
+
**CommonJS:**
|
|
85
|
+
|
|
60
86
|
```js
|
|
61
|
-
justFlipIt('
|
|
87
|
+
const justFlipIt = require('@simhub2/justflipit');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The browser bundle exposes `window.justFlipIt`. ESM and CommonJS imports include matching TypeScript declarations.
|
|
91
|
+
|
|
92
|
+
</details>
|
|
93
|
+
|
|
94
|
+
## Frameworks
|
|
95
|
+
|
|
96
|
+
Use two direct child elements as the front and back faces. The adapters attach behavior to those existing nodes, so reactive content remains owned by your framework.
|
|
97
|
+
|
|
98
|
+
| Framework | Import | Integration | Cleanup |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| React 18+ | `@simhub2/justflipit/react` | `useFlip()` hook | Effect cleanup; StrictMode compatible |
|
|
101
|
+
| Vue 3 | `@simhub2/justflipit/vue` | `vFlip` directive | `beforeUnmount` |
|
|
102
|
+
| Svelte 4/5 | `@simhub2/justflipit/svelte` | `flip` action | Action `destroy()` |
|
|
103
|
+
| Other frameworks | `@simhub2/justflipit` | Core with `Faces` selectors | Call `destroy()` before unmount |
|
|
104
|
+
|
|
105
|
+
<details open>
|
|
106
|
+
<summary><strong>React</strong> — hook, ref and controller</summary>
|
|
107
|
+
|
|
108
|
+
```jsx
|
|
109
|
+
import { useFlip } from '@simhub2/justflipit/react';
|
|
110
|
+
|
|
111
|
+
export function ProductCard({ title, details }) {
|
|
112
|
+
const { ref, controller } = useFlip({ Click: true, Tilt: true });
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<section>
|
|
116
|
+
<div ref={ref}>
|
|
117
|
+
<div data-flip-front>{title}</div>
|
|
118
|
+
<div data-flip-back>{details}</div>
|
|
119
|
+
</div>
|
|
120
|
+
<button onClick={() => controller.current?.toggle()}>Flip card</button>
|
|
121
|
+
</section>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
In Next.js App Router, put this hook in a Client Component with `'use client'`.
|
|
127
|
+
|
|
128
|
+
</details>
|
|
129
|
+
|
|
130
|
+
<details>
|
|
131
|
+
<summary><strong>Vue 3</strong> — directive with reactive options</summary>
|
|
132
|
+
|
|
133
|
+
```vue
|
|
134
|
+
<script setup>
|
|
135
|
+
import { vFlip } from '@simhub2/justflipit/vue';
|
|
136
|
+
|
|
137
|
+
defineProps({ title: String, details: String });
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<template>
|
|
141
|
+
<div v-flip="{ Click: true, Effect: 'scale' }">
|
|
142
|
+
<div data-flip-front>{{ title }}</div>
|
|
143
|
+
<div data-flip-back>{{ details }}</div>
|
|
144
|
+
</div>
|
|
145
|
+
</template>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
</details>
|
|
149
|
+
|
|
150
|
+
<details>
|
|
151
|
+
<summary><strong>Svelte</strong> — action with lifecycle updates</summary>
|
|
152
|
+
|
|
153
|
+
```svelte
|
|
154
|
+
<script>
|
|
155
|
+
import { flip } from '@simhub2/justflipit/svelte';
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<div use:flip={{ Click: true, Tilt: true }}>
|
|
159
|
+
<div data-flip-front>Front</div>
|
|
160
|
+
<div data-flip-back>Details</div>
|
|
161
|
+
</div>
|
|
62
162
|
```
|
|
63
163
|
|
|
64
|
-
|
|
164
|
+
</details>
|
|
165
|
+
|
|
166
|
+
**Lifecycle notes:** Imports are safe during server rendering; initialize DOM controllers on the client. Keep `Faces` selectors stable while mounted. Render framework content through application state and omit `Template`. Avoid simultaneously managing the plugin's style, class and accessibility attributes from your framework.
|
|
167
|
+
|
|
168
|
+
## Agent tools
|
|
169
|
+
|
|
170
|
+
Connect any agent runtime that supports tool calls. Register the cards your agent may control, supply the exported schema to your runtime, and pass parsed tool arguments to the executor.
|
|
171
|
+
|
|
65
172
|
```js
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
tilt: {
|
|
72
|
-
max: 15,
|
|
73
|
-
perspective: 800
|
|
74
|
-
}
|
|
173
|
+
import justFlipIt from '@simhub2/justflipit';
|
|
174
|
+
import { createAgentBridge } from '@simhub2/justflipit/agent';
|
|
175
|
+
|
|
176
|
+
const product = justFlipIt(document.querySelector('.product-card'), {
|
|
177
|
+
Click: true,
|
|
75
178
|
});
|
|
179
|
+
const bridge = createAgentBridge({ product });
|
|
180
|
+
|
|
181
|
+
// Wrap this definition in the tool format expected by your agent runtime.
|
|
182
|
+
const tool = bridge.tool;
|
|
183
|
+
|
|
184
|
+
// Execute the parsed arguments from a tool call.
|
|
185
|
+
bridge.execute({ id: 'product', action: 'setText', text: 'Selected for you.' });
|
|
186
|
+
const result = bridge.execute({ id: 'product', action: 'flip' });
|
|
187
|
+
// { id: 'product', flipped: true, destroyed: false, effect: 'flip' }
|
|
76
188
|
```
|
|
77
189
|
|
|
78
|
-
|
|
190
|
+
| Action | Result |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `state` | Read the card's current state |
|
|
193
|
+
| `flip` | Show the back |
|
|
194
|
+
| `reset` | Show the front |
|
|
195
|
+
| `toggle` | Switch sides |
|
|
196
|
+
| `setText` | Replace the generated back face with plain text |
|
|
79
197
|
|
|
80
|
-
|
|
198
|
+
The bridge validates commands and accepts only registered card IDs. `setText` uses `textContent`, accepts up to 100,000 characters, and rejects framework-managed cards; update framework content through application state.
|
|
81
199
|
|
|
82
|
-
|
|
200
|
+
The executor runs in the browser alongside the DOM. Relay server-side tool calls through your application. The package provides a schema and executor; your application supplies the agent runtime and any model credentials.
|
|
83
201
|
|
|
84
|
-
|
|
85
|
-
| --------------- | ------- | -------- | ----------------------------------------------------------- |
|
|
86
|
-
| `tilt` | Boolean / Object | `false` | Enables a 3D tilt effect. Customize with `{ max, perspective }`. |
|
|
87
|
-
| `Click` | Boolean / String | `false` | `true` for click trigger, or a selector for a custom trigger. |
|
|
88
|
-
| `FlipX` | Boolean | `false` | `true` for vertical flip (around X-axis). |
|
|
89
|
-
| `Template` | String | `null` | HTML content for the back side. |
|
|
90
|
-
| `preserveClasses` | Boolean | `true` | Inherit all CSS classes from the original element. |
|
|
91
|
-
| `preserveStyles`| Boolean | `true` | Inherit all inline styles from the original element. |
|
|
92
|
-
| `Duration` | Number | `400` | Animation duration in ms. |
|
|
93
|
-
| `Easing` | String | `ease-in-out` | CSS easing function for the animation. |
|
|
94
|
-
| `Style` | Array | `[]` | Apply custom styles to the flip panels (see demo). |
|
|
202
|
+
## Controller API
|
|
95
203
|
|
|
204
|
+
```js
|
|
205
|
+
card.flip(); // Show back
|
|
206
|
+
card.flip(false); // Show front
|
|
207
|
+
card.toggle(); // Switch sides
|
|
208
|
+
card.update({ Effect: 'fade' }); // Change options in place
|
|
209
|
+
card.getState(); // { flipped, destroyed, effect }
|
|
210
|
+
card.destroy(); // Remove listeners and restore the element
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
| Call | Returns |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `justFlipIt(element, options)` | One controller |
|
|
216
|
+
| `justFlipIt(selectorOrCollection, options)` | An array of controllers |
|
|
217
|
+
| `justFlipIt.get(element)` | Existing controller, or `undefined` |
|
|
218
|
+
| `justFlipIt.destroy(target)` | Destroys matching instances |
|
|
219
|
+
|
|
220
|
+
Repeated initialization returns the existing instance. Use `update()` to change its options. `destroy()` is safe to call more than once. Initialize an HTML container, such as a `div`, rather than a void element such as `img`.
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
card.hoverPanel.addEventListener('justflipit:change', ({ detail }) => {
|
|
224
|
+
console.log(detail.flipped, detail.effect);
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Options
|
|
229
|
+
|
|
230
|
+
| Option | Default | What it controls |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| `Click` | `false` | Hover by default; `true` for click; a selector or selector array for delegated clicks |
|
|
233
|
+
| `FlipX` | `false` | X-axis rotation instead of Y-axis |
|
|
234
|
+
| `Template` | Clone of original | Trusted HTML for a generated back face |
|
|
235
|
+
| `Duration` | `400` | Non-negative duration in milliseconds |
|
|
236
|
+
| `Easing` | `cubic-bezier(.22,.61,.36,1)` | CSS easing function |
|
|
237
|
+
| `Effect` | `'flip'` | `'flip'`, `'fade'`, or `'scale'` |
|
|
238
|
+
| `Tilt` | `false` | `true`, `'only'`, or `{ max, perspective, scale }` |
|
|
239
|
+
| `ReducedMotion` | `true` | Respect live `prefers-reduced-motion` preferences |
|
|
240
|
+
| `Faces` | Omitted | Existing direct children: `{ front: selector, back: selector }` |
|
|
241
|
+
| `preserveClasses` | `true` | Copy original classes onto a generated container |
|
|
242
|
+
| `preserveStyles` | `true` | Copy original inline styles onto a generated container |
|
|
243
|
+
| `Style` | `[]` | Apply `{ el: 'self' or selector, style: {...} }` entries |
|
|
96
244
|
|
|
97
245
|
<details>
|
|
98
|
-
<summary><
|
|
246
|
+
<summary><strong>Style existing faces and choose a different effect</strong></summary>
|
|
99
247
|
|
|
100
248
|
```js
|
|
101
|
-
justFlipIt(
|
|
102
|
-
|
|
249
|
+
justFlipIt(document.querySelector('.card'), {
|
|
250
|
+
Faces: { front: '[data-front]', back: '[data-back]' },
|
|
103
251
|
Click: true,
|
|
104
252
|
FlipX: true,
|
|
105
|
-
|
|
106
|
-
Duration:
|
|
107
|
-
Easing: "cubic-bezier(0.68, -0.55, 0.27, 1.55)",
|
|
108
|
-
Template: "<div>My custom back side!</div>",
|
|
253
|
+
Effect: 'scale',
|
|
254
|
+
Duration: 500,
|
|
109
255
|
Style: [
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
style: {
|
|
113
|
-
background: "#fff",
|
|
114
|
-
borderRadius: "1rem",
|
|
115
|
-
boxShadow: "0 2px 8px #0002",
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
{ el: ".front", style: { background: "#f5c939", color: "#222" } },
|
|
119
|
-
{ el: ".backY", style: { background: "#ca9dfe", color: "#fff" } },
|
|
256
|
+
{ el: '[data-front]', style: { background: '#243b2b', borderRadius: '20px' } },
|
|
257
|
+
{ el: '[data-back]', style: { background: '#c1f384', borderRadius: '20px' } },
|
|
120
258
|
],
|
|
121
259
|
});
|
|
122
260
|
```
|
|
261
|
+
|
|
123
262
|
</details>
|
|
124
263
|
|
|
125
|
-
|
|
264
|
+
## Motion and accessibility
|
|
126
265
|
|
|
127
|
-
|
|
266
|
+
- **Reversible transitions:** Rapid input reverses the current CSS transition without accumulating animation objects.
|
|
267
|
+
- **Efficient tilt.** Mouse and finger movement share one animation frame. With `Tilt: true` or custom tilt settings, touch tilts the card until release or until the browser takes over a scroll gesture.
|
|
268
|
+
- **Keyboard:** Focus the card, then use Enter or Space to toggle and Escape to reset.
|
|
269
|
+
- **Touch.** Tap to switch sides with any effect. Hover mode distinguishes a tap from a drag, and click mode uses the browser’s native click. Use `Tilt: 'only'` for touch tilt without tap-to-flip.
|
|
270
|
+
- **Hidden faces:** The inactive face is inert and marked `aria-hidden`.
|
|
271
|
+
- **Reduced motion:** System preference changes disable transitions and tilt automatically. Set `ReducedMotion: false` only when your application intentionally overrides this preference.
|
|
128
272
|
|
|
129
|
-
|
|
130
|
-
- **vanilla**: `justFlipIt-vanilla.js` (CSS transitions only)
|
|
131
|
-
- **combined**: `justFlipIt-combined.js` (Optional jQuery support)
|
|
273
|
+
Targets modern browsers with CSS Grid, Pointer Events, and native `inert` support. Add application-level polyfills when targeting older browsers. SSR safety covers importing the package; DOM initialization requires a browser.
|
|
132
274
|
|
|
133
|
-
|
|
134
|
-
- ✅ Chrome 60+
|
|
135
|
-
- ✅ Firefox 55+
|
|
136
|
-
- ✅ Safari 12+
|
|
137
|
-
- ✅ Edge 79+
|
|
275
|
+
## Upgrading from v3
|
|
138
276
|
|
|
139
|
-
|
|
277
|
+
**v4 is a major release.** Check these changes before upgrading an existing integration:
|
|
140
278
|
|
|
141
|
-
|
|
279
|
+
| v3 integration | v4 equivalent |
|
|
280
|
+
|---|---|
|
|
281
|
+
| UMD-only npm entry | Default ESM import or callable CommonJS export |
|
|
282
|
+
| Optional jQuery plugin registration | Call `justFlipIt(...)` directly |
|
|
283
|
+
| Separate modern / vanilla / combined implementations | One core; historical import paths and browser filenames remain aliases |
|
|
284
|
+
| `.front` / `.backY` generated face selectors | `[data-jfi-face="front"]` / `[data-jfi-face="back"]` |
|
|
285
|
+
| `._flipY_` / `._flip_` state classes | `[data-jfi-flipped="true"]` |
|
|
286
|
+
| Fixed measured dimensions | Responsive grid; add explicit sizes in your design where needed |
|
|
287
|
+
| Framework DOM replacement | Existing direct-child faces via an adapter or `Faces` |
|
|
142
288
|
|
|
143
|
-
|
|
289
|
+
Option names retain PascalCase. `Faces` cannot change after initialization. Teardown restores plugin-owned attributes to their initial values.
|
|
290
|
+
|
|
291
|
+
See the [changelog](CHANGELOG.md) for the release overview.
|
|
292
|
+
|
|
293
|
+
## Development
|
|
294
|
+
|
|
295
|
+
Use Node.js 22 or newer.
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
npm ci
|
|
299
|
+
npm run check # Lifecycle tests and TypeScript consumer checks
|
|
300
|
+
npm run build # ESM, CommonJS, browser bundles, source maps, declarations
|
|
301
|
+
npm pack --dry-run # Inspect published files
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Tests cover original node preservation, teardown, delegated events, agent command validation, reduced motion, tilt batching, React StrictMode, Vue updates, and a compiled Svelte 5 component.
|
|
305
|
+
|
|
306
|
+
See the [release guide](https://github.com/SimHub/justFlipIt/blob/main/RELEASING.md) for the GitHub and npm steps.
|
|
307
|
+
|
|
308
|
+
---
|
|
144
309
|
|
|
145
|
-
|
|
310
|
+
<p align="center">
|
|
311
|
+
Built by <a href="https://github.com/SimHub">SimHub</a> · <a href="LICENSE">MIT license</a> · <a href="https://github.com/SimHub/justFlipIt/issues">Report an issue</a> · <a href="https://www.buymeacoffee.com/simhub">Buy me a coffee</a>
|
|
312
|
+
</p>
|
package/dist/agent.cjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/adapters/agent.js
|
|
20
|
+
var agent_exports = {};
|
|
21
|
+
__export(agent_exports, {
|
|
22
|
+
createAgentBridge: () => createAgentBridge,
|
|
23
|
+
flipTool: () => flipTool
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(agent_exports);
|
|
26
|
+
var flipTool = {
|
|
27
|
+
name: "control_flip_card",
|
|
28
|
+
description: "Inspect or control a registered flip card. Content is plain text.",
|
|
29
|
+
parameters: { type: "object", properties: { id: { type: "string" }, action: { type: "string", enum: ["state", "flip", "reset", "toggle", "setText"] }, text: { type: "string" } }, required: ["id", "action"], additionalProperties: false }
|
|
30
|
+
};
|
|
31
|
+
function createAgentBridge(cards) {
|
|
32
|
+
const registry = new Map(Object.entries(cards));
|
|
33
|
+
return {
|
|
34
|
+
tool: flipTool,
|
|
35
|
+
execute(command) {
|
|
36
|
+
if (!command || typeof command !== "object" || Object.keys(command).some((key) => !["id", "action", "text"].includes(key))) throw new TypeError("Invalid command");
|
|
37
|
+
const card = registry.get(command.id);
|
|
38
|
+
if (!card || card.destroyed) throw new Error("Unknown or destroyed card");
|
|
39
|
+
switch (command.action) {
|
|
40
|
+
case "state":
|
|
41
|
+
break;
|
|
42
|
+
case "flip":
|
|
43
|
+
card.flip(true);
|
|
44
|
+
break;
|
|
45
|
+
case "reset":
|
|
46
|
+
card.flip(false);
|
|
47
|
+
break;
|
|
48
|
+
case "toggle":
|
|
49
|
+
card.toggle();
|
|
50
|
+
break;
|
|
51
|
+
case "setText":
|
|
52
|
+
if (card.managed) throw new Error("Update framework content through application state");
|
|
53
|
+
if (typeof command.text !== "string" || command.text.length > 1e5) throw new TypeError("Expected text up to 100000 characters");
|
|
54
|
+
card.backWrapper.textContent = command.text;
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
throw new TypeError("Unknown action");
|
|
58
|
+
}
|
|
59
|
+
return { id: command.id, ...card.getState() };
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=agent.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/adapters/agent.js"],
|
|
4
|
+
"sourcesContent": ["export const flipTool = {\n name: 'control_flip_card',\n description: 'Inspect or control a registered flip card. Content is plain text.',\n parameters: { type: 'object', properties: { id: { type: 'string' }, action: { type: 'string', enum: ['state', 'flip', 'reset', 'toggle', 'setText'] }, text: { type: 'string' } }, required: ['id', 'action'], additionalProperties: false }\n};\nexport function createAgentBridge(cards) {\n const registry = new Map(Object.entries(cards));\n return {\n tool: flipTool,\n execute(command) {\n if (!command || typeof command !== 'object' || Object.keys(command).some(key => !['id', 'action', 'text'].includes(key))) throw new TypeError('Invalid command');\n const card = registry.get(command.id);\n if (!card || card.destroyed) throw new Error('Unknown or destroyed card');\n switch (command.action) {\n case 'state': break;\n case 'flip': card.flip(true); break;\n case 'reset': card.flip(false); break;\n case 'toggle': card.toggle(); break;\n case 'setText':\n if (card.managed) throw new Error('Update framework content through application state');\n if (typeof command.text !== 'string' || command.text.length > 100000) throw new TypeError('Expected text up to 100000 characters');\n card.backWrapper.textContent = command.text; break;\n default: throw new TypeError('Unknown action');\n }\n return { id: command.id, ...card.getState() };\n }\n };\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,IAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY,EAAE,MAAM,UAAU,YAAY,EAAE,IAAI,EAAE,MAAM,SAAS,GAAG,QAAQ,EAAE,MAAM,UAAU,MAAM,CAAC,SAAS,QAAQ,SAAS,UAAU,SAAS,EAAE,GAAG,MAAM,EAAE,MAAM,SAAS,EAAE,GAAG,UAAU,CAAC,MAAM,QAAQ,GAAG,sBAAsB,MAAM;AAC7O;AACO,SAAS,kBAAkB,OAAO;AACvC,QAAM,WAAW,IAAI,IAAI,OAAO,QAAQ,KAAK,CAAC;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,SAAS;AACf,UAAI,CAAC,WAAW,OAAO,YAAY,YAAY,OAAO,KAAK,OAAO,EAAE,KAAK,SAAO,CAAC,CAAC,MAAM,UAAU,MAAM,EAAE,SAAS,GAAG,CAAC,EAAG,OAAM,IAAI,UAAU,iBAAiB;AAC/J,YAAM,OAAO,SAAS,IAAI,QAAQ,EAAE;AACpC,UAAI,CAAC,QAAQ,KAAK,UAAW,OAAM,IAAI,MAAM,2BAA2B;AACxE,cAAQ,QAAQ,QAAQ;AAAA,QACtB,KAAK;AAAS;AAAA,QACd,KAAK;AAAQ,eAAK,KAAK,IAAI;AAAG;AAAA,QAC9B,KAAK;AAAS,eAAK,KAAK,KAAK;AAAG;AAAA,QAChC,KAAK;AAAU,eAAK,OAAO;AAAG;AAAA,QAC9B,KAAK;AACH,cAAI,KAAK,QAAS,OAAM,IAAI,MAAM,oDAAoD;AACtF,cAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,SAAS,IAAQ,OAAM,IAAI,UAAU,uCAAuC;AACjI,eAAK,YAAY,cAAc,QAAQ;AAAM;AAAA,QAC/C;AAAS,gBAAM,IAAI,UAAU,gBAAgB;AAAA,MAC/C;AACA,aAAO,EAAE,IAAI,QAAQ,IAAI,GAAG,KAAK,SAAS,EAAE;AAAA,IAC9C;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/agent.d.cts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FlipController, FlipState } from './justFlipIt.mjs';
|
|
2
|
+
export interface AgentCommand { id: string; action: 'state' | 'flip' | 'reset' | 'toggle' | 'setText'; text?: string }
|
|
3
|
+
export const flipTool: { name: string; description: string; parameters: Record<string, unknown> };
|
|
4
|
+
export function createAgentBridge(cards: Record<string, FlipController>): { tool: typeof flipTool; execute(command: AgentCommand): FlipState & { id: string } };
|
package/dist/agent.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FlipController, FlipState } from './justFlipIt.mjs';
|
|
2
|
+
export interface AgentCommand { id: string; action: 'state' | 'flip' | 'reset' | 'toggle' | 'setText'; text?: string }
|
|
3
|
+
export const flipTool: { name: string; description: string; parameters: Record<string, unknown> };
|
|
4
|
+
export function createAgentBridge(cards: Record<string, FlipController>): { tool: typeof flipTool; execute(command: AgentCommand): FlipState & { id: string } };
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FlipController, FlipState } from './justFlipIt';
|
|
2
|
+
export interface AgentCommand { id: string; action: 'state' | 'flip' | 'reset' | 'toggle' | 'setText'; text?: string }
|
|
3
|
+
export const flipTool: { name: string; description: string; parameters: Record<string, unknown> };
|
|
4
|
+
export function createAgentBridge(cards: Record<string, FlipController>): { tool: typeof flipTool; execute(command: AgentCommand): FlipState & { id: string } };
|
package/dist/agent.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/adapters/agent.js
|
|
2
|
+
var flipTool = {
|
|
3
|
+
name: "control_flip_card",
|
|
4
|
+
description: "Inspect or control a registered flip card. Content is plain text.",
|
|
5
|
+
parameters: { type: "object", properties: { id: { type: "string" }, action: { type: "string", enum: ["state", "flip", "reset", "toggle", "setText"] }, text: { type: "string" } }, required: ["id", "action"], additionalProperties: false }
|
|
6
|
+
};
|
|
7
|
+
function createAgentBridge(cards) {
|
|
8
|
+
const registry = new Map(Object.entries(cards));
|
|
9
|
+
return {
|
|
10
|
+
tool: flipTool,
|
|
11
|
+
execute(command) {
|
|
12
|
+
if (!command || typeof command !== "object" || Object.keys(command).some((key) => !["id", "action", "text"].includes(key))) throw new TypeError("Invalid command");
|
|
13
|
+
const card = registry.get(command.id);
|
|
14
|
+
if (!card || card.destroyed) throw new Error("Unknown or destroyed card");
|
|
15
|
+
switch (command.action) {
|
|
16
|
+
case "state":
|
|
17
|
+
break;
|
|
18
|
+
case "flip":
|
|
19
|
+
card.flip(true);
|
|
20
|
+
break;
|
|
21
|
+
case "reset":
|
|
22
|
+
card.flip(false);
|
|
23
|
+
break;
|
|
24
|
+
case "toggle":
|
|
25
|
+
card.toggle();
|
|
26
|
+
break;
|
|
27
|
+
case "setText":
|
|
28
|
+
if (card.managed) throw new Error("Update framework content through application state");
|
|
29
|
+
if (typeof command.text !== "string" || command.text.length > 1e5) throw new TypeError("Expected text up to 100000 characters");
|
|
30
|
+
card.backWrapper.textContent = command.text;
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
throw new TypeError("Unknown action");
|
|
34
|
+
}
|
|
35
|
+
return { id: command.id, ...card.getState() };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
createAgentBridge,
|
|
41
|
+
flipTool
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=agent.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/adapters/agent.js"],
|
|
4
|
+
"sourcesContent": ["export const flipTool = {\n name: 'control_flip_card',\n description: 'Inspect or control a registered flip card. Content is plain text.',\n parameters: { type: 'object', properties: { id: { type: 'string' }, action: { type: 'string', enum: ['state', 'flip', 'reset', 'toggle', 'setText'] }, text: { type: 'string' } }, required: ['id', 'action'], additionalProperties: false }\n};\nexport function createAgentBridge(cards) {\n const registry = new Map(Object.entries(cards));\n return {\n tool: flipTool,\n execute(command) {\n if (!command || typeof command !== 'object' || Object.keys(command).some(key => !['id', 'action', 'text'].includes(key))) throw new TypeError('Invalid command');\n const card = registry.get(command.id);\n if (!card || card.destroyed) throw new Error('Unknown or destroyed card');\n switch (command.action) {\n case 'state': break;\n case 'flip': card.flip(true); break;\n case 'reset': card.flip(false); break;\n case 'toggle': card.toggle(); break;\n case 'setText':\n if (card.managed) throw new Error('Update framework content through application state');\n if (typeof command.text !== 'string' || command.text.length > 100000) throw new TypeError('Expected text up to 100000 characters');\n card.backWrapper.textContent = command.text; break;\n default: throw new TypeError('Unknown action');\n }\n return { id: command.id, ...card.getState() };\n }\n };\n}\n"],
|
|
5
|
+
"mappings": ";AAAO,IAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY,EAAE,MAAM,UAAU,YAAY,EAAE,IAAI,EAAE,MAAM,SAAS,GAAG,QAAQ,EAAE,MAAM,UAAU,MAAM,CAAC,SAAS,QAAQ,SAAS,UAAU,SAAS,EAAE,GAAG,MAAM,EAAE,MAAM,SAAS,EAAE,GAAG,UAAU,CAAC,MAAM,QAAQ,GAAG,sBAAsB,MAAM;AAC7O;AACO,SAAS,kBAAkB,OAAO;AACvC,QAAM,WAAW,IAAI,IAAI,OAAO,QAAQ,KAAK,CAAC;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,SAAS;AACf,UAAI,CAAC,WAAW,OAAO,YAAY,YAAY,OAAO,KAAK,OAAO,EAAE,KAAK,SAAO,CAAC,CAAC,MAAM,UAAU,MAAM,EAAE,SAAS,GAAG,CAAC,EAAG,OAAM,IAAI,UAAU,iBAAiB;AAC/J,YAAM,OAAO,SAAS,IAAI,QAAQ,EAAE;AACpC,UAAI,CAAC,QAAQ,KAAK,UAAW,OAAM,IAAI,MAAM,2BAA2B;AACxE,cAAQ,QAAQ,QAAQ;AAAA,QACtB,KAAK;AAAS;AAAA,QACd,KAAK;AAAQ,eAAK,KAAK,IAAI;AAAG;AAAA,QAC9B,KAAK;AAAS,eAAK,KAAK,KAAK;AAAG;AAAA,QAChC,KAAK;AAAU,eAAK,OAAO;AAAG;AAAA,QAC9B,KAAK;AACH,cAAI,KAAK,QAAS,OAAM,IAAI,MAAM,oDAAoD;AACtF,cAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,SAAS,IAAQ,OAAM,IAAI,UAAU,uCAAuC;AACjI,eAAK,YAAY,cAAc,QAAQ;AAAM;AAAA,QAC/C;AAAS,gBAAM,IAAI,UAAU,gBAAgB;AAAA,MAC/C;AACA,aAAO,EAAE,IAAI,QAAQ,IAAI,GAAG,KAAK,SAAS,EAAE;AAAA,IAC9C;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|