diff --git a/web/docs/handbook/contributing.mdx b/web/docs/handbook/contributing.mdx index f84312bc73c..ec6732f0b52 100644 --- a/web/docs/handbook/contributing.mdx +++ b/web/docs/handbook/contributing.mdx @@ -17,4 +17,22 @@ Want to contribute? Why not jump into our github and: - [vote on a new connector](https://github.com/supabase/supabase/issues/4) for a tool that you use - [vote on a new database](https://github.com/supabase/supabase/issues/6) - spread the word if you like what we are doing +- add your project or company to our showcase +## How to add a your project to our showcase + +1. Add your project/company/service's logo in `supabase/web/static/img/showcase-logo/`. +2. Add a new entry in `supabase/web/src/data/showcase.json` (you can replace and use the next one): +``` +{ + "project_or_company": { + "name": "Amazing project", + "description": "This is an amazing description for an amazing project 😄", + "web_site": "my-powerful.site", + "image": "img-name.extension" + }, + "is_open_source": true/false, + "public_repo": "my-repo" +} +``` +3. Make a `pull request` to our [repository](https://github.com/supabase/supabase) labeled with... (@todo). diff --git a/web/docusaurus.config.js b/web/docusaurus.config.js index 2b8632197cb..732435ca2cc 100755 --- a/web/docusaurus.config.js +++ b/web/docusaurus.config.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ - module.exports = { title: 'Supabase', tagline: 'The open source Firebase alternative.', @@ -135,6 +134,11 @@ module.exports = { }, ], }, + { + to: '/showcase', + label: 'Showcase', + position: 'right', + }, // { to: '/docs/pricing', label: 'Pricing', position: 'right' }, { href: 'https://app.supabase.io', label: 'Login', position: 'right' }, ], diff --git a/web/src/components/Section.js b/web/src/components/Section.js new file mode 100644 index 00000000000..ffbc7f3e0df --- /dev/null +++ b/web/src/components/Section.js @@ -0,0 +1,68 @@ +import React, { useRef, useEffect } from 'react' +import PropTypes from 'prop-types'; + + +export default function Section({ title, id, style, children }) { + const hash = String(id ? id : title).trim().replace(/\s/g, "-").toLowerCase(); + const sectionRef = useRef(null); + + useEffect(() => { + function scrollOnHash() { + const clearedLocationHash = window.location.hash.substring(1); + + if (clearedLocationHash == hash) { + sectionRef.current.scrollIntoView({ behavior: 'smooth' }); + } + } + + function changeHash() { + const el = document.getElementById(hash); + + if (el) { + const elementPos = el.getBoundingClientRect(); + + if (elementPos.top <= 80 && elementPos.top >= 0) + replaceHistoryHash(); + } + } + scrollOnHash(); + window.addEventListener("scroll", changeHash); + + return () => { + window.removeEventListener("scroll", changeHash); + }; + }, []) + + function replaceHistoryHash() { + history.replaceState(history.state, '', `#${hash}`); + } + + function handleHashClick() { + replaceHistoryHash(); + sectionRef.current.scrollIntoView({ behavior: 'smooth' }); + } + + return ( +
+
+

+ {title} + # +

+
{children}
+
+
+ ); +} + +const stylePropOptions = { + 'underline': 'with-underline' +}; + +Section.propTypes = { + title: PropTypes.string.isRequired, + style: PropTypes.oneOf(['underline']), +} diff --git a/web/src/css/custom.css b/web/src/css/custom.css index 25a89add5fe..2b16671da54 100755 --- a/web/src/css/custom.css +++ b/web/src/css/custom.css @@ -943,6 +943,193 @@ html[data-theme='dark'] .navbar-item-twitter:before { max-width: 100%; } +/* Section */ +.Section { + position: relative; + display: flex; + flex-direction: column; +} +.Section-Hash { + margin-left: 8px; + color: var(--custom-content-color-light); + opacity: 0.4; +} +.Section-Title:hover .Section-Hash { + color: var(--custom-primary); + opacity: 1; +} +.Section-Title { + cursor: pointer; + scroll-margin: 74px; /* Navbar size (60) + a little margin */ +} + +/* Showcase */ +.ShowcaseCard { + position: relative; + height: 100%; + display: flex; + flex-direction: column; + box-shadow: none; + border: 1px solid #333; + padding: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing); +} + +.ShowcaseCard:hover { + transition: all 0.2s ease; + border: 1px solid var(--custom-primary); + box-shadow: var(--custom-shadow-xl), 0 0 0 1px var(--custom-primary) !important; +} + +.ShowcaseCard-Img { + height: 100%; + width: auto; +} + +.ShowcaseCard-ImgContainer { + height: 140px; + display: flex; + justify-content: center; +} + +.ShowcaseCard-Text { + height: 100%; + display: flex; + flex-direction: column; +} + +.ShowcaseCard-Text p { + width: 100%; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + margin: 0; +} + +.ShowcaseCard-Text h3 { + opacity: 0; + position: absolute; + width: 100%; + top: 0; + left: 0; + backdrop-filter: blur(3px) contrast(80%); + padding: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing); + transition: 0.2s ease; +} + +.ShowcaseCard-Content { + display: flex; + flex-direction: column; + margin-bottom: var(--ifm-card-vertical-spacing); + height: 100%; +} + +.ShowcaseCard:hover h3 { + opacity: 1; +} + +.ShowcaseCard-Tag { + color: var(--custom-primary); + font-weight: bold; + font-size: 12px; + margin: 8px 4px 12px 0px; +} + +.ShowcaseCard-Footer { + display: flex; + flex-direction: row; + justify-content: flex-start; +} + +.ShowcaseCard-Footer-Item { + width: 100%; +} + +/* Spaces between children */ +.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:first-child { + margin-right: 4px; +} + +.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:nth-child(even) { + margin-left: 4px; + margin-right: 4px; +} + +.ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:last-child { + margin-right: 0px; +} + +@media screen and (max-width: 997px) { + .ShowcaseCard-ImgContainer { + height: 120px; + align-self: center; + } + + .ShowcaseCard-Content { + display: flex; + flex-direction: row; + } + + .ShowcaseCard-Text { + width: 100%; + margin-left: var(--ifm-card-horizontal-spacing); + } + + .ShowcaseCard-Tag { + margin-top: 0px; + } + + .ShowcaseCard-Footer a { + margin: var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing) 0 0; + } + + .ShowcaseCard-Text h3 { + position: relative; + bottom: 0; + opacity: 1; + backdrop-filter: blur(0) contrast(100%); + padding: 0; + } +} + +@media screen and (max-width: 650px) { + .ShowcaseCard-ImgContainer { + height: 80px; + } +} + +@media screen and (max-width: 650px) { + .ShowcaseCard-Content { + flex-direction: column; + padding: 0px; + } + + .ShowcaseCard-Text { + margin: 0px; + padding: 0px; + } + + .ShowcaseCard-Text h3 { + margin-top: var(--ifm-card-vertical-spacing); + } +} + +@media screen and (max-width: 380px) { + .ShowcaseCard-Footer { + flex-direction: column; + } + + /* Spaces between children */ + .ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:first-child { + margin-right: 0px; + } + + .ShowcaseCard-Footer-Spaced .ShowcaseCard-Footer-Item:nth-child(even) { + margin-left: 0px; + margin-right: 0px; + } +} + /* New CSS */ @@ -1033,4 +1220,4 @@ h4.method-list-item-label .method-list-item-validation { border-radius: 5px; overflow: hidden; border: 2px solid var(--custom-border-color); -} \ No newline at end of file +} diff --git a/web/src/data/showcase.json b/web/src/data/showcase.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/web/src/data/showcase.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/web/src/pages/showcase.js b/web/src/pages/showcase.js new file mode 100644 index 00000000000..606880836c8 --- /dev/null +++ b/web/src/pages/showcase.js @@ -0,0 +1,81 @@ +import React, { useEffect, useRef, useState } from 'react' +import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +import Layout from '@theme/Layout' +import Section from '../components/Section' +import Link from '@docusaurus/Link' + +import showcase from '../data/showcase.json' + + +export default function Showcase() { + const { siteConfig = {} } = useDocusaurusContext(); + const processedShowcase = showcase + .sort((a, b) => a.project_or_company.name.localeCompare(b.project_or_company.name)) + + return ( + +
+ + {/* Open Source */} + +
+ + {/* Add */} +
+

+ + Learn how + to add your project to our showcase. +

+
+
+ ); +} + +function ShowcaseSection({ showcase }) { + return ( +
+ {/* Display showcase */} + {showcase.map((v, i) => + (
+ +
)) + } +
+ ) +} + +function ShowcaseCard({ projectOrCompany, isOpenSource, publicRepo }) { + const spacedFooter = projectOrCompany.web_site && publicRepo && isOpenSource ? "ShowcaseCard-Footer-Spaced" : null; + const button = (label, href) => ( + {label} + ) + + return ( +
+
+
+ +
+ +
+

{projectOrCompany.name}

+ {isOpenSource ? "#OpenSource" : null} +

{projectOrCompany.description}

+
+
+ +
+ {projectOrCompany.web_site ? button('Web site', projectOrCompany.web_site) : null} + {publicRepo && isOpenSource ? button('View repo', publicRepo) : null} +
+
+ ); +} diff --git a/web/static/img/showcase-logo/supabase-logo.svg b/web/static/img/showcase-logo/supabase-logo.svg new file mode 100644 index 00000000000..1315b0a5ccf --- /dev/null +++ b/web/static/img/showcase-logo/supabase-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + +