@connectorvol/chess-widgets 1.0.0

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.
Files changed (35) hide show
  1. package/dist/button-variants.d.ts +73 -0
  2. package/dist/button-variants.js +31 -0
  3. package/dist/constants/editable-board-settings.d.ts +26 -0
  4. package/dist/constants/editable-board-settings.js +27 -0
  5. package/dist/index.d.ts +12 -0
  6. package/dist/index.js +8 -0
  7. package/dist/position-editor/EditFen.svelte +164 -0
  8. package/dist/position-editor/EditFen.svelte.d.ts +16 -0
  9. package/dist/position-editor/EditMove.svelte +180 -0
  10. package/dist/position-editor/EditMove.svelte.d.ts +9 -0
  11. package/dist/position-editor/EditPanel.svelte +164 -0
  12. package/dist/position-editor/EditPanel.svelte.d.ts +8 -0
  13. package/dist/position-editor/fen.svelte.d.ts +26 -0
  14. package/dist/position-editor/fen.svelte.js +177 -0
  15. package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
  16. package/dist/puzzle/puzzleCreatedPayload.js +24 -0
  17. package/dist/puzzle/puzzleData.d.ts +32 -0
  18. package/dist/puzzle/puzzleData.js +51 -0
  19. package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +14 -0
  20. package/dist/puzzle/puzzleSolverForkAnnotations.js +94 -0
  21. package/dist/puzzle/puzzleStepPreviewSolver.d.ts +39 -0
  22. package/dist/puzzle/puzzleStepPreviewSolver.js +87 -0
  23. package/dist/puzzle-creation/PuzzleCreationWizard.svelte +247 -0
  24. package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +5 -0
  25. package/dist/puzzle-creation/StepMoves.svelte +225 -0
  26. package/dist/puzzle-creation/StepMoves.svelte.d.ts +15 -0
  27. package/dist/puzzle-creation/StepPosition.svelte +210 -0
  28. package/dist/puzzle-creation/StepPosition.svelte.d.ts +11 -0
  29. package/dist/puzzle-creation/StepPreview.svelte +589 -0
  30. package/dist/puzzle-creation/StepPreview.svelte.d.ts +23 -0
  31. package/dist/puzzle-creation/types.d.ts +27 -0
  32. package/dist/puzzle-creation/types.js +1 -0
  33. package/dist/utils.d.ts +15 -0
  34. package/dist/utils.js +8 -0
  35. package/package.json +76 -0
@@ -0,0 +1,210 @@
1
+ <script lang="ts">
2
+ import { untrack } from "svelte";
3
+
4
+ import {
5
+ Chessboard,
6
+ CHESSBOARD_THEMES,
7
+ createBoardApi,
8
+ type ChessboardTheme,
9
+ } from "@connectorvol/chessboard";
10
+
11
+ import { DEFAULT_EDITABLE_BOARD_SETTINGS } from "../constants/editable-board-settings.js";
12
+ import EditFen from "../position-editor/EditFen.svelte";
13
+ import EditMove from "../position-editor/EditMove.svelte";
14
+ import EditPanel from "../position-editor/EditPanel.svelte";
15
+ import { Fen } from "../position-editor/fen.svelte.js";
16
+ import { Popover } from "bits-ui";
17
+ import { buttonVariants } from "../button-variants.js";
18
+ import { cn } from "../utils.js";
19
+ import { Color } from "@connectorvol/shared";
20
+ import type { TChessboardAppearanceSettings } from "./types.js";
21
+
22
+ interface Props {
23
+ initialFen: string;
24
+ onNext: (fen: string) => void;
25
+ boardTheme?: ChessboardTheme;
26
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
27
+ }
28
+
29
+ const props: Props = $props();
30
+
31
+ /** Настройки доски мастера задачи: фиксированный размер, без ручного resize. */
32
+ const puzzleStepBoardSettings = {
33
+ ...DEFAULT_EDITABLE_BOARD_SETTINGS,
34
+ ...(props.boardAppearanceSettings ?? {}),
35
+ boardSize: 29,
36
+ isResizable: false,
37
+ editSettings: DEFAULT_EDITABLE_BOARD_SETTINGS.editSettings,
38
+ };
39
+
40
+ let chessboard = $state(
41
+ createBoardApi({
42
+ fen: (() => props.initialFen)(),
43
+ settings: puzzleStepBoardSettings,
44
+ theme: props.boardTheme ?? CHESSBOARD_THEMES.blue,
45
+ }),
46
+ );
47
+
48
+ /** Один объект Fen на доску: `$derived(new Fen(...))` пересоздавал бы класс при смене orientation и сбрасывал бы «Установить ход». */
49
+ const fen = new Fen(chessboard);
50
+
51
+ /**
52
+ * Представляет подстановку полей FEN (ход, рокировка, счётчики) из строки родителя при смене стартовой позиции.
53
+ */
54
+ $effect(() => {
55
+ const src = props.initialFen;
56
+ untrack(() => {
57
+ try {
58
+ fen.fullFen = src;
59
+ } catch {
60
+ /* некорректная строка FEN — оставляем текущее состояние Fen */
61
+ }
62
+ });
63
+ });
64
+
65
+ $effect(() => {
66
+ const sideToMove = fen.move;
67
+ const targetOrientation = sideToMove === "w" ? Color.WHITE : Color.BLACK;
68
+ if (chessboard.orientation !== targetOrientation) {
69
+ chessboard.orientation = targetOrientation;
70
+ }
71
+ });
72
+
73
+ function handleNext() {
74
+ props.onNext(fen.fullFen);
75
+ }
76
+ </script>
77
+
78
+ <div class="flex flex-col gap-3 pt-2 pb-0">
79
+ <div class="flex w-full flex-wrap items-center gap-2">
80
+ <h2 class="text-xl font-semibold">Шаг 1: Создание позиции</h2>
81
+ <Popover.Root>
82
+ <Popover.Trigger type="button">
83
+ {#snippet child({ props })}
84
+ <button
85
+ {...props}
86
+ type="button"
87
+ class={cn(
88
+ buttonVariants({ variant: "outline", size: "icon-sm" }),
89
+ "size-7 shrink-0 rounded-full text-sm font-semibold",
90
+ )}
91
+ aria-label="Справка: создание стартовой позиции"
92
+ >
93
+ ?
94
+ </button>
95
+ {/snippet}
96
+ </Popover.Trigger>
97
+ <Popover.Portal>
98
+ <Popover.Content
99
+ side="bottom"
100
+ align="start"
101
+ sideOffset={8}
102
+ class={cn(
103
+ "bg-popover text-popover-foreground border-border z-50 max-h-[min(70vh,32rem)] w-[min(calc(100vw-2rem),28rem)] overflow-y-auto rounded-lg border p-4 text-sm shadow-md outline-none",
104
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
105
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
106
+ )}
107
+ >
108
+ <p class="text-muted-foreground leading-relaxed">
109
+ Установите начальную позицию, с которой ученик будет искать решение.
110
+ В поле «Установить ход» доска поворачивается так, что ходящая
111
+ сторона оказывается у нижнего края, а палитры фигур меняются
112
+ местами.
113
+ </p>
114
+ </Popover.Content>
115
+ </Popover.Portal>
116
+ </Popover.Root>
117
+ </div>
118
+
119
+ <div class="flex flex-col md:flex-row gap-3">
120
+ <div class="flex w-full max-w-full flex-col justify-center gap-2">
121
+ {#snippet boardRow()}
122
+ <div
123
+ class="flex w-full max-w-full flex-col gap-3 md:flex-row md:items-start items-center"
124
+ >
125
+ <!-- Явная ширина: при родителе с w-fit/min-content BoardContainer даёт min(100%, Nrem), и 100% может схлопнуться до нуля. -->
126
+ <div
127
+ class="shrink-0 max-w-full"
128
+ style="width: {puzzleStepBoardSettings.boardSize}rem; max-width: 100%;"
129
+ >
130
+ <Chessboard facade={chessboard} />
131
+ </div>
132
+ <div
133
+ class="hidden min-w-0 flex-1 flex-col gap-3 md:flex md:min-w-[280px]"
134
+ >
135
+ <EditFen {fen} api={chessboard} constrainToBoardWidth={false} />
136
+ <div class="space-y-2">
137
+ <label
138
+ for="puzzle-start-fullmove-md"
139
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
140
+ >
141
+ Номер хода в записи партии
142
+ </label>
143
+ <input
144
+ id="puzzle-start-fullmove-md"
145
+ type="number"
146
+ min="1"
147
+ class={cn(
148
+ "border-input bg-background ring-offset-background shadow-xs flex h-9 w-full rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] md:text-sm",
149
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
150
+ )}
151
+ value={fen.fullmove}
152
+ oninput={(e) => {
153
+ const n = Number.parseInt(e.currentTarget.value, 10);
154
+ if (!Number.isFinite(n) || n < 1) return;
155
+ fen.fullmove = n;
156
+ }}
157
+ />
158
+ </div>
159
+ <EditMove {fen} api={chessboard} />
160
+ </div>
161
+ </div>
162
+ {/snippet}
163
+ <!-- Без {#if} по fen.move: иначе при смене стороны размонтируется boardRow/EditMove и bind у select ломается до следующего взаимодействия. -->
164
+ <div class={fen.move === "w" ? "order-1" : "order-3"}>
165
+ <EditPanel api={chessboard} color="b" />
166
+ </div>
167
+ <div class="order-2">{@render boardRow()}</div>
168
+ <div class={fen.move === "w" ? "order-3" : "order-1"}>
169
+ <EditPanel api={chessboard} color="w" />
170
+ </div>
171
+ <div class="md:hidden flex w-full flex-col gap-3">
172
+ <EditFen {fen} api={chessboard} constrainToBoardWidth={false} />
173
+ <div class="space-y-2">
174
+ <label
175
+ for="puzzle-start-fullmove-mobile"
176
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
177
+ >
178
+ Номер хода в записи партии
179
+ </label>
180
+ <input
181
+ id="puzzle-start-fullmove-mobile"
182
+ type="number"
183
+ min="1"
184
+ class={cn(
185
+ "border-input bg-background ring-offset-background shadow-xs flex h-9 w-full rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] md:text-sm",
186
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
187
+ )}
188
+ value={fen.fullmove}
189
+ oninput={(e) => {
190
+ const n = Number.parseInt(e.currentTarget.value, 10);
191
+ if (!Number.isFinite(n) || n < 1) return;
192
+ fen.fullmove = n;
193
+ }}
194
+ />
195
+ </div>
196
+ <EditMove {fen} api={chessboard} />
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <div class="flex justify-end">
202
+ <button
203
+ type="button"
204
+ class={cn(buttonVariants({ variant: "default" }))}
205
+ onclick={handleNext}
206
+ >
207
+ Далее
208
+ </button>
209
+ </div>
210
+ </div>
@@ -0,0 +1,11 @@
1
+ import { type ChessboardTheme } from "@connectorvol/chessboard";
2
+ import type { TChessboardAppearanceSettings } from "./types.js";
3
+ interface Props {
4
+ initialFen: string;
5
+ onNext: (fen: string) => void;
6
+ boardTheme?: ChessboardTheme;
7
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
8
+ }
9
+ declare const StepPosition: import("svelte").Component<Props, {}, "">;
10
+ type StepPosition = ReturnType<typeof StepPosition>;
11
+ export default StepPosition;