mathbox-react 0.0.2 → 0.0.5-dev

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 (54) hide show
  1. package/.eslintrc.js +3 -2
  2. package/.github/workflows/lint.yaml +3 -1
  3. package/.prettierrc +1 -0
  4. package/babel.config.js +3 -0
  5. package/build/cjs/index.js +2 -0
  6. package/build/cjs/index.js.map +1 -0
  7. package/build/cjs/types/components/Cartesian.spec.d.ts +1 -0
  8. package/build/cjs/types/components/Mathbox.d.ts +7 -0
  9. package/build/cjs/types/components/MathboxNodeContext.d.ts +4 -0
  10. package/build/cjs/types/components/components.d.ts +261 -0
  11. package/build/cjs/types/components/hooks.d.ts +4 -0
  12. package/build/cjs/types/components/index.d.ts +2 -0
  13. package/build/cjs/types/components/types.d.ts +7 -0
  14. package/build/cjs/types/index.d.ts +1 -0
  15. package/build/cjs/types/stories/utils.d.ts +5 -0
  16. package/build/cjs/types/testSetup.d.ts +1 -0
  17. package/build/cjs/types/testUtils.d.ts +1 -0
  18. package/build/esm/index.js +16 -0
  19. package/build/esm/index.js.map +1 -0
  20. package/build/esm/types/components/Cartesian.spec.d.ts +1 -0
  21. package/build/esm/types/components/Mathbox.d.ts +7 -0
  22. package/build/esm/types/components/MathboxNodeContext.d.ts +4 -0
  23. package/build/esm/types/components/components.d.ts +261 -0
  24. package/build/esm/types/components/hooks.d.ts +4 -0
  25. package/build/esm/types/components/index.d.ts +2 -0
  26. package/build/esm/types/components/types.d.ts +7 -0
  27. package/build/esm/types/index.d.ts +1 -0
  28. package/build/esm/types/stories/utils.d.ts +5 -0
  29. package/build/esm/types/testSetup.d.ts +1 -0
  30. package/build/esm/types/testUtils.d.ts +1 -0
  31. package/build/index.d.ts +272 -0
  32. package/jest.config.js +12 -0
  33. package/mathbox-react-0.0.4.tgz +0 -0
  34. package/package.json +17 -6
  35. package/scratch.js +149 -0
  36. package/src/components/Cartesian.spec.tsx +101 -0
  37. package/src/components/Mathbox.tsx +50 -33
  38. package/src/components/MathboxNodeContext.ts +6 -0
  39. package/src/components/components.tsx +361 -0
  40. package/src/components/hooks.ts +51 -0
  41. package/src/components/index.ts +2 -0
  42. package/src/components/types.ts +9 -0
  43. package/src/index.ts +1 -3
  44. package/src/stories/Cartesian.stories.tsx +36 -0
  45. package/src/stories/Grid.stories.tsx +38 -0
  46. package/src/stories/Mathbox.stories.tsx +16 -0
  47. package/src/stories/Point.stories.tsx +68 -0
  48. package/src/stories/utils.tsx +31 -0
  49. package/src/testSetup.ts +3 -0
  50. package/src/testUtils.ts +46 -0
  51. package/tsconfig.base.json +0 -1
  52. package/tsconfig.json +1 -0
  53. package/src/components/Mathbox.stories.tsx +0 -28
  54. package/src/types.d.ts +0 -4
Binary file
package/package.json CHANGED
@@ -1,19 +1,25 @@
1
1
  {
2
2
  "name": "mathbox-react",
3
- "version": "0.0.2",
3
+ "version": "0.0.5-dev",
4
4
  "description": "React wrapper for Mathbox",
5
5
  "license": "ISC",
6
6
  "author": "Chris Chudzicki",
7
7
  "main": "build/cjs/index.js",
8
8
  "module": "build/esm/index.js",
9
+ "types": "build/esm/types",
9
10
  "scripts": {
10
- "build": "rollup -c",
11
+ "build": "rm -rf build && rollup -c",
11
12
  "build-storybook": "build-storybook",
12
13
  "storybook": "start-storybook -p 6006",
13
- "lint": "eslint \"src/**/*.{ts,tsx}\" --fix --max-warnings=0"
14
+ "lint": "eslint \"src/**/*.{ts,tsx}\" --max-warnings=0",
15
+ "format": "prettier src",
16
+ "autofix": "npm run format -- --write && npm run lint -- --fix",
17
+ "prepack": "npm run build",
18
+ "test": "jest"
14
19
  },
15
20
  "devDependencies": {
16
21
  "@babel/core": "^7.17.5",
22
+ "@babel/preset-env": "^7.16.11",
17
23
  "@rollup/plugin-commonjs": "^21.0.1",
18
24
  "@rollup/plugin-node-resolve": "^13.1.3",
19
25
  "@rollup/plugin-typescript": "^8.3.0",
@@ -23,8 +29,11 @@
23
29
  "@storybook/addon-links": "^6.4.19",
24
30
  "@storybook/react": "^6.4.19",
25
31
  "@storybook/testing-library": "^0.0.9",
32
+ "@testing-library/react": "^12.1.3",
33
+ "@types/jest": "^27.4.1",
26
34
  "@types/react": "^17.0.39",
27
35
  "@types/three": "^0.137.0",
36
+ "babel-jest": "^27.5.1",
28
37
  "babel-loader": "^8.2.3",
29
38
  "eslint-config-airbnb": "^19.0.4",
30
39
  "eslint-config-airbnb-typescript": "^16.1.0",
@@ -32,7 +41,8 @@
32
41
  "eslint-config-react-app": "^7.0.0",
33
42
  "eslint-import-resolver-typescript": "^2.5.0",
34
43
  "eslint-plugin-prettier": "^4.0.0",
35
- "mathbox": "^2.1.0",
44
+ "jest": "^27.5.1",
45
+ "mathbox": "2.1.2-dev",
36
46
  "prettier": "^2.5.1",
37
47
  "react": "^17.0.2",
38
48
  "react-dom": "^17.0.2",
@@ -41,11 +51,12 @@
41
51
  "rollup-plugin-peer-deps-external": "^2.2.4",
42
52
  "rollup-plugin-terser": "^7.0.2",
43
53
  "three": "^0.137.5",
54
+ "ts-jest": "^27.1.3",
44
55
  "typescript": "^4.5.5"
45
56
  },
46
57
  "peerDependencies": {
47
- "mathbox": "^2.1.0",
58
+ "mathbox": "2.1.2-dev",
48
59
  "react": "^17.0.2",
49
- "three": "^0.137.5"
60
+ "three": ">=0.118.0"
50
61
  }
51
62
  }
package/scratch.js ADDED
@@ -0,0 +1,149 @@
1
+ const classDescriptions = {
2
+ axis: ["draw", "Draw an axis", {}, { end: "true", zBias: "-1" }],
3
+ face: ["draw", "Draw polygon faces"],
4
+ grid: ["draw", "Draw a 2D line grid", {}, { width: "1", zBias: "-2" }],
5
+ line: ["draw", "Draw lines"],
6
+ point: ["draw", "Draw points"],
7
+ strip: ["draw", "Draw triangle strips"],
8
+ surface: ["draw", "Draw surfaces", {}, { lineX: "false", lineY: "false" }],
9
+ ticks: ["draw", "Draw ticks"],
10
+ vector: ["draw", "Draw vectors"],
11
+
12
+ view: ["view", "Adjust view range"],
13
+ cartesian: ["view", "Apply cartesian view"],
14
+ cartesian4: ["view", "Apply 4D cartesian view"],
15
+ polar: ["view", "Apply polar view"],
16
+ spherical: ["view", "Apply spherical view"],
17
+ stereographic: ["view", "Apply stereographic projection"],
18
+ stereographic4: ["view", "Apply 4D stereographic projection"],
19
+
20
+ transform: ["transform", "Transform geometry in 3D"],
21
+ transform4: ["transform", "Transform geometry in 4D"],
22
+ vertex: ["transform", "Apply custom vertex shader pass"],
23
+ fragment: ["transform", "Apply custom fragment shader pass"],
24
+ layer: ["transform", "Independent 2D layer/overlay"],
25
+ mask: ["transform", "Apply custom mask pass"],
26
+
27
+ array: [
28
+ "data",
29
+ "1D array",
30
+ { expr: "function (emit, i, time, delta) { ... }" },
31
+ ],
32
+ interval: [
33
+ "data",
34
+ "1D sampled array",
35
+ { expr: "function (emit, x, i, time, delta) { ... }" },
36
+ ],
37
+ matrix: [
38
+ "data",
39
+ "2D matrix",
40
+ { expr: "function (emit, i, j, time, delta) { ... }" },
41
+ ],
42
+ area: [
43
+ "data",
44
+ "2D sampled matrix",
45
+ { expr: "function (emit, x, y, i, j, time, delta) { ... }" },
46
+ ],
47
+ voxel: [
48
+ "data",
49
+ "3D voxels",
50
+ { expr: "function (emit, i, j, k, time, delta) { ... }" },
51
+ ],
52
+ volume: [
53
+ "data",
54
+ "3D sampled voxels",
55
+ { expr: "function (emit, x, y, z, i, j, k, time, delta) { ... }" },
56
+ ],
57
+ scale: ["data", "Human-friendly divisions on an axis, subdivided as needed"],
58
+
59
+ html: ["overlay", "HTML element source"],
60
+ dom: ["overlay", "HTML DOM injector"],
61
+
62
+ text: [
63
+ "text",
64
+ "GL text source",
65
+ {},
66
+ { minFilter: '"linear"', magFilter: '"linear"' },
67
+ ],
68
+ format: [
69
+ "text",
70
+ "Text formatter",
71
+ { expr: "function (x, y, z, w, i, j, k, l, time, delta) { ... }" },
72
+ { minFilter: '"linear"', magFilter: '"linear"' },
73
+ ],
74
+ retext: ["text", "Text atlas resampler"],
75
+ label: ["text", "Draw GL labels"],
76
+
77
+ clamp: ["operator", "Clamp out-of-bounds samples to the nearest data point"],
78
+ grow: ["operator", "Scale data relative to reference data point"],
79
+ join: [
80
+ "operator",
81
+ "Join two array dimensions into one by concatenating rows/columns/stacks",
82
+ ],
83
+ lerp: ["operator", "Linear interpolation of data"],
84
+ memo: ["operator", "Memoize data to an array/texture"],
85
+ readback: [
86
+ "operator",
87
+ "Read data back to a binary JavaScript array",
88
+ { expr: "function (x, y, z, w, i, j, k, l) { ... }" },
89
+ ],
90
+ resample: ["operator", "Resample data to new dimensions with a shader"],
91
+ repeat: ["operator", "Repeat data in one or more dimensions"],
92
+ swizzle: ["operator", "Swizzle data values"],
93
+ spread: ["operator", "Spread data values according to array indices"],
94
+ split: [
95
+ "operator",
96
+ "Split one array dimension into two by splitting rows/columns/etc",
97
+ ],
98
+ slice: ["operator", "Select one or more rows/columns/stacks"],
99
+ subdivide: ["operator", "Subdivide data points evenly or with a bevel"],
100
+ transpose: ["operator", "Transpose array dimensions"],
101
+
102
+ group: ["base", "Group elements for visibility and activity"],
103
+ inherit: ["base", "Inherit and inject a trait from another element"],
104
+ root: ["base", "Tree root"],
105
+ unit: ["base", "Change unit sizing for drawing ops"],
106
+
107
+ shader: ["shader", "Custom shader snippet"],
108
+
109
+ camera: ["camera", "Camera instance or proxy"],
110
+
111
+ rtt: [
112
+ "rtt",
113
+ "Render objects to a texture",
114
+ {},
115
+ { minFilter: '"linear"', magFilter: '"linear"', type: '"unsignedByte"' },
116
+ ],
117
+ compose: [
118
+ "rtt",
119
+ "Full-screen render pass",
120
+ {},
121
+ { zWrite: "false", zTest: "false", color: '"white"' },
122
+ ],
123
+
124
+ clock: ["time", "Relative clock that starts from zero."],
125
+ now: ["time", "Absolute UNIX time in seconds since 01/01/1970"],
126
+
127
+ move: ["present", "Move elements in/out on transition"],
128
+ play: ["present", "Play a sequenced animation"],
129
+ present: ["present", "Present a tree of slides"],
130
+ reveal: ["present", "Reveal/hide elements on transition"],
131
+ slide: ["present", "Presentation slide"],
132
+ step: ["present", "Step through a sequenced animation"],
133
+ }
134
+
135
+ const capitalize = (s) => `${s[0].toUpperCase()}${s.slice(1)}`
136
+
137
+ const makeDef = (name) => {
138
+ const [group] = classDescriptions[name]
139
+ const Name = capitalize(name)
140
+ const dest = `https://gitgud.io/unconed/mathbox/-/blob/master/docs/primitives.md#${group}${name}`
141
+ const def = `
142
+ /**
143
+ * Component wrapper for mathbox [\`${name}\`](${dest}).
144
+ */
145
+ export const ${Name} = mathboxComponentFactory('${name}');`
146
+ console.log(def)
147
+ }
148
+
149
+ Object.keys(classDescriptions).sort().forEach(makeDef)
@@ -0,0 +1,101 @@
1
+ import React from "react"
2
+ import { render } from "@testing-library/react"
3
+ import Mathbox from "./Mathbox"
4
+ import { Cartesian, Grid } from "./components"
5
+ import { MathboxRef } from "./types"
6
+
7
+ function assertNotUndefined<T>(value: T | undefined): asserts value is T {
8
+ if (value === undefined) {
9
+ throw new Error("Unexpected undefined value")
10
+ }
11
+ }
12
+
13
+ describe("Cartesian", () => {
14
+ it("exposes Mathbox instance via ref", () => {
15
+ const mbRef: MathboxRef<"root"> = { current: null }
16
+ const cartesianRef: MathboxRef<"cartesian"> = { current: null }
17
+ render(
18
+ <Mathbox ref={mbRef}>
19
+ <Cartesian ref={cartesianRef} />
20
+ </Mathbox>
21
+ )
22
+
23
+ expect(mbRef.current?.[0].type).toBe("root")
24
+ expect(cartesianRef.current?.[0].type).toBe("cartesian")
25
+ })
26
+
27
+ it("creates a cartesian instance as child of root", () => {
28
+ const mbRef: MathboxRef<"root"> = { current: null }
29
+ render(
30
+ <Mathbox ref={mbRef}>
31
+ <Cartesian />
32
+ </Mathbox>
33
+ )
34
+ expect(mbRef.current?.select("cartesian").length).toBe(1)
35
+ })
36
+
37
+ it("creates mathbox children as children of itself", () => {
38
+ const mbRef: MathboxRef<"root"> = { current: null }
39
+ render(
40
+ <Mathbox ref={mbRef}>
41
+ <Cartesian>
42
+ <Grid />
43
+ <Grid />
44
+ </Cartesian>
45
+ </Mathbox>
46
+ )
47
+ mbRef.current?.print()
48
+ expect(mbRef.current?.select("cartesian").length).toBe(1)
49
+ expect(mbRef.current?.select("cartesian grid").length).toBe(2)
50
+ })
51
+
52
+ it("removes its mathbox instance when unmounted", () => {
53
+ const mbRef: MathboxRef<"root"> = { current: null }
54
+ const { rerender } = render(
55
+ <Mathbox ref={mbRef}>
56
+ <Cartesian />
57
+ </Mathbox>
58
+ )
59
+ expect(mbRef.current?.select("cartesian").length).toBe(1)
60
+ rerender(<Mathbox ref={mbRef} />)
61
+ expect(mbRef.current?.select("cartesian").length).toBe(0)
62
+ })
63
+
64
+ it.each([
65
+ { props: { visible: true, scale: [3, 2, 1] } },
66
+ { props: { visible: false, scale: [1, 2, 3] } },
67
+ ])("passes appropriate props to its mathbox instance", ({ props }) => {
68
+ const mbRef: MathboxRef<"root"> = { current: null }
69
+ render(
70
+ <Mathbox ref={mbRef}>
71
+ <Cartesian {...props} />
72
+ </Mathbox>
73
+ )
74
+ const cartesian = mbRef.current?.select<"cartesian">("cartesian")
75
+
76
+ assertNotUndefined(cartesian)
77
+
78
+ expect(cartesian.get("visible")).toBe(props.visible)
79
+ // Mathbox converts scale to a ThreeJS Vec3
80
+ expect(cartesian.get("scale").toArray()).toStrictEqual(props.scale)
81
+ })
82
+
83
+ it("updates props on its mathbox instance when rerendered", () => {
84
+ const mbRef: MathboxRef<"root"> = { current: null }
85
+ const { rerender } = render(
86
+ <Mathbox ref={mbRef}>
87
+ <Cartesian />
88
+ </Mathbox>
89
+ )
90
+ const cartesian = mbRef.current?.select<"cartesian">("cartesian")
91
+ assertNotUndefined(cartesian)
92
+
93
+ expect(cartesian.get("visible")).toBe(true)
94
+ rerender(
95
+ <Mathbox ref={mbRef}>
96
+ <Cartesian visible={false} />
97
+ </Mathbox>
98
+ )
99
+ expect(cartesian.get("visible")).toBe(false)
100
+ })
101
+ })
@@ -1,41 +1,58 @@
1
- import { useEffect } from 'react'
2
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
3
- import { Color } from "three/src/math/Color";
4
- import * as MB from 'mathbox'
1
+ import React, {
2
+ useEffect,
3
+ useState,
4
+ forwardRef,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ } from "react"
8
+ import { Color } from "three"
9
+ import { mathBox, MathboxSelection, MathBoxOptions } from "mathbox"
10
+ import MathboxAPIContext from "./MathboxNodeContext"
5
11
 
6
12
  type Props = {
7
- element: HTMLElement
8
- }
13
+ options?: MathBoxOptions
14
+ initialCameraPosition?: number[]
15
+ } & React.HTMLProps<HTMLDivElement>
9
16
 
10
- const Mathbox = (props: Props) => {
17
+ const Mathbox = (
18
+ props: Props,
19
+ ref: React.Ref<MathboxSelection<"root"> | null>
20
+ ) => {
21
+ const { children, initialCameraPosition, options, ...divProps } = props
22
+ const mathboxOptions = useMemo(() => options ?? {}, [options])
23
+ const [selection, setSelection] = useState<MathboxSelection<"root"> | null>(
24
+ null
25
+ )
26
+ const [container, setContainer] = useState<HTMLDivElement | null>(null)
11
27
  useEffect(() => {
12
- const mathbox = MB.mathBox({
13
- plugins: ["core", "controls", "cursor"],
14
- controls: {
15
- klass: OrbitControls,
16
- },
17
- element: props.element,
18
- });
19
-
20
- mathbox.three.camera.position.set(1, 1, 2);
21
- mathbox.three.renderer.setClearColor(new Color(0xffffff), 1.0);
28
+ if (!container) return () => {}
22
29
 
23
- const view = mathbox.cartesian({
24
- range: [
25
- [-2, 2],
26
- [-1, 1],
27
- [-1, 1],
28
- ],
29
- scale: [2, 1, 1],
30
- });
31
- view.grid({
32
- divideX: 20,
33
- width: 5,
34
- opacity: 0.3,
35
- });
30
+ const mathbox = mathBox({
31
+ ...mathboxOptions,
32
+ element: container,
33
+ })
34
+ setSelection(mathbox)
36
35
 
37
- }, [props.element])
38
- return null
36
+ /**
37
+ * TODO: Should Mathbox component allow setting these more easily?
38
+ */
39
+ mathbox.three.renderer.setClearColor(new Color(0xffffff), 1.0)
40
+ if (initialCameraPosition) {
41
+ mathbox.three.camera.position.set(...initialCameraPosition)
42
+ }
43
+ return () => {
44
+ mathbox.select("*").remove()
45
+ mathbox.three.destroy()
46
+ }
47
+ }, [container, mathboxOptions, initialCameraPosition])
48
+ useImperativeHandle(ref, () => selection)
49
+ return (
50
+ <div ref={setContainer} {...divProps}>
51
+ <MathboxAPIContext.Provider value={selection}>
52
+ {children}
53
+ </MathboxAPIContext.Provider>
54
+ </div>
55
+ )
39
56
  }
40
57
 
41
- export default Mathbox
58
+ export default forwardRef(Mathbox)
@@ -0,0 +1,6 @@
1
+ import { createContext } from "react"
2
+ import { MathboxSelection, NodeType } from "mathbox"
3
+
4
+ const MathboxAPIContext = createContext<MathboxSelection<NodeType> | null>(null)
5
+
6
+ export default MathboxAPIContext