@a4ui/core 0.22.0 → 0.23.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/README.md +17 -9
- package/dist/NumberInput-enx_0iVh.js +488 -0
- package/dist/commerce/ConditionScale.d.ts +45 -0
- package/dist/commerce/FacetSidebar.d.ts +55 -0
- package/dist/commerce/PriceBlock.d.ts +50 -0
- package/dist/commerce/index.d.ts +3 -0
- package/dist/commerce.js +465 -150
- package/dist/elements.css +132 -0
- package/dist/full.css +132 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3026 -2827
- package/dist/ui/AnnouncementBar.d.ts +36 -0
- package/dist/ui/LogoWall.d.ts +31 -0
- package/dist/ui/RatingsSummary.d.ts +44 -0
- package/dist/ui/SpecSheet.d.ts +47 -0
- package/package.json +1 -1
- package/src/commerce/ConditionScale.tsx +114 -0
- package/src/commerce/FacetSidebar.tsx +222 -0
- package/src/commerce/PriceBlock.tsx +133 -0
- package/src/commerce/index.ts +9 -0
- package/src/index.ts +5 -1
- package/src/ui/AnnouncementBar.tsx +124 -0
- package/src/ui/LogoWall.tsx +94 -0
- package/src/ui/RatingsSummary.tsx +128 -0
- package/src/ui/SpecSheet.tsx +89 -0
- package/dist/Checkbox-DIzLQltI.js +0 -431
package/README.md
CHANGED
|
@@ -190,11 +190,11 @@ full list):
|
|
|
190
190
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
191
191
|
| Forms | `Input`, `Select`, `Checkbox`, `DateField`, `Combobox`, `TagInput`, `Slider`, `NumberInput` |
|
|
192
192
|
| Overlays | `Modal`, `Drawer`, `Popover`, `Tooltip`, `AlertDialog`, `ContextMenu`, `HoverCard` |
|
|
193
|
-
| Data & display | `Table`, `DataGrid`, `Tree`, `Calendar`, `Timeline`, `Stat`, `Descriptions`, `CalendarHeatmap`
|
|
193
|
+
| Data & display | `Table`, `DataGrid`, `Tree`, `Calendar`, `Timeline`, `Stat`, `Descriptions`, `SpecSheet`, `RatingsSummary`, `LogoWall`, `CalendarHeatmap` |
|
|
194
194
|
| Navigation | `Tabs`, `Breadcrumb`, `Pagination`, `Command`, `Anchor`, `Stepper`, `BottomNavigation` |
|
|
195
|
-
| Feedback | `Alert`, `Toast`, `Progress`, `Skeleton`, `Empty`, `Result`, `NotificationCenter`
|
|
195
|
+
| Feedback | `Alert`, `AnnouncementBar`, `Toast`, `Progress`, `Skeleton`, `Empty`, `Result`, `NotificationCenter` |
|
|
196
196
|
| Layout | `AppShell`, `SpaceBackground`, `ThemedScenery`, `Card`, `Splitter`, `Affix`, `NavGroup` |
|
|
197
|
-
| Commerce | `ProductCard`, `ProductGrid`, `PriceTag`, `QuantityStepper`, `CartLine`, `CartSummary`, `FilterGroup`
|
|
197
|
+
| Commerce | `ProductCard`, `ProductGrid`, `PriceTag`, `PriceBlock`, `QuantityStepper`, `CartLine`, `CartSummary`, `FilterGroup`, `FacetSidebar`, `ConditionScale` |
|
|
198
198
|
| Charts | `Sparkline`, `BarChart`, `DonutChart` (native SVG, theme-tinted) |
|
|
199
199
|
| Motion | `ScrambleText`, `TextReveal`, `Typewriter`, `GradientText`, `HoldToConfirm`, `LoadingDots`, `FillText`, `Curtain`, `Parallax`, `ScrollProgress`, `Ripple`, `Magnetic`, `TiltCard`, `Spotlight`, `NotificationStack`, `MultiStateBadge`, `NowPlaying`, `Expandable`, `flyToCart` |
|
|
200
200
|
|
|
@@ -254,15 +254,23 @@ ships **in the package**, so it works from `node_modules` without visiting the d
|
|
|
254
254
|
- **Typed API + examples** — every export has JSDoc with an `@example` in the
|
|
255
255
|
shipped `.d.ts` (`node_modules/@a4ui/core/dist/index.d.ts`), so editor
|
|
256
256
|
autocomplete and agents get the props and usage inline.
|
|
257
|
-
- **`llms.txt`** — a machine-readable summary of every component
|
|
258
|
-
https://a4ui.pages.dev/llms.txt
|
|
257
|
+
- **`llms.txt`** — a machine-readable summary of every component **plus the
|
|
258
|
+
"Spatial Glass" recipe**: https://a4ui.pages.dev/llms.txt
|
|
259
|
+
- **[Spatial Glass guide](./SPATIAL-GLASS.md)** — how to get the A4ui _look_
|
|
260
|
+
(Aurora backdrop, glass surfaces, the cursor light, tasteful motion, Expandable),
|
|
261
|
+
not just wire up components. Read this before building a page.
|
|
259
262
|
|
|
260
263
|
Drop this into your project's `AGENTS.md` / `CLAUDE.md` to prime your agent:
|
|
261
264
|
|
|
262
|
-
> This project uses **@a4ui/core** (SolidJS design system). Import
|
|
263
|
-
> named exports from `@a4ui/core`; import `@a4ui/core/styles.css`
|
|
264
|
-
> entry; add `@a4ui/core/preset` to `tailwind.config` (optional).
|
|
265
|
-
> with JSDoc/`@example` in the package's `.d.ts`.
|
|
265
|
+
> This project uses **@a4ui/core** (SolidJS "Spatial Glass" design system). Import
|
|
266
|
+
> components as named exports from `@a4ui/core`; import `@a4ui/core/styles.css`
|
|
267
|
+
> once in the app entry; add `@a4ui/core/preset` to `tailwind.config` (optional).
|
|
268
|
+
> Props are typed with JSDoc/`@example` in the package's `.d.ts`.
|
|
269
|
+
> **For the A4ui look, follow the Spatial Glass recipe** (in `llms.txt` and
|
|
270
|
+
> https://a4ui.pages.dev/#/guide-spatial-glass): put an `<Aurora/>` backdrop
|
|
271
|
+
> behind a transparent root, use `<Card glass>` surfaces (glass reads only over a
|
|
272
|
+
> colored backdrop), keep `pointerGlow` on for the cursor light, add restrained
|
|
273
|
+
> motion, and use `Expandable` for galleries. Full component list + recipe:
|
|
266
274
|
> https://a4ui.pages.dev/llms.txt
|
|
267
275
|
|
|
268
276
|
## Develop
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { spread as y, mergeProps as b, insert as d, createComponent as h, template as m, effect as v, className as p, use as $, delegateEvents as k, setAttribute as C } from "solid-js/web";
|
|
2
|
+
import { splitProps as x, Show as L, createSignal as E, createEffect as Q, untrack as F, onCleanup as S, onMount as R, For as I } from "solid-js";
|
|
3
|
+
import { c } from "./cn-B6yFEsav.js";
|
|
4
|
+
import { animate as A, inView as J } from "motion";
|
|
5
|
+
import { Star as B, ChevronDown as W } from "lucide-solid";
|
|
6
|
+
import { NumberField as _ } from "@kobalte/core/number-field";
|
|
7
|
+
var Z = /* @__PURE__ */ m('<span class="relative flex h-1.5 w-1.5"aria-hidden=true><span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-current opacity-75"></span><span class="relative inline-flex h-1.5 w-1.5 rounded-full bg-current">'), ee = /* @__PURE__ */ m("<span>");
|
|
8
|
+
const te = {
|
|
9
|
+
neutral: "bg-muted text-muted-foreground ring-border",
|
|
10
|
+
success: "bg-emerald-500/15 text-emerald-600 ring-emerald-500/30",
|
|
11
|
+
warning: "bg-amber-500/15 text-amber-600 ring-amber-500/30",
|
|
12
|
+
danger: "bg-rose-500/15 text-rose-600 ring-rose-500/30",
|
|
13
|
+
info: "bg-sky-500/15 text-sky-600 ring-sky-500/30"
|
|
14
|
+
}, re = "badge inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold ring-1 ring-inset whitespace-nowrap";
|
|
15
|
+
function Le(e) {
|
|
16
|
+
const [t, n] = x(e, ["tone", "class", "children", "pulse"]);
|
|
17
|
+
return (() => {
|
|
18
|
+
var r = ee();
|
|
19
|
+
return y(r, b({
|
|
20
|
+
get class() {
|
|
21
|
+
return c(re, te[t.tone ?? "neutral"], t.class);
|
|
22
|
+
}
|
|
23
|
+
}, n), !1, !0), d(r, h(L, {
|
|
24
|
+
get when() {
|
|
25
|
+
return t.pulse;
|
|
26
|
+
},
|
|
27
|
+
get children() {
|
|
28
|
+
return Z();
|
|
29
|
+
}
|
|
30
|
+
}), null), d(r, () => t.children, null), r;
|
|
31
|
+
})();
|
|
32
|
+
}
|
|
33
|
+
const Y = "a4ui-effects";
|
|
34
|
+
function U() {
|
|
35
|
+
try {
|
|
36
|
+
return window.localStorage;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function ne() {
|
|
42
|
+
var t;
|
|
43
|
+
try {
|
|
44
|
+
if (new URLSearchParams(window.location.search).get("calm") === "1") return !1;
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
const e = (t = U()) == null ? void 0 : t.getItem(Y);
|
|
48
|
+
return e === null ? !0 : e === "1";
|
|
49
|
+
}
|
|
50
|
+
function V(e) {
|
|
51
|
+
typeof document > "u" || document.documentElement.classList.toggle("calm", !e);
|
|
52
|
+
}
|
|
53
|
+
const z = ne();
|
|
54
|
+
V(z);
|
|
55
|
+
const [G, ae] = E(z);
|
|
56
|
+
function Re() {
|
|
57
|
+
return G;
|
|
58
|
+
}
|
|
59
|
+
function oe() {
|
|
60
|
+
return !G();
|
|
61
|
+
}
|
|
62
|
+
function Ae(e) {
|
|
63
|
+
var t;
|
|
64
|
+
(t = U()) == null || t.setItem(Y, e ? "1" : "0"), V(e), ae(e);
|
|
65
|
+
}
|
|
66
|
+
const ie = "(prefers-reduced-motion: reduce)";
|
|
67
|
+
function le() {
|
|
68
|
+
return typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia(ie).matches;
|
|
69
|
+
}
|
|
70
|
+
const X = "a4ui-motion-forced";
|
|
71
|
+
function j() {
|
|
72
|
+
try {
|
|
73
|
+
return window.localStorage;
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
var P;
|
|
79
|
+
const [M, se] = E(((P = j()) == null ? void 0 : P.getItem(X)) === "1");
|
|
80
|
+
typeof document < "u" && document.documentElement.classList.toggle("force-motion", F(M));
|
|
81
|
+
function Me() {
|
|
82
|
+
return M;
|
|
83
|
+
}
|
|
84
|
+
function Te(e) {
|
|
85
|
+
var t;
|
|
86
|
+
(t = j()) == null || t.setItem(X, e ? "1" : "0"), typeof document < "u" && document.documentElement.classList.toggle("force-motion", e), se(e);
|
|
87
|
+
}
|
|
88
|
+
function w() {
|
|
89
|
+
return oe() || le() && !M();
|
|
90
|
+
}
|
|
91
|
+
function Oe(e, t = 600) {
|
|
92
|
+
const [n, r] = E(0);
|
|
93
|
+
let o;
|
|
94
|
+
return Q(() => {
|
|
95
|
+
const a = e();
|
|
96
|
+
if (o == null || o.stop(), w()) {
|
|
97
|
+
r(a);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const i = F(n);
|
|
101
|
+
i !== a && (o = A(i, a, {
|
|
102
|
+
duration: t / 1e3,
|
|
103
|
+
ease: "easeOut",
|
|
104
|
+
onUpdate: (u) => r(u)
|
|
105
|
+
}));
|
|
106
|
+
}), S(() => o == null ? void 0 : o.stop()), n;
|
|
107
|
+
}
|
|
108
|
+
function De(e, t = {}) {
|
|
109
|
+
w() || A(
|
|
110
|
+
e,
|
|
111
|
+
{ opacity: [0, 1], y: [t.y ?? 8, 0] },
|
|
112
|
+
{ duration: t.duration ?? 0.32, delay: t.delay ?? 0, ease: "easeOut" }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function Ie(e, t = {}) {
|
|
116
|
+
if (w()) return () => {
|
|
117
|
+
};
|
|
118
|
+
e.style.opacity = "0";
|
|
119
|
+
let n = !1;
|
|
120
|
+
return J(
|
|
121
|
+
e,
|
|
122
|
+
() => {
|
|
123
|
+
n || (n = !0, A(
|
|
124
|
+
e,
|
|
125
|
+
{ opacity: [0, 1], y: [t.y ?? 16, 0] },
|
|
126
|
+
{ duration: t.duration ?? 0.5, ease: "easeOut" }
|
|
127
|
+
));
|
|
128
|
+
},
|
|
129
|
+
{ amount: t.amount ?? 0.2 }
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
var ce = /* @__PURE__ */ m("<span>");
|
|
133
|
+
function H(e, t, n = {}) {
|
|
134
|
+
if (w()) return;
|
|
135
|
+
const r = e.getBoundingClientRect(), o = t.clientX - r.left, a = t.clientY - r.top, i = 2 * Math.max(Math.hypot(o, a), Math.hypot(r.width - o, a), Math.hypot(o, r.height - a), Math.hypot(r.width - o, r.height - a)), u = document.createElement("span");
|
|
136
|
+
u.setAttribute("aria-hidden", "true"), Object.assign(u.style, {
|
|
137
|
+
position: "absolute",
|
|
138
|
+
left: `${o}px`,
|
|
139
|
+
top: `${a}px`,
|
|
140
|
+
width: `${i}px`,
|
|
141
|
+
height: `${i}px`,
|
|
142
|
+
borderRadius: "9999px",
|
|
143
|
+
background: n.color ?? "currentColor",
|
|
144
|
+
pointerEvents: "none",
|
|
145
|
+
transform: "translate(-50%,-50%) scale(0)"
|
|
146
|
+
}), e.appendChild(u);
|
|
147
|
+
const s = u.animate([{
|
|
148
|
+
transform: "translate(-50%,-50%) scale(0)",
|
|
149
|
+
opacity: String(n.opacity ?? 0.3)
|
|
150
|
+
}, {
|
|
151
|
+
transform: "translate(-50%,-50%) scale(1)",
|
|
152
|
+
opacity: "0"
|
|
153
|
+
}], {
|
|
154
|
+
duration: 600,
|
|
155
|
+
easing: "ease-out"
|
|
156
|
+
}), l = () => u.remove();
|
|
157
|
+
s.finished.then(l).catch(l);
|
|
158
|
+
}
|
|
159
|
+
function Be(e) {
|
|
160
|
+
let t;
|
|
161
|
+
const n = (r) => {
|
|
162
|
+
t && H(t, r, {
|
|
163
|
+
color: e.color,
|
|
164
|
+
opacity: e.opacity
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
return (() => {
|
|
168
|
+
var r = ce();
|
|
169
|
+
r.$$pointerdown = n;
|
|
170
|
+
var o = t;
|
|
171
|
+
return typeof o == "function" ? $(o, r) : t = r, d(r, () => e.children), v(() => p(r, c("relative inline-block overflow-hidden", e.class))), r;
|
|
172
|
+
})();
|
|
173
|
+
}
|
|
174
|
+
k(["pointerdown"]);
|
|
175
|
+
var ue = /* @__PURE__ */ m("<a>"), de = /* @__PURE__ */ m("<button>");
|
|
176
|
+
const fe = {
|
|
177
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
178
|
+
secondary: "bg-muted text-foreground hover:bg-muted/70",
|
|
179
|
+
outline: "border border-border bg-transparent text-foreground hover:bg-muted",
|
|
180
|
+
ghost: "bg-transparent text-foreground hover:bg-muted"
|
|
181
|
+
}, ge = "inline-flex items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-[color,background-color,transform] duration-150 active:scale-[0.97] focus:outline-none focus:ring-2 focus:ring-ring disabled:pointer-events-none disabled:opacity-50";
|
|
182
|
+
function Ne(e) {
|
|
183
|
+
const [t, n] = x(e, ["variant", "class", "type", "children", "ripple", "onPointerDown", "href", "target", "rel"]), r = () => c(ge, fe[t.variant ?? "primary"], t.ripple && "relative overflow-hidden", t.class), o = (a) => {
|
|
184
|
+
t.ripple && H(a.currentTarget, a, {
|
|
185
|
+
opacity: 0.35
|
|
186
|
+
});
|
|
187
|
+
const i = t.onPointerDown;
|
|
188
|
+
typeof i == "function" ? i(a) : i && i[0](i[1], a);
|
|
189
|
+
};
|
|
190
|
+
return h(L, {
|
|
191
|
+
get when() {
|
|
192
|
+
return t.href !== void 0;
|
|
193
|
+
},
|
|
194
|
+
get fallback() {
|
|
195
|
+
return (() => {
|
|
196
|
+
var a = de();
|
|
197
|
+
return a.$$pointerdown = o, y(a, b({
|
|
198
|
+
get type() {
|
|
199
|
+
return t.type ?? "button";
|
|
200
|
+
},
|
|
201
|
+
get class() {
|
|
202
|
+
return r();
|
|
203
|
+
}
|
|
204
|
+
}, n), !1, !0), d(a, () => t.children), a;
|
|
205
|
+
})();
|
|
206
|
+
},
|
|
207
|
+
get children() {
|
|
208
|
+
var a = ue();
|
|
209
|
+
return a.$$pointerdown = o, y(a, b({
|
|
210
|
+
get href() {
|
|
211
|
+
return t.href;
|
|
212
|
+
},
|
|
213
|
+
get target() {
|
|
214
|
+
return t.target;
|
|
215
|
+
},
|
|
216
|
+
get rel() {
|
|
217
|
+
return t.rel;
|
|
218
|
+
},
|
|
219
|
+
get class() {
|
|
220
|
+
return r();
|
|
221
|
+
}
|
|
222
|
+
}, n), !1, !0), d(a, () => t.children), a;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
k(["pointerdown"]);
|
|
227
|
+
var me = /* @__PURE__ */ m("<div>");
|
|
228
|
+
function K(e, t = {}) {
|
|
229
|
+
if (w()) return () => {
|
|
230
|
+
};
|
|
231
|
+
const n = document.createElement("span");
|
|
232
|
+
n.setAttribute("aria-hidden", "true"), n.className = "pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300", e.appendChild(n);
|
|
233
|
+
const r = t.size ?? 180, o = t.color ?? "hsl(var(--primary))", a = (l, f) => {
|
|
234
|
+
n.style.background = `radial-gradient(${r}px circle at ${l}px ${f}px, color-mix(in srgb, ${o} 30%, transparent), transparent 70%)`;
|
|
235
|
+
}, i = (l) => {
|
|
236
|
+
const f = e.getBoundingClientRect();
|
|
237
|
+
a(l.clientX - f.left, l.clientY - f.top);
|
|
238
|
+
}, u = () => {
|
|
239
|
+
n.style.opacity = "1";
|
|
240
|
+
}, s = () => {
|
|
241
|
+
n.style.opacity = "0";
|
|
242
|
+
};
|
|
243
|
+
return e.addEventListener("pointermove", i), e.addEventListener("pointerenter", u), e.addEventListener("pointerleave", s), () => {
|
|
244
|
+
e.removeEventListener("pointermove", i), e.removeEventListener("pointerenter", u), e.removeEventListener("pointerleave", s), n.remove();
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function Pe(e) {
|
|
248
|
+
let t;
|
|
249
|
+
return R(() => {
|
|
250
|
+
const n = K(t, {
|
|
251
|
+
color: e.color,
|
|
252
|
+
size: e.size
|
|
253
|
+
});
|
|
254
|
+
S(n);
|
|
255
|
+
}), (() => {
|
|
256
|
+
var n = me(), r = t;
|
|
257
|
+
return typeof r == "function" ? $(r, n) : t = n, d(n, () => e.children), v(() => p(n, c("relative overflow-hidden", e.class))), n;
|
|
258
|
+
})();
|
|
259
|
+
}
|
|
260
|
+
var he = /* @__PURE__ */ m("<div style=perspective:800px><div style=transform-style:preserve-3d>");
|
|
261
|
+
function q(e, t, n = {}) {
|
|
262
|
+
if (w()) return () => {
|
|
263
|
+
};
|
|
264
|
+
t.style.willChange = "transform";
|
|
265
|
+
const r = (a) => {
|
|
266
|
+
const i = e.getBoundingClientRect(), u = (a.clientX - i.left) / i.width - 0.5, s = (a.clientY - i.top) / i.height - 0.5, l = n.max ?? 10;
|
|
267
|
+
t.style.transition = "transform 0.15s ease-out", t.style.transform = `perspective(800px) rotateX(${(s * -2 * l).toFixed(2)}deg) rotateY(${(u * 2 * l).toFixed(2)}deg) scale(1.02)`;
|
|
268
|
+
}, o = () => {
|
|
269
|
+
t.style.transition = "transform 0.4s ease", t.style.transform = "perspective(800px) rotateX(0deg) rotateY(0deg) scale(1)";
|
|
270
|
+
};
|
|
271
|
+
return e.addEventListener("pointermove", r), e.addEventListener("pointerleave", o), () => {
|
|
272
|
+
e.removeEventListener("pointermove", r), e.removeEventListener("pointerleave", o), t.style.transform = "", t.style.transition = "", t.style.willChange = "";
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function Fe(e) {
|
|
276
|
+
let t, n;
|
|
277
|
+
return R(() => {
|
|
278
|
+
const r = q(t, n, {
|
|
279
|
+
max: e.max
|
|
280
|
+
});
|
|
281
|
+
S(r);
|
|
282
|
+
}), (() => {
|
|
283
|
+
var r = he(), o = r.firstChild, a = t;
|
|
284
|
+
typeof a == "function" ? $(a, r) : t = r;
|
|
285
|
+
var i = n;
|
|
286
|
+
return typeof i == "function" ? $(i, o) : n = o, d(o, () => e.children), v(() => p(r, c("inline-block", e.class))), r;
|
|
287
|
+
})();
|
|
288
|
+
}
|
|
289
|
+
var T = /* @__PURE__ */ m("<div>"), ve = /* @__PURE__ */ m("<h2>");
|
|
290
|
+
function Ye(e) {
|
|
291
|
+
const [t, n] = x(e, ["class", "children", "glass", "glow", "tilt", "spotlight"]), r = () => (t.glow ?? t.glass) === !0;
|
|
292
|
+
let o;
|
|
293
|
+
return R(() => {
|
|
294
|
+
const a = [t.tilt && o ? q(o, o) : null, t.spotlight && o ? K(o) : null].filter((i) => i !== null);
|
|
295
|
+
S(() => a.forEach((i) => i()));
|
|
296
|
+
}), (() => {
|
|
297
|
+
var a = T(), i = o;
|
|
298
|
+
return typeof i == "function" ? $(i, a) : o = a, y(a, b({
|
|
299
|
+
get class() {
|
|
300
|
+
return c(t.glass ? "card rounded-xl text-card-foreground" : "rounded-xl border border-border bg-card text-card-foreground shadow-sm", r() && "glow-edge", t.spotlight && "relative overflow-hidden", t.tilt && "will-change-transform", t.class);
|
|
301
|
+
}
|
|
302
|
+
}, n), !1, !0), d(a, () => t.children), a;
|
|
303
|
+
})();
|
|
304
|
+
}
|
|
305
|
+
function Ue(e) {
|
|
306
|
+
const [t, n] = x(e, ["class", "children"]);
|
|
307
|
+
return (() => {
|
|
308
|
+
var r = T();
|
|
309
|
+
return y(r, b({
|
|
310
|
+
get class() {
|
|
311
|
+
return c("flex flex-col space-y-1.5 p-6", t.class);
|
|
312
|
+
}
|
|
313
|
+
}, n), !1, !0), d(r, () => t.children), r;
|
|
314
|
+
})();
|
|
315
|
+
}
|
|
316
|
+
function Ve(e) {
|
|
317
|
+
const [t, n] = x(e, ["class", "children"]);
|
|
318
|
+
return (() => {
|
|
319
|
+
var r = ve();
|
|
320
|
+
return y(r, b({
|
|
321
|
+
get class() {
|
|
322
|
+
return c("text-lg font-semibold leading-none tracking-tight", t.class);
|
|
323
|
+
}
|
|
324
|
+
}, n), !1, !0), d(r, () => t.children), r;
|
|
325
|
+
})();
|
|
326
|
+
}
|
|
327
|
+
function ze(e) {
|
|
328
|
+
const [t, n] = x(e, ["class", "children"]);
|
|
329
|
+
return (() => {
|
|
330
|
+
var r = T();
|
|
331
|
+
return y(r, b({
|
|
332
|
+
get class() {
|
|
333
|
+
return c("p-6 pt-0", t.class);
|
|
334
|
+
}
|
|
335
|
+
}, n), !1, !0), d(r, () => t.children), r;
|
|
336
|
+
})();
|
|
337
|
+
}
|
|
338
|
+
var pe = /* @__PURE__ */ m("<div role=radiogroup>"), ye = /* @__PURE__ */ m("<div role=img>"), be = /* @__PURE__ */ m('<button type=button role=radio class="rounded outline-none focus-visible:ring-2 focus-visible:ring-ring/40">');
|
|
339
|
+
function Ge(e) {
|
|
340
|
+
const [t, n] = E(0), r = () => e.max ?? 5, o = () => Array.from({
|
|
341
|
+
length: r()
|
|
342
|
+
}, (s, l) => l + 1), a = () => t() > 0 ? t() : e.value, i = (s) => {
|
|
343
|
+
var l;
|
|
344
|
+
e.readonly || (l = e.onChange) == null || l.call(e, s);
|
|
345
|
+
}, u = (s) => c("h-5 w-5 transition-colors", s <= a() ? "fill-primary text-primary" : "fill-transparent text-muted-foreground");
|
|
346
|
+
return h(L, {
|
|
347
|
+
get when() {
|
|
348
|
+
return !e.readonly;
|
|
349
|
+
},
|
|
350
|
+
get fallback() {
|
|
351
|
+
return (() => {
|
|
352
|
+
var s = ye();
|
|
353
|
+
return d(s, h(I, {
|
|
354
|
+
get each() {
|
|
355
|
+
return o();
|
|
356
|
+
},
|
|
357
|
+
children: (l) => h(B, {
|
|
358
|
+
get class() {
|
|
359
|
+
return u(l);
|
|
360
|
+
},
|
|
361
|
+
"aria-hidden": "true"
|
|
362
|
+
})
|
|
363
|
+
})), v((l) => {
|
|
364
|
+
var f = c("inline-flex items-center gap-1", e.class), g = `Rating ${e.value} of ${r()}`;
|
|
365
|
+
return f !== l.e && p(s, l.e = f), g !== l.t && C(s, "aria-label", l.t = g), l;
|
|
366
|
+
}, {
|
|
367
|
+
e: void 0,
|
|
368
|
+
t: void 0
|
|
369
|
+
}), s;
|
|
370
|
+
})();
|
|
371
|
+
},
|
|
372
|
+
get children() {
|
|
373
|
+
var s = pe();
|
|
374
|
+
return s.addEventListener("mouseleave", () => n(0)), d(s, h(I, {
|
|
375
|
+
get each() {
|
|
376
|
+
return o();
|
|
377
|
+
},
|
|
378
|
+
children: (l) => (() => {
|
|
379
|
+
var f = be();
|
|
380
|
+
return f.$$keydown = (g) => {
|
|
381
|
+
g.key === "ArrowRight" || g.key === "ArrowUp" ? (g.preventDefault(), i(Math.min(e.value + 1, r()))) : (g.key === "ArrowLeft" || g.key === "ArrowDown") && (g.preventDefault(), i(Math.max(e.value - 1, 0)));
|
|
382
|
+
}, f.$$click = () => i(l), f.addEventListener("blur", () => n(0)), f.addEventListener("focus", () => n(l)), f.addEventListener("mouseenter", () => n(l)), d(f, h(B, {
|
|
383
|
+
get class() {
|
|
384
|
+
return u(l);
|
|
385
|
+
},
|
|
386
|
+
"aria-hidden": "true"
|
|
387
|
+
})), v((g) => {
|
|
388
|
+
var O = e.value === l, D = `Rate ${l} of ${r()}`;
|
|
389
|
+
return O !== g.e && C(f, "aria-checked", g.e = O), D !== g.t && C(f, "aria-label", g.t = D), g;
|
|
390
|
+
}, {
|
|
391
|
+
e: void 0,
|
|
392
|
+
t: void 0
|
|
393
|
+
}), f;
|
|
394
|
+
})()
|
|
395
|
+
})), v(() => p(s, c("inline-flex items-center gap-1", e.class))), s;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
k(["click", "keydown"]);
|
|
400
|
+
var xe = /* @__PURE__ */ m('<label><input type=checkbox class="h-4 w-4 rounded border-input accent-primary">');
|
|
401
|
+
function Xe(e) {
|
|
402
|
+
return (() => {
|
|
403
|
+
var t = xe(), n = t.firstChild;
|
|
404
|
+
return n.addEventListener("change", (r) => e.onChange(r.currentTarget.checked)), d(t, () => e.label, null), v(() => p(t, c("inline-flex items-center gap-2 text-sm text-foreground", e.class))), v(() => n.checked = e.checked), t;
|
|
405
|
+
})();
|
|
406
|
+
}
|
|
407
|
+
var we = /* @__PURE__ */ m('<div><button type=button class="flex w-full items-center justify-between rounded-md px-3 py-2 text-sm font-medium text-foreground hover:bg-muted"></button><div><div class=overflow-hidden><div class="px-3 py-2 text-sm text-muted-foreground">');
|
|
408
|
+
function je(e) {
|
|
409
|
+
return (() => {
|
|
410
|
+
var t = we(), n = t.firstChild, r = n.nextSibling, o = r.firstChild, a = o.firstChild;
|
|
411
|
+
return n.$$click = () => e.onOpenChange(!e.open), d(n, () => e.title, null), d(n, h(W, {
|
|
412
|
+
get class() {
|
|
413
|
+
return c("h-4 w-4 shrink-0 transition-transform duration-200", e.open && "rotate-180");
|
|
414
|
+
}
|
|
415
|
+
}), null), d(a, () => e.children), v((i) => {
|
|
416
|
+
var u = c(e.class), s = e.open, l = c("grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none", e.open ? "grid-rows-[1fr]" : "grid-rows-[0fr]");
|
|
417
|
+
return u !== i.e && p(t, i.e = u), s !== i.t && C(n, "aria-expanded", i.t = s), l !== i.a && p(r, i.a = l), i;
|
|
418
|
+
}, {
|
|
419
|
+
e: void 0,
|
|
420
|
+
t: void 0,
|
|
421
|
+
a: void 0
|
|
422
|
+
}), t;
|
|
423
|
+
})();
|
|
424
|
+
}
|
|
425
|
+
k(["click"]);
|
|
426
|
+
const N = "grid h-9 w-9 shrink-0 place-items-center border border-input bg-background text-foreground transition-colors hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50";
|
|
427
|
+
function He(e) {
|
|
428
|
+
return h(_, {
|
|
429
|
+
get rawValue() {
|
|
430
|
+
return e.value;
|
|
431
|
+
},
|
|
432
|
+
onRawValueChange: (t) => e.onChange(t),
|
|
433
|
+
get minValue() {
|
|
434
|
+
return e.min;
|
|
435
|
+
},
|
|
436
|
+
get maxValue() {
|
|
437
|
+
return e.max;
|
|
438
|
+
},
|
|
439
|
+
get class() {
|
|
440
|
+
return c("inline-flex items-stretch", e.class);
|
|
441
|
+
},
|
|
442
|
+
get children() {
|
|
443
|
+
return [h(_.DecrementTrigger, {
|
|
444
|
+
get class() {
|
|
445
|
+
return c(N, "rounded-l-md");
|
|
446
|
+
},
|
|
447
|
+
"aria-label": "Decrement",
|
|
448
|
+
children: "−"
|
|
449
|
+
}), h(_.Input, {
|
|
450
|
+
class: "w-16 border-y border-input bg-background px-3 py-2 text-center text-sm text-foreground outline-none transition-colors a4-field"
|
|
451
|
+
}), h(_.IncrementTrigger, {
|
|
452
|
+
get class() {
|
|
453
|
+
return c(N, "rounded-r-md");
|
|
454
|
+
},
|
|
455
|
+
"aria-label": "Increment",
|
|
456
|
+
children: "+"
|
|
457
|
+
})];
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
export {
|
|
462
|
+
Le as B,
|
|
463
|
+
Ye as C,
|
|
464
|
+
He as N,
|
|
465
|
+
Ge as R,
|
|
466
|
+
Pe as S,
|
|
467
|
+
Fe as T,
|
|
468
|
+
Ne as a,
|
|
469
|
+
Xe as b,
|
|
470
|
+
je as c,
|
|
471
|
+
Oe as d,
|
|
472
|
+
ze as e,
|
|
473
|
+
De as f,
|
|
474
|
+
Ue as g,
|
|
475
|
+
Ve as h,
|
|
476
|
+
Ae as i,
|
|
477
|
+
Be as j,
|
|
478
|
+
K as k,
|
|
479
|
+
q as l,
|
|
480
|
+
w as m,
|
|
481
|
+
oe as n,
|
|
482
|
+
Te as o,
|
|
483
|
+
le as p,
|
|
484
|
+
Me as q,
|
|
485
|
+
Ie as r,
|
|
486
|
+
H as s,
|
|
487
|
+
Re as u
|
|
488
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ConditionTier {
|
|
3
|
+
/** Minimum score (inclusive) at which this tier applies. */
|
|
4
|
+
min: number;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ConditionScaleProps {
|
|
8
|
+
/** Current score. */
|
|
9
|
+
value: number;
|
|
10
|
+
/** Top of the scale. Defaults to 10. */
|
|
11
|
+
max?: number;
|
|
12
|
+
/** Bottom of the scale. Defaults to 1. */
|
|
13
|
+
min?: number;
|
|
14
|
+
/** Tier labels, highest `min` first or any order — component sorts. Defaults to a sensible New→Poor scale. */
|
|
15
|
+
tiers?: ConditionTier[];
|
|
16
|
+
/** Show numeric value (e.g. "8.5 / 10"). Defaults to true. */
|
|
17
|
+
showValue?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Horizontal grading meter (e.g. a pre-owned item's 1–10 condition score)
|
|
22
|
+
* with a marker at the current value, the resolved tier label, and endpoint
|
|
23
|
+
* labels under the track.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <ConditionScale value={8} />
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <ConditionScale
|
|
33
|
+
* value={62}
|
|
34
|
+
* min={0}
|
|
35
|
+
* max={100}
|
|
36
|
+
* tiers={[
|
|
37
|
+
* { min: 90, label: 'Like New' },
|
|
38
|
+
* { min: 70, label: 'Lightly Used' },
|
|
39
|
+
* { min: 40, label: 'Worn' },
|
|
40
|
+
* { min: 0, label: 'Heavily Worn' },
|
|
41
|
+
* ]}
|
|
42
|
+
* />
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function ConditionScale(props: ConditionScaleProps): JSX.Element;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
import { FacetOption } from './FilterGroup';
|
|
3
|
+
export interface FacetSection {
|
|
4
|
+
key: string;
|
|
5
|
+
title: string;
|
|
6
|
+
options: FacetOption[];
|
|
7
|
+
/** Collapsible section. Defaults to true. */
|
|
8
|
+
collapsible?: boolean;
|
|
9
|
+
/** Open by default. Defaults to true. */
|
|
10
|
+
defaultOpen?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface FacetPrice {
|
|
13
|
+
min: number;
|
|
14
|
+
max: number;
|
|
15
|
+
value: [number, number];
|
|
16
|
+
}
|
|
17
|
+
export interface FacetSidebarProps {
|
|
18
|
+
sections: FacetSection[];
|
|
19
|
+
/** Selected values per section key. */
|
|
20
|
+
selected: Record<string, string[]>;
|
|
21
|
+
onChange: (key: string, values: string[]) => void;
|
|
22
|
+
/** Optional price range facet. */
|
|
23
|
+
price?: FacetPrice;
|
|
24
|
+
onPriceChange?: (range: [number, number]) => void;
|
|
25
|
+
/** Result count shown in the header. */
|
|
26
|
+
resultCount?: number;
|
|
27
|
+
onClearAll?: () => void;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Full faceted-filter sidebar panel: composes {@link FilterGroup} checkbox
|
|
32
|
+
* sections (each optionally collapsible) with a min/max price range, a row of
|
|
33
|
+
* removable active-filter chips, a result count, and a clear-all action.
|
|
34
|
+
* Fully controlled — the caller owns `selected` and `price` state.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* const [selected, setSelected] = createSignal<Record<string, string[]>>({ type: ['mug'] })
|
|
39
|
+
* const [price, setPrice] = createSignal<[number, number]>([0, 100])
|
|
40
|
+
* <FacetSidebar
|
|
41
|
+
* class="w-64"
|
|
42
|
+
* sections={[
|
|
43
|
+
* { key: 'type', title: 'Type', options: [{ value: 'mug', label: 'Mug', count: 4 }, { value: 'plate', label: 'Plate', count: 2 }] },
|
|
44
|
+
* { key: 'color', title: 'Color', options: [{ value: 'blue', label: 'Blue', count: 3 }, { value: 'green', label: 'Green', count: 1 }] },
|
|
45
|
+
* ]}
|
|
46
|
+
* selected={selected()}
|
|
47
|
+
* onChange={(key, values) => setSelected({ ...selected(), [key]: values })}
|
|
48
|
+
* price={{ min: 0, max: 100, value: price() }}
|
|
49
|
+
* onPriceChange={setPrice}
|
|
50
|
+
* resultCount={6}
|
|
51
|
+
* onClearAll={() => { setSelected({}); setPrice([0, 100]) }}
|
|
52
|
+
* />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function FacetSidebar(props: FacetSidebarProps): JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** Text size of a {@link PriceBlock}'s main amount. Defaults to `'lg'`. */
|
|
3
|
+
export type PriceBlockSize = 'md' | 'lg';
|
|
4
|
+
export interface PriceBlockCoupon {
|
|
5
|
+
code: string;
|
|
6
|
+
/** Flat amount off (currency units). If omitted, no reduction is computed. */
|
|
7
|
+
amount?: number;
|
|
8
|
+
/** Optional label override. */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PriceBlockFinancing {
|
|
12
|
+
/** Number of monthly installments. */
|
|
13
|
+
months: number;
|
|
14
|
+
/** Annual APR as a decimal, e.g. 0.1 for 10%. Omit/0 = interest-free split. */
|
|
15
|
+
apr?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface PriceBlockProps {
|
|
18
|
+
amount: number;
|
|
19
|
+
/** Original price; struck through and used for the savings badge when > amount. */
|
|
20
|
+
compareAt?: number;
|
|
21
|
+
/** ISO 4217 currency code. Defaults to `'USD'`. */
|
|
22
|
+
currency?: string;
|
|
23
|
+
/** BCP 47 locale used for formatting. Defaults to `'en-US'`. */
|
|
24
|
+
locale?: string;
|
|
25
|
+
/** Coupon; renders a chip and the after-coupon price. */
|
|
26
|
+
coupon?: PriceBlockCoupon;
|
|
27
|
+
/** Financing estimate; renders "As low as $X/mo". */
|
|
28
|
+
financing?: PriceBlockFinancing;
|
|
29
|
+
/** Extra note under the price (e.g. a wire-transfer discount line). */
|
|
30
|
+
note?: JSX.Element;
|
|
31
|
+
/** Text size of the main price. Defaults to `'lg'`. */
|
|
32
|
+
size?: PriceBlockSize;
|
|
33
|
+
class?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Full price display: main amount, optional struck-through compare-at price
|
|
37
|
+
* with a savings badge, a coupon chip with the after-coupon price, and an
|
|
38
|
+
* optional financing estimate ("As low as $X/mo").
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <PriceBlock
|
|
43
|
+
* amount={249}
|
|
44
|
+
* compareAt={329}
|
|
45
|
+
* coupon={{ code: 'SAVE50', amount: 50 }}
|
|
46
|
+
* financing={{ months: 12, apr: 0.1 }}
|
|
47
|
+
* />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function PriceBlock(props: PriceBlockProps): JSX.Element;
|