@astronox/core 0.5.7 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/patterns/CircuitPuzzle/CircuitPuzzle.astro +22 -2
- package/src/patterns/CircuitPuzzle/CircuitPuzzle.css +24 -0
- package/src/patterns/LightMazePuzzle/LightMazePuzzle.astro +210 -0
- package/src/patterns/LightMazePuzzle/LightMazePuzzle.css +195 -0
- package/src/patterns/LightMazePuzzle/contracts.ts +75 -0
- package/src/patterns/LightMazePuzzle/lightMazePuzzleController.ts +684 -0
- package/src/patterns/LightMazePuzzle/presentation.ts +55 -0
- package/src/patterns/LightMazePuzzle/simulation.ts +164 -0
- package/src/patterns/MagneticFieldPuzzle/MagneticFieldPuzzle.astro +64 -3
- package/src/patterns/MagneticFieldPuzzle/MagneticFieldPuzzle.css +69 -0
- package/src/patterns/MagneticFieldPuzzle/contracts.ts +4 -0
- package/src/patterns/SlidingPuzzle/SlidingPuzzle.astro +8 -1
- package/src/patterns/SlidingPuzzle/SlidingPuzzle.css +13 -7
- package/src/patterns/index.ts +4 -0
- package/src/styles/style.css +1 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LightMazeMechanism,
|
|
3
|
+
LightMazePosition,
|
|
4
|
+
LightMazePuzzleLabels,
|
|
5
|
+
LightMazePuzzleModel,
|
|
6
|
+
} from './contracts';
|
|
7
|
+
|
|
8
|
+
export function presentLightMazeCell(
|
|
9
|
+
puzzle: LightMazePuzzleModel,
|
|
10
|
+
mechanism: LightMazeMechanism,
|
|
11
|
+
labels: LightMazePuzzleLabels,
|
|
12
|
+
position: LightMazePosition,
|
|
13
|
+
) {
|
|
14
|
+
const matches = (cell: LightMazePosition): boolean =>
|
|
15
|
+
cell.row === position.row && cell.column === position.column;
|
|
16
|
+
const activator =
|
|
17
|
+
puzzle.activators?.some((entry) => matches(entry.position)) ?? false;
|
|
18
|
+
const consumed = mechanism.activatedCells.some(matches);
|
|
19
|
+
const disabled = mechanism.blockedCells.some(matches);
|
|
20
|
+
const removableWall =
|
|
21
|
+
puzzle.activators?.some((entry) => entry.walls.some(matches)) ?? false;
|
|
22
|
+
const releasedWall = removableWall && !disabled;
|
|
23
|
+
const target = matches(puzzle.target);
|
|
24
|
+
const lit = mechanism.illuminatedCells.some(matches);
|
|
25
|
+
if ((activator || removableWall) && !labels.mechanism)
|
|
26
|
+
throw new Error('Light maze mechanisms require accessible labels.');
|
|
27
|
+
let activationLabel = '';
|
|
28
|
+
if (activator) {
|
|
29
|
+
activationLabel = consumed
|
|
30
|
+
? labels.mechanism!.activatorConsumed
|
|
31
|
+
: labels.mechanism!.activatorAvailable;
|
|
32
|
+
}
|
|
33
|
+
const label = disabled
|
|
34
|
+
? removableWall
|
|
35
|
+
? labels.mechanism!.removableWall
|
|
36
|
+
: labels.blockedCell
|
|
37
|
+
: [
|
|
38
|
+
target ? labels.target : '',
|
|
39
|
+
activationLabel,
|
|
40
|
+
releasedWall ? labels.mechanism!.releasedWall : '',
|
|
41
|
+
lit ? labels.litCell : labels.darkCell,
|
|
42
|
+
]
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join(' — ');
|
|
45
|
+
return {
|
|
46
|
+
disabled,
|
|
47
|
+
target,
|
|
48
|
+
lit,
|
|
49
|
+
activator,
|
|
50
|
+
consumed,
|
|
51
|
+
removableWall,
|
|
52
|
+
releasedWall,
|
|
53
|
+
label,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LightMazeAction,
|
|
3
|
+
LightMazeDirection,
|
|
4
|
+
LightMazeMovement,
|
|
5
|
+
LightMazeMechanism,
|
|
6
|
+
LightMazePosition,
|
|
7
|
+
LightMazePuzzleModel,
|
|
8
|
+
LightMazePuzzleState,
|
|
9
|
+
} from './contracts';
|
|
10
|
+
|
|
11
|
+
const deltas = {
|
|
12
|
+
north: [-1, 0],
|
|
13
|
+
east: [0, 1],
|
|
14
|
+
south: [1, 0],
|
|
15
|
+
west: [0, -1],
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
function same(a: LightMazePosition, b: LightMazePosition): boolean {
|
|
19
|
+
return a.row === b.row && a.column === b.column;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function free(
|
|
23
|
+
puzzle: LightMazePuzzleModel,
|
|
24
|
+
position: LightMazePosition,
|
|
25
|
+
blockedCells: readonly LightMazePosition[],
|
|
26
|
+
): boolean {
|
|
27
|
+
return (
|
|
28
|
+
position.row >= 0 &&
|
|
29
|
+
position.row < puzzle.rows &&
|
|
30
|
+
position.column >= 0 &&
|
|
31
|
+
position.column < puzzle.columns &&
|
|
32
|
+
!blockedCells.some((cell) => same(cell, position))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createLightMazePuzzleState(
|
|
37
|
+
puzzle: LightMazePuzzleModel,
|
|
38
|
+
): LightMazePuzzleState {
|
|
39
|
+
return {
|
|
40
|
+
walker: { ...puzzle.start },
|
|
41
|
+
light: { ...puzzle.light },
|
|
42
|
+
activated: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function blockedCells(
|
|
47
|
+
puzzle: LightMazePuzzleModel,
|
|
48
|
+
state: LightMazePuzzleState,
|
|
49
|
+
): LightMazePosition[] {
|
|
50
|
+
return [
|
|
51
|
+
...puzzle.blockedCells,
|
|
52
|
+
...(puzzle.activators ?? []).flatMap((activator, index) =>
|
|
53
|
+
state.activated.includes(index) ? [] : activator.walls,
|
|
54
|
+
),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isLightMazePuzzleComplete(
|
|
59
|
+
puzzle: LightMazePuzzleModel,
|
|
60
|
+
state: LightMazePuzzleState,
|
|
61
|
+
): boolean {
|
|
62
|
+
return same(state.walker, puzzle.target);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getLightMazeIllumination(
|
|
66
|
+
puzzle: LightMazePuzzleModel,
|
|
67
|
+
state: LightMazePuzzleState,
|
|
68
|
+
): LightMazePosition[] {
|
|
69
|
+
const cells = [{ ...state.light }];
|
|
70
|
+
const blocked = blockedCells(puzzle, state);
|
|
71
|
+
for (const [dr, dc] of Object.values(deltas)) {
|
|
72
|
+
for (let distance = 1; distance <= 2; distance += 1) {
|
|
73
|
+
const cell = {
|
|
74
|
+
row: state.light.row + dr * distance,
|
|
75
|
+
column: state.light.column + dc * distance,
|
|
76
|
+
};
|
|
77
|
+
if (!free(puzzle, cell, blocked)) break;
|
|
78
|
+
cells.push(cell);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return cells;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function evaluateLightMazeMechanism(
|
|
85
|
+
puzzle: LightMazePuzzleModel,
|
|
86
|
+
state: LightMazePuzzleState,
|
|
87
|
+
): LightMazeMechanism {
|
|
88
|
+
const illuminatedCells = getLightMazeIllumination(puzzle, state);
|
|
89
|
+
return {
|
|
90
|
+
illuminatedCells,
|
|
91
|
+
blockedCells: blockedCells(puzzle, state),
|
|
92
|
+
activatedCells: (puzzle.activators ?? []).flatMap((activator, index) =>
|
|
93
|
+
state.activated.includes(index) ? [activator.position] : [],
|
|
94
|
+
),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function simulateLightMazeMove(
|
|
99
|
+
puzzle: LightMazePuzzleModel,
|
|
100
|
+
state: LightMazePuzzleState,
|
|
101
|
+
action: LightMazeAction,
|
|
102
|
+
): LightMazeMovement | null {
|
|
103
|
+
if (isLightMazePuzzleComplete(puzzle, state)) return null;
|
|
104
|
+
const mechanism = evaluateLightMazeMechanism(puzzle, state);
|
|
105
|
+
const traversable = (position: LightMazePosition): boolean =>
|
|
106
|
+
free(puzzle, position, mechanism.blockedCells);
|
|
107
|
+
const [dr, dc] = deltas[action.direction];
|
|
108
|
+
const from = state[action.piece];
|
|
109
|
+
let to = { ...from };
|
|
110
|
+
let stoppedByWalker = false;
|
|
111
|
+
const crossed: LightMazePosition[] = [];
|
|
112
|
+
if (action.piece === 'walker') {
|
|
113
|
+
to = { row: from.row + dr, column: from.column + dc };
|
|
114
|
+
if (
|
|
115
|
+
!traversable(to) ||
|
|
116
|
+
same(to, state.light) ||
|
|
117
|
+
puzzle.activators?.some((activator) => same(activator.position, to)) ||
|
|
118
|
+
!mechanism.illuminatedCells.some((cell) => same(cell, to))
|
|
119
|
+
)
|
|
120
|
+
return null;
|
|
121
|
+
} else {
|
|
122
|
+
while (true) {
|
|
123
|
+
const next = { row: to.row + dr, column: to.column + dc };
|
|
124
|
+
if (!traversable(next)) break;
|
|
125
|
+
if (same(next, state.walker)) {
|
|
126
|
+
stoppedByWalker = true;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
to = next;
|
|
130
|
+
crossed.push(next);
|
|
131
|
+
}
|
|
132
|
+
if (same(from, to)) return null;
|
|
133
|
+
}
|
|
134
|
+
const activated = (puzzle.activators ?? []).flatMap((activator, index) =>
|
|
135
|
+
state.activated.includes(index) ||
|
|
136
|
+
crossed.some((position) => same(activator.position, position))
|
|
137
|
+
? [index]
|
|
138
|
+
: [],
|
|
139
|
+
);
|
|
140
|
+
return {
|
|
141
|
+
...action,
|
|
142
|
+
from: { ...from },
|
|
143
|
+
to,
|
|
144
|
+
stoppedByWalker,
|
|
145
|
+
state: {
|
|
146
|
+
walker: { ...state.walker },
|
|
147
|
+
light: { ...state.light },
|
|
148
|
+
[action.piece]: to,
|
|
149
|
+
activated,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function getLightMazeMoves(
|
|
155
|
+
puzzle: LightMazePuzzleModel,
|
|
156
|
+
state: LightMazePuzzleState,
|
|
157
|
+
): LightMazeMovement[] {
|
|
158
|
+
return (['walker', 'light'] as const).flatMap((piece) =>
|
|
159
|
+
(Object.keys(deltas) as LightMazeDirection[]).flatMap((direction) => {
|
|
160
|
+
const move = simulateLightMazeMove(puzzle, state, { piece, direction });
|
|
161
|
+
return move ? [move] : [];
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -4,14 +4,17 @@ import MinusIcon from '../../components/icons/MinusIcon.astro';
|
|
|
4
4
|
import PlusIcon from '../../components/icons/PlusIcon.astro';
|
|
5
5
|
import SlashIcon from '../../components/icons/SlashIcon.astro';
|
|
6
6
|
import PuzzleGrid from '../PuzzleGrid/PuzzleGrid.astro';
|
|
7
|
+
import Media from '../../primitives/media/Media/Media.astro';
|
|
7
8
|
import type {
|
|
8
9
|
MagneticFieldPuzzleLabels,
|
|
9
10
|
MagneticFieldPuzzleModel,
|
|
11
|
+
MagneticFieldPuzzleMedia,
|
|
10
12
|
} from './contracts';
|
|
11
13
|
|
|
12
14
|
type Props = HTMLAttributes<'section'> & {
|
|
13
15
|
label: string;
|
|
14
16
|
labels: MagneticFieldPuzzleLabels;
|
|
17
|
+
media?: MagneticFieldPuzzleMedia;
|
|
15
18
|
puzzles: readonly MagneticFieldPuzzleModel[];
|
|
16
19
|
};
|
|
17
20
|
|
|
@@ -19,9 +22,16 @@ const {
|
|
|
19
22
|
class: className,
|
|
20
23
|
label,
|
|
21
24
|
labels,
|
|
25
|
+
media,
|
|
22
26
|
puzzles,
|
|
23
27
|
...attrs
|
|
24
28
|
}: Props = Astro.props;
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
media &&
|
|
32
|
+
puzzles.some((puzzle) => puzzle.pieces.some((piece) => !media[piece.id]))
|
|
33
|
+
)
|
|
34
|
+
throw new Error('Magnetic field puzzle media must cover every piece.');
|
|
25
35
|
---
|
|
26
36
|
|
|
27
37
|
<section
|
|
@@ -46,7 +56,53 @@ const {
|
|
|
46
56
|
data-puzzle-id={puzzle.id}
|
|
47
57
|
data-puzzle-status="incomplete"
|
|
48
58
|
hidden={puzzleIndex !== 0}
|
|
59
|
+
style={
|
|
60
|
+
media
|
|
61
|
+
? `--piece-selection-filter: url(#magnetic-outline-${encodeURIComponent(puzzle.id)}); --piece-hover-filter: url(#magnetic-hover-${encodeURIComponent(puzzle.id)});`
|
|
62
|
+
: undefined
|
|
63
|
+
}
|
|
49
64
|
>
|
|
65
|
+
{media && (
|
|
66
|
+
<svg
|
|
67
|
+
class="magnetic-field-puzzle__filters"
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
focusable="false"
|
|
70
|
+
>
|
|
71
|
+
<defs>
|
|
72
|
+
<filter
|
|
73
|
+
id={`magnetic-hover-${encodeURIComponent(puzzle.id)}`}
|
|
74
|
+
color-interpolation-filters="sRGB"
|
|
75
|
+
>
|
|
76
|
+
<feFlood
|
|
77
|
+
class="magnetic-field-puzzle__hover-color"
|
|
78
|
+
flood-opacity="0.1"
|
|
79
|
+
/>
|
|
80
|
+
<feComposite operator="in" in2="SourceAlpha" />
|
|
81
|
+
<feMerge>
|
|
82
|
+
<feMergeNode in="SourceGraphic" />
|
|
83
|
+
<feMergeNode />
|
|
84
|
+
</feMerge>
|
|
85
|
+
</filter>
|
|
86
|
+
<filter
|
|
87
|
+
id={`magnetic-outline-${encodeURIComponent(puzzle.id)}`}
|
|
88
|
+
x="-25%"
|
|
89
|
+
y="-25%"
|
|
90
|
+
width="150%"
|
|
91
|
+
height="150%"
|
|
92
|
+
color-interpolation-filters="sRGB"
|
|
93
|
+
>
|
|
94
|
+
<feMorphology in="SourceAlpha" operator="dilate" radius="2" />
|
|
95
|
+
<feGaussianBlur stdDeviation="0.5" result="outline" />
|
|
96
|
+
<feFlood class="magnetic-field-puzzle__ring-color" />
|
|
97
|
+
<feComposite operator="in" in2="outline" />
|
|
98
|
+
<feMerge>
|
|
99
|
+
<feMergeNode />
|
|
100
|
+
<feMergeNode in="SourceGraphic" />
|
|
101
|
+
</feMerge>
|
|
102
|
+
</filter>
|
|
103
|
+
</defs>
|
|
104
|
+
</svg>
|
|
105
|
+
)}
|
|
50
106
|
<PuzzleGrid
|
|
51
107
|
class="magnetic-field-puzzle__board"
|
|
52
108
|
label={puzzle.label}
|
|
@@ -158,6 +214,7 @@ const {
|
|
|
158
214
|
aria-label={`${piece.label} — ${labels.polarities[piece.polarity]}`}
|
|
159
215
|
aria-pressed="false"
|
|
160
216
|
data-field-piece={piece.id}
|
|
217
|
+
data-field-piece-media={media ? '' : undefined}
|
|
161
218
|
data-piece-label={piece.label}
|
|
162
219
|
data-piece-short-label={piece.shortLabel}
|
|
163
220
|
data-piece-row={piece.row}
|
|
@@ -175,9 +232,13 @@ const {
|
|
|
175
232
|
class="magnetic-field-puzzle__piece-core"
|
|
176
233
|
aria-hidden="true"
|
|
177
234
|
>
|
|
178
|
-
|
|
179
|
-
<
|
|
180
|
-
|
|
235
|
+
{media ? (
|
|
236
|
+
<Media media={media[piece.id]!} size="fill" />
|
|
237
|
+
) : (
|
|
238
|
+
<span class="magnetic-field-puzzle__piece-iris">
|
|
239
|
+
<span class="magnetic-field-puzzle__piece-pupil" />
|
|
240
|
+
</span>
|
|
241
|
+
)}
|
|
181
242
|
<span class="magnetic-field-puzzle__piece-identity-region">
|
|
182
243
|
<span
|
|
183
244
|
class="magnetic-field-puzzle__identity"
|
|
@@ -19,6 +19,21 @@
|
|
|
19
19
|
display: none;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
.magnetic-field-puzzle__filters {
|
|
23
|
+
position: absolute;
|
|
24
|
+
inline-size: 0;
|
|
25
|
+
block-size: 0;
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.magnetic-field-puzzle__ring-color {
|
|
30
|
+
flood-color: var(--nox-source-tone-info);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.magnetic-field-puzzle__hover-color {
|
|
34
|
+
flood-color: var(--nox-tone-color);
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
.magnetic-field-puzzle__board {
|
|
23
38
|
isolation: isolate;
|
|
24
39
|
touch-action: manipulation;
|
|
@@ -287,6 +302,60 @@
|
|
|
287
302
|
);
|
|
288
303
|
}
|
|
289
304
|
|
|
305
|
+
.magnetic-field-puzzle__piece[data-field-piece-media] {
|
|
306
|
+
border: var(--nox-border-width) solid transparent;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]
|
|
310
|
+
.magnetic-field-puzzle__piece-core {
|
|
311
|
+
inline-size: 84%;
|
|
312
|
+
border: 0;
|
|
313
|
+
border-radius: 0;
|
|
314
|
+
background: none;
|
|
315
|
+
box-shadow: none;
|
|
316
|
+
container-type: inline-size;
|
|
317
|
+
pointer-events: none;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]
|
|
321
|
+
.magnetic-field-puzzle__piece-identity-region {
|
|
322
|
+
inset-block: 68% 14%;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]
|
|
326
|
+
.magnetic-field-puzzle__identity {
|
|
327
|
+
font-size: clamp(0.625rem, 22cqi, var(--nox-font-size-md));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]:is(
|
|
331
|
+
[data-piece-selected],
|
|
332
|
+
:focus-visible
|
|
333
|
+
)
|
|
334
|
+
.magnetic-field-puzzle__piece-core {
|
|
335
|
+
filter: var(--piece-selection-filter);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@media (hover: hover) {
|
|
339
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]:hover
|
|
340
|
+
.magnetic-field-puzzle__piece-core {
|
|
341
|
+
filter: var(--piece-hover-filter);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.magnetic-field-puzzle__piece[data-field-piece-media]:is(
|
|
345
|
+
[data-piece-selected],
|
|
346
|
+
:focus-visible
|
|
347
|
+
):hover
|
|
348
|
+
.magnetic-field-puzzle__piece-core {
|
|
349
|
+
filter: var(--piece-hover-filter) var(--piece-selection-filter);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@media (width < 34rem) {
|
|
354
|
+
.magnetic-field-puzzle__piece[data-field-piece-media] {
|
|
355
|
+
margin: 2px;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
290
359
|
@media (prefers-reduced-motion: no-preference) {
|
|
291
360
|
.magnetic-field-puzzle__piece[data-piece-moved] {
|
|
292
361
|
animation: magnetic-field-slide 260ms var(--nox-motion-easing-standard);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { DisplayMedia } from '../../display/contracts';
|
|
2
|
+
|
|
3
|
+
export type MagneticFieldPuzzleMedia = Readonly<Record<string, DisplayMedia>>;
|
|
4
|
+
|
|
1
5
|
export type MagneticFieldDirection = 'north' | 'east' | 'south' | 'west';
|
|
2
6
|
export type MagneticFieldPolarity = 'north' | 'south';
|
|
3
7
|
export type MagneticFieldPupilPosition = 'upper-end' | 'center' | 'lower-start';
|
|
@@ -147,7 +147,14 @@ const {
|
|
|
147
147
|
</svg>
|
|
148
148
|
)}
|
|
149
149
|
{hole && (
|
|
150
|
-
<
|
|
150
|
+
<svg
|
|
151
|
+
class="sliding-puzzle__hole"
|
|
152
|
+
viewBox="0 0 100 100"
|
|
153
|
+
aria-hidden="true"
|
|
154
|
+
focusable="false"
|
|
155
|
+
>
|
|
156
|
+
<circle cx="50" cy="50" r="46.5" />
|
|
157
|
+
</svg>
|
|
151
158
|
)}
|
|
152
159
|
{target && (
|
|
153
160
|
<span class="sliding-puzzle__target" aria-hidden="true">
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
|
|
70
70
|
.sliding-puzzle__hole {
|
|
71
71
|
display: none;
|
|
72
|
-
inline-size:
|
|
72
|
+
inline-size: 84%;
|
|
73
73
|
aspect-ratio: 1;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
stroke: var(--sliding-transfer-color);
|
|
75
|
+
stroke-width: 2.5;
|
|
76
|
+
fill: color-mix(
|
|
77
77
|
in srgb,
|
|
78
78
|
var(--nox-source-tone-neutral) 20%,
|
|
79
79
|
var(--nox-mix-dark)
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
|
|
100
100
|
.sliding-puzzle__target {
|
|
101
101
|
display: grid;
|
|
102
|
-
inline-size:
|
|
102
|
+
inline-size: 84%;
|
|
103
103
|
aspect-ratio: 1;
|
|
104
104
|
pointer-events: none;
|
|
105
105
|
}
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
margin: var(--nox-space-1);
|
|
116
116
|
padding: 0;
|
|
117
117
|
place-items: center;
|
|
118
|
-
border:
|
|
118
|
+
border: var(--nox-border-width) solid transparent;
|
|
119
119
|
border-radius: var(--nox-radius-sm);
|
|
120
120
|
background: transparent;
|
|
121
121
|
cursor: grab;
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
|
|
126
126
|
.sliding-puzzle__piece-media {
|
|
127
127
|
display: grid;
|
|
128
|
-
inline-size:
|
|
128
|
+
inline-size: 84%;
|
|
129
129
|
aspect-ratio: 1;
|
|
130
130
|
pointer-events: none;
|
|
131
131
|
}
|
|
@@ -169,6 +169,12 @@
|
|
|
169
169
|
cursor: default;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
@media (width < 34rem) {
|
|
173
|
+
.sliding-puzzle__piece {
|
|
174
|
+
margin: 2px;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
172
178
|
@media (prefers-reduced-motion: no-preference) {
|
|
173
179
|
.sliding-puzzle__piece[data-piece-moved] {
|
|
174
180
|
animation: sliding-puzzle-move 260ms var(--nox-motion-easing-standard);
|
package/src/patterns/index.ts
CHANGED
|
@@ -11,6 +11,10 @@ export { default as ModalClose } from './Modal/ModalClose.astro';
|
|
|
11
11
|
export { default as ModalOpen } from './Modal/ModalOpen.astro';
|
|
12
12
|
export { default as PuzzleGrid } from './PuzzleGrid/PuzzleGrid.astro';
|
|
13
13
|
export { default as SlidingPuzzle } from './SlidingPuzzle/SlidingPuzzle.astro';
|
|
14
|
+
export { default as LightMazePuzzle } from './LightMazePuzzle/LightMazePuzzle.astro';
|
|
15
|
+
export * from './LightMazePuzzle/contracts';
|
|
16
|
+
export * from './LightMazePuzzle/simulation';
|
|
17
|
+
export * from './LightMazePuzzle/lightMazePuzzleController';
|
|
14
18
|
export * from './SlidingPuzzle/contracts';
|
|
15
19
|
export * from './SlidingPuzzle/simulation';
|
|
16
20
|
export * from './SlidingPuzzle/slidingPuzzleController';
|
package/src/styles/style.css
CHANGED
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
@import '../patterns/PuzzleGrid/PuzzleGrid.css' layer(patterns);
|
|
66
66
|
@import '../patterns/PuzzleGrid/movement.css' layer(patterns);
|
|
67
67
|
@import '../patterns/SlidingPuzzle/SlidingPuzzle.css' layer(patterns);
|
|
68
|
+
@import '../patterns/LightMazePuzzle/LightMazePuzzle.css' layer(patterns);
|
|
68
69
|
@import '../patterns/CircuitPuzzle/CircuitPuzzle.css' layer(patterns);
|
|
69
70
|
@import '../patterns/Combobox/Combobox.css' layer(patterns);
|
|
70
71
|
@import '../patterns/Dropdown/Dropdown.css' layer(patterns);
|