@builder.io/sdk-svelte 0.12.5 → 0.12.7

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 (40) hide show
  1. package/lib/browser/components/block/animator.d.ts +5 -0
  2. package/lib/browser/components/block/animator.js +243 -0
  3. package/lib/browser/components/block/block.svelte +15 -1
  4. package/lib/browser/components/content/components/enable-editor.svelte +14 -6
  5. package/lib/browser/constants/sdk-version.d.ts +1 -1
  6. package/lib/browser/constants/sdk-version.js +1 -1
  7. package/lib/browser/functions/evaluate/browser-runtime/browser.d.ts +5 -1
  8. package/lib/browser/functions/evaluate/browser-runtime/browser.js +22 -10
  9. package/lib/browser/functions/evaluate/should-force-browser-runtime-in-node.js +0 -4
  10. package/lib/browser/types/builder-block.d.ts +18 -1
  11. package/lib/edge/components/block/animator.d.ts +5 -0
  12. package/lib/edge/components/block/animator.js +243 -0
  13. package/lib/edge/components/block/block.svelte +15 -1
  14. package/lib/edge/components/content/components/enable-editor.svelte +14 -6
  15. package/lib/edge/constants/sdk-version.d.ts +1 -1
  16. package/lib/edge/constants/sdk-version.js +1 -1
  17. package/lib/edge/functions/evaluate/browser-runtime/browser.d.ts +5 -1
  18. package/lib/edge/functions/evaluate/browser-runtime/browser.js +22 -10
  19. package/lib/edge/functions/evaluate/should-force-browser-runtime-in-node.js +0 -4
  20. package/lib/edge/types/builder-block.d.ts +18 -1
  21. package/lib/node/components/block/animator.d.ts +5 -0
  22. package/lib/node/components/block/animator.js +243 -0
  23. package/lib/node/components/block/block.svelte +15 -1
  24. package/lib/node/components/content/components/enable-editor.svelte +14 -6
  25. package/lib/node/constants/sdk-version.d.ts +1 -1
  26. package/lib/node/constants/sdk-version.js +1 -1
  27. package/lib/node/functions/evaluate/browser-runtime/browser.d.ts +5 -1
  28. package/lib/node/functions/evaluate/browser-runtime/browser.js +22 -10
  29. package/lib/node/functions/evaluate/should-force-browser-runtime-in-node.js +0 -4
  30. package/lib/node/types/builder-block.d.ts +18 -1
  31. package/package.json +1 -1
  32. package/src/blocks/embed/embed.svelte +1 -0
  33. package/src/blocks/symbol/symbol.svelte +1 -0
  34. package/src/components/block/animator.ts +254 -0
  35. package/src/components/block/block.svelte +17 -0
  36. package/src/components/content/components/enable-editor.svelte +20 -8
  37. package/src/constants/sdk-version.ts +1 -1
  38. package/src/functions/evaluate/browser-runtime/browser.ts +30 -10
  39. package/src/functions/evaluate/should-force-browser-runtime-in-node.ts +0 -6
  40. package/src/types/builder-block.ts +23 -2
@@ -0,0 +1,5 @@
1
+ import type { BuilderAnimation } from '../../types/builder-block.js';
2
+ export declare function bindAnimations(animations: BuilderAnimation[]): void;
3
+ export declare function triggerAnimation(animation: BuilderAnimation): void;
4
+ export declare function bindHoverAnimation(animation: BuilderAnimation): void;
5
+ export declare function bindScrollInViewAnimation(animation: BuilderAnimation): void;
@@ -0,0 +1,243 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-types
2
+ function throttle(func, wait, options = {}) {
3
+ let context;
4
+ let args;
5
+ let result;
6
+ let timeout = null;
7
+ let previous = 0;
8
+ const later = function () {
9
+ previous = options.leading === false ? 0 : Date.now();
10
+ timeout = null;
11
+ result = func.apply(context, args);
12
+ if (!timeout)
13
+ context = args = null;
14
+ };
15
+ return function () {
16
+ const now = Date.now();
17
+ if (!previous && options.leading === false)
18
+ previous = now;
19
+ const remaining = wait - (now - previous);
20
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
21
+ context = this;
22
+ // eslint-disable-next-line prefer-rest-params
23
+ args = arguments;
24
+ if (remaining <= 0 || remaining > wait) {
25
+ if (timeout) {
26
+ clearTimeout(timeout);
27
+ timeout = null;
28
+ }
29
+ previous = now;
30
+ result = func.apply(context, args);
31
+ if (!timeout)
32
+ context = args = null;
33
+ }
34
+ else if (!timeout && options.trailing !== false) {
35
+ timeout = setTimeout(later, remaining);
36
+ }
37
+ return result;
38
+ };
39
+ }
40
+ function assign(target, ..._args) {
41
+ const to = Object(target);
42
+ for (let index = 1; index < arguments.length; index++) {
43
+ // eslint-disable-next-line prefer-rest-params
44
+ const nextSource = arguments[index];
45
+ if (nextSource != null) {
46
+ // Skip over if undefined or null
47
+ for (const nextKey in nextSource) {
48
+ // Avoid bugs when hasOwnProperty is shadowed
49
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
50
+ to[nextKey] = nextSource[nextKey];
51
+ }
52
+ }
53
+ }
54
+ }
55
+ return to;
56
+ }
57
+ const camelCaseToKebabCase = (str) => str ? str.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`) : '';
58
+ export function bindAnimations(animations) {
59
+ for (const animation of animations) {
60
+ switch (animation.trigger) {
61
+ case 'pageLoad':
62
+ triggerAnimation(animation);
63
+ break;
64
+ case 'hover':
65
+ bindHoverAnimation(animation);
66
+ break;
67
+ case 'scrollInView':
68
+ bindScrollInViewAnimation(animation);
69
+ break;
70
+ }
71
+ }
72
+ }
73
+ function warnElementNotPresent(id) {
74
+ console.warn(`Cannot animate element: element with ID ${id} not found!`);
75
+ }
76
+ function augmentAnimation(animation, element) {
77
+ const stylesUsed = getAllStylesUsed(animation);
78
+ const computedStyle = getComputedStyle(element);
79
+ // const computedStyle = getComputedStyle(element);
80
+ // // FIXME: this will break if original load is in one reponsive size then resize to another hmmm
81
+ // Need to use transform instead of left since left can change on screen sizes
82
+ const firstStyles = animation.steps[0].styles;
83
+ const lastStyles = animation.steps[animation.steps.length - 1].styles;
84
+ const bothStyles = [firstStyles, lastStyles];
85
+ // FIXME: this won't work as expected for augmented animations - may need the editor itself to manage this
86
+ for (const styles of bothStyles) {
87
+ for (const style of stylesUsed) {
88
+ if (!(style in styles)) {
89
+ styles[style] = computedStyle[style];
90
+ }
91
+ }
92
+ }
93
+ }
94
+ function getAllStylesUsed(animation) {
95
+ const properties = [];
96
+ for (const step of animation.steps) {
97
+ for (const key in step.styles) {
98
+ if (properties.indexOf(key) === -1) {
99
+ properties.push(key);
100
+ }
101
+ }
102
+ }
103
+ return properties;
104
+ }
105
+ export function triggerAnimation(animation) {
106
+ // TODO: do for ALL elements
107
+ const elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
108
+ if (!elements.length) {
109
+ warnElementNotPresent(animation.elementId || animation.id || '');
110
+ return;
111
+ }
112
+ Array.from(elements).forEach(element => {
113
+ augmentAnimation(animation, element);
114
+ // TODO: do this properly, may have other animations of different properties
115
+ // TODO: only override the properties
116
+ // TODO: if there is an entrance and hover animation, the transition duration will get effed
117
+ // element.setAttribute('style', '');
118
+ // const styledUsed = this.getAllStylesUsed(animation);
119
+ element.style.transition = 'none';
120
+ element.style.transitionDelay = '0';
121
+ assign(element.style, animation.steps[0].styles);
122
+ // TODO: queue/batch these timeouts
123
+ // TODO: only include properties explicitly set in the animation
124
+ // using Object.keys(styles)
125
+ setTimeout(() => {
126
+ element.style.transition = `all ${animation.duration}s ${camelCaseToKebabCase(animation.easing)}`;
127
+ if (animation.delay) {
128
+ element.style.transitionDelay = animation.delay + 's';
129
+ }
130
+ assign(element.style, animation.steps[1].styles);
131
+ // TODO: maybe remove/reset transitoin property after animation duration
132
+ // TODO: queue timers
133
+ setTimeout(() => {
134
+ // TODO: what if has other transition (reset back to what it was)
135
+ element.style.transition = '';
136
+ element.style.transitionDelay = '';
137
+ }, (animation.delay || 0) * 1000 + animation.duration * 1000 + 100);
138
+ });
139
+ });
140
+ }
141
+ export function bindHoverAnimation(animation) {
142
+ // TODO: is it multiple binding when editing...?
143
+ // TODO: unbind on element remove
144
+ // TODO: apply to ALL elements
145
+ const elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
146
+ if (!elements.length) {
147
+ warnElementNotPresent(animation.elementId || animation.id || '');
148
+ return;
149
+ }
150
+ Array.from(elements).forEach(element => {
151
+ augmentAnimation(animation, element);
152
+ const defaultState = animation.steps[0].styles;
153
+ const hoverState = animation.steps[1].styles;
154
+ function attachDefaultState() {
155
+ assign(element.style, defaultState);
156
+ }
157
+ function attachHoverState() {
158
+ assign(element.style, hoverState);
159
+ }
160
+ attachDefaultState();
161
+ element.addEventListener('mouseenter', attachHoverState);
162
+ element.addEventListener('mouseleave', attachDefaultState);
163
+ // TODO: queue/batch these timeouts
164
+ setTimeout(() => {
165
+ element.style.transition = `all ${animation.duration}s ${camelCaseToKebabCase(animation.easing)}`;
166
+ if (animation.delay) {
167
+ element.style.transitionDelay = animation.delay + 's';
168
+ }
169
+ });
170
+ });
171
+ }
172
+ // TODO: unbind on element remove
173
+ export function bindScrollInViewAnimation(animation) {
174
+ // TODO: apply to ALL matching elements
175
+ const elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
176
+ if (!elements.length) {
177
+ warnElementNotPresent(animation.elementId || animation.id || '');
178
+ return;
179
+ }
180
+ // TODO: if server side rendered and scrolled into view don't animate...
181
+ Array.from(elements).forEach(element => {
182
+ augmentAnimation(animation, element);
183
+ let triggered = false;
184
+ let pendingAnimation = false;
185
+ function immediateOnScroll() {
186
+ if (!triggered && isScrolledIntoView(element)) {
187
+ triggered = true;
188
+ pendingAnimation = true;
189
+ setTimeout(() => {
190
+ assign(element.style, animation.steps[1].styles);
191
+ if (!animation.repeat) {
192
+ document.removeEventListener('scroll', onScroll);
193
+ }
194
+ setTimeout(() => {
195
+ pendingAnimation = false;
196
+ if (!animation.repeat) {
197
+ element.style.transition = '';
198
+ element.style.transitionDelay = '';
199
+ }
200
+ }, (animation.duration + (animation.delay || 0)) * 1000 + 100);
201
+ });
202
+ }
203
+ else if (animation.repeat && triggered && !pendingAnimation && !isScrolledIntoView(element)) {
204
+ // we want to repeat the animation every time the the element is out of view and back again
205
+ triggered = false;
206
+ assign(element.style, animation.steps[0].styles);
207
+ }
208
+ }
209
+ // TODO: roll all of these in one for more efficiency of checking all the rects
210
+ const onScroll = throttle(immediateOnScroll, 200, {
211
+ leading: false
212
+ });
213
+ // TODO: fully in view or partially
214
+ function isScrolledIntoView(elem) {
215
+ const rect = elem.getBoundingClientRect();
216
+ const windowHeight = window.innerHeight;
217
+ const thresholdPercent = (animation.thresholdPercent || 0) / 100;
218
+ const threshold = thresholdPercent * windowHeight;
219
+ // TODO: partial in view? or what if element is larger than screen itself
220
+ return rect.bottom > threshold && rect.top < windowHeight - threshold // Element is peeking top or bottom
221
+ ;
222
+ }
223
+ const defaultState = animation.steps[0].styles;
224
+ function attachDefaultState() {
225
+ assign(element.style, defaultState);
226
+ }
227
+ attachDefaultState();
228
+ // TODO: queue/batch these timeouts!
229
+ setTimeout(() => {
230
+ element.style.transition = `all ${animation.duration}s ${camelCaseToKebabCase(animation.easing)}`;
231
+ if (animation.delay) {
232
+ element.style.transitionDelay = animation.delay + 's';
233
+ }
234
+ });
235
+ // TODO: one listener for everything
236
+ document.addEventListener('scroll', onScroll, {
237
+ capture: true,
238
+ passive: true
239
+ });
240
+ // Do an initial check
241
+ immediateOnScroll();
242
+ });
243
+ }
@@ -1,10 +1,12 @@
1
1
  <script context="module"></script>
2
2
 
3
- <script>import { writable } from "svelte/store";
3
+ <script>import { onMount } from "svelte";
4
+ import { writable } from "svelte/store";
4
5
  import "../../functions/extract-text-styles.js";
5
6
  import { getBlockComponentOptions } from "../../functions/get-block-component-options.js";
6
7
  import { getProcessedBlock } from "../../functions/get-processed-block.js";
7
8
  import "../../functions/get-style.js";
9
+ import { bindAnimations } from "./animator.js";
8
10
  import { getComponent, getRepeatItemData } from "./block.helpers.js";
9
11
  import BlockStyles from "./components/block-styles.svelte";
10
12
  import BlockWrapper from "./components/block-wrapper.svelte";
@@ -100,6 +102,18 @@ $: componentRefProps = () => {
100
102
  };
101
103
  };
102
104
  let childrenContext = writable($context);
105
+ onMount(() => {
106
+ const blockId = processedBlock().id;
107
+ const animations = processedBlock().animations;
108
+ if (animations && blockId) {
109
+ bindAnimations(animations
110
+ .filter((item) => item.trigger !== "hover")
111
+ .map((animation) => ({
112
+ ...animation,
113
+ elementId: blockId,
114
+ })));
115
+ }
116
+ });
103
117
  </script>
104
118
 
105
119
  {#if canShowBlock()}
@@ -17,6 +17,7 @@ import { getDefaultCanTrack } from "../../../helpers/canTrack.js";
17
17
  import { logger } from "../../../helpers/logger.js";
18
18
  import "../../../helpers/preview-lru-cache/set.js";
19
19
  import { registerInsertMenu, setupBrowserForEditing, } from "../../../scripts/init-editing.js";
20
+ import { triggerAnimation } from "../../block/animator.js";
20
21
  export let builderContextSignal;
21
22
  export let contentWrapper = undefined;
22
23
  export let trustedHosts = undefined;
@@ -83,6 +84,10 @@ function processMessage(event) {
83
84
  forceReRenderCount = forceReRenderCount + 1; // This is a hack to force Qwik to re-render.
84
85
  break;
85
86
  }
87
+ case "builder.triggerAnimation": {
88
+ triggerAnimation(data.data);
89
+ break;
90
+ }
86
91
  case "builder.contentUpdate": {
87
92
  const messageContent = data.data;
88
93
  const key = messageContent.key ||
@@ -289,18 +294,21 @@ $: onUpdateFn_1(...[shouldSendResetCookie]);
289
294
  function onUpdateFn_2(..._args) {
290
295
  evaluateJsCode();
291
296
  }
292
- $: onUpdateFn_2(...[
293
- $builderContextSignal.content?.data?.jsCode,
294
- $builderContextSignal.rootState,
295
- ]);
297
+ $: onUpdateFn_2_builderContextSignal_content__data__jsCode =
298
+ $builderContextSignal.content?.data?.jsCode;
299
+ $: onUpdateFn_2(...[onUpdateFn_2_builderContextSignal_content__data__jsCode]);
296
300
  function onUpdateFn_3(..._args) {
297
301
  runHttpRequests();
298
302
  }
299
- $: onUpdateFn_3(...[$builderContextSignal.content?.data?.httpRequests]);
303
+ $: onUpdateFn_3_builderContextSignal_content__data__httpRequests =
304
+ $builderContextSignal.content?.data?.httpRequests;
305
+ $: onUpdateFn_3(...[onUpdateFn_3_builderContextSignal_content__data__httpRequests]);
300
306
  function onUpdateFn_4(..._args) {
301
307
  emitStateUpdate();
302
308
  }
303
- $: onUpdateFn_4(...[$builderContextSignal.rootState]);
309
+ $: onUpdateFn_4_builderContextSignal_rootState =
310
+ $builderContextSignal.rootState;
311
+ $: onUpdateFn_4(...[onUpdateFn_4_builderContextSignal_rootState]);
304
312
  function onUpdateFn_5(..._args) {
305
313
  if (data) {
306
314
  mergeNewRootState(data);
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.12.5";
1
+ export declare const SDK_VERSION = "0.12.7";
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.12.5";
1
+ export const SDK_VERSION = "0.12.7";
@@ -1,4 +1,8 @@
1
1
  import type { BuilderRenderState } from '../../../context/types.js';
2
2
  import type { ExecutorArgs } from '../helpers.js';
3
3
  export declare const runInBrowser: ({ code, builder, context, event, localState, rootSetState, rootState }: ExecutorArgs) => any;
4
- export declare function flattenState(rootState: Record<string | symbol, any>, localState: Record<string | symbol, any> | undefined, rootSetState: ((rootState: BuilderRenderState) => void) | undefined): BuilderRenderState;
4
+ export declare function flattenState({ rootState, localState, rootSetState }: {
5
+ rootState: Record<string | symbol, any>;
6
+ localState: Record<string | symbol, any> | undefined;
7
+ rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
8
+ }): BuilderRenderState;
@@ -4,27 +4,39 @@ export const runInBrowser = ({ code, builder, context, event, localState, rootSe
4
4
  builder,
5
5
  context,
6
6
  event,
7
- state: flattenState(rootState, localState, rootSetState)
7
+ state: flattenState({
8
+ rootState,
9
+ localState,
10
+ rootSetState
11
+ })
8
12
  });
9
13
  return new Function(...functionArgs.map(([name]) => name), code)(...functionArgs.map(([, value]) => value));
10
14
  };
11
- export function flattenState(rootState, localState, rootSetState) {
12
- if (rootState === localState) {
13
- throw new Error('rootState === localState');
14
- }
15
+ export function flattenState({ rootState, localState, rootSetState }) {
15
16
  return new Proxy(rootState, {
16
- get: (_, prop) => {
17
+ get: (target, prop) => {
17
18
  if (localState && prop in localState) {
18
19
  return localState[prop];
19
20
  }
20
- return rootState[prop];
21
+ const val = target[prop];
22
+ if (typeof val === 'object') {
23
+ return flattenState({
24
+ rootState: val,
25
+ localState: undefined,
26
+ rootSetState: rootSetState ? subState => {
27
+ target[prop] = subState;
28
+ rootSetState(target);
29
+ } : undefined
30
+ });
31
+ }
32
+ return val;
21
33
  },
22
- set: (_, prop, value) => {
34
+ set: (target, prop, value) => {
23
35
  if (localState && prop in localState) {
24
36
  throw new Error('Writing to local state is not allowed as it is read-only.');
25
37
  }
26
- rootState[prop] = value;
27
- rootSetState?.(rootState);
38
+ target[prop] = value;
39
+ rootSetState?.(target);
28
40
  return true;
29
41
  }
30
42
  });
@@ -5,11 +5,7 @@ export const shouldForceBrowserRuntimeInNode = () => {
5
5
  return false;
6
6
  const isArm64 = process.arch === 'arm64';
7
7
  const isNode20 = process.version.startsWith('v20');
8
- // if the user passed this node option, then the `isolated-vm` package will work
9
8
  const hasNoNodeSnapshotNodeOption = process.env.NODE_OPTIONS?.includes('--no-node-snapshot');
10
- /**
11
- * https://github.com/laverdet/isolated-vm/issues/424
12
- */
13
9
  if (isArm64 && isNode20 && !hasNoNodeSnapshotNodeOption) {
14
10
  logger.log(`Skipping usage of \`isolated-vm\` to avoid crashes in Node v20 on an arm64 machine.
15
11
  If you would like to use the \`isolated-vm\` package on this machine, please provide the \`NODE_OPTIONS=--no-node-snapshot\` config to your Node process.
@@ -4,6 +4,23 @@ interface JSONObject {
4
4
  }
5
5
  interface JSONArray extends Array<JSONValue> {
6
6
  }
7
+ export interface AnimationStep {
8
+ styles: {
9
+ [key: string]: string;
10
+ };
11
+ delay?: number;
12
+ }
13
+ export interface BuilderAnimation {
14
+ elementId: string;
15
+ trigger: string;
16
+ steps: AnimationStep[];
17
+ duration: number;
18
+ delay?: number;
19
+ easing?: string;
20
+ id?: string;
21
+ repeat?: boolean;
22
+ thresholdPercent?: number;
23
+ }
7
24
  /** @todo typedoc this */
8
25
  export interface BuilderBlock {
9
26
  '@type': '@builder.io/sdk:Element';
@@ -52,7 +69,7 @@ export interface BuilderBlock {
52
69
  collection: string;
53
70
  itemName?: string;
54
71
  } | null;
55
- animations?: any[];
72
+ animations?: BuilderAnimation[];
56
73
  style?: Partial<CSSStyleDeclaration>;
57
74
  href?: string;
58
75
  /**
@@ -0,0 +1,5 @@
1
+ import type { BuilderAnimation } from '../../types/builder-block.js';
2
+ export declare function bindAnimations(animations: BuilderAnimation[]): void;
3
+ export declare function triggerAnimation(animation: BuilderAnimation): void;
4
+ export declare function bindHoverAnimation(animation: BuilderAnimation): void;
5
+ export declare function bindScrollInViewAnimation(animation: BuilderAnimation): void;