self-resizing text

This commit is contained in:
Joshua Seigler 2025-04-05 21:52:33 -07:00
parent bc29510bb5
commit 9649d407fd
4 changed files with 63 additions and 10 deletions

View file

@ -12,14 +12,14 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"solid-js": "^1.9.3",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2" "@tauri-apps/plugin-opener": "^2",
"solid-js": "^1.9.3"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.3", "vite": "^6.0.3",
"vite-plugin-solid": "^2.11.0", "vite-plugin-solid": "^2.11.0"
"@tauri-apps/cli": "^2"
} }
} }

View file

@ -76,13 +76,13 @@
flex-direction: column; flex-direction: column;
gap: calc(1 * var(--unit)); gap: calc(1 * var(--unit));
font-size: calc(3 * var(--unit)); font-size: calc(3 * var(--unit));
font-weight: 700;
width: calc((20 * 4 + 3) * var(--unit)); width: calc((20 * 4 + 3) * var(--unit));
margin: 0 auto; margin: 0 auto;
} }
.puzzle-header { .puzzle-header {
display: flex; display: flex;
align-items: center;
flex-direction: row; flex-direction: row;
> h1 { > h1 {
flex-grow: 1; flex-grow: 1;
@ -112,10 +112,11 @@
user-select: none; user-select: none;
flex-basis: 0; flex-basis: 0;
flex-grow: 1; flex-grow: 1;
padding: 0; padding: calc(1 * var(--unit));
border-radius: calc(1 * var(--unit)); border-radius: calc(1 * var(--unit));
background-color: var(--color-foreground-trace); background-color: var(--color-foreground-trace);
font-weight: 500; font-size: calc(5 * var(--unit));
font-weight: 600;
} }
.badge { .badge {
position: absolute; position: absolute;
@ -194,6 +195,7 @@ h1 {
button { button {
appearance: none; appearance: none;
overflow: visible; overflow: visible;
min-width: 2em;
background: var(--color-foreground-faint); background: var(--color-foreground-faint);
color: var(--color-foreground); color: var(--color-foreground);
font-size: inherit; font-size: inherit;
@ -202,7 +204,7 @@ button {
border-radius: calc(1 * var(--unit, 0.5em)); border-radius: calc(1 * var(--unit, 0.5em));
} }
button:disabled { button:disabled {
opacity: 0.5; opacity: 0.25;
} }
button:focus-visible, button:hover { button:focus-visible, button:hover {
box-shadow: 0 0 3em -1.5em inset var(--color-foreground) box-shadow: 0 0 3em -1.5em inset var(--color-foreground)

View file

@ -1,6 +1,7 @@
import { For } from "solid-js"; import { For } from "solid-js";
import "./App.css"; import "./App.css";
import useAppModel from "./useAppModel"; import useAppModel from "./useAppModel";
import FitText from "./FitText";
function App() { function App() {
const { const {
@ -35,6 +36,7 @@ function App() {
on:input={({ target: { value } }) => on:input={({ target: { value } }) =>
handleSelectGame(parseInt(value, 10)) handleSelectGame(parseInt(value, 10))
} }
value={store.puzzleId}
> >
<For each={connections}> <For each={connections}>
{({ id }) => <option value={id}>{id}</option>} {({ id }) => <option value={id}>{id}</option>}
@ -53,6 +55,7 @@ function App() {
</button> </button>
</div> </div>
</header> </header>
<div>Create four groups of four words!</div>
<For each={store.solvedGroups}> <For each={store.solvedGroups}>
{({ group, level, members }) => ( {({ group, level, members }) => (
<div class="puzzle-row"> <div class="puzzle-row">
@ -68,7 +71,7 @@ function App() {
<div class="puzzle-row"> <div class="puzzle-row">
{[0, 1, 2, 3].map((col) => { {[0, 1, 2, 3].map((col) => {
const index = 4 * row + col; const index = 4 * row + col;
const answer = getFromPuzzle(index).answer; const answer = () => getFromPuzzle(index).answer;
return ( return (
<button <button
classList={{ classList={{
@ -85,7 +88,7 @@ function App() {
}); });
}} }}
> >
{answer} <FitText body={answer} />
{store.pinnedCount > index && <div class="badge">🔒</div>} {store.pinnedCount > index && <div class="badge">🔒</div>}
</button> </button>
); );

48
src/FitText.tsx Normal file
View file

@ -0,0 +1,48 @@
import { createEffect, createSignal, on, type Accessor } from "solid-js";
export default function FitText(props: { body: Accessor<string> }) {
let textRef: SVGTextElement | undefined;
const [width, setWidth] = createSignal(100);
const [height, setHeight] = createSignal(100);
createEffect(
on(
props.body,
async () => {
setWidth(100)
setHeight(100)
await new Promise(resolve => setTimeout(resolve, 1))
const bounds = textRef?.getBBox();
if (bounds === undefined) {
return;
}
setWidth(bounds.width);
setHeight(bounds.height);
}
)
)
return (
<svg
style={{
width: "100%",
"max-height": "50%",
}}
viewBox={`0 0 ${width()} ${height()}`}
overflow="visible"
fill="currentcolor"
>
<text
ref={textRef}
x="50%"
y="50%"
dominant-baseline="central"
text-anchor="middle"
font-size="1rem"
font-family="inherit"
>
{props.body()}
</text>
</svg>
);
}