Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fix and new components added. #160

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
export(daqBooleanSwitch)
export(daqColorPicker)
export(daqDarkThemeProvider)
export(daqDirectionCompass)
export(daqGauge)
export(daqGraduatedBar)
export(daqIndicator)
18 changes: 18 additions & 0 deletions R/daqDirectionCompass.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# AUTO GENERATED FILE - DO NOT EDIT

daqDirectionCompass <- function(id=NULL, direction=NULL, label=NULL, labelPosition=NULL, showCurrentValue=NULL, size=NULL, theme=NULL) {

props <- list(id=id, direction=direction, label=label, labelPosition=labelPosition, showCurrentValue=showCurrentValue, size=size, theme=theme)
if (length(props) > 0) {
props <- props[!vapply(props, is.null, logical(1))]
}
component <- list(
props = props,
type = 'DirectionCompass',
namespace = 'dash_daq',
propNames = c('id', 'direction', 'label', 'labelPosition', 'showCurrentValue', 'size', 'theme'),
package = 'dashDaq'
)

structure(component, class = c('dash_component', 'list'))
}
6 changes: 3 additions & 3 deletions R/daqJoystick.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# AUTO GENERATED FILE - DO NOT EDIT

daqJoystick <- function(id=NULL, angle=NULL, className=NULL, force=NULL, label=NULL, labelPosition=NULL, size=NULL, style=NULL, theme=NULL) {
daqJoystick <- function(id=NULL, angle=NULL, className=NULL, force=NULL, label=NULL, labelPosition=NULL, lockX=NULL, lockY=NULL, size=NULL, style=NULL, theme=NULL) {

props <- list(id=id, angle=angle, className=className, force=force, label=label, labelPosition=labelPosition, size=size, style=style, theme=theme)
props <- list(id=id, angle=angle, className=className, force=force, label=label, labelPosition=labelPosition, lockX=lockX, lockY=lockY, size=size, style=style, theme=theme)
if (length(props) > 0) {
props <- props[!vapply(props, is.null, logical(1))]
}
component <- list(
props = props,
type = 'Joystick',
namespace = 'dash_daq',
propNames = c('id', 'angle', 'className', 'force', 'label', 'labelPosition', 'size', 'style', 'theme'),
propNames = c('id', 'angle', 'className', 'force', 'label', 'labelPosition', 'lockX', 'lockY', 'size', 'style', 'theme'),
package = 'dashDaq'
)

68 changes: 68 additions & 0 deletions dash_daq/DirectionCompass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# AUTO GENERATED FILE - DO NOT EDIT

from dash.development.base_component import Component, _explicitize_args


class DirectionCompass(Component):
"""A DirectionCompass component.


Keyword arguments:

- id (string; optional):
id of root element.

- direction (number; default 0):
angle(degrees) of needle of compass.

- label (dict; default ''):
label text.

`label` is a string | dict with keys:

- label (string; optional)

- style (dict; optional)

- labelPosition (a value equal to: 'top', 'bottom'; default 'bottom'):
position of label either top or bottom.

- showCurrentValue (boolean; optional):
show current value.

- size (number; default 150):
size of compass.

- theme (dict; default light):
theme provider.

`theme` is a dict with keys:

- dark (boolean; optional):
True for Dark mode, False for Light.

- detail (string; optional):
Color used for UI details, like borders.

- primary (string; optional):
Highlight color.

- secondary (string; optional):
Supporting color."""
@_explicitize_args
def __init__(self, id=Component.UNDEFINED, size=Component.UNDEFINED, labelPosition=Component.UNDEFINED, label=Component.UNDEFINED, direction=Component.UNDEFINED, theme=Component.UNDEFINED, showCurrentValue=Component.UNDEFINED, **kwargs):
self._prop_names = ['id', 'direction', 'label', 'labelPosition', 'showCurrentValue', 'size', 'theme']
self._type = 'DirectionCompass'
self._namespace = 'dash_daq'
self._valid_wildcard_attributes = []
self.available_properties = ['id', 'direction', 'label', 'labelPosition', 'showCurrentValue', 'size', 'theme']
self.available_wildcard_properties = []
_explicit_args = kwargs.pop('_explicit_args')
_locals = locals()
_locals.update(kwargs) # For wildcard attrs
args = {k: _locals[k] for k in _explicit_args if k != 'children'}
for k in []:
if k not in args:
raise TypeError(
'Required argument `' + k + '` was not specified.')
super(DirectionCompass, self).__init__(**args)
12 changes: 9 additions & 3 deletions dash_daq/Joystick.py
Original file line number Diff line number Diff line change
@@ -36,6 +36,12 @@ class Joystick(Component):
- labelPosition (a value equal to: 'top', 'bottom'; default 'top'):
Where the indicator label is positioned.

- lockX (boolean; optional):
Joystick only move on X axis Its value will either True or False.

- lockY (boolean; optional):
Joystick only move on Y axis Its value will either True or False.

- size (number; default 100):
Size (width) of the component in pixels.

@@ -45,12 +51,12 @@ class Joystick(Component):
- theme (dict; default light):
Theme configuration to be set by a ThemeProvider."""
@_explicitize_args
def __init__(self, id=Component.UNDEFINED, angle=Component.UNDEFINED, force=Component.UNDEFINED, size=Component.UNDEFINED, theme=Component.UNDEFINED, label=Component.UNDEFINED, labelPosition=Component.UNDEFINED, className=Component.UNDEFINED, style=Component.UNDEFINED, **kwargs):
self._prop_names = ['id', 'angle', 'className', 'force', 'label', 'labelPosition', 'size', 'style', 'theme']
def __init__(self, id=Component.UNDEFINED, angle=Component.UNDEFINED, force=Component.UNDEFINED, size=Component.UNDEFINED, theme=Component.UNDEFINED, label=Component.UNDEFINED, labelPosition=Component.UNDEFINED, className=Component.UNDEFINED, style=Component.UNDEFINED, lockX=Component.UNDEFINED, lockY=Component.UNDEFINED, **kwargs):
self._prop_names = ['id', 'angle', 'className', 'force', 'label', 'labelPosition', 'lockX', 'lockY', 'size', 'style', 'theme']
self._type = 'Joystick'
self._namespace = 'dash_daq'
self._valid_wildcard_attributes = []
self.available_properties = ['id', 'angle', 'className', 'force', 'label', 'labelPosition', 'size', 'style', 'theme']
self.available_properties = ['id', 'angle', 'className', 'force', 'label', 'labelPosition', 'lockX', 'lockY', 'size', 'style', 'theme']
self.available_wildcard_properties = []
_explicit_args = kwargs.pop('_explicit_args')
_locals = locals()
2 changes: 2 additions & 0 deletions dash_daq/_imports_.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .BooleanSwitch import BooleanSwitch
from .ColorPicker import ColorPicker
from .DarkThemeProvider import DarkThemeProvider
from .DirectionCompass import DirectionCompass
from .Gauge import Gauge
from .GraduatedBar import GraduatedBar
from .Indicator import Indicator
@@ -20,6 +21,7 @@
"BooleanSwitch",
"ColorPicker",
"DarkThemeProvider",
"DirectionCompass",
"Gauge",
"GraduatedBar",
"Indicator",
2 changes: 1 addition & 1 deletion dash_daq/async-colorpicker.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dash_daq/async-colorpicker.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dash_daq/dash_daq.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dash_daq/dash_daq.min.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dash_daq/package-info.json
Original file line number Diff line number Diff line change
@@ -32,7 +32,6 @@
"build:dev": "npm run build:js-dev && npm run build:py",
"build-tarball": "npm run build && python setup.py sdist",
"test": "jest src/components/__tests__",
"test-gauge": "jest src/components/__tests__/Gauge.test.js",
"test:frontend-cov": "jest --coverage --silent",
"test:watch": "jest --watch",
"uninstall-local": "pip uninstall dash_daq -y",
208 changes: 133 additions & 75 deletions demo.py
Original file line number Diff line number Diff line change
@@ -28,62 +28,83 @@
Thermometer,
ToggleSwitch,
DarkThemeProvider,
DirectionCompass,
)

app = dash.Dash("")

app.css.append_css(
{"external_url": "https://codepen.io/briandennis/pen/zRbYpB.css"})
app.css.append_css({"external_url": "https://codepen.io/briandennis/pen/zRbYpB.css"})

app.config.suppress_callback_exceptions = True
app.scripts.config.serve_locally = True

################ Set up shared layout ################
root_layout = html.Div([
# original
dcc.Location(id='url', refresh=True),
html.Div([
html.H1('dash_daq Dash Demo'),
dcc.Link('Light Theme', href='/', refresh=True),
dcc.Link('Dark Theme', href='/dark', refresh=True),

], style={'width': '80%', 'marginLeft': '10%', 'marginRight': '10%', 'display': 'flex', 'flexDirection': 'row', 'alignItems': 'center', 'justifyContent': 'space-between'}),

html.Hr(),

html.Div([
html.Div(id="page-content"),
root_layout = html.Div(
[
dcc.Location(id="url", refresh=True),
html.Div(
[
BooleanSwitch(id="hiddenBooleanSwitch"),
Gauge(id="hiddenGauge"),
GraduatedBar(id="hiddenGraduatedBar"),
Indicator(id="hiddenIndicator"),
Knob(id="hiddenKnob"),
NumericInput(id="hiddenNumericInput"),
PowerButton(id="hiddenPowerButton"),
StopButton(id="hiddenStopButton"),
Tank(id="hiddenTank"),
Thermometer(id="hiddenThermometer"),
ToggleSwitch(id="hiddenToggleSwitch"),
DarkThemeProvider(),
html.H1("dash_daq Dash Demo"),
dcc.Link("Light Theme", href="/", refresh=True),
dcc.Link("Dark Theme", href="/dark", refresh=True),
],
style={"display": "none"},
style={
"width": "80%",
"marginLeft": "10%",
"marginRight": "10%",
"display": "flex",
"flexDirection": "row",
"alignItems": "center",
"justifyContent": "space-between",
},
),
])]
html.Hr(),
html.Div(
[
html.Div(id="page-content"),
html.Div(
[
BooleanSwitch(id="hiddenBooleanSwitch"),
Gauge(id="hiddenGauge"),
GraduatedBar(id="hiddenGraduatedBar"),
Indicator(id="hiddenIndicator"),
Knob(id="hiddenKnob"),
NumericInput(id="hiddenNumericInput"),
PowerButton(id="hiddenPowerButton"),
StopButton(id="hiddenStopButton"),
Tank(id="hiddenTank"),
Thermometer(id="hiddenThermometer"),
ToggleSwitch(id="hiddenToggleSwitch"),
DarkThemeProvider(),
],
style={"display": "none"},
),
]
),
]
)

################ Set up light layout ################
controls = html.Div(
[
Joystick(id="demojoystick", label="Joystick", labelPosition="bottom"),
Joystick(
id="demojoystick",
label="Joystick",
labelPosition="bottom",
lockX=False,
lockY=True,
),
Knob(id="demoKnob", label="Knob", min=0, max=15, value=2),
NumericInput(id="demoNumericInput", min=1, max=10000, value=100),
BooleanSwitch(id="demoSwitch"),
ToggleSwitch(id="demoToggleSwitch", value="false", vertical=True),
StopButton(id="demoStopButton", n_clicks=0),
PowerButton(id="demoPowerButton", on="false", onButtonStyle={
"backgroundColor": "red"}, offButtonStyle={"backgroundColor": "blue"}),
PowerButton(
id="demoPowerButton",
on="false",
onButtonStyle={"backgroundColor": "red"},
offButtonStyle={"backgroundColor": "blue"},
),
Slider(
id="demoSlider",
min=0,
@@ -115,7 +136,7 @@


def warning(x, y):
return x+" "+y
return x + " " + y


indicators = html.Div(
@@ -131,18 +152,20 @@ def warning(x, y):
scale={"start": 0, "interval": 1, "labelInterval": 1},
value=1,
showCurrentValue=True,
color={"gradient": True, "default": "yellow",
"ranges": {"red": [0, 2], "green": [2, 4]}}
color={
"gradient": True,
"default": "yellow",
"ranges": {"red": [0, 2], "green": [2, 4]},
},
),
Tank(
id="demoTank",
label="Tank",
min=0,
max=10,
value=2,
DirectionCompass(
id="direction-compass",
direction=20,
label={
"label": "label",
"style": {"color": "red", "textDecoration": "underline"},
},
showCurrentValue=True,
scale={"custom": {0: "Low", 5: "Medium", 10: "High"}},
exceedMessage="Exceed",
),
Thermometer(
id="demoThermometer", label="Thermometer", min=0, max=10, value=2
@@ -154,8 +177,9 @@ def warning(x, y):
max=100,
value=40,
),
LEDDisplay(id="demoLEDDisplay", value="-1.2",
backgroundColor="#FFFFFF"),
LEDDisplay(
id="demoLEDDisplay", value="-1.2", backgroundColor="#FFFFFF"
),
html.Div(
[
html.Div(
@@ -223,12 +247,27 @@ def warning(x, y):

light_layout = html.Div(
[
html.Link(href="https://codepen.io/plotly/pen/EQZeaW.css",
rel="stylesheet"),
html.Link(href="https://codepen.io/plotly/pen/EQZeaW.css", rel="stylesheet"),
html.Div([html.H2("Controls"), controls], style={"width": "80%"}),
html.Br(),
html.Br(),
html.Div([html.H2("Indicators"), indicators], style={"width": "80%"}),
html.Div(
[
html.H2("Indicators"),
indicators,
Tank(
id="demoTank",
label="Tank",
min=0,
max=10,
value=2,
showCurrentValue=True,
scale={"custom": {0: "Low", 5: "Medium", 10: "High"}},
exceedMessage="Exceed",
),
],
style={"width": "80%"},
),
],
style={
"width": "100%",
@@ -246,8 +285,14 @@ def warning(x, y):
id="dark-demojoystick",
label="Joystick",
),
Knob(id="dark-demoKnob", label="Knob", min=0,
max=10, value=2, showCurrentValue=True),
Knob(
id="dark-demoKnob",
label="Knob",
min=0,
max=10,
value=2,
showCurrentValue=True,
),
NumericInput(id="dark-demoNumericInput", min=1, max=10000, value=100),
BooleanSwitch(id="dark-demoSwitch"),
ToggleSwitch(id="dark-demoToggleSwitch", value="false", vertical=True),
@@ -305,7 +350,7 @@ def warning(x, y):
value=2,
scale={"custom": {0: "Low", 5: "Medium", 10: "High"}},
# textColor="red",
showCurrentValue=True
showCurrentValue=True,
),
Thermometer(
id="dark-demoThermometer",
@@ -321,8 +366,9 @@ def warning(x, y):
max=100,
value=40,
),
LEDDisplay(id="dark-demoLEDDisplay", value=1.2,
backgroundColor="#FFFFFF"),
LEDDisplay(
id="dark-demoLEDDisplay", value=1.2, backgroundColor="#FFFFFF"
),
html.Div(
[
html.Div(
@@ -391,16 +437,30 @@ def warning(x, y):

dark_layout = DarkThemeProvider(
[
html.Link(href="https://codepen.io/anon/pen/BYEPbO.css",
rel="stylesheet"),
html.Link(href="https://codepen.io/anon/pen/BYEPbO.css", rel="stylesheet"),
html.Div(
[
html.Div([html.H2("Controls"), dark_controls],
style={"width": "80%"}),
html.Div([html.H2("Controls"), dark_controls], style={"width": "80%"}),
html.Br(),
html.Br(),
html.Div(
[html.H2("Indicators"), dark_indicators], style={"width": "80%"}
[
html.H2("Indicators"),
dark_indicators,
DirectionCompass(
id="dark-direction-compass",
direction=20,
label={
"label": "label",
"style": {
"color": "red",
"textDecoration": "underline",
},
},
showCurrentValue=True,
),
],
style={"width": "80%"},
),
],
style={
@@ -433,6 +493,9 @@ def update_LEDDisplay(value):
digits = str(value)[:2]
return "-" + ".".join(digits)

@app.callback(Output("direction-compass", "direction"), [Input("demoKnob", "value")])
def update_direction_compass(value):
return int((value * 360 / 15) * 100) / 100

@app.callback(Output("demoGauge", "value"), [Input("demoNumericInput", "value")])
def update_gauge(value):
@@ -480,8 +543,7 @@ def update_boolean_indicator(on):


@app.callback(
Output("demoToggleIndicator", "value"), [
Input("demoToggleSwitch", "value")]
Output("demoToggleIndicator", "value"), [Input("demoToggleSwitch", "value")]
)
def update_boolean_indicator(value):
return value
@@ -503,17 +565,15 @@ def update_thermometer(on):

################ Set up dark callbacks ################
@app.callback(
Output("dark-demoLEDDisplay",
"value"), [Input("dark-demoPrecisionInput", "value")]
Output("dark-demoLEDDisplay", "value"), [Input("dark-demoPrecisionInput", "value")]
)
def update_LEDDisplay(value):
digits = str(value)[:2]
return "-" + ".".join(digits)


@app.callback(
Output("dark-demoGauge",
"value"), [Input("dark-demoNumericInput", "value")]
Output("dark-demoGauge", "value"), [Input("dark-demoNumericInput", "value")]
)
def dark_update_gauge(value):
return value
@@ -534,16 +594,14 @@ def dark_update_tank_color(value):


@app.callback(
Output("dark-demoGraduatedBar",
"color"), [Input("dark-demoColorPicker", "value")]
Output("dark-demoGraduatedBar", "color"), [Input("dark-demoColorPicker", "value")]
)
def dark_update_graduatedBar_color(value):
return value["hex"]


@app.callback(
Output("dark-demoThermometer",
"color"), [Input("dark-demoColorPicker", "value")]
Output("dark-demoThermometer", "color"), [Input("dark-demoColorPicker", "value")]
)
def dark_update_thermometer_color(value):
return value["hex"]
@@ -560,18 +618,19 @@ def dark_update_tank(value):
def dark_update_thermometer(value):
return value

@app.callback(Output("dark-direction-compass", "direction"), [Input("dark-demoKnob", "value")])
def update_direction_compass(value):
return int((value * 360 / 15) * 100) / 100

@app.callback(
Output("dark-demoGraduatedBar",
"value"), [Input("dark-demoSlider", "value")]
Output("dark-demoGraduatedBar", "value"), [Input("dark-demoSlider", "value")]
)
def dark_update_graduated_bar(value):
return value


@app.callback(
Output("dark-demoBooleanIndicator",
"value"), [Input("dark-demoSwitch", "on")]
Output("dark-demoBooleanIndicator", "value"), [Input("dark-demoSwitch", "on")]
)
def dark_update_boolean_indicator(on):
return on
@@ -595,8 +654,7 @@ def dark_update_indicator(_, indicatorState):


@app.callback(
Output("dark-demoPowerIndicator",
"value"), [Input("dark-demoPowerButton", "on")]
Output("dark-demoPowerIndicator", "value"), [Input("dark-demoPowerButton", "on")]
)
def dark_update_thermometer(on):
return on
2 changes: 1 addition & 1 deletion inst/deps/async-colorpicker.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/deps/async-colorpicker.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions inst/deps/dash_daq.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/deps/dash_daq.min.js.map

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions man/daqDirectionCompass.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
% Auto-generated: do not edit by hand
\name{daqDirectionCompass}

\alias{daqDirectionCompass}

\title{DirectionCompass component}

\description{

}

\usage{
daqDirectionCompass(id=NULL, direction=NULL, label=NULL, labelPosition=NULL,
showCurrentValue=NULL, size=NULL, theme=NULL)
}

\arguments{
\item{id}{Character. id of root element}

\item{direction}{Numeric. angle(degrees) of needle of compass}

\item{label}{Character | lists containing elements 'label', 'style'.
those elements have the following types:
- label (character; optional)
- style (named list; optional). label text}

\item{labelPosition}{A value equal to: 'top', 'bottom'. position of label either top or bottom}

\item{showCurrentValue}{Logical. show current value}

\item{size}{Numeric. size of compass}

\item{theme}{Lists containing elements 'primary', 'secondary', 'detail', 'dark'.
those elements have the following types:
- primary (character; optional): highlight color
- secondary (character; optional): supporting color
- detail (character; optional): color used for ui details, like borders
- dark (logical; optional): true for dark mode, false for light. theme provider}
}

\value{named list of JSON elements corresponding to React.js properties and their values}

9 changes: 8 additions & 1 deletion man/daqJoystick.Rd
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ A joystick.

\usage{
daqJoystick(id=NULL, angle=NULL, className=NULL, force=NULL, label=NULL,
labelPosition=NULL, size=NULL, style=NULL, theme=NULL)
labelPosition=NULL, lockX=NULL, lockY=NULL, size=NULL,
style=NULL, theme=NULL)
}

\arguments{
@@ -31,6 +32,12 @@ pass an object with label and style properties}

\item{labelPosition}{A value equal to: 'top', 'bottom'. Where the indicator label is positioned}

\item{lockX}{Logical. Joystick only move on X axis
Its value will either true or false}

\item{lockY}{Logical. Joystick only move on Y axis
Its value will either true or false}

\item{size}{Numeric. Size (width) of the component in pixels}

\item{style}{Named list. Style to apply to the root component element}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -32,7 +32,6 @@
"build:dev": "npm run build:js-dev && npm run build:py",
"build-tarball": "npm run build && python setup.py sdist",
"test": "jest src/components/__tests__",
"test-gauge": "jest src/components/__tests__/Gauge.test.js",
"test:frontend-cov": "jest --coverage --silent",
"test:watch": "jest --watch",
"uninstall-local": "pip uninstall dash_daq -y",
105 changes: 105 additions & 0 deletions src/components/DirectionCompass.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import PropTypes from 'prop-types';

import { getClassName, getFilteredProps } from '../helpers/classNameGenerator';
import DirectionCompassSVG from '../helpers/DirectionCompassSVG';
import { light } from '../styled/constants';
import CurrentValue from '../styled/CurrentValue.styled';
import LabelContainer from './../styled/shared/LabelContainer.styled';

const DirectionCompass = props => {
const { id, size, direction, showCurrentValue } = props;

const filteredProps = getFilteredProps(props);

const elementName = getClassName('direction-compass');

const currentValue = (
<CurrentValue
className={elementName + '__current-value'}
valueSize={((size + 32) * 13.3333) / 100}
css={'transform: translateY(0%); top: 0;'}
>
{direction}
</CurrentValue>
);

return (
<div id={id} className={elementName}>
<LabelContainer
{...filteredProps}
labelCSS={props.labelPosition === 'top' ? null : 'transform: translateY(-50%);'}
>
<DirectionCompassSVG size={size} {...filteredProps} />
{showCurrentValue && currentValue}
</LabelContainer>
</div>
);
};

DirectionCompass.defaultProps = {
size: 150,
direction: 0,
label: '',
labelPosition: 'bottom',
theme: light
};

DirectionCompass.propTypes = {
/**
* id of root element
*/
id: PropTypes.string,

/**
* size of compass
*/
size: PropTypes.number,

/**
* position of label either top or bottom
*/
labelPosition: PropTypes.oneOf(['top', 'bottom']),

/**
* label text
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({ label: PropTypes.string, style: PropTypes.object })
]),

/**
* angle(degrees) of needle of compass
*/
direction: PropTypes.number,

/**
* theme provider
*/
theme: PropTypes.shape({
/**
* Highlight color
*/
primary: PropTypes.string,
/**
* Supporting color
*/
secondary: PropTypes.string,
/**
* Color used for UI details, like borders
*/
detail: PropTypes.string,
/**
* True for Dark mode, false for Light
*/
dark: PropTypes.bool
}),

/**
* show current value
*/
showCurrentValue: PropTypes.bool
};

export default DirectionCompass;
16 changes: 15 additions & 1 deletion src/components/Joystick.react.js
Original file line number Diff line number Diff line change
@@ -23,6 +23,8 @@ class Joystick extends Component {
mode: 'static',
color: 'grey',
size: size,
lockY: this.props.lockY || false,
lockX: this.props.lockX || false,
position: { left: '50%', top: '50%' },
zone: this.zoneRef
});
@@ -147,7 +149,19 @@ Joystick.propTypes = {
/**
* Style to apply to the root component element
*/
style: PropTypes.object
style: PropTypes.object,

/**
* Joystick only move on X axis
* Its value will either true or false
*/
lockX: PropTypes.bool,

/**
* Joystick only move on Y axis
* Its value will either true or false
*/
lockY: PropTypes.bool
};

export default withTheme(Joystick);
2 changes: 1 addition & 1 deletion src/components/Knob.react.js
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ class Knob extends Component {
noop() {}

getValue(value) {
return value > this.props.min && value < this.props.max
return value >= this.props.min && value <= this.props.max
? value
: this.props.min > value
? this.props.min
5 changes: 4 additions & 1 deletion src/components/__tests__/ColorPicker.test.js
Original file line number Diff line number Diff line change
@@ -33,7 +33,10 @@ describe('Color Picker', () => {
const component = mount(
shallow(<ColorPicker value={{ rgb: { r: 50, g: 100, b: 150, a: 0.8 } }} />).get(0)
);
expect(component.state('value')).toEqual({ rgb: { r: 50, g: 100, b: 150, a: 0.8 } });
expect(component.state('value')).toEqual({
hex: '#326496CC',
rgb: { r: 50, g: 100, b: 150, a: 0.8 }
});
});

it('calls setProps callbacks', () => {
72 changes: 72 additions & 0 deletions src/components/__tests__/DirectionCompass.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable */
import React from 'react';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

import { shallow, mount } from 'enzyme';

import DirectionCompass from './../DirectionCompass.react';
import DarkThemeProvider from '../DarkThemeProvider.react';
import CurrentValueStyled from '../../styled/CurrentValue.styled';
import Label from '../../styled/shared/Label.styled';
import DirectionCompassSVG from '../../helpers/DirectionCompassSVG';

describe.only('Direction Compass', () => {
it('Renders', () => {
const component = shallow(<DirectionCompass />);
expect(component).toBeTruthy();
});

it('Renders dark theme', () => {
const component = mount(
<DarkThemeProvider>
<DirectionCompass />
</DarkThemeProvider>
);
expect(component).toBeTruthy();
});

it('direction angle', () => {
const component = mount(<DirectionCompass direction={50} />);
expect(component.prop('direction')).toBe(50);
expect(component.find(DirectionCompassSVG).prop('direction')).toBe(50);

component.setProps({ direction: 100 });

expect(component.prop('direction')).toBe(100);
expect(component.find(DirectionCompassSVG).prop('direction')).toBe(100);
});

it('current value', () => {
const component = mount(<DirectionCompass showCurrentValue={true} />);

expect(component.contains(CurrentValueStyled)).toBeTruthy();
expect(component.find(CurrentValueStyled).prop('children')).toBe(0);
component.setProps({ direction: 50 });
expect(component.find(CurrentValueStyled).prop('children')).toBe(50);
});

it('label', () => {
const component = mount(<DirectionCompass label="Compass" />);

expect(component.find(Label)).toHaveLength(1);
expect(component.find(Label).text()).toBe('Compass');

component.setProps({ label: { label: 'new label', style: { color: 'red' } } });

expect(component.find(Label).text()).toBe('new label');
expect(component.find(Label).prop('style')).toStrictEqual({ color: 'red' });
});

it('label position', () => {
const component = mount(<DirectionCompass label="Compass" labelPosition="top" />);

expect(component.find(Label)).toHaveLength(1);
expect(component.find(Label).prop('position')).toBe('top');

component.setProps({ labelPosition: 'bottom' });

expect(component.find(Label).prop('position')).toBe('bottom');
});
});
32 changes: 28 additions & 4 deletions src/fragments/ColorPicker.react.js
Original file line number Diff line number Diff line change
@@ -17,8 +17,8 @@ const parseValue = value => {
value = value || {};

if (value.rgb) {
const rgba = Object.values(value.rgb);
return `rgba(${rgba[0]}, ${rgba[1]}, ${rgba[2]}, ${rgba[3]})`;
const rgba = value.rgb;
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
}

if (value.hex)
@@ -29,17 +29,41 @@ const parseValue = value => {
return DEFAULT_COLOR;
};

const componentToHex = c => {
var hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
};

// converts color from rgb to hexadecimal.
const rgbToHex = ({ r, g, b, a }) => {
return (
'#' +
componentToHex(r) +
componentToHex(g) +
componentToHex(b) +
(Math.round(a * 255) + 0x10000)
.toString(16)
.substr(-2)
.toUpperCase()
);
};

/**
* A color picker.
*/
class ColorPicker extends Component {
constructor(props) {
super(props);

let newValue = props.value;
if (props.value && !props.value.hex && props.value.rgb) {
const value = rgbToHex(props.value.rgb);
newValue = { hex: value, rgb: props.value.rgb };
if (this.props.setProps) this.props.setProps({ value: newValue });
}
this.state = {
value: props.value
value: newValue
};

this.calcHandleGlow = this.calcHandleGlow.bind(this);
this.setValue = this.setValue.bind(this);
}
228 changes: 228 additions & 0 deletions src/helpers/DirectionCompassSVG.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import React from 'react';
import SvgContainer from '../styled/DirectionCompass.styled';

const DirectionCompassSVG = props => {
const { size, direction, theme } = props;

const center = { x: size / 2, y: size / 2 };
const radius = size / 2 - 3;
const majorScaleLength = size / 12;
const minorScaleLength = size / 24;
const needleLength = radius * 0.85;

const getPointAtAngle = ({ cx, cy, angle, radius }) => {
angle = (angle * Math.PI) / 180;
let x = Math.ceil(cx + radius * Math.cos(angle));
let y = Math.ceil(cy + radius * Math.sin(angle));
return { x, y, angle };
};

const linePointsAtAngle = ({ cx, cy, angle, radius, lineLength }) => {
angle = (angle * Math.PI) / 180;
let x = cx + radius * Math.cos(angle);
let y = cy + radius * Math.sin(angle);

let x1 = Math.round(x);
let y1 = Math.round(y);

let x2 = Math.round(x - lineLength * Math.cos(angle));
let y2 = Math.round(y - lineLength * Math.sin(angle));
return { x1, y1, x2, y2 };
};

const lines = [
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 0,
radius: radius,
lineLength: majorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 90,
radius: radius,
lineLength: majorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 180,
radius: radius,
lineLength: majorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 270,
radius: radius,
lineLength: majorScaleLength
}),

linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 45,
radius: radius,
lineLength: minorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 135,
radius: radius,
lineLength: minorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 225,
radius: radius,
lineLength: minorScaleLength
}),
linePointsAtAngle({
cx: center.x,
cy: center.y,
angle: 315,
radius: radius,
lineLength: minorScaleLength
})
];

const texts = [
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 270,
radius: radius - majorScaleLength - 0.1 * size
}),
text: 'N'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 0,
radius: radius - majorScaleLength - 0.1 * size
}),
text: 'E'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 90,
radius: radius - majorScaleLength - 0.1 * size
}),
text: 'S'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 180,
radius: radius - majorScaleLength - 0.1 * size
}),
text: 'W'
},

{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 315,
radius: radius - minorScaleLength - 0.1 * size
}),
text: 'NE'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 45,
radius: radius - minorScaleLength - 0.1 * size
}),
text: 'SE'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 135,
radius: radius - minorScaleLength - 0.1 * size
}),
text: 'SW'
},
{
...getPointAtAngle({
cx: center.x,
cy: center.y,
angle: 225,
radius: radius - minorScaleLength - 0.1 * size
}),
text: 'NW'
}
];

return (
<SvgContainer scaleTextSize={size * 0.05} theme={theme}>
<svg width={size} height={size}>
<g>
<circle
cx={center.x}
cy={center.y}
r={radius}
fill="none"
strokeWidth={5}
stroke={theme.primary}
/>
</g>
<g>
{lines.map((obj, index) => {
return (
<line key={index} {...obj} stroke={theme.primary} strokeWidth={1.5} fill="none" />
);
})}
</g>
<g>
{texts.map((obj, index) => {
return (
<text
key={index}
className="scale-text"
x={obj.x}
y={obj.y}
textAnchor="middle"
alignmentBaseline="middle"
>
{obj.text}
</text>
);
})}
</g>
<g style={{ transformOrigin: 'center', transform: `rotateZ(${direction}deg)` }}>
<path
d={`M${center.x} ${center.y} L${center.x + needleLength / 8} ${center.y} L${
center.x
} ${center.y - needleLength / 2} L${center.x - needleLength / 8} ${center.y} Z`}
fill={'#f03e3e'}
/>
<path
d={`M${center.x} ${center.y} L${center.x + needleLength / 8} ${center.y} L${
center.x
} ${center.y + needleLength / 2} L${center.x - needleLength / 8} ${center.y} Z`}
fill={'#9E9E9E'}
/>
</g>
</svg>
</SvgContainer>
);
};

DirectionCompassSVG.defaultProps = {};

DirectionCompassSVG.propTypes = {};

export default DirectionCompassSVG;
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -16,3 +16,4 @@ export { default as Thermometer } from './components/Thermometer.react';
export { default as ToggleSwitch } from './components/ToggleSwitch.react';
export { default as DarkThemeProvider } from './components/DarkThemeProvider.react';
export { default as Joystick } from './components/Joystick.react';
export { default as DirectionCompass } from './components/DirectionCompass.react';
10 changes: 10 additions & 0 deletions src/styled/DirectionCompass.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styled from 'styled-components';

const SvgContainer = styled.div`
& .scale-text {
${({ scaleTextSize }) => `font-size: ${scaleTextSize}px;`}
${({ theme }) => (theme.dark ? `fill: ${theme.detail};` : null)}
}
`;

export default SvgContainer;