From 0222fda811f66c617fa8415d56f5bf5fc5f47962 Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Thu, 2 Apr 2020 18:30:29 +0100 Subject: [PATCH 1/5] create vx-primitives package with initial setup --- packages/vx-primitives/.npmrc | 1 + packages/vx-primitives/README.md | 9 ++ packages/vx-primitives/package.json | 49 ++++++++++ packages/vx-primitives/src/VxPrimitives.ts | 92 +++++++++++++++++++ packages/vx-primitives/src/core.android.ts | 10 ++ packages/vx-primitives/src/core.ios.ts | 10 ++ packages/vx-primitives/src/core.native.ts | 55 +++++++++++ packages/vx-primitives/src/core.sketch.ts | 56 +++++++++++ packages/vx-primitives/src/core.ts | 4 + packages/vx-primitives/src/core.web.ts | 31 +++++++ packages/vx-primitives/src/index.ts | 1 + .../vx-primitives/src/modules/Platform.ts | 25 +++++ 12 files changed, 343 insertions(+) create mode 100644 packages/vx-primitives/.npmrc create mode 100644 packages/vx-primitives/README.md create mode 100644 packages/vx-primitives/package.json create mode 100644 packages/vx-primitives/src/VxPrimitives.ts create mode 100644 packages/vx-primitives/src/core.android.ts create mode 100644 packages/vx-primitives/src/core.ios.ts create mode 100644 packages/vx-primitives/src/core.native.ts create mode 100644 packages/vx-primitives/src/core.sketch.ts create mode 100644 packages/vx-primitives/src/core.ts create mode 100644 packages/vx-primitives/src/core.web.ts create mode 100644 packages/vx-primitives/src/index.ts create mode 100644 packages/vx-primitives/src/modules/Platform.ts diff --git a/packages/vx-primitives/.npmrc b/packages/vx-primitives/.npmrc new file mode 100644 index 000000000..43c97e719 --- /dev/null +++ b/packages/vx-primitives/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/vx-primitives/README.md b/packages/vx-primitives/README.md new file mode 100644 index 000000000..35b7b816a --- /dev/null +++ b/packages/vx-primitives/README.md @@ -0,0 +1,9 @@ +# @vx/primitives + + + + + +The `@vx/primitives` package is here to help you make cross-platform apps. + +Inspired by https://github.com/lelandrichardson/react-primitives and https://github.com/chengyin/react-primitives-svg. diff --git a/packages/vx-primitives/package.json b/packages/vx-primitives/package.json new file mode 100644 index 000000000..4c449e26f --- /dev/null +++ b/packages/vx-primitives/package.json @@ -0,0 +1,49 @@ +{ + "name": "@vx/primitives", + "version": "0.0.195", + "description": "vx primitives", + "sideEffects": false, + "main": "lib/index.js", + "module": "esm/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib", + "esm" + ], + "scripts": { + "docs": "cd ./docs && ../../../node_modules/.bin/react-docgen ../src/components | ../../../scripts/buildDocs.sh" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hshoff/vx.git" + }, + "keywords": [ + "vx", + "react", + "d3", + "visualizations", + "charts" + ], + "author": "@macintoshhelper", + "license": "MIT", + "bugs": { + "url": "https://github.com/hshoff/vx/issues" + }, + "homepage": "https://github.com/hshoff/vx#readme", + "dependencies": { + "@types/lodash": "^4.14.146", + "@types/react": "*", + "lodash": "^4.17.10", + "prop-types": "^15.6.1" + }, + "peerDependencies": { + "react": "^15.0.0-0 || ^16.0.0-0" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "react-native-svg": "^12.0.3", + "react-sketchapp": "^3.1.1" + } +} diff --git a/packages/vx-primitives/src/VxPrimitives.ts b/packages/vx-primitives/src/VxPrimitives.ts new file mode 100644 index 000000000..59d275add --- /dev/null +++ b/packages/vx-primitives/src/VxPrimitives.ts @@ -0,0 +1,92 @@ +import { ReactNode } from 'react'; + +import Platform from './modules/Platform'; + +type Primitives = { + Svg?: ReactNode | string; + + Circle?: ReactNode | string; + ClipPath?: ReactNode | string; + Defs?: ReactNode | string; + Ellipse?: ReactNode | string; + G?: ReactNode | string; + Image?: ReactNode | string; + Line?: ReactNode | string; + LinearGradient?: ReactNode | string; + Path?: ReactNode | string; + Pattern?: ReactNode | string; + Polygon?: ReactNode | string; + Polyline?: ReactNode | string; + RadialGradient?: ReactNode | string; + Rect?: ReactNode | string; + Stop?: ReactNode | string; + Symbol?: ReactNode | string; + Text?: ReactNode | string; + TextPath?: ReactNode | string; + TSpan?: ReactNode | string; + Use?: ReactNode | string; + Platform: typeof Platform; +}; + +const VxPrimitives: Primitives & { inject: (elements: Primitives) => void } = { + /* Svg primitives: */ + Svg: null, + + Circle: null, + ClipPath: null, + Defs: null, + Ellipse: null, + G: null, + Image: null, + Line: null, + LinearGradient: null, + Path: null, + Pattern: null, + Polygon: null, + Polyline: null, + RadialGradient: null, + Rect: null, + Stop: null, + Symbol: null, + Text: null, + TextPath: null, + TSpan: null, + Use: null, + + // Touchable: null, + // View: null, + // Text: null, + // Dimensions: null, + // PixelRatio: require('./modules/PixelRatio'), + Platform, + inject: api => { + [ + 'Svg', + 'Circle', + 'Ellipse', + 'G', + 'LinearGradient', + 'RadialGradient', + 'Line', + 'Path', + 'Polygon', + 'Polyline', + 'Rect', + 'Symbol', + 'Text', + 'Use', + 'Defs', + 'Stop', + ].forEach(k => { + if (api[k]) { + VxPrimitives[k] = api[k]; + } + }); + + if (api.Platform) { + VxPrimitives['Platform'].inject(api.Platform); + } + }, +}; + +export default VxPrimitives; diff --git a/packages/vx-primitives/src/core.android.ts b/packages/vx-primitives/src/core.android.ts new file mode 100644 index 000000000..556930b76 --- /dev/null +++ b/packages/vx-primitives/src/core.android.ts @@ -0,0 +1,10 @@ +import './core.native'; + +import VxPrimitives from './VxPrimitives'; + +VxPrimitives.inject({ + Platform: { + OS: 'android', + Version: 1, + }, +}); diff --git a/packages/vx-primitives/src/core.ios.ts b/packages/vx-primitives/src/core.ios.ts new file mode 100644 index 000000000..5a32a1751 --- /dev/null +++ b/packages/vx-primitives/src/core.ios.ts @@ -0,0 +1,10 @@ +import './core.native'; + +import VxPrimitives from './VxPrimitives'; + +VxPrimitives.inject({ + Platform: { + OS: 'ios', + Version: 1, + }, +}); diff --git a/packages/vx-primitives/src/core.native.ts b/packages/vx-primitives/src/core.native.ts new file mode 100644 index 000000000..f6075eb9f --- /dev/null +++ b/packages/vx-primitives/src/core.native.ts @@ -0,0 +1,55 @@ +import { + Svg, + Circle, + ClipPath, + Defs, + Ellipse, + G, + Image, + Line, + LinearGradient, + Path, + Pattern, + Polygon, + Polyline, + RadialGradient, + Rect, + Stop, + Symbol, + Text, + TextPath, + TSpan, + Use, +} from 'react-native-svg'; + +import VxPrimitives from './VxPrimitives'; + +VxPrimitives.inject({ + Svg, + Circle, + ClipPath, + Ellipse, + G, + Image, + LinearGradient, + RadialGradient, + Line, + Path, + Pattern, + Polygon, + Polyline, + Rect, + Symbol, + Text, + TextPath, + TSpan, + Use, + Defs, + Stop, + Platform: { + OS: 'native', + Version: 1, + }, +}); + +module.exports = VxPrimitives; diff --git a/packages/vx-primitives/src/core.sketch.ts b/packages/vx-primitives/src/core.sketch.ts new file mode 100644 index 000000000..402f63a0d --- /dev/null +++ b/packages/vx-primitives/src/core.sketch.ts @@ -0,0 +1,56 @@ +import { Svg } from 'react-sketchapp'; + +const { + Circle, + ClipPath, + Defs, + Ellipse, + G, + Image, + Line, + LinearGradient, + Path, + Pattern, + Polygon, + Polyline, + RadialGradient, + Rect, + Stop, + Symbol, + Text, + TextPath, + TSpan, + Use, +} = Svg; + +import VxPrimitives from './VxPrimitives'; + +VxPrimitives.inject({ + Svg, + Circle, + ClipPath, + Ellipse, + G, + Image, + LinearGradient, + RadialGradient, + Line, + Path, + Pattern, + Polygon, + Polyline, + Rect, + Symbol, + Text, + TextPath, + TSpan, + Use, + Defs, + Stop, + Platform: { + OS: 'sketch', + Version: 1, + }, +}); + +module.exports = VxPrimitives; diff --git a/packages/vx-primitives/src/core.ts b/packages/vx-primitives/src/core.ts new file mode 100644 index 000000000..2511f7a96 --- /dev/null +++ b/packages/vx-primitives/src/core.ts @@ -0,0 +1,4 @@ +// Default to web implementation if there is no platform extension resolver +if (typeof window !== 'undefined') { + require('./core.web'); +} diff --git a/packages/vx-primitives/src/core.web.ts b/packages/vx-primitives/src/core.web.ts new file mode 100644 index 000000000..dab193a36 --- /dev/null +++ b/packages/vx-primitives/src/core.web.ts @@ -0,0 +1,31 @@ +import 'react-dom'; + +import VxPrimitives from './VxPrimitives'; + +VxPrimitives.inject({ + Svg: 'svg', + Circle: 'circle', + ClipPath: 'clipPath', + Ellipse: 'ellipse', + G: 'g', + Image: 'image', + LinearGradient: 'linearGradient', + RadialGradient: 'radialGradient', + Line: 'line', + Path: 'path', + Pattern: 'pattern', + Polygon: 'polygon', + Polyline: 'polyline', + Rect: 'rect', + Symbol: 'symbol', + Text: 'text', + TextPath: 'textPath', + TSpan: 'tspan', + Use: 'use', + Defs: 'defs', + Stop: 'stop', + Platform: { + OS: 'web', + Version: 1, + }, +}); diff --git a/packages/vx-primitives/src/index.ts b/packages/vx-primitives/src/index.ts new file mode 100644 index 000000000..0b879cb24 --- /dev/null +++ b/packages/vx-primitives/src/index.ts @@ -0,0 +1 @@ +module.exports = require('./core'); diff --git a/packages/vx-primitives/src/modules/Platform.ts b/packages/vx-primitives/src/modules/Platform.ts new file mode 100644 index 000000000..4cba12db7 --- /dev/null +++ b/packages/vx-primitives/src/modules/Platform.ts @@ -0,0 +1,25 @@ +const { hasOwnProperty } = Object.prototype; + +type PlatformType = { + OS: string; + Version: number; +}; + +const Platform: PlatformType & { select?: (Object) => void; inject?: (PlatformType) => void } = { + OS: 'unknown', + Version: 0, + select: obj => { + if (hasOwnProperty.call(obj, Platform.OS)) { + return obj[Platform.OS]; + } + return obj.default; + }, + inject: platform => { + // Use bracket accessor notation as workaround for + // https://github.com/facebook/metro-bundler/issues/27 + Platform['OS'] = platform.OS; // eslint-disable-line dot-notation + Platform['Version'] = platform.Version; // eslint-disable-line dot-notation + }, +}; + +export default Platform; From 8eb3412356e053153bf9a136b4a86a356716b3f9 Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Fri, 3 Apr 2020 01:21:19 +0100 Subject: [PATCH 2/5] port initial components (shapes/gradients) to @vx/primitives --- packages/vx-geo/src/graticule/Graticule.tsx | 16 +++++++++++----- packages/vx-geo/src/projections/Projection.tsx | 13 +++++++------ .../vx-gradient/src/gradients/LinearGradient.tsx | 15 ++++++++------- packages/vx-group/src/Group.tsx | 15 ++++++++------- packages/vx-shape/package.json | 1 + packages/vx-shape/src/shapes/Bar.tsx | 9 ++++++++- packages/vx-shape/src/shapes/Pie.tsx | 11 ++++++++--- 7 files changed, 51 insertions(+), 29 deletions(-) diff --git a/packages/vx-geo/src/graticule/Graticule.tsx b/packages/vx-geo/src/graticule/Graticule.tsx index 24fe746c7..0d1512c24 100644 --- a/packages/vx-geo/src/graticule/Graticule.tsx +++ b/packages/vx-geo/src/graticule/Graticule.tsx @@ -3,6 +3,7 @@ import { Group } from '@vx/group'; import { geoGraticule, GeoGraticuleGenerator } from 'd3-geo'; // eslint-disable-next-line import/no-unresolved import { LineString, MultiLineString, Polygon } from 'geojson'; +import { G, Path } from '@vx/primitives'; export type GraticuleProps = { /** @@ -67,16 +68,21 @@ export default function Graticule({ return ( {graticule && ( - + )} {lines && currGraticule.lines().map((line, i) => ( - - - + + + ))} {outline && ( - + )} ); diff --git a/packages/vx-geo/src/projections/Projection.tsx b/packages/vx-geo/src/projections/Projection.tsx index f0daa9155..ab171de32 100644 --- a/packages/vx-geo/src/projections/Projection.tsx +++ b/packages/vx-geo/src/projections/Projection.tsx @@ -14,6 +14,7 @@ import { } from 'd3-geo'; // eslint-disable-next-line import/no-unresolved import { LineString, Polygon, MultiLineString } from 'geojson'; +import { Platform, G, Path } from '@vx/primitives'; import Graticule, { GraticuleProps } from '../graticule/Graticule'; import { GeoPermissibleObjects, ProjectionPreset, Projection as ProjectionShape } from '../types'; @@ -172,15 +173,15 @@ export default function Projection({ )} {features.map((feature, i) => ( - - + {centroid && centroid(feature.centroid, feature)} - + ))} {/* TODO: Maybe find a different way to pass projection function to use for example invert */} diff --git a/packages/vx-gradient/src/gradients/LinearGradient.tsx b/packages/vx-gradient/src/gradients/LinearGradient.tsx index e8caa5977..c83e9d0e8 100644 --- a/packages/vx-gradient/src/gradients/LinearGradient.tsx +++ b/packages/vx-gradient/src/gradients/LinearGradient.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Stop, Defs, LinearGradient as SvgLinearGradient } from '@vx/primitives'; type LinearGradientOwnProps = { /** Unique id for the gradient. Should be unique across all page elements. */ @@ -65,20 +66,20 @@ export default function LinearGradient({ y2 = '1'; } return ( - - + {!!children && children} - {!children && } - {!children && } - - + {!children && } + {!children && } + + ); } diff --git a/packages/vx-group/src/Group.tsx b/packages/vx-group/src/Group.tsx index e62a73ba4..92f1537c1 100644 --- a/packages/vx-group/src/Group.tsx +++ b/packages/vx-group/src/Group.tsx @@ -1,12 +1,13 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import cx from 'classnames'; +import { Platform, G } from '@vx/primitives'; type GroupProps = { top?: number; left?: number; transform?: string; className?: string; - children?: React.ReactNode; + children?: ReactNode; innerRef?: React.Ref; }; @@ -20,13 +21,13 @@ export default function Group({ ...restProps }: GroupProps & Omit, keyof GroupProps>) { return ( - } + className={Platform.OS === 'web' ? cx('vx-group', className) : undefined} transform={transform || `translate(${left}, ${top})`} - {...restProps} + {...(restProps as any)} > {children} - + ); } diff --git a/packages/vx-shape/package.json b/packages/vx-shape/package.json index c3c6c9d4b..44c7b7f21 100644 --- a/packages/vx-shape/package.json +++ b/packages/vx-shape/package.json @@ -30,6 +30,7 @@ "@types/react": "*", "@vx/curve": "0.0.195", "@vx/group": "0.0.195", + "@vx/primitives": "0.0.195", "classnames": "^2.2.5", "d3-path": "^1.0.5", "d3-shape": "^1.2.0", diff --git a/packages/vx-shape/src/shapes/Bar.tsx b/packages/vx-shape/src/shapes/Bar.tsx index e29b1388d..e449d8bfd 100644 --- a/packages/vx-shape/src/shapes/Bar.tsx +++ b/packages/vx-shape/src/shapes/Bar.tsx @@ -1,5 +1,6 @@ import React from 'react'; import cx from 'classnames'; +import { Platform, Rect } from '@vx/primitives'; export type BarProps = { /** className to apply to rect element. */ @@ -13,5 +14,11 @@ export default function Bar({ innerRef, ...restProps }: BarProps & Omit, keyof BarProps>) { - return ; + return ( + + ); } diff --git a/packages/vx-shape/src/shapes/Pie.tsx b/packages/vx-shape/src/shapes/Pie.tsx index 74f282e93..204ebc797 100644 --- a/packages/vx-shape/src/shapes/Pie.tsx +++ b/packages/vx-shape/src/shapes/Pie.tsx @@ -2,6 +2,7 @@ import React from 'react'; import cx from 'classnames'; import { Group } from '@vx/group'; +import { Platform, G, Path } from '@vx/primitives'; import { arc as d3Arc, Arc as ArcType, @@ -99,10 +100,14 @@ export default function Pie({ return ( {arcs.map((arc, i) => ( - - + + {centroid && centroid(path.centroid(arc), arc)} - + ))} ); From 947b92a6191c7a88c1eda9fac8eed6500ccd0846 Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Fri, 3 Apr 2020 01:24:46 +0100 Subject: [PATCH 3/5] fix injection in @vx/primitives --- .../src/@types/react-native/index.d.ts | 315 ++++++++++++++++++ packages/vx-primitives/src/VxPrimitives.ts | 88 +++-- packages/vx-primitives/src/core.ts | 4 +- packages/vx-primitives/src/core.web.ts | 2 + packages/vx-primitives/src/index.ts | 72 +++- .../vx-primitives/src/modules/Platform.ts | 2 +- 6 files changed, 451 insertions(+), 32 deletions(-) create mode 100644 packages/vx-primitives/src/@types/react-native/index.d.ts diff --git a/packages/vx-primitives/src/@types/react-native/index.d.ts b/packages/vx-primitives/src/@types/react-native/index.d.ts new file mode 100644 index 000000000..716e1d6f9 --- /dev/null +++ b/packages/vx-primitives/src/@types/react-native/index.d.ts @@ -0,0 +1,315 @@ + +declare module 'react-native' { + const a: any; + + export default a; + export const GestureResponderHandlers: any; + export const ImageProps: any; + export interface ViewProperties { [key: string]: any } + + type NodeHandle = number; + + + type Falsy = undefined | null | false; + interface RecursiveArray extends Array | RecursiveArray> { } + /** Keep a brand of 'T' so that calls to `StyleSheet.flatten` can take `RegisteredStyle` and return `T`. */ + type RegisteredStyle = number & { __registeredStyleBrand: T }; + export type StyleProp = T | RegisteredStyle | RecursiveArray | Falsy> | Falsy; + + /** + * @see https://facebook.github.io/react-native/docs/image.html + */ + export interface ImagePropsBase { + /** + * onLayout function + * + * Invoked on mount and layout changes with + * + * {nativeEvent: { layout: {x, y, width, height} }}. + */ + onLayout?: (event: any) => void; + + /** + * Invoked on load error with {nativeEvent: {error}} + */ + onError?: (error: NativeSyntheticEvent) => void; + + /** + * Invoked when load completes successfully + * { source: { url, height, width } }. + */ + onLoad?: (event: NativeSyntheticEvent) => void; + + /** + * Invoked when load either succeeds or fails + */ + onLoadEnd?: () => void; + + /** + * Invoked on load start + */ + onLoadStart?: () => void; + + progressiveRenderingEnabled?: boolean; + + borderRadius?: number; + + borderTopLeftRadius?: number; + + borderTopRightRadius?: number; + + borderBottomLeftRadius?: number; + + borderBottomRightRadius?: number; + + /** + * Determines how to resize the image when the frame doesn't match the raw + * image dimensions. + * + * 'cover': Scale the image uniformly (maintain the image's aspect ratio) + * so that both dimensions (width and height) of the image will be equal + * to or larger than the corresponding dimension of the view (minus padding). + * + * 'contain': Scale the image uniformly (maintain the image's aspect ratio) + * so that both dimensions (width and height) of the image will be equal to + * or less than the corresponding dimension of the view (minus padding). + * + * 'stretch': Scale width and height independently, This may change the + * aspect ratio of the src. + * + * 'repeat': Repeat the image to cover the frame of the view. + * The image will keep it's size and aspect ratio. (iOS only) + * + * 'center': Scale the image down so that it is completely visible, + * if bigger than the area of the view. + * The image will not be scaled up. + */ + resizeMode?: any; + + /** + * The mechanism that should be used to resize the image when the image's dimensions + * differ from the image view's dimensions. Defaults to `auto`. + * + * - `auto`: Use heuristics to pick between `resize` and `scale`. + * + * - `resize`: A software operation which changes the encoded image in memory before it + * gets decoded. This should be used instead of `scale` when the image is much larger + * than the view. + * + * - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is + * faster (usually hardware accelerated) and produces higher quality images. This + * should be used if the image is smaller than the view. It should also be used if the + * image is slightly bigger than the view. + * + * More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html. + * + * @platform android + */ + resizeMethod?: 'auto' | 'resize' | 'scale'; + + /** + * The image source (either a remote URL or a local file resource). + * + * This prop can also contain several remote URLs, specified together with their width and height and potentially with scale/other URI arguments. + * The native side will then choose the best uri to display based on the measured size of the image container. + * A cache property can be added to control how networked request interacts with the local cache. + * + * The currently supported formats are png, jpg, jpeg, bmp, gif, webp (Android only), psd (iOS only). + */ + source: any; + + /** + * similarly to `source`, this property represents the resource used to render + * the loading indicator for the image, displayed until image is ready to be + * displayed, typically after when it got downloaded from network. + */ + loadingIndicatorSource?: any; + + /** + * A unique identifier for this element to be used in UI Automation testing scripts. + */ + testID?: string; + + /** + * A static image to display while downloading the final image off the network. + */ + defaultSource?: any | number; + } + + /** + * Image style + * @see https://facebook.github.io/react-native/docs/image.html#style + * @see https://github.com/facebook/react-native/blob/master/Libraries/Image/ImageStylePropTypes.js + */ + export interface ImageStyle { + resizeMode?: any; + backfaceVisibility?: 'visible' | 'hidden'; + borderBottomLeftRadius?: number; + borderBottomRightRadius?: number; + backgroundColor?: string; + borderColor?: string; + borderWidth?: number; + borderRadius?: number; + borderTopLeftRadius?: number; + borderTopRightRadius?: number; + overflow?: 'visible' | 'hidden'; + overlayColor?: string; + tintColor?: string; + opacity?: number; + } + + export interface ImageProps extends ImagePropsBase { + /** + * + * Style + */ + style?: StyleProp; + } + + + export interface NativeSyntheticEvent extends React.BaseSyntheticEvent { } + + + export interface NativeTouchEvent { + /** + * Array of all touch events that have changed since the last event + */ + changedTouches: NativeTouchEvent[]; + + /** + * The ID of the touch + */ + identifier: string; + + /** + * The X position of the touch, relative to the element + */ + locationX: number; + + /** + * The Y position of the touch, relative to the element + */ + locationY: number; + + /** + * The X position of the touch, relative to the screen + */ + pageX: number; + + /** + * The Y position of the touch, relative to the screen + */ + pageY: number; + + /** + * The node id of the element receiving the touch event + */ + target: string; + + /** + * A time identifier for the touch, useful for velocity calculation + */ + timestamp: number; + + /** + * Array of all current touches on the screen + */ + touches: NativeTouchEvent[]; + } + + export interface GestureResponderEvent extends NativeSyntheticEvent { } + + + export interface GestureResponderHandlers { + /** + * A view can become the touch responder by implementing the correct negotiation methods. + * There are two methods to ask the view if it wants to become responder: + */ + + /** + * Does this view want to become responder on the start of a touch? + */ + onStartShouldSetResponder?: (event: GestureResponderEvent) => boolean; + + /** + * Called for every touch move on the View when it is not the responder: does this view want to "claim" touch responsiveness? + */ + onMoveShouldSetResponder?: (event: GestureResponderEvent) => boolean; + + /** + * If the View returns true and attempts to become the responder, one of the following will happen: + */ + + onResponderEnd?: (event: GestureResponderEvent) => void; + + /** + * The View is now responding for touch events. + * This is the time to highlight and show the user what is happening + */ + onResponderGrant?: (event: GestureResponderEvent) => void; + + /** + * Something else is the responder right now and will not release it + */ + onResponderReject?: (event: GestureResponderEvent) => void; + + /** + * If the view is responding, the following handlers can be called: + */ + + /** + * The user is moving their finger + */ + onResponderMove?: (event: GestureResponderEvent) => void; + + /** + * Fired at the end of the touch, ie "touchUp" + */ + onResponderRelease?: (event: GestureResponderEvent) => void; + + onResponderStart?: (event: GestureResponderEvent) => void; + + /** + * Something else wants to become responder. + * Should this view release the responder? Returning true allows release + */ + onResponderTerminationRequest?: (event: GestureResponderEvent) => boolean; + + /** + * The responder has been taken from the View. + * Might be taken by other views after a call to onResponderTerminationRequest, + * or might be taken by the OS without asking (happens with control center/ notification center on iOS) + */ + onResponderTerminate?: (event: GestureResponderEvent) => void; + + /** + * onStartShouldSetResponder and onMoveShouldSetResponder are called with a bubbling pattern, + * where the deepest node is called first. + * That means that the deepest component will become responder when multiple Views return true for *ShouldSetResponder handlers. + * This is desirable in most cases, because it makes sure all controls and buttons are usable. + * + * However, sometimes a parent will want to make sure that it becomes responder. + * This can be handled by using the capture phase. + * Before the responder system bubbles up from the deepest component, + * it will do a capture phase, firing on*ShouldSetResponderCapture. + * So if a parent View wants to prevent the child from becoming responder on a touch start, + * it should have a onStartShouldSetResponderCapture handler which returns true. + */ + onStartShouldSetResponderCapture?: (event: GestureResponderEvent) => boolean; + + /** + * onStartShouldSetResponder and onMoveShouldSetResponder are called with a bubbling pattern, + * where the deepest node is called first. + * That means that the deepest component will become responder when multiple Views return true for *ShouldSetResponder handlers. + * This is desirable in most cases, because it makes sure all controls and buttons are usable. + * + * However, sometimes a parent will want to make sure that it becomes responder. + * This can be handled by using the capture phase. + * Before the responder system bubbles up from the deepest component, + * it will do a capture phase, firing on*ShouldSetResponderCapture. + * So if a parent View wants to prevent the child from becoming responder on a touch start, + * it should have a onStartShouldSetResponderCapture handler which returns true. + */ + onMoveShouldSetResponderCapture?: (event: GestureResponderEvent) => boolean; + } +} diff --git a/packages/vx-primitives/src/VxPrimitives.ts b/packages/vx-primitives/src/VxPrimitives.ts index 59d275add..ee7149b30 100644 --- a/packages/vx-primitives/src/VxPrimitives.ts +++ b/packages/vx-primitives/src/VxPrimitives.ts @@ -3,32 +3,65 @@ import { ReactNode } from 'react'; import Platform from './modules/Platform'; type Primitives = { - Svg?: ReactNode | string; - - Circle?: ReactNode | string; - ClipPath?: ReactNode | string; - Defs?: ReactNode | string; - Ellipse?: ReactNode | string; - G?: ReactNode | string; - Image?: ReactNode | string; - Line?: ReactNode | string; - LinearGradient?: ReactNode | string; - Path?: ReactNode | string; - Pattern?: ReactNode | string; - Polygon?: ReactNode | string; - Polyline?: ReactNode | string; - RadialGradient?: ReactNode | string; - Rect?: ReactNode | string; - Stop?: ReactNode | string; - Symbol?: ReactNode | string; - Text?: ReactNode | string; - TextPath?: ReactNode | string; - TSpan?: ReactNode | string; - Use?: ReactNode | string; - Platform: typeof Platform; + Svg: null | ReactNode | string; + Circle: null | ReactNode | string; + ClipPath: null | ReactNode | string; + Defs: null | ReactNode | string; + Ellipse: null | ReactNode | string; + G: null | ReactNode | string; + Image: null | ReactNode | string; + Line: null | ReactNode | string; + LinearGradient: null | ReactNode | string; + Path: null | ReactNode | string; + Pattern: null | ReactNode | string; + Polygon: null | ReactNode | string; + Polyline: null | ReactNode | string; + RadialGradient: null | ReactNode | string; + Rect: null | ReactNode | string; + Stop: null | ReactNode | string; + Symbol: null | ReactNode | string; + Text: null | ReactNode | string; + TextPath: null | ReactNode | string; + TSpan: null | ReactNode | string; + Use: null | ReactNode | string; + Platform: { + OS: string; + Version: number; + } & { + select?: ((_: any) => void) | undefined; + inject?: ((_: { OS: string; Version: number }) => void) | undefined; + }; + inject: ( + api: { + [key: string]: any; + } & { + Platform: { + OS: string; + Version: number; + }; + }, + ) => void; }; -const VxPrimitives: Primitives & { inject: (elements: Primitives) => void } = { +type SvgElement = + | 'Svg' + | 'Circle' + | 'Ellipse' + | 'G' + | 'LinearGradient' + | 'RadialGradient' + | 'Line' + | 'Path' + | 'Polygon' + | 'Polyline' + | 'Rect' + | 'Symbol' + | 'Text' + | 'Use' + | 'Defs' + | 'Stop'; + +const VxPrimitives: Primitives = { /* Svg primitives: */ Svg: null, @@ -59,7 +92,7 @@ const VxPrimitives: Primitives & { inject: (elements: Primitives) => void } = { // Dimensions: null, // PixelRatio: require('./modules/PixelRatio'), Platform, - inject: api => { + inject: (api: { [key: string]: any } & { Platform: { OS: string; Version: number } }) => { [ 'Svg', 'Circle', @@ -77,13 +110,14 @@ const VxPrimitives: Primitives & { inject: (elements: Primitives) => void } = { 'Use', 'Defs', 'Stop', - ].forEach(k => { + // @ts-ignore + ].forEach((k: SvgElement) => { if (api[k]) { VxPrimitives[k] = api[k]; } }); - if (api.Platform) { + // @ts-ignore VxPrimitives['Platform'].inject(api.Platform); } }, diff --git a/packages/vx-primitives/src/core.ts b/packages/vx-primitives/src/core.ts index 2511f7a96..e9b7cb114 100644 --- a/packages/vx-primitives/src/core.ts +++ b/packages/vx-primitives/src/core.ts @@ -1,4 +1,2 @@ // Default to web implementation if there is no platform extension resolver -if (typeof window !== 'undefined') { - require('./core.web'); -} +module.exports = require('./core.web'); diff --git a/packages/vx-primitives/src/core.web.ts b/packages/vx-primitives/src/core.web.ts index dab193a36..127434551 100644 --- a/packages/vx-primitives/src/core.web.ts +++ b/packages/vx-primitives/src/core.web.ts @@ -29,3 +29,5 @@ VxPrimitives.inject({ Version: 1, }, }); + +module.exports = VxPrimitives; diff --git a/packages/vx-primitives/src/index.ts b/packages/vx-primitives/src/index.ts index 0b879cb24..08253e9ef 100644 --- a/packages/vx-primitives/src/index.ts +++ b/packages/vx-primitives/src/index.ts @@ -1 +1,71 @@ -module.exports = require('./core'); +// @ts-ignore +declare module '@vx/primitives' { + import { FunctionComponent, ReactNode, ComponentClass } from 'react'; + import { + CommonPathProps, + ImageProps, + LineProps, + LinearGradientProps, + PathProps, + PatternProps, + EllipseProps, + DefinitionProps, + SvgProps, + PolygonProps, + PolylineProps, + RadialGradientProps, + RectProps, + StopProps, + SymbolProps, + TextProps, + TextPathProps, + TSpanProps, + UseProps, + GProps, + } from 'react-native-svg'; + + // type BaseSvgElement = FunctionComponent<{ className?: string | undefined, children?: ReactNode }> + type BaseProps = { className?: string; ref?: any }; + + export const Defs: ComponentClass; + export const Ellipse: ComponentClass; + export const G: ComponentClass; + export const Image: ComponentClass; + export const Line: ComponentClass; + export const LinearGradient: ComponentClass; + export const Path: ComponentClass; + export const Pattern: ComponentClass; + export const Polygon: ComponentClass; + export const Polyline: ComponentClass; + export const RadialGradient: ComponentClass; + export const Rect: ComponentClass; + export const Stop: ComponentClass; + export const Symbol: ComponentClass; + export const Text: ComponentClass; + export const TextPath: ComponentClass; + export const TSpan: ComponentClass; + export const Use: ComponentClass; + + export const Platform: { + OS: string; + Version: number; + } & { + select?: ((_: any) => void) | undefined; + inject?: ((_: { OS: string; Version: number }) => void) | undefined; + }; + + export const inject: ( + api: { + [key: string]: any; + } & { + Platform: { + OS: string; + Version: number; + }; + }, + ) => void; +} + +const _core = require('./core'); + +module.exports = _core; diff --git a/packages/vx-primitives/src/modules/Platform.ts b/packages/vx-primitives/src/modules/Platform.ts index 4cba12db7..5f490a57a 100644 --- a/packages/vx-primitives/src/modules/Platform.ts +++ b/packages/vx-primitives/src/modules/Platform.ts @@ -5,7 +5,7 @@ type PlatformType = { Version: number; }; -const Platform: PlatformType & { select?: (Object) => void; inject?: (PlatformType) => void } = { +const Platform: PlatformType & { select?: (_: any) => void; inject?: (_: PlatformType) => void } = { OS: 'unknown', Version: 0, select: obj => { From 0ffdc6215b0b49625ce80c48990891b17c7da412 Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Fri, 3 Apr 2020 01:30:00 +0100 Subject: [PATCH 4/5] create vx-demo sketch example --- packages/vx-demo/src/sketch/README.md | 28 +++++++++ packages/vx-demo/src/sketch/package.json | 44 ++++++++++++++ packages/vx-demo/src/sketch/src/index.js | 56 +++++++++++++++++ packages/vx-demo/src/sketch/src/manifest.json | 17 ++++++ .../vx-demo/src/sketch/src/pages/Bars.jsx | 60 +++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 packages/vx-demo/src/sketch/README.md create mode 100644 packages/vx-demo/src/sketch/package.json create mode 100644 packages/vx-demo/src/sketch/src/index.js create mode 100644 packages/vx-demo/src/sketch/src/manifest.json create mode 100644 packages/vx-demo/src/sketch/src/pages/Bars.jsx diff --git a/packages/vx-demo/src/sketch/README.md b/packages/vx-demo/src/sketch/README.md new file mode 100644 index 000000000..36ce867be --- /dev/null +++ b/packages/vx-demo/src/sketch/README.md @@ -0,0 +1,28 @@ +# react-sketchapp vx demo + +Cross platform VX primitives used to render to `react-sketchapp` + +## How to use + +Download the example or [clone the repo](https://github.com/hshoff/vx): + +```bash +curl https://codeload.github.com/hshoff/vx/tar.gz/master | tar -xz --strip=2 vx-master/packages/vx-demo/src/sketch +cd sketch +``` + +Install the dependencies + +```bash +npm install +``` + +Then, open Sketch and navigate to `Plugins → vx-demo: Sketch` + +### Run it in Sketch + +Run with live reloading in Sketch + +```bash +npm run render +``` diff --git a/packages/vx-demo/src/sketch/package.json b/packages/vx-demo/src/sketch/package.json new file mode 100644 index 000000000..b7d2489c2 --- /dev/null +++ b/packages/vx-demo/src/sketch/package.json @@ -0,0 +1,44 @@ +{ + "name": "@vx/demo-sketch", + "private": true, + "version": "1.0.0", + "description": "", + "skpm": { + "main": "vx-demo.sketchplugin", + "manifest": "src/manifest.json" + }, + "scripts": { + "build": "skpm-build", + "watch": "skpm-build --watch", + "render": "skpm-build --watch --run", + "render:once": "skpm-build --run", + "postinstall": "npm run build && skpm-link" + }, + "author": "macintoshhelper", + "license": "MIT", + "devDependencies": { + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", + "@babel/plugin-proposal-class-properties": "^7.5.5", + "@babel/preset-env": "^7.5.5", + "@babel/preset-flow": "^7.0.0", + "@babel/preset-react": "^7.0.0", + "@skpm/builder": "^0.5.16" + }, + "dependencies": { + "chroma-js": "^1.2.2", + "d3-hierarchy": "^1.1.8", + "d3-scale": "^1.0.6", + "d3-shape": "^1.3.5", + "elemental-react": "^0.1.1", + "prop-types": "^15.5.8", + "react": "^16.9.0", + "react-primitives": "^0.6.0", + "react-primitives-svg": "0.0.3", + "react-sketchapp": "^3.1.1", + "react-test-renderer": "^16.9.0", + "styled-components": "^4.3.2", + "styled-system": "^5.1.1", + "topojson-client": "^3.0.0" + } +} diff --git a/packages/vx-demo/src/sketch/src/index.js b/packages/vx-demo/src/sketch/src/index.js new file mode 100644 index 000000000..c5d86425b --- /dev/null +++ b/packages/vx-demo/src/sketch/src/index.js @@ -0,0 +1,56 @@ +import React from 'react'; +import * as PropTypes from 'prop-types'; +import styled from 'styled-components/primitives'; +import { render, Document, Artboard, Page, Text, RedBox } from 'react-sketchapp'; +import { + width as styledWidth, + position, + space, + height as styledHeight, + color, +} from 'styled-system'; +import chroma from 'chroma-js'; +import { Svg, Platform } from '@vx/primitives'; + +import Bars from './pages/Bars'; + +const Screen = styled(Artboard)` + ${styledWidth} + ${position} + ${space} + ${styledHeight} + ${color} +`; + +Screen.defaultProps = { + width: 360, + position: 'relative', + ml: 0, +}; + +const DocumentContainer = ({ a }) => ( + + + + + + + +); + +DocumentContainer.propTypes = { + colors: PropTypes.objectOf(PropTypes.string).isRequired, +}; + +export default () => { + const data = context.document.documentData(); + const pages = context.document.pages(); + + data.setCurrentPage(pages.firstObject()); + + try { + render(); + } catch (err) { + render(); + } +}; diff --git a/packages/vx-demo/src/sketch/src/manifest.json b/packages/vx-demo/src/sketch/src/manifest.json new file mode 100644 index 000000000..a89b81e42 --- /dev/null +++ b/packages/vx-demo/src/sketch/src/manifest.json @@ -0,0 +1,17 @@ +{ + "compatibleVersion": 3, + "bundleVersion": 1, + "commands": [ + { + "name": "vx-demo: Sketch", + "identifier": "main", + "script": "./index.js" + } + ], + "menu": { + "isRoot": true, + "items": [ + "main" + ] + } +} diff --git a/packages/vx-demo/src/sketch/src/pages/Bars.jsx b/packages/vx-demo/src/sketch/src/pages/Bars.jsx new file mode 100644 index 000000000..cc556ad32 --- /dev/null +++ b/packages/vx-demo/src/sketch/src/pages/Bars.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Bar } from '@vx/shape'; +import { Group } from '@vx/group'; +import { GradientTealBlue } from '@vx/gradient'; +import { letterFrequency } from '@vx/mock-data'; +import { scaleBand, scaleLinear } from '@vx/scale'; + +import { Svg, Rect } from '@vx/primitives'; + +const data = letterFrequency.slice(5); + +// accessors +const x = d => d.letter; +const y = d => +d.frequency * 100; + +export default ({ width, height }) => { + // bounds + const xMax = width; + const yMax = height - 120; + + // scales + const xScale = scaleBand({ + rangeRound: [0, xMax], + domain: data.map(x), + padding: 0.4, + }); + const yScale = scaleLinear({ + rangeRound: [yMax, 0], + domain: [0, Math.max(...data.map(y))], + }); + + return ( + + + + + {data.map((d, i) => { + const letter = x(d); + const barWidth = xScale.bandwidth(); + const barHeight = yMax - yScale(y(d)); + const barX = xScale(letter); + const barY = yMax - barHeight; + return ( + { + // alert(`clicked: ${JSON.stringify(Object.values(d))}`); + // }} + /> + ); + })} + + + ); +}; From e8ab9d0b8a3a319709eb31195e0df0b2fe2c1ce2 Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Sat, 9 May 2020 04:04:22 +0100 Subject: [PATCH 5/5] Update README.md --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca216e53a..02b27d96d 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,22 @@

-### vx +### vx-primitives -vx is a collection of reusable low-level visualization components. vx combines the power of d3 to +vx-primitives is a collection of reusable cross-platform low-level visualization components. vx combines the power of d3 to generate your visualization with the benefits of react for updating the DOM. +This repository is a working fork of [hshoff/vx](https://github.com/hshoff/vx) that adds support for react-primitives platforms. + +You can find the working development branch at: https://github.com/elemental-design/vx-primitives/tree/develop + +Supported platforms: + +- [x] [React Web](https://github.com/facebook/react/tree/master/packages/react-dom) +- [x] [React Sketch.app](https://github.com/airbnb/react-sketchapp/) +- [ ] [React Native](https://github.com/facebook/react-native) +- [ ] [React Native Windows](https://github.com/Microsoft/react-native-windows) +