diff --git a/apps/coldsurf-io/package.json b/apps/coldsurf-io/package.json index 3505b0a7..c981995b 100644 --- a/apps/coldsurf-io/package.json +++ b/apps/coldsurf-io/package.json @@ -1,6 +1,6 @@ { "name": "@coldsurfers/coldsurf-io", - "version": "1.0.4", + "version": "1.0.5", "private": true, "scripts": { "build": "next build", @@ -20,7 +20,8 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "react-lite-youtube-embed": "*", - "sst": "*" + "sst": "*", + "zustand": "*" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/apps/coldsurf-io/public/product-image/billets.png b/apps/coldsurf-io/public/product-image/billets.png new file mode 100644 index 00000000..ee88eabc Binary files /dev/null and b/apps/coldsurf-io/public/product-image/billets.png differ diff --git a/apps/coldsurf-io/public/product-image/surf-tree.png b/apps/coldsurf-io/public/product-image/surf-tree.png new file mode 100644 index 00000000..4caf44f0 Binary files /dev/null and b/apps/coldsurf-io/public/product-image/surf-tree.png differ diff --git a/apps/coldsurf-io/src/app/products/(components)/index.ts b/apps/coldsurf-io/src/app/products/(components)/index.ts new file mode 100644 index 00000000..5bdf9887 --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/index.ts @@ -0,0 +1,2 @@ +export * from './product-card' +export * from './product-card-bottom-sheet' diff --git a/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/index.ts b/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/index.ts new file mode 100644 index 00000000..42596c8f --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/index.ts @@ -0,0 +1 @@ +export * from './product-card-bottom-sheet' diff --git a/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/product-card-bottom-sheet.tsx b/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/product-card-bottom-sheet.tsx new file mode 100644 index 00000000..050dbd17 --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/product-card-bottom-sheet/product-card-bottom-sheet.tsx @@ -0,0 +1,23 @@ +'use client' + +import { useProductStore } from '@/stores' +import { useShallow } from 'zustand/shallow' +import { ProductCardBottomSheet as BottomSheet } from '../../(ui)/product-card-bottom-sheet' + +export function ProductCardBottomSheet() { + const { + isProductBottomSheetOpen: isOpen, + setIsProductBottomSheetOpen: setIsOpen, + selectedProduct, + } = useProductStore( + useShallow((state) => ({ + isProductBottomSheetOpen: state.isProductBottomSheetOpen, + setIsProductBottomSheetOpen: state.setIsProductBottomSheetOpen, + selectedProduct: state.selectedProduct, + })), + ) + if (!selectedProduct) { + return null + } + return setIsOpen(false)} selectedProduct={selectedProduct} /> +} diff --git a/apps/coldsurf-io/src/app/products/(components)/product-card/index.ts b/apps/coldsurf-io/src/app/products/(components)/product-card/index.ts new file mode 100644 index 00000000..1667f119 --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/product-card/index.ts @@ -0,0 +1 @@ +export * from './product-card' diff --git a/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.tsx b/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.tsx new file mode 100644 index 00000000..781bc67a --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.tsx @@ -0,0 +1,24 @@ +'use client' + +import { useProductStore } from '@/stores' +import { useShallow } from 'zustand/shallow' +import { ProductCard as Card } from '../../(ui)' +import { ProductCardProps } from './product-card.types' + +export function ProductCard(props: ProductCardProps) { + const { setSelectedProduct, setIsProductBottomSheetOpen } = useProductStore( + useShallow((state) => ({ + setSelectedProduct: state.setSelectedProduct, + setIsProductBottomSheetOpen: state.setIsProductBottomSheetOpen, + })), + ) + return ( + { + setIsProductBottomSheetOpen(true) + setSelectedProduct(props) + }} + {...props} + /> + ) +} diff --git a/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.types.ts b/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.types.ts new file mode 100644 index 00000000..9d78e8dc --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(components)/product-card/product-card.types.ts @@ -0,0 +1,3 @@ +import { Product } from '@/types' + +export type ProductCardProps = Product diff --git a/apps/coldsurf-io/src/app/products/(ui)/index.ts b/apps/coldsurf-io/src/app/products/(ui)/index.ts index 200ac753..e035dbaf 100644 --- a/apps/coldsurf-io/src/app/products/(ui)/index.ts +++ b/apps/coldsurf-io/src/app/products/(ui)/index.ts @@ -1,3 +1,4 @@ export * from './product-card' +export * from './product-card-bottom-sheet' export * from './product-card-list-layout' export * from './top-title' diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/index.ts b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/index.ts new file mode 100644 index 00000000..42596c8f --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/index.ts @@ -0,0 +1 @@ +export * from './product-card-bottom-sheet' diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.styled.ts b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.styled.ts new file mode 100644 index 00000000..4b9d116c --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.styled.ts @@ -0,0 +1,121 @@ +import { colors, IconButton, media, semantics, Text } from '@coldsurfers/ocean-road' +import { css } from '@emotion/react' +import styled from '@emotion/styled' +import { motion } from 'framer-motion' + +// Styled container for the bottom sheet +export const SheetContainer = styled(motion.div)` + position: fixed; + bottom: 0; + left: 0; + right: 0; + + width: 100%; + height: 85vh; + background: white; + border-radius: 16px 16px 0 0; + box-shadow: 0 -5px 10px rgba(0, 0, 0, 0.2); + z-index: 101; + display: flex; + flex-direction: column; + padding: 40px 40px 16px; + + background-color: ${semantics.color.background[4]}; + + ${media.medium(css` + padding: 24px 24px 16px; + `)} +` + +export const SheetInner = styled.div` + min-width: 0px; + padding: 24px; + overflow: hidden auto; + padding-left: 40px; + padding-right: 40px; +` + +export const Overlay = styled(motion.div)` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 100; + backdrop-filter: blur(5px); +` + +export const CloseButton = styled(IconButton)` + padding: 8px; + position: absolute; + top: 14px; + right: 14px; + color: inherit; + cursor: pointer; + border: none; + z-index: 10000; + background: ${semantics.color.background[3]}; + border-radius: 100%; + display: flex; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; +` + +export const StyledTitle = styled(Text)` + font-size: clamp(28px, 8vmin, 56px); + font-weight: 820; + letter-spacing: -0.02em; + line-height: 1.05; + text-align: left; +` + +export const StyledDescription = styled(Text)` + font-weight: 418; + letter-spacing: 0.01em; + line-height: 1.5; + + font-size: 16px; + white-space: pre-wrap; + ${media.small(css` + font-size: 14px; + `)} +` + +export const StyledProductImageContainer = styled.div` + width: 390px; + margin-left: auto; +` + +export const StyledProductImage = styled.img` + aspect-ratio: 0.5625 / 1; + width: 100%; + height: auto; + border-radius: 20px; + object-fit: cover; + object-position: 50%; +` + +export const StyledContentGrid = styled.div` + display: grid; + grid-template-areas: + 'breadcrumbs . .' + 'content . image' + 'ctaButton . image' + 'linkApps . image' + 'tags . image'; + grid-template-columns: 3fr 1fr; + + max-width: 1200px; + margin: 0 auto; +` + +export const StyledCtaText = styled(Text)` + font-size: 16px; + font-weight: 418; + letter-spacing: 0.01em; + line-height: 1.5; + color: ${colors.oc.white.value}; +` diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.tsx b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.tsx new file mode 100644 index 00000000..752eebfd --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.tsx @@ -0,0 +1,81 @@ +'use client' + +import { Button, Text } from '@coldsurfers/ocean-road' +import { AnimatePresence } from 'framer-motion' +import { X as XIcon } from 'lucide-react' +import { + CloseButton, + Overlay, + SheetContainer, + SheetInner, + StyledContentGrid, + StyledCtaText, + StyledDescription, + StyledProductImage, + StyledProductImageContainer, + StyledTitle, +} from './product-card-bottom-sheet.styled' +import { ProductCardBottomSheetProps } from './product-card-bottom-sheet.types' + +export function ProductCardBottomSheet({ isOpen, onClose, selectedProduct }: ProductCardBottomSheetProps) { + // Animation variants for the sheet + const sheetVariants = { + hidden: { y: '100%', opacity: 0 }, + visible: { y: 0, opacity: 1 }, + } + + return ( + + {isOpen && ( + <> + {/* Overlay */} + onClose()} /> + + {/* Bottom Sheet */} + + onClose()}> + + + + +
+ Products / {selectedProduct.title} +
+
+ {selectedProduct.title} + {selectedProduct.longDescription} +
+
+ {selectedProduct.ctaTitle && ( + + )} +
+
+ + + +
+
+
+
+ + )} +
+ ) +} diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.types.ts b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.types.ts new file mode 100644 index 00000000..5268dab0 --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card-bottom-sheet/product-card-bottom-sheet.types.ts @@ -0,0 +1,3 @@ +import { Product } from '@/types' + +export type ProductCardBottomSheetProps = { isOpen: boolean; onClose: () => void; selectedProduct: Product } diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.tsx b/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.tsx index 063614d5..5d485109 100644 --- a/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.tsx +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.tsx @@ -6,20 +6,15 @@ import { StyledProductCardTitle, StyledProductImage, } from './product-card.styled' +import { ProductCardProps } from './product-card.types' -export function ProductCard({ - title, - description, - imgSrc, - backgroundImgSrc, -}: { - title: string - description: string - imgSrc: string - backgroundImgSrc: string -}) { +export function ProductCard({ title, description, imgSrc, backgroundImgSrc, onClick }: ProductCardProps) { return ( - + {title} {description} diff --git a/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.types.ts b/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.types.ts new file mode 100644 index 00000000..0907e2b3 --- /dev/null +++ b/apps/coldsurf-io/src/app/products/(ui)/product-card/product-card.types.ts @@ -0,0 +1,5 @@ +import { Product } from '@/types' + +export type ProductCardProps = Product & { + onClick: () => void +} diff --git a/apps/coldsurf-io/src/app/products/page.tsx b/apps/coldsurf-io/src/app/products/page.tsx index 2ca6156d..8b026e5d 100644 --- a/apps/coldsurf-io/src/app/products/page.tsx +++ b/apps/coldsurf-io/src/app/products/page.tsx @@ -1,5 +1,11 @@ +import { Metadata } from 'next' +import { ProductCard, ProductCardBottomSheet } from './(components)' import { ProductCardListLayout, TopTitle } from './(ui)' -import { ProductCard } from './(ui)/product-card' + +export const metadata: Metadata = { + title: 'Products - COLDSURF', + description: 'Explore our products to grow your artistic life even more easily!', +} export default function ProductsPage() { return ( @@ -9,28 +15,41 @@ export default function ProductsPage() { + ) } diff --git a/apps/coldsurf-io/src/stores/index.ts b/apps/coldsurf-io/src/stores/index.ts new file mode 100644 index 00000000..3d3e93b5 --- /dev/null +++ b/apps/coldsurf-io/src/stores/index.ts @@ -0,0 +1 @@ +export * from './product-store' diff --git a/apps/coldsurf-io/src/stores/product-store/index.ts b/apps/coldsurf-io/src/stores/product-store/index.ts new file mode 100644 index 00000000..3d3e93b5 --- /dev/null +++ b/apps/coldsurf-io/src/stores/product-store/index.ts @@ -0,0 +1 @@ +export * from './product-store' diff --git a/apps/coldsurf-io/src/stores/product-store/product-store.ts b/apps/coldsurf-io/src/stores/product-store/product-store.ts new file mode 100644 index 00000000..a3ce0a84 --- /dev/null +++ b/apps/coldsurf-io/src/stores/product-store/product-store.ts @@ -0,0 +1,16 @@ +import { Product } from '@/types' +import { create } from 'zustand' + +export type ProductStore = { + isProductBottomSheetOpen: boolean + setIsProductBottomSheetOpen: (value: boolean) => void + selectedProduct: Product | null + setSelectedProduct: (product: Product | null) => void +} + +export const useProductStore = create((set) => ({ + isProductBottomSheetOpen: false, + setIsProductBottomSheetOpen: (value: boolean) => set({ isProductBottomSheetOpen: value }), + selectedProduct: null, + setSelectedProduct: (product: Product | null) => set({ selectedProduct: product }), +})) diff --git a/apps/coldsurf-io/src/types/index.ts b/apps/coldsurf-io/src/types/index.ts new file mode 100644 index 00000000..49ddadc8 --- /dev/null +++ b/apps/coldsurf-io/src/types/index.ts @@ -0,0 +1 @@ +export * from './types.product' diff --git a/apps/coldsurf-io/src/types/types.product.ts b/apps/coldsurf-io/src/types/types.product.ts new file mode 100644 index 00000000..d42691f0 --- /dev/null +++ b/apps/coldsurf-io/src/types/types.product.ts @@ -0,0 +1,10 @@ +export type Product = { + title: string + description: string + longDescription: string + imgSrc: string + backgroundImgSrc: string + productImgSrc: string + ctaTitle?: string + ctaLink?: string +} diff --git a/apps/wamuseum-client/features/banner/ui/appstore-banner-generator/appstore-banner-generator.styled.ts b/apps/wamuseum-client/features/banner/ui/appstore-banner-generator/appstore-banner-generator.styled.ts index 9d4723c1..cc534ccb 100644 --- a/apps/wamuseum-client/features/banner/ui/appstore-banner-generator/appstore-banner-generator.styled.ts +++ b/apps/wamuseum-client/features/banner/ui/appstore-banner-generator/appstore-banner-generator.styled.ts @@ -26,7 +26,7 @@ export const StyledDndFileZone = styled(DndFileZone)<{ export const StyledPromotionText = styled(Text)` text-align: center; color: ${colors.oc.black.value}; - font-size: 3.5vw; + font-size: 3vw; margin: 2.5rem 0px 0px 0px; ` diff --git a/packages/ocean-road/src/icon-button/icon-button.styled.ts b/packages/ocean-road/src/icon-button/icon-button.styled.ts new file mode 100644 index 00000000..0e909e70 --- /dev/null +++ b/packages/ocean-road/src/icon-button/icon-button.styled.ts @@ -0,0 +1,8 @@ +import styled from '@emotion/styled' + +export const StyledIconButton = styled.button` + background: initial; + border-radius: 50%; + border: none; + cursor: pointer; +` diff --git a/packages/ocean-road/src/icon-button/icon-button.tsx b/packages/ocean-road/src/icon-button/icon-button.tsx new file mode 100644 index 00000000..ca264029 --- /dev/null +++ b/packages/ocean-road/src/icon-button/icon-button.tsx @@ -0,0 +1,11 @@ +import { forwardRef } from 'react' +import { StyledIconButton } from './icon-button.styled' +import { IconButtonProps } from './icon-button.types' + +export const IconButton = forwardRef(({ children, ...otherProps }, ref) => { + return ( + + {children} + + ) +}) diff --git a/packages/ocean-road/src/icon-button/icon-button.types.ts b/packages/ocean-road/src/icon-button/icon-button.types.ts new file mode 100644 index 00000000..a525fe61 --- /dev/null +++ b/packages/ocean-road/src/icon-button/icon-button.types.ts @@ -0,0 +1,3 @@ +import { ButtonHTMLAttributes } from 'react' + +export type IconButtonProps = {} & ButtonHTMLAttributes diff --git a/packages/ocean-road/src/icon-button/index.ts b/packages/ocean-road/src/icon-button/index.ts new file mode 100644 index 00000000..69acc17f --- /dev/null +++ b/packages/ocean-road/src/icon-button/index.ts @@ -0,0 +1,2 @@ +export * from './icon-button' +export * from './icon-button.types' diff --git a/packages/ocean-road/src/index.d.ts b/packages/ocean-road/src/index.d.ts index 062c1c84..8a1cb4a0 100644 --- a/packages/ocean-road/src/index.d.ts +++ b/packages/ocean-road/src/index.d.ts @@ -2,6 +2,7 @@ export { Button } from './button' export * from './contexts/ColorSchemeProvider' export { default as ColorSchemeProvider, useColorScheme } from './contexts/ColorSchemeProvider' export { default as GlobalStyle } from './GlobalStyle' +export * from './icon-button' export * from './modal' export * from './spinner' export * from './text' diff --git a/packages/ocean-road/src/index.ts b/packages/ocean-road/src/index.ts index 062c1c84..8a1cb4a0 100644 --- a/packages/ocean-road/src/index.ts +++ b/packages/ocean-road/src/index.ts @@ -2,6 +2,7 @@ export { Button } from './button' export * from './contexts/ColorSchemeProvider' export { default as ColorSchemeProvider, useColorScheme } from './contexts/ColorSchemeProvider' export { default as GlobalStyle } from './GlobalStyle' +export * from './icon-button' export * from './modal' export * from './spinner' export * from './text' diff --git a/yarn.lock b/yarn.lock index 5af4efb8..71e70a32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3651,6 +3651,7 @@ __metadata: react-lite-youtube-embed: "npm:*" sst: "npm:*" typescript: "npm:*" + zustand: "npm:*" languageName: unknown linkType: soft