hpo-react-visualizer 0.0.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.
- package/README.md +59 -0
- package/dist/index.cjs +1483 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +244 -0
- package/dist/index.d.ts +244 -0
- package/dist/index.js +1473 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
- package/src/HpoVisualizer.tsx +210 -0
- package/src/OrganSvg.tsx +141 -0
- package/src/__tests__/colorScheme.test.tsx +146 -0
- package/src/__tests__/createStrictColorPalette.test.ts +135 -0
- package/src/__tests__/renderOrder.test.tsx +146 -0
- package/src/__tests__/setup.ts +1 -0
- package/src/constants.ts +158 -0
- package/src/index.ts +34 -0
- package/src/lib/createStrictColorPalette.ts +57 -0
- package/src/lib/index.ts +1 -0
- package/src/svg/Blood.tsx +23 -0
- package/src/svg/Body.tsx +24 -0
- package/src/svg/Breast.tsx +42 -0
- package/src/svg/Cell.tsx +79 -0
- package/src/svg/Constitutional.tsx +29 -0
- package/src/svg/Digestive.tsx +28 -0
- package/src/svg/Ear.tsx +23 -0
- package/src/svg/Endocrine.tsx +32 -0
- package/src/svg/Eye.tsx +23 -0
- package/src/svg/Growth.tsx +23 -0
- package/src/svg/Head.tsx +51 -0
- package/src/svg/Heart.tsx +23 -0
- package/src/svg/Immune.tsx +32 -0
- package/src/svg/Integument.tsx +58 -0
- package/src/svg/Kidney.tsx +41 -0
- package/src/svg/Limbs.tsx +46 -0
- package/src/svg/Lung.tsx +23 -0
- package/src/svg/Metabolism.tsx +41 -0
- package/src/svg/Muscle.tsx +225 -0
- package/src/svg/Neoplasm.tsx +23 -0
- package/src/svg/Nervous.tsx +49 -0
- package/src/svg/Prenatal.tsx +23 -0
- package/src/svg/ThoracicCavity.tsx +28 -0
- package/src/svg/Voice.tsx +23 -0
- package/src/svg/index.ts +54 -0
- package/src/types.ts +162 -0
- package/src/useOrganInteraction.ts +130 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import type { OrganId, OrganInteractionHandlers, OrganInteractionState } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface UseOrganInteractionOptions {
|
|
5
|
+
/** Controlled hover state from parent */
|
|
6
|
+
hoveredOrgan?: OrganId | null;
|
|
7
|
+
/** Controlled selected state from parent */
|
|
8
|
+
selectedOrgan?: OrganId | null;
|
|
9
|
+
/** Callback when hover state changes */
|
|
10
|
+
onHover?: (organId: OrganId | null) => void;
|
|
11
|
+
/** Callback when selected state changes */
|
|
12
|
+
onSelect?: (organId: OrganId | null) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UseOrganInteractionResult {
|
|
16
|
+
/** Current interaction state */
|
|
17
|
+
state: OrganInteractionState;
|
|
18
|
+
/** Event handlers for organs */
|
|
19
|
+
handlers: OrganInteractionHandlers;
|
|
20
|
+
/** Check if an organ is currently highlighted (hovered or selected) */
|
|
21
|
+
isHighlighted: (organId: OrganId) => boolean;
|
|
22
|
+
/** Check if an organ is currently selected */
|
|
23
|
+
isSelected: (organId: OrganId) => boolean;
|
|
24
|
+
/** Check if an organ is currently hovered */
|
|
25
|
+
isHovered: (organId: OrganId) => boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Hook to manage organ hover and click interactions
|
|
30
|
+
*
|
|
31
|
+
* Behavior:
|
|
32
|
+
* - Mouse enter on organ updates hoveredOrgan and calls onHover
|
|
33
|
+
* - Click on organ sets selectedOrgan and calls onSelect
|
|
34
|
+
* - Click on already selected organ deselects it
|
|
35
|
+
* - Click on different organ changes selection
|
|
36
|
+
* - Hover effects work independently even when an organ is selected
|
|
37
|
+
*/
|
|
38
|
+
export function useOrganInteraction(
|
|
39
|
+
options: UseOrganInteractionOptions = {},
|
|
40
|
+
): UseOrganInteractionResult {
|
|
41
|
+
const {
|
|
42
|
+
hoveredOrgan: controlledHovered,
|
|
43
|
+
selectedOrgan: controlledSelected,
|
|
44
|
+
onHover,
|
|
45
|
+
onSelect,
|
|
46
|
+
} = options;
|
|
47
|
+
|
|
48
|
+
// Internal state for uncontrolled mode
|
|
49
|
+
const [internalHovered, setInternalHovered] = useState<OrganId | null>(null);
|
|
50
|
+
const [internalSelected, setInternalSelected] = useState<OrganId | null>(null);
|
|
51
|
+
|
|
52
|
+
// Determine if we're in controlled mode
|
|
53
|
+
const isHoverControlled = controlledHovered !== undefined;
|
|
54
|
+
const isSelectControlled = controlledSelected !== undefined;
|
|
55
|
+
|
|
56
|
+
// Current state values
|
|
57
|
+
const hoveredOrgan = isHoverControlled ? controlledHovered : internalHovered;
|
|
58
|
+
const selectedOrgan = isSelectControlled ? controlledSelected : internalSelected;
|
|
59
|
+
|
|
60
|
+
const handleMouseEnter = useCallback(
|
|
61
|
+
(organId: OrganId) => {
|
|
62
|
+
if (!isHoverControlled) {
|
|
63
|
+
setInternalHovered(organId);
|
|
64
|
+
}
|
|
65
|
+
onHover?.(organId);
|
|
66
|
+
},
|
|
67
|
+
[isHoverControlled, onHover],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const handleMouseLeave = useCallback(() => {
|
|
71
|
+
if (!isHoverControlled) {
|
|
72
|
+
setInternalHovered(null);
|
|
73
|
+
}
|
|
74
|
+
onHover?.(null);
|
|
75
|
+
}, [isHoverControlled, onHover]);
|
|
76
|
+
|
|
77
|
+
const handleClick = useCallback(
|
|
78
|
+
(organId: OrganId) => {
|
|
79
|
+
const newSelected = selectedOrgan === organId ? null : organId;
|
|
80
|
+
|
|
81
|
+
if (!isSelectControlled) {
|
|
82
|
+
setInternalSelected(newSelected);
|
|
83
|
+
}
|
|
84
|
+
onSelect?.(newSelected);
|
|
85
|
+
|
|
86
|
+
// When deselecting, also clear hover state
|
|
87
|
+
if (newSelected === null) {
|
|
88
|
+
if (!isHoverControlled) {
|
|
89
|
+
setInternalHovered(null);
|
|
90
|
+
}
|
|
91
|
+
onHover?.(null);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[selectedOrgan, isSelectControlled, isHoverControlled, onSelect, onHover],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const state: OrganInteractionState = useMemo(
|
|
98
|
+
() => ({
|
|
99
|
+
hoveredOrgan,
|
|
100
|
+
selectedOrgan,
|
|
101
|
+
}),
|
|
102
|
+
[hoveredOrgan, selectedOrgan],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const handlers: OrganInteractionHandlers = useMemo(
|
|
106
|
+
() => ({
|
|
107
|
+
handleMouseEnter,
|
|
108
|
+
handleMouseLeave,
|
|
109
|
+
handleClick,
|
|
110
|
+
}),
|
|
111
|
+
[handleMouseEnter, handleMouseLeave, handleClick],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const isSelected = useCallback((organId: OrganId) => selectedOrgan === organId, [selectedOrgan]);
|
|
115
|
+
|
|
116
|
+
const isHovered = useCallback((organId: OrganId) => hoveredOrgan === organId, [hoveredOrgan]);
|
|
117
|
+
|
|
118
|
+
const isHighlighted = useCallback(
|
|
119
|
+
(organId: OrganId) => isSelected(organId) || isHovered(organId),
|
|
120
|
+
[isSelected, isHovered],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
state,
|
|
125
|
+
handlers,
|
|
126
|
+
isHighlighted,
|
|
127
|
+
isSelected,
|
|
128
|
+
isHovered,
|
|
129
|
+
};
|
|
130
|
+
}
|