stellar-drive 1.0.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 +607 -0
- package/dist/actions/remoteChange.d.ts +204 -0
- package/dist/actions/remoteChange.d.ts.map +1 -0
- package/dist/actions/remoteChange.js +424 -0
- package/dist/actions/remoteChange.js.map +1 -0
- package/dist/actions/truncateTooltip.d.ts +56 -0
- package/dist/actions/truncateTooltip.d.ts.map +1 -0
- package/dist/actions/truncateTooltip.js +312 -0
- package/dist/actions/truncateTooltip.js.map +1 -0
- package/dist/auth/crypto.d.ts +41 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +50 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/deviceVerification.d.ts +283 -0
- package/dist/auth/deviceVerification.d.ts.map +1 -0
- package/dist/auth/deviceVerification.js +575 -0
- package/dist/auth/deviceVerification.js.map +1 -0
- package/dist/auth/displayUtils.d.ts +98 -0
- package/dist/auth/displayUtils.d.ts.map +1 -0
- package/dist/auth/displayUtils.js +145 -0
- package/dist/auth/displayUtils.js.map +1 -0
- package/dist/auth/loginGuard.d.ts +134 -0
- package/dist/auth/loginGuard.d.ts.map +1 -0
- package/dist/auth/loginGuard.js +276 -0
- package/dist/auth/loginGuard.js.map +1 -0
- package/dist/auth/offlineCredentials.d.ts +105 -0
- package/dist/auth/offlineCredentials.d.ts.map +1 -0
- package/dist/auth/offlineCredentials.js +176 -0
- package/dist/auth/offlineCredentials.js.map +1 -0
- package/dist/auth/offlineSession.d.ts +96 -0
- package/dist/auth/offlineSession.d.ts.map +1 -0
- package/dist/auth/offlineSession.js +145 -0
- package/dist/auth/offlineSession.js.map +1 -0
- package/dist/auth/resolveAuthState.d.ts +85 -0
- package/dist/auth/resolveAuthState.d.ts.map +1 -0
- package/dist/auth/resolveAuthState.js +249 -0
- package/dist/auth/resolveAuthState.js.map +1 -0
- package/dist/auth/singleUser.d.ts +498 -0
- package/dist/auth/singleUser.d.ts.map +1 -0
- package/dist/auth/singleUser.js +1282 -0
- package/dist/auth/singleUser.js.map +1 -0
- package/dist/bin/commands.d.ts +14 -0
- package/dist/bin/commands.d.ts.map +1 -0
- package/dist/bin/commands.js +68 -0
- package/dist/bin/commands.js.map +1 -0
- package/dist/bin/install-pwa.d.ts +41 -0
- package/dist/bin/install-pwa.d.ts.map +1 -0
- package/dist/bin/install-pwa.js +4594 -0
- package/dist/bin/install-pwa.js.map +1 -0
- package/dist/config.d.ts +249 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +395 -0
- package/dist/config.js.map +1 -0
- package/dist/conflicts.d.ts +306 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +807 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/crdt/awareness.d.ts +128 -0
- package/dist/crdt/awareness.d.ts.map +1 -0
- package/dist/crdt/awareness.js +284 -0
- package/dist/crdt/awareness.js.map +1 -0
- package/dist/crdt/channel.d.ts +165 -0
- package/dist/crdt/channel.d.ts.map +1 -0
- package/dist/crdt/channel.js +522 -0
- package/dist/crdt/channel.js.map +1 -0
- package/dist/crdt/config.d.ts +58 -0
- package/dist/crdt/config.d.ts.map +1 -0
- package/dist/crdt/config.js +123 -0
- package/dist/crdt/config.js.map +1 -0
- package/dist/crdt/helpers.d.ts +104 -0
- package/dist/crdt/helpers.d.ts.map +1 -0
- package/dist/crdt/helpers.js +116 -0
- package/dist/crdt/helpers.js.map +1 -0
- package/dist/crdt/offline.d.ts +58 -0
- package/dist/crdt/offline.d.ts.map +1 -0
- package/dist/crdt/offline.js +130 -0
- package/dist/crdt/offline.js.map +1 -0
- package/dist/crdt/persistence.d.ts +65 -0
- package/dist/crdt/persistence.d.ts.map +1 -0
- package/dist/crdt/persistence.js +171 -0
- package/dist/crdt/persistence.js.map +1 -0
- package/dist/crdt/provider.d.ts +109 -0
- package/dist/crdt/provider.d.ts.map +1 -0
- package/dist/crdt/provider.js +543 -0
- package/dist/crdt/provider.js.map +1 -0
- package/dist/crdt/store.d.ts +111 -0
- package/dist/crdt/store.d.ts.map +1 -0
- package/dist/crdt/store.js +158 -0
- package/dist/crdt/store.js.map +1 -0
- package/dist/crdt/types.d.ts +281 -0
- package/dist/crdt/types.d.ts.map +1 -0
- package/dist/crdt/types.js +26 -0
- package/dist/crdt/types.js.map +1 -0
- package/dist/data.d.ts +502 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +862 -0
- package/dist/data.js.map +1 -0
- package/dist/database.d.ts +153 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +325 -0
- package/dist/database.js.map +1 -0
- package/dist/debug.d.ts +87 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +135 -0
- package/dist/debug.js.map +1 -0
- package/dist/demo.d.ts +131 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +168 -0
- package/dist/demo.js.map +1 -0
- package/dist/deviceId.d.ts +47 -0
- package/dist/deviceId.d.ts.map +1 -0
- package/dist/deviceId.js +106 -0
- package/dist/deviceId.js.map +1 -0
- package/dist/diagnostics.d.ts +292 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +378 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/engine.d.ts +230 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +2636 -0
- package/dist/engine.js.map +1 -0
- package/dist/entries/actions.d.ts +16 -0
- package/dist/entries/actions.d.ts.map +1 -0
- package/dist/entries/actions.js +29 -0
- package/dist/entries/actions.js.map +1 -0
- package/dist/entries/auth.d.ts +19 -0
- package/dist/entries/auth.d.ts.map +1 -0
- package/dist/entries/auth.js +50 -0
- package/dist/entries/auth.js.map +1 -0
- package/dist/entries/config.d.ts +15 -0
- package/dist/entries/config.d.ts.map +1 -0
- package/dist/entries/config.js +20 -0
- package/dist/entries/config.js.map +1 -0
- package/dist/entries/crdt.d.ts +32 -0
- package/dist/entries/crdt.d.ts.map +1 -0
- package/dist/entries/crdt.js +52 -0
- package/dist/entries/crdt.js.map +1 -0
- package/dist/entries/kit.d.ts +22 -0
- package/dist/entries/kit.d.ts.map +1 -0
- package/dist/entries/kit.js +58 -0
- package/dist/entries/kit.js.map +1 -0
- package/dist/entries/stores.d.ts +22 -0
- package/dist/entries/stores.d.ts.map +1 -0
- package/dist/entries/stores.js +57 -0
- package/dist/entries/stores.js.map +1 -0
- package/dist/entries/types.d.ts +23 -0
- package/dist/entries/types.d.ts.map +1 -0
- package/dist/entries/types.js +12 -0
- package/dist/entries/types.js.map +1 -0
- package/dist/entries/utils.d.ts +12 -0
- package/dist/entries/utils.d.ts.map +1 -0
- package/dist/entries/utils.js +42 -0
- package/dist/entries/utils.js.map +1 -0
- package/dist/entries/vite.d.ts +20 -0
- package/dist/entries/vite.d.ts.map +1 -0
- package/dist/entries/vite.js +26 -0
- package/dist/entries/vite.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +234 -0
- package/dist/index.js.map +1 -0
- package/dist/kit/auth.d.ts +80 -0
- package/dist/kit/auth.d.ts.map +1 -0
- package/dist/kit/auth.js +75 -0
- package/dist/kit/auth.js.map +1 -0
- package/dist/kit/confirm.d.ts +111 -0
- package/dist/kit/confirm.d.ts.map +1 -0
- package/dist/kit/confirm.js +169 -0
- package/dist/kit/confirm.js.map +1 -0
- package/dist/kit/loads.d.ts +187 -0
- package/dist/kit/loads.d.ts.map +1 -0
- package/dist/kit/loads.js +208 -0
- package/dist/kit/loads.js.map +1 -0
- package/dist/kit/server.d.ts +175 -0
- package/dist/kit/server.d.ts.map +1 -0
- package/dist/kit/server.js +297 -0
- package/dist/kit/server.js.map +1 -0
- package/dist/kit/sw.d.ts +176 -0
- package/dist/kit/sw.d.ts.map +1 -0
- package/dist/kit/sw.js +320 -0
- package/dist/kit/sw.js.map +1 -0
- package/dist/queue.d.ts +306 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +925 -0
- package/dist/queue.js.map +1 -0
- package/dist/realtime.d.ts +280 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +1031 -0
- package/dist/realtime.js.map +1 -0
- package/dist/runtime/runtimeConfig.d.ts +110 -0
- package/dist/runtime/runtimeConfig.d.ts.map +1 -0
- package/dist/runtime/runtimeConfig.js +260 -0
- package/dist/runtime/runtimeConfig.js.map +1 -0
- package/dist/schema.d.ts +150 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +891 -0
- package/dist/schema.js.map +1 -0
- package/dist/stores/authState.d.ts +204 -0
- package/dist/stores/authState.d.ts.map +1 -0
- package/dist/stores/authState.js +336 -0
- package/dist/stores/authState.js.map +1 -0
- package/dist/stores/factories.d.ts +140 -0
- package/dist/stores/factories.d.ts.map +1 -0
- package/dist/stores/factories.js +157 -0
- package/dist/stores/factories.js.map +1 -0
- package/dist/stores/network.d.ts +48 -0
- package/dist/stores/network.d.ts.map +1 -0
- package/dist/stores/network.js +261 -0
- package/dist/stores/network.js.map +1 -0
- package/dist/stores/remoteChanges.d.ts +417 -0
- package/dist/stores/remoteChanges.d.ts.map +1 -0
- package/dist/stores/remoteChanges.js +626 -0
- package/dist/stores/remoteChanges.js.map +1 -0
- package/dist/stores/sync.d.ts +165 -0
- package/dist/stores/sync.d.ts.map +1 -0
- package/dist/stores/sync.js +275 -0
- package/dist/stores/sync.js.map +1 -0
- package/dist/supabase/auth.d.ts +219 -0
- package/dist/supabase/auth.d.ts.map +1 -0
- package/dist/supabase/auth.js +459 -0
- package/dist/supabase/auth.js.map +1 -0
- package/dist/supabase/client.d.ts +88 -0
- package/dist/supabase/client.d.ts.map +1 -0
- package/dist/supabase/client.js +313 -0
- package/dist/supabase/client.js.map +1 -0
- package/dist/supabase/validate.d.ts +118 -0
- package/dist/supabase/validate.d.ts.map +1 -0
- package/dist/supabase/validate.js +208 -0
- package/dist/supabase/validate.js.map +1 -0
- package/dist/sw/build/vite-plugin.d.ts +149 -0
- package/dist/sw/build/vite-plugin.d.ts.map +1 -0
- package/dist/sw/build/vite-plugin.js +517 -0
- package/dist/sw/build/vite-plugin.js.map +1 -0
- package/dist/sw/sw.js +664 -0
- package/dist/types.d.ts +363 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +85 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +156 -0
- package/dist/utils.js.map +1 -0
- package/package.json +117 -0
- package/src/components/DeferredChangesBanner.svelte +477 -0
- package/src/components/DemoBanner.svelte +110 -0
- package/src/components/SyncStatus.svelte +1732 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Remote Change Animation Action
|
|
3
|
+
*
|
|
4
|
+
* A Svelte action that automatically adds remote change animations to elements.
|
|
5
|
+
* Use this on list items, cards, or any element that can be updated remotely
|
|
6
|
+
* (e.g., via Supabase Realtime subscriptions).
|
|
7
|
+
*
|
|
8
|
+
* The action detects the ACTION TYPE from the remote change and applies
|
|
9
|
+
* the appropriate CSS animation class:
|
|
10
|
+
* - `'create'` --> `item-created` (slide in with burst)
|
|
11
|
+
* - `'delete'` --> `item-deleting` (slide out with fade)
|
|
12
|
+
* - `'toggle'` --> `item-toggled` (+ checkbox-animating + completion-ripple)
|
|
13
|
+
* - `'increment'` --> `counter-increment` (bump up)
|
|
14
|
+
* - `'decrement'` --> `counter-decrement` (bump down)
|
|
15
|
+
* - `'reorder'` --> `item-reordering` (slide to new position)
|
|
16
|
+
* - `'rename'` --> `text-changed` (highlight flash)
|
|
17
|
+
* - `'update'` --> `item-changed` (default highlight)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```svelte
|
|
21
|
+
* <div use:remoteChangeAnimation={{ entityId: item.id, entityType: 'goals' }}>
|
|
22
|
+
* ...
|
|
23
|
+
* </div>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link remoteChangeAnimation} for the main Svelte action
|
|
27
|
+
* @see {@link trackEditing} for deferred-change tracking on forms
|
|
28
|
+
* @see {@link triggerLocalAnimation} for programmatic local animations
|
|
29
|
+
*/
|
|
30
|
+
import { type RemoteActionType } from '../stores/remoteChanges';
|
|
31
|
+
/**
|
|
32
|
+
* Configuration options for the {@link remoteChangeAnimation} Svelte action.
|
|
33
|
+
*/
|
|
34
|
+
interface RemoteChangeOptions {
|
|
35
|
+
/** The unique identifier of the entity being watched (e.g., a row UUID). */
|
|
36
|
+
entityId: string;
|
|
37
|
+
/** The entity type / table name (e.g., `'goals'`, `'tasks'`). */
|
|
38
|
+
entityType: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional list of field names to watch. When provided, animations are
|
|
41
|
+
* only triggered if the remote change includes at least one of these
|
|
42
|
+
* fields (or the wildcard `'*'`). Omit to animate on any field change.
|
|
43
|
+
*/
|
|
44
|
+
fields?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Optional CSS class override. When set, this class is used instead of
|
|
47
|
+
* the default mapping from {@link ACTION_ANIMATION_MAP}.
|
|
48
|
+
*/
|
|
49
|
+
animationClass?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Optional callback invoked when a remote action is detected.
|
|
52
|
+
* Useful for component-specific handling beyond CSS animations
|
|
53
|
+
* (e.g., updating local state, playing sounds, showing toasts).
|
|
54
|
+
*
|
|
55
|
+
* @param actionType - The type of remote action detected.
|
|
56
|
+
* @param fields - The list of fields that changed.
|
|
57
|
+
*/
|
|
58
|
+
onAction?: (actionType: RemoteActionType, fields: string[]) => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Svelte action that watches for remote changes on a specific entity and
|
|
62
|
+
* applies the appropriate CSS animation class to the host element.
|
|
63
|
+
*
|
|
64
|
+
* **Lifecycle:**
|
|
65
|
+
* 1. On mount, checks for a recent change that may have arrived before
|
|
66
|
+
* the element was rendered (important for CREATE animations on new items).
|
|
67
|
+
* 2. Subscribes to the `remoteChangesStore` for future changes.
|
|
68
|
+
* 3. Subscribes to a pending-delete indicator for delete animations.
|
|
69
|
+
* 4. On update, re-subscribes if the entity identity changes.
|
|
70
|
+
* 5. On destroy, unsubscribes and cleans up CSS classes.
|
|
71
|
+
*
|
|
72
|
+
* @param node - The DOM element to animate.
|
|
73
|
+
* @param options - Configuration specifying which entity to watch.
|
|
74
|
+
* @returns A Svelte action lifecycle object with `update` and `destroy` methods.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```svelte
|
|
78
|
+
* <div use:remoteChangeAnimation={{ entityId: item.id, entityType: 'goals' }}>
|
|
79
|
+
* {item.name}
|
|
80
|
+
* </div>
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function remoteChangeAnimation(node: HTMLElement, options: RemoteChangeOptions): {
|
|
84
|
+
/**
|
|
85
|
+
* Called when the action's options change. If the entity identity
|
|
86
|
+
* (`entityId` or `entityType`) has changed, tears down old subscriptions
|
|
87
|
+
* and creates new ones for the updated entity.
|
|
88
|
+
*
|
|
89
|
+
* @param newOptions - The updated {@link RemoteChangeOptions}.
|
|
90
|
+
*/
|
|
91
|
+
update(newOptions: RemoteChangeOptions): void;
|
|
92
|
+
/**
|
|
93
|
+
* Cleanup handler — unsubscribes from all stores, removes the base
|
|
94
|
+
* CSS class, and clears the element from the animation tracking set.
|
|
95
|
+
*/
|
|
96
|
+
destroy(): void;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Svelte action for form elements that should track editing state.
|
|
100
|
+
* Use this on modal forms with Save buttons to defer remote changes
|
|
101
|
+
* while the user is actively editing, preventing disruptive overwrites.
|
|
102
|
+
*
|
|
103
|
+
* When the form is destroyed (e.g., modal closes), any deferred changes
|
|
104
|
+
* are passed to the `onDeferredChanges` callback so the component can
|
|
105
|
+
* decide how to reconcile them.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```svelte
|
|
109
|
+
* <form use:trackEditing={{ entityId: item.id, entityType: 'goals', formType: 'manual-save' }}>
|
|
110
|
+
* ...
|
|
111
|
+
* </form>
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Configuration options for the {@link trackEditing} Svelte action.
|
|
116
|
+
*/
|
|
117
|
+
interface TrackEditingOptions {
|
|
118
|
+
/** The unique identifier of the entity being edited. */
|
|
119
|
+
entityId: string;
|
|
120
|
+
/** The entity type / table name (e.g., `'goals'`, `'tasks'`). */
|
|
121
|
+
entityType: string;
|
|
122
|
+
/**
|
|
123
|
+
* The save behaviour of the form:
|
|
124
|
+
* - `'auto-save'` — changes are saved immediately (e.g., inline editing).
|
|
125
|
+
* - `'manual-save'` — changes are saved on explicit submit (e.g., modal form).
|
|
126
|
+
*/
|
|
127
|
+
formType: 'auto-save' | 'manual-save';
|
|
128
|
+
/**
|
|
129
|
+
* Optional list of field names this form edits. When provided, only
|
|
130
|
+
* remote changes to these fields are deferred; changes to other fields
|
|
131
|
+
* are applied immediately.
|
|
132
|
+
*/
|
|
133
|
+
fields?: string[];
|
|
134
|
+
/**
|
|
135
|
+
* Callback invoked when the form closes and there are deferred changes
|
|
136
|
+
* that need processing (e.g., conflict resolution, data refresh).
|
|
137
|
+
*
|
|
138
|
+
* @param changes - The array of deferred remote change objects.
|
|
139
|
+
*/
|
|
140
|
+
onDeferredChanges?: (changes: unknown[]) => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Svelte action that marks an entity as "being edited" in the remote changes
|
|
144
|
+
* store. While editing, incoming remote changes for the same entity are
|
|
145
|
+
* deferred instead of applied immediately.
|
|
146
|
+
*
|
|
147
|
+
* **Lifecycle:**
|
|
148
|
+
* 1. On mount, calls `remoteChangesStore.startEditing()` to begin deferral.
|
|
149
|
+
* 2. Periodically checks for deferred changes and toggles a CSS class
|
|
150
|
+
* (`has-deferred-changes`) on the node for visual indication.
|
|
151
|
+
* 3. On update, re-registers if the entity identity changes.
|
|
152
|
+
* 4. On destroy, calls `remoteChangesStore.stopEditing()` and invokes
|
|
153
|
+
* `onDeferredChanges` if any changes were deferred.
|
|
154
|
+
*
|
|
155
|
+
* @param node - The form DOM element.
|
|
156
|
+
* @param options - Configuration specifying which entity is being edited.
|
|
157
|
+
* @returns A Svelte action lifecycle object with `update` and `destroy` methods.
|
|
158
|
+
*/
|
|
159
|
+
export declare function trackEditing(node: HTMLElement, options: TrackEditingOptions): {
|
|
160
|
+
/**
|
|
161
|
+
* Called when the action's options change. If the entity identity
|
|
162
|
+
* changes, stops tracking the old entity and starts tracking the new one.
|
|
163
|
+
*
|
|
164
|
+
* @param newOptions - The updated {@link TrackEditingOptions}.
|
|
165
|
+
*/
|
|
166
|
+
update(newOptions: TrackEditingOptions): void;
|
|
167
|
+
/**
|
|
168
|
+
* Cleanup handler — stops the polling interval, removes CSS classes,
|
|
169
|
+
* stops editing in the store, and notifies the callback of any
|
|
170
|
+
* deferred changes that accumulated during the editing session.
|
|
171
|
+
*/
|
|
172
|
+
destroy(): void;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Trigger a local action animation on an element.
|
|
176
|
+
*
|
|
177
|
+
* Use this to make local user actions (e.g., tapping a checkbox, incrementing
|
|
178
|
+
* a counter) animate with the same visual treatment as remote changes, giving
|
|
179
|
+
* the UI a consistent feel.
|
|
180
|
+
*
|
|
181
|
+
* For `increment` and `decrement` actions, rapid repeated invocations will
|
|
182
|
+
* restart the animation instead of being blocked — this allows the counter
|
|
183
|
+
* to visually "bump" on each tap.
|
|
184
|
+
*
|
|
185
|
+
* @param element - The DOM element to animate (or `null`, in which case this is a no-op).
|
|
186
|
+
* @param actionType - The type of animation to apply.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```svelte
|
|
190
|
+
* <script>
|
|
191
|
+
* import { triggerLocalAnimation } from 'stellar-drive';
|
|
192
|
+
* let element: HTMLElement;
|
|
193
|
+
*
|
|
194
|
+
* function handleToggle() {
|
|
195
|
+
* triggerLocalAnimation(element, 'toggle');
|
|
196
|
+
* onToggle?.();
|
|
197
|
+
* }
|
|
198
|
+
* </script>
|
|
199
|
+
* <div bind:this={element}>...</div>
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export declare function triggerLocalAnimation(element: HTMLElement | null, actionType: RemoteActionType): void;
|
|
203
|
+
export {};
|
|
204
|
+
//# sourceMappingURL=remoteChange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteChange.d.ts","sourceRoot":"","sources":["../../src/actions/remoteChange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAMjC;;GAEG;AACH,UAAU,mBAAmB;IAC3B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IAEjB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACrE;AAsDD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB;IAgJjF;;;;;;OAMG;uBACgB,mBAAmB;IA4BtC;;;OAGG;;EAQN;AAMD;;;;;;;;;;;;;;;GAeG;AAEH;;GAEG;AACH,UAAU,mBAAmB;IAC3B,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IAEjB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,EAAE,WAAW,GAAG,aAAa,CAAC;IAEtC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CAClD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB;IAwBxE;;;;;OAKG;uBACgB,mBAAmB;IAatC;;;;OAIG;;EAcN;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,UAAU,EAAE,gBAAgB,GAC3B,IAAI,CA8DN"}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Remote Change Animation Action
|
|
3
|
+
*
|
|
4
|
+
* A Svelte action that automatically adds remote change animations to elements.
|
|
5
|
+
* Use this on list items, cards, or any element that can be updated remotely
|
|
6
|
+
* (e.g., via Supabase Realtime subscriptions).
|
|
7
|
+
*
|
|
8
|
+
* The action detects the ACTION TYPE from the remote change and applies
|
|
9
|
+
* the appropriate CSS animation class:
|
|
10
|
+
* - `'create'` --> `item-created` (slide in with burst)
|
|
11
|
+
* - `'delete'` --> `item-deleting` (slide out with fade)
|
|
12
|
+
* - `'toggle'` --> `item-toggled` (+ checkbox-animating + completion-ripple)
|
|
13
|
+
* - `'increment'` --> `counter-increment` (bump up)
|
|
14
|
+
* - `'decrement'` --> `counter-decrement` (bump down)
|
|
15
|
+
* - `'reorder'` --> `item-reordering` (slide to new position)
|
|
16
|
+
* - `'rename'` --> `text-changed` (highlight flash)
|
|
17
|
+
* - `'update'` --> `item-changed` (default highlight)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```svelte
|
|
21
|
+
* <div use:remoteChangeAnimation={{ entityId: item.id, entityType: 'goals' }}>
|
|
22
|
+
* ...
|
|
23
|
+
* </div>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link remoteChangeAnimation} for the main Svelte action
|
|
27
|
+
* @see {@link trackEditing} for deferred-change tracking on forms
|
|
28
|
+
* @see {@link triggerLocalAnimation} for programmatic local animations
|
|
29
|
+
*/
|
|
30
|
+
import { remoteChangesStore, createRecentChangeIndicator, createPendingDeleteIndicator } from '../stores/remoteChanges';
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// ACTION-TO-CSS ANIMATION MAPPING
|
|
33
|
+
// =============================================================================
|
|
34
|
+
/**
|
|
35
|
+
* Maps each {@link RemoteActionType} to the CSS class name that triggers
|
|
36
|
+
* the corresponding animation. The consuming app must define these CSS
|
|
37
|
+
* classes (keyframes + durations) in its stylesheet.
|
|
38
|
+
*/
|
|
39
|
+
const ACTION_ANIMATION_MAP = {
|
|
40
|
+
create: 'item-created',
|
|
41
|
+
delete: 'item-deleting',
|
|
42
|
+
toggle: 'item-toggled',
|
|
43
|
+
increment: 'counter-increment',
|
|
44
|
+
decrement: 'counter-decrement',
|
|
45
|
+
reorder: 'item-reordering',
|
|
46
|
+
rename: 'text-changed',
|
|
47
|
+
update: 'item-changed'
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Maps each {@link RemoteActionType} to its animation duration in
|
|
51
|
+
* milliseconds. Used for fallback cleanup timers in case the
|
|
52
|
+
* `animationend` DOM event never fires (e.g., display:none elements).
|
|
53
|
+
*/
|
|
54
|
+
const ACTION_DURATION_MAP = {
|
|
55
|
+
create: 600,
|
|
56
|
+
delete: 500,
|
|
57
|
+
toggle: 600,
|
|
58
|
+
increment: 400,
|
|
59
|
+
decrement: 400,
|
|
60
|
+
reorder: 400,
|
|
61
|
+
rename: 700,
|
|
62
|
+
update: 1600
|
|
63
|
+
};
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// ANIMATION OVERLAP PREVENTION
|
|
66
|
+
// =============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Tracks elements that currently have an active animation. Prevents
|
|
69
|
+
* overlapping animations on the same element which would cause visual
|
|
70
|
+
* glitches. Uses `WeakSet` so entries are automatically garbage-collected
|
|
71
|
+
* when the element is removed from the DOM.
|
|
72
|
+
*/
|
|
73
|
+
const animatingElements = new WeakSet();
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// SVELTE ACTION: remoteChangeAnimation
|
|
76
|
+
// =============================================================================
|
|
77
|
+
/**
|
|
78
|
+
* Svelte action that watches for remote changes on a specific entity and
|
|
79
|
+
* applies the appropriate CSS animation class to the host element.
|
|
80
|
+
*
|
|
81
|
+
* **Lifecycle:**
|
|
82
|
+
* 1. On mount, checks for a recent change that may have arrived before
|
|
83
|
+
* the element was rendered (important for CREATE animations on new items).
|
|
84
|
+
* 2. Subscribes to the `remoteChangesStore` for future changes.
|
|
85
|
+
* 3. Subscribes to a pending-delete indicator for delete animations.
|
|
86
|
+
* 4. On update, re-subscribes if the entity identity changes.
|
|
87
|
+
* 5. On destroy, unsubscribes and cleans up CSS classes.
|
|
88
|
+
*
|
|
89
|
+
* @param node - The DOM element to animate.
|
|
90
|
+
* @param options - Configuration specifying which entity to watch.
|
|
91
|
+
* @returns A Svelte action lifecycle object with `update` and `destroy` methods.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```svelte
|
|
95
|
+
* <div use:remoteChangeAnimation={{ entityId: item.id, entityType: 'goals' }}>
|
|
96
|
+
* {item.name}
|
|
97
|
+
* </div>
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export function remoteChangeAnimation(node, options) {
|
|
101
|
+
let { entityId, entityType, fields, animationClass, onAction } = options;
|
|
102
|
+
/* Add base class for styling hooks (e.g., transition defaults) */
|
|
103
|
+
node.classList.add('syncable-item');
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// ANIMATION APPLICATION LOGIC
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
/**
|
|
108
|
+
* Apply the appropriate CSS animation to the node based on the change's
|
|
109
|
+
* action type and affected fields.
|
|
110
|
+
*
|
|
111
|
+
* Handles special cases for toggle (checkbox + ripple), increment/decrement
|
|
112
|
+
* (counter sub-element), and delete (no class removal — element will be
|
|
113
|
+
* removed from DOM by the parent component).
|
|
114
|
+
*
|
|
115
|
+
* @param change - The remote change descriptor with `actionType` and `fields`.
|
|
116
|
+
*/
|
|
117
|
+
function applyAnimation(change) {
|
|
118
|
+
/* If specific fields are configured, only animate if at least one matches */
|
|
119
|
+
if (fields && fields.length > 0) {
|
|
120
|
+
const fieldsList = fields; /* Capture for closure safety */
|
|
121
|
+
const hasRelevantChange = change.fields.some((f) => f === '*' || fieldsList.includes(f));
|
|
122
|
+
if (!hasRelevantChange)
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
/* Prevent overlapping animations on the same element */
|
|
126
|
+
if (animatingElements.has(node))
|
|
127
|
+
return;
|
|
128
|
+
animatingElements.add(node);
|
|
129
|
+
/* Determine animation class based on action type */
|
|
130
|
+
const actionType = change.actionType;
|
|
131
|
+
const cssClass = animationClass || ACTION_ANIMATION_MAP[actionType] || 'item-changed';
|
|
132
|
+
const duration = ACTION_DURATION_MAP[actionType] || 1600;
|
|
133
|
+
/* Call action callback if provided (for component-specific handling) */
|
|
134
|
+
if (onAction) {
|
|
135
|
+
onAction(actionType, change.fields);
|
|
136
|
+
}
|
|
137
|
+
/* Apply animation class */
|
|
138
|
+
node.classList.add(cssClass);
|
|
139
|
+
/* For toggle actions, also add checkbox animation to child checkbox elements */
|
|
140
|
+
if (actionType === 'toggle') {
|
|
141
|
+
const checkbox = node.querySelector('.checkbox, [class*="checkbox"]');
|
|
142
|
+
if (checkbox) {
|
|
143
|
+
checkbox.classList.add('checkbox-animating');
|
|
144
|
+
setTimeout(() => checkbox.classList.remove('checkbox-animating'), 500);
|
|
145
|
+
}
|
|
146
|
+
/* Add completion ripple effect — a temporary <span> that auto-removes */
|
|
147
|
+
const ripple = document.createElement('span');
|
|
148
|
+
ripple.className = 'completion-ripple';
|
|
149
|
+
node.appendChild(ripple);
|
|
150
|
+
setTimeout(() => ripple.remove(), 700);
|
|
151
|
+
}
|
|
152
|
+
/* For increment/decrement, animate the counter sub-element specifically */
|
|
153
|
+
if (actionType === 'increment' || actionType === 'decrement') {
|
|
154
|
+
const counter = node.querySelector('[class*="value"], [class*="counter"], [class*="current"]');
|
|
155
|
+
if (counter) {
|
|
156
|
+
counter.classList.add(cssClass);
|
|
157
|
+
setTimeout(() => counter.classList.remove(cssClass), duration);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/* For delete animations, don't remove the class — the element will be
|
|
161
|
+
* removed from DOM after the animation. Removing it early causes the item
|
|
162
|
+
* to briefly reappear between animation end and DOM removal. */
|
|
163
|
+
if (actionType === 'delete')
|
|
164
|
+
return;
|
|
165
|
+
/* Remove class after animation completes */
|
|
166
|
+
const handleAnimationEnd = () => {
|
|
167
|
+
node.classList.remove(cssClass);
|
|
168
|
+
animatingElements.delete(node);
|
|
169
|
+
node.removeEventListener('animationend', handleAnimationEnd);
|
|
170
|
+
};
|
|
171
|
+
node.addEventListener('animationend', handleAnimationEnd);
|
|
172
|
+
/* Fallback removal in case animationend doesn't fire
|
|
173
|
+
* (e.g., element is display:none or animation is interrupted) */
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
node.classList.remove(cssClass);
|
|
176
|
+
animatingElements.delete(node);
|
|
177
|
+
}, duration + 100);
|
|
178
|
+
}
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// INITIAL CHECK (HANDLES CREATE-ON-MOUNT SCENARIO)
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
/* Check for a recent change immediately on mount. This handles the case
|
|
183
|
+
* where the element mounts after a remote INSERT — the store already has
|
|
184
|
+
* the change recorded, and we need to animate the newly-rendered item. */
|
|
185
|
+
const initialChange = remoteChangesStore.getRecentChange(entityId, entityType);
|
|
186
|
+
if (initialChange) {
|
|
187
|
+
/* Use requestAnimationFrame to ensure DOM is fully ready */
|
|
188
|
+
requestAnimationFrame(() => {
|
|
189
|
+
applyAnimation(initialChange);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// STORE SUBSCRIPTIONS
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
/* Create derived stores to watch for future changes and pending deletes */
|
|
196
|
+
let changeIndicator = createRecentChangeIndicator(entityId, entityType);
|
|
197
|
+
let deleteIndicator = createPendingDeleteIndicator(entityId, entityType);
|
|
198
|
+
/* Track the current unsubscribe functions */
|
|
199
|
+
let unsubscribeChange = changeIndicator.subscribe((change) => {
|
|
200
|
+
/* Skip if no change or if this is the same change we already animated on mount */
|
|
201
|
+
if (!change)
|
|
202
|
+
return;
|
|
203
|
+
if (initialChange && change.timestamp === initialChange.timestamp)
|
|
204
|
+
return;
|
|
205
|
+
applyAnimation(change);
|
|
206
|
+
});
|
|
207
|
+
/* Watch for pending deletes to apply delete animation */
|
|
208
|
+
let unsubscribeDelete = deleteIndicator.subscribe((isPendingDelete) => {
|
|
209
|
+
if (isPendingDelete) {
|
|
210
|
+
/* Apply delete animation immediately */
|
|
211
|
+
const deleteClass = ACTION_ANIMATION_MAP['delete'];
|
|
212
|
+
node.classList.add(deleteClass);
|
|
213
|
+
/* Call action callback if provided */
|
|
214
|
+
if (onAction) {
|
|
215
|
+
onAction('delete', ['*']);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// SVELTE ACTION LIFECYCLE
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
return {
|
|
223
|
+
/**
|
|
224
|
+
* Called when the action's options change. If the entity identity
|
|
225
|
+
* (`entityId` or `entityType`) has changed, tears down old subscriptions
|
|
226
|
+
* and creates new ones for the updated entity.
|
|
227
|
+
*
|
|
228
|
+
* @param newOptions - The updated {@link RemoteChangeOptions}.
|
|
229
|
+
*/
|
|
230
|
+
update(newOptions) {
|
|
231
|
+
/* If entity changed, re-subscribe with new entity */
|
|
232
|
+
if (newOptions.entityId !== entityId || newOptions.entityType !== entityType) {
|
|
233
|
+
unsubscribeChange();
|
|
234
|
+
unsubscribeDelete();
|
|
235
|
+
entityId = newOptions.entityId;
|
|
236
|
+
entityType = newOptions.entityType;
|
|
237
|
+
fields = newOptions.fields;
|
|
238
|
+
animationClass = newOptions.animationClass;
|
|
239
|
+
onAction = newOptions.onAction;
|
|
240
|
+
changeIndicator = createRecentChangeIndicator(entityId, entityType);
|
|
241
|
+
deleteIndicator = createPendingDeleteIndicator(entityId, entityType);
|
|
242
|
+
unsubscribeChange = changeIndicator.subscribe((change) => {
|
|
243
|
+
if (!change)
|
|
244
|
+
return;
|
|
245
|
+
applyAnimation(change);
|
|
246
|
+
});
|
|
247
|
+
unsubscribeDelete = deleteIndicator.subscribe((isPendingDelete) => {
|
|
248
|
+
if (isPendingDelete) {
|
|
249
|
+
const deleteClass = ACTION_ANIMATION_MAP['delete'];
|
|
250
|
+
node.classList.add(deleteClass);
|
|
251
|
+
if (onAction) {
|
|
252
|
+
onAction('delete', ['*']);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
/**
|
|
259
|
+
* Cleanup handler — unsubscribes from all stores, removes the base
|
|
260
|
+
* CSS class, and clears the element from the animation tracking set.
|
|
261
|
+
*/
|
|
262
|
+
destroy() {
|
|
263
|
+
unsubscribeChange();
|
|
264
|
+
unsubscribeDelete();
|
|
265
|
+
node.classList.remove('syncable-item');
|
|
266
|
+
animatingElements.delete(node);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Svelte action that marks an entity as "being edited" in the remote changes
|
|
272
|
+
* store. While editing, incoming remote changes for the same entity are
|
|
273
|
+
* deferred instead of applied immediately.
|
|
274
|
+
*
|
|
275
|
+
* **Lifecycle:**
|
|
276
|
+
* 1. On mount, calls `remoteChangesStore.startEditing()` to begin deferral.
|
|
277
|
+
* 2. Periodically checks for deferred changes and toggles a CSS class
|
|
278
|
+
* (`has-deferred-changes`) on the node for visual indication.
|
|
279
|
+
* 3. On update, re-registers if the entity identity changes.
|
|
280
|
+
* 4. On destroy, calls `remoteChangesStore.stopEditing()` and invokes
|
|
281
|
+
* `onDeferredChanges` if any changes were deferred.
|
|
282
|
+
*
|
|
283
|
+
* @param node - The form DOM element.
|
|
284
|
+
* @param options - Configuration specifying which entity is being edited.
|
|
285
|
+
* @returns A Svelte action lifecycle object with `update` and `destroy` methods.
|
|
286
|
+
*/
|
|
287
|
+
export function trackEditing(node, options) {
|
|
288
|
+
const { entityId, entityType, formType, fields, onDeferredChanges } = options;
|
|
289
|
+
/* Start tracking when the element mounts */
|
|
290
|
+
remoteChangesStore.startEditing(entityId, entityType, formType, fields);
|
|
291
|
+
/**
|
|
292
|
+
* Update the `has-deferred-changes` CSS class based on whether
|
|
293
|
+
* remote changes have been deferred for this entity.
|
|
294
|
+
*/
|
|
295
|
+
const updateDeferredIndicator = () => {
|
|
296
|
+
const hasDeferred = remoteChangesStore.hasDeferredChanges(entityId, entityType);
|
|
297
|
+
if (hasDeferred) {
|
|
298
|
+
node.classList.add('has-deferred-changes');
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
node.classList.remove('has-deferred-changes');
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
/* Check periodically for deferred changes (1-second polling interval) */
|
|
305
|
+
const interval = setInterval(updateDeferredIndicator, 1000);
|
|
306
|
+
updateDeferredIndicator();
|
|
307
|
+
return {
|
|
308
|
+
/**
|
|
309
|
+
* Called when the action's options change. If the entity identity
|
|
310
|
+
* changes, stops tracking the old entity and starts tracking the new one.
|
|
311
|
+
*
|
|
312
|
+
* @param newOptions - The updated {@link TrackEditingOptions}.
|
|
313
|
+
*/
|
|
314
|
+
update(newOptions) {
|
|
315
|
+
/* If entity changed, stop old tracking and start new */
|
|
316
|
+
if (newOptions.entityId !== entityId || newOptions.entityType !== entityType) {
|
|
317
|
+
remoteChangesStore.stopEditing(entityId, entityType);
|
|
318
|
+
remoteChangesStore.startEditing(newOptions.entityId, newOptions.entityType, newOptions.formType, newOptions.fields);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
/**
|
|
322
|
+
* Cleanup handler — stops the polling interval, removes CSS classes,
|
|
323
|
+
* stops editing in the store, and notifies the callback of any
|
|
324
|
+
* deferred changes that accumulated during the editing session.
|
|
325
|
+
*/
|
|
326
|
+
destroy() {
|
|
327
|
+
clearInterval(interval);
|
|
328
|
+
node.classList.remove('has-deferred-changes');
|
|
329
|
+
/* Stop tracking and get any deferred changes */
|
|
330
|
+
const deferredChanges = remoteChangesStore.stopEditing(entityId, entityType);
|
|
331
|
+
/* Notify callback if there are deferred changes */
|
|
332
|
+
if (deferredChanges.length > 0 && onDeferredChanges) {
|
|
333
|
+
onDeferredChanges(deferredChanges);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// =============================================================================
|
|
339
|
+
// PROGRAMMATIC LOCAL ANIMATION TRIGGER
|
|
340
|
+
// =============================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Trigger a local action animation on an element.
|
|
343
|
+
*
|
|
344
|
+
* Use this to make local user actions (e.g., tapping a checkbox, incrementing
|
|
345
|
+
* a counter) animate with the same visual treatment as remote changes, giving
|
|
346
|
+
* the UI a consistent feel.
|
|
347
|
+
*
|
|
348
|
+
* For `increment` and `decrement` actions, rapid repeated invocations will
|
|
349
|
+
* restart the animation instead of being blocked — this allows the counter
|
|
350
|
+
* to visually "bump" on each tap.
|
|
351
|
+
*
|
|
352
|
+
* @param element - The DOM element to animate (or `null`, in which case this is a no-op).
|
|
353
|
+
* @param actionType - The type of animation to apply.
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```svelte
|
|
357
|
+
* <script>
|
|
358
|
+
* import { triggerLocalAnimation } from 'stellar-drive';
|
|
359
|
+
* let element: HTMLElement;
|
|
360
|
+
*
|
|
361
|
+
* function handleToggle() {
|
|
362
|
+
* triggerLocalAnimation(element, 'toggle');
|
|
363
|
+
* onToggle?.();
|
|
364
|
+
* }
|
|
365
|
+
* </script>
|
|
366
|
+
* <div bind:this={element}>...</div>
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
export function triggerLocalAnimation(element, actionType) {
|
|
370
|
+
if (!element)
|
|
371
|
+
return;
|
|
372
|
+
const cssClass = ACTION_ANIMATION_MAP[actionType] || 'item-changed';
|
|
373
|
+
const duration = ACTION_DURATION_MAP[actionType] || 1600;
|
|
374
|
+
/* For increment/decrement, restart animation on rapid taps instead of blocking */
|
|
375
|
+
if (actionType === 'increment' || actionType === 'decrement') {
|
|
376
|
+
if (animatingElements.has(element)) {
|
|
377
|
+
/* Force restart: remove class, trigger reflow via offsetWidth read, re-add */
|
|
378
|
+
element.classList.remove(cssClass);
|
|
379
|
+
void element.offsetWidth;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
/* Prevent overlapping animations for other types */
|
|
384
|
+
if (animatingElements.has(element))
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
animatingElements.add(element);
|
|
388
|
+
/* Apply animation class */
|
|
389
|
+
element.classList.add(cssClass);
|
|
390
|
+
/* For toggle actions, also animate checkbox elements */
|
|
391
|
+
if (actionType === 'toggle') {
|
|
392
|
+
const checkbox = element.querySelector('.checkbox, [class*="checkbox"]');
|
|
393
|
+
if (checkbox) {
|
|
394
|
+
checkbox.classList.add('checkbox-animating');
|
|
395
|
+
setTimeout(() => checkbox.classList.remove('checkbox-animating'), 500);
|
|
396
|
+
}
|
|
397
|
+
/* Add completion ripple effect */
|
|
398
|
+
const ripple = document.createElement('span');
|
|
399
|
+
ripple.className = 'completion-ripple';
|
|
400
|
+
element.appendChild(ripple);
|
|
401
|
+
setTimeout(() => ripple.remove(), 700);
|
|
402
|
+
}
|
|
403
|
+
/* For increment/decrement, animate the counter sub-element specifically */
|
|
404
|
+
if (actionType === 'increment' || actionType === 'decrement') {
|
|
405
|
+
const counter = element.querySelector('[class*="value"], [class*="counter"], [class*="current"]');
|
|
406
|
+
if (counter) {
|
|
407
|
+
counter.classList.add(cssClass);
|
|
408
|
+
setTimeout(() => counter.classList.remove(cssClass), duration);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/* Remove class after animation completes */
|
|
412
|
+
const handleAnimationEnd = () => {
|
|
413
|
+
element.classList.remove(cssClass);
|
|
414
|
+
animatingElements.delete(element);
|
|
415
|
+
element.removeEventListener('animationend', handleAnimationEnd);
|
|
416
|
+
};
|
|
417
|
+
element.addEventListener('animationend', handleAnimationEnd);
|
|
418
|
+
/* Fallback removal in case animationend doesn't fire */
|
|
419
|
+
setTimeout(() => {
|
|
420
|
+
element.classList.remove(cssClass);
|
|
421
|
+
animatingElements.delete(element);
|
|
422
|
+
}, duration + 100);
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=remoteChange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteChange.js","sourceRoot":"","sources":["../../src/actions/remoteChange.ts"],"names":[],"mappings":"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"}
|