{"_id":"@alexbrand09/famtreejs","_rev":"2-0285dc051bb2f59cacd761cd177d648f","name":"@alexbrand09/famtreejs","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@alexbrand09/famtreejs","version":"0.1.0","keywords":["react","family-tree","genealogy","visualization","d3"],"author":{"name":"Alexander Brand"},"license":"MIT","_id":"@alexbrand09/famtreejs@0.1.0","maintainers":[{"name":"alexbrand09","email":"alexbrand09@gmail.com"}],"homepage":"https://github.com/alexbrand/famtreejs#readme","bugs":{"url":"https://github.com/alexbrand/famtreejs/issues"},"dist":{"shasum":"88f6fb3815570f675219254dbae5f56781a1f26b","tarball":"https://registry.npmjs.org/@alexbrand09/famtreejs/-/famtreejs-0.1.0.tgz","fileCount":27,"integrity":"sha512-r+44sFsxk3ioB1eXGl7UYOZEcaRKwUnl8xB2XUOfbcGAqwrii0DmM9GPWGqwukprT4ES+5cZponUkWDwxlZDtw==","signatures":[{"sig":"MEQCIDCnq6w3spv+j2Xkdoo8um4WNFU2VbJu0GjuLGPxH8r3AiBlTizU/rt2xnNrRDJMOETdwLTd7ImBsYiUy6B6W0nh8Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":226658},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./dist/index.css"},"gitHead":"f919e8e052ae4db9aeb784751c718c6d61b4c447","scripts":{"dev":"vite","lint":"eslint src --ext .ts,.tsx","test":"vitest","build":"vite build && tsc --emitDeclarationOnly --outDir dist","format":"prettier --write \"src/**/*.{ts,tsx}\"","preview":"vite preview","release":"standard-version","test:run":"vitest run","storybook":"storybook dev -p 6006","release:first":"standard-version --first-release","build-storybook":"storybook build"},"_npmUser":{"name":"alexbrand09","email":"alexbrand09@gmail.com"},"repository":{"url":"git+https://github.com/alexbrand/famtreejs.git","type":"git"},"_npmVersion":"11.6.2","description":"React library for rendering interactive family trees with partnership-centric data model","directories":{},"_nodeVersion":"24.12.0","dependencies":{"d3":"^7.9.0","zustand":"^5.0.9","framer-motion":"^12.23.26"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.0","jsdom":"^27.4.0","react":"^19.2.3","eslint":"^9.39.2","vitest":"^4.0.16","globals":"^17.0.0","prettier":"^3.7.4","@types/d3":"^7.4.3","react-dom":"^19.2.3","storybook":"^10.1.11","@eslint/js":"^9.39.2","playwright":"^1.57.0","typescript":"^5.9.3","@types/react":"^19.2.7","@types/react-dom":"^19.2.3","standard-version":"^9.5.0","@vitest/coverage-v8":"^4.0.16","eslint-plugin-react":"^7.37.5","@vitejs/plugin-react":"^5.1.2","@storybook/addon-a11y":"^10.1.11","@storybook/addon-docs":"^10.1.11","@storybook/react-vite":"^10.1.11","@testing-library/react":"^16.3.1","@storybook/addon-vitest":"^10.1.11","eslint-plugin-storybook":"^10.1.11","@chromatic-com/storybook":"^4.1.3","@testing-library/jest-dom":"^6.9.1","@typescript-eslint/parser":"^8.51.0","eslint-plugin-react-hooks":"^7.0.1","@vitest/browser-playwright":"^4.0.16","@storybook/addon-onboarding":"^10.1.11","@typescript-eslint/eslint-plugin":"^8.51.0"},"peerDependencies":{"react":"^18.0.0","react-dom":"^18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/famtreejs_0.1.0_1767421618881_0.931099520623341","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@alexbrand09/famtreejs","version":"0.1.2","description":"React library for rendering interactive family trees with partnership-centric data model","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./styles.css":"./dist/index.css"},"scripts":{"dev":"vite","build":"vite build && tsc --emitDeclarationOnly --outDir dist","preview":"vite preview","test":"vitest","test:run":"vitest run","lint":"eslint src --ext .ts,.tsx","format":"prettier --write \"src/**/*.{ts,tsx}\"","storybook":"storybook dev -p 6006","build-storybook":"storybook build","release":"standard-version","release:first":"standard-version --first-release"},"peerDependencies":{"react":"^18.0.0","react-dom":"^18.0.0"},"keywords":["react","family-tree","genealogy","visualization"],"author":{"name":"Alexander Brand"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/alexbrand/famtreejs.git"},"devDependencies":{"@chromatic-com/storybook":"^4.1.3","@eslint/js":"^9.39.2","@storybook/addon-a11y":"^10.1.11","@storybook/addon-docs":"^10.1.11","@storybook/addon-onboarding":"^10.1.11","@storybook/addon-vitest":"^10.1.11","@storybook/react-vite":"^10.1.11","@testing-library/jest-dom":"^6.9.1","@testing-library/react":"^16.3.1","@types/react":"^19.2.7","@types/react-dom":"^19.2.3","@typescript-eslint/eslint-plugin":"^8.51.0","@typescript-eslint/parser":"^8.51.0","@vitejs/plugin-react":"^5.1.2","@vitest/browser-playwright":"^4.0.16","@vitest/coverage-v8":"^4.0.16","eslint":"^9.39.2","eslint-plugin-react":"^7.37.5","eslint-plugin-react-hooks":"^7.0.1","eslint-plugin-storybook":"^10.1.11","globals":"^17.0.0","jsdom":"^27.4.0","playwright":"^1.57.0","prettier":"^3.7.4","react":"^19.2.3","react-dom":"^19.2.3","standard-version":"^9.5.0","storybook":"^10.1.11","typescript":"^5.9.3","vite":"^7.3.0","vitest":"^4.0.16"},"dependencies":{"framer-motion":"^12.23.26","zustand":"^5.0.9"},"gitHead":"7b3248ecef828b43caab852d7cccb311101de47b","_id":"@alexbrand09/famtreejs@0.1.2","bugs":{"url":"https://github.com/alexbrand/famtreejs/issues"},"homepage":"https://github.com/alexbrand/famtreejs#readme","_nodeVersion":"20.19.6","_npmVersion":"11.7.0","dist":{"integrity":"sha512-QiorYv4VxOgP0sImY5TqNvhuEQc8N6CSLlrf1iAnmFEZa60C3yE7wNdJVYtV+TAz8h8zWm9bzqCpk032jnOXpQ==","shasum":"5d873eef7983e4580201f005c2bc7752067c1c00","tarball":"https://registry.npmjs.org/@alexbrand09/famtreejs/-/famtreejs-0.1.2.tgz","fileCount":27,"unpackedSize":226601,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexbrand09%2ffamtreejs@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICMgEWGSj9zcROrGyHlb8Q/idk6Oe/HrTOAZISXkgG98AiEAqJEDYxnV6RcpG6Ug65/SaFJPPv38k1juznSL0Qw6OV8="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3e1a794a-5908-4ae6-8599-1b6113e195c0"}},"directories":{},"maintainers":[{"name":"alexbrand09","email":"alexbrand09@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/famtreejs_0.1.2_1767423245275_0.5382746917844945"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-03T06:26:58.721Z","modified":"2026-01-03T06:54:05.776Z","0.1.0":"2026-01-03T06:26:59.027Z","0.1.2":"2026-01-03T06:54:05.447Z"},"bugs":{"url":"https://github.com/alexbrand/famtreejs/issues"},"author":{"name":"Alexander Brand"},"license":"MIT","homepage":"https://github.com/alexbrand/famtreejs#readme","keywords":["react","family-tree","genealogy","visualization"],"repository":{"type":"git","url":"git+https://github.com/alexbrand/famtreejs.git"},"description":"React library for rendering interactive family trees with partnership-centric data model","maintainers":[{"name":"alexbrand09","email":"alexbrand09@gmail.com"}],"readme":"# @alexbrand09/famtreejs\n\n[![npm version](https://img.shields.io/npm/v/@alexbrand09/famtreejs.svg)](https://www.npmjs.com/package/@alexbrand09/famtreejs)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA React library for rendering interactive family trees with a partnership-centric data model. Unlike traditional family tree libraries where children belong to a single parent, this library treats partnerships (marriages, unions) as first-class entities, with children descending from partnerships rather than individuals.\n\n## Features\n\n- **Partnership-centric data model** - Children belong to partnerships, not individuals\n- **Multiple orientations** - Top-down, bottom-up, left-right, right-left layouts\n- **Interactive** - Pan, zoom, click, hover, expand/collapse branches\n- **Keyboard accessible** - Full keyboard navigation with ARIA support\n- **Themeable** - Light/dark themes with CSS variables for customization\n- **Animated** - Smooth transitions with Framer Motion, respects `prefers-reduced-motion`\n- **TypeScript** - Full type safety with generics for custom data\n\n## Installation\n\n```bash\nnpm install @alexbrand09/famtreejs\n```\n\n## Quick Start\n\n```tsx\nimport { FamilyTree, BasicPersonCard } from '@alexbrand09/famtreejs';\nimport '@alexbrand09/famtreejs/styles.css';\nimport type { FamilyTreeData } from '@alexbrand09/famtreejs';\n\nconst data: FamilyTreeData<{ name: string }> = {\n  people: [\n    { id: 'p1', data: { name: 'John' } },\n    { id: 'p2', data: { name: 'Mary' } },\n    { id: 'c1', data: { name: 'Alice' } },\n  ],\n  partnerships: [\n    { id: 'u1', partnerIds: ['p1', 'p2'], childIds: ['c1'] }\n  ],\n};\n\nfunction App() {\n  return (\n    <div style={{ width: '100%', height: '600px' }}>\n      <FamilyTree\n        data={data}\n        nodeComponent={BasicPersonCard}\n        onPersonClick={(id, data) => console.log('Clicked:', id, data)}\n      />\n    </div>\n  );\n}\n```\n\n## Data Model\n\n### PersonNode\n\n```typescript\ninterface PersonNode<T = unknown> {\n  id: string;      // Unique identifier\n  data: T;         // Your custom data (passed to nodeComponent)\n}\n```\n\n### Partnership\n\n```typescript\ninterface Partnership {\n  id: string;\n  partnerIds: [string, string | null];  // [partner1, partner2] or [parent, null] for single parent\n  childIds: string[];                    // Children from this partnership\n  type?: 'marriage' | 'civil-union' | 'partnership' | 'other';\n}\n```\n\n### FamilyTreeData\n\n```typescript\ninterface FamilyTreeData<T = unknown> {\n  people: PersonNode<T>[];\n  partnerships: Partnership[];\n  rootPersonId?: string;  // Optional starting point\n}\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `FamilyTreeData<T>` | required | The family tree data |\n| `nodeComponent` | `ComponentType<NodeComponentProps<T>>` | required | Component to render each person |\n| `orientation` | `'top-down' \\| 'bottom-up' \\| 'left-right' \\| 'right-left'` | `'top-down'` | Tree layout direction |\n| `theme` | `'light' \\| 'dark'` | `'light'` | Built-in theme |\n| `spacing` | `SpacingConfig` | `{ generation: 120, siblings: 60, partners: 40 }` | Spacing between nodes |\n| `lineStyle` | `LineStyle` | - | Custom line styling |\n| `initialZoom` | `number` | `1` | Initial zoom level |\n| `minZoom` | `number` | `0.1` | Minimum zoom level |\n| `maxZoom` | `number` | `3` | Maximum zoom level |\n| `disableAnimations` | `boolean` | `false` | Disable all animations |\n| `animationDuration` | `number` | `300` | Animation duration in ms |\n| `onPersonClick` | `(id: string, data: T) => void` | - | Called when a person is clicked |\n| `onPersonHover` | `(id: string \\| null, data: T \\| null) => void` | - | Called on hover |\n| `onPartnershipClick` | `(id: string) => void` | - | Called when a partnership line is clicked |\n| `onZoomChange` | `(zoom: number) => void` | - | Called when zoom changes |\n\n## Custom Node Components\n\nCreate your own node component by implementing `NodeComponentProps`:\n\n```tsx\nimport type { NodeComponentProps } from '@alexbrand09/famtreejs';\n\ninterface MyPersonData {\n  name: string;\n  birthYear?: number;\n  photoUrl?: string;\n}\n\nfunction MyPersonCard({ data, isSelected, isHovered, isExpanded, onToggleExpand }: NodeComponentProps<MyPersonData>) {\n  return (\n    <div style={{\n      padding: '10px',\n      border: isSelected ? '2px solid blue' : '1px solid gray',\n      backgroundColor: isHovered ? '#f0f0f0' : 'white',\n    }}>\n      {data.photoUrl && <img src={data.photoUrl} alt={data.name} />}\n      <div>{data.name}</div>\n      {data.birthYear && <div>Born: {data.birthYear}</div>}\n      <button onClick={onToggleExpand}>\n        {isExpanded ? 'Collapse' : 'Expand'}\n      </button>\n    </div>\n  );\n}\n```\n\n## Built-in Components\n\n### BasicPersonCard\n\nSimple card showing just the name:\n\n```tsx\nimport { BasicPersonCard } from '@alexbrand09/famtreejs';\n```\n\n### DetailedPersonCard\n\nCard with photo, name, and dates:\n\n```tsx\nimport { DetailedPersonCard } from '@alexbrand09/famtreejs';\n\n// Data should include: name, birthDate?, deathDate?, photoUrl?\n```\n\n## Ref API\n\nAccess imperative methods via ref:\n\n```tsx\nimport { useRef } from 'react';\nimport { FamilyTree } from '@alexbrand09/famtreejs';\nimport type { FamilyTreeHandle } from '@alexbrand09/famtreejs';\n\nfunction App() {\n  const treeRef = useRef<FamilyTreeHandle>(null);\n\n  return (\n    <>\n      <button onClick={() => treeRef.current?.zoomIn()}>Zoom In</button>\n      <button onClick={() => treeRef.current?.fitToView()}>Fit</button>\n      <FamilyTree ref={treeRef} data={data} nodeComponent={BasicPersonCard} />\n    </>\n  );\n}\n```\n\n### Available Methods\n\n- `zoomTo(level: number)` - Set zoom level\n- `zoomIn()` / `zoomOut()` - Zoom by step\n- `centerOnPerson(id: string)` - Center view on a person\n- `fitToView()` - Fit entire tree in view\n- `expandAll()` / `collapseAll()` - Expand/collapse all branches\n- `toggleBranch(id: string)` - Toggle a specific branch\n- `setRoot(id: string)` - Re-root the tree\n- `getZoom()` - Get current zoom level\n- `getRoot()` - Get current root person ID\n\n## Hooks\n\nUse hooks inside the FamilyTree component tree:\n\n```tsx\nimport { useFamilyTreeState, useFamilyTreeActions } from '@alexbrand09/famtreejs';\n\nfunction MyControls() {\n  const { zoomLevel, selectedPersonId } = useFamilyTreeState();\n  const { zoomIn, zoomOut, fitToView } = useFamilyTreeActions();\n\n  return (\n    <div>\n      <span>Zoom: {Math.round(zoomLevel * 100)}%</span>\n      <button onClick={zoomIn}>+</button>\n      <button onClick={zoomOut}>-</button>\n    </div>\n  );\n}\n```\n\n## Theming\n\n### CSS Variables\n\nCustomize the appearance using CSS variables:\n\n```css\n.family-tree {\n  --ft-line-color: #333;\n  --ft-line-width: 2px;\n  --ft-node-background: #fff;\n  --ft-node-border: #ddd;\n  --ft-node-text: #333;\n  --ft-node-shadow: rgba(0, 0, 0, 0.1);\n  --ft-node-hover-border: #999;\n  --ft-node-selected-border: #0066cc;\n  --ft-node-selected-shadow: rgba(0, 102, 204, 0.3);\n}\n```\n\n## Keyboard Navigation\n\n| Key | Action |\n|-----|--------|\n| Arrow keys | Navigate between nodes |\n| Enter / Space | Select focused node |\n| E | Toggle expand/collapse on focused node |\n| + / - | Zoom in / out |\n| 0 | Fit to view |\n| Home / End | Jump to first / last node |\n\n## Accessibility\n\n- ARIA tree structure with `role=\"tree\"` and `role=\"treeitem\"`\n- `aria-selected`, `aria-expanded` states\n- Visible focus indicators\n- Respects `prefers-reduced-motion` media query\n- WCAG AA compliant contrast ratios\n\n## Browser Support\n\n- Modern browsers (Chrome, Firefox, Safari, Edge)\n- React 18+\n\n## License\n\nMIT\n","readmeFilename":"README.md"}