liqvued 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -60,12 +60,13 @@ import { Liqvued } from 'liqvued'
60
60
  | bezel | number | 22 | Width of the displacement bezel in pixels |
61
61
  | thickness | number | 42 | Maximum displacement magnitude in pixels |
62
62
  | refraction | number | 1 | Refraction index multiplier |
63
+ | refractionMode | string | `'edge'` | Refraction direction mode: `edge`, `center`, or `split` |
63
64
  | magnification | number | 0 | Center magnification amount. Positive values enlarge, negative values shrink |
64
65
  | magnificationFocus | number | 0.82 | Focus radius of the magnification area. Lower values concentrate the effect near center |
65
66
  | blur | number | 0.4 | CSS backdrop-filter blur amount |
66
67
  | surface | string | `'convex'` | Glass surface profile shape |
67
68
  | specularOpacity | number | 0.45 | Opacity of the specular highlight |
68
- | glareAngle | number | -60 | Light source angle in degrees for specular highlight |
69
+ | glareAngle | number \| false | -60 | Light source angle in degrees for specular highlight. Set `false` to disable |
69
70
  | glassBackground | string | — | Glass panel background color (auto-derived from `asProps.color` when available) |
70
71
  | fallbackOnly | boolean | `false` | When `true`, disables the SVG displacement effect and uses only CSS blur |
71
72
  | as | string \| Component | `'div'` | HTML tag or Vue component to render as (rendered as a child of the glass root) |
@@ -83,6 +84,12 @@ import { Liqvued } from 'liqvued'
83
84
  - `noise` — irregular organic edge distortion
84
85
  - `asymmetric` — biased profile with more weight toward one side of the edge band
85
86
 
87
+ ### Refraction Modes
88
+
89
+ - `edge` — original behavior; displacement follows the rounded edge normal
90
+ - `center` — displacement points inward from each edge toward the shape centerline
91
+ - `split` — outer and inner bezel halves bend in opposite directions with a neutral midpoint
92
+
86
93
  ## Dynamic Element Rendering
87
94
 
88
95
  Use the `as` prop to render the glass effect on any element or component:
@@ -106,7 +113,7 @@ The fallback applies `backdrop-filter: blur(12px)` with a subtle border and shad
106
113
 
107
114
  ## How It Works
108
115
 
109
- The component generates a per-pixel displacement map as an RGBA PNG (red/green channels encode X/Y displacement vectors), injects it into an SVG `<feDisplacementMap>` filter, and applies it via `backdrop-filter` combined with CSS blur. The surface profile functions define the edge refraction slope along the bezel, while `magnification` and `magnificationFocus` control an optional center lens effect. A separate specular highlight map is generated and composited via `feBlend` to simulate surface lighting.
116
+ The component generates a per-pixel displacement map as an RGBA PNG (red/green channels encode X/Y displacement vectors), injects it into an SVG `<feDisplacementMap>` filter, and applies it via `backdrop-filter` combined with CSS blur. The surface profile functions define the edge refraction strength along the bezel, while `refractionMode` controls the displacement direction. `magnification` and `magnificationFocus` control an optional center lens effect. A separate specular highlight map is generated and composited via `feBlend` to simulate surface lighting.
110
117
 
111
118
  ## License
112
119
 
package/dist/liqvued.css CHANGED
@@ -1,2 +1,2 @@
1
- .liquid-glass[data-v-56a7fa97]{isolation:isolate;position:relative;overflow:hidden}.liquid-glass__svg[data-v-56a7fa97]{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}@supports not ((-webkit-backdrop-filter:url("#x")) or (backdrop-filter:url("#x"))){.liquid-glass[data-v-56a7fa97]{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}}
1
+ .liquid-glass[data-v-5607a45c]{isolation:isolate;position:relative;overflow:hidden}.liquid-glass__svg[data-v-5607a45c]{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}@supports not ((-webkit-backdrop-filter:url("#x")) or (backdrop-filter:url("#x"))){.liquid-glass[data-v-5607a45c]{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}}
2
2
  /*$vite$:1*/
package/dist/liqvued.mjs CHANGED
@@ -1,56 +1,78 @@
1
- import { computed as C, createBlock as he, createCommentVNode as fe, createElementBlock as Z, createElementVNode as k, defineComponent as pe, guardReactiveProps as ve, inject as ge, nextTick as ee, normalizeProps as me, normalizeStyle as Me, onBeforeUnmount as be, onMounted as ye, openBlock as W, provide as xe, ref as w, renderSlot as _e, resolveDynamicComponent as ke, unref as we, watch as Ie, withCtx as Re } from "vue";
2
- function q(r, a, s, o, l) {
3
- const b = Math.min(l, Math.min(s, o) / 2), y = s / 2 - b, x = o / 2 - b, c = Math.abs(r - s / 2) - y, S = Math.abs(a - o / 2) - x;
4
- return -(Math.min(Math.max(c, S), 0) + Math.hypot(Math.max(c, 0), Math.max(S, 0)) - b);
1
+ import { computed as C, createBlock as he, createCommentVNode as pe, createElementBlock as ee, createElementVNode as _, defineComponent as ve, guardReactiveProps as ge, inject as me, nextTick as te, normalizeProps as Me, normalizeStyle as ye, onBeforeUnmount as be, onMounted as xe, openBlock as N, provide as _e, ref as k, renderSlot as ke, resolveDynamicComponent as we, unref as Se, watch as Be, withCtx as Ie } from "vue";
2
+ function Re(n, t, i, r, c, y, g) {
3
+ if (n === "center") return t;
4
+ const h = {
5
+ x: -t.x,
6
+ y: -t.y
7
+ };
8
+ if (n === "split") {
9
+ const o = g < 0.5 ? -1 : 1;
10
+ return {
11
+ x: h.x * o,
12
+ y: h.y * o
13
+ };
14
+ }
15
+ return h;
16
+ }
17
+ function $e(n, t) {
18
+ return n !== "split" ? 1 : Math.min(1, Math.abs(t - 0.5) * 2);
19
+ }
20
+ function q(n, t, i, r, c) {
21
+ const y = Math.min(c, Math.min(i, r) / 2), g = i / 2 - y, h = r / 2 - y, o = Math.abs(n - i / 2) - g, I = Math.abs(t - r / 2) - h;
22
+ return -(Math.min(Math.max(o, I), 0) + Math.hypot(Math.max(o, 0), Math.max(I, 0)) - y);
5
23
  }
6
- function Se(r, a, s, o, l) {
7
- const y = q(r - 1.5, a, s, o, l) - q(r + 1.5, a, s, o, l), x = q(r, a - 1.5, s, o, l) - q(r, a + 1.5, s, o, l), c = Math.hypot(y, x);
8
- return c < 1e-10 ? {
24
+ function Ce(n, t, i, r, c) {
25
+ const g = q(n - 1.5, t, i, r, c) - q(n + 1.5, t, i, r, c), h = q(n, t - 1.5, i, r, c) - q(n, t + 1.5, i, r, c), o = Math.hypot(g, h);
26
+ return o < 1e-10 ? {
9
27
  x: 0,
10
28
  y: -1
11
29
  } : {
12
- x: y / c,
13
- y: x / c
30
+ x: g / o,
31
+ y: h / o
14
32
  };
15
33
  }
16
- var Be = {
34
+ var qe = {
17
35
  key: 0,
18
36
  class: "liquid-glass__svg",
19
37
  "aria-hidden": "true"
20
- }, $e = [
38
+ }, Pe = [
21
39
  "href",
22
40
  "width",
23
41
  "height"
24
- ], Ce = ["scale"], qe = [
42
+ ], Fe = ["scale"], Oe = [
25
43
  "href",
26
44
  "width",
27
45
  "height"
28
- ], Pe = /* @__PURE__ */ pe({
46
+ ], De = /* @__PURE__ */ ve({
29
47
  __name: "Liqvued",
30
48
  props: {
31
49
  as: { default: "div" },
32
50
  asProps: { default: () => ({}) },
33
51
  radius: { default: 32 },
34
- borderRadius: {},
52
+ borderRadius: { default: void 0 },
35
53
  bezel: { default: 22 },
36
54
  thickness: { default: 42 },
37
55
  refraction: { default: 1 },
56
+ refractionMode: { default: "edge" },
38
57
  magnification: { default: 0 },
39
58
  magnificationFocus: { default: 0.82 },
40
59
  blur: { default: 0.4 },
41
60
  surface: { default: "convex" },
42
61
  specularOpacity: { default: 0.45 },
43
- glareAngle: { default: -60 },
62
+ glareAngle: {
63
+ type: [Number, Boolean],
64
+ default: -60
65
+ },
44
66
  glassBackground: { default: void 0 },
45
67
  fallbackOnly: {
46
68
  type: Boolean,
47
69
  default: !1
48
70
  }
49
71
  },
50
- setup(r) {
51
- const a = r, s = w(null), o = w(1), l = w(1), b = w(""), y = w(""), x = w(1), c = typeof CSS < "u" && typeof navigator < "u" && CSS.supports?.("backdrop-filter", "url(#x)") && /Chrome/.test(navigator.userAgent), S = `${`lg-${Math.random().toString(36).slice(2)}`}-filter`, N = ge("_liqvued", !1);
52
- xe("_liqvued", !0);
53
- const I = w(!1), V = {
72
+ setup(n) {
73
+ const t = n, i = k(null), r = k(1), c = k(1), y = k(""), g = k(""), h = k(1), o = typeof CSS < "u" && typeof navigator < "u" && CSS.supports?.("backdrop-filter", "url(#x)") && /Chrome/.test(navigator.userAgent), I = `${`lg-${Math.random().toString(36).slice(2)}`}-filter`, W = me("_liqvued", !1);
74
+ _e("_liqvued", !0);
75
+ const w = k(!1), V = {
54
76
  0: 0,
55
77
  sm: 4,
56
78
  md: 8,
@@ -61,106 +83,109 @@ var Be = {
61
83
  shaped: 16
62
84
  };
63
85
  function j() {
64
- return s.value;
86
+ return i.value;
65
87
  }
66
- function i(e, t = 0, n = 1) {
67
- return Math.max(t, Math.min(n, e));
88
+ function u(e, a = 0, s = 1) {
89
+ return Math.max(a, Math.min(s, e));
68
90
  }
69
- function B(e) {
70
- return e = i(e), e * e * e * (e * (e * 6 - 15) + 10);
91
+ function R(e) {
92
+ return e = u(e), e * e * e * (e * (e * 6 - 15) + 10);
71
93
  }
72
94
  function P(e) {
73
- return Math.pow(1 - Math.pow(1 - i(e), 4), 1 / 4);
95
+ return Math.pow(1 - Math.pow(1 - u(e), 4), 1 / 4);
74
96
  }
75
- function te(e, t, n) {
76
- return i(e + Math.sin(i(e) * Math.PI * t) * n);
97
+ function ae(e, a, s) {
98
+ return u(e + Math.sin(u(e) * Math.PI * a) * s);
77
99
  }
78
- function ae(e) {
79
- const t = i(e);
80
- return i(t + Math.sin(t * 39.1) * 0.06 + Math.sin(t * 91.7) * 0.035);
100
+ function ne(e) {
101
+ const a = u(e);
102
+ return u(a + Math.sin(a * 39.1) * 0.06 + Math.sin(a * 91.7) * 0.035);
81
103
  }
82
- function ne(e, t, n, f, m, R) {
83
- const v = n / 2, g = f / 2, $ = Math.max(1, n / 2), M = Math.max(1, f / 2), z = (e - v) / $, D = (t - g) / M, u = Math.hypot(z, D), d = i(R, 0.2, 1.4), h = Math.pow(B(1 - i(u / d)), 1.6), p = m >= 0 ? 1 + m * h : 1 / (1 + Math.abs(m) * h);
104
+ function re(e, a, s, v, b, S) {
105
+ const m = s / 2, M = v / 2, $ = Math.max(1, s / 2), x = Math.max(1, v / 2), D = (e - m) / $, z = (a - M) / x, l = Math.hypot(D, z), d = u(S, 0.2, 1.4), p = Math.pow(R(1 - u(l / d)), 1.6), f = b >= 0 ? 1 + b * p : 1 / (1 + Math.abs(b) * p);
84
106
  return {
85
- x: (e - v) / p - (e - v),
86
- y: (t - g) / p - (t - g)
107
+ x: (e - m) / f - (e - m),
108
+ y: (a - M) / f - (a - M)
87
109
  };
88
110
  }
89
111
  function H(e) {
90
- const t = i(e);
91
- switch (a.surface) {
112
+ const a = u(e);
113
+ switch (t.surface) {
92
114
  case "concave":
93
115
  case "bowl":
94
- return 1 - P(t);
116
+ return 1 - P(a);
95
117
  case "lip":
96
- return P(t) * (1 - B(t)) + (1 - P(t)) * B(t);
118
+ return P(a) * (1 - R(a)) + (1 - P(a)) * R(a);
97
119
  case "bevel":
98
- return t;
120
+ return a;
99
121
  case "saddle":
100
- return 0.5 + Math.sin((t - 0.5) * Math.PI) * 0.5;
122
+ return 0.5 + Math.sin((a - 0.5) * Math.PI) * 0.5;
101
123
  case "ripple":
102
- return te(t, 3, 0.12);
124
+ return ae(a, 3, 0.12);
103
125
  case "noise":
104
- return ae(t);
126
+ return ne(a);
105
127
  case "asymmetric":
106
- return i(Math.pow(t, 0.65) * 0.65 + B(t) * 0.35);
128
+ return u(Math.pow(a, 0.65) * 0.65 + R(a) * 0.35);
107
129
  case "convex":
108
- return P(t);
130
+ return P(a);
109
131
  }
110
132
  }
111
- function re(e) {
133
+ function oe(e) {
112
134
  return (H(e + 1e-3) - H(e - 1e-3)) / (2 * 1e-3);
113
135
  }
114
- function oe(e) {
115
- const f = Math.atan(e), m = 1 / 1.5 * Math.sin(f), R = Math.asin(i(m, -1, 1));
116
- return Math.tan(f - R);
136
+ function se(e) {
137
+ const v = Math.atan(e), b = 1 / 1.5 * Math.sin(v), S = Math.asin(u(b, -1, 1));
138
+ return Math.tan(v - S);
117
139
  }
118
140
  function J(e) {
119
141
  return e.toDataURL("image/png");
120
142
  }
121
- function T() {
122
- if (a.fallbackOnly) return;
123
- const e = Math.max(1, Math.round(o.value)), t = Math.max(1, Math.round(l.value)), n = document.createElement("canvas");
124
- n.width = e, n.height = t;
125
- const f = document.createElement("canvas");
126
- f.width = e, f.height = t;
127
- const m = n.getContext("2d"), R = f.getContext("2d"), v = m.createImageData(e, t), g = R.createImageData(e, t), $ = [];
128
- let M = 1;
129
- const z = {
130
- x: Math.cos(a.glareAngle * Math.PI / 180),
131
- y: Math.sin(a.glareAngle * Math.PI / 180)
132
- }, D = Math.max(1, Math.min(a.bezel, Math.min(e, t) * 0.28));
133
- for (let u = 0; u < t; u++) for (let d = 0; d < e; d++) {
134
- const h = q(d, u, e, t, F.value), p = i(h / D);
143
+ function A() {
144
+ if (t.fallbackOnly) return;
145
+ const e = Math.max(1, Math.round(r.value)), a = Math.max(1, Math.round(c.value)), s = document.createElement("canvas");
146
+ s.width = e, s.height = a;
147
+ const v = document.createElement("canvas");
148
+ v.width = e, v.height = a;
149
+ const b = s.getContext("2d"), S = v.getContext("2d"), m = b.createImageData(e, a), M = S.createImageData(e, a), $ = [];
150
+ let x = 1;
151
+ const D = t.glareAngle !== !1 ? {
152
+ x: Math.cos(t.glareAngle * Math.PI / 180),
153
+ y: Math.sin(t.glareAngle * Math.PI / 180)
154
+ } : {
155
+ x: 0,
156
+ y: 0
157
+ }, z = Math.max(1, Math.min(t.bezel, Math.min(e, a) * 0.28));
158
+ for (let l = 0; l < a; l++) for (let d = 0; d < e; d++) {
159
+ const p = q(d, l, e, a, F.value), f = u(p / z);
135
160
  let L = 0, E = 0, X = 0;
136
- if (h >= 0 && h < D) {
137
- const _ = Se(d, u, e, t, F.value), Y = oe(re(p)) * a.thickness * B(1 - p) * a.refraction;
138
- L = -_.x * Y, E = -_.y * Y, M = Math.max(M, Math.abs(L), Math.abs(E));
139
- const de = i(_.x * z.x + _.y * z.y, 0, 1);
140
- X = Math.pow(de, 18) * a.specularOpacity;
161
+ if (p >= 0 && p < z) {
162
+ const B = Ce(d, l, e, a, F.value), Y = se(oe(f)) * t.thickness * R(1 - f) * $e(t.refractionMode, f) * t.refraction, Z = Re(t.refractionMode, B, d, l, e, a, f);
163
+ L = Z.x * Y, E = Z.y * Y, x = Math.max(x, Math.abs(L), Math.abs(E));
164
+ const fe = u(B.x * D.x + B.y * D.y, 0, 1);
165
+ X = Math.pow(fe, 18) * t.specularOpacity;
141
166
  }
142
- if (a.magnification !== 0 && h >= 0) {
143
- const _ = ne(d, u, e, t, a.magnification, a.magnificationFocus);
144
- L += _.x, E += _.y;
167
+ if (t.magnification !== 0 && p >= 0) {
168
+ const B = re(d, l, e, a, t.magnification, t.magnificationFocus);
169
+ L += B.x, E += B.y;
145
170
  }
146
171
  $.push([L, E]);
147
- const U = (u * e + d) * 4, ue = Math.round(X * 255);
148
- g.data[U] = 255, g.data[U + 1] = 255, g.data[U + 2] = 255, g.data[U + 3] = ue;
172
+ const U = (l * e + d) * 4, de = Math.round(X * 255);
173
+ M.data[U] = 255, M.data[U + 1] = 255, M.data[U + 2] = 255, M.data[U + 3] = de;
149
174
  }
150
- M = $.reduce((u, [d, h]) => Math.max(u, Math.abs(d), Math.abs(h)), 1), $.forEach(([u, d], h) => {
151
- const p = h * 4;
152
- v.data[p] = Math.round(128 + u / M * 127), v.data[p + 1] = Math.round(128 + d / M * 127), v.data[p + 2] = 128, v.data[p + 3] = 255;
153
- }), m.putImageData(v, 0, 0), R.putImageData(g, 0, 0), b.value = J(n), y.value = J(f), x.value = M;
175
+ x = $.reduce((l, [d, p]) => Math.max(l, Math.abs(d), Math.abs(p)), 1), $.forEach(([l, d], p) => {
176
+ const f = p * 4;
177
+ m.data[f] = Math.round(128 + l / x * 127), m.data[f + 1] = Math.round(128 + d / x * 127), m.data[f + 2] = 128, m.data[f + 3] = 255;
178
+ }), b.putImageData(m, 0, 0), S.putImageData(M, 0, 0), y.value = J(s), g.value = J(v), h.value = x;
154
179
  }
155
180
  function K() {
156
181
  const e = j();
157
- e && (o.value = Math.max(1, e.offsetWidth), l.value = Math.max(1, e.offsetHeight));
182
+ e && (r.value = Math.max(1, e.offsetWidth), c.value = Math.max(1, e.offsetHeight));
158
183
  }
159
- const Q = c ? "rgba(255, 255, 255, 0.08)" : "rgba(255, 255, 255, 0.22)", F = C(() => {
160
- if (a.radius !== 32) return a.radius;
161
- const e = a.asProps;
162
- return typeof e.rounded == "string" && e.rounded in V ? V[e.rounded] : a.radius;
163
- }), se = C(() => a.borderRadius || `${F.value}px`), le = /* @__PURE__ */ new Set([
184
+ const Q = o ? "rgba(255, 255, 255, 0.08)" : "rgba(255, 255, 255, 0.22)", F = C(() => {
185
+ if (t.radius !== 32) return t.radius;
186
+ const e = t.asProps;
187
+ return typeof e.rounded == "string" && e.rounded in V ? V[e.rounded] : t.radius;
188
+ }), ie = C(() => t.borderRadius || `${F.value}px`), ce = /* @__PURE__ */ new Set([
164
189
  "primary",
165
190
  "secondary",
166
191
  "accent",
@@ -171,62 +196,63 @@ var Be = {
171
196
  "surface",
172
197
  "background"
173
198
  ]);
174
- function ce(e, t) {
175
- if (le.has(e)) return `rgba(var(--v-theme-${e}), ${t})`;
199
+ function ue(e, a) {
200
+ if (ce.has(e)) return `rgba(var(--v-theme-${e}), ${a})`;
176
201
  if (e.startsWith("#")) {
177
- const n = e.replace("#", "");
178
- return `rgba(${parseInt(n.slice(0, 2), 16)}, ${parseInt(n.slice(2, 4), 16)}, ${parseInt(n.slice(4, 6), 16)}, ${t})`;
202
+ const s = e.replace("#", "");
203
+ return `rgba(${parseInt(s.slice(0, 2), 16)}, ${parseInt(s.slice(2, 4), 16)}, ${parseInt(s.slice(4, 6), 16)}, ${a})`;
179
204
  }
180
- return e.startsWith("rgb") ? e.replace("rgb(", "rgba(").replace(")", `, ${t})`) : Q;
205
+ return e.startsWith("rgb") ? e.replace("rgb(", "rgba(").replace(")", `, ${a})`) : Q;
181
206
  }
182
- const A = C(() => {
183
- if (a.glassBackground !== void 0) return a.glassBackground;
184
- const e = a.asProps;
185
- return typeof e.color == "string" ? ce(e.color, 0.6) : Q;
186
- }), O = C(() => a.fallbackOnly || !c ? "blur(12px)" : `url(#${S}) blur(${a.blur}px)`), ie = C(() => ({
187
- borderRadius: `${se.value}`,
188
- ...A.value !== void 0 ? { backgroundColor: A.value } : {},
189
- ...A.value !== void 0 ? { boxShadow: ["inset 0 0 0 1px rgba(255, 255, 255, 0.28)", "0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ") } : {},
190
- ...I.value && !N ? {
207
+ const T = C(() => {
208
+ if (t.glassBackground !== void 0) return t.glassBackground;
209
+ const e = t.asProps;
210
+ return typeof e.color == "string" ? ue(e.color, 0.6) : Q;
211
+ }), O = C(() => t.fallbackOnly || !o ? "blur(12px)" : `url(#${I}) blur(${t.blur}px)`), le = C(() => ({
212
+ borderRadius: `${ie.value}`,
213
+ ...T.value !== void 0 ? { backgroundColor: T.value } : {},
214
+ ...T.value !== void 0 ? { boxShadow: ["inset 0 0 0 1px rgba(255, 255, 255, 0.28)", "0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ") } : {},
215
+ ...w.value && !W ? {
191
216
  backdropFilter: O.value,
192
217
  WebkitBackdropFilter: O.value
193
218
  } : {},
194
- ...I.value && N && !c ? {
219
+ ...w.value && W && !o ? {
195
220
  backdropFilter: O.value,
196
221
  WebkitBackdropFilter: O.value
197
222
  } : {}
198
223
  }));
199
224
  let G = null;
200
- return ye(async () => {
201
- if (await ee(), c) {
202
- K(), T(), await ee(), I.value = !0, G = new ResizeObserver(() => {
203
- K(), T(), I.value = !0;
225
+ return xe(async () => {
226
+ if (await te(), o) {
227
+ K(), A(), await te(), w.value = !0, G = new ResizeObserver(() => {
228
+ K(), A(), w.value = !0;
204
229
  });
205
230
  const e = j();
206
231
  e && G.observe(e);
207
- } else I.value = !0;
208
- }), Ie(() => [
232
+ } else w.value = !0;
233
+ }), Be(() => [
209
234
  F.value,
210
- a.bezel,
211
- a.thickness,
212
- a.refraction,
213
- a.magnification,
214
- a.magnificationFocus,
215
- a.surface,
216
- a.specularOpacity,
217
- a.glareAngle,
218
- a.fallbackOnly
235
+ t.bezel,
236
+ t.thickness,
237
+ t.refraction,
238
+ t.refractionMode,
239
+ t.magnification,
240
+ t.magnificationFocus,
241
+ t.surface,
242
+ t.specularOpacity,
243
+ t.glareAngle,
244
+ t.fallbackOnly
219
245
  ], () => {
220
- c && T(), I.value = !0;
246
+ o && A(), w.value = !0;
221
247
  }), be(() => {
222
248
  G?.disconnect();
223
- }), (e, t) => (W(), Z("div", {
249
+ }), (e, a) => (N(), ee("div", {
224
250
  ref_key: "root",
225
- ref: s,
251
+ ref: i,
226
252
  class: "liquid-glass",
227
- style: Me(ie.value)
228
- }, [we(c) ? (W(), Z("svg", Be, [k("defs", null, [k("filter", {
229
- id: S,
253
+ style: ye(le.value)
254
+ }, [Se(o) ? (N(), ee("svg", qe, [_("defs", null, [_("filter", {
255
+ id: I,
230
256
  "color-interpolation-filters": "sRGB",
231
257
  filterUnits: "userSpaceOnUse",
232
258
  x: "0",
@@ -234,53 +260,53 @@ var Be = {
234
260
  width: "10000",
235
261
  height: "10000"
236
262
  }, [
237
- k("feImage", {
238
- href: b.value,
263
+ _("feImage", {
264
+ href: y.value,
239
265
  x: "0",
240
266
  y: "0",
241
- width: o.value,
242
- height: l.value,
267
+ width: r.value,
268
+ height: c.value,
243
269
  result: "displacement_map"
244
- }, null, 8, $e),
245
- t[0] || (t[0] = k("feGaussianBlur", {
270
+ }, null, 8, Pe),
271
+ a[0] || (a[0] = _("feGaussianBlur", {
246
272
  in: "displacement_map",
247
273
  stdDeviation: "2",
248
274
  result: "smooth_disp"
249
275
  }, null, -1)),
250
- k("feDisplacementMap", {
276
+ _("feDisplacementMap", {
251
277
  in: "SourceGraphic",
252
278
  in2: "smooth_disp",
253
- scale: x.value,
279
+ scale: h.value,
254
280
  xChannelSelector: "R",
255
281
  yChannelSelector: "G",
256
282
  result: "refracted"
257
- }, null, 8, Ce),
258
- k("feImage", {
259
- href: y.value,
283
+ }, null, 8, Fe),
284
+ _("feImage", {
285
+ href: g.value,
260
286
  x: "0",
261
287
  y: "0",
262
- width: o.value,
263
- height: l.value,
288
+ width: r.value,
289
+ height: c.value,
264
290
  result: "specular"
265
- }, null, 8, qe),
266
- t[1] || (t[1] = k("feBlend", {
291
+ }, null, 8, Oe),
292
+ a[1] || (a[1] = _("feBlend", {
267
293
  in: "refracted",
268
294
  in2: "specular",
269
295
  mode: "screen"
270
296
  }, null, -1))
271
- ])])])) : fe("", !0), (W(), he(ke(r.as), me(ve(r.asProps)), {
272
- default: Re(() => [_e(e.$slots, "default", {}, void 0, !0)]),
297
+ ])])])) : pe("", !0), (N(), he(we(n.as), Me(ge(n.asProps)), {
298
+ default: Ie(() => [ke(e.$slots, "default", {}, void 0, !0)]),
273
299
  _: 3
274
300
  }, 16))], 4));
275
301
  }
276
- }), Fe = (r, a) => {
277
- const s = r.__vccOpts || r;
278
- for (const [o, l] of a) s[o] = l;
279
- return s;
280
- }, Oe = /* @__PURE__ */ Fe(Pe, [["__scopeId", "data-v-56a7fa97"]]), De = { install(r) {
281
- r.component("Liqvued", Oe);
302
+ }), ze = (n, t) => {
303
+ const i = n.__vccOpts || n;
304
+ for (const [r, c] of t) i[r] = c;
305
+ return i;
306
+ }, Le = /* @__PURE__ */ ze(De, [["__scopeId", "data-v-5607a45c"]]), Ue = { install(n) {
307
+ n.component("Liqvued", Le);
282
308
  } };
283
309
  export {
284
- Oe as Liqvued,
285
- De as default
310
+ Le as Liqvued,
311
+ Ue as default
286
312
  };
@@ -1 +1 @@
1
- (function(v,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(v=typeof globalThis<"u"?globalThis:v||self,t(v.Liqvued={},v.Vue))})(this,function(v,t){Object.defineProperties(v,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function I(o,n,c,s,l){const x=Math.min(l,Math.min(c,s)/2),_=c/2-x,k=s/2-x,i=Math.abs(o-c/2)-_,R=Math.abs(n-s/2)-k;return-(Math.min(Math.max(i,R),0)+Math.hypot(Math.max(i,0),Math.max(R,0))-x)}function Y(o,n,c,s,l){const _=I(o-1.5,n,c,s,l)-I(o+1.5,n,c,s,l),k=I(o,n-1.5,c,s,l)-I(o,n+1.5,c,s,l),i=Math.hypot(_,k);return i<1e-10?{x:0,y:-1}:{x:_/i,y:k/i}}var Z={key:0,class:"liquid-glass__svg","aria-hidden":"true"},ee=["href","width","height"],te=["scale"],ae=["href","width","height"],ne=(0,t.defineComponent)({__name:"Liqvued",props:{as:{default:"div"},asProps:{default:()=>({})},radius:{default:32},borderRadius:{},bezel:{default:22},thickness:{default:42},refraction:{default:1},magnification:{default:0},magnificationFocus:{default:.82},blur:{default:.4},surface:{default:"convex"},specularOpacity:{default:.45},glareAngle:{default:-60},glassBackground:{default:void 0},fallbackOnly:{type:Boolean,default:!1}},setup(o){const n=o,c=(0,t.ref)(null),s=(0,t.ref)(1),l=(0,t.ref)(1),x=(0,t.ref)(""),_=(0,t.ref)(""),k=(0,t.ref)(1),i=typeof CSS<"u"&&typeof navigator<"u"&&CSS.supports?.("backdrop-filter","url(#x)")&&/Chrome/.test(navigator.userAgent),R=`${`lg-${Math.random().toString(36).slice(2)}`}-filter`,A=(0,t.inject)("_liqvued",!1);(0,t.provide)("_liqvued",!0);const B=(0,t.ref)(!1),G={0:0,sm:4,md:8,lg:12,xl:16,pill:9999,circle:9999,shaped:16};function W(){return c.value}function u(e,a=0,r=1){return Math.max(a,Math.min(r,e))}function E(e){return e=u(e),e*e*e*(e*(e*6-15)+10)}function q(e){return Math.pow(1-Math.pow(1-u(e),4),1/4)}function se(e,a,r){return u(e+Math.sin(u(e)*Math.PI*a)*r)}function ce(e){const a=u(e);return u(a+Math.sin(a*39.1)*.06+Math.sin(a*91.7)*.035)}function le(e,a,r,p,b,S){const g=r/2,M=p/2,$=Math.max(1,r/2),y=Math.max(1,p/2),O=(e-g)/$,V=(a-M)/y,d=Math.hypot(O,V),f=u(S,.2,1.4),h=Math.pow(E(1-u(d/f)),1.6),m=b>=0?1+b*h:1/(1+Math.abs(b)*h);return{x:(e-g)/m-(e-g),y:(a-M)/m-(a-M)}}function j(e){const a=u(e);switch(n.surface){case"concave":case"bowl":return 1-q(a);case"lip":return q(a)*(1-E(a))+(1-q(a))*E(a);case"bevel":return a;case"saddle":return .5+Math.sin((a-.5)*Math.PI)*.5;case"ripple":return se(a,3,.12);case"noise":return ce(a);case"asymmetric":return u(Math.pow(a,.65)*.65+E(a)*.35);case"convex":return q(a)}}function ie(e){return(j(e+.001)-j(e-.001))/(2*.001)}function ue(e){const p=Math.atan(e),b=1/1.5*Math.sin(p),S=Math.asin(u(b,-1,1));return Math.tan(p-S)}function H(e){return e.toDataURL("image/png")}function L(){if(n.fallbackOnly)return;const e=Math.max(1,Math.round(s.value)),a=Math.max(1,Math.round(l.value)),r=document.createElement("canvas");r.width=e,r.height=a;const p=document.createElement("canvas");p.width=e,p.height=a;const b=r.getContext("2d"),S=p.getContext("2d"),g=b.createImageData(e,a),M=S.createImageData(e,a),$=[];let y=1;const O={x:Math.cos(n.glareAngle*Math.PI/180),y:Math.sin(n.glareAngle*Math.PI/180)},V=Math.max(1,Math.min(n.bezel,Math.min(e,a)*.28));for(let d=0;d<a;d++)for(let f=0;f<e;f++){const h=I(f,d,e,a,C.value),m=u(h/V);let F=0,N=0,Q=0;if(h>=0&&h<V){const w=Y(f,d,e,a,C.value),X=ue(ie(m))*n.thickness*E(1-m)*n.refraction;F=-w.x*X,N=-w.y*X,y=Math.max(y,Math.abs(F),Math.abs(N));const ge=u(w.x*O.x+w.y*O.y,0,1);Q=Math.pow(ge,18)*n.specularOpacity}if(n.magnification!==0&&h>=0){const w=le(f,d,e,a,n.magnification,n.magnificationFocus);F+=w.x,N+=w.y}$.push([F,N]);const T=(d*e+f)*4,me=Math.round(Q*255);M.data[T]=255,M.data[T+1]=255,M.data[T+2]=255,M.data[T+3]=me}y=$.reduce((d,[f,h])=>Math.max(d,Math.abs(f),Math.abs(h)),1),$.forEach(([d,f],h)=>{const m=h*4;g.data[m]=Math.round(128+d/y*127),g.data[m+1]=Math.round(128+f/y*127),g.data[m+2]=128,g.data[m+3]=255}),b.putImageData(g,0,0),S.putImageData(M,0,0),x.value=H(r),_.value=H(p),k.value=y}function J(){const e=W();e&&(s.value=Math.max(1,e.offsetWidth),l.value=Math.max(1,e.offsetHeight))}const K=i?"rgba(255, 255, 255, 0.08)":"rgba(255, 255, 255, 0.22)",C=(0,t.computed)(()=>{if(n.radius!==32)return n.radius;const e=n.asProps;return typeof e.rounded=="string"&&e.rounded in G?G[e.rounded]:n.radius}),de=(0,t.computed)(()=>n.borderRadius||`${C.value}px`),fe=new Set(["primary","secondary","accent","info","warning","error","success","surface","background"]);function he(e,a){if(fe.has(e))return`rgba(var(--v-theme-${e}), ${a})`;if(e.startsWith("#")){const r=e.replace("#","");return`rgba(${parseInt(r.slice(0,2),16)}, ${parseInt(r.slice(2,4),16)}, ${parseInt(r.slice(4,6),16)}, ${a})`}return e.startsWith("rgb")?e.replace("rgb(","rgba(").replace(")",`, ${a})`):K}const z=(0,t.computed)(()=>{if(n.glassBackground!==void 0)return n.glassBackground;const e=n.asProps;return typeof e.color=="string"?he(e.color,.6):K}),P=(0,t.computed)(()=>n.fallbackOnly||!i?"blur(12px)":`url(#${R}) blur(${n.blur}px)`),pe=(0,t.computed)(()=>({borderRadius:`${de.value}`,...z.value!==void 0?{backgroundColor:z.value}:{},...z.value!==void 0?{boxShadow:["inset 0 0 0 1px rgba(255, 255, 255, 0.28)","0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ")}:{},...B.value&&!A?{backdropFilter:P.value,WebkitBackdropFilter:P.value}:{},...B.value&&A&&!i?{backdropFilter:P.value,WebkitBackdropFilter:P.value}:{}}));let D=null;return(0,t.onMounted)(async()=>{if(await(0,t.nextTick)(),i){J(),L(),await(0,t.nextTick)(),B.value=!0,D=new ResizeObserver(()=>{J(),L(),B.value=!0});const e=W();e&&D.observe(e)}else B.value=!0}),(0,t.watch)(()=>[C.value,n.bezel,n.thickness,n.refraction,n.magnification,n.magnificationFocus,n.surface,n.specularOpacity,n.glareAngle,n.fallbackOnly],()=>{i&&L(),B.value=!0}),(0,t.onBeforeUnmount)(()=>{D?.disconnect()}),(e,a)=>((0,t.openBlock)(),(0,t.createElementBlock)("div",{ref_key:"root",ref:c,class:"liquid-glass",style:(0,t.normalizeStyle)(pe.value)},[(0,t.unref)(i)?((0,t.openBlock)(),(0,t.createElementBlock)("svg",Z,[(0,t.createElementVNode)("defs",null,[(0,t.createElementVNode)("filter",{id:R,"color-interpolation-filters":"sRGB",filterUnits:"userSpaceOnUse",x:"0",y:"0",width:"10000",height:"10000"},[(0,t.createElementVNode)("feImage",{href:x.value,x:"0",y:"0",width:s.value,height:l.value,result:"displacement_map"},null,8,ee),a[0]||(a[0]=(0,t.createElementVNode)("feGaussianBlur",{in:"displacement_map",stdDeviation:"2",result:"smooth_disp"},null,-1)),(0,t.createElementVNode)("feDisplacementMap",{in:"SourceGraphic",in2:"smooth_disp",scale:k.value,xChannelSelector:"R",yChannelSelector:"G",result:"refracted"},null,8,te),(0,t.createElementVNode)("feImage",{href:_.value,x:"0",y:"0",width:s.value,height:l.value,result:"specular"},null,8,ae),a[1]||(a[1]=(0,t.createElementVNode)("feBlend",{in:"refracted",in2:"specular",mode:"screen"},null,-1))])])])):(0,t.createCommentVNode)("",!0),((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(o.as),(0,t.normalizeProps)((0,t.guardReactiveProps)(o.asProps)),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,"default",{},void 0,!0)]),_:3},16))],4))}}),re=(o,n)=>{const c=o.__vccOpts||o;for(const[s,l]of n)c[s]=l;return c},U=re(ne,[["__scopeId","data-v-56a7fa97"]]),oe={install(o){o.component("Liqvued",U)}};v.Liqvued=U,v.default=oe});
1
+ (function(b,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(b=typeof globalThis<"u"?globalThis:b||self,n(b.Liqvued={},b.Vue))})(this,function(b,n){Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function Z(r,t,c,o,l,x,M){if(r==="center")return t;const p={x:-t.x,y:-t.y};if(r==="split"){const s=M<.5?-1:1;return{x:p.x*s,y:p.y*s}}return p}function ee(r,t){return r!=="split"?1:Math.min(1,Math.abs(t-.5)*2)}function I(r,t,c,o,l){const x=Math.min(l,Math.min(c,o)/2),M=c/2-x,p=o/2-x,s=Math.abs(r-c/2)-M,R=Math.abs(t-o/2)-p;return-(Math.min(Math.max(s,R),0)+Math.hypot(Math.max(s,0),Math.max(R,0))-x)}function te(r,t,c,o,l){const M=I(r-1.5,t,c,o,l)-I(r+1.5,t,c,o,l),p=I(r,t-1.5,c,o,l)-I(r,t+1.5,c,o,l),s=Math.hypot(M,p);return s<1e-10?{x:0,y:-1}:{x:M/s,y:p/s}}var ae={key:0,class:"liquid-glass__svg","aria-hidden":"true"},ne=["href","width","height"],re=["scale"],oe=["href","width","height"],se=(0,n.defineComponent)({__name:"Liqvued",props:{as:{default:"div"},asProps:{default:()=>({})},radius:{default:32},borderRadius:{default:void 0},bezel:{default:22},thickness:{default:42},refraction:{default:1},refractionMode:{default:"edge"},magnification:{default:0},magnificationFocus:{default:.82},blur:{default:.4},surface:{default:"convex"},specularOpacity:{default:.45},glareAngle:{type:[Number,Boolean],default:-60},glassBackground:{default:void 0},fallbackOnly:{type:Boolean,default:!1}},setup(r){const t=r,c=(0,n.ref)(null),o=(0,n.ref)(1),l=(0,n.ref)(1),x=(0,n.ref)(""),M=(0,n.ref)(""),p=(0,n.ref)(1),s=typeof CSS<"u"&&typeof navigator<"u"&&CSS.supports?.("backdrop-filter","url(#x)")&&/Chrome/.test(navigator.userAgent),R=`${`lg-${Math.random().toString(36).slice(2)}`}-filter`,A=(0,n.inject)("_liqvued",!1);(0,n.provide)("_liqvued",!0);const w=(0,n.ref)(!1),G={0:0,sm:4,md:8,lg:12,xl:16,pill:9999,circle:9999,shaped:16};function W(){return c.value}function u(e,a=0,i=1){return Math.max(a,Math.min(i,e))}function E(e){return e=u(e),e*e*e*(e*(e*6-15)+10)}function q(e){return Math.pow(1-Math.pow(1-u(e),4),1/4)}function le(e,a,i){return u(e+Math.sin(u(e)*Math.PI*a)*i)}function ue(e){const a=u(e);return u(a+Math.sin(a*39.1)*.06+Math.sin(a*91.7)*.035)}function de(e,a,i,g,_,B){const v=i/2,y=g/2,$=Math.max(1,i/2),k=Math.max(1,g/2),N=(e-v)/$,O=(a-y)/k,d=Math.hypot(N,O),f=u(B,.2,1.4),m=Math.pow(E(1-u(d/f)),1.6),h=_>=0?1+_*m:1/(1+Math.abs(_)*m);return{x:(e-v)/h-(e-v),y:(a-y)/h-(a-y)}}function j(e){const a=u(e);switch(t.surface){case"concave":case"bowl":return 1-q(a);case"lip":return q(a)*(1-E(a))+(1-q(a))*E(a);case"bevel":return a;case"saddle":return .5+Math.sin((a-.5)*Math.PI)*.5;case"ripple":return le(a,3,.12);case"noise":return ue(a);case"asymmetric":return u(Math.pow(a,.65)*.65+E(a)*.35);case"convex":return q(a)}}function fe(e){return(j(e+.001)-j(e-.001))/(2*.001)}function he(e){const g=Math.atan(e),_=1/1.5*Math.sin(g),B=Math.asin(u(_,-1,1));return Math.tan(g-B)}function H(e){return e.toDataURL("image/png")}function D(){if(t.fallbackOnly)return;const e=Math.max(1,Math.round(o.value)),a=Math.max(1,Math.round(l.value)),i=document.createElement("canvas");i.width=e,i.height=a;const g=document.createElement("canvas");g.width=e,g.height=a;const _=i.getContext("2d"),B=g.getContext("2d"),v=_.createImageData(e,a),y=B.createImageData(e,a),$=[];let k=1;const N=t.glareAngle!==!1?{x:Math.cos(t.glareAngle*Math.PI/180),y:Math.sin(t.glareAngle*Math.PI/180)}:{x:0,y:0},O=Math.max(1,Math.min(t.bezel,Math.min(e,a)*.28));for(let d=0;d<a;d++)for(let f=0;f<e;f++){const m=I(f,d,e,a,C.value),h=u(m/O);let V=0,F=0,Q=0;if(m>=0&&m<O){const S=te(f,d,e,a,C.value),X=he(fe(h))*t.thickness*E(1-h)*ee(t.refractionMode,h)*t.refraction,Y=Z(t.refractionMode,S,f,d,e,a,h);V=Y.x*X,F=Y.y*X,k=Math.max(k,Math.abs(V),Math.abs(F));const ye=u(S.x*N.x+S.y*N.y,0,1);Q=Math.pow(ye,18)*t.specularOpacity}if(t.magnification!==0&&m>=0){const S=de(f,d,e,a,t.magnification,t.magnificationFocus);V+=S.x,F+=S.y}$.push([V,F]);const T=(d*e+f)*4,ve=Math.round(Q*255);y.data[T]=255,y.data[T+1]=255,y.data[T+2]=255,y.data[T+3]=ve}k=$.reduce((d,[f,m])=>Math.max(d,Math.abs(f),Math.abs(m)),1),$.forEach(([d,f],m)=>{const h=m*4;v.data[h]=Math.round(128+d/k*127),v.data[h+1]=Math.round(128+f/k*127),v.data[h+2]=128,v.data[h+3]=255}),_.putImageData(v,0,0),B.putImageData(y,0,0),x.value=H(i),M.value=H(g),p.value=k}function J(){const e=W();e&&(o.value=Math.max(1,e.offsetWidth),l.value=Math.max(1,e.offsetHeight))}const K=s?"rgba(255, 255, 255, 0.08)":"rgba(255, 255, 255, 0.22)",C=(0,n.computed)(()=>{if(t.radius!==32)return t.radius;const e=t.asProps;return typeof e.rounded=="string"&&e.rounded in G?G[e.rounded]:t.radius}),pe=(0,n.computed)(()=>t.borderRadius||`${C.value}px`),me=new Set(["primary","secondary","accent","info","warning","error","success","surface","background"]);function ge(e,a){if(me.has(e))return`rgba(var(--v-theme-${e}), ${a})`;if(e.startsWith("#")){const i=e.replace("#","");return`rgba(${parseInt(i.slice(0,2),16)}, ${parseInt(i.slice(2,4),16)}, ${parseInt(i.slice(4,6),16)}, ${a})`}return e.startsWith("rgb")?e.replace("rgb(","rgba(").replace(")",`, ${a})`):K}const L=(0,n.computed)(()=>{if(t.glassBackground!==void 0)return t.glassBackground;const e=t.asProps;return typeof e.color=="string"?ge(e.color,.6):K}),P=(0,n.computed)(()=>t.fallbackOnly||!s?"blur(12px)":`url(#${R}) blur(${t.blur}px)`),Me=(0,n.computed)(()=>({borderRadius:`${pe.value}`,...L.value!==void 0?{backgroundColor:L.value}:{},...L.value!==void 0?{boxShadow:["inset 0 0 0 1px rgba(255, 255, 255, 0.28)","0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ")}:{},...w.value&&!A?{backdropFilter:P.value,WebkitBackdropFilter:P.value}:{},...w.value&&A&&!s?{backdropFilter:P.value,WebkitBackdropFilter:P.value}:{}}));let z=null;return(0,n.onMounted)(async()=>{if(await(0,n.nextTick)(),s){J(),D(),await(0,n.nextTick)(),w.value=!0,z=new ResizeObserver(()=>{J(),D(),w.value=!0});const e=W();e&&z.observe(e)}else w.value=!0}),(0,n.watch)(()=>[C.value,t.bezel,t.thickness,t.refraction,t.refractionMode,t.magnification,t.magnificationFocus,t.surface,t.specularOpacity,t.glareAngle,t.fallbackOnly],()=>{s&&D(),w.value=!0}),(0,n.onBeforeUnmount)(()=>{z?.disconnect()}),(e,a)=>((0,n.openBlock)(),(0,n.createElementBlock)("div",{ref_key:"root",ref:c,class:"liquid-glass",style:(0,n.normalizeStyle)(Me.value)},[(0,n.unref)(s)?((0,n.openBlock)(),(0,n.createElementBlock)("svg",ae,[(0,n.createElementVNode)("defs",null,[(0,n.createElementVNode)("filter",{id:R,"color-interpolation-filters":"sRGB",filterUnits:"userSpaceOnUse",x:"0",y:"0",width:"10000",height:"10000"},[(0,n.createElementVNode)("feImage",{href:x.value,x:"0",y:"0",width:o.value,height:l.value,result:"displacement_map"},null,8,ne),a[0]||(a[0]=(0,n.createElementVNode)("feGaussianBlur",{in:"displacement_map",stdDeviation:"2",result:"smooth_disp"},null,-1)),(0,n.createElementVNode)("feDisplacementMap",{in:"SourceGraphic",in2:"smooth_disp",scale:p.value,xChannelSelector:"R",yChannelSelector:"G",result:"refracted"},null,8,re),(0,n.createElementVNode)("feImage",{href:M.value,x:"0",y:"0",width:o.value,height:l.value,result:"specular"},null,8,oe),a[1]||(a[1]=(0,n.createElementVNode)("feBlend",{in:"refracted",in2:"specular",mode:"screen"},null,-1))])])])):(0,n.createCommentVNode)("",!0),((0,n.openBlock)(),(0,n.createBlock)((0,n.resolveDynamicComponent)(r.as),(0,n.normalizeProps)((0,n.guardReactiveProps)(r.asProps)),{default:(0,n.withCtx)(()=>[(0,n.renderSlot)(e.$slots,"default",{},void 0,!0)]),_:3},16))],4))}}),ie=(r,t)=>{const c=r.__vccOpts||r;for(const[o,l]of t)c[o]=l;return c},U=ie(se,[["__scopeId","data-v-5607a45c"]]),ce={install(r){r.component("Liqvued",U)}};b.Liqvued=U,b.default=ce});
package/index.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { Component } from 'vue'
2
+
3
+ export type Surface =
4
+ | 'convex'
5
+ | 'concave'
6
+ | 'lip'
7
+ | 'bowl'
8
+ | 'bevel'
9
+ | 'saddle'
10
+ | 'ripple'
11
+ | 'noise'
12
+ | 'asymmetric'
13
+
14
+ export type RefractionMode = 'edge' | 'center' | 'split'
15
+
16
+ export interface LiqvuedProps {
17
+ as?: string | Component
18
+ asProps?: Record<string, unknown>
19
+ radius?: number
20
+ borderRadius?: string
21
+ bezel?: number
22
+ thickness?: number
23
+ refraction?: number
24
+ refractionMode?: RefractionMode
25
+ magnification?: number
26
+ magnificationFocus?: number
27
+ blur?: number
28
+ surface?: Surface
29
+ specularOpacity?: number
30
+ glareAngle?: number | false
31
+ glassBackground?: string
32
+ fallbackOnly?: boolean
33
+ }
34
+
35
+ export declare const Liqvued: new() => {
36
+ $props: LiqvuedProps
37
+ }
38
+
39
+ declare const _default: {
40
+ install(app: unknown): void
41
+ }
42
+
43
+ export default _default
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "liqvued",
3
3
  "types": "./index.d.ts",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "description": "Liquid-glass for Vue",
6
6
  "exports": {
7
7
  ".": {
@@ -49,32 +49,32 @@
49
49
  "author": "Igor Voytovich",
50
50
  "license": "MIT",
51
51
  "repository": "https://github.com/IVoyt/liqvued",
52
- "packageManager": "pnpm@10.33.4",
52
+ "packageManager": "pnpm@11.17.0",
53
53
  "peerDependencies": {
54
54
  "vue": "^3.5.14"
55
55
  },
56
56
  "devDependencies": {
57
- "@intlify/unplugin-vue-i18n": "^11.1.1",
57
+ "@intlify/unplugin-vue-i18n": "^11.2.4",
58
58
  "@rollup/plugin-alias": "^5.1.1",
59
59
  "@stylistic/eslint-plugin": "^5.10.0",
60
- "@types/node": "^22.19.15",
61
- "@vitejs/plugin-vue": "^6.0.5",
62
- "@vitejs/plugin-vue-jsx": "^5.1.5",
63
- "eslint": "^10.3.0",
60
+ "@types/node": "^26.1.1",
61
+ "@vitejs/plugin-vue": "^6.0.8",
62
+ "@vitejs/plugin-vue-jsx": "^5.1.6",
63
+ "eslint": "^10.8.0",
64
64
  "eslint-import-resolver-typescript": "^4.4.4",
65
65
  "eslint-plugin-case-police": "^2.2.1",
66
66
  "eslint-plugin-import": "^2.32.0",
67
- "eslint-plugin-promise": "^7.2.1",
68
- "eslint-plugin-sonarjs": "^4.0.3",
69
- "eslint-plugin-unicorn": "^64.0.0",
67
+ "eslint-plugin-promise": "^7.3.0",
68
+ "eslint-plugin-sonarjs": "^4.2.0",
69
+ "eslint-plugin-unicorn": "^72.0.0",
70
70
  "eslint-plugin-vue": "^10.9.0",
71
- "sass-embedded": "^1.98.0",
72
- "tsx": "^4.21.0",
71
+ "sass-embedded": "^1.100.0",
72
+ "tsx": "^4.23.1",
73
73
  "typescript": "^5.9.3",
74
- "typescript-eslint": "^8.59.0",
75
- "unplugin-vue-components": "^28.8.0",
76
- "vite": "^8.0.10",
74
+ "typescript-eslint": "^8.65.0",
75
+ "unplugin-vue-components": "^32.1.0",
76
+ "vite": "^8.1.5",
77
77
  "vue-eslint-parser": "^10.4.0",
78
- "vue-tsc": "^3.2.6"
78
+ "vue-tsc": "^3.3.8"
79
79
  }
80
- }
80
+ }
package/src/Liqvued.vue CHANGED
@@ -10,8 +10,9 @@ import {
10
10
  ref,
11
11
  watch,
12
12
  } from 'vue'
13
+ import { refractionDirection, refractionModeStrength } from './refraction'
13
14
  import { distanceToRoundedRectEdge, normalFromRoundedRect } from './shape'
14
- import type { Surface } from './types'
15
+ import type { RefractionMode, Surface } from './types'
15
16
 
16
17
  const props = withDefaults(defineProps<{
17
18
  as?: string | Component
@@ -21,12 +22,13 @@ const props = withDefaults(defineProps<{
21
22
  bezel?: number
22
23
  thickness?: number
23
24
  refraction?: number
25
+ refractionMode?: RefractionMode
24
26
  magnification?: number
25
27
  magnificationFocus?: number
26
28
  blur?: number
27
29
  surface?: Surface
28
30
  specularOpacity?: number
29
- glareAngle?: number
31
+ glareAngle?: number | false
30
32
  glassBackground?: string
31
33
  fallbackOnly?: boolean
32
34
  }>(), {
@@ -34,9 +36,11 @@ const props = withDefaults(defineProps<{
34
36
  asProps: () => ({}),
35
37
  fallbackOnly: false,
36
38
  radius: 32,
39
+ borderRadius: undefined,
37
40
  bezel: 22,
38
41
  thickness: 42,
39
42
  refraction: 1,
43
+ refractionMode: 'edge',
40
44
  magnification: 0,
41
45
  magnificationFocus: 0.82,
42
46
  blur: 0.4,
@@ -208,10 +212,12 @@ function buildMaps() {
208
212
  const vectors: Array<[number, number]> = []
209
213
  let max = 1
210
214
 
211
- const light = {
212
- x: Math.cos((props.glareAngle * Math.PI) / 180),
213
- y: Math.sin((props.glareAngle * Math.PI) / 180),
214
- }
215
+ const light = props.glareAngle !== false
216
+ ? {
217
+ x: Math.cos((props.glareAngle * Math.PI) / 180),
218
+ y: Math.sin((props.glareAngle * Math.PI) / 180),
219
+ }
220
+ : { x: 0, y: 0 }
215
221
  const edgeBand = Math.max(
216
222
  1,
217
223
  Math.min(props.bezel, Math.min(w, h) * 0.28),
@@ -235,10 +241,13 @@ function buildMaps() {
235
241
  bend *
236
242
  props.thickness *
237
243
  smootherStep(1 - t) *
244
+ refractionModeStrength(props.refractionMode, t) *
238
245
  props.refraction
239
246
 
240
- vx = -n.x * amount
241
- vy = -n.y * amount
247
+ const direction = refractionDirection(props.refractionMode, n, x, y, w, h, t)
248
+
249
+ vx = direction.x * amount
250
+ vy = direction.y * amount
242
251
 
243
252
  max = Math.max(max, Math.abs(vx), Math.abs(vy))
244
253
 
@@ -412,6 +421,7 @@ watch(
412
421
  props.bezel,
413
422
  props.thickness,
414
423
  props.refraction,
424
+ props.refractionMode,
415
425
  props.magnification,
416
426
  props.magnificationFocus,
417
427
  props.surface,
@@ -0,0 +1,36 @@
1
+ import { deepStrictEqual, ok } from 'node:assert'
2
+ import { refractionDirection, refractionModeStrength } from './refraction'
3
+
4
+ const normal = { x: 1, y: 0 }
5
+
6
+ deepStrictEqual(
7
+ refractionDirection('edge', normal, 20, 50, 100, 100, 0.25),
8
+ { x: -1, y: -0 },
9
+ )
10
+
11
+ deepStrictEqual(
12
+ refractionDirection('center', normal, 20, 50, 100, 100, 0.25),
13
+ { x: 1, y: 0 },
14
+ )
15
+
16
+ deepStrictEqual(
17
+ refractionDirection('center', { x: 0, y: 1 }, 50, 20, 100, 100, 0.25),
18
+ { x: 0, y: 1 },
19
+ )
20
+
21
+ deepStrictEqual(
22
+ refractionDirection('center', { x: 0, y: 1 }, 20, 20, 100, 100, 0.25),
23
+ { x: 0, y: 1 },
24
+ )
25
+
26
+ const outerSplit = refractionDirection('split', normal, 20, 50, 100, 100, 0.25)
27
+ const innerSplit = refractionDirection('split', normal, 20, 50, 100, 100, 0.75)
28
+
29
+ ok(outerSplit.x * innerSplit.x < 0)
30
+ deepStrictEqual(outerSplit, { x: 1, y: 0 })
31
+ deepStrictEqual(innerSplit, { x: -1, y: -0 })
32
+
33
+ deepStrictEqual(refractionModeStrength('center', 0.5), 1)
34
+ deepStrictEqual(refractionModeStrength('split', 0.5), 0)
35
+ ok(refractionModeStrength('split', 0.1) > refractionModeStrength('split', 0.4))
36
+ ok(refractionModeStrength('split', 0.9) > refractionModeStrength('split', 0.6))
@@ -0,0 +1,50 @@
1
+ import type { RefractionMode } from './types'
2
+
3
+ export const refractionModeOptions = [
4
+ 'edge',
5
+ 'center',
6
+ 'split',
7
+ ] as const satisfies readonly RefractionMode[]
8
+
9
+ interface Vector {
10
+ x: number
11
+ y: number
12
+ }
13
+
14
+ export function refractionDirection(
15
+ mode: RefractionMode,
16
+ normal: Vector,
17
+ _x: number,
18
+ _y: number,
19
+ _width: number,
20
+ _height: number,
21
+ t: number,
22
+ ): Vector {
23
+ if (mode === 'center') {
24
+ return normal
25
+ }
26
+
27
+ const edge = {
28
+ x: -normal.x,
29
+ y: -normal.y,
30
+ }
31
+
32
+ if (mode === 'split') {
33
+ const side = t < 0.5 ? -1 : 1
34
+
35
+ return {
36
+ x: edge.x * side,
37
+ y: edge.y * side,
38
+ }
39
+ }
40
+
41
+ return edge
42
+ }
43
+
44
+ export function refractionModeStrength(mode: RefractionMode, t: number): number {
45
+ if (mode !== 'split') {
46
+ return 1
47
+ }
48
+
49
+ return Math.min(1, Math.abs(t - 0.5) * 2)
50
+ }
package/src/types.ts CHANGED
@@ -8,3 +8,5 @@ export type Surface =
8
8
  | 'ripple'
9
9
  | 'noise'
10
10
  | 'asymmetric'
11
+
12
+ export type RefractionMode = 'edge' | 'center' | 'split'