Skip to content

Commit 337192f

Browse files
committed
feat(package): initial commit
1 parent 70dada3 commit 337192f

File tree

8 files changed

+470
-24
lines changed

8 files changed

+470
-24
lines changed

.flowconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
[include]
77
./src
8+
./stories
89
./test
910

1011
[libs]

README.md

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# react-svg-arrow
1+
# react-arrow
22

3-
[![Build Status](https://travis-ci.org/jcoreio/react-svg-arrow.svg?branch=master)](https://travis-ci.org/jcoreio/react-svg-arrow)
4-
[![Coverage Status](https://coveralls.io/repos/github/jcoreio/react-svg-arrow/badge.svg?branch=master)](https://coveralls.io/github/jcoreio/react-svg-arrow?branch=master)
3+
[![Build Status](https://travis-ci.org/jcoreio/react-arrow.svg?branch=master)](https://travis-ci.org/jcoreio/react-arrow)
4+
[![Coverage Status](https://coveralls.io/repos/github/jcoreio/react-arrow/badge.svg?branch=master)](https://coveralls.io/github/jcoreio/react-arrow?branch=master)
55
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
66
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
77

@@ -10,6 +10,97 @@ a simple svg arrow component
1010
## Usage
1111

1212
```sh
13-
npm install --save react-svg-arrow
13+
npm install --save react-arrow
1414
```
1515

16+
```js
17+
import React from 'react'
18+
import Arrow from 'react-arrow'
19+
20+
const element = (
21+
<Arrow
22+
direction="down"
23+
shaftWidth={10}
24+
shaftLength={10}
25+
headWidth={30}
26+
headLength={15}
27+
fill="blue"
28+
stroke="red"
29+
strokeWidth={2}
30+
onClick={() => alert('You clicked the arrow!')}
31+
/>
32+
)
33+
```
34+
35+
## Geometry
36+
37+
```
38+
<----- head width ----->
39+
^ ^
40+
/ \ |
41+
/ \ |
42+
/ \ |
43+
/ \ |
44+
/ \
45+
/ \ head length
46+
/ \
47+
/ \ |
48+
/ \ |
49+
/ \ |
50+
/ \ |
51+
/___ ___\ v
52+
| | ^
53+
| | |
54+
| | |
55+
| | |
56+
| |
57+
| | shaft length
58+
| |
59+
| | |
60+
| | |
61+
| | |
62+
|_______________| v
63+
<- shaft width ->
64+
```
65+
66+
## Props
67+
68+
##### `direction: 'left' | 'right' | 'up' | 'down'` **(required)**
69+
70+
The direction you want the arrow to point
71+
72+
##### `shaftWidth: number` **(required)**
73+
74+
The width of the arrow's shaft
75+
76+
##### `shaftLength: number` **(required)**
77+
78+
The length of the arrow's shaft
79+
80+
##### `headWidth: number` **(required)**
81+
82+
The width of the arrow's head (from one side of the triangle's base to the other)
83+
84+
##### `headLength: number` **(required)**
85+
86+
The length of the arrow's head (from the base to the tip of the triangle)
87+
88+
##### `unclosed: boolean`
89+
90+
If `true`, the path will be left unclosed.
91+
92+
### Other SVG props
93+
94+
*Most* props applicable to the `<path>` element will be passed down to it. Any other props besides the aforementioned
95+
props will be passed down to the `<svg>` element. For the list of props that will be passed to the `<path>` element,
96+
see `pathProps` in the source code.
97+
98+
### `stroke` note
99+
100+
If you provide a `stroke`, `Arrow` simulates the proposed `stroke-alignment: inside` property by doubling the
101+
`strokeWidth` and applying a `clip-path` with a unique id.
102+
103+
### `transform` note
104+
105+
`Arrow` doesn't currently go to the trouble to pick the correct `viewBox`, `style.width`, and `style.height` to fit the
106+
arrow if you provide a `transform` property.

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "react-svg-arrow",
2+
"name": "react-arrow",
33
"version": "0.0.0-development",
44
"description": "a simple svg arrow component",
55
"main": "lib/index.js",
@@ -9,7 +9,7 @@
99
"lint:watch": "esw --watch src test stories",
1010
"flow": "flow",
1111
"flow:coverage": "for file in src/**.js test/**.js; do echo $file; flow coverage $file; done",
12-
"flow:watch": "flow-watch -e js,js.flow,flowconfig --ignore lib/ --ignore node_modules/ --watch .flowconfig --watch src/ --watch test/",
12+
"flow:watch": "flow-watch -e js,js.flow,flowconfig --ignore lib/ --ignore node_modules/ --watch .flowconfig --watch src/ --watch stories/ --watch test/",
1313
"gen-flow-files": "flow gen-flow-files src/ --out-dir lib",
1414
"copy-flow-files": "cd src; copy *.js.flow **/*.js.flow ../lib",
1515
"build": "rimraf lib && babel src --out-dir lib",
@@ -42,7 +42,7 @@
4242
},
4343
"repository": {
4444
"type": "git",
45-
"url": "https://github.com/jcoreio/react-svg-arrow.git"
45+
"url": "https://github.com/jcoreio/react-arrow.git"
4646
},
4747
"keywords": [
4848
"es2015",
@@ -52,9 +52,9 @@
5252
"author": "Andy Edwards",
5353
"license": "MIT",
5454
"bugs": {
55-
"url": "https://github.com/jcoreio/react-svg-arrow/issues"
55+
"url": "https://github.com/jcoreio/react-arrow/issues"
5656
},
57-
"homepage": "https://github.com/jcoreio/react-svg-arrow#readme",
57+
"homepage": "https://github.com/jcoreio/react-arrow#readme",
5858
"devDependencies": {
5959
"@jedwards1211/eslint-config-flow": "^1.0.0",
6060
"@jedwards1211/eslint-config-react": "^2.0.1",
@@ -100,6 +100,7 @@
100100
"react": "0.14.x || ^15.0.0"
101101
},
102102
"dependencies": {
103+
"isotrope": "^1.0.0",
103104
"prop-types": "^15.0.0"
104105
}
105-
}
106+
}

src/index.js

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,140 @@
1-
/* @flow */
1+
// @flow
22

33
import React from 'react'
44

5-
const Hello = () => <div>Hello world!</div>
5+
import type {Direction, Side, Axis} from 'isotrope'
6+
import {oppositeOf, signumOf, sideInDirection, axisFor, loSide, hiSide} from 'isotrope'
7+
8+
type Props = {
9+
shaftWidth: number,
10+
shaftLength: number,
11+
headWidth: number,
12+
headLength: number,
13+
direction: 'left' | 'right' | 'up' | 'down',
14+
unclosed?: boolean,
15+
transform?: string,
16+
}
17+
18+
const ownProps: {[key: string]: boolean} = {
19+
shaftWidth: true,
20+
shaftLength: true,
21+
headWidth: true,
22+
headLength: true,
23+
direction: true,
24+
unclosed: true,
25+
transform: true,
26+
}
27+
28+
const pathProps: {[key: string]: boolean} = {
29+
alignmentBaseline: true,
30+
baselineShift: true,
31+
clip: true,
32+
clipPath: true,
33+
clipRule: true,
34+
color: true,
35+
cursor: true,
36+
direction: true,
37+
display: true,
38+
dominantBaseline: true,
39+
enableBackground: true,
40+
fill: true,
41+
fillOpacity: true,
42+
filter: true,
43+
floodColor: true,
44+
floodOpacity: true,
45+
mask: true,
46+
opacity: true,
47+
overflow: true,
48+
pointerEvents: true,
49+
shapeRendering: true,
50+
stopColor: true,
51+
stopOpacity: true,
52+
stroke: true,
53+
strokeDasharray: true,
54+
strokeDashoffset: true,
55+
strokeLinecap: true,
56+
strokeLinejoin: true,
57+
strokeMiterlimit: true,
58+
strokeOpacity: true,
59+
strokeWidth: true,
60+
visibility: true,
61+
}
62+
63+
const pointFns: {[direction: Direction]: (x: number, y: number) => string} = {
64+
left: (x, y) => (-x) + ',' + y,
65+
right: (x, y) => x + ',' + y,
66+
up: (x, y) => y + ',' + (-x),
67+
down: (x, y) => y + ',' + x,
68+
}
69+
70+
let nextId = 0
71+
72+
export default class Arrow extends React.Component<void, Props, void> {
73+
_id: number = nextId++
74+
75+
render(): React.Element<any> {
76+
const {props} = this
77+
const {shaftWidth, shaftLength, headWidth, headLength, unclosed, direction, transform} = props
78+
79+
const side = sideInDirection[direction]
80+
const sideSignum = signumOf[side]
81+
const axis: Axis = axisFor[side]
82+
const oaxis = oppositeOf[axis]
83+
84+
const bounds: { [side: Side]: number } = {}
85+
86+
bounds[side] = headLength * sideSignum
87+
bounds[oppositeOf[side]] = -shaftLength * sideSignum
88+
bounds[loSide[oaxis]] = -headWidth / 2
89+
bounds[hiSide[oaxis]] = headWidth / 2
90+
91+
const point = pointFns[direction]
92+
93+
const pathd = `M${point(-shaftLength, -shaftWidth / 2)}
94+
L${point(0, -shaftWidth / 2)}
95+
L${point(0, -headWidth / 2)}
96+
L${point(headLength, 0)}
97+
L${point(0, headWidth / 2)}
98+
L${point(0, shaftWidth / 2)}
99+
L${point(-shaftLength, shaftWidth / 2)}${unclosed ? '' : ' z'}`
100+
101+
const width = bounds.right - bounds.left
102+
const height = bounds.bottom - bounds.top
103+
104+
const propsForPath = {d: pathd}
105+
const propsForSvg: Object = {
106+
viewBox: `${bounds.left} ${bounds.top} ${width} ${height}`,
107+
preserveAspectRatio: "xMidYMid meet",
108+
}
109+
110+
for (let key in props) {
111+
if (pathProps[key]) propsForPath[key] = props[key]
112+
else if (!ownProps[key]) propsForSvg[key] = props[key]
113+
}
114+
const clipPathId = `react-arrow-stroke-clip-${this._id}`
115+
if (propsForPath.stroke && !propsForPath.clipPath) {
116+
// double the stroke width and clip it to simulate stroke-alignment="inside"
117+
if (propsForPath.strokeWidth != null) propsForPath.strokeWidth *= 2
118+
else propsForPath.strokeWidth = 2
119+
propsForPath.clipPath = `url(#${clipPathId})`
120+
}
121+
const style = propsForSvg.style = (propsForSvg.style ? {...propsForSvg.style} : {})
122+
style.width = width
123+
style.height = height
124+
125+
return (
126+
<svg {...propsForSvg}>
127+
{propsForPath.stroke && <defs transform={transform}>
128+
<clipPath id={clipPathId}>
129+
<path d={pathd} />
130+
</clipPath>
131+
</defs>}
132+
{transform
133+
? <g transform={transform}><path {...propsForPath} /></g>
134+
: <path {...propsForPath} />
135+
}
136+
</svg>
137+
)
138+
}
139+
}
6140

7-
export default Hello

src/index.js.flow

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
// @flow
22

3-
import React from "react";
4-
declare export default function Hello(): React$Element<{}>;
3+
import React from 'react'
4+
5+
import type {Direction} from 'isotrope'
6+
7+
type Props = {
8+
shaftWidth: number,
9+
shaftLength: number,
10+
headWidth: number,
11+
headLength: number,
12+
direction: Direction,
13+
preserveAspectRatio?: string,
14+
unclosed?: boolean,
15+
}
16+
17+
declare export default function Arrow(props: Props): React.Element<any>
518

stories/index.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1+
// @flow
2+
13
import React from 'react'
24
import { storiesOf } from '@storybook/react'
3-
import Hello from '../src/index'
5+
import Arrow from '../src/index'
6+
7+
const props = {
8+
shaftLength: 20,
9+
shaftWidth: 10,
10+
headWidth: 30,
11+
headLength: 20,
12+
fill: 'blue',
13+
stroke: 'red',
14+
}
415

5-
storiesOf('react-library-skeleton', module)
6-
.add('Hello', () => (
7-
<Hello />
16+
storiesOf('Arrow', module)
17+
.add('left', () => <Arrow direction="left" {...props} />)
18+
.add('right', () => <Arrow direction="right" {...props} />)
19+
.add('up', () => <Arrow direction="up" {...props} />)
20+
.add('down', () => <Arrow direction="down" {...props} />)
21+
.add('rotated', () => <Arrow direction="down" transform="rotate(45)" {...props} />)
22+
.add('multiple', () => (
23+
<div>
24+
<Arrow direction="down" {...props} />
25+
<Arrow direction="right" {...props} strokeWidth={5} />
26+
</div>
827
))

0 commit comments

Comments
 (0)