@cimplify/sdk 0.12.2 → 0.13.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/dist/react.d.mts +7 -1
- package/dist/react.d.ts +7 -1
- package/dist/react.js +27 -52
- package/dist/react.mjs +27 -52
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/product-card.json +1 -1
package/dist/react.d.mts
CHANGED
|
@@ -885,6 +885,12 @@ interface ProductCardProps {
|
|
|
885
885
|
alt: string;
|
|
886
886
|
className?: string;
|
|
887
887
|
}) => React.ReactNode;
|
|
888
|
+
/** Custom link renderer for page mode (e.g. Next.js Link). */
|
|
889
|
+
renderLink?: (props: {
|
|
890
|
+
href: string;
|
|
891
|
+
className?: string;
|
|
892
|
+
children: React.ReactNode;
|
|
893
|
+
}) => React.ReactElement;
|
|
888
894
|
/** Replace the entire default card body. */
|
|
889
895
|
children?: React.ReactNode;
|
|
890
896
|
/** Image aspect ratio. Default: "4/3". */
|
|
@@ -898,7 +904,7 @@ interface ProductCardProps {
|
|
|
898
904
|
* - **card** (default): clickable button that opens a native `<dialog>` modal with a ProductSheet
|
|
899
905
|
* - **page**: a plain `<a>` link for SEO-friendly product pages
|
|
900
906
|
*/
|
|
901
|
-
declare function ProductCard({ product, displayMode, href, renderModal, renderImage, children, aspectRatio, className, classNames, }: ProductCardProps): React.ReactElement;
|
|
907
|
+
declare function ProductCard({ product, displayMode, href, renderModal, renderImage, renderLink, children, aspectRatio, className, classNames, }: ProductCardProps): React.ReactElement;
|
|
902
908
|
|
|
903
909
|
interface ProductGridClassNames {
|
|
904
910
|
root?: string;
|
package/dist/react.d.ts
CHANGED
|
@@ -885,6 +885,12 @@ interface ProductCardProps {
|
|
|
885
885
|
alt: string;
|
|
886
886
|
className?: string;
|
|
887
887
|
}) => React.ReactNode;
|
|
888
|
+
/** Custom link renderer for page mode (e.g. Next.js Link). */
|
|
889
|
+
renderLink?: (props: {
|
|
890
|
+
href: string;
|
|
891
|
+
className?: string;
|
|
892
|
+
children: React.ReactNode;
|
|
893
|
+
}) => React.ReactElement;
|
|
888
894
|
/** Replace the entire default card body. */
|
|
889
895
|
children?: React.ReactNode;
|
|
890
896
|
/** Image aspect ratio. Default: "4/3". */
|
|
@@ -898,7 +904,7 @@ interface ProductCardProps {
|
|
|
898
904
|
* - **card** (default): clickable button that opens a native `<dialog>` modal with a ProductSheet
|
|
899
905
|
* - **page**: a plain `<a>` link for SEO-friendly product pages
|
|
900
906
|
*/
|
|
901
|
-
declare function ProductCard({ product, displayMode, href, renderModal, renderImage, children, aspectRatio, className, classNames, }: ProductCardProps): React.ReactElement;
|
|
907
|
+
declare function ProductCard({ product, displayMode, href, renderModal, renderImage, renderLink, children, aspectRatio, className, classNames, }: ProductCardProps): React.ReactElement;
|
|
902
908
|
|
|
903
909
|
interface ProductGridClassNames {
|
|
904
910
|
root?: string;
|
package/dist/react.js
CHANGED
|
@@ -9562,6 +9562,7 @@ function ProductCard({
|
|
|
9562
9562
|
href,
|
|
9563
9563
|
renderModal,
|
|
9564
9564
|
renderImage,
|
|
9565
|
+
renderLink,
|
|
9565
9566
|
children,
|
|
9566
9567
|
aspectRatio = "4/3",
|
|
9567
9568
|
className,
|
|
@@ -9569,13 +9570,18 @@ function ProductCard({
|
|
|
9569
9570
|
}) {
|
|
9570
9571
|
const mode = displayMode ?? product.display_mode ?? "card";
|
|
9571
9572
|
const [isOpen, setIsOpen] = React3.useState(false);
|
|
9573
|
+
const [shouldFetch, setShouldFetch] = React3.useState(false);
|
|
9572
9574
|
const dialogRef = React3.useRef(null);
|
|
9573
9575
|
const { product: productDetails } = useProduct(
|
|
9574
9576
|
product.slug ?? product.id,
|
|
9575
|
-
{ enabled: isOpen }
|
|
9577
|
+
{ enabled: shouldFetch || isOpen }
|
|
9576
9578
|
);
|
|
9579
|
+
const handlePrefetch = React3.useCallback(() => {
|
|
9580
|
+
setShouldFetch(true);
|
|
9581
|
+
}, []);
|
|
9577
9582
|
const handleOpen = React3.useCallback(() => {
|
|
9578
9583
|
setIsOpen(true);
|
|
9584
|
+
setShouldFetch(true);
|
|
9579
9585
|
dialogRef.current?.showModal();
|
|
9580
9586
|
}, []);
|
|
9581
9587
|
const handleClose = React3.useCallback(() => {
|
|
@@ -9660,14 +9666,18 @@ function ProductCard({
|
|
|
9660
9666
|
)
|
|
9661
9667
|
] });
|
|
9662
9668
|
if (mode === "page") {
|
|
9669
|
+
const linkHref = href ?? `/menu/${product.slug}`;
|
|
9670
|
+
const linkClassName = cn("block no-underline text-[inherit]", className, classNames?.root);
|
|
9671
|
+
if (renderLink) {
|
|
9672
|
+
return renderLink({ href: linkHref, className: linkClassName, children: cardBody });
|
|
9673
|
+
}
|
|
9663
9674
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9664
9675
|
"a",
|
|
9665
9676
|
{
|
|
9666
|
-
href:
|
|
9677
|
+
href: linkHref,
|
|
9667
9678
|
"data-cimplify-product-card": true,
|
|
9668
9679
|
"data-display-mode": "page",
|
|
9669
|
-
className:
|
|
9670
|
-
style: { display: "block", textDecoration: "none", color: "inherit" },
|
|
9680
|
+
className: linkClassName,
|
|
9671
9681
|
children: cardBody
|
|
9672
9682
|
}
|
|
9673
9683
|
);
|
|
@@ -9678,21 +9688,15 @@ function ProductCard({
|
|
|
9678
9688
|
{
|
|
9679
9689
|
type: "button",
|
|
9680
9690
|
"aria-haspopup": "dialog",
|
|
9691
|
+
onPointerEnter: handlePrefetch,
|
|
9681
9692
|
onClick: handleOpen,
|
|
9682
9693
|
"data-cimplify-product-card": true,
|
|
9683
9694
|
"data-display-mode": "card",
|
|
9684
|
-
className: cn(
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
background: "none",
|
|
9690
|
-
border: "none",
|
|
9691
|
-
padding: 0,
|
|
9692
|
-
cursor: "pointer",
|
|
9693
|
-
font: "inherit",
|
|
9694
|
-
color: "inherit"
|
|
9695
|
-
},
|
|
9695
|
+
className: cn(
|
|
9696
|
+
"block w-full text-left bg-transparent border-none p-0 cursor-pointer font-[inherit] text-[inherit]",
|
|
9697
|
+
className,
|
|
9698
|
+
classNames?.root
|
|
9699
|
+
),
|
|
9696
9700
|
children: cardBody
|
|
9697
9701
|
}
|
|
9698
9702
|
),
|
|
@@ -9703,16 +9707,10 @@ function ProductCard({
|
|
|
9703
9707
|
onCancel: handleCancel,
|
|
9704
9708
|
onClick: handleBackdropClick,
|
|
9705
9709
|
"data-cimplify-product-card-modal": true,
|
|
9706
|
-
className:
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
padding: "1.5rem",
|
|
9711
|
-
maxWidth: "32rem",
|
|
9712
|
-
width: "100%",
|
|
9713
|
-
maxHeight: "90vh",
|
|
9714
|
-
overflow: "auto"
|
|
9715
|
-
},
|
|
9710
|
+
className: cn(
|
|
9711
|
+
"border-none rounded-2xl p-0 max-w-lg w-full max-h-[85vh] overflow-auto bg-background shadow-2xl backdrop:bg-black/50 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:slide-in-from-bottom-4",
|
|
9712
|
+
classNames?.modal
|
|
9713
|
+
),
|
|
9716
9714
|
children: isOpen && (productDetails ? renderModal ? renderModal(productDetails) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9717
9715
|
ProductSheet,
|
|
9718
9716
|
{
|
|
@@ -9725,33 +9723,10 @@ function ProductCard({
|
|
|
9725
9723
|
{
|
|
9726
9724
|
"data-cimplify-product-card-modal-loading": true,
|
|
9727
9725
|
"aria-busy": "true",
|
|
9728
|
-
|
|
9729
|
-
display: "flex",
|
|
9730
|
-
flexDirection: "column",
|
|
9731
|
-
gap: "1rem"
|
|
9732
|
-
},
|
|
9726
|
+
className: "flex flex-col gap-4 p-6",
|
|
9733
9727
|
children: [
|
|
9734
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9735
|
-
|
|
9736
|
-
{
|
|
9737
|
-
style: {
|
|
9738
|
-
aspectRatio: "4/3",
|
|
9739
|
-
backgroundColor: "rgba(0,0,0,0.06)",
|
|
9740
|
-
borderRadius: "0.5rem"
|
|
9741
|
-
}
|
|
9742
|
-
}
|
|
9743
|
-
),
|
|
9744
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9745
|
-
"div",
|
|
9746
|
-
{
|
|
9747
|
-
style: {
|
|
9748
|
-
height: "1.5rem",
|
|
9749
|
-
width: "60%",
|
|
9750
|
-
backgroundColor: "rgba(0,0,0,0.06)",
|
|
9751
|
-
borderRadius: "0.25rem"
|
|
9752
|
-
}
|
|
9753
|
-
}
|
|
9754
|
-
)
|
|
9728
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[4/3] bg-muted rounded-lg" }),
|
|
9729
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-3/5 bg-muted rounded" })
|
|
9755
9730
|
]
|
|
9756
9731
|
}
|
|
9757
9732
|
))
|
package/dist/react.mjs
CHANGED
|
@@ -9556,6 +9556,7 @@ function ProductCard({
|
|
|
9556
9556
|
href,
|
|
9557
9557
|
renderModal,
|
|
9558
9558
|
renderImage,
|
|
9559
|
+
renderLink,
|
|
9559
9560
|
children,
|
|
9560
9561
|
aspectRatio = "4/3",
|
|
9561
9562
|
className,
|
|
@@ -9563,13 +9564,18 @@ function ProductCard({
|
|
|
9563
9564
|
}) {
|
|
9564
9565
|
const mode = displayMode ?? product.display_mode ?? "card";
|
|
9565
9566
|
const [isOpen, setIsOpen] = useState(false);
|
|
9567
|
+
const [shouldFetch, setShouldFetch] = useState(false);
|
|
9566
9568
|
const dialogRef = useRef(null);
|
|
9567
9569
|
const { product: productDetails } = useProduct(
|
|
9568
9570
|
product.slug ?? product.id,
|
|
9569
|
-
{ enabled: isOpen }
|
|
9571
|
+
{ enabled: shouldFetch || isOpen }
|
|
9570
9572
|
);
|
|
9573
|
+
const handlePrefetch = useCallback(() => {
|
|
9574
|
+
setShouldFetch(true);
|
|
9575
|
+
}, []);
|
|
9571
9576
|
const handleOpen = useCallback(() => {
|
|
9572
9577
|
setIsOpen(true);
|
|
9578
|
+
setShouldFetch(true);
|
|
9573
9579
|
dialogRef.current?.showModal();
|
|
9574
9580
|
}, []);
|
|
9575
9581
|
const handleClose = useCallback(() => {
|
|
@@ -9654,14 +9660,18 @@ function ProductCard({
|
|
|
9654
9660
|
)
|
|
9655
9661
|
] });
|
|
9656
9662
|
if (mode === "page") {
|
|
9663
|
+
const linkHref = href ?? `/menu/${product.slug}`;
|
|
9664
|
+
const linkClassName = cn("block no-underline text-[inherit]", className, classNames?.root);
|
|
9665
|
+
if (renderLink) {
|
|
9666
|
+
return renderLink({ href: linkHref, className: linkClassName, children: cardBody });
|
|
9667
|
+
}
|
|
9657
9668
|
return /* @__PURE__ */ jsx(
|
|
9658
9669
|
"a",
|
|
9659
9670
|
{
|
|
9660
|
-
href:
|
|
9671
|
+
href: linkHref,
|
|
9661
9672
|
"data-cimplify-product-card": true,
|
|
9662
9673
|
"data-display-mode": "page",
|
|
9663
|
-
className:
|
|
9664
|
-
style: { display: "block", textDecoration: "none", color: "inherit" },
|
|
9674
|
+
className: linkClassName,
|
|
9665
9675
|
children: cardBody
|
|
9666
9676
|
}
|
|
9667
9677
|
);
|
|
@@ -9672,21 +9682,15 @@ function ProductCard({
|
|
|
9672
9682
|
{
|
|
9673
9683
|
type: "button",
|
|
9674
9684
|
"aria-haspopup": "dialog",
|
|
9685
|
+
onPointerEnter: handlePrefetch,
|
|
9675
9686
|
onClick: handleOpen,
|
|
9676
9687
|
"data-cimplify-product-card": true,
|
|
9677
9688
|
"data-display-mode": "card",
|
|
9678
|
-
className: cn(
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
background: "none",
|
|
9684
|
-
border: "none",
|
|
9685
|
-
padding: 0,
|
|
9686
|
-
cursor: "pointer",
|
|
9687
|
-
font: "inherit",
|
|
9688
|
-
color: "inherit"
|
|
9689
|
-
},
|
|
9689
|
+
className: cn(
|
|
9690
|
+
"block w-full text-left bg-transparent border-none p-0 cursor-pointer font-[inherit] text-[inherit]",
|
|
9691
|
+
className,
|
|
9692
|
+
classNames?.root
|
|
9693
|
+
),
|
|
9690
9694
|
children: cardBody
|
|
9691
9695
|
}
|
|
9692
9696
|
),
|
|
@@ -9697,16 +9701,10 @@ function ProductCard({
|
|
|
9697
9701
|
onCancel: handleCancel,
|
|
9698
9702
|
onClick: handleBackdropClick,
|
|
9699
9703
|
"data-cimplify-product-card-modal": true,
|
|
9700
|
-
className:
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
padding: "1.5rem",
|
|
9705
|
-
maxWidth: "32rem",
|
|
9706
|
-
width: "100%",
|
|
9707
|
-
maxHeight: "90vh",
|
|
9708
|
-
overflow: "auto"
|
|
9709
|
-
},
|
|
9704
|
+
className: cn(
|
|
9705
|
+
"border-none rounded-2xl p-0 max-w-lg w-full max-h-[85vh] overflow-auto bg-background shadow-2xl backdrop:bg-black/50 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:slide-in-from-bottom-4",
|
|
9706
|
+
classNames?.modal
|
|
9707
|
+
),
|
|
9710
9708
|
children: isOpen && (productDetails ? renderModal ? renderModal(productDetails) : /* @__PURE__ */ jsx(
|
|
9711
9709
|
ProductSheet,
|
|
9712
9710
|
{
|
|
@@ -9719,33 +9717,10 @@ function ProductCard({
|
|
|
9719
9717
|
{
|
|
9720
9718
|
"data-cimplify-product-card-modal-loading": true,
|
|
9721
9719
|
"aria-busy": "true",
|
|
9722
|
-
|
|
9723
|
-
display: "flex",
|
|
9724
|
-
flexDirection: "column",
|
|
9725
|
-
gap: "1rem"
|
|
9726
|
-
},
|
|
9720
|
+
className: "flex flex-col gap-4 p-6",
|
|
9727
9721
|
children: [
|
|
9728
|
-
/* @__PURE__ */ jsx(
|
|
9729
|
-
|
|
9730
|
-
{
|
|
9731
|
-
style: {
|
|
9732
|
-
aspectRatio: "4/3",
|
|
9733
|
-
backgroundColor: "rgba(0,0,0,0.06)",
|
|
9734
|
-
borderRadius: "0.5rem"
|
|
9735
|
-
}
|
|
9736
|
-
}
|
|
9737
|
-
),
|
|
9738
|
-
/* @__PURE__ */ jsx(
|
|
9739
|
-
"div",
|
|
9740
|
-
{
|
|
9741
|
-
style: {
|
|
9742
|
-
height: "1.5rem",
|
|
9743
|
-
width: "60%",
|
|
9744
|
-
backgroundColor: "rgba(0,0,0,0.06)",
|
|
9745
|
-
borderRadius: "0.25rem"
|
|
9746
|
-
}
|
|
9747
|
-
}
|
|
9748
|
-
)
|
|
9722
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[4/3] bg-muted rounded-lg" }),
|
|
9723
|
+
/* @__PURE__ */ jsx("div", { className: "h-6 w-3/5 bg-muted rounded" })
|
|
9749
9724
|
]
|
|
9750
9725
|
}
|
|
9751
9726
|
))
|
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.container{width:100%}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.w-full{width:100%}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.\[appearance\:textfield\]{appearance:textfield}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.border-transparent{border-color:#0000}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/5{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.bg-primary\/5{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.text-center{text-align:center}.text-left{text-align:left}.text-\[10px\]{font-size:10px}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-muted-foreground\/70{color:#636363b3}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/70{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 70%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.uppercase{text-transform:uppercase}.opacity-70{opacity:.7}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.outline-none{--tw-outline-style:none;outline-style:none}.\[cimplify\:checkout\]{cimplify:checkout}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-muted\/50:hover{background-color:#eeeeee80}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.container{width:100%}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[4\/3\]{aspect-ratio:4/3}.max-h-\[85vh\]{max-height:85vh}.w-3\/5{width:60%}.w-full{width:100%}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.\[appearance\:textfield\]{appearance:textfield}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.rounded{border-radius:var(--radius,.5rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background,oklch(99% 0 0))}.bg-muted{background-color:var(--color-muted,oklch(95% 0 0))}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/5{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.bg-primary\/5{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.text-center{text-align:center}.text-left{text-align:left}.font-\[inherit\]{font-family:inherit}.text-\[10px\]{font-size:10px}.text-\[inherit\]{color:inherit}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-muted-foreground\/70{color:#636363b3}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/70{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 70%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.opacity-70{opacity:.7}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.outline-none{--tw-outline-style:none;outline-style:none}.\[cimplify\:checkout\]{cimplify:checkout}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-muted\/50:hover{background-color:#eeeeee80}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "product-card.tsx",
|
|
14
|
-
"content": "\"use client\";\n\nimport React, { useCallback, useRef, useState } from \"react\";\nimport type { Product, ProductWithDetails } from \"@cimplify/sdk\";\nimport { useProduct } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { ProductSheet } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nconst ASPECT_STYLES: Record<string, React.CSSProperties> = {\n square: { aspectRatio: \"1/1\" },\n \"4/3\": { aspectRatio: \"4/3\" },\n \"16/10\": { aspectRatio: \"16/10\" },\n \"3/4\": { aspectRatio: \"3/4\" },\n};\n\nexport interface ProductCardClassNames {\n root?: string;\n imageContainer?: string;\n image?: string;\n body?: string;\n name?: string;\n description?: string;\n price?: string;\n badges?: string;\n badge?: string;\n modal?: string;\n modalOverlay?: string;\n}\n\nexport interface ProductCardProps {\n /** The product to display. */\n product: Product;\n /** Display mode: \"card\" opens a modal, \"page\" renders as a link. Auto-detected from product.display_mode. */\n displayMode?: \"card\" | \"page\";\n /** Link href for page mode. Default: `/menu/${product.slug}` */\n href?: string;\n /** Custom modal content renderer. Receives the fully-loaded product. */\n renderModal?: (product: ProductWithDetails) => React.ReactNode;\n /** Custom image renderer (e.g. Next.js Image). */\n renderImage?: (props: {\n src: string;\n alt: string;\n className?: string;\n }) => React.ReactNode;\n /** Replace the entire default card body. */\n children?: React.ReactNode;\n /** Image aspect ratio. Default: \"4/3\". */\n aspectRatio?: \"square\" | \"4/3\" | \"16/10\" | \"3/4\";\n className?: string;\n classNames?: ProductCardClassNames;\n}\n\n/**\n * ProductCard — a product display card with two modes:\n *\n * - **card** (default): clickable button that opens a native `<dialog>` modal with a ProductSheet\n * - **page**: a plain `<a>` link for SEO-friendly product pages\n */\nexport function ProductCard({\n product,\n displayMode,\n href,\n renderModal,\n renderImage,\n children,\n aspectRatio = \"4/3\",\n className,\n classNames,\n}: ProductCardProps): React.ReactElement {\n const mode = displayMode ?? product.display_mode ?? \"card\";\n const [isOpen, setIsOpen] = useState(false);\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n //
|
|
14
|
+
"content": "\"use client\";\n\nimport React, { useCallback, useRef, useState } from \"react\";\nimport type { Product, ProductWithDetails } from \"@cimplify/sdk\";\nimport { useProduct } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { ProductSheet } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nconst ASPECT_STYLES: Record<string, React.CSSProperties> = {\n square: { aspectRatio: \"1/1\" },\n \"4/3\": { aspectRatio: \"4/3\" },\n \"16/10\": { aspectRatio: \"16/10\" },\n \"3/4\": { aspectRatio: \"3/4\" },\n};\n\nexport interface ProductCardClassNames {\n root?: string;\n imageContainer?: string;\n image?: string;\n body?: string;\n name?: string;\n description?: string;\n price?: string;\n badges?: string;\n badge?: string;\n modal?: string;\n modalOverlay?: string;\n}\n\nexport interface ProductCardProps {\n /** The product to display. */\n product: Product;\n /** Display mode: \"card\" opens a modal, \"page\" renders as a link. Auto-detected from product.display_mode. */\n displayMode?: \"card\" | \"page\";\n /** Link href for page mode. Default: `/menu/${product.slug}` */\n href?: string;\n /** Custom modal content renderer. Receives the fully-loaded product. */\n renderModal?: (product: ProductWithDetails) => React.ReactNode;\n /** Custom image renderer (e.g. Next.js Image). */\n renderImage?: (props: {\n src: string;\n alt: string;\n className?: string;\n }) => React.ReactNode;\n /** Custom link renderer for page mode (e.g. Next.js Link). */\n renderLink?: (props: {\n href: string;\n className?: string;\n children: React.ReactNode;\n }) => React.ReactElement;\n /** Replace the entire default card body. */\n children?: React.ReactNode;\n /** Image aspect ratio. Default: \"4/3\". */\n aspectRatio?: \"square\" | \"4/3\" | \"16/10\" | \"3/4\";\n className?: string;\n classNames?: ProductCardClassNames;\n}\n\n/**\n * ProductCard — a product display card with two modes:\n *\n * - **card** (default): clickable button that opens a native `<dialog>` modal with a ProductSheet\n * - **page**: a plain `<a>` link for SEO-friendly product pages\n */\nexport function ProductCard({\n product,\n displayMode,\n href,\n renderModal,\n renderImage,\n renderLink,\n children,\n aspectRatio = \"4/3\",\n className,\n classNames,\n}: ProductCardProps): React.ReactElement {\n const mode = displayMode ?? product.display_mode ?? \"card\";\n const [isOpen, setIsOpen] = useState(false);\n const [shouldFetch, setShouldFetch] = useState(false);\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n // Prefetch on pointer enter, always fetch when open\n const { product: productDetails } = useProduct(\n product.slug ?? product.id,\n { enabled: shouldFetch || isOpen },\n );\n\n const handlePrefetch = useCallback(() => {\n setShouldFetch(true);\n }, []);\n\n const handleOpen = useCallback(() => {\n setIsOpen(true);\n setShouldFetch(true);\n dialogRef.current?.showModal();\n }, []);\n\n const handleClose = useCallback(() => {\n dialogRef.current?.close();\n setIsOpen(false);\n }, []);\n\n const handleCancel = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent<HTMLDialogElement>) => {\n if (e.target === dialogRef.current) {\n handleClose();\n }\n },\n [handleClose],\n );\n\n const imageUrl = product.image_url || product.images?.[0];\n\n const cardBody = children ?? (\n <>\n {/* Image */}\n {imageUrl && (\n <div\n data-cimplify-product-card-image-container\n className={classNames?.imageContainer}\n style={{\n overflow: \"hidden\",\n ...ASPECT_STYLES[aspectRatio],\n }}\n >\n {renderImage ? (\n renderImage({\n src: imageUrl,\n alt: product.name,\n className: classNames?.image,\n })\n ) : (\n <img\n src={imageUrl}\n alt={product.name}\n className={classNames?.image}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\" }}\n data-cimplify-product-card-image\n />\n )}\n </div>\n )}\n\n {/* Body */}\n <div\n data-cimplify-product-card-body\n className={classNames?.body}\n >\n <span\n data-cimplify-product-card-name\n className={classNames?.name}\n >\n {product.name}\n </span>\n {product.description && (\n <span\n data-cimplify-product-card-description\n className={classNames?.description}\n style={{\n display: \"-webkit-box\",\n WebkitLineClamp: 2,\n WebkitBoxOrient: \"vertical\",\n overflow: \"hidden\",\n }}\n >\n {product.description}\n </span>\n )}\n <Price\n amount={product.default_price}\n className={classNames?.price}\n />\n </div>\n </>\n );\n\n // Page mode — render as a link\n if (mode === \"page\") {\n const linkHref = href ?? `/menu/${product.slug}`;\n const linkClassName = cn(\"block no-underline text-[inherit]\", className, classNames?.root);\n\n if (renderLink) {\n return renderLink({ href: linkHref, className: linkClassName, children: cardBody });\n }\n\n return (\n <a\n href={linkHref}\n data-cimplify-product-card\n data-display-mode=\"page\"\n className={linkClassName}\n >\n {cardBody}\n </a>\n );\n }\n\n // Card mode — render as button + native dialog\n return (\n <>\n <button\n type=\"button\"\n aria-haspopup=\"dialog\"\n onPointerEnter={handlePrefetch}\n onClick={handleOpen}\n data-cimplify-product-card\n data-display-mode=\"card\"\n className={cn(\n \"block w-full text-left bg-transparent border-none p-0 cursor-pointer font-[inherit] text-[inherit]\",\n className,\n classNames?.root,\n )}\n >\n {cardBody}\n </button>\n\n <dialog\n ref={dialogRef}\n onCancel={handleCancel}\n onClick={handleBackdropClick}\n data-cimplify-product-card-modal\n className={cn(\n \"border-none rounded-2xl p-0 max-w-lg w-full max-h-[85vh] overflow-auto bg-background shadow-2xl backdrop:bg-black/50 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:slide-in-from-bottom-4\",\n classNames?.modal,\n )}\n >\n {isOpen && (\n productDetails ? (\n renderModal ? (\n renderModal(productDetails)\n ) : (\n <ProductSheet\n product={productDetails}\n onClose={handleClose}\n renderImage={renderImage}\n />\n )\n ) : (\n <div\n data-cimplify-product-card-modal-loading\n aria-busy=\"true\"\n className=\"flex flex-col gap-4 p-6\"\n >\n <div className=\"aspect-[4/3] bg-muted rounded-lg\" />\n <div className=\"h-6 w-3/5 bg-muted rounded\" />\n </div>\n )\n )}\n </dialog>\n </>\n );\n}\n"
|
|
15
15
|
}
|
|
16
16
|
]
|
|
17
17
|
}
|