import { useMemo } from 'react';
import { Box } from '@chakra-ui/react';
import { CodeTab, PreviewTab, TabsLayout } from '../../components/common/TabsLayout';
import { ComponentPropsProvider } from '../../components/context/ComponentPropsContext';
import CodeExample from '../../components/code/CodeExample';
import PropTable from '../../components/common/Preview/PropTable';
import Customize from '../../components/common/Preview/Customize';
import PreviewColorList from '../../components/common/Preview/PreviewColorList';
import PreviewInput from '../../components/common/Preview/PreviewInput';
import PreviewSelect from '../../components/common/Preview/PreviewSelect';
import PreviewSlider from '../../components/common/Preview/PreviewSlider';
import PreviewSwitch from '../../components/common/Preview/PreviewSwitch';
import useComponentProps from '../../hooks/useComponentProps';
import { useColorModeValue } from '../../components/setup/color-mode';
import ASCIIText from '@content/TextAnimations/ASCIIText/ASCIIText';
import { asciiText } from '../../constants/code/TextAnimations/asciiTextCode';
const CLASSIC = ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$';
const DEFAULT_PROPS = {
preset: 'classic',
text: 'Hey!',
charset: CLASSIC,
colors: ['#ff6188', '#fc9867', '#ffd866'],
asciiFontSize: 8,
textScale: 1,
blocks: 0.9,
waves: 1,
waveSpeed: 1,
chroma: 1,
tilt: 1,
hueShift: 1,
scramble: 0.6,
clickRipple: true,
idle: true,
interactive: true
};
const PRESETS = {
classic: {},
terminal: {
charset: ' .:-=+*#%@',
colors: ['#b8f5c8', '#5ee08a', '#1f9d55'],
blocks: 0,
chroma: 0.3,
hueShift: 0,
asciiFontSize: 10
},
blocks: { charset: ' ░▒▓█', colors: ['#ffffff', '#d4d4d8', '#a1a1aa'], blocks: 0.2, hueShift: 0, chroma: 0.6 },
binary: { charset: ' 01', colors: ['#ffffff', '#bae6fd', '#38bdf8'], blocks: 0.35, hueShift: 0.4, asciiFontSize: 9 },
mono: { colors: ['#ffffff', '#e4e4e7', '#a1a1aa'], blocks: 0.15, chroma: 0, hueShift: 0, waves: 0.6 }
};
const PRESET_OPTIONS = [
{ value: 'classic', label: 'Classic' },
{ value: 'terminal', label: 'Terminal' },
{ value: 'blocks', label: 'Blocks' },
{ value: 'binary', label: 'Binary' },
{ value: 'mono', label: 'Mono' }
];
const propData = [
{ name: 'text', type: 'string', default: "'Hey!'", description: 'Text rendered in ASCII.' },
{
name: 'asciiFontSize',
type: 'number',
default: '8',
description: 'Height of each character cell in pixels. Smaller cells give finer detail.'
},
{
name: 'charset',
type: 'string',
default: "' .'`^\",:;Il!i~+_-?][}{1)(|/...'",
description: 'Characters from darkest to brightest. Any string works, including Unicode blocks.'
},
{
name: 'colors',
type: 'string[]',
default: "['#ff6188', '#fc9867', '#ffd866']",
description: 'Gradient for the characters, from the center outward. Up to three colors.'
},
{ name: 'textColor', type: 'string', default: "'#fdf9f3'", description: 'Color of the text behind the characters.' },
{
name: 'fontFamily',
type: 'string',
default: "'IBM Plex Mono', ui-monospace, ...",
description: 'Font for the text and the characters.'
},
{ name: 'fontWeight', type: 'number', default: '600', description: 'Weight of the rendered text.' },
{ name: 'textScale', type: 'number', default: '1', description: 'Size of the text relative to the container.' },
{
name: 'blocks',
type: 'number',
default: '0.9',
description: 'Opacity of the colored pixel blocks under the characters, from 0 to 1.'
},
{ name: 'waves', type: 'number', default: '1', description: 'Strength of the wavy motion. 0 keeps the text flat.' },
{ name: 'waveSpeed', type: 'number', default: '1', description: 'Speed of the wavy motion.' },
{ name: 'chroma', type: 'number', default: '1', description: 'Amount of color fringing around the letters.' },
{ name: 'tilt', type: 'number', default: '1', description: 'How far the text turns toward the pointer.' },
{
name: 'hueShift',
type: 'number',
default: '1',
description: 'Rotates the hue as the pointer circles the text. 0 keeps the colors fixed.'
},
{
name: 'scramble',
type: 'number',
default: '0.6',
description: 'Size and strength of the glitchy scramble under the pointer, from 0 to 1.'
},
{
name: 'clickRipple',
type: 'boolean',
default: 'true',
description: 'Sends a ring of characters across the canvas from where you click.'
},
{
name: 'intro',
type: 'boolean',
default: 'true',
description: 'Decodes the text out of random characters on load.'
},
{ name: 'idle', type: 'boolean', default: 'true', description: 'Sways the text gently when nobody interacts.' },
{
name: 'interactive',
type: 'boolean',
default: 'true',
description: 'Reacts to the pointer with tilt, hue, scramble and ripples.'
},
{
name: 'theme',
type: "'dark' | 'light'",
default: "'dark'",
description: 'Light inverts the colors so the effect works on white pages.'
},
{ name: 'className', type: 'string', default: "''", description: 'Extra class names for the container.' },
{ name: 'style', type: 'CSSProperties', default: '-', description: 'Inline styles for the container.' }
];
const ASCIITextDemo = () => {
const { props, updateProp, updateProps, resetProps, hasChanges } = useComponentProps(DEFAULT_PROPS);
const { preset, ...settings } = props;
const theme = useColorModeValue('light', 'dark');
const applyPreset = value => {
const base = Object.fromEntries(Object.keys(DEFAULT_PROPS).map(key => [key, DEFAULT_PROPS[key]]));
updateProps({ ...base, ...PRESETS[value], text: props.text, preset: value });
};
const computedProps = useMemo(() => (theme === 'light' ? { theme: 'light' } : {}), [theme]);
return (
<ComponentPropsProvider
props={props}
defaultProps={DEFAULT_PROPS}
resetProps={resetProps}
hasChanges={hasChanges}
demoOnlyProps={['preset']}
computedProps={computedProps}
>
<TabsLayout>
<PreviewTab>
<Box position="relative" className="demo-container" h={440} overflow="hidden">
<ASCIIText {...settings} theme={theme} />
</Box>
<Customize>
<PreviewColorList
title="Colors"
colors={props.colors}
min={1}
max={3}
onChange={value => updateProp('colors', value)}
/>
<PreviewSelect title="Preset" options={PRESET_OPTIONS} value={preset} onChange={applyPreset} />
<PreviewInput
title="Text"
value={props.text}
placeholder="Enter text..."
width={200}
maxLength={12}
onChange={value => updateProp('text', value)}
/>
<PreviewInput
title="Characters"
value={props.charset}
placeholder=" .:-=+*#%@"
width={200}
maxLength={80}
onChange={value => updateProp('charset', value)}
/>
<PreviewSlider
title="Cell Size"
min={4}
max={24}
step={1}
value={props.asciiFontSize}
valueUnit="px"
onChange={value => updateProp('asciiFontSize', value)}
/>
<PreviewSlider
title="Text Scale"
min={0.4}
max={2}
step={0.05}
value={props.textScale}
onChange={value => updateProp('textScale', value)}
/>
<PreviewSlider
title="Blocks"
min={0}
max={1}
step={0.05}
value={props.blocks}
onChange={value => updateProp('blocks', value)}
/>
<PreviewSlider
title="Waves"
min={0}
max={2}
step={0.05}
value={props.waves}
onChange={value => updateProp('waves', value)}
/>
<PreviewSlider
title="Wave Speed"
min={0}
max={3}
step={0.05}
value={props.waveSpeed}
onChange={value => updateProp('waveSpeed', value)}
/>
<PreviewSlider
title="Chroma"
min={0}
max={3}
step={0.05}
value={props.chroma}
onChange={value => updateProp('chroma', value)}
/>
<PreviewSlider
title="Tilt"
min={0}
max={2}
step={0.05}
value={props.tilt}
onChange={value => updateProp('tilt', value)}
/>
<PreviewSlider
title="Hue Shift"
min={0}
max={2}
step={0.05}
value={props.hueShift}
onChange={value => updateProp('hueShift', value)}
/>
<PreviewSlider
title="Scramble"
min={0}
max={1}
step={0.05}
value={props.scramble}
onChange={value => updateProp('scramble', value)}
/>
<PreviewSwitch
title="Click Ripple"
isChecked={props.clickRipple}
onChange={value => updateProp('clickRipple', value)}
/>
<PreviewSwitch title="Idle Sway" isChecked={props.idle} onChange={value => updateProp('idle', value)} />
<PreviewSwitch
title="Interactive"
isChecked={props.interactive}
onChange={value => updateProp('interactive', value)}
/>
</Customize>
<PropTable data={propData} />
</PreviewTab>
<CodeTab>
<CodeExample codeObject={asciiText} componentName="ASCIIText" />
</CodeTab>
</TabsLayout>
</ComponentPropsProvider>
);
};
export default ASCIITextDemo;