@hiroleague/taskmanager 0.0.1 → 0.0.2
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/LICENSE +21 -0
- package/README.md +41 -52
- package/dist/assets/architecture-YZFGNWBL-C1MoQeSs.js +1 -0
- package/dist/assets/{architectureDiagram-Q4EWVU46-DSQ1_74_.js → architectureDiagram-Q4EWVU46-DUEfvDBu.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DfOGNphI.js → blockDiagram-DXYQGD6D-DQzEOPT2.js} +1 -1
- package/dist/assets/{chunk-2KRD3SAO-9yt00aGC.js → chunk-2KRD3SAO-C2e-_49I.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-DF8yJBFl.js → chunk-4TB4RGXK-AZq3s1Dh.js} +1 -1
- package/dist/assets/{chunk-67CJDMHE-5wFKo04G.js → chunk-67CJDMHE-B1-M78qu.js} +1 -1
- package/dist/assets/{chunk-7N4EOEYR-BRRGX_NC.js → chunk-7N4EOEYR-D7mYFpz-.js} +1 -1
- package/dist/assets/{chunk-AA7GKIK3-DUZv_pNI.js → chunk-AA7GKIK3-VWI9k39i.js} +1 -1
- package/dist/assets/{chunk-CIAEETIT-mA5aM_d7.js → chunk-CIAEETIT-hnu4zamm.js} +1 -1
- package/dist/assets/{chunk-FOC6F5B3-B-cqGCPC.js → chunk-FOC6F5B3-BJsh9nO9.js} +1 -1
- package/dist/assets/{chunk-K5T4RW27-BLRDzioh.js → chunk-K5T4RW27-BLIPdXaZ.js} +1 -1
- package/dist/assets/{chunk-KGLVRYIC-CTkQSeKy.js → chunk-KGLVRYIC-DvaW2TkT.js} +1 -1
- package/dist/assets/{chunk-LIHQZDEY-Cf34Nu3J.js → chunk-LIHQZDEY-CUsM0M11.js} +1 -1
- package/dist/assets/{chunk-ORNJ4GCN-D3uXgbay.js → chunk-ORNJ4GCN-CfluNV0_.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-syQho5jf.js → chunk-OYMX7WX6-CkWzw4JX.js} +1 -1
- package/dist/assets/{classDiagram-6PBFFD2Q-CotFZI8-.js → classDiagram-6PBFFD2Q-Dx_f-9b7.js} +1 -1
- package/dist/assets/{classDiagram-v2-HSJHXN6E-DAPzeDGn.js → classDiagram-v2-HSJHXN6E-CSfvZ-nt.js} +1 -1
- package/dist/assets/clone-CXokakwV.js +1 -0
- package/dist/assets/{dagre-rhyPjnsQ.js → dagre-Do0eD9eI.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-BBqulDtd.js → dagre-KV5264BT-lveZDhBf.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-Ky3EXXj0.js → diagram-5BDNPKRD-Dq5yM_uY.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-t7LbT0Uz.js → diagram-G4DWMVQ6-D-SYOmKm.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-CdnLXEMx.js → diagram-MMDJMWI5-lU5t9BZA.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-CnzTqJBM.js → diagram-TYMM5635-6tfUbY3R.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-BN5eJerP.js → erDiagram-SMLLAGMA-dx09stuy.js} +1 -1
- package/dist/assets/{flatten-C5NL-f24.js → flatten-B2BZ0pzY.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CbFskc8S.js → flowDiagram-DWJPFMVM-CJi2WISS.js} +1 -1
- package/dist/assets/gitGraph-7Q5UKJZL-BXTuQaDM.js +1 -0
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-wpqI2kyI.js → gitGraphDiagram-UUTBAWPF-Bjj94M12.js} +1 -1
- package/dist/assets/{graphlib-COiJG5Qv.js → graphlib-BIlXYGdM.js} +1 -1
- package/dist/assets/{index-lyyIVcc_.js → index-CZZuue3D.js} +5 -5
- package/dist/assets/info-OMHHGYJF-BeeKt8-X.js +1 -0
- package/dist/assets/{infoDiagram-42DDH7IO-BbvTdpSV.js → infoDiagram-42DDH7IO-wq_opQKO.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-Epc23N_0.js → ishikawaDiagram-UXIWVN3A-Cnc1bwBo.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-C8dW_26n.js → kanban-definition-6JOO6SKY-CwHbIze0.js} +1 -1
- package/dist/assets/{mermaid-parser.core-6Tn8epr_.js → mermaid-parser.core-DrLhKJ48.js} +2 -2
- package/dist/assets/{mindmap-definition-QFDTVHPH-CvpNtrKT.js → mindmap-definition-QFDTVHPH-DswAJiEd.js} +1 -1
- package/dist/assets/packet-4T2RLAQJ-DQ-H9_jd.js +1 -0
- package/dist/assets/pie-ZZUOXDRM-BSj0Jsyj.js +1 -0
- package/dist/assets/{pieDiagram-DEJITSTG-eENymoXZ.js → pieDiagram-DEJITSTG-DgQTCddl.js} +1 -1
- package/dist/assets/radar-PYXPWWZC-B7-oRPFL.js +1 -0
- package/dist/assets/{reduce-BDOBPIXr.js → reduce-Uumu9GdR.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-CmRO3hLp.js → requirementDiagram-MS252O5E-D1moa23Z.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-B7qNcwNo.js → sequenceDiagram-FGHM5R23-Dvhj7HGn.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-CYfGMoR8.js → stateDiagram-FHFEXIEX-Dx5CjenB.js} +1 -1
- package/dist/assets/{stateDiagram-v2-QKLJ7IA2-CO1W_n55.js → stateDiagram-v2-QKLJ7IA2-C_PkrTdc.js} +1 -1
- package/dist/assets/{timeline-definition-GMOUNBTQ-CQWqDPGG.js → timeline-definition-GMOUNBTQ-z-IncVmK.js} +1 -1
- package/dist/assets/treeView-SZITEDCU-CFXle9Az.js +1 -0
- package/dist/assets/treemap-W4RFUUIX-CAW3vWh8.js +1 -0
- package/dist/assets/{vennDiagram-DHZGUBPP-BjTbuhcb.js → vennDiagram-DHZGUBPP-CT1ehozU.js} +1 -1
- package/dist/assets/wardley-RL74JXVD-7q3ju4kc.js +1 -0
- package/dist/assets/{wardleyDiagram-NUSXRM2D-DNhPIFCg.js → wardleyDiagram-NUSXRM2D-D-kouujI.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-BDblAZ11.js → xychartDiagram-5P7HB3ND-D1lnM0pL.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +101 -92
- package/scripts/postinstall-message.mjs +160 -0
- package/scripts/stubs/node-domexception/index.cjs +18 -0
- package/scripts/stubs/node-domexception/package.json +7 -0
- package/skills/hiro-task-manager-cli/SKILL.md +97 -0
- package/skills/hiro-task-manager-cli/reference/boards.md +143 -0
- package/skills/hiro-task-manager-cli/reference/cli-access-policy.md +72 -0
- package/skills/hiro-task-manager-cli/reference/errors.md +85 -0
- package/skills/hiro-task-manager-cli/reference/lists.md +106 -0
- package/skills/hiro-task-manager-cli/reference/releases.md +87 -0
- package/skills/hiro-task-manager-cli/reference/search.md +38 -0
- package/skills/hiro-task-manager-cli/reference/statuses.md +25 -0
- package/skills/hiro-task-manager-cli/reference/tasks.md +144 -0
- package/skills/hiro-task-manager-cli/reference/trash.md +50 -0
- package/src/cli/bootstrap/launcher.test.ts +66 -0
- package/src/cli/bootstrap/launcher.ts +375 -35
- package/src/cli/bootstrap/program.ts +4 -0
- package/src/cli/bootstrap/runtime.test.ts +15 -0
- package/src/cli/bootstrap/runtime.ts +27 -1
- package/src/cli/commands/query.ts +56 -56
- package/src/cli/commands/server.ts +27 -19
- package/src/cli/handlers/boards.test.ts +669 -669
- package/src/cli/handlers/cli-wiring.test.ts +1 -1
- package/src/cli/handlers/search.test.ts +374 -374
- package/src/cli/handlers/search.ts +17 -17
- package/src/cli/handlers/server.test.ts +55 -13
- package/src/cli/handlers/server.ts +16 -3
- package/src/cli/lib/api-client.test.ts +35 -2
- package/src/cli/lib/api-client.ts +43 -10
- package/src/cli/lib/cli-http-errors.test.ts +85 -85
- package/src/cli/lib/command-helpers.ts +161 -154
- package/src/cli/lib/config.ts +4 -0
- package/src/cli/lib/launcherUi.ts +166 -0
- package/src/cli/lib/process.test.ts +24 -5
- package/src/cli/lib/process.ts +86 -55
- package/src/cli/ports/process.ts +8 -2
- package/src/cli/subprocess.real-stack.test.ts +611 -598
- package/src/cli/subprocess.smoke.test.ts +954 -969
- package/src/cli/types/config.ts +2 -6
- package/src/client/components/auth/AuthScreen.tsx +3 -3
- package/src/client/components/board/BoardStatsChips.tsx +233 -233
- package/src/client/components/board/BoardStatsContext.tsx +41 -41
- package/src/client/components/board/boardHeaderButtonStyles.ts +38 -38
- package/src/client/components/board/shortcuts/useBoardShortcutKeydown.ts +49 -49
- package/src/client/components/board/useBoardCanvasPanScroll.ts +108 -108
- package/src/client/components/board/useBoardTaskContainerDroppableReact.ts +33 -33
- package/src/client/components/board/useBoardTaskSortableReact.ts +26 -26
- package/src/client/components/multi-select.tsx +1206 -1206
- package/src/client/components/routing/BoardPage.tsx +20 -20
- package/src/client/components/routing/NavigationRegistrar.tsx +13 -13
- package/src/client/components/settings/SettingsPage.tsx +1 -1
- package/src/client/components/task/TaskCard.tsx +643 -643
- package/src/client/components/ui/badge.tsx +49 -49
- package/src/client/components/ui/button.tsx +65 -65
- package/src/client/components/ui/command.tsx +193 -193
- package/src/client/components/ui/dialog.tsx +163 -163
- package/src/client/components/ui/input-group.tsx +155 -155
- package/src/client/components/ui/input.tsx +19 -19
- package/src/client/components/ui/popover.tsx +87 -87
- package/src/client/components/ui/separator.tsx +28 -28
- package/src/client/components/ui/textarea.tsx +18 -18
- package/src/client/index.css +248 -248
- package/src/client/lib/appNavigate.ts +16 -16
- package/src/client/lib/taskCardDate.ts +111 -111
- package/src/client/lib/utils.ts +6 -6
- package/src/server/auth.ts +351 -302
- package/src/server/bootstrapDev.ts +11 -2
- package/src/server/bootstrapInstalled.ts +6 -1
- package/src/server/index.ts +33 -7
- package/src/server/migrations/013_cli_policy_and_provenance.ts +2 -2
- package/src/server/migrations/019_cli_global_create_board_default_on.ts +14 -0
- package/src/server/migrations/registry.ts +43 -41
- package/src/server/parseBootstrapProfile.ts +42 -0
- package/src/server/storage/cliPolicy.ts +2 -1
- package/src/shared/runtimeConfig.ts +256 -237
- package/src/shared/runtimeIdentity.test.ts +47 -0
- package/src/shared/runtimeIdentity.ts +35 -0
- package/src/shared/serverStatus.ts +21 -0
- package/src/shared/skillsInstall.ts +71 -0
- package/src/shared/terminalColors.ts +24 -0
- package/dist/assets/architecture-YZFGNWBL-3h1eIYfB.js +0 -1
- package/dist/assets/clone-BRQpYu_n.js +0 -1
- package/dist/assets/gitGraph-7Q5UKJZL-CG8f8JF7.js +0 -1
- package/dist/assets/info-OMHHGYJF-C8_SHoRO.js +0 -1
- package/dist/assets/packet-4T2RLAQJ-BvpAX0kJ.js +0 -1
- package/dist/assets/pie-ZZUOXDRM-Ow26Yf-E.js +0 -1
- package/dist/assets/radar-PYXPWWZC-e_ron5jQ.js +0 -1
- package/dist/assets/treeView-SZITEDCU-DsEr3xeq.js +0 -1
- package/dist/assets/treemap-W4RFUUIX-DV7nk2AB.js +0 -1
- package/dist/assets/wardley-RL74JXVD-CrrFU9AE.js +0 -1
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import type { Board } from "../../../../shared/models";
|
|
3
|
-
import { boardShortcutRegistry } from "./boardShortcutRegistry";
|
|
4
|
-
import type { BoardShortcutActions } from "./boardShortcutTypes";
|
|
5
|
-
import { isEditableKeyboardTarget } from "./isEditableKeyboardTarget";
|
|
6
|
-
import { useShortcutScope } from "./ShortcutScopeContext";
|
|
7
|
-
|
|
8
|
-
interface UseBoardShortcutKeydownOptions {
|
|
9
|
-
board: Board | null;
|
|
10
|
-
actions: BoardShortcutActions;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Registers the board shortcut dispatcher with {@link ShortcutScopeProvider}.
|
|
15
|
-
* The global listener only invokes it when the scope stack is empty (board is active).
|
|
16
|
-
*/
|
|
17
|
-
export function useBoardShortcutKeydown({
|
|
18
|
-
board,
|
|
19
|
-
actions,
|
|
20
|
-
}: UseBoardShortcutKeydownOptions): void {
|
|
21
|
-
const { registerBoardKeyHandler } = useShortcutScope();
|
|
22
|
-
const actionsRef = useRef(actions);
|
|
23
|
-
actionsRef.current = actions;
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (!board) {
|
|
27
|
-
return registerBoardKeyHandler(() => {});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const onKeyDown = (e: KeyboardEvent) => {
|
|
31
|
-
if (!board) return;
|
|
32
|
-
if (isEditableKeyboardTarget(e.target)) return;
|
|
33
|
-
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
34
|
-
// Shift+Tab keeps native reverse tab order; unmodified Tab establishes board highlight.
|
|
35
|
-
if (e.key === "Tab" && e.shiftKey) return;
|
|
36
|
-
|
|
37
|
-
for (const def of boardShortcutRegistry) {
|
|
38
|
-
if (def.helpOnly) continue;
|
|
39
|
-
if (!def.matchKey(e.key)) continue;
|
|
40
|
-
if (def.enabled && !def.enabled(board)) continue;
|
|
41
|
-
if (def.preventDefault) e.preventDefault();
|
|
42
|
-
def.run(board, actionsRef.current);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
return registerBoardKeyHandler(onKeyDown);
|
|
48
|
-
}, [board, registerBoardKeyHandler]);
|
|
49
|
-
}
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import type { Board } from "../../../../shared/models";
|
|
3
|
+
import { boardShortcutRegistry } from "./boardShortcutRegistry";
|
|
4
|
+
import type { BoardShortcutActions } from "./boardShortcutTypes";
|
|
5
|
+
import { isEditableKeyboardTarget } from "./isEditableKeyboardTarget";
|
|
6
|
+
import { useShortcutScope } from "./ShortcutScopeContext";
|
|
7
|
+
|
|
8
|
+
interface UseBoardShortcutKeydownOptions {
|
|
9
|
+
board: Board | null;
|
|
10
|
+
actions: BoardShortcutActions;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Registers the board shortcut dispatcher with {@link ShortcutScopeProvider}.
|
|
15
|
+
* The global listener only invokes it when the scope stack is empty (board is active).
|
|
16
|
+
*/
|
|
17
|
+
export function useBoardShortcutKeydown({
|
|
18
|
+
board,
|
|
19
|
+
actions,
|
|
20
|
+
}: UseBoardShortcutKeydownOptions): void {
|
|
21
|
+
const { registerBoardKeyHandler } = useShortcutScope();
|
|
22
|
+
const actionsRef = useRef(actions);
|
|
23
|
+
actionsRef.current = actions;
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!board) {
|
|
27
|
+
return registerBoardKeyHandler(() => {});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
31
|
+
if (!board) return;
|
|
32
|
+
if (isEditableKeyboardTarget(e.target)) return;
|
|
33
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
34
|
+
// Shift+Tab keeps native reverse tab order; unmodified Tab establishes board highlight.
|
|
35
|
+
if (e.key === "Tab" && e.shiftKey) return;
|
|
36
|
+
|
|
37
|
+
for (const def of boardShortcutRegistry) {
|
|
38
|
+
if (def.helpOnly) continue;
|
|
39
|
+
if (!def.matchKey(e.key)) continue;
|
|
40
|
+
if (def.enabled && !def.enabled(board)) continue;
|
|
41
|
+
if (def.preventDefault) e.preventDefault();
|
|
42
|
+
def.run(board, actionsRef.current);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return registerBoardKeyHandler(onKeyDown);
|
|
48
|
+
}, [board, registerBoardKeyHandler]);
|
|
49
|
+
}
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from "react";
|
|
2
|
-
|
|
3
|
-
const ACTIVATION_PX = 6;
|
|
4
|
-
|
|
5
|
-
/** Selectors for elements that must not start horizontal board pan (columns, chrome, controls). */
|
|
6
|
-
const NO_PAN =
|
|
7
|
-
"[data-board-no-pan],button,a[href],input,textarea,select,option,label,[role='button'],[contenteditable='true']";
|
|
8
|
-
|
|
9
|
-
function targetStartsPan(target: EventTarget | null): boolean {
|
|
10
|
-
if (!(target instanceof Element)) return false;
|
|
11
|
-
return target.closest(NO_PAN) == null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type PanMode = "undecided" | "horizontal" | "none";
|
|
15
|
-
|
|
16
|
-
export function useBoardCanvasPanScroll() {
|
|
17
|
-
const scrollRef = useRef<HTMLDivElement>(null);
|
|
18
|
-
const panRef = useRef<{
|
|
19
|
-
pointerId: number;
|
|
20
|
-
startX: number;
|
|
21
|
-
startY: number;
|
|
22
|
-
startScrollLeft: number;
|
|
23
|
-
mode: PanMode;
|
|
24
|
-
} | null>(null);
|
|
25
|
-
|
|
26
|
-
const [panning, setPanning] = useState(false);
|
|
27
|
-
|
|
28
|
-
const onPointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
29
|
-
if (e.button !== 0) return;
|
|
30
|
-
if (!targetStartsPan(e.target)) return;
|
|
31
|
-
const scroller = scrollRef.current;
|
|
32
|
-
if (!scroller) return;
|
|
33
|
-
|
|
34
|
-
panRef.current = {
|
|
35
|
-
pointerId: e.pointerId,
|
|
36
|
-
startX: e.clientX,
|
|
37
|
-
startY: e.clientY,
|
|
38
|
-
startScrollLeft: scroller.scrollLeft,
|
|
39
|
-
mode: "undecided",
|
|
40
|
-
};
|
|
41
|
-
}, []);
|
|
42
|
-
|
|
43
|
-
const onPointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
44
|
-
const pan = panRef.current;
|
|
45
|
-
const scroller = scrollRef.current;
|
|
46
|
-
if (!pan || !scroller || e.pointerId !== pan.pointerId) return;
|
|
47
|
-
|
|
48
|
-
if (pan.mode === "none") return;
|
|
49
|
-
|
|
50
|
-
if (pan.mode === "undecided") {
|
|
51
|
-
const dx = e.clientX - pan.startX;
|
|
52
|
-
const dy = e.clientY - pan.startY;
|
|
53
|
-
if (
|
|
54
|
-
Math.abs(dx) < ACTIVATION_PX &&
|
|
55
|
-
Math.abs(dy) < ACTIVATION_PX
|
|
56
|
-
) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
60
|
-
pan.mode = "horizontal";
|
|
61
|
-
try {
|
|
62
|
-
scroller.setPointerCapture(e.pointerId);
|
|
63
|
-
} catch {
|
|
64
|
-
/* already captured or unsupported */
|
|
65
|
-
}
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
setPanning(true);
|
|
68
|
-
} else {
|
|
69
|
-
pan.mode = "none";
|
|
70
|
-
panRef.current = null;
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (pan.mode === "horizontal") {
|
|
76
|
-
const dx = e.clientX - pan.startX;
|
|
77
|
-
scroller.scrollLeft = pan.startScrollLeft - dx;
|
|
78
|
-
e.preventDefault();
|
|
79
|
-
}
|
|
80
|
-
}, []);
|
|
81
|
-
|
|
82
|
-
const endPan = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
83
|
-
const pan = panRef.current;
|
|
84
|
-
const scroller = scrollRef.current;
|
|
85
|
-
if (!pan || e.pointerId !== pan.pointerId) return;
|
|
86
|
-
|
|
87
|
-
if (pan.mode === "horizontal" && scroller) {
|
|
88
|
-
try {
|
|
89
|
-
scroller.releasePointerCapture(e.pointerId);
|
|
90
|
-
} catch {
|
|
91
|
-
/* not captured */
|
|
92
|
-
}
|
|
93
|
-
setPanning(false);
|
|
94
|
-
}
|
|
95
|
-
panRef.current = null;
|
|
96
|
-
}, []);
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
scrollRef,
|
|
100
|
-
panning,
|
|
101
|
-
boardCanvasPanHandlers: {
|
|
102
|
-
onPointerDown,
|
|
103
|
-
onPointerMove,
|
|
104
|
-
onPointerUp: endPan,
|
|
105
|
-
onPointerCancel: endPan,
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
}
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
const ACTIVATION_PX = 6;
|
|
4
|
+
|
|
5
|
+
/** Selectors for elements that must not start horizontal board pan (columns, chrome, controls). */
|
|
6
|
+
const NO_PAN =
|
|
7
|
+
"[data-board-no-pan],button,a[href],input,textarea,select,option,label,[role='button'],[contenteditable='true']";
|
|
8
|
+
|
|
9
|
+
function targetStartsPan(target: EventTarget | null): boolean {
|
|
10
|
+
if (!(target instanceof Element)) return false;
|
|
11
|
+
return target.closest(NO_PAN) == null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type PanMode = "undecided" | "horizontal" | "none";
|
|
15
|
+
|
|
16
|
+
export function useBoardCanvasPanScroll() {
|
|
17
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
18
|
+
const panRef = useRef<{
|
|
19
|
+
pointerId: number;
|
|
20
|
+
startX: number;
|
|
21
|
+
startY: number;
|
|
22
|
+
startScrollLeft: number;
|
|
23
|
+
mode: PanMode;
|
|
24
|
+
} | null>(null);
|
|
25
|
+
|
|
26
|
+
const [panning, setPanning] = useState(false);
|
|
27
|
+
|
|
28
|
+
const onPointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
29
|
+
if (e.button !== 0) return;
|
|
30
|
+
if (!targetStartsPan(e.target)) return;
|
|
31
|
+
const scroller = scrollRef.current;
|
|
32
|
+
if (!scroller) return;
|
|
33
|
+
|
|
34
|
+
panRef.current = {
|
|
35
|
+
pointerId: e.pointerId,
|
|
36
|
+
startX: e.clientX,
|
|
37
|
+
startY: e.clientY,
|
|
38
|
+
startScrollLeft: scroller.scrollLeft,
|
|
39
|
+
mode: "undecided",
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
const onPointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
44
|
+
const pan = panRef.current;
|
|
45
|
+
const scroller = scrollRef.current;
|
|
46
|
+
if (!pan || !scroller || e.pointerId !== pan.pointerId) return;
|
|
47
|
+
|
|
48
|
+
if (pan.mode === "none") return;
|
|
49
|
+
|
|
50
|
+
if (pan.mode === "undecided") {
|
|
51
|
+
const dx = e.clientX - pan.startX;
|
|
52
|
+
const dy = e.clientY - pan.startY;
|
|
53
|
+
if (
|
|
54
|
+
Math.abs(dx) < ACTIVATION_PX &&
|
|
55
|
+
Math.abs(dy) < ACTIVATION_PX
|
|
56
|
+
) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
60
|
+
pan.mode = "horizontal";
|
|
61
|
+
try {
|
|
62
|
+
scroller.setPointerCapture(e.pointerId);
|
|
63
|
+
} catch {
|
|
64
|
+
/* already captured or unsupported */
|
|
65
|
+
}
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
setPanning(true);
|
|
68
|
+
} else {
|
|
69
|
+
pan.mode = "none";
|
|
70
|
+
panRef.current = null;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (pan.mode === "horizontal") {
|
|
76
|
+
const dx = e.clientX - pan.startX;
|
|
77
|
+
scroller.scrollLeft = pan.startScrollLeft - dx;
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
}
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const endPan = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
83
|
+
const pan = panRef.current;
|
|
84
|
+
const scroller = scrollRef.current;
|
|
85
|
+
if (!pan || e.pointerId !== pan.pointerId) return;
|
|
86
|
+
|
|
87
|
+
if (pan.mode === "horizontal" && scroller) {
|
|
88
|
+
try {
|
|
89
|
+
scroller.releasePointerCapture(e.pointerId);
|
|
90
|
+
} catch {
|
|
91
|
+
/* not captured */
|
|
92
|
+
}
|
|
93
|
+
setPanning(false);
|
|
94
|
+
}
|
|
95
|
+
panRef.current = null;
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
scrollRef,
|
|
100
|
+
panning,
|
|
101
|
+
boardCanvasPanHandlers: {
|
|
102
|
+
onPointerDown,
|
|
103
|
+
onPointerMove,
|
|
104
|
+
onPointerUp: endPan,
|
|
105
|
+
onPointerCancel: endPan,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { CollisionPriority } from "@dnd-kit/abstract";
|
|
2
|
-
import { useDroppable } from "@dnd-kit/react";
|
|
3
|
-
import {
|
|
4
|
-
BOARD_TASK_CONTAINER_DND_TYPE,
|
|
5
|
-
BOARD_TASK_DND_TYPE,
|
|
6
|
-
type BoardDndLayout,
|
|
7
|
-
boardTaskContainerData,
|
|
8
|
-
} from "./dndReactModel";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Phase 1 React-first wrapper for task drop containers.
|
|
12
|
-
* This encodes the board's container metadata once so stacked lists and lane
|
|
13
|
-
* bands can share the same droppable configuration in later phases.
|
|
14
|
-
*/
|
|
15
|
-
export function useBoardTaskContainerDroppableReact({
|
|
16
|
-
containerId,
|
|
17
|
-
layout,
|
|
18
|
-
listId,
|
|
19
|
-
status,
|
|
20
|
-
}: {
|
|
21
|
-
containerId: string;
|
|
22
|
-
layout: BoardDndLayout;
|
|
23
|
-
listId: number;
|
|
24
|
-
status?: string;
|
|
25
|
-
}) {
|
|
26
|
-
return useDroppable({
|
|
27
|
-
id: containerId,
|
|
28
|
-
type: BOARD_TASK_CONTAINER_DND_TYPE,
|
|
29
|
-
accept: BOARD_TASK_DND_TYPE,
|
|
30
|
-
collisionPriority: CollisionPriority.Low,
|
|
31
|
-
data: boardTaskContainerData(containerId, layout, listId, status),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
1
|
+
import { CollisionPriority } from "@dnd-kit/abstract";
|
|
2
|
+
import { useDroppable } from "@dnd-kit/react";
|
|
3
|
+
import {
|
|
4
|
+
BOARD_TASK_CONTAINER_DND_TYPE,
|
|
5
|
+
BOARD_TASK_DND_TYPE,
|
|
6
|
+
type BoardDndLayout,
|
|
7
|
+
boardTaskContainerData,
|
|
8
|
+
} from "./dndReactModel";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Phase 1 React-first wrapper for task drop containers.
|
|
12
|
+
* This encodes the board's container metadata once so stacked lists and lane
|
|
13
|
+
* bands can share the same droppable configuration in later phases.
|
|
14
|
+
*/
|
|
15
|
+
export function useBoardTaskContainerDroppableReact({
|
|
16
|
+
containerId,
|
|
17
|
+
layout,
|
|
18
|
+
listId,
|
|
19
|
+
status,
|
|
20
|
+
}: {
|
|
21
|
+
containerId: string;
|
|
22
|
+
layout: BoardDndLayout;
|
|
23
|
+
listId: number;
|
|
24
|
+
status?: string;
|
|
25
|
+
}) {
|
|
26
|
+
return useDroppable({
|
|
27
|
+
id: containerId,
|
|
28
|
+
type: BOARD_TASK_CONTAINER_DND_TYPE,
|
|
29
|
+
accept: BOARD_TASK_DND_TYPE,
|
|
30
|
+
collisionPriority: CollisionPriority.Low,
|
|
31
|
+
data: boardTaskContainerData(containerId, layout, listId, status),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { useSortable } from "@dnd-kit/react/sortable";
|
|
2
|
-
import { BOARD_TASK_DND_TYPE, boardTaskDragData } from "./dndReactModel";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Phase 1 React-first wrapper for sortable task rows.
|
|
6
|
-
* Group and index are explicit so grouped multi-list movement can follow the
|
|
7
|
-
* official multiple-sortable-lists approach during later phases.
|
|
8
|
-
*/
|
|
9
|
-
export function useBoardTaskSortableReact(
|
|
10
|
-
taskId: number,
|
|
11
|
-
sortableId: string,
|
|
12
|
-
containerId: string,
|
|
13
|
-
index: number,
|
|
14
|
-
) {
|
|
15
|
-
return useSortable({
|
|
16
|
-
id: sortableId,
|
|
17
|
-
index,
|
|
18
|
-
group: containerId,
|
|
19
|
-
type: BOARD_TASK_DND_TYPE,
|
|
20
|
-
accept: BOARD_TASK_DND_TYPE,
|
|
21
|
-
// Mirror the working board-list route so task drags use the same
|
|
22
|
-
// React-first feedback clone behavior as columns.
|
|
23
|
-
feedback: "clone",
|
|
24
|
-
data: boardTaskDragData(taskId, containerId),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
1
|
+
import { useSortable } from "@dnd-kit/react/sortable";
|
|
2
|
+
import { BOARD_TASK_DND_TYPE, boardTaskDragData } from "./dndReactModel";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Phase 1 React-first wrapper for sortable task rows.
|
|
6
|
+
* Group and index are explicit so grouped multi-list movement can follow the
|
|
7
|
+
* official multiple-sortable-lists approach during later phases.
|
|
8
|
+
*/
|
|
9
|
+
export function useBoardTaskSortableReact(
|
|
10
|
+
taskId: number,
|
|
11
|
+
sortableId: string,
|
|
12
|
+
containerId: string,
|
|
13
|
+
index: number,
|
|
14
|
+
) {
|
|
15
|
+
return useSortable({
|
|
16
|
+
id: sortableId,
|
|
17
|
+
index,
|
|
18
|
+
group: containerId,
|
|
19
|
+
type: BOARD_TASK_DND_TYPE,
|
|
20
|
+
accept: BOARD_TASK_DND_TYPE,
|
|
21
|
+
// Mirror the working board-list route so task drags use the same
|
|
22
|
+
// React-first feedback clone behavior as columns.
|
|
23
|
+
feedback: "clone",
|
|
24
|
+
data: boardTaskDragData(taskId, containerId),
|
|
25
|
+
});
|
|
26
|
+
}
|