@builder.io/sdk-svelte 0.1.3-0 → 0.1.3-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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "0.1.2",
4
+ "version": "0.1.3-0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "package",
@@ -118,6 +118,7 @@
118
118
  "./functions/set": "./functions/set.js",
119
119
  "./functions/track/helpers": "./functions/track/helpers.js",
120
120
  "./functions/track": "./functions/track/index.js",
121
+ "./functions/track/interaction": "./functions/track/interaction.js",
121
122
  "./functions/transform-block-properties": "./functions/transform-block-properties.js",
122
123
  "./functions/transform-block": "./functions/transform-block.js",
123
124
  "./helpers/ab-tests": "./helpers/ab-tests.js",
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script>const isEvent = (attr) => attr.startsWith("on:");
4
4
  const isNonEvent = (attr) => !attr.startsWith("on:");
5
- const filterAttrs = (attrs, filter) => {
5
+ const filterAttrs = (attrs = {}, filter) => {
6
6
  const validAttr = {};
7
7
  Object.keys(attrs).forEach((attr) => {
8
8
  if (filter(attr)) {
@@ -8,7 +8,7 @@ import BuilderBlocks from "../../components/render-blocks.svelte";
8
8
  import { isEditing } from "../../functions/is-editing.js";
9
9
  const isEvent = (attr) => attr.startsWith("on:");
10
10
  const isNonEvent = (attr) => !attr.startsWith("on:");
11
- const filterAttrs = (attrs, filter) => {
11
+ const filterAttrs = (attrs = {}, filter) => {
12
12
  const validAttr = {};
13
13
  Object.keys(attrs).forEach((attr) => {
14
14
  if (filter(attr)) {
@@ -3,7 +3,7 @@
3
3
  <script>import { isEditing } from "../../functions/is-editing.js";
4
4
  const isEvent = (attr) => attr.startsWith("on:");
5
5
  const isNonEvent = (attr) => !attr.startsWith("on:");
6
- const filterAttrs = (attrs, filter) => {
6
+ const filterAttrs = (attrs = {}, filter) => {
7
7
  const validAttr = {};
8
8
  Object.keys(attrs).forEach((attr) => {
9
9
  if (filter(attr)) {
@@ -3,7 +3,7 @@
3
3
  <script>import { isEditing } from "../../functions/is-editing.js";
4
4
  const isEvent = (attr) => attr.startsWith("on:");
5
5
  const isNonEvent = (attr) => !attr.startsWith("on:");
6
- const filterAttrs = (attrs, filter) => {
6
+ const filterAttrs = (attrs = {}, filter) => {
7
7
  const validAttr = {};
8
8
  Object.keys(attrs).forEach((attr) => {
9
9
  if (filter(attr)) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script>const isEvent = (attr) => attr.startsWith("on:");
4
4
  const isNonEvent = (attr) => !attr.startsWith("on:");
5
- const filterAttrs = (attrs, filter) => {
5
+ const filterAttrs = (attrs = {}, filter) => {
6
6
  const validAttr = {};
7
7
  Object.keys(attrs).forEach((attr) => {
8
8
  if (filter(attr)) {
@@ -3,7 +3,7 @@
3
3
  <script>import { isEditing } from "../../functions/is-editing.js";
4
4
  const isEvent = (attr) => attr.startsWith("on:");
5
5
  const isNonEvent = (attr) => !attr.startsWith("on:");
6
- const filterAttrs = (attrs, filter) => {
6
+ const filterAttrs = (attrs = {}, filter) => {
7
7
  const validAttr = {};
8
8
  Object.keys(attrs).forEach((attr) => {
9
9
  if (filter(attr)) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script>const isEvent = (attr) => attr.startsWith("on:");
4
4
  const isNonEvent = (attr) => !attr.startsWith("on:");
5
- const filterAttrs = (attrs, filter) => {
5
+ const filterAttrs = (attrs = {}, filter) => {
6
6
  const validAttr = {};
7
7
  Object.keys(attrs).forEach((attr) => {
8
8
  if (filter(attr)) {
@@ -7,7 +7,7 @@ import { getContent } from "../../functions/get-content/index.js";
7
7
  import { TARGET } from "../../constants/target";
8
8
  const isEvent = (attr) => attr.startsWith("on:");
9
9
  const isNonEvent = (attr) => !attr.startsWith("on:");
10
- const filterAttrs = (attrs, filter) => {
10
+ const filterAttrs = (attrs = {}, filter) => {
11
11
  const validAttr = {};
12
12
  Object.keys(attrs).forEach((attr) => {
13
13
  if (filter(attr)) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script>const isEvent = (attr) => attr.startsWith("on:");
4
4
  const isNonEvent = (attr) => !attr.startsWith("on:");
5
- const filterAttrs = (attrs, filter) => {
5
+ const filterAttrs = (attrs = {}, filter) => {
6
6
  const validAttr = {};
7
7
  Object.keys(attrs).forEach((attr) => {
8
8
  if (filter(attr)) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  <script>const isEvent = (attr) => attr.startsWith("on:");
4
4
  const isNonEvent = (attr) => !attr.startsWith("on:");
5
- const filterAttrs = (attrs, filter) => {
5
+ const filterAttrs = (attrs = {}, filter) => {
6
6
  const validAttr = {};
7
7
  Object.keys(attrs).forEach((attr) => {
8
8
  if (filter(attr)) {
@@ -16,6 +16,7 @@ import RenderContentStyles from "./components/render-styles.svelte";
16
16
  import builderContext from "../../context/builder.context.js";
17
17
  import { registerInsertMenu, setupBrowserForEditing, } from "../../scripts/init-editing.js";
18
18
  import { checkIsDefined } from "../../helpers/nullable.js";
19
+ import { getInteractionPropertiesForEvent } from "../../functions/track/interaction.js";
19
20
  export let content;
20
21
  export let data;
21
22
  export let canTrack;
@@ -87,7 +88,7 @@ function evaluateJsCode() {
87
88
  });
88
89
  }
89
90
  }
90
- function onClick(_event) {
91
+ function onClick(event) {
91
92
  if (useContent()) {
92
93
  const variationId = useContent?.()?.testVariationId;
93
94
  const contentId = useContent?.()?.id;
@@ -97,8 +98,13 @@ function onClick(_event) {
97
98
  contentId,
98
99
  apiKey: apiKey,
99
100
  variationId: variationId !== contentId ? variationId : undefined,
101
+ ...getInteractionPropertiesForEvent(event),
102
+ unique: !clicked,
100
103
  });
101
104
  }
105
+ if (!clicked) {
106
+ clicked = true;
107
+ }
102
108
  }
103
109
  function evalExpression(expression) {
104
110
  return expression.replace(/{{([^}]+)}}/g, (_match, group) => evaluate({
@@ -205,6 +211,7 @@ let allRegisteredComponents = [
205
211
  [curr.name]: curr,
206
212
  }), {});
207
213
  let httpReqsData = {};
214
+ let clicked = false;
208
215
  onMount(() => {
209
216
  if (!apiKey) {
210
217
  console.error("[Builder.io]: No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
@@ -0,0 +1,13 @@
1
+ declare type Offset = {
2
+ x: number;
3
+ y: number;
4
+ };
5
+ export declare const getInteractionPropertiesForEvent: (event: MouseEvent) => {
6
+ targetBuilderElement: string | undefined;
7
+ metadata: {
8
+ targetOffset: Offset | undefined;
9
+ builderTargetOffset: Offset | undefined;
10
+ builderElementIndex: number | undefined;
11
+ };
12
+ };
13
+ export {};
@@ -0,0 +1,54 @@
1
+ function round(num) {
2
+ return Math.round(num * 1000) / 1000;
3
+ }
4
+ const findParentElement = (target, callback, checkElement = true) => {
5
+ if (!(target instanceof HTMLElement)) {
6
+ return null;
7
+ }
8
+ let parent = checkElement ? target : target.parentElement;
9
+ do {
10
+ if (!parent) {
11
+ return null;
12
+ }
13
+ const matches = callback(parent);
14
+ if (matches) {
15
+ return parent;
16
+ }
17
+ } while ((parent = parent.parentElement));
18
+ return null;
19
+ };
20
+ const findBuilderParent = (target) => findParentElement(target, (el) => {
21
+ const id = el.getAttribute('builder-id') || el.id;
22
+ return Boolean(id?.indexOf('builder-') === 0);
23
+ });
24
+ const computeOffset = ({ event, target, }) => {
25
+ const targetRect = target.getBoundingClientRect();
26
+ const xOffset = event.clientX - targetRect.left;
27
+ const yOffset = event.clientY - targetRect.top;
28
+ const xRatio = round(xOffset / targetRect.width);
29
+ const yRatio = round(yOffset / targetRect.height);
30
+ return {
31
+ x: xRatio,
32
+ y: yRatio,
33
+ };
34
+ };
35
+ export const getInteractionPropertiesForEvent = (event) => {
36
+ const target = event.target;
37
+ const targetBuilderElement = target && findBuilderParent(target);
38
+ const builderId = targetBuilderElement?.getAttribute('builder-id') ||
39
+ targetBuilderElement?.id;
40
+ return {
41
+ targetBuilderElement: builderId || undefined,
42
+ metadata: {
43
+ targetOffset: target ? computeOffset({ event, target }) : undefined,
44
+ builderTargetOffset: targetBuilderElement
45
+ ? computeOffset({ event, target: targetBuilderElement })
46
+ : undefined,
47
+ builderElementIndex: targetBuilderElement && builderId
48
+ ? [].slice
49
+ .call(document.getElementsByClassName(builderId))
50
+ .indexOf(targetBuilderElement)
51
+ : undefined,
52
+ },
53
+ };
54
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "0.1.3-0",
4
+ "version": "0.1.3-1",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "package",
@@ -10,7 +10,7 @@
10
10
  <script lang="ts">
11
11
  const isEvent = (attr) => attr.startsWith("on:");
12
12
  const isNonEvent = (attr) => !attr.startsWith("on:");
13
- const filterAttrs = (attrs, filter) => {
13
+ const filterAttrs = (attrs = {}, filter) => {
14
14
  const validAttr = {};
15
15
  Object.keys(attrs).forEach((attr) => {
16
16
  if (filter(attr)) {
@@ -66,7 +66,7 @@
66
66
 
67
67
  const isEvent = (attr) => attr.startsWith("on:");
68
68
  const isNonEvent = (attr) => !attr.startsWith("on:");
69
- const filterAttrs = (attrs, filter) => {
69
+ const filterAttrs = (attrs = {}, filter) => {
70
70
  const validAttr = {};
71
71
  Object.keys(attrs).forEach((attr) => {
72
72
  if (filter(attr)) {
@@ -23,7 +23,7 @@
23
23
 
24
24
  const isEvent = (attr) => attr.startsWith("on:");
25
25
  const isNonEvent = (attr) => !attr.startsWith("on:");
26
- const filterAttrs = (attrs, filter) => {
26
+ const filterAttrs = (attrs = {}, filter) => {
27
27
  const validAttr = {};
28
28
  Object.keys(attrs).forEach((attr) => {
29
29
  if (filter(attr)) {
@@ -15,7 +15,7 @@
15
15
 
16
16
  const isEvent = (attr) => attr.startsWith("on:");
17
17
  const isNonEvent = (attr) => !attr.startsWith("on:");
18
- const filterAttrs = (attrs, filter) => {
18
+ const filterAttrs = (attrs = {}, filter) => {
19
19
  const validAttr = {};
20
20
  Object.keys(attrs).forEach((attr) => {
21
21
  if (filter(attr)) {
@@ -10,7 +10,7 @@
10
10
  <script lang="ts">
11
11
  const isEvent = (attr) => attr.startsWith("on:");
12
12
  const isNonEvent = (attr) => !attr.startsWith("on:");
13
- const filterAttrs = (attrs, filter) => {
13
+ const filterAttrs = (attrs = {}, filter) => {
14
14
  const validAttr = {};
15
15
  Object.keys(attrs).forEach((attr) => {
16
16
  if (filter(attr)) {
@@ -16,7 +16,7 @@
16
16
 
17
17
  const isEvent = (attr) => attr.startsWith("on:");
18
18
  const isNonEvent = (attr) => !attr.startsWith("on:");
19
- const filterAttrs = (attrs, filter) => {
19
+ const filterAttrs = (attrs = {}, filter) => {
20
20
  const validAttr = {};
21
21
  Object.keys(attrs).forEach((attr) => {
22
22
  if (filter(attr)) {
@@ -8,7 +8,7 @@
8
8
  <script lang="ts">
9
9
  const isEvent = (attr) => attr.startsWith("on:");
10
10
  const isNonEvent = (attr) => !attr.startsWith("on:");
11
- const filterAttrs = (attrs, filter) => {
11
+ const filterAttrs = (attrs = {}, filter) => {
12
12
  const validAttr = {};
13
13
  Object.keys(attrs).forEach((attr) => {
14
14
  if (filter(attr)) {
@@ -34,7 +34,7 @@
34
34
 
35
35
  const isEvent = (attr) => attr.startsWith("on:");
36
36
  const isNonEvent = (attr) => !attr.startsWith("on:");
37
- const filterAttrs = (attrs, filter) => {
37
+ const filterAttrs = (attrs = {}, filter) => {
38
38
  const validAttr = {};
39
39
  Object.keys(attrs).forEach((attr) => {
40
40
  if (filter(attr)) {
@@ -11,7 +11,7 @@
11
11
  <script lang="ts">
12
12
  const isEvent = (attr) => attr.startsWith("on:");
13
13
  const isNonEvent = (attr) => !attr.startsWith("on:");
14
- const filterAttrs = (attrs, filter) => {
14
+ const filterAttrs = (attrs = {}, filter) => {
15
15
  const validAttr = {};
16
16
  Object.keys(attrs).forEach((attr) => {
17
17
  if (filter(attr)) {
@@ -29,7 +29,7 @@
29
29
  <script lang="ts">
30
30
  const isEvent = (attr) => attr.startsWith("on:");
31
31
  const isNonEvent = (attr) => !attr.startsWith("on:");
32
- const filterAttrs = (attrs, filter) => {
32
+ const filterAttrs = (attrs = {}, filter) => {
33
33
  const validAttr = {};
34
34
  Object.keys(attrs).forEach((attr) => {
35
35
  if (filter(attr)) {
@@ -64,6 +64,7 @@
64
64
  setupBrowserForEditing,
65
65
  } from "../../scripts/init-editing.js";
66
66
  import { checkIsDefined } from "../../helpers/nullable.js";
67
+ import { getInteractionPropertiesForEvent } from "../../functions/track/interaction.js";
67
68
 
68
69
  export let content: RenderContentProps["content"];
69
70
  export let data: RenderContentProps["data"];
@@ -140,7 +141,7 @@
140
141
  });
141
142
  }
142
143
  }
143
- function onClick(_event) {
144
+ function onClick(event) {
144
145
  if (useContent()) {
145
146
  const variationId = useContent?.()?.testVariationId;
146
147
  const contentId = useContent?.()?.id;
@@ -150,8 +151,13 @@
150
151
  contentId,
151
152
  apiKey: apiKey,
152
153
  variationId: variationId !== contentId ? variationId : undefined,
154
+ ...getInteractionPropertiesForEvent(event),
155
+ unique: !clicked,
153
156
  });
154
157
  }
158
+ if (!clicked) {
159
+ clicked = true;
160
+ }
155
161
  }
156
162
  function evalExpression(expression) {
157
163
  return expression.replace(/{{([^}]+)}}/g, (_match, group) =>
@@ -270,6 +276,7 @@
270
276
  {}
271
277
  );
272
278
  let httpReqsData = {};
279
+ let clicked = false;
273
280
 
274
281
  onMount(() => {
275
282
  if (!apiKey) {
@@ -0,0 +1,80 @@
1
+ function round(num: number) {
2
+ return Math.round(num * 1000) / 1000;
3
+ }
4
+ const findParentElement = (
5
+ target: HTMLElement,
6
+ callback: (element: HTMLElement) => boolean,
7
+ checkElement = true
8
+ ): HTMLElement | null => {
9
+ if (!(target instanceof HTMLElement)) {
10
+ return null;
11
+ }
12
+ let parent: HTMLElement | null = checkElement ? target : target.parentElement;
13
+ do {
14
+ if (!parent) {
15
+ return null;
16
+ }
17
+
18
+ const matches = callback(parent);
19
+ if (matches) {
20
+ return parent;
21
+ }
22
+ } while ((parent = parent.parentElement));
23
+
24
+ return null;
25
+ };
26
+
27
+ const findBuilderParent = (target: HTMLElement) =>
28
+ findParentElement(target, (el) => {
29
+ const id = el.getAttribute('builder-id') || el.id;
30
+ return Boolean(id?.indexOf('builder-') === 0);
31
+ });
32
+
33
+ type Offset = {
34
+ x: number;
35
+ y: number;
36
+ };
37
+
38
+ const computeOffset = ({
39
+ event,
40
+ target,
41
+ }: {
42
+ event: MouseEvent;
43
+ target: HTMLElement;
44
+ }): Offset => {
45
+ const targetRect = target.getBoundingClientRect();
46
+ const xOffset = event.clientX - targetRect.left;
47
+ const yOffset = event.clientY - targetRect.top;
48
+
49
+ const xRatio = round(xOffset / targetRect.width);
50
+ const yRatio = round(yOffset / targetRect.height);
51
+
52
+ return {
53
+ x: xRatio,
54
+ y: yRatio,
55
+ };
56
+ };
57
+
58
+ export const getInteractionPropertiesForEvent = (event: MouseEvent) => {
59
+ const target = event.target as HTMLElement | null;
60
+ const targetBuilderElement = target && findBuilderParent(target);
61
+ const builderId =
62
+ targetBuilderElement?.getAttribute('builder-id') ||
63
+ targetBuilderElement?.id;
64
+
65
+ return {
66
+ targetBuilderElement: builderId || undefined,
67
+ metadata: {
68
+ targetOffset: target ? computeOffset({ event, target }) : undefined,
69
+ builderTargetOffset: targetBuilderElement
70
+ ? computeOffset({ event, target: targetBuilderElement })
71
+ : undefined,
72
+ builderElementIndex:
73
+ targetBuilderElement && builderId
74
+ ? ([] as Element[]).slice
75
+ .call(document.getElementsByClassName(builderId))
76
+ .indexOf(targetBuilderElement)
77
+ : undefined,
78
+ },
79
+ };
80
+ };