Skip to content

Commit

Permalink
Merge pull request #59 from Setsun/typescript
Browse files Browse the repository at this point in the history
feat(typescript): Basic TypeScript support
  • Loading branch information
drcmda committed Apr 26, 2019
2 parents b565628 + 43215c8 commit 58dabd9
Show file tree
Hide file tree
Showing 12 changed files with 614 additions and 940 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ $RECYCLE.BIN/
package-lock.json
coverage/
.idea
yarn-error.log
18 changes: 9 additions & 9 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"dist/index.js": {
"bundled": 25765,
"minified": 12114,
"gzipped": 4384,
"bundled": 26062,
"minified": 12282,
"gzipped": 4404,
"treeshaked": {
"rollup": {
"code": 9879,
"import_statements": 602
"code": 10015,
"import_statements": 647
},
"webpack": {
"code": 11438
"code": 11631
}
}
},
"dist/index.cjs.js": {
"bundled": 29141,
"minified": 13944,
"gzipped": 4648
"bundled": 29439,
"minified": 14081,
"gzipped": 4661
}
}
13 changes: 13 additions & 0 deletions .tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"jsx": "react",
"pretty": true,
"skipLibCheck": true,
"allowJs": true
},
"include": ["./src/**/*"],
"exclude": ["./node_modules/**/*"]
}
48 changes: 28 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
"sideEffects": false,
"scripts": {
"prebuild": "rimraf dist",
"build": "rollup -c",
"build": "rollup -c && npm run typegen",
"prepare": "npm run build",
"eslint": "eslint ./src",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"typecheck": "tsc --noEmit --jsx react src/*",
"typegen": "tsc --jsx react --emitDeclarationOnly -d --declarationDir types src/*"
},
"husky": {
"hooks": {
Expand All @@ -26,7 +28,7 @@
"printWidth": 120
},
"lint-staged": {
"*.{js,}": [
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"git add"
]
Expand All @@ -49,30 +51,33 @@
},
"homepage": "https://github.com/drcmda/react-three-fiber#readme",
"dependencies": {
"@babel/runtime": "^7.3.4",
"@babel/runtime": "^7.4.3",
"@types/three": "^0.103.2",
"lodash-es": "^4.17.11",
"pointer-events-polyfill": "^0.4.4-pre",
"react-reconciler": "^0.20.1",
"react-reconciler": "^0.20.4",
"resize-observer-polyfill": "^1.5.1",
"scheduler": "^0.13.3"
"scheduler": "^0.14.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8",
"three": ">=0.100"
"three": ">=0.103"
},
"devDependencies": {
"@babel/core": "7.3.4",
"@babel/plugin-proposal-class-properties": "7.3.4",
"@babel/core": "7.4.3",
"@babel/plugin-proposal-class-properties": "7.4.0",
"@babel/plugin-proposal-do-expressions": "7.2.0",
"@babel/plugin-proposal-object-rest-spread": "7.3.4",
"@babel/plugin-transform-modules-commonjs": "7.2.0",
"@babel/plugin-transform-parameters": "7.3.3",
"@babel/plugin-transform-runtime": "7.3.4",
"@babel/plugin-proposal-object-rest-spread": "7.4.3",
"@babel/plugin-transform-modules-commonjs": "7.4.3",
"@babel/plugin-transform-parameters": "7.4.3",
"@babel/plugin-transform-runtime": "7.4.3",
"@babel/plugin-transform-template-literals": "7.2.0",
"@babel/preset-env": "7.3.4",
"@babel/preset-env": "7.4.3",
"@babel/preset-react": "7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@types/lodash-es": "^4.17.3",
"@types/react": "^16.8.14",
"babel-eslint": "^10.0.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"eslint": "^5.16.0",
Expand All @@ -81,15 +86,18 @@
"eslint-plugin-react-hooks": "^1.6.0",
"husky": "^1.3.1",
"lint-staged": "^8.1.5",
"prettier": "^1.16.4",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"rollup": "^1.10.1",
"prettier": "^1.17.0",
"react": ">=16.8",
"react-dom": ">=16.8",
"rollup": "^1.4.1",
"rollup": "^1.10.1",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.1",
"rollup-plugin-node-resolve": "^4.0.1",
"rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-size-snapshot": "^0.8.0",
"three": ">=0.100",
"typescript": "^3.3.3333"
"three": "^0.103.0",
"typescript": "^3.4.5"
}
}
1 change: 1 addition & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { sizeSnapshot } from 'rollup-plugin-size-snapshot'
const root = process.platform === 'win32' ? path.resolve('/') : '/'
const external = id => !id.startsWith('.') && !id.startsWith(root)
const extensions = ['.js', '.jsx', '.ts', '.tsx']

const getBabelOptions = ({ useESModules }, targets) => ({
babelrc: false,
extensions,
Expand Down
109 changes: 88 additions & 21 deletions src/canvas.js → src/canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,93 @@
import * as THREE from 'three'
import React, { useRef, useEffect, useMemo, useState, useCallback, useContext } from 'react'
import * as React from 'react'
import { useRef, useEffect, useMemo, useState, useCallback } from 'react'
import ResizeObserver from 'resize-observer-polyfill'
import { invalidate, applyProps, render, unmountComponentAtNode } from './reconciler'

export const stateContext = React.createContext()
export type CanvasContext = {
canvas?: React.MutableRefObject<any>
subscribers: Array<Function>
frames: 0
aspect: 0
gl?: THREE.WebGLRenderer
camera?: THREE.Camera
scene?: THREE.Scene
canvasRect?: DOMRectReadOnly
viewport?: { width: number; height: number }
size?: { left: number; top: number; width: number; height: number }
ready: boolean
manual: boolean
active: boolean
captured: boolean
invalidateFrameloop: boolean
subscribe?: (callback: Function, main: any) => () => any
setManual: (takeOverRenderloop: boolean) => any
setDefaultCamera: (camera: THREE.Camera) => any
invalidate: () => any
}

export type CanvasProps = {
children: React.ReactNode
gl: THREE.WebGLRenderer
orthographic: THREE.OrthographicCamera | THREE.PerspectiveCamera
raycaster: THREE.Raycaster
camera?: THREE.Camera
style?: React.CSSProperties
pixelRatio?: number
invalidateFrameloop?: boolean
onCreated: Function
}

export type Measure = [
{ ref: React.MutableRefObject<any> },
{ left: number; top: number; width: number; height: number }
]

export type IntersectObject = Event &
THREE.Intersection & {
ray: THREE.Raycaster
stopped: { current: boolean }
uuid: string
transform: {
x: Function
y: Function
}
}

const defaultRef = {
ready: false,
subscribers: [],
manual: false,
active: true,
canvas: undefined,
gl: undefined,
camera: undefined,
scene: undefined,
size: undefined,
canvasRect: undefined,
frames: 0,
aspect: 0,
viewport: undefined,
captured: undefined,
invalidateFrameloop: false,
subscribe: (fn, main) => () => {},
setManual: takeOverRenderloop => {},
setDefaultCamera: cam => {},
invalidate: () => {},
}

export const stateContext = React.createContext(defaultRef)

function useMeasure() {
function useMeasure(): Measure {
const ref = useRef()

const [bounds, set] = useState({ left: 0, top: 0, width: 0, height: 0 })
const [ro] = useState(() => new ResizeObserver(([entry]) => set(entry.contentRect)))
useEffect(() => {
if (ref.current) ro.observe(ref.current)
return () => ro.disconnect()
}, [ref.current])

return [{ ref }, bounds]
}

Expand All @@ -28,7 +103,7 @@ export const Canvas = React.memo(
invalidateFrameloop = false,
onCreated,
...rest
}) => {
}: CanvasProps) => {
// Local, reactive state
const canvas = useRef()
const [ready, setReady] = useState(false)
Expand All @@ -50,19 +125,7 @@ export const Canvas = React.memo(

// Public state
const state = useRef({
ready: false,
subscribers: [],
manual: false,
active: true,
canvas: undefined,
gl: undefined,
camera: undefined,
scene: undefined,
size: undefined,
canvasRect: undefined,
frames: 0,
viewport: undefined,
captured: undefined,
...defaultRef,
subscribe: (fn, main) => {
state.current.subscribers.push(fn)
return () => (state.current.subscribers = state.current.subscribers.filter(s => s !== fn))
Expand Down Expand Up @@ -187,8 +250,10 @@ export const Canvas = React.memo(
/** Intersects interaction objects using the event input */
const intersect = useCallback((event, prepare = true) => {
if (prepare) prepareRay(event)

const intersects = defaultRaycaster.intersectObjects(state.current.scene.__interaction, true)
const hits = []

for (let intersect of intersects) {
let object = intersect.object
// Bubble event up
Expand All @@ -198,10 +263,10 @@ export const Canvas = React.memo(
}
}
return hits
})
}, [])

/** Handles intersections by forwarding them to handlers */
const handleIntersects = useCallback((event, fn) => {
const handleIntersects = useCallback((event: React.PointerEvent<any>, fn) => {
prepareRay(event)
// If the interaction is captured, take the last known hit instead of raycasting again
const hits =
Expand All @@ -218,6 +283,7 @@ export const Canvas = React.memo(

for (let hit of hits) {
let stopped = { current: false }

fn({
...Object.assign({}, event),
...hit,
Expand All @@ -227,6 +293,7 @@ export const Canvas = React.memo(
// Hijack stopPropagation, which just sets a flag
stopPropagation: () => (stopped.current = true),
})

if (stopped.current === true) break
}
}
Expand All @@ -246,7 +313,7 @@ export const Canvas = React.memo(
)

const hovered = useRef({})
const handlePointerMove = useCallback(event => {
const handlePointerMove = useCallback((event: React.PointerEvent<any>) => {
if (!state.current.ready) return
const hits = handleIntersects(event, data => {
const object = data.object
Expand Down Expand Up @@ -278,7 +345,7 @@ export const Canvas = React.memo(
handlePointerCancel(event, hits)
}, [])

const handlePointerCancel = useCallback((event, hits) => {
const handlePointerCancel = useCallback((event: React.PointerEvent<any>, hits?: []) => {
if (!hits) hits = handleIntersects(event, () => null)
Object.values(hovered.current).forEach(data => {
if (!hits.length || !hits.find(i => i.object === data.object)) {
Expand Down
27 changes: 19 additions & 8 deletions src/hooks.js → src/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React, { useRef, useContext, useEffect, useMemo, useState } from 'react'
import { useRef, useContext, useEffect, useMemo, useState } from 'react'
import { stateContext } from './canvas'

export function useRender(fn, takeOverRenderloop) {
export function useRender(fn: Function, takeOverRenderloop: boolean): any {
const { subscribe, setManual } = useContext(stateContext)

// This calls into the host to inform it whether the render-loop is manual or not
useMemo(() => takeOverRenderloop && setManual(true), [takeOverRenderloop])

useEffect(() => {
// Subscribe to the render-loop
const unsubscribe = subscribe(fn, takeOverRenderloop)

return () => {
// Call subscription off on unmount
unsubscribe()
Expand All @@ -21,21 +24,29 @@ export function useThree() {
return props
}

export function useUpdate(callback, dependents, optionalRef) {
export function useUpdate(
callback: Function,
dependents: [],
optionalRef: React.MutableRefObject<any>
): React.MutableRefObject<any> {
const { invalidate } = useContext(stateContext)
let ref = useRef()
if (optionalRef) ref = optionalRef
const ref = optionalRef ? optionalRef : useRef()

useEffect(() => {
callback(ref.current)
invalidate()
}, dependents)

return ref
}

export function useResource(optionalRef) {
let ref = useRef()
if (optionalRef) ref = optionalRef
export function useResource(
optionalRef: React.MutableRefObject<any>
): React.MutableRefObject<any> {
const [resource, set] = useState()
const ref = optionalRef ? optionalRef : useRef()

useEffect(() => void set(ref.current), [ref.current])

return resource
}

0 comments on commit 58dabd9

Please sign in to comment.