diff --git a/README.md b/README.md index b6cde27..f580976 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,15 @@ [![GPLv3 License](https://img.shields.io/github/license/kusti8/proton-native.svg)](https://github.com/kusti8/proton-native/blob/master/LICENSE) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/Proton-Native) - Create native desktop applications through a React syntax, on all platforms ## Features -- Same syntax as React Native -- Works with existing React libraries such as Redux -- Cross platform -- Native components. No more Electron -- Compatible with all normal Node.js packages +* Same syntax as React Native +* Works with existing React libraries such as Redux +* Cross platform +* Native components. No more Electron +* Compatible with all normal Node.js packages ## Images @@ -39,10 +38,10 @@ Examples can be found in `examples/`. All contributions are welcome. Just make a PR. Below is a list of general improvements that need to be added that I would love help with: -- [ ] More examples and improve documentation -- [ ] Add UiArea -- [ ] Clean up code -- [ ] Make packaging easier -- [x] Make creating a new app easier (something similar to `create-react-app`): https://github.com/albe-rosado/create-proton-app +* [ ] More examples and improve documentation +* [x] Add UiArea +* [ ] Clean up code +* [ ] Make packaging easier +* [x] Make creating a new app easier (something similar to `create-react-app`): https://github.com/albe-rosado/create-proton-app Accelerated by KeyCDN diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 3d36def..10b9159 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -5,6 +5,15 @@ * Components * [App](component_APIs/app.md) + * [Area](component_APIs/area.md) + * [Props](component_APIs/area_props.md) + * [Area.Group](component_APIs/area_group.md) + * [Area.Rectangle](component_APIs/area_rectangle.md) + * [Area.Line](component_APIs/area_line.md) + * [Area.Circle](component_APIs/area_circle.md) + * [Area.Arc](component_APIs/area_bezier.md) + * [Area.Bezier](component_APIs/area_bezier.md) + * [Area.Path](component_APIs/area_path.md) * [Box](component_APIs/box.md) * [Button](component_APIs/button.md) * [Checkbox](component_APIs/checkbox.md) diff --git a/docs/component_APIs/area.md b/docs/component_APIs/area.md new file mode 100644 index 0000000..0164909 --- /dev/null +++ b/docs/component_APIs/area.md @@ -0,0 +1,108 @@ +# Area + +A component onto which area components like rectangles or circles can be drawn. + +Some props can be applied to all area components (**including Area itself** and children): see [Area Props](area_props.md). + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area, Rectangle } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [visible](#visible) +* [onMouseMove](#onMouseMove) +* [onMouseUp](#onMouseUp) +* [onMouseDown](#onMouseDown) +* [onMouseEnter](#onMouseEnter) +* [onMouseLeave](#onMouseLeave) +* [onKeyUp](#onKeyUp) +* [onKeyDown](#onKeyDown) + +## Reference + +### visible + +Whether the area can be seen. + +| **Type** | **Required** | **Default** | +| -------- | ------------ | ----------- | +| bool | No | true | + +### onMouseMove + +Called when the mouse is moved over the area + +| **Type** | **Required** | +| ----------------------------------------------------------------------------------------- | ------------ | +| function({x: number, y: number, width: number, height: number, buttons: Array\}) | No | + +### onMouseUp + +**Not working at the moment** + +Called when releasing a mouse button over the area. + +| **Type** | **Required** | +| ------------------------------------------------------------------------------- | ------------ | +| function({x: number, y: number, width: number, height: number, button: number}) | No | + +### onMouseDown + +Called when pressing a mouse button over the area. + +| **Type** | **Required** | +| ------------------------------------------------------------------------------- | ------------ | +| function({x: number, y: number, width: number, height: number, button: number}) | No | + +### onMouseEnter + +Called when the mouse enters the area. + +| **Type** | **Required** | +| ---------- | ------------ | +| function() | No | + +### onMouseLeave + +Called when the mouse leaves the area. + +| **Type** | **Required** | +| ---------- | ------------ | +| function() | No | + +### onKeyUp + +Called when pressing a key. +Return `true` to signal that this event got handled (always returning true will disable any menu accelerators). + +| **Type** | **Required** | +| ------------------------------------------------------------------------------- | ------------ | +| function({key: string, extKey: number, modifierKey: number, modifiers: number}) | No | + +### onKeyDown + +Called when releasing a key. +Return `true` to signal that this event got handled (always returning true will disable any menu accelerators). + +| **Type** | **Required** | +| ------------------------------------------------------------------------------- | ------------ | +| function({key: string, extKey: number, modifierKey: number, modifiers: number}) | No | diff --git a/docs/component_APIs/area_arc.md b/docs/component_APIs/area_arc.md new file mode 100644 index 0000000..4fb212f --- /dev/null +++ b/docs/component_APIs/area_arc.md @@ -0,0 +1,83 @@ +# Area.Arc + +A circular arc to be displayed in an [Area](area.md) component. + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [x](#x) +* [y](#y) +* [r](#r) +* [start](#start) +* [sweep](#sweep) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### x + +The x coordinate of the center of the arc. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y + +The y coordinate of the center of the arc. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### r + +The arc's radius. Percentage values use the Area's width. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### start + +The start angle of the arc in degrees. Value increases clockwise with `0` meaning the rightmost point ("east") of the imaginary circle. + +| **Type** | **Required** | **Default** | +| ------------------------- | ------------ | ----------- | +| number \| string (number) | false | 0 | + +### sweep + +The sweep angle of the arc in degrees. Value increases clockwise. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | diff --git a/docs/component_APIs/area_bezier.md b/docs/component_APIs/area_bezier.md new file mode 100644 index 0000000..9a2f068 --- /dev/null +++ b/docs/component_APIs/area_bezier.md @@ -0,0 +1,114 @@ +# Area.Bezier + +A Bezier curve to be displayed in an [Area](area.md) component. + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [x1](#x1) +* [y1](#y1) +* [cx1](#cx1) +* [cy1](#cy1) +* [x2](#x2) +* [y2](#y2) +* [cx2](#cx2) +* [cy2](#cy2) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### x1 + +The x coordinate of the curve's start point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y1 + +The y coordinate of the curve's start point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### cx1 + +The x coordinate of the curve's control point at the start. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### cy1 + +The y coordinate of the curve's control point at the start. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### x2 + +The x coordinate of the curve's end point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y2 + +The y coordinate of the curve's end point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### cx2 + +The x coordinate of the curve's control point at the end. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### cy2 + +The y coordinate of the curve's control point at the end. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | diff --git a/docs/component_APIs/area_circle.md b/docs/component_APIs/area_circle.md new file mode 100644 index 0000000..d1e33b8 --- /dev/null +++ b/docs/component_APIs/area_circle.md @@ -0,0 +1,58 @@ +# Area.Circle + +A circle to be displayed in an [Area](area.md) component. + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [x](#x) +* [y](#y) +* [r](#r) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### x + +The x coordinate of the center of the cirle. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y + +The y coordinate of the center of the cirle. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### r + +The circle's radius. Percentage values use the Area's width. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | diff --git a/docs/component_APIs/area_group.md b/docs/component_APIs/area_group.md new file mode 100644 index 0000000..d908335 --- /dev/null +++ b/docs/component_APIs/area_group.md @@ -0,0 +1,44 @@ +# Area.Group + +A component to apply props to all it's children in an [Area](area.md) component. + +To be able to use percentage values in transforms, the props `width` and `height` need to be specified (they have no graphical effect). + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* All props listed in [Area Props](area_props.md) diff --git a/docs/component_APIs/area_line.md b/docs/component_APIs/area_line.md new file mode 100644 index 0000000..8f3ed1b --- /dev/null +++ b/docs/component_APIs/area_line.md @@ -0,0 +1,67 @@ +# Area.Line + +A straigt line to be displayed in an [Area](area.md) component. + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [x1](#x1) +* [y1](#y1) +* [x2](#x2) +* [y2](#y2) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### x1 + +The x coordinate of the line's start point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y1 + +The y coordinate of the line's start point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### x2 + +The x coordinate of the line's end point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y2 + +The y coordinate of the line's end point. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | diff --git a/docs/component_APIs/area_path.md b/docs/component_APIs/area_path.md new file mode 100644 index 0000000..21745ad --- /dev/null +++ b/docs/component_APIs/area_path.md @@ -0,0 +1,61 @@ +# Area.Path + +A component describing a path to be displayed in an [Area](area.md) component. + +To be able to use percentage values in transforms, the props `width` and `height` need to be specified (they have no graphical effect). + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [d](#d) +* [fillMode](#fillmode) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### d + +A string describing the path (uses SVG's path syntax, explanation [here](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths)). + +A warning is displayed whan an unimplemented shaped are used (Quadratic Beziers and Arcs). + +| **Type** | **Required** | +| -------- | ------------ | +| string | true | + +### fillMode + +Sets the methods how to determine wheter to fill a path. Explanation [here](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule). + +| **Type** | **Required** | +| -------------------------- | ------------ | +| enum('nonzero', 'evenodd') | true | diff --git a/docs/component_APIs/area_props.md b/docs/component_APIs/area_props.md new file mode 100644 index 0000000..9263076 --- /dev/null +++ b/docs/component_APIs/area_props.md @@ -0,0 +1,175 @@ +# Area Props + +Some props can be applied to Area itself and all Area components (for those the normal [universal props](universal_props.md) don't work). They get inherited to all corresponding children. The [AreaGroup](area_group.md) component can be used to apply these to multiple children. + +All color properties use the CSS color syntax. Examples: "red", "#070707", "#222", "rgba(255, 255, 0, 1)", "hsl(0, 100%, 50%)". + +* [fill](#fill) +* [fillOpacity](#fillopacity) +* [stroke](#stroke) +* [strokeOpacity](#strokeopacity) +* [strokeWidth](#strokewidth) +* [strokeLinecap](#strokelinecap) +* [strokeLinejoin](#strokelinejoin) +* [strokeMiterlimit](#strokemiterlimit) +* [transform](#transform) + * [rotate](#rotate) + * [translate](#translate) + * [scale](#scale) + * [skew](#skew) + * [matrix](#matrix) + +## fill + +The fill color for the component. + +| **Type** | **Required** | **Default** | +| -------------- | ------------ | ----------- | +| string (Color) | false | 'none' | + +## fillOpacity + +The opacity of the fill (between 0 and 1). Gets multiplied with the fill colors alpha value. + +| **Type** | **Required** | **Default** | +| ------------------------- | ------------ | ----------- | +| number \| string (number) | false | 1 | + +## stroke + +The stroke (line) color for the component. + +| **Type** | **Required** | **Default** | +| -------------- | ------------ | ----------- | +| string (Color) | false | 'none' | + +## strokeOpacity + +The opacity of the stroke (between 0 and 1). Gets multiplied with the stroke colors alpha value. + +| **Type** | **Required** | **Default** | +| ------------------------- | ------------ | ----------- | +| number \| string (number) | false | 1 | + +## strokeWidth + +| **Type** | **Required** | **Default** | +| ------------------------- | ------------ | ----------- | +| number \| string (number) | false | 1 | + +## strokeLinecap + +| **Type** | **Required** | **Default** | +| ------------------------------- | ------------ | ----------- | +| enum('flat', 'round', 'square') | false | 'flat' | + +## strokeLinejoin + +| **Type** | **Required** | **Default** | +| ------------------------------- | ------------ | ----------- | +| enum('miter', 'round', 'bevel') | false | 'miter' | + +## strokeMiterlimit + +How far to extend the stroke at a sharp corner when using `strokeLinejoin='miter'`, see [here](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit) for a more detailed explanation. + +| **Type** | **Required** | **Default** | +| ------------------------- | ------------ | ----------- | +| number \| string (number) | false | 10 | + +## transform + +List of transformations to apply to the component (are quite similar to SVG transformations). Example for multiple transformations: `transform="translate(100, 100) rotate(90)"`. +All x and y coordinates specified in a transformation are relative _to the component itself_, meaning that `translate(-50%, 0)` will translate the component by 50% of it's own width to left. + +| **Type** | **Required** | **Default** | +| -------- | ------------ | ----------- | +| string | false | '' | + +### rotate + +Rotates the component `a` degrees around the center `(xCenter, yCenter)`. + +Syntax: + +``` +rotate(a) +rotate(a, xCenter, yCenter) +``` + +| **Variable** | **Required** | **Default** | +| ------------ | ------------ | ----------- | +| xCenter | false | 50% | +| yCenter | false | 50% | + +### translate + +Moves the component `x` to the right and `t` down. + +Syntax: + +``` +translate(x) +translate(x, y) +``` + +| **Variable** | **Required** | **Default** | +| ------------ | ------------ | ----------- | +| x | true | | +| y | false | x | + +### scale + +Scales the component by `x` in the horizontal axis and by `y` in the vertical axis. The scale center is specified by `(xCenter, yCenter)`. + +Syntax: + +``` +scale(x) +scale(x, y) +scale(x, xCenter, yCenter) +scale(x, y, xCenter, yCenter) +``` + +| **Variable** | **Required** | **Default** | +| ------------ | ------------ | ----------- | +| x | true | 50% | +| y | false | y | +| xCenter | false | 50% | +| yCenter | false | 50% | + +### skew + +Skews the component by `x` degrees in the horizontal axis and by `y` degrees in the vertical axis. The center is specified by `(xCenter, yCenter)`. + +Syntax: + +``` +skew(x, y) +skew(x, y, xCenter, yCenter) +``` + +| **Variable** | **Required** | **Default** | +| ------------ | ------------ | ----------- | +| x | true | | +| y | true | | +| xCenter | false | 50% | +| yCenter | false | 50% | + +### matrix + +Applies a matrix transformation. (Overwrites all other transformations.) + +Syntax: + +``` +matrix(a, b, c, d, e, f) +``` + +Applies the matrix: + +``` +/ a b 0 \ +| c d 0 | +\ e f 1 / +``` diff --git a/docs/component_APIs/area_rectangle.md b/docs/component_APIs/area_rectangle.md new file mode 100644 index 0000000..cadae0b --- /dev/null +++ b/docs/component_APIs/area_rectangle.md @@ -0,0 +1,73 @@ +# Area.Rectangle + +A rectangle to be displayed in an [Area](area.md) component. + +```jsx +import React, { Component } from 'react'; + +import { render, Window, App, Area } from 'proton-native'; + +class Example extends Component { + render() { + return ( + + + + + + + + ); + } +} + +render(); +``` + +## Props + +* [x](#x) +* [y](#y) +* [width](#width) +* [height](#height) +* (All props listed in [Area Props](area_props.md)) + +## Reference + +### x + +The x coordinate of the rectangles top left corner. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### y + +The y coordinate of the rectangles top left corner. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### width + +The width of the rectangle. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | + +### height + +The height of the rectangle. + +| **Type** | **Required** | +| ------------------------- | ------------ | +| number \| string (number) | true | diff --git a/examples/area.js b/examples/area.js new file mode 100644 index 0000000..4089993 --- /dev/null +++ b/examples/area.js @@ -0,0 +1,180 @@ +import React, { Component } from 'react'; +import { render, Window, App, Box, Menu, Button, Slider, Area } from '../src/'; + +class Example extends Component { + state = { bool: false, val: 40, dragging: false, pos: { x: 50, y: 220 } }; + + render() { + return ( + + + + { + // console.log('up', e.key); + // return true; + // }} + // onKeyDown={e => { + // console.log('down', e.key); + // return true; + // }} + // onMouseEnter={() => console.log('enter')} + // onMouseLeave={() => console.log('leave')} + onMouseMove={e => { + if (e.buttons.includes(1)) { + if (this.state.dragging) + this.setState({ pos: { x: e.x, y: e.y } }); + } + }} + onMouseUp={e => { + this.setState({ dragging: false }); + }} + onMouseDown={e => { + if ( + Math.sqrt( + Math.pow(this.state.pos.x - e.x, 2) + + Math.pow(this.state.pos.y - e.y, 2) + ) < 40 + ) { + this.setState({ dragging: true }); + } + }} + strokeWidth="4" + > + + + + + + + + + + + + + + + + this.setState({ val: v })} + /> + + + + ); + } +} + +render(); diff --git a/package-lock.json b/package-lock.json index d88cab1..59f6ce6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "proton-native", - "version": "1.0.16", + "version": "1.0.17", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2775,6 +2775,7 @@ "ajv": { "version": "4.11.8", "bundled": true, + "dev": true, "optional": true, "requires": { "co": "4.6.0", @@ -2944,6 +2945,7 @@ "debug": { "version": "2.6.8", "bundled": true, + "dev": true, "optional": true, "requires": { "ms": "2.0.0" @@ -3001,6 +3003,7 @@ "form-data": { "version": "2.1.4", "bundled": true, + "dev": true, "optional": true, "requires": { "asynckit": "0.4.0", @@ -3095,6 +3098,7 @@ "har-validator": { "version": "4.2.1", "bundled": true, + "dev": true, "optional": true, "requires": { "ajv": "4.11.8", @@ -3110,6 +3114,7 @@ "hawk": { "version": "3.1.3", "bundled": true, + "dev": true, "requires": { "boom": "2.10.1", "cryptiles": "2.0.5", @@ -3156,6 +3161,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "dev": true, "requires": { "number-is-nan": "1.0.1" } @@ -3234,17 +3240,20 @@ "assert-plus": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true } } }, "mime-db": { "version": "1.27.0", - "bundled": true + "bundled": true, + "dev": true }, "mime-types": { "version": "2.1.15", "bundled": true, + "dev": true, "requires": { "mime-db": "1.27.0" } @@ -3252,17 +3261,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "dev": true, "requires": { "brace-expansion": "1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "dev": true }, "mkdirp": { "version": "0.5.1", "bundled": true, + "dev": true, "requires": { "minimist": "0.0.8" } @@ -3270,6 +3282,7 @@ "ms": { "version": "2.0.0", "bundled": true, + "dev": true, "optional": true }, "node-pre-gyp": { @@ -3294,6 +3307,7 @@ "nopt": { "version": "4.0.1", "bundled": true, + "dev": true, "optional": true, "requires": { "abbrev": "1.1.0", @@ -3314,16 +3328,19 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "dev": true }, "oauth-sign": { "version": "0.8.2", "bundled": true, + "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", "bundled": true, + "dev": true, "optional": true }, "once": { @@ -3337,16 +3354,19 @@ "os-homedir": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "osenv": { "version": "0.1.4", "bundled": true, + "dev": true, "optional": true, "requires": { "os-homedir": "1.0.2", @@ -3355,30 +3375,36 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true + "bundled": true, + "dev": true }, "performance-now": { "version": "0.2.0", "bundled": true, + "dev": true, "optional": true }, "process-nextick-args": { "version": "1.0.7", - "bundled": true + "bundled": true, + "dev": true }, "punycode": { "version": "1.4.1", "bundled": true, + "dev": true, "optional": true }, "qs": { "version": "6.4.0", "bundled": true, + "dev": true, "optional": true }, "rc": { "version": "1.2.1", "bundled": true, + "dev": true, "optional": true, "requires": { "deep-extend": "0.4.2", @@ -3390,6 +3416,7 @@ "minimist": { "version": "1.2.0", "bundled": true, + "dev": true, "optional": true } } @@ -3448,26 +3475,31 @@ }, "safe-buffer": { "version": "5.0.1", - "bundled": true + "bundled": true, + "dev": true }, "semver": { "version": "5.3.0", "bundled": true, + "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", "bundled": true, + "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", "bundled": true, + "dev": true, "optional": true }, "sntp": { "version": "1.0.9", "bundled": true, + "dev": true, "requires": { "hoek": "2.16.3" } @@ -3492,6 +3524,7 @@ "assert-plus": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true } } @@ -3499,6 +3532,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "dev": true, "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", @@ -3508,6 +3542,7 @@ "string_decoder": { "version": "1.0.1", "bundled": true, + "dev": true, "requires": { "safe-buffer": "5.0.1" } @@ -3515,11 +3550,13 @@ "stringstream": { "version": "0.0.5", "bundled": true, + "dev": true, "optional": true }, "strip-ansi": { "version": "3.0.1", "bundled": true, + "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -3527,6 +3564,7 @@ "strip-json-comments": { "version": "2.0.1", "bundled": true, + "dev": true, "optional": true }, "tar": { @@ -3558,6 +3596,7 @@ "tough-cookie": { "version": "2.3.2", "bundled": true, + "dev": true, "optional": true, "requires": { "punycode": "1.4.1" @@ -7935,6 +7974,12 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "svg-path-parser": { + "version": "1.1.0", + "resolved": + "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz", + "integrity": "sha1-4WtLOd8NKw056DR9t5/doUU6YEY=" + }, "symbol-observable": { "version": "0.2.4", "resolved": diff --git a/package.json b/package.json index 3f5ffd9..8b76b97 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "prop-types": "^15.6.1", "react": "^16.3.0", "react-dom": "^16.3.0", - "react-reconciler": "^0.7.0" + "react-reconciler": "^0.7.0", + "svg-path-parser": "^1.1.0" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/src/components/Area.js b/src/components/Area.js new file mode 100644 index 0000000..71916f5 --- /dev/null +++ b/src/components/Area.js @@ -0,0 +1,694 @@ +import DesktopComponent, { + universalPropTypes, + universalDefaultProps, +} from './DesktopComponent'; +import { Component } from 'react'; +import libui from 'libui-node'; +import PropTypes from 'prop-types'; +import Color from 'color'; +import parseSVG from 'svg-path-parser'; + +class Area extends DesktopComponent { + constructor(root, props) { + super(root, props); + this.root = root; + this.props = { ...props }; + this.setDefaults(props); + + this.element = new libui.UiArea( + (area, p) => { + for (let i = 0; i < this.children.length; i += 1) { + if (typeof this.children[i] === 'object') { + this.children[i].render(this, area, p); + } + } + }, + (area, evt) => { + const down = evt.getDown(); + const up = 0; //evt.getUp(); + if (up) { + this.props.onMouseUp({ + x: evt.getX(), + y: evt.getY(), + width: evt.getAreaWidth(), + height: evt.getAreaHeight(), + button: up, + }); + } else if (down) { + this.props.onMouseDown({ + x: evt.getX(), + y: evt.getY(), + width: evt.getAreaWidth(), + height: evt.getAreaHeight(), + button: down, + }); + } else { + const buttons = []; + const held = evt.getHeld1To64(); + if (held > 0) { + for (let i = 0; i <= 6; i++) { + if (held & Math.pow(2, i)) buttons.push(i + 1); + if (!(held >> (i + 1))) break; + } + } + this.props.onMouseMove({ + x: evt.getX(), + y: evt.getY(), + width: evt.getAreaWidth(), + height: evt.getAreaHeight(), + buttons, + }); + } + }, + (area, inOut) => { + if (inOut === 0) { + this.props.onMouseEnter(); + } else { + this.props.onMouseLeave(); + } + }, + function dragBroken() {}, + (area, event) => { + if (event.getUp()) { + return this.props.onKeyUp({ + key: event.getKey(), + extKey: event.getExtKey(), + modifierKey: event.getModifier(), + modifiers: event.getModifiers(), + }); + } else { + return this.props.onKeyDown({ + key: event.getKey(), + extKey: event.getExtKey(), + modifierKey: event.getModifier(), + modifiers: event.getModifiers(), + }); + } + } + ); + } + + getArea() { + return this.element; + } + + // to prevent TypeError: Cannot read property 'undefined' of undefined + // because onMouseMove, ... shouldn't be handled by DesktopComponent + update(oldProps, newProps) {} + + render(parent) { + this.addParent(parent); + } +} + +Area.PropTypes = { + ...universalPropTypes, + onMouseMove: PropTypes.func, + onMouseUp: PropTypes.func, + onMouseDown: PropTypes.func, + onMouseEnter: PropTypes.func, + onMouseLeave: PropTypes.func, + onKeyUp: PropTypes.func, + onKeyDown: PropTypes.func, + children: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.arrayOf(PropTypes.element), + ]), +}; + +Area.defaultProps = { + ...universalDefaultProps, + onMouseMove: e => {}, + onMouseUp: e => {}, + onMouseDown: e => {}, + onMouseEnter: area => {}, + onMouseLeave: area => {}, + onKeyUp: (area, event) => {}, + onKeyDown: (area, event) => {}, +}; + +function fallback(...vals) { + let func = a => Number(a); + if (typeof vals[vals.length - 1] === 'function') { + func = vals.pop(); + } + + for (let v of vals) { + if (typeof v !== 'undefined') { + return func(v); + } + } +} + +function createBrush(color, alpha) { + const brush = new libui.DrawBrush(); + brush.color = new libui.Color( + color.red() / 255, + color.green() / 255, + color.blue() / 255, + color.alpha() * alpha + ); + brush.type = 0 /*uiDrawBrushTypeSolid*/; + + return brush; +} + +class AreaComponent { + constructor(root, props) { + this.root = root; + this.props = { ...props }; + this.setDefaults(props); + } + + setDefaults(props) { + for (let prop in this.constructor.defaultProps) { + if (!(prop in props) || typeof props[prop] === 'undefined') { + // children can exist, but be undefined + this.props[prop] = this.constructor.defaultProps[prop]; + } + } + PropTypes.checkPropTypes( + this.constructor.PropTypes, + this.props, + 'prop', + this.constructor.name + ); + } + + getArea() { + return this.parent.getArea(); + } + + update(oldProps, newProps) { + this.props = { ...this.props, ...newProps }; + if (this.parent) this.getArea().queueRedrawAll(); + } + + getWidth(p) { + return this.props.width ? this.parseParent(this.props.width, p) : 0; + } + + getHeight(p) { + return this.props.width ? this.parseParent(this.props.height, p, true) : 0; + } + + // parse numbers (especially percentages with respect to the parent) + parseParent(val, p, y = false) { + if (typeof val === 'string') { + let num = Number(val); + if (num == val) { + return num; + } else if (val.slice(-1) == '%') { + let num = Number(val.slice(0, -1)); + return num / 100 * (y ? p.getAreaHeight() : p.getAreaWidth()); + } + } else if (typeof val === 'number') { + return val; + } + } + + // parse numbers (especially percentages with respect to itself) + parseSelf(val, p, y = false) { + if (typeof val === 'string') { + let num = Number(val); + if (num == val) { + return num; + } else if (val.slice(-1) == '%') { + let num = Number(val.slice(0, -1)); + return num / 100 * (y ? this.getHeight(p) : this.getWidth(p)); + } + } else if (typeof val === 'number') { + return val; + } + } + + // translates coordinates relative to this component into the area coordinate system + selfToParent(xx, yy, p) { + // get top left corner + let x = 0; + let y = 0; + if (this.props.x) { + x = this.parseParent(this.props.x, p); + } else if (this.props.x1 && this.props.x2) { + const realX1 = this.parseParent(this.props.x1, p); + const realX2 = this.parseParent(this.props.x2, p); + x = realX1 < realX2 ? realX1 : realX2; + } + if (this.props.y) { + y = this.parseParent(this.props.y, p, true); + } else if (this.props.y1 && this.props.y2) { + const realY1 = this.parseParent(this.props.y1, p, true); + const realY2 = this.parseParent(this.props.y2, p, true); + y = realY1 < realY2 ? realY1 : realY2; + } + + return { + x: x + this.parseSelf(xx, p), + y: y + this.parseSelf(yy, p, true), + }; + } + + render(parent, area, p, props) { + this.parent = parent; + const { children, ...appendProps } = this.props; + props = { ...props, ...appendProps }; + + if (this.props.transform) { + p.getContext().save(); + + const mat = new libui.UiDrawMatrix(); + mat.setIdentity(); + + for (const transform of this.props.transform.match(/\w+\([^)]+\)/g)) { + // rotate(deg [,x, y]) + // default x: 50%, y: 50% + const rotate = transform.match( + /rotate\s*\(\s*([-0-9.]+)(?:\s*,\s*([-0-9.%]+)\s*,\s*([-0-9.%]+))?\s*\)/ + ); + if (rotate) { + const xy = this.selfToParent( + fallback(rotate[2], '50%', v => v), + fallback(rotate[3], '50%', v => v), + p + ); + const rad = Number(rotate[1]) * (Math.PI / 180); + mat.rotate(xy.x, xy.y, rad); + } + + // translate(x [y]) + // default y: x + const translate = transform.match( + /translate\s*\(\s*([-0-9.%]+)(?:\s*,\s*([-0-9.%]+))?\s*\)/ + ); + if (translate) { + mat.translate( + this.parseSelf(translate[1], p), + fallback(translate[2], translate[1], v => + this.parseSelf(v, p, true) + ) + ); + } + + // 1: scale(x) + // 2: scale(x, y) + // 3: scale(x, xCenter, yCenter) + // 4: scale(x, y, xCenter, yCenter) + // default y: x, xCenter=yCenter: 50% + const scale = transform.match( + /scale\s*\(([-0-9.]+)(?:(?:\s*,\s*([-0-9.]+))?(?:\s*,\s*([-0-9.%]+)\s*,\s*([-0-9.%]+))?)?\)/ + ); + if (scale) { + const xy = this.selfToParent( + fallback(scale[3], '50%', v => v), + fallback(scale[4], '50%', v => v), + p + ); + if (process.platform === 'win32') { + mat.scale(xy.x, xy.y, scale[1], fallback(scale[2], scale[1])); + } else { + // https://github.com/andlabs/libui/issues/331: + mat.translate(xy.x, xy.y); + mat.scale(0, 0, scale[1], fallback(scale[2], scale[1])); + mat.translate(-xy.x, -xy.y); + } + } + + // skew(a, b [,x, y]) + // a, b: x/y angle + // default x=y: 50% + const skew = transform.match( + /skew\s*\(\s*([-0-9.]+)\s*,\s*([-0-9.]+)(?:,\s*([-0-9.%]+),\s*([-0-9.%]+))?\)/ + ); + if (skew) { + const rad1 = Number(skew[1]) * (Math.PI / 180); + const rad2 = Number(skew[2]) * (Math.PI / 180); + mat.skew( + fallback(skew[2], '50%', v => this.parseSelf(v, p)), + fallback(skew[3], '50%', v => this.parseSelf(v, p, true)), + rad1, + rad2 + ); + } + + // matrix(a, b, c, d, e, f, g) + const matrix = transform.match( + /matrix\s*\(\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*,\s*([-0-9.]+)\s*\)/ + ); + if (matrix) { + mat.setM11(matrix[1]); + mat.setM12(matrix[2]); + mat.setM21(matrix[3]); + mat.setM22(matrix[4]); + mat.setM31(matrix[5]); + mat.setM32(matrix[6]); + } + } + + p.getContext().transform(mat); + } + + const path = this.draw(area, p, props); + + if (path) { + const fillBrush = + props.fill && + props.fill != 'none' && + createBrush(Color(props.fill), Number(props.fillOpacity)); + const strokeBrush = + props.stroke && + props.stroke != 'none' && + createBrush(Color(props.stroke), Number(props.strokeOpacity)); + + if (strokeBrush) { + const sp = new libui.DrawStrokeParams(); + + switch (props.strokeLinecap) { + case 'flat': + sp.cap = 0; + break; + case 'round': + sp.cap = 1; + break; + case 'square': + sp.cap = 2; + break; + } + + switch (props.strokeLinejoin) { + case 'miter': + sp.join = 0; + break; + case 'round': + sp.join = 1; + break; + case 'bevel': + sp.join = 2; + break; + } + + sp.thickness = Number(props.strokeWidth); + sp.miterLimit = Number(props.strokeMiterlimit); + + p.getContext().stroke(path, strokeBrush, sp); + + //sp.free(); + //strokBrush.free(); + } + + if (fillBrush) { + p.getContext().fill(path, fillBrush); + //fillBrush.free(); + } + + path.freePath(); + } + + if (this.props.transform) { + p.getContext().restore(); + } + } + + draw(area, p) {} +} + +const AreaComponentPropTypes = { + transform: PropTypes.string, + fill: PropTypes.string, + fillOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + stroke: PropTypes.string, + strokeOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + strokeWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + strokeLinecap: PropTypes.oneOf(['flat', 'round', 'square']), + strokeLinejoin: PropTypes.oneOf(['miter', 'round', 'bevel']), + strokeMiterlimit: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), +}; + +const AreaComponentDefaultProps = { + fillOpacity: 1, + strokeOpacity: 1, + strokeWidth: 1, + strokeMiterlimit: 10, + strokeLinecap: 'flat', + strokeLinejoin: 'miter', +}; + +Area.Group = class AreaGroup extends AreaComponent { + constructor(root, props) { + super(root, props); + this.children = []; + } + + appendChild(child) { + this.children.push(child); + } + + draw(area, p, props) { + for (let i = 0; i < this.children.length; i += 1) { + if (typeof this.children[i] === 'object') { + this.children[i].render(this, area, p, props); + } + } + } +}; + +Area.Group.PropTypes = { + ...AreaComponentPropTypes, + width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), +}; + +Area.Rectangle = class Rectangle extends AreaComponent { + getWidth(p) { + return this.parseParent(this.props.width, p); + } + + getHeight(p) { + return this.parseParent(this.props.height, p, true); + } + + draw(area, p) { + const path = new libui.UiDrawPath(0 /*uiDrawFillModeWinding*/); + path.addRectangle( + this.parseParent(this.props.x, p), + this.parseParent(this.props.y, p, true), + this.parseParent(this.props.width, p), + this.parseParent(this.props.height, p, true) + ); + path.end(); + return path; + } +}; + +Area.Rectangle.PropTypes = { + ...AreaComponentPropTypes, + x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +Area.Line = class Line extends AreaComponent { + getWidth(p) { + return Math.abs( + this.parseParent(this.props.x2, p) - this.parseParent(this.props.x1, p) + ); + } + + getHeight(p) { + return Math.abs( + this.parseParent(this.props.y2, p, true) - + this.parseParent(this.props.y1, p, true) + ); + } + + draw(area, p) { + const path = new libui.UiDrawPath(0 /*uiDrawFillModeWinding*/); + path.newFigure( + this.parseParent(this.props.x1, p), + this.parseParent(this.props.y1, p, true) + ); + path.lineTo( + this.parseParent(this.props.x2, p), + this.parseParent(this.props.y2, p, true) + ); + path.end(); + + return path; + } +}; + +Area.Line.PropTypes = { + ...AreaComponentPropTypes, + x1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + x2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +Area.Arc = class Arc extends AreaComponent { + getWidth(p) { + return 2 * this.parseParent(this.props.r, p); + } + + getHeight(p) { + return getWidth(p); + } + + draw(area, p) { + const path = new libui.UiDrawPath(0 /*uiDrawFillModeWinding*/); + path.newFigureWithArc( + this.parseParent(this.props.x, p), + this.parseParent(this.props.y, p, true), + this.parseParent(this.props.r, p), + Number(this.props.start) * (Math.PI / 180), + Number(this.props.sweep) * (Math.PI / 180), + false + ); + path.end(); + return path; + } +}; + +Area.Arc.PropTypes = { + ...AreaComponentPropTypes, + x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + r: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + start: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + sweep: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +Area.Arc.defaultProps = { + ...AreaComponentDefaultProps, + start: 0, +}; + +Area.Circle = class Circle extends AreaComponent { + getWidth(p) { + return 2 * this.parseParent(this.props.r, p); + } + + getHeight(p) { + return getWidth(p); + } + + draw(area, p) { + const path = new libui.UiDrawPath(0 /*uiDrawFillModeWinding*/); + path.newFigureWithArc( + this.parseParent(this.props.x, p), + this.parseParent(this.props.y, p, true), + this.parseParent(this.props.r, p), + 0, + 2 * Math.PI, + false + ); + path.end(); + return path; + } +}; + +Area.Circle.PropTypes = { + ...AreaComponentPropTypes, + x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + r: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +Area.Circle.defaultProps = { + ...AreaComponentDefaultProps, +}; + +Area.Bezier = class Bezier extends AreaComponent { + draw(area, p) { + const path = new libui.UiDrawPath(0 /*uiDrawFillModeWinding*/); + path.newFigure( + this.parseParent(this.props.x1, p), + this.parseParent(this.props.y1, p, true) + ); + path.bezierTo( + this.parseParent(this.props.cx1, p), + this.parseParent(this.props.cy1, p, true), + this.parseParent(this.props.cx2, p), + this.parseParent(this.props.cy2, p, true), + this.parseParent(this.props.x2, p), + this.parseParent(this.props.y2, p, true) + ); + path.end(); + + return path; + } +}; + +Area.Bezier.PropTypes = { + ...AreaComponentPropTypes, + x1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + cx1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + cy1: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + x2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + y2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + cx2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + cy2: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +Area.Path = class Path extends AreaComponent { + draw(area, p) { + const path = new libui.UiDrawPath( + this.props.fillRule === 'evenodd' ? 1 : 0 + ); + const commands = parseSVG(this.props.d); + parseSVG.makeAbsolute(commands); + + for (let i = 0; i < commands.length; i++) { + const c = commands[i]; + switch (c.command) { + case 'moveto': + path.newFigure(c.x, c.y); + break; + + case 'lineto': + case 'horizontal lineto': + case 'vertical lineto': + path.lineTo(c.x, c.y); + break; + + case 'curveto': + path.bezierTo(c.x1, c.y1, c.x2, c.y2, c.x, c.y); + break; + + case 'smooth curveto': + //uses point from previous curve + const x1 = c.x0 - (commands[i - 1].x2 - c.x0); + const y1 = c.y0 - (commands[i - 1].y2 - c.y0); + path.bezierTo(x1, y1, c.x2, c.y2, c.x, c.y); + break; + + case 'closepath': + path.closeFigure(); + break; + + default: + // 'quadratic curveto', 'elliptical arc' + throw new Error( + 'Not implemented in Area.Path - ' + c.code + ': ' + c.command + ); + } + } + + path.end(); + + return path; + } +}; + +Area.Path.PropTypes = { + ...AreaComponentPropTypes, + d: PropTypes.string.isRequired, + fillRule: PropTypes.oneOf(['nonzero', 'evenodd']), +}; + +Area.Path.defaultProps = { + fillRule: 'nonzero', +}; + +export default Area; diff --git a/src/components/index.js b/src/components/index.js index 856fdd6..f4b80fb 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -25,6 +25,7 @@ import ProgressBar from './ProgressBar'; import MenuBar from './MenuBar'; import FontButton from './FontButton'; import Dialog from './Dialog'; +import Area from './Area'; export { Root, @@ -54,4 +55,5 @@ export { MenuBar, FontButton, Dialog, + Area, }; diff --git a/src/index.js b/src/index.js index 3f52c58..d9263ee 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import { RadioButtons, Separator, Menu, + Area, } from './react-components'; import { Dialog } from './components'; @@ -38,6 +39,14 @@ const ProgressBar = 'PROGRESSBAR'; const MenuBar = 'MENUBAR'; const MenuBarItem = 'MENUBARITEM'; const FontButton = 'FONTBUTTON'; +const AreaInternal = 'AREA'; +Area.Rectangle = 'AREARECTANGLE'; +Area.Line = 'AREALINE'; +Area.Arc = 'AREAARC'; +Area.Bezier = 'AREABEZIER'; +Area.Path = 'AREAPATH'; +Area.Group = 'AREAGROUP'; +Area.Circle = 'AREACIRCLE'; export { render, @@ -76,4 +85,6 @@ export { Menu, FontButton, Dialog, + AreaInternal, + Area, }; diff --git a/src/react-components/Area.js b/src/react-components/Area.js new file mode 100644 index 0000000..6fcda06 --- /dev/null +++ b/src/react-components/Area.js @@ -0,0 +1,79 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { AreaInternal } from '../'; + +const AreaComponentPropTypes = { + transform: PropTypes.string, + fill: PropTypes.string, + fillOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + stroke: PropTypes.string, + strokeOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + strokeWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + strokeLinecap: PropTypes.oneOf(['flat', 'round', 'square']), + strokeLinejoin: PropTypes.oneOf(['miter', 'round', 'bevel']), + strokeMiterlimit: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), +}; + +const AreaComponentDefaultProps = { + fillOpacity: 1, + strokeOpacity: 1, + strokeWidth: 1, + strokeMiterlimit: 10, + strokeLinecap: 'flat', + strokeLinejoin: 'miter', +}; + +class Area extends Component { + render() { + const { + children, + stretchy, + label, + column, + row, + span, + expand, + align, + onMouseMove, + onMouseUp, + onMouseDown, + onMouseEnter, + onMouseLeave, + onKeyUp, + onKeyDown, + ...groupProps + } = this.props; + const areaProps = { + children, + stretchy, + label, + column, + row, + span, + expand, + align, + onMouseMove, + onMouseUp, + onMouseDown, + onMouseEnter, + onMouseLeave, + onKeyUp, + onKeyDown, + }; + return React.createElement( + AreaInternal, + areaProps, + React.createElement(Area.Group, groupProps, children) + ); + } +} + +Area.propTypes = { + ...AreaComponentPropTypes, +}; + +Area.defaultProps = { + ...AreaComponentDefaultProps, +}; + +export default Area; diff --git a/src/react-components/index.js b/src/react-components/index.js index b747608..a6b1765 100644 --- a/src/react-components/index.js +++ b/src/react-components/index.js @@ -4,5 +4,6 @@ import Picker from './Picker'; import RadioButtons from './RadioButtons'; import Separator from './Separator'; import Menu from './Menu'; +import Area from './Area'; -export { Box, TextInput, Picker, RadioButtons, Separator, Menu }; +export { Box, TextInput, Picker, RadioButtons, Separator, Menu, Area }; diff --git a/src/utils/createElement.js b/src/utils/createElement.js index f3b1dca..cbe3421 100644 --- a/src/utils/createElement.js +++ b/src/utils/createElement.js @@ -25,6 +25,7 @@ import { ProgressBar, MenuBar, FontButton, + Area, } from '../components/'; import { ROOT_NODE } from '../render/'; @@ -64,6 +65,14 @@ function createElement(type, props) { MENUBAR: () => new MenuBar(ROOT_NODE, props), MENUBARITEM: () => new MenuBar.Item(ROOT_NODE, props), FONTBUTTON: () => new FontButton(ROOT_NODE, props), + AREA: () => new Area(ROOT_NODE, props), + AREARECTANGLE: () => new Area.Rectangle(ROOT_NODE, props), + AREALINE: () => new Area.Line(ROOT_NODE, props), + AREAARC: () => new Area.Arc(ROOT_NODE, props), + AREABEZIER: () => new Area.Bezier(ROOT_NODE, props), + AREAPATH: () => new Area.Path(ROOT_NODE, props), + AREAGROUP: () => new Area.Group(ROOT_NODE, props), + AREACIRCLE: () => new Area.Circle(ROOT_NODE, props), default: undefined, };