Skip to content

Commit

Permalink
Add experimental hook-based examples (#1256)
Browse files Browse the repository at this point in the history
* refactor: rename the hook-example tags in the index

* docs: add a docsite queryarg to switch into experimental mode

This will allow us to test out the hooks-api without having to change what users normally see.

* docs: add headerto chessboard hooks example

* docs: move dustbin box to hooks

* docs: working on hook examples

* docs: get hooks examples compiling

* refactor: make the begin() function optional

* refactor: hooks api cleanup

* docs: add headers to hook-based examples

* fix: use drop return value in useDropTargetHandler

* docs: get dustbin examples working

* docs: update dragLayer example

* docs: fix custom drag layer example

* docs: get hooks working with more examples

* feat: allow the hooks dragPreview to be a promise

* fix: remove text fixtures from hooks examples
  • Loading branch information
darthtrevino committed Mar 8, 2019
1 parent 35352b2 commit e649b6a
Show file tree
Hide file tree
Showing 64 changed files with 1,154 additions and 1,599 deletions.
1 change: 1 addition & 0 deletions packages/documentation/package.json
Expand Up @@ -31,6 +31,7 @@
"react": "link:../react-dnd/node_modules/react",
"react-dnd": "^7.2.1",
"react-dnd-documentation-examples": "^7.2.1",
"react-dnd-documentation-examples-hooks": "^7.2.1",
"react-dnd-html5-backend": "^7.2.0",
"react-dnd-test-backend": "^7.2.0",
"react-dom": "link:../react-dnd/node_modules/react-dom",
Expand Down
29 changes: 22 additions & 7 deletions packages/documentation/src/components/header.tsx
Expand Up @@ -4,17 +4,32 @@ import NavBar from './navbar'

export interface HeaderProps {
debugMode?: boolean
experimentalMode?: boolean
}

const DebugModeFlag = () => (
<a className="github-fork-ribbon" data-ribbon="Debug Mode" title="Debug Mode">
Debug Mode
</a>
)
const DebugModeFlag = ({ debugMode, experimentalMode }: any) => {
if (!debugMode && !experimentalMode) {
return null
}

let text = ''
if (debugMode && experimentalMode) {
text = 'Dbg Experimental.'
} else if (debugMode) {
text = 'Debug'
} else if (experimentalMode) {
text = 'Experimental'
}
return (
<a className="github-fork-ribbon" data-ribbon={text} title={text}>
{text}
</a>
)
}

const Header: React.FC<HeaderProps> = ({ debugMode }) => (
const Header: React.FC<HeaderProps> = ({ debugMode, experimentalMode }) => (
<Container>
{debugMode ? <DebugModeFlag /> : null}
<DebugModeFlag debugMode={debugMode} experimentalMode={experimentalMode} />
<NavBar />
</Container>
)
Expand Down
4 changes: 3 additions & 1 deletion packages/documentation/src/components/layout.tsx
Expand Up @@ -14,6 +14,7 @@ import { PageGroup } from '../constants'
import { APIPages, ExamplePages } from '../constants'
import Header from './header'
import './layout.css'
import { isExperimentalApiMode } from '../util/renderHtmlAst'
require('prismjs/themes/prism.css')
const favicon = require('../favicon.png')

Expand All @@ -31,6 +32,7 @@ const Layout: React.FC<LayoutProps> = props => {
const sidebarItems: PageGroup[] = isExampleUrl ? ExamplePages : APIPages
const hideSidebar = props.hideSidebar || sitepath === '/about'
const debugMode = isDebugMode()
const experimentalMode = isExperimentalApiMode()
return (
<>
<Helmet
Expand All @@ -47,7 +49,7 @@ const Layout: React.FC<LayoutProps> = props => {
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.2/gh-fork-ribbon.min.css"
/>
</Helmet>
<Header debugMode={debugMode} />
<Header debugMode={debugMode} experimentalMode={experimentalMode} />
<DragDropContextProvider backend={HTML5Backend} debugMode={debugMode}>
<ContentContainer>
<PageBody hasSidebar={sitepath !== '/about'}>
Expand Down
15 changes: 14 additions & 1 deletion packages/documentation/src/util/renderHtmlAst.ts
@@ -1,14 +1,27 @@
declare var require: any
import { createElement } from 'react'
import { componentIndex } from 'react-dnd-documentation-examples/lib/esm/index'
import { componentIndex as hookComponentIndex } from 'react-dnd-documentation-examples-hooks/lib/esm/index'
import { parse } from 'query-string'
import processImages from './processImagesInMarkdownAst'
const log = require('debug')('site:renderHtmlAst')
const rehypeReact = require('rehype-react')

export function isExperimentalApiMode() {
if (typeof window !== 'undefined') {
const queryObject = parse(window.location.search)
return queryObject.experimental !== undefined
} else {
return false
}
}

// Registers the examples as custom components
const renderAst = new rehypeReact({
createElement,
components: componentIndex,
components: {
...(isExperimentalApiMode() ? hookComponentIndex : componentIndex),
},
}).Compiler

export default function renderHtmlAst(node: any) {
Expand Down
2 changes: 1 addition & 1 deletion packages/examples-hooks/src/00 Chessboard/Board.tsx
@@ -1,4 +1,4 @@
import React from 'react'
import * as React from 'react'
import { BoardSquare } from './BoardSquare'
import { Knight } from './Knight'

Expand Down
4 changes: 2 additions & 2 deletions packages/examples-hooks/src/00 Chessboard/BoardSquare.tsx
@@ -1,4 +1,4 @@
import React, { useRef } from 'react'
import * as React from 'react'
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ } from 'react-dnd'
import { Square } from './Square'
import { canMoveKnight, moveKnight } from './Game'
Expand All @@ -17,7 +17,7 @@ export interface BoardSquareProps {
export const BoardSquare: React.FC<BoardSquareProps> = (
props: BoardSquareProps,
) => {
const ref = useRef(null)
const ref = React.useRef(null)
const { isOver, canDrop } = useDrop({
ref,
type: ItemTypes.KNIGHT,
Expand Down
9 changes: 4 additions & 5 deletions packages/examples-hooks/src/00 Chessboard/Knight.tsx
@@ -1,4 +1,4 @@
import React, { useRef, useMemo } from 'react'
import * as React from 'react'
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ } from 'react-dnd'
import ItemTypes from './ItemTypes'
import knightImage from './knightImage'
Expand All @@ -23,13 +23,12 @@ function createKnightImage() {
}

export const Knight: React.FC = () => {
const ref = useRef(null)
const dragPreview = useMemo(createKnightImage, [])
const ref = React.useRef(null)
const dragPreview = React.useMemo(createKnightImage, [])
const { isDragging } = useDrag({
ref,
type: ItemTypes.KNIGHT,
begin: () => ({}),
dragPreview,
preview: dragPreview,
collect: mon => ({
isDragging: !!mon.isDragging(),
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/examples-hooks/src/00 Chessboard/Overlay.tsx
@@ -1,4 +1,4 @@
import React from 'react'
import * as React from 'react'

export interface OverlayProps {
color: string
Expand Down
2 changes: 1 addition & 1 deletion packages/examples-hooks/src/00 Chessboard/Square.tsx
@@ -1,4 +1,4 @@
import React from 'react'
import * as React from 'react'

export interface SquareProps {
black: boolean
Expand Down
15 changes: 10 additions & 5 deletions packages/examples-hooks/src/00 Chessboard/index.tsx
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import * as React from 'react'
import Board from './Board'
import { observe } from './Game'

Expand All @@ -16,13 +16,18 @@ const containerStyle: React.CSSProperties = {
* The Chessboard Tutorial Application
*/
const ChessboardTutorialApp: React.FC = () => {
const [knightPos, setKnightPos] = useState<[number, number]>([1, 7])
const [knightPos, setKnightPos] = React.useState<[number, number]>([1, 7])

// the observe function will return an unsubscribe callback
useEffect(() => observe((newPos: [number, number]) => setKnightPos(newPos)))
React.useEffect(() =>
observe((newPos: [number, number]) => setKnightPos(newPos)),
)
return (
<div style={containerStyle}>
<Board knightPosition={knightPos} />
<div>
<h1>EXPERIMENTAL API</h1>
<div style={containerStyle}>
<Board knightPosition={knightPos} />
</div>
</div>
)
}
Expand Down
106 changes: 46 additions & 60 deletions packages/examples-hooks/src/01 Dustbin/Copy or Move/Box.tsx
@@ -1,11 +1,9 @@
import React from 'react'
import {
DragSource,
ConnectDragSource,
DragSourceConnector,
DragSourceMonitor,
} from 'react-dnd'
import * as React from 'react'
import ItemTypes from '../Single Target/ItemTypes'
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ } from 'react-dnd'
const {
useDrag,
} = __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__

const style: React.CSSProperties = {
border: '1px dashed gray',
Expand All @@ -16,61 +14,49 @@ const style: React.CSSProperties = {
float: 'left',
}

const boxSource = {
beginDrag(props: BoxProps) {
return {
name: props.name,
}
},

endDrag(props: BoxProps, monitor: DragSourceMonitor) {
const item = monitor.getItem()
const dropResult = monitor.getDropResult()

if (dropResult) {
let alertMessage = ''
const isDropAllowed =
dropResult.allowedDropEffect === 'any' ||
dropResult.allowedDropEffect === dropResult.dropEffect

if (isDropAllowed) {
const isCopyAction = dropResult.dropEffect === 'copy'
const actionName = isCopyAction ? 'copied' : 'moved'
alertMessage = `You ${actionName} ${item.name} into ${dropResult.name}!`
} else {
alertMessage = `You cannot ${dropResult.dropEffect} an item into the ${
dropResult.name
}`
}
alert(alertMessage)
}
},
}

export interface BoxProps {
name: string
}

interface BoxCollectedProps {
isDragging: boolean
connectDragSource: ConnectDragSource
}

class Box extends React.Component<BoxProps & BoxCollectedProps> {
public render() {
const { isDragging, connectDragSource } = this.props
const { name } = this.props
const opacity = isDragging ? 0.4 : 1

return connectDragSource(<div style={{ ...style, opacity }}>{name}</div>)
}
const Box: React.FC<BoxProps> = ({ name }) => {
const ref = React.useRef(null)
const { opacity } = useDrag({
ref,
type: ItemTypes.BOX,
begin: () => ({ name }),
end(monitor) {
const item = monitor.getItem()
const dropResult = monitor.getDropResult()

if (dropResult) {
let alertMessage = ''
const isDropAllowed =
dropResult.allowedDropEffect === 'any' ||
dropResult.allowedDropEffect === dropResult.dropEffect

if (isDropAllowed) {
const isCopyAction = dropResult.dropEffect === 'copy'
const actionName = isCopyAction ? 'copied' : 'moved'
alertMessage = `You ${actionName} ${item.name} into ${
dropResult.name
}!`
} else {
alertMessage = `You cannot ${
dropResult.dropEffect
} an item into the ${dropResult.name}`
}
alert(alertMessage)
}
},
collect: monitor => ({
opacity: monitor.isDragging() ? 0.4 : 1,
}),
})

return (
<div ref={ref} style={{ ...style, opacity }}>
{name}
</div>
)
}

export default DragSource(
ItemTypes.BOX,
boxSource,
(connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}),
)(Box)
export default Box

0 comments on commit e649b6a

Please sign in to comment.