Update to material-ui 1.0 beta

This commit is contained in:
uwap 2017-10-31 17:01:12 +01:00
parent 48a96b57fa
commit bef07649d0
14 changed files with 1164 additions and 395 deletions

View file

@ -1,5 +1,4 @@
[ignore]
.*node_modules/react-event-listener/.*
[include]
src/

BIN
img/layers/rzl/details.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
img/layers/rzl/labels.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
img/layers/rzl/rooms.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -57,11 +57,14 @@
/* Support for IE. */
font-feature-settings: 'liga';
}
#drawer_uiComponents .material-icons {
font-size: 24px;
}
</style>
</head>
<body>
<div id="content">
</div>
<script src="public/dist/main.js"></script>
<script src="dist/main.js"></script>
</body>

View file

@ -1,8 +1,8 @@
{
"name": "reacttest",
"name": "spacemap",
"version": "1.0.0",
"author": "uwap <me@uwap.name>",
"description": "react",
"author": "uwap <me+spacemap.package.json@uwap.name>",
"description": "control devices via mqtt on a beautiful map of your space",
"scripts": {
"build": "webpack --bail",
"watch": "webpack --watch"
@ -10,7 +10,8 @@
"dependencies": {
"babel-preset-env": "^1.6.0",
"leaflet": "^1.2.0",
"material-ui": "^0.18.7",
"material-ui": "next",
"material-ui-old": "npm:material-ui@latest",
"mqtt": "^2.11.0",
"ramda": "^0.24.1",
"react": "^15.6.1",
@ -26,6 +27,7 @@
"babel-preset-react": "^6.24.1",
"flow": "^0.2.3",
"flow-bin": "^0.50.0",
"flow-typed": "^2.2.1",
"webpack": "^3.1.0",
"webpack-shell-plugin": "^0.5.0"
},

View file

@ -1,26 +1,38 @@
// @flow
import React from "react";
import Toggle from "material-ui/Toggle";
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import Slider from 'material-ui/Slider';
import Switch from "material-ui/Switch";
import Select from 'material-ui/Select';
import { MenuItem } from 'material-ui/Menu';
import Slider from 'material-ui-old/Slider';
import MuiThemeProvider from 'material-ui-old/styles/MuiThemeProvider';
import Icon from 'material-ui/Icon';
import Config from "./config";
import { keyOf } from "./util";
import Input, { InputLabel } from 'material-ui/Input';
import { FormControl } from 'material-ui/Form';
import R from "ramda";
import List, {
ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
ListSubheader,
} from 'material-ui/List';
const enabled = (props: ControlUI, state: State) => {
if (props.enableCondition == null) return true;
else {
const val = state.values[props.topic];
return props.enableCondition(
val.internal == null ? val.actual : val.internal, val.actual);
val.internal == null ? val.actual : val.internal, val.actual, R.map(x => x.internal == null ?
x.actual : x.internal, state.values == null ? {} : state.values));
}
};
const getValue = (topic: string, val: string) =>
Config.topics[topic].values[val];
const onToggle = (topic: string, props: ControlUI, state: State) =>
const onSwitch = (topic: string, props: ControlUI, state: State) =>
(x, toggled: boolean) => {
if (state.mqtt != null) {
state.mqtt.publish(Config.topics[topic].command,
@ -39,31 +51,49 @@ export const toggle = (state: State, props: ControlUI) => {
return val.internal === R.propOr("on", "on", props);
}
})();
return (<Toggle label={props.text}
toggled={toggled}
onToggle={onToggle(props.topic, props, state)}
return (
<ListItem>
{props.icon && <ListItemIcon><Icon>{props.icon}</Icon></ListItemIcon>}
<ListItemText primary={props.text} />
<ListItemSecondaryAction>
<Switch label={props.text}
checked={toggled}
onChange={onSwitch(props.topic, props, state)}
disabled={!(enabled(props, state))} />
</ListItemSecondaryAction>
</ListItem>
);
}
const onDropDownChange = (topic: string, props: ControlUI, state: State) =>
(event, index, value) => {
(event) => {
if (state.mqtt != null) {
state.mqtt.publish(Config.topics[topic].command, value);
state.mqtt.publish(Config.topics[topic].command, event.target.value);
}
};
const dropDownItem = (topic: string) => (text: string, key: string) => (
<MenuItem value={Config.topics[topic].values[key]} primaryText={text} />
<MenuItem value={Config.topics[topic].values[key]} key={key}>{text}</MenuItem>
);
export const dropDown = (state: State, props: ControlUI) => (
<SelectField value={state.values[props.topic].actual}
export const dropDown = (state: State, props: ControlUI) => {
const id = `${props.topic}.${Object.keys(props.options).reduce((v,r) => v + "." + r)}`;
return (
<ListItem>
{props.icon && <ListItemIcon><Icon>{props.icon}</Icon></ListItemIcon>}
<FormControl>
<InputLabel htmlFor={id}>{props.text}</InputLabel>
<Select value={state.values[props.topic].actual}
onChange={onDropDownChange(props.topic, props, state)}
disabled={!(enabled(props, state))}>
disabled={!(enabled(props, state))}
input={<Input id={id} />}
>
{R.values(R.mapObjIndexed(dropDownItem(props.topic), props.options))}
</SelectField>
</Select>
</FormControl>
</ListItem>
);
};
const onSliderChange = (state: State, props: ControlUI) =>
(event, value) => {
@ -73,13 +103,22 @@ const onSliderChange = (state: State, props: ControlUI) =>
};
export const slider = (state: State, props: ControlUI) => (
<div>
<span>{props.text}</span>
<ListItem>
{props.icon && <ListItemIcon><Icon>{props.icon}</Icon></ListItemIcon>}
<ListItemText primary={props.text} />
<ListItemSecondaryAction>
<MuiThemeProvider>
<Slider value={state.values[props.topic].actual}
min={props.min == null ? 0 : props.min}
max={props.max == null ? 1 : props.max}
step={props.step == null ? 1 : props.step}
onChange={onSliderChange(state, props)}
/>
</div>
style={{width: 100}}
/></MuiThemeProvider>
</ListItemSecondaryAction>
</ListItem>
);
export const section = (state: State, props: ControlUI) => (
<ListSubheader>{props.text}</ListSubheader>
);

View file

@ -1,57 +1,44 @@
// @flow
import React from "react";
import AppBar from "material-ui/AppBar";
import CircularProgress from "material-ui/CircularProgress";
import MapIcon from "material-ui/svg-icons/maps/map";
import PhonelinkOffIcon from "material-ui/svg-icons/hardware/phonelink-off";
import LayersIcon from "material-ui/svg-icons/maps/layers";
import IconMenu from "material-ui/IconMenu";
import Toolbar from 'material-ui/Toolbar';
import { CircularProgress } from "material-ui/Progress";
import Icon from 'material-ui/Icon';
import IconButton from "material-ui/IconButton";
import MenuItem from "material-ui/MenuItem";
import { orange400, grey50 } from "material-ui/styles/colors";
import Menu, { MenuItem } from "material-ui/Menu";
import Typography from 'material-ui/Typography';
const TopBarLayerSelector = (props: Object) => (
<IconMenu
iconButtonElement={
<IconButton style={{width: 48, height: 48, padding: 0}}
iconStyle={{width: 48, height: 48}}>
<LayersIcon color={grey50} />
</IconButton>}
style={{width:48, height:48}}>
<MenuItem primaryText="Layer1" />
</IconMenu>
<IconButton>
<Icon>layers</Icon>
</IconButton>
)
const TopBarIndicatorMenu = (props: Object) => (
<IconMenu
iconButtonElement={
<IconButton style={{width:48, height:48, padding: 0}}
iconStyle={{width:48, height: 48}}
tooltip={props.mqtt.connected ? "Connected!" : "Disconnected!"}>
<IconButton>
{props.mqtt.connected ?
(<MapIcon color={grey50} />) :
(<PhonelinkOffIcon color={grey50} />)}
</IconButton>}
style={{width:48, height:48}}>
<MenuItem primaryText="Reconnect (Not yet implemented)" />
</IconMenu>
(<Icon style={{fontSize: 48}}>map</Icon>) :
(<Icon style={{fontSize: 48}}>phonelink_off</Icon>)}
</IconButton>
);
const TopBarIndicator = (props: Object) => {
if (props.mqtt == null || props.mqtt.reconnecting) {
return (<CircularProgress size={48} color={grey50} />);
return (<CircularProgress size={48} style={{color: "rgba(0, 0, 0, 0.54)"}} />);
} else {
return (<TopBarIndicatorMenu {...props} />);
}
};
const TopBar = (props: Object) => (
<AppBar title={props.title}
style={{background:orange400}}
iconElementLeft={<TopBarIndicator {...props} />}
iconElementRight={<TopBarLayerSelector {...props} />}
className="nav"
/>);
<AppBar position="static">
<Toolbar>
<TopBarIndicator {...props} />
<Typography type="title">{props.title}</Typography>
{false && <TopBarLayerSelector {...props} />}
</Toolbar>
</AppBar>
);
export default TopBar;

View file

@ -51,6 +51,20 @@ const config : Config = {
blue: "blue", green: "green", red: "red", random: "random",
cycle: "cycle-random" }
},
onkyo_power: {
state: "/service/onkyo/status/system-power",
command: "/service/onkyo/command",
defaultValue: "PWR00",
values: { off: "PWR00", on: "PWR01" },
parseState: msg => JSON.parse(msg.toString()).onkyo_raw
},
onkyo_mute: {
state: "/service/onkyo/status/audio-muting",
command: "/service/onkyo/command",
defaultValue: "AMT00",
values: { off: "AMT00", on: "AMT01" },
parseState: msg => JSON.parse(msg.toString()).onkyo_raw
},
onkyo_volume: {
state: "/service/onkyo/status/volume",
command: "/service/onkyo/set/volume",
@ -62,9 +76,16 @@ const config : Config = {
state: "/service/onkyo/status/input-selector",
command: "/service/onkyo/command",
defaultValue: "SLI00",
values: { tisch: "SLI11", chromecast: "SLI01", pult: "SLI10" },
values: { tisch: "SLI11", chromecast: "SLI01", pult: "SLI10", netzwerk: "SLI2B" },
parseState: msg => JSON.parse(msg.toString()).onkyo_raw
},
onkyo_radios: {
state: "/service/onkyo/status/latest-NPR",
command: "/service/onkyo/command",
defaultValue: "",
values: { mpd: "NPR01", kohina: "NPR02", somafm_dronezone: "NPR03", somafm_thetrip: "NPR04",
querfunk: "NPR05", somafm_defconradio: "NPR06", somafm_secretagent: "NPR07", somafm_lush: "NPR08"}
},
rundumleuchte: {
state: "/service/openhab/out/pca301_rundumleuchte/state",
command: "/service/openhab/in/pca301_rundumleuchte/command",
@ -87,98 +108,105 @@ const config : Config = {
controls: {
led_stahltrager: {
name: "LED Stahlträger",
position: [390, 100],
position: [380, 300],
icon: "wb_incandescent",
iconColor: state => state.led_stahltraeger == "on" ? "#CCCC00" : "#000000",
ui: [
{
type: "toggle",
text: "Stahlträger LED",
topic: "led_stahltraeger"
topic: "led_stahltraeger",
icon: "power_settings_new"
},
]
},
snackbar: {
name: "Snackbar",
position: [560, 465],
position: [510, 500],
icon: "kitchen",
iconColor: state => state.snackbar == "on" ? "#E20074" : "#000000",
ui: [
{
type: "toggle",
text: "Snackbar",
topic: "snackbar"
topic: "snackbar",
icon: "power_settings_new"
}
]
},
twinkle: {
name: "Twinkle",
position: [500, 540],
position: [530, 560],
icon: "wb_incandescent",
iconColor: state => state.twinkle == "on" ? "#CCCC00" : "#000000",
ui: [
{
type: "toggle",
text: "Twinkle",
topic: "twinkle"
topic: "twinkle",
icon: "power_settings_new"
}
]
},
fan: {
name: "Ventilator",
position: [530, 450],
position: [510, 460],
icon: "toys",
iconColor: state => state.fan == "on" ? "#00FF00" : "#000000",
ui: [
{
type: "toggle",
text: "Ventilator",
topic: "fan"
topic: "fan",
icon: "power_settings_new"
}
]
},
videogames: {
name: "Videospiele",
position: [79, 50],
position: [100, 100],
icon: "videogame_asset",
iconColor: state => state.videogames == "on" ? "#00FF00" : "#000000",
ui: [
{
type: "toggle",
text: "Videospiele",
topic: "videogames"
topic: "videogames",
icon: "power_settings_new"
}
]
},
olymp_pc: {
name: "Rechner und Drucker",
position: [298, 20],
position: [297, 90],
icon: "desktop_windows",
iconColor: state => state.olymp_pc == "on" ? "#00FF00" : "#000000",
ui: [
{
type: "toggle",
text: "Rechner und Drucker",
topic: "olymp_pc"
topic: "olymp_pc",
icon: "power_settings_new"
}
]
},
flyfry: {
name: "Fliegenbratgerät",
position: [450, 560],
position: [450, 590],
icon: "whatshot",
iconColor: state => state.flyfry == "on" ? "#6666FF" : "#000000",
ui: [
{
type: "toggle",
text: "Fliegenbratgerät",
topic: "flyfry"
topic: "flyfry",
icon: "power_settings_new"
}
]
},
artnet: {
name: "Artnet",
position: [560,430],
position: [535,475],
icon: "wb_incandescent",
iconColor: state =>
({
@ -195,77 +223,121 @@ const config : Config = {
text: "An/Aus",
topic: "artnet",
on: "cycle",
toggled: val => val != "off"
toggled: val => val != "off",
icon: "power_settings_new"
},
{
type: "dropDown",
text: "Artnet",
text: "Farbe",
topic: "artnet",
options: {
yellow: "Gelb",
red: "Rot",
purple: "Pink",
green: "Grün",
cycle: "Cycle Random"
cycle: "Farbwechsel"
},
enableCondition: val => val != "off"
enableCondition: val => val != "off",
icon: "color_lens"
}
]
},
onkyo: {
name: "Onkyo",
position: [350, 620],
position: [350, 650],
icon: "volume_up",
ui: [
{
type: "toggle",
text: "Power",
icon: "power_settings_new",
topic: "onkyo_power"
},
{
type: "section",
text: "Lautstärkeregelung"
},
{
type: "slider",
text: "Volume",
topic: "onkyo_volume",
min: 0,
max: 100
max: 100,
icon: "volume_up"
},
{
type: "toggle",
text: "Mute",
topic: "onkyo_mute",
icon: "volume_off"
},
{
type: "section",
text: "Eingänge"
},
{
type: "dropDown",
text: "Inputs",
text: "Eingang",
topic: "onkyo_inputs",
options: {
netzwerk: "Netzwerk",
tisch: "Tisch",
chromecast: "Chromecast",
pult: "Pult"
}
},
icon: "settings_input_component"
},
{
type: "dropDown",
text: "Netzwerksender",
topic: "onkyo_radios",
options: {
mpd: "MPD",
kohina: "Kohina",
somafm_dronezone: "Drone Zone (SomaFM)",
somafm_thetrip: "The Trip (SomaFM)",
querfunk: "Querfunk",
somafm_defconradio: "Defcon Radio (SomaFM)",
somafm_secretagent: "Secret Agent (SomaFM)",
somafm_lush: "Lush (SomaFM)"
},
icon: "radio",
enableCondition: (a, b, state) => state.onkyo_inputs == "netzwerk"
}
]
},
rundumleuchte: {
name: "Rundumleuchte",
position: [310,220],
position: [310,275],
icon: "wb_sunny",
iconColor: state => state.rundumleuchte == "on" ? "#CCCC00" : "#000000",
ui: [
{
type: "toggle",
text: "Rundumleuchte",
topic: "rundumleuchte"
topic: "rundumleuchte",
icon: "power_settings_new"
}
]
},
door: {
name: "Tür",
position: [480,300],
position: [455,350],
icon: "swap_vert",
iconColor: state => state.door_status == "on" ? "#00FF00" : "#FF0000",
ui: []
},
infoscreen: {
name: "Infoscreen",
position: [255, 455],
position: [255, 495],
icon: "developer_board",
iconColor: state => state.infoscreen == "on" ? "#4444FF" : "#000000",
ui: [
{
type: "toggle",
text: "Infoscreen",
topic: "infoscreen"
topic: "infoscreen",
icon: "power_settings_new"
}
]
}

View file

@ -2,17 +2,25 @@
import React from "react";
import ReactDOM from "react-dom";
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import createMuiTheme from 'material-ui/styles/createMuiTheme';
import withStyles from 'material-ui/styles/withStyles';
import Drawer from 'material-ui/Drawer';
import injectTapEventPlugin from 'react-tap-event-plugin';
import { store } from "./state";
import { store, Actions } from "./state";
import connectMqtt from "./mqtt";
import AppBar from "./appbar";
import Toggle from "material-ui/Toggle";
import SpaceMapBar from "./appbar";
import Switch from "material-ui/Switch";
import * as UiItems from "./UiItems.js";
import SpaceMap from "./map.js";
import R from "ramda";
import Config from "./config";
import { Toolbar, ToolbarGroup, ToolbarTitle } from "material-ui/Toolbar";
import Toolbar from "material-ui/Toolbar";
import orange from 'material-ui/colors/orange';
import Typography from 'material-ui/Typography';
import List, { ListSubheader } from 'material-ui/List';
import IconButton from 'material-ui/IconButton';
import Icon from 'material-ui/Icon';
import AppBar from 'material-ui/AppBar';
injectTapEventPlugin();
@ -23,36 +31,60 @@ const renderUi = (state: State, key: ?string) =>
key != null && Config.controls[key] != null ?
R.map(UiItem(state), Config.controls[key].ui) : null;
const App = (state: State) => {
const theme = createMuiTheme({
palette: {
primary: orange
}
});
const appStyles = withStyles((theme) => ({
drawerPaper: {
width: 320
}
}));
class app extends React.Component<{state: State, classes: Object}> {
render() {
const state = this.props.state;
const classes = this.props.classes;
if (state == null) return (<div></div>);
return (
<div>
<MuiThemeProvider>
<MuiThemeProvider theme={theme}>
<div>
<AppBar title="RZL Map" {...state} />
<SpaceMapBar title="RZL Map" {...state} />
<Drawer open={state.uiOpened != null}
openSecondary={true} disableSwipeToOpen={true}>
anchor="right"
onRequestClose={() => store.dispatch({type: Actions.CHANGE_UI})}
classes={{paper: classes.drawerPaper}}
type="persistent"
>
<AppBar position="static">
<Toolbar>
<ToolbarGroup firstChild={true}>
<ToolbarTitle text={
state.uiOpened == null ? "" : Config.controls[state.uiOpened].name}
style={{"marginLeft": 10}} />
</ToolbarGroup>
<IconButton onClick={() => store.dispatch({type: Actions.CHANGE_UI})}>
<Icon>keyboard_tab</Icon>
</IconButton>
<Typography type="title">
{state.uiOpened == null ? "" : Config.controls[state.uiOpened].name}
</Typography>
</Toolbar>
<div id="drawer_uiComponents">
</AppBar>
<List id="drawer_uiComponents">
{renderUi(state, state.uiOpened)}
</div>
</List>
</Drawer>
</div>
</MuiThemeProvider>
<SpaceMap width={950} height={640} image="rzl.png" zoom={0.1} state={state} />
<SpaceMap width={1000} height={700} image="rzl.png" zoom={0.1} state={state} />
</div>
);}
);
}
}
const App = appStyles(app);
store.subscribe(() => ReactDOM.render(<App {...store.getState()} />, document.getElementById("content")));
store.subscribe(() => ReactDOM.render(<App state={store.getState()} />, document.getElementById("content")));
store.dispatch({type:null});
// 192.168.178.6
connectMqtt("ws://172.22.36.207:1884", store); // wss://mqtt.starletp9.de/mqtt", store);
connectMqtt("ws://172.22.36.207:1884", store);

View file

@ -8,7 +8,6 @@ import { Actions } from "./state";
import { keyOf } from "./util";
import { store } from "./state";
import ActionInfo from 'material-ui/svg-icons/action/info';
import ReactDOM from "react-dom";
// convert width/height coordinates to -height/width coordinates

View file

@ -55,7 +55,8 @@ const handleEvent = (state: State = initState, action: StateAction) => {
return match(action.type, {
[Actions.MQTT_CONNECT ]: R.merge(state, { mqtt: action.payload }),
[Actions.MQTT_MESSAGE ]: onMessage(state, action),
[Actions.CHANGE_UI ]: R.merge(state, { uiOpened: action.payload })
[Actions.CHANGE_UI ]: R.merge(state, { uiOpened: action.payload }),
[null]: state
});
}

View file

@ -10,9 +10,10 @@ declare type Topic = {
declare type Topics = Map<string,Topic>;
declare type ControlUI = {
type: "toggle" | "dropDown" | "slider",
type: "toggle" | "dropDown" | "slider" | "section",
text: string,
topic: string,
topic?: string,
icon?: string,
enableCondition?: (internal: string, actual: any) => boolean,
@ -23,6 +24,7 @@ declare type ControlUI = {
// DROPDOWN optional properties
options?: Map<string,any>, //options for dropDown
renderValue?: (value: string) => string
// SLIDER optional properties
min?: number,

1163
yarn.lock

File diff suppressed because it is too large Load diff