@figtreejs/core 0.0.1-alpha.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.
Files changed (111) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/eslint.config.js +9 -0
  3. package/package.json +76 -0
  4. package/src/@custom-types/normalize-svg-path.d.ts +13 -0
  5. package/src/@custom-types/parse-svg-path.d.ts +8 -0
  6. package/src/@custom-types/svg-path-types.d.ts +37 -0
  7. package/src/bauble-makers/makers.ts +112 -0
  8. package/src/bauble-makers/set-up-baubles.ts +197 -0
  9. package/src/bauble-makers/utils.ts +61 -0
  10. package/src/components/baubles/bauble.tsx +61 -0
  11. package/src/components/baubles/branches.tsx +13 -0
  12. package/src/components/baubles/clades/cartoon.tsx +68 -0
  13. package/src/components/baubles/clades/highlight.tsx +96 -0
  14. package/src/components/baubles/clades/index.ts +1 -0
  15. package/src/components/baubles/clades.tsx +45 -0
  16. package/src/components/baubles/helpers.tsx +62 -0
  17. package/src/components/baubles/index.ts +16 -0
  18. package/src/components/baubles/labels.tsx +38 -0
  19. package/src/components/baubles/nodes.tsx +51 -0
  20. package/src/components/baubles/shapes/branch.tsx +53 -0
  21. package/src/components/baubles/shapes/circle.tsx +64 -0
  22. package/src/components/baubles/shapes/index.ts +9 -0
  23. package/src/components/baubles/shapes/label.tsx +104 -0
  24. package/src/components/baubles/shapes/rectangle.tsx +83 -0
  25. package/src/components/baubles/types.ts +99 -0
  26. package/src/components/decorations/axis/axis-types.ts +123 -0
  27. package/src/components/decorations/axis/axis.tsx +21 -0
  28. package/src/components/decorations/axis/index.ts +2 -0
  29. package/src/components/decorations/axis/polar-axis-bars.tsx +102 -0
  30. package/src/components/decorations/axis/polar-axis.tsx +175 -0
  31. package/src/components/decorations/axis/rectangular-axis-bars.tsx +53 -0
  32. package/src/components/decorations/axis/rectangular-axis.tsx +151 -0
  33. package/src/components/decorations/index.ts +2 -0
  34. package/src/components/decorations/legend/discrete-legend.tsx +93 -0
  35. package/src/components/decorations/legend/index.ts +1 -0
  36. package/src/components/decorations/legend/legend.tsx +1 -0
  37. package/src/components/figtree/figtree-types.ts +69 -0
  38. package/src/components/figtree/figtree.tsx +136 -0
  39. package/src/components/figtree/index.ts +3 -0
  40. package/src/components/hoc/index.ts +7 -0
  41. package/src/components/hoc/with-branch.tsx +148 -0
  42. package/src/components/hoc/with-branches.tsx +54 -0
  43. package/src/components/hoc/with-clades.tsx +47 -0
  44. package/src/components/hoc/with-node.tsx +183 -0
  45. package/src/components/hoc/with-nodes.tsx +45 -0
  46. package/src/components/index.ts +4 -0
  47. package/src/context/aminated-context.ts +3 -0
  48. package/src/context/dimension-context.ts +22 -0
  49. package/src/context/layout-context.ts +20 -0
  50. package/src/context/scale-context.ts +12 -0
  51. package/src/evo/index.ts +1 -0
  52. package/src/evo/tree/index.ts +5 -0
  53. package/src/evo/tree/mcc-tree.ts +0 -0
  54. package/src/evo/tree/normalized-tree/immutable-tree-helpers.ts +136 -0
  55. package/src/evo/tree/normalized-tree/immutable-tree.test.ts +158 -0
  56. package/src/evo/tree/normalized-tree/immutable-tree.ts +1365 -0
  57. package/src/evo/tree/normalized-tree/index.ts +3 -0
  58. package/src/evo/tree/parsers/annotation-parser.ts +276 -0
  59. package/src/evo/tree/parsers/index.ts +3 -0
  60. package/src/evo/tree/parsers/newick-character-parser.ts +246 -0
  61. package/src/evo/tree/parsers/newick-parsing.ts +22 -0
  62. package/src/evo/tree/parsers/nexus-parser.ts +12 -0
  63. package/src/evo/tree/parsers/nexus-parsing.ts +68 -0
  64. package/src/evo/tree/parsers/parsing.test.ts +289 -0
  65. package/src/evo/tree/parsers/stream-reader/index.ts +1 -0
  66. package/src/evo/tree/parsers/stream-reader/newick-importer.txt +395 -0
  67. package/src/evo/tree/parsers/stream-reader/nexus-importer.test.ts +99 -0
  68. package/src/evo/tree/parsers/stream-reader/nexus-importer.ts +293 -0
  69. package/src/evo/tree/parsers/stream-reader/nexus-tokenizer.ts +77 -0
  70. package/src/evo/tree/parsers/stream-reader/nexus-transform-stream.txt +109 -0
  71. package/src/evo/tree/taxa/helper-functions.ts +46 -0
  72. package/src/evo/tree/taxa/index.ts +1 -0
  73. package/src/evo/tree/taxa/taxon.ts +116 -0
  74. package/src/evo/tree/traversals/index.ts +1 -0
  75. package/src/evo/tree/traversals/preorder-traversal.ts +89 -0
  76. package/src/evo/tree/traversals/traversal-types.ts +6 -0
  77. package/src/evo/tree/tree-types.ts +197 -0
  78. package/src/evo/tree/utilities.ts +44 -0
  79. package/src/index.ts +6 -0
  80. package/src/layouts/functional/index.ts +2 -0
  81. package/src/layouts/functional/radial-layout.ts +150 -0
  82. package/src/layouts/functional/rectangular-layout.ts +71 -0
  83. package/src/layouts/index.ts +3 -0
  84. package/src/layouts/layout-interface.ts +90 -0
  85. package/src/layouts/types.ts +32 -0
  86. package/src/path.helpers.ts +81 -0
  87. package/src/store/polar-scale.ts +145 -0
  88. package/src/store/store.ts +144 -0
  89. package/src/tests/baubles/__snapshots__/branch-labels.test.tsx.snap +901 -0
  90. package/src/tests/baubles/__snapshots__/node-labels.test.tsx.snap +1516 -0
  91. package/src/tests/baubles/branch-labels.test.tsx +103 -0
  92. package/src/tests/baubles/label.svg +131 -0
  93. package/src/tests/baubles/node-labels.test.tsx +126 -0
  94. package/src/tests/clades/__snapshots__/cartoon.test.tsx.snap +327 -0
  95. package/src/tests/clades/__snapshots__/highlight.test.tsx.snap +337 -0
  96. package/src/tests/clades/cartoon.test.tsx +65 -0
  97. package/src/tests/clades/highlight.test.tsx +66 -0
  98. package/src/tests/figtree/__snapshots__/figtree.test.tsx.snap +761 -0
  99. package/src/tests/figtree/figtree.test.tsx +123 -0
  100. package/src/tests/figtree/simple.svg +47 -0
  101. package/src/tests/layouts/radiallayout.test.ts +23 -0
  102. package/src/tests/layouts/rectangularlayout.test.ts +65 -0
  103. package/src/tests/shapes/branch.test.tsx +40 -0
  104. package/src/tests/shapes/circle.test.tsx +47 -0
  105. package/src/tests/shapes/label.test.tsx +101 -0
  106. package/src/tests/shapes/rectangle.test.tsx +67 -0
  107. package/src/tests/shapes/types.ts +1 -0
  108. package/src/utils.ts +57 -0
  109. package/tsconfig.json +12 -0
  110. package/vite.config.ts +34 -0
  111. package/vitetest.config.ts +11 -0
@@ -0,0 +1,99 @@
1
+ import { SpringValue } from "@react-spring/web";
2
+ import type { NodeRef } from "../../evo";
3
+
4
+ // **Types for animated shapes that can accept either animated values or primitives */
5
+ export type numerical = number | SpringValue<number>;
6
+ export type stringy = string | SpringValue<string>;
7
+
8
+ /** Helper functions for unspringing types so we only have to define possible types once */
9
+ export type DeSpring<T> = T extends SpringValue<infer U> ? U : T;
10
+ export type StripSprings<T> = { [K in keyof T]: DeSpring<T[K]> };
11
+
12
+ /** Types of interactions that are accepted on rendered objects.
13
+ * These wrap functions provided by the users
14
+ */
15
+ export type InternalInteractionType = {
16
+ onClick?: () => void;
17
+ OnMouseOver?: () => void;
18
+ onEnter?: () => void;
19
+ onExit?: () => void;
20
+ };
21
+
22
+ export type InteractionType = {
23
+ onClick?: (n: NodeRef) => void;
24
+ OnMouseOver?: (n: NodeRef) => void;
25
+ onEnter?: (n: NodeRef) => void;
26
+ onExit?: (n: NodeRef) => void;
27
+ };
28
+
29
+ /** Attributes as they enter rendered possibly animated svg elements */
30
+ export type BaseAttrs = Record<string, numerical | stringy>;
31
+ export type BasePos = Record<string, numerical | stringy>;
32
+ /** Attribute as they are passed internally to possibly animated svg elements */
33
+ export type Attrs = Record<string, number | string>;
34
+ export type AttrsRecord<A extends object> = Record<string, A>;
35
+ /** Attributes provided by the user. They will be applied to elements prior to rendering */
36
+ export type UserAttrs = Record<
37
+ string,
38
+ number | string | ((n: NodeRef) => number | string)
39
+ >;
40
+
41
+ /** Strip x/y/d/attrs of their possible spring values
42
+ * This is used to infer the input types to animated values
43
+ */
44
+ export type StripProps<P> = {
45
+ [K in keyof P]: K extends "attrs"
46
+ ? StripSprings<P[K]>
47
+ : K extends "pos"
48
+ ? StripSprings<P[K]>
49
+ : P[K];
50
+ };
51
+
52
+ export type XYAttrs = { x: numerical; y: numerical } & BaseAttrs;
53
+ /** Wide type for all svg base baubles */
54
+ export type BaseBaubleProps<A extends BaseAttrs> = {
55
+ interactions?: InternalInteractionType;
56
+ attrs: A;
57
+ };
58
+
59
+ export type BaseXYProps<A extends BaseAttrs> = {
60
+ interactions?: InternalInteractionType;
61
+ x: numerical;
62
+ y: numerical;
63
+ attrs: A;
64
+ };
65
+
66
+ /** props for baubles that need x,y for positioning */
67
+ export type XYShape<A extends Attrs> = {
68
+ interactions?: InternalInteractionType;
69
+ attrs: A & { x: number; y: number };
70
+ animated?: boolean;
71
+ };
72
+ export type XYBaseShape<A extends BaseAttrs> = {
73
+ interactions?: InternalInteractionType;
74
+ attrs: A & { x: numerical; y: numerical };
75
+ animated?: boolean;
76
+ };
77
+ /** props for Baubles that need d for positioning */
78
+ export type DShape<A extends Attrs> = {
79
+ interactions?: InternalInteractionType;
80
+ d: string;
81
+ attrs: A;
82
+ animated?: boolean;
83
+ };
84
+
85
+ export type AttrAndInteractionApplier<A extends Attrs> = (n: NodeRef) => {
86
+ attrs: A;
87
+ interactions?: Record<string, () => void>;
88
+ };
89
+
90
+ // possibly redundant
91
+ export type Fn = (n: NodeRef) => number | string;
92
+ // resolves functions -> return type, leaves literals as-is
93
+ export const isSpringNumber = (v: numerical): v is SpringValue<number> =>
94
+ v instanceof SpringValue;
95
+ type NodeFn<T> = (n: NodeRef) => T;
96
+ /** A helper function map Attrs accepted by Baubles to those supplied by the user */
97
+ export type LiftToUser<A extends Attrs> = {
98
+ [K in keyof A]: A[K] | NodeFn<A[K]>;
99
+ };
@@ -0,0 +1,123 @@
1
+ import { format } from "d3-format";
2
+ import type { ScaleContinuousNumeric } from "d3-scale";
3
+ import type { Attrs } from "../../baubles/types";
4
+
5
+ export type AxisOrientation = "horizontal" | "vertical" | "polar";
6
+
7
+ export type AxisTicksOptions = {
8
+ number?: number;
9
+ format?: (value: number) => string;
10
+ padding?: number;
11
+ style?: { [key: string]: number | string };
12
+ length?: number;
13
+ values?: number[];
14
+ };
15
+ // export type fullAxisTickOptions =
16
+ // optionals are filled by default below
17
+ export interface AxisProps {
18
+ offsetBy?: number;
19
+ scaleBy?: number;
20
+ reverse?: boolean;
21
+ gap?: number;
22
+ title?: { text: string; padding: number; style: Attrs };
23
+ ticks?: AxisTicksOptions;
24
+ direction?: AxisOrientation;
25
+ scale?: ScaleContinuousNumeric<number, number>; // context figure scale
26
+ // x: number, // optional?
27
+ // y: number,
28
+ // children?: React.ReactNode,
29
+ attrs: Attrs;
30
+ type?: "Polar" | "Rectangular"; //yuck
31
+ bars?: AxisBarOptions;
32
+ }
33
+
34
+ export type DefaultAxisPropType = {
35
+ offsetBy: number;
36
+ scaleBy: number;
37
+ reverse: boolean;
38
+ gap: number;
39
+ title: { text: string; padding: number; style: Attrs };
40
+ ticks: {
41
+ number: number;
42
+ format: (n: number) => string;
43
+ padding: number;
44
+ style: Attrs;
45
+ length: number;
46
+ };
47
+ direction: AxisOrientation;
48
+ type: "Polar" | "Rectangular";
49
+ attrs: Record<string, number | string>;
50
+ };
51
+
52
+ export const defaultAxisProps: DefaultAxisPropType = {
53
+ offsetBy: 0,
54
+ scaleBy: 1,
55
+ reverse: false,
56
+ gap: 5,
57
+ title: { text: "", padding: 40, style: {} },
58
+ ticks: {
59
+ number: 5,
60
+ format: format(".1f"),
61
+ padding: 20,
62
+ style: {},
63
+ length: 6,
64
+ },
65
+ direction: "horizontal",
66
+ attrs: { strokeWidth: 1 },
67
+ type: "Rectangular",
68
+ };
69
+
70
+ export interface WorkingTipOptions extends AxisTicksOptions {
71
+ number: number;
72
+ format: (n: number) => string;
73
+ padding: number;
74
+ style: Attrs;
75
+ length: number;
76
+ values?: number[];
77
+ }
78
+ export interface WorkingAxisProps extends AxisProps {
79
+ offsetBy: number;
80
+ scaleBy: number;
81
+ reverse: boolean;
82
+ gap: number;
83
+ title: { text: string; padding: number; style: Attrs };
84
+ ticks: {
85
+ number: number;
86
+ format: (n: number) => string;
87
+ padding: number;
88
+ style: Attrs;
89
+ length: number;
90
+ values?: number[];
91
+ };
92
+ direction: AxisOrientation;
93
+ strokeWidth: number;
94
+ type: "Polar" | "Rectangular";
95
+ }
96
+
97
+ export const defaultAxisBarsProps = {
98
+ evenFill: "#EDEDED",
99
+ oddFill: "none",
100
+ attrs: {
101
+ rx: 2,
102
+ ry: 2,
103
+ },
104
+ lift: 5,
105
+ };
106
+
107
+ export interface AxisBarsProps {
108
+ evenFill?: string;
109
+ oddFill?: string;
110
+ attrs?: Attrs;
111
+ lift?: number;
112
+ type?: "Rectangular" | "Polar"; // type and layoutClass?
113
+ tickValues: number[];
114
+ scale: ScaleContinuousNumeric<number, number>;
115
+ // figureScale:scaleType, // context
116
+ axisY: number;
117
+ // layoutClass:layoutClass //context
118
+ }
119
+ //these are added by the axis
120
+ export type AxisBarOptions = Omit<
121
+ AxisBarsProps,
122
+ "axisY" | "type" | "tickValues" | "scale" | "figureScale" | "layoutClass"
123
+ >;
@@ -0,0 +1,21 @@
1
+ import PolarAxis from "./polar-axis";
2
+ import RectangularAxis from "./rectangular-axis";
3
+ import type { AxisProps } from "./axis-types";
4
+ import { layoutClass } from "../../../layouts";
5
+ import { useContext } from "react";
6
+ import { DimensionContext } from "../../../context/dimension-context";
7
+
8
+ //TODO do things to scale and allow date as origin not maxD.
9
+
10
+ export default function Axis(props: AxisProps) {
11
+ const dimensions = useContext(DimensionContext);
12
+ const { layoutClass: layoutType } = dimensions;
13
+ if (layoutType === layoutClass.Polar) {
14
+ return <PolarAxis {...props} />;
15
+ } else if (layoutType === layoutClass.Rectangular) {
16
+ return <RectangularAxis {...props} />;
17
+ } else {
18
+ console.warn(`Axis not supported for ${layoutType}`);
19
+ return null;
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ export { default as Axis } from "./axis";
2
+ export type { AxisProps, AxisBarsProps } from "./axis-types";
@@ -0,0 +1,102 @@
1
+ import type { AxisBarsProps } from "./axis-types";
2
+ import { defaultAxisBarsProps } from "./axis-types";
3
+ import type { PolarScaleType } from "../../../store/polar-scale";
4
+ import { BasePath } from "../../baubles";
5
+
6
+ /**
7
+ * This component adds vertical bars to the backgound of a figure. It is used a child of an Axis component and gets
8
+ * it's size and position attributes from it's parent.
9
+ * @param props
10
+ * @return {*}
11
+ * @constructor
12
+ */
13
+ // we are already rotated by the axis parent
14
+ export default function PolarAxisBars(props: AxisBarsProps) {
15
+ const {
16
+ attrs,
17
+ evenFill = defaultAxisBarsProps.evenFill,
18
+ oddFill = defaultAxisBarsProps.oddFill,
19
+ tickValues,
20
+ scale,
21
+ figureScale,
22
+ axisY,
23
+ } = props as { figureScale: PolarScaleType } & AxisBarsProps;
24
+
25
+ return (
26
+ <g className={"axisBars"}>
27
+ {tickValues
28
+ .filter((_t: number, i: number, all: number[]) => i < all.length - 1)
29
+ .map((t: number, i: number) => {
30
+ const start = figureScale({ x: scale(t), y: axisY });
31
+ const end = figureScale({ x: scale(t), y: 0 });
32
+
33
+ const secondStart = figureScale({
34
+ x: scale(tickValues[i + 1]),
35
+ y: 0,
36
+ });
37
+ const secondEnd = figureScale({
38
+ x: scale(tickValues[i + 1]),
39
+ y: axisY,
40
+ });
41
+
42
+ const arcBit =
43
+ start.theta === end.theta || start.r === 0
44
+ ? ""
45
+ : `A${start.r},${start.r} 0 1 0 ${end.x},${end.y}`;
46
+
47
+ const secondArcBit =
48
+ secondStart.theta === secondEnd.theta || secondStart.r === 0
49
+ ? ""
50
+ : `A${secondStart.r},${secondStart.r} 0 1 1 ${secondEnd.x},${secondEnd.y}`;
51
+
52
+ const shape = `M${start.x},${start.y} ${arcBit} L${end.x},${end.y} L${secondStart.x},${secondStart.y} ${secondArcBit} L ${start.x} ${start.y} Z`;
53
+ const fill = i % 2 === 0 ? evenFill : oddFill;
54
+
55
+ return (
56
+ <BasePath
57
+ key={i}
58
+ d={shape}
59
+ fill={fill}
60
+ {...attrs}
61
+ animated={false}
62
+ />
63
+ );
64
+ })}
65
+ </g>
66
+ );
67
+ // const {
68
+ // attrs,
69
+ // evenFill=defaultAxisBarsProps.evenFill,
70
+ // oddFill=defaultAxisBarsProps.oddFill,
71
+ // lift=defaultAxisBarsProps.lift} = props;
72
+
73
+ // const {tickValues,scale,gap,direction} = useAxisContext();
74
+ // const {theta} = useLayout();
75
+ // //d3 starts with 0 at 12 o'clock and svg starts with 0 at 3 o'clock
76
+
77
+ // const angleRange = theta![0]>theta![1]+0.1?2*Math.PI-(theta![0]-(theta![1]+0.1)):(theta![1]+0.1)-theta![0];
78
+
79
+ // const startAngle = theta![0]+Math.PI/2 ;
80
+ // const endAngle = angleRange+startAngle;
81
+
82
+ // return(
83
+ // <g className={"axisBars"}>
84
+ // {tickValues.reduce((acc:JSX.Element[],curr,i)=>{
85
+
86
+ // const shape = arc(
87
+ // {
88
+ // innerRadius:scale(tickValues[i]),
89
+ // outerRadius:scale(tickValues[i+1]),
90
+ // startAngle: startAngle,
91
+ // endAngle:endAngle
92
+ // }
93
+ // )!
94
+
95
+ // const fill = i%2===0?evenFill:oddFill;
96
+ // acc.push(<path key={i} d={shape} fill={fill} {...attrs} />);
97
+
98
+ // return acc;
99
+ // },[])}
100
+ // </g>
101
+ // )
102
+ }
@@ -0,0 +1,175 @@
1
+ import { useContext } from "react";
2
+
3
+ import { mean } from "d3-array";
4
+ import { scaleLinear } from "d3-scale";
5
+ import type { AxisProps, WorkingTipOptions } from "./axis-types";
6
+ import { defaultAxisProps } from "./axis-types";
7
+
8
+ import { normalizeAngle } from "../../../store/polar-scale";
9
+ import type { dimensionType } from "../../figtree/figtree-types";
10
+ import { unNullify } from "../../../utils";
11
+ import { DimensionContext } from "../../../context/dimension-context";
12
+ import { ScaleContext } from "../../../context/scale-context";
13
+ import PolarAxisBars from "./polar-axis-bars";
14
+ import type { PolarVertex } from "../../../layouts/types";
15
+
16
+ //TODO do things to scale and allow date as origin not maxD.
17
+
18
+ export default function PolarAxis(props: AxisProps) {
19
+ const dimensions = useContext(DimensionContext);
20
+ const figureScale = useContext(ScaleContext);
21
+ const { bars, attrs } = props;
22
+
23
+ const ticks: WorkingTipOptions = props.ticks
24
+ ? { ...defaultAxisProps.ticks, ...props.ticks }
25
+ : defaultAxisProps.ticks;
26
+ const title = props.title
27
+ ? { ...defaultAxisProps.title, ...props.title }
28
+ : defaultAxisProps.title;
29
+
30
+ // todo options to provide tick values so can specify breaks
31
+ // we make the scale and then move it to the origin.
32
+ const scale = makeAxisScale(props, dimensions);
33
+
34
+ let tickValues: number[];
35
+ if (ticks.values != undefined) {
36
+ tickValues = ticks.values;
37
+ } else {
38
+ // if (!scale.ticks) {
39
+ // tickValues = range(ticks.number).map((i) =>
40
+ // quantile(scale.domain(), i / (ticks.number - 1)),
41
+ // ) as number[]
42
+ // } else {
43
+ tickValues = scale.ticks(ticks.number);
44
+ // }
45
+ }
46
+
47
+ // start at the root and go outwards
48
+
49
+ const theta = normalizeAngle(
50
+ figureScale({
51
+ x: dimensions.domainX[1],
52
+ y: dimensions.domainY[1],
53
+ } as PolarVertex).theta,
54
+ );
55
+ // const startAngle = figureScale({x:dimensions.domainX[1],y:dimensions.domainY[0]}).theta + Math.PI/2 ;
56
+ // const endAngle = startAngle + 0.05+ (figureScale({x:dimensions.domainX[1],y:dimensions.domainY[1]}).theta - figureScale({x:dimensions.domainX[1],y:dimensions.domainY[0]}).theta);
57
+
58
+ const axisY = dimensions.domainY[1] + dimensions.domainY[1] * 0.005;
59
+ const start = figureScale({ x: dimensions.domainX[0], y: axisY });
60
+ const end = figureScale({ x: dimensions.domainX[1], y: axisY });
61
+ const axisPath = `M${start.x},${start.y} L${end.x},${end.y}`;
62
+
63
+ // We draw the ticks in line with the axis then rotate them 90 degrees
64
+ const x2 = ticks.length * Math.cos(theta);
65
+ const y2 = ticks.length * Math.sin(theta);
66
+
67
+ const xPadding = ticks.padding * Math.cos(theta);
68
+ const yPadding = ticks.padding * Math.sin(theta);
69
+
70
+ // const rawBars = props.children
71
+ // ? Array.isArray(props.children)
72
+ // ? props.children
73
+ // : [props.children]
74
+ // : null
75
+ // const bars = rawBars
76
+ // ? rawBars.map((b: React.ReactElement,i:number) =>
77
+ // React.cloneElement(b, {
78
+ // key:i,
79
+ // figureScale,
80
+ // scale,
81
+ // axisY,
82
+ // direction,
83
+ // layoutClass,
84
+ // dimensions,
85
+ // tickValues,
86
+ // gap,
87
+ // reverse:props.reverse,
88
+ // startAngle,
89
+ // endAngle
90
+ // }),
91
+ // )
92
+ // : null
93
+
94
+ const xPos = unNullify(
95
+ mean(scale.range()),
96
+ `Error calculating x position for title`,
97
+ );
98
+ const titlePos = figureScale({ x: xPos, y: axisY });
99
+ const titleXPadding = title.padding * Math.cos(theta);
100
+ const titleYPadding = title.padding * Math.sin(theta);
101
+
102
+ return (
103
+ <g className={"axis"}>
104
+ <PolarAxisBars
105
+ {...bars}
106
+ tickValues={tickValues}
107
+ scale={scale}
108
+ axisY={axisY}
109
+ />
110
+ :
111
+ <path d={axisPath} stroke={"black"} {...attrs} />
112
+ <g>
113
+ {tickValues.map((t, i) => {
114
+ const point = figureScale({ x: scale(t), y: axisY });
115
+ return (
116
+ <g
117
+ key={`tick-${i}`}
118
+ transform={`translate(${point.x},${point.y}) rotate(90)`}
119
+ >
120
+ <line x1={x2} y1={y2} x2={0} y2={0} stroke={"black"} {...attrs} />
121
+ <text
122
+ transform={`translate(${xPadding},${yPadding}) rotate(-90)`}
123
+ textAnchor={"middle"}
124
+ dominantBaseline={"central"}
125
+ {...ticks.style}
126
+ >
127
+ {ticks.format(t)}
128
+ </text>
129
+ </g>
130
+ );
131
+ })}
132
+ {/*TODO sometimes scale doesn't have a range*/}
133
+ <g transform={`translate(${titlePos.x},${titlePos.y}) rotate(90)`}>
134
+ <text
135
+ textAnchor={"middle"}
136
+ transform={`translate(${titleXPadding},${titleYPadding}) rotate(-90)`}
137
+ >
138
+ {title.text}
139
+ </text>
140
+ </g>
141
+ </g>
142
+ </g>
143
+ );
144
+ }
145
+
146
+ //TODO can make maxR and height the same parameter and use this all axes
147
+
148
+ export function makeAxisScale(props: AxisProps, dimensions: dimensionType) {
149
+ const {
150
+ reverse = defaultAxisProps.reverse,
151
+ offsetBy = defaultAxisProps.offsetBy,
152
+ scaleBy = defaultAxisProps.scaleBy,
153
+ scale,
154
+ } = props;
155
+ const { domainX } = dimensions;
156
+
157
+ // just radius
158
+ // negative range to play nicely with transform above
159
+ const axisScale =
160
+ scale === undefined
161
+ ? scaleLinear().domain(domainX).range(domainX)
162
+ : scale.copy();
163
+ if (scale === undefined) {
164
+ // assume domain goes 0 to max divergence make adjustments on this scale and then update min if it is not 0
165
+ const offset = domainX.map((d) => d + offsetBy);
166
+ const newDomain = offset.map((d) => (d - offsetBy) * scaleBy + offsetBy);
167
+
168
+ axisScale.domain(newDomain);
169
+
170
+ if (reverse) {
171
+ axisScale.domain([offsetBy - (newDomain[1] - newDomain[0]), offsetBy]);
172
+ }
173
+ }
174
+ return axisScale.nice();
175
+ }
@@ -0,0 +1,53 @@
1
+ import { useContext } from "react";
2
+ import { ScaleContext } from "../../../context/scale-context";
3
+ import type { AxisBarsProps } from "./axis-types";
4
+ import { defaultAxisBarsProps } from "./axis-types";
5
+ import { BaseRectangle } from "../../baubles";
6
+
7
+ /**
8
+ * This component adds vertical bars to the backgound of a figure. It is used a child of an Axis component and gets
9
+ * it's size and position attributes from it's parent.
10
+ */
11
+
12
+ export default function AxisBars(props: AxisBarsProps) {
13
+ const {
14
+ attrs,
15
+ evenFill = defaultAxisBarsProps.evenFill,
16
+ oddFill = defaultAxisBarsProps.oddFill,
17
+ tickValues,
18
+ scale,
19
+ axisY,
20
+ } = props;
21
+
22
+ const figureScale = useContext(ScaleContext);
23
+
24
+ return (
25
+ <g className={"axisBars"} key="axisBars">
26
+ {tickValues
27
+ .filter((_t: number, i: number, all: number[]) => i < all.length - 1)
28
+ .map((t: number, i: number) => {
29
+ const start = figureScale({ x: scale(t), y: axisY });
30
+ const end = figureScale({ x: scale(t), y: -0.05 });
31
+
32
+ const secondStart = figureScale({
33
+ x: scale(tickValues[i + 1]),
34
+ y: 0,
35
+ });
36
+
37
+ const fill = i % 2 === 0 ? evenFill : oddFill;
38
+ return (
39
+ <BaseRectangle
40
+ key={`recBar-${i}`}
41
+ x={start.x}
42
+ width={secondStart.x - start.x} // to deal with negative scales
43
+ y={end.y}
44
+ height={start.y - end.y}
45
+ fill={fill}
46
+ {...{ rx: 2, ry: 2, ...attrs }}
47
+ animated={false} // animated can come from context in the shape not needed here
48
+ />
49
+ );
50
+ }, [])}
51
+ </g>
52
+ );
53
+ }