feat: add font style field component
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
import TextControl from "./fields/schema/TextControl.svelte";
|
||||
import OffsetControl from "./fields/schema/OffsetControl.svelte";
|
||||
import PositionControl from "./fields/schema/PositionControl.svelte";
|
||||
import FontStyleControl from "./fields/schema/FontStyleControl.svelte";
|
||||
|
||||
interface FieldControlProps {
|
||||
label: string;
|
||||
@@ -34,6 +35,7 @@
|
||||
dimension: DimensionField,
|
||||
offset: OffsetControl,
|
||||
position9: PositionControl,
|
||||
"font-style": FontStyleControl,
|
||||
};
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
<script lang="ts">
|
||||
import type { TextFont } from "$lib/core/domText";
|
||||
import type {
|
||||
FieldSpec,
|
||||
FontStyle,
|
||||
FontStyleSpec,
|
||||
} from "$lib/registry-schema";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
value: unknown;
|
||||
spec: FieldSpec;
|
||||
onchange?: (value: FontStyle) => void;
|
||||
}
|
||||
let { label, value, spec, onchange }: Props = $props();
|
||||
|
||||
const sp = $derived(spec as FontStyleSpec);
|
||||
const current = $derived.by(() => {
|
||||
const v = value as Partial<FontStyle> | undefined;
|
||||
return {
|
||||
font:
|
||||
v?.font === "sans" || v?.font === "serif" || v?.font === "mono"
|
||||
? v.font
|
||||
: sp.font,
|
||||
size:
|
||||
typeof v?.size === "number" && Number.isFinite(v.size)
|
||||
? v.size
|
||||
: sp.size,
|
||||
bold: typeof v?.bold === "boolean" ? v.bold : sp.bold,
|
||||
color: typeof v?.color === "string" ? v.color : sp.color,
|
||||
};
|
||||
});
|
||||
|
||||
function set<K extends keyof FontStyle>(key: K, val: FontStyle[K]) {
|
||||
onchange?.({ ...current, [key]: val });
|
||||
}
|
||||
|
||||
const FONTS: { value: TextFont; label: string }[] = [
|
||||
{ value: "sans", label: "Sans" },
|
||||
{ value: "serif", label: "Serif" },
|
||||
{ value: "mono", label: "Mono" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="control font-style-control">
|
||||
<span class="fs-label">{label}</span>
|
||||
<div class="fs-grid">
|
||||
<label class="fs-cell">
|
||||
<span class="fs-sub">Font</span>
|
||||
<select
|
||||
class="fs-select"
|
||||
value={current.font}
|
||||
oninput={(e) =>
|
||||
set("font", (e.target as HTMLSelectElement).value as TextFont)}
|
||||
>
|
||||
{#each FONTS as option (option.value)}
|
||||
<option value={option.value}>{option.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label class="fs-cell">
|
||||
<span class="fs-sub">Size</span>
|
||||
<input
|
||||
class="fs-input"
|
||||
type="number"
|
||||
min={sp.min}
|
||||
max={sp.max}
|
||||
value={current.size}
|
||||
oninput={(e) =>
|
||||
set("size", Number((e.target as HTMLInputElement).value))}
|
||||
/>
|
||||
</label>
|
||||
<label class="fs-cell">
|
||||
<span class="fs-sub">Color</span>
|
||||
<span class="fs-color">
|
||||
<span class="swatch" style="background:{current.color}"></span>
|
||||
<input
|
||||
class="fs-color-input"
|
||||
type="color"
|
||||
value={current.color}
|
||||
oninput={(e) => set("color", (e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<label class="fs-cell fs-bold">
|
||||
<span class="fs-sub">Bold</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={current.bold}
|
||||
onchange={(e) => set("bold", (e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.fs-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.fs-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.fs-cell {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.fs-sub {
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
.fs-select,
|
||||
.fs-input,
|
||||
.fs-color-input {
|
||||
width: 100%;
|
||||
padding: var(--space-m) var(--space-l);
|
||||
background: var(--color-background);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
color: var(--color-text);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
}
|
||||
.fs-color {
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-xxl) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.fs-color-input {
|
||||
height: var(--space-xxl);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.fs-bold {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
.fs-bold input[type="checkbox"] {
|
||||
width: var(--space-xl);
|
||||
height: var(--space-xl);
|
||||
accent-color: var(--color-main);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user