feat: Move some components into ui-patterns (#20774)

* Change all imports in the ui package which import via the @ui shortcut.

* Add a new ui-patterns package. Add it to all apps.

* Migrate PrivacySettings from ui to ui-patterns.

* Migrate ConsentToast from ui to ui-patterns.

* Remove providers folder from ui package.

* Move GlassPanel.

* Migrate IconPanel.

* Migrate TweetCard.

* Migrate ThemeImage.

* Remove LWXCountdownBanner.

* Migrate CountdownWidget.

* Migrate SchemaTableNode.

* Migrate ExpandableVideo.

* Migrate ThemeToggle.

* Fix bunch of imports in the docs app.

* Revert some unnecessary changes.

* Expand the README.md.

* Fix the tailwind configs, they were using old folder structure.

* Fix leftover merge conflicts.

* Remove a deleted page in master.

---------

Co-authored-by: Terry Sutton <saltcod@gmail.com>
This commit is contained in:
Ivan VasilovandTerry Sutton authored and GitHub committed 2024-02-12 12:44:00 +01:00
1 parent b06dc92cae
commit 5754c23abb
91 files changed
+372 -771

No files matched your search

@@ -1,5 +1,5 @@
'use client'
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
export default function ThemeSwitcher() {
return <ThemeToggle />
+1 -1
View File
@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['ui'],
transpilePackages: ['ui', 'ui-patterns'],
webpack: (config, { dev, isServer, webpack, nextRuntime }) => {
config.module.rules.push({
test: /\.node$/,
+1
View File
@@ -27,6 +27,7 @@
"react-flow": "^1.0.3",
"sql-formatter": "^13.1.0",
"ui": "*",
"ui-patterns": "*",
"valtio": "^1.12.0",
"zod": "^3.22.4"
},
+1 -1
View File
@@ -5,8 +5,8 @@ export default config({
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
'../../packages/ui/src/**/*.{tsx,ts,js}',
'../../packages/ui-patterns/**/*.{tsx,ts,js}',
],
theme: {
extend: {
+1 -1
View File
@@ -1,5 +1,5 @@
import { IconPanel } from 'ui-patterns/IconPanel'
import providers from '../data/authProviders'
import { IconPanel } from 'ui'
import Link from 'next/link'
export default function AuthProviders({ type }: { type: string }) {
+2 -1
View File
@@ -1,7 +1,8 @@
import Link from 'next/link'
import React, { useState } from 'react'
import { extensions } from 'shared-data'
import { GlassPanel, IconX, Input } from 'ui'
import { IconX, Input } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
type Extension = {
name: string
+2 -1
View File
@@ -1,7 +1,8 @@
import Link from 'next/link'
import { IconBackground, IconPanel, IconPlay } from 'ui'
import { IconBackground, IconPlay } from 'ui'
import { useBreakpoint } from 'common'
import DocsCoverLogo from './DocsCoverLogo'
import { IconPanel } from 'ui-patterns/IconPanel'
const HomePageCover = (props) => {
const isXs = useBreakpoint(639)
@@ -4,8 +4,7 @@ import Link from 'next/link'
import { FC } from 'react'
import { Button, IconCommand, IconGitHub, IconSearch, SearchButton } from 'ui'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
const TopNavBar: FC = () => {
const isLoggedIn = useIsLoggedIn()
+7 -15
View File
@@ -4,36 +4,28 @@
// Basic UI things
import Link from 'next/link'
import {
Accordion,
Admonition,
Alert,
Button,
CodeBlock,
GlassPanel,
IconPanel,
markdownComponents,
Tabs,
ThemeImage,
} from 'ui'
import { Heading } from './CustomHTMLElements'
import { Accordion, Admonition, Alert, Button, CodeBlock, markdownComponents, Tabs } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { IconPanel } from 'ui-patterns/IconPanel'
import { ThemeImage } from 'ui-patterns/ThemeImage'
// Common components
import { CH } from '@code-hike/mdx/components'
import StepHikeCompact from '~/components/StepHikeCompact'
import ButtonCard from './ButtonCard'
import { Heading } from './CustomHTMLElements'
// Reference guide specific
// [Charis] I think we can factor these out so they aren't in the bundle for absolutely everything
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
import RefHeaderSection from './reference/RefHeaderSection'
import RefSubLayout from '~/layouts/ref/RefSubLayout'
import RefHeaderSection from './reference/RefHeaderSection'
// Other components
import AuthProviders from '~/components/AuthProviders'
import { ProjectConfigVariables } from './ProjectConfigVariables'
import Options from '~/components/Options'
import Param from '~/components/Params'
import { ProjectConfigVariables } from './ProjectConfigVariables'
// Data wrappers
import { NavData } from './NavData'
+2 -1
View File
@@ -1,4 +1,5 @@
import { IconCheckCircle, IconLifeBuoy, PrivacySettings } from 'ui'
import { IconCheckCircle, IconLifeBuoy } from 'ui'
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
export const primaryLinks = [
{
+3 -2
View File
@@ -1,16 +1,17 @@
import { MDXProvider } from '@mdx-js/react'
import { NextSeo } from 'next-seo'
import Head from 'next/head'
import { usePathname } from 'next/navigation'
import { useRouter } from 'next/router'
import { FC, useEffect, useRef, useState } from 'react'
import { ExpandableVideo, IconExternalLink } from 'ui'
import { IconExternalLink } from 'ui'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
import components from '~/components'
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import useHash from '~/hooks/useHash'
import { LayoutMainContent } from '../DefaultLayout'
import { usePathname } from 'next/navigation'
interface Props {
meta: {
+9 -1
View File
@@ -60,7 +60,15 @@ const nextConfig = {
transform: 'lodash/{{member}}',
},
},
transpilePackages: ['ui', 'common', 'mermaid', 'mdx-mermaid', 'dayjs', 'shared-data'],
transpilePackages: [
'ui',
'ui-patterns',
'common',
'mermaid',
'mdx-mermaid',
'dayjs',
'shared-data',
],
async headers() {
return [
{
+1
View File
@@ -68,6 +68,7 @@
"remark-gfm": "^3.0.1",
"shared-data": "*",
"ui": "*",
"ui-patterns": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"unist-util-visit": "^4.1.2",
+6 -5
View File
@@ -9,18 +9,19 @@ import { createClient } from '@supabase/supabase-js'
import { QueryClientProvider, useQueryClient } from '@tanstack/react-query'
import { AuthProvider, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common'
import { useRouter } from 'next/router'
import { type PropsWithChildren, useCallback, useEffect, useState } from 'react'
import { AppPropsWithLayout } from 'types'
import { CommandMenuProvider, PortalToast, useConsent } from 'ui'
import { TabsProvider } from 'ui/src/components/Tabs'
import { useCallback, useEffect, useState, type PropsWithChildren } from 'react'
import { CommandMenuProvider, PortalToast, TabsProvider } from 'ui'
import { useConsent } from 'ui-patterns/ConsentToast'
import Favicons from '~/components/Favicons'
import { IPv4DeprecationBanner } from '~/components/IPv4DeprecationBanner'
import SiteLayout from '~/layouts/SiteLayout'
import { IS_PLATFORM } from '~/lib/constants'
import { unauthedAllowedPost } from '~/lib/fetch/fetchWrappers'
import { useRootQueryClient } from '~/lib/fetch/queryClient'
import { useOnLogout } from '~/lib/userAuth'
import { LOCAL_STORAGE_KEYS, remove } from '~/lib/storage'
import { useOnLogout } from '~/lib/userAuth'
import { AppPropsWithLayout } from '~/types'
/**
*
+3 -1
View File
@@ -1,6 +1,8 @@
import Layout from '~/layouts/DefaultLayout'
import Link from 'next/link'
import { GlassPanel, IconPanel, Button, IconChevronRight } from 'ui'
import { Button, IconChevronRight } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { IconPanel } from 'ui-patterns/IconPanel'
export const meta = {
id: 'getting-started',
+3 -1
View File
@@ -1,6 +1,8 @@
import Layout from '~/layouts/DefaultLayout'
import Link from 'next/link'
import { GlassPanel, IconPanel, Button, IconChevronRight } from 'ui'
import { Button, IconChevronRight } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { IconPanel } from 'ui-patterns/IconPanel'
export const meta = {
title: 'Resources',
+3 -9
View File
@@ -1,14 +1,8 @@
import HomeLayout from '~/layouts/HomeLayout'
import Link from 'next/link'
import {
Button,
GlassPanel,
IconBackground,
IconChevronRight,
IconPanel,
IconPlay,
TextLink,
} from 'ui'
import { Button, IconBackground, IconChevronRight, IconPlay, TextLink } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { IconPanel } from 'ui-patterns/IconPanel'
import HomeMenuIconPicker from '~/components/Navigation/NavigationMenu/HomeMenuIconPicker'
+4 -4
View File
@@ -2,13 +2,13 @@ const config = require('config/tailwind.config')
module.exports = config({
content: [
'../../packages/ui/src/**/*.{tsx,ts,js}',
'./pages/**/*.{tsx,mdx}',
'./components/**/*.tsx',
'./layouts/**/*.tsx',
'./src/**/*.{ts,tsx,mdx}',
'./content/**/*.{ts,tsx,mdx}',
'./docs/**/*.{tsx,mdx}',
'./layouts/**/*.tsx',
'./pages/**/*.{tsx,mdx}',
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/**/*.{tsx,ts,js}',
],
plugins: [
function ({ addUtilities, addVariant }) {
@@ -1,5 +1,5 @@
import { useTheme } from 'next-themes'
import { IconMoon, IconSun, Listbox, Theme, themes } from 'ui'
import { IconMoon, IconSun, Listbox, themes } from 'ui'
import Panel from 'components/ui/Panel'
@@ -18,7 +18,7 @@ const ThemeSettings = () => {
icon={resolvedTheme === 'dark' ? <IconMoon /> : <IconSun />}
onChange={(value: string) => setTheme(value)}
>
{themes.map((theme: Theme) => (
{themes.map((theme) => (
<Listbox.Option key={theme.value} label={theme.name} value={theme.value}>
{theme.name}
</Listbox.Option>
@@ -18,7 +18,8 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte
import { useTablesQuery } from 'data/tables/tables-query'
import { useTheme } from 'next-themes'
import 'reactflow/dist/style.css'
import { IconLoader, TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableNode } from 'ui'
import { IconLoader } from 'ui'
import { TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableNode } from 'ui-patterns/SchemaTableNode'
type TableNodeData = {
name: string
+1 -1
View File
@@ -389,7 +389,7 @@ const nextConfig = {
'vercel.com',
],
},
transpilePackages: ['ui', 'common', 'shared-data'],
transpilePackages: ['ui', 'ui-patterns', 'common', 'shared-data'],
webpack(config) {
config.module?.rules
.find((rule) => rule.oneOf)
+1
View File
@@ -100,6 +100,7 @@
"sql-formatter": "^12.2.0",
"sqlstring": "^2.3.2",
"ui": "*",
"ui-patterns": "*",
"uuid": "^9.0.1",
"valtio": "^1.11.2",
"yup": "^0.32.11",
+6 -6
View File
@@ -1,5 +1,3 @@
import 'ui/build/css/themes/dark.css'
import 'ui/build/css/themes/light.css'
import 'styles/code.scss'
import 'styles/contextMenu.scss'
import 'styles/date-picker.scss'
@@ -7,14 +5,16 @@ import 'styles/editor.scss'
import 'styles/graphiql-base.scss'
import 'styles/grid.scss'
import 'styles/main.scss'
import 'styles/markdown-preview.scss'
import 'styles/monaco.scss'
import 'styles/react-data-grid-logs.scss'
import 'styles/reactflow.scss'
import 'styles/storage.scss'
import 'styles/stripe.scss'
import 'styles/toast.scss'
import 'styles/ui.scss'
import 'styles/reactflow.scss'
import 'styles/markdown-preview.scss'
import 'ui/build/css/themes/dark.css'
import 'ui/build/css/themes/light.css'
import { loader } from '@monaco-editor/react'
import { TooltipProvider } from '@radix-ui/react-tooltip'
@@ -33,7 +33,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import toast from 'react-hot-toast'
// @ts-ignore
import Prism from 'prism-react-renderer/prism'
import ConsentToast from 'ui/src/components/ConsentToast'
import { ConsentToast } from 'ui-patterns/ConsentToast'
import PortalToast from 'ui/src/layout/PortalToast'
import Favicons from 'components/head/Favicons'
@@ -42,6 +42,7 @@ import {
CommandMenuWrapper,
RouteValidationWrapper,
} from 'components/interfaces/App'
import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWrapperContext'
import {
FeaturePreviewContextProvider,
FeaturePreviewModal,
@@ -58,7 +59,6 @@ import { useAppStateSnapshot } from 'state/app-state'
import { RootStore } from 'stores'
import HCaptchaLoadedStore from 'stores/hcaptcha-loaded-store'
import { AppPropsWithLayout } from 'types'
import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWrapperContext'
dayjs.extend(customParseFormat)
dayjs.extend(utc)
+2 -3
View File
@@ -2,12 +2,11 @@ const config = require('config/tailwind.config')
module.exports = config({
content: [
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
// purge styles from grid library
'./../node_modules/@supabase/grid/src/components/**/*.{js,ts,jsx,tsx}',
'./../node_modules/@supabase/grid/src/components/**/**/*.{js,ts,jsx,tsx}',
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/**/*.{tsx,ts,js}',
],
theme: {
extend: {
@@ -1,5 +1,5 @@
import { createClient } from '@supabase/supabase-js'
import { Button, ThemeImage } from 'ui'
import { Button } from 'ui'
import { useState } from 'react'
// Import Swiper React components
import { useRouter } from 'next/router'
@@ -9,6 +9,7 @@ import { ColorSwatchIcon, MenuIcon } from '@heroicons/react/outline'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { useTheme } from 'next-themes'
import { ThemeImage } from 'ui-patterns/ThemeImage'
function AuthWidgetSection() {
const supabase = createClient(
@@ -1,5 +1,5 @@
import Link from 'next/link'
import { GlassPanel } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import SectionContainer from '../Layouts/SectionContainer'
import TextLink from '../TextLink'
+1 -1
View File
@@ -4,13 +4,13 @@ import { useTheme } from 'next-themes'
import { Badge, IconDiscord, IconGitHubSolid, IconTwitterX, IconYoutubeSolid, cn } from 'ui'
import Image from 'next/image'
import { useRouter } from 'next/router'
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
import { CheckIcon } from '@heroicons/react/outline'
import SectionContainer from '../Layouts/SectionContainer'
import footerData from 'data/Footer'
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
interface Props {
className?: string
@@ -1,6 +1,6 @@
import React from 'react'
import Countdown from 'react-countdown'
import { CountdownWidget } from 'ui'
import { CountdownWidget } from 'ui-patterns/CountdownWidget'
const CountdownComponent = ({
date,
@@ -1,7 +1,8 @@
import { Badge, ExpandableVideo } from 'ui'
import { Badge } from 'ui'
import Link from 'next/link'
import Image from 'next/image'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
export const PencilSvg = () => (
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -1,6 +1,6 @@
import React from 'react'
import Countdown from 'react-countdown'
import { CountdownWidget } from 'ui'
import { CountdownWidget } from 'ui-patterns/CountdownWidget'
const CountdownComponent = ({
date,
@@ -1,15 +1,9 @@
import { useEffect, useState } from 'react'
import {
ExpandableVideo,
IconDocumentation,
IconMicSolid,
IconProductHunt,
IconYoutubeSolid,
cn,
} from 'ui'
import { IconDocumentation, IconMicSolid, IconProductHunt, IconYoutubeSolid, cn } from 'ui'
import Link from 'next/link'
import { StepLink } from '../data/lwx_data'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
interface DayLink extends StepLink {
className?: string
@@ -4,13 +4,13 @@ import { useRouter } from 'next/router'
import Link from 'next/link'
import { useWindowSize } from 'react-use'
import { TweetCard } from 'ui'
import { Swiper, SwiperSlide } from 'swiper/react'
import SwiperCore, { Autoplay } from 'swiper'
import 'swiper/swiper.min.css'
import SectionContainer from '../Layouts/SectionContainer'
import { useBreakpoint } from 'common'
import { TweetCard } from 'ui-patterns/TweetCard'
SwiperCore.use([Autoplay])
@@ -1,8 +1,9 @@
import { useState } from 'react'
import { useRouter } from 'next/router'
import { Button, IconMessageCircle, TweetCard } from 'ui'
import { Button, IconMessageCircle } from 'ui'
import Tweets from '../../data/tweets/Tweets.json'
import Link from 'next/link'
import { TweetCard } from 'ui-patterns/TweetCard'
function TwitterSocialProof() {
// base path for images
+1 -1
View File
@@ -1,4 +1,4 @@
import { PrivacySettings } from 'ui'
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
const footerData = [
{
+1 -1
View File
@@ -14,13 +14,13 @@ import {
CollapsibleContent_Shadcn_,
IconTriangle,
cn,
ThemeImage,
Admonition,
} from 'ui'
import ImageFadeStack from '~/components/ImageFadeStack'
import ZoomableImg from '~/components/ZoomableImg/ZoomableImg'
import 'react-medium-image-zoom/dist/styles.css'
import { ThemeImage } from 'ui-patterns/ThemeImage'
// import all components used in blog articles here
// to do: move this into a helper/utils, it is used elsewhere
+1 -1
View File
@@ -41,7 +41,7 @@ const nextConfig = {
basePath: '',
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
trailingSlash: false,
transpilePackages: ['ui', 'common', 'shared-data'],
transpilePackages: ['ui', 'ui-patterns', 'common', 'shared-data'],
images: {
dangerouslyAllowSVG: true,
domains: [
+1
View File
@@ -63,6 +63,7 @@
"typed.js": "^2.0.16",
"typescript": "^5.2.2",
"ui": "*",
"ui-patterns": "*",
"use-debounce": "^7.0.1",
"vanilla-tilt": "1.7.0",
"yup": "^0.32.11"
+4 -2
View File
@@ -4,14 +4,16 @@ import '../styles/index.css'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { AuthProvider, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common'
import { API_URL, APP_NAME, DEFAULT_META_DESCRIPTION } from 'lib/constants'
import { DefaultSeo } from 'next-seo'
import { AppProps } from 'next/app'
import Head from 'next/head'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { CommandMenuProvider, PortalToast, themes, useConsent } from 'ui'
import { CommandMenuProvider, PortalToast, themes } from 'ui'
import { useConsent } from 'ui-patterns/ConsentToast'
import Meta from '~/components/Favicons'
import { API_URL, APP_NAME, DEFAULT_META_DESCRIPTION } from '~/lib/constants'
import { post } from '~/lib/fetchWrapper'
import supabase from '~/lib/supabase'
+2 -9
View File
@@ -1,13 +1,5 @@
import { createClient } from '@supabase/supabase-js'
import {
Button,
IconArrowUpRight,
IconBriefcase,
IconEye,
IconLink,
IconShield,
ThemeImage,
} from 'ui'
import { Button, IconArrowUpRight, IconBriefcase, IconEye, IconLink, IconShield } from 'ui'
import ApiExamples from 'data/products/auth/auth-api-examples'
import AuthSqlRulesExamples from 'data/products/auth/auth-sql-rules-examples'
import Solutions from 'data/Solutions'
@@ -26,6 +18,7 @@ import APISection from '~/components/Sections/APISection'
import GithubExamples from '~/components/Sections/GithubExamples'
import ProductHeader from '~/components/Sections/ProductHeader'
import AuthProviders from '~/data/auth.json'
import { ThemeImage } from 'ui-patterns/ThemeImage'
function AuthPage() {
// base path for images
+1 -1
View File
@@ -12,7 +12,7 @@ import PostTypes from '~/types/post'
import { motion } from 'framer-motion'
import styles from './customers.module.css'
import Link from 'next/link'
import { GlassPanel } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
export async function getStaticProps() {
const allPostsData = getSortedPosts({ directory: '_customers' })
+3 -2
View File
@@ -1,4 +1,4 @@
import { Badge, Button, IconArrowUpRight, IconX, Tabs, ThemeImage } from 'ui'
import { Badge, Button, IconArrowUpRight, IconX, Tabs } from 'ui'
// data
import ApiExamplesData from 'data/products/database/api-examples'
import ExtensionsExamplesData from 'data/products/database/extensions-examples'
@@ -23,7 +23,8 @@ import ProductIcon from '~/components/ProductIcon'
import APISection from '~/components/Sections/APISection'
import GithubExamples from '~/components/Sections/GithubExamples'
import ProductHeader from '~/components/Sections/ProductHeader'
import { TweetCard } from 'ui'
import { ThemeImage } from 'ui-patterns/ThemeImage'
import { TweetCard } from 'ui-patterns/TweetCard'
// install Swiper's Controller component
// SwiperCore.use([Controller])
@@ -1,4 +1,4 @@
import { Badge, IconCode, IconFastForward, IconGlobe, IconRefreshCcw, ThemeImage, cn } from 'ui'
import { Badge, IconCode, IconFastForward, IconGlobe, IconRefreshCcw, cn } from 'ui'
import UseCaseExamples from 'data/products/functions/usecase-examples'
import Solutions from 'data/Solutions'
import { NextSeo } from 'next-seo'
@@ -11,6 +11,7 @@ import SectionContainer from '~/components/Layouts/SectionContainer'
import ScrollableCodeBlock from '~/components/ScrollableCodeBlock'
import FunctionsUsecases from '~/components/Sections/FunctionsUsecases'
import ProductHeader from '~/components/Sections/ProductHeader'
import { ThemeImage } from 'ui-patterns/ThemeImage'
const featureBlocks = [
{
@@ -11,13 +11,14 @@ import { Dispatch, SetStateAction, useState } from 'react'
import remarkGfm from 'remark-gfm'
import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/swiper.min.css'
import { Admonition, Button, ExpandableVideo, IconChevronLeft, IconExternalLink } from 'ui'
import { Admonition, Button, IconChevronLeft, IconExternalLink } from 'ui'
import ImageModal from '~/components/ImageModal'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import supabase from '~/lib/supabaseMisc'
import { Partner } from '~/types/partners'
import Error404 from '../../404'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
/**
* Returns custom components so that the markdown converts to a nice looking html.
+1 -1
View File
@@ -1,6 +1,6 @@
import Layout from '../layouts/Layout'
import SectionContainer from '../components/Layouts/SectionContainer'
import { PrivacySettings } from 'ui'
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
export const meta = {
title: 'Privacy Policy | Supabase',
+1 -1
View File
@@ -1,6 +1,6 @@
import Layout from '../layouts/Layout'
import SectionContainer from '../components/Layouts/SectionContainer'
import { PrivacySettings } from 'ui'
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
export const meta = {
title: 'Privacy Policy | Supabase',
+2 -1
View File
@@ -1,4 +1,4 @@
import { Button, IconGrid, IconLayers, IconMenu, ThemeImage } from 'ui'
import { Button, IconGrid, IconLayers, IconMenu } from 'ui'
import ApiExamples from 'data/products/realtime/api-examples'
import AppExamples from 'data/products/realtime/app-examples'
import Solutions from 'data/Solutions'
@@ -13,6 +13,7 @@ import ProductHeader from '~/components/Sections/ProductHeader'
import RealtimeStyles from './Realtime.module.css'
import 'swiper/swiper.min.css'
import { ThemeImage } from 'ui-patterns/ThemeImage'
const Cursor = ({ className = '', color = 'none' }) => {
return (
+2 -1
View File
@@ -1,4 +1,4 @@
import { Button, IconArrowUpRight, IconShuffle, IconWifi, IconX, ThemeImage } from 'ui'
import { Button, IconArrowUpRight, IconShuffle, IconWifi, IconX } from 'ui'
import ApiExamples from 'data/products/storage/api-examples'
import DashboardViewData from 'data/products/storage/dashboard-carousel.json'
import StoragePermissionsData from 'data/products/storage/permissions-examples'
@@ -16,6 +16,7 @@ import SectionContainer from '~/components/Layouts/SectionContainer'
import ProductIcon from '~/components/ProductIcon'
import APISection from '~/components/Sections/APISection'
import ProductHeader from '~/components/Sections/ProductHeader'
import { ThemeImage } from 'ui-patterns/ThemeImage'
function StoragePage() {
// base path for images
+5 -5
View File
@@ -2,13 +2,13 @@ const config = require('config/tailwind.config')
module.exports = config({
content: [
'../../packages/ui/src/**/*.{tsx,ts,js}',
'./pages/**/*.{tsx,mdx}',
'./lib/mdx/mdxComponents.tsx',
'./_blog/*.mdx',
'./components/**/*.tsx',
'./layouts/**/*.tsx',
'./src/**/*.{ts,tsx,mdx}',
'./_blog/*.mdx',
'./lib/mdx/mdxComponents.tsx',
'./pages/**/*.{tsx,mdx}',
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/**/*.{tsx,ts,js}',
],
theme: {
extend: {
+28
View File
@@ -50,6 +50,7 @@
"react-flow": "^1.0.3",
"sql-formatter": "^13.1.0",
"ui": "*",
"ui-patterns": "*",
"valtio": "^1.12.0",
"zod": "^3.22.4"
},
@@ -515,6 +516,7 @@
"remark-gfm": "^3.0.1",
"shared-data": "*",
"ui": "*",
"ui-patterns": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"unist-util-visit": "^4.1.2",
@@ -844,6 +846,7 @@
"sql-formatter": "^12.2.0",
"sqlstring": "^2.3.2",
"ui": "*",
"ui-patterns": "*",
"uuid": "^9.0.1",
"valtio": "^1.11.2",
"yup": "^0.32.11",
@@ -1125,6 +1128,7 @@
"typed.js": "^2.0.16",
"typescript": "^5.2.2",
"ui": "*",
"ui-patterns": "*",
"use-debounce": "^7.0.1",
"vanilla-tilt": "1.7.0",
"yup": "^0.32.11"
@@ -35228,6 +35232,10 @@
"resolved": "packages/ui",
"link": true
},
"node_modules/ui-patterns": {
"resolved": "packages/ui-patterns",
"link": true
},
"node_modules/un-eval": {
"version": "1.2.0",
"license": "MIT"
@@ -37335,6 +37343,26 @@
"react-dom": "*"
}
},
"packages/ui-patterns": {
"license": "MIT",
"dependencies": {
"common": "*",
"lodash": "*",
"lucide-react": "*",
"next-themes": "*",
"react-hot-toast": "*",
"react-tooltip": "*",
"reactflow": "*",
"tsconfig": "*",
"ui": "*"
},
"devDependencies": {},
"peerDependencies": {
"next": "*",
"react": "*",
"react-dom": "*"
}
},
"packages/ui/node_modules/@radix-ui/react-alert-dialog": {
"version": "1.0.5",
"license": "MIT",
+136
View File
@@ -0,0 +1,136 @@
import {
LOCAL_STORAGE_KEYS,
handlePageTelemetry,
isBrowser,
useBreakpoint,
useTelemetryProps,
} from 'common'
import { noop } from 'lodash'
import Link from 'next/link'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import { Button } from 'ui'
interface ConsentToastProps {
onAccept: () => void
onOptOut: () => void
}
export const ConsentToast = ({ onAccept = noop, onOptOut = noop }: ConsentToastProps) => {
const isMobile = useBreakpoint(639)
return (
<div className="space-y-3 py-1 flex flex-col w-full">
<div>
<p className="text-foreground">
We only collect analytics essential to ensuring smooth operation of our services.{' '}
<Link
className="inline sm:hidden underline text-light"
target="_blank"
rel="noreferrer"
href="https://supabase.com/privacy"
>
Learn more
</Link>
</p>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
onClick={onAccept}
size={isMobile ? 'small' : 'tiny'}
block={isMobile}
>
Accept
</Button>
<Button
type={isMobile ? 'outline' : 'text'}
onClick={onOptOut}
size={isMobile ? 'small' : 'tiny'}
block={isMobile}
>
Opt out
</Button>
<Button asChild type="text" className="hidden sm:block text-light hover:text-foreground">
<Link target="_blank" rel="noreferrer" href="https://supabase.com/privacy">
Learn more
</Link>
</Button>
</div>
</div>
)
}
// Use with PortalToast from 'ui/src/layout/PortalToast'
export const useConsent = () => {
const { TELEMETRY_CONSENT } = LOCAL_STORAGE_KEYS
const consentToastId = useRef<string>()
const isClient = typeof window !== 'undefined'
if (!isClient) return {}
const telemetryProps = useTelemetryProps()
const [consentValue, setConsentValue] = useState<string | null>(
localStorage?.getItem(TELEMETRY_CONSENT)
)
const handleConsent = (value: 'true' | 'false') => {
if (!isClient) return
setConsentValue(value)
localStorage.setItem(TELEMETRY_CONSENT, value)
if (consentToastId.current) toast.dismiss(consentToastId.current)
if (value === 'true')
handlePageTelemetry(process.env.NEXT_PUBLIC_API_URL!, location.pathname, telemetryProps)
}
useEffect(() => {
const handleSetLocalStorage = () => {
if (localStorage?.getItem(TELEMETRY_CONSENT)) toast.dismiss(consentToastId.current)
}
window.addEventListener('storage', handleSetLocalStorage)
return window.removeEventListener('storage', () => null)
}, [])
useEffect(() => {
if (isClient && consentValue === null) {
consentToastId.current = toast(
<ConsentToast
onAccept={() => handleConsent('true')}
onOptOut={() => handleConsent('false')}
/>,
{
id: 'consent-toast',
position: 'bottom-right',
duration: Infinity,
className:
'!w-screen !-m-4 !border-t !rounded-none !max-w-none !bg-overlay !text sm:!m-0 sm:!rounded-lg sm:!w-auto sm:!max-w-[400px] sm:border',
}
)
}
}, [consentValue])
return { consentValue, setConsentValue, hasAcceptedConsent: consentValue === 'true' }
}
export const useConsentValue = (KEY_NAME: string) => {
if (!isBrowser) return {}
const telemetryProps = useTelemetryProps()
const [consentValue, setConsentValue] = useState<string | null>(localStorage?.getItem(KEY_NAME))
const handleConsent = (value: 'true' | 'false') => {
if (!isBrowser) return
setConsentValue(value)
localStorage.setItem(KEY_NAME, value)
window.dispatchEvent(new Event('storage'))
if (value === 'true')
handlePageTelemetry(process.env.NEXT_PUBLIC_API_URL!, location.pathname, telemetryProps)
}
return {
consentValue,
setConsentValue,
hasAccepted: consentValue === 'true',
handleConsent,
}
}
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { cn } from '../../lib/utils/cn'
import { cn } from 'ui'
interface CountdownStepProps {
value: string | number
@@ -8,7 +8,13 @@ interface CountdownWidgetProps {
showCard?: boolean
}
function CountdownWidget({ days, hours, minutes, seconds, showCard = true }: CountdownWidgetProps) {
export function CountdownWidget({
days,
hours,
minutes,
seconds,
showCard = true,
}: CountdownWidgetProps) {
const Colon = () => <span className="text-xs mx-px text-foreground-lighter">:</span>
const showItem = (item: string | undefined) => item !== undefined && item !== '0'
@@ -39,5 +45,3 @@ function CountdownWidget({ days, hours, minutes, seconds, showCard = true }: Cou
</div>
)
}
export default CountdownWidget
@@ -1,7 +1,7 @@
import { useTheme } from 'next-themes'
import Image from 'next/image'
import * as React from 'react'
import { cn } from '../../lib/utils/cn'
import { cn } from 'ui'
interface Props {
title: string
@@ -18,7 +18,7 @@ interface Props {
showIconBg?: boolean
}
const GlassPanel = ({
export const GlassPanel = ({
title,
span,
icon,
@@ -125,5 +125,3 @@ const GlassPanel = ({
</div>
)
}
export default GlassPanel
@@ -19,7 +19,7 @@ interface Props {
hideArrow?: boolean
}
const IconPanel = ({
export const IconPanel = ({
title,
tooltip,
icon,
@@ -74,8 +74,8 @@ const IconPanel = ({
title !== undefined
? `${title} Icon`
: tooltip !== undefined
? `${tooltip} Icon`
: 'Icon'
? `${tooltip} Icon`
: 'Icon'
}
/>
</IconContainer>
@@ -122,5 +122,3 @@ const IconPanel = ({
</>
)
}
export default IconPanel
@@ -1,9 +1,14 @@
import { PropsWithChildren, useEffect, useState } from 'react'
import Link from 'next/link'
import { Modal, Toggle, useConsentValue } from 'ui'
import { LOCAL_STORAGE_KEYS } from 'common'
import Link from 'next/link'
import { PropsWithChildren, useEffect, useState } from 'react'
import { Modal, Toggle } from 'ui'
const PrivacySettings = ({ children, ...props }: PropsWithChildren<{ className?: string }>) => {
import { useConsentValue } from '../ConsentToast'
export const PrivacySettings = ({
children,
...props
}: PropsWithChildren<{ className?: string }>) => {
const [isOpen, setIsOpen] = useState(false)
const { hasAccepted, handleConsent } = useConsentValue(LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT)
const [telemetryValue, setTelemetryValue] = useState(hasAccepted)
@@ -91,5 +96,3 @@ const PrivacySettings = ({ children, ...props }: PropsWithChildren<{ className?:
</>
)
}
export default PrivacySettings
+9
View File
@@ -0,0 +1,9 @@
# Supabase UI Patterns
This package should be used for components which are built using NPM libraries (`react-markdown`, `reactflow` for example),
which make no sense to be included in all apps. It should also be used for components which are constructed using various
components from the `ui` package.
This package does not have a barrel file - each component has to be imported from its index
(`ui-patterns/PrivacySettings`, for example). This is intentional so that adding a new component which is used only in
1 or 2 apps doesn't burden the rest of the apps.
@@ -132,4 +132,4 @@ const TableNode = ({ data, targetPosition, sourcePosition }: NodeProps<TableNode
)
}
export { TableNode, TABLE_NODE_WIDTH, TABLE_NODE_ROW_HEIGHT }
export { TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableNode }
@@ -1,8 +1,7 @@
import { useTheme } from 'next-themes'
import Image from 'next/image'
import { useEffect, useState } from 'react'
import { cn } from '../../lib/utils/cn'
import { cn } from 'ui'
export const ThemeImage = ({ src, ...props }: any) => {
const { resolvedTheme } = useTheme()
@@ -26,12 +25,10 @@ export const ThemeImage = ({ src, ...props }: any) => {
typeof src === 'string'
? src
: mounted && resolvedTheme?.includes('dark')
? src.dark
: src.light
? src.dark
: src.light
}
/>
</span>
)
}
export default ThemeImage
@@ -9,7 +9,6 @@ import {
DropdownMenuTrigger,
IconMoon,
IconSun,
Theme,
themes,
} from 'ui'
@@ -17,7 +16,7 @@ interface ThemeToggleProps {
forceDark?: boolean
}
const ThemeToggle = ({ forceDark = false }: ThemeToggleProps) => {
export const ThemeToggle = ({ forceDark = false }: ThemeToggleProps) => {
const { theme, setTheme } = useTheme()
const [open, setOpen] = useState(false)
@@ -44,7 +43,7 @@ const ThemeToggle = ({ forceDark = false }: ThemeToggleProps) => {
setTheme(value)
}}
>
{themes.map((theme: Theme) => (
{themes.map((theme) => (
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
@@ -55,5 +54,3 @@ const ThemeToggle = ({ forceDark = false }: ThemeToggleProps) => {
</DropdownMenu>
)
}
export default ThemeToggle
@@ -6,7 +6,7 @@ interface TweetCard {
img_url: string
}
function TweetCard(props: TweetCard) {
export function TweetCard(props: TweetCard) {
return (
<div
className="
@@ -51,4 +51,3 @@ function TweetCard(props: TweetCard) {
</div>
)
}
export default TweetCard
+14
View File
@@ -0,0 +1,14 @@
/**
* The components are listed here so that VsCode can find out about them and list them as import suggestions. Don't
* import directly from here.
*/
export * from './ConsentToast'
export * from './CountdownWidget'
export * from './ExpandableVideo'
export * from './GlassPanel'
export * from './IconPanel'
export * from './PrivacySettings'
export * from './SchemaTableNode'
export * from './ThemeImage'
export * from './ThemeToggle'
export * from './TweetCard'
+27
View File
@@ -0,0 +1,27 @@
{
"name": "ui-patterns",
"main": "./index.tsx",
"types": "./index.tsx",
"license": "MIT",
"sideEffects": false,
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"common": "*",
"lodash": "*",
"lucide-react": "*",
"next-themes": "*",
"react-hot-toast": "*",
"react-tooltip": "*",
"reactflow": "*",
"tsconfig": "*",
"ui": "*"
},
"devDependencies": {},
"peerDependencies": {
"next": "*",
"react": "*",
"react-dom": "*"
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "tsconfig/react-library.json",
"compilerOptions": {
"resolveJsonModule": true
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
+4
View File
@@ -0,0 +1,4 @@
declare module '*.css' {
export const styles: Record<string, string>
export default styles
}
+1 -22
View File
@@ -2,29 +2,21 @@
import * as React from 'react'
// Providers
export * from './src/providers'
// Markdown components
export * from './src/lib/Markdown'
// // GENERAL
// GENERAL
export * from './src/components/Admonition'
export * from './src/components/Button'
export * from './src/components/Icon'
export * from './src/components/Icon/IconContext'
export * from './src/components/GlassPanel'
export * from './src/components/IconPanel'
export * from './src/components/Icon/IconBackground'
export * from './src/components/ThemeImage'
// DISPLAYS
export * from './src/components/Card'
export * from './src/components/TweetCard'
export * from './src/components/Badge'
export * from './src/components/Tabs'
export * from './src/components/Alert'
@@ -41,10 +33,7 @@ export * from './src/components/NavMenu'
export * from './src/components/Modal'
export * from './src/components/SidePanel'
export * from './src/components/ConsentToast'
export * from './src/components/ContextMenu'
export * from './src/components/ExpandableVideo'
export * from './src/components/PrivacySettings'
export * from './src/components/PromoToast'
export * from './src/components/Popover'
export * from './src/layout/PortalToast'
@@ -71,9 +60,6 @@ export * from './src/components/Form'
// CMD+K
export * from './src/components/Command'
// Widgets
export * from './src/components/CountdownWidget'
// layout
export * from './src/components/LoadingLine'
@@ -87,11 +73,8 @@ export * from './src/layout/ai-icon-animation'
export * from './src/lib/utils'
// theme switcher
export * from './src/components/ThemeProvider/ThemeToggle'
export * from './src/components/ThemeProvider/themes'
// animated ui
// shadcn
export * from './src/components/shadcn/ui/dropdown-menu'
@@ -209,10 +192,6 @@ export { ScrollArea, ScrollBar } from './src/components/shadcn/ui/scroll-area'
export { Separator } from './src/components/shadcn/ui/separator'
// patterns
export * from './src/patterns/SchemaTableNode'
// links
export * from './src/components/TextLink'
@@ -2,7 +2,7 @@ import { CommandGroup } from 'cmdk'
import { useCommandMenu } from './CommandMenuProvider'
import ChildItem from './ChildItem'
import { useTheme } from 'next-themes'
import { Theme, themes } from 'ui'
import { Theme, themes } from '../ThemeProvider/themes'
const ThemeOptions = ({ isSubItem = false }) => {
const { setIsOpen } = useCommandMenu()
@@ -1,56 +0,0 @@
import { useBreakpoint } from 'common'
import { noop } from 'lodash'
import Link from 'next/link'
import { Button } from 'ui'
interface ConsentToastProps {
onAccept: () => void
onOptOut: () => void
}
const ConsentToast = ({ onAccept = noop, onOptOut = noop }: ConsentToastProps) => {
const isMobile = useBreakpoint(639)
return (
<div className="space-y-3 py-1 flex flex-col w-full">
<div>
<p className="text-foreground">
We only collect analytics essential to ensuring smooth operation of our services.{' '}
<Link
className="inline sm:hidden underline text-light"
target="_blank"
rel="noreferrer"
href="https://supabase.com/privacy"
>
Learn more
</Link>
</p>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
onClick={onAccept}
size={isMobile ? 'small' : 'tiny'}
block={isMobile}
>
Accept
</Button>
<Button
type={isMobile ? 'outline' : 'text'}
onClick={onOptOut}
size={isMobile ? 'small' : 'tiny'}
block={isMobile}
>
Opt out
</Button>
<Button asChild type="text" className="hidden sm:block text-light hover:text-foreground">
<Link target="_blank" rel="noreferrer" href="https://supabase.com/privacy">
Learn more
</Link>
</Button>
</div>
</div>
)
}
export default ConsentToast
@@ -1,3 +0,0 @@
import ConsentToast from './ConsentToast'
export default ConsentToast
@@ -1,35 +0,0 @@
import React from 'react'
import Countdown from 'react-countdown'
import { CountdownWidget } from 'ui'
const CountdownComponent = ({
date,
showCard = true,
}: {
date: string | number | Date
showCard?: boolean
}) => {
if (!date) return null
const renderer = ({ days, hours, minutes, seconds, completed }: any) => {
if (completed) {
// Render a completed state
return null
} else {
// Render countdown
return (
<CountdownWidget
days={days}
hours={hours}
minutes={minutes}
seconds={seconds}
showCard={showCard}
/>
)
}
}
return <Countdown date={new Date(date)} renderer={renderer} />
}
export default CountdownComponent
@@ -1 +0,0 @@
export { default as CountdownWidget } from './CountdownWidget'
@@ -1 +0,0 @@
export * from './ExpandableVideo'
@@ -1 +0,0 @@
export { default as GlassPanel } from './GlassPanel'
@@ -1 +0,0 @@
export { default as IconPanel } from './IconPanel'
@@ -1 +0,0 @@
export { default as PrivacySettings } from './PrivacySettings'
@@ -1 +0,0 @@
export { default as ThemeImage } from './ThemeImage'
@@ -1,7 +1,6 @@
import React, { createContext, useMemo } from 'react'
import { createContext } from 'react'
import defaultTheme from '../../lib/theme/defaultTheme'
import { mergeDeep } from './../../lib/utils'
// import useDarkMode from './utils/useDarkMode'
interface ThemeContextInterface {
@@ -15,52 +14,3 @@ export const ThemeContext = createContext<ThemeContextInterface>({
// mode: 'light',
// toggleMode: true,
})
interface Props extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode
/**
* Defines the styles used throughout the library
*/
theme?: object
/**
* Defines dark mode as the default theme
*/
// dark?: boolean
/**
* Allows the change of theme, reading user's preferences and saving them
*/
// usePreferences?: boolean
}
const ThemeProvider: React.FC<Props> = ({
children,
theme: customTheme,
// dark,
// usePreferences = false,
}) => {
const mergedTheme = mergeDeep(defaultTheme, customTheme)
// const [mode, setMode, toggleMode] = useDarkMode(usePreferences)
// useLayoutEffect(() => {
// if (dark) {
// if (setMode != null) {
// setMode('dark')
// }
// document.documentElement.classList.add(`dark`)
// }
// }, [dark])
const value = useMemo(
() => ({
theme: mergedTheme,
// mode,
// toggleMode,
}),
[]
)
// console.log('defaultTheme', defaultTheme)
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
}
export default ThemeProvider
@@ -1 +0,0 @@
export { default as ThemeProvider } from './ThemeProvider'
@@ -1 +0,0 @@
export { default as TweetCard } from './TweetCard'
@@ -1,5 +1,6 @@
import { Meta } from '@storybook/react'
import { BellRing, Check } from 'lucide-react'
import React from 'react'
import { cn } from '../../../lib/utils/cn'
import { Button } from '../ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '../ui/card'
@@ -4,11 +4,10 @@ import { zodResolver } from '@hookform/resolvers/zod'
import type { Meta, StoryObj } from '@storybook/react'
import { format } from 'date-fns'
import { CalendarIcon } from 'lucide-react'
import React from 'react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
import { cn } from '../../../lib/utils'
import { Popover, PopoverContent, PopoverTrigger } from '../ui//popover'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui//select'
import { Button } from '../ui/button'
import { Calendar } from '../ui/calendar'
import {
@@ -21,6 +20,8 @@ import {
FormMessage,
} from '../ui/form'
import { Input } from '../ui/input'
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
import { toast } from '../ui/use-toast'
const meta: Meta<typeof Form> = {
@@ -1,4 +1,5 @@
import { Meta } from '@storybook/react'
import React from 'react'
import { cn } from '../../../lib/utils/cn'
import { Slider } from '../ui/slider'
@@ -1,35 +0,0 @@
import React from 'react'
import Countdown from 'react-countdown'
import { CountdownWidget } from 'ui'
const CountdownComponent = ({
date,
showCard = true,
}: {
date: string | number | Date
showCard?: boolean
}) => {
if (!date) return null
const renderer = ({ days, hours, minutes, seconds, completed }: any) => {
if (completed) {
// Render a completed state
return null
} else {
// Render countdown
return (
<CountdownWidget
days={days}
hours={hours}
minutes={minutes}
seconds={seconds}
showCard={showCard}
/>
)
}
}
return <Countdown date={new Date(date)} renderer={renderer} />
}
export default CountdownComponent
@@ -1,224 +0,0 @@
export default function LWXBannerBg({ className }: { className?: string }) {
return (
<div className={className}>
<svg
width="672"
height="33"
viewBox="0 0 672 33"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="24.75" cy="27.75" r="0.75" fill="#FFFFF9" fillOpacity="0.3" />
<circle
cx="55.9193"
cy="24.9189"
r="0.75"
transform="rotate(165 55.9193 24.9189)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="68.9193"
cy="25.9189"
r="0.75"
transform="rotate(165 68.9193 25.9189)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="54.2182"
cy="14.4814"
r="0.75"
transform="rotate(165 54.2182 14.4814)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="30.9076"
cy="7.90677"
r="0.9"
transform="rotate(89.5676 30.9076 7.90677)"
fill="#FFFFF9"
/>
<circle
cx="61.9076"
cy="31.9068"
r="0.9"
transform="rotate(89.5676 61.9076 31.9068)"
fill="#FFFFF9"
/>
<circle
cx="9.37818"
cy="15.1301"
r="0.75"
transform="rotate(16.7993 9.37818 15.1301)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="39.1633"
cy="16.8098"
r="0.75"
transform="rotate(16.7993 39.1633 16.8098)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="71.3684"
cy="3.55194"
r="0.75"
transform="rotate(16.7993 71.3684 3.55194)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="46.7368"
cy="23.4534"
r="0.75"
transform="rotate(-178.201 46.7368 23.4534)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="58.8931"
cy="28.1682"
r="0.75"
transform="rotate(-178.201 58.8931 28.1682)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="48.1235"
cy="12.969"
r="0.75"
transform="rotate(-178.201 48.1235 12.969)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="1.11724"
cy="17.1177"
r="0.9"
transform="rotate(106.367 1.11724 17.1177)"
fill="#FFFFF9"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(-1 0 0 1 654.5 25)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(-1 0 0 1 625.5 18)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.965926 0.258819 0.258819 -0.965926 614.662 23.4492)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.965926 0.258819 0.258819 -0.965926 601.662 24.4492)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.965926 0.258819 0.258819 -0.965926 616.363 13.0117)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.9"
cy="0.9"
r="0.9"
transform="matrix(-0.0075466 0.999972 0.999972 0.0075466 639.699 5)"
fill="#FFFFF9"
/>
<circle
cx="0.9"
cy="0.9"
r="0.9"
transform="matrix(-0.0075466 0.999972 0.999972 0.0075466 608.699 29)"
fill="#FFFFF9"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(-0.957323 0.28902 0.28902 0.957323 662.623 12.1953)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(-0.957323 0.28902 0.28902 0.957323 632.838 13.875)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(-0.957323 0.28902 0.28902 0.957323 600.633 0.617188)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.999507 -0.0313981 -0.0313981 -0.999507 624.037 22.2266)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.999507 -0.0313981 -0.0313981 -0.999507 611.881 26.9414)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.75"
cy="0.75"
r="0.75"
transform="matrix(0.999507 -0.0313981 -0.0313981 -0.999507 622.65 11.7422)"
fill="#FFFFF9"
fillOpacity="0.3"
/>
<circle
cx="0.9"
cy="0.9"
r="0.9"
transform="matrix(0.281787 0.959477 0.959477 -0.281787 669.266 14.5078)"
fill="#FFFFF9"
/>
<circle
cx="0.9"
cy="0.9"
r="0.9"
transform="matrix(0.281787 0.959477 0.959477 -0.281787 602.266 8.50781)"
fill="#FFFFF9"
/>
</svg>
</div>
)
}
@@ -1,49 +0,0 @@
import { useRouter } from 'next/router'
import Image from 'next/image'
import { Button, cn } from 'ui'
export function LWXCountdownBanner() {
const { pathname } = useRouter()
const isHomePage = pathname === '/'
const isLaunchWeekPage = pathname === '/launch-week'
const isLaunchWeekSection = pathname.includes('launch-week')
const LWXLogo =
'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx/assets/lwx_logo.svg?t=2023-11-22T17%3A45%3A52.077Z'
if (isLaunchWeekPage || isHomePage) return null
return (
<div className="relative w-full h-14 p-2 flex items-center group justify-center text-foreground bg-alternative hover:bg-surface-200 dark:bg-[#020405] hover:dark:bg-[#05080a] transition-colors overflow-hidden">
<div className="relative z-10 flex items-center justify-center">
<div
className={cn(
'w-full flex gap-5 md:gap-10 items-center md:justify-center text-sm',
isLaunchWeekSection && '!justify-center'
)}
>
<div className="flex gap-1.5 items-center">
<p>Launch Week</p>
<Image
src={LWXLogo}
alt="Supabase Launch Week X Logo"
width={14}
height={14}
className="filter contrast-0 dark:contrast-100"
/>
</div>
<div className="hidden sm:block">11-15 Dec</div>
<Button
onClick={() => null}
size="tiny"
type="secondary"
className="px-2 py-1 !leading-none text-xs"
>
View announcements
</Button>
</div>
</div>
</div>
)
}
export default LWXCountdownBanner
@@ -1,22 +0,0 @@
import Image from 'next/image'
const VideoPreview = () => (
<div className="relative h-8 !aspect-video flex items-center justify-center rounded overflow-hidden border transition-colors">
<div className="absolute z-10 w-2.5 h-2.5 text-white opacity-100">
<svg viewBox="0 0 81 91" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M76.5621 37.998C82.3369 41.3321 82.3369 49.6673 76.5621 53.0014L13.2198 89.5721C7.44504 92.9062 0.226562 88.7386 0.226562 82.0704L0.226566 8.92901C0.226566 2.26085 7.44506 -1.90673 13.2199 1.42735L76.5621 37.998Z"
fill="currentColor"
/>
</svg>
</div>
<Image
src="https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8/assets/yt_d5.jpg?t=2023-08-11T09%3A31%3A39.412Z"
alt="Video thumbnail"
layout="fill"
objectFit="cover"
/>
</div>
)
export default VideoPreview
@@ -1 +0,0 @@
export { LWXCountdownBanner } from './LWXCountdownBanner'
+1 -2
View File
@@ -1,4 +1,3 @@
export { default as Announcement } from './Announcement'
export { default as LW8CountdownBanner } from './LW8CountdownBanner/LW8CountdownBanner'
export { default as LWXCountdownBanner } from './LWXCountdownBanner/LWXCountdownBanner'
export { default as IPV4Banner } from './IPV4Banner'
export { default as LW8CountdownBanner } from './LW8CountdownBanner/LW8CountdownBanner'
+1 -78
View File
@@ -1,7 +1,4 @@
import { LOCAL_STORAGE_KEYS, handlePageTelemetry, isBrowser, useTelemetryProps } from 'common'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import ConsentToast from '../components/ConsentToast/ConsentToast'
import { useEffect } from 'react'
/*
reference https://usehooks.com/useOnClickOutside/
@@ -38,77 +35,3 @@ function useOnClickOutside(ref: any, handler: Function) {
}
export { useOnClickOutside }
// Use with PortalToast from 'ui/src/layout/PortalToast'
export const useConsent = () => {
const { TELEMETRY_CONSENT } = LOCAL_STORAGE_KEYS
const consentToastId = useRef<string>()
const isClient = typeof window !== 'undefined'
if (!isClient) return {}
const telemetryProps = useTelemetryProps()
const [consentValue, setConsentValue] = useState<string | null>(
localStorage?.getItem(TELEMETRY_CONSENT)
)
const handleConsent = (value: 'true' | 'false') => {
if (!isClient) return
setConsentValue(value)
localStorage.setItem(TELEMETRY_CONSENT, value)
if (consentToastId.current) toast.dismiss(consentToastId.current)
if (value === 'true')
handlePageTelemetry(process.env.NEXT_PUBLIC_API_URL!, location.pathname, telemetryProps)
}
useEffect(() => {
const handleSetLocalStorage = () => {
if (localStorage?.getItem(TELEMETRY_CONSENT)) toast.dismiss(consentToastId.current)
}
window.addEventListener('storage', handleSetLocalStorage)
return window.removeEventListener('storage', () => null)
}, [])
useEffect(() => {
if (isClient && consentValue === null) {
consentToastId.current = toast(
<ConsentToast
onAccept={() => handleConsent('true')}
onOptOut={() => handleConsent('false')}
/>,
{
id: 'consent-toast',
position: 'bottom-right',
duration: Infinity,
className:
'!w-screen !-m-4 !border-t !rounded-none !max-w-none !bg-overlay !text sm:!m-0 sm:!rounded-lg sm:!w-auto sm:!max-w-[400px] sm:border',
}
)
}
}, [consentValue])
return { consentValue, setConsentValue, hasAcceptedConsent: consentValue === 'true' }
}
export const useConsentValue = (KEY_NAME: string) => {
if (!isBrowser) return {}
const telemetryProps = useTelemetryProps()
const [consentValue, setConsentValue] = useState<string | null>(localStorage?.getItem(KEY_NAME))
const handleConsent = (value: 'true' | 'false') => {
if (!isBrowser) return
setConsentValue(value)
localStorage.setItem(KEY_NAME, value)
window.dispatchEvent(new Event('storage'))
if (value === 'true')
handlePageTelemetry(process.env.NEXT_PUBLIC_API_URL!, location.pathname, telemetryProps)
}
return {
consentValue,
setConsentValue,
hasAccepted: consentValue === 'true',
handleConsent,
}
}
-44
View File
@@ -1,44 +0,0 @@
import * as React from 'react'
interface UseThemeProps {
isDarkMode?: boolean
toggleTheme: () => void
}
interface ThemeProviderProps {
children?: any
}
export const ThemeContext = React.createContext<UseThemeProps>({
isDarkMode: true,
toggleTheme: () => {},
})
export const useTheme = () => React.useContext(ThemeContext)
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
const [isDarkMode, setIsDarkMode] = React.useState(false)
React.useEffect(() => {
const key = localStorage.getItem('supabaseDarkMode')
// Default to dark mode if no preference config
setIsDarkMode(!key || key === 'true')
}, [])
const toggleTheme = () => {
setIsDarkMode(!isDarkMode)
}
return (
<>
<ThemeContext.Provider
value={{
isDarkMode,
toggleTheme,
}}
>
{children}
</ThemeContext.Provider>
</>
)
}
-1
View File
@@ -1 +0,0 @@
export * from './Theme'