mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: slider for width
This commit is contained in:
1 parent
b0dfa6949c
commit
1a2f440e11
3 files changed
+114
-104
No files matched your search
@@ -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,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>
|
||||||
Reference in new issue
Block a user