feat: slider for width

This commit is contained in:
TrèsAbhi committed 2024-08-06 21:01:45 -05:00
1 parent b0dfa6949c
commit 1a2f440e11
3 files changed
+114 -104

No files matched your search

+13 -22
View File
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Dispatch, SetStateAction, use, useState } from 'react'; import { use } from 'react';
import { EmbedConfigInputs } from '../../../../../components/EmbedConfigInputs'; import { EmbedCustomize } from '../../../../../components/EmbedCustomize';
import { import {
TanksEmbedCard, TanksEmbedCard,
TanksEmbedWrapper, TanksEmbedWrapper,
@@ -12,12 +12,11 @@ import {
EmbedConfigType, EmbedConfigType,
ExtractEmbedConfigType, ExtractEmbedConfigType,
} from '../../types'; } from '../../types';
import { extractEmbedConfigDefaults } from '../../utilities';
const tankEmbedConfig = { const tankEmbedConfig = {
showTotal: { type: EmbedConfigType.Boolean, default: true }, showTotal: { type: EmbedConfigType.Boolean, default: true },
listWidth: { type: EmbedConfigType.Number, default: 320, unit: 'px' }, listWidth: { type: EmbedConfigType.Slider, default: 320, min: 128, max: 640 },
listGap: { type: EmbedConfigType.Size, default: '2' }, listGap: { type: EmbedConfigType.Size, default: '2' },
listMaxTanks: { type: EmbedConfigType.Number, default: 4, pad: true }, listMaxTanks: { type: EmbedConfigType.Number, default: 4, pad: true },
@@ -64,27 +63,19 @@ export type TanksEmbedState = ExtractEmbedConfigType<typeof tankEmbedConfig>;
export default function Page() { export default function Page() {
const tanks = use(tanksDefinitionsArray); const tanks = use(tanksDefinitionsArray);
const [state, setState] = useState(
extractEmbedConfigDefaults(tankEmbedConfig),
);
return ( return (
<EmbedConfigInputs <EmbedCustomize
config={tankEmbedConfig} config={tankEmbedConfig}
state={state} preview={(state) => (
setState={ <TanksEmbedWrapper state={state}>
setState as Dispatch< {state.showTotal && <TanksEmbedCard state={state} tank={null} />}
SetStateAction<ExtractEmbedConfigType<EmbedConfig>>
>
}
>
<TanksEmbedWrapper state={state}>
{state.showTotal && <TanksEmbedCard state={state} tank={null} />}
{tanks.slice(0, state.listMaxTanks).map((tank) => ( {tanks.slice(0, state.listMaxTanks).map((tank) => (
<TanksEmbedCard key={tank.id} tank={tank} state={state} /> <TanksEmbedCard key={tank.id} tank={tank} state={state} />
))} ))}
</TanksEmbedWrapper> </TanksEmbedWrapper>
</EmbedConfigInputs> )}
/>
); );
} }
+10 -3
View File
@@ -10,6 +10,7 @@ export enum EmbedConfigType {
String, String,
FullTextControl, FullTextControl,
Boolean, Boolean,
Slider,
} }
export const radixGrays = [ export const radixGrays = [
@@ -26,6 +27,12 @@ export const radixTextWeights = ['light', 'regular', 'medium', 'bold'] as const;
export type EmbedConfig = Record<string, EmbedConfigItem>; export type EmbedConfig = Record<string, EmbedConfigItem>;
export type EmbedConfigItem = ( export type EmbedConfigItem = (
| {
type: EmbedConfigType.Slider;
default: number;
min: number;
max: number;
}
| { | {
type: EmbedConfigType.Boolean; type: EmbedConfigType.Boolean;
default: boolean; default: boolean;
@@ -71,9 +78,6 @@ export type EmbedConfigItem = (
pad?: boolean; pad?: boolean;
}; };
export type EmbedConfigItemType<Type extends EmbedConfigType> =
(EmbedConfigItem & { type: Type })['default'];
export type RadixRadius = `${1 | 2 | 3 | 4}` | 'full'; export type RadixRadius = `${1 | 2 | 3 | 4}` | 'full';
export type RadixSize = `${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`; export type RadixSize = `${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
export type RadixTextWeight = (typeof radixTextWeights)[number]; export type RadixTextWeight = (typeof radixTextWeights)[number];
@@ -97,3 +101,6 @@ export type ExtractEmbedConfigType<Config extends EmbedConfig> = {
type: Config[Key]['type']; type: Config[Key]['type'];
} & EmbedConfigItem)['default']; } & EmbedConfigItem)['default'];
}; };
export type EmbedConfigItemType<Type extends EmbedConfigType> =
(EmbedConfigItem & { type: Type })
@@ -15,22 +15,14 @@ import {
Inset, Inset,
ScrollArea, ScrollArea,
Select, Select,
Separator, Slider,
Switch, Switch,
Text, Text,
TextField, TextField,
} from '@radix-ui/themes'; } from '@radix-ui/themes';
import { produce } from 'immer'; import { Immutable, produce } from 'immer';
import { capitalize, startCase, times } from 'lodash'; import { capitalize, startCase, times } from 'lodash';
import { import { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
Dispatch,
ReactNode,
SetStateAction,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { import {
EmbedConfig, EmbedConfig,
EmbedConfigItemType, EmbedConfigItemType,
@@ -48,27 +40,28 @@ import {
RadixTextWeight, RadixTextWeight,
radixTextWeights, radixTextWeights,
} from '../app/tools/embed/types'; } from '../app/tools/embed/types';
import { toColorVar, toRadiusVar } from '../app/tools/embed/utilities'; import {
extractEmbedConfigDefaults,
toColorVar,
toRadiusVar,
} from '../app/tools/embed/utilities';
import { imgur } from '../core/blitzkit/imgur'; import { imgur } from '../core/blitzkit/imgur';
import { NAVBAR_HEIGHT } from './Navbar'; import { NAVBAR_HEIGHT } from './Navbar';
import PageWrapper from './PageWrapper'; import PageWrapper from './PageWrapper';
interface EmbedConfigInputsProps { interface EmbedConfigInputsProps<Config extends EmbedConfig> {
config: EmbedConfig; config: Config;
state: ExtractEmbedConfigType<EmbedConfig>; preview: (state: ExtractEmbedConfigType<Config>) => ReactNode;
setState: Dispatch<SetStateAction<ExtractEmbedConfigType<EmbedConfig>>>;
children: ReactNode;
} }
export function EmbedConfigInputs({ export function EmbedCustomize<Config extends EmbedConfig>({
config, config,
state, preview,
setState, }: EmbedConfigInputsProps<Config>) {
children, const [state, setState] = useState(extractEmbedConfigDefaults(config));
}: EmbedConfigInputsProps) {
const [backgroundImage, setBackgroundImage] = useState(imgur('SO13zur')); const [backgroundImage, setBackgroundImage] = useState(imgur('SO13zur'));
const mutateState = useCallback( const mutateState = useCallback(
(recipe: (draft: ExtractEmbedConfigType<EmbedConfig>) => void) => { (recipe: (draft: Immutable<ExtractEmbedConfigType<Config>>) => void) => {
setState(produce(recipe)); setState(produce(recipe));
}, },
[], [],
@@ -107,17 +100,49 @@ export function EmbedConfigInputs({
<ImageIcon /> Upload test background <ImageIcon /> Upload test background
</Button> </Button>
{Object.entries(config).map(([key, setting]) => { {Object.entries(config).map(([keyUntyped, setting]) => {
const key = keyUntyped as keyof (
| Immutable<ExtractEmbedConfigType<Config>>
| ExtractEmbedConfigType<Config>
);
let control: ReactNode; let control: ReactNode;
switch (setting.type) { switch (setting.type) {
case EmbedConfigType.Slider: {
control = (
<Slider
value={[state[key] as number]}
min={
(
config[
key
] as EmbedConfigItemType<EmbedConfigType.Slider>
).min
}
max={
(
config[
key
] as EmbedConfigItemType<EmbedConfigType.Slider>
).max
}
onValueChange={([value]) =>
mutateState((draft) => {
(draft[key] as number) = value;
})
}
/>
);
break;
}
case EmbedConfigType.Boolean: { case EmbedConfigType.Boolean: {
control = ( control = (
<Switch <Switch
checked={state[key] as boolean} checked={state[key] as boolean}
onCheckedChange={(checked) => onCheckedChange={(checked) =>
mutateState((draft) => { mutateState((draft) => {
draft[key] = checked; (draft[key] as boolean) = checked;
}) })
} }
/> />
@@ -129,13 +154,13 @@ export function EmbedConfigInputs({
control = ( control = (
<> <>
<Select.Root <Select.Root
value={`${(state[key] as EmbedConfigItemType<EmbedConfigType.FullTextControl>).color}`} value={`${(state[key] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']).color}`}
onValueChange={(value) => { onValueChange={(value) => {
mutateState((draft) => { mutateState((draft) => {
( (
draft[ draft[
key key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl> ] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']
).color = value as RadixTextColor; ).color = value as RadixTextColor;
}); });
}} }}
@@ -201,7 +226,7 @@ export function EmbedConfigInputs({
( (
state[ state[
key key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl> ] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']
).size ).size
} }
onValueChange={(value) => { onValueChange={(value) => {
@@ -209,7 +234,7 @@ export function EmbedConfigInputs({
( (
draft[ draft[
key key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl> ] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']
).size = value as RadixSizeNo0; ).size = value as RadixSizeNo0;
}); });
}} }}
@@ -228,7 +253,7 @@ export function EmbedConfigInputs({
( (
state[ state[
key key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl> ] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']
).weight ).weight
} }
onValueChange={(value) => { onValueChange={(value) => {
@@ -236,7 +261,7 @@ export function EmbedConfigInputs({
( (
draft[ draft[
key key
] as EmbedConfigItemType<EmbedConfigType.FullTextControl> ] as EmbedConfigItemType<EmbedConfigType.FullTextControl>['default']
).weight = value as RadixTextWeight; ).weight = value as RadixTextWeight;
}); });
}} }}
@@ -263,7 +288,7 @@ export function EmbedConfigInputs({
value={state[key] as number} value={state[key] as number}
onChange={(event) => { onChange={(event) => {
mutateState((draft) => { mutateState((draft) => {
draft[key] = ( (draft[key] as number) = (
event.target as HTMLInputElement event.target as HTMLInputElement
).valueAsNumber; ).valueAsNumber;
}); });
@@ -285,7 +310,9 @@ export function EmbedConfigInputs({
value={state[key] as string} value={state[key] as string}
onChange={(event) => { onChange={(event) => {
mutateState((draft) => { mutateState((draft) => {
draft[key] = (event.target as HTMLInputElement).value; (draft[key] as string) = (
event.target as HTMLInputElement
).value;
}); });
}} }}
/> />
@@ -296,50 +323,38 @@ export function EmbedConfigInputs({
case EmbedConfigType.SizeNo0: case EmbedConfigType.SizeNo0:
case EmbedConfigType.Size: { case EmbedConfigType.Size: {
control = ( control = (
<Select.Root <Slider
value={state[key] as RadixSize} value={[Number(state[key])]}
onValueChange={(value) => { min={setting.type === EmbedConfigType.SizeNo0 ? 1 : 0}
max={9}
onValueChange={([value]) =>
mutateState((draft) => { mutateState((draft) => {
draft[key] = value as RadixSize; (draft[key] as RadixSize) = `${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; break;
} }
case EmbedConfigType.Radius: { case EmbedConfigType.Radius: {
control = ( control = (
<Select.Root <Slider
value={state[key] as RadixRadius} value={[
onValueChange={(value) => { (state[key] as RadixRadius) === 'full'
? 5
: Number(state[key]),
]}
min={0}
max={5}
onValueChange={([value]) =>
mutateState((draft) => { mutateState((draft) => {
draft[key] = value as RadixRadius; (draft[key] as RadixRadius) = (
}); value === 5 ? 'full' : `${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; break;
} }
@@ -403,7 +418,7 @@ export function EmbedConfigInputs({
return ( return (
<> <>
<Select.Item key={key} value={variant}> <Select.Item value={variant}>
<Box <Box
style={{ style={{
display: 'inline-block', display: 'inline-block',
@@ -419,7 +434,7 @@ export function EmbedConfigInputs({
/>{' '} />{' '}
{variant} {variant}
</Select.Item> </Select.Item>
<Select.Item key={key} value={`a${variant}`}> <Select.Item value={`a${variant}`}>
<Box <Box
style={{ style={{
display: 'inline-block', display: 'inline-block',
@@ -463,7 +478,8 @@ export function EmbedConfigInputs({
value={`${state[key]}`} value={`${state[key]}`}
onValueChange={(value) => { onValueChange={(value) => {
mutateState((draft) => { mutateState((draft) => {
draft[key] = value as RadixTextColor; (draft[key] as RadixTextColor) =
value as RadixTextColor;
}); });
}} }}
> >
@@ -535,11 +551,7 @@ export function EmbedConfigInputs({
mb="1" mb="1"
pb={setting.pad ? '6' : undefined} pb={setting.pad ? '6' : undefined}
> >
<Flex align="center" gap="4"> <Text>{capitalize(startCase(key as string))}</Text>
<Text>{capitalize(startCase(key))}</Text>
<Separator style={{ flex: 1 }} />
</Flex>
{control && <Flex gap="1">{control}</Flex>} {control && <Flex gap="1">{control}</Flex>}
{!control && <Text color="red">Immutable</Text>} {!control && <Text color="red">Immutable</Text>}
@@ -567,7 +579,7 @@ export function EmbedConfigInputs({
backgroundColor: 'var(--color-panel-translucent)', backgroundColor: 'var(--color-panel-translucent)',
}} }}
> >
<Text color="gray">Streaming software</Text> <Text color="gray">Example preview</Text>
<Flex align="center" gap="3"> <Flex align="center" gap="3">
<IconButton color="gray" disabled size="1" variant="ghost"> <IconButton color="gray" disabled size="1" variant="ghost">
@@ -594,7 +606,7 @@ export function EmbedConfigInputs({
align="center" align="center"
justify="center" justify="center"
> >
{children} {preview(state)}
</Flex> </Flex>
</Flex> </Flex>
</Card> </Card>