diff --git a/.nvmrc b/.nvmrc index 7950a445..3f430af8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.17.0 +v18 diff --git a/package.json b/package.json index d0cbaf88..aa3c244d 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,10 @@ { - "name": "@luxdefi/ui", + "name": "@luxdefi/ui/cli", "version": "0.0.1", - "private": true, "license": "MIT", "type": "module", "author": { - "name": "luxdefi", + "name": "@lx/ui", "url": "https://twitter.com/luxdefi" }, "workspaces": [ diff --git a/packages/cli/package.json b/packages/cli/package.json index 44baf32f..67d04940 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,4 +1,5 @@ - "name": "luxdefi-ui", +{ + "name": "@luxdefi/ui/cli", "version": "0.6.0", "description": "Add components to your apps.", "publishConfig": { diff --git a/packages/ui/blocks/components/accordian-block.tsx b/packages/ui/blocks/components/accordian-block.tsx new file mode 100644 index 00000000..6597c248 --- /dev/null +++ b/packages/ui/blocks/components/accordian-block.tsx @@ -0,0 +1,50 @@ +import React from 'react' + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, + ApplyTypography +} from '../../primitives' + +import { cn } from '../../util' + +import type { Block, AccordianBlock } from '../def' + +const AccordianBlockComponent: React.FC<{ + block: Block + className?: string +}> = ({ + block, + className='' +}) => { + + if (block.blockType !== 'accordian') { + return <>accordian block required + } + + const accordian = block as AccordianBlock + + return ( + + {accordian.items.map((item, index) => ( + + + + {/* styles specific to accordion. From old site */} +
{item.trigger}
+
+
+ + + {(typeof item.content === 'string') ? (

{item.content}

) : item.content} +
+
+
+ ))} +
+ ) +} + +export default AccordianBlockComponent diff --git a/packages/ui/blocks/components/banner-block.tsx b/packages/ui/blocks/components/banner-block.tsx new file mode 100644 index 00000000..e550fc24 --- /dev/null +++ b/packages/ui/blocks/components/banner-block.tsx @@ -0,0 +1,103 @@ + +import React from 'react' + +import { Dimensions, type TShirtSize } from '../../types' +import type { Block, BannerBlock } from '../def' + +import VideoBlockComponent from './video-block' +import CTABlockComponent from './cta-block' + +type BannerGrouping = 'all-separate' | 'title-media-cta' | 'titleAndMedia-cta' + +const BannerBlockComponent: React.FC<{ + block: Block, + videoSize?: TShirtSize + videoConstraint?: Dimensions + grouping?: BannerGrouping + groupingClasses?: string[] // count should match number of siblings in the chosen grouping + ctaItemClassName?: string +}> = ({ + block, + grouping = 'titleAndMedia-cta', + groupingClasses=[], + ctaItemClassName='', + videoSize='md', + videoConstraint +}) => { + + if (block.blockType !== 'banner') { + return <>banner block required + } + const banner = block as BannerBlock + + if (grouping === 'title-media-cta') { + const titleClasses = (groupingClasses && groupingClasses[0]) ? groupingClasses[0] : '' + const mediaClasses = (groupingClasses && groupingClasses[1]) ? groupingClasses[1] : '' + const ctaClasses = (groupingClasses && groupingClasses[2]) ? groupingClasses[2] : '' + return (<> +
+

{banner.title}

+ {banner.byline && (
{banner.byline}
)} +
+
+ {banner.contentBefore && banner.contentBefore} + {banner.video && ( + + )} + {banner.contentAfter && banner.contentAfter } +
+ {banner.cta && ( +
+ +
+ )} + ) + } + else if (grouping === 'titleAndMedia-cta') { + const titleAndMediaClasses = (groupingClasses && groupingClasses[0]) ? groupingClasses[0] : '' + const ctaClasses = (groupingClasses && groupingClasses[1]) ? groupingClasses[1] : '' + return (<> +
+

{banner.title}

+ {banner.byline && (
{banner.byline}
)} + {banner.contentBefore && banner.contentBefore} + {banner.video && ( + + )} + {banner.contentAfter && banner.contentAfter } +
+ {banner.cta && ( +
+ +
+ )} + ) + } + + const titleClasses = (groupingClasses && groupingClasses[0]) ? groupingClasses[0] : '' + const bylineClasses = (groupingClasses && groupingClasses[1]) ? groupingClasses[1] : '' + const contentBeforeClasses = (groupingClasses && groupingClasses[2]) ? groupingClasses[2] : '' + const mediaClasses = (groupingClasses && groupingClasses[3]) ? groupingClasses[3] : '' + const contentAfterClasses = (groupingClasses && groupingClasses[4]) ? groupingClasses[4] : '' + const ctaClasses = (groupingClasses && groupingClasses[5]) ? groupingClasses[5] : '' + + return (<> +

{banner.title}

+ {banner.byline && (
{banner.byline}
)} + {banner.contentBefore && (
banner.contentBefore
)} + {banner.video && ( + + )} + {banner.contentAfter && (
banner.contentAfter
)} + {banner.cta && ( +
+ +
+ )} + ) +} + +export { + BannerBlockComponent as default, + type BannerGrouping as AssetBannderGroupingType +} diff --git a/packages/ui/blocks/components/block-factory.tsx b/packages/ui/blocks/components/block-factory.tsx new file mode 100644 index 00000000..676b05df --- /dev/null +++ b/packages/ui/blocks/components/block-factory.tsx @@ -0,0 +1,52 @@ +import React from 'react' + +import type * as B from '../def' + +import CardBlockComponent from './card-block' +import HeadingBlockComponent from './heading-block' +import CTABlockComponent from './cta-block' +import SpaceBlockComponent from './space-block' +import ImageBlockComponent from './image-block' +import VideoBlockComponent from './video-block' +import AccordianBlockComponent from './accordian-block' +import GroupBlockComponent from './group-block' + +const BlockFactory: React.FC<{ + block: B.Block + className?: string +}> = ({ + block, + className='' +}) => { + + switch (block.blockType) { + case 'group': { + return + } + case 'card': { + return + } + case 'cta': { + return + } + case 'heading': { + return + } + case 'space': { + return + } + case 'video': { + return + } + case 'image': { + return + } + case 'accordian': { + return + } + } + + return <>unknown block type +} + +export default BlockFactory diff --git a/packages/ui/blocks/components/card-block.tsx b/packages/ui/blocks/components/card-block.tsx new file mode 100644 index 00000000..595b7261 --- /dev/null +++ b/packages/ui/blocks/components/card-block.tsx @@ -0,0 +1,214 @@ +import React from 'react' + +import type { Dimensions, LinkDef, TShirtDimensions } from '../../types' + +import { + ApplyTypography, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + type TypographySize +} from '../../primitives' + +import { + Icons, + LinkElement +} from '../../common' + +import { + getSpecifierData, + getPrimaryStartingWith, + getDim, +} from '../../util/specifier' + +import type { Block, CardBlock } from '../def' + + +import ImageBlockComponent from './image-block' +import VideoBlockComponent from './video-block' +import CTABlockComponent from './cta-block' + +const ArrowLinkElement: React.FC<{ + def: LinkDef, +}> = ({ + def +}) => ( + } + iconAfter + /> +) + +const getTypographySize = (s: string): TypographySize => ( + getSpecifierData( + s, + (s: string) => (getPrimaryStartingWith(s, 'typography')), + (s: string): TypographySize | undefined => { + const subTokenArray = s.split('-') + return subTokenArray[subTokenArray.length - 1] as TypographySize + }, + 'responsive' + ) as TypographySize +) + +const getSmallIconDim = (s: string): Dimensions | undefined => ( + getSpecifierData( + s, + (s: string) => (getPrimaryStartingWith(s, 'small-icon')), + getDim, + ) +) + +const CardBlockComponent: React.FC<{ + block: Block + className?: string + contentClassName?: string +}> = ({ + block, + className='', + contentClassName='' +}) => { + + if (block.blockType !== 'card') { + return <>card block required + } + + const card = block as CardBlock + const has = (s: string) => (card.specifiers?.includes(s)) + + const ghost = has('ghost') // no outer padding, no borders, larger title, all left-aligned bg is same (default) + + const contentclx = (has('content-left') ? 'items-start ' : 'items-center ') + contentClassName + const disabledBorder = (has('appear-disabled' ) ? ' border-muted-4' : ' border-muted-3') + const outerBorder = ((has('no-outer-border') || ghost) ? ' border-0' : '') + const innerBorder = (ghost ? ' border-0' : '') + const paddingclx = ghost ? ' px-0 py-0' : ' px-6 py-3' + const mainGap = ghost ? ' gap-2' : '' + const disabledText = (has('appear-disabled') ? ' text-muted-2' : '') + const disabledTypoText = (has('appear-disabled') ? ' typography-p:text-muted-2' : '') + const bgclx = (has('bg-card') ? ' bg-level-1' : '') + const titleclx = (has('heading-style-title') ? ' font-heading text-base leading-tight' : '') + + (ghost ? ' text-left md:text-xl' : '') + + const typoSize: TypographySize = (card.specifiers) ? getTypographySize(card.specifiers) : 'responsive' + const typoclx = (typoSize === 'sm') ? 'typography-sm typography-p:text-sm ' : (typoSize === 'lg') ? 'typography-lg ' : '' + + const contentBefore = has('content-before') + const iconInline = has('icon-inline') + const contentOnHover = has('reveal-content-on-hover') + //const smallIconDim = (contentOnHover && card.specifiers) ? getSmallIconDim(card.specifiers) : undefined + + const Header: React.FC<{ + inContent?: boolean + className?: string + }> = ({ + inContent=false, + className='' + }) => ( + (card.title || card.byline || card.icon) ? ( + +
+ {(card.icon && !card.iconAfter ) && (
{card.icon}
)} + {card.title && ( + + {card.title} + + )} + {(card.icon && card.iconAfter) && (
{card.icon}
)} +
+ {card.byline && ({card.byline})} +
+ ) : null + ) + + const MediaAndContent: React.FC<{ + className?: string + }> = ({ + className='' + }) => (has('media-left') ? ( + // media left layout + + {card.media && ( +
+ +
+ )} + {card.content && ( + + {(typeof card.content === 'string') ? (

{card.content}

) : card.content} +
+ )} +
+ ) : ( // default layout + + {contentOnHover && (
)} + {card.content && contentBefore && ( + (typeof card.content === 'string') ? (

{card.content}

) : card.content + )} + {card.media && (card.media.blockType === 'image' ? ( + + ) : ( + + ))} + {card.content && !contentBefore && ( + (typeof card.content === 'string') ? (

{card.content}

) : card.content + )} + + )) + + const Footer: React.FC = () => ( !card.cta ? null : (has('links-w-arrow') ? ( + // links w arrow + + ()} + /> + + ) : ( // default + + + + ))) + + return ( + +