@connectorvol/tree 2.1.0 → 2.1.1

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.
@@ -31,11 +31,6 @@ export declare class ChessTree {
31
31
  * Возвращает родительский узел или null, если текущий узел — корень.
32
32
  */
33
33
  getCurrentNodeParent(): ChessTreeNode | null;
34
- /**
35
- * Представляет поиск родительского узла для произвольного узла дерева.
36
- * Возвращает родителя или null, если родитель не найден в индексе.
37
- */
38
- getParentNode(node: ChessTreeNode): ChessTreeNode | null;
39
34
  /**
40
35
  * Представляет функцию удаления варианта из дерева.
41
36
  * Удаляет указанный узел и все его дочерние узлы.
@@ -110,13 +110,6 @@ export class ChessTree {
110
110
  getCurrentNodeParent() {
111
111
  return this.findParentNode(this.currentNode);
112
112
  }
113
- /**
114
- * Представляет поиск родительского узла для произвольного узла дерева.
115
- * Возвращает родителя или null, если родитель не найден в индексе.
116
- */
117
- getParentNode(node) {
118
- return this.findParentNode(node);
119
- }
120
113
  /**
121
114
  * Представляет функцию удаления варианта из дерева.
122
115
  * Удаляет указанный узел и все его дочерние узлы.
@@ -24,8 +24,6 @@
24
24
  needSpace: boolean;
25
25
  needRenderParentChildrens?: boolean;
26
26
  pieceSet: PieceSet;
27
- /** Представляет режим виртуализации: не рекурсировать в первого ребёнка (следующая строка списка). */
28
- virtualStopMainRecursion?: boolean;
29
27
  }
30
28
 
31
29
  const {
@@ -35,7 +33,6 @@
35
33
  needSpace,
36
34
  needRenderParentChildrens = true,
37
35
  pieceSet,
38
- virtualStopMainRecursion = false,
39
36
  }: Props = $props();
40
37
 
41
38
  const { chessTree, onSelectNode, onDeleteVariant, selectable } = getGameContext();
@@ -67,7 +64,6 @@
67
64
  <!-- svelte-ignore a11y_no_static_element_interactions -->
68
65
  <ContextMenu.Root>
69
66
  <ContextMenu.Trigger
70
- data-node-id={chessNode.id}
71
67
  class={{
72
68
  "cursor-pointer ": selectable,
73
69
  "cursor-default": !selectable,
@@ -277,7 +273,7 @@
277
273
  {@render emptyMove()}
278
274
  {/if}
279
275
  {#if childMoves?.[0]}
280
- {#if !isForcedNodeId && !virtualStopMainRecursion}
276
+ {#if !isForcedNodeId}
281
277
  <Move
282
278
  {pieceSet}
283
279
  chessNode={chessNode.children[0]}
@@ -8,8 +8,6 @@ interface Props {
8
8
  needSpace: boolean;
9
9
  needRenderParentChildrens?: boolean;
10
10
  pieceSet: PieceSet;
11
- /** Представляет режим виртуализации: не рекурсировать в первого ребёнка (следующая строка списка). */
12
- virtualStopMainRecursion?: boolean;
13
11
  }
14
12
  declare const Move: import("svelte").Component<Props, {}, "">;
15
13
  type Move = ReturnType<typeof Move>;
@@ -1,19 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { ClassValue } from "svelte/elements";
3
- import { onMount } from "svelte";
4
- import { get } from "svelte/store";
5
- import { createVirtualizer } from "@tanstack/svelte-virtual";
6
-
7
3
  import { ChessTree } from "../(classes)/chessTree.svelte.js";
8
4
 
9
5
  import Move from "./Move.svelte";
10
6
  import { initGameContext } from "../(utils)/context.js";
11
- import {
12
- buildMainLineNodes,
13
- getVirtualRowIndexForNode,
14
- needSpaceForMainLineNode,
15
- } from "../(utils)/treeVirtualRows.js";
16
- import { setTreeViewerVirtualScrollApi } from "../(utils)/treeViewerVirtualScroll.js";
17
7
  import type { PieceSet } from "@connectorvol/shared";
18
8
 
19
9
  type Props = {
@@ -45,66 +35,8 @@
45
35
  onDeleteVariant,
46
36
  setChessFen,
47
37
  setChessboardFen,
48
- selectable,
38
+ selectable
49
39
  );
50
-
51
- let scrollEl = $state<HTMLDivElement | null>(null);
52
-
53
- const mainLineNodes = $derived(buildMainLineNodes(chessTree.rootNode.moves));
54
-
55
- const virtualizerStore = createVirtualizer({
56
- count: 0,
57
- getScrollElement: () => null,
58
- estimateSize: () => 96,
59
- overscan: 8,
60
- });
61
-
62
- $effect(() => {
63
- const v = get(virtualizerStore);
64
- v.setOptions({
65
- count: mainLineNodes.length,
66
- getScrollElement: scrollEl ? () => scrollEl : () => null,
67
- estimateSize: () => 96,
68
- overscan: 8,
69
- });
70
- });
71
-
72
- function measureRow(node: HTMLDivElement) {
73
- queueMicrotask(() => {
74
- get(virtualizerStore).measureElement(node);
75
- });
76
- return {};
77
- }
78
-
79
- onMount(() => {
80
- setTreeViewerVirtualScrollApi({
81
- scrollToCurrentNode: (behavior: ScrollBehavior = "instant") => {
82
- const v = get(virtualizerStore);
83
- const line = buildMainLineNodes(chessTree.rootNode.moves);
84
- if (line.length === 0) {
85
- return;
86
- }
87
- const idx = getVirtualRowIndexForNode(
88
- chessTree.currentNode,
89
- line,
90
- (n) => chessTree.getParentNode(n),
91
- );
92
- v.scrollToIndex(Math.min(idx, line.length - 1), {
93
- align: "center",
94
- behavior,
95
- });
96
- },
97
- scrollToStart: (behavior: ScrollBehavior = "instant") => {
98
- const v = get(virtualizerStore);
99
- const line = buildMainLineNodes(chessTree.rootNode.moves);
100
- if (line.length === 0) {
101
- return;
102
- }
103
- v.scrollToIndex(0, { align: "start", behavior });
104
- },
105
- });
106
- return () => setTreeViewerVirtualScrollApi(null);
107
- });
108
40
  </script>
109
41
 
110
42
  {#snippet comment(text: string)}
@@ -120,53 +52,31 @@
120
52
  {/snippet}
121
53
 
122
54
  <div
123
- bind:this={scrollEl}
124
55
  id="tree-viewer-container"
125
56
  class={[
126
- "flex-1 relative overflow-auto border border-gray-300 rounded-l-sm shadow-sm",
57
+ "flex-1 relative overflow-auto border border-gray-300 rounded-l-sm shadow-sm ",
127
58
  className,
128
59
  ]}
129
60
  role="region"
130
61
  aria-label="Chess move tree"
131
62
  oncontextmenu={(e) => e.preventDefault()}
132
63
  >
133
- <div class="min-w-0">
64
+ <div
65
+ class={{
66
+ "grid flex-1 grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none ": true,
67
+ }}
68
+ >
134
69
  {#if chessTree.rootNode.comments}
135
- <div
136
- class="grid grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none"
137
- >
138
- {#each chessTree.rootNode.comments as firstComment}
139
- {@render comment(firstComment)}
140
- {/each}
141
- </div>
70
+ {#each chessTree.rootNode.comments as firstComment}
71
+ {@render comment(firstComment)}
72
+ {/each}
142
73
  {/if}
143
-
144
- <div class="relative w-full" style="min-height: 1px;">
145
- <div
146
- class="relative w-full"
147
- style="height: {$virtualizerStore.getTotalSize()}px;"
148
- >
149
- {#each $virtualizerStore.getVirtualItems() as row (row.key)}
150
- {@const rowNode = mainLineNodes[row.index]}
151
- <div
152
- class="absolute left-0 top-0 w-full grid grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none"
153
- style="transform: translateY({row.start}px);"
154
- data-index={row.index}
155
- use:measureRow
156
- >
157
- <Move
158
- chessNode={rowNode}
159
- depth={1}
160
- needSpace={needSpaceForMainLineNode(rowNode, (n) =>
161
- chessTree.getParentNode(n),
162
- )}
163
- parentNode={chessTree.getParentNode(rowNode)}
164
- // virtualStopMainRecursion={true}
165
- {pieceSet}
166
- />
167
- </div>
168
- {/each}
169
- </div>
170
- </div>
74
+ <Move
75
+ chessNode={chessTree.rootNode.moves}
76
+ depth={1}
77
+ needSpace={false}
78
+ parentNode={null}
79
+ {pieceSet}
80
+ />
171
81
  </div>
172
82
  </div>
@@ -1,4 +1,3 @@
1
- import { getTreeViewerVirtualScrollApi } from "./treeViewerVirtualScroll.js";
2
1
  export const ACTIVE_MOVE_CLASS = "active-move-in-tree-viewer";
3
2
  /**
4
3
  * Представляет функцию для плавного скролла до активного хода в дереве ходов
@@ -6,11 +5,6 @@ export const ACTIVE_MOVE_CLASS = "active-move-in-tree-viewer";
6
5
  * @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
7
6
  */
8
7
  function scrollToActiveMove(containerId = "tree-viewer-container", behavior = "instant") {
9
- const virtual = getTreeViewerVirtualScrollApi();
10
- if (virtual) {
11
- virtual.scrollToCurrentNode(behavior);
12
- return;
13
- }
14
8
  const container = document.getElementById(containerId);
15
9
  if (!container) {
16
10
  console.warn(`Контейнер с ID "${containerId}" не найден`);
@@ -72,13 +66,6 @@ function isActiveMoveVisible(containerId = "tree-viewer-container") {
72
66
  * @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
73
67
  */
74
68
  export function scrollToActiveMoveIfNeeded(containerId = "tree-viewer-container", behavior = "instant") {
75
- const virtual = getTreeViewerVirtualScrollApi();
76
- if (virtual) {
77
- if (!isActiveMoveVisible(containerId)) {
78
- virtual.scrollToCurrentNode(behavior);
79
- }
80
- return;
81
- }
82
69
  if (!isActiveMoveVisible(containerId)) {
83
70
  scrollToActiveMove(containerId, behavior);
84
71
  }
@@ -1,15 +1,9 @@
1
- import { getTreeViewerVirtualScrollApi } from "./treeViewerVirtualScroll.js";
2
1
  /**
3
2
  * Представляет функцию для скролла к началу дерева ходов (первый видимый ход)
4
3
  * @param containerId - ID контейнера с деревом ходов
5
4
  * @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
6
5
  */
7
6
  export function scrollToStart(containerId = "tree-viewer-container", behavior = "instant") {
8
- const virtual = getTreeViewerVirtualScrollApi();
9
- if (virtual) {
10
- virtual.scrollToStart(behavior);
11
- return;
12
- }
13
7
  const container = document.getElementById(containerId);
14
8
  if (!container) {
15
9
  console.warn(`Контейнер с ID "${containerId}" не найден`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connectorvol/tree",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",
@@ -39,37 +39,34 @@
39
39
  "test:ui": "npx playwright test --ui"
40
40
  },
41
41
  "dependencies": {
42
- "@connectorvol/chessops": "1.0.0",
42
+ "@connectorvol/chessops": "2.0.0",
43
43
  "@connectorvol/shared": "2.0.0",
44
44
  "clsx": "^2.1.1",
45
- "tailwind-merge": "^3.3.1",
46
- "tailwind-variants": "^3.1.1"
45
+ "tailwind-merge": "3.3.1",
46
+ "tailwind-variants": "3.1.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@connectorvol/chessboard": "2.0.0",
49
+ "@connectorvol/chessboard": "2.1.0",
50
50
  "@ianvs/prettier-plugin-sort-imports": "4.5.1",
51
51
  "@internationalized/date": "3.9.0",
52
- "@lucide/svelte": "^0.548.0",
52
+ "@lucide/svelte": "^0.553.0",
53
53
  "@playwright/test": "1.54.1",
54
54
  "@sveltejs/adapter-static": "3.0.10",
55
55
  "@sveltejs/kit": "2.48.0",
56
56
  "@sveltejs/package": "2.4.0",
57
- "@sveltejs/vite-plugin-svelte": "6.0.0",
57
+ "@sveltejs/vite-plugin-svelte": "7.0.0",
58
58
  "@tailwindcss/forms": "0.5.10",
59
59
  "@tailwindcss/postcss": "4.1.11",
60
60
  "@tailwindcss/typography": "0.5.16",
61
61
  "@tailwindcss/vite": "4.1.11",
62
62
  "autoprefixer": "10.4.21",
63
63
  "mode-watcher": "^1.1.0",
64
- "prettier": "3.6.2",
65
- "prettier-plugin-svelte": "3.4.0",
66
- "prettier-plugin-tailwindcss": "0.6.14",
67
64
  "svelte-check": "4.3.2",
68
65
  "svelte-sonner": "^1.0.5",
69
66
  "tailwindcss": "4.1.11",
70
- "tw-animate-css": "^1.4.0",
71
- "typescript": "5.9.2",
72
- "vite": "^8.0.0",
67
+ "tw-animate-css": "1.4.0",
68
+ "typescript": "6.0.2",
69
+ "vite": "8.0.7",
73
70
  "vite-plugin-checker": "0.12.0",
74
71
  "vite-plugin-svelte-checker": "0.1.2"
75
72
  },
@@ -1,14 +0,0 @@
1
- /**
2
- * Представляет API скролла виртуализованного TreeViewer для соседних компонентов
3
- * (панель навигации не является потомком TreeViewer в DOM).
4
- */
5
- export type TreeViewerVirtualScrollApi = {
6
- /** Возвращает void после прокрутки к строке с текущим узлом. */
7
- scrollToCurrentNode: (behavior?: ScrollBehavior) => void;
8
- /** Возвращает void после прокрутки к началу списка ходов. */
9
- scrollToStart: (behavior?: ScrollBehavior) => void;
10
- };
11
- /** Представляет регистрацию API на время жизни TreeViewer. */
12
- export declare function setTreeViewerVirtualScrollApi(next: TreeViewerVirtualScrollApi | null): void;
13
- /** Представляет получение API скролла, если смонтирован TreeViewer. */
14
- export declare function getTreeViewerVirtualScrollApi(): TreeViewerVirtualScrollApi | null;
@@ -1,9 +0,0 @@
1
- let api = null;
2
- /** Представляет регистрацию API на время жизни TreeViewer. */
3
- export function setTreeViewerVirtualScrollApi(next) {
4
- api = next;
5
- }
6
- /** Представляет получение API скролла, если смонтирован TreeViewer. */
7
- export function getTreeViewerVirtualScrollApi() {
8
- return api;
9
- }
@@ -1,14 +0,0 @@
1
- import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
2
- /**
3
- * Представляет цепочку главной линии: от корневого узла ходов по первым дочерним узлам.
4
- */
5
- export declare function buildMainLineNodes(rootMoves: ChessTreeNode): ChessTreeNode[];
6
- /**
7
- * Представляет индекс строки виртуального списка для узла: главная линия или вариант,
8
- * отображаемый внутри строки первого ребёнка родителя.
9
- */
10
- export declare function getVirtualRowIndexForNode(node: ChessTreeNode, mainLine: ChessTreeNode[], getParent: (n: ChessTreeNode) => ChessTreeNode | null): number;
11
- /**
12
- * Представляет значение needSpace для узла главной линии (как у рекурсивного Move).
13
- */
14
- export declare function needSpaceForMainLineNode(node: ChessTreeNode, getParent: (n: ChessTreeNode) => ChessTreeNode | null): boolean;
@@ -1,43 +0,0 @@
1
- /**
2
- * Представляет цепочку главной линии: от корневого узла ходов по первым дочерним узлам.
3
- */
4
- export function buildMainLineNodes(rootMoves) {
5
- const out = [];
6
- let cur = rootMoves;
7
- while (cur) {
8
- out.push(cur);
9
- cur = cur.children[0];
10
- }
11
- return out;
12
- }
13
- /**
14
- * Представляет индекс строки виртуального списка для узла: главная линия или вариант,
15
- * отображаемый внутри строки первого ребёнка родителя.
16
- */
17
- export function getVirtualRowIndexForNode(node, mainLine, getParent) {
18
- const mainLineIndex = new Map(mainLine.map((n, i) => [n.id, i]));
19
- let cur = node;
20
- while (cur) {
21
- const idx = mainLineIndex.get(cur.id);
22
- if (idx !== undefined) {
23
- return idx;
24
- }
25
- const parent = getParent(cur);
26
- if (!parent) {
27
- return 0;
28
- }
29
- if (parent.children[0]?.id !== cur.id) {
30
- const anchor = parent.children[0];
31
- return anchor ? (mainLineIndex.get(anchor.id) ?? 0) : 0;
32
- }
33
- cur = parent;
34
- }
35
- return 0;
36
- }
37
- /**
38
- * Представляет значение needSpace для узла главной линии (как у рекурсивного Move).
39
- */
40
- export function needSpaceForMainLineNode(node, getParent) {
41
- const parent = getParent(node);
42
- return parent ? parent.children.length > 1 : false;
43
- }