mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: recover embed code
This commit is contained in:
1 parent
cee082ab67
commit
9daab37ba1
16 files changed
+746
No files matched your search
@@ -0,0 +1,19 @@
|
|||||||
|
import { Switch } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function Boolean({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
variant="soft"
|
||||||
|
checked={EmbedState.use((state) => state[configKey] as boolean)}
|
||||||
|
onCheckedChange={() => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = !draft[configKey];
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
import { ColorControllerRaw } from './ColorControllerRaw';
|
||||||
|
|
||||||
|
export function Color({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
const color = EmbedState.use((state) => state[configKey] as string);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColorControllerRaw
|
||||||
|
value={color}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Slider } from '@radix-ui/themes';
|
||||||
|
|
||||||
|
interface ColorControllerRawProps {
|
||||||
|
value: string;
|
||||||
|
onValueChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ColorControllerRaw({
|
||||||
|
value,
|
||||||
|
onValueChange,
|
||||||
|
}: ColorControllerRawProps) {
|
||||||
|
const tintString = value.slice(0, 7);
|
||||||
|
const alphaString = value.slice(7, 9);
|
||||||
|
const alpha = alphaString.length === 0 ? 255 : parseInt(alphaString, 16);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={value.slice(0, 7)}
|
||||||
|
onChange={(event) => {
|
||||||
|
let alpha = value.slice(7, 9);
|
||||||
|
if (alpha.length === 0) alpha = 'ff';
|
||||||
|
onValueChange(`${event.target.value}${alpha}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Slider
|
||||||
|
variant="soft"
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
value={[alpha]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
onValueChange(`${tintString}${value.toString(16)}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Select } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
import type {
|
||||||
|
EmbedConfigItemType,
|
||||||
|
EmbedItemType,
|
||||||
|
} from '../../stores/embedState/constants';
|
||||||
|
|
||||||
|
export function Enum({
|
||||||
|
configKey,
|
||||||
|
config,
|
||||||
|
}: EmbedPreviewControllerProps & {
|
||||||
|
config: EmbedConfigItemType<EmbedItemType.Enum>;
|
||||||
|
}) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select.Root
|
||||||
|
value={EmbedState.use((state) => state[configKey] as string)}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Trigger />
|
||||||
|
<Select.Content>
|
||||||
|
{config.options.map((option) => (
|
||||||
|
<Select.Item key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</Select.Item>
|
||||||
|
))}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
Tooltip,
|
||||||
|
} from '@radix-ui/themes';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function Import() {
|
||||||
|
const importInput = useRef<HTMLInputElement>(null);
|
||||||
|
const [showInvalidURLPrompt, setShowInvalidURLPrompt] = useState(false);
|
||||||
|
const [showConfirmation, setConfirmation] = useState(false);
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Heading>Import</Heading>
|
||||||
|
<Text size="2" color="gray" mb="2">
|
||||||
|
Paste your current URL if you have one already to import your settings
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<AlertDialog.Root
|
||||||
|
open={showInvalidURLPrompt}
|
||||||
|
onOpenChange={setShowInvalidURLPrompt}
|
||||||
|
>
|
||||||
|
<AlertDialog.Content>
|
||||||
|
<AlertDialog.Title>Invalid URL</AlertDialog.Title>
|
||||||
|
<AlertDialog.Description>
|
||||||
|
Try pasting the URL that BlitzKit previously generated, you silly
|
||||||
|
goose!
|
||||||
|
</AlertDialog.Description>
|
||||||
|
|
||||||
|
<Flex gap="3" mt="4" justify="end">
|
||||||
|
<AlertDialog.Cancel>
|
||||||
|
<Button>Okay, my bad</Button>
|
||||||
|
</AlertDialog.Cancel>
|
||||||
|
</Flex>
|
||||||
|
</AlertDialog.Content>
|
||||||
|
</AlertDialog.Root>
|
||||||
|
|
||||||
|
<Flex gap="2" mb="6">
|
||||||
|
<TextField.Root
|
||||||
|
ref={importInput}
|
||||||
|
placeholder="Paste URL..."
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
<Tooltip content="Applied!" open={showConfirmation}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!importInput.current) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(importInput.current.value);
|
||||||
|
const state = url.searchParams.get('state');
|
||||||
|
if (state === null) throw undefined;
|
||||||
|
const json = JSON.parse(state);
|
||||||
|
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
Object.assign(draft, json);
|
||||||
|
});
|
||||||
|
|
||||||
|
importInput.current.value = '';
|
||||||
|
setConfirmation(true);
|
||||||
|
setTimeout(() => setConfirmation(false), 2000);
|
||||||
|
} catch {
|
||||||
|
setShowInvalidURLPrompt(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { keyframes, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
const fadeOutBorder = keyframes({
|
||||||
|
from: {
|
||||||
|
outline: '1rem solid #ff0000ff',
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
outline: '1rem solid #ff000000',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const wrapper = style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const animated = style({
|
||||||
|
animation: `${fadeOutBorder} 0.5s linear`,
|
||||||
|
|
||||||
|
':hover': {
|
||||||
|
outline: '1rem solid #ff000080',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Box } from '@radix-ui/themes';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { previews } from '../../../constants/embeds';
|
||||||
|
import { useEmbedStateCurry } from '../../../stores/embedState/utilities';
|
||||||
|
import * as styles from './index.css';
|
||||||
|
|
||||||
|
interface PreviewWrapperProps {
|
||||||
|
name: keyof typeof previews;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PreviewWrapper({ name }: PreviewWrapperProps) {
|
||||||
|
const { useState } = useEmbedStateCurry();
|
||||||
|
const width = useState('width');
|
||||||
|
const height = useState('height');
|
||||||
|
const Preview = previews[name];
|
||||||
|
const wrapper = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!wrapper.current) return;
|
||||||
|
|
||||||
|
wrapper.current.classList.remove(styles.animated);
|
||||||
|
void wrapper.current.offsetWidth;
|
||||||
|
wrapper.current.classList.add(styles.animated);
|
||||||
|
}, [width, height]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
ref={wrapper}
|
||||||
|
className={styles.wrapper}
|
||||||
|
width={`${width}px`}
|
||||||
|
height={`${height}px`}
|
||||||
|
>
|
||||||
|
<Preview />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Slider } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState, type RadixRadius } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function Radius({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
variant="soft"
|
||||||
|
min={0}
|
||||||
|
max={5}
|
||||||
|
value={[
|
||||||
|
EmbedState.use((state) => {
|
||||||
|
const radius = state[configKey] as RadixRadius;
|
||||||
|
return radius === 'full' ? 5 : parseInt(radius);
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = value === 5 ? 'full' : `${value}`;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
import type {
|
||||||
|
EmbedConfigItemType,
|
||||||
|
EmbedItemType,
|
||||||
|
} from '../../stores/embedState/constants';
|
||||||
|
import { ColorControllerRaw } from './ColorControllerRaw';
|
||||||
|
|
||||||
|
export function RichText({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
const state = EmbedState.use(
|
||||||
|
(state) =>
|
||||||
|
state[
|
||||||
|
configKey
|
||||||
|
] as EmbedConfigItemType<EmbedItemType.RichText>['default'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColorControllerRaw
|
||||||
|
value={state.color}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
(
|
||||||
|
draft[
|
||||||
|
configKey
|
||||||
|
] as EmbedConfigItemType<EmbedItemType.RichText>['default']
|
||||||
|
).color = value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Slider } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState, type RadixSize } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function Size({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
variant="soft"
|
||||||
|
min={0}
|
||||||
|
max={9}
|
||||||
|
value={[
|
||||||
|
EmbedState.use((state) => parseInt(state[configKey] as RadixSize)),
|
||||||
|
]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = `${value}`;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Slider } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState, type RadixSizeWithout0 } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function SizeWithout0({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
variant="soft"
|
||||||
|
min={1}
|
||||||
|
max={9}
|
||||||
|
value={[
|
||||||
|
EmbedState.use((state) =>
|
||||||
|
parseInt(state[configKey] as RadixSizeWithout0),
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = `${value}`;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Slider as RadixSlider } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState, type RadixSize } from '../../stores/embedState';
|
||||||
|
import type {
|
||||||
|
EmbedConfigItemType,
|
||||||
|
EmbedItemType,
|
||||||
|
} from '../../stores/embedState/constants';
|
||||||
|
|
||||||
|
export function Slider({
|
||||||
|
configKey,
|
||||||
|
config,
|
||||||
|
}: EmbedPreviewControllerProps & {
|
||||||
|
config: EmbedConfigItemType<EmbedItemType.Slider>;
|
||||||
|
}) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RadixSlider
|
||||||
|
variant="soft"
|
||||||
|
min={config.min}
|
||||||
|
max={config.max}
|
||||||
|
value={[
|
||||||
|
EmbedState.use((state) => parseInt(state[configKey] as RadixSize)),
|
||||||
|
]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { TextField } from '@radix-ui/themes';
|
||||||
|
import type { EmbedPreviewControllerProps } from '../../pages/tools/embed/[embed]/_index';
|
||||||
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
|
||||||
|
export function TextController({ configKey }: EmbedPreviewControllerProps) {
|
||||||
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField.Root
|
||||||
|
placeholder="Empty"
|
||||||
|
value={EmbedState.use((state) => state[configKey] as string)}
|
||||||
|
onChange={(event) => {
|
||||||
|
mutateEmbedState((draft) => {
|
||||||
|
draft[configKey] = event.target.value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
import { imgur } from '@blitzkit/core';
|
||||||
|
import {
|
||||||
|
CodeIcon,
|
||||||
|
HeightIcon,
|
||||||
|
ImageIcon,
|
||||||
|
Link2Icon,
|
||||||
|
ResetIcon,
|
||||||
|
TimerIcon,
|
||||||
|
WidthIcon,
|
||||||
|
} from '@radix-ui/react-icons';
|
||||||
|
import { Box, Button, Flex, Heading, ScrollArea, Text } from '@radix-ui/themes';
|
||||||
|
import { capitalize, startCase } from 'lodash-es';
|
||||||
|
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
||||||
|
import { stringify } from 'urlon';
|
||||||
|
import { CopyButton } from '../../../../components/CopyButton';
|
||||||
|
import { Boolean } from '../../../../components/Embeds/Boolean';
|
||||||
|
import { Color } from '../../../../components/Embeds/Color';
|
||||||
|
import { Enum } from '../../../../components/Embeds/Enum';
|
||||||
|
import { Import } from '../../../../components/Embeds/Import';
|
||||||
|
import { PreviewWrapper } from '../../../../components/Embeds/PreviewWrapper';
|
||||||
|
import { Radius } from '../../../../components/Embeds/Radius';
|
||||||
|
import { RichText } from '../../../../components/Embeds/RichText';
|
||||||
|
import { Size } from '../../../../components/Embeds/Size';
|
||||||
|
import { SizeWithout0 } from '../../../../components/Embeds/SizeWithout0';
|
||||||
|
import { Slider } from '../../../../components/Embeds/Slider';
|
||||||
|
import { TextController } from '../../../../components/Embeds/TextController';
|
||||||
|
import { PageWrapper } from '../../../../components/PageWrapper';
|
||||||
|
import {
|
||||||
|
configurations,
|
||||||
|
extractEmbedConfigDefaults,
|
||||||
|
} from '../../../../constants/embeds';
|
||||||
|
import { NAVBAR_HEIGHT } from '../../../../constants/navbar';
|
||||||
|
import { App } from '../../../../stores/app';
|
||||||
|
import {
|
||||||
|
EmbedState,
|
||||||
|
type EmbedConfig,
|
||||||
|
type EmbedStateStore,
|
||||||
|
} from '../../../../stores/embedState';
|
||||||
|
import { EmbedItemType } from '../../../../stores/embedState/constants';
|
||||||
|
|
||||||
|
export interface EmbedPreviewControllerProps {
|
||||||
|
configKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
embed: keyof typeof configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Page({ embed }: PageProps) {
|
||||||
|
const config = configurations[embed];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<App.Provider>
|
||||||
|
<EmbedState.Provider data={extractEmbedConfigDefaults(config)}>
|
||||||
|
<Content embed={embed} />
|
||||||
|
</EmbedState.Provider>
|
||||||
|
</App.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Content({ embed }: PageProps) {
|
||||||
|
const embedStateStore = EmbedState.useStore();
|
||||||
|
const appStore = App.useStore();
|
||||||
|
const config = configurations[embed] as EmbedConfig;
|
||||||
|
const [backgroundImage, setBackgroundImage] = useState(imgur('SO13zur'));
|
||||||
|
const fileInput = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fileInput.current = document.createElement('input');
|
||||||
|
fileInput.current.type = 'file';
|
||||||
|
fileInput.current.style.display = 'none';
|
||||||
|
fileInput.current.accept = 'image/*';
|
||||||
|
fileInput.current.onchange = (event) => {
|
||||||
|
const file = (event.target as HTMLInputElement).files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
setBackgroundImage(URL.createObjectURL(file));
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageWrapper padding="0" size={1028 + 640} color="red">
|
||||||
|
<Flex>
|
||||||
|
<ScrollArea
|
||||||
|
scrollbars="vertical"
|
||||||
|
style={{ height: '100%', maxWidth: 320 }}
|
||||||
|
>
|
||||||
|
<Flex direction="column" gap="2" p="4">
|
||||||
|
<Import />
|
||||||
|
|
||||||
|
<Heading>Export</Heading>
|
||||||
|
<Text size="2" color="gray" mb="2">
|
||||||
|
Copy all properties for you streaming software
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Flex mb="6" gap="2" wrap="wrap">
|
||||||
|
<CopyButton
|
||||||
|
copy={() => {
|
||||||
|
const { wargaming } = appStore.getState().logins;
|
||||||
|
|
||||||
|
if (!wargaming) return;
|
||||||
|
|
||||||
|
const state = embedStateStore.getState();
|
||||||
|
const initial = embedStateStore.getInitialState();
|
||||||
|
const shallowState: EmbedStateStore = {};
|
||||||
|
|
||||||
|
Object.entries(state).forEach(([key, value]) => {
|
||||||
|
if (value !== initial[key]) {
|
||||||
|
shallowState[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
id: `${wargaming.id}`,
|
||||||
|
state: stringify(shallowState),
|
||||||
|
});
|
||||||
|
|
||||||
|
return `${location.origin}/tools/embed/${embed}/host?${searchParams.toString()}`;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link2Icon />
|
||||||
|
Copy URL
|
||||||
|
</CopyButton>
|
||||||
|
<CopyButton
|
||||||
|
variant="outline"
|
||||||
|
copy={() =>
|
||||||
|
'body { margin: 0; background-color: transparent; overflow: hidden; }'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CodeIcon />
|
||||||
|
Copy CSS
|
||||||
|
</CopyButton>
|
||||||
|
<CopyButton
|
||||||
|
variant="outline"
|
||||||
|
copy={() => `${embedStateStore.getState().width}`}
|
||||||
|
>
|
||||||
|
<WidthIcon />
|
||||||
|
Copy width
|
||||||
|
</CopyButton>
|
||||||
|
<CopyButton
|
||||||
|
variant="outline"
|
||||||
|
copy={() => `${embedStateStore.getState().height}`}
|
||||||
|
>
|
||||||
|
<HeightIcon />
|
||||||
|
Copy height
|
||||||
|
</CopyButton>
|
||||||
|
<CopyButton variant="outline" copy={() => '`'}>
|
||||||
|
<TimerIcon />
|
||||||
|
Copy framerate
|
||||||
|
</CopyButton>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex justify="between" align="center">
|
||||||
|
<Heading>Customize</Heading>
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
embedStateStore.setState(
|
||||||
|
embedStateStore.getInitialState(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ResetIcon /> Reset
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<Text size="2" color="gray" mb="2">
|
||||||
|
Bells and whistles of your embed
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{Object.keys(config).map((configKey) => {
|
||||||
|
const item = config[configKey];
|
||||||
|
let control: ReactNode;
|
||||||
|
let oneLiner = false;
|
||||||
|
|
||||||
|
switch (item.type) {
|
||||||
|
case EmbedItemType.Boolean: {
|
||||||
|
control = <Boolean configKey={configKey} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.Color: {
|
||||||
|
control = <Color configKey={configKey} />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.Radius: {
|
||||||
|
control = <Radius configKey={configKey} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.Size: {
|
||||||
|
control = <Size configKey={configKey} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.SizeWithout0: {
|
||||||
|
control = <SizeWithout0 configKey={configKey} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.Slider: {
|
||||||
|
control = <Slider configKey={configKey} config={item} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.String: {
|
||||||
|
control = <TextController configKey={configKey} />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.RichText: {
|
||||||
|
control = <RichText configKey={configKey} />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EmbedItemType.Enum: {
|
||||||
|
control = <Enum configKey={configKey} config={item} />;
|
||||||
|
oneLiner = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
direction={oneLiner ? undefined : 'column'}
|
||||||
|
gap={oneLiner ? '2' : '1'}
|
||||||
|
justify={oneLiner ? 'between' : undefined}
|
||||||
|
mb={oneLiner ? '2' : '4'}
|
||||||
|
pb={item.pad ? '6' : undefined}
|
||||||
|
>
|
||||||
|
<Text>{capitalize(startCase(configKey as string))}</Text>
|
||||||
|
<Flex
|
||||||
|
gap="2"
|
||||||
|
align="center"
|
||||||
|
flexGrow="1"
|
||||||
|
justify={oneLiner ? 'end' : undefined}
|
||||||
|
>
|
||||||
|
{control}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
p="4"
|
||||||
|
flexGrow="1"
|
||||||
|
position="sticky"
|
||||||
|
top={`${NAVBAR_HEIGHT}px`}
|
||||||
|
height={`calc(100vh - ${NAVBAR_HEIGHT}px)`}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
height="100%"
|
||||||
|
style={{
|
||||||
|
borderRadius: 'var(--radius-3)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: 'var(--shadow-3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
justify="between"
|
||||||
|
align="center"
|
||||||
|
p="2"
|
||||||
|
pl="3"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--color-panel-solid)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text color="gray">Example preview</Text>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="gray"
|
||||||
|
onClick={() => fileInput.current?.click()}
|
||||||
|
>
|
||||||
|
<ImageIcon /> Upload test background
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
flexGrow="1"
|
||||||
|
style={{
|
||||||
|
background: `url(${backgroundImage}) center / cover no-repeat`,
|
||||||
|
}}
|
||||||
|
position="relative"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
<PreviewWrapper name={embed} />
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</PageWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
import type { GetStaticPaths } from 'astro';
|
||||||
|
import { configurations } from '../../../../constants/embeds';
|
||||||
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
||||||
|
import { Page } from './_index';
|
||||||
|
|
||||||
|
export const getStaticPaths = (() => {
|
||||||
|
return Object.keys(configurations).map((embed) => ({
|
||||||
|
params: { embed: embed as keyof typeof configurations },
|
||||||
|
}));
|
||||||
|
}) satisfies GetStaticPaths;
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout>
|
||||||
|
<Page client:load embed={Astro.params.embed} />
|
||||||
|
</BaseLayout>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
import { PageWrapper } from '../../../components/PageWrapper';
|
||||||
|
import { configurations } from '../../../constants/embeds';
|
||||||
|
import { embedTool } from '../../../constants/tools';
|
||||||
|
import ToolLayout from '../../../layouts/ToolLayout.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<ToolLayout tool={embedTool}>
|
||||||
|
<PageWrapper>
|
||||||
|
{
|
||||||
|
Object.entries(configurations).map(([embed, config]) => (
|
||||||
|
<a href={`/tools/embed/${embed}`}>{embed}</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</PageWrapper>
|
||||||
|
</ToolLayout>
|
||||||
Reference in new issue
Block a user