@boyernick/standard-ui-react 0.4.2-canary.59 → 0.4.2-canary.61
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/package.json +1 -1
- package/src/carousel.tsx +34 -24
- package/src/glass.tsx +22 -41
- package/src/lib/lens-glass/config.ts +225 -0
- package/src/lib/lens-glass/renderer.ts +586 -0
- package/src/lib/lens-glass/shader.ts +138 -0
- package/src/lib/media-glass.ts +12 -0
- package/src/modal.tsx +122 -114
- package/src/video-player.tsx +7 -18
- package/src/lib/liquidglass/NOTICE.md +0 -24
- package/src/lib/liquidglass/index.d.ts +0 -45
- package/src/lib/liquidglass/index.js +0 -2862
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export const VERTEX_SHADER = `
|
|
2
|
+
attribute vec2 a_position;
|
|
3
|
+
varying vec2 v_uv;
|
|
4
|
+
void main() {
|
|
5
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
6
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
7
|
+
}`;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One pane, drawn into its own output rectangle. Coordinates are CSS pixels
|
|
11
|
+
* with y down: `u_origin` is the output's top-left in scene space, and the
|
|
12
|
+
* pane is centred on `u_center` with half-size `u_half`.
|
|
13
|
+
*/
|
|
14
|
+
export const FRAGMENT_SHADER = `
|
|
15
|
+
precision highp float;
|
|
16
|
+
varying vec2 v_uv;
|
|
17
|
+
|
|
18
|
+
uniform sampler2D u_scene;
|
|
19
|
+
uniform vec2 u_sceneSize;
|
|
20
|
+
uniform vec2 u_origin;
|
|
21
|
+
uniform vec2 u_outSize;
|
|
22
|
+
uniform vec2 u_center;
|
|
23
|
+
uniform vec2 u_half;
|
|
24
|
+
uniform float u_dpr;
|
|
25
|
+
|
|
26
|
+
uniform float u_radius;
|
|
27
|
+
uniform float u_power;
|
|
28
|
+
uniform float u_bezel;
|
|
29
|
+
uniform float u_bend;
|
|
30
|
+
uniform float u_dispersionRed;
|
|
31
|
+
uniform float u_dispersionBlue;
|
|
32
|
+
uniform float u_rim;
|
|
33
|
+
uniform float u_rimWidth;
|
|
34
|
+
uniform float u_specular;
|
|
35
|
+
uniform float u_frost;
|
|
36
|
+
uniform float u_grain;
|
|
37
|
+
uniform float u_dim;
|
|
38
|
+
uniform float u_fadeStart;
|
|
39
|
+
uniform float u_fadeEnd;
|
|
40
|
+
uniform float u_shadow;
|
|
41
|
+
uniform float u_hover;
|
|
42
|
+
uniform float u_press;
|
|
43
|
+
|
|
44
|
+
// A rounded rectangle whose corners are superellipses. Power 2 is a circular
|
|
45
|
+
// arc; around 4 is the continuous "squircle" corner.
|
|
46
|
+
float sdPane(vec2 p) {
|
|
47
|
+
float r = min(u_radius, min(u_half.x, u_half.y));
|
|
48
|
+
vec2 q = abs(p) - u_half + r;
|
|
49
|
+
vec2 m = max(q, 0.0);
|
|
50
|
+
float corner = u_power == 2.0
|
|
51
|
+
? length(m)
|
|
52
|
+
: pow(pow(m.x, u_power) + pow(m.y, u_power), 1.0 / u_power);
|
|
53
|
+
return corner + min(max(q.x, q.y), 0.0) - r;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
vec2 outwardNormal(vec2 p) {
|
|
57
|
+
float e = 0.5;
|
|
58
|
+
vec2 g = vec2(
|
|
59
|
+
sdPane(p + vec2(e, 0.0)) - sdPane(p - vec2(e, 0.0)),
|
|
60
|
+
sdPane(p + vec2(0.0, e)) - sdPane(p - vec2(0.0, e))
|
|
61
|
+
);
|
|
62
|
+
float l = length(g);
|
|
63
|
+
return l > 1e-5 ? g / l : vec2(0.0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
vec3 ground(vec2 scenePx) {
|
|
67
|
+
vec2 uv = clamp(scenePx / u_sceneSize, vec2(0.0), vec2(1.0));
|
|
68
|
+
return texture2D(u_scene, uv).rgb;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Frost: 16 taps on a golden-angle spiral out to about two sigma.
|
|
72
|
+
vec3 frosted(vec2 scenePx) {
|
|
73
|
+
if (u_frost < 0.05) return ground(scenePx);
|
|
74
|
+
vec3 sum = vec3(0.0);
|
|
75
|
+
float weights = 0.0;
|
|
76
|
+
for (int i = 0; i < 16; i++) {
|
|
77
|
+
float fi = float(i);
|
|
78
|
+
float r = sqrt((fi + 0.5) / 16.0) * 2.0 * u_frost;
|
|
79
|
+
float a = fi * 2.39996323;
|
|
80
|
+
float w = exp(-(r * r) / (2.0 * u_frost * u_frost));
|
|
81
|
+
sum += ground(scenePx + vec2(cos(a), sin(a)) * r) * w;
|
|
82
|
+
weights += w;
|
|
83
|
+
}
|
|
84
|
+
return sum / weights;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
float hash(vec2 p) {
|
|
88
|
+
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
void main() {
|
|
92
|
+
vec2 scenePx = u_origin + vec2(v_uv.x, 1.0 - v_uv.y) * u_outSize;
|
|
93
|
+
vec2 p = scenePx - u_center;
|
|
94
|
+
float d = sdPane(p);
|
|
95
|
+
|
|
96
|
+
// Outside: only a soft contact shadow, heavier below the pane.
|
|
97
|
+
if (d > 0.0) {
|
|
98
|
+
float below = smoothstep(-0.6, 0.8, p.y / max(u_half.y, 1.0));
|
|
99
|
+
float shadow = u_shadow * (1.0 - 0.35 * u_press) * exp(-(d * d) / 40.0) * below;
|
|
100
|
+
gl_FragColor = vec4(0.0, 0.0, 0.0, shadow);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
float inside = -d;
|
|
105
|
+
vec2 n = outwardNormal(p);
|
|
106
|
+
|
|
107
|
+
// The bezel bends the ground outward across its width: the pane shows what
|
|
108
|
+
// lies just past its edge, compressed into the band, as a thick lens does.
|
|
109
|
+
float t = u_bezel > 0.0 ? clamp(1.0 - inside / u_bezel, 0.0, 1.0) : 0.0;
|
|
110
|
+
float profile = t * t * (3.0 - 2.0 * t);
|
|
111
|
+
vec2 offset = n * u_bend * (1.0 - 0.35 * u_press) * profile;
|
|
112
|
+
|
|
113
|
+
vec3 color = vec3(
|
|
114
|
+
frosted(scenePx + offset * u_dispersionRed).r,
|
|
115
|
+
frosted(scenePx + offset).g,
|
|
116
|
+
frosted(scenePx + offset * u_dispersionBlue).b
|
|
117
|
+
);
|
|
118
|
+
color *= u_dim + 0.06 * u_hover;
|
|
119
|
+
|
|
120
|
+
// Rim light, strongest where the edge faces the light (top left), and a
|
|
121
|
+
// sheen across the lit side of the bezel.
|
|
122
|
+
vec2 light = normalize(vec2(-0.55, -1.0));
|
|
123
|
+
float facing = max(dot(n, light), 0.0);
|
|
124
|
+
float rim = 1.0 - smoothstep(0.0, u_rimWidth, inside);
|
|
125
|
+
color += u_rim * (1.0 + 0.4 * u_hover) * rim * (0.55 + 0.45 * facing);
|
|
126
|
+
color += u_specular * 0.35 * pow(t, 3.0) * facing;
|
|
127
|
+
|
|
128
|
+
// Grain, fixed to the device pixel grid so a still pane never needs redrawing.
|
|
129
|
+
color += (hash(floor(scenePx * u_dpr)) - 0.5) * u_grain;
|
|
130
|
+
|
|
131
|
+
// Optional fade down the pane.
|
|
132
|
+
float yFrac = (p.y + u_half.y) / (2.0 * u_half.y);
|
|
133
|
+
float fade = u_fadeEnd > u_fadeStart ? 1.0 - smoothstep(u_fadeStart, u_fadeEnd, yFrac) : 1.0;
|
|
134
|
+
color = mix(ground(scenePx), color, fade);
|
|
135
|
+
|
|
136
|
+
float coverage = clamp(inside * u_dpr + 0.5, 0.0, 1.0);
|
|
137
|
+
gl_FragColor = vec4(clamp(color, 0.0, 1.0), coverage);
|
|
138
|
+
}`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GlassConfig } from "./lens-glass/config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared material for controls over images and video. Their icons are white
|
|
5
|
+
* (`text-fg-scrim`), so the ground is dimmed further than the default to keep
|
|
6
|
+
* them legible over a bright frame, and frosted a little more.
|
|
7
|
+
*/
|
|
8
|
+
export const mediaGlass: GlassConfig = {
|
|
9
|
+
dim: 0.62,
|
|
10
|
+
frost: 4,
|
|
11
|
+
shadow: 0.2,
|
|
12
|
+
}
|
package/src/modal.tsx
CHANGED
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
type ReactElement,
|
|
15
15
|
type ReactNode,
|
|
16
16
|
} from "react"
|
|
17
|
-
import {
|
|
17
|
+
import { buttonVariants } from "./button"
|
|
18
|
+
import { GlassButton, GlassRoot } from "./glass"
|
|
19
|
+
import { mediaGlass } from "./lib/media-glass"
|
|
18
20
|
import {
|
|
19
21
|
Carousel,
|
|
20
22
|
CarouselContent,
|
|
@@ -51,10 +53,9 @@ import {
|
|
|
51
53
|
const TRACKPAD_LABEL =
|
|
52
54
|
"Swipe, use the arrow keys, or use the controls to navigate"
|
|
53
55
|
|
|
54
|
-
//
|
|
55
|
-
// hover fill is a page grey and is cleared, or it shows through the body.
|
|
56
|
+
// Match the video controls while preserving the image viewer focus treatment.
|
|
56
57
|
const modalControlClassName =
|
|
57
|
-
"glass glass-
|
|
58
|
+
"glass-optical glass-optical-media text-fg-scrim hover:text-fg-scrim outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-surface-scrim focus-visible:ring-ring/20"
|
|
58
59
|
|
|
59
60
|
export const modalContentVariants = cva(
|
|
60
61
|
"h-dvh max-h-dvh w-screen max-w-none gap-0 overflow-hidden rounded-none border-0 bg-transparent p-0 shadow-none max-sm:max-w-none",
|
|
@@ -249,11 +250,14 @@ const ModalDownload = ({
|
|
|
249
250
|
<ModalControlTooltip label="Download image">
|
|
250
251
|
<a
|
|
251
252
|
href={src}
|
|
253
|
+
data-slot="glass"
|
|
254
|
+
data-glass-radius="css"
|
|
255
|
+
data-config={JSON.stringify({ ...mediaGlass, button: true })}
|
|
252
256
|
download={name ?? ""}
|
|
253
257
|
aria-label="Download image"
|
|
254
258
|
className={cn(
|
|
255
259
|
buttonVariants({
|
|
256
|
-
variant: "
|
|
260
|
+
variant: "glass",
|
|
257
261
|
size: "md",
|
|
258
262
|
rounded: true,
|
|
259
263
|
iconOnly: true,
|
|
@@ -273,9 +277,9 @@ const ModalDismiss = () => (
|
|
|
273
277
|
<ModalControlTooltip label="Close image">
|
|
274
278
|
<DialogClose
|
|
275
279
|
render={
|
|
276
|
-
<
|
|
280
|
+
<GlassButton
|
|
277
281
|
type="button"
|
|
278
|
-
|
|
282
|
+
config={mediaGlass}
|
|
279
283
|
size="md"
|
|
280
284
|
iconOnly
|
|
281
285
|
rounded
|
|
@@ -322,22 +326,24 @@ export const ModalContent = ({
|
|
|
322
326
|
{...props}
|
|
323
327
|
>
|
|
324
328
|
<TooltipProvider delay={250}>
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
329
|
+
<GlassRoot interactiveLighting defaults={mediaGlass} className="size-full [--glass-ground:var(--color-surface-scrim)]">
|
|
330
|
+
<ModalBackground src={src} />
|
|
331
|
+
<DialogTitle className="sr-only">{alt}</DialogTitle>
|
|
332
|
+
<div
|
|
333
|
+
data-slot="modal-stage"
|
|
334
|
+
className="relative z-10 flex size-full flex-col items-center justify-center p-4"
|
|
335
|
+
>
|
|
336
|
+
<ModalImage src={src} alt={alt} imgProps={imgProps} />
|
|
337
|
+
{variant === "caption" && (caption || children) ? (
|
|
338
|
+
<ModalCaption>{caption ?? children}</ModalCaption>
|
|
339
|
+
) : null}
|
|
340
|
+
</div>
|
|
341
|
+
<ModalDismiss />
|
|
342
|
+
<ModalDownload
|
|
343
|
+
src={downloadSrc ?? src}
|
|
344
|
+
name={downloadName}
|
|
345
|
+
/>
|
|
346
|
+
</GlassRoot>
|
|
341
347
|
</TooltipProvider>
|
|
342
348
|
</DialogPopup>
|
|
343
349
|
</DialogPortal>
|
|
@@ -493,99 +499,101 @@ const GalleryContent = ({
|
|
|
493
499
|
)}
|
|
494
500
|
>
|
|
495
501
|
<TooltipProvider delay={250}>
|
|
496
|
-
<
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
<
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
<div className="flex min-h-0 w-full items-center justify-center
|
|
518
|
-
<
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
502
|
+
<GlassRoot interactiveLighting defaults={mediaGlass} className="size-full [--glass-ground:var(--color-surface-scrim)]">
|
|
503
|
+
<ModalBackground src={activeImage.src} />
|
|
504
|
+
<DialogTitle className="sr-only">{activeImage.alt}</DialogTitle>
|
|
505
|
+
<p className="sr-only" aria-live="polite">
|
|
506
|
+
Image {activeIndex + 1} of {images.length}. {TRACKPAD_LABEL}.
|
|
507
|
+
</p>
|
|
508
|
+
<div className="relative z-10 flex size-full flex-col gap-4 p-4">
|
|
509
|
+
<Carousel
|
|
510
|
+
setApi={setApi}
|
|
511
|
+
opts={{ loop, startIndex: activeIndex, duration: 24, dragFree: false }}
|
|
512
|
+
data-slot="modal-stage"
|
|
513
|
+
className="relative min-h-0 w-full min-w-0 flex-1 [&_[data-slot=carousel-viewport]]:h-full [&_[data-slot=carousel-viewport]]:min-h-0"
|
|
514
|
+
aria-label="Gallery"
|
|
515
|
+
>
|
|
516
|
+
<CarouselContent className="h-full !ml-0 !pr-0">
|
|
517
|
+
{images.map((image, index) => (
|
|
518
|
+
<CarouselItem
|
|
519
|
+
key={`${image.src}-${index}`}
|
|
520
|
+
className="!basis-full flex h-full min-h-0 flex-col items-center justify-center !pl-0"
|
|
521
|
+
aria-label={`${index + 1} of ${images.length}`}
|
|
522
|
+
>
|
|
523
|
+
<div className="flex max-h-full min-h-0 w-full flex-col items-center justify-center">
|
|
524
|
+
<div className="flex min-h-0 w-full items-center justify-center overflow-hidden">
|
|
525
|
+
<ModalImage
|
|
526
|
+
{...image}
|
|
527
|
+
fit="contain"
|
|
528
|
+
imgProps={{
|
|
529
|
+
...image.imgProps,
|
|
530
|
+
className: cn(
|
|
531
|
+
"max-w-[min(94vw,72rem)]",
|
|
532
|
+
image.imgProps?.className,
|
|
533
|
+
),
|
|
534
|
+
}}
|
|
535
|
+
/>
|
|
536
|
+
</div>
|
|
537
|
+
{variant === "caption" && image.caption ? (
|
|
538
|
+
<ModalCaption>{image.caption}</ModalCaption>
|
|
539
|
+
) : null}
|
|
529
540
|
</div>
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
541
|
+
</CarouselItem>
|
|
542
|
+
))}
|
|
543
|
+
</CarouselContent>
|
|
544
|
+
</Carousel>
|
|
545
|
+
<GalleryFilmstrip
|
|
546
|
+
images={images}
|
|
547
|
+
activeIndex={activeIndex}
|
|
548
|
+
onSelect={handleSelect}
|
|
549
|
+
/>
|
|
550
|
+
</div>
|
|
551
|
+
<ModalDismiss />
|
|
552
|
+
<ModalDownload
|
|
553
|
+
src={activeImage.downloadSrc ?? activeImage.src}
|
|
554
|
+
name={activeImage.downloadName}
|
|
542
555
|
/>
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
<IconChevronRightSmall aria-hidden />
|
|
585
|
-
</Button>
|
|
586
|
-
</ModalControlTooltip>
|
|
587
|
-
</>
|
|
588
|
-
) : null}
|
|
556
|
+
{images.length > 1 ? (
|
|
557
|
+
<>
|
|
558
|
+
<ModalControlTooltip label="Previous image" side="right">
|
|
559
|
+
<GlassButton
|
|
560
|
+
type="button"
|
|
561
|
+
config={mediaGlass}
|
|
562
|
+
size="lg"
|
|
563
|
+
iconOnly
|
|
564
|
+
rounded
|
|
565
|
+
aria-label="Previous image"
|
|
566
|
+
disabled={!loop && !api?.canScrollPrev()}
|
|
567
|
+
onClick={() => api?.scrollPrev()}
|
|
568
|
+
className={cn(
|
|
569
|
+
modalControlClassName,
|
|
570
|
+
"absolute top-1/2 left-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
|
|
571
|
+
)}
|
|
572
|
+
>
|
|
573
|
+
<IconChevronRightSmall className="rotate-180" aria-hidden />
|
|
574
|
+
</GlassButton>
|
|
575
|
+
</ModalControlTooltip>
|
|
576
|
+
<ModalControlTooltip label="Next image" side="left">
|
|
577
|
+
<GlassButton
|
|
578
|
+
type="button"
|
|
579
|
+
config={mediaGlass}
|
|
580
|
+
size="lg"
|
|
581
|
+
iconOnly
|
|
582
|
+
rounded
|
|
583
|
+
aria-label="Next image"
|
|
584
|
+
disabled={!loop && !api?.canScrollNext()}
|
|
585
|
+
onClick={() => api?.scrollNext()}
|
|
586
|
+
className={cn(
|
|
587
|
+
modalControlClassName,
|
|
588
|
+
"absolute top-1/2 right-4 z-20 -translate-y-1/2 active:!-translate-y-1/2",
|
|
589
|
+
)}
|
|
590
|
+
>
|
|
591
|
+
<IconChevronRightSmall aria-hidden />
|
|
592
|
+
</GlassButton>
|
|
593
|
+
</ModalControlTooltip>
|
|
594
|
+
</>
|
|
595
|
+
) : null}
|
|
596
|
+
</GlassRoot>
|
|
589
597
|
</TooltipProvider>
|
|
590
598
|
</DialogPopup>
|
|
591
599
|
</DialogPortal>
|
package/src/video-player.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import { IconVolumeFull } from "@central-icons-react/round-filled-radius-2-strok
|
|
|
18
18
|
import { IconVolumeOff } from "@central-icons-react/round-filled-radius-2-stroke-2/IconVolumeOff"
|
|
19
19
|
import { cn } from "./lib/cn"
|
|
20
20
|
import { Glass, GlassButton, GlassRoot } from "./glass"
|
|
21
|
+
import { mediaGlass } from "./lib/media-glass"
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Picture-in-Picture support is a static browser capability, not React state.
|
|
@@ -36,19 +37,7 @@ const formatTime = (seconds: number) => {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// All panes are direct children of the video root, sharing one WebGL context.
|
|
39
|
-
|
|
40
|
-
blurAmount: 0.08,
|
|
41
|
-
refraction: 0.12,
|
|
42
|
-
zRadius: 3,
|
|
43
|
-
chromAberration: 0,
|
|
44
|
-
brightness: -0.3,
|
|
45
|
-
edgeHighlight: 0.045,
|
|
46
|
-
specular: 0.02,
|
|
47
|
-
fresnel: 1,
|
|
48
|
-
shadowOpacity: 0.065,
|
|
49
|
-
shadowSpread: 12,
|
|
50
|
-
shadowOffsetY: 3,
|
|
51
|
-
}
|
|
40
|
+
|
|
52
41
|
|
|
53
42
|
export type VideoPlayerProps = ComponentProps<"div"> & {
|
|
54
43
|
src: string
|
|
@@ -284,7 +273,7 @@ export const VideoPlayer = ({
|
|
|
284
273
|
return (
|
|
285
274
|
<GlassRoot
|
|
286
275
|
interactiveLighting
|
|
287
|
-
defaults={
|
|
276
|
+
defaults={mediaGlass}
|
|
288
277
|
ref={playerRef}
|
|
289
278
|
data-slot="video-player"
|
|
290
279
|
data-playing={playing || undefined}
|
|
@@ -350,7 +339,7 @@ export const VideoPlayer = ({
|
|
|
350
339
|
type="button"
|
|
351
340
|
rounded
|
|
352
341
|
iconOnly
|
|
353
|
-
config={
|
|
342
|
+
config={mediaGlass}
|
|
354
343
|
className={cn(controlVisibility, "left-2 group-data-[fullscreen]:left-5", (failed || loading) && "invisible")}
|
|
355
344
|
aria-label={ended ? "Replay" : playing ? "Pause" : "Play"}
|
|
356
345
|
onClick={handleTogglePlay}
|
|
@@ -365,7 +354,7 @@ export const VideoPlayer = ({
|
|
|
365
354
|
type="button"
|
|
366
355
|
rounded
|
|
367
356
|
iconOnly
|
|
368
|
-
config={
|
|
357
|
+
config={mediaGlass}
|
|
369
358
|
className={cn(controlVisibility, "left-13 group-data-[fullscreen]:left-16", (failed || loading) && "invisible")}
|
|
370
359
|
aria-label={muted ? "Unmute" : "Mute"}
|
|
371
360
|
onClick={handleToggleMute}
|
|
@@ -473,7 +462,7 @@ export const VideoPlayer = ({
|
|
|
473
462
|
type="button"
|
|
474
463
|
rounded
|
|
475
464
|
iconOnly
|
|
476
|
-
config={
|
|
465
|
+
config={mediaGlass}
|
|
477
466
|
className={cn(controlVisibility, "right-13 group-data-[fullscreen]:right-16", (!pipSupported || failed || loading) && "invisible")}
|
|
478
467
|
aria-label={
|
|
479
468
|
pipActive
|
|
@@ -494,7 +483,7 @@ export const VideoPlayer = ({
|
|
|
494
483
|
type="button"
|
|
495
484
|
rounded
|
|
496
485
|
iconOnly
|
|
497
|
-
config={
|
|
486
|
+
config={mediaGlass}
|
|
498
487
|
className={cn(controlVisibility, "right-2 group-data-[fullscreen]:right-5", (failed || loading) && "invisible")}
|
|
499
488
|
aria-label={fullscreen ? "Exit fullscreen" : "Fullscreen"}
|
|
500
489
|
aria-pressed={fullscreen}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# LiquidGlass
|
|
2
|
-
|
|
3
|
-
Vendored from `@ybouane/liquidglass@1.0.3`.
|
|
4
|
-
|
|
5
|
-
Source: https://github.com/ybouane/liquidglass
|
|
6
|
-
License: MIT (as declared by the upstream package).
|
|
7
|
-
|
|
8
|
-
`index.js` is the package's published bundled WebGL renderer. StandardUI
|
|
9
|
-
vendors the build because version 1.0.3 publishes a `postinstall`
|
|
10
|
-
command whose `patch-package` executable is not a runtime dependency.
|
|
11
|
-
|
|
12
|
-
Local patch: `LiquidGlass.init()` destroys a partially initialized instance
|
|
13
|
-
before rethrowing a startup error, releasing its canvas, listeners and WebGL
|
|
14
|
-
resources.
|
|
15
|
-
|
|
16
|
-
StandardUI's `data-glass-radius="css"` panes derive the shader radius from the
|
|
17
|
-
computed CSS radius. Disabled buttons suppress shader hover/press feedback.
|
|
18
|
-
|
|
19
|
-
Responsive images are decoded from `currentSrc` without `srcset` before canvas
|
|
20
|
-
sampling. This keeps object-fit crop coordinates in resource pixels instead of
|
|
21
|
-
mixing density-corrected `naturalWidth` with full-resolution canvas coordinates.
|
|
22
|
-
|
|
23
|
-
Injected canvases compensate for the host's border so the rendered lens stays
|
|
24
|
-
centered on bordered controls and their icons.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type surface for the vendored @ybouane/liquidglass 1.0.3 renderer.
|
|
3
|
-
* Source: https://github.com/ybouane/liquidglass
|
|
4
|
-
* License: MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export interface GlassConfig {
|
|
8
|
-
blurAmount: number;
|
|
9
|
-
refraction: number;
|
|
10
|
-
chromAberration: number;
|
|
11
|
-
edgeHighlight: number;
|
|
12
|
-
specular: number;
|
|
13
|
-
fresnel: number;
|
|
14
|
-
distortion: number;
|
|
15
|
-
cornerRadius: number;
|
|
16
|
-
zRadius: number;
|
|
17
|
-
opacity: number;
|
|
18
|
-
saturation: number;
|
|
19
|
-
tintStrength: number;
|
|
20
|
-
brightness: number;
|
|
21
|
-
shadowOpacity: number;
|
|
22
|
-
shadowSpread: number;
|
|
23
|
-
shadowOffsetY: number;
|
|
24
|
-
floating: boolean;
|
|
25
|
-
button: boolean;
|
|
26
|
-
bevelMode: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface LiquidGlassOptions {
|
|
30
|
-
root: HTMLElement;
|
|
31
|
-
glassElements?: NodeListOf<HTMLElement> | HTMLElement[];
|
|
32
|
-
defaults?: Partial<GlassConfig>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export declare class LiquidGlass {
|
|
36
|
-
static init(options: LiquidGlassOptions): Promise<LiquidGlass>;
|
|
37
|
-
readonly root: HTMLElement;
|
|
38
|
-
readonly defaults: GlassConfig;
|
|
39
|
-
fps: number;
|
|
40
|
-
destroy(): void;
|
|
41
|
-
markChanged(element?: HTMLElement): void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export declare const DEFAULTS: GlassConfig;
|
|
45
|
-
export declare function invalidateFontEmbedCache(): void;
|