diff --git a/api/models/Display.js b/api/models/Display.js index 5b6defd..3208045 100644 --- a/api/models/Display.js +++ b/api/models/Display.js @@ -1,12 +1,19 @@ const mongoose = require('mongoose') +const shortid = require('shortid') + const Schema = mongoose.Schema const Display = new Schema({ name: { type: String }, layout: { type: String, default: 'spaced', enum: ['compact', 'spaced'] }, statusBar: { - type: [{ type: String, enum: ['time', 'date', 'connection', 'spacer'] }], - default: ['date', 'spacer', 'connection', 'time'] + type: [{ type: String }], + default: () => [ + 'date_' + shortid.generate(), + 'spacer_' + shortid.generate(), + 'connection_' + shortid.generate(), + 'time_' + shortid.generate() + ] }, widgets: [{ type: Schema.Types.ObjectId, ref: 'Widget' }] }) diff --git a/components/Admin/EditableWidget.js b/components/Admin/EditableWidget.js index 9275499..e274f28 100644 --- a/components/Admin/EditableWidget.js +++ b/components/Admin/EditableWidget.js @@ -58,7 +58,6 @@ class EditableWidget extends React.Component { {widget.name || 'Broken Widget'} - {/* NEWS */} diff --git a/components/Admin/StatusBarElement.js b/components/Admin/StatusBarElement.js new file mode 100644 index 0000000..d491f97 --- /dev/null +++ b/components/Admin/StatusBarElement.js @@ -0,0 +1,127 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { library, config } from '@fortawesome/fontawesome-svg-core' +import { faTimes } from '@fortawesome/free-solid-svg-icons' +import { Draggable } from 'react-beautiful-dnd' + +import { StatusBarElementTypes } from '../../helpers/statusbar.js' + +config.autoAddCss = false +library.add(faTimes) + +class StatusBarElement extends React.Component { + constructor(props) { + super(props) + } + + deleteClicked = e => { + if (e) e.stopPropagation() + const { onDelete = () => {} } = this.props + onDelete() + } + + render() { + const { item, index } = this.props + const type = item.split('_')[0] + return ( + + {provided => ( +
+
+
+ +
+
+
+
+ +
+ {type || 'Unknown'} +
+ +
+ )} +
+ ) + } +} + +export default StatusBarElement diff --git a/components/Display/Frame.js b/components/Display/Frame.js index 5b47ee9..8c6aedc 100644 --- a/components/Display/Frame.js +++ b/components/Display/Frame.js @@ -13,41 +13,28 @@ class Frame extends React.Component { super(props) } - renderStatusBarElement = type => { - return ( -
- {type == 'date' ? ( - - ) : type == 'connection' ? ( - - ) : type == 'time' ? ( - - ) : ( - ' ' - )} -
- ) - } - render() { const { children, statusBar = [] } = this.props return (
{statusBar && statusBar.length > 0 && (
- {statusBar.map(type => ( -
- {type == 'date' ? ( - - ) : type == 'connection' ? ( - - ) : type == 'time' ? ( - - ) : ( - ' ' - )} -
- ))} + {statusBar.map(item => { + const type = item.split('_')[0] + return ( +
+ {type == 'date' ? ( + + ) : type == 'connection' ? ( + + ) : type == 'time' ? ( + + ) : ( + ' ' + )} +
+ ) + })}
)} {children} diff --git a/helpers/statusbar.js b/helpers/statusbar.js new file mode 100644 index 0000000..f1df6ad --- /dev/null +++ b/helpers/statusbar.js @@ -0,0 +1,27 @@ +import { library, config } from '@fortawesome/fontawesome-svg-core' +import { faRss, faGripVertical, faClock, faCalendarAlt } from '@fortawesome/free-solid-svg-icons' + +config.autoAddCss = false +library.add(faRss) +library.add(faGripVertical) +library.add(faClock) +library.add(faCalendarAlt) + +export const StatusBarElementTypes = { + time: { + name: 'time', + icon: faClock + }, + date: { + name: 'date', + icon: faCalendarAlt + }, + spacer: { + name: 'spacer', + icon: faGripVertical + }, + connection: { + name: 'connection', + icon: faRss + } +} diff --git a/package.json b/package.json index cbad24a..8e74b40 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "passport-local": "^1.0.0", "passport-local-mongoose": "^5.0.1", "react": "^16.8.6", + "react-beautiful-dnd": "^11.0.4", "react-color": "^2.17.0", "react-content-loader": "^4.2.1", "react-dom": "^16.8.2", @@ -74,10 +75,12 @@ "react-sortable-hoc": "^1.8.3", "react-switch": "^5.0.0", "react-youtube": "^7.9.0", + "shortid": "^2.2.14", "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", "styled-components": "^4.2.0", "superagent": "^5.0.5", + "uuid": "^3.3.2", "webfontloader": "^1.6.28" }, "devDependencies": { diff --git a/pages/index.js b/pages/index.js index 11e784a..6d4fbc7 100644 --- a/pages/index.js +++ b/pages/index.js @@ -22,7 +22,7 @@ class Index extends React.Component { } navigateToDisplay = id => { - Router.push('/display?display=' + id) + Router.push('/display/' + id) } render() { diff --git a/pages/layout.js b/pages/layout.js index bcda892..3492005 100644 --- a/pages/layout.js +++ b/pages/layout.js @@ -1,15 +1,20 @@ import React from 'react' -import { faThLarge, faTh } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faThLarge, faTh, faPencilAlt } from '@fortawesome/free-solid-svg-icons' import GridLayout from 'react-grid-layout' import { view } from 'react-easy-state' +import { DragDropContext, Droppable } from 'react-beautiful-dnd' import Frame from '../components/Admin/Frame.js' import EditableWidget from '../components/Admin/EditableWidget' +import StatusBarElement from '../components/Admin/StatusBarElement' import WidthProvider from '../components/Widgets/WidthProvider' import DropdownButton from '../components/DropdownButton' import { Form, Switch } from '../components/Form' +import { StatusBarElementTypes } from '../helpers/statusbar.js' + import Widgets from '../widgets' import { addWidget, getWidgets, deleteWidget, updateWidget } from '../actions/widgets' @@ -66,6 +71,14 @@ class Layout extends React.Component { } } + onDragEnd = result => { + if (!result.destination) { + return + } + + display.reorderStatusBarItems(result.source.index, result.destination.index) + } + render() { const { widgets } = this.state const { loggedIn } = this.props @@ -81,6 +94,70 @@ class Layout extends React.Component {

Layout

+
+ { + const target = event.target + const title = target && target.value + display.updateName(title) + }} + onClick={e => { + if (e) e.stopPropagation() + }} + size={display && display.name && display.name.length} + /> +
+ +
+
+
+
+ ({ + key: statusBarEl, + name: StatusBarElementTypes[statusBarEl].name, + icon: StatusBarElementTypes[statusBarEl].icon + }))} + /> +
+
+ {display && display.statusBar && ( + + + {provided => ( +
+ {display.statusBar.map((item, index) => ( + + ))} + {provided.placeholder} +
+ )} +
+
+ )}
diff --git a/routes.js b/routes.js index 5643335..7cd43d8 100644 --- a/routes.js +++ b/routes.js @@ -1,3 +1,5 @@ const routes = require('next-routes') -module.exports = routes().add('/slideshow/:id', 'slideshow') +module.exports = routes() + .add('/slideshow/:id', 'slideshow') + .add('/display/:display', 'display') diff --git a/stores/display.js b/stores/display.js index 243cff5..6c3e31f 100644 --- a/stores/display.js +++ b/stores/display.js @@ -2,6 +2,7 @@ const ReactEasyState = require('react-easy-state') const store = ReactEasyState.store const _ = require('lodash') const DisplayActions = require('../actions/display') +const shortid = require('shortid') const updateDisplayThrottled = _.debounce((id, data) => { return DisplayActions.updateDisplay(id, data) @@ -35,6 +36,23 @@ const display = store({ if (!layout || !['spaced', 'compact'].includes(layout)) return display.layout = layout updateDisplayThrottled(display.id, { layout }) + }, + addStatusBarItem(type) { + display.statusBar = [...display.statusBar, type + '_' + shortid.generate()] + updateDisplayThrottled(display.id, { statusBar: display.statusBar }) + return Promise.resolve() + }, + removeStatusBarItem(id) { + display.statusBar = [...display.statusBar.slice(0, id).concat(display.statusBar.slice(id + 1))] + updateDisplayThrottled(display.id, { statusBar: display.statusBar }) + }, + reorderStatusBarItems(startIndex, endIndex) { + const result = Array.from(display.statusBar) + const [removed] = result.splice(startIndex, 1) + result.splice(endIndex, 0, removed) + + display.statusBar = result + updateDisplayThrottled(display.id, { statusBar: display.statusBar }) } })