react-native-body-parts-anatomy 0.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.
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/THIRD_PARTY_NOTICES.md +41 -0
- package/lib/module/BodySilhouette.js +240 -0
- package/lib/module/BodySilhouette.js.map +1 -0
- package/lib/module/data/bodyRegions.generated.js +1907 -0
- package/lib/module/data/bodyRegions.generated.js.map +1 -0
- package/lib/module/hooks/useReducedMotion.js +37 -0
- package/lib/module/hooks/useReducedMotion.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/lookups.js +18 -0
- package/lib/module/lookups.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/BodySilhouette.d.ts +24 -0
- package/lib/typescript/src/BodySilhouette.d.ts.map +1 -0
- package/lib/typescript/src/data/bodyRegions.generated.d.ts +26 -0
- package/lib/typescript/src/data/bodyRegions.generated.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useReducedMotion.d.ts +2 -0
- package/lib/typescript/src/hooks/useReducedMotion.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/lookups.d.ts +3 -0
- package/lib/typescript/src/lookups.d.ts.map +1 -0
- package/package.json +109 -0
- package/src/BodySilhouette.tsx +221 -0
- package/src/data/bodyRegions.generated.ts +717 -0
- package/src/hooks/useReducedMotion.ts +38 -0
- package/src/index.ts +12 -0
- package/src/lookups.ts +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eslam Elfateh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# react-native-body-parts-anatomy
|
|
2
|
+
|
|
3
|
+
Tappable, zoomable, individually-addressable human body diagram for React Native — 308 SVG
|
|
4
|
+
fragments across male/female, front/back, zero native code.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- **308 individually-tappable fragments** (vs. ~35 merged regions in other body-map libraries) —
|
|
9
|
+
each muscle group is split into its real anatomical sub-pieces (e.g. `abs` is 8 separate
|
|
10
|
+
fragments per gender/view), each with an auto-derived `side` (`left`/`right`/`null`) and `axis`
|
|
11
|
+
(`upper`/`middle`/`lower`/`inner`/`center`/`outer`/`null`) label.
|
|
12
|
+
- Male/female × front/back — 4 view combinations.
|
|
13
|
+
- Pinch-to-zoom, pan-while-zoomed, and step +/− buttons, all in one component.
|
|
14
|
+
- Two modes: **interactive picker** (`onFragmentPress`) or **read-only diagram**
|
|
15
|
+
(`zoomable={false}`, colored via `colorForSlug`).
|
|
16
|
+
- **Zero native code** — pure JS/TSX, only existing community peers
|
|
17
|
+
(`react-native-svg`, `react-native-gesture-handler`, `react-native-reanimated`).
|
|
18
|
+
- Fully themeable via props — no bundled theme system, no i18n framework assumed.
|
|
19
|
+
- Honors the OS Reduce Motion setting automatically.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install react-native-body-parts-anatomy
|
|
25
|
+
npx expo install react-native-svg react-native-gesture-handler react-native-reanimated
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Required setup
|
|
29
|
+
|
|
30
|
+
**Wrap your app root in `GestureHandlerRootView`.** The zoomable mode uses a `GestureDetector`
|
|
31
|
+
for pinch/pan, which throws at runtime without it:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
35
|
+
|
|
36
|
+
export default function App() {
|
|
37
|
+
return (
|
|
38
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
39
|
+
{/* ...your app... */}
|
|
40
|
+
</GestureHandlerRootView>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
You can skip this only if you exclusively use `zoomable={false}`.
|
|
46
|
+
|
|
47
|
+
On the bare React Native CLI (not Expo), also follow each peer's own native setup —
|
|
48
|
+
notably `react-native-reanimated`'s Babel plugin. Those are the peers' setup steps, not this
|
|
49
|
+
package's.
|
|
50
|
+
|
|
51
|
+
> **Reanimated v4 users:** Reanimated 4 moved worklets into a separate `react-native-worklets`
|
|
52
|
+
> package and requires the New Architecture. This package never imports `react-native-worklets`
|
|
53
|
+
> directly, so it isn't declared as a peer here — but you must satisfy *Reanimated's* own peer
|
|
54
|
+
> requirement, and keep a **single** `react-native-worklets` version in your tree. A mismatch
|
|
55
|
+
> between the installed JS version and the Babel plugin version fails at startup with
|
|
56
|
+
> `[Worklets] Mismatch between JavaScript code version and Worklets Babel plugin version`.
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
### Interactive picker
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { useState } from 'react';
|
|
64
|
+
import { BodySilhouette } from 'react-native-body-parts-anatomy';
|
|
65
|
+
|
|
66
|
+
function PainAreaPicker() {
|
|
67
|
+
const [selectedSlugs, setSelectedSlugs] = useState<string[]>([]);
|
|
68
|
+
|
|
69
|
+
const toggleFragment = (slug: string) => {
|
|
70
|
+
setSelectedSlugs((current) =>
|
|
71
|
+
current.includes(slug) ? current.filter((item) => item !== slug) : [...current, slug]
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<BodySilhouette
|
|
77
|
+
gender="male"
|
|
78
|
+
view="front"
|
|
79
|
+
selectedSlugs={selectedSlugs}
|
|
80
|
+
onFragmentPress={toggleFragment}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Read-only diagram
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { BodySilhouette } from 'react-native-body-parts-anatomy';
|
|
90
|
+
|
|
91
|
+
function SymptomDiagram({ colorBySlug }: { colorBySlug: Map<string, string> }) {
|
|
92
|
+
return (
|
|
93
|
+
<BodySilhouette
|
|
94
|
+
gender="female"
|
|
95
|
+
view="back"
|
|
96
|
+
zoomable={false}
|
|
97
|
+
selectedSlugs={Array.from(colorBySlug.keys())}
|
|
98
|
+
colorForSlug={(slug) => colorBySlug.get(slug)}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Props
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Description |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `gender` | `'male' \| 'female'` | — (required) | |
|
|
109
|
+
| `view` | `'front' \| 'back'` | — (required) | |
|
|
110
|
+
| `selectedSlugs` | `string[]` | — (required) | Fragment slugs currently highlighted. |
|
|
111
|
+
| `colorForSlug` | `(slug: string) => string \| undefined` | — | Per-slug color override for selected fragments; falls back to `selectedFragmentColor` when it returns `undefined`. |
|
|
112
|
+
| `onFragmentPress` | `(slug: string) => void` | — | Omit for a read-only diagram. |
|
|
113
|
+
| `zoomable` | `boolean` | `true` | Enables pinch/pan/step-zoom and the floating +/− buttons. |
|
|
114
|
+
| `outlineColor` | `string` | `'#333333'` | Skin-outline stroke color. |
|
|
115
|
+
| `unselectedFragmentColor` | `string` | `'#D6D6D6'` | Fill for fragments not in `selectedSlugs`. |
|
|
116
|
+
| `selectedFragmentColor` | `string` | `'#2563EB'` | Fallback fill when `colorForSlug` is omitted or returns `undefined`. |
|
|
117
|
+
| `zoomButtonBackgroundColor` | `string` | `'#FFFFFF'` | |
|
|
118
|
+
| `zoomButtonBorderColor` | `string` | `'#D6D6D6'` | |
|
|
119
|
+
| `zoomButtonIconColor` | `string` | `'#333333'` | |
|
|
120
|
+
| `zoomInAccessibilityLabel` | `string` | `'Zoom in'` | |
|
|
121
|
+
| `zoomOutAccessibilityLabel` | `string` | `'Zoom out'` | |
|
|
122
|
+
| `minScale` | `number` | `1` | |
|
|
123
|
+
| `maxScale` | `number` | `3` | |
|
|
124
|
+
| `zoomStep` | `number` | `0.5` | Scale delta per tap of a zoom button. |
|
|
125
|
+
| `zoomAnimationDurationMs` | `number` | `200` | |
|
|
126
|
+
|
|
127
|
+
## Exported data & types
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { BODY_REGIONS, FRAGMENTS_BY_PARENT, FRAGMENT_BY_SLUG } from 'react-native-body-parts-anatomy';
|
|
131
|
+
import type { BodyFragment, BodyRegionSet } from 'react-native-body-parts-anatomy';
|
|
132
|
+
|
|
133
|
+
// The full fragment tree, if you need to iterate it yourself.
|
|
134
|
+
BODY_REGIONS.male.front.fragments; // BodyFragment[]
|
|
135
|
+
|
|
136
|
+
// Reverse lookup: parent muscle name -> every fragment slug under it
|
|
137
|
+
// (e.g. legacy-data migration, or building a "select whole muscle" affordance).
|
|
138
|
+
FRAGMENTS_BY_PARENT['abs']; // ["abs-male-front-1", "abs-female-front-1", ...]
|
|
139
|
+
|
|
140
|
+
// O(1) lookup by fragment slug — useful for building your own label text.
|
|
141
|
+
const fragment = FRAGMENT_BY_SLUG['abs-male-front-3'];
|
|
142
|
+
// { slug, parentSlug: 'abs', side: 'left', axis: 'upper', pathData }
|
|
143
|
+
const label = [fragment.parentSlug, fragment.side, fragment.axis].filter(Boolean).join(' · ');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Regenerating the data (maintainers only)
|
|
147
|
+
|
|
148
|
+
`src/data/bodyRegions.generated.ts` is generated, not hand-written, by
|
|
149
|
+
`scripts/generate-body-regions.mjs`, which reads `react-native-body-highlighter`'s raw SVG path
|
|
150
|
+
data. This script is repo-only — it is **not** part of the published package (see the `files`
|
|
151
|
+
allowlist in `package.json`) and pulls in `svgpath` as a devDependency only it uses.
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
npm run generate-data
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This temporarily installs `react-native-body-highlighter@3.2.0` (`--no-save`), runs the codegen,
|
|
158
|
+
then leaves your `package.json` untouched. See `THIRD_PARTY_NOTICES.md` for why this package
|
|
159
|
+
depends on that library's data at build time but never at runtime.
|
|
160
|
+
|
|
161
|
+
## Known limitations
|
|
162
|
+
|
|
163
|
+
- **`head` and `hair` are excluded** from the fragment data (not relevant to the pain-tracking use
|
|
164
|
+
case this was originally built for). Re-including them is a one-line change to
|
|
165
|
+
`EXCLUDED_SLUGS` in `scripts/generate-body-regions.mjs`, followed by regenerating and
|
|
166
|
+
re-verifying the data — not done for this initial release.
|
|
167
|
+
|
|
168
|
+
## License & attribution
|
|
169
|
+
|
|
170
|
+
MIT — see [LICENSE](./LICENSE).
|
|
171
|
+
|
|
172
|
+
The fragment path data is derived from `react-native-body-highlighter` (MIT, ELABBASSI Hicham) —
|
|
173
|
+
see [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for the full attribution.
|
|
174
|
+
|
|
175
|
+
## Contributing
|
|
176
|
+
|
|
177
|
+
- [Development workflow](CONTRIBUTING.md#development-workflow)
|
|
178
|
+
- [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
|
|
179
|
+
- [Code of conduct](CODE_OF_CONDUCT.md)
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
This package's anatomical path data (`src/data/bodyRegions.generated.ts`) is derived from
|
|
4
|
+
**react-native-body-highlighter** by ELABBASSI Hicham (npm: `HichamELBSI`), an MIT-licensed
|
|
5
|
+
package.
|
|
6
|
+
|
|
7
|
+
Specifically: this package's SVG fragment paths and skin-outline paths are extracted, split into
|
|
8
|
+
finer-grained addressable pieces, and re-serialized from `react-native-body-highlighter`'s
|
|
9
|
+
bundled SVG path data (see `scripts/generate-body-regions.mjs` for the extraction process). No
|
|
10
|
+
code from `react-native-body-highlighter` is executed at runtime by this package — it is only a
|
|
11
|
+
build-time input, temporarily installed as a dev dependency when regenerating the data.
|
|
12
|
+
|
|
13
|
+
Because the resulting path data is a derivative of `react-native-body-highlighter`'s copyrighted
|
|
14
|
+
SVG assets, its original MIT license and copyright notice are reproduced below, as required by
|
|
15
|
+
the MIT license's terms.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## react-native-body-highlighter — MIT License
|
|
20
|
+
|
|
21
|
+
MIT License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2022 ELABBASSI Hicham
|
|
24
|
+
|
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
26
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
27
|
+
in the Software without restriction, including without limitation the rights
|
|
28
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
29
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
30
|
+
furnished to do so, subject to the following conditions:
|
|
31
|
+
|
|
32
|
+
The above copyright notice and this permission notice shall be included in all
|
|
33
|
+
copies or substantial portions of the Software.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
41
|
+
SOFTWARE.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { StyleSheet, View, Pressable, Text } from 'react-native';
|
|
5
|
+
import Svg, { Path } from 'react-native-svg';
|
|
6
|
+
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
|
|
7
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
|
|
8
|
+
import { BODY_REGIONS } from "./data/bodyRegions.generated.js";
|
|
9
|
+
import { useReducedMotion } from "./hooks/useReducedMotion.js";
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
function viewBoxAspectRatio(viewBox) {
|
|
12
|
+
const tokens = viewBox.split(' ');
|
|
13
|
+
const width = Number(tokens[2]);
|
|
14
|
+
const height = Number(tokens[3]);
|
|
15
|
+
return width / height;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Only four gender/view combinations exist (BODY_REGIONS is static generated
|
|
19
|
+
// data), so their aspect ratios are precomputed once here rather than built
|
|
20
|
+
// as a fresh style object inline on every render.
|
|
21
|
+
const CONTAINER_ASPECT_RATIO_STYLE = {
|
|
22
|
+
'male-front': {
|
|
23
|
+
aspectRatio: viewBoxAspectRatio(BODY_REGIONS.male.front.viewBox)
|
|
24
|
+
},
|
|
25
|
+
'male-back': {
|
|
26
|
+
aspectRatio: viewBoxAspectRatio(BODY_REGIONS.male.back.viewBox)
|
|
27
|
+
},
|
|
28
|
+
'female-front': {
|
|
29
|
+
aspectRatio: viewBoxAspectRatio(BODY_REGIONS.female.front.viewBox)
|
|
30
|
+
},
|
|
31
|
+
'female-back': {
|
|
32
|
+
aspectRatio: viewBoxAspectRatio(BODY_REGIONS.female.back.viewBox)
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function clampWorklet(value, min, max) {
|
|
36
|
+
'worklet';
|
|
37
|
+
|
|
38
|
+
return Math.min(Math.max(value, min), max);
|
|
39
|
+
}
|
|
40
|
+
function maxTranslateWorklet(currentScale, containerSize) {
|
|
41
|
+
'worklet';
|
|
42
|
+
|
|
43
|
+
return Math.max(0, containerSize * (currentScale - 1) / 2);
|
|
44
|
+
}
|
|
45
|
+
export function BodySilhouette({
|
|
46
|
+
gender,
|
|
47
|
+
view,
|
|
48
|
+
selectedSlugs,
|
|
49
|
+
colorForSlug,
|
|
50
|
+
onFragmentPress,
|
|
51
|
+
zoomable = true,
|
|
52
|
+
outlineColor = '#333333',
|
|
53
|
+
unselectedFragmentColor = '#D6D6D6',
|
|
54
|
+
selectedFragmentColor = '#2563EB',
|
|
55
|
+
zoomButtonBackgroundColor = '#FFFFFF',
|
|
56
|
+
zoomButtonBorderColor = '#D6D6D6',
|
|
57
|
+
zoomButtonIconColor = '#333333',
|
|
58
|
+
zoomInAccessibilityLabel = 'Zoom in',
|
|
59
|
+
zoomOutAccessibilityLabel = 'Zoom out',
|
|
60
|
+
minScale = 1,
|
|
61
|
+
maxScale = 3,
|
|
62
|
+
zoomStep = 0.5,
|
|
63
|
+
zoomAnimationDurationMs = 200
|
|
64
|
+
}) {
|
|
65
|
+
const reducedMotion = useReducedMotion();
|
|
66
|
+
const regionSet = BODY_REGIONS[gender][view];
|
|
67
|
+
const aspectRatioStyle = CONTAINER_ASPECT_RATIO_STYLE[`${gender}-${view}`];
|
|
68
|
+
const scale = useSharedValue(1);
|
|
69
|
+
const savedScale = useSharedValue(1);
|
|
70
|
+
const translateX = useSharedValue(0);
|
|
71
|
+
const translateY = useSharedValue(0);
|
|
72
|
+
const savedTranslateX = useSharedValue(0);
|
|
73
|
+
const savedTranslateY = useSharedValue(0);
|
|
74
|
+
const containerWidth = useSharedValue(0);
|
|
75
|
+
const containerHeight = useSharedValue(0);
|
|
76
|
+
|
|
77
|
+
// A zoom/pan carried over onto a different gender or view is confusing,
|
|
78
|
+
// not helpful — always start fresh when the displayed image changes.
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
scale.value = 1;
|
|
81
|
+
savedScale.value = 1;
|
|
82
|
+
translateX.value = 0;
|
|
83
|
+
translateY.value = 0;
|
|
84
|
+
savedTranslateX.value = 0;
|
|
85
|
+
savedTranslateY.value = 0;
|
|
86
|
+
}, [gender, view, scale, savedScale, translateX, translateY, savedTranslateX, savedTranslateY]);
|
|
87
|
+
const pinchGesture = Gesture.Pinch().onUpdate(event => {
|
|
88
|
+
scale.value = clampWorklet(savedScale.value * event.scale, minScale, maxScale);
|
|
89
|
+
}).onEnd(() => {
|
|
90
|
+
savedScale.value = scale.value;
|
|
91
|
+
const maxX = maxTranslateWorklet(scale.value, containerWidth.value);
|
|
92
|
+
const maxY = maxTranslateWorklet(scale.value, containerHeight.value);
|
|
93
|
+
translateX.value = clampWorklet(translateX.value, -maxX, maxX);
|
|
94
|
+
translateY.value = clampWorklet(translateY.value, -maxY, maxY);
|
|
95
|
+
savedTranslateX.value = translateX.value;
|
|
96
|
+
savedTranslateY.value = translateY.value;
|
|
97
|
+
});
|
|
98
|
+
const panGesture = Gesture.Pan().onUpdate(event => {
|
|
99
|
+
if (savedScale.value <= minScale) return;
|
|
100
|
+
const maxX = maxTranslateWorklet(savedScale.value, containerWidth.value);
|
|
101
|
+
const maxY = maxTranslateWorklet(savedScale.value, containerHeight.value);
|
|
102
|
+
translateX.value = clampWorklet(savedTranslateX.value + event.translationX, -maxX, maxX);
|
|
103
|
+
translateY.value = clampWorklet(savedTranslateY.value + event.translationY, -maxY, maxY);
|
|
104
|
+
}).onEnd(() => {
|
|
105
|
+
savedTranslateX.value = translateX.value;
|
|
106
|
+
savedTranslateY.value = translateY.value;
|
|
107
|
+
});
|
|
108
|
+
const composedGesture = Gesture.Simultaneous(pinchGesture, panGesture);
|
|
109
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
110
|
+
transform: [{
|
|
111
|
+
translateX: translateX.value
|
|
112
|
+
}, {
|
|
113
|
+
translateY: translateY.value
|
|
114
|
+
}, {
|
|
115
|
+
scale: scale.value
|
|
116
|
+
}]
|
|
117
|
+
}));
|
|
118
|
+
const applyZoomStep = direction => {
|
|
119
|
+
const nextScale = clampWorklet(savedScale.value + direction * zoomStep, minScale, maxScale);
|
|
120
|
+
savedScale.value = nextScale;
|
|
121
|
+
scale.value = reducedMotion ? nextScale : withTiming(nextScale, {
|
|
122
|
+
duration: zoomAnimationDurationMs
|
|
123
|
+
});
|
|
124
|
+
if (nextScale <= minScale) {
|
|
125
|
+
savedTranslateX.value = 0;
|
|
126
|
+
savedTranslateY.value = 0;
|
|
127
|
+
translateX.value = reducedMotion ? 0 : withTiming(0, {
|
|
128
|
+
duration: zoomAnimationDurationMs
|
|
129
|
+
});
|
|
130
|
+
translateY.value = reducedMotion ? 0 : withTiming(0, {
|
|
131
|
+
duration: zoomAnimationDurationMs
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const handleLayout = event => {
|
|
136
|
+
containerWidth.value = event.nativeEvent.layout.width;
|
|
137
|
+
containerHeight.value = event.nativeEvent.layout.height;
|
|
138
|
+
};
|
|
139
|
+
const diagramContent = /*#__PURE__*/_jsxs(Svg, {
|
|
140
|
+
viewBox: regionSet.viewBox,
|
|
141
|
+
width: "100%",
|
|
142
|
+
height: "100%",
|
|
143
|
+
accessible: true,
|
|
144
|
+
accessibilityLabel: `${gender}-body-${view}`,
|
|
145
|
+
children: [/*#__PURE__*/_jsx(Path, {
|
|
146
|
+
d: regionSet.outlineD,
|
|
147
|
+
stroke: outlineColor,
|
|
148
|
+
strokeWidth: 2,
|
|
149
|
+
fill: "none",
|
|
150
|
+
vectorEffect: "non-scaling-stroke"
|
|
151
|
+
}), regionSet.fragments.map(fragment => {
|
|
152
|
+
const isSelected = selectedSlugs.includes(fragment.slug);
|
|
153
|
+
const fillColor = isSelected ? colorForSlug?.(fragment.slug) ?? selectedFragmentColor : unselectedFragmentColor;
|
|
154
|
+
return /*#__PURE__*/_jsx(Path, {
|
|
155
|
+
d: fragment.pathData,
|
|
156
|
+
fill: fillColor,
|
|
157
|
+
onPressIn: onFragmentPress ? () => onFragmentPress(fragment.slug) : undefined
|
|
158
|
+
}, fragment.slug);
|
|
159
|
+
})]
|
|
160
|
+
});
|
|
161
|
+
if (!zoomable) {
|
|
162
|
+
return /*#__PURE__*/_jsx(View, {
|
|
163
|
+
style: [localStyles.container, aspectRatioStyle],
|
|
164
|
+
children: diagramContent
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const zoomButtonStyle = {
|
|
168
|
+
backgroundColor: zoomButtonBackgroundColor,
|
|
169
|
+
borderColor: zoomButtonBorderColor
|
|
170
|
+
};
|
|
171
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
172
|
+
style: [localStyles.zoomableContainer, aspectRatioStyle],
|
|
173
|
+
onLayout: handleLayout,
|
|
174
|
+
children: [/*#__PURE__*/_jsx(GestureDetector, {
|
|
175
|
+
gesture: composedGesture,
|
|
176
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
177
|
+
style: [localStyles.container, animatedStyle],
|
|
178
|
+
children: diagramContent
|
|
179
|
+
})
|
|
180
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
181
|
+
style: localStyles.zoomControls,
|
|
182
|
+
children: [/*#__PURE__*/_jsx(Pressable, {
|
|
183
|
+
style: [localStyles.zoomButton, zoomButtonStyle],
|
|
184
|
+
onPress: () => applyZoomStep(-1),
|
|
185
|
+
hitSlop: 8,
|
|
186
|
+
accessibilityRole: "button",
|
|
187
|
+
accessibilityLabel: zoomOutAccessibilityLabel,
|
|
188
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
189
|
+
style: [localStyles.zoomButtonGlyph, {
|
|
190
|
+
color: zoomButtonIconColor
|
|
191
|
+
}],
|
|
192
|
+
children: "\u2212"
|
|
193
|
+
})
|
|
194
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
195
|
+
style: [localStyles.zoomButton, zoomButtonStyle],
|
|
196
|
+
onPress: () => applyZoomStep(1),
|
|
197
|
+
hitSlop: 8,
|
|
198
|
+
accessibilityRole: "button",
|
|
199
|
+
accessibilityLabel: zoomInAccessibilityLabel,
|
|
200
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
201
|
+
style: [localStyles.zoomButtonGlyph, {
|
|
202
|
+
color: zoomButtonIconColor
|
|
203
|
+
}],
|
|
204
|
+
children: "+"
|
|
205
|
+
})
|
|
206
|
+
})]
|
|
207
|
+
})]
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const localStyles = StyleSheet.create({
|
|
211
|
+
container: {
|
|
212
|
+
width: '100%',
|
|
213
|
+
height: '100%'
|
|
214
|
+
},
|
|
215
|
+
zoomableContainer: {
|
|
216
|
+
width: '100%',
|
|
217
|
+
overflow: 'hidden'
|
|
218
|
+
},
|
|
219
|
+
zoomControls: {
|
|
220
|
+
position: 'absolute',
|
|
221
|
+
bottom: 8,
|
|
222
|
+
end: 8,
|
|
223
|
+
flexDirection: 'row',
|
|
224
|
+
gap: 8
|
|
225
|
+
},
|
|
226
|
+
zoomButton: {
|
|
227
|
+
width: 36,
|
|
228
|
+
height: 36,
|
|
229
|
+
borderRadius: 18,
|
|
230
|
+
borderWidth: 1,
|
|
231
|
+
alignItems: 'center',
|
|
232
|
+
justifyContent: 'center'
|
|
233
|
+
},
|
|
234
|
+
zoomButtonGlyph: {
|
|
235
|
+
fontSize: 16,
|
|
236
|
+
fontWeight: '600',
|
|
237
|
+
lineHeight: 18
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
//# sourceMappingURL=BodySilhouette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","StyleSheet","View","Pressable","Text","Svg","Path","GestureDetector","Gesture","Animated","useSharedValue","useAnimatedStyle","withTiming","BODY_REGIONS","useReducedMotion","jsx","_jsx","jsxs","_jsxs","viewBoxAspectRatio","viewBox","tokens","split","width","Number","height","CONTAINER_ASPECT_RATIO_STYLE","aspectRatio","male","front","back","female","clampWorklet","value","min","max","Math","maxTranslateWorklet","currentScale","containerSize","BodySilhouette","gender","view","selectedSlugs","colorForSlug","onFragmentPress","zoomable","outlineColor","unselectedFragmentColor","selectedFragmentColor","zoomButtonBackgroundColor","zoomButtonBorderColor","zoomButtonIconColor","zoomInAccessibilityLabel","zoomOutAccessibilityLabel","minScale","maxScale","zoomStep","zoomAnimationDurationMs","reducedMotion","regionSet","aspectRatioStyle","scale","savedScale","translateX","translateY","savedTranslateX","savedTranslateY","containerWidth","containerHeight","pinchGesture","Pinch","onUpdate","event","onEnd","maxX","maxY","panGesture","Pan","translationX","translationY","composedGesture","Simultaneous","animatedStyle","transform","applyZoomStep","direction","nextScale","duration","handleLayout","nativeEvent","layout","diagramContent","accessible","accessibilityLabel","children","d","outlineD","stroke","strokeWidth","fill","vectorEffect","fragments","map","fragment","isSelected","includes","slug","fillColor","pathData","onPressIn","undefined","style","localStyles","container","zoomButtonStyle","backgroundColor","borderColor","zoomableContainer","onLayout","gesture","zoomControls","zoomButton","onPress","hitSlop","accessibilityRole","zoomButtonGlyph","color","create","overflow","position","bottom","end","flexDirection","gap","borderRadius","borderWidth","alignItems","justifyContent","fontSize","fontWeight","lineHeight"],"sourceRoot":"../../src","sources":["BodySilhouette.tsx"],"mappings":"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","ignoreList":[]}
|