Skip to content

Commit

Permalink
Admin-powered product details
Browse files Browse the repository at this point in the history
  • Loading branch information
tlgimenes committed Oct 14, 2023
1 parent f1cd9dc commit 340fe3d
Show file tree
Hide file tree
Showing 21 changed files with 772 additions and 238 deletions.
50 changes: 50 additions & 0 deletions components/product/Gallery/FrontBack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ProductDetailsPage } from "apps/commerce/types.ts";
import Image from "apps/website/components/Image.tsx";

export interface Props {
/** @title Integration */
page: ProductDetailsPage | null;

layout: {
width: number;
height: number;
};
}

/**
* @title Product Image Front Back
* @description Renders two images side by side both on mobile and on desktop. On mobile, the overflow is reached causing a scrollbar to be rendered.
*/
function GalleryFrontBack(props: Props) {
if (props.page === null) {
throw new Error("Missing Product Details Page Info");
}

const {
page: { product: { image: images = [] } },
layout: { width, height },
} = props;
const aspectRatio = `${width} / ${height}`;

return (
<ul class="carousel carousel-center gap-6">
{[images[0], images[1] ?? images[0]].map((img, index) => (
<li class="carousel-item min-w-[100vw] sm:min-w-[24vw]">
<Image
sizes="(max-width: 640px) 100vw, 24vw"
style={{ aspectRatio }}
src={img.url!}
alt={img.alternateName}
width={width}
height={height}
// Preload LCP image for better web vitals
preload={index === 0}
loading={index === 0 ? "eager" : "lazy"}
/>
</li>
))}
</ul>
);
}

export default GalleryFrontBack;
108 changes: 108 additions & 0 deletions components/product/Gallery/ImageSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Icon from "$store/components/ui/Icon.tsx";
import Slider from "$store/components/ui/Slider.tsx";
import ProductImageZoom from "$store/islands/ProductImageZoom.tsx";
import SliderJS from "$store/islands/SliderJS.tsx";
import { useId } from "$store/sdk/useId.ts";
import { ProductDetailsPage } from "apps/commerce/types.ts";
import Image from "apps/website/components/Image.tsx";

export interface Props {
/** @title Integration */
page: ProductDetailsPage | null;

layout: {
width: number;
height: number;
};
}

/**
* @title Product Image Slider
* @description Creates a three columned grid on destkop, one for the dots preview, one for the image slider and the other for product info
* On mobile, there's one single column with 3 rows. Note that the orders are different from desktop to mobile, that's why
* we rearrange each cell with col-start- directives
*/
export default function GallerySlider(props: Props) {
const id = useId();

if (props.page === null) {
throw new Error("Missing Product Details Page Info");
}

const {
page: { product: { image: images = [] } },
layout: { width, height },
} = props;
const aspectRatio = `${width} / ${height}`;

return (
<div id={id} class="grid grid-flow-row sm:grid-flow-col">
{/* Image Slider */}
<div class="relative order-1 sm:order-2">
<Slider class="carousel carousel-center gap-6 w-screen sm:w-[40vw]">
{images.map((img, index) => (
<Slider.Item
index={index}
class="carousel-item w-full"
>
<Image
class="w-full"
sizes="(max-width: 640px) 100vw, 40vw"
style={{ aspectRatio }}
src={img.url!}
alt={img.alternateName}
width={width}
height={height}
// Preload LCP image for better web vitals
preload={index === 0}
loading={index === 0 ? "eager" : "lazy"}
/>
</Slider.Item>
))}
</Slider>

<Slider.PrevButton
class="no-animation absolute left-2 top-1/2 btn btn-circle btn-outline"
disabled
>
<Icon size={24} id="ChevronLeft" strokeWidth={3} />
</Slider.PrevButton>

<Slider.NextButton
class="no-animation absolute right-2 top-1/2 btn btn-circle btn-outline"
disabled={images.length < 2}
>
<Icon size={24} id="ChevronRight" strokeWidth={3} />
</Slider.NextButton>

<div class="absolute top-2 right-2 bg-base-100 rounded-full">
<ProductImageZoom
images={images}
width={700}
height={Math.trunc(700 * height / width)}
/>
</div>
</div>

{/* Dots */}
<ul class="carousel carousel-center gap-1 px-4 sm:px-0 sm:flex-col order-2 sm:order-1">
{images.map((img, index) => (
<li class="carousel-item min-w-[63px] sm:min-w-[100px]">
<Slider.Dot index={index}>
<Image
style={{ aspectRatio }}
class="group-disabled:border-base-300 border rounded "
width={63}
height={87.5}
src={img.url!}
alt={img.alternateName}
/>
</Slider.Dot>
</li>
))}
</ul>

<SliderJS rootId={id} />
</div>
);
}
Loading

1 comment on commit 340fe3d

@deno-deploy
Copy link
Contributor

@deno-deploy deno-deploy bot commented on 340fe3d Oct 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An internal server error occurred.

Please sign in to comment.