Skip to content

Commit

Permalink
fix: frame issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagebakers committed Dec 11, 2023
1 parent 116ec45 commit 94d970d
Show file tree
Hide file tree
Showing 21 changed files with 330 additions and 111 deletions.
5 changes: 5 additions & 0 deletions .changeset/good-ties-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@saas-ui/file-upload': minor
---

Add getRootNode prop to support shadowdom and frames
5 changes: 5 additions & 0 deletions .changeset/late-suns-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@saas-ui/theme-glass': patch
---

Added neutral color scheme support for buttons
5 changes: 5 additions & 0 deletions .changeset/perfect-chairs-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@saas-ui/core': patch
---

Export SaasProviderProps
11 changes: 8 additions & 3 deletions apps/website/src/components/blocks/canvas-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import { ColorControl } from './color-control'
import { UiComponent } from '../../data/blocks'
import { useAuth } from '@saas-ui/auth'
import { useRouter } from 'next/router'
import { ThemeControl } from './theme-control'

export interface CanvasHeaderProps
extends UiComponent,
React.ComponentPropsWithoutRef<'div'> {
state: string
onStateChange(state: string): void
onPrimaryColorChange(color: string): void
onThemeChange(theme: string): void
theme: string
primaryColor: string
excludeExternal?: boolean
zIndex?: number
Expand All @@ -35,6 +38,8 @@ export function CanvasHeader({
onStateChange,
primaryColor,
onPrimaryColorChange,
theme,
onThemeChange,
excludeExternal = false,
zIndex,
...rest
Expand Down Expand Up @@ -80,9 +85,9 @@ export function CanvasHeader({
</HStack>

<HStack>
{attributes.withColor && (
<ColorControl onChange={onPrimaryColorChange} value={primaryColor} />
)}
<ColorControl onChange={onPrimaryColorChange} value={primaryColor} />

<ThemeControl onChange={onThemeChange} value={theme} />

{isUnlocked ? (
<ButtonGroup isAttached>
Expand Down
76 changes: 56 additions & 20 deletions apps/website/src/components/blocks/color-control.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react'
import React, { useState } from 'react'
import { FiPenTool, FiCheck } from 'react-icons/fi'
import {
Box,
Expand All @@ -7,53 +7,89 @@ import {
PopoverTrigger,
PopoverContent,
useTheme,
IconButton,
Badge,
} from '@chakra-ui/react'
import { LuCheck, LuPenTool } from 'react-icons/lu'

interface ColorControlProps {
onChange(color: string): void
value: string
}

const ignore = [
'black',
'white',
'gray',
'transparent',
'current',
'code',
'linkedin',
'facebook',
'messenger',
'whatsapp',
'twitter',
'telegram',
]

export function ColorControl({ onChange, value }: ColorControlProps) {
const [opened, setOpened] = useState(false)
const theme = useTheme()
const colors = Object.keys(theme.colors).map((color) => ({
swatch: theme.colors[color][6],
color,
}))

// @todo remove this hack to prevent hydration errors
const initializedRef = React.useRef(false)
React.useEffect(() => {
if (!initializedRef.current) {
initializedRef.current = true
}
}, [])

if (!initializedRef.current) {
return null
}

const colors = Object.keys(theme.colors)
.filter((color) => !color.match('Alpha') && !ignore.includes(color))
.map((color) => ({
swatch: theme.colors[color][500],
color,
}))

const swatches = colors.map(({ color, swatch }) => (
<Box
as="button"
type="button"
<IconButton
aria-label={color}
onClick={() => onChange(color)}
isRound
size="xs"
key={color}
color={swatch}
bg={swatch}
style={{ color: theme.white, cursor: 'pointer' }}
>
{value === color && <FiCheck size={10} />}
</Box>
{value === color && <LuCheck size="1.2em" />}
</IconButton>
))

return (
<Popover
isOpen={opened}
onClose={() => setOpened(false)}
placement="bottom-end"
isLazy
>
<PopoverTrigger>
<Box
as="button"
type="button"
color={theme.colors[value][6]}
<IconButton
aria-label="Change primary color"
icon={
<Badge rounded="full" boxSize="3" bg={theme.colors[value][500]} />
}
variant="tertiary"
onClick={() => setOpened((o) => !o)}
style={{ display: 'block', cursor: 'pointer' }}
>
<FiPenTool style={{ width: 14, height: 14 }} color="#fff" />
</Box>
/>
</PopoverTrigger>
<PopoverContent>
<Stack gap="xs">{swatches}</Stack>
<Stack gap="2" flexDirection="row" flexWrap="wrap" p="2">
{swatches}
</Stack>
</PopoverContent>
</Popover>
)
Expand Down
54 changes: 49 additions & 5 deletions apps/website/src/components/blocks/component-canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import React, { useState } from 'react'
import { Box, Card, CardBody, Stack } from '@chakra-ui/react'
import { Box, Card, CardBody, Stack, extendTheme } from '@chakra-ui/react'
import * as UiComponents from '../../../../../packages/pro/saas-ui/templates'
import { UiComponent } from '../../data/blocks'
import { ComponentPreview } from './component-preview'
import { CanvasHeader } from './canvas-header'
import { CodeTabs } from './code-tabs'
import { useFetch } from 'use-http'
import { useCurrentUser } from '@saas-ui/auth'
import { LoadingOverlay, LoadingSpinner, SaasProvider } from '@saas-ui/react'
import {
LoadingOverlay,
LoadingSpinner,
SaasProvider,
useLocalStorage,
} from '@saas-ui/react'
import { User } from '@supabase/supabase-js'

import { theme } from '@saas-ui-pro/react'
import { theme as glassTheme } from '@saas-ui-pro/theme-glass'

const LinkStub = (props: any) => <Box {...props} />

const themes = {
'saas-ui': theme,
glass: glassTheme,
}

import { ChakraFrame } from '../code-panel/chakra-frame'

export function ComponentCanvas(props: UiComponent & { zIndex: number }) {
const user = useCurrentUser<User>()
const [state, setState] = useState('preview')
const [primaryColor, setPrimaryColor] = useState('blue')
const [primaryColor, setPrimaryColor] = useLocalStorage(
'theme.primaryColor',
'primary'
)
const [themeId, setTheme] = useLocalStorage('theme.id', 'saas-ui')
const [error, setError] = useState(false)
const Component: any =
UiComponents[props.component as keyof typeof UiComponents]
Expand Down Expand Up @@ -47,6 +65,19 @@ export function ComponentCanvas(props: UiComponent & { zIndex: number }) {
}, [isUnlocked])

const [frameHeight, setFrameHeight] = useState<string | undefined>()
const frameRef = React.useRef<HTMLIFrameElement | null>(null)
const containerRef = React.useRef<HTMLBodyElement | null>(null)

const selectedTheme = React.useMemo(() => {
return extendTheme(
{
colors: {
primary: themes[themeId].colors[primaryColor],
},
},
themes[themeId]
)
}, [themeId, primaryColor])

return (
<Box overflow="hidden" mb="20">
Expand All @@ -56,6 +87,8 @@ export function ComponentCanvas(props: UiComponent & { zIndex: number }) {
primaryColor={primaryColor}
onStateChange={setState}
onPrimaryColorChange={setPrimaryColor}
onThemeChange={setTheme}
theme={themeId}
/>

<Card rounded="xl" overflow="hidden" mb="20">
Expand All @@ -69,9 +102,20 @@ export function ComponentCanvas(props: UiComponent & { zIndex: number }) {
justifyContent="stretch"
fontSize="md"
>
<ChakraFrame onHeightChange={(height) => setFrameHeight(height)}>
<ChakraFrame
frameRef={(el) => {
frameRef.current = el
}}
onHeightChange={(height) => setFrameHeight(String(height))}
theme={selectedTheme}
>
<ComponentPreview canvas={props.attributes.canvas}>
<Component {...props.attributes.props} />
<Component
{...props.attributes.props}
getRootNode={() =>
frameRef.current?.contentWindow?.document
}
/>
</ComponentPreview>
</ChakraFrame>
</Stack>
Expand Down
24 changes: 9 additions & 15 deletions apps/website/src/components/blocks/component-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,19 @@ interface ComponentPreviewProps {
withSpacing?: boolean
}

export function ComponentPreview({
children,
canvas,
withSpacing = false,
}: ComponentPreviewProps) {
if (canvas?.center) {
return (
<Box maxW={canvas?.maxWidth ?? 'container.lg'} margin="0 auto" p="12">
{children}
</Box>
)
}
export function ComponentPreview({ children, canvas }: ComponentPreviewProps) {
return (
<Box
style={{
paddingTop: canvas?.maxWidth && withSpacing ? 4 : 0,
maxWidth: canvas?.maxWidth ? canvas.maxWidth : '100%',
sx={{
padding: canvas?.center ? 12 : 0,
maxWidth: canvas?.maxWidth
? canvas.maxWidth
: canvas?.center
? 'container.lg'
: '100%',
marginLeft: canvas?.center ? 'auto' : 'unset',
marginRight: canvas?.center ? 'auto' : 'unset',
height: canvas?.height ?? 'auto',
}}
>
{children}
Expand Down
49 changes: 49 additions & 0 deletions apps/website/src/components/blocks/theme-control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useState } from 'react'
import {
Button,
Menu,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
} from '@chakra-ui/react'

import { LuPaintbrush } from 'react-icons/lu'

interface ColorControlProps {
onChange(color: string): void
value: string
}

const themes: Record<string, string> = {
'saas-ui': 'Saas UI',
glass: 'Glass',
}

export function ThemeControl({ onChange, value }: ColorControlProps) {
// @todo remove this hack to prevent hydration errors
const initializedRef = React.useRef(false)
React.useEffect(() => {
if (!initializedRef.current) {
initializedRef.current = true
}
}, [])

if (!initializedRef.current) {
return null
}

return (
<Menu>
<MenuButton as={Button} leftIcon={<LuPaintbrush />} variant="tertiary">
{themes[value]}
</MenuButton>
<MenuList>
<MenuOptionGroup value={value} onChange={onChange} type="radio">
<MenuItemOption value="saas-ui">Saas UI (default)</MenuItemOption>
<MenuItemOption value="glass">Glass</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
)
}
Loading

0 comments on commit 94d970d

Please sign in to comment.