@reykjavik/webtools 0.1.22 → 0.1.23-canary.2

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 CHANGED
@@ -4,13 +4,18 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.1.22
7
+ ## 0.1.23-canary.2
8
8
 
9
- _2024-03-14_
9
+ _2024-03-21_
10
10
 
11
- - fix: Relax unnecessary restriction on fallback props
11
+ - feat: Add framework agnostic `@reykjavik/webtools/SiteImprove` module
12
+ deprecate `@reykjavik/webtools/next/SiteImprove` instead
13
+ - `@reykjavik/webtools/remix/Wait`:
14
+ - fix: Properly reject `data.$error`s as to not trigger error boundaries
15
+ - `@reykjavik/webtools/async`:
16
+ - fix: `maxWait` should gracefully ignore rejecting promises
12
17
 
13
- ## 0.1.21
18
+ ## 0.1.21 – 0.1.22
14
19
 
15
20
  _2024-03-14_
16
21
 
package/README.md CHANGED
@@ -22,23 +22,27 @@ bun add @reykjavik/webtools
22
22
  - [`cacheControl` helper](#cachecontrol-helper)
23
23
  - [Type `TTLConfig`](#type-ttlconfig)
24
24
  - [`toSec` TTL helper](#tosec-ttl-helper)
25
- - [`@reykjavik/webtools/CookieHubConsent`](#reykjavikwebtoolscookiehubconsent)
26
- - [`CookieHubProvider` component](#cookiehubprovider-component)
27
- - [`useCookieHubConsent`](#usecookiehubconsent)
28
25
  - [`@reykjavik/webtools/async`](#reykjavikwebtoolsasync)
29
26
  - [`promiseAllObject`](#promiseallobject)
30
27
  - [`maxWait`](#maxwait)
28
+ - [`@reykjavik/webtools/fixIcelandicLocale`](#reykjavikwebtoolsfixicelandiclocale)
29
+ - [Limitations](#limitations)
30
+ - [`@reykjavik/webtools/SiteImprove`](#reykjavikwebtoolssiteimprove)
31
+ - [`SiteImprove` component](#siteimprove-component)
32
+ - [`pingSiteImprove` helper](#pingsiteimprove-helper)
33
+ - [`pingSiteImproveOutbound` helper](#pingsiteimproveoutbound-helper)
34
+ - [`@reykjavik/webtools/CookieHubConsent`](#reykjavikwebtoolscookiehubconsent)
35
+ - [`CookieHubProvider` component](#cookiehubprovider-component)
36
+ - [`useCookieHubConsent`](#usecookiehubconsent)
31
37
  - [`@reykjavik/webtools/vanillaExtract`](#reykjavikwebtoolsvanillaextract)
32
38
  - [`vanillaGlobal`](#vanillaglobal)
33
39
  - [`vanillaProps`](#vanillaprops)
34
40
  - [`vanillaClass`](#vanillaclass)
35
41
  - [`vanillaClassNested`](#vanillaclassnested)
36
42
  - [`vanillaNest`](#vanillanest)
37
- - [`@reykjavik/webtools/fixIcelandicLocale`](#reykjavikwebtoolsfixicelandiclocale)
38
- - [Limitations](#limitations)
39
43
  - [Framework Specific Tools](#framework-specific-tools)
40
- - [Next.js Tools](#nextjs-tools)
41
44
  - [Remix.run Tools](#remixrun-tools)
45
+ - [Next.js Tools](#nextjs-tools)
42
46
  - [Contributing](#contributing)
43
47
  - [Changelog](#changelog)
44
48
 
@@ -184,6 +188,219 @@ const ttlSec = toSec(ttl);
184
188
 
185
189
  ---
186
190
 
191
+ ## `@reykjavik/webtools/async`
192
+
193
+ Contains a few small helpers for working with async functions and promises.
194
+
195
+ ---
196
+
197
+ ### `promiseAllObject`
198
+
199
+ **Syntax:**
200
+ `promiseAllObject<T extends PlainObj>(promisesMap: T>): Promise<{ [K in keyof T]: Awaited<T[K]>; }>`
201
+
202
+ A variation of `Promise.all()` that accepts an object with named promises and
203
+ returns a same-shaped object with the resolved values.
204
+
205
+ ```ts
206
+ import { promiseAllObject } from '@reykjavik/webtools/async';
207
+
208
+ const { user, posts } = await promiseAllObject({
209
+ user: fetchUser(),
210
+ posts: fetchPosts(),
211
+ });
212
+ ```
213
+
214
+ ---
215
+
216
+ ### `maxWait`
217
+
218
+ **Syntax:** `maxWait(timeout: number, promises: Array<any>): Promise<void>`
219
+ **Syntax:**
220
+ `maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]: { value: Awaited<T[K]> } | undefined }>`
221
+
222
+ This somewhat esoteric helper resolves soon as all of the passed `promises`
223
+ have resolved, or after `timeout` milliseconds — whichever comes first.
224
+
225
+ If an object is passed, the resolved value will be an object with the same
226
+ keys, with undefined values for any promises that didn't resolve in time, and
227
+ the resolved values in a `value` container object.
228
+
229
+ If any of the promises reject, their values become undefined in the returned
230
+ object.
231
+
232
+ ```ts
233
+ import { maxWait } from '@reykjavik/webtools/async';
234
+
235
+ const user = fetchUser();
236
+ const posts = fetchPosts();
237
+
238
+ // Array of promises resolves to void
239
+ await maxWait(500, [user, posts]);
240
+
241
+ // Object of promises resolves to an object with any resolved values at that time
242
+ const { user, posts } = await maxWait(500, { user, posts });
243
+
244
+ console.log(user?.value); // undefined | User
245
+ console.log(posts?.value); // undefined | Array<Post>
246
+ ```
247
+
248
+ ---
249
+
250
+ ## `@reykjavik/webtools/fixIcelandicLocale`
251
+
252
+ As of early 2024, Google Chrome still does not support the Icelandic locale
253
+ `is`/`is-IS` in any way. Meanwhile other browsers have supported it for over a
254
+ decade.
255
+
256
+ This module does attempts to patches the following methods/classes by
257
+ substituting the `is` locale with `da` (Danish) and apply a few post-hoc fixes
258
+ to their return values.
259
+
260
+ - `Intl.Collator` and `String.prototype.localeCompare`
261
+ - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
262
+ - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
263
+ - `Intl.PluralRules`
264
+ - `Intl.ListFormat`
265
+
266
+ This provides usable (but not perfect) results, with some caveats listed
267
+ below.
268
+
269
+ To apply these patches, simply "side-effect import" this module at the top of
270
+ your app's entry point:
271
+
272
+ ```ts
273
+ import '@reykjavik/webtools/fixIcelandicLocale';
274
+
275
+ // Then continue with your day and use `localeCompare` and other Intl.* methods
276
+ // as you normally would. (See "limitations" below.)
277
+ ```
278
+
279
+ (**NOTE** The patch is only applied in engines that fail a simple feature
280
+ detection test.)
281
+
282
+ ### Limitations
283
+
284
+ **`Intl.Collator` and `localeCompare`:**
285
+
286
+ - It incorrectly treats `ð` and `d` as the same letter (most of the time), and
287
+ the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
288
+ with their non-accented counterparts (unless the compared).
289
+ We fix this only for the first letter in the string, but not for the rest of
290
+ it.
291
+
292
+ **`Intl.NumberFormat` and `toLocaleString`:**
293
+
294
+ - The `style: "unit"` option is not supported and prints units in Danish. (Soo
295
+ many units and unit-variants…)
296
+ - The `currencyDisplay: "name"` option is not supported and prints the
297
+ currency's full name in Danish.
298
+
299
+ **`Intl.DateTimeFormat` and `toLocaleDateString`:**
300
+
301
+ - The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
302
+ print the corresponding Danish initials.
303
+ - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
304
+ will appear in Danish.
305
+ - The `timeStyle: 'full'` option prints the timezone names in Danish
306
+ - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
307
+ noon.
308
+
309
+ ---
310
+
311
+ ## `@reykjavik/webtools/SiteImprove`
312
+
313
+ Contains React helpers for loading SiteImprove's analytics scripts, and
314
+ perform page-view and custom event tracking in applications with client-side
315
+ (`pushState`) routing.
316
+
317
+ ### `SiteImprove` component
318
+
319
+ A component for loading a SiteImprove analytics script and set up page-view
320
+ tracking across URL routes.
321
+
322
+ It also automatically logs all out-bound link clicks, to match the behavior of
323
+ the vanilla SiteImprove script.
324
+
325
+ **Props:**
326
+
327
+ The Component's props have detailed JSDoc comments (displayed in your code
328
+ editor), but there's a brief summary:
329
+
330
+ - `accountId?: string` — Your SiteImprove account ID. (alternative to
331
+ `scriptUrl` prop).
332
+ - `scriptUrl?: string` — The full SiteImprove analytics script URL.
333
+ (alternative to `accountId` prop).
334
+ - `hasConsented?: boolean` — Manual GDPR 'analytics' consent flag. Allows hard
335
+ opt-out, but defers to
336
+ [`CookieHubProvider` values](./README.md#usecookiehubconsent) if they are
337
+ available.
338
+ - `onLoad?: (e: unknown) => void` — Fires when the script has loaded.
339
+ - `onError?: (e: unknown) => void` — Fires if loading the script failed.
340
+
341
+ Example usage somewhere in your application:
342
+
343
+ ```js
344
+ import { SiteImprove } from '@reykjavik/webtools/SiteImprove';
345
+
346
+ // ideally emit this from your loader function
347
+ const siteImproveAccountId = '[ACCOUNT_ID]'; // e.g. "7654321"
348
+
349
+ const location = useRouter()
350
+ // ...then Inside root.tsx component:
351
+ <SiteImprove
352
+ accountId={siteImproveAccountId}
353
+ onError={(error) =>
354
+ Logger('error', 'An error occured initializing siteimprove', error)
355
+ }
356
+ />;
357
+ ```
358
+
359
+ In dev mode it does NOT load the SiteImprove script and merely logs page-view
360
+ events to the console.
361
+
362
+ ### `pingSiteImprove` helper
363
+
364
+ **Syntax:**
365
+ `pingSiteImprove(category: string, action: string, label?: string): void`
366
+
367
+ A small helper for tracking custom UI events and reporting them to SiteImrove.
368
+
369
+ It safely manages GDPR consent, so you can use it unconditionally.
370
+
371
+ ```js
372
+ import { pingSiteImprove } from '@reykjavik/webtools/SiteImprove';
373
+
374
+ const handleSubmit = () => {
375
+ // perform submit action...
376
+ if (success) {
377
+ pingSiteImprove('application', 'add_new');
378
+ }
379
+ };
380
+ ```
381
+
382
+ ### `pingSiteImproveOutbound` helper
383
+
384
+ **Syntax:** `pingSiteImproveOutbound(ourl: string): void`
385
+
386
+ A small helper for reporting to SiteImrove when the user is programmatically
387
+ being sent to a different URL/resource.
388
+
389
+ ```js
390
+ import { pingSiteImproveOutbound } from '@reykjavik/webtools/SiteImprove';
391
+
392
+ const handleSubmit = () => {
393
+ // perform submit action...
394
+ if (success) {
395
+ const fileUrl = '/download/report.pdf';
396
+ pingSiteImproveOutbound(fileUrl);
397
+ document.location.href = fileUrl;
398
+ }
399
+ };
400
+ ```
401
+
402
+ ---
403
+
187
404
  ## `@reykjavik/webtools/CookieHubConsent`
188
405
 
189
406
  Contains React helpers for loading CookieHub's consent manager and reading
@@ -257,62 +474,6 @@ this hook will return an empty object.
257
474
 
258
475
  ---
259
476
 
260
- ## `@reykjavik/webtools/async`
261
-
262
- Contains a few small helpers for working with async functions and promises.
263
-
264
- ---
265
-
266
- ### `promiseAllObject`
267
-
268
- **Syntax:**
269
- `promiseAllObject<T extends PlainObj>(promisesMap: T>): Promise<{ [K in keyof T]: Awaited<T[K]>; }>`
270
-
271
- A variation of `Promise.all()` that accepts an object with named promises and
272
- returns a same-shaped object with the resolved values.
273
-
274
- ```ts
275
- import { promiseAllObject } from '@reykjavik/webtools/async';
276
-
277
- const { user, posts } = await promiseAllObject({
278
- user: fetchUser(),
279
- posts: fetchPosts(),
280
- });
281
- ```
282
-
283
- ---
284
-
285
- ### `maxWait`
286
-
287
- **Syntax:** `maxWait(timeout: number, promises: Array<any>): Promise<void>`
288
- **Syntax:**
289
- `maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]: { value: Awaited<T[K]> } | undefined }>`
290
-
291
- This somewhat esoteric helper resolves soon as all of the passed `promises`
292
- have resolved, or after `timeout` milliseconds — whichever comes first.
293
-
294
- If an object is passed, the resolved value will be an object with the same
295
- keys, with undefined values for any promises that didn't resolve in time, and
296
- the resolved values in a `value` container object.
297
-
298
- ```ts
299
- import { maxWait } from '@reykjavik/webtools/async';
300
-
301
- const user = fetchUser();
302
- const posts = fetchPosts();
303
-
304
- // Array of promises resolves to void
305
- await maxWait(500, [user, posts]);
306
-
307
- // Object of promises resolves to an object with any resolved values at that time
308
- const { user, posts } = await maxWait(500, { user, posts });
309
-
310
- console.log(user?.value); // undefined | User
311
- console.log(posts?.value); // undefined | Array<Post>
312
- ```
313
-
314
- ---
315
-
316
477
  ## `@reykjavik/webtools/vanillaExtract`
317
478
 
318
479
  Contains helpers for writing [vanilla-extract](https://vanilla-extract.style)
@@ -486,68 +647,14 @@ vanillaGlobal(`
486
647
 
487
648
  ---
488
649
 
489
- ## `@reykjavik/webtools/fixIcelandicLocale`
490
-
491
- As of early 2004, Google Chrome still does not support the Icelandic locale
492
- `is`/`is-IS` in any way. Meanwhile other browsers have supported it for over a
493
- decade.
494
-
495
- This module does attempts to patches the following methods/classes by
496
- substituting the `is` locale with `da` (Danish) and apply a few post-hoc fixes
497
- to their return values.
498
-
499
- - `Intl.Collator` and `String.prototype.localeCompare`
500
- - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
501
- - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
502
- - `Intl.PluralRules`
503
- - `Intl.ListFormat`
504
-
505
- This provides usable (but not perfect) results, with some caveats listed
506
- below.
507
-
508
- To apply these patches, simply "side-effect import" this module at the top of
509
- your app's entry point:
510
-
511
- ```ts
512
- import '@reykjavik/webtools/fixIcelandicLocale';
513
-
514
- // Then continue with your day and use `localeCompare` and other Intl.* methods
515
- // as you normally would. (See "limitations" below.)
516
- ```
517
-
518
- (**NOTE** The patch is only applied in engines that fail a simple feature
519
- detection test.)
520
-
521
- ### Limitations
522
-
523
- **`Intl.Collator` and `localeCompare`:**
524
-
525
- - It incorrectly treats `ð` and `d` as the same letter (most of the time), and
526
- the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
527
- with their non-accented counterparts (unless the compared).
528
- We fix this only for the first letter in the string, but not for the rest of
529
- it.
530
-
531
- **`Intl.NumberFormat` and `toLocaleString`:**
532
-
533
- - The `style: "unit"` option is not supported and prints units in Danish. (Soo
534
- many units and unit-variants…)
535
- - The `currencyDisplay: "name"` option is not supported and prints the
536
- currency's full name in Danish.
537
-
538
- **`Intl.DateTimeFormat` and `toLocaleDateString`:**
539
-
540
- - The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
541
- print the corresponding Danish initials.
542
- - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
543
- will appear in Danish.
544
- - The `timeStyle: 'full'` option prints the timezone names in Danish
545
- - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
546
- noon.
650
+ ## Framework Specific Tools
547
651
 
548
652
  ---
549
653
 
550
- ## Framework Specific Tools
654
+ ### Remix.run Tools
655
+
656
+ See [README-remix.md](./README-remix.md) for helpers and components
657
+ specifically designed for use in Remix.run projects.
551
658
 
552
659
  ---
553
660
 
@@ -556,24 +663,11 @@ detection test.)
556
663
  <a name="reykjavikwebtoolsnexthttp"></a> <a name="makeerrorizeapphoc"></a>
557
664
  <a name="showerrorpage-helper"></a> <a name="notmodified304-helper"></a>
558
665
  <a name="reykjavikwebtoolsnextsiteimprove"></a>
559
- <a name="siteimprove-component"></a> <a name="pingsiteimprove-helper"></a>
560
- <a name="pingsiteimproveoutbound-helper"></a>
561
666
 
562
667
  ### Next.js Tools
563
668
 
564
- This package contains some helpers and components that are specifically
565
- designed for use in [Next.js](https://nextjs.org/) projects.
566
-
567
- See [README-nextjs.md](./README-nextjs.md) for more info.
568
-
569
- ---
570
-
571
- ### Remix.run Tools
572
-
573
- This package contains some helpers and components that are specifically
574
- designed for use in [Remix.run](https://remix.run) projects.
575
-
576
- See [README-remix.md](./README-remix.md) for more info.
669
+ See [README-nextjs.md](./README-nextjs.md) for helpers and components
670
+ specifically designed for use in Next.js projects.
577
671
 
578
672
  ---
579
673
 
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { EitherObj } from '@reykjavik/hanna-utils';
2
3
  declare global {
3
4
  interface Window {
@@ -14,8 +15,8 @@ declare global {
14
15
  };
15
16
  }
16
17
  }
17
- export type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
18
- export type SiteImprovePageView = [
18
+ type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
19
+ type SiteImprovePageView = [
19
20
  type: 'trackdynamic',
20
21
  data: {
21
22
  /** New page URL */
@@ -26,7 +27,7 @@ export type SiteImprovePageView = [
26
27
  title?: string;
27
28
  }
28
29
  ];
29
- export type SiteImproveRequest = [
30
+ type SiteImproveRequest = [
30
31
  type: 'request',
31
32
  data: {
32
33
  /** Outbound URL */
@@ -36,33 +37,25 @@ export type SiteImproveRequest = [
36
37
  autoonclick?: 1;
37
38
  }
38
39
  ];
39
- export type SiteImproveCustomEvent = [
40
+ type SiteImproveCustomEvent = [
40
41
  type: 'event',
41
42
  category: string,
42
43
  action: string,
43
44
  label?: string
44
45
  ];
45
- /**
46
- * A small helper to send "trackdynamic" page view/load events to SiteImrove.
47
- *
48
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
- */
50
- export declare const trackDynamicPageView: (url: string, refUrl?: string, title?: string) => void;
51
46
  /**
52
47
  * A small helper for tracking custom UI events and reporting them to SiteImrove.
53
48
  *
54
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
55
50
  */
56
51
  export declare const pingSiteImprove: (category: string, action: string, label?: string) => void;
57
52
  /**
58
53
  * A small helper for reporting to SiteImrove when the user is programmatically
59
54
  * being sent to a different URL/resource.
60
55
  *
61
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimproveoutbound-helper
56
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
62
57
  */
63
58
  export declare const pingSiteImproveOutbound: (ourl: string) => void;
64
- export declare const logOutboundLinks: () => () => void;
65
- export declare const makeScriptUrl: (accountId: string) => string;
66
59
  export type SiteImproveProps = EitherObj<{
67
60
  /**
68
61
  * Your SiteImprove account ID.
@@ -88,7 +81,7 @@ export type SiteImproveProps = EitherObj<{
88
81
  * A value of `true` still defers to the 'analytics' consent state provided
89
82
  * by the `CookieHubProvider` component (if present).
90
83
  */
91
- hasConstented?: boolean;
84
+ hasConsented?: boolean;
92
85
  /**
93
86
  * Custom callback for when the SiteImprove script has loaded.
94
87
  */
@@ -98,3 +91,11 @@ export type SiteImproveProps = EitherObj<{
98
91
  */
99
92
  onError?: (e: unknown) => void;
100
93
  };
94
+ /**
95
+ * A component for loading a SiteImprove analytics script and set up page-view
96
+ * tracking across client-side (pushState, replaceState) routing.
97
+ *
98
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
99
+ */
100
+ export declare const SiteImprove: (props: SiteImproveProps) => React.JSX.Element | null;
101
+ export {};
package/SiteImprove.js ADDED
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SiteImprove = exports.pingSiteImproveOutbound = exports.pingSiteImprove = void 0;
27
+ const react_1 = __importStar(require("react"));
28
+ const CookieHubConsent_js_1 = require("./CookieHubConsent.js");
29
+ // END: Mock typing of SiteImprove's event tracking API
30
+ // --------------------------------------------------------------------------
31
+ //
32
+ // ---------------------------------------------------------------------------
33
+ const _emitEvent = typeof window === 'undefined'
34
+ ? () => undefined
35
+ : (event) => {
36
+ let _sz = window._sz;
37
+ if (!_sz) {
38
+ _sz = window._sz = [];
39
+ _sz._jit_defined_ = true;
40
+ }
41
+ _sz.push(event);
42
+ if (process.env.NODE_ENV === 'development') {
43
+ console.info('SiteImprove:', event);
44
+ }
45
+ };
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * A small helper to send "trackdynamic" page view/load events to SiteImrove.
49
+ */
50
+ const trackDynamicPageView = (url, refUrl, title) => _emitEvent(['trackdynamic', { url, ref: refUrl, title }]);
51
+ // ---------------------------------------------------------------------------
52
+ /**
53
+ * A small helper for tracking custom UI events and reporting them to SiteImrove.
54
+ *
55
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
56
+ */
57
+ const pingSiteImprove = (category, action, label) => {
58
+ if (process.env.NODE_ENV === 'development' &&
59
+ (!window._sz || window._sz._jit_defined_)) {
60
+ console.warn('`pingSiteImprove` was called before SiteImprove script was loaded.');
61
+ }
62
+ _emitEvent(['event', category, action, label]);
63
+ };
64
+ exports.pingSiteImprove = pingSiteImprove;
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * A small helper for reporting to SiteImrove when the user is programmatically
68
+ * being sent to a different URL/resource.
69
+ *
70
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
71
+ */
72
+ const pingSiteImproveOutbound = (ourl) => {
73
+ if (process.env.NODE_ENV === 'development' &&
74
+ (!window._sz || window._sz._jit_defined_)) {
75
+ console.warn('`pingSiteImproveOutbound` was called before SiteImprove script was loaded.');
76
+ }
77
+ _emitEvent(['request', { ourl, ref: document.location.href }]);
78
+ };
79
+ exports.pingSiteImproveOutbound = pingSiteImproveOutbound;
80
+ // ---------------------------------------------------------------------------
81
+ const logOutboundLinks = () => {
82
+ // NOTE: It's normal for SiteImprove's outbound logging to happen also for internal
83
+ // links, but it's not a problem since SiteImprove filter the results themselves.
84
+ const captureLinkClicks = (e) => {
85
+ const link = e.target.closest('a[href]');
86
+ if (!link || link.$$bound) {
87
+ return;
88
+ }
89
+ link.$$bound = true;
90
+ // Waiting for the bubble phase allows other click handlers to preventDefault()
91
+ link.addEventListener('click', (e) => {
92
+ var _a, _b;
93
+ if (e.defaultPrevented) {
94
+ return;
95
+ }
96
+ // Skip logging outbound request if SiteImprove has already done so.
97
+ // BTW, SiteImprove binds its autoonclick handlers on "mousedown"
98
+ // so they're guaranteed to have run before our "click" listener.
99
+ const events = (_b = (_a = window._sz) === null || _a === void 0 ? void 0 : _a.core) === null || _b === void 0 ? void 0 : _b.data;
100
+ const [type, data] = (events && events[events.length - 1]) || [];
101
+ if (type === 'request' && data.autoonclick && data.ourl === link.href) {
102
+ return;
103
+ }
104
+ (0, exports.pingSiteImproveOutbound)(link.href);
105
+ });
106
+ };
107
+ const { body } = document;
108
+ // bind 'click' listener to the capture phase
109
+ body.addEventListener('click', captureLinkClicks, true);
110
+ return () => body.removeEventListener('click', captureLinkClicks, true);
111
+ };
112
+ // ---------------------------------------------------------------------------
113
+ const makeScriptUrl = (accountId) => `https://siteimproveanalytics.com/js/siteanalyze_${accountId}.js`;
114
+ // ---------------------------------------------------------------------------
115
+ const useResolvedAnalyticsConsent = (hasConsented) => {
116
+ const { analytics } = (0, CookieHubConsent_js_1.useCookieHubConsent)();
117
+ return ((analytics && hasConsented !== false) || (analytics === undefined && hasConsented));
118
+ };
119
+ // ---------------------------------------------------------------------------
120
+ const mockSIGlobalIfNeeded = (props) => {
121
+ if (process.env.NODE_ENV !== 'production') {
122
+ setTimeout(() => {
123
+ if (!window._sz) {
124
+ console.info('Mock loading SiteImprove in development mode.', props.scriptUrl || props.accountId);
125
+ window._sz = [];
126
+ }
127
+ }, 300);
128
+ }
129
+ };
130
+ // ===========================================================================
131
+ let lastUrl;
132
+ const loc = typeof document !== 'undefined' ? document.location : {};
133
+ /**
134
+ * A component for loading a SiteImprove analytics script and set up page-view
135
+ * tracking across client-side (pushState, replaceState) routing.
136
+ *
137
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
138
+ */
139
+ const SiteImprove = (props) => {
140
+ var _a;
141
+ const consented = useResolvedAnalyticsConsent(props.hasConsented);
142
+ const thisUrl = `${loc.pathname}${loc.search}${loc.hash}`;
143
+ (0, react_1.useEffect)(() => {
144
+ if (!consented) {
145
+ lastUrl = thisUrl;
146
+ return;
147
+ }
148
+ if (lastUrl === undefined) {
149
+ lastUrl = thisUrl;
150
+ }
151
+ const checkCurrentUrl = () => {
152
+ if (thisUrl !== lastUrl) {
153
+ trackDynamicPageView(thisUrl, lastUrl, document.title);
154
+ lastUrl = thisUrl;
155
+ }
156
+ };
157
+ checkCurrentUrl();
158
+ window.addEventListener('popstate', checkCurrentUrl);
159
+ return () => window.removeEventListener('popstate', checkCurrentUrl);
160
+ }, [consented, thisUrl]);
161
+ (0, react_1.useEffect)(() => {
162
+ if (!consented) {
163
+ return;
164
+ }
165
+ mockSIGlobalIfNeeded(props);
166
+ return logOutboundLinks();
167
+ },
168
+ // eslint-disable-next-line react-hooks/exhaustive-deps
169
+ [consented]);
170
+ if (!consented || process.env.NODE_ENV !== 'production') {
171
+ return null;
172
+ }
173
+ const scriptUrl = (_a = props.scriptUrl) !== null && _a !== void 0 ? _a : makeScriptUrl(props.accountId);
174
+ return react_1.default.createElement("script", { defer: true, src: scriptUrl, onLoad: props.onLoad, onError: props.onError });
175
+ };
176
+ exports.SiteImprove = SiteImprove;