[+] Color editor & drag and drop

This commit is contained in:
2024-12-16 18:02:59 -05:00
parent c13073b0da
commit 101a992538
2 changed files with 71 additions and 16 deletions
+65 -13
View File
@@ -17,13 +17,16 @@
const [solutionHStates, solutionVStates] = [zero8(eRows * eCols), zero8(eRows * eCols)]
// Colors
let [hColors, vColors] = [zero8(eRows * eCols), zero8(eRows * eCols)]
let colors = ["#90A4AEFF"]
let [hColors, vColors, colors, css, ci] = [zero8(eRows * eCols), zero8(eRows * eCols), ["#90A4AE"], document.createElement('style'), 0]
document.head.appendChild(css)
const updateColors = () => css.innerHTML = colors.map((c, i) => `.grid-line.c${i} { --c: ${c} }`).join('\n')
updateColors()
// Editing controls
let grid: HTMLDivElement
const editMode = true
let maskMode = false
let [editMode, dragging] = [true, false]
const modes = ['line', 'mask', 'color']
let mode = 'line'
// Checkpoints
interface Checkpoint { hStates: i8s, vStates: i8s, hColors: i8s, vColors: i8s, numbers: i8s, nMask: i8s, colors: string[] }
@@ -42,6 +45,7 @@
pt.hColors.forEach((n, idx) => hColors[idx] = n)
pt.vColors.forEach((n, idx) => vColors[idx] = n)
colors = pt.colors
updateColors()
}
// Run something on the edges of a cell
@@ -91,9 +95,19 @@
const updateArea = Array.from({ length: 5 }, (_, i) => Array.from({ length: 5 }, (_, j) =>
[i - 2, j - 2, +(Math.abs(i - 2) == 2 || Math.abs(j - 2) == 2)])).flat()
// Called when the user starts dragging
function startDrag(event: MouseEvent) {
if (mode === 'mask') return
dragging = true
}
// Called when the user clicks on the grid
function clickDiv(event: MouseEvent) {
if (maskMode) return
if (mode === 'mask') return
// Dragging is only for applying colors for now
const [move, color] = [event.type === 'mousemove', mode === 'color']
if (move && !color) return
// Compute the x and y coordinates of the clicked cell
const rect = grid.getBoundingClientRect()
@@ -109,9 +123,19 @@
const idx = (sy + osy) * eCols + (sx + osx)
// Flip the state of the edge
const state = event.type === 'click' ? 1 : 2
if (vertical) vStates[idx] = vStates[idx] === state ? 0 : state
else hStates[idx] = hStates[idx] === state ? 0 : state
const state = event.type === 'contextmenu' ? 2 : 1
if (color) {
// Press shift to select the entire block
if (event.shiftKey) {
}
if (vertical) vColors[idx] = ci
else hColors[idx] = ci
}
else {
if (vertical) vStates[idx] = vStates[idx] !== state ? state : 0
else hStates[idx] = hStates[idx] !== state ? state : 0
}
// Check the validity of the clicked cell and its neighbors (check 5x5 but clean only 3x3)
updateArea.forEach(([dx, dy, outer]) => outer ? 0 : clearAutoMark(sx + dx, sy + dy))
@@ -142,7 +166,7 @@
// Mask the numbers
function editModeClickNumber(event: MouseEvent, x: number, y: number) {
if (!maskMode) return
if (mode !== 'mask') return
nMask[y * cols + x] = nMask[y * cols + x] === 0 ? 1 : 0
updateArea.forEach(([dx, dy, outer]) => outer ? 0 : clearAutoMark(x + dx, y + dy))
updateArea.forEach(([dx, dy, _]) => checkPos(x + dx, y + dy))
@@ -178,7 +202,7 @@
}
</script>
<main>
<main class:color-mode={mode === 'color'}>
<div class="heading">
<img src={viteLogo} class="logo" alt="Vite Logo" />
<span class="title">Slither Link</span>
@@ -188,6 +212,7 @@
<div class="puzzle-grid" style={`height: ${rows * cfg.totalW}px; width: ${cols * cfg.totalW}px;`}
on:click={clickDiv} on:contextmenu={clickDiv} on:keypress={console.log} role="grid" tabindex="0"
on:mousedown={startDrag} on:mousemove={e => dragging && clickDiv(e)} on:mouseup={() => dragging = false}
bind:this={grid}>
{#each range(rows) as y}
{#each range(cols) as x}
@@ -209,7 +234,7 @@
{#if editMode}
<div class="btn-div">
{#if maskMode}
{#if mode === 'mask'}
<button on:click={() => {
solve(rows, cols, numbers, nMask).then(({horiStates, vertStates}) => {
horiStates.forEach((st, idx) => hStates[idx] = st)
@@ -227,8 +252,20 @@
<button on:click={() => alert("TODO")}>Gen Numbers</button>
<button on:click={() => JsonTy.download(ckpt(), 'slitherlink-checkpoint.json')}>Download</button>
{/if}
<button on:click={() => maskMode = !maskMode}>{maskMode ? "Line Mode" : "Mask Mode"}</button>
<button on:click={() => mode = modes[(modes.indexOf(mode) + 1) % modes.length]}>Mode: {mode}</button>
</div>
<!-- Edit Colors (input for now, maybe a color picker later) -->
{#if mode === 'color'}
<div class="btn-div">
<button on:click={() => colors = [...colors, "#90A4AE"]}>+</button>
{#each colors as color, idx}
<button class="color-picker" on:click={() => ci = idx} aria-label="color" class:selected={ci === idx}>
<input type="color" value={color} on:input={e => { colors[idx] = e.target.value; updateColors() }}/>
</button>
{/each}
</div>
{/if}
{/if}
<!-- Add Checkpoint -->
@@ -276,5 +313,20 @@
display: flex
gap: 1em
flex-wrap: wrap
justify-content: center
place-items: center
.color-picker
display: flex
flex-direction: column
padding: 1em 0 0 0
overflow: hidden
height: 42px
width: 30px
&.selected
border-color: #646cff
input
padding: 0
border: 0
</style>
+6 -3
View File
@@ -41,7 +41,7 @@ $color-cross: #ff7f7f
height: 4px
&.selected
background: $color-line
background: var(--c)
&.crossed:before, &.auto-crossed:before
content: "x"
@@ -51,6 +51,9 @@ $color-cross: #ff7f7f
top: -7px
&:hover
background: color.scale($color-line, $alpha: -50%)
background: var(--c)
opacity: 0.5
.color-mode .grid-line:not(.selected):not(.crossed):not(.auto-crossed)
background: var(--c)
opacity: 0.1