@captiveorigins/onetaxonomy-react 0.0.0-stage → 0.1.0-alpha.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.
@@ -0,0 +1,76 @@
1
+ import type { SVGProps } from 'react';
2
+ /**
3
+ * The brand family's line icons (brand/README.md), the same in OnePedigree and
4
+ * OneTaxonomy: drawings from Lucide (https://lucide.dev, ISC license, © Lucide
5
+ * Contributors), the set Captive Origins already uses, flattened to one path each.
6
+ * 24 × 24, 1.75 stroke, round caps and joins, currentColor. Decorative by default
7
+ * (aria-hidden): put the words in the button's text, or in aria-label plus a
8
+ * visible tooltip (`data-tip`) when the icon stands alone.
9
+ */
10
+ export declare const ICON_PATHS: {
11
+ /** lucide: plus */
12
+ readonly plus: "M5 12h14M12 5v14";
13
+ /** lucide: minus */
14
+ readonly minus: "M5 12h14";
15
+ /** lucide: zoom-in */
16
+ readonly zoomIn: "M3 11a8 8 0 1 0 16 0a8 8 0 1 0 -16 0M21 21L16.65 16.65M11 8L11 14M8 11L14 11";
17
+ /** lucide: zoom-out */
18
+ readonly zoomOut: "M3 11a8 8 0 1 0 16 0a8 8 0 1 0 -16 0M21 21L16.65 16.65M8 11L14 11";
19
+ /** lucide: scan */
20
+ readonly fit: "M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2";
21
+ /** lucide: download */
22
+ readonly download: "M12 15V3M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m7 10 5 5 5-5";
23
+ /** lucide: printer */
24
+ readonly print: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6M7 14h10a1 1 0 0 1 1 1v6a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1 -1v-6a1 1 0 0 1 1 -1Z";
25
+ /** lucide: undo-2 */
26
+ readonly undo: "M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11";
27
+ /** lucide: redo-2 */
28
+ readonly redo: "m15 14 5-5-5-5M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13";
29
+ /** lucide: chevrons-up-down */
30
+ readonly expand: "m7 15 5 5 5-5m7 9 5-5 5 5";
31
+ /** lucide: chevrons-down-up */
32
+ readonly collapse: "m7 20 5-5 5 5m7 4 5 5 5-5";
33
+ /** lucide: arrow-down-up */
34
+ readonly flip: "m3 16 4 4 4-4M7 20V4m21 8-4-4-4 4M17 4v16";
35
+ /** lucide: focus */
36
+ readonly focus: "M9 12a3 3 0 1 0 6 0a3 3 0 1 0 -6 0M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2";
37
+ /** lucide: expand */
38
+ readonly unfocus: "m15 15 6 6m15 9 6-6M21 16v5h-5M21 8V3h-5M3 16v5h5m3 21 6-6M3 8V3h5M9 9 3 3";
39
+ /** lucide: network */
40
+ readonly layout: "M17 16h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM3 16h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM10 2h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3M12 12V8";
41
+ /** lucide: funnel */
42
+ readonly filter: "M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z";
43
+ /** lucide: palette */
44
+ readonly palette: "M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8zM13 6.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M17 10.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M6 12.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M8 7.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0";
45
+ /** lucide: x */
46
+ readonly close: "M18 6 6 18m6 6 12 12";
47
+ /** lucide: external-link */
48
+ readonly open: "M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6";
49
+ /** lucide: grip-vertical */
50
+ readonly grip: "M8 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M8 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M8 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0";
51
+ /** lucide: info */
52
+ readonly info: "M2 12a10 10 0 1 0 20 0a10 10 0 1 0 -20 0M12 16v-4M12 8h.01";
53
+ /** lucide: check */
54
+ readonly check: "M20 6 9 17l-5-5";
55
+ /** lucide: percent */
56
+ readonly support: "M19 5L5 19M4 6.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0M15 17.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0";
57
+ /** lucide: bone */
58
+ readonly fossil: "M17 10c.7-.7 1.69 0 2.5 0a2.5 2.5 0 1 0 0-5 .5.5 0 0 1-.5-.5 2.5 2.5 0 1 0-5 0c0 .81.7 1.8 0 2.5l-7 7c-.7.7-1.69 0-2.5 0a2.5 2.5 0 0 0 0 5c.28 0 .5.22.5.5a2.5 2.5 0 1 0 5 0c0-.81-.7-1.8 0-2.5Z";
59
+ /** lucide: image */
60
+ readonly image: "M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2ZM7 9a2 2 0 1 0 4 0a2 2 0 1 0 -4 0m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21";
61
+ /** lucide: file-json */
62
+ readonly file: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2zM14 2v5a1 1 0 0 0 1 1h5M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1";
63
+ /** lucide: book-open */
64
+ readonly book: "M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z";
65
+ /** lucide: chevron-left */
66
+ readonly previous: "m15 18-6-6 6-6";
67
+ /** lucide: chevron-right */
68
+ readonly next: "m9 18 6-6-6-6";
69
+ /** lucide: dna */
70
+ readonly dna: "m10 16 1.5 1.5m14 8-1.5-1.5M15 2c-1.798 1.998-2.518 3.995-2.807 5.993m16.5 10.5 1 1m17 6-2.891-2.891M2 15c6.667-6 13.333 0 20-6m20 9 .891.891M3.109 14.109 4 15m6.5 12.5 1 1m7 18 2.891 2.891M9 22c1.798-1.998 2.518-3.995 2.807-5.993";
71
+ };
72
+ export type IconName = keyof typeof ICON_PATHS;
73
+ export declare function Icon({ name, size, className, ...rest }: {
74
+ name: IconName;
75
+ size?: number;
76
+ } & Omit<SVGProps<SVGSVGElement>, 'name'>): import("react").JSX.Element;
package/dist/icons.js ADDED
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * The brand family's line icons (brand/README.md), the same in OnePedigree and
4
+ * OneTaxonomy: drawings from Lucide (https://lucide.dev, ISC license, © Lucide
5
+ * Contributors), the set Captive Origins already uses, flattened to one path each.
6
+ * 24 × 24, 1.75 stroke, round caps and joins, currentColor. Decorative by default
7
+ * (aria-hidden): put the words in the button's text, or in aria-label plus a
8
+ * visible tooltip (`data-tip`) when the icon stands alone.
9
+ */
10
+ export const ICON_PATHS = {
11
+ /** lucide: plus */
12
+ plus: 'M5 12h14M12 5v14',
13
+ /** lucide: minus */
14
+ minus: 'M5 12h14',
15
+ /** lucide: zoom-in */
16
+ zoomIn: 'M3 11a8 8 0 1 0 16 0a8 8 0 1 0 -16 0M21 21L16.65 16.65M11 8L11 14M8 11L14 11',
17
+ /** lucide: zoom-out */
18
+ zoomOut: 'M3 11a8 8 0 1 0 16 0a8 8 0 1 0 -16 0M21 21L16.65 16.65M8 11L14 11',
19
+ /** lucide: scan */
20
+ fit: 'M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2',
21
+ /** lucide: download */
22
+ download: 'M12 15V3M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m7 10 5 5 5-5',
23
+ /** lucide: printer */
24
+ print: 'M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6M7 14h10a1 1 0 0 1 1 1v6a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1 -1v-6a1 1 0 0 1 1 -1Z',
25
+ /** lucide: undo-2 */
26
+ undo: 'M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11',
27
+ /** lucide: redo-2 */
28
+ redo: 'm15 14 5-5-5-5M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13',
29
+ /** lucide: chevrons-up-down */
30
+ expand: 'm7 15 5 5 5-5m7 9 5-5 5 5',
31
+ /** lucide: chevrons-down-up */
32
+ collapse: 'm7 20 5-5 5 5m7 4 5 5 5-5',
33
+ /** lucide: arrow-down-up */
34
+ flip: 'm3 16 4 4 4-4M7 20V4m21 8-4-4-4 4M17 4v16',
35
+ /** lucide: focus */
36
+ focus: 'M9 12a3 3 0 1 0 6 0a3 3 0 1 0 -6 0M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2',
37
+ /** lucide: expand */
38
+ unfocus: 'm15 15 6 6m15 9 6-6M21 16v5h-5M21 8V3h-5M3 16v5h5m3 21 6-6M3 8V3h5M9 9 3 3',
39
+ /** lucide: network */
40
+ layout: 'M17 16h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM3 16h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM10 2h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1v-4a1 1 0 0 1 1 -1ZM5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3M12 12V8',
41
+ /** lucide: funnel */
42
+ filter: 'M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z',
43
+ /** lucide: palette */
44
+ palette: 'M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8zM13 6.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M17 10.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M6 12.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0M8 7.5a0.5 0.5 0 1 0 1 0a0.5 0.5 0 1 0 -1 0',
45
+ /** lucide: x */
46
+ close: 'M18 6 6 18m6 6 12 12',
47
+ /** lucide: external-link */
48
+ open: 'M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6',
49
+ /** lucide: grip-vertical */
50
+ grip: 'M8 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M8 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M8 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0M14 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0',
51
+ /** lucide: info */
52
+ info: 'M2 12a10 10 0 1 0 20 0a10 10 0 1 0 -20 0M12 16v-4M12 8h.01',
53
+ /** lucide: check */
54
+ check: 'M20 6 9 17l-5-5',
55
+ /** lucide: percent */
56
+ support: 'M19 5L5 19M4 6.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0M15 17.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0',
57
+ /** lucide: bone */
58
+ fossil: 'M17 10c.7-.7 1.69 0 2.5 0a2.5 2.5 0 1 0 0-5 .5.5 0 0 1-.5-.5 2.5 2.5 0 1 0-5 0c0 .81.7 1.8 0 2.5l-7 7c-.7.7-1.69 0-2.5 0a2.5 2.5 0 0 0 0 5c.28 0 .5.22.5.5a2.5 2.5 0 1 0 5 0c0-.81-.7-1.8 0-2.5Z',
59
+ /** lucide: image */
60
+ image: 'M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2ZM7 9a2 2 0 1 0 4 0a2 2 0 1 0 -4 0m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21',
61
+ /** lucide: file-json */
62
+ file: 'M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2zM14 2v5a1 1 0 0 0 1 1h5M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1',
63
+ /** lucide: book-open */
64
+ book: 'M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z',
65
+ /** lucide: chevron-left */
66
+ previous: 'm15 18-6-6 6-6',
67
+ /** lucide: chevron-right */
68
+ next: 'm9 18 6-6-6-6',
69
+ /** lucide: dna */
70
+ dna: 'm10 16 1.5 1.5m14 8-1.5-1.5M15 2c-1.798 1.998-2.518 3.995-2.807 5.993m16.5 10.5 1 1m17 6-2.891-2.891M2 15c6.667-6 13.333 0 20-6m20 9 .891.891M3.109 14.109 4 15m6.5 12.5 1 1m7 18 2.891 2.891M9 22c1.798-1.998 2.518-3.995 2.807-5.993',
71
+ };
72
+ export function Icon({ name, size = 16, className, ...rest }) {
73
+ return (_jsx("svg", { viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", className: ["ot-icon", className].filter(Boolean).join(" "), ...rest, children: _jsx("path", { d: ICON_PATHS[name] }) }));
74
+ }
@@ -0,0 +1,8 @@
1
+ export { TreeView, type TreeViewProps } from './TreeView.js';
2
+ export { TreeOutline, type TreeOutlineProps } from './TreeOutline.js';
3
+ export { Explorer, Legend, layoutFor, useTaxonomyView, type ExplorerProps, type ExplorerLabels, type UseTaxonomyView } from './Explorer.js';
4
+ export { treeViewBox, type ViewBox } from './TreeView.js';
5
+ export { dropOrder, dropTarget, drawnSubtree } from './drag.js';
6
+ export { Icon, ICON_PATHS, type IconName } from './icons.js';
7
+ export { citationLine, disclosureLines, downloadBlob, standaloneSvg, svgToPng } from './export.js';
8
+ export { cameraBox, LessonPlayer, type LessonLabels, type LessonMode, type LessonPlayerProps } from './Lesson.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { TreeView } from './TreeView.js';
2
+ export { TreeOutline } from './TreeOutline.js';
3
+ export { Explorer, Legend, layoutFor, useTaxonomyView } from './Explorer.js';
4
+ export { treeViewBox } from './TreeView.js';
5
+ export { dropOrder, dropTarget, drawnSubtree } from './drag.js';
6
+ export { Icon, ICON_PATHS } from './icons.js';
7
+ export { citationLine, disclosureLines, downloadBlob, standaloneSvg, svgToPng } from './export.js';
8
+ export { cameraBox, LessonPlayer } from './Lesson.js';
@@ -0,0 +1,183 @@
1
+ /*
2
+ * OneTaxonomy default styles. Optional: skip this file and style the stable
3
+ * .ot-* classes and data-* attributes yourself, or override the custom properties.
4
+ */
5
+ .ot-root,
6
+ .ot-explorer,
7
+ .ot-lesson {
8
+ --ot-font: system-ui, -apple-system, 'Segoe UI', sans-serif;
9
+ /* Brand family tokens (brand/README.md): Captive Origins brown ink, orange for selection and focus. */
10
+ --ot-ink: #4c312c;
11
+ --ot-text: #1d2330;
12
+ --ot-muted: #5b6577;
13
+ --ot-edge: #a08a83;
14
+ --ot-edge-width: 1.5px;
15
+ --ot-node: var(--ot-ink);
16
+ --ot-selected: #cf8137;
17
+ --ot-surface: #ffffff;
18
+ --ot-panel: #fbf7f4;
19
+ --ot-border: #eaddd6;
20
+ --ot-focus: #cf8137;
21
+ font-family: var(--ot-font);
22
+ color: var(--ot-text);
23
+ }
24
+ @media (prefers-color-scheme: dark) {
25
+ .ot-root,
26
+ .ot-explorer,
27
+ .ot-lesson {
28
+ --ot-ink: #eda9ac;
29
+ --ot-text: #eef1f6;
30
+ --ot-muted: #a7b0c0;
31
+ --ot-edge: #8a7470;
32
+ --ot-node: var(--ot-ink);
33
+ --ot-selected: #e7a066;
34
+ --ot-surface: #1c1311;
35
+ --ot-panel: #241916;
36
+ --ot-border: #3a2a26;
37
+ --ot-focus: #e7a066;
38
+ }
39
+ }
40
+ .ot-root { width: 100%; height: 100%; overflow: visible; }
41
+ .ot-edge { fill: none; stroke: var(--ot-edge); stroke-width: var(--ot-edge-width); }
42
+ .ot-node-dot, .ot-node-collapsed { fill: var(--ot-node); }
43
+ .ot-node-halo { opacity: 0.35; }
44
+ .ot-label { fill: var(--ot-text); font-size: 12px; }
45
+ .ot-label[data-italic] { font-style: italic; }
46
+ .ot-node[data-status='extinct'] .ot-node-dot { fill: none; stroke: var(--ot-node); }
47
+ .ot-node[data-selected] .ot-label { fill: var(--ot-selected); font-weight: 600; }
48
+ .ot-node[role='button'] { cursor: pointer; }
49
+ .ot-node:focus { outline: none; }
50
+ .ot-node:focus-visible { outline: 2px solid var(--ot-focus); }
51
+ .ot-canvas { user-select: none; -webkit-user-select: none; }
52
+ .ot-axis-line, .ot-axis-tick { stroke: var(--ot-muted); }
53
+ /* Overlays (M5): only stated values are drawn. */
54
+ .ot-trait { fill: var(--ot-muted); opacity: 0.85; }
55
+ .ot-node[data-trait='mixed'] .ot-trait { fill: none; stroke: var(--ot-muted); stroke-dasharray: 2 2; }
56
+ .ot-support { fill: var(--ot-muted); font-size: 10px; }
57
+ .ot-support[data-weak] { fill: #b45309; }
58
+ .ot-edge[data-weak] { stroke-dasharray: 5 4; }
59
+ .ot-axis-label, .ot-axis-unit { fill: var(--ot-muted); font-size: 11px; }
60
+
61
+ .ot-icon { flex: none; }
62
+ /* Controls: 32 px, radius 8, 1 px border (shared with OnePedigree). */
63
+ .ot-toolbar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 8px; }
64
+ .ot-toolbar-gap { width: 6px; }
65
+ .ot-explorer button, .ot-lesson button, .ot-select {
66
+ display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px; border-radius: 8px;
67
+ border: 1px solid var(--ot-border); background: var(--ot-surface); color: inherit; font: inherit; font-size: 13px; cursor: pointer;
68
+ }
69
+ .ot-select select { font: inherit; border: 0; background: transparent; color: inherit; cursor: pointer; }
70
+ .ot-explorer button:hover:not(:disabled), .ot-lesson button:hover:not(:disabled), .ot-select:hover { border-color: var(--ot-ink); }
71
+ .ot-explorer button:disabled, .ot-lesson button:disabled { opacity: 0.45; cursor: default; }
72
+ .ot-explorer button:focus-visible, .ot-lesson button:focus-visible, .ot-select:focus-within, .ot-canvas:focus-visible { outline: 2px solid var(--ot-focus); outline-offset: 2px; }
73
+ .ot-panes { display: grid; grid-template-columns: minmax(0, 2fr) minmax(15rem, 1fr); gap: 12px; }
74
+ @media (max-width: 48rem) { .ot-panes { grid-template-columns: 1fr; } }
75
+ .ot-canvas { position: relative; overflow: hidden; touch-action: none; border: 1px solid var(--ot-border); border-radius: 12px; background: var(--ot-surface); cursor: grab; }
76
+ .ot-canvas:active { cursor: grabbing; }
77
+ .ot-canvas > svg { display: block; width: 100%; height: 100%; }
78
+ .ot-floating { position: absolute; top: 8px; right: 8px; z-index: 1; display: flex; gap: 4px; }
79
+ .ot-floating button { width: 32px; padding: 0; justify-content: center; }
80
+ .ot-node[data-dragging] { cursor: grabbing; }
81
+ .ot-node[data-dragging] .ot-label { fill: var(--ot-selected); }
82
+ .ot-edge[data-dragging] { stroke-dasharray: 4 3; }
83
+ .ot-canvas[data-dragging] .ot-edge:not([data-dragging]) { opacity: 0.5; }
84
+ .ot-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
85
+ .ot-outline, .ot-inspector { border: 1px solid var(--ot-border); border-radius: 10px; background: var(--ot-panel); padding: 10px 12px; }
86
+ .ot-outline, .ot-outline ul { list-style: none; margin: 0; padding-inline-start: 1rem; }
87
+ .ot-outline { max-height: 20rem; overflow: auto; font-size: 14px; }
88
+ .ot-outline-item:focus { outline: none; }
89
+ .ot-outline-item:focus-visible > .ot-outline-row { outline: 2px solid var(--ot-focus); border-radius: 0.25rem; }
90
+ .ot-outline-item[aria-selected='true'] > .ot-outline-row { font-weight: 600; color: var(--ot-selected); }
91
+ .ot-outline-row { display: inline-flex; gap: 0.25rem; align-items: baseline; cursor: pointer; }
92
+ .ot-outline-label[data-unnamed] { color: var(--ot-muted); font-style: italic; }
93
+ .ot-outline-twisty { display: inline-block; width: 1rem; text-align: center; }
94
+ .ot-outline-hidden, .ot-assessment, .ot-notice { color: var(--ot-muted); font-size: 0.875em; }
95
+ .ot-inspector { font-size: 14px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.06); }
96
+ .ot-inspector-title { margin: 0; font-size: 15px; }
97
+ .ot-hint { display: flex; gap: 8px; margin: 0; color: var(--ot-muted); }
98
+ .ot-inspector dl { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 0.75rem; margin: 0.5rem 0; }
99
+ .ot-inspector dt { color: var(--ot-muted); }
100
+ .ot-inspector dd { margin: 0; }
101
+ .ot-inspector-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
102
+ .ot-highlights { list-style: none; padding: 0; margin: 0.5rem 0; }
103
+ .ot-swatch { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 50%; margin-inline-end: 0.375rem; background: var(--ot-muted); }
104
+ .ot-error { color: #b91c1c; }
105
+ .ot-explorer button[aria-pressed='true'] { border-color: var(--ot-selected); background: color-mix(in srgb, var(--ot-selected) 14%, var(--ot-surface)); }
106
+ .ot-menu { position: relative; }
107
+ .ot-menu-list { position: absolute; top: 36px; right: 0; display: flex; flex-direction: column; gap: 2px; padding: 4px; min-width: 12rem; border: 1px solid var(--ot-border); border-radius: 10px; background: var(--ot-surface); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12); }
108
+ .ot-floating .ot-menu-list button { width: auto; justify-content: flex-start; padding: 0 10px; border-color: transparent; }
109
+ .ot-facts { font-size: 13px; }
110
+ .ot-facts p { margin: 0.375rem 0; }
111
+ .ot-fact-label { color: var(--ot-muted); }
112
+ .ot-citations { color: var(--ot-muted); overflow-wrap: anywhere; }
113
+ .ot-legend { margin-top: 8px; }
114
+ .ot-legend-traits { list-style: none; display: flex; flex-wrap: wrap; gap: 4px 14px; padding: 0; margin: 6px 0; font-size: 13px; }
115
+ .ot-swatch[data-trait='mixed'] { background: transparent; border: 1px dashed var(--ot-muted); }
116
+ .ot-swatch[data-trait='none'] { background: transparent; border: 1px solid var(--ot-border); }
117
+ .ot-about { margin-top: 8px; font-size: 13px; color: var(--ot-muted); }
118
+ .ot-about summary { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; color: var(--ot-text); }
119
+ .ot-about h4 { margin: 8px 0 4px; font-size: 13px; color: var(--ot-text); }
120
+ .ot-about ul { margin: 4px 0; padding-inline-start: 1.25rem; overflow-wrap: anywhere; }
121
+ .ot-print-disclosure { display: none; }
122
+ .ot-clade-check { display: flex; flex-direction: column; gap: 8px; }
123
+ .ot-selection { margin: 0; font-size: 13px; }
124
+ .ot-assessment-box { border-inline-start: 3px solid var(--ot-muted); padding: 4px 10px; background: var(--ot-surface); border-radius: 6px; }
125
+ .ot-assessment-box p { margin: 4px 0; }
126
+ .ot-clade-check[data-status='monophyletic'] .ot-assessment-box { border-color: #2f7d5c; }
127
+ .ot-clade-check[data-status='not-monophyletic'] .ot-assessment-box { border-color: #b45309; }
128
+ .ot-select input { font: inherit; border: 0; background: transparent; color: inherit; min-width: 6rem; }
129
+ .ot-explorer .ot-remove { height: 20px; width: 20px; padding: 0; justify-content: center; margin-inline-start: 6px; vertical-align: middle; }
130
+ @media print {
131
+ html[data-ot-printing] body * { visibility: hidden; }
132
+ html[data-ot-printing] [data-ot-printing], html[data-ot-printing] [data-ot-printing] * { visibility: visible; }
133
+ html[data-ot-printing] [data-ot-printing] { position: fixed; inset: 0; height: 100% !important; border: 0; border-radius: 0; }
134
+ .ot-floating { display: none; }
135
+ html[data-ot-printing] [data-ot-printing] { display: flex; flex-direction: column; }
136
+ html[data-ot-printing] [data-ot-printing] > svg { flex: 1; min-height: 0; }
137
+ .ot-print-disclosure { display: block; padding: 8px 12px; font-size: 9pt; color: #4c312c; }
138
+ .ot-print-disclosure p { margin: 0 0 2px; }
139
+ }
140
+ @media (prefers-reduced-motion: no-preference) {
141
+ .ot-node:not([data-dragging]) { transition: transform 150ms ease; }
142
+ }
143
+ /* Visible tooltips for icon-only buttons, on hover and on keyboard focus (title= shows on hover only). */
144
+ .ot-floating [data-tip] { position: relative; }
145
+ .ot-floating [data-tip]:is(:hover, :focus-visible)::after {
146
+ content: attr(data-tip); position: absolute; top: calc(100% + 6px); right: 0; z-index: 2; white-space: nowrap; pointer-events: none;
147
+ padding: 4px 8px; border-radius: 6px; background: var(--ot-ink); color: var(--ot-surface); font-size: 12px; line-height: 1.3;
148
+ }
149
+ .ot-floating .ot-menu:has(.ot-menu-list) [data-tip]::after { display: none; }
150
+ /* Lessons */
151
+ .ot-lesson { font-family: var(--ot-font); color: var(--ot-text); }
152
+ .ot-segmented { display: inline-flex; border: 1px solid var(--ot-border); border-radius: 8px; overflow: hidden; }
153
+ .ot-lesson .ot-segmented button { border: 0; border-radius: 0; }
154
+ .ot-lesson .ot-segmented button[aria-checked='true'] { background: color-mix(in srgb, var(--ot-selected) 16%, var(--ot-surface)); font-weight: 600; }
155
+ .ot-lesson-position { font-size: 13px; color: var(--ot-muted); min-width: 9rem; text-align: center; }
156
+ .ot-lesson-title { margin: 8px 0 2px; font-size: 18px; }
157
+ .ot-lesson-caption { margin: 0 0 8px; color: var(--ot-muted); }
158
+ .ot-present { cursor: default; }
159
+ .ot-annotations { align-self: start; margin: 0; padding: 10px 12px 10px 2rem; border: 1px solid var(--ot-border); border-radius: 10px; background: var(--ot-panel); font-size: 14px; }
160
+ .ot-annotations li { margin: 4px 0; }
161
+ .ot-annotations li[data-tone='question'] { font-style: italic; }
162
+ .ot-annotations li[data-tone='caution'] { color: #b45309; }
163
+ .ot-badge circle { fill: var(--ot-selected); }
164
+ .ot-badge text { fill: #fff; font-size: 9px; font-weight: 700; }
165
+ .ot-lesson-notes { margin-top: 8px; padding: 8px 12px; border-inline-start: 3px solid var(--ot-muted); font-size: 13px; color: var(--ot-muted); }
166
+ .ot-studio-tools { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
167
+
168
+ /* Proposed mode: always visibly someone's proposal, never the accepted arrangement. */
169
+ .ot-node[data-moved] .ot-label { font-style: italic; text-decoration: underline dashed; text-underline-offset: 3px; }
170
+ .ot-node[data-moved] .ot-node-dot { stroke: var(--ot-selected); stroke-width: 2; stroke-dasharray: 2 2; }
171
+ .ot-node[data-drop-target] .ot-node-dot { r: 8px; fill: var(--ot-selected); }
172
+ .ot-node[data-drop-target] .ot-label { fill: var(--ot-selected); font-weight: 600; }
173
+ .ot-proposed-badge { position: absolute; bottom: 8px; inset-inline-start: 8px; z-index: 2; max-width: 70%; padding: 4px 8px; border: 1px dashed var(--ot-selected); border-radius: 6px; background: var(--ot-bg, #fff); color: var(--ot-selected); font-size: 12px; font-weight: 600; }
174
+ .ot-proposal { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px dashed var(--ot-selected); border-radius: 8px; margin-block-end: 8px; }
175
+ .ot-proposal h3 { margin: 0; font-size: 14px; }
176
+ .ot-proposal label { display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
177
+ .ot-proposal input, .ot-proposal textarea { padding: 4px 6px; font: inherit; border: 1px solid var(--ot-border, #ccc); border-radius: 4px; background: transparent; color: inherit; }
178
+ .ot-proposal ul { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; font-size: 13px; }
179
+ .ot-proposal li { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
180
+ .ot-compare { margin-block-start: 8px; }
181
+ .ot-compare-panes { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; margin-block-start: 8px; }
182
+ .ot-compare-panes h4 { margin: 0 0 4px; font-size: 13px; }
183
+ .ot-compare-panes svg { width: 100%; height: auto; max-height: 28rem; }
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@captiveorigins/onetaxonomy-react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0-alpha.2",
4
+ "description": "OneTaxonomy for React: a server-renderable SVG tree, an accessible keyboard tree outline, and an explorer with undo/redo, filters, layouts and an inspector that keeps clades and groups apart.",
5
+ "keywords": [
6
+ "taxonomy",
7
+ "phylogeny",
8
+ "react",
9
+ "svg",
10
+ "tree",
11
+ "accessibility",
12
+ "education"
13
+ ],
14
+ "homepage": "https://github.com/mgilardi/onetaxonomy#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/mgilardi/onetaxonomy/issues"
17
+ },
18
+ "license": "PolyForm-Noncommercial-1.0.0",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/mgilardi/onetaxonomy.git",
22
+ "directory": "packages/react"
23
+ },
24
+ "type": "module",
25
+ "sideEffects": [
26
+ "*.css"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./styles.css": "./dist/styles.css"
34
+ },
35
+ "main": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE",
41
+ "THIRD-PARTY-LICENSE-LUCIDE.txt"
42
+ ],
43
+ "engines": {
44
+ "node": ">=22"
45
+ },
46
+ "dependencies": {
47
+ "@captiveorigins/onetaxonomy-layout": "^0.1.0-alpha.2",
48
+ "@captiveorigins/onetaxonomy-education": "^0.1.0-alpha.2",
49
+ "@captiveorigins/onetaxonomy-core": "^0.1.0-alpha.2"
50
+ },
51
+ "peerDependencies": {
52
+ "react": ">=18.2"
53
+ },
54
+ "devDependencies": {
55
+ "@testing-library/react": "^16.3.3",
56
+ "@types/react": "^19.3.0",
57
+ "@types/react-dom": "^19.3.0",
58
+ "jsdom": "^30.1.3",
59
+ "react": "^19.3.1",
60
+ "react-dom": "^19.3.1"
61
+ },
62
+ "scripts": {
63
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/styles.css dist/styles.css",
64
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
65
+ "test": "vitest run"
66
+ }
6
67
  }