mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
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:
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,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$/,
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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,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 }) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IconCheckCircle, IconLifeBuoy, PrivacySettings } from 'ui'
|
||||
import { IconCheckCircle, IconLifeBuoy } from 'ui'
|
||||
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
|
||||
|
||||
export const primaryLinks = [
|
||||
{
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 [
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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'
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import { PrivacySettings } from 'ui'
|
||||
import { PrivacySettings } from 'ui-patterns/PrivacySettings'
|
||||
|
||||
const footerData = [
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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,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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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,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,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,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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Generated
+28
@@ -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",
|
||||
|
||||
@@ -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
-1
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn } from '../../lib/utils/cn'
|
||||
import { cn } from 'ui'
|
||||
|
||||
interface CountdownStepProps {
|
||||
value: string | number
|
||||
+7
-3
@@ -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
|
||||
File renamed without changes.
+2
-4
@@ -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
|
||||
+3
-5
@@ -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
|
||||
+9
-6
@@ -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
|
||||
@@ -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.
|
||||
+1
-1
@@ -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 }
|
||||
+3
-6
@@ -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
|
||||
+2
-5
@@ -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
|
||||
+1
-2
@@ -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
|
||||
@@ -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'
|
||||
@@ -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": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "tsconfig/react-library.json",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare module '*.css' {
|
||||
export const styles: Record<string, string>
|
||||
export default styles
|
||||
}
|
||||
+1
-22
@@ -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,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,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,
|
||||
}
|
||||
}
|
||||
@@ -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 +0,0 @@
|
||||
export * from './Theme'
|
||||
Reference in new issue
Block a user