From b60b37dc2e6b6a1352c751ae5a1f67025132ffe9 Mon Sep 17 00:00:00 2001 From: Adam Driscoll Date: Thu, 4 Apr 2019 16:04:53 -0600 Subject: [PATCH] Move links and icons to Materialize module. (#728) * Move links and icons to Materialize module. * Add default framework id. Move error card. * Moving all materialize controls out of main module. * Fixing tests. * Fix default dashboard. * Remove font icon tests. * Fix some tests. * Fix cache tests. * Fix a few more tests. --- .../UniversalDashboard.MaterialUI.psm1 | 1 + src/UniversalDashboard.Materialize/.babelrc | 20 +- .../Components}/custom-cell.jsx | 5 +- .../Components/error-card.jsx | 16 + .../Components/index.js | 32 +- .../Components}/text-size.jsx | 0 .../Components/ud-chart.jsx | 241 + .../Components/ud-counter.jsx | 102 + .../Components/ud-footer.jsx | 38 + .../Components/ud-grid.jsx | 273 + .../Components/ud-icon.jsx | 9 + .../Components/ud-input-field.jsx | 301 + .../Components/ud-input.jsx | 203 + .../Components/ud-link.jsx | 24 + .../Components/ud-loading.jsx | 79 + .../Components/ud-monitor.jsx | 289 + .../Components/ud-navbar.jsx | 58 + .../Components/ud-navigation.jsx | 196 + .../Scripts/card.ps1 | 2 +- .../Scripts}/icon.ps1 | 0 .../Scripts/link.ps1 | 28 + .../Scripts/table.ps1 | 2 +- .../Tests/collapsible.tests.ps1 | 10 +- .../UniversalDashboard.Materialize.psm1 | 7 + src/UniversalDashboard.Materialize/build.ps1 | 3 + .../package-lock.json | 5509 +++++++++++------ .../package.json | 25 +- .../webpack.config.js | 4 +- .../Cmdlet/New-UDDashboard.Tests.ps1 | 3 +- .../Integration/Cache.Tests.ps1 | 2 + .../Integration/Counter.Tests.ps1 | 2 +- .../Integration/Dashboard.Tests.ps1 | 8 +- .../Integration/FontIcon.Tests.ps1 | 78 - .../Integration/Link.Tests.ps1 | 2 + .../Manifest.Tests.ps1 | 4 +- .../Cmdlets/Charting/NewChartCommand.cs | 2 +- .../Cmdlets/Charting/NewMonitorCommand.cs | 2 +- .../Cmdlets/NewCounterCommand.cs | 3 +- .../Cmdlets/NewDashboardCommand.cs | 16 +- .../Cmdlets/NewFooterCommand.cs | 3 +- .../Cmdlets/NewGridCommand.cs | 3 +- .../Cmdlets/NewImageCarouselCommand.cs | 54 - .../Cmdlets/NewImageCarouselItemCommand.cs | 63 - .../Cmdlets/NewLinkCommand.cs | 46 - src/UniversalDashboard/Models/Chart.cs | 2 +- src/UniversalDashboard/Models/Counter.cs | 5 +- src/UniversalDashboard/Models/Dashboard.cs | 7 +- src/UniversalDashboard/Models/Footer.cs | 3 +- src/UniversalDashboard/Models/Grid.cs | 5 +- src/UniversalDashboard/Models/Input.cs | 5 +- src/UniversalDashboard/Models/Link.cs | 23 - .../New-UDModuleManifest.ps1 | 7 +- .../Services/AssetService.cs | 10 + src/build.ps1 | 1 + src/client/package-lock.json | 144 +- src/client/package.json | 7 - src/client/src/app/basics/websocket.jsx | 93 - src/client/src/app/constants.js | 6 - src/client/src/app/error-card.jsx | 18 +- src/client/src/app/index.jsx | 1 - .../src/app/services/render-service.jsx | 2 + .../services/universal-dashboard-service.jsx | 4 +- src/client/src/app/ud-chart.jsx | 243 +- src/client/src/app/ud-counter.jsx | 102 +- src/client/src/app/ud-dashboard.jsx | 42 +- src/client/src/app/ud-footer.jsx | 36 +- src/client/src/app/ud-grid.jsx | 273 +- src/client/src/app/ud-icon.jsx | 9 +- src/client/src/app/ud-input-field.jsx | 300 +- src/client/src/app/ud-input.jsx | 203 +- src/client/src/app/ud-link.jsx | 24 +- src/client/src/app/ud-loading.jsx | 79 +- src/client/src/app/ud-monitor.jsx | 289 +- src/client/src/app/ud-navbar.jsx | 58 +- src/client/src/app/ud-navigation.jsx | 195 +- src/poshud/dashboard.ps1 | 2 +- src/poshud/pages/charts.ps1 | 2 +- src/poshud/pages/components.ps1 | 2 +- src/poshud/pages/counters.ps1 | 4 +- src/poshud/pages/icon.ps1 | 2 +- src/poshud/pages/monitors.ps1 | 2 +- 81 files changed, 5869 insertions(+), 4109 deletions(-) rename src/{client/src/app/basics => UniversalDashboard.Materialize/Components}/custom-cell.jsx (89%) create mode 100644 src/UniversalDashboard.Materialize/Components/error-card.jsx rename src/{client/src/app/basics => UniversalDashboard.Materialize/Components}/text-size.jsx (100%) create mode 100644 src/UniversalDashboard.Materialize/Components/ud-chart.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-counter.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-footer.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-grid.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-icon.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-input-field.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-input.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-link.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-loading.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-monitor.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-navbar.jsx create mode 100644 src/UniversalDashboard.Materialize/Components/ud-navigation.jsx rename src/{UniversalDashboard/Controls/src => UniversalDashboard.Materialize/Scripts}/icon.ps1 (100%) create mode 100644 src/UniversalDashboard.Materialize/Scripts/link.ps1 delete mode 100644 src/UniversalDashboard.UITest/Integration/FontIcon.Tests.ps1 delete mode 100644 src/UniversalDashboard/Cmdlets/NewImageCarouselCommand.cs delete mode 100644 src/UniversalDashboard/Cmdlets/NewImageCarouselItemCommand.cs delete mode 100644 src/UniversalDashboard/Cmdlets/NewLinkCommand.cs delete mode 100644 src/UniversalDashboard/Models/Link.cs delete mode 100644 src/client/src/app/basics/websocket.jsx delete mode 100644 src/client/src/app/constants.js diff --git a/src/UniversalDashboard.MaterialUI/UniversalDashboard.MaterialUI.psm1 b/src/UniversalDashboard.MaterialUI/UniversalDashboard.MaterialUI.psm1 index 64164a69..6529852a 100644 --- a/src/UniversalDashboard.MaterialUI/UniversalDashboard.MaterialUI.psm1 +++ b/src/UniversalDashboard.MaterialUI/UniversalDashboard.MaterialUI.psm1 @@ -16,6 +16,7 @@ $Maps | ForEach-Object { } $MUAssetId = [UniversalDashboard.Services.AssetService]::Instance.RegisterScript($JSFile.FullName) +[UniversalDashboard.Services.AssetService]::Instance.RegisterFramework("MaterialUI", $MUAssetId) # Load out controls Get-ChildItem (Join-Path $PSScriptRoot "Scripts") -File -Filter "*.ps1" | ForEach-Object { diff --git a/src/UniversalDashboard.Materialize/.babelrc b/src/UniversalDashboard.Materialize/.babelrc index db959b1f..edc2d8eb 100644 --- a/src/UniversalDashboard.Materialize/.babelrc +++ b/src/UniversalDashboard.Materialize/.babelrc @@ -1,3 +1,19 @@ { - "presets" : ["es2015", "react"] - } \ No newline at end of file + "presets" : [ + [ + "@babel/preset-env", { + "targets":{ + "browsers":[ + ">0.5%", + "not dead" + ] + } + } + ], + "@babel/preset-react" + ], + "plugins": [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-syntax-dynamic-import" + ] +} \ No newline at end of file diff --git a/src/client/src/app/basics/custom-cell.jsx b/src/UniversalDashboard.Materialize/Components/custom-cell.jsx similarity index 89% rename from src/client/src/app/basics/custom-cell.jsx rename to src/UniversalDashboard.Materialize/Components/custom-cell.jsx index c4e19989..b12fcf9e 100644 --- a/src/client/src/app/basics/custom-cell.jsx +++ b/src/UniversalDashboard.Materialize/Components/custom-cell.jsx @@ -1,6 +1,5 @@ import React from 'react'; import moment from 'moment'; -import renderComponent from './../services/render-service.jsx'; export default class CustomCell extends React.Component { render() { @@ -27,11 +26,11 @@ export default class CustomCell extends React.Component { } } else if (y.type && validComponents.indexOf(y.type) !== -1) { y.preventUnregister = true; - return renderComponent(y); + return UniversalDashboard.renderComponent(y); } else if (y.type) { y.preventUnregister = true; - return renderComponent(y); + return UniversalDashboard.renderComponent(y); }else if (typeof(y) === "boolean"){ y = y.toString(); } diff --git a/src/UniversalDashboard.Materialize/Components/error-card.jsx b/src/UniversalDashboard.Materialize/Components/error-card.jsx new file mode 100644 index 00000000..6ab16078 --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/error-card.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import {Card, Row, Col} from 'react-materialize'; +import UdIcon from './ud-icon.jsx'; + +export default class ErrorCard extends React.Component { + render() { + return + + +

{this.props.message}

+
{this.props.location}
+ +
+
+ } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/index.js b/src/UniversalDashboard.Materialize/Components/index.js index 5366470f..25257882 100644 --- a/src/UniversalDashboard.Materialize/Components/index.js +++ b/src/UniversalDashboard.Materialize/Components/index.js @@ -1,5 +1,35 @@ +import Materialize from "materialize-css"; + import TabContainer from './tabs'; +import UDChart from './ud-chart'; +import UDCounter from './ud-counter'; +import UDErrorCard from './error-card'; +import UDGrid from './ud-grid'; +import UDLink from './ud-link'; import UdImageCarousel from './ud-image-carousel'; +import UDIcon from './ud-icon'; +import UDNavbar from './ud-navbar'; +import UDNavigation from './ud-navigation'; +import UdFooter from './ud-footer'; +import UdInput from './ud-input'; +import UdInputField from './ud-input-field'; +import UdMonitor from './ud-monitor'; +import UDLoading from './ud-loading'; + +import("font-awesome/css/font-awesome.min.css" /* webpackChunkName: "font-awesome" */); +UniversalDashboard.register("ud-chart", UDChart); +UniversalDashboard.register("ud-counter", UDCounter); UniversalDashboard.register("tab-container", TabContainer); -UniversalDashboard.register("image-carousel", UdImageCarousel); \ No newline at end of file +UniversalDashboard.register("image-carousel", UdImageCarousel); +UniversalDashboard.register("ud-errorcard", UDErrorCard); +UniversalDashboard.register("ud-grid", UDGrid); +UniversalDashboard.register("ud-footer", UdFooter); +UniversalDashboard.register("ud-icon", UDIcon); +UniversalDashboard.register("ud-link", UDLink); +UniversalDashboard.register("ud-monitor", UdMonitor); +UniversalDashboard.register("ud-navbar", UDNavbar); +UniversalDashboard.register("ud-udnavigation", UDNavigation); +UniversalDashboard.register("ud-input", UdInput); +UniversalDashboard.register("ud-input-field", UdInputField); +UniversalDashboard.register("ud-loading", UDLoading); \ No newline at end of file diff --git a/src/client/src/app/basics/text-size.jsx b/src/UniversalDashboard.Materialize/Components/text-size.jsx similarity index 100% rename from src/client/src/app/basics/text-size.jsx rename to src/UniversalDashboard.Materialize/Components/text-size.jsx diff --git a/src/UniversalDashboard.Materialize/Components/ud-chart.jsx b/src/UniversalDashboard.Materialize/Components/ud-chart.jsx new file mode 100644 index 00000000..81877077 --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-chart.jsx @@ -0,0 +1,241 @@ +import React,{Suspense} from 'react'; +import {Row, Col} from 'react-materialize'; +import {Doughnut, Bar, Line, Polar, Radar, Pie, HorizontalBar} from 'react-chartjs-2'; +import ReactInterval from 'react-interval'; +import UdInputField from './ud-input-field.jsx'; + +const UdLinkComponent = React.lazy(() => import('./ud-link.jsx' /* webpackChunkName: "ud-link" */)) +const UdErrorCardComponent = React.lazy(() => import('./error-card.jsx' /* webpackChunkName: "ud-error-card" */)) + +export default class UdChart extends React.Component { + constructor(props) { + super(props); + + this.state = { + chartData: null, + errorMessage: "", + hasError: false, + fields: props.filterFields + } + } + + onIncomingEvent(eventName, event) { + if (event.type === "syncElement") { + this.loadData(); + } + } + + onChartClicked(elements) { + + if (!this.props.clickable) { + return; + } + + UniversalDashboard.publish('element-event', { + type: "clientEvent", + eventId: this.props.id + "onClick", + eventName: 'onClick', + eventData: JSON.stringify(elements.map(x => x._view)) + }); + } + + componentWillUnmount() { + UniversalDashboard.unsubscribe(this.pubSubToken); + } + + onValueChanged(fieldName, value) { + var fields = this.state.fields.map(function(x) { + if (x.name === fieldName) { + x.value = value; + } + + return x; + }); + + this.setState({ + fields: fields + }); + + this.loadData(); + } + + loadData(){ + + var queryString = ""; + + if (this.state.fields != null) { + queryString = "?" + for(var i = 0; i < this.state.fields.length; i++) { + var field = this.state.fields[i]; + + queryString += field.name + "=" + escape(field.value) + "&"; + } + + queryString = queryString.substr(0, queryString.length - 1); + } + + UniversalDashboard.get(`/api/internal/component/element/${this.props.id}${queryString}`, function(json){ + if (json.error) { + this.setState({ + hasError: true, + errorMessage: json.error.message + }) + } else { + this.setState({ + chartData: json + }); + } + }.bind(this)); + } + + componentWillMount() { + this.loadData(); + this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this)); + } + + renderArea() { + return + } + + renderDoughnut() { + return + } + + renderBar() { + return + } + + renderLine() { + return + } + + renderRadar() { + return + } + + renderPie() { + return + } + + renderHorizontalBar() { + return + } + + render() { + if (this.state.hasError) { + return [ + Loading...}> + , + + ] + } + + var fields = null; + + if (this.state.fields != null) { + fields = this.state.fields.map(x => { + return + }); + + fields = {fields} + } + + if(this.props.width !== null && this.props.height !== null){ + var cardStyle = { + background:this.props.backgroundColor, + color:this.props.fontColor, + width:this.props.width, + height:this.props.height, + marginBottom:'3rem' + } + var options = { + maintainAspectRatio: false, + layout:{ + padding:{ + bottom:25 + } + } + } + + this.props.options = {...options, ...this.props.options} + + } + else if(this.props.width !== null && this.props.height === null){ + var cardStyle = { + background:this.props.backgroundColor, + color:this.props.fontColor, + width:this.props.width, + } + } + else if(this.props.width === null && this.props.height !== null){ + return [ + Loading...}> + , + + ] + + } + else{ + var cardStyle = { + background:this.props.backgroundColor, + color:this.props.fontColor, + } + } + + var chart = null; + if (this.state.chartData != null) { + switch(this.props.chartType) { + // Bar + case 0: + chart = this.renderBar(); + break; + // Line + case 1: + chart = this.renderLine(); + break; + // Area + case 2: + chart = this.renderArea(); + break; + // Doughnut + case 3: + chart = this.renderDoughnut(); + break; + // Radar + case 4: + chart = this.renderRadar(); + break; + // Pie + case 5: + chart = this.renderPie(); + break; + // Horizontal Bar + case 6: + chart = this.renderHorizontalBar(); + break; + } + } + + var actions = null + if (this.props.links) { + var links = this.props.links.map(function(x, i) { + return Loading...}> + + + }) + actions =
+ {links} +
+ } + + return
+
+ {this.props.title} + {chart} + {fields} + +
+ {actions} +
+ } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/ud-counter.jsx b/src/UniversalDashboard.Materialize/Components/ud-counter.jsx new file mode 100644 index 00000000..aec36e3b --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-counter.jsx @@ -0,0 +1,102 @@ +import React from 'react'; +import UdIcon from './ud-icon.jsx'; +var numeral = require("numeral"); +import ReactInterval from 'react-interval'; +import ErrorCard from './error-card.jsx'; +import UdLink from './ud-link.jsx'; +import TextSize from './text-size.jsx'; + +export default class UdCounter extends React.Component { + constructor() { + super(); + + this.state = { + value: 0, + errorMessage: "", + hasError: false + } + } + + loadData() { + if (!this.props.id || this.props.id === "") { + return; + } + + UniversalDashboard.get(`/api/internal/component/element/${this.props.id}`,function(json){ + if (json.error) { + this.setState({ + hasError: true, + errorMessage: json.error.message + }) + } else { + this.setState({ + hasError: false, + value: json + }); + } + }.bind(this)); + } + + componentWillMount() { + this.loadData(); + this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this)); + } + + onIncomingEvent(eventName, event) { + if (event.type === "syncElement") { + this.loadData(); + } + } + + componentWillUnmount() { + UniversalDashboard.unsubscribe(this.pubSubToken); + } + + componentDidUpdate(prevProps) { + if (prevProps.id !== this.props.id) { + this.loadData(); + } + } + + render() { + if (this.state.hasError) { + return [, ] + } + + let icon = null; + if (this.props.icon !== 'none') { + var em = "6em"; + if (this.props.value == null) { + em = "4em"; + } + + icon = + } + + var value = numeral(this.state.value).format(this.props.format); + + var content =
+ {icon} + {value} +
+ + var actions = null + if (this.props.links) { + var links = this.props.links.map(function(x, i) { + return + }); + actions =
+ {links} +
+ } + + return
+
+ {this.props.title} + {content} +
+ {actions} + +
+ } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/ud-footer.jsx b/src/UniversalDashboard.Materialize/Components/ud-footer.jsx new file mode 100644 index 00000000..ab42ba10 --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-footer.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import UdLink from './ud-link.jsx' + +export default class UdFooter extends React.Component { + render() { + if (this.props.footer == null) { + return + } else { + var links = null; + if (this.props.footer.links) { + links = this.props.footer.links.map(function(x, i) { + return + }); + } + + var backgroundColor = this.props.footer.backgroundColor ? this.props.footer.backgroundColor : this.props.backgroundColor; + var fontColor = this.props.footer.fontColor ? this.props.footer.fontColor : this.props.fontColor; + + return + } + } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/ud-grid.jsx b/src/UniversalDashboard.Materialize/Components/ud-grid.jsx new file mode 100644 index 00000000..9f83dff9 --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-grid.jsx @@ -0,0 +1,273 @@ +import React from 'react'; +import ReactInterval from 'react-interval'; +import Griddle, { RowDefinition, ColumnDefinition, plugins } from 'griddle-react'; +import ErrorCard from './error-card.jsx'; +import UdLink from './ud-link.jsx'; +import CustomCell from './custom-cell.jsx'; +import {DebounceInput} from 'react-debounce-input'; + +function strMapToObj(strMap) { + if (strMap == undefined) return null; + if (strMap._tail != undefined && strMap._tail.array != undefined) { + return strMap._tail.array.map(x => strMapToObj(x)); + } + if (!strMap.__iterate) return strMap; + + let obj = Object.create({}); + for (let [k,v] of strMap) { + // We don’t escape the key '__proto__' + // which can cause problems on older engines + obj[k] = strMapToObj(v); + } + return obj; +} + +class CustomColumn extends React.Component { + render() { + var value = strMapToObj(this.props.value); + return ; + } +} + +export default class UdGrid extends React.Component { + constructor(props) { + super(props); + + var defaultSortColumn = props.defaultSortColumn; + if (props.defaultSortColumn == null && props.properties != null) { + defaultSortColumn = props.properties[0]; + } else if (defaultSortColumn == null) { + defaultSortColumn = '' + } + + this.state = { + data: [], + currentPage: 1, + pageSize: props.pageSize, + recordCount: 0, + hasError: false, + errorMessage: "", + sortColumn: defaultSortColumn, + sortAscending: !props.defaultSortDescending, + filterText: "", + properties: props.properties, + headers: props.headers + }; + } + + onIncomingEvent(eventName, event) { + if (event.type === "syncElement") { + this.reload(); + } + } + + componentWillUnmount() { + this.state.data.forEach(x => { + for(var propertyName in x) { + var property = x[propertyName]; + if (property.type === "element") { + for(var i = 0; i < property.events.length; i++) { + UniversalDashboard.publish('element-event', { + type: "unregisterEvent", + eventId: property.events[i].id + }); + } + + if (property.hasCallback) { + UniversalDashboard.publish('element-event', { + type: "unregisterEvent", + eventId: property.id + }); + } + } + } + }); + + UniversalDashboard.unsubscribe(this.pubSubToken); + } + + + reload() { + const { currentPage, pageSize, sortColumn, sortAscending, filterText } = this.state; + this.loadData(currentPage, pageSize, sortColumn, sortAscending, filterText); + } + + loadData(page, pageSize, sortColumn, sortAscending, filterText) { + var skip = (page - 1) * pageSize; + + UniversalDashboard.get(`/api/internal/component/datatable/${this.props.id}?start=${skip}&length=${pageSize}&sortColumn=${sortColumn}&sortAscending=${sortAscending}&filterText=${filterText}`, function(json){ + if (json.error) { + this.setState({ + hasError: true, + errorMessage: json.error.message + }) + } else { + if (!json.data.length) { + json.data = [json.data] + } + + if (json.data.length === 1 && json.data[0] === null) { + return + } + + if (json.data.length == 1 && typeof json.data[0] === 'string') { + return; + } + + if (Object.prototype.toString.call( json.data ) === '[object Array]' && json.data.length === 1) { + if (Object.prototype.toString.call( json.data[0] ) === '[object Array]' && json.data[0].length === 0) + return; + } + + this.updateTableState({ + data: json.data, + currentPage: page, + recordCount: json.recordsTotal, + sortColumn: sortColumn, + sortAscending: sortAscending, + filterText: filterText + }) + } + + }.bind(this)); + } + + updateTableState({data, currentPage, recordCount, sortColumn, sortAscending, filterText}) { + if (Object.prototype.toString.call( data ) !== '[object Array]' ) { + data = [] + } + this.setState({ data, currentPage, recordCount, hasError: false, sortColumn, sortAscending , filterText}); + } + + onSort(sortProperties) { + const { currentPage, pageSize, filterText } = this.state; + this.loadData(currentPage, pageSize, sortProperties.id, sortProperties.sortAscending, filterText); + } + + onFilter(e) { + const { currentPage, pageSize, sortColumn, sortAscending } = this.state; + this.loadData(currentPage, pageSize, sortColumn, sortAscending, e.target.value); + } + + onNextPage() { + const { currentPage, pageSize, sortColumn, sortAscending, filterText } = this.state; + this.loadData(currentPage + 1, pageSize, sortColumn, sortAscending, filterText); + } + + onPreviousPage() { + const { currentPage, pageSize, sortColumn, sortAscending, filterText } = this.state; + this.loadData(currentPage - 1, pageSize, sortColumn, sortAscending, filterText); + } + + onGetPage(pageNumber) { + const { pageSize, sortColumn, sortAscending, filterText } = this.state; + this.loadData(pageNumber, pageSize, sortColumn, sortAscending, filterText); + } + + componentWillMount() { + const { currentPage, pageSize, sortColumn, sortAscending, filterText } = this.state; + this.loadData(currentPage, pageSize, sortColumn, sortAscending, filterText); + this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this)); + } + + render() { + if (this.state.hasError) { + return [, ] + } + + var rowDefinition = null; + if (this.props.headers) { + var columns = this.props.headers.map(function(x, i) { + return + }.bind(this)); + + rowDefinition = {columns}; + } else if (this.state.data && this.state.data.length > 0) { + var columns = []; + var i = 0; + for(var key in this.state.data[0]) { + columns.push() + i++; + } + + rowDefinition = {columns}; + } + + const styleConfig = { + classNames: { + NextButton: "btn", + PreviousButton: "btn" + } + } + + var actions = null + if (this.props.links) { + var links = this.props.links.map(function(x, i) { + return + }); + actions =
+ {links} +
+ } + + var gridPlugins = []; + var serverSort, serverFilter, serverNext, serverPrev, serverGetPage, serverFilterControl; + var components = { + SettingsToggle: () => + } + var serverFilterControl = + + if (!this.props.serverSideProcessing) { + gridPlugins = [plugins.LocalPlugin] + serverFilterControl = null; + } else { + serverSort = this.onSort.bind(this); + serverFilter = this.onFilter.bind(this); + serverPrev = this.onPreviousPage.bind(this); + serverNext = this.onNextPage.bind(this); + serverGetPage = this.onGetPage.bind(this); + + components = { + Filter: () => , + SettingsToggle: () => + } + } + + return ( +
+
+ {this.props.title} + {serverFilterControl} + { rowDefinition ? + + + {rowDefinition} + :
} +
+ {actions} + +
+ ); + } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/ud-icon.jsx b/src/UniversalDashboard.Materialize/Components/ud-icon.jsx new file mode 100644 index 00000000..d365b235 --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-icon.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export default class UdIcon extends React.Component { + render() { + let iconClass = `fa fa-${this.props.icon}`; + + return ; + } +} \ No newline at end of file diff --git a/src/UniversalDashboard.Materialize/Components/ud-input-field.jsx b/src/UniversalDashboard.Materialize/Components/ud-input-field.jsx new file mode 100644 index 00000000..c1c446ef --- /dev/null +++ b/src/UniversalDashboard.Materialize/Components/ud-input-field.jsx @@ -0,0 +1,301 @@ +import React from 'react'; +import {Input as RInput} from 'react-materialize'; +import {DebounceInput} from 'react-debounce-input'; + +export default class UdInputField extends React.Component { + + constructor() { + super(); + + this.state = { + file: '' + } + } + + onSelectChanged(field, e) { + this.props.onValueChanged(field.name, e.target.value); + } + + onRadioChanged(field, e) { + this.props.onValueChanged(field.name, e.currentTarget.value); + } + + onTextFieldChange(field, e) { + this.props.onValueChanged(field.name, e.target.value); + } + + onCheckboxChanged(field, e) { + this.props.onValueChanged(field.name, e.target.checked); + } + + onFileChanged(field, e) { + var file = e.target; + var fileContent = ""; + var reader = new FileReader(); + + var _this = this; + reader.onload = function (e) { + fileContent = reader.result; + _this.props.onValueChanged(field.name, fileContent); + } + reader.readAsText(file.files[0]); + + var file = e.target.files[0].name; + this.setState({ file: file }); + + + } + + onValidateField(field, e) { + if (!this.props.validate) return; + + if (this.props.required && (!e.target.value || e.target.value === "")) { + this.props.onValidateComplete(field.name, `${field.name} is required.`); + return; + } + + this.props.onValidating(field.name); + + UniversalDashboard.post(`/api/internal/component/input/validate/${field.validationEndpoint}/${field.name}`, e.target.value, function(result) { + if (result.error != null) { + + var message = this.props.validationErrorMessage; + if (!message || message === "") { + message = result.error.message; + } + + this.props.onValidateComplete(field.name, message); + } + else + { + this.props.onValidateComplete(field.name); + } + }.bind(this)) + } + + setupDatePicker() { + var comp = this; + + $(this.datetime).pickadate({ + selectYears: 100, + clearText: this.props.clearText, + okText: this.props.okText, + cancelText: this.props.cancelText, + closeOnSelect: true, + onSet: function(e) { + // you can use any of the pickadate options here + var val = this.get('select', 'dd-mm-yyyy'); + comp.onTextFieldChange({name: comp.props.name}, {target: {value: val}}); + // auto close on select + //this.close(); + } + }) + } + + setupTimePicker() { + var comp = this; + + $(this.datetime).pickatime({ + clearText: this.props.clearText, + okText: this.props.okText, + cancelText: this.props.cancelText, + closeOnSelect: true, + onSet: function(e) { + var val = this.get('select'); + comp.onTextFieldChange({name: comp.props.name}, {target: {value: val}}); + // auto close on select + this.close(); + } + }) + } + + componentDidMount() { + if (Materialize && Materialize.updateTextFields) + { + Materialize.updateTextFields(); + } + + if (this.props.type === 'date') { + this.setupDatePicker(); + } + + if (this.props.type === 'time') { + this.setupTimePicker(); + } + + if (this.props.type === 'select') { + $('.select-wrapper').parent().removeClass("col"); + } + } + + updateTextField() { + if (this.textField != null) { + let $this = this.textField; + if ( + element.value.length > 0 || + $(element).is(':focus') || + element.autofocus || + $this.attr('placeholder') !== null + ) { + $this.siblings('label').addClass('active'); + } else if (element.validity) { + $this.siblings('label').toggleClass('active', element.validity.badInput === true); + } else { + $this.siblings('label').removeClass('active'); + } + } + } + + componentDidUpdate() { + this.updateTextField(); + + if (this.props.type === 'date') { + this.setupDatePicker(); + } + + if (this.props.type === 'time') { + this.setupTimePicker(); + } + + $('.tooltipped').tooltip(); + } + + onKeyDown(e) { + if (e.keyCode == 13) { this.props.onEnter(e); } + } + + render() { + var field = { + name: this.props.name, + type: this.props.type, + value: this.props.value, + placeholder: this.props.placeholder, + validOptions: this.props.validOptions, + validating: this.props.validating, + validationError: this.props.validationError, + validationEndpoint: this.props.validationEndpoint + } + + if (field.type === 'textbox' || field.type === 'password') { + var type = field.type == 'textbox' ? 'text' : 'password'; + + var validationIcon = null; + if (this.props.validating) { + validationIcon = + } + else if (this.props.validationError != null) { + validationIcon = + } + + var textfield = null; + if (this.props.debounceTimeout == null) { + + textfield = this.onTextFieldChange(field, e) } value={field.value} onBlur={e => this.onValidateField(field, e)} onKeyDown={this.onKeyDown.bind(this)} ref={ref => this.textField}/> + } else { + textfield = this.onTextFieldChange(field, e) } value={field.value} debounceTimeout={this.props.debounceTimeout} onKeyDown={this.onKeyDown.bind(this)}/> + } + + return
+ {validationIcon} + {textfield} + +
+ } + + if (field.type === 'textarea') { + return
+