@dazecoop/liquid-glass 0.0.0-stage → 0.1.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/lib/react.cjs ADDED
@@ -0,0 +1,238 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const react = require("react");
6
+ const index = require("./index.cjs");
7
+ const resolve = (target) => typeof target === "function" ? target() : target?.current ?? target ?? null;
8
+ const cx = (...names) => names.filter(Boolean).join(" ");
9
+ function useForwarded(ref) {
10
+ const own = react.useRef(null);
11
+ react.useImperativeHandle(ref, () => own.current, []);
12
+ return own;
13
+ }
14
+ function useQuiet() {
15
+ const quiet = react.useRef(false);
16
+ return [quiet, (fn) => {
17
+ quiet.current = true;
18
+ try {
19
+ fn();
20
+ } finally {
21
+ quiet.current = false;
22
+ }
23
+ }];
24
+ }
25
+ function useValue(value, initial) {
26
+ const [own, setOwn] = react.useState(initial);
27
+ return [value === void 0 ? own : value, setOwn];
28
+ }
29
+ function useLiquidGlass(source, { anchor, container, button = false, throttle = 0, ...params } = {}) {
30
+ const [glass, setGlass] = react.useState(null);
31
+ const latest = react.useRef(params);
32
+ latest.current = params;
33
+ react.useEffect(() => {
34
+ const el = resolve(source);
35
+ if (!el) return;
36
+ const g = new index.LiquidGlass(el, {
37
+ anchor: resolve(anchor),
38
+ container: resolve(container),
39
+ button,
40
+ throttle,
41
+ ...latest.current
42
+ });
43
+ setGlass(g);
44
+ return () => {
45
+ g.destroy();
46
+ setGlass(null);
47
+ };
48
+ }, [source, anchor, container, button, throttle]);
49
+ const key = JSON.stringify(params);
50
+ const applied = react.useRef(null);
51
+ react.useEffect(() => {
52
+ if (!glass) return;
53
+ if (applied.current === glass) glass.set(latest.current);
54
+ applied.current = glass;
55
+ }, [glass, key]);
56
+ return glass;
57
+ }
58
+ const GlassScene = react.forwardRef(function GlassScene2({ as: Tag = "div", glass: params, onPointerMove, onPointerDown, onPointerUp, onPointerLeave, children, ...rest }, ref) {
59
+ const el = useForwarded(ref);
60
+ const lens = useLiquidGlass(el, { container: el, ...params });
61
+ return /* @__PURE__ */ jsxRuntime.jsx(
62
+ Tag,
63
+ {
64
+ ref: el,
65
+ ...rest,
66
+ onPointerMove: (e) => {
67
+ lens?.moveTo(e.clientX, e.clientY);
68
+ onPointerMove?.(e);
69
+ },
70
+ onPointerDown: (e) => {
71
+ lens?.moveTo(e.clientX, e.clientY);
72
+ lens?.press(true);
73
+ onPointerDown?.(e);
74
+ },
75
+ onPointerUp: (e) => {
76
+ lens?.press(false);
77
+ onPointerUp?.(e);
78
+ },
79
+ onPointerLeave: (e) => {
80
+ lens?.press(false);
81
+ lens?.show(false);
82
+ onPointerLeave?.(e);
83
+ },
84
+ children
85
+ }
86
+ );
87
+ });
88
+ const GlassPanel = react.forwardRef(function GlassPanel2({ as: Tag = "div", source, button = false, glass: params, children, ...rest }, ref) {
89
+ const el = useForwarded(ref);
90
+ const from = react.useCallback(() => resolve(source) ?? el.current?.parentElement, [source, el]);
91
+ useLiquidGlass(from, { anchor: el, button, ...params });
92
+ return /* @__PURE__ */ jsxRuntime.jsx(Tag, { ref: el, ...rest, children });
93
+ });
94
+ const GlassSwitch = react.forwardRef(function GlassSwitch2({ checked, defaultChecked = false, onChange, look, className, ...rest }, ref) {
95
+ const el = useForwarded(ref);
96
+ const [on, setOn] = useValue(checked, defaultChecked);
97
+ const inst = react.useRef(null);
98
+ const [quiet, silently] = useQuiet();
99
+ const latest = react.useRef(onChange);
100
+ latest.current = onChange;
101
+ react.useEffect(() => {
102
+ const root = el.current;
103
+ const s = inst.current = new index.GlassSwitch(root, look && { look });
104
+ const changed = (e) => {
105
+ if (e.target !== root || quiet.current) return;
106
+ setOn(e.detail.value === 1);
107
+ latest.current?.(e.detail.value === 1);
108
+ };
109
+ root.addEventListener("change", changed);
110
+ return () => {
111
+ root.removeEventListener("change", changed);
112
+ s.destroy();
113
+ inst.current = null;
114
+ };
115
+ }, [JSON.stringify(look)]);
116
+ react.useEffect(() => {
117
+ if (inst.current) silently(() => inst.current.select(on ? 1 : 0));
118
+ }, [on]);
119
+ return /* @__PURE__ */ jsxRuntime.jsxs("button", { ref: el, type: "button", role: "switch", "aria-checked": on, className: cx("lg-switch", className), ...rest, children: [
120
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "refract", "aria-hidden": "true" }),
121
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "thumb", "aria-hidden": "true" }),
122
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-opt": "" }),
123
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-opt": "" })
124
+ ] });
125
+ });
126
+ const GlassSegment = react.forwardRef(function GlassSegment2({ options, value, defaultValue, onChange, look, className, ...rest }, ref) {
127
+ const el = useForwarded(ref);
128
+ const items = options.map((o) => typeof o === "object" ? o : { value: o, label: o });
129
+ const [current, setCurrent] = useValue(value, defaultValue ?? items[0].value);
130
+ const index$1 = Math.max(0, items.findIndex((o) => o.value === current));
131
+ const inst = react.useRef(null);
132
+ const [quiet, silently] = useQuiet();
133
+ const latest = react.useRef({ onChange, items });
134
+ latest.current = { onChange, items };
135
+ react.useEffect(() => {
136
+ const root = el.current;
137
+ const s = inst.current = new index.GlassSwitch(root, look && { look });
138
+ const changed = (e) => {
139
+ if (e.target !== root || quiet.current) return;
140
+ const next = latest.current.items[e.detail.value].value;
141
+ setCurrent(next);
142
+ latest.current.onChange?.(next);
143
+ };
144
+ root.addEventListener("change", changed);
145
+ return () => {
146
+ root.removeEventListener("change", changed);
147
+ s.destroy();
148
+ inst.current = null;
149
+ };
150
+ }, [JSON.stringify(look)]);
151
+ react.useEffect(() => {
152
+ if (inst.current) silently(() => inst.current.select(index$1));
153
+ }, [index$1]);
154
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: el, role: "radiogroup", className: cx("lg-segment", className), ...rest, children: [
155
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "refract", "aria-hidden": "true" }),
156
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "thumb", "aria-hidden": "true" }),
157
+ items.slice(0, 2).map((o, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", role: "radio", "aria-checked": i === index$1, "data-opt": "", children: o.label }, String(o.value)))
158
+ ] });
159
+ });
160
+ const GlassTabBar = react.forwardRef(function GlassTabBar2({ items, value, defaultValue, onChange, look, className, ...rest }, ref) {
161
+ const el = useForwarded(ref);
162
+ const [current, setCurrent] = useValue(value, defaultValue ?? items[0]?.value);
163
+ const inst = react.useRef(null);
164
+ const [quiet, silently] = useQuiet();
165
+ const latest = react.useRef({ onChange, items });
166
+ latest.current = { onChange, items };
167
+ const keys = items.map((t) => String(t.value)).join("\n");
168
+ react.useEffect(() => {
169
+ const root = el.current;
170
+ const bar = inst.current = new index.GlassTabBar(root, look && { look });
171
+ const changed = (e) => {
172
+ if (e.target !== root || quiet.current) return;
173
+ const next = latest.current.items[bar.tabs.indexOf(e.detail.tab)]?.value;
174
+ setCurrent(next);
175
+ latest.current.onChange?.(next);
176
+ };
177
+ root.addEventListener("change", changed);
178
+ return () => {
179
+ root.removeEventListener("change", changed);
180
+ bar.destroy();
181
+ inst.current = null;
182
+ };
183
+ }, [keys, JSON.stringify(look)]);
184
+ react.useEffect(() => {
185
+ const bar = inst.current;
186
+ const tab = bar?.tabs[items.findIndex((t) => t.value === current)];
187
+ if (tab) silently(() => bar.choose(tab));
188
+ }, [current, keys]);
189
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { ref: el, className: cx("lg-tabs", className), ...rest, children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lg-pill", "aria-hidden": "true", hidden: true }),
191
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lg-glowbox", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lg-glow" }) }),
192
+ items.map((t) => {
193
+ const Tab = t.href ? "a" : "button";
194
+ return /* @__PURE__ */ jsxRuntime.jsxs(
195
+ Tab,
196
+ {
197
+ ...t.href ? { href: t.href } : { type: "button" },
198
+ "aria-current": t.value === current ? "page" : void 0,
199
+ children: [
200
+ t.icon,
201
+ t.label
202
+ ]
203
+ },
204
+ String(t.value)
205
+ );
206
+ })
207
+ ] });
208
+ });
209
+ const GlassButton = react.forwardRef(function GlassButton2({ source, tint, grow, press, lit, hang, look, className, style, children, ...rest }, ref) {
210
+ const el = useForwarded(ref);
211
+ const options = { grow, press, lit, hang, look };
212
+ const key = JSON.stringify(options);
213
+ react.useEffect(() => {
214
+ const given = Object.fromEntries(Object.entries(options).filter(([, v]) => v !== void 0));
215
+ const from = resolve(source);
216
+ const b = new index.GlassButton(el.current, from ? { ...given, source: from } : given);
217
+ return () => b.destroy();
218
+ }, [source, key]);
219
+ return /* @__PURE__ */ jsxRuntime.jsx(
220
+ "button",
221
+ {
222
+ ref: el,
223
+ type: "button",
224
+ className: cx("lg-button", className),
225
+ style: tint ? { "--tint": tint, ...style } : style,
226
+ ...rest,
227
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lg-content", children })
228
+ }
229
+ );
230
+ });
231
+ exports.LiquidGlass = index.LiquidGlass;
232
+ exports.GlassButton = GlassButton;
233
+ exports.GlassPanel = GlassPanel;
234
+ exports.GlassScene = GlassScene;
235
+ exports.GlassSegment = GlassSegment;
236
+ exports.GlassSwitch = GlassSwitch;
237
+ exports.GlassTabBar = GlassTabBar;
238
+ exports.useLiquidGlass = useLiquidGlass;
@@ -0,0 +1,91 @@
1
+ import type { ComponentPropsWithoutRef, ElementType, ForwardRefExoticComponent, ReactNode, RefAttributes, RefObject } from "react";
2
+ import type { GlassOptions, GlassParams, LiquidGlass } from "./index.js";
3
+
4
+ export { LiquidGlass } from "./index.js";
5
+ export type { GlassOptions, GlassParams, PresetName } from "./index.js";
6
+
7
+ /** A ref, an element, or a function returning one */
8
+ export type ElementTarget = RefObject<HTMLElement | null> | HTMLElement | null | (() => HTMLElement | null | undefined);
9
+
10
+ export interface UseLiquidGlassOptions extends GlassParams {
11
+ anchor?: ElementTarget;
12
+ container?: ElementTarget;
13
+ button?: boolean;
14
+ throttle?: number;
15
+ }
16
+
17
+ /**
18
+ * Builds a LiquidGlass over `source` once mounted and destroys it on unmount. Parameter
19
+ * changes are applied with set(). Returns the instance, or null until it exists.
20
+ * Pass a stable `source` (a ref, or a memoised function), or it rebuilds every render.
21
+ */
22
+ export function useLiquidGlass(source: ElementTarget, options?: UseLiquidGlassOptions): LiquidGlass | null;
23
+
24
+ type Polymorphic<P> = P & { as?: ElementType } & Omit<ComponentPropsWithoutRef<"div">, keyof P>;
25
+
26
+ export interface GlassSceneProps {
27
+ /** The lens's parameters, e.g. LiquidGlass.presets.frosted */
28
+ glass?: GlassParams;
29
+ children?: ReactNode;
30
+ }
31
+ /** A lens that follows the pointer over its children */
32
+ export const GlassScene: ForwardRefExoticComponent<Polymorphic<GlassSceneProps> & RefAttributes<HTMLElement>>;
33
+
34
+ export interface GlassPanelProps {
35
+ /** What the glass refracts: something behind the panel. Default its parent */
36
+ source?: ElementTarget;
37
+ /** Brighten on hover and flatten on press */
38
+ button?: boolean;
39
+ glass?: GlassParams;
40
+ children?: ReactNode;
41
+ }
42
+ /** An element with glass as its background */
43
+ export const GlassPanel: ForwardRefExoticComponent<Polymorphic<GlassPanelProps> & RefAttributes<HTMLElement>>;
44
+
45
+ export interface GlassSwitchProps extends Omit<ComponentPropsWithoutRef<"button">, "onChange" | "role"> {
46
+ checked?: boolean;
47
+ defaultChecked?: boolean;
48
+ onChange?: (checked: boolean) => void;
49
+ look?: GlassParams;
50
+ }
51
+ export const GlassSwitch: ForwardRefExoticComponent<GlassSwitchProps & RefAttributes<HTMLButtonElement>>;
52
+
53
+ export type SegmentOption<V> = V | { value: V; label: ReactNode };
54
+ export interface GlassSegmentProps<V = string> extends Omit<ComponentPropsWithoutRef<"div">, "onChange" | "defaultValue"> {
55
+ /** Exactly two options */
56
+ options: [SegmentOption<V>, SegmentOption<V>];
57
+ value?: V;
58
+ defaultValue?: V;
59
+ onChange?: (value: V) => void;
60
+ look?: GlassParams;
61
+ }
62
+ export const GlassSegment: <V = string>(props: GlassSegmentProps<V> & RefAttributes<HTMLDivElement>) => ReactNode;
63
+
64
+ export interface TabItem<V = string> {
65
+ value: V;
66
+ label: ReactNode;
67
+ icon?: ReactNode;
68
+ /** Renders the tab as a link */
69
+ href?: string;
70
+ }
71
+ export interface GlassTabBarProps<V = string> extends Omit<ComponentPropsWithoutRef<"nav">, "onChange" | "defaultValue"> {
72
+ items: TabItem<V>[];
73
+ value?: V;
74
+ defaultValue?: V;
75
+ onChange?: (value: V) => void;
76
+ look?: GlassParams;
77
+ }
78
+ export const GlassTabBar: <V = string>(props: GlassTabBarProps<V> & RefAttributes<HTMLElement>) => ReactNode;
79
+
80
+ export interface GlassButtonProps extends ComponentPropsWithoutRef<"button"> {
81
+ /** What the lens refracts. Default the parent's [data-glass-source] child */
82
+ source?: ElementTarget;
83
+ /** The colour it lights up in. Default var(--lg-accent) */
84
+ tint?: string;
85
+ grow?: number;
86
+ press?: number;
87
+ lit?: number;
88
+ hang?: number;
89
+ look?: GlassParams;
90
+ }
91
+ export const GlassButton: ForwardRefExoticComponent<GlassButtonProps & RefAttributes<HTMLButtonElement>>;
package/lib/react.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ import type { ComponentPropsWithoutRef, ElementType, ForwardRefExoticComponent, ReactNode, RefAttributes, RefObject } from "react";
2
+ import type { GlassOptions, GlassParams, LiquidGlass } from "./index.js";
3
+
4
+ export { LiquidGlass } from "./index.js";
5
+ export type { GlassOptions, GlassParams, PresetName } from "./index.js";
6
+
7
+ /** A ref, an element, or a function returning one */
8
+ export type ElementTarget = RefObject<HTMLElement | null> | HTMLElement | null | (() => HTMLElement | null | undefined);
9
+
10
+ export interface UseLiquidGlassOptions extends GlassParams {
11
+ anchor?: ElementTarget;
12
+ container?: ElementTarget;
13
+ button?: boolean;
14
+ throttle?: number;
15
+ }
16
+
17
+ /**
18
+ * Builds a LiquidGlass over `source` once mounted and destroys it on unmount. Parameter
19
+ * changes are applied with set(). Returns the instance, or null until it exists.
20
+ * Pass a stable `source` (a ref, or a memoised function), or it rebuilds every render.
21
+ */
22
+ export function useLiquidGlass(source: ElementTarget, options?: UseLiquidGlassOptions): LiquidGlass | null;
23
+
24
+ type Polymorphic<P> = P & { as?: ElementType } & Omit<ComponentPropsWithoutRef<"div">, keyof P>;
25
+
26
+ export interface GlassSceneProps {
27
+ /** The lens's parameters, e.g. LiquidGlass.presets.frosted */
28
+ glass?: GlassParams;
29
+ children?: ReactNode;
30
+ }
31
+ /** A lens that follows the pointer over its children */
32
+ export const GlassScene: ForwardRefExoticComponent<Polymorphic<GlassSceneProps> & RefAttributes<HTMLElement>>;
33
+
34
+ export interface GlassPanelProps {
35
+ /** What the glass refracts: something behind the panel. Default its parent */
36
+ source?: ElementTarget;
37
+ /** Brighten on hover and flatten on press */
38
+ button?: boolean;
39
+ glass?: GlassParams;
40
+ children?: ReactNode;
41
+ }
42
+ /** An element with glass as its background */
43
+ export const GlassPanel: ForwardRefExoticComponent<Polymorphic<GlassPanelProps> & RefAttributes<HTMLElement>>;
44
+
45
+ export interface GlassSwitchProps extends Omit<ComponentPropsWithoutRef<"button">, "onChange" | "role"> {
46
+ checked?: boolean;
47
+ defaultChecked?: boolean;
48
+ onChange?: (checked: boolean) => void;
49
+ look?: GlassParams;
50
+ }
51
+ export const GlassSwitch: ForwardRefExoticComponent<GlassSwitchProps & RefAttributes<HTMLButtonElement>>;
52
+
53
+ export type SegmentOption<V> = V | { value: V; label: ReactNode };
54
+ export interface GlassSegmentProps<V = string> extends Omit<ComponentPropsWithoutRef<"div">, "onChange" | "defaultValue"> {
55
+ /** Exactly two options */
56
+ options: [SegmentOption<V>, SegmentOption<V>];
57
+ value?: V;
58
+ defaultValue?: V;
59
+ onChange?: (value: V) => void;
60
+ look?: GlassParams;
61
+ }
62
+ export const GlassSegment: <V = string>(props: GlassSegmentProps<V> & RefAttributes<HTMLDivElement>) => ReactNode;
63
+
64
+ export interface TabItem<V = string> {
65
+ value: V;
66
+ label: ReactNode;
67
+ icon?: ReactNode;
68
+ /** Renders the tab as a link */
69
+ href?: string;
70
+ }
71
+ export interface GlassTabBarProps<V = string> extends Omit<ComponentPropsWithoutRef<"nav">, "onChange" | "defaultValue"> {
72
+ items: TabItem<V>[];
73
+ value?: V;
74
+ defaultValue?: V;
75
+ onChange?: (value: V) => void;
76
+ look?: GlassParams;
77
+ }
78
+ export const GlassTabBar: <V = string>(props: GlassTabBarProps<V> & RefAttributes<HTMLElement>) => ReactNode;
79
+
80
+ export interface GlassButtonProps extends ComponentPropsWithoutRef<"button"> {
81
+ /** What the lens refracts. Default the parent's [data-glass-source] child */
82
+ source?: ElementTarget;
83
+ /** The colour it lights up in. Default var(--lg-accent) */
84
+ tint?: string;
85
+ grow?: number;
86
+ press?: number;
87
+ lit?: number;
88
+ hang?: number;
89
+ look?: GlassParams;
90
+ }
91
+ export const GlassButton: ForwardRefExoticComponent<GlassButtonProps & RefAttributes<HTMLButtonElement>>;
package/lib/react.js ADDED
@@ -0,0 +1,238 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useCallback, useRef, useEffect, useImperativeHandle, useState } from "react";
4
+ import { GlassSwitch as GlassSwitch$1, GlassTabBar as GlassTabBar$1, GlassButton as GlassButton$1, LiquidGlass } from "./index.js";
5
+ const resolve = (target) => typeof target === "function" ? target() : target?.current ?? target ?? null;
6
+ const cx = (...names) => names.filter(Boolean).join(" ");
7
+ function useForwarded(ref) {
8
+ const own = useRef(null);
9
+ useImperativeHandle(ref, () => own.current, []);
10
+ return own;
11
+ }
12
+ function useQuiet() {
13
+ const quiet = useRef(false);
14
+ return [quiet, (fn) => {
15
+ quiet.current = true;
16
+ try {
17
+ fn();
18
+ } finally {
19
+ quiet.current = false;
20
+ }
21
+ }];
22
+ }
23
+ function useValue(value, initial) {
24
+ const [own, setOwn] = useState(initial);
25
+ return [value === void 0 ? own : value, setOwn];
26
+ }
27
+ function useLiquidGlass(source, { anchor, container, button = false, throttle = 0, ...params } = {}) {
28
+ const [glass, setGlass] = useState(null);
29
+ const latest = useRef(params);
30
+ latest.current = params;
31
+ useEffect(() => {
32
+ const el = resolve(source);
33
+ if (!el) return;
34
+ const g = new LiquidGlass(el, {
35
+ anchor: resolve(anchor),
36
+ container: resolve(container),
37
+ button,
38
+ throttle,
39
+ ...latest.current
40
+ });
41
+ setGlass(g);
42
+ return () => {
43
+ g.destroy();
44
+ setGlass(null);
45
+ };
46
+ }, [source, anchor, container, button, throttle]);
47
+ const key = JSON.stringify(params);
48
+ const applied = useRef(null);
49
+ useEffect(() => {
50
+ if (!glass) return;
51
+ if (applied.current === glass) glass.set(latest.current);
52
+ applied.current = glass;
53
+ }, [glass, key]);
54
+ return glass;
55
+ }
56
+ const GlassScene = forwardRef(function GlassScene2({ as: Tag = "div", glass: params, onPointerMove, onPointerDown, onPointerUp, onPointerLeave, children, ...rest }, ref) {
57
+ const el = useForwarded(ref);
58
+ const lens = useLiquidGlass(el, { container: el, ...params });
59
+ return /* @__PURE__ */ jsx(
60
+ Tag,
61
+ {
62
+ ref: el,
63
+ ...rest,
64
+ onPointerMove: (e) => {
65
+ lens?.moveTo(e.clientX, e.clientY);
66
+ onPointerMove?.(e);
67
+ },
68
+ onPointerDown: (e) => {
69
+ lens?.moveTo(e.clientX, e.clientY);
70
+ lens?.press(true);
71
+ onPointerDown?.(e);
72
+ },
73
+ onPointerUp: (e) => {
74
+ lens?.press(false);
75
+ onPointerUp?.(e);
76
+ },
77
+ onPointerLeave: (e) => {
78
+ lens?.press(false);
79
+ lens?.show(false);
80
+ onPointerLeave?.(e);
81
+ },
82
+ children
83
+ }
84
+ );
85
+ });
86
+ const GlassPanel = forwardRef(function GlassPanel2({ as: Tag = "div", source, button = false, glass: params, children, ...rest }, ref) {
87
+ const el = useForwarded(ref);
88
+ const from = useCallback(() => resolve(source) ?? el.current?.parentElement, [source, el]);
89
+ useLiquidGlass(from, { anchor: el, button, ...params });
90
+ return /* @__PURE__ */ jsx(Tag, { ref: el, ...rest, children });
91
+ });
92
+ const GlassSwitch = forwardRef(function GlassSwitch2({ checked, defaultChecked = false, onChange, look, className, ...rest }, ref) {
93
+ const el = useForwarded(ref);
94
+ const [on, setOn] = useValue(checked, defaultChecked);
95
+ const inst = useRef(null);
96
+ const [quiet, silently] = useQuiet();
97
+ const latest = useRef(onChange);
98
+ latest.current = onChange;
99
+ useEffect(() => {
100
+ const root = el.current;
101
+ const s = inst.current = new GlassSwitch$1(root, look && { look });
102
+ const changed = (e) => {
103
+ if (e.target !== root || quiet.current) return;
104
+ setOn(e.detail.value === 1);
105
+ latest.current?.(e.detail.value === 1);
106
+ };
107
+ root.addEventListener("change", changed);
108
+ return () => {
109
+ root.removeEventListener("change", changed);
110
+ s.destroy();
111
+ inst.current = null;
112
+ };
113
+ }, [JSON.stringify(look)]);
114
+ useEffect(() => {
115
+ if (inst.current) silently(() => inst.current.select(on ? 1 : 0));
116
+ }, [on]);
117
+ return /* @__PURE__ */ jsxs("button", { ref: el, type: "button", role: "switch", "aria-checked": on, className: cx("lg-switch", className), ...rest, children: [
118
+ /* @__PURE__ */ jsx("span", { className: "refract", "aria-hidden": "true" }),
119
+ /* @__PURE__ */ jsx("span", { className: "thumb", "aria-hidden": "true" }),
120
+ /* @__PURE__ */ jsx("span", { "data-opt": "" }),
121
+ /* @__PURE__ */ jsx("span", { "data-opt": "" })
122
+ ] });
123
+ });
124
+ const GlassSegment = forwardRef(function GlassSegment2({ options, value, defaultValue, onChange, look, className, ...rest }, ref) {
125
+ const el = useForwarded(ref);
126
+ const items = options.map((o) => typeof o === "object" ? o : { value: o, label: o });
127
+ const [current, setCurrent] = useValue(value, defaultValue ?? items[0].value);
128
+ const index = Math.max(0, items.findIndex((o) => o.value === current));
129
+ const inst = useRef(null);
130
+ const [quiet, silently] = useQuiet();
131
+ const latest = useRef({ onChange, items });
132
+ latest.current = { onChange, items };
133
+ useEffect(() => {
134
+ const root = el.current;
135
+ const s = inst.current = new GlassSwitch$1(root, look && { look });
136
+ const changed = (e) => {
137
+ if (e.target !== root || quiet.current) return;
138
+ const next = latest.current.items[e.detail.value].value;
139
+ setCurrent(next);
140
+ latest.current.onChange?.(next);
141
+ };
142
+ root.addEventListener("change", changed);
143
+ return () => {
144
+ root.removeEventListener("change", changed);
145
+ s.destroy();
146
+ inst.current = null;
147
+ };
148
+ }, [JSON.stringify(look)]);
149
+ useEffect(() => {
150
+ if (inst.current) silently(() => inst.current.select(index));
151
+ }, [index]);
152
+ return /* @__PURE__ */ jsxs("div", { ref: el, role: "radiogroup", className: cx("lg-segment", className), ...rest, children: [
153
+ /* @__PURE__ */ jsx("span", { className: "refract", "aria-hidden": "true" }),
154
+ /* @__PURE__ */ jsx("span", { className: "thumb", "aria-hidden": "true" }),
155
+ items.slice(0, 2).map((o, i) => /* @__PURE__ */ jsx("button", { type: "button", role: "radio", "aria-checked": i === index, "data-opt": "", children: o.label }, String(o.value)))
156
+ ] });
157
+ });
158
+ const GlassTabBar = forwardRef(function GlassTabBar2({ items, value, defaultValue, onChange, look, className, ...rest }, ref) {
159
+ const el = useForwarded(ref);
160
+ const [current, setCurrent] = useValue(value, defaultValue ?? items[0]?.value);
161
+ const inst = useRef(null);
162
+ const [quiet, silently] = useQuiet();
163
+ const latest = useRef({ onChange, items });
164
+ latest.current = { onChange, items };
165
+ const keys = items.map((t) => String(t.value)).join("\n");
166
+ useEffect(() => {
167
+ const root = el.current;
168
+ const bar = inst.current = new GlassTabBar$1(root, look && { look });
169
+ const changed = (e) => {
170
+ if (e.target !== root || quiet.current) return;
171
+ const next = latest.current.items[bar.tabs.indexOf(e.detail.tab)]?.value;
172
+ setCurrent(next);
173
+ latest.current.onChange?.(next);
174
+ };
175
+ root.addEventListener("change", changed);
176
+ return () => {
177
+ root.removeEventListener("change", changed);
178
+ bar.destroy();
179
+ inst.current = null;
180
+ };
181
+ }, [keys, JSON.stringify(look)]);
182
+ useEffect(() => {
183
+ const bar = inst.current;
184
+ const tab = bar?.tabs[items.findIndex((t) => t.value === current)];
185
+ if (tab) silently(() => bar.choose(tab));
186
+ }, [current, keys]);
187
+ return /* @__PURE__ */ jsxs("nav", { ref: el, className: cx("lg-tabs", className), ...rest, children: [
188
+ /* @__PURE__ */ jsx("span", { className: "lg-pill", "aria-hidden": "true", hidden: true }),
189
+ /* @__PURE__ */ jsx("span", { className: "lg-glowbox", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "lg-glow" }) }),
190
+ items.map((t) => {
191
+ const Tab = t.href ? "a" : "button";
192
+ return /* @__PURE__ */ jsxs(
193
+ Tab,
194
+ {
195
+ ...t.href ? { href: t.href } : { type: "button" },
196
+ "aria-current": t.value === current ? "page" : void 0,
197
+ children: [
198
+ t.icon,
199
+ t.label
200
+ ]
201
+ },
202
+ String(t.value)
203
+ );
204
+ })
205
+ ] });
206
+ });
207
+ const GlassButton = forwardRef(function GlassButton2({ source, tint, grow, press, lit, hang, look, className, style, children, ...rest }, ref) {
208
+ const el = useForwarded(ref);
209
+ const options = { grow, press, lit, hang, look };
210
+ const key = JSON.stringify(options);
211
+ useEffect(() => {
212
+ const given = Object.fromEntries(Object.entries(options).filter(([, v]) => v !== void 0));
213
+ const from = resolve(source);
214
+ const b = new GlassButton$1(el.current, from ? { ...given, source: from } : given);
215
+ return () => b.destroy();
216
+ }, [source, key]);
217
+ return /* @__PURE__ */ jsx(
218
+ "button",
219
+ {
220
+ ref: el,
221
+ type: "button",
222
+ className: cx("lg-button", className),
223
+ style: tint ? { "--tint": tint, ...style } : style,
224
+ ...rest,
225
+ children: /* @__PURE__ */ jsx("span", { className: "lg-content", children })
226
+ }
227
+ );
228
+ });
229
+ export {
230
+ GlassButton,
231
+ GlassPanel,
232
+ GlassScene,
233
+ GlassSegment,
234
+ GlassSwitch,
235
+ GlassTabBar,
236
+ LiquidGlass,
237
+ useLiquidGlass
238
+ };