feat: better layout

This commit is contained in:
TrèsAbhi committed 2024-08-06 14:05:16 -05:00
1 parent ca39b8e215
commit 4b2367699a
3 files changed
+464 -475

No files matched your search

+4 -3
View File
@@ -28,9 +28,10 @@ const robotoFlex = Roboto_Flex({
export default function RootLayout({ children }: RootLayoutProps) {
const pathname = usePathname();
const hideNav =
pathname.startsWith('/tools/embed/host') ||
pathname.startsWith('/tools/embed/customize');
// const hideNav =
// pathname.startsWith('/tools/embed/host') ||
// pathname.startsWith('/tools/embed/customize');
const hideNav = false;
const isRoot = pathname === '/';
return (
+456 -471
View File
@@ -1,9 +1,9 @@
import * as radixColors from '@radix-ui/colors';
import {
DragHandleDots2Icon,
EnterFullScreenIcon,
ExitFullScreenIcon,
BorderSolidIcon,
Cross1Icon,
ImageIcon,
SquareIcon,
} from '@radix-ui/react-icons';
import {
Box,
@@ -47,6 +47,9 @@ import {
radixTextWeights,
} from '../app/tools/embed/types';
import { toColorVar, toRadiusVar } from '../app/tools/embed/utilities';
import { imgur } from '../core/blitzkit/imgur';
import { NAVBAR_HEIGHT } from './Navbar';
import PageWrapper from './PageWrapper';
interface EmbedConfigInputsProps {
config: EmbedConfig;
@@ -61,10 +64,7 @@ export function EmbedConfigInputs({
setState,
children,
}: EmbedConfigInputsProps) {
const [expanded, setExpanded] = useState(true);
const [backgroundImage, setBackgroundImage] = useState(
'https://upload.wikimedia.org/wikipedia/commons/f/f6/Swiss_National_Park_131.JPG',
);
const [backgroundImage, setBackgroundImage] = useState(imgur('SO13zur'));
const mutateState = useCallback(
(recipe: (draft: ExtractEmbedConfigType<EmbedConfig>) => void) => {
setState(produce(recipe));
@@ -99,444 +99,53 @@ export function EmbedConfigInputs({
}, []);
return (
<Flex
width="100vw"
height="100vh"
align="center"
justify="center"
overflow="hidden"
style={{
background: `url(${backgroundImage}) center / cover no-repeat`,
}}
>
<Card
style={{ position: 'fixed', top: 32, right: 32, minWidth: 320 }}
ref={wrapper}
>
<Inset mb="4">
<Flex
align="center"
justify="center"
p="2"
mb={expanded ? '2' : '0'}
position="relative"
style={{
backgroundColor: 'var(--color-panel-translucent)',
cursor: 'grab',
}}
onPointerDown={(event) => {
event.preventDefault();
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}}
>
<Text color="gray" trim="end">
<DragHandleDots2Icon style={{ transform: 'rotate(90deg)' }} />
</Text>
<Box
position="absolute"
right="0"
top="50%"
pr="2"
style={{ transform: 'translateY(-25%)' }}
<PageWrapper padding="0" size={1028 + 640}>
<Flex>
<ScrollArea
scrollbars="vertical"
style={{ height: '100%', maxWidth: 320 }}
>
<Flex direction="column" gap="2" p="4">
<Button
variant="outline"
color="gray"
mb="6"
onClick={() => fileInput.current?.click()}
>
<IconButton
variant="ghost"
color="gray"
onClick={() => setExpanded((expanded) => !expanded)}
>
{expanded ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />}
</IconButton>
</Box>
</Flex>
</Inset>
<ImageIcon /> Upload test background
</Button>
{expanded && (
<ScrollArea
scrollbars="vertical"
style={{ height: '100%', maxHeight: 640 }}
>
<Flex direction="column" gap="2">
<Button
variant="outline"
color="gray"
mb="6"
onClick={() => fileInput.current?.click()}
>
<ImageIcon /> Upload test background
</Button>
{Object.entries(config).map(([key, setting]) => {
let control: ReactNode;
{Object.entries(config).map(([key, setting]) => {
let control: ReactNode;
switch (setting.type) {
case EmbedConfigType.Boolean: {
control = (
<Switch
checked={state[key] as boolean}
onCheckedChange={(checked) =>
mutateState((draft) => {
draft[key] = checked;
})
}
/>
);
break;
}
switch (setting.type) {
case EmbedConfigType.Boolean: {
control = (
<Switch
checked={state[key] as boolean}
onCheckedChange={(checked) =>
case EmbedConfigType.FullTextControl: {
control = (
<>
<Select.Root
size="1"
value={`${(state[key] as EmbedConfigItemType<EmbedConfigType.FullTextControl>).color}`}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = checked;
})
}
/>
);
break;
}
case EmbedConfigType.FullTextControl: {
control = (
<>
<Select.Root
size="1"
value={`${(state[key] as EmbedConfigItemType<EmbedConfigType.FullTextControl>).color}`}
onValueChange={(value) => {
mutateState((draft) => {
(
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).color = value as RadixTextColor;
});
}}
>
<Select.Trigger />
<Select.Content>
<Select.Item value="undefined">
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: 'white',
}}
/>{' '}
White
</Select.Item>
<Select.Item value="gray">
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: 'gray',
}}
/>{' '}
Gray
</Select.Item>
{Object.entries(radixColors)
.filter(
([key]) =>
!key.endsWith('A') &&
!key.endsWith('P3') &&
!key.endsWith('Dark') &&
!radixGrays.includes(key),
)
.map(([key]) => (
<Select.Item key={key} value={key}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: key as RadixColorBase,
variant: '9',
}),
}}
/>{' '}
{capitalize(key)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Select.Root
size="1"
value={
(
state[
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).size
}
onValueChange={(value) => {
mutateState((draft) => {
(
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).size = value as RadixSizeNo0;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(9, (index) => (
<Select.Item key={index} value={`${index + 1}`}>
Size {index + 1}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Select.Root
size="1"
value={
(
state[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).weight
}
onValueChange={(value) => {
mutateState((draft) => {
(
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).weight = value as RadixTextWeight;
});
}}
>
<Select.Trigger />
<Select.Content>
{radixTextWeights.map((weight) => (
<Select.Item key={weight} value={weight}>
{startCase(weight)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
</>
);
break;
}
case EmbedConfigType.Number: {
control = (
<TextField.Root
size="1"
style={{ width: 64 }}
type="number"
value={state[key] as number}
onChange={(event) => {
mutateState((draft) => {
draft[key] = (
event.target as HTMLInputElement
).valueAsNumber;
});
}}
>
{setting.unit && (
<TextField.Slot side="right">px</TextField.Slot>
)}
</TextField.Root>
);
break;
}
case EmbedConfigType.String: {
control = (
<TextField.Root
placeholder="Empty"
size="1"
style={{ width: 64 }}
value={state[key] as string}
onChange={(event) => {
mutateState((draft) => {
draft[key] = (
event.target as HTMLInputElement
).value;
});
}}
/>
);
break;
}
case EmbedConfigType.SizeNo0:
case EmbedConfigType.Size: {
control = (
<Select.Root
size="1"
value={state[key] as RadixSize}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixSize;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(10, (index) =>
setting.type === EmbedConfigType.SizeNo0 &&
index === 0 ? null : (
<Select.Item key={index} value={`${index}`}>
{index}
</Select.Item>
),
)}
</Select.Content>
</Select.Root>
);
break;
}
case EmbedConfigType.Radius: {
control = (
<Select.Root
size="1"
value={state[key] as RadixRadius}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixRadius;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(5, (index) => (
<Select.Item key={index} value={`${index}`}>
{index}
</Select.Item>
))}
<Select.Item value="full">Full</Select.Item>
</Select.Content>
</Select.Root>
);
break;
}
case EmbedConfigType.Color: {
control = (
<>
<Select.Root
size="1"
value={(state[key] as RadixColor).base}
onValueChange={(value) => {
mutateState((draft) => {
(draft[key] as RadixColor).base =
value as RadixColorBase;
});
}}
>
<Select.Trigger />
<Select.Content>
{Object.entries(radixColors)
.filter(
([key]) =>
!key.endsWith('A') &&
!key.endsWith('P3') &&
!key.endsWith('Dark'),
)
.map(([key]) => (
<Select.Item key={key} value={key}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: key as RadixColorBase,
variant: '9',
}),
}}
/>{' '}
{capitalize(key)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Select.Root
size="1"
value={(state[key] as RadixColor).variant}
onValueChange={(value) => {
mutateState((draft) => {
(draft[key] as RadixColor).variant =
value as RadixColorVariant;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(12, (index) => {
const variant =
`${index + 1}` as RadixColorVariantRaw;
return (
<>
<Select.Item key={key} value={variant}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: (state[key] as RadixColor).base,
variant,
}),
}}
/>{' '}
{variant}
</Select.Item>
<Select.Item key={key} value={`a${variant}`}>
<Box
style={{
display: 'inline-block',
overflow: 'hidden',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: (state[key] as RadixColor).base,
variant: `a${variant}`,
}),
}}
>
<Box
style={{
width: '50%',
height: '100%',
backgroundColor: toColorVar({
base: (state[key] as RadixColor)
.base,
variant,
}),
}}
/>
</Box>{' '}
{variant} translucent
</Select.Item>
</>
);
})}
</Select.Content>
</Select.Root>
</>
);
break;
}
case EmbedConfigType.TextColor: {
control = (
<Select.Root
size="1"
value={`${state[key]}`}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixTextColor;
).color = value as RadixTextColor;
});
}}
>
@@ -596,44 +205,420 @@ export function EmbedConfigInputs({
))}
</Select.Content>
</Select.Root>
);
break;
}
<Select.Root
size="1"
value={
(
state[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).size
}
onValueChange={(value) => {
mutateState((draft) => {
(
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).size = value as RadixSizeNo0;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(9, (index) => (
<Select.Item key={index} value={`${index + 1}`}>
Size {index + 1}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Select.Root
size="1"
value={
(
state[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).weight
}
onValueChange={(value) => {
mutateState((draft) => {
(
draft[
key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl>
).weight = value as RadixTextWeight;
});
}}
>
<Select.Trigger />
<Select.Content>
{radixTextWeights.map((weight) => (
<Select.Item key={weight} value={weight}>
{startCase(weight)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
</>
);
break;
}
return (
<Flex
direction="column"
gap="1"
mb="1"
pb={setting.pad ? '6' : undefined}
>
<Text color="gray" size="2">
{capitalize(startCase(key))}
case EmbedConfigType.Number: {
control = (
<TextField.Root
size="1"
style={{ width: 64 }}
type="number"
value={state[key] as number}
onChange={(event) => {
mutateState((draft) => {
draft[key] = (
event.target as HTMLInputElement
).valueAsNumber;
});
}}
>
{setting.unit && (
<TextField.Slot side="right">px</TextField.Slot>
)}
</TextField.Root>
);
break;
}
case EmbedConfigType.String: {
control = (
<TextField.Root
placeholder="Empty"
size="1"
style={{ width: 64 }}
value={state[key] as string}
onChange={(event) => {
mutateState((draft) => {
draft[key] = (event.target as HTMLInputElement).value;
});
}}
/>
);
break;
}
case EmbedConfigType.SizeNo0:
case EmbedConfigType.Size: {
control = (
<Select.Root
size="1"
value={state[key] as RadixSize}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixSize;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(10, (index) =>
setting.type === EmbedConfigType.SizeNo0 &&
index === 0 ? null : (
<Select.Item key={index} value={`${index}`}>
{index}
</Select.Item>
),
)}
</Select.Content>
</Select.Root>
);
break;
}
case EmbedConfigType.Radius: {
control = (
<Select.Root
size="1"
value={state[key] as RadixRadius}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixRadius;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(5, (index) => (
<Select.Item key={index} value={`${index}`}>
{index}
</Select.Item>
))}
<Select.Item value="full">Full</Select.Item>
</Select.Content>
</Select.Root>
);
break;
}
case EmbedConfigType.Color: {
control = (
<>
<Select.Root
size="1"
value={(state[key] as RadixColor).base}
onValueChange={(value) => {
mutateState((draft) => {
(draft[key] as RadixColor).base =
value as RadixColorBase;
});
}}
>
<Select.Trigger />
<Select.Content>
{Object.entries(radixColors)
.filter(
([key]) =>
!key.endsWith('A') &&
!key.endsWith('P3') &&
!key.endsWith('Dark'),
)
.map(([key]) => (
<Select.Item key={key} value={key}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: key as RadixColorBase,
variant: '9',
}),
}}
/>{' '}
{capitalize(key)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Select.Root
size="1"
value={(state[key] as RadixColor).variant}
onValueChange={(value) => {
mutateState((draft) => {
(draft[key] as RadixColor).variant =
value as RadixColorVariant;
});
}}
>
<Select.Trigger />
<Select.Content>
{times(12, (index) => {
const variant =
`${index + 1}` as RadixColorVariantRaw;
return (
<>
<Select.Item key={key} value={variant}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: (state[key] as RadixColor).base,
variant,
}),
}}
/>{' '}
{variant}
</Select.Item>
<Select.Item key={key} value={`a${variant}`}>
<Box
style={{
display: 'inline-block',
overflow: 'hidden',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: (state[key] as RadixColor).base,
variant: `a${variant}`,
}),
}}
>
<Box
style={{
width: '50%',
height: '100%',
backgroundColor: toColorVar({
base: (state[key] as RadixColor).base,
variant,
}),
}}
/>
</Box>{' '}
{variant} translucent
</Select.Item>
</>
);
})}
</Select.Content>
</Select.Root>
</>
);
break;
}
case EmbedConfigType.TextColor: {
control = (
<Select.Root
size="1"
value={`${state[key]}`}
onValueChange={(value) => {
mutateState((draft) => {
draft[key] = value as RadixTextColor;
});
}}
>
<Select.Trigger />
<Select.Content>
<Select.Item value="undefined">
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: 'white',
}}
/>{' '}
White
</Select.Item>
<Select.Item value="gray">
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: 'gray',
}}
/>{' '}
Gray
</Select.Item>
{Object.entries(radixColors)
.filter(
([key]) =>
!key.endsWith('A') &&
!key.endsWith('P3') &&
!key.endsWith('Dark') &&
!radixGrays.includes(key),
)
.map(([key]) => (
<Select.Item key={key} value={key}>
<Box
style={{
display: 'inline-block',
width: '1em',
height: '1em',
verticalAlign: 'text-top',
borderRadius: toRadiusVar('full'),
backgroundColor: toColorVar({
base: key as RadixColorBase,
variant: '9',
}),
}}
/>{' '}
{capitalize(key)}
</Select.Item>
))}
</Select.Content>
</Select.Root>
);
break;
}
}
return (
<Flex
direction="column"
gap="1"
mb="1"
pb={setting.pad ? '6' : undefined}
>
<Text color="gray" size="2">
{capitalize(startCase(key))}
</Text>
{control && <Flex gap="1">{control}</Flex>}
{!control && (
<Text color="red" size="1">
Immutable
</Text>
{control && <Flex gap="1">{control}</Flex>}
{!control && (
<Text color="red" size="1">
Immutable
</Text>
)}
</Flex>
);
})}
</Flex>
</ScrollArea>
)}
{!expanded && (
<Flex justify="center">
<Text size="2" color="gray">
Expand to configure
</Text>
)}
</Flex>
);
})}
</Flex>
)}
</Card>
</ScrollArea>
{children}
</Flex>
<Box
p="4"
flexGrow="1"
position="sticky"
top={`${NAVBAR_HEIGHT}px`}
height={`calc(100vh - ${NAVBAR_HEIGHT}px)`}
>
<Card style={{ height: '100%' }}>
<Flex direction="column" gap="2" height="100%">
<Inset style={{ borderRadius: 0 }}>
<Flex
justify="between"
p="2"
px="3"
style={{
backgroundColor: 'var(--color-panel-translucent)',
}}
>
<Text color="gray">Streaming software</Text>
<Flex align="center" gap="3">
<IconButton color="gray" disabled size="1" variant="ghost">
<BorderSolidIcon />
</IconButton>
<IconButton color="gray" disabled size="1" variant="ghost">
<SquareIcon />
</IconButton>
<IconButton color="gray" disabled size="1" variant="ghost">
<Cross1Icon />
</IconButton>
</Flex>
</Flex>
</Inset>
<Flex
mt="4"
flexGrow="1"
style={{
borderRadius: toRadiusVar('3'),
overflow: 'hidden',
background: `url(${backgroundImage}) center / cover no-repeat`,
}}
align="center"
justify="center"
>
{children}
</Flex>
</Flex>
</Card>
</Box>
</Flex>
</PageWrapper>
);
}
+4 -1
View File
@@ -1,5 +1,6 @@
import { Flex, FlexProps, Theme } from '@radix-ui/themes';
import { ComponentProps } from 'react';
import { RadixSize } from '../app/tools/embed/types';
type PageWrapperProps = FlexProps & {
color?: ComponentProps<typeof Theme>['accentColor'];
@@ -7,6 +8,7 @@ type PageWrapperProps = FlexProps & {
noMaxWidth?: boolean;
containerProps?: ComponentProps<typeof Theme>;
noFlex1?: boolean;
padding?: RadixSize;
};
export default function PageWrapper({
@@ -17,6 +19,7 @@ export default function PageWrapper({
noMaxWidth = false,
noFlex1 = false,
containerProps,
padding = '4',
...props
}: PageWrapperProps) {
return (
@@ -33,7 +36,7 @@ export default function PageWrapper({
<Flex
direction="column"
gap="4"
p="4"
p={padding}
style={{
width: '100%',
maxWidth: noMaxWidth ? undefined : size,