v0.2 – the persistence
- modular data model - localStorage for persistence
This commit is contained in:
parent
bcd99f0181
commit
b67adb823d
@ -1,6 +1,8 @@
|
|||||||
# colorer
|
# colorer
|
||||||
|
|
||||||
Play around with hsla color
|
Play around with hsla color!
|
||||||
|
|
||||||
|
Try it online: https://colorer.vercel.app/
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
57
src/App.tsx
57
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 startHsla = { h: 1, s: 50, l: 50, a: 1 };
|
||||||
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 changeH = (e) => setH(e.currentTarget.value);
|
export default () =>
|
||||||
const changeS = (e) => setS(e.currentTarget.value);
|
<HslaProvider hsla={startHsla}>
|
||||||
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>
|
<header>
|
||||||
<h1>colorer</h1>
|
<h1>colorer</h1>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<Colorer />
|
<Colorer />
|
||||||
</main>
|
</main>
|
||||||
</>
|
</HslaProvider>
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
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;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
@apply bg-gray-700;
|
@apply bg-gray-700;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
font-size: 12pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
@ -30,21 +31,19 @@ code {
|
|||||||
"main";
|
"main";
|
||||||
}
|
}
|
||||||
|
|
||||||
#colorer {
|
|
||||||
@apply flex flex-col;
|
|
||||||
}
|
|
||||||
|
|
||||||
header { grid-area: header; color: white; }
|
header { grid-area: header; color: white; }
|
||||||
header h1 { @apply font-mono text-4xl text-center mt-2; }
|
header h1 { @apply font-mono text-4xl text-center mt-2; }
|
||||||
|
|
||||||
main {
|
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;
|
@apply flex justify-center content-center;
|
||||||
grid-area: main;
|
grid-area: main;
|
||||||
width: min(50%, 75vw);
|
width: min(50%, 75vw);
|
||||||
height: min(75%, 50vh);
|
height: min(75%, 50vh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main pre { @apply float-right; }
|
||||||
|
|
||||||
.main-container { width: 100%; }
|
.main-container { width: 100%; }
|
||||||
|
|
||||||
label { @apply bg-white p-1 pl-0; }
|
label { @apply bg-white p-1 pl-0; }
|
||||||
@ -53,8 +52,9 @@ label > input { @apply font-mono text-right; }
|
|||||||
|
|
||||||
#colorer { float: left; }
|
#colorer { float: left; }
|
||||||
|
|
||||||
input[type=number] {
|
.color-input label { @apply flex gap-0.5; }
|
||||||
width: 6ch;
|
.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%; }
|
.color-box { width: 100%; height: 100%; }
|
||||||
|
Loading…
Reference in New Issue
Block a user