@reykjavik/webtools 0.1.19 → 0.1.21
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 +14 -10
- package/README.md +78 -9
- package/async.d.ts +26 -0
- package/async.js +46 -0
- package/esm/async.d.ts +26 -0
- package/esm/async.js +40 -0
- package/esm/fixIcelandicLocale.privates.js +47 -74
- package/esm/index.d.ts +3 -0
- package/esm/remix/Wait.d.ts +49 -0
- package/esm/remix/Wait.js +21 -0
- package/esm/remix/http.d.ts +7 -0
- package/esm/remix/http.js +11 -0
- package/fixIcelandicLocale.privates.js +47 -74
- package/index.d.ts +3 -0
- package/package.json +17 -1
- package/remix/Wait.d.ts +49 -0
- package/remix/Wait.js +48 -0
- package/remix/http.d.ts +7 -0
- package/remix/http.js +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,22 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
-
## 0.1.
|
|
7
|
+
## 0.1.21
|
|
8
8
|
|
|
9
|
-
_2024-03-
|
|
9
|
+
_2024-03-14_
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- fix: Incorrect `Intl.ListFormat` format in narrow+unit mode
|
|
11
|
+
- feat: Add `@reykjavik/webtools/remix/Wait` component
|
|
12
|
+
- feat: Add `@reykjavik/webtools/remix/http` module — with `isClientFetch`
|
|
13
|
+
helper
|
|
14
|
+
- feat: Add `@reykjavik/webtools/remix/async` module with promise helpers
|
|
16
15
|
|
|
17
|
-
## 0.1.18
|
|
16
|
+
## 0.1.18 – 0.1.20
|
|
18
17
|
|
|
19
18
|
_2024-03-11_
|
|
20
19
|
|
|
21
20
|
- `@reykjavik/webtools/fixIcelandicLocale`:
|
|
22
21
|
- feat: Patch `Intl.PluralRules` and `Intl.ListFormat`
|
|
22
|
+
- fix: Incorrect alphabetization of accented characters as part of a word …
|
|
23
|
+
(not just a single character) This fix corrects the sorting of initial
|
|
24
|
+
letters, but characters inside the string stay mixed in with their
|
|
25
|
+
unaccented base character.
|
|
26
|
+
- fix: Make all pached `Intl.*` methods bound to their instances
|
|
23
27
|
|
|
24
28
|
## 0.1.16 – 0.1.17
|
|
25
29
|
|
|
@@ -49,8 +53,8 @@ _2024-03-06_
|
|
|
49
53
|
|
|
50
54
|
_2024-02-29_
|
|
51
55
|
|
|
52
|
-
- feat: Add `@reykjavik/webtools/next/vanillaExtract`
|
|
53
|
-
|
|
56
|
+
- feat: Add `@reykjavik/webtools/next/vanillaExtract` module — with plain-CSS
|
|
57
|
+
injection helpers
|
|
54
58
|
- fix: Mark `peerDependencies` as optional
|
|
55
59
|
|
|
56
60
|
## 0.1.11
|
package/README.md
CHANGED
|
@@ -25,6 +25,9 @@ bun add @reykjavik/webtools
|
|
|
25
25
|
- [`@reykjavik/webtools/CookieHubConsent`](#reykjavikwebtoolscookiehubconsent)
|
|
26
26
|
- [`CookieHubProvider` component](#cookiehubprovider-component)
|
|
27
27
|
- [`useCookieHubConsent`](#usecookiehubconsent)
|
|
28
|
+
- [`@reykjavik/webtools/async`](#reykjavikwebtoolsasync)
|
|
29
|
+
- [`promiseAllObject`](#promiseallobject)
|
|
30
|
+
- [`maxWait`](#maxwait)
|
|
28
31
|
- [`@reykjavik/webtools/vanillaExtract`](#reykjavikwebtoolsvanillaextract)
|
|
29
32
|
- [`vanillaGlobal`](#vanillaglobal)
|
|
30
33
|
- [`vanillaProps`](#vanillaprops)
|
|
@@ -35,6 +38,7 @@ bun add @reykjavik/webtools
|
|
|
35
38
|
- [Limitations](#limitations)
|
|
36
39
|
- [Framework Specific Tools](#framework-specific-tools)
|
|
37
40
|
- [Next.js Tools](#nextjs-tools)
|
|
41
|
+
- [Remix.run Tools](#remixrun-tools)
|
|
38
42
|
- [Contributing](#contributing)
|
|
39
43
|
- [Changelog](#changelog)
|
|
40
44
|
|
|
@@ -165,7 +169,7 @@ behavior.
|
|
|
165
169
|
### `toSec` TTL helper
|
|
166
170
|
|
|
167
171
|
**Syntax:**
|
|
168
|
-
`` toSec
|
|
172
|
+
`` toSec(ttl: number | `${number}${'s'|'m'|'h'|'d'|'w'}`): number ``
|
|
169
173
|
|
|
170
174
|
Converts a `TTL` (max-age) value into seconds, and returns `0` for bad and/or
|
|
171
175
|
negative input values.
|
|
@@ -253,6 +257,62 @@ this hook will return an empty object.
|
|
|
253
257
|
|
|
254
258
|
---
|
|
255
259
|
|
|
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
|
+
|
|
256
316
|
## `@reykjavik/webtools/vanillaExtract`
|
|
257
317
|
|
|
258
318
|
Contains helpers for writing [vanilla-extract](https://vanilla-extract.style)
|
|
@@ -265,7 +325,7 @@ CSS.
|
|
|
265
325
|
|
|
266
326
|
### `vanillaGlobal`
|
|
267
327
|
|
|
268
|
-
**Syntax:** `vanillaGlobal
|
|
328
|
+
**Syntax:** `vanillaGlobal(css: string): void`
|
|
269
329
|
|
|
270
330
|
Inserts free-form CSS as a vanilla-extract `globalStyle`.
|
|
271
331
|
|
|
@@ -282,7 +342,7 @@ vanillaGlobal(`
|
|
|
282
342
|
|
|
283
343
|
### `vanillaProps`
|
|
284
344
|
|
|
285
|
-
**Syntax:** `vanillaProps
|
|
345
|
+
**Syntax:** `vanillaProps(css: string): GlobalStyleRule`
|
|
286
346
|
|
|
287
347
|
Spreads the return value into a style object, to inject free-form CSS
|
|
288
348
|
properties (or nested blocks)
|
|
@@ -307,8 +367,8 @@ const myStyle = style({
|
|
|
307
367
|
|
|
308
368
|
### `vanillaClass`
|
|
309
369
|
|
|
310
|
-
**Syntax:** `vanillaClass
|
|
311
|
-
**Syntax:** `vanillaClass
|
|
370
|
+
**Syntax:** `vanillaClass(css: string): string`
|
|
371
|
+
**Syntax:** `vanillaClass(debugId: string, css: string): string`
|
|
312
372
|
|
|
313
373
|
Returns a scoped cssClassName styled with free-form CSS. This function is a
|
|
314
374
|
thin wrapper around vanilla-extract's `style` function.
|
|
@@ -333,8 +393,8 @@ export const humanReadableClass = vanillaClass(
|
|
|
333
393
|
|
|
334
394
|
### `vanillaClassNested`
|
|
335
395
|
|
|
336
|
-
**Syntax:** `vanillaClassNested
|
|
337
|
-
**Syntax:** `vanillaClassNested
|
|
396
|
+
**Syntax:** `vanillaClassNested(css: string): string`
|
|
397
|
+
**Syntax:** `vanillaClassNested(debugId: string, css: string): string`
|
|
338
398
|
|
|
339
399
|
Returns a scoped cssClassName styled with free-form CSS.
|
|
340
400
|
|
|
@@ -373,7 +433,7 @@ comments, etc. If you need something more sophisticated, use a custom
|
|
|
373
433
|
|
|
374
434
|
### `vanillaNest`
|
|
375
435
|
|
|
376
|
-
**Syntax:** `vanillaNest
|
|
436
|
+
**Syntax:** `vanillaNest(ampSelector: string, css: string): string`
|
|
377
437
|
|
|
378
438
|
Replaces all `&` tokens with the given selector string, in a direct (read.
|
|
379
439
|
"dumb") way. It's mainly useful when used with style-mixins, etc.
|
|
@@ -504,7 +564,16 @@ detection test.)
|
|
|
504
564
|
This package contains some helpers and components that are specifically
|
|
505
565
|
designed for use in [Next.js](https://nextjs.org/) projects.
|
|
506
566
|
|
|
507
|
-
See [README-nextjs.md](README-nextjs.md) for more info.
|
|
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.
|
|
508
577
|
|
|
509
578
|
---
|
|
510
579
|
|
package/async.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type PlainObj = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Simple sleep function. Returns a promise that resolves after `length`
|
|
4
|
+
* milliseconds.
|
|
5
|
+
*/
|
|
6
|
+
export declare const sleep: (length: number) => Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves soon as all of the passed `promises` have resolved, or after
|
|
9
|
+
* `timeout` milliseconds — whichever comes first.
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#maxwait
|
|
12
|
+
*/
|
|
13
|
+
export declare function maxWait(timeout: number, promises: Array<unknown>): Promise<void>;
|
|
14
|
+
export declare function maxWait<PromiseMap extends PlainObj>(timeout: number, promises: PromiseMap): Promise<{
|
|
15
|
+
-readonly [K in keyof PromiseMap]: {
|
|
16
|
+
value: Awaited<PromiseMap[K]>;
|
|
17
|
+
} | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* A variation of `Promise.all()` that accepts an object with named promises
|
|
21
|
+
* and returns a same-shaped object with the resolved values.
|
|
22
|
+
*
|
|
23
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#promiseallobject
|
|
24
|
+
*/
|
|
25
|
+
export declare const promiseAllObject: <T extends PlainObj>(promisesMap: T) => Promise<{ -readonly [K in keyof T]: Awaited<T[K]>; }>;
|
|
26
|
+
export {};
|
package/async.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promiseAllObject = exports.maxWait = exports.sleep = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple sleep function. Returns a promise that resolves after `length`
|
|
6
|
+
* milliseconds.
|
|
7
|
+
*/
|
|
8
|
+
const sleep = (length) => new Promise((resolve) => setTimeout(resolve, length));
|
|
9
|
+
exports.sleep = sleep;
|
|
10
|
+
function maxWait(timeout, promises) {
|
|
11
|
+
if (Array.isArray(promises)) {
|
|
12
|
+
return Promise.race([(0, exports.sleep)(timeout), Promise.all(promises).then(() => undefined)]);
|
|
13
|
+
}
|
|
14
|
+
return Promise.race([(0, exports.sleep)(timeout), Promise.all(Object.values(promises))]).then(() => {
|
|
15
|
+
Object.entries(promises).forEach(([key, value]) => {
|
|
16
|
+
if (value instanceof Promise) {
|
|
17
|
+
promises[key] = undefined;
|
|
18
|
+
value.then((value) => {
|
|
19
|
+
promises[key] = { value };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
promises[key] = { value };
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return (0, exports.sleep)(0).then(() => promises);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
exports.maxWait = maxWait;
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Adapted from https://github.com/marcelowa/promise-all-properties
|
|
32
|
+
/**
|
|
33
|
+
* A variation of `Promise.all()` that accepts an object with named promises
|
|
34
|
+
* and returns a same-shaped object with the resolved values.
|
|
35
|
+
*
|
|
36
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#promiseallobject
|
|
37
|
+
*/
|
|
38
|
+
const promiseAllObject = (promisesMap) => Promise.all(Object.values(promisesMap)).then((results) => {
|
|
39
|
+
const keys = Object.keys(promisesMap);
|
|
40
|
+
const resolvedMap = {};
|
|
41
|
+
for (let i = 0; i < results.length; i++) {
|
|
42
|
+
resolvedMap[keys[i]] = results[i];
|
|
43
|
+
}
|
|
44
|
+
return resolvedMap;
|
|
45
|
+
});
|
|
46
|
+
exports.promiseAllObject = promiseAllObject;
|
package/esm/async.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type PlainObj = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Simple sleep function. Returns a promise that resolves after `length`
|
|
4
|
+
* milliseconds.
|
|
5
|
+
*/
|
|
6
|
+
export declare const sleep: (length: number) => Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves soon as all of the passed `promises` have resolved, or after
|
|
9
|
+
* `timeout` milliseconds — whichever comes first.
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#maxwait
|
|
12
|
+
*/
|
|
13
|
+
export declare function maxWait(timeout: number, promises: Array<unknown>): Promise<void>;
|
|
14
|
+
export declare function maxWait<PromiseMap extends PlainObj>(timeout: number, promises: PromiseMap): Promise<{
|
|
15
|
+
-readonly [K in keyof PromiseMap]: {
|
|
16
|
+
value: Awaited<PromiseMap[K]>;
|
|
17
|
+
} | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* A variation of `Promise.all()` that accepts an object with named promises
|
|
21
|
+
* and returns a same-shaped object with the resolved values.
|
|
22
|
+
*
|
|
23
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#promiseallobject
|
|
24
|
+
*/
|
|
25
|
+
export declare const promiseAllObject: <T extends PlainObj>(promisesMap: T) => Promise<{ -readonly [K in keyof T]: Awaited<T[K]>; }>;
|
|
26
|
+
export {};
|
package/esm/async.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple sleep function. Returns a promise that resolves after `length`
|
|
3
|
+
* milliseconds.
|
|
4
|
+
*/
|
|
5
|
+
export const sleep = (length) => new Promise((resolve) => setTimeout(resolve, length));
|
|
6
|
+
export function maxWait(timeout, promises) {
|
|
7
|
+
if (Array.isArray(promises)) {
|
|
8
|
+
return Promise.race([sleep(timeout), Promise.all(promises).then(() => undefined)]);
|
|
9
|
+
}
|
|
10
|
+
return Promise.race([sleep(timeout), Promise.all(Object.values(promises))]).then(() => {
|
|
11
|
+
Object.entries(promises).forEach(([key, value]) => {
|
|
12
|
+
if (value instanceof Promise) {
|
|
13
|
+
promises[key] = undefined;
|
|
14
|
+
value.then((value) => {
|
|
15
|
+
promises[key] = { value };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
promises[key] = { value };
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return sleep(0).then(() => promises);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Adapted from https://github.com/marcelowa/promise-all-properties
|
|
27
|
+
/**
|
|
28
|
+
* A variation of `Promise.all()` that accepts an object with named promises
|
|
29
|
+
* and returns a same-shaped object with the resolved values.
|
|
30
|
+
*
|
|
31
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#promiseallobject
|
|
32
|
+
*/
|
|
33
|
+
export const promiseAllObject = (promisesMap) => Promise.all(Object.values(promisesMap)).then((results) => {
|
|
34
|
+
const keys = Object.keys(promisesMap);
|
|
35
|
+
const resolvedMap = {};
|
|
36
|
+
for (let i = 0; i < results.length; i++) {
|
|
37
|
+
resolvedMap[keys[i]] = results[i];
|
|
38
|
+
}
|
|
39
|
+
return resolvedMap;
|
|
40
|
+
});
|
|
@@ -27,29 +27,24 @@ const PatchedCollator = function Collator(locales, options) {
|
|
|
27
27
|
return new PatchedCollator(locales, options);
|
|
28
28
|
}
|
|
29
29
|
const mappedLocales = mapLocales(locales);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
constructor: PatchedCollator,
|
|
38
|
-
compare(a, b) {
|
|
39
|
-
const res1 = this.super.compare(a, b);
|
|
30
|
+
const parent = _Collator(mappedLocales || locales, options);
|
|
31
|
+
const mapped = !!mappedLocales;
|
|
32
|
+
this.compare = (a, b) => {
|
|
33
|
+
const res1 = parent.compare(a, b);
|
|
34
|
+
if (!mapped) {
|
|
35
|
+
return res1;
|
|
36
|
+
}
|
|
40
37
|
const a0 = a.charAt(0);
|
|
41
38
|
const b0 = b.charAt(0);
|
|
42
39
|
if (/\d/.test(a0 + b0)) {
|
|
43
40
|
return res1;
|
|
44
41
|
}
|
|
45
|
-
const res2 =
|
|
42
|
+
const res2 = parent.compare(a0, b0);
|
|
46
43
|
return res2 !== 0 ? res2 : res1;
|
|
47
|
-
}
|
|
48
|
-
resolvedOptions()
|
|
49
|
-
return this.super.resolvedOptions();
|
|
50
|
-
},
|
|
44
|
+
};
|
|
45
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
51
46
|
};
|
|
52
|
-
PatchedCollator.prototype =
|
|
47
|
+
PatchedCollator.prototype = { constructor: PatchedCollator };
|
|
53
48
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
54
49
|
PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
|
|
55
50
|
PatchedCollator.$original = _Collator;
|
|
@@ -64,11 +59,8 @@ _patchedLocaleCompare.$original = _localeCompare;
|
|
|
64
59
|
// NumberFormat
|
|
65
60
|
// ===========================================================================
|
|
66
61
|
const _NumberFormat = Intl.NumberFormat;
|
|
67
|
-
const reformatNumberParts = function (parts) {
|
|
68
|
-
|
|
69
|
-
return parts;
|
|
70
|
-
}
|
|
71
|
-
const options = this.super.resolvedOptions();
|
|
62
|
+
const reformatNumberParts = function (parent, parts) {
|
|
63
|
+
const options = parent.resolvedOptions();
|
|
72
64
|
if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
|
|
73
65
|
const currency = options.currency;
|
|
74
66
|
if (currency === 'DKK' || currency === 'ISK') {
|
|
@@ -87,31 +79,21 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
|
|
|
87
79
|
return new PatchedNumberFormat(locales, options);
|
|
88
80
|
}
|
|
89
81
|
const mappedLocales = mapLocales(locales);
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
const parent = _NumberFormat(mappedLocales || locales, options);
|
|
83
|
+
const mapped = !!mappedLocales;
|
|
84
|
+
this.format = (value) => combineParts(this.formatToParts(value));
|
|
85
|
+
this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
|
|
86
|
+
this.formatToParts = (value) => {
|
|
87
|
+
const parts = parent.formatToParts(value);
|
|
88
|
+
return mapped ? reformatNumberParts(parent, parts) : parts;
|
|
89
|
+
};
|
|
90
|
+
this.formatRangeToParts = (value1, value2) => {
|
|
91
|
+
const parts = parent.formatRangeToParts(value1, value2);
|
|
92
|
+
return mapped ? reformatNumberParts(parent, parts) : parts;
|
|
93
|
+
};
|
|
94
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
92
95
|
};
|
|
93
|
-
|
|
94
|
-
// ability to instantiate without `new` is a bit of a pain.
|
|
95
|
-
// Eagerly interested in finding a better way to do this.
|
|
96
|
-
const numberFormatProto = {
|
|
97
|
-
constructor: PatchedNumberFormat,
|
|
98
|
-
format(value) {
|
|
99
|
-
return combineParts(this.formatToParts(value));
|
|
100
|
-
},
|
|
101
|
-
formatRange(value1, value2) {
|
|
102
|
-
return combineParts(this.formatRangeToParts(value1, value2));
|
|
103
|
-
},
|
|
104
|
-
formatToParts(value) {
|
|
105
|
-
return reformatNumberParts.call(this, this.super.formatToParts(value));
|
|
106
|
-
},
|
|
107
|
-
formatRangeToParts(value1, value2) {
|
|
108
|
-
return reformatNumberParts.call(this, this.super.formatRangeToParts(value1, value2));
|
|
109
|
-
},
|
|
110
|
-
resolvedOptions() {
|
|
111
|
-
return this.super.resolvedOptions();
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
PatchedNumberFormat.prototype = numberFormatProto;
|
|
96
|
+
PatchedNumberFormat.prototype = { constructor: PatchedNumberFormat };
|
|
115
97
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
116
98
|
PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
|
|
117
99
|
PatchedNumberFormat.$original = _NumberFormat;
|
|
@@ -197,11 +179,8 @@ const partMappers = {
|
|
|
197
179
|
}
|
|
198
180
|
},
|
|
199
181
|
};
|
|
200
|
-
const reformatDateTimeParts = function (parts) {
|
|
201
|
-
|
|
202
|
-
return parts;
|
|
203
|
-
}
|
|
204
|
-
const options = this.super.resolvedOptions();
|
|
182
|
+
const reformatDateTimeParts = function (parent, parts) {
|
|
183
|
+
const options = parent.resolvedOptions();
|
|
205
184
|
parts.forEach((part, idx) => {
|
|
206
185
|
var _a;
|
|
207
186
|
const mapper = partMappers[part.type];
|
|
@@ -226,31 +205,21 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
|
|
|
226
205
|
hourCycle: 'h11',
|
|
227
206
|
};
|
|
228
207
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
},
|
|
243
|
-
formatToParts(value) {
|
|
244
|
-
return reformatDateTimeParts.call(this, this.super.formatToParts(value));
|
|
245
|
-
},
|
|
246
|
-
formatRangeToParts(value1, value2) {
|
|
247
|
-
return reformatDateTimeParts.call(this, this.super.formatRangeToParts(value1, value2));
|
|
248
|
-
},
|
|
249
|
-
resolvedOptions() {
|
|
250
|
-
return this.super.resolvedOptions();
|
|
251
|
-
},
|
|
208
|
+
const parent = _DateTimeFormat(mappedLocales || locales, options);
|
|
209
|
+
const mapped = !!mappedLocales;
|
|
210
|
+
this.format = (value) => combineParts(this.formatToParts(value));
|
|
211
|
+
this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
|
|
212
|
+
this.formatToParts = (value) => {
|
|
213
|
+
const parts = parent.formatToParts(value);
|
|
214
|
+
return mapped ? reformatDateTimeParts(parent, parts) : parts;
|
|
215
|
+
};
|
|
216
|
+
this.formatRangeToParts = (value1, value2) => {
|
|
217
|
+
const parts = parent.formatRangeToParts(value1, value2);
|
|
218
|
+
return mapped ? reformatDateTimeParts(parent, parts) : parts;
|
|
219
|
+
};
|
|
220
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
252
221
|
};
|
|
253
|
-
PatchedDateTimeFormat.prototype =
|
|
222
|
+
PatchedDateTimeFormat.prototype = { constructor: PatchedDateTimeFormat };
|
|
254
223
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
255
224
|
PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
|
|
256
225
|
PatchedDateTimeFormat.$original = _DateTimeFormat;
|
|
@@ -277,6 +246,8 @@ if (_PluralRules) {
|
|
|
277
246
|
super(mappedLocales || locales, options);
|
|
278
247
|
this.mapped = !!mappedLocales;
|
|
279
248
|
this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
|
|
249
|
+
this.select = this.select.bind(this);
|
|
250
|
+
this.selectRange = this.selectRange.bind(this);
|
|
280
251
|
}
|
|
281
252
|
select(n) {
|
|
282
253
|
if (this.mapped) {
|
|
@@ -310,6 +281,8 @@ if (_ListFormat) {
|
|
|
310
281
|
const mappedLocales = mapLocales(locales);
|
|
311
282
|
super(mappedLocales || locales, options);
|
|
312
283
|
this.mapped = !!mappedLocales;
|
|
284
|
+
this.format = this.format.bind(this);
|
|
285
|
+
this.formatToParts = this.formatToParts.bind(this);
|
|
313
286
|
}
|
|
314
287
|
format(list) {
|
|
315
288
|
return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
|
package/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/// <reference path="./vanillaExtract.d.ts" />
|
|
2
2
|
/// <reference path="./http.d.ts" />
|
|
3
|
+
/// <reference path="./async.d.ts" />
|
|
3
4
|
/// <reference path="./fixIcelandicLocale.d.ts" />
|
|
5
|
+
/// <reference path="./remix/http.d.ts" />
|
|
4
6
|
/// <reference path="./CookieHubConsent.d.tsx" />
|
|
7
|
+
/// <reference path="./remix/Wait.d.tsx" />
|
|
5
8
|
/// <reference path="./next/SiteImprove.d.tsx" />
|
|
6
9
|
/// <reference path="./next/http.d.tsx" />
|
|
7
10
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
type WaitPropsBase<T> = {
|
|
3
|
+
/**
|
|
4
|
+
* The value you want to wait for before rendering
|
|
5
|
+
*/
|
|
6
|
+
for: Promise<T> | T;
|
|
7
|
+
/**
|
|
8
|
+
* A function to render the children when the value is resolved.
|
|
9
|
+
*
|
|
10
|
+
* (If the promise resolved to an object with a truthy `$error` property,
|
|
11
|
+
* then the `$error` is thrown and this function skipped.)
|
|
12
|
+
*/
|
|
13
|
+
children: (data: Exclude<T, {
|
|
14
|
+
$error: string | number | true | object;
|
|
15
|
+
}>) => ReactNode;
|
|
16
|
+
};
|
|
17
|
+
type WaitFallbacks = {
|
|
18
|
+
/**
|
|
19
|
+
* Custom loading/spinner component.
|
|
20
|
+
*/
|
|
21
|
+
meanwhile?: Exclude<ReactNode, number>;
|
|
22
|
+
/**
|
|
23
|
+
* Custom error component if the promise is rejected or if it resolves to an
|
|
24
|
+
* object with an `error` property.
|
|
25
|
+
*/
|
|
26
|
+
error?: Exclude<ReactNode, number>;
|
|
27
|
+
};
|
|
28
|
+
export type WaitProps<T> = WaitPropsBase<T> & WaitFallbacks;
|
|
29
|
+
/**
|
|
30
|
+
* A function component that wraps `@reykjavik/webtools/remix/Wait` to provide
|
|
31
|
+
* custom properties for `meanwhile` and `error` fallbacks, and/or other
|
|
32
|
+
* behaviors.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#type-waitcomponent
|
|
35
|
+
*/
|
|
36
|
+
export type WaitComponent<CustomProps extends Record<string, unknown> = Record<never, never>> = (<T>(props: WaitPropsBase<T> & CustomProps) => ReactElement) & {
|
|
37
|
+
displayName?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
|
|
41
|
+
* component, to provide a more ergonomic API.
|
|
42
|
+
*
|
|
43
|
+
* If the awaited promise (`props.for`) resolves to an object with a truthy
|
|
44
|
+
* `$error` property, the `$error` will be thrown.
|
|
45
|
+
*
|
|
46
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#wait-component
|
|
47
|
+
*/
|
|
48
|
+
export declare const Wait: WaitComponent<WaitFallbacks>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { Suspense } from 'react';
|
|
2
|
+
import { Await } from '@remix-run/react';
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
|
|
5
|
+
* component, to provide a more ergonomic API.
|
|
6
|
+
*
|
|
7
|
+
* If the awaited promise (`props.for`) resolves to an object with a truthy
|
|
8
|
+
* `$error` property, the `$error` will be thrown.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#wait-component
|
|
11
|
+
*/
|
|
12
|
+
export const Wait = (props) => (React.createElement(Suspense, { fallback: props.meanwhile || 'Loading...' },
|
|
13
|
+
React.createElement(Await, { resolve: props.for, errorElement: props.error || 'An error occurred.' }, (value) => {
|
|
14
|
+
if (value && // eslint-disable-line @typescript-eslint/no-unnecessary-condition
|
|
15
|
+
typeof value === 'object' &&
|
|
16
|
+
'$error' in value &&
|
|
17
|
+
value.$error) {
|
|
18
|
+
throw value.$error;
|
|
19
|
+
}
|
|
20
|
+
return props.children(value);
|
|
21
|
+
})));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the request is a client fetch, or an initial/full-page load.
|
|
3
|
+
* Useful for deciding whether to defer data fetching or not.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#isclientfetch
|
|
6
|
+
*/
|
|
7
|
+
export declare const isClientFetch: (request: Request) => boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the request is a client fetch, or an initial/full-page load.
|
|
3
|
+
* Useful for deciding whether to defer data fetching or not.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#isclientfetch
|
|
6
|
+
*/
|
|
7
|
+
export const isClientFetch = (request) =>
|
|
8
|
+
// For info about this detection method:
|
|
9
|
+
// - https://github.com/remix-run/remix/discussions/5583
|
|
10
|
+
// - https://github.com/sergiodxa/remix-utils/discussions/311#discussioncomment-8572497
|
|
11
|
+
(request.headers.get('Sec-Fetch-Dest') || '') === 'empty';
|
|
@@ -30,29 +30,24 @@ const PatchedCollator = function Collator(locales, options) {
|
|
|
30
30
|
return new PatchedCollator(locales, options);
|
|
31
31
|
}
|
|
32
32
|
const mappedLocales = mapLocales(locales);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
constructor: PatchedCollator,
|
|
41
|
-
compare(a, b) {
|
|
42
|
-
const res1 = this.super.compare(a, b);
|
|
33
|
+
const parent = _Collator(mappedLocales || locales, options);
|
|
34
|
+
const mapped = !!mappedLocales;
|
|
35
|
+
this.compare = (a, b) => {
|
|
36
|
+
const res1 = parent.compare(a, b);
|
|
37
|
+
if (!mapped) {
|
|
38
|
+
return res1;
|
|
39
|
+
}
|
|
43
40
|
const a0 = a.charAt(0);
|
|
44
41
|
const b0 = b.charAt(0);
|
|
45
42
|
if (/\d/.test(a0 + b0)) {
|
|
46
43
|
return res1;
|
|
47
44
|
}
|
|
48
|
-
const res2 =
|
|
45
|
+
const res2 = parent.compare(a0, b0);
|
|
49
46
|
return res2 !== 0 ? res2 : res1;
|
|
50
|
-
}
|
|
51
|
-
resolvedOptions()
|
|
52
|
-
return this.super.resolvedOptions();
|
|
53
|
-
},
|
|
47
|
+
};
|
|
48
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
54
49
|
};
|
|
55
|
-
PatchedCollator.prototype =
|
|
50
|
+
PatchedCollator.prototype = { constructor: PatchedCollator };
|
|
56
51
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
57
52
|
PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
|
|
58
53
|
PatchedCollator.$original = _Collator;
|
|
@@ -68,11 +63,8 @@ exports._patchedLocaleCompare.$original = _localeCompare;
|
|
|
68
63
|
// NumberFormat
|
|
69
64
|
// ===========================================================================
|
|
70
65
|
const _NumberFormat = Intl.NumberFormat;
|
|
71
|
-
const reformatNumberParts = function (parts) {
|
|
72
|
-
|
|
73
|
-
return parts;
|
|
74
|
-
}
|
|
75
|
-
const options = this.super.resolvedOptions();
|
|
66
|
+
const reformatNumberParts = function (parent, parts) {
|
|
67
|
+
const options = parent.resolvedOptions();
|
|
76
68
|
if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
|
|
77
69
|
const currency = options.currency;
|
|
78
70
|
if (currency === 'DKK' || currency === 'ISK') {
|
|
@@ -91,31 +83,21 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
|
|
|
91
83
|
return new PatchedNumberFormat(locales, options);
|
|
92
84
|
}
|
|
93
85
|
const mappedLocales = mapLocales(locales);
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
const parent = _NumberFormat(mappedLocales || locales, options);
|
|
87
|
+
const mapped = !!mappedLocales;
|
|
88
|
+
this.format = (value) => combineParts(this.formatToParts(value));
|
|
89
|
+
this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
|
|
90
|
+
this.formatToParts = (value) => {
|
|
91
|
+
const parts = parent.formatToParts(value);
|
|
92
|
+
return mapped ? reformatNumberParts(parent, parts) : parts;
|
|
93
|
+
};
|
|
94
|
+
this.formatRangeToParts = (value1, value2) => {
|
|
95
|
+
const parts = parent.formatRangeToParts(value1, value2);
|
|
96
|
+
return mapped ? reformatNumberParts(parent, parts) : parts;
|
|
97
|
+
};
|
|
98
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
96
99
|
};
|
|
97
|
-
|
|
98
|
-
// ability to instantiate without `new` is a bit of a pain.
|
|
99
|
-
// Eagerly interested in finding a better way to do this.
|
|
100
|
-
const numberFormatProto = {
|
|
101
|
-
constructor: PatchedNumberFormat,
|
|
102
|
-
format(value) {
|
|
103
|
-
return combineParts(this.formatToParts(value));
|
|
104
|
-
},
|
|
105
|
-
formatRange(value1, value2) {
|
|
106
|
-
return combineParts(this.formatRangeToParts(value1, value2));
|
|
107
|
-
},
|
|
108
|
-
formatToParts(value) {
|
|
109
|
-
return reformatNumberParts.call(this, this.super.formatToParts(value));
|
|
110
|
-
},
|
|
111
|
-
formatRangeToParts(value1, value2) {
|
|
112
|
-
return reformatNumberParts.call(this, this.super.formatRangeToParts(value1, value2));
|
|
113
|
-
},
|
|
114
|
-
resolvedOptions() {
|
|
115
|
-
return this.super.resolvedOptions();
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
PatchedNumberFormat.prototype = numberFormatProto;
|
|
100
|
+
PatchedNumberFormat.prototype = { constructor: PatchedNumberFormat };
|
|
119
101
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
120
102
|
PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
|
|
121
103
|
PatchedNumberFormat.$original = _NumberFormat;
|
|
@@ -202,11 +184,8 @@ const partMappers = {
|
|
|
202
184
|
}
|
|
203
185
|
},
|
|
204
186
|
};
|
|
205
|
-
const reformatDateTimeParts = function (parts) {
|
|
206
|
-
|
|
207
|
-
return parts;
|
|
208
|
-
}
|
|
209
|
-
const options = this.super.resolvedOptions();
|
|
187
|
+
const reformatDateTimeParts = function (parent, parts) {
|
|
188
|
+
const options = parent.resolvedOptions();
|
|
210
189
|
parts.forEach((part, idx) => {
|
|
211
190
|
var _a;
|
|
212
191
|
const mapper = partMappers[part.type];
|
|
@@ -231,31 +210,21 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
|
|
|
231
210
|
hourCycle: 'h11',
|
|
232
211
|
};
|
|
233
212
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
},
|
|
248
|
-
formatToParts(value) {
|
|
249
|
-
return reformatDateTimeParts.call(this, this.super.formatToParts(value));
|
|
250
|
-
},
|
|
251
|
-
formatRangeToParts(value1, value2) {
|
|
252
|
-
return reformatDateTimeParts.call(this, this.super.formatRangeToParts(value1, value2));
|
|
253
|
-
},
|
|
254
|
-
resolvedOptions() {
|
|
255
|
-
return this.super.resolvedOptions();
|
|
256
|
-
},
|
|
213
|
+
const parent = _DateTimeFormat(mappedLocales || locales, options);
|
|
214
|
+
const mapped = !!mappedLocales;
|
|
215
|
+
this.format = (value) => combineParts(this.formatToParts(value));
|
|
216
|
+
this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
|
|
217
|
+
this.formatToParts = (value) => {
|
|
218
|
+
const parts = parent.formatToParts(value);
|
|
219
|
+
return mapped ? reformatDateTimeParts(parent, parts) : parts;
|
|
220
|
+
};
|
|
221
|
+
this.formatRangeToParts = (value1, value2) => {
|
|
222
|
+
const parts = parent.formatRangeToParts(value1, value2);
|
|
223
|
+
return mapped ? reformatDateTimeParts(parent, parts) : parts;
|
|
224
|
+
};
|
|
225
|
+
this.resolvedOptions = () => parent.resolvedOptions();
|
|
257
226
|
};
|
|
258
|
-
PatchedDateTimeFormat.prototype =
|
|
227
|
+
PatchedDateTimeFormat.prototype = { constructor: PatchedDateTimeFormat };
|
|
259
228
|
// Static methods (not patched since "is" is not ACTUALLY supported.)
|
|
260
229
|
PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
|
|
261
230
|
PatchedDateTimeFormat.$original = _DateTimeFormat;
|
|
@@ -283,6 +252,8 @@ if (_PluralRules) {
|
|
|
283
252
|
super(mappedLocales || locales, options);
|
|
284
253
|
this.mapped = !!mappedLocales;
|
|
285
254
|
this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
|
|
255
|
+
this.select = this.select.bind(this);
|
|
256
|
+
this.selectRange = this.selectRange.bind(this);
|
|
286
257
|
}
|
|
287
258
|
select(n) {
|
|
288
259
|
if (this.mapped) {
|
|
@@ -316,6 +287,8 @@ if (_ListFormat) {
|
|
|
316
287
|
const mappedLocales = mapLocales(locales);
|
|
317
288
|
super(mappedLocales || locales, options);
|
|
318
289
|
this.mapped = !!mappedLocales;
|
|
290
|
+
this.format = this.format.bind(this);
|
|
291
|
+
this.formatToParts = this.formatToParts.bind(this);
|
|
319
292
|
}
|
|
320
293
|
format(list) {
|
|
321
294
|
return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/// <reference path="./vanillaExtract.d.ts" />
|
|
2
2
|
/// <reference path="./http.d.ts" />
|
|
3
|
+
/// <reference path="./async.d.ts" />
|
|
3
4
|
/// <reference path="./fixIcelandicLocale.d.ts" />
|
|
5
|
+
/// <reference path="./remix/http.d.ts" />
|
|
4
6
|
/// <reference path="./CookieHubConsent.d.tsx" />
|
|
7
|
+
/// <reference path="./remix/Wait.d.tsx" />
|
|
5
8
|
/// <reference path="./next/SiteImprove.d.tsx" />
|
|
6
9
|
/// <reference path="./next/http.d.tsx" />
|
|
7
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reykjavik/webtools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "ssh://git@github.com:reykjavikcity/webtools.git",
|
|
@@ -14,12 +14,16 @@
|
|
|
14
14
|
"@reykjavik/hanna-utils": "^0.2.3"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
+
"@remix-run/react": "^2.6.0",
|
|
17
18
|
"@vanilla-extract/css": "^1.14.1",
|
|
18
19
|
"next": ">=11",
|
|
19
20
|
"react": ">=16.8.0",
|
|
20
21
|
"react-dom": ">=16.8.0"
|
|
21
22
|
},
|
|
22
23
|
"peerDependenciesMeta": {
|
|
24
|
+
"@remix-run/react": {
|
|
25
|
+
"optional": true
|
|
26
|
+
},
|
|
23
27
|
"@vanilla-extract/css": {
|
|
24
28
|
"optional": true
|
|
25
29
|
},
|
|
@@ -52,14 +56,26 @@
|
|
|
52
56
|
"import": "./esm/http.js",
|
|
53
57
|
"require": "./http.js"
|
|
54
58
|
},
|
|
59
|
+
"./async": {
|
|
60
|
+
"import": "./esm/async.js",
|
|
61
|
+
"require": "./async.js"
|
|
62
|
+
},
|
|
55
63
|
"./fixIcelandicLocale": {
|
|
56
64
|
"import": "./esm/fixIcelandicLocale.js",
|
|
57
65
|
"require": "./fixIcelandicLocale.js"
|
|
58
66
|
},
|
|
67
|
+
"./remix/http": {
|
|
68
|
+
"import": "./esm/remix/http.js",
|
|
69
|
+
"require": "./remix/http.js"
|
|
70
|
+
},
|
|
59
71
|
"./CookieHubConsent": {
|
|
60
72
|
"import": "./esm/CookieHubConsent.js",
|
|
61
73
|
"require": "./CookieHubConsent.js"
|
|
62
74
|
},
|
|
75
|
+
"./remix/Wait": {
|
|
76
|
+
"import": "./esm/remix/Wait.js",
|
|
77
|
+
"require": "./remix/Wait.js"
|
|
78
|
+
},
|
|
63
79
|
"./next/SiteImprove": {
|
|
64
80
|
"import": "./esm/next/SiteImprove.js",
|
|
65
81
|
"require": "./next/SiteImprove.js"
|
package/remix/Wait.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
type WaitPropsBase<T> = {
|
|
3
|
+
/**
|
|
4
|
+
* The value you want to wait for before rendering
|
|
5
|
+
*/
|
|
6
|
+
for: Promise<T> | T;
|
|
7
|
+
/**
|
|
8
|
+
* A function to render the children when the value is resolved.
|
|
9
|
+
*
|
|
10
|
+
* (If the promise resolved to an object with a truthy `$error` property,
|
|
11
|
+
* then the `$error` is thrown and this function skipped.)
|
|
12
|
+
*/
|
|
13
|
+
children: (data: Exclude<T, {
|
|
14
|
+
$error: string | number | true | object;
|
|
15
|
+
}>) => ReactNode;
|
|
16
|
+
};
|
|
17
|
+
type WaitFallbacks = {
|
|
18
|
+
/**
|
|
19
|
+
* Custom loading/spinner component.
|
|
20
|
+
*/
|
|
21
|
+
meanwhile?: Exclude<ReactNode, number>;
|
|
22
|
+
/**
|
|
23
|
+
* Custom error component if the promise is rejected or if it resolves to an
|
|
24
|
+
* object with an `error` property.
|
|
25
|
+
*/
|
|
26
|
+
error?: Exclude<ReactNode, number>;
|
|
27
|
+
};
|
|
28
|
+
export type WaitProps<T> = WaitPropsBase<T> & WaitFallbacks;
|
|
29
|
+
/**
|
|
30
|
+
* A function component that wraps `@reykjavik/webtools/remix/Wait` to provide
|
|
31
|
+
* custom properties for `meanwhile` and `error` fallbacks, and/or other
|
|
32
|
+
* behaviors.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#type-waitcomponent
|
|
35
|
+
*/
|
|
36
|
+
export type WaitComponent<CustomProps extends Record<string, unknown> = Record<never, never>> = (<T>(props: WaitPropsBase<T> & CustomProps) => ReactElement) & {
|
|
37
|
+
displayName?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
|
|
41
|
+
* component, to provide a more ergonomic API.
|
|
42
|
+
*
|
|
43
|
+
* If the awaited promise (`props.for`) resolves to an object with a truthy
|
|
44
|
+
* `$error` property, the `$error` will be thrown.
|
|
45
|
+
*
|
|
46
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#wait-component
|
|
47
|
+
*/
|
|
48
|
+
export declare const Wait: WaitComponent<WaitFallbacks>;
|
|
49
|
+
export {};
|
package/remix/Wait.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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.Wait = void 0;
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
28
|
+
const react_2 = require("@remix-run/react");
|
|
29
|
+
/**
|
|
30
|
+
* Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
|
|
31
|
+
* component, to provide a more ergonomic API.
|
|
32
|
+
*
|
|
33
|
+
* If the awaited promise (`props.for`) resolves to an object with a truthy
|
|
34
|
+
* `$error` property, the `$error` will be thrown.
|
|
35
|
+
*
|
|
36
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#wait-component
|
|
37
|
+
*/
|
|
38
|
+
const Wait = (props) => (react_1.default.createElement(react_1.Suspense, { fallback: props.meanwhile || 'Loading...' },
|
|
39
|
+
react_1.default.createElement(react_2.Await, { resolve: props.for, errorElement: props.error || 'An error occurred.' }, (value) => {
|
|
40
|
+
if (value && // eslint-disable-line @typescript-eslint/no-unnecessary-condition
|
|
41
|
+
typeof value === 'object' &&
|
|
42
|
+
'$error' in value &&
|
|
43
|
+
value.$error) {
|
|
44
|
+
throw value.$error;
|
|
45
|
+
}
|
|
46
|
+
return props.children(value);
|
|
47
|
+
})));
|
|
48
|
+
exports.Wait = Wait;
|
package/remix/http.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the request is a client fetch, or an initial/full-page load.
|
|
3
|
+
* Useful for deciding whether to defer data fetching or not.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#isclientfetch
|
|
6
|
+
*/
|
|
7
|
+
export declare const isClientFetch: (request: Request) => boolean;
|
package/remix/http.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isClientFetch = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Detects if the request is a client fetch, or an initial/full-page load.
|
|
6
|
+
* Useful for deciding whether to defer data fetching or not.
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-remix.md#isclientfetch
|
|
9
|
+
*/
|
|
10
|
+
const isClientFetch = (request) =>
|
|
11
|
+
// For info about this detection method:
|
|
12
|
+
// - https://github.com/remix-run/remix/discussions/5583
|
|
13
|
+
// - https://github.com/sergiodxa/remix-utils/discussions/311#discussioncomment-8572497
|
|
14
|
+
(request.headers.get('Sec-Fetch-Dest') || '') === 'empty';
|
|
15
|
+
exports.isClientFetch = isClientFetch;
|