@cruxgarden/plasma-ui 0.2.3 → 0.3.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/CHANGELOG.md +126 -0
- package/README.md +82 -7
- package/dist/Plasma.d.ts +31 -6
- package/dist/PlasmaProvider.d.ts +77 -12
- package/dist/index.d.ts +12 -3
- package/dist/index.js +397 -173
- package/dist/renderer.d.ts +50 -3
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/PlasmaProvider.tsx
|
|
2
|
-
import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
|
|
4
4
|
// src/shaders.ts
|
|
5
5
|
var DEFAULT_MAX_SHAPES = 16;
|
|
@@ -178,7 +178,7 @@ void main(){
|
|
|
178
178
|
const compFrag = `#version 300 es
|
|
179
179
|
${common}
|
|
180
180
|
uniform sampler2D uH, uS, uTint, uBg, uBgM, uBgH, uFrost;
|
|
181
|
-
uniform float uRefract, uDisp, uRim, uRimMode, uRimWidth, uSpec, uHair;
|
|
181
|
+
uniform float uRefract, uDisp, uRim, uRimMode, uRimWidth, uSpec, uHair, uShim, uGlow, uWash, uGrain;
|
|
182
182
|
uniform vec3 uRimColor;
|
|
183
183
|
out vec4 o;
|
|
184
184
|
|
|
@@ -235,7 +235,7 @@ void main(){
|
|
|
235
235
|
// Palette phases run on viewport position, like the grain: a frame drawn for
|
|
236
236
|
// a taller region must colour its viewport band exactly as the live frame does.
|
|
237
237
|
vec2 vp = p - uView.xy;
|
|
238
|
-
col += pal(uTime*.03 + vp.x/1400.) * .06 * smoothstep(.0, .45, hHere) * (1.+uEnergy);
|
|
238
|
+
col += pal(uTime*.03 + vp.x/1400.) * .06 * smoothstep(.0, .45, hHere) * (1.+uEnergy) * uGlow;
|
|
239
239
|
|
|
240
240
|
if(sd > -2.){
|
|
241
241
|
vec2 th = 2. / vec2(textureSize(uH, 0));
|
|
@@ -255,7 +255,12 @@ void main(){
|
|
|
255
255
|
seen(p + off, fr).g,
|
|
256
256
|
seen(p + off*(1.-disp), fr).b
|
|
257
257
|
);
|
|
258
|
+
// uWash 0 hands the background through untouched - glass with no cast of
|
|
259
|
+
// its own. 1 is the original desaturate-and-lift that gives the material
|
|
260
|
+
// its body.
|
|
261
|
+
vec3 clearRefr = refr;
|
|
258
262
|
refr = mix(refr, vec3(dot(refr, vec3(.333))), .18) * mix(1.08, .9, uLight) + .03*(1.-uLight);
|
|
263
|
+
refr = mix(clearRefr, refr, uWash);
|
|
259
264
|
// frosted: milkier and a little brighter
|
|
260
265
|
refr = mix(refr, mix(refr, vec3(dot(refr, vec3(.333))), .25) * mix(1.12, .97, uLight) + mix(.05, .03, uLight), fr);
|
|
261
266
|
float hl = 1. - .55*uLight;
|
|
@@ -280,7 +285,7 @@ void main(){
|
|
|
280
285
|
plasma += rimCol * fres * .45 * hl * uRim;
|
|
281
286
|
plasma += vec3(1.) * spec * .75 * hl * uSpec;
|
|
282
287
|
// faint shimmer across the body; fades out as the tint becomes opaque
|
|
283
|
-
plasma += pal(uTime*.04 + vp.y/900. + uEnergy*.3) * .05 * lift * (1.+uEnergy*2.) * hl * (1. - talpha);
|
|
288
|
+
plasma += pal(uTime*.04 + vp.y/900. + uEnergy*.3) * .05 * lift * (1.+uEnergy*2.) * hl * (1. - talpha) * uShim;
|
|
284
289
|
vec3 hairCol = uRimMode > .5 ? mix(vec3(1.), rimCol / 1.4, .6) : vec3(.9,.95,1.);
|
|
285
290
|
plasma += hairCol * (1.-smoothstep(0., 1.6, abs(sd - .7))) * .4 * hl * uHair;
|
|
286
291
|
|
|
@@ -290,7 +295,7 @@ void main(){
|
|
|
290
295
|
}
|
|
291
296
|
// Keyed on viewport position, so a frame drawn for a taller region has the
|
|
292
297
|
// same grain in its viewport band as the live frame that replaces it.
|
|
293
|
-
col += (hash(p - uView.xy + uTime) - .5) * .025 * grain;
|
|
298
|
+
col += (hash(p - uView.xy + uTime) - .5) * .025 * grain * uGrain;
|
|
294
299
|
o = vec4(col, 1.);
|
|
295
300
|
}`;
|
|
296
301
|
return { maskFrag, tintFrag, blurFrag, bgFrag, compFrag };
|
|
@@ -361,7 +366,11 @@ var UNIFORMS = [
|
|
|
361
366
|
"uRimColor",
|
|
362
367
|
"uRimWidth",
|
|
363
368
|
"uSpec",
|
|
364
|
-
"uHair"
|
|
369
|
+
"uHair",
|
|
370
|
+
"uShim",
|
|
371
|
+
"uGlow",
|
|
372
|
+
"uWash",
|
|
373
|
+
"uGrain"
|
|
365
374
|
];
|
|
366
375
|
var MASK_SCALE = 0.5;
|
|
367
376
|
var MAX_PIXELS = 26e5;
|
|
@@ -419,13 +428,25 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
419
428
|
this.imgSrc = null;
|
|
420
429
|
this.bgColor = null;
|
|
421
430
|
this.srcEl = null;
|
|
431
|
+
// re-uploaded each frame
|
|
432
|
+
this.floatOK = false;
|
|
433
|
+
/** True between webglcontextlost and webglcontextrestored: draw nothing. */
|
|
434
|
+
this.lost = false;
|
|
435
|
+
this.warnedOverflow = false;
|
|
436
|
+
/**
|
|
437
|
+
* Set by destroy(). The canvas and its context outlive this renderer, so an
|
|
438
|
+
* async callback that lands afterwards would happily allocate on a context
|
|
439
|
+
* nothing can free it from.
|
|
440
|
+
*/
|
|
441
|
+
this.destroyed = false;
|
|
442
|
+
/** Everything initGL() created, so destroy() can free it by hand. */
|
|
443
|
+
this.owned = { tex: [], fb: [], prog: [], buf: [] };
|
|
422
444
|
this.recs = /* @__PURE__ */ new Map();
|
|
423
445
|
this.nextId = 1;
|
|
424
446
|
this.raf = 0;
|
|
425
447
|
this.last = 0;
|
|
426
448
|
this.time = 0;
|
|
427
449
|
this.dpr = 1;
|
|
428
|
-
this.resizeQueued = false;
|
|
429
450
|
/** Coarse pointer = touch. Only there does a fling run on the compositor with rAF deferred. */
|
|
430
451
|
this.coarse = typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
431
452
|
this.frozen = false;
|
|
@@ -450,6 +471,31 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
450
471
|
this.lightQuery = typeof matchMedia !== "undefined" ? matchMedia("(prefers-color-scheme: light)") : null;
|
|
451
472
|
this.tintCache = /* @__PURE__ */ new Map();
|
|
452
473
|
this.RP = new Float32Array(MAX_PULSES * 4);
|
|
474
|
+
/**
|
|
475
|
+
* A drag-resize reallocates eight render targets per distinct size, and the
|
|
476
|
+
* driver can drop the context under that. Without these two handlers the
|
|
477
|
+
* field simply stopped: the frame loop kept running against a dead context
|
|
478
|
+
* and nothing ever brought it back. preventDefault is what asks the browser
|
|
479
|
+
* to attempt a restore at all.
|
|
480
|
+
*/
|
|
481
|
+
this.onContextLost = (e) => {
|
|
482
|
+
e.preventDefault();
|
|
483
|
+
this.lost = true;
|
|
484
|
+
cancelAnimationFrame(this.raf);
|
|
485
|
+
this.raf = 0;
|
|
486
|
+
};
|
|
487
|
+
this.onContextRestored = () => {
|
|
488
|
+
this.lost = false;
|
|
489
|
+
this.initGL();
|
|
490
|
+
this.last = 0;
|
|
491
|
+
if (this.frozen) {
|
|
492
|
+
clearTimeout(this.scrollIdle);
|
|
493
|
+
this.thaw();
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
this.applyResize();
|
|
497
|
+
if (!document.hidden && !this.raf) this.raf = requestAnimationFrame(this.frame);
|
|
498
|
+
};
|
|
453
499
|
/**
|
|
454
500
|
* A hidden tab draws nothing anyone can see. Browsers throttle rAF there
|
|
455
501
|
* but do not all stop it, so the loop stops itself and picks up where it
|
|
@@ -511,12 +557,8 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
511
557
|
* otherwise reallocated every texture repeatedly mid-scroll.
|
|
512
558
|
*/
|
|
513
559
|
this.resize = () => {
|
|
514
|
-
|
|
515
|
-
this.
|
|
516
|
-
requestAnimationFrame(() => {
|
|
517
|
-
this.resizeQueued = false;
|
|
518
|
-
this.applyResize();
|
|
519
|
-
});
|
|
560
|
+
clearTimeout(this.resizeSettle);
|
|
561
|
+
this.resizeSettle = setTimeout(this.applyResize, 120);
|
|
520
562
|
};
|
|
521
563
|
this.applyResize = () => {
|
|
522
564
|
if (this.frozen) {
|
|
@@ -559,6 +601,10 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
559
601
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
560
602
|
};
|
|
561
603
|
this.frame = (now2) => {
|
|
604
|
+
if (this.lost) {
|
|
605
|
+
this.raf = 0;
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
562
608
|
if (!this.oneShot) this.raf = requestAnimationFrame(this.frame);
|
|
563
609
|
const dt = this.last ? Math.min((now2 - this.last) / 1e3, 0.05) : 0.016;
|
|
564
610
|
this.last = now2;
|
|
@@ -679,6 +725,10 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
679
725
|
r.drawn = { l, t, w: rgt - l, h: btm - t };
|
|
680
726
|
});
|
|
681
727
|
this.drawnScroll = [scrollX, scrollY];
|
|
728
|
+
if (list.length > this.max && !this.warnedOverflow) {
|
|
729
|
+
this.warnedOverflow = true;
|
|
730
|
+
console.warn(`[plasma-ui] ${list.length} plasma surfaces are on screen but maxSurfaces is ${this.max}; the rest are not drawn. Raise maxSurfaces on <PlasmaProvider>.`);
|
|
731
|
+
}
|
|
682
732
|
this.lastList = list.slice(0, this.max);
|
|
683
733
|
this.draw(this.lastList);
|
|
684
734
|
};
|
|
@@ -689,33 +739,12 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
689
739
|
this.gl = gl;
|
|
690
740
|
this.settings = settings;
|
|
691
741
|
this.max = Math.max(1, Math.round(settings.maxSurfaces || DEFAULT_MAX_SHAPES));
|
|
692
|
-
this.
|
|
693
|
-
this.
|
|
694
|
-
this.F = new Float32Array(this.max);
|
|
695
|
-
this.T = new Float32Array(this.max * 4);
|
|
696
|
-
this.FR = new Float32Array(this.max);
|
|
697
|
-
this.EL = new Float32Array(this.max);
|
|
698
|
-
this.SOLO = new Float32Array(this.max);
|
|
699
|
-
this.floatOK = !!gl.getExtension("EXT_color_buffer_float");
|
|
700
|
-
const sh = makeShaders(this.max);
|
|
701
|
-
this.progs = { bg: this.program(sh.bgFrag), mask: this.program(sh.maskFrag), tint: this.program(sh.tintFrag), blur: this.program(sh.blurFrag), comp: this.program(sh.compFrag) };
|
|
702
|
-
const buf = gl.createBuffer();
|
|
703
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
704
|
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
|
705
|
-
gl.enableVertexAttribArray(0);
|
|
706
|
-
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
707
|
-
this.rtA = this.target();
|
|
708
|
-
this.rtB = this.target();
|
|
709
|
-
this.rtC = this.target();
|
|
710
|
-
this.rtT = this.target();
|
|
711
|
-
this.rtFr = this.target();
|
|
712
|
-
this.rtBg = this.target();
|
|
713
|
-
this.rtBgM = this.target();
|
|
714
|
-
this.rtBgH = this.target();
|
|
715
|
-
this.mrtFb = gl.createFramebuffer();
|
|
716
|
-
this.configure(settings, true);
|
|
742
|
+
this.allocUniformArrays();
|
|
743
|
+
this.initGL();
|
|
717
744
|
this.mouse.x = this.mouse.tx = innerWidth / 2;
|
|
718
745
|
this.mouse.y = this.mouse.ty = innerHeight / 2;
|
|
746
|
+
canvas.addEventListener("webglcontextlost", this.onContextLost);
|
|
747
|
+
canvas.addEventListener("webglcontextrestored", this.onContextRestored);
|
|
719
748
|
addEventListener("resize", this.resize);
|
|
720
749
|
document.addEventListener("visibilitychange", this.onVisibility);
|
|
721
750
|
if (this.coarse) addEventListener("scroll", this.onScroll, { passive: true });
|
|
@@ -761,7 +790,7 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
761
790
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
762
791
|
this.imgTex = tex;
|
|
763
792
|
const up = (source, w, h) => {
|
|
764
|
-
if (!w || !h) return;
|
|
793
|
+
if (this.destroyed || !w || !h) return;
|
|
765
794
|
gl.bindTexture(gl.TEXTURE_2D, this.imgTex);
|
|
766
795
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
767
796
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
@@ -776,7 +805,7 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
776
805
|
const img2 = src;
|
|
777
806
|
if (img2.complete && img2.naturalWidth) up(img2, img2.naturalWidth, img2.naturalHeight);
|
|
778
807
|
else img2.addEventListener("load", () => {
|
|
779
|
-
if (this.imgSrc === src) up(img2, img2.naturalWidth, img2.naturalHeight);
|
|
808
|
+
if (!this.destroyed && this.imgSrc === src) up(img2, img2.naturalWidth, img2.naturalHeight);
|
|
780
809
|
}, { once: true });
|
|
781
810
|
}
|
|
782
811
|
return;
|
|
@@ -790,7 +819,7 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
790
819
|
const img = new Image();
|
|
791
820
|
img.crossOrigin = "anonymous";
|
|
792
821
|
img.onload = () => {
|
|
793
|
-
if (this.imgSrc !== src) return;
|
|
822
|
+
if (this.destroyed || this.imgSrc !== src) return;
|
|
794
823
|
const tex = gl.createTexture();
|
|
795
824
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
796
825
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
@@ -808,7 +837,12 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
808
837
|
}
|
|
809
838
|
configure(s, immediate = false) {
|
|
810
839
|
const qualityChanged = s.quality !== this.settings.quality;
|
|
840
|
+
const max = Math.max(1, Math.round(s.maxSurfaces || DEFAULT_MAX_SHAPES));
|
|
811
841
|
this.settings = s;
|
|
842
|
+
if (max !== this.max) {
|
|
843
|
+
this.setMax(max);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
812
846
|
this.loadBackground(s.background ?? null);
|
|
813
847
|
this.colorTarget = s.colors.map(hexToRgb);
|
|
814
848
|
if (immediate) {
|
|
@@ -862,12 +896,17 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
862
896
|
}
|
|
863
897
|
};
|
|
864
898
|
}
|
|
865
|
-
/**
|
|
866
|
-
|
|
899
|
+
/**
|
|
900
|
+
* Layout boxes of all shapes (lean removed; animating draggables report
|
|
901
|
+
* their destination). Pass `group` to see only the surfaces in that group;
|
|
902
|
+
* omit it - as any caller written before groups existed does - to see them all.
|
|
903
|
+
*/
|
|
904
|
+
layoutBoxes(excludeId, fusingOnly = false, group) {
|
|
867
905
|
const out = [];
|
|
868
906
|
this.recs.forEach((r) => {
|
|
869
907
|
if (r.id === excludeId) return;
|
|
870
908
|
if (fusingOnly && r.fuse === false) return;
|
|
909
|
+
if (group !== void 0 && (r.group ?? null) !== group) return;
|
|
871
910
|
out.push(this.layoutBoxOf(r));
|
|
872
911
|
});
|
|
873
912
|
return out;
|
|
@@ -893,8 +932,78 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
893
932
|
bump(e) {
|
|
894
933
|
this.energy = Math.max(this.energy, Math.min(e, 1));
|
|
895
934
|
}
|
|
935
|
+
/** Rebuild the shaders and uniform arrays for a new surface budget. */
|
|
936
|
+
setMax(max) {
|
|
937
|
+
this.max = max;
|
|
938
|
+
this.allocUniformArrays();
|
|
939
|
+
this.releaseGL();
|
|
940
|
+
this.initGL();
|
|
941
|
+
this.applyResize();
|
|
942
|
+
this.warnedOverflow = false;
|
|
943
|
+
}
|
|
944
|
+
allocUniformArrays() {
|
|
945
|
+
this.P = new Float32Array(this.max * 4);
|
|
946
|
+
this.R = new Float32Array(this.max * 4);
|
|
947
|
+
this.F = new Float32Array(this.max);
|
|
948
|
+
this.T = new Float32Array(this.max * 4);
|
|
949
|
+
this.FR = new Float32Array(this.max);
|
|
950
|
+
this.EL = new Float32Array(this.max);
|
|
951
|
+
this.SOLO = new Float32Array(this.max);
|
|
952
|
+
}
|
|
953
|
+
/** Delete every GL object this renderer created, leaving the canvas usable. */
|
|
954
|
+
releaseGL() {
|
|
955
|
+
const gl = this.gl;
|
|
956
|
+
if (!gl.isContextLost()) {
|
|
957
|
+
this.owned.tex.forEach((t) => gl.deleteTexture(t));
|
|
958
|
+
this.owned.fb.forEach((f) => gl.deleteFramebuffer(f));
|
|
959
|
+
this.owned.prog.forEach((pr) => gl.deleteProgram(pr));
|
|
960
|
+
this.owned.buf.forEach((b) => gl.deleteBuffer(b));
|
|
961
|
+
if (this.imgTex) gl.deleteTexture(this.imgTex);
|
|
962
|
+
}
|
|
963
|
+
this.imgTex = null;
|
|
964
|
+
this.imgSrc = null;
|
|
965
|
+
this.srcEl = null;
|
|
966
|
+
this.owned = { tex: [], fb: [], prog: [], buf: [] };
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Every GL object this renderer owns. A lost context invalidates all of
|
|
970
|
+
* them, so creation lives here rather than in the constructor: the restore
|
|
971
|
+
* handler runs exactly the same path.
|
|
972
|
+
*/
|
|
973
|
+
initGL() {
|
|
974
|
+
const gl = this.gl;
|
|
975
|
+
this.owned = { tex: [], fb: [], prog: [], buf: [] };
|
|
976
|
+
this.imgTex = null;
|
|
977
|
+
this.imgSrc = null;
|
|
978
|
+
this.srcEl = null;
|
|
979
|
+
this.floatOK = !!gl.getExtension("EXT_color_buffer_float");
|
|
980
|
+
const sh = makeShaders(this.max);
|
|
981
|
+
this.progs = { bg: this.program(sh.bgFrag), mask: this.program(sh.maskFrag), tint: this.program(sh.tintFrag), blur: this.program(sh.blurFrag), comp: this.program(sh.compFrag) };
|
|
982
|
+
const buf = gl.createBuffer();
|
|
983
|
+
if (buf) this.owned.buf.push(buf);
|
|
984
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
985
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
|
986
|
+
gl.enableVertexAttribArray(0);
|
|
987
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
988
|
+
this.rtA = this.target();
|
|
989
|
+
this.rtB = this.target();
|
|
990
|
+
this.rtC = this.target();
|
|
991
|
+
this.rtT = this.target();
|
|
992
|
+
this.rtFr = this.target();
|
|
993
|
+
this.rtBg = this.target();
|
|
994
|
+
this.rtBgM = this.target();
|
|
995
|
+
this.rtBgH = this.target();
|
|
996
|
+
this.mrtFb = gl.createFramebuffer();
|
|
997
|
+
this.owned.fb.push(this.mrtFb);
|
|
998
|
+
this.configure(this.settings, true);
|
|
999
|
+
this.canvas.width = this.canvas.height = 0;
|
|
1000
|
+
}
|
|
896
1001
|
destroy() {
|
|
1002
|
+
this.destroyed = true;
|
|
897
1003
|
cancelAnimationFrame(this.raf);
|
|
1004
|
+
clearTimeout(this.resizeSettle);
|
|
1005
|
+
this.canvas.removeEventListener("webglcontextlost", this.onContextLost);
|
|
1006
|
+
this.canvas.removeEventListener("webglcontextrestored", this.onContextRestored);
|
|
898
1007
|
removeEventListener("resize", this.resize);
|
|
899
1008
|
document.removeEventListener("visibilitychange", this.onVisibility);
|
|
900
1009
|
removeEventListener("scroll", this.onScroll);
|
|
@@ -906,7 +1015,7 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
906
1015
|
r.el.style.scale = "";
|
|
907
1016
|
});
|
|
908
1017
|
this.recs.clear();
|
|
909
|
-
this.
|
|
1018
|
+
this.releaseGL();
|
|
910
1019
|
}
|
|
911
1020
|
freeze() {
|
|
912
1021
|
const c = this.canvas;
|
|
@@ -1024,6 +1133,18 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
1024
1133
|
gl.useProgram(bl.pr);
|
|
1025
1134
|
gl.uniform1i(bl.u.uTex, 0);
|
|
1026
1135
|
gl.activeTexture(gl.TEXTURE0);
|
|
1136
|
+
const bgBlur = Math.min(40, Math.max(0, s.backgroundBlur || 0));
|
|
1137
|
+
if (bgBlur > 0) {
|
|
1138
|
+
const step = bgBlur * this.dpr * MASK_SCALE / 4;
|
|
1139
|
+
pass(this.rtBg, this.rtBgM, 0, 0);
|
|
1140
|
+
for (let i = 0; i < 3; i++) {
|
|
1141
|
+
pass(this.rtBgM, this.rtB, step, 0);
|
|
1142
|
+
pass(this.rtB, this.rtBgM, 0, step);
|
|
1143
|
+
}
|
|
1144
|
+
gl.viewport(0, 0, this.rtBg.w, this.rtBg.h);
|
|
1145
|
+
pass(this.rtBgM, this.rtBg, 0, 0);
|
|
1146
|
+
gl.viewport(0, 0, this.rtA.w, this.rtA.h);
|
|
1147
|
+
}
|
|
1027
1148
|
const n = list.length;
|
|
1028
1149
|
let hasFrost = false, hasTint = false, hasElev = false;
|
|
1029
1150
|
for (let i = 0; i < n; i++) {
|
|
@@ -1090,6 +1211,10 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
1090
1211
|
gl.uniform1f(c.u.uRimWidth, s.rimWidth);
|
|
1091
1212
|
gl.uniform1f(c.u.uSpec, s.highlight);
|
|
1092
1213
|
gl.uniform1f(c.u.uHair, s.edgeLine);
|
|
1214
|
+
gl.uniform1f(c.u.uShim, s.shimmer);
|
|
1215
|
+
gl.uniform1f(c.u.uGlow, s.glow);
|
|
1216
|
+
gl.uniform1f(c.u.uWash, s.wash);
|
|
1217
|
+
gl.uniform1f(c.u.uGrain, s.grain);
|
|
1093
1218
|
gl.activeTexture(gl.TEXTURE0);
|
|
1094
1219
|
gl.bindTexture(gl.TEXTURE_2D, this.rtC.tex);
|
|
1095
1220
|
gl.activeTexture(gl.TEXTURE1);
|
|
@@ -1136,19 +1261,45 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
1136
1261
|
const sh = gl.createShader(type);
|
|
1137
1262
|
gl.shaderSource(sh, src);
|
|
1138
1263
|
gl.compileShader(sh);
|
|
1139
|
-
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS))
|
|
1264
|
+
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
|
1265
|
+
const log = gl.getShaderInfoLog(sh);
|
|
1266
|
+
gl.deleteShader(sh);
|
|
1267
|
+
throw new Error(log || "shader compile failed");
|
|
1268
|
+
}
|
|
1140
1269
|
return sh;
|
|
1141
1270
|
};
|
|
1142
1271
|
const pr = gl.createProgram();
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1272
|
+
const attached = [];
|
|
1273
|
+
try {
|
|
1274
|
+
const vs = compile(gl.VERTEX_SHADER, vert);
|
|
1275
|
+
let fs;
|
|
1276
|
+
try {
|
|
1277
|
+
fs = compile(gl.FRAGMENT_SHADER, fsrc);
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
gl.deleteShader(vs);
|
|
1280
|
+
throw e;
|
|
1281
|
+
}
|
|
1282
|
+
gl.attachShader(pr, vs);
|
|
1283
|
+
attached.push(vs);
|
|
1284
|
+
gl.attachShader(pr, fs);
|
|
1285
|
+
attached.push(fs);
|
|
1286
|
+
gl.bindAttribLocation(pr, 0, "p");
|
|
1287
|
+
gl.linkProgram(pr);
|
|
1288
|
+
if (!gl.getProgramParameter(pr, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(pr) || "program link failed");
|
|
1289
|
+
} catch (e) {
|
|
1290
|
+
gl.deleteProgram(pr);
|
|
1291
|
+
throw e;
|
|
1292
|
+
} finally {
|
|
1293
|
+
attached.forEach((sh) => {
|
|
1294
|
+
gl.detachShader(pr, sh);
|
|
1295
|
+
gl.deleteShader(sh);
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1148
1298
|
const u = {};
|
|
1149
1299
|
UNIFORMS.forEach((n) => {
|
|
1150
1300
|
u[n] = gl.getUniformLocation(pr, n);
|
|
1151
1301
|
});
|
|
1302
|
+
this.owned.prog.push(pr);
|
|
1152
1303
|
return { pr, u };
|
|
1153
1304
|
}
|
|
1154
1305
|
target() {
|
|
@@ -1159,7 +1310,10 @@ var _PlasmaRenderer = class _PlasmaRenderer {
|
|
|
1159
1310
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1160
1311
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1161
1312
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1162
|
-
|
|
1313
|
+
const fb = gl.createFramebuffer();
|
|
1314
|
+
this.owned.tex.push(tex);
|
|
1315
|
+
this.owned.fb.push(fb);
|
|
1316
|
+
return { tex, fb, w: 0, h: 0 };
|
|
1163
1317
|
}
|
|
1164
1318
|
};
|
|
1165
1319
|
_PlasmaRenderer.RUNWAY = 1;
|
|
@@ -1169,21 +1323,35 @@ var PlasmaRenderer = _PlasmaRenderer;
|
|
|
1169
1323
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1170
1324
|
var noop = () => {
|
|
1171
1325
|
};
|
|
1172
|
-
var
|
|
1173
|
-
|
|
1326
|
+
var DEFAULT_RUNTIME = { renderer: null, supported: false, reducedMotion: false, pulse: noop, bump: noop };
|
|
1327
|
+
var DEFAULT_DEFAULTS = {
|
|
1174
1328
|
tint: "#ffffff",
|
|
1175
1329
|
opacity: 0,
|
|
1176
1330
|
frost: 0,
|
|
1177
1331
|
radius: 26,
|
|
1178
|
-
supported: false,
|
|
1179
1332
|
grid: 24,
|
|
1180
1333
|
magnet: 40,
|
|
1181
|
-
spring: { stiffness: 170, damping: 16 }
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
var
|
|
1334
|
+
spring: { stiffness: 170, damping: 16 }
|
|
1335
|
+
};
|
|
1336
|
+
var RuntimeContext = createContext(DEFAULT_RUNTIME);
|
|
1337
|
+
var DefaultsContext = createContext(DEFAULT_DEFAULTS);
|
|
1338
|
+
var AttachContext = createContext(null);
|
|
1339
|
+
var usePlasmaRuntime = () => useContext(RuntimeContext);
|
|
1340
|
+
var usePlasmaDefaults = () => useContext(DefaultsContext);
|
|
1341
|
+
function usePlasma() {
|
|
1342
|
+
const runtime = usePlasmaRuntime();
|
|
1343
|
+
const defaults = usePlasmaDefaults();
|
|
1344
|
+
return useMemo(() => ({ ...runtime, ...defaults }), [runtime, defaults]);
|
|
1345
|
+
}
|
|
1346
|
+
var DEV = typeof process !== "undefined" && true;
|
|
1347
|
+
var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
1348
|
+
function useLatest(value) {
|
|
1349
|
+
const ref = useRef(value);
|
|
1350
|
+
useIsoLayoutEffect(() => {
|
|
1351
|
+
ref.current = value;
|
|
1352
|
+
});
|
|
1353
|
+
return ref;
|
|
1354
|
+
}
|
|
1187
1355
|
var FALLBACK_CSS = `
|
|
1188
1356
|
.plasma-panel{box-sizing:border-box}
|
|
1189
1357
|
.plasma-fallback{
|
|
@@ -1206,6 +1374,31 @@ function useReducedMotion() {
|
|
|
1206
1374
|
}, []);
|
|
1207
1375
|
return reduced;
|
|
1208
1376
|
}
|
|
1377
|
+
function PlasmaCanvas({ className, style, zIndex = -1 }) {
|
|
1378
|
+
const attach = useContext(AttachContext);
|
|
1379
|
+
const { supported } = usePlasmaRuntime();
|
|
1380
|
+
useEffect(() => {
|
|
1381
|
+
if (DEV && !attach) console.warn("[plasma-ui] <PlasmaCanvas> must be rendered inside a <PlasmaProvider>.");
|
|
1382
|
+
}, [attach]);
|
|
1383
|
+
return /* @__PURE__ */ jsx(
|
|
1384
|
+
"canvas",
|
|
1385
|
+
{
|
|
1386
|
+
ref: attach ?? void 0,
|
|
1387
|
+
"aria-hidden": "true",
|
|
1388
|
+
className,
|
|
1389
|
+
style: {
|
|
1390
|
+
position: "fixed",
|
|
1391
|
+
inset: 0,
|
|
1392
|
+
width: "100%",
|
|
1393
|
+
height: "100%",
|
|
1394
|
+
zIndex,
|
|
1395
|
+
pointerEvents: "none",
|
|
1396
|
+
display: supported ? "block" : "none",
|
|
1397
|
+
...style
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1209
1402
|
function PlasmaProvider({
|
|
1210
1403
|
children,
|
|
1211
1404
|
mood = "tidal",
|
|
@@ -1228,6 +1421,11 @@ function PlasmaProvider({
|
|
|
1228
1421
|
rimWidth = 1,
|
|
1229
1422
|
highlight = 1,
|
|
1230
1423
|
edgeLine = 1,
|
|
1424
|
+
shimmer = 1,
|
|
1425
|
+
glow = 1,
|
|
1426
|
+
wash = 1,
|
|
1427
|
+
grain = 1,
|
|
1428
|
+
backgroundBlur = 0,
|
|
1231
1429
|
pointerDrop = true,
|
|
1232
1430
|
ambientDrops = false,
|
|
1233
1431
|
grid = 24,
|
|
@@ -1235,9 +1433,10 @@ function PlasmaProvider({
|
|
|
1235
1433
|
quality = 1.25,
|
|
1236
1434
|
maxSurfaces = 16,
|
|
1237
1435
|
zIndex = -1,
|
|
1238
|
-
freezeOnScroll = false
|
|
1436
|
+
freezeOnScroll = false,
|
|
1437
|
+
canvas = true
|
|
1239
1438
|
}) {
|
|
1240
|
-
const
|
|
1439
|
+
const [canvasEl, setCanvasEl] = useState(null);
|
|
1241
1440
|
const [renderer, setRenderer] = useState(null);
|
|
1242
1441
|
const [supported, setSupported] = useState(true);
|
|
1243
1442
|
const reducedMotion = useReducedMotion();
|
|
@@ -1261,6 +1460,11 @@ function PlasmaProvider({
|
|
|
1261
1460
|
rimWidth,
|
|
1262
1461
|
highlight,
|
|
1263
1462
|
edgeLine,
|
|
1463
|
+
shimmer,
|
|
1464
|
+
glow,
|
|
1465
|
+
wash,
|
|
1466
|
+
grain,
|
|
1467
|
+
backgroundBlur,
|
|
1264
1468
|
viscosity,
|
|
1265
1469
|
stretch,
|
|
1266
1470
|
flow,
|
|
@@ -1269,91 +1473,55 @@ function PlasmaProvider({
|
|
|
1269
1473
|
maxSurfaces,
|
|
1270
1474
|
background: background ?? null
|
|
1271
1475
|
};
|
|
1272
|
-
const settingsRef =
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
const r = PlasmaRenderer.create(
|
|
1476
|
+
const settingsRef = useLatest(settings);
|
|
1477
|
+
useIsoLayoutEffect(() => {
|
|
1478
|
+
if (!canvasEl) return;
|
|
1479
|
+
const r = PlasmaRenderer.create(canvasEl, settingsRef.current);
|
|
1276
1480
|
if (!r) {
|
|
1277
1481
|
setSupported(false);
|
|
1278
1482
|
return;
|
|
1279
1483
|
}
|
|
1484
|
+
setSupported(true);
|
|
1280
1485
|
setRenderer(r);
|
|
1281
1486
|
return () => {
|
|
1282
1487
|
r.destroy();
|
|
1283
1488
|
setRenderer(null);
|
|
1284
1489
|
};
|
|
1285
|
-
}, []);
|
|
1490
|
+
}, [canvasEl]);
|
|
1491
|
+
useEffect(() => {
|
|
1492
|
+
if (!DEV || canvasEl) return;
|
|
1493
|
+
const t = setTimeout(() => console.warn("[plasma-ui] PlasmaProvider has no canvas. Either leave `canvas` on, or render a <PlasmaCanvas /> inside the provider."), 0);
|
|
1494
|
+
return () => clearTimeout(t);
|
|
1495
|
+
}, [canvasEl]);
|
|
1496
|
+
const settingsDeps = Object.values(settings).map((v) => Array.isArray(v) ? v.join() : v);
|
|
1286
1497
|
useEffect(() => {
|
|
1287
1498
|
renderer?.configure(settings);
|
|
1288
|
-
}, [
|
|
1289
|
-
renderer,
|
|
1290
|
-
m.colors.join(),
|
|
1291
|
-
settings.blend,
|
|
1292
|
-
refraction,
|
|
1293
|
-
dispersion,
|
|
1294
|
-
rim,
|
|
1295
|
-
smoothness,
|
|
1296
|
-
settings.pointerDrop,
|
|
1297
|
-
ambientDrops,
|
|
1298
|
-
theme,
|
|
1299
|
-
quality,
|
|
1300
|
-
reducedMotion,
|
|
1301
|
-
freezeOnScroll,
|
|
1302
|
-
tint,
|
|
1303
|
-
opacity,
|
|
1304
|
-
rimColor,
|
|
1305
|
-
rimWidth,
|
|
1306
|
-
highlight,
|
|
1307
|
-
edgeLine,
|
|
1308
|
-
viscosity,
|
|
1309
|
-
stretch,
|
|
1310
|
-
flow,
|
|
1311
|
-
frost,
|
|
1312
|
-
elevation,
|
|
1313
|
-
background
|
|
1314
|
-
]);
|
|
1499
|
+
}, [renderer, ...settingsDeps]);
|
|
1315
1500
|
const vis = Math.min(Math.max(viscosity, 0), 1);
|
|
1316
1501
|
const stiffK = vis < 0.5 ? 1.6 - 1.2 * vis : 1 - 1.1 * (vis - 0.5);
|
|
1317
1502
|
const dampK = vis < 0.5 ? 0.6 + 0.8 * vis : 1 + 1.6 * (vis - 0.5);
|
|
1318
|
-
const
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
supported,
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
spring,
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
}), [renderer, tint, opacity, frost, radius, supported, grid, magnet, spring.stiffness, spring.damping, reducedMotion]);
|
|
1333
|
-
return /* @__PURE__ */ jsxs(PlasmaContext.Provider, { value, children: [
|
|
1503
|
+
const stiffness = m.spring.stiffness * stiffK;
|
|
1504
|
+
const damping = m.spring.damping * dampK * Math.sqrt(stiffK);
|
|
1505
|
+
const rendererRef = useLatest(renderer);
|
|
1506
|
+
const pulse = useCallback((x, y, s) => rendererRef.current?.pulse(x, y, s), []);
|
|
1507
|
+
const bump = useCallback((e) => rendererRef.current?.bump(e), []);
|
|
1508
|
+
const runtime = useMemo(
|
|
1509
|
+
() => ({ renderer, supported, reducedMotion, pulse, bump }),
|
|
1510
|
+
[renderer, supported, reducedMotion, pulse, bump]
|
|
1511
|
+
);
|
|
1512
|
+
const defaults = useMemo(
|
|
1513
|
+
() => ({ tint, opacity, frost, radius, grid, magnet, spring: { stiffness, damping } }),
|
|
1514
|
+
[tint, opacity, frost, radius, grid, magnet, stiffness, damping]
|
|
1515
|
+
);
|
|
1516
|
+
return /* @__PURE__ */ jsx(RuntimeContext.Provider, { value: runtime, children: /* @__PURE__ */ jsx(DefaultsContext.Provider, { value: defaults, children: /* @__PURE__ */ jsxs(AttachContext.Provider, { value: setCanvasEl, children: [
|
|
1334
1517
|
/* @__PURE__ */ jsx("style", { children: FALLBACK_CSS }),
|
|
1335
|
-
/* @__PURE__ */ jsx(
|
|
1336
|
-
"canvas",
|
|
1337
|
-
{
|
|
1338
|
-
ref: canvasRef,
|
|
1339
|
-
"aria-hidden": "true",
|
|
1340
|
-
style: {
|
|
1341
|
-
position: "fixed",
|
|
1342
|
-
inset: 0,
|
|
1343
|
-
width: "100%",
|
|
1344
|
-
height: "100%",
|
|
1345
|
-
zIndex,
|
|
1346
|
-
pointerEvents: "none",
|
|
1347
|
-
display: supported ? "block" : "none"
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
),
|
|
1518
|
+
canvas && /* @__PURE__ */ jsx(PlasmaCanvas, { zIndex }),
|
|
1351
1519
|
children
|
|
1352
|
-
] });
|
|
1520
|
+
] }) }) });
|
|
1353
1521
|
}
|
|
1354
1522
|
|
|
1355
1523
|
// src/Plasma.tsx
|
|
1356
|
-
import { forwardRef, useCallback
|
|
1524
|
+
import { forwardRef, useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
|
|
1357
1525
|
|
|
1358
1526
|
// src/spring.ts
|
|
1359
1527
|
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
@@ -1464,6 +1632,25 @@ function boxGap(a, b) {
|
|
|
1464
1632
|
// src/Plasma.tsx
|
|
1465
1633
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1466
1634
|
var NO_DRAG = "button,a,input,textarea,select,label,[contenteditable],[data-plasma-nodrag]";
|
|
1635
|
+
var NO_SIDES = { top: false, right: false, bottom: false, left: false };
|
|
1636
|
+
var noSides = () => NO_SIDES;
|
|
1637
|
+
function createSidesStore() {
|
|
1638
|
+
let value = NO_SIDES;
|
|
1639
|
+
const subs = /* @__PURE__ */ new Set();
|
|
1640
|
+
return {
|
|
1641
|
+
subscribe(fn) {
|
|
1642
|
+
subs.add(fn);
|
|
1643
|
+
return () => {
|
|
1644
|
+
subs.delete(fn);
|
|
1645
|
+
};
|
|
1646
|
+
},
|
|
1647
|
+
get: () => value,
|
|
1648
|
+
set(next) {
|
|
1649
|
+
value = next;
|
|
1650
|
+
subs.forEach((fn) => fn());
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1467
1654
|
function fallbackTint(hex, a) {
|
|
1468
1655
|
if (!(a > 0) || !/^#([0-9a-f]{6})$/i.test(hex)) return null;
|
|
1469
1656
|
const n = parseInt(hex.slice(1), 16);
|
|
@@ -1473,7 +1660,12 @@ function assignRef(ref, v) {
|
|
|
1473
1660
|
if (typeof ref === "function") ref(v);
|
|
1474
1661
|
else if (ref) ref.current = v;
|
|
1475
1662
|
}
|
|
1476
|
-
|
|
1663
|
+
function useConst(make) {
|
|
1664
|
+
const ref = useRef2(null);
|
|
1665
|
+
if (ref.current === null) ref.current = make();
|
|
1666
|
+
return ref.current;
|
|
1667
|
+
}
|
|
1668
|
+
var PlasmaInner = forwardRef(function Plasma({
|
|
1477
1669
|
as: Comp = "div",
|
|
1478
1670
|
radius,
|
|
1479
1671
|
lean = 10,
|
|
@@ -1485,6 +1677,7 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1485
1677
|
padding,
|
|
1486
1678
|
draggable = false,
|
|
1487
1679
|
snap = true,
|
|
1680
|
+
group,
|
|
1488
1681
|
bounds,
|
|
1489
1682
|
offset,
|
|
1490
1683
|
defaultOffset,
|
|
@@ -1499,29 +1692,45 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1499
1692
|
tabIndex,
|
|
1500
1693
|
...rest
|
|
1501
1694
|
}, ref) {
|
|
1502
|
-
const
|
|
1503
|
-
const
|
|
1695
|
+
const runtime = usePlasmaRuntime();
|
|
1696
|
+
const defaults = usePlasmaDefaults();
|
|
1697
|
+
const r = radius ?? defaults.radius;
|
|
1504
1698
|
const el = useRef2(null);
|
|
1505
1699
|
const handle = useRef2(null);
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1700
|
+
const x = useConst(() => springValue(offset?.x ?? defaultOffset?.x ?? 0));
|
|
1701
|
+
const y = useConst(() => springValue(offset?.y ?? defaultOffset?.y ?? 0));
|
|
1508
1702
|
const dest = useRef2({ x: x.get(), y: y.get() });
|
|
1509
1703
|
const anims = useRef2([]);
|
|
1510
1704
|
const [dragging, setDragging] = useState2(false);
|
|
1511
1705
|
const positioned = draggable || !!offset || !!defaultOffset;
|
|
1512
|
-
const joinCb =
|
|
1513
|
-
|
|
1514
|
-
const
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
const
|
|
1706
|
+
const joinCb = useLatest(onJoinChange);
|
|
1707
|
+
const sidesStore = useConst(createSidesStore);
|
|
1708
|
+
const sides = useSyncExternalStore(sidesStore.subscribe, sidesStore.get, noSides);
|
|
1709
|
+
const runtimeRef = useLatest(runtime);
|
|
1710
|
+
const defaultsRef = useLatest(defaults);
|
|
1711
|
+
const boundsRef = useLatest(bounds);
|
|
1712
|
+
const snapRef = useLatest(snap);
|
|
1713
|
+
const onDragEndRef = useLatest(onDragEnd);
|
|
1714
|
+
const onDragStartRef = useLatest(onDragStart);
|
|
1715
|
+
const opts = {
|
|
1716
|
+
radius: r,
|
|
1717
|
+
lean: lean || 0,
|
|
1718
|
+
tint: tint ?? null,
|
|
1719
|
+
opacity: opacity ?? null,
|
|
1720
|
+
frost: frost ?? null,
|
|
1721
|
+
elevation: elevation ?? null,
|
|
1722
|
+
fuse,
|
|
1723
|
+
group: group ?? null
|
|
1724
|
+
};
|
|
1725
|
+
const optsRef = useLatest(opts);
|
|
1726
|
+
const setRef = useCallback2((node) => {
|
|
1518
1727
|
el.current = node;
|
|
1519
1728
|
assignRef(ref, node);
|
|
1520
1729
|
}, [ref]);
|
|
1521
|
-
|
|
1522
|
-
const node = el.current, ren =
|
|
1730
|
+
useIsoLayoutEffect(() => {
|
|
1731
|
+
const node = el.current, ren = runtime.renderer;
|
|
1523
1732
|
if (!node || !ren) return;
|
|
1524
|
-
const h = ren.register(node,
|
|
1733
|
+
const h = ren.register(node, optsRef.current, (j) => joinCb.current?.(j), sidesStore.set);
|
|
1525
1734
|
if (positioned) {
|
|
1526
1735
|
h.setLayoutBox(() => {
|
|
1527
1736
|
const rect = node.getBoundingClientRect();
|
|
@@ -1540,11 +1749,11 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1540
1749
|
h.remove();
|
|
1541
1750
|
handle.current = null;
|
|
1542
1751
|
};
|
|
1543
|
-
}, [
|
|
1544
|
-
|
|
1545
|
-
handle.current?.update(
|
|
1546
|
-
}, [r, lean, tint, opacity, frost, elevation, fuse]);
|
|
1547
|
-
|
|
1752
|
+
}, [runtime.renderer, positioned]);
|
|
1753
|
+
useIsoLayoutEffect(() => {
|
|
1754
|
+
handle.current?.update(opts);
|
|
1755
|
+
}, [r, lean, tint, opacity, frost, elevation, fuse, group]);
|
|
1756
|
+
useIsoLayoutEffect(() => {
|
|
1548
1757
|
const node = el.current;
|
|
1549
1758
|
if (!node || !positioned) return;
|
|
1550
1759
|
const apply = () => {
|
|
@@ -1557,18 +1766,18 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1557
1766
|
b();
|
|
1558
1767
|
};
|
|
1559
1768
|
}, [positioned]);
|
|
1560
|
-
const springTo =
|
|
1769
|
+
const springTo = useCallback2((tx, ty, vx = 0, vy = 0) => {
|
|
1561
1770
|
dest.current = { x: tx, y: ty };
|
|
1562
1771
|
anims.current.forEach((a) => a.stop());
|
|
1563
|
-
const { spring
|
|
1564
|
-
if (reducedMotion) {
|
|
1772
|
+
const { spring } = defaultsRef.current;
|
|
1773
|
+
if (runtimeRef.current.reducedMotion) {
|
|
1565
1774
|
x.set(tx);
|
|
1566
1775
|
y.set(ty);
|
|
1567
1776
|
anims.current = [];
|
|
1568
1777
|
return;
|
|
1569
1778
|
}
|
|
1570
|
-
const
|
|
1571
|
-
anims.current = [animateSpring(x, tx, { ...
|
|
1779
|
+
const o = { stiffness: spring.stiffness, damping: spring.damping };
|
|
1780
|
+
anims.current = [animateSpring(x, tx, { ...o, velocity: vx }), animateSpring(y, ty, { ...o, velocity: vy })];
|
|
1572
1781
|
}, []);
|
|
1573
1782
|
useEffect2(() => {
|
|
1574
1783
|
if (!offset) return;
|
|
@@ -1576,15 +1785,26 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1576
1785
|
springTo(offset.x, offset.y);
|
|
1577
1786
|
}, [offset?.x, offset?.y]);
|
|
1578
1787
|
useEffect2(() => () => anims.current.forEach((a) => a.stop()), []);
|
|
1579
|
-
|
|
1580
|
-
|
|
1788
|
+
useEffect2(() => {
|
|
1789
|
+
if (!DEV) return;
|
|
1790
|
+
if (!runtime.renderer && runtime.supported) {
|
|
1791
|
+
console.warn("[plasma-ui] <Plasma> rendered outside a <PlasmaProvider>: it will draw as the plain CSS fallback.");
|
|
1792
|
+
}
|
|
1793
|
+
const s = style;
|
|
1794
|
+
if (positioned && s && ("transform" in s || "translate" in s || "scale" in s)) {
|
|
1795
|
+
console.warn("[plasma-ui] <Plasma> owns transform, translate and scale on its element. A `style` that sets any of them will fight the drag and lean animations.");
|
|
1796
|
+
}
|
|
1797
|
+
}, [runtime.renderer, runtime.supported, positioned, style]);
|
|
1798
|
+
const boundsBox = useCallback2(() => {
|
|
1799
|
+
const b = boundsRef.current?.current?.getBoundingClientRect();
|
|
1581
1800
|
return b ? { l: b.left, t: b.top, w: b.width, h: b.height } : { l: 0, t: 0, w: innerWidth, h: innerHeight };
|
|
1582
|
-
};
|
|
1583
|
-
const settle = (proposed, vx = 0, vy = 0) => {
|
|
1801
|
+
}, []);
|
|
1802
|
+
const settle = useCallback2((proposed, vx = 0, vy = 0) => {
|
|
1584
1803
|
const node = el.current, h = handle.current;
|
|
1585
|
-
const { grid, magnet
|
|
1804
|
+
const { grid, magnet } = defaultsRef.current;
|
|
1805
|
+
const { renderer } = runtimeRef.current;
|
|
1586
1806
|
let target = proposed;
|
|
1587
|
-
if (
|
|
1807
|
+
if (snapRef.current && h && renderer) {
|
|
1588
1808
|
const rect = node.getBoundingClientRect();
|
|
1589
1809
|
const w = node.offsetWidth, hh = node.offsetHeight;
|
|
1590
1810
|
const lo = h.leanOffset();
|
|
@@ -1592,15 +1812,15 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1592
1812
|
const baseT = rect.top + (rect.height - hh) / 2 - lo.y - y.get();
|
|
1593
1813
|
const s = snapBox(
|
|
1594
1814
|
{ l: baseL + proposed.x, t: baseT + proposed.y, w, h: hh },
|
|
1595
|
-
renderer.layoutBoxes(h.id, true),
|
|
1596
|
-
{ grid, magnet, bounds: boundsBox(), inset:
|
|
1815
|
+
renderer.layoutBoxes(h.id, true, optsRef.current.group ?? null),
|
|
1816
|
+
{ grid, magnet, bounds: boundsBox(), inset: boundsRef.current ? 0 : 8 }
|
|
1597
1817
|
);
|
|
1598
1818
|
target = { x: s.l - baseL, y: s.t - baseT };
|
|
1599
1819
|
}
|
|
1600
1820
|
springTo(target.x, target.y, vx, vy);
|
|
1601
|
-
|
|
1602
|
-
};
|
|
1603
|
-
const handlePointerDown = (e) => {
|
|
1821
|
+
onDragEndRef.current?.(target);
|
|
1822
|
+
}, []);
|
|
1823
|
+
const handlePointerDown = useCallback2((e) => {
|
|
1604
1824
|
onPointerDown?.(e);
|
|
1605
1825
|
if (!draggable || e.defaultPrevented || e.button !== 0) return;
|
|
1606
1826
|
if (e.target.closest(NO_DRAG)) return;
|
|
@@ -1618,12 +1838,12 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1618
1838
|
const clamp = (v, a, b) => Math.min(Math.max(v, a), Math.max(a, b));
|
|
1619
1839
|
handle.current?.setDragging(true);
|
|
1620
1840
|
setDragging(true);
|
|
1621
|
-
|
|
1841
|
+
onDragStartRef.current?.();
|
|
1622
1842
|
const move = (ev) => {
|
|
1623
1843
|
x.set(clamp(start.x + ev.clientX - start.px, minX - 40, maxX + 40));
|
|
1624
1844
|
y.set(clamp(start.y + ev.clientY - start.py, minY - 40, maxY + 40));
|
|
1625
1845
|
dest.current = { x: x.get(), y: y.get() };
|
|
1626
|
-
|
|
1846
|
+
runtimeRef.current.bump(Math.hypot(x.getVelocity(), y.getVelocity()) / 1800);
|
|
1627
1847
|
};
|
|
1628
1848
|
const up = () => {
|
|
1629
1849
|
node.removeEventListener("pointermove", move);
|
|
@@ -1637,17 +1857,17 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1637
1857
|
node.addEventListener("pointermove", move);
|
|
1638
1858
|
node.addEventListener("pointerup", up);
|
|
1639
1859
|
node.addEventListener("pointercancel", up);
|
|
1640
|
-
};
|
|
1641
|
-
const handleKeyDown = (e) => {
|
|
1860
|
+
}, [draggable, onPointerDown]);
|
|
1861
|
+
const handleKeyDown = useCallback2((e) => {
|
|
1642
1862
|
onKeyDown?.(e);
|
|
1643
1863
|
if (!draggable || e.defaultPrevented || e.target !== el.current) return;
|
|
1644
|
-
const step =
|
|
1864
|
+
const step = defaultsRef.current.grid || 24;
|
|
1645
1865
|
const d = { ArrowLeft: [-step, 0], ArrowRight: [step, 0], ArrowUp: [0, -step], ArrowDown: [0, step] }[e.key];
|
|
1646
1866
|
if (!d) return;
|
|
1647
1867
|
e.preventDefault();
|
|
1648
1868
|
settle({ x: dest.current.x + d[0], y: dest.current.y + d[1] });
|
|
1649
|
-
};
|
|
1650
|
-
const classes = ["plasma-panel",
|
|
1869
|
+
}, [draggable, onKeyDown]);
|
|
1870
|
+
const classes = ["plasma-panel", runtime.supported ? "" : "plasma-fallback", className].filter(Boolean).join(" ");
|
|
1651
1871
|
return /* @__PURE__ */ jsx2(
|
|
1652
1872
|
Comp,
|
|
1653
1873
|
{
|
|
@@ -1659,7 +1879,7 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1659
1879
|
padding: `${sides.top ? padding / 2 : padding}px ${sides.right ? padding / 2 : padding}px ${sides.bottom ? padding / 2 : padding}px ${sides.left ? padding / 2 : padding}px`,
|
|
1660
1880
|
transition: "padding 250ms ease"
|
|
1661
1881
|
},
|
|
1662
|
-
...
|
|
1882
|
+
...runtime.supported ? null : fallbackTint(tint ?? defaults.tint, opacity ?? defaults.opacity),
|
|
1663
1883
|
...style
|
|
1664
1884
|
},
|
|
1665
1885
|
"data-plasma-draggable": draggable || void 0,
|
|
@@ -1672,9 +1892,11 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1672
1892
|
}
|
|
1673
1893
|
);
|
|
1674
1894
|
});
|
|
1895
|
+
var Plasma2 = PlasmaInner;
|
|
1675
1896
|
export {
|
|
1676
1897
|
DEFAULT_MAX_SHAPES,
|
|
1677
|
-
Plasma,
|
|
1898
|
+
Plasma2 as Plasma,
|
|
1899
|
+
PlasmaCanvas,
|
|
1678
1900
|
PlasmaProvider,
|
|
1679
1901
|
PlasmaRenderer,
|
|
1680
1902
|
boxGap,
|
|
@@ -1682,5 +1904,7 @@ export {
|
|
|
1682
1904
|
moods,
|
|
1683
1905
|
resolveMood,
|
|
1684
1906
|
snapBox,
|
|
1685
|
-
usePlasma
|
|
1907
|
+
usePlasma,
|
|
1908
|
+
usePlasmaDefaults,
|
|
1909
|
+
usePlasmaRuntime
|
|
1686
1910
|
};
|