@opensite/ui 0.0.7 → 0.0.8
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 +46 -0
- package/dist/components.cjs +430 -26
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +3 -1
- package/dist/components.d.ts +3 -1
- package/dist/components.js +425 -23
- package/dist/components.js.map +1 -1
- package/dist/dynamic-icon.cjs +90 -19
- package/dist/dynamic-icon.cjs.map +1 -1
- package/dist/dynamic-icon.d.cts +12 -6
- package/dist/dynamic-icon.d.ts +12 -6
- package/dist/dynamic-icon.js +90 -19
- package/dist/dynamic-icon.js.map +1 -1
- package/dist/feature-showcase.cjs +123 -66
- package/dist/feature-showcase.cjs.map +1 -1
- package/dist/feature-showcase.js +119 -62
- package/dist/feature-showcase.js.map +1 -1
- package/dist/hooks.cjs +207 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +2 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +185 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.cjs +432 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +426 -23
- package/dist/index.js.map +1 -1
- package/dist/media-hover-ctas.cjs +75 -0
- package/dist/media-hover-ctas.cjs.map +1 -0
- package/dist/media-hover-ctas.d.cts +83 -0
- package/dist/media-hover-ctas.d.ts +83 -0
- package/dist/media-hover-ctas.js +73 -0
- package/dist/media-hover-ctas.js.map +1 -0
- package/dist/pressable.cjs +333 -0
- package/dist/pressable.cjs.map +1 -0
- package/dist/pressable.d.cts +133 -0
- package/dist/pressable.d.ts +133 -0
- package/dist/pressable.js +311 -0
- package/dist/pressable.js.map +1 -0
- package/dist/registry.cjs +240 -66
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +237 -63
- package/dist/registry.js.map +1 -1
- package/dist/types.d.cts +58 -1
- package/dist/types.d.ts +58 -1
- package/dist/use-navigation.cjs +206 -0
- package/dist/use-navigation.cjs.map +1 -0
- package/dist/use-navigation.d.cts +49 -0
- package/dist/use-navigation.d.ts +49 -0
- package/dist/use-navigation.js +184 -0
- package/dist/use-navigation.js.map +1 -0
- package/package.json +22 -1
package/dist/registry.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
3
|
+
import * as React4 from 'react';
|
|
4
|
+
import React4__default, { useState, useRef, useEffect } from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
import { Img } from '@page-speed/img';
|
|
6
7
|
import useEmblaCarousel from 'embla-carousel-react';
|
|
7
8
|
import { Slot } from '@radix-ui/react-slot';
|
|
8
9
|
import { cva } from 'class-variance-authority';
|
|
@@ -20,7 +21,7 @@ var maxWidthStyles = {
|
|
|
20
21
|
"4xl": "max-w-[1536px]",
|
|
21
22
|
full: "max-w-full"
|
|
22
23
|
};
|
|
23
|
-
var Container =
|
|
24
|
+
var Container = React4__default.forwardRef(
|
|
24
25
|
({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
|
|
25
26
|
const Component = as;
|
|
26
27
|
return /* @__PURE__ */ jsx(
|
|
@@ -54,7 +55,7 @@ var spacingStyles = {
|
|
|
54
55
|
lg: "py-20 md:py-32",
|
|
55
56
|
xl: "py-24 md:py-40"
|
|
56
57
|
};
|
|
57
|
-
var Section =
|
|
58
|
+
var Section = React4__default.forwardRef(
|
|
58
59
|
({
|
|
59
60
|
id,
|
|
60
61
|
title,
|
|
@@ -130,54 +131,66 @@ function AlternatingBlocks({
|
|
|
130
131
|
}
|
|
131
132
|
);
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}) => {
|
|
139
|
-
return /* @__PURE__ */ jsx(
|
|
140
|
-
"svg",
|
|
141
|
-
{
|
|
142
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
143
|
-
width: size,
|
|
144
|
-
height: size,
|
|
145
|
-
viewBox: "0 0 24 24",
|
|
146
|
-
fill: "none",
|
|
147
|
-
stroke: "currentColor",
|
|
148
|
-
strokeWidth,
|
|
149
|
-
strokeLinecap: "round",
|
|
150
|
-
strokeLinejoin: "round",
|
|
151
|
-
className,
|
|
152
|
-
...props,
|
|
153
|
-
children: /* @__PURE__ */ jsx("path", { d: "m12 19l-7-7l7-7m7 7H5" })
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
var ArrowRight = ({
|
|
158
|
-
size = 24,
|
|
159
|
-
className,
|
|
160
|
-
strokeWidth = 2,
|
|
161
|
-
...props
|
|
162
|
-
}) => {
|
|
163
|
-
return /* @__PURE__ */ jsx(
|
|
164
|
-
"svg",
|
|
134
|
+
function MediaHoverCtas(props) {
|
|
135
|
+
const { sectionClassName, gridClassName, items, optixFlowConfig } = props;
|
|
136
|
+
const resolvedItems = items ?? [];
|
|
137
|
+
return /* @__PURE__ */ jsx("section", { className: cn("py-32", sectionClassName), children: /* @__PURE__ */ jsx(
|
|
138
|
+
"div",
|
|
165
139
|
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
140
|
+
className: cn(
|
|
141
|
+
"grid min-h-100 grid-cols-1 gap-1 lg:grid-cols-2",
|
|
142
|
+
gridClassName
|
|
143
|
+
),
|
|
144
|
+
children: resolvedItems.map((item, index) => {
|
|
145
|
+
const CardComponent = item.cardHref ? "a" : "div";
|
|
146
|
+
const hasHoverImage = Boolean(item.onHoverImgSrc);
|
|
147
|
+
const applyHoverBackground = Boolean(
|
|
148
|
+
item.onHoverBackgroundColor && !hasHoverImage
|
|
149
|
+
);
|
|
150
|
+
const cardStyle = item.initialBackgroundColor || applyHoverBackground ? {
|
|
151
|
+
...item.initialBackgroundColor ? { "--media-hover-cta-bg": item.initialBackgroundColor } : {},
|
|
152
|
+
...applyHoverBackground ? {
|
|
153
|
+
"--media-hover-cta-hover-bg": item.onHoverBackgroundColor
|
|
154
|
+
} : {}
|
|
155
|
+
} : void 0;
|
|
156
|
+
const baseBackgroundClassName = item.initialBackgroundColor ? "bg-[var(--media-hover-cta-bg)]" : "bg-muted-foreground/10";
|
|
157
|
+
const hoverBackgroundClassName = applyHoverBackground ? "group-hover:bg-[var(--media-hover-cta-hover-bg)]" : "";
|
|
158
|
+
const hoverImageAltText = item.altText ?? "";
|
|
159
|
+
return /* @__PURE__ */ jsxs(
|
|
160
|
+
CardComponent,
|
|
161
|
+
{
|
|
162
|
+
...item.cardHref ? { href: item.cardHref } : {},
|
|
163
|
+
className: cn(
|
|
164
|
+
"group relative flex min-h-100 cursor-pointer items-center overflow-hidden justify-start p-10 transition-colors duration-500",
|
|
165
|
+
index % 2 === 0 ? "md:justify-center" : "md:justify-start md:pl-24",
|
|
166
|
+
baseBackgroundClassName,
|
|
167
|
+
hoverBackgroundClassName
|
|
168
|
+
),
|
|
169
|
+
style: cardStyle,
|
|
170
|
+
children: [
|
|
171
|
+
item.onHoverImgSrc ? /* @__PURE__ */ jsx(
|
|
172
|
+
Img,
|
|
173
|
+
{
|
|
174
|
+
src: item.onHoverImgSrc,
|
|
175
|
+
alt: hoverImageAltText,
|
|
176
|
+
"aria-hidden": item.altText ? void 0 : true,
|
|
177
|
+
className: cn(
|
|
178
|
+
"absolute top-0 left-0 z-[-1] h-full w-full object-cover opacity-0 transition-opacity duration-500 group-hover:opacity-100",
|
|
179
|
+
item.imgHoverClassName
|
|
180
|
+
),
|
|
181
|
+
loading: "lazy",
|
|
182
|
+
optixFlowConfig
|
|
183
|
+
}
|
|
184
|
+
) : null,
|
|
185
|
+
item.content
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
index
|
|
189
|
+
);
|
|
190
|
+
})
|
|
178
191
|
}
|
|
179
|
-
);
|
|
180
|
-
}
|
|
192
|
+
) });
|
|
193
|
+
}
|
|
181
194
|
var buttonVariants = cva(
|
|
182
195
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
183
196
|
{
|
|
@@ -225,9 +238,114 @@ function Button({
|
|
|
225
238
|
}
|
|
226
239
|
);
|
|
227
240
|
}
|
|
228
|
-
var
|
|
241
|
+
var svgCache = /* @__PURE__ */ new Map();
|
|
242
|
+
function DynamicIcon({
|
|
243
|
+
name,
|
|
244
|
+
size = 28,
|
|
245
|
+
color,
|
|
246
|
+
className,
|
|
247
|
+
alt
|
|
248
|
+
}) {
|
|
249
|
+
const [svgContent, setSvgContent] = React4.useState(null);
|
|
250
|
+
const [isLoading, setIsLoading] = React4.useState(true);
|
|
251
|
+
const [error, setError] = React4.useState(null);
|
|
252
|
+
const { url, iconName } = React4.useMemo(() => {
|
|
253
|
+
const separator = name.includes("/") ? "/" : ":";
|
|
254
|
+
const [prefix, iconName2] = name.split(separator);
|
|
255
|
+
const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
|
|
256
|
+
return {
|
|
257
|
+
url: baseUrl,
|
|
258
|
+
iconName: iconName2
|
|
259
|
+
};
|
|
260
|
+
}, [name, size]);
|
|
261
|
+
React4.useEffect(() => {
|
|
262
|
+
let isMounted = true;
|
|
263
|
+
const fetchSvg = async () => {
|
|
264
|
+
const cached = svgCache.get(url);
|
|
265
|
+
if (cached) {
|
|
266
|
+
if (isMounted) {
|
|
267
|
+
setSvgContent(cached);
|
|
268
|
+
setIsLoading(false);
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
setIsLoading(true);
|
|
274
|
+
setError(null);
|
|
275
|
+
const response = await fetch(url);
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
throw new Error(`Failed to fetch icon: ${response.status}`);
|
|
278
|
+
}
|
|
279
|
+
let svg = await response.text();
|
|
280
|
+
svg = processSvgForCurrentColor(svg);
|
|
281
|
+
svgCache.set(url, svg);
|
|
282
|
+
if (isMounted) {
|
|
283
|
+
setSvgContent(svg);
|
|
284
|
+
setIsLoading(false);
|
|
285
|
+
}
|
|
286
|
+
} catch (err) {
|
|
287
|
+
if (isMounted) {
|
|
288
|
+
setError(err instanceof Error ? err.message : "Failed to load icon");
|
|
289
|
+
setIsLoading(false);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
fetchSvg();
|
|
294
|
+
return () => {
|
|
295
|
+
isMounted = false;
|
|
296
|
+
};
|
|
297
|
+
}, [url]);
|
|
298
|
+
if (isLoading) {
|
|
299
|
+
return /* @__PURE__ */ jsx(
|
|
300
|
+
"span",
|
|
301
|
+
{
|
|
302
|
+
className: cn("inline-block", className),
|
|
303
|
+
style: { width: size, height: size },
|
|
304
|
+
"aria-hidden": "true"
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (error || !svgContent) {
|
|
309
|
+
return /* @__PURE__ */ jsx(
|
|
310
|
+
"span",
|
|
311
|
+
{
|
|
312
|
+
className: cn("inline-block", className),
|
|
313
|
+
style: { width: size, height: size },
|
|
314
|
+
role: "img",
|
|
315
|
+
"aria-label": alt || iconName
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
return /* @__PURE__ */ jsx(
|
|
320
|
+
"span",
|
|
321
|
+
{
|
|
322
|
+
className: cn("inline-flex items-center justify-center", className),
|
|
323
|
+
style: {
|
|
324
|
+
width: size,
|
|
325
|
+
height: size,
|
|
326
|
+
color: color || "inherit"
|
|
327
|
+
},
|
|
328
|
+
role: "img",
|
|
329
|
+
"aria-label": alt || iconName,
|
|
330
|
+
dangerouslySetInnerHTML: { __html: svgContent }
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
function processSvgForCurrentColor(svg) {
|
|
335
|
+
let processed = svg;
|
|
336
|
+
processed = processed.replace(
|
|
337
|
+
/stroke=["'](#000000|#000|black)["']/gi,
|
|
338
|
+
'stroke="currentColor"'
|
|
339
|
+
);
|
|
340
|
+
processed = processed.replace(
|
|
341
|
+
/fill=["'](#000000|#000|black)["']/gi,
|
|
342
|
+
'fill="currentColor"'
|
|
343
|
+
);
|
|
344
|
+
return processed;
|
|
345
|
+
}
|
|
346
|
+
var CarouselContext = React4.createContext(null);
|
|
229
347
|
function useCarousel() {
|
|
230
|
-
const context =
|
|
348
|
+
const context = React4.useContext(CarouselContext);
|
|
231
349
|
if (!context) {
|
|
232
350
|
throw new Error("useCarousel must be used within a <Carousel />");
|
|
233
351
|
}
|
|
@@ -249,20 +367,20 @@ function Carousel({
|
|
|
249
367
|
},
|
|
250
368
|
plugins
|
|
251
369
|
);
|
|
252
|
-
const [canScrollPrev, setCanScrollPrev] =
|
|
253
|
-
const [canScrollNext, setCanScrollNext] =
|
|
254
|
-
const onSelect =
|
|
370
|
+
const [canScrollPrev, setCanScrollPrev] = React4.useState(false);
|
|
371
|
+
const [canScrollNext, setCanScrollNext] = React4.useState(false);
|
|
372
|
+
const onSelect = React4.useCallback((api2) => {
|
|
255
373
|
if (!api2) return;
|
|
256
374
|
setCanScrollPrev(api2.canScrollPrev());
|
|
257
375
|
setCanScrollNext(api2.canScrollNext());
|
|
258
376
|
}, []);
|
|
259
|
-
const scrollPrev =
|
|
377
|
+
const scrollPrev = React4.useCallback(() => {
|
|
260
378
|
api?.scrollPrev();
|
|
261
379
|
}, [api]);
|
|
262
|
-
const scrollNext =
|
|
380
|
+
const scrollNext = React4.useCallback(() => {
|
|
263
381
|
api?.scrollNext();
|
|
264
382
|
}, [api]);
|
|
265
|
-
const handleKeyDown =
|
|
383
|
+
const handleKeyDown = React4.useCallback(
|
|
266
384
|
(event) => {
|
|
267
385
|
if (event.key === "ArrowLeft") {
|
|
268
386
|
event.preventDefault();
|
|
@@ -274,11 +392,11 @@ function Carousel({
|
|
|
274
392
|
},
|
|
275
393
|
[scrollPrev, scrollNext]
|
|
276
394
|
);
|
|
277
|
-
|
|
395
|
+
React4.useEffect(() => {
|
|
278
396
|
if (!api || !setApi) return;
|
|
279
397
|
setApi(api);
|
|
280
398
|
}, [api, setApi]);
|
|
281
|
-
|
|
399
|
+
React4.useEffect(() => {
|
|
282
400
|
if (!api) return;
|
|
283
401
|
onSelect(api);
|
|
284
402
|
api.on("reInit", onSelect);
|
|
@@ -376,7 +494,7 @@ function CarouselPrevious({
|
|
|
376
494
|
onClick: scrollPrev,
|
|
377
495
|
...props,
|
|
378
496
|
children: [
|
|
379
|
-
/* @__PURE__ */ jsx(
|
|
497
|
+
/* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/arrow-left" }),
|
|
380
498
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Previous slide" })
|
|
381
499
|
]
|
|
382
500
|
}
|
|
@@ -404,7 +522,7 @@ function CarouselNext({
|
|
|
404
522
|
onClick: scrollNext,
|
|
405
523
|
...props,
|
|
406
524
|
children: [
|
|
407
|
-
/* @__PURE__ */ jsx(
|
|
525
|
+
/* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/arrow-right" }),
|
|
408
526
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Next slide" })
|
|
409
527
|
]
|
|
410
528
|
}
|
|
@@ -558,6 +676,60 @@ var BLOCK_REGISTRY = {
|
|
|
558
676
|
mediaLeft: true
|
|
559
677
|
}
|
|
560
678
|
]}
|
|
679
|
+
/>
|
|
680
|
+
`.trim()
|
|
681
|
+
},
|
|
682
|
+
"media-hover-ctas": {
|
|
683
|
+
id: "media-hover-ctas",
|
|
684
|
+
name: "Media Hover CTAs",
|
|
685
|
+
description: "Display CTA cards that reveal background imagery or color on hover. Ideal for mission/vision tiles, service highlights, or campaign prompts.",
|
|
686
|
+
semanticTags: [
|
|
687
|
+
"cta",
|
|
688
|
+
"call-to-action",
|
|
689
|
+
"hover",
|
|
690
|
+
"media",
|
|
691
|
+
"cards",
|
|
692
|
+
"grid",
|
|
693
|
+
"image-hover",
|
|
694
|
+
"tiles",
|
|
695
|
+
"mission",
|
|
696
|
+
"vision"
|
|
697
|
+
],
|
|
698
|
+
category: "cta",
|
|
699
|
+
component: MediaHoverCtas,
|
|
700
|
+
props: "MediaHoverCtasProps",
|
|
701
|
+
exampleUsage: `
|
|
702
|
+
<MediaHoverCtas
|
|
703
|
+
items={[
|
|
704
|
+
{
|
|
705
|
+
content: (
|
|
706
|
+
<div className="flex max-w-sm flex-col gap-4">
|
|
707
|
+
<span className="text-sm font-semibold uppercase tracking-wide text-primary">
|
|
708
|
+
Our Mission
|
|
709
|
+
</span>
|
|
710
|
+
<p className="text-muted-foreground">
|
|
711
|
+
Deliver remarkable experiences with thoughtful design.
|
|
712
|
+
</p>
|
|
713
|
+
</div>
|
|
714
|
+
),
|
|
715
|
+
onHoverImgSrc: "/images/mission.jpg",
|
|
716
|
+
altText: "Our Mission"
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
content: (
|
|
720
|
+
<div className="flex max-w-sm flex-col gap-4">
|
|
721
|
+
<span className="text-sm font-semibold uppercase tracking-wide text-primary">
|
|
722
|
+
Our Vision
|
|
723
|
+
</span>
|
|
724
|
+
<p className="text-muted-foreground">
|
|
725
|
+
Build the future of adaptive customer experiences.
|
|
726
|
+
</p>
|
|
727
|
+
</div>
|
|
728
|
+
),
|
|
729
|
+
initialBackgroundColor: "var(--brand-100)",
|
|
730
|
+
onHoverBackgroundColor: "var(--brand-900)"
|
|
731
|
+
}
|
|
732
|
+
]}
|
|
561
733
|
/>
|
|
562
734
|
`.trim()
|
|
563
735
|
},
|
|
@@ -645,7 +817,9 @@ function getAllCategories() {
|
|
|
645
817
|
function searchBlocks(query) {
|
|
646
818
|
const lowercaseQuery = query.toLowerCase();
|
|
647
819
|
return Object.values(BLOCK_REGISTRY).filter(
|
|
648
|
-
(block) => block.name.toLowerCase().includes(lowercaseQuery) || block.description.toLowerCase().includes(lowercaseQuery) || block.semanticTags.some(
|
|
820
|
+
(block) => block.name.toLowerCase().includes(lowercaseQuery) || block.description.toLowerCase().includes(lowercaseQuery) || block.semanticTags.some(
|
|
821
|
+
(tag) => tag.toLowerCase().includes(lowercaseQuery)
|
|
822
|
+
)
|
|
649
823
|
);
|
|
650
824
|
}
|
|
651
825
|
|