v0.2 – the persistence
- modular data model - localStorage for persistence
This commit is contained in:
parent
bcd99f0181
commit
b67adb823d
@ -1,6 +1,8 @@
|
||||
# colorer
|
||||
|
||||
Play around with hsla color
|
||||
Play around with hsla color!
|
||||
|
||||
Try it online: https://colorer.vercel.app/
|
||||
|
||||
## Usage
|
||||
|
||||
|
69
src/App.tsx
69
src/App.tsx
@ -1,59 +1,14 @@
|
||||
import { createSignal } from "solid-js";
|
||||
import { HslaProvider } from "/@/hsla-context";
|
||||
import Colorer from "/@lib/colorer";
|
||||
|
||||
function Colorer(props) {
|
||||
const [h, setH] = createSignal(1);
|
||||
const [s, setS] = createSignal(50);
|
||||
const [l, setL] = createSignal(50);
|
||||
const [a, setA] = createSignal(1);
|
||||
const hslColor = () => `hsla(${h()}, ${s()}%, ${l()}%, ${a()})`;
|
||||
const startHsla = { h: 1, s: 50, l: 50, a: 1 };
|
||||
|
||||
const changeH = (e) => setH(e.currentTarget.value);
|
||||
const changeS = (e) => setS(e.currentTarget.value);
|
||||
const changeL = (e) => setL(e.currentTarget.value);
|
||||
const changeA = (e) => setA(e.currentTarget.value);
|
||||
|
||||
return (
|
||||
<div class="main-container">
|
||||
<div id="colorer">
|
||||
<label>H <input type="number" min="0" max="359"
|
||||
value={h()} onInput={changeH} />
|
||||
<input type="range" min="0" max="359"
|
||||
value={h()} onInput={changeH} />
|
||||
</label>
|
||||
<label>S <input type="number" min="0" max="100"
|
||||
value={s()} onInput={changeS} />
|
||||
<input type="range" min="0" max="100"
|
||||
value={s()} onInput={changeS} />
|
||||
</label>
|
||||
<label>L <input type="number" min="0" max="100"
|
||||
value={l()} onInput={changeL} />
|
||||
<input type="range" min="0" max="100"
|
||||
value={l()} onInput={changeL} />
|
||||
</label>
|
||||
<label>A <input type="number" min="0" max="1" step="0.01"
|
||||
value={a()} onInput={changeA} />
|
||||
<input type="range" min="0" max="1" step="0.01"
|
||||
value={a()} onInput={changeA} />
|
||||
</label>
|
||||
</div>
|
||||
<div class="color-box" style={{
|
||||
"background-color": hslColor()
|
||||
}}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<header>
|
||||
<h1>colorer</h1>
|
||||
</header>
|
||||
<main>
|
||||
<Colorer />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default () =>
|
||||
<HslaProvider hsla={startHsla}>
|
||||
<header>
|
||||
<h1>colorer</h1>
|
||||
</header>
|
||||
<main>
|
||||
<Colorer />
|
||||
</main>
|
||||
</HslaProvider>
|
||||
|
26
src/components/color-input.tsx
Normal file
26
src/components/color-input.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { mergeProps } from "solid-js";
|
||||
|
||||
function ColorInput(props) {
|
||||
const options = mergeProps({ step: 1 }, props);
|
||||
|
||||
const setValue = (e) => {
|
||||
let value = parseFloat(e.currentTarget.value);
|
||||
options.onInput(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="color-input">
|
||||
<label>
|
||||
<span>{options.label}</span>
|
||||
<input type="number"
|
||||
min={options.min} max={options.max} step={options.step}
|
||||
value={options.value} onInput={setValue} />
|
||||
<input type="range"
|
||||
min={options.min} max={options.max} step={options.step}
|
||||
value={options.value} onInput={setValue} />
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ColorInput;
|
25
src/components/colorer.tsx
Normal file
25
src/components/colorer.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { useHslaContext } from "/@/hsla-context";
|
||||
import ColorInput from "/@lib/color-input";
|
||||
|
||||
function Colorer(props) {
|
||||
const [hsla, { setH, setS, setL, setA }] = useHslaContext();
|
||||
|
||||
const hslColor = () => `hsla(${hsla().h}, ${hsla().s}%, ${hsla().l}%, ${hsla().a})`;
|
||||
|
||||
return (
|
||||
<div class="main-container">
|
||||
<div id="colorer">
|
||||
<ColorInput label="H" min="1" max="360" value={hsla().h} onInput={setH} />
|
||||
<ColorInput label="S" min="0" max="100" value={hsla().s} onInput={setS} />
|
||||
<ColorInput label="L" min="0" max="100" value={hsla().l} onInput={setL} />
|
||||
<ColorInput label="A" min="0" max="1" step={0.05} value={hsla().a} onInput={setA} />
|
||||
</div>
|
||||
<div class="color-box" style={{
|
||||
"background-color": hslColor()
|
||||
}}></div>
|
||||
<pre>{hslColor()}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Colorer;
|
53
src/hsla-context.tsx
Normal file
53
src/hsla-context.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { createSignal, createEffect, createContext, useContext } from "solid-js";
|
||||
|
||||
const HslaContext = createContext();
|
||||
|
||||
export function HslaProvider(props) {
|
||||
const defaultHsla = { h: 1, s: 50, l: 50, a: 1 };
|
||||
const [hsla, setHsla] = createSignal(props.hsla || defaultHsla),
|
||||
store = [
|
||||
hsla,
|
||||
{
|
||||
setHsla(value) {
|
||||
setHsla(value)
|
||||
},
|
||||
setH(value) {
|
||||
setHsla({ ...hsla(), h: value })
|
||||
},
|
||||
setS(value) {
|
||||
setHsla({ ...hsla(), s: value })
|
||||
},
|
||||
setL(value) {
|
||||
setHsla({ ...hsla(), l: value })
|
||||
},
|
||||
setA(value) {
|
||||
setHsla({ ...hsla(), a: value })
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<HslaContext.Provider value={store}>
|
||||
{props.children}
|
||||
</HslaContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useHslaContext() {
|
||||
const context = useContext(HslaContext);
|
||||
const storageName = "hsla"
|
||||
const [hsla, { setHsla, ...rest }] = context;
|
||||
|
||||
if (window.localStorage) {
|
||||
const storedHsla = window.localStorage.getItem(storageName);
|
||||
if (storedHsla != null) {
|
||||
setHsla(JSON.parse(storedHsla))
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
window.localStorage.setItem(storageName, JSON.stringify(hsla()));
|
||||
});
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
@ -12,6 +12,7 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@apply bg-gray-700;
|
||||
height: 100vh;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
code {
|
||||
@ -30,21 +31,19 @@ code {
|
||||
"main";
|
||||
}
|
||||
|
||||
#colorer {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
header { grid-area: header; color: white; }
|
||||
header h1 { @apply font-mono text-4xl text-center mt-2; }
|
||||
|
||||
main {
|
||||
@apply bg-white m-16 mx-auto p-4;
|
||||
@apply bg-white mt-8 mx-auto p-4 pb-5;
|
||||
@apply flex justify-center content-center;
|
||||
grid-area: main;
|
||||
width: min(50%, 75vw);
|
||||
height: min(75%, 50vh);
|
||||
}
|
||||
|
||||
main pre { @apply float-right; }
|
||||
|
||||
.main-container { width: 100%; }
|
||||
|
||||
label { @apply bg-white p-1 pl-0; }
|
||||
@ -53,8 +52,9 @@ label > input { @apply font-mono text-right; }
|
||||
|
||||
#colorer { float: left; }
|
||||
|
||||
input[type=number] {
|
||||
width: 6ch;
|
||||
}
|
||||
.color-input label { @apply flex gap-0.5; }
|
||||
.color-input label span { @apply inline-block w-4; }
|
||||
.color-input input[type=number] { width: 6ch; }
|
||||
.color-input input[type=range] { @apply flex-shrink; }
|
||||
|
||||
.color-box { width: 100%; height: 100%; }
|
||||
|
Loading…
Reference in New Issue
Block a user