@connectorvol/tree 2.1.2 → 4.1.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 (52) hide show
  1. package/dist/(classes)/chessTree.svelte.d.ts +83 -0
  2. package/dist/(classes)/chessTree.svelte.js +198 -0
  3. package/dist/(components)/DrillBreadcrumbs.svelte +135 -0
  4. package/dist/(components)/DrillBreadcrumbs.svelte.d.ts +20 -0
  5. package/dist/(components)/DrillForkSanLabel.svelte +26 -0
  6. package/dist/(components)/DrillForkSanLabel.svelte.d.ts +11 -0
  7. package/dist/(components)/DrillVariationList.svelte +65 -0
  8. package/dist/(components)/DrillVariationList.svelte.d.ts +15 -0
  9. package/dist/(components)/Move.svelte +242 -173
  10. package/dist/(components)/Move.svelte.d.ts +11 -5
  11. package/dist/(components)/MoveComment.svelte +32 -0
  12. package/dist/(components)/MoveComment.svelte.d.ts +11 -0
  13. package/dist/(components)/MoveContextMenu.svelte +73 -0
  14. package/dist/(components)/MoveContextMenu.svelte.d.ts +17 -0
  15. package/dist/(components)/MoveSanWithMenu.svelte +158 -0
  16. package/dist/(components)/MoveSanWithMenu.svelte.d.ts +42 -0
  17. package/dist/(components)/NagBadges.svelte +37 -0
  18. package/dist/(components)/NagBadges.svelte.d.ts +9 -0
  19. package/dist/(components)/TreeViewer.svelte +861 -42
  20. package/dist/(components)/TreeViewer.svelte.d.ts +29 -3
  21. package/dist/(components)/TreeViewerPanelManager.svelte +18 -155
  22. package/dist/(components)/VariantDropdownNavigator.svelte +173 -0
  23. package/dist/(components)/VariantDropdownNavigator.svelte.d.ts +16 -0
  24. package/dist/(components)/VariationGroup.svelte +100 -0
  25. package/dist/(components)/VariationGroup.svelte.d.ts +21 -0
  26. package/dist/(constants)/png.d.ts +1 -1
  27. package/dist/(constants)/png.js +2 -4
  28. package/dist/(models)/nagCatalog.d.ts +36 -0
  29. package/dist/(models)/nagCatalog.js +97 -0
  30. package/dist/(models)/pgnNodeCustomData.d.ts +3 -0
  31. package/dist/(utils)/context.d.ts +10 -3
  32. package/dist/(utils)/context.js +3 -14
  33. package/dist/(utils)/createPreviewHover.d.ts +19 -0
  34. package/dist/(utils)/createPreviewHover.js +37 -0
  35. package/dist/(utils)/nagBadgeStyle.d.ts +1 -0
  36. package/dist/(utils)/nagBadgeStyle.js +1 -0
  37. package/dist/(utils)/scrollToActiveMove.js +6 -5
  38. package/dist/(utils)/transformNag.d.ts +1 -1
  39. package/dist/(utils)/transformNag.js +1 -34
  40. package/dist/(utils)/transformPgnToChessNode.js +13 -0
  41. package/dist/(utils)/treeViewerPanelNavigation.svelte.d.ts +39 -0
  42. package/dist/(utils)/treeViewerPanelNavigation.svelte.js +257 -0
  43. package/dist/components/ui/button/button-variants.d.ts +65 -0
  44. package/dist/components/ui/button/button-variants.js +28 -0
  45. package/dist/components/ui/button/button.svelte +3 -43
  46. package/dist/components/ui/button/button.svelte.d.ts +1 -61
  47. package/dist/components/ui/button/index.d.ts +3 -2
  48. package/dist/components/ui/button/index.js +3 -4
  49. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  50. package/dist/index.d.ts +2 -1
  51. package/dist/index.js +2 -1
  52. package/package.json +4 -4
@@ -0,0 +1,73 @@
1
+ <script lang="ts">
2
+ import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
3
+
4
+ import ArrowUpIcon from "@lucide/svelte/icons/arrow-up";
5
+ import CheckCheckIcon from "@lucide/svelte/icons/check-check";
6
+ import FlagIcon from "@lucide/svelte/icons/flag";
7
+ import Trash2Icon from "@lucide/svelte/icons/trash-2";
8
+ import * as ContextMenu from "../components/ui/context-menu/index.js";
9
+
10
+ import type { ChessTree } from "../(classes)/chessTree.svelte.js";
11
+
12
+ type TMoveContextMenuProps = {
13
+ /** Возвращает узел хода, к которому применяется меню. */
14
+ chessNode: ChessTreeNode;
15
+ /** Возвращает дерево партии. */
16
+ chessTree: ChessTree;
17
+ /** Возвращает true, если ход отображается в ячейке верхней сетки. */
18
+ isHighestLevel: boolean;
19
+ /** Возвращает true, если показывать пункт «Поднять приоритет варианта». */
20
+ showUpPriorityItem: boolean;
21
+ /** Возвращает колбэк после удаления варианта. */
22
+ onDeleteVariant: () => void;
23
+ };
24
+
25
+ const {
26
+ chessNode,
27
+ chessTree,
28
+ isHighestLevel,
29
+ showUpPriorityItem,
30
+ onDeleteVariant,
31
+ }: TMoveContextMenuProps = $props();
32
+ </script>
33
+
34
+ <ContextMenu.Content class="outline-none">
35
+ {#if !isHighestLevel}
36
+ <ContextMenu.Item
37
+ class="gap-4"
38
+ onclick={() => chessTree.makeMainVariant(chessNode)}
39
+ >Сделать вариант главной линией <ContextMenu.Shortcut
40
+ ><CheckCheckIcon /></ContextMenu.Shortcut
41
+ ></ContextMenu.Item
42
+ >
43
+ {#if showUpPriorityItem}
44
+ <ContextMenu.Item
45
+ class="gap-4"
46
+ onclick={() => chessTree.upPriority(chessNode)}
47
+ >Поднять приоритет варианта <ContextMenu.Shortcut
48
+ ><ArrowUpIcon /></ContextMenu.Shortcut
49
+ ></ContextMenu.Item
50
+ >
51
+ {/if}
52
+ {:else}
53
+ <ContextMenu.Item
54
+ class="gap-4"
55
+ onclick={() =>
56
+ (chessTree.forcedNodeId = chessNode.parentId ?? null)}
57
+ >Завершить главную линию <ContextMenu.Shortcut
58
+ ><FlagIcon /></ContextMenu.Shortcut
59
+ ></ContextMenu.Item
60
+ >
61
+ {/if}
62
+ <ContextMenu.Separator />
63
+ <ContextMenu.Item
64
+ class="gap-4 "
65
+ onclick={() => {
66
+ chessTree.deleteVariant(chessNode);
67
+ onDeleteVariant();
68
+ }}
69
+ >Удалить вариант <ContextMenu.Shortcut
70
+ ><Trash2Icon /></ContextMenu.Shortcut
71
+ ></ContextMenu.Item
72
+ >
73
+ </ContextMenu.Content>
@@ -0,0 +1,17 @@
1
+ import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
2
+ import type { ChessTree } from "../(classes)/chessTree.svelte.js";
3
+ type TMoveContextMenuProps = {
4
+ /** Возвращает узел хода, к которому применяется меню. */
5
+ chessNode: ChessTreeNode;
6
+ /** Возвращает дерево партии. */
7
+ chessTree: ChessTree;
8
+ /** Возвращает true, если ход отображается в ячейке верхней сетки. */
9
+ isHighestLevel: boolean;
10
+ /** Возвращает true, если показывать пункт «Поднять приоритет варианта». */
11
+ showUpPriorityItem: boolean;
12
+ /** Возвращает колбэк после удаления варианта. */
13
+ onDeleteVariant: () => void;
14
+ };
15
+ declare const MoveContextMenu: import("svelte").Component<TMoveContextMenuProps, {}, "">;
16
+ type MoveContextMenu = ReturnType<typeof MoveContextMenu>;
17
+ export default MoveContextMenu;
@@ -0,0 +1,158 @@
1
+ <script lang="ts">
2
+ import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
3
+
4
+ import * as ContextMenu from "../components/ui/context-menu/index.js";
5
+ import NagBadges from "./NagBadges.svelte";
6
+ import MoveWithIcon from "./MoveWithIcon.svelte";
7
+ import MoveContextMenu from "./MoveContextMenu.svelte";
8
+ import type { PieceSet } from "@connectorvol/shared";
9
+ import { ACTIVE_MOVE_CLASS } from "../(utils)/scrollToActiveMove.js";
10
+ import type { ChessTree } from "../(classes)/chessTree.svelte.js";
11
+
12
+ type TPreviewHoverHandlers = {
13
+ /** Возвращает обработчик `mouseenter` для превью FEN. */
14
+ onmouseenter: (e: MouseEvent) => void;
15
+ /** Возвращает обработчик `mouseleave` для превью FEN. */
16
+ onmouseleave: (e: MouseEvent) => void;
17
+ };
18
+
19
+ type TMoveSanWithMenuProps = {
20
+ /** Возвращает узел хода. */
21
+ chessNode: ChessTreeNode;
22
+ /** Возвращает родительский узел (для подписей полухода) или null. */
23
+ parentNode: ChessTreeNode | null;
24
+ /** Возвращает набор фигур для иконок SAN. */
25
+ pieceSet: PieceSet;
26
+ /** Возвращает true, если клик по ходу активен. */
27
+ selectable: boolean;
28
+ /** Возвращает true, если ход отображается в верхней строке сетки. */
29
+ isHighestLevel: boolean;
30
+ /** Возвращает true, если этот ход совпадает с текущим выбранным узлом. */
31
+ isCurrentMove: boolean;
32
+ /** Возвращает обработчики превью при наведении. */
33
+ previewHover: TPreviewHoverHandlers;
34
+ /** Представляет обработчик выбора узла по событию `mousedown`. */
35
+ onMoveMouseDown: () => void;
36
+ /** Возвращает класс отступов для inline-триггера не на верхнем уровне. */
37
+ spaceInlineBlockClass: string;
38
+ /** Возвращает список NAG после SAN. */
39
+ nags: number[] | undefined;
40
+ /** Возвращает дерево партии. */
41
+ chessTree: ChessTree;
42
+ /** Возвращает true, если показывать пункт поднятия приоритета в меню. */
43
+ showUpPriorityItem: boolean;
44
+ /** Возвращает колбэк после удаления варианта. */
45
+ onDeleteVariant: () => void;
46
+ /** Возвращает true, если у ячейки SAN в верхней сетке нужна нижняя граница при неполном ряду (нет чёрной ноды). */
47
+ mainRowBottomEdge?: boolean | undefined;
48
+ };
49
+
50
+ const {
51
+ chessNode,
52
+ parentNode,
53
+ pieceSet,
54
+ selectable,
55
+ isHighestLevel,
56
+ isCurrentMove,
57
+ previewHover,
58
+ onMoveMouseDown,
59
+ spaceInlineBlockClass,
60
+ nags,
61
+ chessTree,
62
+ showUpPriorityItem,
63
+ onDeleteVariant,
64
+ mainRowBottomEdge = false,
65
+ }: TMoveSanWithMenuProps = $props();
66
+
67
+ const node = $derived(chessNode);
68
+ const selectedOnHighestLevel = $derived(isCurrentMove && isHighestLevel);
69
+
70
+ /** Представляет true, если событие пришло из области NAG (ПКМ там не переключает узел). */
71
+ function isFromNagBadges(target: EventTarget | null): boolean {
72
+ return (
73
+ target instanceof Element && target.closest("[data-nag-badges]") !== null
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Представляет выбор узла по ЛКМ везде, по ПКМ — только по SAN (не по NAG); прочие кнопки игнорируются.
79
+ */
80
+ function handleRowMouseDown(e: MouseEvent) {
81
+ if (e.button === 0) {
82
+ onMoveMouseDown();
83
+ return;
84
+ }
85
+ if (e.button === 2 && !isFromNagBadges(e.target)) {
86
+ onMoveMouseDown();
87
+ }
88
+ }
89
+ </script>
90
+
91
+ <ContextMenu.Root>
92
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
93
+ <span
94
+ class={{
95
+ "items-center gap-0": true,
96
+ "flex w-full min-w-0 justify-center": isHighestLevel,
97
+ "inline-flex": !isHighestLevel,
98
+ [`${spaceInlineBlockClass} text-center`]: isHighestLevel,
99
+ [`z-10 ${ACTIVE_MOVE_CLASS}`]: selectedOnHighestLevel,
100
+ [ACTIVE_MOVE_CLASS]: isCurrentMove && !isHighestLevel,
101
+ "border-b border-gray-300": isHighestLevel && mainRowBottomEdge,
102
+ "tree-viewer-overflow-tail-border": isHighestLevel && mainRowBottomEdge,
103
+ }}
104
+ onmousedown={handleRowMouseDown}
105
+ >
106
+ <ContextMenu.Trigger
107
+ class={{
108
+ "outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0": true,
109
+ "cursor-pointer ": selectable,
110
+ "cursor-default": !selectable,
111
+ inline: !isHighestLevel,
112
+ }}
113
+ >
114
+ <span
115
+ role="group"
116
+ aria-label="Нотация хода, превью доски при наведении"
117
+ class="inline-flex items-baseline"
118
+ onmouseenter={previewHover.onmouseenter}
119
+ onmouseleave={previewHover.onmouseleave}
120
+ >
121
+ {#if !isHighestLevel && node.data.fullMoves && node.data.ply % 2 === 1}
122
+ {node.data.fullMoves}.
123
+ {/if}
124
+ {#if !isHighestLevel && node.data.fullMoves && node.data.ply % 2 === 0 && (parentNode?.children?.[0]?.id !== chessNode.id || (parentNode?.children?.length ?? 0) > 1)}
125
+ {node.data.fullMoves - 1}...
126
+ {/if}
127
+ <span
128
+ class={{
129
+ "inline-grid items-baseline": true,
130
+ }}
131
+ >
132
+ <span
133
+ class="invisible col-start-1 row-start-1 font-bold"
134
+ aria-hidden="true"
135
+ >
136
+ <MoveWithIcon san={node.data.san} ply={node.data.ply} {pieceSet} />
137
+ </span>
138
+ <span
139
+ class={{
140
+ "col-start-1 row-start-1": true,
141
+ "font-bold": isCurrentMove,
142
+ }}
143
+ >
144
+ <MoveWithIcon san={node.data.san} ply={node.data.ply} {pieceSet} />
145
+ </span>
146
+ </span>
147
+ </span>
148
+ </ContextMenu.Trigger>
149
+ <NagBadges {nags} class="!ml-0.5 shrink-0" />
150
+ </span>
151
+ <MoveContextMenu
152
+ {chessNode}
153
+ {chessTree}
154
+ {isHighestLevel}
155
+ {showUpPriorityItem}
156
+ {onDeleteVariant}
157
+ />
158
+ </ContextMenu.Root>
@@ -0,0 +1,42 @@
1
+ import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
2
+ import type { PieceSet } from "@connectorvol/shared";
3
+ import type { ChessTree } from "../(classes)/chessTree.svelte.js";
4
+ type TPreviewHoverHandlers = {
5
+ /** Возвращает обработчик `mouseenter` для превью FEN. */
6
+ onmouseenter: (e: MouseEvent) => void;
7
+ /** Возвращает обработчик `mouseleave` для превью FEN. */
8
+ onmouseleave: (e: MouseEvent) => void;
9
+ };
10
+ type TMoveSanWithMenuProps = {
11
+ /** Возвращает узел хода. */
12
+ chessNode: ChessTreeNode;
13
+ /** Возвращает родительский узел (для подписей полухода) или null. */
14
+ parentNode: ChessTreeNode | null;
15
+ /** Возвращает набор фигур для иконок SAN. */
16
+ pieceSet: PieceSet;
17
+ /** Возвращает true, если клик по ходу активен. */
18
+ selectable: boolean;
19
+ /** Возвращает true, если ход отображается в верхней строке сетки. */
20
+ isHighestLevel: boolean;
21
+ /** Возвращает true, если этот ход совпадает с текущим выбранным узлом. */
22
+ isCurrentMove: boolean;
23
+ /** Возвращает обработчики превью при наведении. */
24
+ previewHover: TPreviewHoverHandlers;
25
+ /** Представляет обработчик выбора узла по событию `mousedown`. */
26
+ onMoveMouseDown: () => void;
27
+ /** Возвращает класс отступов для inline-триггера не на верхнем уровне. */
28
+ spaceInlineBlockClass: string;
29
+ /** Возвращает список NAG после SAN. */
30
+ nags: number[] | undefined;
31
+ /** Возвращает дерево партии. */
32
+ chessTree: ChessTree;
33
+ /** Возвращает true, если показывать пункт поднятия приоритета в меню. */
34
+ showUpPriorityItem: boolean;
35
+ /** Возвращает колбэк после удаления варианта. */
36
+ onDeleteVariant: () => void;
37
+ /** Возвращает true, если у ячейки SAN в верхней сетке нужна нижняя граница при неполном ряду (нет чёрной ноды). */
38
+ mainRowBottomEdge?: boolean | undefined;
39
+ };
40
+ declare const MoveSanWithMenu: import("svelte").Component<TMoveSanWithMenuProps, {}, "">;
41
+ type MoveSanWithMenu = ReturnType<typeof MoveSanWithMenu>;
42
+ export default MoveSanWithMenu;
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { nagBadgeBackground, transformNag } from "@connectorvol/shared";
3
+ import { cn } from "../utils.js";
4
+ import { nagDescription } from "../(models)/nagCatalog.js";
5
+
6
+ type TNagBadgesProps = {
7
+ /** Возвращает список NAG для отображения. */
8
+ nags: number[] | undefined;
9
+ /** Возвращает дополнительные классы корневого контейнера. */
10
+ class?: string;
11
+ };
12
+
13
+ const { nags, class: className = "" }: TNagBadgesProps = $props();
14
+ </script>
15
+
16
+ {#if nags?.length}
17
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
18
+ <span
19
+ data-nag-badges
20
+ class={cn(
21
+ "ml-0.5 inline-flex max-w-full flex-nowrap items-center gap-0.5 align-middle whitespace-nowrap overflow-hidden",
22
+ className,
23
+ )}
24
+ oncontextmenu={(e) => e.preventDefault()}
25
+ >
26
+ {#each nags ?? [] as nag, i (i)}
27
+ {@const label = transformNag(nag)}
28
+ {#if label}
29
+ <span
30
+ class="inline-flex h-[1.125rem] w-[1.125rem] shrink-0 cursor-help items-center justify-center rounded-full border border-white/25 text-center font-serif text-[0.625rem] font-semibold leading-none text-white shadow-[0_1px_2px_rgba(0,0,0,0.25)]"
31
+ style:background={nagBadgeBackground(nag)}
32
+ title={nagDescription(nag)}>{label}</span
33
+ >
34
+ {/if}
35
+ {/each}
36
+ </span>
37
+ {/if}
@@ -0,0 +1,9 @@
1
+ type TNagBadgesProps = {
2
+ /** Возвращает список NAG для отображения. */
3
+ nags: number[] | undefined;
4
+ /** Возвращает дополнительные классы корневого контейнера. */
5
+ class?: string;
6
+ };
7
+ declare const NagBadges: import("svelte").Component<TNagBadgesProps, {}, "">;
8
+ type NagBadges = ReturnType<typeof NagBadges>;
9
+ export default NagBadges;