diff --git a/src/assets/icons/cra-logo.svg b/src/assets/icons/cra-logo.svg new file mode 100644 index 000000000000..7bd1599766bb --- /dev/null +++ b/src/assets/icons/cra-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/next-logo.svg b/src/assets/icons/next-logo.svg new file mode 100644 index 000000000000..e8675a6d0f59 --- /dev/null +++ b/src/assets/icons/next-logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/vite-logo.svg b/src/assets/icons/vite-logo.svg new file mode 100644 index 000000000000..e7b8dfb1b2a6 --- /dev/null +++ b/src/assets/icons/vite-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/blocks/TemplatesBlock/TemplatesBlock.scss b/src/blocks/TemplatesBlock/TemplatesBlock.scss new file mode 100644 index 000000000000..01aeff006b35 --- /dev/null +++ b/src/blocks/TemplatesBlock/TemplatesBlock.scss @@ -0,0 +1,14 @@ +@use '~@gravity-ui/page-constructor/styles/styles.scss' as pcStyles; +@use '~@gravity-ui/page-constructor/styles/variables.scss' as pcVariables; +@use '../../variables.scss'; + +$block: '.#{variables.$ns}templates-block'; + +#{$block} { + @include pcStyles.animate(); + + &__title { + @include pcStyles.heading2(); + margin-bottom: pcVariables.$indentM; + } +} diff --git a/src/blocks/TemplatesBlock/TemplatesBlock.tsx b/src/blocks/TemplatesBlock/TemplatesBlock.tsx new file mode 100644 index 000000000000..c17f6554f703 --- /dev/null +++ b/src/blocks/TemplatesBlock/TemplatesBlock.tsx @@ -0,0 +1,33 @@ +import {Animatable, AnimateBlock, HTML} from '@gravity-ui/page-constructor'; +import React from 'react'; + +import {Templates} from '../../components/Templates'; +import type {Tab} from '../../components/Templates'; +import {block} from '../../utils'; +import {CustomBlock} from '../constants'; + +import './TemplatesBlock.scss'; + +const b = block('templates-block'); + +export type TemplatesProps = Animatable & { + title: string; + tabs: Tab[]; +}; + +export type TemplatesModel = TemplatesProps & { + type: CustomBlock.Templates; +}; + +export const TemplatesBlock: React.FC = ({animated, title, tabs}) => { + return ( + + + {title} + + + + ); +}; + +export default TemplatesBlock; diff --git a/src/blocks/constants.ts b/src/blocks/constants.ts index 42885b8e70fc..b4d0d26e91eb 100644 --- a/src/blocks/constants.ts +++ b/src/blocks/constants.ts @@ -4,4 +4,5 @@ export enum CustomBlock { CustomBanner = 'custom-banner', Examples = 'examples', Roadmap = 'roadmap', + Templates = 'templates', } diff --git a/src/blocks/types.ts b/src/blocks/types.ts index 2dd07298df4c..53306cde7d2f 100644 --- a/src/blocks/types.ts +++ b/src/blocks/types.ts @@ -3,10 +3,12 @@ import {CustomExtendedFeaturesModel} from './CustomExtendedFeatures/CustomExtend import {CustomHeaderModel} from './CustomHeader/CustomHeader'; import {ExamplesModel} from './Examples/Examples'; import {RoadmapModel} from './RoadmapBlock/RoadmapBlock'; +import {TemplatesModel} from './TemplatesBlock/TemplatesBlock'; export type CustomBlockModel = | CustomHeaderModel | CustomExtendedFeaturesModel | CustomBannerModel | ExamplesModel - | RoadmapModel; + | RoadmapModel + | TemplatesModel; diff --git a/src/components/Landing/Landing.tsx b/src/components/Landing/Landing.tsx index 2cac0b6426eb..40013da77e42 100644 --- a/src/components/Landing/Landing.tsx +++ b/src/components/Landing/Landing.tsx @@ -6,6 +6,7 @@ import {CustomExtendedFeatures} from '../../blocks/CustomExtendedFeatures/Custom import {CustomHeader} from '../../blocks/CustomHeader/CustomHeader'; import {Examples} from '../../blocks/Examples/Examples'; import {RoadmapBlock} from '../../blocks/RoadmapBlock/RoadmapBlock'; +import TemplatesBlock from '../../blocks/TemplatesBlock/TemplatesBlock'; import {CustomBlock} from '../../blocks/constants'; import {landing} from '../../content/landing'; import {useSectionScroll} from '../../hooks/useSectionScroll'; @@ -23,6 +24,7 @@ export const Landing: React.FC = () => { [CustomBlock.CustomBanner]: CustomBanner, [CustomBlock.Examples]: Examples, [CustomBlock.Roadmap]: RoadmapBlock, + [CustomBlock.Templates]: TemplatesBlock, }, }} /> diff --git a/src/components/Templates/Templates.scss b/src/components/Templates/Templates.scss new file mode 100644 index 000000000000..491a6244c3f4 --- /dev/null +++ b/src/components/Templates/Templates.scss @@ -0,0 +1,37 @@ +@use '~@gravity-ui/page-constructor/styles/styles.scss' as pcStyles; +@use '~@gravity-ui/page-constructor/styles/variables.scss' as pcVariables; +@use '../../variables.scss'; + +$block: '.#{variables.$ns}templates'; + +#{$block} { + background: #251b25; + border-radius: pcVariables.$borderRadius; + padding: 12px 32px 24px; + display: flex; + flex-direction: column; + gap: 20px; + + &__commands-wrapper { + width: 100%; + padding: 16px; + background: #160d1b; + border-radius: 16px; + + @media (max-width: map-get(pcVariables.$gridBreakpoints, 'lg') - 1) { + width: inherit; + } + } + + &__commands { + overflow-x: auto; + white-space: nowrap; + position: relative; + } + + &__copy { + position: absolute; + top: 0; + right: 0; + } +} diff --git a/src/components/Templates/Templates.tsx b/src/components/Templates/Templates.tsx new file mode 100644 index 000000000000..be8ae857dd70 --- /dev/null +++ b/src/components/Templates/Templates.tsx @@ -0,0 +1,74 @@ +import {Button, ClipboardButton, Icon, Tabs, Text} from '@gravity-ui/uikit'; +import React from 'react'; + +import {block} from '../../utils'; + +import './Templates.scss'; +import type {Tab} from './types'; + +const b = block('templates'); + +interface CommandsProps { + commands: string[]; +} + +const Commands: React.FC = ({commands}) => { + return ( + + + {commands.map((item, index) => { + return ( + + {item} + + ); + })} + + + + ); +}; + +interface TabContentProps { + data?: Pick; +} + +const TabContent: React.FC = ({data}) => { + if (!data) { + return null; + } + + return ( + <> + {data.commands && } + {data.button?.href && ( + + + {data.button.title} + + + )} + > + ); +}; + +interface TemplatesProps { + tabs: Tab[]; +} + +export const Templates: React.FC = ({tabs}) => { + const [activeTab, setActiveTab] = React.useState(() => tabs[0]?.title); + const tabsItems = tabs.map(({title, icon}) => ({ + id: title, + title, + icon: icon ? : undefined, + })); + const activeTabData = tabs.find((el) => el.title === activeTab); + + return ( + + + + + ); +}; diff --git a/src/components/Templates/index.ts b/src/components/Templates/index.ts new file mode 100644 index 000000000000..026620706e1b --- /dev/null +++ b/src/components/Templates/index.ts @@ -0,0 +1,3 @@ +export type {Tab} from './types'; + +export {Templates} from './Templates'; diff --git a/src/components/Templates/types.ts b/src/components/Templates/types.ts new file mode 100644 index 000000000000..723820fe20c1 --- /dev/null +++ b/src/components/Templates/types.ts @@ -0,0 +1,12 @@ +import {SVGIconData} from '@gravity-ui/uikit/build/esm/components/Icon/types'; + +export interface Tab { + title: string; + icon?: SVGIconData; + commands?: string[]; + button?: { + title: string; + href?: string; + target?: string; + }; +} diff --git a/src/content/landing.ts b/src/content/landing.ts index 0910dbbedc03..960bbf64e42f 100644 --- a/src/content/landing.ts +++ b/src/content/landing.ts @@ -1,15 +1,17 @@ import {Block, BlockType, PageContent} from '@gravity-ui/page-constructor'; import backgroundAsset from '../assets/background.jpg'; -import bannerAsset from '../assets/banner.svg'; import companiesDesktopAsset from '../assets/companies-desktop.svg'; import companiesMobileAsset from '../assets/companies-mobile.svg'; import companiesTabletAsset from '../assets/companies-tablet.svg'; import featureShieldAsset from '../assets/feature-shield.svg'; import featureStarAsset from '../assets/feature-star.svg'; import featureUnionAsset from '../assets/feature-union.svg'; +import craLogo from '../assets/icons/cra-logo.svg'; import figmaFillIcon from '../assets/icons/figma-fill.svg'; import githubIcon from '../assets/icons/github.svg'; +import nextLogo from '../assets/icons/next-logo.svg'; +import viteLogo from '../assets/icons/vite-logo.svg'; import {CustomBlock} from '../blocks/constants'; import {CustomBlockModel} from '../blocks/types'; import {libs} from '../libs.mjs'; @@ -150,14 +152,52 @@ const typedLanding: CustomPageContent = { tasks: roadmapTasks, }, { - type: CustomBlock.CustomBanner, + type: CustomBlock.Templates, title: 'Start creating with Gravity UI', - color: '#23151e', - image: bannerAsset, - commands: [ - 'git clone git@github.com:gravity-ui/uikit-example-cra.git my-project && cd my-project', - 'npm i', - 'npm run start', + tabs: [ + { + title: 'CRA', + icon: craLogo, + commands: [ + 'npx create-react-app my-app --template gravity-ui-pure', + 'cd my-app', + 'npm start', + ], + // button: { + // href: 'ya.ru', + // target: '_blank', + // title: 'Open CRA Playground', + // }, + }, + { + title: 'Next.js', + icon: nextLogo, + commands: [ + 'npx create-next-app@latest my-app --example "https://github.com/gravity-ui/gravity-ui-nextjs-example"', + 'cd my-app', + 'npm run dev', + ], + // button: { + // href: 'ya.ru', + // target: '_blank', + // title: 'Open Next.js Playground', + // }, + }, + { + title: 'Vite', + icon: viteLogo, + commands: [ + 'npx degit gravity-ui/gravity-ui-vite-example#main my-project', + 'cd my-project', + 'npm install', + 'npm run dev', + ], + // button: { + // href: 'ya.ru', + // target: '_blank', + // title: 'Open Next.js Playground', + // }, + }, ], }, {