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.
Files changed (45) hide show
  1. package/README.md +59 -0
  2. package/dist/index.cjs +1483 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +244 -0
  5. package/dist/index.d.ts +244 -0
  6. package/dist/index.js +1473 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +60 -0
  9. package/src/HpoVisualizer.tsx +210 -0
  10. package/src/OrganSvg.tsx +141 -0
  11. package/src/__tests__/colorScheme.test.tsx +146 -0
  12. package/src/__tests__/createStrictColorPalette.test.ts +135 -0
  13. package/src/__tests__/renderOrder.test.tsx +146 -0
  14. package/src/__tests__/setup.ts +1 -0
  15. package/src/constants.ts +158 -0
  16. package/src/index.ts +34 -0
  17. package/src/lib/createStrictColorPalette.ts +57 -0
  18. package/src/lib/index.ts +1 -0
  19. package/src/svg/Blood.tsx +23 -0
  20. package/src/svg/Body.tsx +24 -0
  21. package/src/svg/Breast.tsx +42 -0
  22. package/src/svg/Cell.tsx +79 -0
  23. package/src/svg/Constitutional.tsx +29 -0
  24. package/src/svg/Digestive.tsx +28 -0
  25. package/src/svg/Ear.tsx +23 -0
  26. package/src/svg/Endocrine.tsx +32 -0
  27. package/src/svg/Eye.tsx +23 -0
  28. package/src/svg/Growth.tsx +23 -0
  29. package/src/svg/Head.tsx +51 -0
  30. package/src/svg/Heart.tsx +23 -0
  31. package/src/svg/Immune.tsx +32 -0
  32. package/src/svg/Integument.tsx +58 -0
  33. package/src/svg/Kidney.tsx +41 -0
  34. package/src/svg/Limbs.tsx +46 -0
  35. package/src/svg/Lung.tsx +23 -0
  36. package/src/svg/Metabolism.tsx +41 -0
  37. package/src/svg/Muscle.tsx +225 -0
  38. package/src/svg/Neoplasm.tsx +23 -0
  39. package/src/svg/Nervous.tsx +49 -0
  40. package/src/svg/Prenatal.tsx +23 -0
  41. package/src/svg/ThoracicCavity.tsx +28 -0
  42. package/src/svg/Voice.tsx +23 -0
  43. package/src/svg/index.ts +54 -0
  44. package/src/types.ts +162 -0
  45. 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
+ }