├── .gitignore ├── LICENSE ├── README.md ├── components ├── ReactBricksApp.tsx ├── errorNoFooter.tsx ├── errorNoHeader.tsx ├── errorNoKeys.tsx └── layout.tsx ├── css ├── Button.module.css ├── FeatureItem.module.css ├── Features.module.css ├── Footer.module.css ├── FooterColumn.module.css ├── FooterLink.module.css ├── Header.module.css ├── HeaderMenuItem.module.css ├── HeaderMenuSubItem.module.css ├── HeroUnit.module.css ├── Pokemon.module.css ├── errorNoFooter.module.css ├── errorNoHeader.module.css ├── errorNoKeys.module.css ├── layout.module.css └── style.css ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── [[...slug]].tsx ├── _app.tsx ├── admin │ ├── app-settings.tsx │ ├── editor.tsx │ ├── index.tsx │ ├── media.tsx │ ├── playground.tsx │ ├── sso-failure.tsx │ ├── sso-login.tsx │ └── sso-success.tsx └── preview.tsx ├── public ├── favicon.ico ├── logo.svg ├── react-bricks-icon.svg └── react-bricks-logo.svg ├── react-bricks ├── NextLink.tsx ├── bricks │ ├── HeroUnit.tsx │ ├── features │ │ ├── FeatureItem.tsx │ │ ├── Features.tsx │ │ ├── defaultImages.ts │ │ └── index.ts │ ├── index.ts │ └── layout │ │ ├── Button.tsx │ │ ├── Footer.tsx │ │ ├── FooterColumn.tsx │ │ ├── FooterLink.tsx │ │ ├── Header.tsx │ │ ├── HeaderMenuItem.tsx │ │ ├── HeaderMenuSubItem.tsx │ │ ├── index.ts │ │ └── useClickOutside.ts ├── config.ts └── pageTypes.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # lock files 28 | yarn.lock 29 | pnpm-lock.yaml 30 | package-lock.json 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 React Bricks 2 | 3 | Permission is hereby granted, free of charge, to any user of the React Bricks 4 | services, to use this starter project and the included pre-made content blocks 5 | in order to create a website using the React Bricks services. 6 | This is a "source available" license. 7 | 8 | As for any part of the React Bricks services, the use is subject to the 9 | acceptance of the general Terms of Service, which can be found at: 10 | https://reactbricks.com/legal/terms 11 | 12 | In particular, the users may not use this project for the purpose of (i) 13 | building a competitive product or service, (ii) building a product using 14 | similar ideas, features, functions, user interface of graphics of React 15 | Bricks Services, (iii) copying any ideas, features, functions, user 16 | interface or graphics of the React Bricks Services. 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Bricks starter with Next.js 2 | 3 | Kick-start your project with this boilerplate for a complete Next.js website based on [React Bricks](https://reactbricks.com), with both the front-end and admin dashboard. 4 | 5 | ## 🚀 Quick start 6 | 7 | We suggest that you use the CLI and choose this starter. 8 | In this way you will have the credentials already set up in a `.env.local` file: 9 | 10 | ```bash 11 | npx create-reactbricks-app 12 | ``` 13 | 14 | Otherwise you can directly clone this repo: 15 | 16 | ```bash 17 | git clone https://github.com/reactbricks/nextjs-starter-reactbricks your-project 18 | ``` 19 | 20 | ## 📖 Documentation 21 | 22 | Please, read our documentation at [Reactbricks.com](https://reactbricks.com). 23 | -------------------------------------------------------------------------------- /components/ReactBricksApp.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { ReactBricks } from 'react-bricks/frontend' 3 | import type { AppProps } from 'next/app' 4 | import { Nunito_Sans } from 'next/font/google' 5 | import config from '../react-bricks/config' 6 | import { useTheme } from 'next-themes' 7 | 8 | const nunito = Nunito_Sans({ 9 | subsets: ['latin'], 10 | display: 'swap', 11 | weight: ['300', '400', '600', '700', '800', '900'], 12 | style: ['normal', 'italic'], 13 | variable: '--font-nunito', 14 | }) 15 | 16 | const ReactBricksApp = ({ Component, pageProps }: AppProps) => { 17 | // Color Mode Management 18 | const savedColorMode = 19 | typeof window === 'undefined' ? '' : localStorage.getItem('color-mode') 20 | 21 | const [colorMode, setColorMode] = useState(savedColorMode || 'light') 22 | 23 | const { setTheme } = useTheme() 24 | 25 | const toggleColorMode = () => { 26 | const newColorMode = colorMode === 'light' ? 'dark' : 'light' 27 | setColorMode(newColorMode) 28 | localStorage.setItem('color-mode', newColorMode) 29 | setTheme(newColorMode) 30 | } 31 | 32 | const reactBricksConfig = { 33 | ...config, 34 | isDarkColorMode: colorMode === 'dark', 35 | toggleColorMode, 36 | contentClassName: `${nunito.className} ${colorMode} ${ 37 | colorMode === 'dark' ? 'dark darkContentClass' : 'light whiteContentClass' 38 | }`, 39 | } 40 | 41 | return ( 42 | 43 | 44 | 45 | ) 46 | } 47 | 48 | export default ReactBricksApp 49 | -------------------------------------------------------------------------------- /components/errorNoFooter.tsx: -------------------------------------------------------------------------------- 1 | import styles from "../css/errorNoFooter.module.css" 2 | 3 | const ErrorNoFooter = () => { 4 | return ( 5 |
6 |

Warning: there is no footer.

7 |

8 | React Bricks cannot find an entity for the footer. 9 |

10 |
11 | ) 12 | } 13 | 14 | export default ErrorNoFooter 15 | -------------------------------------------------------------------------------- /components/errorNoHeader.tsx: -------------------------------------------------------------------------------- 1 | import styles from "../css/errorNoHeader.module.css" 2 | 3 | const ErrorNoHeader = () => { 4 | return ( 5 |
6 |

Warning: there is no header.

7 |

8 | React Bricks cannot find an entity for the header. 9 |

10 |
11 | ) 12 | } 13 | 14 | export default ErrorNoHeader 15 | -------------------------------------------------------------------------------- /components/errorNoKeys.tsx: -------------------------------------------------------------------------------- 1 | import styles from "../css/errorNoKeys.module.css" 2 | 3 | const ErrorNoKeys = () => { 4 | return ( 5 |
6 |

Warning: missing App credentials

7 |

8 | NEXT_PUBLIC_APP_ID and{" "} 9 | API_KEY are not configured in 10 | your .env.local file. 11 |

12 |

13 | Please create a .env.local{" "} 14 | file with: 15 |

16 |
17 |         {`NEXT_PUBLIC_APP_ID=...
18 | API_KEY=...`}
19 |       
20 |
21 | ) 22 | } 23 | 24 | export default ErrorNoKeys 25 | -------------------------------------------------------------------------------- /components/layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useContext, useEffect, useState } from 'react' 2 | import { Nunito_Sans } from 'next/font/google' 3 | import styles from '../css/layout.module.css' 4 | 5 | interface LayoutProps { 6 | children?: ReactNode 7 | } 8 | 9 | const nunito = Nunito_Sans({ 10 | subsets: ['latin'], 11 | display: 'swap', 12 | weight: ['300', '400', '600', '700', '800', '900'], 13 | style: ['normal', 'italic'], 14 | variable: '--font-nunito', 15 | }) 16 | 17 | const Layout: React.FC = ({ children }) => { 18 | return ( 19 |
20 |
{children}
21 |
22 | ) 23 | } 24 | 25 | export default Layout 26 | -------------------------------------------------------------------------------- /css/Button.module.css: -------------------------------------------------------------------------------- 1 | .buttonWrapper { 2 | display: inline-block; 3 | white-space: nowrap; 4 | text-align: center; 5 | border-radius: 9999px; 6 | font-weight: 700; 7 | line-height: 1; 8 | transition-property: all; 9 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 10 | transition-duration: 150ms; 11 | } 12 | 13 | .buttonWrapper:hover { 14 | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 15 | transform: translateY(-0.125rem); 16 | } 17 | 18 | .buttonPsmall { 19 | padding-top: 0.5rem; 20 | padding-bottom: 0.5rem; 21 | padding-left: 1rem; 22 | padding-right: 1rem; 23 | font-size: 0.875rem; 24 | line-height: 1.25rem; 25 | min-width: 75px; 26 | } 27 | 28 | .buttonPnormal { 29 | padding-top: 0.75rem; 30 | padding-bottom: 0.75rem; 31 | padding-left: 1.25rem; 32 | padding-right: 1.25rem; 33 | min-width: 120px; 34 | } 35 | 36 | .buttonColorSolid { 37 | background-color: rgb(14 165 233); 38 | color: white; 39 | } 40 | .buttonColorSolid:hover { 41 | background-color: rgb(2 132 199); 42 | } 43 | 44 | .buttonColorOutline { 45 | border-width: 1px; 46 | border-color: rgb(2 132 199); 47 | color: rgb(2 132 199); 48 | } 49 | :global(.dark) .buttonColorOutline { 50 | border-color: rgb(255 255 255); 51 | color: white; 52 | } 53 | -------------------------------------------------------------------------------- /css/FeatureItem.module.css: -------------------------------------------------------------------------------- 1 | .cols2, 2 | .cols3, 3 | .cols4 { 4 | margin-bottom: 3rem; 5 | } 6 | 7 | .featureItemContainer { 8 | font-size: 1rem; 9 | line-height: 1.5rem; 10 | } 11 | 12 | .imageClassName { 13 | display: block; 14 | width: 3rem; 15 | height: 3rem; 16 | object-fit: contain; 17 | } 18 | 19 | .imageWrapper { 20 | float: left; 21 | margin-right: 1.25rem; 22 | margin-top: 0.25rem; 23 | } 24 | 25 | .textFeatureItemContainer { 26 | overflow: hidden; 27 | } 28 | 29 | .title { 30 | font-weight: 700; 31 | margin-bottom: 0.25rem; 32 | color: rgb(31 41 55); 33 | } 34 | 35 | :global(.dark) .title { 36 | color: white; 37 | } 38 | 39 | .textColor { 40 | color: rgb(107 114 128); 41 | } 42 | 43 | :global(.dark) .textColor { 44 | color: white; 45 | } 46 | 47 | .linkContainer { 48 | margin-top: 0.5rem; 49 | } 50 | 51 | .linkWrapper { 52 | cursor: pointer; 53 | color: rgb(14 165 233); 54 | transition-property: all; 55 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 56 | transition-duration: 150ms; 57 | } 58 | 59 | .linkWrapper:hover { 60 | color: rgb(2 132 199); 61 | transform: translateY(-1px); 62 | } 63 | 64 | .linkTextPlain1 { 65 | display: flex; 66 | align-items: center; 67 | } 68 | 69 | .linkTextPlain1 > :not([hidden]) ~ :not([hidden]) { 70 | margin-left: 0.25rem; 71 | } 72 | 73 | .svgClass { 74 | width: 10px; 75 | height: 10px; 76 | } 77 | 78 | .linkTextPlain2 { 79 | display: inline-block; 80 | } 81 | 82 | .linkTextPlain3 { 83 | display: none; 84 | } 85 | 86 | @media (min-width: 640px) { 87 | .cols2 { 88 | flex: 0 1 45%; 89 | margin-bottom: 4rem; 90 | } 91 | 92 | .cols3 { 93 | flex: 0 1 27%; 94 | margin-bottom: 4rem; 95 | } 96 | 97 | .cols4 { 98 | flex: 0 1 45%; 99 | margin-bottom: 4rem; 100 | } 101 | 102 | .imageWrapper { 103 | float: none; 104 | margin-right: 0; 105 | margin-top: 0; 106 | margin-bottom: 1.25rem; 107 | } 108 | } 109 | 110 | @media (min-width: 1024px) { 111 | .cols4 { 112 | flex: 0 1 20.1%; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /css/Features.module.css: -------------------------------------------------------------------------------- 1 | .section { 2 | background-color: white; 3 | } 4 | 5 | :global(.dark) .section { 6 | background-color: rgb(17 24 39); 7 | } 8 | 9 | .container { 10 | display: flex; 11 | flex-wrap: wrap; 12 | justify-content: space-between; 13 | padding-top: 3rem; 14 | padding-bottom: 3rem; 15 | } 16 | 17 | .sizeSmall { 18 | padding-left: 1.25rem; 19 | padding-right: 1.25rem; 20 | } 21 | 22 | .sizeNormal { 23 | padding-left: 1.25rem; 24 | padding-right: 1.25rem; 25 | } 26 | 27 | @media (min-width: 640px) { 28 | .sizeSmall { 29 | margin-right: 16.66666%; 30 | margin-left: 16.66666%; 31 | } 32 | 33 | .sizeNormal { 34 | margin-right: 5.55555%; 35 | margin-left: 5.55555%; 36 | } 37 | } 38 | 39 | @media (min-width: 1024px) { 40 | .container { 41 | padding-top: 4rem; 42 | padding-bottom: 4rem; 43 | } 44 | } 45 | 46 | @media (min-width: 1280px) { 47 | .sizeSmall { 48 | margin-right: 22.2222%; 49 | margin-left: 22.2222%; 50 | } 51 | .sizeNormal { 52 | margin-right: 11.1111%; 53 | margin-left: 11.1111%; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /css/Footer.module.css: -------------------------------------------------------------------------------- 1 | .section { 2 | background-color: #f9fafb; 3 | } 4 | 5 | :global(.dark) .section { 6 | background-color: rgb(17 24 39); 7 | } 8 | 9 | .container { 10 | display: flex; 11 | justify-content: space-between; 12 | flex-wrap: wrap; 13 | padding-top: 3rem; 14 | padding-bottom: 3rem; 15 | padding-left: 1.25rem; 16 | padding-right: 1.25rem; 17 | } 18 | 19 | @media (min-width: 640px) { 20 | .container { 21 | margin-left: 5.55555%; 22 | margin-right: 5.55555%; 23 | } 24 | } 25 | 26 | @media (min-width: 1280px) { 27 | .container { 28 | margin-left: 11.1111%; 29 | margin-right: 11.1111%; 30 | } 31 | } 32 | 33 | .elementsInfo { 34 | width: 100%; 35 | margin-bottom: 3rem; 36 | } 37 | 38 | @media (min-width: 1024px) { 39 | .elementsInfo { 40 | width: auto; 41 | margin-bottom: 0px; 42 | margin-right: 2rem; 43 | } 44 | 45 | .container { 46 | padding-top: 4rem; 47 | padding-bottom: 4rem; 48 | } 49 | } 50 | 51 | .linkLogo { 52 | display: block; 53 | margin-bottom: 1rem; 54 | } 55 | 56 | .imageLogo { 57 | width: 12rem; 58 | height: 1.75rem; 59 | object-fit: contain; 60 | object-position: left; 61 | } 62 | 63 | .paragraphRichText { 64 | font-size: 0.875rem; 65 | line-height: 1.25rem; 66 | color: rgb(107 114 128); 67 | } 68 | 69 | :global(.dark) .paragraphRichText { 70 | color: white; 71 | } 72 | 73 | .renderLink { 74 | color: rgb(14 165 233); 75 | transition-property: all; 76 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 77 | transition-duration: 150ms; 78 | } 79 | 80 | .renderLink:hover { 81 | color: rgb(2 132 199); 82 | transform: translateY(-1px); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /css/FooterColumn.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 50%; 3 | margin-bottom: 2rem; 4 | } 5 | 6 | @media (min-width: 640px) { 7 | .container { 8 | width: auto; 9 | margin-right: 2rem; 10 | } 11 | } 12 | 13 | .text { 14 | margin-bottom: 0.75rem; 15 | font-size: 0.75rem; 16 | line-height: 1rem; 17 | font-weight: 900; 18 | text-transform: uppercase; 19 | letter-spacing: 0.35em; 20 | min-width: 120px; 21 | color: rgb(156 163 175); 22 | } 23 | 24 | :global(.dark) .text { 25 | color: rgb(243 244 246); 26 | } 27 | -------------------------------------------------------------------------------- /css/FooterLink.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | font-size: 0.875rem; 3 | line-height: 1.25rem; 4 | margin-bottom: 0.75rem; 5 | color: rgb(107 114 128); 6 | transition-property: all; 7 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 8 | transition-duration: 150ms; 9 | } 10 | 11 | .text:hover { 12 | color: rgb(75 85 99); 13 | transform: translateY(-1px); 14 | } 15 | 16 | :global(.dark) .text { 17 | color: white; 18 | } 19 | -------------------------------------------------------------------------------- /css/Header.module.css: -------------------------------------------------------------------------------- 1 | .section { 2 | background-color: white; 3 | } 4 | 5 | :global(.dark) .section { 6 | background-color: rgb(17 24 39); 7 | } 8 | 9 | .navClass { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | padding: 1.25rem; 14 | } 15 | 16 | .linkLogo { 17 | display: inline-flex; 18 | padding-top: 0.375rem; 19 | padding-bottom: 0.375rem; 20 | padding-left: 0.5rem; 21 | padding-right: 0.5rem; 22 | margin-right: 1.5rem; 23 | } 24 | 25 | .imageClass { 26 | display: block; 27 | width: 8rem; 28 | height: 1.75rem; 29 | object-fit: contain; 30 | object-position: left; 31 | } 32 | 33 | .containerMenuItems { 34 | display: none; 35 | align-items: center; 36 | } 37 | .containerMenuItems > :not([hidden]) ~ :not([hidden]) { 38 | margin-left: 0.5rem; 39 | } 40 | 41 | .containerButtons { 42 | display: none; 43 | margin-left: auto; 44 | } 45 | 46 | .buttonsWrapper { 47 | display: flex; 48 | flex-direction: row; 49 | align-items: center; 50 | justify-content: flex-end; 51 | } 52 | 53 | .buttonsWrapper > :not([hidden]) ~ :not([hidden]) { 54 | margin-left: 1.25rem; 55 | } 56 | 57 | .containerHamburgerMenu { 58 | position: relative; 59 | display: flex; 60 | height: 100%; 61 | align-items: center; 62 | } 63 | 64 | .buttonHamburgerMenu { 65 | display: flex; 66 | justify-content: center; 67 | align-items: center; 68 | padding: 0.25rem; 69 | width: 1.75rem; 70 | height: 1.75rem; 71 | border-radius: 5px; 72 | } 73 | 74 | .buttonHamburgerMenu:hover { 75 | background-color: rgb(14 165 233 / 0.2); 76 | color: rgb(2 132 199); 77 | } 78 | 79 | .buttonHamburgerMenu:focus { 80 | background-color: rgb(14 165 233 / 0.2); 81 | color: rgb(2 132 199); 82 | } 83 | 84 | :global(.dark) .buttonHamburgerMenu { 85 | background-color: rgb(17 24 39); 86 | } 87 | 88 | :global(.dark) .buttonHamburgerMenu:hover { 89 | background-color: rgb(14 165 233 / 0.4); 90 | color: white; 91 | } 92 | 93 | :global(.dark) .buttonHamburgerMenu:focus { 94 | background-color: rgb(14 165 233 / 0.4); 95 | color: white; 96 | } 97 | 98 | .containerHamburgerMenuItems { 99 | position: absolute; 100 | top: 2rem; 101 | right: 0px; 102 | width: 16rem; 103 | background-color: white; 104 | padding: 1.25rem; 105 | border-width: 1px; 106 | border-radius: 0.5rem; 107 | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 108 | z-index: 10; 109 | } 110 | 111 | .darkModeButton { 112 | display: flex; 113 | align-items: center; 114 | justify-content: center; 115 | width: 2rem; 116 | height: 2rem; 117 | margin-right: 1rem; 118 | margin-left: auto; 119 | cursor: pointer; 120 | background-color: transparent; 121 | color: rgb(156 163 175); 122 | } 123 | 124 | :global(.dark) .darkModeButton { 125 | color: rgb(229 231 235); 126 | } 127 | 128 | .hamburgerMenuFiX { 129 | color: rgb(75 85 99); 130 | } 131 | 132 | .hamburgerMenuFiMenu { 133 | color: rgb(75 85 99); 134 | } 135 | 136 | :global(.dark) .hamburgerMenuFiX { 137 | color: white; 138 | } 139 | 140 | :global(.dark) .hamburgerMenuFiMenu { 141 | color: white; 142 | } 143 | 144 | @media (min-width: 640px) { 145 | .navClass { 146 | margin-left: 5.55555%; 147 | margin-right: 5.55555%; 148 | } 149 | 150 | .containerHamburgerMenu { 151 | gap: 10px; 152 | } 153 | } 154 | 155 | @media (min-width: 1024px) { 156 | .darkModeButton{ 157 | margin-left: 2rem; 158 | } 159 | .containerMenuItems { 160 | display: flex; 161 | } 162 | .containerButtons { 163 | display: block; 164 | } 165 | .containerHamburgerMenu { 166 | display: none; 167 | } 168 | } 169 | 170 | @media (min-width: 1280px) { 171 | .navClass { 172 | margin-left: 11.1111%; 173 | margin-right: 11.1111%; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /css/HeaderMenuItem.module.css: -------------------------------------------------------------------------------- 1 | .linkMenuItem { 2 | display: none; 3 | justify-content: center; 4 | align-items: center; 5 | font-size: 0.875rem; 6 | line-height: 1.25rem; 7 | font-weight: 700; 8 | padding-top: 0.375rem; 9 | padding-bottom: 0.375rem; 10 | padding-left: 0.5rem; 11 | padding-right: 0.5rem; 12 | border-radius: 5px; 13 | transition-property: color, background-color, border-color, 14 | text-decoration-color, fill, stroke; 15 | transition-duration: 150ms; 16 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 17 | color: rgb(75 85 99); 18 | } 19 | 20 | .linkMenuItemActive { 21 | color: rgb(2 132 199); 22 | background-color: rgb(14 165 233 / 0.1); 23 | } 24 | 25 | :global(.dark) .linkMenuItemActive { 26 | color: white; 27 | background-color: rgb(14 165 233 / 0.3); 28 | } 29 | 30 | :global(.dark) .linkMenuItem { 31 | color: white; 32 | } 33 | 34 | .linkMenuItem:hover { 35 | background-color: rgb(14 165 233 / 0.2); 36 | color: rgb(2 132 199); 37 | } 38 | 39 | :global(.dark) .linkMenuItem:hover { 40 | color: white; 41 | background-color: rgb(14 165 233 / 0.4); 42 | } 43 | 44 | .linkHamburgerMenuItem { 45 | display: block; 46 | font-size: 0.875rem; 47 | line-height: 1.25rem; 48 | margin-bottom: 0.75rem; 49 | transition-property: color, background-color, border-color, 50 | text-decoration-color, fill, stroke; 51 | transition-duration: 150ms; 52 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 53 | color: rgb(31 41 55); 54 | } 55 | 56 | /*:global(.dark) .linkHamburgerMenuItem { 57 | color: white; 58 | }*/ 59 | 60 | .linkHamburgerMenuItem:hover { 61 | color: rgb(2 132 199); 62 | } 63 | 64 | /*:global(.dark) .linkHamburgerMenuItem:hover { 65 | color: rgb(14 165 233); 66 | }*/ 67 | 68 | .containerLinkItemWithSubItems { 69 | display: none; 70 | position: relative; 71 | } 72 | 73 | .buttonLinkItemWithSubItems { 74 | display: inline-flex; 75 | justify-content: center; 76 | align-items: center; 77 | font-size: 0.875rem; 78 | font-weight: 700; 79 | line-height: 1.25rem; 80 | 81 | color: rgb(75 85 99); 82 | padding-top: 0.375rem; 83 | padding-bottom: 0.375rem; 84 | padding-left: 0.5rem; 85 | padding-right: 0.5rem; 86 | border-radius: 5px; 87 | transition-property: color, background-color, border-color, 88 | text-decoration-color, fill, stroke; 89 | transition-duration: 150ms; 90 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 91 | background-color: transparent; 92 | } 93 | 94 | :global(.dark) .buttonLinkItemWithSubItems { 95 | color: white; 96 | } 97 | 98 | .buttonLinkItemWithSubItems:hover { 99 | background-color: rgb(14 165 233 / 0.2); 100 | color: rgb(2 132 199); 101 | } 102 | 103 | .buttonLinkItemWithSubItems:focus { 104 | background-color: rgb(14 165 233 / 0.2); 105 | color: rgb(2 132 199); 106 | } 107 | 108 | :global(.dark) .buttonLinkItemWithSubItems:hover { 109 | color: white; 110 | background-color: rgb(14 165 233 / 0.4); 111 | } 112 | :global(.dark) .buttonLinkItemWithSubItems:focus { 113 | color: white; 114 | background-color: rgb(14 165 233 / 0.4); 115 | } 116 | 117 | .buttonLinkItemWithSubItemsOpen { 118 | background-color: rgb(14 165 233 / 0.4); 119 | color: rgb(2 132 199); 120 | } 121 | 122 | :global(.dark) .buttonLinkItemWithSubItemsOpen { 123 | color: white; 124 | } 125 | 126 | .buttonTextActive { 127 | color: rgb(2 132 199); 128 | background-color: rgb(14 165 233 / 0.1); 129 | } 130 | 131 | :global(.dark) .buttonTextActive { 132 | color: rgb(56 189 248); 133 | } 134 | 135 | .svgClass { 136 | display: inline-block; 137 | width: 10px; 138 | height: 10px; 139 | margin-left: 5px; 140 | } 141 | 142 | .containerSubmenuItemsOpen { 143 | position: absolute; 144 | top: 2.25rem; 145 | z-index: 10; 146 | width: 16rem; 147 | background-color: white; 148 | padding: 0.75rem; 149 | border-width: 1px; 150 | border-radius: 0.5rem; 151 | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 152 | } 153 | 154 | /*:global(.dark) .containerSubmenuItemsOpen { 155 | background-color: rgb(17 24 39); 156 | border-color: rgb(156 163 175); 157 | }*/ 158 | 159 | .containerSubmenuItems { 160 | margin-bottom: 1.5rem; 161 | } 162 | 163 | .containerLinkText { 164 | font-size: 0.75rem; 165 | line-height: 1rem; 166 | font-weight: 800; 167 | text-transform: uppercase; 168 | color: rgb(107 114 128); 169 | letter-spacing: 0.35rem; 170 | margin-bottom: 1rem; 171 | } 172 | 173 | /*:global(.dark) .containerLinkText { 174 | color: white; 175 | }*/ 176 | 177 | @media (min-width: 1024px) { 178 | .linkMenuItem { 179 | display: inline-flex; 180 | } 181 | .linkHamburgerMenuItem { 182 | display: none; 183 | } 184 | .containerLinkItemWithSubItems { 185 | display: block; 186 | } 187 | .containerSubmenuItems { 188 | display: none; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /css/HeaderMenuSubItem.module.css: -------------------------------------------------------------------------------- 1 | .linkContainer { 2 | padding: 0; 3 | margin-bottom: 0.75rem; 4 | display: flex; 5 | align-items: flex-start; 6 | } 7 | 8 | .fiContainer { 9 | color: rgb(14 165 233); 10 | margin-right: 0.5rem; 11 | } 12 | 13 | .textContainer { 14 | flex: 1 1 0%; 15 | overflow: hidden; 16 | } 17 | 18 | .linkContainer:hover .linkText { 19 | color: rgb(2 132 199); 20 | } 21 | 22 | /*:global(.dark) .linkContainer:hover .linkText { 23 | color: rgb(14 165 233); 24 | }*/ 25 | 26 | .linkText { 27 | color: rgb(17 24 39); 28 | font-size: 0.875rem; 29 | line-height: 1.25rem; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | white-space: nowrap; 33 | transition-property: color, background-color, border-color, 34 | text-decoration-color, fill, stroke; 35 | transition-duration: 150ms; 36 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 37 | } 38 | 39 | /*:global(.dark) .linkText { 40 | color: white; 41 | }*/ 42 | 43 | .descriptionContainer { 44 | display: none; 45 | } 46 | 47 | .linkDescription { 48 | color: rgb(75 85 99); 49 | font-size: 0.875rem; 50 | line-height: 1.25rem; 51 | transition-property: color, background-color, border-color, 52 | text-decoration-color, fill, stroke; 53 | transition-duration: 150ms; 54 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); 55 | } 56 | 57 | /*:global(.dark) .linkDescription { 58 | color: white; 59 | }*/ 60 | 61 | @media (min-width: 1024px) { 62 | .linkContainer { 63 | padding: 0.75rem; 64 | } 65 | .fiContainer { 66 | display: none; 67 | } 68 | .textContainer { 69 | overflow: auto; 70 | } 71 | .linkText { 72 | overflow: auto; 73 | white-space: normal; 74 | font-weight: 700; 75 | } 76 | .descriptionContainer { 77 | display: block; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /css/HeroUnit.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: white; 3 | } 4 | 5 | :global(.dark) .container { 6 | background-color: rgb(17 24 39); 7 | } 8 | 9 | .padding { 10 | max-width: 36rem; 11 | margin-left: auto; 12 | margin-right: auto; 13 | padding-left: 1.5rem; 14 | padding-right: 1.5rem; 15 | } 16 | 17 | .bigPadding { 18 | padding-top: 5rem; 19 | padding-bottom: 5rem; 20 | } 21 | 22 | .smallPadding { 23 | padding-top: 3rem; 24 | padding-bottom: 3rem; 25 | } 26 | 27 | .heroImage { 28 | width: 5rem; 29 | margin-bottom: 1.25rem; 30 | margin-left: auto; 31 | margin-right: auto; 32 | } 33 | 34 | .title { 35 | font-size: 1.875rem; 36 | line-height: 2.25rem; 37 | color: rgb(17 24 39); 38 | text-align: center; 39 | font-weight: 900; 40 | line-height: 1.25; 41 | margin-bottom: 0.75rem; 42 | } 43 | 44 | :global(.dark) .title { 45 | color: rgb(255 255 255); 46 | } 47 | 48 | .placeholderSpan { 49 | opacity: 0.3; 50 | } 51 | 52 | .richText { 53 | font-size: 1.25rem; 54 | line-height: 1.75rem; 55 | text-align: center; 56 | line-height: 1.625; 57 | color: rgb(55 65 81); 58 | } 59 | 60 | :global(.dark) .richText { 61 | color: rgb(243 244 246); 62 | } 63 | 64 | .code { 65 | font-size: 0.875rem; 66 | line-height: 1.25rem; 67 | padding-top: 0.25rem; 68 | padding-bottom: 0.25rem; 69 | padding-left: 0.5rem; 70 | padding-right: 0.5rem; 71 | background-color: rgb(229 231 235); 72 | border-radius: 0.25rem; 73 | } 74 | 75 | :global(.dark) .code { 76 | background-color: rgb(55 65 81); 77 | } 78 | 79 | .richTextLink { 80 | color: rgb(14 165 233); 81 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; 82 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 83 | transition-duration: 150ms; 84 | } 85 | 86 | .richTextLink:hover { 87 | color: rgb(2 132 199); 88 | } 89 | 90 | @media (min-width: 640px) { 91 | .title { 92 | font-size: 2.25rem; 93 | line-height: 2.5rem; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /css/Pokemon.module.css: -------------------------------------------------------------------------------- 1 | .containerNotFound{ 2 | text-align: center; 3 | color: rgb(239 68 68); 4 | text-decoration-line: underline; 5 | font-size: 1.25rem; /* 20px */ 6 | line-height: 1.75rem; /* 28px */ 7 | } 8 | 9 | .container { 10 | max-width: 48rem; /* 768px */ 11 | margin-left: auto; 12 | margin-right: auto; 13 | } 14 | 15 | .image{ 16 | margin-left: auto; 17 | margin-right: auto; 18 | width: 9rem; /* 144px */ 19 | margin-bottom: 1rem; /* 16px */ 20 | } 21 | 22 | .title{ 23 | font-size: 3rem; /* 48px */ 24 | line-height: 1; 25 | font-weight: 800; 26 | text-align: center; 27 | margin-bottom: 1.5rem; /* 24px */ 28 | } -------------------------------------------------------------------------------- /css/errorNoFooter.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 64rem; 3 | margin-left: auto; 4 | margin-right: auto; 5 | padding-left: 1.5rem; 6 | padding-right: 1.5rem; 7 | padding-top: 5rem; 8 | padding-bottom: 5rem; 9 | color: rgb(75 85 99); 10 | } 11 | 12 | .title { 13 | font-size: 1.5rem; 14 | line-height: 2rem; 15 | 16 | color: rgb(248 113 113); 17 | font-weight: 600; 18 | margin-bottom: 2rem; 19 | } 20 | 21 | .paragraph { 22 | margin-bottom: 1.5rem; 23 | } 24 | -------------------------------------------------------------------------------- /css/errorNoHeader.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 64rem; 3 | margin-left: auto; 4 | margin-right: auto; 5 | padding-left: 1.5rem; 6 | padding-right: 1.5rem; 7 | padding-top: 5rem; 8 | padding-bottom: 5rem; 9 | 10 | color: rgb(75 85 99); 11 | } 12 | 13 | .title { 14 | font-size: 1.5rem; 15 | line-height: 2rem; 16 | color: rgb(248 113 113); 17 | font-weight: 600; 18 | margin-bottom: 2rem; 19 | } 20 | 21 | .paragraph { 22 | margin-bottom: 1.5rem; 23 | } 24 | -------------------------------------------------------------------------------- /css/errorNoKeys.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 64rem; 3 | margin-left: auto; 4 | margin-right: auto; 5 | padding-left: 1.5rem; 6 | padding-right: 1.5rem; 7 | padding-top: 5rem; 8 | padding-bottom: 5rem; 9 | color: rgb(75 85 99); 10 | } 11 | 12 | .title { 13 | font-size: 1.5rem; 14 | line-height: 2rem; 15 | color: rgb(220 38 38); 16 | font-weight: 600; 17 | margin-bottom: 2rem; 18 | } 19 | 20 | .marginFromTitle { 21 | margin-bottom: 1.5rem; 22 | } 23 | 24 | .codeError { 25 | font-size: 0.875rem; 26 | line-height: 1.25rem; 27 | color: rgb(0 0 0); 28 | background-color: rgb(243 244 246); 29 | padding-left: 0.25rem; 30 | padding-right: 0.25rem; 31 | padding-top: 0.25rem; 32 | padding-bottom: 0.25rem; 33 | border-radius: 0.25rem; 34 | } 35 | 36 | .marginFromCode { 37 | margin-bottom: 0.5rem; 38 | } 39 | -------------------------------------------------------------------------------- /css/layout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | min-height: 100vh; 5 | justify-content: space-between; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | .childrenContainer { 11 | margin-bottom: auto; 12 | } 13 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | *, 2 | ::before, 3 | ::after { 4 | box-sizing: border-box; 5 | /* 1 */ 6 | border-width: 0; 7 | /* 2 */ 8 | border-style: solid; 9 | /* 2 */ 10 | border-color: #e5e7eb; 11 | /* 2 */ 12 | } 13 | 14 | html { 15 | line-height: 1.5; 16 | /* 1 */ 17 | -webkit-text-size-adjust: 100%; 18 | /* 2 */ 19 | -moz-tab-size: 4; 20 | /* 3 */ 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | /* 3 */ 24 | } 25 | 26 | body { 27 | margin: 0; 28 | line-height: inherit; 29 | } 30 | 31 | hr { 32 | height: 0; 33 | /* 1 */ 34 | color: inherit; 35 | /* 2 */ 36 | border-top-width: 1px; 37 | /* 3 */ 38 | } 39 | 40 | abbr:where([title]) { 41 | -webkit-text-decoration: underline dotted; 42 | text-decoration: underline dotted; 43 | } 44 | 45 | h1, 46 | h2, 47 | h3, 48 | h4, 49 | h5, 50 | h6 { 51 | font-size: inherit; 52 | font-weight: inherit; 53 | } 54 | 55 | a { 56 | color: inherit; 57 | text-decoration: inherit; 58 | } 59 | 60 | b, 61 | strong { 62 | font-weight: bolder; 63 | } 64 | 65 | code, 66 | kbd, 67 | samp, 68 | pre { 69 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 70 | 'Courier New', monospace; 71 | /* 1 */ 72 | font-size: 1em; 73 | /* 2 */ 74 | } 75 | 76 | small { 77 | font-size: 80%; 78 | } 79 | 80 | sub, 81 | sup { 82 | font-size: 75%; 83 | line-height: 0; 84 | position: relative; 85 | vertical-align: baseline; 86 | } 87 | 88 | sub { 89 | bottom: -0.25em; 90 | } 91 | 92 | sup { 93 | top: -0.5em; 94 | } 95 | 96 | table { 97 | text-indent: 0; 98 | /* 1 */ 99 | border-color: inherit; 100 | /* 2 */ 101 | border-collapse: collapse; 102 | /* 3 */ 103 | } 104 | 105 | button, 106 | input, 107 | optgroup, 108 | select, 109 | textarea { 110 | font-family: inherit; 111 | /* 1 */ 112 | font-size: 100%; 113 | /* 1 */ 114 | line-height: inherit; 115 | /* 1 */ 116 | color: inherit; 117 | /* 1 */ 118 | margin: 0; 119 | /* 2 */ 120 | padding: 0; 121 | /* 3 */ 122 | } 123 | 124 | button, 125 | select { 126 | text-transform: none; 127 | } 128 | 129 | button, 130 | [type='button'], 131 | [type='reset'], 132 | [type='submit'] { 133 | background-image: none; 134 | } 135 | 136 | :-moz-focusring { 137 | outline: auto; 138 | } 139 | 140 | :-moz-ui-invalid { 141 | box-shadow: none; 142 | } 143 | 144 | progress { 145 | vertical-align: baseline; 146 | } 147 | 148 | ::-webkit-inner-spin-button, 149 | ::-webkit-outer-spin-button { 150 | height: auto; 151 | } 152 | 153 | [type='search'] { 154 | /* 1 */ 155 | outline-offset: -2px; 156 | /* 2 */ 157 | } 158 | 159 | ::-webkit-search-decoration { 160 | -webkit-appearance: none; 161 | } 162 | 163 | ::-webkit-file-upload-button { 164 | -webkit-appearance: button; 165 | /* 1 */ 166 | font: inherit; 167 | /* 2 */ 168 | } 169 | 170 | summary { 171 | display: list-item; 172 | } 173 | 174 | blockquote, 175 | dl, 176 | dd, 177 | h1, 178 | h2, 179 | h3, 180 | h4, 181 | h5, 182 | h6, 183 | hr, 184 | figure, 185 | p, 186 | pre { 187 | margin: 0; 188 | } 189 | 190 | fieldset { 191 | margin: 0; 192 | padding: 0; 193 | } 194 | 195 | legend { 196 | padding: 0; 197 | } 198 | 199 | ol, 200 | ul, 201 | menu { 202 | list-style: none; 203 | margin: 0; 204 | padding: 0; 205 | } 206 | 207 | textarea { 208 | resize: vertical; 209 | } 210 | 211 | input::-moz-placeholder, 212 | textarea::-moz-placeholder { 213 | opacity: 1; 214 | /* 1 */ 215 | color: #9ca3af; 216 | /* 2 */ 217 | } 218 | 219 | input:-ms-input-placeholder, 220 | textarea:-ms-input-placeholder { 221 | opacity: 1; 222 | /* 1 */ 223 | color: #9ca3af; 224 | /* 2 */ 225 | } 226 | 227 | input::placeholder, 228 | textarea::placeholder { 229 | opacity: 1; 230 | /* 1 */ 231 | color: #9ca3af; 232 | /* 2 */ 233 | } 234 | 235 | button, 236 | [role='button'] { 237 | cursor: pointer; 238 | } 239 | 240 | :disabled { 241 | cursor: default; 242 | } 243 | 244 | img, 245 | svg, 246 | video, 247 | canvas, 248 | audio, 249 | iframe, 250 | embed, 251 | object { 252 | display: block; 253 | } 254 | 255 | /* 256 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 257 | */ 258 | 259 | img, 260 | video { 261 | max-width: 100%; 262 | height: auto; 263 | } 264 | 265 | /* 266 | Ensure the default browser behavior of the `hidden` attribute. 267 | */ 268 | 269 | [hidden] { 270 | display: none; 271 | } 272 | 273 | .whiteContentClass { 274 | -webkit-font-smoothing: antialiased; 275 | -moz-osx-font-smoothing: grayscale; 276 | background-color: white; 277 | } 278 | 279 | .darkContentClass { 280 | background-color: rgb(17 24 39); 281 | } 282 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | i18n: { 3 | locales: ['en'], // Add your languages here 4 | defaultLocale: 'en', 5 | localeDetection: false, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-starter-reactbricks", 3 | "private": true, 4 | "license": "MIT", 5 | "version": "1.3.6", 6 | "author": "Matteo Frana ", 7 | "scripts": { 8 | "dev": "next dev", 9 | "build": "next build", 10 | "start": "next start" 11 | }, 12 | "dependencies": { 13 | "classnames": "^2.5.1", 14 | "next": "15.2.2", 15 | "next-themes": "^0.4.6", 16 | "normalize.css": "^8.0.1", 17 | "react": "^19.0.0", 18 | "react-bricks": "^4.7.1", 19 | "react-dom": "^19.0.0", 20 | "react-icons": "^5.5.0" 21 | }, 22 | "devDependencies": { 23 | "@types/jsonp": "^0.2.3", 24 | "@types/node": "^22.0.0", 25 | "@types/prismjs": "^1.26.5", 26 | "@types/react": "^19.0.10", 27 | "@types/react-slick": "^0.23.13", 28 | "typescript": "^5.5.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pages/[[...slug]].tsx: -------------------------------------------------------------------------------- 1 | import { GetStaticPaths, GetStaticProps } from "next"; 2 | import Head from "next/head"; 3 | import { ReactNode } from "react"; 4 | import { 5 | PageViewer, 6 | cleanPage, 7 | fetchPage, 8 | fetchPages, 9 | renderJsonLd, 10 | renderMeta, 11 | types, 12 | useReactBricksContext, 13 | } from "react-bricks/frontend"; 14 | import ErrorNoFooter from "../components/errorNoFooter"; 15 | import ErrorNoHeader from "../components/errorNoHeader"; 16 | import ErrorNoKeys from "../components/errorNoKeys"; 17 | import Layout from "../components/layout"; 18 | import config from "../react-bricks/config"; 19 | 20 | interface PageProps { 21 | page: types.Page; 22 | header: types.Page; 23 | footer: types.Page; 24 | errorNoKeys: string; 25 | errorPage: boolean; 26 | errorHeader: boolean; 27 | errorFooter: boolean; 28 | } 29 | 30 | const Page: React.FC = ({ 31 | page, 32 | header, 33 | footer, 34 | errorNoKeys, 35 | errorPage, 36 | errorHeader, 37 | errorFooter, 38 | }) => { 39 | // Clean the received content 40 | // Removes unknown or not allowed bricks 41 | const { pageTypes, bricks } = useReactBricksContext(); 42 | const pageOk = page ? cleanPage(page, pageTypes, bricks) : null; 43 | const headerOk = header ? cleanPage(header, pageTypes, bricks) : null; 44 | const footerOk = footer ? cleanPage(footer, pageTypes, bricks) : null; 45 | 46 | return ( 47 | 48 | {pageOk && !errorPage && !errorNoKeys && ( 49 | <> 50 | 51 | {renderMeta(pageOk) as ReactNode} 52 | {renderJsonLd(pageOk) as ReactNode} 53 | 54 | {headerOk && !errorHeader ? ( 55 | 56 | ) : ( 57 | 58 | )} 59 | 60 | {footerOk && !errorFooter ? ( 61 | 62 | ) : ( 63 | 64 | )} 65 | 66 | )} 67 | {errorNoKeys && } 68 | 69 | ); 70 | }; 71 | 72 | export const getStaticProps: GetStaticProps = async (context) => { 73 | let errorNoKeys: boolean = false; 74 | 75 | if (!config.apiKey) { 76 | errorNoKeys = true; 77 | return { props: { errorNoKeys } }; 78 | } 79 | 80 | const { slug } = context.params; 81 | 82 | let errorPage: boolean = false; 83 | let errorHeader: boolean = false; 84 | let errorFooter: boolean = false; 85 | 86 | let cleanSlug = ""; 87 | 88 | if (!slug) { 89 | cleanSlug = "/"; 90 | } else if (typeof slug === "string") { 91 | cleanSlug = slug; 92 | } else { 93 | cleanSlug = slug.join("/"); 94 | } 95 | 96 | const [page, header, footer] = await Promise.all([ 97 | fetchPage({ slug: cleanSlug, language: context.locale, config }).catch( 98 | () => { 99 | errorPage = true; 100 | return {}; 101 | } 102 | ), 103 | fetchPage({ slug: "header", language: context.locale, config }).catch( 104 | () => { 105 | errorHeader = true; 106 | return {}; 107 | } 108 | ), 109 | fetchPage({ slug: "footer", language: context.locale, config }).catch( 110 | () => { 111 | errorFooter = true; 112 | return {}; 113 | } 114 | ), 115 | ]); 116 | 117 | return { 118 | props: { 119 | page, 120 | header, 121 | footer, 122 | errorNoKeys, 123 | errorPage, 124 | errorHeader, 125 | errorFooter, 126 | }, 127 | }; 128 | }; 129 | 130 | export const getStaticPaths: GetStaticPaths = async (context) => { 131 | if (!config.apiKey) { 132 | return { paths: [], fallback: true }; 133 | } 134 | const allPages = await fetchPages(config.apiKey); 135 | 136 | const paths = allPages 137 | .map((page) => 138 | page.translations 139 | .filter( 140 | (translation) => context.locales.indexOf(translation.language) > -1 141 | ) 142 | .map((translation) => ({ 143 | params: { 144 | slug: translation.slug === "/" ? [""] : translation.slug.split("/"), 145 | }, 146 | locale: translation.language, 147 | })) 148 | ) 149 | .flat(); 150 | 151 | return { paths, fallback: false }; 152 | }; 153 | 154 | export default Page; 155 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from "next/app" 2 | import { ThemeProvider } from "next-themes" 3 | import ReactBricksApp from "../components/ReactBricksApp" 4 | 5 | import "../css/style.css" 6 | 7 | const MyApp = (props: AppProps) => { 8 | return ( 9 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default MyApp 17 | -------------------------------------------------------------------------------- /pages/admin/app-settings.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Admin, AppSettings } from 'react-bricks' 4 | 5 | const AdminAppSettings: React.FC = () => { 6 | return ( 7 | 8 | 9 | App Settings 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminAppSettings 17 | -------------------------------------------------------------------------------- /pages/admin/editor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Admin, Editor } from 'react-bricks' 4 | 5 | const AdminEditor: React.FC = () => { 6 | return ( 7 | 8 | 9 | Editor 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminEditor 17 | -------------------------------------------------------------------------------- /pages/admin/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Admin, Login } from 'react-bricks' 4 | 5 | const AdminLogin: React.FC = () => { 6 | return ( 7 | 8 | 9 | Login 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminLogin 17 | -------------------------------------------------------------------------------- /pages/admin/media.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Admin, MediaLibrary } from 'react-bricks' 4 | 5 | const AdminMediaLibrary: React.FC = () => { 6 | return ( 7 | 8 | 9 | Media Library 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminMediaLibrary 17 | -------------------------------------------------------------------------------- /pages/admin/playground.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Admin, Playground } from 'react-bricks' 4 | 5 | const AdminPlayground: React.FC = () => { 6 | return ( 7 | 8 | 9 | Playground 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminPlayground 17 | -------------------------------------------------------------------------------- /pages/admin/sso-failure.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Admin, SsoLoginFailure } from 'react-bricks' 3 | 4 | const AdminSsoFailure: React.FC = () => { 5 | useEffect(() => { 6 | document.title = 'SSO Login Failure' 7 | }, []) 8 | 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminSsoFailure 17 | -------------------------------------------------------------------------------- /pages/admin/sso-login.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Admin, SsoLogin } from 'react-bricks' 3 | 4 | const AdminSSOLogin: React.FC = () => { 5 | useEffect(() => { 6 | document.title = 'SSO Login' 7 | }, []) 8 | 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminSSOLogin 17 | -------------------------------------------------------------------------------- /pages/admin/sso-success.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Admin, SsoLoginSuccess } from 'react-bricks' 3 | 4 | const AdminSsoSuccess: React.FC = () => { 5 | useEffect(() => { 6 | document.title = 'SSO Login Success' 7 | }, []) 8 | 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default AdminSsoSuccess 17 | -------------------------------------------------------------------------------- /pages/preview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { Preview } from 'react-bricks/frontend' 4 | 5 | const PagePreview: React.FC = () => { 6 | return ( 7 | <> 8 | 9 | Preview 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default PagePreview 17 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactBricks/nextjs-starter-reactbricks/816bd62738f6b0898ff4c68c37a55db5aff45ff6/public/favicon.ico -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | screen_endorsement_Risorsa 1 -------------------------------------------------------------------------------- /public/react-bricks-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 64 | 67 | 72 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /public/react-bricks-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-bricks/NextLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import { useRouter } from 'next/router' 3 | import { types } from 'react-bricks/frontend' 4 | 5 | const NextLink: types.RenderLocalLink = ({ 6 | href, 7 | target, 8 | rel, 9 | className, 10 | activeClassName, 11 | children, 12 | }) => { 13 | const router = useRouter() 14 | 15 | let anchorClassName = '' 16 | 17 | if (router.asPath === href) { 18 | anchorClassName = `${className} ${activeClassName}` 19 | } else { 20 | anchorClassName = className 21 | } 22 | 23 | return ( 24 | 25 | {children} 26 | 27 | ) 28 | } 29 | 30 | export default NextLink 31 | -------------------------------------------------------------------------------- /react-bricks/bricks/HeroUnit.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, RichText, Image, types } from 'react-bricks/frontend' 3 | import styles from '../../css/HeroUnit.module.css' 4 | 5 | //============================= 6 | // Local Types 7 | //============================= 8 | type Padding = 'big' | 'small' 9 | 10 | interface HeroUnitProps { 11 | padding: Padding 12 | title: string 13 | text: string 14 | } 15 | 16 | //============================= 17 | // Component to be rendered 18 | //============================= 19 | const MyHeroUnit: types.Brick = ({ padding }) => { 20 | return ( 21 |
22 |
27 |
28 | Icon 35 | ( 37 |

{props.children}

38 | )} 39 | placeholder="Type a title..." 40 | propName="title" 41 | /> 42 | ( 44 |

{props.children}

45 | )} 46 | placeholder="Type a text..." 47 | propName="text" 48 | allowedFeatures={[ 49 | types.RichTextFeatures.Bold, 50 | types.RichTextFeatures.Italic, 51 | types.RichTextFeatures.Highlight, 52 | types.RichTextFeatures.Code, 53 | types.RichTextFeatures.Link, 54 | ]} 55 | renderCode={(props) => ( 56 | {props.children} 57 | )} 58 | renderLink={(props) => ( 59 | 60 | {props.children} 61 | 62 | )} 63 | /> 64 |
65 |
66 |
67 | ) 68 | } 69 | 70 | //============================= 71 | // Brick Schema 72 | //============================= 73 | MyHeroUnit.schema = { 74 | name: 'my-hero-unit', 75 | label: 'Custom Hero Unit', 76 | getDefaultProps: () => ({ 77 | padding: 'big', 78 | title: 'This is a custom Hero Unit', 79 | text: "We are a hi-tech web development company committed to deliver great products on time. We love to understand our customers' needs and exceed expectations.", 80 | }), 81 | sideEditProps: [ 82 | { 83 | name: 'padding', 84 | label: 'Padding', 85 | type: types.SideEditPropType.Select, 86 | selectOptions: { 87 | display: types.OptionsDisplay.Select, 88 | options: [ 89 | { value: 'big', label: 'Big Padding' }, 90 | { value: 'small', label: 'Small Padding' }, 91 | ], 92 | }, 93 | }, 94 | ], 95 | } 96 | 97 | export default MyHeroUnit 98 | -------------------------------------------------------------------------------- /react-bricks/bricks/features/FeatureItem.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Image, types, Text, Link, Plain } from 'react-bricks/frontend' 3 | import classNames from 'classnames' 4 | import styles from '../../../css/FeatureItem.module.css' 5 | import { ColsNumber } from './Features' 6 | import { icons } from './defaultImages' 7 | 8 | export interface FeatureItemProps { 9 | colsNumber: ColsNumber 10 | withIcon: boolean 11 | withLink: boolean 12 | linkText: any 13 | linkPath: string 14 | } 15 | 16 | const getColumnClass = (colsNumber: ColsNumber) => { 17 | switch (colsNumber) { 18 | case '2': 19 | return styles.cols2 20 | case '3': 21 | return styles.cols3 22 | case '4': 23 | return styles.cols4 24 | } 25 | } 26 | 27 | const FeatureItem: types.Brick = ({ 28 | colsNumber, 29 | withIcon, 30 | withLink, 31 | linkText, 32 | linkPath, 33 | }) => { 34 | const linkTextPlain = 35 | typeof linkText === 'string' ? linkText : Plain.serialize(linkText) 36 | 37 | return ( 38 |
44 | {withIcon && ( 45 | feature { 51 | return
{children}
52 | }} 53 | /> 54 | )} 55 | 56 |
57 | ( 61 |
{props.children}
62 | )} 63 | /> 64 | ( 68 |
{props.children}
69 | )} 70 | /> 71 | {withLink && ( 72 |
73 | 80 |
81 |

{props.children}

} 83 | placeholder="Link..." 84 | propName="linkText" 85 | /> 86 |
87 | 96 | 100 | 101 | 102 |
103 | )} 104 |
105 |
106 | ) 107 | } 108 | FeatureItem.schema = { 109 | name: 'feature-item', 110 | label: 'Feature', 111 | category: 'main content', 112 | hideFromAddMenu: true, 113 | playgroundLinkLabel: 'View source code on Github', 114 | playgroundLinkUrl: 115 | 'https://github.com/ReactBricks/react-bricks-ui/blob/master/src/website/Features/FeatureItem.tsx', 116 | 117 | getDefaultProps: () => ({ 118 | title: 'The best experience for editors', 119 | text: 'Your marketing team hates gray forms. Give them the easiest UX.', 120 | withIcon: true, 121 | withLink: false, 122 | image: icons.PHOTO_STACK, 123 | colsNumber: '2', 124 | linkText: '', 125 | linkPath: '', 126 | }), 127 | sideEditProps: [ 128 | { 129 | name: 'withIcon', 130 | label: 'With icon', 131 | type: types.SideEditPropType.Boolean, 132 | }, 133 | { 134 | name: 'withLink', 135 | label: 'With link', 136 | type: types.SideEditPropType.Boolean, 137 | }, 138 | { 139 | name: 'linkPath', 140 | label: 'Link to', 141 | type: types.SideEditPropType.Text, 142 | show: ({ withLink }) => !!withLink, 143 | }, 144 | ], 145 | } 146 | 147 | export default FeatureItem 148 | -------------------------------------------------------------------------------- /react-bricks/bricks/features/Features.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Repeater, types } from "react-bricks/frontend" 3 | 4 | import classNames from "classnames" 5 | 6 | import styles from "../../../css/Features.module.css" 7 | 8 | import { icons } from "./defaultImages" 9 | 10 | export type ColsNumber = "2" | "3" | "4" 11 | 12 | interface FeaturesProps { 13 | colsNumber: ColsNumber 14 | } 15 | 16 | const Features: types.Brick = ({ colsNumber }) => { 17 | return ( 18 |
19 |
25 | 26 |
27 |
28 | ) 29 | } 30 | Features.schema = { 31 | name: "features", 32 | label: "Features", 33 | category: "main content", 34 | tags: ["features"], 35 | playgroundLinkLabel: "View source code on Github", 36 | playgroundLinkUrl: 37 | "https://github.com/ReactBricks/react-bricks-ui/blob/master/src/website/Features/Features.tsx", 38 | 39 | getDefaultProps: () => ({ 40 | colsNumber: "2", 41 | features: [ 42 | { 43 | title: "The best experience for editors", 44 | text: "Your marketing team hates gray forms. Give them the easiest UX.", 45 | withIcon: true, 46 | withLink: false, 47 | image: icons.PHOTO_STACK, 48 | }, 49 | { 50 | title: "React components for devs", 51 | text: "Leverage React to create amazing visually editable content blocks.", 52 | withIcon: true, 53 | withLink: false, 54 | image: icons.MIND_MAP, 55 | }, 56 | { 57 | title: "Your design system", 58 | text: "Deploy your pixel-perfect design system and be sure nobody can break it..", 59 | withIcon: true, 60 | withLink: false, 61 | image: icons.RADAR_PLOT, 62 | }, 63 | { 64 | title: "Enterprise ready", 65 | text: "Collaboration, localization, granular permissions, SSO, top support: we got you covered.", 66 | withIcon: true, 67 | withLink: false, 68 | image: icons.DATABASE, 69 | }, 70 | ], 71 | }), 72 | repeaterItems: [ 73 | { 74 | name: "features", 75 | itemType: "feature-item", 76 | itemLabel: "Feature", 77 | min: 0, 78 | max: 9, 79 | }, 80 | ], 81 | sideEditProps: [ 82 | { 83 | groupName: "Columns", 84 | defaultOpen: true, 85 | props: [ 86 | { 87 | name: "colsNumber", 88 | label: "Columns number", 89 | type: types.SideEditPropType.Select, 90 | selectOptions: { 91 | display: types.OptionsDisplay.Radio, 92 | options: [ 93 | { value: "2", label: "2 columns" }, 94 | { value: "3", label: "3 columns" }, 95 | { value: "4", label: "4 columns" }, 96 | ], 97 | }, 98 | }, 99 | ], 100 | }, 101 | ], 102 | } 103 | export default Features 104 | -------------------------------------------------------------------------------- /react-bricks/bricks/features/defaultImages.ts: -------------------------------------------------------------------------------- 1 | import { types } from "react-bricks" 2 | 3 | type Images = Record 4 | 5 | export const customers = { 6 | WOOSMAP: { 7 | src: "https://images.reactbricks.com/original/93ed8ddd-a8cd-40dc-a4dd-d954ea568cad.svg", 8 | placeholderSrc: 9 | "https://images.reactbricks.com/original/93ed8ddd-a8cd-40dc-a4dd-d954ea568cad.svg", 10 | srcSet: "", 11 | alt: "Woosmap", 12 | seoName: "woosmap", 13 | width: 997.334, 14 | height: 198.205, 15 | }, 16 | CAPBASE: { 17 | src: "https://images.reactbricks.com/original/6278b20a-e04d-4e0e-b2dd-d8e27228c069.svg", 18 | placeholderSrc: 19 | "https://images.reactbricks.com/original/6278b20a-e04d-4e0e-b2dd-d8e27228c069.svg", 20 | srcSet: "", 21 | alt: "Capbase", 22 | seoName: "capbase", 23 | width: 1000, 24 | height: 300, 25 | }, 26 | CASAVO: { 27 | src: "https://images.reactbricks.com/original/b6895334-198a-43d9-aa53-f27b7ff75f53.svg", 28 | placeholderSrc: 29 | "https://images.reactbricks.com/original/b6895334-198a-43d9-aa53-f27b7ff75f53.svg", 30 | srcSet: "", 31 | alt: "Casavo", 32 | seoName: "casavo", 33 | width: 520.76, 34 | height: 135.83, 35 | }, 36 | EVERFUND: { 37 | src: "https://images.reactbricks.com/original/9124b82c-686e-4de5-bd14-291d2fce37b8.svg", 38 | placeholderSrc: 39 | "https://images.reactbricks.com/original/9124b82c-686e-4de5-bd14-291d2fce37b8.svg", 40 | srcSet: "", 41 | alt: "Everfund", 42 | seoName: "everfund", 43 | width: 2698.39, 44 | height: 585.2, 45 | }, 46 | NEOSKOP: { 47 | src: "https://images.reactbricks.com/original/e39a61c5-0a25-49bd-9f77-3d29fb43e5af.svg", 48 | placeholderSrc: 49 | "https://images.reactbricks.com/original/e39a61c5-0a25-49bd-9f77-3d29fb43e5af.svg", 50 | srcSet: "", 51 | alt: "Neoskop", 52 | seoName: "neoskop", 53 | width: 145, 54 | height: 40, 55 | }, 56 | } as const satisfies Images 57 | 58 | export const logos = { 59 | REACT: { 60 | src: "https://images.reactbricks.com/original/5a717763-afd5-4ec5-8a68-12a0d6e4fd08.svg", 61 | placeholderSrc: 62 | "https://images.reactbricks.com/original/5a717763-afd5-4ec5-8a68-12a0d6e4fd08.svg", 63 | srcSet: "", 64 | alt: "React", 65 | seoName: "react", 66 | width: 120, 67 | height: 60, 68 | }, 69 | VUE: { 70 | src: "https://images.reactbricks.com/original/272fad93-049f-4cd7-bdd9-4ed823ba2599.svg", 71 | placeholderSrc: 72 | "https://images.reactbricks.com/original/272fad93-049f-4cd7-bdd9-4ed823ba2599.svg", 73 | srcSet: "", 74 | alt: "Vue", 75 | seoName: "vue", 76 | width: 120, 77 | height: 60, 78 | }, 79 | SVELTE: { 80 | src: "https://images.reactbricks.com/original/44c7c7db-06f9-4e33-b017-1b32a397b96b.svg", 81 | placeholderSrc: 82 | "https://images.reactbricks.com/original/44c7c7db-06f9-4e33-b017-1b32a397b96b.svg", 83 | srcSet: "", 84 | alt: "Svelte", 85 | seoName: "svelte", 86 | width: 800, 87 | height: 800, 88 | }, 89 | SOLID: { 90 | src: "https://images.reactbricks.com/original/99a92b01-c9a6-482b-8ed6-aefb20687754.svg", 91 | placeholderSrc: 92 | "https://images.reactbricks.com/original/99a92b01-c9a6-482b-8ed6-aefb20687754.svg", 93 | srcSet: "", 94 | alt: "Solidjs", 95 | seoName: "solidjs", 96 | width: 382.23, 97 | height: 70.7, 98 | }, 99 | ASTRO: { 100 | src: "https://images.reactbricks.com/original/faba4d56-5733-432c-a38d-25d701ea7dcf.svg", 101 | placeholderSrc: 102 | "https://images.reactbricks.com/original/faba4d56-5733-432c-a38d-25d701ea7dcf.svg", 103 | srcSet: "", 104 | alt: "Astro", 105 | seoName: "astro-build", 106 | width: 2712, 107 | height: 894, 108 | }, 109 | REACT_BRICKS: { 110 | src: "https://images.reactbricks.com/original/7fd7ef1a-928f-45d6-b7a7-ff34bf91c15e.svg", 111 | placeholderSrc: 112 | "https://images.reactbricks.com/original/7fd7ef1a-928f-45d6-b7a7-ff34bf91c15e.svg", 113 | srcSet: "", 114 | alt: "React Bricks", 115 | seoName: "react-bricks", 116 | width: 1700.787, 117 | height: 377.953, 118 | }, 119 | } as const satisfies Images 120 | 121 | export const iconLogos = { 122 | REACT: { 123 | src: "https://images.reactbricks.com/original/6a840f50-48f3-45e4-9946-18586c039b2a.svg", 124 | placeholderSrc: 125 | "https://images.reactbricks.com/original/6a840f50-48f3-45e4-9946-18586c039b2a.svg", 126 | srcSet: "", 127 | alt: "React", 128 | seoName: "react", 129 | width: 23, 130 | height: 20.46348, 131 | }, 132 | VUE: { 133 | src: "https://images.reactbricks.com/original/ce61fb1d-bc1d-4619-91ad-f01e3981521f.svg", 134 | placeholderSrc: 135 | "https://images.reactbricks.com/original/ce61fb1d-bc1d-4619-91ad-f01e3981521f.svg", 136 | srcSet: "", 137 | alt: "Vue", 138 | seoName: "vue", 139 | width: 261.76, 140 | height: 226.69, 141 | }, 142 | SVELTE: { 143 | src: "https://images.reactbricks.com/original/d6598c2b-1093-4fbd-9a1d-5a89af2cde26.svg", 144 | placeholderSrc: 145 | "https://images.reactbricks.com/original/d6598c2b-1093-4fbd-9a1d-5a89af2cde26.svg", 146 | srcSet: "", 147 | alt: "Svelte", 148 | seoName: "svelte", 149 | width: 98.1, 150 | height: 118, 151 | }, 152 | SOLID: { 153 | src: "https://images.reactbricks.com/original/2f0e0ce5-a679-42de-9979-3e870540dd49.svg", 154 | placeholderSrc: 155 | "https://images.reactbricks.com/original/2f0e0ce5-a679-42de-9979-3e870540dd49.svg", 156 | srcSet: "", 157 | alt: "Solidjs", 158 | seoName: "solidjs", 159 | width: 166, 160 | height: 155.3, 161 | }, 162 | ASTRO: { 163 | src: "https://images.reactbricks.com/original/b51d2e41-02b9-4a5f-acab-68498c22b384.svg", 164 | placeholderSrc: 165 | "https://images.reactbricks.com/original/b51d2e41-02b9-4a5f-acab-68498c22b384.svg", 166 | srcSet: "", 167 | alt: "Astro", 168 | seoName: "astro-build", 169 | width: 1280, 170 | height: 1280, 171 | }, 172 | REACT_BRICKS: { 173 | src: "https://images.reactbricks.com/original/0502a7bd-0319-4300-b1df-89876e82c965.svg", 174 | placeholderSrc: 175 | "https://images.reactbricks.com/original/0502a7bd-0319-4300-b1df-89876e82c965.svg", 176 | srcSet: "", 177 | alt: "React Bricks", 178 | seoName: "react-bricks", 179 | width: 980, 180 | height: 979.97, 181 | }, 182 | } as const satisfies Images 183 | 184 | export const avatars = { 185 | MATTEO_FRANA: { 186 | src: "https://images.reactbricks.com/original/910d4267-6e46-4d9e-8790-53348ede99fb.svg", 187 | placeholderSrc: 188 | "https://images.reactbricks.com/original/910d4267-6e46-4d9e-8790-53348ede99fb.svg", 189 | srcSet: "", 190 | alt: "Matteo Frana", 191 | seoName: "matteo-frana", 192 | }, 193 | STEFAN_NAGEY: { 194 | src: "https://images.reactbricks.com/original/16068142-973d-49e7-b1a2-264c16bd5c34.webp", 195 | placeholderSrc: 196 | "https://images.reactbricks.com/placeholder/16068142-973d-49e7-b1a2-264c16bd5c34.jpg", 197 | srcSet: 198 | "https://images.reactbricks.com/src_set/16068142-973d-49e7-b1a2-264c16bd5c34-100.webp 100w", 199 | alt: "Stefan Nagey", 200 | seoName: "stefan-nagey", 201 | fallbackSrc: 202 | "https://images.reactbricks.com/original/16068142-973d-49e7-b1a2-264c16bd5c34.png", 203 | fallbackSrcSet: 204 | "https://images.reactbricks.com/src_set/16068142-973d-49e7-b1a2-264c16bd5c34-100.png 100w", 205 | fallbackType: "image/jpeg", 206 | width: 100, 207 | height: 100, 208 | }, 209 | LAURIE_VOSS: { 210 | src: "https://images.reactbricks.com/original/1f56e3f7-e1cf-4bfa-82d4-6a1a7415b954.webp", 211 | placeholderSrc: 212 | "https://images.reactbricks.com/placeholder/1f56e3f7-e1cf-4bfa-82d4-6a1a7415b954.jpg", 213 | srcSet: 214 | "https://images.reactbricks.com/src_set/1f56e3f7-e1cf-4bfa-82d4-6a1a7415b954-96.webp 96w", 215 | alt: "Laurie Voss", 216 | seoName: "laurie-voss", 217 | fallbackSrc: 218 | "https://images.reactbricks.com/original/1f56e3f7-e1cf-4bfa-82d4-6a1a7415b954.jpg", 219 | fallbackSrcSet: 220 | "https://images.reactbricks.com/src_set/1f56e3f7-e1cf-4bfa-82d4-6a1a7415b954-96.jpg 96w", 221 | fallbackType: "image/jpeg", 222 | width: 96, 223 | height: 96, 224 | }, 225 | MAIK_JABLONSKI: { 226 | src: "https://images.reactbricks.com/original/44e9d50a-95a1-4573-aafc-84a94496e319.webp", 227 | placeholderSrc: 228 | "https://images.reactbricks.com/placeholder/44e9d50a-95a1-4573-aafc-84a94496e319.jpg", 229 | srcSet: 230 | "https://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-450.webp 450w,\nhttps://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-400.webp 400w,\nhttps://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-200.webp 200w", 231 | alt: "Maik Jablonski", 232 | seoName: "maik-jablonski", 233 | fallbackSrc: 234 | "https://images.reactbricks.com/original/44e9d50a-95a1-4573-aafc-84a94496e319.jpg", 235 | fallbackSrcSet: 236 | "https://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-450.jpg 450w,\nhttps://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-400.jpg 400w,\nhttps://images.reactbricks.com/src_set/44e9d50a-95a1-4573-aafc-84a94496e319-200.jpg 200w", 237 | fallbackType: "image/jpeg", 238 | width: 450, 239 | height: 450, 240 | }, 241 | PLACEHOLDER1: { 242 | src: "https://images.reactbricks.com/original/cc2a047d-15f7-47d2-af8f-d4c3a394ca41.svg", 243 | placeholderSrc: 244 | "https://images.reactbricks.com/original/cc2a047d-15f7-47d2-af8f-d4c3a394ca41.svg", 245 | srcSet: "", 246 | alt: "Place Holder", 247 | seoName: "placeholder", 248 | width: 1249.24, 249 | height: 1249.24, 250 | }, 251 | AVATAR_MALE: { 252 | src: "https://images.reactbricks.com/original/2f84867e-3c4b-46b6-b64f-6c2f81b5232f.svg", 253 | placeholderSrc: 254 | "https://images.reactbricks.com/original/2f84867e-3c4b-46b6-b64f-6c2f81b5232f.svg", 255 | srcSet: "", 256 | alt: "Alvin", 257 | seoName: "alvin", 258 | width: 102.45, 259 | height: 102.45, 260 | }, 261 | AVATAR_FEMALE: { 262 | src: "https://images.reactbricks.com/original/24b26149-514a-40c4-9029-0dfaa22cbe3c.svg", 263 | placeholderSrc: 264 | "https://images.reactbricks.com/original/24b26149-514a-40c4-9029-0dfaa22cbe3c.svg", 265 | srcSet: "", 266 | alt: "Catherine", 267 | seoName: "catherine", 268 | width: 102.45, 269 | height: 102.45, 270 | }, 271 | } as const satisfies Images 272 | 273 | export const icons = { 274 | PHOTOS: { 275 | src: "https://images.reactbricks.com/original/21ee754a-8c3d-427a-bc44-8816c05299ae.svg", 276 | placeholderSrc: 277 | "https://images.reactbricks.com/original/21ee754a-8c3d-427a-bc44-8816c05299ae.svg", 278 | srcSet: "", 279 | alt: "Great for content creators and developers", 280 | seoName: "content-creators-ux-cms", 281 | width: 48, 282 | height: 48, 283 | }, 284 | TWITTER: { 285 | src: "https://images.reactbricks.com/original/3a2856d6-c209-4c90-9483-85d9959999e2.svg", 286 | placeholderSrc: 287 | "https://images.reactbricks.com/original/3a2856d6-c209-4c90-9483-85d9959999e2.svg", 288 | srcSet: "", 289 | width: 248, 290 | height: 204, 291 | alt: "Twitter icon", 292 | seoName: "twitter-icon", 293 | }, 294 | YOUTUBE: { 295 | src: "https://images.reactbricks.com/original/02fb7799-0da2-4537-a6c7-4822bc1410a2.svg", 296 | placeholderSrc: 297 | "https://images.reactbricks.com/original/02fb7799-0da2-4537-a6c7-4822bc1410a2.svg", 298 | srcSet: "", 299 | width: 159, 300 | height: 110, 301 | alt: "Youtube icon", 302 | seoName: "youtube-icon", 303 | }, 304 | PHOTO_STACK: { 305 | src: "https://images.reactbricks.com/original/aca3dbf3-ccb6-47cf-973e-059e85e55571.svg", 306 | placeholderSrc: 307 | "https://images.reactbricks.com/original/aca3dbf3-ccb6-47cf-973e-059e85e55571.svg", 308 | srcSet: "", 309 | width: 1, 310 | height: 1, 311 | alt: "Best UX for editors", 312 | seoName: "best-ux-editors", 313 | }, 314 | DATABASE: { 315 | src: "https://images.reactbricks.com/original/0037d5f4-d486-4cdf-a64c-dcbf0260ebb3.svg", 316 | placeholderSrc: 317 | "https://images.reactbricks.com/original/0037d5f4-d486-4cdf-a64c-dcbf0260ebb3.svg", 318 | srcSet: "", 319 | width: 1, 320 | height: 1, 321 | alt: "Enterprise-ready", 322 | seoName: "enterprise-ready", 323 | }, 324 | MIND_MAP: { 325 | src: "https://images.reactbricks.com/original/dd14c0fe-3f21-4fc1-8362-22e005e82897.svg", 326 | placeholderSrc: 327 | "https://images.reactbricks.com/original/dd14c0fe-3f21-4fc1-8362-22e005e82897.svg", 328 | srcSet: "", 329 | width: 1, 330 | height: 1, 331 | alt: "React components", 332 | seoName: "react-components", 333 | }, 334 | RADAR_PLOT: { 335 | src: "https://images.reactbricks.com/original/6f0a3910-b542-4791-a2ab-57474b9b2bb1.svg", 336 | placeholderSrc: 337 | "https://images.reactbricks.com/original/6f0a3910-b542-4791-a2ab-57474b9b2bb1.svg", 338 | srcSet: "", 339 | width: 1, 340 | height: 1, 341 | alt: "Design system", 342 | seoName: "design-system", 343 | }, 344 | VISUAL_EDITING: { 345 | src: "https://images.reactbricks.com/original/50313730-79c9-4d6a-b7e0-d8aeb2a936e2.svg", 346 | placeholderSrc: 347 | "https://images.reactbricks.com/original/50313730-79c9-4d6a-b7e0-d8aeb2a936e2.svg", 348 | srcSet: "", 349 | width: 48, 350 | height: 48, 351 | alt: "Best UX for editors", 352 | seoName: "best-ux-editors", 353 | }, 354 | COMPONENTS: { 355 | src: "https://images.reactbricks.com/original/cca08a7b-c3ad-4928-a69b-84b5d9e06ef4.svg", 356 | placeholderSrc: 357 | "https://images.reactbricks.com/original/cca08a7b-c3ad-4928-a69b-84b5d9e06ef4.svg", 358 | srcSet: "", 359 | width: 48, 360 | height: 48, 361 | alt: "React components", 362 | seoName: "react-components", 363 | }, 364 | MULTILANGUAGE: { 365 | src: "https://images.reactbricks.com/original/643f6d1e-2c4f-40bd-8478-82b43694054b.svg", 366 | placeholderSrc: 367 | "https://images.reactbricks.com/original/643f6d1e-2c4f-40bd-8478-82b43694054b.svg", 368 | srcSet: "", 369 | width: 48, 370 | height: 48, 371 | alt: "", 372 | seoName: "", 373 | }, 374 | SCHEDULED_PUBLISHING: { 375 | src: "https://images.reactbricks.com/original/3eaa0b6b-bcf0-4430-b099-3c4f872a6d91.svg", 376 | placeholderSrc: 377 | "https://images.reactbricks.com/original/3eaa0b6b-bcf0-4430-b099-3c4f872a6d91.svg", 378 | srcSet: "", 379 | width: 48, 380 | height: 48, 381 | alt: "Enterprise-ready", 382 | seoName: "enterprise-ready", 383 | }, 384 | } as const satisfies Images 385 | 386 | export const photos = { 387 | DESK_MAC: { 388 | src: "https://images.reactbricks.com/original/91a94bb2-7916-4254-9c60-af8d69701dfc.webp", 389 | placeholderSrc: 390 | "https://images.reactbricks.com/placeholder/91a94bb2-7916-4254-9c60-af8d69701dfc.jpg", 391 | srcSet: 392 | "https://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-800.webp 800w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-400.webp 400w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-200.webp 200w", 393 | alt: "person writing on white paper", 394 | seoName: "dashboard", 395 | fallbackSrc: 396 | "https://images.reactbricks.com/original/91a94bb2-7916-4254-9c60-af8d69701dfc.jpg", 397 | fallbackSrcSet: 398 | "https://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-800.jpg 800w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-400.jpg 400w,\nhttps://images.reactbricks.com/src_set/91a94bb2-7916-4254-9c60-af8d69701dfc-200.jpg 200w", 399 | fallbackType: "image/jpeg", 400 | width: 1080, 401 | height: 717, 402 | }, 403 | IMAGE_TEXT_STORY_HERO: { 404 | src: "https://images.reactbricks.com/original/1a8b35b7-4793-4c72-836b-86a491718494.webp", 405 | placeholderSrc: 406 | "https://images.reactbricks.com/placeholder/1a8b35b7-4793-4c72-836b-86a491718494.jpg", 407 | srcSet: 408 | "https://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-800.webp 800w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-400.webp 400w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-200.webp 200w", 409 | alt: "macbook pro displaying computer icons", 410 | seoName: "dashboard", 411 | fallbackSrc: 412 | "https://images.reactbricks.com/original/1a8b35b7-4793-4c72-836b-86a491718494macbook pro displaying computer icons", 413 | fallbackSrcSet: 414 | "https://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-1080macbook pro displaying computer icons 1080w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-800macbook pro displaying computer icons 800w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-400macbook pro displaying computer icons 400w,\nhttps://images.reactbricks.com/src_set/1a8b35b7-4793-4c72-836b-86a491718494-200macbook pro displaying computer icons 200w", 415 | fallbackType: "image/jpeg", 416 | width: 1080, 417 | height: 608, 418 | }, 419 | SEASIDE: { 420 | fallbackSrc: 421 | "https://images.reactbricks.com/original/71fd29e5-d54d-4c99-a2da-681bd8d888d1.jpg", 422 | fallbackSrcSet: 423 | "https://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-300.jpg 300w", 424 | fallbackType: "image/jpeg", 425 | src: "https://images.reactbricks.com/original/71fd29e5-d54d-4c99-a2da-681bd8d888d1.webp", 426 | placeholderSrc: 427 | "https://images.reactbricks.com/placeholder/71fd29e5-d54d-4c99-a2da-681bd8d888d1.jpg", 428 | srcSet: 429 | "https://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-600.webp 600w,\nhttps://images.reactbricks.com/src_set/71fd29e5-d54d-4c99-a2da-681bd8d888d1-300.webp 300w", 430 | width: 1080, 431 | height: 606, 432 | alt: "aerial photography of islands during daytime", 433 | seoName: "seaside", 434 | }, 435 | CAROUSEL_MOUNTAINS_1: { 436 | src: "https://images.reactbricks.com/original/b83f614b-8dc9-4a20-b39f-a7e90374d4cc.webp", 437 | placeholderSrc: 438 | "https://images.reactbricks.com/placeholder/b83f614b-8dc9-4a20-b39f-a7e90374d4cc.jpg", 439 | srcSet: 440 | "https://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-600.webp 600w,\nhttps://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-300.webp 300w", 441 | width: 1080, 442 | height: 270, 443 | alt: "aerial photography of mountain range covered with snow under white and blue sky at daytime", 444 | seoName: "mountains", 445 | fallbackSrc: 446 | "https://images.reactbricks.com/original/b83f614b-8dc9-4a20-b39f-a7e90374d4cc.jpg", 447 | fallbackSrcSet: 448 | "https://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/b83f614b-8dc9-4a20-b39f-a7e90374d4cc-300.jpg 300w", 449 | fallbackType: "image/jpeg", 450 | }, 451 | CAROUSEL_MOUNTAINS_2: { 452 | src: "https://images.reactbricks.com/original/79c16949-6349-45de-996b-5a11b31800e6.webp", 453 | placeholderSrc: 454 | "https://images.reactbricks.com/placeholder/79c16949-6349-45de-996b-5a11b31800e6.jpg", 455 | srcSet: 456 | "https://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-600.webp 600w,\nhttps://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-300.webp 300w", 457 | width: 1080, 458 | height: 270, 459 | alt: "snow mountain under stars", 460 | seoName: "mountains", 461 | fallbackSrc: 462 | "https://images.reactbricks.com/original/79c16949-6349-45de-996b-5a11b31800e6.jpg", 463 | fallbackSrcSet: 464 | "https://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/79c16949-6349-45de-996b-5a11b31800e6-300.jpg 300w", 465 | fallbackType: "image/jpeg", 466 | }, 467 | CAROUSEL_SEA_1: { 468 | src: "https://images.reactbricks.com/original/ae2eea74-6e50-42cd-8dbe-9d17774a1643.webp", 469 | placeholderSrc: 470 | "https://images.reactbricks.com/placeholder/ae2eea74-6e50-42cd-8dbe-9d17774a1643.jpg", 471 | srcSet: 472 | "https://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-720.webp 720w,\nhttps://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-600.webp 600w,\nhttps://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-300.webp 300w", 473 | width: 720, 474 | height: 720, 475 | alt: "boat on seashore", 476 | seoName: "seaside", 477 | fallbackSrc: 478 | "https://images.reactbricks.com/original/ae2eea74-6e50-42cd-8dbe-9d17774a1643.jpg", 479 | fallbackSrcSet: 480 | "https://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-720.jpg 720w,\nhttps://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/ae2eea74-6e50-42cd-8dbe-9d17774a1643-300.jpg 300w", 481 | fallbackType: "image/jpeg", 482 | }, 483 | CAROUSEL_SEA_2: { 484 | src: "https://images.reactbricks.com/original/807fd735-89a6-4f30-b2fe-6eaba36e3319.webp", 485 | placeholderSrc: 486 | "https://images.reactbricks.com/placeholder/807fd735-89a6-4f30-b2fe-6eaba36e3319.jpg", 487 | srcSet: 488 | "https://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-600.webp 600w,\nhttps://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-300.webp 300w", 489 | width: 1080, 490 | height: 1080, 491 | alt: "empty seashore", 492 | seoName: "sea", 493 | fallbackSrc: 494 | "https://images.reactbricks.com/original/807fd735-89a6-4f30-b2fe-6eaba36e3319.jpg", 495 | fallbackSrcSet: 496 | "https://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/807fd735-89a6-4f30-b2fe-6eaba36e3319-300.jpg 300w", 497 | fallbackType: "image/jpeg", 498 | }, 499 | CAROUSEL_SEA_3: { 500 | src: "https://images.reactbricks.com/original/a30ad110-b981-4aea-bdd4-8bd27319d319.webp", 501 | placeholderSrc: 502 | "https://images.reactbricks.com/placeholder/a30ad110-b981-4aea-bdd4-8bd27319d319.jpg", 503 | srcSet: 504 | "https://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-600.webp 600w,\nhttps://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-300.webp 300w", 505 | width: 1080, 506 | height: 1080, 507 | alt: "aerial photography of large body of water and shoreline", 508 | seoName: "sea", 509 | fallbackSrc: 510 | "https://images.reactbricks.com/original/a30ad110-b981-4aea-bdd4-8bd27319d319.jpg", 511 | fallbackSrcSet: 512 | "https://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/a30ad110-b981-4aea-bdd4-8bd27319d319-300.jpg 300w", 513 | fallbackType: "image/jpeg", 514 | }, 515 | CAROUSEL_SEA_4: { 516 | src: "https://images.reactbricks.com/original/b87f99ed-7b67-467c-a747-7327546a0fee.webp", 517 | placeholderSrc: 518 | "https://images.reactbricks.com/placeholder/b87f99ed-7b67-467c-a747-7327546a0fee.jpg", 519 | srcSet: 520 | "https://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-1080.webp 1080w,\nhttps://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-600.webp 600w,\nhttps://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-300.webp 300w", 521 | width: 1080, 522 | height: 1080, 523 | alt: "crystal clear water near coconut trees under the sun", 524 | seoName: "sea", 525 | fallbackSrc: 526 | "https://images.reactbricks.com/original/b87f99ed-7b67-467c-a747-7327546a0fee.jpg", 527 | fallbackSrcSet: 528 | "https://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-1080.jpg 1080w,\nhttps://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-600.jpg 600w,\nhttps://images.reactbricks.com/src_set/b87f99ed-7b67-467c-a747-7327546a0fee-300.jpg 300w", 529 | fallbackType: "image/jpeg", 530 | }, 531 | } as const satisfies Images 532 | -------------------------------------------------------------------------------- /react-bricks/bricks/features/index.ts: -------------------------------------------------------------------------------- 1 | import { types } from "react-bricks/frontend" 2 | import Features from "./Features" 3 | import FeatureItem from "./FeatureItem" 4 | 5 | const layout: types.Brick[] = [Features, FeatureItem] 6 | 7 | export default layout 8 | -------------------------------------------------------------------------------- /react-bricks/bricks/index.ts: -------------------------------------------------------------------------------- 1 | import { types } from "react-bricks/frontend" 2 | import layout from "./layout" 3 | import HeroUnit from "./HeroUnit" 4 | import features from "./features" 5 | 6 | const bricks: types.Brick[] = [HeroUnit, ...layout, ...features] 7 | 8 | export default bricks 9 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/Button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import classNames from 'classnames' 3 | import { Text, Link, types, useAdminContext } from 'react-bricks/frontend' 4 | 5 | import styles from '../../../css/Button.module.css' 6 | 7 | export interface ButtonProps { 8 | type: 'button' | 'link' 9 | text: string 10 | href: string 11 | isTargetBlank: boolean 12 | buttonType: 'submit' | 'button' | 'reset' 13 | simpleAnchorLink: boolean 14 | variant: 'solid' | 'outline' 15 | padding: 'normal' | 'small' 16 | className?: string 17 | } 18 | 19 | const Button: types.Brick = ({ 20 | type, 21 | href, 22 | isTargetBlank, 23 | buttonType, 24 | simpleAnchorLink = false, 25 | variant, 26 | padding, 27 | className, 28 | }) => { 29 | const target = isTargetBlank 30 | ? { target: '_blank', rel: 'noopener noreferrer' } 31 | : {} 32 | 33 | if (type === 'link') { 34 | return ( 35 | 49 | {children}} 53 | /> 54 | 55 | ) 56 | } 57 | 58 | // Button 59 | const { isAdmin, previewMode } = useAdminContext() 60 | 61 | return ( 62 | 81 | ) 82 | } 83 | 84 | Button.schema = { 85 | name: 'button', 86 | label: 'Button', 87 | category: 'shared', 88 | hideFromAddMenu: true, 89 | playgroundLinkLabel: 'View source code on Github', 90 | playgroundLinkUrl: 91 | 'https://github.com/ReactBricks/react-bricks-ui/blob/master/src/website/shared/Button.tsx', 92 | 93 | getDefaultProps: () => ({ 94 | type: 'link', 95 | text: 'Click me', 96 | href: '', 97 | isTargetBlank: false, 98 | buttonType: 'submit', 99 | simpleAnchorLink: false, 100 | variant: 'solid', 101 | padding: 'normal', 102 | }), 103 | sideEditProps: [ 104 | { 105 | groupName: 'Button functionality', 106 | defaultOpen: true, 107 | props: [ 108 | { 109 | name: 'type', 110 | label: 'Type', 111 | type: types.SideEditPropType.Select, 112 | selectOptions: { 113 | display: types.OptionsDisplay.Radio, 114 | options: [ 115 | { value: 'link', label: 'Link' }, 116 | { value: 'button', label: 'Form Button' }, 117 | ], 118 | }, 119 | }, 120 | { 121 | name: 'href', 122 | label: 'Link (external or path)', 123 | type: types.SideEditPropType.Text, 124 | show: (props) => props.type === 'link', 125 | }, 126 | { 127 | name: 'isTargetBlank', 128 | label: 'Open in new window', 129 | type: types.SideEditPropType.Boolean, 130 | show: (props) => props.type === 'link', 131 | }, 132 | { 133 | name: 'simpleAnchorLink', 134 | label: 'Simple anchor (no SPA link)', 135 | type: types.SideEditPropType.Boolean, 136 | show: (props) => props.type === 'link', 137 | }, 138 | { 139 | name: 'buttonType', 140 | label: 'Button type', 141 | type: types.SideEditPropType.Select, 142 | selectOptions: { 143 | display: types.OptionsDisplay.Radio, 144 | options: [ 145 | { value: 'submit', label: 'Form submit' }, 146 | { value: 'reset', label: 'Form reset' }, 147 | { value: 'button', label: 'Button' }, 148 | ], 149 | }, 150 | show: (props) => props.type === 'button', 151 | }, 152 | ], 153 | }, 154 | { 155 | groupName: 'Visual', 156 | props: [ 157 | { 158 | name: 'variant', 159 | label: 'Variant', 160 | type: types.SideEditPropType.Select, 161 | selectOptions: { 162 | display: types.OptionsDisplay.Radio, 163 | options: [ 164 | { value: 'solid', label: 'Solid' }, 165 | { value: 'outline', label: 'Outline' }, 166 | ], 167 | }, 168 | }, 169 | { 170 | name: 'padding', 171 | label: 'Size', 172 | type: types.SideEditPropType.Select, 173 | selectOptions: { 174 | display: types.OptionsDisplay.Radio, 175 | options: [ 176 | { value: 'normal', label: 'Normal' }, 177 | { value: 'small', label: 'Small' }, 178 | ], 179 | }, 180 | }, 181 | ], 182 | }, 183 | ], 184 | } 185 | 186 | export default Button 187 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { RichText, Image, Repeater, types, Link } from 'react-bricks/frontend' 3 | 4 | import styles from '../../../css/Footer.module.css' 5 | 6 | interface FooterProps {} 7 | 8 | const Footer: types.Brick = ({}) => { 9 | return ( 10 |
11 |
12 |
13 |
14 | 15 | Logo 21 | 22 | ( 26 |

{children}

27 | )} 28 | allowedFeatures={[types.RichTextFeatures.Link]} 29 | renderLink={({ children, href, target, rel }) => ( 30 | 36 | {children} 37 | 38 | )} 39 | /> 40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | 48 | Footer.schema = { 49 | name: 'footer', 50 | label: 'Footer', 51 | category: 'layout', 52 | tags: ['footer'], 53 | repeaterItems: [ 54 | { 55 | name: 'columns', 56 | itemType: 'footer-column', 57 | max: 4, 58 | }, 59 | ], 60 | // Defaults when a new brick is added 61 | getDefaultProps: () => ({ 62 | logo: { 63 | src: 'https://images.reactbricks.com/original/7fd7ef1a-928f-45d6-b7a7-ff34bf91c15e.svg', 64 | placeholderSrc: 65 | 'https://images.reactbricks.com/original/7fd7ef1a-928f-45d6-b7a7-ff34bf91c15e.svg', 66 | srcSet: '', 67 | alt: 'React Bricks', 68 | seoName: 'react-bricks', 69 | width: 1700.787, 70 | height: 377.953, 71 | }, 72 | copyright: [ 73 | { 74 | type: 'paragraph', 75 | children: [ 76 | { 77 | text: '© React Bricks, Inc.', 78 | }, 79 | ], 80 | }, 81 | { 82 | type: 'paragraph', 83 | children: [ 84 | { 85 | text: 'Proudly made in Italy', 86 | }, 87 | ], 88 | }, 89 | ], 90 | columns: [ 91 | { 92 | title: 'Company', 93 | links: [ 94 | { 95 | linkText: 'About us', 96 | linkPath: '/', 97 | }, 98 | { 99 | linkText: 'Why React Bricks?', 100 | linkPath: '/', 101 | }, 102 | { 103 | linkText: 'Terms of service', 104 | linkPath: '/', 105 | }, 106 | { 107 | linkText: 'Privacy', 108 | linkPath: '/', 109 | }, 110 | ], 111 | }, 112 | { 113 | title: 'Features', 114 | links: [ 115 | { 116 | linkText: 'Visual editing', 117 | linkPath: '/', 118 | }, 119 | { 120 | linkText: 'React components', 121 | linkPath: '/', 122 | }, 123 | { 124 | linkText: 'Enterprise-ready', 125 | linkPath: '/', 126 | }, 127 | { 128 | linkText: 'Roadmap', 129 | linkPath: '/', 130 | }, 131 | ], 132 | }, 133 | { 134 | title: 'Use cases', 135 | links: [ 136 | { 137 | linkText: 'Content editors', 138 | linkPath: '/', 139 | }, 140 | { 141 | linkText: 'Developers', 142 | linkPath: '/', 143 | }, 144 | { 145 | linkText: 'Enterprises', 146 | linkPath: '/', 147 | }, 148 | ], 149 | }, 150 | { 151 | title: 'Learn', 152 | links: [ 153 | { 154 | linkText: 'Tutorial', 155 | linkPath: '/', 156 | }, 157 | { 158 | linkText: 'Documentation', 159 | linkPath: '/', 160 | }, 161 | { 162 | linkText: 'Videos', 163 | linkPath: '/', 164 | }, 165 | { 166 | linkText: 'Blog', 167 | linkPath: '/', 168 | }, 169 | ], 170 | }, 171 | ], 172 | }), 173 | 174 | // Sidebar Edit controls for props 175 | sideEditProps: [], 176 | } 177 | 178 | export default Footer 179 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/FooterColumn.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Text, Repeater, types } from "react-bricks/frontend" 3 | import styles from "../../../css/FooterColumn.module.css" 4 | 5 | interface FooterColumnProps {} 6 | 7 | const FooterColumn: types.Brick = (props) => { 8 | return ( 9 |
10 | ( 14 |
{children}
15 | )} 16 | /> 17 | 18 |
19 | ) 20 | } 21 | 22 | FooterColumn.schema = { 23 | name: "footer-column", 24 | label: "Column", 25 | category: "layout", 26 | hideFromAddMenu: true, 27 | // tags: [], 28 | repeaterItems: [ 29 | { 30 | name: "links", 31 | itemType: "footer-link", 32 | }, 33 | ], 34 | 35 | // Defaults when a new brick is added 36 | getDefaultProps: () => ({ 37 | title: "Features", 38 | }), 39 | 40 | // Sidebar Edit controls for props 41 | sideEditProps: [], 42 | } 43 | 44 | export default FooterColumn 45 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/FooterLink.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Text, types, Link } from "react-bricks/frontend" 3 | 4 | import styles from "../../../css/FooterLink.module.css" 5 | 6 | interface FooterLinkProps { 7 | linkPath: string 8 | } 9 | 10 | const FooterLink: types.Brick = ({ linkPath }) => { 11 | return ( 12 | 13 | ( 17 |
{children}
18 | )} 19 | /> 20 | 21 | ) 22 | } 23 | 24 | FooterLink.schema = { 25 | name: "footer-link", 26 | label: "Link", 27 | category: "layout", 28 | hideFromAddMenu: true, 29 | // tags: [], 30 | 31 | // Defaults when a new brick is added 32 | getDefaultProps: () => ({ 33 | linkText: "Pricing", 34 | linkPath: "/", 35 | }), 36 | 37 | // Sidebar Edit controls for props 38 | sideEditProps: [ 39 | { 40 | name: "linkPath", 41 | label: "Link to...", 42 | type: types.SideEditPropType.Text, 43 | }, 44 | ], 45 | } 46 | 47 | export default FooterLink 48 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/Header.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react' 2 | import { 3 | Image, 4 | Repeater, 5 | types, 6 | Link, 7 | useAdminContext, 8 | } from 'react-bricks/frontend' 9 | import { useReactBricksContext } from 'react-bricks/frontend' 10 | import { BsMoonFill, BsSunFill } from 'react-icons/bs' 11 | import { FiMenu, FiX } from 'react-icons/fi' 12 | import useOnClickOutside from './useClickOutside' 13 | import { useTheme } from 'next-themes' 14 | 15 | import styles from '../../../css/Header.module.css' 16 | 17 | interface HeaderProps {} 18 | 19 | const Header: types.Brick = ({}) => { 20 | const [mobileMenuOpen, setMobileMenuOpen] = useState(false) 21 | const { isDarkColorMode, toggleColorMode } = useReactBricksContext() 22 | const { theme } = useTheme() 23 | const { isAdmin } = useAdminContext() 24 | 25 | const currentTheme = isAdmin ? (isDarkColorMode ? 'dark' : 'light') : theme 26 | 27 | const [mounted, setMounted] = useState(false) 28 | 29 | useEffect(() => { 30 | setMounted(true) 31 | }, []) 32 | 33 | const ref = useRef(null) 34 | useOnClickOutside(ref, () => setMobileMenuOpen(false)) 35 | 36 | return ( 37 |
38 | 104 |
105 | ) 106 | } 107 | 108 | Header.schema = { 109 | name: 'header', 110 | label: 'Header', 111 | category: 'layout', 112 | tags: ['header', 'menu'], 113 | repeaterItems: [ 114 | { 115 | name: 'menuItems', 116 | itemType: 'header-menu-item', 117 | itemLabel: 'Item', 118 | min: 0, 119 | max: 6, 120 | }, 121 | { 122 | name: 'buttons', 123 | itemType: 'button', 124 | itemLabel: 'Button', 125 | min: 0, 126 | max: 2, 127 | }, 128 | ], 129 | sideEditProps: [], 130 | getDefaultProps: () => ({ 131 | menuItems: [ 132 | { 133 | linkPath: '/', 134 | linkText: 'Home', 135 | }, 136 | { 137 | linkPath: '/about-us', 138 | linkText: 'About us', 139 | }, 140 | { 141 | linkPath: '', 142 | linkText: 'Features', 143 | submenuItems: [ 144 | { 145 | linkText: 'Visual editing', 146 | linkDescription: 147 | 'The best visual experience for your content editors', 148 | linkPath: '/', 149 | }, 150 | ], 151 | }, 152 | ], 153 | logo: { 154 | src: 'https://images.reactbricks.com/original/8d0eb40f-6e1a-4f6c-9895-a06767fcf5fa.svg', 155 | placeholderSrc: 156 | 'https://images.reactbricks.com/original/8d0eb40f-6e1a-4f6c-9895-a06767fcf5fa.svg', 157 | srcSet: '', 158 | width: 450, 159 | height: 100, 160 | alt: 'React Bricks', 161 | seoName: 'react-bricks', 162 | }, 163 | buttons: [ 164 | { 165 | text: 'Edit content', 166 | href: '/admin', 167 | isTargetBlank: false, 168 | type: 'link', 169 | variant: 'solid', 170 | padding: 'small', 171 | }, 172 | ], 173 | }), 174 | } 175 | 176 | export default Header 177 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/HeaderMenuItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react' 2 | import { Text, Repeater, types, Link, Plain } from 'react-bricks/frontend' 3 | import useOnClickOutside from './useClickOutside' 4 | 5 | import styles from '../../../css/HeaderMenuItem.module.css' 6 | 7 | interface HeaderMenuItemProps { 8 | linkPath: string 9 | linkText: any 10 | submenuItems: any 11 | mobileRef?: React.MutableRefObject 12 | setMobileMenuOpen?: React.Dispatch> 13 | } 14 | 15 | const HeaderMenuItem: types.Brick = ({ 16 | linkPath, 17 | linkText, 18 | submenuItems, 19 | mobileRef, 20 | setMobileMenuOpen, 21 | }) => { 22 | const [open, setOpen] = useState(false) 23 | const ref = useRef(null) 24 | 25 | useOnClickOutside(ref, () => setOpen(false)) 26 | useOnClickOutside(mobileRef, () => setMobileMenuOpen(false)) 27 | if (!submenuItems || !submenuItems.length) { 28 | return ( 29 |
30 | 35 | {children}} 39 | /> 40 | 41 | 42 |
setMobileMenuOpen(false)}> 43 | {' '} 44 | {typeof linkText === 'string' 45 | ? linkText 46 | : Plain.serialize(linkText)} 47 |
48 | 49 |
50 | ) 51 | } 52 | 53 | return ( 54 |
55 |
56 | 94 | {open && ( 95 |
96 | ( 99 |
setOpen((current) => !current)} 102 | > 103 | {props} 104 |
105 | )} 106 | /> 107 |
108 | )} 109 |
110 | 111 |
112 |
113 | {typeof linkText === 'string' ? linkText : Plain.serialize(linkText)} 114 |
115 | ( 118 |
setMobileMenuOpen(false)}> 119 | {props} 120 |
121 | )} 122 | /> 123 |
124 |
125 | ) 126 | } 127 | 128 | HeaderMenuItem.schema = { 129 | name: 'header-menu-item', 130 | label: 'Menu Item', 131 | category: 'layout', 132 | hideFromAddMenu: true, 133 | 134 | repeaterItems: [ 135 | { 136 | name: 'submenuItems', 137 | itemType: 'header-menu-sub-item', 138 | }, 139 | ], 140 | 141 | getDefaultProps: () => ({ 142 | submenuItems: [], 143 | linkPath: '/about-us', 144 | isActive: false, 145 | linkText: 'About us', 146 | }), 147 | 148 | sideEditProps: [ 149 | { 150 | name: 'linkPath', 151 | label: 'Link to...', 152 | type: types.SideEditPropType.Text, 153 | }, 154 | ], 155 | } 156 | 157 | export default HeaderMenuItem 158 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/HeaderMenuSubItem.tsx: -------------------------------------------------------------------------------- 1 | import styles from "../../../css/HeaderMenuSubItem.module.css" 2 | 3 | import React from "react" 4 | import { Text, types, Link } from "react-bricks/frontend" 5 | import { FiChevronRight } from "react-icons/fi" 6 | 7 | interface HeaderMenuSubItemProps { 8 | linkPath: string 9 | } 10 | 11 | const HeaderMenuSubItem: types.Brick = ({ 12 | linkPath, 13 | }) => { 14 | return ( 15 | 16 |
17 | 18 |
19 |
20 | ( 24 |
{children}
25 | )} 26 | /> 27 |
28 | ( 32 |
{children}
33 | )} 34 | /> 35 |
36 |
37 | 38 | ) 39 | } 40 | 41 | HeaderMenuSubItem.schema = { 42 | name: "header-menu-sub-item", 43 | label: "Submenu Item", 44 | category: "layout", 45 | hideFromAddMenu: true, 46 | 47 | getDefaultProps: () => ({ 48 | linkText: "Changelog", 49 | linkDescription: "Release notes for all React Bricks versions", 50 | linkPath: "/", 51 | }), 52 | 53 | sideEditProps: [ 54 | { 55 | name: "linkPath", 56 | label: "Link to...", 57 | type: types.SideEditPropType.Text, 58 | }, 59 | ], 60 | } 61 | 62 | export default HeaderMenuSubItem 63 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/index.ts: -------------------------------------------------------------------------------- 1 | import { types } from "react-bricks/frontend" 2 | import Header from "./Header" 3 | import HeaderMenuItem from "./HeaderMenuItem" 4 | import HeaderMenuSubItem from "./HeaderMenuSubItem" 5 | import Footer from "./Footer" 6 | import FooterColumn from "./FooterColumn" 7 | import FooterLink from "./FooterLink" 8 | import Button from "./Button" 9 | 10 | const layout: types.Brick[] = [ 11 | Header, 12 | HeaderMenuItem, 13 | HeaderMenuSubItem, 14 | Footer, 15 | FooterColumn, 16 | FooterLink, 17 | Button, 18 | ] 19 | 20 | export default layout 21 | -------------------------------------------------------------------------------- /react-bricks/bricks/layout/useClickOutside.ts: -------------------------------------------------------------------------------- 1 | import { MouseEventHandler, RefObject, useEffect } from 'react' 2 | 3 | const useOnClickOutside = ( 4 | ref: RefObject, 5 | handler: MouseEventHandler 6 | ) => { 7 | useEffect(() => { 8 | const listener = (event: any) => { 9 | // Do nothing if clicking ref's element or descendent elements 10 | if (!ref?.current || ref.current.contains(event.target)) { 11 | return 12 | } 13 | handler(event) 14 | } 15 | document.addEventListener('mousedown', listener) 16 | document.addEventListener('touchstart', listener) 17 | return () => { 18 | document.removeEventListener('mousedown', listener) 19 | document.removeEventListener('touchstart', listener) 20 | } 21 | }, [ref, handler]) 22 | } 23 | 24 | export default useOnClickOutside 25 | -------------------------------------------------------------------------------- /react-bricks/config.ts: -------------------------------------------------------------------------------- 1 | import Router from "next/router" 2 | import { types } from "react-bricks/frontend" 3 | 4 | import bricks from "./bricks" 5 | import pageTypes from "./pageTypes" 6 | import NextLink from "./NextLink" 7 | 8 | const config: types.ReactBricksConfig = { 9 | appId: process.env.NEXT_PUBLIC_APP_ID, 10 | apiKey: process.env.API_KEY, 11 | environment: process.env.NEXT_PUBLIC_ENVIRONMENT, 12 | bricks, 13 | pageTypes, 14 | customFields: [], 15 | logo: "/logo.svg", 16 | loginUI: {}, 17 | contentClassName: "", 18 | //defaultTheme: "", 19 | renderLocalLink: NextLink, 20 | navigate: (path: string) => Router.push(path), 21 | loginPath: "/admin", 22 | editorPath: "/admin/editor", 23 | playgroundPath: "/admin/playground", 24 | appSettingsPath: "/admin/app-settings", 25 | previewPath: "/preview", 26 | //getAdminMenu: () => [], 27 | isDarkColorMode: false, 28 | toggleColorMode: () => {}, 29 | useCssInJs: false, 30 | appRootElement: "#__next", 31 | clickToEditSide: types.ClickToEditSide.BottomRight, 32 | //responsiveBreakpoints: [{ type: types.DeviceType.Phone, width: 480, label: "Smartphone" },], 33 | enableAutoSave: true, 34 | disableSaveIfInvalidProps: false, 35 | enablePreview: true, 36 | blockIconsPosition: types.BlockIconsPosition.OutsideBlock, 37 | enableUnsplash: true, 38 | unsplashApiKey: "", 39 | enablePreviewImage: true, 40 | enableDefaultEmbedBrick: true, 41 | //permissions, Fine-grained permissions for enterprise plans 42 | } 43 | 44 | export default config 45 | -------------------------------------------------------------------------------- /react-bricks/pageTypes.ts: -------------------------------------------------------------------------------- 1 | import { types } from 'react-bricks/frontend' 2 | 3 | const pageTypes: types.IPageType[] = [ 4 | { 5 | name: 'page', 6 | pluralName: 'pages', 7 | defaultLocked: false, 8 | defaultStatus: types.PageStatus.Published, 9 | getDefaultContent: () => [], 10 | }, 11 | { 12 | name: 'layout', 13 | pluralName: 'layout', 14 | defaultLocked: false, 15 | defaultStatus: types.PageStatus.Published, 16 | getDefaultContent: () => [], 17 | isEntity: true, 18 | allowedBlockTypes: ['header', 'footer'], 19 | }, 20 | ] 21 | 22 | export default pageTypes 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "incremental": true 21 | }, 22 | "include": [ 23 | "next-env.d.ts", 24 | "**/*.ts", 25 | "**/*.tsx" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------