feat: implement state and components for text config

This commit is contained in:
2026-02-06 02:26:14 +05:00
parent a063a2d752
commit 5244de7cc0
11 changed files with 128 additions and 68 deletions
+7 -6
View File
@@ -6,27 +6,28 @@
import ColorPicker from "$components/ui/ColorPicker.svelte"; import ColorPicker from "$components/ui/ColorPicker.svelte";
import RangeSlider from "$components/ui/RangeSlider.svelte"; import RangeSlider from "$components/ui/RangeSlider.svelte";
import SelectFont from "$components/ui/SelectFont.svelte"; import SelectFont from "$components/ui/SelectFont.svelte";
import { textConfigState } from "$states/textConfig.svelte";
</script> </script>
<Card title="Настройки текста"> <Card title="Настройки текста">
<SettingsGrid> <SettingsGrid>
<SettingsRow label="Размер"> <SettingsRow label="Размер">
<RangeSlider /> <RangeSlider bind:value={textConfigState.fontSize} min={10} max={100} step={1} />
</SettingsRow> </SettingsRow>
<SettingsRow label="Шрифт"> <SettingsRow label="Шрифт">
<SelectFont /> <SelectFont bind:value={textConfigState.fontFamily} />
</SettingsRow> </SettingsRow>
<SettingsRow label="Цвет"> <SettingsRow label="Цвет">
<ColorPicker /> <ColorPicker bind:value={textConfigState.color} />
</SettingsRow> </SettingsRow>
<SettingsRow label="Выравнивание"> <SettingsRow label="Выравнивание">
<Alignment /> <Alignment bind:align={textConfigState.align} />
</SettingsRow> </SettingsRow>
<SettingsRow label="Отступы"> <SettingsRow label="Отступы">
<RangeSlider /> <RangeSlider bind:value={textConfigState.paddingX} min={0} max={100} step={1} />
</SettingsRow> </SettingsRow>
<SettingsRow label="Смещение"> <SettingsRow label="Смещение">
<RangeSlider /> <RangeSlider bind:value={textConfigState.offsetY} min={-100} max={100} step={1} />
</SettingsRow> </SettingsRow>
</SettingsGrid> </SettingsGrid>
</Card> </Card>
+10 -3
View File
@@ -1,16 +1,23 @@
<script lang="ts"> <script lang="ts">
import type { TextAlign } from "$lib/types/text";
import IconAlignCenter from "./Icons/IconAlignCenter.svelte"; import IconAlignCenter from "./Icons/IconAlignCenter.svelte";
import IconAlignLeft from "./Icons/IconAlignLeft.svelte"; import IconAlignLeft from "./Icons/IconAlignLeft.svelte";
import IconAlignRight from "./Icons/IconAlignRight.svelte"; import IconAlignRight from "./Icons/IconAlignRight.svelte";
interface Props {
align: TextAlign;
}
let { align = $bindable("left") }: Props = $props();
</script> </script>
<button class="align-btn active"> <button class="align-btn" class:active={align === "left"} onclick={() => (align = "left")}>
<IconAlignLeft /> <IconAlignLeft />
</button> </button>
<button class="align-btn"> <button class="align-btn" class:active={align === "center"} onclick={() => (align = "center")}>
<IconAlignCenter /> <IconAlignCenter />
</button> </button>
<button class="align-btn"> <button class="align-btn" class:active={align === "right"} onclick={() => (align = "right")}>
<IconAlignRight /> <IconAlignRight />
</button> </button>
+14 -2
View File
@@ -1,5 +1,15 @@
<input type="color" value="#ffffff" class="color-input" /> <script lang="ts">
<span class="color-value">#ffffff</span> import type { HexColor } from "$lib/types/text";
interface Props {
value: HexColor;
}
let { value = $bindable() }: Props = $props();
</script>
<input type="color" bind:value class="color-input" />
<span class="color-value">{value}</span>
<style> <style>
.color-input { .color-input {
@@ -10,6 +20,8 @@
cursor: pointer; cursor: pointer;
transition: var(--transition); transition: var(--transition);
background: var(--bg-primary); background: var(--bg-primary);
padding: 0px 8px;
flex: 1;
} }
.color-input:hover { .color-input:hover {
+16 -2
View File
@@ -1,5 +1,19 @@
<input type="range" id="fontSize" min="12" max="48" value="18" class="slider" /> <script lang="ts">
<span class="value-display" id="fontSizeValue">18</span> import type { ChangeEventHandler } from "svelte/elements";
interface Props {
value: number;
min?: number;
max?: number;
step?: number;
onchange?: ChangeEventHandler<HTMLInputElement>;
}
let { value = $bindable(), min = 0, max = 100, step = 1, onchange = () => {} }: Props = $props();
</script>
<input type="range" {min} {max} {step} bind:value class="slider" {onchange} />
<span class="value-display">{value}</span>
<style> <style>
.slider { .slider {
+9 -1
View File
@@ -1,4 +1,12 @@
<select class="select-input"> <script lang="ts">
interface Props {
value: string;
}
let { value = $bindable() }: Props = $props();
</script>
<select class="select-input" bind:value>
<option value="Arial">Arial</option> <option value="Arial">Arial</option>
<option value="Verdana">Verdana</option> <option value="Verdana">Verdana</option>
<option value="Georgia">Georgia</option> <option value="Georgia">Georgia</option>
-48
View File
@@ -1,48 +0,0 @@
export type TextAlign = "left" | "center" | "right";
export interface TextItem {
id: string;
text: string;
fontSize: number;
fontFamily: string;
color: string;
textAlign: TextAlign;
paddingX: number;
verticalOffset: number;
}
export interface Panel {
id: string;
backgroundImage: string;
text: TextItem;
height: number;
createdAt: Date;
updatedAt: Date;
}
export interface ImageUploadResult {
success: boolean;
image?: string;
error?: string;
}
export interface CropOptions {
width: number;
height: number;
x?: number;
y?: number;
}
export interface ImageCropResult {
success: boolean;
croppedImage?: string;
error?: string;
}
export interface UIState {
isLoading: boolean;
error: string | undefined;
currentStep: "upload" | "crop" | "text" | "preview";
showCropModal: boolean;
showTextManager: boolean;
}
+3
View File
@@ -0,0 +1,3 @@
export type TextAlign = "left" | "center" | "right";
export type HexColor = `#${string}`;
+2 -2
View File
@@ -24,7 +24,7 @@
<style> <style>
:root { :root {
--bg-primary: #ffffff; --bg-primary: #ffffff;
--bg-secondary: #fafafa; --bg-secondary: #dddddd;
--bg-card: #ffffff; --bg-card: #ffffff;
--bg-hover: #f5f5f5; --bg-hover: #f5f5f5;
@@ -50,7 +50,7 @@
:global([data-theme="dark"]) { :global([data-theme="dark"]) {
--bg-primary: #0a0a0a; --bg-primary: #0a0a0a;
--bg-secondary: #1a1a1a; --bg-secondary: #333333;
--bg-card: #1a1a1a; --bg-card: #1a1a1a;
--bg-hover: #2a2a2a; --bg-hover: #2a2a2a;
+63
View File
@@ -0,0 +1,63 @@
import type { HexColor, TextAlign } from "$lib/types/text";
export type TextConfig = {
fontSize: number;
fontFamily: string;
color: HexColor;
align: TextAlign;
paddingX: number;
offsetY: number;
};
function createState() {
const defaults: TextConfig = {
fontSize: 24,
fontFamily: "Arial",
color: "#ffffff",
align: "center",
paddingX: 10,
offsetY: 0,
};
let state: TextConfig = $state({ ...defaults });
return {
get fontSize() {
return state.fontSize;
},
set fontSize(value: number) {
state.fontSize = value;
},
get fontFamily() {
return state.fontFamily;
},
set fontFamily(fontFamily: string) {
state.fontFamily = this.fontFamily;
},
get color() {
return state.color;
},
set color(color: HexColor) {
state.color = color;
},
get align() {
return state.align;
},
set align(align: TextAlign) {
state.align = align;
},
get paddingX() {
return state.paddingX;
},
set paddingX(paddingX: number) {
state.paddingX = paddingX;
},
get offsetY() {
return state.offsetY;
},
set offsetY(offsetY: number) {
state.offsetY = offsetY;
},
};
}
export const textConfigState = createState();
+2 -2
View File
@@ -5,7 +5,7 @@ export interface TextItem {
const defaultTexts: Array<TextItem> = ["About me", "Links", "Projects"].map((text, idx) => ({ text, id: idx })); const defaultTexts: Array<TextItem> = ["About me", "Links", "Projects"].map((text, idx) => ({ text, id: idx }));
function createTextsState() { function createState() {
let texts: Array<TextItem> = $state(defaultTexts); let texts: Array<TextItem> = $state(defaultTexts);
let nextId = $state(defaultTexts.length); let nextId = $state(defaultTexts.length);
@@ -23,4 +23,4 @@ function createTextsState() {
}; };
} }
export const textsState = createTextsState(); export const textsState = createState();
+2 -2
View File
@@ -1,6 +1,6 @@
export type Theme = "dark" | "light"; export type Theme = "dark" | "light";
function createThemeState() { function createState() {
let current: Theme = $state("dark"); let current: Theme = $state("dark");
return { return {
@@ -13,4 +13,4 @@ function createThemeState() {
}; };
} }
export const themeState = createThemeState(); export const themeState = createState();