@uniflowed/web 0.34.0 → 0.36.0
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/cookie.js +3 -3
- package/head.js +6 -6
- package/internal/image-endpoint.js +2 -2
- package/internal/remote-image.js +4 -4
- package/media.js +30 -30
- package/package.json +4 -4
- package/regions.js +15 -15
- package/time.js +4 -4
- package/vitals.js +18 -18
package/cookie.js
CHANGED
|
@@ -31,10 +31,10 @@ import * as React from "@uniflowed/react";
|
|
|
31
31
|
/** How a cookie is written, in the browser. */
|
|
32
32
|
export type CookieOptions = {
|
|
33
33
|
/** Seconds until it expires. Absent means a session cookie. */
|
|
34
|
-
readonly maxAge
|
|
35
|
-
readonly path
|
|
34
|
+
readonly maxAge? : number,
|
|
35
|
+
readonly path? : string,
|
|
36
36
|
readonly sameSite?: "lax" | "strict" | "none",
|
|
37
|
-
readonly secure
|
|
37
|
+
readonly secure? : boolean,
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
/** Where a server-rendered page's cookies are handed to the client half. */
|
package/head.js
CHANGED
|
@@ -36,23 +36,23 @@ import * as React from "@uniflowed/react";
|
|
|
36
36
|
|
|
37
37
|
/** One `<meta>`, by whichever attribute names it. */
|
|
38
38
|
export type Meta = {
|
|
39
|
-
readonly name
|
|
39
|
+
readonly name? : string,
|
|
40
40
|
readonly property?: string,
|
|
41
|
-
readonly content: string,
|
|
41
|
+
readonly content : string,
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
/** One `<link>`. */
|
|
45
45
|
export type Link = {
|
|
46
|
-
readonly rel: string,
|
|
47
|
-
readonly href: string,
|
|
48
|
-
readonly type
|
|
46
|
+
readonly rel : string,
|
|
47
|
+
readonly href : string,
|
|
48
|
+
readonly type? : string,
|
|
49
49
|
readonly sizes?: string,
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
/** What a component wants in the head. */
|
|
53
53
|
export type Head = {
|
|
54
54
|
readonly title?: string,
|
|
55
|
-
readonly meta
|
|
55
|
+
readonly meta? : $ReadOnlyArray<Meta>,
|
|
56
56
|
readonly links?: $ReadOnlyArray<Link>,
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -35,7 +35,7 @@ export type ImageEndpoint = {|
|
|
|
35
35
|
* `@uniflowed/server/image`'s.
|
|
36
36
|
*/
|
|
37
37
|
export const IMAGE_ENDPOINT: ImageEndpoint = {
|
|
38
|
-
path: null,
|
|
39
|
-
widths: [640, 750, 828, 1080, 1200, 1440, 1920],
|
|
38
|
+
path : null,
|
|
39
|
+
widths : [640, 750, 828, 1080, 1200, 1440, 1920],
|
|
40
40
|
quality: 75,
|
|
41
41
|
};
|
package/internal/remote-image.js
CHANGED
|
@@ -17,10 +17,10 @@ import type { ImageEndpoint } from "./image-endpoint.js";
|
|
|
17
17
|
* `null` when the project has no endpoint, or the URL is not one it fetches.
|
|
18
18
|
*/
|
|
19
19
|
export function remoteSources(
|
|
20
|
-
endpoint: ImageEndpoint,
|
|
21
|
-
src: string,
|
|
20
|
+
endpoint : ImageEndpoint,
|
|
21
|
+
src : string,
|
|
22
22
|
intrinsic: number,
|
|
23
|
-
quality: number,
|
|
23
|
+
quality : number,
|
|
24
24
|
): {| readonly src: string, readonly srcSet: string |} | null {
|
|
25
25
|
const path = endpoint.path;
|
|
26
26
|
if (path == null) return null;
|
|
@@ -36,7 +36,7 @@ export function remoteSources(
|
|
|
36
36
|
const rungs = widths.filter((width) => width < cover).map((width) => [width, width]);
|
|
37
37
|
rungs.push([cover, Math.min(cover, intrinsic)]);
|
|
38
38
|
return {
|
|
39
|
-
src: at(cover),
|
|
39
|
+
src : at(cover),
|
|
40
40
|
srcSet: rungs.map(([asked, described]) => `${at(asked)} ${described}w`).join(", "),
|
|
41
41
|
};
|
|
42
42
|
}
|
package/media.js
CHANGED
|
@@ -70,7 +70,7 @@ export type Loading = "eager" | "lazy";
|
|
|
70
70
|
/** One alternative rendering of the same image. */
|
|
71
71
|
export type Source = {
|
|
72
72
|
readonly srcSet: string,
|
|
73
|
-
readonly type
|
|
73
|
+
readonly type? : string,
|
|
74
74
|
readonly media?: string,
|
|
75
75
|
};
|
|
76
76
|
|
|
@@ -236,20 +236,20 @@ function defaultSizes(width: number): string {
|
|
|
236
236
|
* was written.
|
|
237
237
|
*/
|
|
238
238
|
export component Image(
|
|
239
|
-
src: string | ImageAsset,
|
|
240
|
-
alt: string,
|
|
241
|
-
width
|
|
242
|
-
height
|
|
243
|
-
loading
|
|
244
|
-
priority
|
|
245
|
-
sizes
|
|
246
|
-
srcSet
|
|
239
|
+
src : string | ImageAsset,
|
|
240
|
+
alt : string,
|
|
241
|
+
width? : number,
|
|
242
|
+
height? : number,
|
|
243
|
+
loading? : Loading,
|
|
244
|
+
priority? : boolean = false,
|
|
245
|
+
sizes? : string,
|
|
246
|
+
srcSet? : string,
|
|
247
247
|
placeholder?: boolean = true,
|
|
248
|
-
quality
|
|
248
|
+
quality? : number,
|
|
249
249
|
unoptimized?: boolean = false,
|
|
250
|
-
className
|
|
251
|
-
style
|
|
252
|
-
...rest: ReactTypes.ElementConfig<"img">
|
|
250
|
+
className? : string,
|
|
251
|
+
style? : { readonly [string]: string | number },
|
|
252
|
+
...rest : ReactTypes.ElementConfig<"img">
|
|
253
253
|
) {
|
|
254
254
|
// Refined rather than cast: `src` is a union and `typeof` narrows it, so
|
|
255
255
|
// neither branch needs `any`. A cast here would be the one place in this
|
|
@@ -316,8 +316,8 @@ export component Image(
|
|
|
316
316
|
// reader, and the browser paints over it as the real image
|
|
317
317
|
// decodes instead of swapping one node for another.
|
|
318
318
|
...style,
|
|
319
|
-
backgroundImage: style?.backgroundImage ?? `url("${blur}")`,
|
|
320
|
-
backgroundSize: style?.backgroundSize ?? "cover",
|
|
319
|
+
backgroundImage : style?.backgroundImage ?? `url("${blur}")`,
|
|
320
|
+
backgroundSize : style?.backgroundSize ?? "cover",
|
|
321
321
|
backgroundPosition: style?.backgroundPosition ?? "center",
|
|
322
322
|
}
|
|
323
323
|
}
|
|
@@ -386,12 +386,12 @@ export component Image(
|
|
|
386
386
|
* mistake that only shows up in the one browser that took none of the sources.
|
|
387
387
|
*/
|
|
388
388
|
export component Picture(
|
|
389
|
-
src: string,
|
|
390
|
-
alt: string,
|
|
391
|
-
width: number,
|
|
392
|
-
height: number,
|
|
393
|
-
sources
|
|
394
|
-
loading
|
|
389
|
+
src : string,
|
|
390
|
+
alt : string,
|
|
391
|
+
width : number,
|
|
392
|
+
height : number,
|
|
393
|
+
sources? : $ReadOnlyArray<Source> = [],
|
|
394
|
+
loading? : Loading = "lazy",
|
|
395
395
|
className?: string,
|
|
396
396
|
) {
|
|
397
397
|
return (
|
|
@@ -473,10 +473,10 @@ export component Picture(
|
|
|
473
473
|
* request.
|
|
474
474
|
*/
|
|
475
475
|
export component Font(
|
|
476
|
-
src: string | FontAsset,
|
|
477
|
-
type
|
|
476
|
+
src : string | FontAsset,
|
|
477
|
+
type? : string,
|
|
478
478
|
crossOrigin?: "anonymous" | "use-credentials" = "anonymous",
|
|
479
|
-
preload
|
|
479
|
+
preload? : boolean,
|
|
480
480
|
) {
|
|
481
481
|
const asset: FontAsset | null = typeof src === "string" ? null : src;
|
|
482
482
|
const url: string = typeof src === "string" ? src : src.src;
|
|
@@ -599,13 +599,13 @@ export component IconSprite(sprite: SpriteAsset) {
|
|
|
599
599
|
* when that is what you mean.
|
|
600
600
|
*/
|
|
601
601
|
export component Icon(
|
|
602
|
-
icon: IconAsset,
|
|
603
|
-
label
|
|
604
|
-
size
|
|
605
|
-
width
|
|
606
|
-
height
|
|
602
|
+
icon : IconAsset,
|
|
603
|
+
label? : string,
|
|
604
|
+
size? : number = 24,
|
|
605
|
+
width? : number,
|
|
606
|
+
height? : number,
|
|
607
607
|
className?: string,
|
|
608
|
-
...rest: ReactTypes.ElementConfig<"svg">
|
|
608
|
+
...rest : ReactTypes.ElementConfig<"svg">
|
|
609
609
|
) {
|
|
610
610
|
return (
|
|
611
611
|
<svg
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniflowed/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "The primitives a web page is built from, for the Unified Toolchain for Flow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"!*.test.js"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@uniflowed/core": "0.
|
|
29
|
-
"@uniflowed/hooks": "0.
|
|
30
|
-
"@uniflowed/react": "0.
|
|
28
|
+
"@uniflowed/core": "0.36.0",
|
|
29
|
+
"@uniflowed/hooks": "0.36.0",
|
|
30
|
+
"@uniflowed/react": "0.36.0"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/regions.js
CHANGED
|
@@ -45,17 +45,17 @@ const MAIN_ID: string = "uf-main";
|
|
|
45
45
|
*/
|
|
46
46
|
const VISUALLY_HIDDEN = {
|
|
47
47
|
position: "absolute",
|
|
48
|
-
width: "1px",
|
|
49
|
-
height: "1px",
|
|
50
|
-
margin: "-1px",
|
|
51
|
-
padding: 0,
|
|
48
|
+
width : "1px",
|
|
49
|
+
height : "1px",
|
|
50
|
+
margin : "-1px",
|
|
51
|
+
padding : 0,
|
|
52
52
|
overflow: "hidden",
|
|
53
53
|
// Both spellings: `clip` is deprecated and still the one older screen
|
|
54
54
|
// readers honour.
|
|
55
|
-
clip: "rect(0 0 0 0)",
|
|
56
|
-
clipPath: "inset(50%)",
|
|
55
|
+
clip : "rect(0 0 0 0)",
|
|
56
|
+
clipPath : "inset(50%)",
|
|
57
57
|
whiteSpace: "nowrap",
|
|
58
|
-
border: 0,
|
|
58
|
+
border : 0,
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -73,8 +73,8 @@ const VISUALLY_HIDDEN = {
|
|
|
73
73
|
*/
|
|
74
74
|
export component Announcer(
|
|
75
75
|
children?: React.Node,
|
|
76
|
-
urgency
|
|
77
|
-
atomic
|
|
76
|
+
urgency? : "polite" | "assertive" = "polite",
|
|
77
|
+
atomic? : boolean = true,
|
|
78
78
|
) {
|
|
79
79
|
return (
|
|
80
80
|
<div aria-live={urgency} aria-atomic={atomic} style={VISUALLY_HIDDEN}>
|
|
@@ -122,17 +122,17 @@ export component SkipLink(children?: React.Node = "Skip to content") {
|
|
|
122
122
|
focused
|
|
123
123
|
? {
|
|
124
124
|
position: "absolute",
|
|
125
|
-
top: "0.5rem",
|
|
126
|
-
left: "0.5rem",
|
|
125
|
+
top : "0.5rem",
|
|
126
|
+
left : "0.5rem",
|
|
127
127
|
// Above anything a page is likely to have stacked, since the
|
|
128
128
|
// link has to be readable wherever it lands.
|
|
129
|
-
zIndex: 1000,
|
|
130
|
-
padding: "0.5rem 0.75rem",
|
|
129
|
+
zIndex : 1000,
|
|
130
|
+
padding : "0.5rem 0.75rem",
|
|
131
131
|
background: "Canvas",
|
|
132
|
-
color: "CanvasText",
|
|
132
|
+
color : "CanvasText",
|
|
133
133
|
// `CanvasText` follows the reader's own light or dark setting,
|
|
134
134
|
// so the link stays legible without knowing the page's theme.
|
|
135
|
-
border: "1px solid CanvasText",
|
|
135
|
+
border : "1px solid CanvasText",
|
|
136
136
|
borderRadius: "0.25rem",
|
|
137
137
|
}
|
|
138
138
|
: VISUALLY_HIDDEN
|
package/time.js
CHANGED
|
@@ -154,10 +154,10 @@ export function relative(at: TimeValue, from?: TimeValue): string {
|
|
|
154
154
|
* would defeat the entire component.
|
|
155
155
|
*/
|
|
156
156
|
export component Time(
|
|
157
|
-
value: TimeValue,
|
|
158
|
-
format
|
|
159
|
-
zone
|
|
160
|
-
locale
|
|
157
|
+
value : TimeValue,
|
|
158
|
+
format? : TimeFormat = "iso",
|
|
159
|
+
zone? : string,
|
|
160
|
+
locale? : string,
|
|
161
161
|
className?: string,
|
|
162
162
|
) {
|
|
163
163
|
const at = asInstant(value);
|
package/vitals.js
CHANGED
|
@@ -95,8 +95,8 @@ export type NavigationType = "navigate" | "reload" | "back-forward" | "prerender
|
|
|
95
95
|
export type Vital = {
|
|
96
96
|
readonly name: VitalName,
|
|
97
97
|
/** Milliseconds, except `CLS`, which is a unitless layout-shift score. */
|
|
98
|
-
readonly value: number,
|
|
99
|
-
readonly rating: Rating,
|
|
98
|
+
readonly value : number,
|
|
99
|
+
readonly rating : Rating,
|
|
100
100
|
readonly navigationType: NavigationType,
|
|
101
101
|
};
|
|
102
102
|
|
|
@@ -111,7 +111,7 @@ export type VitalsReporter = (vital: Vital) => void;
|
|
|
111
111
|
* identifies the page rather than the report.
|
|
112
112
|
*/
|
|
113
113
|
export type VitalsReport = {
|
|
114
|
-
readonly url: string,
|
|
114
|
+
readonly url : string,
|
|
115
115
|
readonly vitals: $ReadOnlyArray<Vital>,
|
|
116
116
|
};
|
|
117
117
|
|
|
@@ -167,10 +167,10 @@ function defaultVitalsTarget(): string {
|
|
|
167
167
|
*/
|
|
168
168
|
const THRESHOLDS: { readonly [VitalName]: { readonly good: number, readonly poor: number } } = {
|
|
169
169
|
TTFB: { good: 800, poor: 1800 },
|
|
170
|
-
FCP: { good: 1800, poor: 3000 },
|
|
171
|
-
LCP: { good: 2500, poor: 4000 },
|
|
172
|
-
CLS: { good: 0.1, poor: 0.25 },
|
|
173
|
-
INP: { good: 200, poor: 500 },
|
|
170
|
+
FCP : { good: 1800, poor: 3000 },
|
|
171
|
+
LCP : { good: 2500, poor: 4000 },
|
|
172
|
+
CLS : { good: 0.1, poor: 0.25 },
|
|
173
|
+
INP : { good: 200, poor: 500 },
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
/**
|
|
@@ -210,9 +210,9 @@ const INTERACTIONS_PER_STEP: number = 50;
|
|
|
210
210
|
/** The part of a performance entry this module reads. */
|
|
211
211
|
type TimingEntry = {
|
|
212
212
|
readonly entryType: string,
|
|
213
|
-
readonly name: string,
|
|
213
|
+
readonly name : string,
|
|
214
214
|
readonly startTime: number,
|
|
215
|
-
readonly duration: number,
|
|
215
|
+
readonly duration : number,
|
|
216
216
|
/** `layout-shift`: how much moved. */
|
|
217
217
|
readonly value?: number,
|
|
218
218
|
/** `layout-shift`: whether a reader had just done something. */
|
|
@@ -237,7 +237,7 @@ type EntryList = { readonly getEntries: () => $ReadOnlyArray<TimingEntry>, ... }
|
|
|
237
237
|
|
|
238
238
|
/** The part of a live observer this module uses. */
|
|
239
239
|
type ObserverHandle = {
|
|
240
|
-
readonly observe: (options: { readonly [string]: mixed }) => mixed,
|
|
240
|
+
readonly observe : (options: { readonly [string]: mixed }) => mixed,
|
|
241
241
|
readonly disconnect: () => mixed,
|
|
242
242
|
...
|
|
243
243
|
};
|
|
@@ -249,7 +249,7 @@ type ObserverHandle = {
|
|
|
249
249
|
* page is over" are not dispatched on the same object.
|
|
250
250
|
*/
|
|
251
251
|
type Listens = {
|
|
252
|
-
readonly addEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
252
|
+
readonly addEventListener : (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
253
253
|
readonly removeEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
254
254
|
...
|
|
255
255
|
};
|
|
@@ -269,8 +269,8 @@ type Listens = {
|
|
|
269
269
|
*/
|
|
270
270
|
type BrowserWindow = {
|
|
271
271
|
readonly document: {
|
|
272
|
-
readonly visibilityState: string,
|
|
273
|
-
readonly addEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
272
|
+
readonly visibilityState : string,
|
|
273
|
+
readonly addEventListener : (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
274
274
|
readonly removeEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
275
275
|
...
|
|
276
276
|
},
|
|
@@ -290,7 +290,7 @@ type BrowserWindow = {
|
|
|
290
290
|
readonly supportedEntryTypes?: $ReadOnlyArray<string>,
|
|
291
291
|
...
|
|
292
292
|
},
|
|
293
|
-
readonly addEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
293
|
+
readonly addEventListener : (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
294
294
|
readonly removeEventListener: (type: string, listener: () => mixed, options?: mixed) => mixed,
|
|
295
295
|
...
|
|
296
296
|
};
|
|
@@ -335,10 +335,10 @@ function supports(win: BrowserWindow, type: string): boolean {
|
|
|
335
335
|
* observers hand back checked.
|
|
336
336
|
*/
|
|
337
337
|
function observeEntries(
|
|
338
|
-
win: BrowserWindow,
|
|
339
|
-
type: string,
|
|
338
|
+
win : BrowserWindow,
|
|
339
|
+
type : string,
|
|
340
340
|
onEntries: (entries: $ReadOnlyArray<TimingEntry>) => void,
|
|
341
|
-
extra
|
|
341
|
+
extra? : { readonly [string]: mixed },
|
|
342
342
|
): ObserverHandle | null {
|
|
343
343
|
const Observer = win.PerformanceObserver;
|
|
344
344
|
if (Observer == null || !supports(win, type)) {
|
|
@@ -730,7 +730,7 @@ export function vitalsBeacon(endpoint?: string): VitalsReporter {
|
|
|
730
730
|
method: "POST",
|
|
731
731
|
body,
|
|
732
732
|
keepalive: true,
|
|
733
|
-
headers: { "content-type": "application/json" },
|
|
733
|
+
headers : { "content-type": "application/json" },
|
|
734
734
|
}).catch(noop);
|
|
735
735
|
}
|
|
736
736
|
};
|