@reykjavik/webtools 0.1.20 → 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 +10 -6
- 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/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/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,13 +4,16 @@
|
|
|
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
|
-
-
|
|
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
|
|
12
15
|
|
|
13
|
-
## 0.1.18 – 0.1.
|
|
16
|
+
## 0.1.18 – 0.1.20
|
|
14
17
|
|
|
15
18
|
_2024-03-11_
|
|
16
19
|
|
|
@@ -20,6 +23,7 @@ _2024-03-11_
|
|
|
20
23
|
(not just a single character) This fix corrects the sorting of initial
|
|
21
24
|
letters, but characters inside the string stay mixed in with their
|
|
22
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
|
+
});
|
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';
|
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;
|