├── examples
├── published-folders
│ ├── files
│ │ └── new-file.txt
│ ├── published-folders.png
│ └── dashboard.ps1
├── input-dynamic-pages.png
├── input
│ ├── toast.ps1
│ ├── custom-help-text.ps1
│ ├── new-component.ps1
│ ├── input-types.ps1
│ └── dynamic-page.ps1
├── service-table.ps1
├── formatting
│ ├── dynamic.ps1
│ ├── layout.ps1
│ └── rows-and-columns.ps1
├── service-cards.ps1
├── login-pages
│ ├── microsoft.ps1
│ └── forms.ps1
├── grid
│ ├── simple.ps1
│ ├── custom-search.ps1
│ ├── custom-columns.ps1
│ └── high-performance-sql-grid.ps1
├── charts
│ ├── legend.ps1
│ ├── title.ps1
│ ├── custom-axis.ps1
│ ├── multiple-datasets.ps1
│ ├── tooltips.ps1
│ └── colors.ps1
├── login-page.ps1
├── ad-onboarding-dashboard.ps1
├── ping-grid.ps1
├── input-dynamic-pages.ps1
└── tree-view
│ └── filesystem.ps1
├── src
├── templates
│ ├── Blank.ps1
│ ├── Multiple Pages.ps1
│ └── Rows and Columns.ps1
├── favicon.ico
├── UniversalDashboard.psd1
├── Scripts
│ ├── interactive
│ │ ├── Hide-Modal.ps1
│ │ ├── Hide-Toast.ps1
│ │ ├── Invoke-JavaScript.ps1
│ │ ├── Invoke-Redirect.ps1
│ │ ├── Select-Element.ps1
│ │ ├── Get-Element.ps1
│ │ ├── Clear-Element.ps1
│ │ ├── Set-Clipboard.ps1
│ │ ├── Start-Download.ps1
│ │ ├── Sync-Element.ps1
│ │ ├── Remove-Element.ps1
│ │ ├── Add-Element.ps1
│ │ ├── Set-Element.ps1
│ │ └── Show-Modal.ps1
│ ├── map
│ │ ├── featuregroup.ps1
│ │ ├── layercontrol.ps1
│ │ ├── baselayer.ps1
│ │ ├── overlay.ps1
│ │ ├── cluster-layer.ps1
│ │ ├── icon.ps1
│ │ ├── heatmap.ps1
│ │ ├── popup.ps1
│ │ ├── rasterlayer.ps1
│ │ ├── marker.ps1
│ │ └── map.ps1
│ ├── iframe.ps1
│ ├── span.ps1
│ ├── editor.ps1
│ ├── event.ps1
│ ├── error.ps1
│ ├── errorboundary.ps1
│ ├── hidden.ps1
│ ├── splitpane.ps1
│ ├── paragraph.ps1
│ ├── container.ps1
│ ├── alert.ps1
│ ├── backdrop.ps1
│ ├── heading.ps1
│ ├── skeleton.ps1
│ ├── avatar.ps1
│ ├── style.ps1
│ ├── stack.ps1
│ ├── badge.ps1
│ ├── debugdashboard.ps1
│ ├── timepicker.ps1
│ ├── tooltip.ps1
│ ├── layout.ps1
│ ├── icon-button.ps1
│ ├── paper.ps1
│ ├── timeline.ps1
│ ├── datetime.ps1
│ ├── progress.ps1
│ └── drawer.ps1
├── Components
│ ├── framework
│ │ ├── loading.jsx
│ │ ├── not-authorized.jsx
│ │ ├── togglecolormodes.jsx
│ │ ├── not-found.jsx
│ │ ├── not-running.jsx
│ │ ├── ud-link.jsx
│ │ ├── error-card.jsx
│ │ └── ud-modal.jsx
│ ├── table
│ │ ├── v2
│ │ │ ├── Alert.js
│ │ │ ├── EditableCell.js
│ │ │ ├── table.js
│ │ │ └── columns.js
│ │ └── components
│ │ │ ├── baseTableFooter.js
│ │ │ └── baseTableHead.js
│ ├── schema-form.jsx
│ ├── grid.jsx
│ ├── container.jsx
│ ├── hidden.jsx
│ ├── card-header.jsx
│ ├── icon.jsx
│ ├── map
│ │ ├── utils.jsx
│ │ ├── raster-layer.jsx
│ │ ├── popup.jsx
│ │ ├── MarkerCluster.css
│ │ ├── index.js
│ │ ├── MarkerCluster.Default.css
│ │ ├── cluster-layer.jsx
│ │ ├── heatmap-layer.jsx
│ │ ├── UDLayerControlBaseLayer.jsx
│ │ └── UDLayerControlOverlay.jsx
│ ├── stack.jsx
│ ├── avatar.jsx
│ ├── alert.jsx
│ ├── utilities.jsx
│ ├── datetime.jsx
│ ├── backdrop.jsx
│ ├── style.jsx
│ ├── badge.jsx
│ ├── skeleton.jsx
│ ├── charts
│ │ ├── nivo-pie.jsx
│ │ ├── nivo-line.jsx
│ │ ├── nivo-bar.jsx
│ │ ├── nivo-bubble.jsx
│ │ ├── nivo-stream.jsx
│ │ ├── nivo-heatmap.jsx
│ │ ├── nivo-treemap.jsx
│ │ ├── nivo-calendar.jsx
│ │ ├── index.js
│ │ └── sparklines.jsx
│ ├── card-body.jsx
│ ├── card-media.jsx
│ ├── link.jsx
│ ├── drawer.jsx
│ ├── progress.jsx
│ ├── paper.jsx
│ ├── icon-button.jsx
│ ├── slider.jsx
│ ├── dynamic.jsx
│ ├── radio.jsx
│ ├── transition.jsx
│ ├── button.jsx
│ ├── timeline.jsx
│ ├── floating-action-button.jsx
│ ├── editor.jsx
│ ├── switch.jsx
│ ├── card-expand.jsx
│ ├── datepicker.jsx
│ ├── checkbox.jsx
│ ├── timepicker.jsx
│ ├── code-editor.jsx
│ └── menu.jsx
├── app
│ ├── ud-html.jsx
│ ├── component-registration.js
│ ├── app-context.jsx
│ ├── public-path.js
│ ├── log-service.jsx
│ ├── index.jsx
│ ├── services
│ │ ├── toaster.jsx
│ │ └── render-service.jsx
│ ├── config.jsx
│ ├── ud-tooltip.jsx
│ ├── ud-icon.jsx
│ ├── error-boundary.jsx
│ ├── polyfills.js
│ └── basics
│ │ └── lazy-element.jsx
├── classes
│ ├── classes.csproj
│ ├── Cmdlets
│ │ ├── NewNivoFillCommand.cs
│ │ └── NewNivoChartAxisOptionsCommand.cs
│ └── classes.sln
├── .babelrc
├── .gitignore
└── index.html
├── images
├── logo.png
└── splash.png
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .vscode
└── tasks.json
├── CHANGELOG.md
├── azure-pipelines.yml
├── README.md
└── docs
└── working-on-a-component.md
/examples/published-folders/files/new-file.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/Blank.ps1:
--------------------------------------------------------------------------------
1 | New-UDDashboard -Title 'PowerShell Universal' -Content {
2 |
3 | }
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/images/logo.png
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/images/splash.png
--------------------------------------------------------------------------------
/src/UniversalDashboard.psd1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/src/UniversalDashboard.psd1
--------------------------------------------------------------------------------
/examples/input-dynamic-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/examples/input-dynamic-pages.png
--------------------------------------------------------------------------------
/src/Scripts/interactive/Hide-Modal.ps1:
--------------------------------------------------------------------------------
1 | function Hide-UDModal {
2 | $DashboardHub.SendWebSocketMessage($ConnectionId, "closeModal", $null)
3 | }
--------------------------------------------------------------------------------
/examples/published-folders/published-folders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironmansoftware/universal-dashboard/HEAD/examples/published-folders/published-folders.png
--------------------------------------------------------------------------------
/src/Components/framework/loading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class Loading extends React.Component {
4 | render() {
5 | return
6 | }
7 | }
--------------------------------------------------------------------------------
/src/app/ud-html.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class UdHtml extends React.Component {
4 | render() {
5 | return
6 | }
7 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report a Bug for Universal Dashboard
4 | labels: bug
5 | ---
6 |
7 | # Please file issues on the [PowerShell Universal](https://github.com/ironmansoftware/powershell-universal/issues) issue tracker.
8 |
--------------------------------------------------------------------------------
/src/app/component-registration.js:
--------------------------------------------------------------------------------
1 | import UDSplitPane from './ud-splitpane';
2 | import UDTooltip from './ud-tooltip';
3 | require ('./../Components/index.js');
4 |
5 | UniversalDashboard.register("ud-splitpane", UDSplitPane);
6 | UniversalDashboard.register("ud-tooltip", UDTooltip);
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Enhancement Request
3 | about: Suggest an Enhancement for Universal Dashboard
4 | labels: enhancement
5 | ---
6 |
7 | # Please file issues on the [PowerShell Universal](https://github.com/ironmansoftware/powershell-universal/issues) issue tracker.
8 |
--------------------------------------------------------------------------------
/src/app/app-context.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const theme = sessionStorage.getItem('theme')
4 |
5 | export const DefaultAppContext = {
6 | theme: theme ? theme : 'light',
7 | setTheme: () => {}
8 | }
9 |
10 | export const AppContext = React.createContext(DefaultAppContext);
--------------------------------------------------------------------------------
/src/Components/table/v2/Alert.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Alert, AlertTitle } from '@mui/material';
3 |
4 | export default function AlertCard({ error }) {
5 | return (
6 |
7 | Error
8 | {error.message}
9 |
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/Components/schema-form.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { MuiForm5 } from "@rjsf/material-ui";
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | function SchemaForm(props) {
6 | return
7 | }
8 |
9 | export default withComponentFeatures(SchemaForm);
--------------------------------------------------------------------------------
/src/Components/grid.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '@mui/material/Grid';
3 |
4 | const UDGrid = (props) => {
5 | return (
6 |
7 | { props.children && UniversalDashboard.renderComponent(props.children)}
8 |
9 | )
10 | }
11 |
12 | export default UDGrid;
--------------------------------------------------------------------------------
/src/Components/container.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Container from '@mui/material/Container';
3 | import { withComponentFeatures } from 'universal-dashboard'
4 |
5 | const UDContainer = props => {
6 | return {props.render(props.children)}
7 | }
8 |
9 | export default withComponentFeatures(UDContainer)
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Build and Test",
6 | "type": "shell",
7 | "command": "pwsh -Command ' & { Invoke-Build -File ${workspaceFolder}/src/build.ps1 }' ",
8 | "group": "build",
9 | "problemMatcher": []
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/src/Components/hidden.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Hidden from '@mui/material/Hidden';
3 |
4 | const UDHidden = (props) => {
5 | return (
6 |
7 | { props.children && UniversalDashboard.renderComponent(props.children)}
8 |
9 | )
10 | }
11 |
12 | export default UDHidden;
--------------------------------------------------------------------------------
/src/Components/card-header.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CardHeader from '@mui/material/CardHeader';
3 |
4 | const UDCardHeader = (props) => {
5 | return (
6 |
7 | )
8 | }
9 |
10 | export default UDCardHeader;
--------------------------------------------------------------------------------
/src/Scripts/interactive/Hide-Toast.ps1:
--------------------------------------------------------------------------------
1 | function Hide-UDToast {
2 | param(
3 | [Parameter(Mandatory, Position = 0)]
4 | [string]$Id
5 | )
6 |
7 | if ($id -notmatch '^[a-zA-Z0-9]+$') {
8 | throw "Invalid ID. ID must be alphanumeric."
9 | }
10 |
11 | $DashboardHub.SendWebSocketMessage($ConnectionId, "hideToast", "x" + $Id)
12 | }
13 |
--------------------------------------------------------------------------------
/src/classes/classes.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Components/table/v2/EditableCell.js:
--------------------------------------------------------------------------------
1 | import { Input } from '@mui/material'
2 |
3 | export default function EditableCell({
4 | cell: { value },
5 | row: { index },
6 | column: { id },
7 | updateData,
8 | inputProps,
9 | }) {
10 | const onChange = (event) => {
11 | updateData(index, id, event.target.value)
12 | }
13 |
14 | return
15 | }
--------------------------------------------------------------------------------
/src/app/public-path.js:
--------------------------------------------------------------------------------
1 | if (document.getElementsByTagName('base')[0].href.indexOf("_BASEHREF_") == -1)
2 | {
3 | __webpack_public_path__ = document.getElementsByTagName('base')[0].href;
4 | window.baseUrl = document.getElementsByTagName('base')[0].href.replace(window.location.origin, "").replace(/\/$/, "");;
5 | }
6 | else
7 | {
8 | __webpack_public_path__ = "/"
9 | window.baseUrl = ""
10 | }
--------------------------------------------------------------------------------
/examples/input/toast.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of returning a toast message to the user.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Input - Toast" -Content {
8 | New-UDInput -Title "Input" -Endpoint {
9 | param($Text)
10 |
11 | New-UDInputAction -Toast $Text -ClearInput
12 | }
13 | }
14 |
15 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/service-table.ps1:
--------------------------------------------------------------------------------
1 | Start-UDDashboard -Content {
2 | New-UDDashboard -Title "Server Performance Dashboard" -Color '#FF050F7F' -Content {
3 | New-UDTable -Title "Server Information" -Headers @("Name", "CommandLine", "Status") -Endpoint {
4 | Get-Service | Select Name,CommandLine,Status | Out-UDTableData -Property @("Name", "CommandLine", "Status")
5 | }
6 | }
7 | } -Port 1001
8 |
9 |
--------------------------------------------------------------------------------
/examples/input/custom-help-text.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of providing custom help text on the input
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Input - Custom Help Text" -Content {
8 | New-UDInput -Title "Input" -Endpoint {
9 | param([Parameter(HelpMessage="Enter your text here")]$Text)
10 | }
11 | }
12 |
13 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/icon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react'
3 | import classNames from "classnames"
4 |
5 | export default class UDIcon extends React.Component {
6 | render() {
7 | return UniversalDashboard.renderComponent({
8 | className: classNames(this.props.className, "ud-mu-icon"),
9 | style: { ...this.props.style },
10 | ...this.props,
11 | type: 'icon'
12 | })
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Components/map/utils.jsx:
--------------------------------------------------------------------------------
1 | export function isGuid(str) {
2 | if (str == null) { return false }
3 |
4 | if (str[0] === "{")
5 | {
6 | str = str.substring(1, str.length - 1);
7 | }
8 | var regexGuid = /^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$/gi;
9 | var regexGuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
10 | return regexGuid.test(str);
11 | }
--------------------------------------------------------------------------------
/src/Components/stack.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Stack } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | function UDStack(props) {
6 |
7 | const children = props.render(props.children);
8 | let divider = null;
9 | if (props.divider) {
10 | divider = props.render(props.divider)
11 | }
12 |
13 | return {children}
14 | }
15 |
16 | export default withComponentFeatures(UDStack);
--------------------------------------------------------------------------------
/examples/input/new-component.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of returning a new component after taking input.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Input - New Component" -Content {
8 | New-UDInput -Title "Input" -Endpoint {
9 | param($Text)
10 |
11 | New-UDInputAction -Content @(
12 | New-UDCard -Title $Text -Text "This is text"
13 | )
14 | }
15 | }
16 |
17 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/avatar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import withStyles from '@mui/styles/withStyles';
4 | import Avatar from '@mui/material/Avatar';
5 | import classNames from 'classnames'
6 |
7 | function UDMuAvatar(props) {
8 |
9 | const { classes } = props;
10 |
11 | return (
12 |
13 | );
14 | }
15 |
16 | export default UDMuAvatar;
17 |
--------------------------------------------------------------------------------
/src/app/log-service.jsx:
--------------------------------------------------------------------------------
1 | import {fetchPost} from './services/fetch-service.jsx';
2 |
3 | function Log(component, level, message) {
4 | fetchPost(`/log/${component}/${level}`, {message: message})
5 | }
6 |
7 | export function LogDebug(component, message) {
8 | Log(component, "debug", message);
9 | }
10 |
11 | export function LogError(component, message) {
12 | Log(component, "error", message);
13 | }
14 |
15 | export function LogInfo(component, message) {
16 | Log(component, "info", message);
17 | }
--------------------------------------------------------------------------------
/examples/input/input-types.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of the different type of input types.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Input - Input Types" -Content {
8 | New-UDInput -Title "Input" -Endpoint {
9 | param($Text, [bool]$CheckBox, [System.DayOfWeek]$SelectBox, [ValidateSet("One", "Two", "Three")]$SelectBox2)
10 |
11 | New-UDInputAction -Toast "Clicked!"
12 | }
13 | }
14 |
15 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/alert.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Alert, AlertTitle } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | function UDAlert(props) {
6 | return
7 | {props.title && props.title !== "" ? {props.title} : }
8 | {props.render(props.children)}
9 |
10 | }
11 |
12 | export default withComponentFeatures(UDAlert);
--------------------------------------------------------------------------------
/src/Scripts/map/featuregroup.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapFeatureGroup
2 | {
3 | param(
4 | [Parameter()]
5 | [string]$Id = (New-Guid).ToString(),
6 | [Parameter()]
7 | [Hashtable]$Popup,
8 | [Parameter(Mandatory)]
9 | [ScriptBlock]$Content
10 | )
11 |
12 | End {
13 | @{
14 | type = 'map-feature-group'
15 | id = $id
16 | isPlugin = $true
17 | assetId = $AssetId
18 | content = & $Content
19 | popup = $Popup
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Components/map/raster-layer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TileLayer } from 'react-leaflet';
3 | import BingLayer from './bing-layer';
4 |
5 | export default class UDRasterLayer extends React.Component {
6 | render() {
7 | if (this.props.bing) {
8 | return
9 | } else {
10 | return
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Components/utilities.jsx:
--------------------------------------------------------------------------------
1 | import {useEffect, useRef} from 'react';
2 |
3 | export function useTraceUpdate(props) {
4 | const prev = useRef(props);
5 | useEffect(() => {
6 | const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
7 | if (prev.current[k] !== v) {
8 | ps[k] = [prev.current[k], v];
9 | }
10 | return ps;
11 | }, {});
12 | if (Object.keys(changedProps).length > 0) {
13 | console.log('Changed props:', changedProps);
14 | }
15 | prev.current = props;
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/Scripts/map/layercontrol.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapLayerControl {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter()]
6 | [ValidateSet("topright", "topleft", "bottomright", "bottomleft")]
7 | [string]$Position = "topright",
8 | [Parameter()]
9 | [ScriptBlock]$Content
10 | )
11 |
12 | @{
13 | type = 'map-layer-control'
14 | isPlugin = $true
15 | assetId = $AssetId
16 | id = $Id
17 | content = & $Content
18 | position = $Position
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Scripts/map/baselayer.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapBaseLayer {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter(Mandatory)]
6 | [string]$Name,
7 | [Parameter(Mandatory)]
8 | [ScriptBlock]$Content,
9 | [Parameter()]
10 | [Switch]$Checked
11 | )
12 |
13 | @{
14 | type = "map-base-layer"
15 | isPlugin = $true
16 | assetId = $AssetId
17 | id = $Id
18 | name = $Name
19 | content = & $Content
20 | checked = $Checked.IsPresent
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Scripts/map/overlay.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapOverlay {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter(Mandatory)]
6 | [string]$Name,
7 | [Parameter(Mandatory)]
8 | [ScriptBlock]$Content,
9 | [Parameter()]
10 | [Switch]$Checked
11 | )
12 |
13 | @{
14 | type = 'map-overlay'
15 | isPlugin = $true
16 | assetId = $AssetId
17 |
18 | id = $id
19 | name = $Name
20 | content = & $Content
21 | checked = $Checked.IsPresent
22 | }
23 | }
--------------------------------------------------------------------------------
/examples/formatting/dynamic.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an of example of using a dynamic column.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Formatting - Layout" -Content {
8 | New-UDRow -Columns {
9 | New-UDColumn -Size 12 -AutoRefresh -RefreshInterval 5 -Endpoint {
10 | $Max = Get-Random -Minimum 1 -Maximum 12
11 |
12 | 1..$Max | ForEach-Object {
13 | New-UDCard -Title "Card $_"
14 | }
15 | }
16 |
17 | }
18 | }
19 |
20 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/formatting/layout.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of using a simple layout.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Formatting - Layout" -Content {
8 | New-UDLayout -Columns 3 -Content {
9 | New-UDCard -Title "Card 1"
10 | New-UDCard -Title "Card 2"
11 | New-UDCard -Title "Card 3"
12 | New-UDCard -Title "Card 4"
13 | New-UDCard -Title "Card 5"
14 | New-UDCard -Title "Card 6"
15 | New-UDCard -Title "Card 7"
16 | }
17 | }
18 |
19 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "browsers": [
8 | ">1.0%",
9 | "not dead",
10 | "IE 11"
11 | ]
12 | }
13 | }
14 | ],
15 | "babel-preset-react-app"
16 | ],
17 | "plugins": [
18 | [
19 | "minify-dead-code-elimination",
20 | {
21 | "optimizeRawSize": true
22 | }
23 | ],
24 | "@babel/plugin-proposal-class-properties",
25 | "@babel/plugin-syntax-dynamic-import",
26 | "@babel/plugin-transform-arrow-functions"
27 | ]
28 | }
--------------------------------------------------------------------------------
/src/Scripts/map/cluster-layer.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapMarkerClusterLayer {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter()]
6 | [Hashtable[]]$Markers,
7 | [Parameter()]
8 | [int]$MinimumClusterSize = 2,
9 | [Parameter()]
10 | [int]$GridSize = 60
11 | )
12 |
13 | @{
14 | type = "map-cluster-layer"
15 | isPlugin = $true
16 | assetId = $assetId
17 |
18 | id = $id
19 | markers = $Markers
20 | minClusterSize = $MinimumClusterSize
21 | gridSize = $GridSize
22 | }
23 | }
--------------------------------------------------------------------------------
/examples/service-cards.ps1:
--------------------------------------------------------------------------------
1 | $Dashboard = New-UDDashboard -Title Services -Content {
2 | New-UDRow -Endpoint {
3 | Get-Service | ForEach-Object {
4 | New-UDColumn -Size 2 -Content {
5 | $BackgroundColor = "Red"
6 | if ($_.Status -eq 'Running') {
7 | $BackgroundColor = "Green"
8 | }
9 |
10 | New-UDCard -Title $_.Name -Text $_.Status.ToString() -BackgroundColor $BackgroundColor -FontColor 'white'
11 | }
12 | }
13 | } -AutoRefresh -RefreshInterval 60
14 | }
15 |
16 | Start-UDDashboard -Dashboard $Dashboard -Port 10000
--------------------------------------------------------------------------------
/examples/login-pages/microsoft.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of a Microsoft Account login page.
3 | #>
4 | Import-Module UniversalDashboard
5 |
6 | $MicrosoftLogin = New-UDAuthenticationMethod -AppId "xyz" -AppSecret "123" -Provider Microsoft
7 |
8 | $LoginPage = New-UDLoginPage -AuthenticationMethod @($MicrosoftLogin)
9 |
10 | $MyDashboardPage = New-UDPage -Url "/myDashboard" -Endpoint {
11 | New-UDCard -Title "Welcome, $User" -Text "This is your custom dashboard."
12 | }
13 |
14 | $Dashboard = New-UDDashboard -LoginPage $LoginPage -Page @(
15 | $MyDashboardPage
16 | )
17 |
18 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/grid/simple.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of a simple grid.
3 | #>
4 | Import-Module UniversalDashboard
5 |
6 | $Data = @(
7 | [PSCustomObject]@{Animal="Frog";Order="Anura"}
8 | [PSCustomObject]@{Animal="Tiger";Order="Carnivora"}
9 | [PSCustomObject]@{Animal="Bat";Order="Chiroptera"}
10 | [PSCustomObject]@{Animal="Fox";Order="Carnivora"}
11 | )
12 |
13 | $Dashboard = New-UDDashboard -Title "Grids - Simple" -Content {
14 | New-UDGrid -Title "Animals" -Headers @("Animal", "Order") -Properties @("Animal", "Order") -Endpoint {
15 | $Data | Out-UDGridData
16 | }
17 | }
18 |
19 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/datetime.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | var dayjs = require('dayjs')
3 | var localizedFormat = require('dayjs/plugin/localizedFormat')
4 | var relativeTime = require('dayjs/plugin/relativeTime')
5 | var utc = require('dayjs/plugin/utc')
6 | dayjs.extend(relativeTime)
7 | dayjs.extend(utc)
8 | dayjs.extend(localizedFormat)
9 |
10 | const DayTime = (props) => {
11 | if (props.format.indexOf("L") === -1 && props.format.indexOf("l") === -1) {
12 | return dayjs(props.inputObject).format(props.format);
13 | }
14 | else {
15 | return dayjs(props.inputObject).local().format(props.format);
16 | }
17 | }
18 |
19 | export default DayTime;
20 |
21 |
--------------------------------------------------------------------------------
/src/Scripts/iframe.ps1:
--------------------------------------------------------------------------------
1 | function New-UDIFrame {
2 | <#
3 | .SYNOPSIS
4 | An HTML IFrame component.
5 |
6 | .DESCRIPTION
7 | An HTML IFrame component.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Uri
13 | The URI for the iframe.
14 |
15 | .EXAMPLE
16 | Defines an IFrame for Google.
17 |
18 | New-UDIFrame -Uri https://www.google.com
19 | #>
20 | param(
21 | [Parameter()]
22 | [String]$Id = ([Guid]::NewGuid()),
23 | [Parameter()]
24 | $Uri
25 | )
26 |
27 | New-UDElement -Id $Id -Tag "iframe" -Attributes @{
28 | src = $Uri
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Scripts/interactive/Invoke-JavaScript.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-UDJavaScript {
2 | <#
3 | .SYNOPSIS
4 | Invokes JavaScript within the browser.
5 |
6 | .DESCRIPTION
7 | Invokes JavaScript within the browser. JavaScript is executed with eval()
8 |
9 | .PARAMETER JavaScript
10 | The JavaScript to execute.
11 |
12 | .EXAMPLE
13 | New-UDButton -Text 'Click Me' -OnClick {
14 | Invoke-UDJavaScript 'alert("Hello World!")'
15 | }
16 | #>
17 | param(
18 | [Parameter(Mandatory)]
19 | [string]$JavaScript
20 | )
21 |
22 | $DashboardHub.SendWebSocketMessage($ConnectionId, "invokejavascript", $JavaScript)
23 | }
--------------------------------------------------------------------------------
/examples/input/dynamic-page.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of redirecting to a dynamic page after taking input.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $HomePage = New-UDPage -Name "Home" -Content {
8 | New-UDInput -Title "Input" -Endpoint {
9 | param($Text)
10 |
11 | New-UDInputAction -RedirectUrl "/dynamic/$text"
12 | }
13 | }
14 |
15 | $DynamicPage = New-UDPage -Url "/dynamic/:text" -Endpoint {
16 | param($text)
17 |
18 | New-UDCard -Title $text
19 | }
20 |
21 | $Dashboard = New-UDDashboard -Title "Input - Dynamic Page" -Pages @(
22 | $HomePage
23 | $DynamicPage
24 | )
25 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/backdrop.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Backdrop } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 | import makeStyles from '@mui/styles/makeStyles';
5 |
6 | const useStyles = makeStyles((theme) => ({
7 | backdrop: props => ({
8 | zIndex: theme.zIndex.drawer + 1,
9 | color: props.color,
10 | }),
11 | }));
12 |
13 |
14 | function UDBackdrop(props) {
15 | const classes = useStyles(props);
16 |
17 | return props.onClick()}>{props.render(props.children)}
18 | }
19 |
20 | export default withComponentFeatures(UDBackdrop);
--------------------------------------------------------------------------------
/src/Components/style.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { css } from 'emotion';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 | import { Box } from '@mui/material';
5 |
6 | const UDStyle = (props) => {
7 | var children = props.content;
8 | if (!Array.isArray(children)) {
9 | children = [children];
10 | }
11 |
12 | children = children.map(x => props.render(x));
13 |
14 | if (props.sx) {
15 | return {children}
16 | }
17 |
18 | const style = css(props.style);
19 | return React.createElement(props.tag, { className: style }, children);
20 | }
21 |
22 | export default withComponentFeatures(UDStyle);
--------------------------------------------------------------------------------
/src/Components/badge.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Badge } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | const UDBadge = (props) => {
6 | return
14 | {props.render(props.children)}
15 |
16 | }
17 |
18 | export default withComponentFeatures(UDBadge);
--------------------------------------------------------------------------------
/src/Scripts/span.ps1:
--------------------------------------------------------------------------------
1 | function New-UDSpan {
2 | <#
3 | .SYNOPSIS
4 | A span component.
5 |
6 | .DESCRIPTION
7 | A span component. Defines a span HTML tag.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Content
13 | The content of the span.
14 |
15 | .EXAMPLE
16 | An example
17 |
18 | New-UDSpan -Content {
19 | New-UDTypography -Text 'Text'
20 | }
21 | #>
22 | param(
23 | [Parameter()]
24 | [String]$Id = ([Guid]::NewGuid()),
25 | [Parameter()]
26 | $Content
27 | )
28 |
29 | New-UDElement -Id $Id -Tag "span" -Content {
30 | $Content
31 | }
32 | }
--------------------------------------------------------------------------------
/src/app/index.jsx:
--------------------------------------------------------------------------------
1 | import './public-path';
2 | import React from 'react';
3 | import { render } from 'react-dom';
4 | import 'whatwg-fetch';
5 | import Promise from 'promise-polyfill';
6 | import { UniversalDashboardService } from './services/universal-dashboard-service.jsx';
7 | import App from './App';
8 |
9 | window.react = require('react');
10 | window['reactdom'] = require('react-dom');
11 | window['reactrouterdom'] = require('react-router-dom');
12 |
13 | // To add to window
14 | if (!window.Promise) {
15 | window.Promise = Promise;
16 | }
17 |
18 | window.UniversalDashboard = UniversalDashboardService;
19 | require('./component-registration');
20 |
21 | render(, document.getElementById('app'));
--------------------------------------------------------------------------------
/src/Components/framework/not-authorized.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@mui/material/Typography';
3 | import UDIcon from '../icon';
4 | import { withComponentFeatures } from 'universal-dashboard';
5 |
6 | const NotAuthorized = (props) => {
7 | const [comp, setComponent] = React.useState(null);
8 | React.useEffect(() => {
9 | if (props.notAuthorized) {
10 | props.notAuthorized().then(json => setComponent(json));
11 | }
12 | return () => { }
13 | }, [])
14 |
15 | if (comp === null) return <>>
16 | var element = props.render(JSON.parse(comp));
17 | return element;
18 | }
19 |
20 | export default withComponentFeatures(NotAuthorized);
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## v2.9.1-beta2
8 |
9 | ### Changed
10 |
11 | - Added UDGrid, UDLayout, UDModal, UDLink Tests
12 | - Fixed *-UDElement cmdlets
13 | - Added dependency on Universal module
14 | - Fixed custom component loading
15 |
16 | ## v3.0.0-beta3
17 |
18 | ### Changed
19 |
20 | - Added dependency on Universal module
21 | - Fixed custom component loading
22 | - Updated demo dashboard
23 | - Removed styles that made paper and buttons look weird
24 |
--------------------------------------------------------------------------------
/src/Scripts/editor.ps1:
--------------------------------------------------------------------------------
1 | function New-UDEditor {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter()]
6 | [Hashtable]$Data,
7 | [Parameter()]
8 | [Endpoint]$OnChange,
9 | [Parameter()]
10 | [ValidateSet("json", "html")]
11 | [string]$Format = "json"
12 | )
13 |
14 | if ($OnChange) {
15 | $OnChange.Register($Id, $PSCmdlet);
16 | }
17 |
18 | @{
19 | assetId = $AssetId
20 | isPlugin = $true
21 | type = "ud-editor"
22 | id = $Id
23 |
24 | onChange = $OnChange
25 | data = $Data
26 | format = $Format.ToLower()
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Scripts/map/icon.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapIcon {
2 | param(
3 | [Parameter(Mandatory)]
4 | [string]$Url,
5 | [Parameter()]
6 | [int]$Height,
7 | [Parameter()]
8 | [int]$Width,
9 | [Parameter()]
10 | [int]$AnchorX,
11 | [Parameter()]
12 | [int]$AnchorY,
13 | [Parameter()]
14 | [int]$PopupAnchorX,
15 | [Parameter()]
16 | [int]$PopupAnchorY
17 | )
18 |
19 | $Options = @{
20 | }
21 |
22 | foreach($boundParameter in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) {
23 | $Options[[char]::ToLowerInvariant($boundParameter.Key[0]) + $boundParameter.Key.Substring(1)] = $boundParameter.Value
24 | }
25 |
26 | $Options
27 | }
--------------------------------------------------------------------------------
/src/Components/skeleton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Skeleton } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | function UDSkeleton(props) {
6 |
7 | var animation = null;
8 | if (props.animation === "disabled") {
9 | animation = false;
10 | }
11 |
12 | if (props.animation === "wave") {
13 | animation = 'wave';
14 | }
15 |
16 | return
23 | }
24 |
25 | export default withComponentFeatures(UDSkeleton);
--------------------------------------------------------------------------------
/src/app/services/toaster.jsx:
--------------------------------------------------------------------------------
1 | import iziToast from 'izitoast/dist/js/iziToast.min.js'
2 | require('izitoast/dist/css/iziToast.min.css')
3 | require('@fortawesome/fontawesome-free/css/all.min.css')
4 |
5 | const toaster = {
6 | show: (model) => {
7 |
8 |
9 | iziToast.show(model);
10 | },
11 | hide: (id) => {
12 | var toast = document.querySelector('#' + id);
13 | iziToast.hide({}, toast);
14 | },
15 | info: (model) => {
16 | iziToast.info(model);
17 | },
18 | success: (model) => {
19 | iziToast.success(model);
20 | },
21 | warning: (model) => {
22 | iziToast.warning(model);
23 | },
24 | error: (model) => {
25 | iziToast.error(model);
26 | }
27 | }
28 |
29 | export default toaster;
--------------------------------------------------------------------------------
/src/Scripts/event.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-UDEvent {
2 | param(
3 | [Parameter(
4 | Mandatory = $true,
5 | ValueFromPipeline = $true,
6 | Position = 0
7 | )]
8 | [String]$Id,
9 | [Parameter(
10 | Mandatory = $true,
11 | Position = 1,
12 | ParameterSetName = "onClick"
13 | )]
14 | [ValidateSet("onClick")]
15 | [string]$event
16 | )
17 |
18 | Begin {
19 |
20 | }
21 |
22 | Process {
23 | if ($PSCmdlet.ParameterSetName -eq "onClick") {
24 | Invoke-UDJavaScript -javaScript "
25 | document.getElementById('$Id').click();
26 | "
27 | }
28 | }
29 |
30 | End {
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Scripts/error.ps1:
--------------------------------------------------------------------------------
1 | function New-UDError {
2 | <#
3 | .SYNOPSIS
4 | Creates a new error card.
5 |
6 | .DESCRIPTION
7 | Creates a new error card.
8 |
9 | .PARAMETER Message
10 | The message to display.
11 |
12 | .PARAMETER Title
13 | A title for the card.
14 |
15 | .EXAMPLE
16 | Displays the error 'Invalid data' on the page.
17 |
18 | New-UDError -Message 'Invalid data'
19 | #>
20 | param(
21 | [Parameter(Mandatory)]
22 | [string]$Message,
23 | [Parameter()]
24 | [string]$Title
25 | )
26 |
27 | @{
28 | type = "error"
29 | isPlugin = $true
30 | assetId = $AssetId
31 |
32 | errorRecords = @(@{message= $Message})
33 | title = $Title
34 | }
35 | }
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # Starter pipeline
2 | # Start with a minimal pipeline that you can customize to build and deploy your code.
3 | # Add steps that build, run tests, deploy, and more:
4 | # https://aka.ms/yaml
5 |
6 | pool:
7 | vmImage: 'vs2017-win2016'
8 |
9 | steps:
10 |
11 | - powershell: Install-Module Universal -Scope CurrentUser -Force
12 | displayName: Install Universal
13 | errorActionPreference: continue
14 |
15 | - powershell: Install-Module Selenium -Scope CurrentUser -Force
16 | displayName: Install Selenium
17 | errorActionPreference: continue
18 |
19 | - powershell: Install-Module InvokeBuild -Scope CurrentUser -Force
20 | displayName: Install InvokeBuild
21 | errorActionPreference: continue
22 |
23 | - powershell: Invoke-Build -File .\src\build.ps1
24 | displayName: Build
25 | errorActionPreference: continue
--------------------------------------------------------------------------------
/src/Components/map/popup.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Popup } from 'react-leaflet';
3 |
4 | export default class UDPopup extends React.Component {
5 | render() {
6 |
7 | var content = null;
8 | if (Array.isArray(this.props.content)) {
9 | content = this.props.content.map(x => UniversalDashboard.renderComponent(x));
10 | } else {
11 | content = UniversalDashboard.renderComponent(this.props.content);
12 | }
13 |
14 | var position = null;
15 | if (this.props.latitude && this.props.longitude) {
16 | position = [this.props.latitude, this.props.longitude];
17 | }
18 |
19 | return
20 | {content}
21 |
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-pie.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ResponsivePie, Pie } from '@nivo/pie';
3 |
4 | export default class NivoPie extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 |
17 | render() {
18 | if (this.props.responsive) {
19 | return
20 | } else {
21 | return
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-line.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Line, ResponsiveLine } from '@nivo/line';
3 |
4 | export default class NivoLine extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 |
17 | render() {
18 |
19 | if (this.props.responsive) {
20 | return
21 | } else {
22 | return
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/Scripts/errorboundary.ps1:
--------------------------------------------------------------------------------
1 | function New-UDErrorBoundary {
2 | <#
3 | .SYNOPSIS
4 | Defines a new error boundary around a section of code.
5 |
6 | .DESCRIPTION
7 | Defines a new error boundary around a section of code. Error boundaries are used to trap errors and display them on the page.
8 |
9 | .PARAMETER Content
10 | The content to trap in an error boundary.
11 |
12 | .EXAMPLE
13 | Defines an error boundary that traps the exception that is thrown and displays it on the page.
14 |
15 | New-UDErrorBoundary -Content {
16 | throw 'This is an error'
17 | }
18 | #>
19 | param(
20 | [Parameter(Mandatory)]
21 | [ScriptBlock]$Content
22 | )
23 |
24 | try
25 | {
26 | & $Content
27 | }
28 | catch
29 | {
30 | New-UDError -Message $_
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-bar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ResponsiveBar, Bar } from '@nivo/bar';
3 |
4 | export default class NivoBar extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 | render() {
17 | if (this.props.responsive) {
18 | return
19 | } else {
20 | return
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-bubble.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ResponsiveBubble, Bubble } from '@nivo/circle-packing';
3 |
4 | export default class NivoBubble extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 | render() {
17 | if (this.props.responsive) {
18 | return
19 | } else {
20 | return
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-stream.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Stream, ResponsiveStream } from '@nivo/stream';
3 |
4 | export default class NivoStream extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 |
17 | render() {
18 |
19 | if (this.props.responsive) {
20 | return
21 | } else {
22 | return
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-heatmap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { HeatMap, ResponsiveHeatMap } from '@nivo/heatmap';
3 |
4 | export default class NivoHeatmap extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 | render() {
17 |
18 |
19 | if (this.props.responsive) {
20 | return
21 | } else {
22 | return
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-treemap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TreeMap, ResponsiveTreeMap } from '@nivo/treemap';
3 |
4 | export default class NivoTreemap extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 |
17 | render() {
18 |
19 | if (this.props.responsive) {
20 | return
21 | } else {
22 | return
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/Components/charts/nivo-calendar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Calendar, ResponsiveCalendar } from '@nivo/calendar';
3 |
4 | export default class NivoCalendar extends React.Component {
5 | onClick(e) {
6 | if (this.props.hasCallback) {
7 | UniversalDashboard.publish('element-event', {
8 | type: "clientEvent",
9 | eventId: this.props.id,
10 | eventName: 'onClick',
11 | eventData: JSON.stringify(e)
12 | });
13 | }
14 | }
15 |
16 |
17 | render() {
18 |
19 | if (this.props.responsive) {
20 | return
21 | } else {
22 | return
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/examples/charts/legend.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of customizing the legend.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10}
9 | @{Animal="Tiger";Count=1}
10 | @{Animal="Bat";Count=34}
11 | @{Animal="Fox";Count=20}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Legend" -Content {
15 |
16 | $LegendOptions = New-UDChartLegendOptions -Position bottom
17 | $Options = New-UDLineChartOptions -LegendOptions $LegendOptions
18 |
19 | New-UDChart -Title "Bottom Legend" -Type "Line" -Endpoint {
20 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
21 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BackgroundColor "#205D4CFF" -BorderColor "#5D4CFF" -BorderWidth 3
22 | )
23 | } -Options $Options
24 | }
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Scripts/interactive/Invoke-Redirect.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-UDRedirect {
2 | <#
3 | .SYNOPSIS
4 | Redirect the user to another page.
5 |
6 | .DESCRIPTION
7 | Redirect the user to another page.
8 |
9 | .PARAMETER Url
10 | The URL to redirect the user to.
11 |
12 | .PARAMETER OpenInNewWindow
13 | Whether to open the URL in a new window.
14 |
15 | .EXAMPLE
16 | New-UDButton -Text 'Click Me' -OnClick {
17 | Invoke-UDRedirect 'https://www.google.com'
18 | }
19 | #>
20 | param(
21 | [Parameter(Mandatory)]
22 | [string]$Url,
23 | [Parameter()]
24 | [Switch]$OpenInNewWindow
25 | )
26 |
27 | $Data = @{
28 | url = $Url
29 | openInNewWindow = $OpenInNewWindow.IsPresent
30 | }
31 |
32 | $DashboardHub.SendWebSocketMessage($ConnectionId, "redirect", $Data)
33 | }
--------------------------------------------------------------------------------
/src/templates/Multiple Pages.ps1:
--------------------------------------------------------------------------------
1 | $Navigation = @(
2 | New-UDListItem -Label 'Home' -Icon (New-UDIcon -Icon Home) -OnClick {
3 | Invoke-UDRedirect -Url '/home'
4 | }
5 | New-UDListItem -Label 'Users' -Icon (New-UDIcon -Icon User) -OnClick {
6 | Invoke-UDRedirect -Url '/users'
7 | }
8 | New-UDListItem -Label 'Groups' -Icon (New-UDIcon -Icon Users) -OnClick {
9 | Invoke-UDRedirect -Url '/groups'
10 | }
11 | )
12 |
13 | $HomePage = New-UDPage -Name 'Home' -Content {
14 | New-UDTypography -Text 'Home'
15 | }
16 |
17 | $Users = New-UDPage -Name 'Users' -Content {
18 | New-UDTypography -Text 'Users'
19 | }
20 |
21 | $Groups = New-UDPage -Name 'Groups' -Content {
22 | New-UDTypography -Text 'Groups'
23 | }
24 |
25 | New-UDDashboard -Title 'PowerShell Universal' -Pages @(
26 | $HomePage
27 | $Users
28 | $Groups
29 | ) -Navigation $Navigation
--------------------------------------------------------------------------------
/src/Components/framework/togglecolormodes.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@mui/material/IconButton';
3 | import DarkIcon from '@mui/icons-material/Brightness7';
4 | import LightIcon from '@mui/icons-material/Brightness4';
5 | import { AppContext } from './../../app/app-context';
6 |
7 | export default props => {
8 | return (
9 |
10 | {context => {
11 | return (
12 | {
15 | const next = context.theme === 'dark' ? 'light' : 'dark'
16 | localStorage.setItem('theme', next);
17 | context.setTheme(next)
18 | }}
19 | size="large">
20 | {context.theme === 'dark' ? : }
21 |
22 | );
23 |
24 | }}
25 |
26 | );
27 | }
--------------------------------------------------------------------------------
/examples/charts/title.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of customizing a chart's title.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10}
9 | @{Animal="Tiger";Count=1}
10 | @{Animal="Bat";Count=34}
11 | @{Animal="Fox";Count=20}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Title" -Content {
15 |
16 | $LegendOptions = New-UDChartTitleOptions -Display -Text "Animals" -FontSize 50
17 | $Options = New-UDLineChartOptions -TitleOptions $LegendOptions
18 |
19 | New-UDChart -Title "Chart with title" -Type "Line" -Endpoint {
20 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
21 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BackgroundColor "#205D4CFF" -BorderColor "#5D4CFF" -BorderWidth 3
22 | )
23 | } -Options $Options
24 | }
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/login-pages/forms.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of a forms login page.
3 | #>
4 | Import-Module UniversalDashboard
5 |
6 | $FormLogin = New-UDAuthenticationMethod -Endpoint {
7 | param([PSCredential]$Credentials)
8 |
9 | if ($Credentials.UserName -eq "Adam" -and $Credentials.GetNetworkCredential().Password -eq "SuperSecretPassword") {
10 | New-UDAuthenticationResult -Success -UserName "Adam"
11 | }
12 |
13 | New-UDAuthenticationResult -ErrorMessage $Credentials.GetNetworkCredential().Password
14 | }
15 |
16 | $LoginPage = New-UDLoginPage -AuthenticationMethod @($FormLogin)
17 |
18 | $MyDashboardPage = New-UDPage -Url "/myDashboard" -Endpoint {
19 | New-UDCard -Title "Welcome, $User" -Text "This is your custom dashboard."
20 | }
21 |
22 | $Dashboard = New-UDDashboard -LoginPage $LoginPage -Page @(
23 | $MyDashboardPage
24 | )
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/grid/custom-search.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of creating a grid with a custom filtering algorithm.
3 | #>
4 | Import-Module UniversalDashboard
5 |
6 | $Data = @(
7 | [PSCustomObject]@{Animal="Frog";Order="Anura"}
8 | [PSCustomObject]@{Animal="Tiger";Order="Carnivora"}
9 | [PSCustomObject]@{Animal="Bat";Order="Chiroptera"}
10 | [PSCustomObject]@{Animal="Fox";Order="Carnivora"}
11 | )
12 |
13 | $Dashboard = New-UDDashboard -Title "Grids - Simple" -Content {
14 | New-UDGrid -Title "Animals" -Headers @("Animal", "Order") -Properties @("Animal", "Order") -Endpoint {
15 | #By default, filtering checks all properties. This overrides it to only check the animal property.
16 | #$filterText is always provided in a grid endpoint
17 | $Data | Where-Object Animal -Match $filterText | Out-UDGridData -NoAutoFilter
18 | }
19 | }
20 |
21 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/card-body.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { CardContent } from "@mui/material";
3 | import withStyles from '@mui/styles/withStyles';
4 | import classNames from "classnames";
5 |
6 | const styles = theme => ({
7 | content: {
8 | //display: "flex"
9 | }
10 | });
11 |
12 | export class UDCardBody extends React.Component {
13 | render() {
14 | const {
15 | className,
16 | classes,
17 | id,
18 | style
19 | } = this.props;
20 |
21 | return (
22 |
23 |
28 | {UniversalDashboard.renderComponent(this.props.content)}
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default withStyles(styles)(UDCardBody);
36 |
--------------------------------------------------------------------------------
/src/Components/card-media.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import classNames from "classnames"
3 | import makeStyles from '@mui/styles/makeStyles';
4 | import { CardMedia } from "@mui/material";
5 |
6 | const useStyles = makeStyles(theme => ({
7 | media: {
8 | objectFit: "cover",
9 | backgroundColor: "transparent"
10 | // height: "100%"
11 | }
12 | }));
13 |
14 |
15 | const UDCardMedia = (props) => {
16 | const classes = useStyles();
17 | const { component, alt, height, image, title, source } = props;
18 |
19 | return (
20 |
30 | );
31 | }
32 |
33 | export default UDCardMedia;
34 |
--------------------------------------------------------------------------------
/examples/charts/custom-axis.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of creating a custom axis with a minimum and maximum value for the axis.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10}
9 | @{Animal="Tiger";Count=1}
10 | @{Animal="Bat";Count=34}
11 | @{Animal="Fox";Count=20}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Custom Axis" -Content {
15 |
16 | $MinMaxAxis = New-UDLinearChartAxis -Minimum 10 -Maximum 20
17 | $Options = New-UDLineChartOptions -yAxes $MinMaxAxis
18 |
19 | New-UDChart -Title "Line Chart" -Type "Line" -Endpoint {
20 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
21 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BackgroundColor "#205D4CFF" -BorderColor "#5D4CFF" -BorderWidth 3
22 | )
23 | } -Options $Options
24 | }
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/charts/multiple-datasets.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provies an example of creating a chart with multiple datasets.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10;Babies=3}
9 | @{Animal="Tiger";Count=1;Babies=0}
10 | @{Animal="Bat";Count=34;Babies=30}
11 | @{Animal="Fox";Count=20;Babies=10}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Multiple Datasets" -Content {
15 | New-UDChart -Title "Bottom Legend" -Type "Bar" -Endpoint {
16 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
17 | New-UDBarChartDataset -Label "Adults" -DataProperty Count -BackgroundColor "#205D4CFF" -BorderColor "#5D4CFF" -BorderWidth 3
18 | New-UDBarChartDataset -Label "Babies" -DataProperty Babies -BackgroundColor "#20A07DFF" -BorderColor "#A07DFF" -BorderWidth 3
19 | )
20 | }
21 | }
22 |
23 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/map/MarkerCluster.css:
--------------------------------------------------------------------------------
1 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
2 | -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
3 | -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
4 | -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
5 | transition: transform 0.3s ease-out, opacity 0.3s ease-in;
6 | }
7 |
8 | .leaflet-cluster-spider-leg {
9 | /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
10 | -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
11 | -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
12 | -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
13 | transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
14 | }
15 |
--------------------------------------------------------------------------------
/src/Scripts/map/heatmap.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapHeatmapLayer {
2 | param(
3 | [Parameter(Mandatory)]
4 | $Points,
5 | [Parameter()]
6 | [string]$Id = (New-Guid).ToString(),
7 | [Parameter()]
8 | [double]$MaxIntensity,
9 | [Parameter()]
10 | [double]$Radius,
11 | [Parameter()]
12 | [int]$MaxZoom,
13 | [Parameter()]
14 | [double]$MinOpacity,
15 | [Parameter()]
16 | [int]$Blur,
17 | [Parameter()]
18 | [Hashtable]$Gradient
19 | )
20 |
21 | $Options = @{
22 | type = 'map-heatmap-layer'
23 | isPlugin = $true
24 | assetId = $AssetId
25 | }
26 |
27 | foreach($boundParameter in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) {
28 | $Options[[char]::ToLowerInvariant($boundParameter.Key[0]) + $boundParameter.Key.Substring(1)] = $boundParameter.Value
29 | }
30 |
31 | $Options
32 | }
--------------------------------------------------------------------------------
/src/Components/framework/not-found.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@mui/material/Typography';
3 | import UDIcon from '../icon';
4 | import { withComponentFeatures } from 'universal-dashboard';
5 |
6 | const NotFound = (props) => {
7 | const [comp, setComponent] = React.useState(null);
8 | React.useEffect(() => {
9 | if (props.pageNotFound) {
10 | props.pageNotFound().then(json => setComponent(json));
11 | }
12 | return () => { }
13 | }, [])
14 |
15 | if (props.pageNotFound) {
16 | if (comp === null) return <>>
17 | var element = props.render(JSON.parse(comp));
18 | return element;
19 | } else {
20 | return
21 | Page Not Found
22 |
23 | }
24 | }
25 |
26 | export default withComponentFeatures(NotFound);
--------------------------------------------------------------------------------
/src/app/config.jsx:
--------------------------------------------------------------------------------
1 | export function getApiPath() {
2 | return "";
3 | }
4 |
5 | export function getWsApiPath() {
6 | var protocol = "ws://";
7 | if (window.location.protocol.toLowerCase().startsWith("https")) {
8 | protocol = "wss://";
9 | }
10 |
11 | return protocol + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
12 | }
13 |
14 | function getMeta(metaName) {
15 | const metas = document.getElementsByTagName('meta');
16 |
17 | for (let i = 0; i < metas.length; i++) {
18 | if (metas[i].getAttribute('name') === metaName) {
19 | return metas[i].getAttribute('content');
20 | }
21 | }
22 |
23 | return '';
24 | }
25 |
26 | export function getDashboardId() {
27 | var dashboardId = localStorage.getItem("ud-dashboard");
28 | if (!dashboardId || dashboardId === '')
29 | {
30 | dashboardId = getMeta('ud-dashboard');
31 | }
32 |
33 | return dashboardId;
34 | }
--------------------------------------------------------------------------------
/examples/login-page.ps1:
--------------------------------------------------------------------------------
1 | Import-Module UniversalDashboard
2 |
3 | $FormLogin = New-UDAuthenticationMethod -Endpoint {
4 | param([PSCredential]$Credentials)
5 |
6 | if ($Credentials.UserName -eq "Adam" -and $Credentials.GetNetworkCredential().Password -eq "SuperSecretPassword") {
7 | New-UDAuthenticationResult -Success -UserName "Adam"
8 | }
9 |
10 | New-UDAuthenticationResult -ErrorMessage $Credentials.GetNetworkCredential().Password
11 | }
12 |
13 | $MicrosoftLogin = New-UDAuthenticationMethod -AppId "xyz" -AppSecret "123" -Provider Microsoft
14 |
15 | $LoginPage = New-UDLoginPage -AuthenticationMethod @($FormLogin, $MicrosoftLogin)
16 |
17 | $MyDashboardPage = New-UDPage -Url "/myDashboard" -Endpoint {
18 | New-UDCard -Title "Welcome, $User" -Text "This is your custom dashboard."
19 | }
20 |
21 | $Dashboard = New-UDDashboard -LoginPage $LoginPage -Page @(
22 | $MyDashboardPage
23 | )
24 |
25 | Start-UDDashboard -Dashboard $Dashboard -Port 10000
26 |
--------------------------------------------------------------------------------
/src/Components/link.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from '@mui/material/Link';
3 | import classNames from 'classnames'
4 | import { withComponentFeatures } from 'universal-dashboard'
5 |
6 | const UDLink = (props) => {
7 | const {
8 | id,
9 | url,
10 | underline,
11 | style,
12 | variant,
13 | className,
14 | openInNewWindow,
15 | content,
16 | text,
17 | onClick
18 | } = props;
19 |
20 | return (
21 | {
31 | if (onClick) {
32 | e.preventDefault();
33 | onClick();
34 | }
35 | }}>
36 | {!text ? (UniversalDashboard.renderComponent(content)) : text}
37 |
38 | )
39 | }
40 |
41 | export default withComponentFeatures(UDLink);
42 |
--------------------------------------------------------------------------------
/src/Components/framework/not-running.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@mui/material/Typography';
3 | import UDIcon from '../icon';
4 | import { Redirect } from 'react-router-dom'
5 |
6 | const NotRunning = () => {
7 |
8 | const [redirect, setRedirect] = React.useState(false);
9 |
10 | React.useEffect(() => {
11 | var token = setInterval(() => {
12 | UniversalDashboard.get('/api/internal/dashboard', function (json) {
13 | setRedirect(true);
14 | });
15 | }, 5000);
16 |
17 | return () => { clearInterval(token) }
18 | }, [true]);
19 |
20 | if (redirect) {
21 | return
22 | }
23 |
24 | return (
25 |
26 | Dashboard is not running
27 |
28 | )
29 | }
30 |
31 | export default NotRunning;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Universal Dashboard
2 |
3 | # Universal Dashboard is now a part of PowerShell Universal. Please submit issues on the [PowerShell Universal](https://github.com/ironmansoftware/powershell-universal) repository.
4 |
5 | Create beautiful, interactive websites with PowerShell. Universal Dashboard is part of [PowerShell Universal](https://ironmansoftware.com/powershell-universal/).
6 |
7 | 
8 |
9 | ## Resources
10 |
11 | - [Documentation](https://docs.ironmansoftware.com)
12 | - [Licensing](https://store.ironmansoftware.com/pricing/powershell-universal)
13 | - [Live Preview](https://poshud.com/)
14 | - [YouTube Videos](https://www.youtube.com/playlist?list=PL-0mHH7DlSiSZ4ozleNTUSXNkF6dlySVz)
15 | - [Forums](https://forums.universaldashboard.io/)
16 |
17 | ## Install
18 |
19 | Learn how to get started on our [documentation](https://docs.ironmansoftware.com/get-started).
20 |
21 | ## Examples
22 |
23 | [Examples](https://docs.ironmansoftware.com/examples)
24 |
--------------------------------------------------------------------------------
/src/Scripts/map/popup.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapPopup {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter()]
6 | [ScriptBlock]$Content,
7 | [Parameter()]
8 | [float]$Longitude,
9 | [Parameter()]
10 | [float]$Latitude,
11 | [Parameter()]
12 | [int]$MaxWidth,
13 | [Parameter()]
14 | [int]$MinWidth
15 | )
16 |
17 | $Options = @{
18 | type = "map-popup"
19 | isPlugin = $true
20 | assetId = $AssetId
21 |
22 | id = $id
23 | content = & $Content
24 |
25 | maxWidth = $MaxWidth
26 | minWidth = $MinWidth
27 | }
28 |
29 | if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Longitude")) {
30 | $Options["longitude"] = $Longitude
31 | }
32 |
33 | if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Latitude")) {
34 | $Options["latitude"] = $Latitude
35 | }
36 |
37 | $Options
38 | }
--------------------------------------------------------------------------------
/src/classes/Cmdlets/NewNivoFillCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Management.Automation;
3 |
4 | namespace UniversalDashboard.Enterprise.Cmdlets
5 | {
6 | [Cmdlet(VerbsCommon.New, "UDNivoFill")]
7 | public class NewNivoFillCommand : PSCmdlet
8 | {
9 | private readonly Dictionary _values = new Dictionary();
10 |
11 | [Parameter(Mandatory = true)]
12 | public string ElementId { get; set; }
13 |
14 | [Alias("GradientId")]
15 | [Parameter(Mandatory = true)]
16 | public string PatternId { get; set; }
17 |
18 | protected override void BeginProcessing()
19 | {
20 | WriteObject(new Dictionary
21 | {
22 | { "match", new Dictionary {
23 | { "id", ElementId }
24 | }
25 | },
26 | { "id", PatternId }
27 | });
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Scripts/interactive/Select-Element.ps1:
--------------------------------------------------------------------------------
1 | function Select-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Selects the specified element.
5 |
6 | .DESCRIPTION
7 | Selects the specified element. This cmdlet is useful for selecting input fields.
8 |
9 | .PARAMETER Id
10 | The ID of the element to select.
11 |
12 | .PARAMETER ScrollToElement
13 | Whether to scroll to the element.
14 |
15 | .EXAMPLE
16 | New-UDTextbox -Id 'txtName' -Label 'Name'
17 |
18 | New-UDButton -Text 'Click Me' -OnClick {
19 | Select-UDElement -Id 'txtName'
20 | }
21 | #>
22 | param(
23 | [Parameter(Mandatory, ParameterSetName = "Normal")]
24 | [string]$Id,
25 | [Parameter(ParameterSetName = "Normal")]
26 | [Switch]$ScrollToElement
27 | )
28 |
29 | $Data = @{
30 | id = $Id
31 | scrollToElement = $ScrollToElement
32 | }
33 |
34 | $DashboardHub.SendWebSocketMessage($ConnectionId, "select", $Data)
35 | }
--------------------------------------------------------------------------------
/examples/grid/custom-columns.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of using custom columns.
3 | #>
4 | Import-Module UniversalDashboard
5 |
6 | $Data = @(
7 | [PSCustomObject]@{Animal="Frog";Order="Anura";Article=(New-UDLink -Text "Wikipedia" -Url "https://en.wikipedia.org/wiki/Frog")}
8 | [PSCustomObject]@{Animal="Tiger";Order="Carnivora";Article=(New-UDLink -Text "Wikipedia" -Url "https://en.wikipedia.org/wiki/Tiger")}
9 | [PSCustomObject]@{Animal="Bat";Order="Chiroptera";Article=(New-UDLink -Text "Wikipedia" -Url "https://en.wikipedia.org/wiki/Bat")}
10 | [PSCustomObject]@{Animal="Fox";Order="Carnivora";Article=(New-UDLink -Text "Wikipedia" -Url "https://en.wikipedia.org/wiki/Fox")}
11 | )
12 |
13 | $Dashboard = New-UDDashboard -Title "Grids - Custom Columns" -Content {
14 | New-UDGrid -Title "Animals" -Headers @("Animal", "Order", "Wikipedia") -Properties @("Animal", "Order", "Article") -Endpoint {
15 | $Data | Out-UDGridData
16 | }
17 | }
18 |
19 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/charts/tooltips.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of configuring a chart's tooltips.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10}
9 | @{Animal="Tiger";Count=1}
10 | @{Animal="Bat";Count=34}
11 | @{Animal="Fox";Count=20}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Tooltips" -Content {
15 |
16 | $LegendOptions = New-UDChartTooltipOptions -BackgroundColor "#2928FF" -BodyFontColor "#FFFFFF" -TitleFontColor "#FFFFFF" -CornerRadius 20 -TitleFontSize 50
17 | $Options = New-UDLineChartOptions -TooltipOptions $LegendOptions
18 |
19 | New-UDChart -Title "Big tooltips with custom colors" -Type "Line" -Endpoint {
20 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
21 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BackgroundColor "#205D4CFF" -BorderColor "#5D4CFF" -BorderWidth 3
22 | )
23 | } -Options $Options
24 | }
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/charts/colors.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of working with colors in charts.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Data = @(
8 | @{Animal="Frog";Count=10}
9 | @{Animal="Tiger";Count=1}
10 | @{Animal="Bat";Count=34}
11 | @{Animal="Fox";Count=20}
12 | )
13 |
14 | $Dashboard = New-UDDashboard -Title "Charts - Colors" -Content {
15 | New-UDChart -Title "Line Chart" -Type "Line" -Endpoint {
16 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
17 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BackgroundColor "#234FE2" -BorderColor "#35FF8A" -BorderWidth 3
18 | )
19 | }
20 |
21 | New-UDChart -Title "Line Chart without Underfill" -Type "Line" -Endpoint {
22 | $Data | Out-UDChartData -LabelProperty "Animal" -Dataset @(
23 | New-UDLineChartDataset -Label "Animals" -DataProperty Count -BorderColor "#35FF8A" -BorderWidth 3 -Fill $false
24 | )
25 | }
26 | }
27 |
28 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/src/Components/drawer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Drawer from '@mui/material/Drawer'
3 | import { withComponentFeatures } from 'universal-dashboard'
4 | import makeStyles from '@mui/styles/makeStyles';
5 | var classNames = require('classnames');
6 |
7 | const useStyles = makeStyles(theme => ({
8 | button: {
9 | margin: theme.spacing(),
10 | },
11 | leftIcon: {
12 | marginRight: theme.spacing(),
13 | },
14 | rightIcon: {
15 | marginLeft: theme.spacing(),
16 | },
17 | list: {
18 | width: 250,
19 | },
20 | }))
21 |
22 | const UDDrawer = props => {
23 | const classes = useStyles()
24 |
25 | const onClose = () => {
26 | props.setState({ open: false })
27 | }
28 |
29 | return (
30 |
31 |
32 | {props.render(props.children)}
33 |
34 |
35 | )
36 | }
37 |
38 | export default withComponentFeatures(UDDrawer)
39 |
--------------------------------------------------------------------------------
/src/Scripts/interactive/Get-Element.ps1:
--------------------------------------------------------------------------------
1 | function Get-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Get the state of the specified element.
5 |
6 | .DESCRIPTION
7 | Get the state of the specified element. This cmdlet may behave differently depending on the type of parent element.
8 |
9 | .PARAMETER Id
10 | The ID of the element to retreive the state of.
11 |
12 | .EXAMPLE
13 | New-UDCodeEditor -Id 'editor' -Code 'Hello World'
14 |
15 | New-UDButton -Text 'Click Me' -OnClick {
16 | Show-UDToast (Get-UDElement).Code
17 | }
18 | #>
19 | [CmdletBinding()]
20 | param(
21 | [Parameter(Mandatory)]
22 | [string]$Id
23 | )
24 |
25 | $requestId = ''
26 |
27 | $requestId = [Guid]::NewGuid().ToString()
28 |
29 | $Data = @{
30 | requestId = $requestId
31 | componentId = $Id
32 | }
33 |
34 | $DashboardHub.SendWebSocketMessage($ConnectionId, "requestState", $Data)
35 | ConvertFrom-Json -InputObject ($stateRequestService.Get($requestId))
36 | }
37 |
--------------------------------------------------------------------------------
/src/Components/progress.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CircularProgress from '@mui/material/CircularProgress';
3 | import LinearProgress from '@mui/material/LinearProgress';
4 | import makeStyles from '@mui/styles/makeStyles';
5 |
6 | const useStyles = makeStyles((theme) => ({
7 | bar: props => ({
8 | backgroundColor: props.progressColor ?? null
9 | }),
10 | root: props => ({
11 | backgroundColor: props.backgroundColor ?? null
12 | }),
13 | circular: props => ({
14 | color: props.progressColor ?? null
15 | })
16 | }))
17 |
18 | export default function Progress(props) {
19 |
20 | const classes = useStyles(props);
21 |
22 | if (props.circular) {
23 | return
26 | }
27 |
28 | return
32 | }
--------------------------------------------------------------------------------
/src/Components/paper.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import makeStyles from '@mui/styles/makeStyles';
3 | import Paper from "@mui/material/Paper";
4 | import classNames from "classnames"
5 | import { withComponentFeatures } from 'universal-dashboard';
6 |
7 | const useStyles = makeStyles(theme => ({
8 | root: {
9 | flexGrow: 1,
10 | display: "flex",
11 | padding: theme.spacing(2),
12 | margin: theme.spacing()
13 | }
14 | }));
15 |
16 | const UdPaper = (props) => {
17 |
18 | const classes = useStyles();
19 |
20 | const {
21 | elevation,
22 | style,
23 | height,
24 | width,
25 | square
26 | } = props;
27 |
28 | return (
29 |
38 | {props.render(props.children)}
39 |
40 | );
41 | }
42 |
43 | export default withComponentFeatures(UdPaper);
44 |
--------------------------------------------------------------------------------
/src/Scripts/interactive/Clear-Element.ps1:
--------------------------------------------------------------------------------
1 | function Clear-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Removes all children from the specified element.
5 |
6 | .DESCRIPTION
7 | Removes all children from the specified element. This cmdlet may behave differently depending on the type of parent element.
8 |
9 | .PARAMETER Id
10 | The ID of the element to clear.
11 |
12 | .PARAMETER Broadcast
13 | Whether to clear the element on all connected clients.
14 |
15 | .EXAMPLE
16 | New-UDElement -Tag 'div' -Id 'parent' -Content {
17 | New-UDTypography -Text 'Hello World'
18 | }
19 |
20 | New-UDButton -Text 'Click Me' -OnClick {
21 | Clear-UDElement -Id 'parent'
22 | }
23 | #>
24 | param(
25 | [Parameter(Mandatory)]
26 | [string]$Id,
27 | [Parameter()]
28 | [Switch]$Broadcast
29 | )
30 |
31 | if ($Broadcast) {
32 | $DashboardHub.SendWebSocketMessage("clearElement", $Id)
33 | }
34 | else {
35 | $DashboardHub.SendWebSocketMessage($ConnectionId, "clearElement", $Id)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Components/icon-button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import IconButton from '@mui/material/IconButton';
4 | import classNames from "classnames"
5 | import { withComponentFeatures } from 'universal-dashboard'
6 | import makeStyles from '@mui/styles/makeStyles';
7 | import UdMuIcon from './icon'
8 |
9 | const useStyles = makeStyles(theme => ({
10 | button: {
11 | margin: theme.spacing(),
12 | },
13 | }))
14 |
15 | const UdIconButton = props => {
16 | const { className, disabled, id, style, href, icon, onClick } = props;
17 | const classes = useStyles();
18 |
19 | const handleClick = () => {
20 | if (onClick == null) return
21 | onClick();
22 | }
23 |
24 | return (
25 |
33 |
34 |
35 | );
36 | }
37 |
38 | export default withComponentFeatures(UdIconButton);
--------------------------------------------------------------------------------
/src/Scripts/interactive/Set-Clipboard.ps1:
--------------------------------------------------------------------------------
1 | function Set-UDClipboard {
2 | <#
3 | .SYNOPSIS
4 | Sets string data into the clipboard.
5 |
6 | .DESCRIPTION
7 | Sets string data into the clipboard.
8 |
9 | .PARAMETER Data
10 | The data to set into the clipboard.
11 |
12 | .PARAMETER ToastOnSuccess
13 | Show a toast if the clipboard data was sent successfully.
14 |
15 | .PARAMETER ToastOnError
16 | Show a toast if the clipboard data was not sent successfully.
17 |
18 | .EXAMPLE
19 | New-UDButton -Text 'Click Me' -OnClick {
20 | Set-UDClipboard -Data 'Hello World!' -ShowToastOnSuccess
21 | }
22 | #>
23 | param(
24 | [Parameter(Mandatory)]
25 | [string]$Data,
26 | [Parameter()]
27 | [Switch]$ToastOnSuccess,
28 | [Parameter()]
29 | [Switch]$ToastOnError
30 | )
31 |
32 | $cpData = @{
33 | data = $Data
34 | toastOnSuccess = $ToastOnSuccess.IsPresent
35 | toastOnError = $ToastOnError.IsPresent
36 | }
37 |
38 | $DashboardHub.SendWebSocketMessage($ConnectionId, "clipboard", $cpData)
39 | }
40 |
--------------------------------------------------------------------------------
/src/Scripts/hidden.ps1:
--------------------------------------------------------------------------------
1 | function New-UDHidden {
2 | param(
3 | [Parameter()]
4 | [string]$Id = [Guid]::NewGuid(),
5 | [Parameter(ParameterSetName = 'Down')]
6 | [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
7 | [string]$Down,
8 | [Parameter(ParameterSetName = 'Up')]
9 | [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
10 | [string]$Up,
11 | [Parameter(ParameterSetName = 'Only')]
12 | [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
13 | [string[]]$Only,
14 | [Parameter()]
15 | [ScriptBlock]$Content
16 | )
17 |
18 | $Component = @{
19 | type = 'mu-hidden'
20 | id = $Id
21 | isPlugin = $true
22 |
23 | children = & $Content
24 | }
25 |
26 | if ($PSCmdlet.ParameterSetName -eq 'Only')
27 | {
28 | $Component['only'] = $Only
29 | }
30 |
31 | if ($PSCmdlet.ParameterSetName -eq 'Down')
32 | {
33 | $Component["$($Down.ToLower())Down"] = $true
34 | }
35 |
36 | if ($PSCmdlet.ParameterSetName -eq 'Up')
37 | {
38 | $Component["$($Up.ToLower())Up"] = $true
39 | }
40 |
41 | $Component
42 | }
--------------------------------------------------------------------------------
/src/Scripts/interactive/Start-Download.ps1:
--------------------------------------------------------------------------------
1 | function Start-UDDownload {
2 | <#
3 | .SYNOPSIS
4 | Starts the download of a file within the dashboard.
5 |
6 | .DESCRIPTION
7 | Starts the download of a file within the dashboard. Only text files are supported
8 |
9 | .PARAMETER FileName
10 | The name of the file.
11 |
12 | .PARAMETER StringData
13 | The data to be written to the file.
14 |
15 | .PARAMETER ContentType
16 | The content type of the file.
17 |
18 | .EXAMPLE
19 | New-UDButton -Text 'Click Me' -OnClick {
20 | Start-UDDownload -FileName 'myfile.txt' -StringData 'Hello World' -ContentType 'text/plain'
21 | }
22 | #>
23 | param(
24 | [Parameter()]
25 | [string]$FileName = "text.txt",
26 | [Parameter(Mandatory)]
27 | [string]$StringData,
28 | [Parameter()]
29 | [string]$ContentType = "text/plain"
30 | )
31 |
32 | $Data = @{
33 | fileName = $FileName
34 | stringData = $StringData
35 | contentType = $ContentType
36 | }
37 |
38 | $DashboardHub.SendWebSocketMessage($ConnectionId, "download", $data)
39 | }
--------------------------------------------------------------------------------
/src/Components/slider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import {withComponentFeatures} from 'universal-dashboard';
3 | import Slider from '@mui/material/Slider';
4 | import {FormContext} from './form';
5 |
6 | const UDSliderWithContext = (props) => {
7 | return (
8 |
9 | {
10 | ({onFieldChange}) =>
11 | }
12 |
13 | )
14 | }
15 |
16 | const UDSlider = (props) => {
17 | const onChange = (e, value) => {
18 | props.setState({ value })
19 | props.onFieldChange({id: props.id, value });
20 | }
21 |
22 | const onChangeCommitted = (e, value) => {
23 | if (props.onChange)
24 | {
25 | props.onChange(value);
26 | }
27 | }
28 |
29 | useEffect(() => {
30 | props.onFieldChange({id: props.id, value: props.value });
31 | return () => {}
32 | }, true)
33 |
34 | return
35 | }
36 |
37 | export default withComponentFeatures(UDSliderWithContext);
--------------------------------------------------------------------------------
/examples/published-folders/dashboard.ps1:
--------------------------------------------------------------------------------
1 | Import-Module "F:\universal-dashboard-enterprise\src\output\UniversalDashboard.psd1"
2 |
3 | Get-UDDashboard | Stop-UDDashboard
4 |
5 | $Root = $PSScriptRoot
6 | $EndpointInit = New-UDEndpointInitialization -Variable Root
7 |
8 | $Folder = Publish-UDFolder -Path (Join-Path $PSScriptRoot "files") -RequestPath "/files"
9 |
10 | $Dashboard = New-UDDashboard -Title "Downloads" -Content {
11 | New-UDRow -Columns {
12 | New-UDColumn {
13 | New-UDTable -Title "Downloads" -Headers @("Name", "Size", "Download") -Endpoint {
14 | Get-ChildItem -Path (Join-Path $Root "files") | ForEach-Object {
15 | [PSCustomObject]@{
16 | Name = $_.Name
17 | Size = "$([Math]::Floor($_.Length / 1KB))KB"
18 | Download = New-UDLink -Text "Download" -Url "/files/$($_.Name)"
19 | }
20 | } | Out-UDTableData -Property @("Name", "Size", "Download")
21 | }
22 | }
23 | }
24 | } -EndpointInitialization $EndpointInit
25 |
26 | Start-UDDashboard -Dashboard $Dashboard -Port 10001 -PublishedFolder $Folder
--------------------------------------------------------------------------------
/src/classes/classes.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28917.181
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "classes", "classes.csproj", "{E95B1845-A139-4038-939B-060E0FFE2064}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {E95B1845-A139-4038-939B-060E0FFE2064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {E95B1845-A139-4038-939B-060E0FFE2064}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {E95B1845-A139-4038-939B-060E0FFE2064}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {E95B1845-A139-4038-939B-060E0FFE2064}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {7511C456-2988-4E67-9B34-C220B9DFFF36}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/Scripts/interactive/Sync-Element.ps1:
--------------------------------------------------------------------------------
1 | function Sync-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Causes an element to update.
5 |
6 | .DESCRIPTION
7 | Causes an element to update. Not all elements can be updated. For elements that cannot be updated, wrap them in New-UDDynamic and update that.
8 |
9 | .PARAMETER Id
10 | The ID of the element to update.
11 |
12 | .PARAMETER Broadcast
13 | Whether to broadcast the update to all clients.
14 |
15 | .EXAMPLE
16 | New-UDDyanmic -Id 'dateTime' -Content {
17 | Get-Date
18 | }
19 |
20 | New-UDButton -Text 'Refresh' -Content {
21 | Sync-UDElement 'dateTime'
22 | }
23 | #>
24 | param(
25 | [Parameter(Mandatory, ValueFromPipeline)]
26 | [string[]]$Id,
27 | [Parameter()]
28 | [Switch]$Broadcast
29 | )
30 |
31 | Process {
32 | foreach ($i in $Id) {
33 | if ($Broadcast) {
34 | $DashboardHub.SendWebSocketMessage("syncElement", $I)
35 | }
36 | else {
37 | $DashboardHub.SendWebSocketMessage($ConnectionId, "syncElement", $I)
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | public/
61 | output/
62 |
63 | classes/bin
64 | classes/obj
65 |
66 | *.bom.xml
--------------------------------------------------------------------------------
/src/Scripts/splitpane.ps1:
--------------------------------------------------------------------------------
1 | function New-UDSplitPane {
2 | param(
3 | [Parameter()]
4 | [string]$Id = ([Guid]::NewGuid()).ToString(),
5 | [Parameter(Mandatory)]
6 | [ScriptBlock]$Content,
7 | [Parameter()]
8 | [ValidateSet("vertical", "horizontal")]
9 | [string]$Direction = "vertical",
10 | [Parameter()]
11 | [int]$MinimumSize,
12 | [Parameter()]
13 | [int]$DefaultSize
14 | )
15 |
16 | try {
17 | $Children = & $Content
18 | }
19 | catch {
20 | $Children = New-UDError -Message $_
21 | }
22 |
23 | if ($Children.Length -ne 2) {
24 | Write-Error "Split pane requires exactly two components in Content"
25 | return
26 | }
27 |
28 | $Options = @{
29 | content = $Children
30 | id = $Id
31 | split = $Direction.ToLower()
32 | type = "ud-splitpane"
33 | }
34 |
35 | if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("MinimumSize")) {
36 | $Options["minSize"] = $MinimumSize
37 | }
38 |
39 | if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("DefaultSize")) {
40 | $Options["defaultSize"] = $DefaultSize
41 | }
42 |
43 | $Options
44 | }
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | PowerShell Universal Dashboard
9 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Scripts/interactive/Remove-Element.ps1:
--------------------------------------------------------------------------------
1 | function Remove-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Removes an element from the page.
5 |
6 | .DESCRIPTION
7 | Removes an element from the page.
8 |
9 | .PARAMETER Id
10 | The ID of the element to remove.
11 |
12 | .PARAMETER ParentId
13 | Not used
14 |
15 | .PARAMETER Broadcast
16 | Whether to remove this element from the page of all connected users.
17 |
18 | .EXAMPLE
19 | New-UDElement -Id 'myElement' -Tag 'div' -Content {
20 | New-UDTypography -Text 'Hello World'
21 | }
22 |
23 | New-UDButton -Text 'Click Me' -OnClick {
24 | Remove-UDElement -Id 'myElement'
25 | }
26 | #>
27 | param(
28 | [Parameter(Mandatory)]
29 | [string]$Id,
30 | [Parameter()]
31 | [string]$ParentId,
32 | [Parameter()]
33 | [Switch]$Broadcast
34 | )
35 |
36 | $Data = @{
37 | componentId = $Id
38 | parentId = $ParentId
39 | }
40 |
41 | if ($Broadcast) {
42 | $DashboardHub.SendWebSocketMessage("removeElement", $Data)
43 | }
44 | else {
45 | $DashboardHub.SendWebSocketMessage($ConnectionId, "removeElement", $Data)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/templates/Rows and Columns.ps1:
--------------------------------------------------------------------------------
1 | New-UDDashboard -Title 'Rows and Columns' -Content {
2 | New-UDRow -Columns {
3 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
4 | New-UDCard -Text "Row 1, Col 1"
5 | }
6 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
7 | New-UDCard -Text "Row 1, Col 2"
8 | }
9 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
10 | New-UDCard -Text "Row 1, Col 3"
11 | }
12 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
13 | New-UDCard -Text "Row 1, Col 4"
14 | }
15 | }
16 |
17 | New-UDRow -Columns {
18 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
19 | New-UDCard -Text "Row 2, Col 1"
20 | }
21 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
22 | New-UDCard -Text "Row 2, Col 2"
23 | }
24 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
25 | New-UDCard -Text "Row 2, Col 3"
26 | }
27 | New-UDColumn -SmallSize 12 -MediumSize 6 -LargeSize 3 -Content {
28 | New-UDCard -Text "Row 2, Col 4"
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Components/table/v2/table.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withComponentFeatures } from "universal-dashboard";
3 | import { ErrorBoundary } from "react-error-boundary";
4 | import AlertCard from "./Alert";
5 | import TableBase from "./baseTable";
6 | import { generateColumns } from "./utilities";
7 |
8 | const Table = ({ data, columns, ...restOfProps }) => {
9 | const cols = React.useMemo(() => generateColumns(columns), [restOfProps]);
10 | const isServerSide = restOfProps.loadData ? true : false;
11 | const sortColumn = cols.find(col => col.isDefaultSortColumn)?.accessor || ""
12 |
13 | return (
14 |
15 | ({
22 | // style: {
23 | // backgroundColor: cellInfo.value === "Stopped" ? "red" : cellInfo.value === "Running" ? "green" : "unset",
24 | // color: cellInfo.value === "Stopped" ? "#fff" : cellInfo.value === "Running" ? "#fff" : "unset"
25 | // },
26 | // })}
27 | />
28 |
29 | );
30 | };
31 |
32 | export default withComponentFeatures(Table);
--------------------------------------------------------------------------------
/src/Scripts/paragraph.ps1:
--------------------------------------------------------------------------------
1 | function New-UDParagraph {
2 | <#
3 | .SYNOPSIS
4 | A paragraph.
5 |
6 | .DESCRIPTION
7 | A paragraph. Used to define a P HTML tag.
8 |
9 | .PARAMETER Content
10 | The content of the paragraph.
11 |
12 | .PARAMETER Text
13 | The text of the paragraph.
14 |
15 | .PARAMETER Color
16 | The font color of the paragraph.
17 |
18 | .EXAMPLE
19 | A simple paragraph.
20 |
21 | New-UDParagraph -Text 'Hello, world!'
22 | #>
23 | param(
24 | [Parameter(ParameterSetName = 'content')]
25 | [ScriptBlock]$Content,
26 | [Parameter(ParameterSetName = 'text')]
27 | [string]$Text,
28 | [Parameter()]
29 | [UniversalDashboard.Models.DashboardColor]$Color = 'black'
30 | )
31 |
32 | if ($PSCmdlet.ParameterSetName -eq 'content') {
33 | New-UDElement -Tag 'p' -Content $Content -Attributes @{
34 | style = @{
35 | color = $Color.HtmlColor
36 | }
37 | }
38 | }
39 | else {
40 | New-UDElement -Tag 'p' -Content {
41 | $Text
42 | } -Attributes @{
43 | style = @{
44 | color = $Color.HtmlColor
45 | }
46 | }
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/src/app/services/render-service.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react';
2 |
3 | const UdHtmlComponent = React.lazy(() => import('./../ud-html.jsx' /* webpackChunkName: "ud-html" */))
4 | const UdElementComponent = React.lazy(() => import('./../ud-element.jsx' /* webpackChunkName: "ud-element" */))
5 | const UdIcon = React.lazy(() => import('./../ud-icon.jsx' /* webpackChunkName: "ud-icon" */))
6 |
7 | export function internalRenderComponent(component, history) {
8 | if (!component) return null;
9 |
10 | switch (component.type) {
11 |
12 | case "icon":
13 | return ;
14 |
15 | case "element":
16 | return
17 |
18 |
19 |
20 | case "rawHtml":
21 | return
22 |
23 |
24 | }
25 |
26 | return component;
27 | }
28 |
29 | export default function renderComponent(component, history, dynamicallyLoaded) {
30 | return window.UniversalDashboard.renderComponent(component, history, dynamicallyLoaded);
31 | }
--------------------------------------------------------------------------------
/src/app/ud-tooltip.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactTooltip from 'react-tooltip';
3 |
4 | export default class UDTooltip extends React.Component {
5 | render()
6 | {
7 | var tooltipContent = this.props.tooltipContent;
8 | if (!Array.isArray(tooltipContent))
9 | {
10 | tooltipContent = [tooltipContent]
11 | }
12 |
13 | var content = this.props.content;
14 | if (!Array.isArray(content))
15 | {
16 | content = [content]
17 | }
18 |
19 | content = content.map(x => {
20 | return UniversalDashboard.renderComponent(x);
21 | });
22 |
23 | tooltipContent = tooltipContent.map(x => {
24 | if (typeof x === 'string' || x instanceof String)
25 | {
26 | return x;
27 | }
28 |
29 | return UniversalDashboard.renderComponent(x);
30 | });
31 |
32 | return (
33 |
34 | {content}
35 | {tooltipContent}
36 |
37 | )
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Components/dynamic.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 | import ReactInterval from 'react-interval';
4 |
5 | const UDDynamic = (props) => {
6 | const [component, setComponent] = useState({});
7 | const [loading, setLoading] = useState(props.loadingComponent != null);
8 |
9 | const loadData = () => {
10 | setLoading(props.loadingComponent != null);
11 | props.get(props.id).then(x => {
12 |
13 | if (Array.isArray(x)) {
14 | x.forEach(y => y.__version = props.__version);
15 | }
16 |
17 | setComponent(x);
18 | setLoading(false);
19 | });
20 | }
21 |
22 | const refresh = () => {
23 | props.setState({ __version: Math.random().toString(36).substr(2, 5) });
24 | }
25 |
26 | useEffect(() => {
27 | loadData();
28 |
29 | return () => { }
30 | }, [props.__version])
31 |
32 | if (loading) {
33 | return props.render(props.loadingComponent);
34 | }
35 |
36 | return [
37 | props.render(component),
38 |
39 | ]
40 | }
41 |
42 | export default withComponentFeatures(UDDynamic);
--------------------------------------------------------------------------------
/src/Scripts/map/rasterlayer.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapRasterLayer {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter(ParameterSetName = "Generic")]
6 | [string]$TileServer = 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
7 | [Parameter(ParameterSetName = "Bing", Mandatory)]
8 | [string]$ApiKey,
9 | [Parameter(ParameterSetName = "Bing")]
10 | [ValidateSet("Aerial", "AerialWithLabels", "AerialWithLabelsOnDemand", "CanvasDark", "CanvasLight", "CanvasGray", "Road")]
11 | [string]$Type = "Aerial",
12 | [Parameter(ParameterSetName = "Bing", Mandatory)]
13 | [Switch]$Bing,
14 | [Parameter()]
15 | [string]$Attribution,
16 | [Parameter()]
17 | [int]$Opacity,
18 | [Parameter()]
19 | [int]$ZIndex,
20 | [Parameter()]
21 | [string]$Name
22 | )
23 |
24 | @{
25 | type = "map-raster-layer"
26 | isPlugin = $true
27 | assetId = $AssetId
28 |
29 | id = $id
30 | tileServer = $TileServer
31 | apiKey = $ApiKey
32 | attribution = $Attribution
33 | opacity = $Opactiy
34 | zIndex = $ZIndex
35 | name = $Name
36 | bing = $Bing.IsPresent
37 | mapType = $Type
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Components/framework/ud-link.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class UdLink extends React.Component {
4 |
5 |
6 | onClick(e) {
7 | if (this.props.onClick == null) {
8 | return
9 | }
10 |
11 | e.preventDefault();
12 |
13 | UniversalDashboard.publish('element-event', {
14 | type: "clientEvent",
15 | eventId: this.props.onClick,
16 | eventName: 'onClick',
17 | eventData: ''
18 | });
19 | }
20 |
21 | render() {
22 | var target = this.props.openInNewWindow ? "_blank" : "_self";
23 |
24 | var style = {
25 | color: this.props.color
26 | }
27 |
28 | if (this.props.icon) {
29 | var icon = UniversalDashboard.renderComponent({type: 'icon', icon: this.props.icon});
30 | return
31 | {icon} {this.props.text}
32 |
33 | }
34 | else {
35 | return {this.props.text}
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Scripts/map/marker.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMapMarker {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter(ParameterSetName = "LatLng", Mandatory)]
6 | [float]$Longitude,
7 | [Parameter(ParameterSetName = "LatLng", Mandatory)]
8 | [float]$Latitude,
9 | [Parameter()]
10 | [string]$Attribution,
11 | [Parameter()]
12 | [int]$Opacity,
13 | [Parameter()]
14 | [int]$ZIndex,
15 | [Parameter()]
16 | [Hashtable]$Popup,
17 | [Parameter()]
18 | [Hashtable]$Icon,
19 | [Parameter(ParameterSetName = "GeoJSON", Mandatory)]
20 | [string]$GeoJSON
21 | )
22 |
23 | if ($PSCmdlet.ParameterSetName -eq 'GeoJSON') {
24 | $Json = $GeoJSON | ConvertFrom-Json
25 | $Coordinates = $Json.Geometry.Coordinates
26 | $Latitude = $Coordinates[1]
27 | $Longitude = $Coordinates[0]
28 | }
29 |
30 | @{
31 | type = "map-marker"
32 | isPlugin = $true
33 | assetId = $AssetId
34 |
35 | id = $id
36 | longitude = $Longitude
37 | latitude = $Latitude
38 | attribution = $Attribution
39 | opacity = $Opacity
40 | zIndex = $ZIndex
41 | popup = $Popup
42 | icon = $Icon
43 | }
44 | }
--------------------------------------------------------------------------------
/examples/ad-onboarding-dashboard.ps1:
--------------------------------------------------------------------------------
1 | Import-Module UniversalDashboard
2 | Add-Type -AssemblyName 'System.Web'
3 | $Dashboard = New-UDDashboard -Title "Create new user" -Content {
4 | New-UDInput -Title "Create new user" -Endpoint {
5 | param(
6 | [Parameter(Mandatory)]
7 | [string]$FirstName,
8 | [Parameter(Mandatory)]
9 | [string]$LastName,
10 | [Parameter(Mandatory)]
11 | [string]$UserName,
12 | [Parameter(Mandatory)]
13 | [ValidateSet("IT", "HR", "Accounting", "Development")]
14 | [string]$Group
15 | )
16 |
17 | $password = [System.Web.Security.Membership]::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3)
18 | $securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
19 |
20 | $NewAdUserParameters = @{
21 | GivenName = $FirstName
22 | Surname = $LastName
23 | Name = $UserName
24 | AccountPassword = $securePassword
25 | }
26 |
27 | New-AdUser @NewAdUserParameters
28 | Add-AdGroupMember -Identity $Group -Members $userName
29 |
30 | New-UDInputAction -Content {
31 | New-UDCard -Title "Temporary Password" -Text $Password
32 | }
33 | } -Validate
34 | }
35 |
36 | Start-UDDashboard -Dashboard $Dashboard -Port 10000
--------------------------------------------------------------------------------
/src/Components/charts/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | const NivoBar = React.lazy(() => import('./nivo-bar'));
3 | const NivoCalendar = React.lazy(() => import('./nivo-calendar'));
4 | const NivoHeatmap = React.lazy(() => import('./nivo-heatmap'));
5 | const NivoLine = React.lazy(() => import('./nivo-line'));
6 | const NivoPie = React.lazy(() => import('./nivo-pie'));
7 | const NivoStream = React.lazy(() => import('./nivo-stream'));
8 | const NivoTreemap = React.lazy(() => import('./nivo-treemap'));
9 | const NivoBubble = React.lazy(() => import('./nivo-bubble'));
10 | const UDSparklines = React.lazy(() => import('./sparklines'));
11 | const UDChart = React.lazy(() => import('./ud-chart'));
12 | const UdMonitor = React.lazy(() => import('./monitor'));
13 |
14 | UniversalDashboard.register("nivo-bar", NivoBar);
15 | UniversalDashboard.register("nivo-calendar", NivoCalendar);
16 | UniversalDashboard.register("nivo-heatmap", NivoHeatmap);
17 | UniversalDashboard.register("nivo-line", NivoLine);
18 | UniversalDashboard.register("nivo-pie", NivoPie);
19 | UniversalDashboard.register("nivo-stream", NivoStream);
20 | UniversalDashboard.register("nivo-treemap", NivoTreemap);
21 | UniversalDashboard.register("nivo-bubble", NivoBubble);
22 | UniversalDashboard.register("sparklines", UDSparklines);
23 | UniversalDashboard.register("ud-chartjs", UDChart);
24 | UniversalDashboard.register("chartjs-monitor", UdMonitor);
--------------------------------------------------------------------------------
/src/Components/charts/sparklines.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Sparklines, SparklinesLine, SparklinesBars } from 'react-sparklines';
3 |
4 | export default class UDSparklines extends React.Component {
5 | render() {
6 |
7 | var { data, limit, width, height, margin, color, sparkType, min, max } = this.props;
8 |
9 | if (sparkType === "lines") {
10 | return (
11 |
12 |
13 |
14 | )
15 | }
16 |
17 | if (sparkType === "bars") {
18 | return (
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | if (sparkType === "both") {
26 | return (
27 |
28 |
29 |
30 |
31 | )
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Scripts/container.ps1:
--------------------------------------------------------------------------------
1 | function New-UDContainer {
2 | <#
3 | .SYNOPSIS
4 | Creates a Material UI container.
5 |
6 | .DESCRIPTION
7 | Creates a Material UI container. Containers pad the left and right side of the contained content to center it on larger resolution screens.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Children
13 | The child items to include within the container.
14 |
15 | .EXAMPLE
16 | Creates a container with some text.
17 |
18 | New-UDContainer -Content {
19 | New-UDTypography -Text 'Nice'
20 | }
21 | #>
22 | param(
23 | [Parameter()]
24 | [string]$Id = ([Guid]::NewGuid()).ToString(),
25 |
26 | [Alias("Content")]
27 | [Parameter(Mandatory, Position = 0)]
28 | [ScriptBlock]$Children,
29 |
30 | [Parameter()]
31 | [string]$ClassName
32 | )
33 |
34 | Process {
35 | try {
36 | $c = New-UDErrorBoundary -Content $Children
37 | }
38 | catch {
39 | $c = New-UDError -Message $_
40 | }
41 |
42 |
43 | @{
44 | isPlugin = $true
45 | id = $id
46 | assetId = $MUAssetId
47 | type = "mu-container"
48 |
49 | children = $c
50 | className = $ClassName
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/app/ud-icon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classNames from "classnames"
3 |
4 | import { library } from '@fortawesome/fontawesome-svg-core'
5 | import { fab } from '@fortawesome/free-brands-svg-icons'
6 | import { fas } from '@fortawesome/free-solid-svg-icons'
7 | import { far } from '@fortawesome/free-regular-svg-icons'
8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
9 |
10 | import brands from './fontawesome.brands'
11 | import regular from './fontawesome.regular'
12 | import solid from './fontawesome.solid'
13 |
14 | library.add(far, fab, fas)
15 |
16 | const UDIcon = (props) => {
17 |
18 | var lookup = Array.isArray(props.icon) ? props.icon[0] : props.icon;
19 | var iconName = Array.isArray(props.icon) ? props.icon[0] : props.icon;
20 | iconName = iconName.match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/g).join('-').toLowerCase();
21 |
22 | var icon;
23 | if (regular.find(x => x === lookup)) {
24 | icon = ["far", iconName]
25 | }
26 | else if (solid.find(x => x === lookup)) {
27 | icon = ["fas", iconName]
28 | }
29 | else if (brands.find(x => x === lookup)) {
30 | icon = ["fab", iconName]
31 | } else {
32 | icon = ["far", 'questionCircle']
33 | }
34 |
35 | return (
36 |
37 | )
38 | }
39 | export default UDIcon;
--------------------------------------------------------------------------------
/src/Scripts/interactive/Add-Element.ps1:
--------------------------------------------------------------------------------
1 | function Add-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Adds an element to a parent element.
5 |
6 | .DESCRIPTION
7 | Adds an element to a parent element. This cmdlet may behave differently depending on the type of parent element.
8 |
9 | .PARAMETER ParentId
10 | The parent element ID to add the item to.
11 |
12 | .PARAMETER Content
13 | The content to add to the parent element.
14 |
15 | .PARAMETER Broadcast
16 | Whether to update all connected dashboards (all users).
17 |
18 | .EXAMPLE
19 | New-UDElement -Tag 'div' -Id 'parent' -Content {
20 |
21 | }
22 |
23 | New-UDButton -Text 'Click Me' -OnClick {
24 | Add-UDElement -ParentId 'parent' -Content {
25 | New-UDTypography -Text 'Hello World'
26 | }
27 | }
28 | #>
29 | param(
30 | [Parameter(Mandatory)]
31 | [string]$ParentId,
32 | [Parameter(Mandatory)]
33 | [ScriptBlock]$Content,
34 | [Parameter()]
35 | [Switch]$Broadcast
36 | )
37 |
38 | $NewContent = & $Content
39 |
40 | $Data = @{
41 | componentId = $ParentId
42 | elements = $NewContent
43 | }
44 |
45 | if ($Broadcast) {
46 | $DashboardHub.SendWebSocketMessage("addElement", $Data)
47 | }
48 | else {
49 | $DashboardHub.SendWebSocketMessage($ConnectionId, "addElement", $Data)
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Scripts/alert.ps1:
--------------------------------------------------------------------------------
1 | function New-UDAlert {
2 | <#
3 | .SYNOPSIS
4 | Creates an alert.
5 |
6 | .DESCRIPTION
7 | Creates an alert.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Severity
13 | The severity of this alert.
14 |
15 | .PARAMETER Children
16 | Content of the alert.
17 |
18 | .PARAMETER Text
19 | Text for the body of the alert.
20 |
21 | .PARAMETER Title
22 | A title for this alert.
23 | #>
24 | param(
25 | [Parameter()]
26 | [string]$Id = [Guid]::NewGuid(),
27 | [Parameter()]
28 | [ValidateSet("success", "error", "warning", "info")]
29 | [string]$Severity = "success",
30 | [Parameter(ParameterSetName = "Content")]
31 | [Alias("Content")]
32 | [scriptblock]$Children,
33 | [Parameter(ParameterSetName = "Text")]
34 | [string]$Text,
35 | [Parameter()]
36 | [string]$Title,
37 | [Parameter()]
38 | [string]$ClassName
39 | )
40 |
41 | if ($PSCmdlet.ParameterSetName -eq 'Text') {
42 | $Children = { $Text }
43 | }
44 |
45 | @{
46 | type = "mu-alert"
47 | id = $id
48 | isPlugin = $true
49 | assetId = $MUAssetId
50 |
51 | severity = $Severity.ToLower()
52 | children = & $Children
53 | title = $Title
54 | className = $ClassName
55 | }
56 | }
--------------------------------------------------------------------------------
/examples/formatting/rows-and-columns.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of using rows and columns.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Formatting - Rows and Columns" -Content {
8 | New-UDRow -Columns {
9 | New-UDColumn -Size 6 -Content {
10 | New-UDCard -Title "Row 1 - Column 1 - Size 6"
11 | }
12 | New-UDColumn -Size 6 -Content {
13 | New-UDCard -Title "Row 1 - Column 2 - Size 6"
14 | }
15 | }
16 | New-UDRow -Columns {
17 | New-UDColumn -Size 3 -Content {
18 | New-UDCard -Title "Row 2 - Column 1 - Size 3"
19 | }
20 | New-UDColumn -Size 3 -Content {
21 | New-UDCard -Title "Row 2 - Column 2 - Size 3"
22 | }
23 | New-UDColumn -Size 3 -Content {
24 | New-UDCard -Title "Row 2 - Column 3 - Size 3"
25 | }
26 | New-UDColumn -Size 3 -Content {
27 | New-UDCard -Title "Row 2 - Column 4 - Size 3"
28 | }
29 | }
30 | New-UDRow -Columns {
31 | New-UDColumn -Size 6 -Content {
32 | New-UDCard -Title "Row 3 - Column 1 - Size 6"
33 | }
34 | New-UDColumn -Size 3 -Content {
35 | New-UDCard -Title "Row 3 - Column 2 - Size 3"
36 | }
37 | New-UDColumn -Size 3 -Content {
38 | New-UDCard -Title "Row 3 - Column 3 - Size 3"
39 | }
40 | }
41 | }
42 |
43 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
--------------------------------------------------------------------------------
/examples/grid/high-performance-sql-grid.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Provides an example of overriding the default filtering, sort and paging mechanism by querying SQL.
3 | #>
4 |
5 | Import-Module UniversalDashboard
6 |
7 | $Dashboard = New-UDDashboard -Title "Grids - Simple" -Content {
8 | New-UDGrid -Title "Animals" -Headers @("Animal", "Order") -Properties @("Animal", "Order") -Endpoint {
9 | #Overrides filtering, sorting and paging. This ensures that SQL does the actions.
10 | #All UDGrid endpioints have the following variables:
11 | # - $sortAscending - Whether or not the sort is ascending
12 | # - $sortColumn - The name of the column to sort on
13 | # - $filterText - The text to filter the data by
14 | # - $skip - The number of items to skip (used in paging)
15 | # - $take - The number of items to return (used in paging)
16 |
17 | $sortOrder = "DESC"
18 | if ($sortAscending ) {
19 | $sortOrder = "ASC"
20 | }
21 |
22 | $TotalItems = (Invoke-SqlCmd -Query "SELECT COUNT(*) as Total FROM animals WHERE Animal LIKE '%$filterText%' ").Total
23 |
24 | $Result = Invoke-SqlCmd -Query "SELECT Animal, Order FROM animals WHERE Animal LIKE '%$filterText%' ORDER BY $sortColumn $sortOrder OFFSET ($skip) ROWS FETCH NEXT ($take) ROWS ONLY "
25 |
26 | $Result | Out-UDGridData -TotalItems $TotalItems
27 | } -ServerSideProcessing
28 | }
29 |
30 | Start-UDDashboard -Dashboard $Dashboard -Port 8080
31 |
--------------------------------------------------------------------------------
/src/Components/map/index.js:
--------------------------------------------------------------------------------
1 | require('leaflet/dist/leaflet.css')
2 | require('leaflet/dist/leaflet.js')
3 | import React from 'react';
4 |
5 | const UDMap = React.lazy(() => import('./map'));
6 | const UDLayerControl = React.lazy(() => import('./UDLayerControl'));
7 | const UDLayerControlBaseLayer = React.lazy(() => import('./UDLayerControlBaseLayer'));
8 | const UDLayerControlOverlay = React.lazy(() => import('./UDLayerControlOverlay'));
9 | const UDMarker = React.lazy(() => import('./marker'));
10 | const UDVectorLayer = React.lazy(() => import('./vector-layer'));
11 | const UDRasterLayer = React.lazy(() => import('./raster-layer'));
12 | const UDFeatureGroup = React.lazy(() => import('./feature-group'));
13 | const UDHeatmapLayer = React.lazy(() => import('./heatmap-layer'));
14 | const UDClusterLayer = React.lazy(() => import('./cluster-layer'));
15 |
16 | UniversalDashboard.register("ud-map", UDMap);
17 | UniversalDashboard.register("map-layer-control", UDLayerControl);
18 | UniversalDashboard.register("map-base-layer", UDLayerControlBaseLayer);
19 | UniversalDashboard.register("map-overlay", UDLayerControlOverlay);
20 | UniversalDashboard.register("map-marker", UDMarker);
21 | UniversalDashboard.register("map-vector-layer", UDVectorLayer);
22 | UniversalDashboard.register("map-raster-layer", UDRasterLayer);
23 | UniversalDashboard.register("map-feature-group", UDFeatureGroup);
24 | UniversalDashboard.register("map-heatmap-layer", UDHeatmapLayer);
25 | UniversalDashboard.register("map-cluster-layer", UDClusterLayer);
--------------------------------------------------------------------------------
/src/Scripts/backdrop.ps1:
--------------------------------------------------------------------------------
1 | function New-UDBackdrop {
2 | <#
3 | .SYNOPSIS
4 | Creates an overlay over the current page.
5 |
6 | .DESCRIPTION
7 | Creates an overlay over the current page.
8 |
9 | .PARAMETER Id
10 | The ID of this component
11 |
12 | .PARAMETER Color
13 | The color of the backdrop.
14 |
15 | .PARAMETER Children
16 | Child components to include in the backdrop.
17 |
18 | .PARAMETER Open
19 | Whether the backdrop is open.
20 |
21 | .PARAMETER OnClick
22 | A script block to invoke when the backdrop is clicked.
23 | #>
24 | param(
25 | [Parameter ()]
26 | [string]$Id = ([Guid]::NewGuid()).ToString(),
27 | [Parameter()]
28 | [DashboardColor]$Color = '#fff',
29 | [Parameter(Mandatory)]
30 | [Alias("Content")]
31 | [ScriptBlock]$Children,
32 | [Parameter()]
33 | [Switch]$Open,
34 | [Parameter()]
35 | [Endpoint]$OnClick,
36 | [Parameter()]
37 | [string]$ClassName
38 | )
39 |
40 | if ($OnClick) {
41 | $OnClick.Register($Id, $PSCmdlet)
42 | }
43 |
44 | @{
45 | type = 'mu-backdrop'
46 | isPlugin = $true
47 | assetId = $MUAssetId
48 |
49 | id = $Id
50 | color = $Color.HtmlColor
51 | children = & $Children
52 | open = $Open.IsPresent
53 | onClick = $OnClick
54 | className = $ClassName
55 | }
56 | }
--------------------------------------------------------------------------------
/src/Scripts/heading.ps1:
--------------------------------------------------------------------------------
1 | function New-UDHeading {
2 | <#
3 | .SYNOPSIS
4 | Defines a new heading
5 |
6 | .DESCRIPTION
7 | Defines a new heading. This generates a new H tag.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Content
13 | The content of the heading.
14 |
15 | .PARAMETER Text
16 | The text of the heading.
17 |
18 | .PARAMETER Size
19 | This size of this heading. This can be 1,2,3,4,5 or 6.
20 |
21 | .PARAMETER Color
22 | The text color.
23 |
24 | .EXAMPLE
25 | A heading
26 |
27 | New-UDHeading -Text 'Heading 1' -Size 1 -Color Blue
28 | #>
29 | param(
30 | [Parameter()]
31 | [String]$Id = ([Guid]::NewGuid()),
32 | [Parameter(ParameterSetName = "Content")]
33 | [ScriptBlock]$Content,
34 | [Parameter(ParameterSetName = "Text")]
35 | [string]$Text,
36 | [Parameter()]
37 | [ValidateSet("1", "2", "3", "4", "5", "6")]
38 | $Size,
39 | [Parameter()]
40 | [UniversalDashboard.Models.DashboardColor]$Color = 'black',
41 | [Parameter()]
42 | [string]$ClassName
43 | )
44 |
45 | if ($PSCmdlet.ParameterSetName -eq "Text") {
46 | $Content = { $Text }
47 | }
48 |
49 | New-UDElement -Id $Id -Tag "h$size" -Content $Content -Attributes @{
50 | className = $className
51 | style = @{
52 | color = $Color.HtmlColor
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Scripts/skeleton.ps1:
--------------------------------------------------------------------------------
1 | function New-UDSkeleton {
2 | <#
3 | .SYNOPSIS
4 | Creates a loading skeleton
5 |
6 | .DESCRIPTION
7 | Creates a loading skeleton
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Variant
13 | The type of skeleton to display. Valid values are "text", "rect", "circle"
14 |
15 | .PARAMETER Height
16 | The static height of the skeleton.
17 |
18 | .PARAMETER Width
19 | The static width of the skeleton.
20 |
21 | .PARAMETER Animation
22 | The type of animation to use for the skeleton. Valid values are: "wave", "disabled", "pulsate"
23 | #>
24 | param(
25 | [Parameter()]
26 | [String]$Id = ([Guid]::NewGuid()),
27 | [Parameter()]
28 | [ValidateSet("text", "rect", "circle")]
29 | [string]$Variant = 'text',
30 | [Parameter()]
31 | [int]$Height,
32 | [Parameter()]
33 | [int]$Width,
34 | [Parameter()]
35 | [ValidateSet("wave", "disabled", "pulsate")]
36 | [string]$Animation = "pulsate",
37 | [Parameter()]
38 | [string]$ClassName
39 | )
40 |
41 | @{
42 | type = "mu-skeleton"
43 | id = $Id
44 | isPlugin = $true
45 | assetId = $MUAssetId
46 |
47 | variant = $Variant.ToLower()
48 | height = $Height
49 | width = $Width
50 | animation = $Animation.ToLower()
51 | className = $ClassName
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Components/radio.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 | import { FormContext } from './form';
4 | import RadioGroup from '@mui/material/RadioGroup';
5 | import Radio from '@mui/material/Radio';
6 | import FormControlLabel from '@mui/material/FormControlLabel';
7 |
8 | export const UDRadioGroupWithContext = withComponentFeatures((props) => {
9 | return (
10 |
11 | {
12 | ({ onFieldChange }) =>
13 | }
14 |
15 | )
16 | })
17 |
18 | const UDRadioGroup = (props) => {
19 |
20 | const onChange = (event) => {
21 | props.onFieldChange({ id: props.id, value: event.target.value })
22 | props.setState({ value: event.target.value })
23 |
24 | if (props.onChange) {
25 | props.onChange(event.target.value);
26 | }
27 | }
28 |
29 | useEffect(() => {
30 | props.onFieldChange({ id: props.id, value: props.value });
31 | return () => { }
32 | }, true)
33 |
34 | return (
35 | onChange(e)}>
36 | {props.render(props.children)}
37 |
38 | )
39 | }
40 |
41 | export const UDRadio = withComponentFeatures((props) => {
42 | return (
43 | }>
44 |
45 | )
46 | })
--------------------------------------------------------------------------------
/src/Components/transition.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Collapse, Slide, Zoom, Grow, Fade } from '@mui/material';
3 | import { withComponentFeatures } from 'universal-dashboard';
4 |
5 | class TransitionWrapper extends React.Component {
6 | render() {
7 | return this.props.children;
8 | }
9 | }
10 |
11 | function Transition(props) {
12 | if (props.transition === "fade") {
13 | return
14 | {props.render(props.children)}
15 |
16 | }
17 |
18 | if (props.transition === "zoom") {
19 | return
20 | {props.render(props.children)}
21 |
22 | }
23 |
24 | if (props.transition === "grow") {
25 | return
26 | {props.render(props.children)}
27 |
28 | }
29 |
30 | if (props.transition === "slide") {
31 | return
32 | {props.render(props.children)}
33 |
34 | }
35 |
36 | if (props.transition === "collapse") {
37 | return (
38 |
39 | {props.render(props.children)}
40 |
41 | );
42 | }
43 | }
44 |
45 | export default withComponentFeatures(Transition);
--------------------------------------------------------------------------------
/src/Components/map/MarkerCluster.Default.css:
--------------------------------------------------------------------------------
1 | .marker-cluster-small {
2 | background-color: rgba(181, 226, 140, 0.6);
3 | }
4 | .marker-cluster-small div {
5 | background-color: rgba(110, 204, 57, 0.6);
6 | }
7 |
8 | .marker-cluster-medium {
9 | background-color: rgba(241, 211, 87, 0.6);
10 | }
11 | .marker-cluster-medium div {
12 | background-color: rgba(240, 194, 12, 0.6);
13 | }
14 |
15 | .marker-cluster-large {
16 | background-color: rgba(253, 156, 115, 0.6);
17 | }
18 | .marker-cluster-large div {
19 | background-color: rgba(241, 128, 23, 0.6);
20 | }
21 |
22 | /* IE 6-8 fallback colors */
23 | .leaflet-oldie .marker-cluster-small {
24 | background-color: rgb(181, 226, 140);
25 | }
26 | .leaflet-oldie .marker-cluster-small div {
27 | background-color: rgb(110, 204, 57);
28 | }
29 |
30 | .leaflet-oldie .marker-cluster-medium {
31 | background-color: rgb(241, 211, 87);
32 | }
33 | .leaflet-oldie .marker-cluster-medium div {
34 | background-color: rgb(240, 194, 12);
35 | }
36 |
37 | .leaflet-oldie .marker-cluster-large {
38 | background-color: rgb(253, 156, 115);
39 | }
40 | .leaflet-oldie .marker-cluster-large div {
41 | background-color: rgb(241, 128, 23);
42 | }
43 |
44 | .marker-cluster {
45 | background-clip: padding-box;
46 | border-radius: 20px;
47 | }
48 | .marker-cluster div {
49 | width: 30px;
50 | height: 30px;
51 | margin-left: 5px;
52 | margin-top: 5px;
53 |
54 | text-align: center;
55 | border-radius: 15px;
56 | font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
57 | }
58 | .marker-cluster span {
59 | line-height: 30px;
60 | }
--------------------------------------------------------------------------------
/src/Scripts/interactive/Set-Element.ps1:
--------------------------------------------------------------------------------
1 | function Set-UDElement {
2 | <#
3 | .SYNOPSIS
4 | Set properties of an element.
5 |
6 | .DESCRIPTION
7 | Set the properties of an element.
8 |
9 | .PARAMETER Id
10 | The element to set properites on.
11 |
12 | .PARAMETER Properties
13 | The properties to set in the form of a hashtable.
14 |
15 | .PARAMETER Broadcast
16 | Whether to update this element on all connected clients.
17 |
18 | .PARAMETER Content
19 | Content to set within the element.
20 |
21 | .EXAMPLE
22 | New-UDButton -Id 'button' -Text 'Disable Me' -OnClick {
23 | Set-UDElement -Id 'button -Properties @{
24 | 'disabled' = $true
25 | }
26 | }
27 | #>
28 | param(
29 | [Parameter(Mandatory)]
30 | [string]$Id,
31 | [Alias("Attributes")]
32 | [Parameter()]
33 | [Hashtable]$Properties,
34 | [Parameter()]
35 | [Switch]$Broadcast,
36 | [Parameter()]
37 | [ScriptBlock]$Content
38 | )
39 |
40 | if ($Content -and -not $Properties) {
41 | $Properties = @{}
42 | }
43 |
44 | if ($Content) {
45 | $Properties['content'] = [Array](& $Content)
46 | }
47 |
48 | $Data = @{
49 | componentId = $Id
50 | state = $Properties
51 | }
52 |
53 | if ($Broadcast) {
54 | $DashboardHub.SendWebSocketMessage("setState", $data)
55 | }
56 | else {
57 | $DashboardHub.SendWebSocketMessage($ConnectionId, "setState", $Data)
58 | }
59 | }
--------------------------------------------------------------------------------
/src/Components/table/v2/columns.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { Checkbox } from '@mui/material'
3 |
4 | const SelectCheckBox = React.forwardRef(
5 | ({ indeterminate, onRowSelection, singleSelection, toggleAllRowsSelected, onChange: initialOnChange, ...rest }, ref) => {
6 | const defaultRef = React.useRef()
7 | const resolvedRef = ref || defaultRef
8 |
9 | React.useEffect(() => {
10 | resolvedRef.current.indeterminate = indeterminate
11 | }, [resolvedRef, indeterminate])
12 |
13 | function onChange(event, checked) {
14 | if (singleSelection)
15 | {
16 | toggleAllRowsSelected(false);
17 | }
18 |
19 | rest.row.toggleRowSelected()
20 | if (onRowSelection) {
21 | onRowSelection({ id: rest.row.id, ...rest.row.values, selected: !rest.row.isSelected })
22 | }
23 | }
24 |
25 | return
26 | }
27 | )
28 |
29 | const SelectAllCheckBox = React.forwardRef(
30 | ({ indeterminate, onRowSelection, onChange, ...rest }, ref) => {
31 | const defaultRef = React.useRef()
32 | const resolvedRef = ref || defaultRef
33 |
34 | React.useEffect(() => {
35 | resolvedRef.current.indeterminate = indeterminate
36 | }, [resolvedRef, indeterminate])
37 |
38 | function onChangeSelection(event, checked) {
39 | onChange(event, checked);
40 | if (onRowSelection) {
41 | onRowSelection({ id: 'all', selected: checked })
42 | }
43 | }
44 |
45 | return
46 | }
47 | )
48 |
49 | export { SelectCheckBox, SelectAllCheckBox }
--------------------------------------------------------------------------------
/src/Scripts/avatar.ps1:
--------------------------------------------------------------------------------
1 | function New-UDAvatar {
2 | <#
3 | .SYNOPSIS
4 | Creates a new Avatar.
5 |
6 | .DESCRIPTION
7 | Creates a new Avatar. An avatar is typically an image of a user.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER Image
13 | The URL of an image to show in the avatar.
14 |
15 | .PARAMETER Alt
16 | The alt text to assign to the avatar.
17 |
18 | .PARAMETER ClassName
19 | Classes to assign to the avatar component.
20 |
21 | .PARAMETER Variant
22 | The variant type of the avatar.
23 |
24 | .EXAMPLE
25 | A small avatar using Alon's image.
26 |
27 | New-UDAvatar -Image 'https://avatars2.githubusercontent.com/u/34351424?s=460&v=4' -Alt 'alon gvili avatar' -Id 'avatarContent' -Variant small
28 | #>
29 | param(
30 | [Parameter ()][string]$Id = ([Guid]::NewGuid()).ToString(),
31 | [Parameter ()][string]$Image,
32 | [Parameter ()][string]$Alt,
33 | [Parameter ()][string]$ClassName,
34 | [Parameter ()][string]$Variant
35 | )
36 | End {
37 | $Avatar = @{
38 | type = 'mu-avatar'
39 | isPlugin = $true
40 | assetId = $MUAssetId
41 |
42 | id = $Id
43 | image = $Image
44 | alt = $Alt
45 | variant = $Variant
46 | className = $ClassName
47 | }
48 | $Avatar.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.Avatar") | Out-Null
49 | $Avatar
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Components/framework/error-card.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import makeStyles from '@mui/styles/makeStyles';
3 | import Card from '@mui/material/Card';
4 | import CardActions from '@mui/material/CardActions';
5 | import CardContent from '@mui/material/CardContent';
6 | import Typography from '@mui/material/Typography';
7 | import UDIcon from '../../app/ud-icon';
8 |
9 | const useStyles = makeStyles({
10 | root: {
11 | minWidth: 275,
12 | },
13 | bullet: {
14 | display: 'inline-block',
15 | margin: '0 2px',
16 | transform: 'scale(0.8)',
17 | },
18 | title: {
19 | fontSize: 14,
20 | },
21 | pos: {
22 | marginBottom: 12,
23 | },
24 | });
25 |
26 | export default function ErrorCard(props) {
27 |
28 | const classes = useStyles();
29 | var icon =
30 |
31 | var records = null;
32 | if (!Array.isArray(props.errorRecords)) {
33 | records = Unknown error occurred
34 | }
35 | else {
36 | records = props.errorRecords.map(x => {
37 | return (
38 |
39 |
40 | {x.message}
41 |
42 |
43 | {x.location}
44 |
45 |
46 | )
47 | })
48 | }
49 |
50 | return (
51 |
52 |
53 |
54 | {icon} {props.title ? props.title : "One or more errors occurred"}
55 |
56 | {records}
57 |
58 |
59 | )
60 | }
--------------------------------------------------------------------------------
/src/Scripts/style.ps1:
--------------------------------------------------------------------------------
1 | function New-UDStyle {
2 | <#
3 | .SYNOPSIS
4 | Style a component with CSS styles.
5 |
6 | .DESCRIPTION
7 | Style a component with CSS styles.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Style
13 | The CSS style to apply
14 |
15 | .PARAMETER Tag
16 | The outer HTML tag to use.
17 |
18 | .PARAMETER Content
19 | The content of this style.
20 |
21 | .PARAMETER Sx
22 | A hashtable of theme-aware CSS properties.
23 |
24 | .PARAMETER Component
25 | The root component to apply the Sx component to.
26 |
27 | .EXAMPLE
28 |
29 | #>
30 | param(
31 | [Parameter()]
32 | [string]$Id = (New-Guid).ToString(),
33 | [Parameter(Mandatory = $true, ParameterSetName = 'Style')]
34 | [string]$Style,
35 | [Parameter(Mandatory = $true, ParameterSetName = 'Sx')]
36 | [Hashtable]$Sx,
37 | [Parameter(ParameterSetName = 'Sx')]
38 | [string]$Component,
39 | [Parameter()]
40 | [string]$Tag = 'div',
41 | [Parameter()]
42 | [ScriptBlock]$Content
43 | )
44 |
45 | End {
46 |
47 | $Children = $null
48 | if ($null -ne $Content) {
49 | $Children = & $Content
50 | }
51 |
52 | @{
53 | assetId = $AssetId
54 | isPlugin = $true
55 | type = "ud-style"
56 | id = $Id
57 | style = $Style
58 | tag = $Tag
59 | content = $Children
60 | sx = $Sx
61 | component = $Component
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Components/button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classNames from 'classnames'
3 | import Button from '@mui/material/Button'
4 | import UdMuIcon from './icon'
5 | import { withComponentFeatures } from 'universal-dashboard'
6 | import makeStyles from '@mui/styles/makeStyles';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | button: {
10 | margin: theme.spacing(),
11 | },
12 | leftIcon: {
13 | marginRight: theme.spacing(),
14 | },
15 | rightIcon: {
16 | marginLeft: theme.spacing(),
17 | },
18 | }))
19 |
20 | const UdButton = props => {
21 | const classes = useStyles()
22 |
23 | const handleClick = () => {
24 | if (props.onClick == null) return
25 | props.onClick();
26 | }
27 |
28 | var icon = props.icon ? (
29 |
35 | ) : null
36 |
37 | return (
38 |
55 | )
56 | }
57 |
58 | export default withComponentFeatures(UdButton)
59 |
--------------------------------------------------------------------------------
/src/Components/timeline.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Timeline from '@mui/lab/Timeline';
3 | import TimelineItem from '@mui/lab/TimelineItem';
4 | import TimelineSeparator from '@mui/lab/TimelineSeparator';
5 | import TimelineConnector from '@mui/lab/TimelineConnector';
6 | import TimelineContent from '@mui/lab/TimelineContent';
7 | import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';
8 | import TimelineDot from '@mui/lab/TimelineDot';
9 | import { withComponentFeatures } from "universal-dashboard";
10 |
11 | const UDTimeline = (props) => {
12 | let children = props.children;
13 | if (!Array.isArray(children)) {
14 | children = [children];
15 | }
16 |
17 | return
18 | {children.map(x => {
19 | return (
20 |
21 | {x.oppositeContent && (
22 |
23 | {props.render(x.oppositeContent)}
24 |
25 | )}
26 |
27 |
28 | {x.icon && props.render(x.icon)}
29 |
30 |
31 |
32 |
33 | {props.render(x.content)}
34 |
35 |
36 | )
37 | })}
38 |
39 | }
40 |
41 | export default withComponentFeatures(UDTimeline);
--------------------------------------------------------------------------------
/src/Components/floating-action-button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import makeStyles from '@mui/styles/makeStyles';
3 | import Fab from '@mui/material/Fab';
4 | var classNames = require('classnames');
5 |
6 | function onClick(props) {
7 | if (props.onClick) {
8 | UniversalDashboard.publish('element-event', {
9 | type: "clientEvent",
10 | eventId: props.onClick,
11 | eventName: 'onChange',
12 | eventData: ''
13 | });
14 | }
15 | }
16 |
17 | export default function FloatingActionButton(props) {
18 | const useStyles = makeStyles(theme => ({
19 | root: {
20 | '& > *': {
21 | margin: theme.spacing(1),
22 | },
23 | },
24 | extendedIcon: {
25 | marginRight: theme.spacing(1),
26 | },
27 | fabRight: {
28 | position: 'absolute',
29 | bottom: theme.spacing(2),
30 | right: theme.spacing(2),
31 | },
32 | fabLeft: {
33 | position: 'absolute',
34 | bottom: theme.spacing(2),
35 | left: theme.spacing(2),
36 | },
37 | }));
38 |
39 | const classes = useStyles();
40 |
41 | var icon = null;
42 | if (props.icon) {
43 | icon = UniversalDashboard.renderComponent(props.icon)
44 | }
45 |
46 | let position = null;
47 | if (props.position === 'bottomright') {
48 | position = classes.fabRight;
49 | }
50 |
51 | if (props.position === 'bottomleft') {
52 | position = classes.fabLeft;
53 | }
54 |
55 | return (
56 |
57 | onClick(props)} size={props.size} id={props.id} color={props.themeColor}>
58 | {icon}
59 |
60 |
61 | );
62 | }
--------------------------------------------------------------------------------
/src/app/error-boundary.jsx:
--------------------------------------------------------------------------------
1 | import { Alert, AlertTitle, Button, Snackbar } from "@mui/material";
2 | import React from "react";
3 | import copy from 'copy-to-clipboard'
4 |
5 | export default class ErrorBoundary extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = { hasError: false, error: null, errorInfo: null, snackbarOpen: false };
9 | }
10 |
11 | componentDidCatch(error, errorInfo) {
12 | this.setState({ hasError: true, error, errorInfo })
13 | }
14 |
15 | render() {
16 | const copyDetails = () => {
17 | copy(`Error rendering component (${this.props.componentType || 'unknown'})\r\n${this.state.error && this.state.error.toString()}\r\n${this.state.errorInfo.componentStack}`)
18 | this.setState({ snackbarOpen: true })
19 | }
20 |
21 | if (this.state.hasError) {
22 | return <>
23 |
24 | {`Error rendering component (${this.props.componentType || 'unknown'})`}
25 | This error is not expected. Please report it to Ironman Software.
26 |
27 |
28 | this.setState({ snackbarOpen: false })}
31 | autoHideDuration={6000}
32 | message="Error details copied"
33 | />
34 | >
35 | }
36 |
37 | return this.props.children;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Scripts/map/map.ps1:
--------------------------------------------------------------------------------
1 | function New-UDMap {
2 | param(
3 | [Parameter()]
4 | [string]$Id = (New-Guid).ToString(),
5 | [Parameter()]
6 | [float]$Longitude,
7 | [Parameter()]
8 | [float]$Latitude,
9 | [Parameter()]
10 | [int]$Zoom,
11 | [Parameter()]
12 | [string]$Height = '500px',
13 | [Parameter()]
14 | [string]$Width = '100%',
15 | [Parameter(Mandatory)]
16 | [object]$Endpoint,
17 | [ValidateSet("topright", "topleft", "bottomright", "bottomleft")]
18 | [string]$ZoomControlPosition = "topright",
19 | [ValidateSet("topright", "topleft", "bottomright", "bottomleft", "hide")]
20 | [string]$ScaleControlPosition = "bottomleft",
21 | [Parameter()]
22 | [Switch]$Animate,
23 | [Parameter()]
24 | [int]$MaxZoom = 18
25 | )
26 |
27 | End {
28 |
29 | if ($Endpoint -is [scriptblock]) {
30 | $Endpoint = New-UDEndpoint -Endpoint $Endpoint -Id $Id
31 | }
32 | elseif ($Endpoint -isnot [UniversalDashboard.Models.Endpoint]) {
33 | throw "Endpoint must be a script block or UDEndpoint"
34 | }
35 |
36 | @{
37 | assetId = $AssetId
38 | isPlugin = $true
39 | type = "ud-map"
40 | id = $Id
41 |
42 | longitude = $Longitude
43 | latitude = $Latitude
44 | zoom = $Zoom
45 | height = $Height
46 | width = $Width
47 | zoomControlPosition = $ZoomControlPosition
48 | animate = $Animate.IsPresent
49 | scaleControlPosition = $ScaleControlPosition
50 | maxZoom = $MaxZoom
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Scripts/stack.ps1:
--------------------------------------------------------------------------------
1 | function New-UDStack {
2 | <#
3 | .SYNOPSIS
4 | The Stack component manages layout of immediate children along the vertical or horizontal axis with optional spacing and/or dividers between each child.
5 |
6 | .DESCRIPTION
7 | Stack is concerned with one-dimensional layouts, while Grid handles two-dimensional layouts. The default direction is column which stacks children vertically.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Children
13 | The children to stack.
14 |
15 | .PARAMETER Divider
16 | An optional divider component.
17 |
18 | .PARAMETER Spacing
19 | The spacing between the items.
20 |
21 | .PARAMETER Direction
22 | The stack direction.
23 |
24 | .EXAMPLE
25 | New-UDStack -Content {
26 | New-UDPaper
27 | New-UDPaper
28 | New-UDPaper
29 | } -Spacing 2
30 |
31 | Creates a stack of papers with 2 spacing.
32 | #>
33 | param(
34 | [Parameter()]
35 | [String]$Id = ([Guid]::NewGuid()),
36 | [Parameter(Mandatory)]
37 | [Alias("Content")]
38 | [scriptblock]$Children,
39 | [Parameter()]
40 | [scriptblock]$Divider = {},
41 | [Parameter()]
42 | [int]$Spacing,
43 | [Parameter()]
44 | [ValidateSet('row', 'column')]
45 | [string]$Direction = 'row'
46 | )
47 |
48 | @{
49 | id = $id
50 | assetId = $MUAssetId
51 | isPlugin = $true
52 | type = "mu-stack"
53 |
54 | children = & $Children
55 | divider = & $Divider
56 | spacing = $Spacing
57 | direction = $Direction.ToLower()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Components/editor.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 |
4 | import Embed from '@editorjs/embed'
5 | import Table from '@editorjs/table'
6 | import Paragraph from '@editorjs/paragraph'
7 | import List from '@editorjs/list'
8 | import Warning from '@editorjs/warning'
9 | import Code from '@editorjs/code'
10 | import LinkTool from '@editorjs/link'
11 | import Image from '@editorjs/image'
12 | import Raw from '@editorjs/raw'
13 | import Header from '@editorjs/header'
14 | import Quote from '@editorjs/quote'
15 | import Marker from '@editorjs/marker'
16 | import CheckList from '@editorjs/checklist'
17 | import Delimiter from '@editorjs/delimiter'
18 | import InlineCode from '@editorjs/inline-code'
19 | import SimpleImage from '@editorjs/simple-image'
20 | import EditorJs from 'react-editor-js';
21 | import Parser from '@son_xx/editor-js-parser'
22 |
23 | const EDITOR_JS_TOOLS = {
24 | embed: Embed,
25 | table: Table,
26 | paragraph: Paragraph,
27 | list: List,
28 | warning: Warning,
29 | code: Code,
30 | linkTool: LinkTool,
31 | image: Image,
32 | raw: Raw,
33 | header: Header,
34 | quote: Quote,
35 | marker: Marker,
36 | checklist: CheckList,
37 | delimiter: Delimiter,
38 | inlineCode: InlineCode,
39 | simpleImage: SimpleImage
40 | }
41 |
42 | const UDEditor = (props) => {
43 | const save = (api, data) => {
44 | if (props.onChange) {
45 | let myData = data;
46 | if (props.format === "html") {
47 | myData = Parser(data.blocks)
48 | }
49 | props.onChange(myData);
50 | }
51 | }
52 |
53 | return (
54 | <>
55 |
56 | >
57 | )
58 | }
59 |
60 | export default withComponentFeatures(UDEditor);
--------------------------------------------------------------------------------
/src/Scripts/badge.ps1:
--------------------------------------------------------------------------------
1 | function New-UDBadge {
2 | param(
3 | [Parameter ()]
4 | [string]$Id = ([Guid]::NewGuid()).ToString(),
5 | [Parameter()]
6 | [ValidateSet("defeault", 'primary', 'secondary', 'error', 'info', 'success', 'warning')]
7 | [string]$Color = 'default',
8 | [Parameter(Mandatory)]
9 | [Alias("Content")]
10 | [ScriptBlock]$Children = {},
11 | [Parameter()]
12 | [scriptblock]$BadgeContent = {},
13 | [Parameter()]
14 | [Switch]$Invisible,
15 | [Parameter()]
16 | [int]$Max = 99,
17 | [Parameter()]
18 | [ValidateSet('circular', 'rectangular')]
19 | [string]$Overlap = 'rectangular',
20 | [Parameter()]
21 | [Switch]$ShowZero,
22 | [Parameter()]
23 | [ValidateSet('standard', 'dot')]
24 | [string]$Variant = 'standard',
25 | [Parameter()]
26 | [ValidateSet('topright', 'topleft', 'bottomright', 'bottomleft')]
27 | [string]$Location = 'topright'
28 | )
29 |
30 | @{
31 | type = 'mu-badge'
32 | isPlugin = $true
33 | assetId = $MUAssetId
34 | id = $Id
35 | color = $Color.ToLower()
36 | children = & $Children
37 | badgeContent = & $BadgeContent
38 | invisible = $Invisible.IsPresent
39 | max = $Max
40 | overlap = $Overlap.ToLower()
41 | showZero = $ShowZero.IsPresent
42 | variant = $Variant.ToLower()
43 | location = $Location.ToLower()
44 | }
45 | }
46 |
47 | <#
48 | New-UDBadge -Content {
49 | New-UDIcon -Icon 'User' -Size 2x
50 | } -BadgeContent {
51 | 100
52 | } -Color primary
53 |
54 | #>
--------------------------------------------------------------------------------
/src/Scripts/debugdashboard.ps1:
--------------------------------------------------------------------------------
1 | function Debug-PSUDashboard {
2 | <#
3 | .SYNOPSIS
4 | Provides a utility function for debugging scripts running PowerShell Universal Dashboard.
5 |
6 | .DESCRIPTION
7 | Provides a utility function for debugging scripts running PowerShell Universal Dashboard. This cmdlet integrates with the VS Code PowerShell Universal extension to automatically connect the debugger to endpoints running in UD.
8 |
9 | .EXAMPLE
10 | Creates an element that invokes the Debug-PSUDashboard cmdlet.
11 |
12 | New-UDElement -Tag div -Endpoint {
13 | Debug-PSUDashboard
14 | }
15 | #>
16 | [CmdletBinding()]
17 | param()
18 |
19 | $DebugPreference = 'continue'
20 |
21 | $Runspace = ([runspace]::DefaultRunspace).id
22 |
23 | Show-UDModal -Header {
24 | New-UDTypography -Text 'Debug Dashboard' -Variant h4
25 | } -Content {
26 | Write-Debug "IN DEBUG MODE: Enter-PSHostProcess -Id $PID then Debug-Runspace -Id $Runspace"
27 | New-UDTypography -Text "You can run the following PowerShell commands in any PowerShell host to debug this dashboard."
28 | New-UDElement -Tag 'pre' -Content {
29 | "Enter-PSHostProcess -Id $PID`r`nDebug-Runspace -Id $Runspace"
30 | }
31 | } -Footer {
32 | New-UDLink -Children {
33 | New-UDButton -Text 'Debug with VS Code'
34 | } -Url "vscode://ironmansoftware.powershell-universal/debug?PID=$PID&RS=$Runspace"
35 | New-UDLink -Children {
36 | New-UDButton -Text 'Debug with VS Code Insiders'
37 | } -Url "vscode-insiders://ironmansoftware.powershell-universal/debug?PID=$PID&RS=$Runspace"
38 | New-UDButton -Text 'Close' -OnClick { Hide-UDModal }
39 | }
40 |
41 | Wait-Debugger
42 | }
--------------------------------------------------------------------------------
/src/Components/table/components/baseTableFooter.js:
--------------------------------------------------------------------------------
1 | //@ts-check
2 | import React from "react"
3 | import { TableFooter, TablePagination, TableRow, LinearProgress } from "@mui/material";
4 | import TablePaginationActions from "../v2/TablePaginationActions";
5 | import { onlyUnique } from "../v2/utilities";
6 |
7 | export default function BaseTableFooter({
8 | userPageSize,
9 | userPageSizeOptions,
10 | colSpan,
11 | count,
12 | pageIndex,
13 | gotoPage,
14 | setPageSize,
15 | pageSize,
16 | isVisible,
17 | loading,
18 | disablePageSizeAll,
19 | }) {
20 | function onChangePage(_, newPage) {
21 | gotoPage(newPage);
22 | }
23 |
24 | function onChangeRowsPerPage(event) {
25 | setPageSize(Number(event.target.value));
26 | }
27 |
28 | var rowsPerPageOptions = [
29 | userPageSize,
30 | ...userPageSizeOptions,
31 | ]
32 |
33 | if (!disablePageSizeAll) {
34 | rowsPerPageOptions.push({ label: "All", value: count })
35 | }
36 |
37 | return (
38 | isVisible &&
39 |
40 | {loading ? : }
41 | {!loading && count === 0 && No data
}
42 | a - b).filter(onlyUnique)}
45 | colSpan={colSpan}
46 | count={count}
47 | rowsPerPage={pageSize}
48 | page={pageIndex}
49 | onPageChange={onChangePage}
50 | onRowsPerPageChange={onChangeRowsPerPage}
51 | ActionsComponent={TablePaginationActions}
52 | />
53 |
54 |
55 | );
56 | }
--------------------------------------------------------------------------------
/src/Components/switch.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Switch from '@mui/material/Switch';
3 | import Grid from '@mui/material/Grid';
4 | import { withComponentFeatures } from 'universal-dashboard';
5 | import { FormContext } from './form';
6 |
7 | const UDSwitchWithContext = (props) => {
8 | return (
9 |
10 | {
11 | ({ onFieldChange }) =>
12 | }
13 |
14 | )
15 | }
16 |
17 | const UDSwitch = (props) => {
18 |
19 | const onChange = (event) => {
20 | props.onFieldChange({ id: props.id, value: event.target.checked })
21 | props.setState({ checked: event.target.checked });
22 |
23 | if (props.onChange) {
24 | props.onChange(event.target.checked)
25 | }
26 | }
27 |
28 | useEffect(() => {
29 | props.onFieldChange({ id: props.id, value: props.checked });
30 | return () => { }
31 | }, true)
32 |
33 | const switchC = onChange(event)}
40 | disabled={props.disabled}
41 | />
42 |
43 | return props.checkedLabel || props.uncheckedLabel || props.label ? (
44 |
45 | {props.uncheckedLabel}
46 |
47 | {switchC}
48 |
49 | {props.checkedLabel || props.label}
50 |
51 | ) : switchC
52 | }
53 |
54 | export default withComponentFeatures(UDSwitchWithContext);
55 |
56 |
--------------------------------------------------------------------------------
/src/Components/map/cluster-layer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import UDMarker from './marker';
3 | import MarkerClusterGroup from './Cluster';
4 | import {isGuid} from './utils';
5 |
6 | export default class UDClusterLayer extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 |
11 | this.state = {
12 | markers: props.markers
13 | }
14 | }
15 |
16 | componentWillMount() {
17 | if (!isGuid(this.props.id)) {
18 | this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this));
19 | }
20 | }
21 |
22 | onIncomingEvent(eventName, event) {
23 | if (event.type === "removeElement") {
24 | this.props.onRemove(event.componentId);
25 | }
26 |
27 | if (event.type === "clearElement") {
28 | this.setState({
29 | markers: []
30 | })
31 | }
32 |
33 | if (event.type === 'addElement') {
34 | var markers = this.state.markers;
35 |
36 | var content = event.elements;
37 | if (!Array.isArray(content)) {
38 | content = [content]
39 | }
40 |
41 | markers = markers.concat(content);
42 |
43 | this.setState({
44 | markers
45 | })
46 | }
47 | }
48 |
49 | onRemoveMarker(id) {
50 | var markers = this.state.markers.filter(x => x.id !== id);
51 |
52 | this.setState({
53 | markers
54 | })
55 | }
56 |
57 | render() {
58 | return (
59 |
60 | {this.state.markers.map(x => )}
61 |
62 | )
63 | }
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/Scripts/timepicker.ps1:
--------------------------------------------------------------------------------
1 | function New-UDTimePicker {
2 | <#
3 | .SYNOPSIS
4 | Creates a time picker.
5 |
6 | .DESCRIPTION
7 | Creates a time picker. This component can be used stand alone or within New-UDForm.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER Label
13 | The label to show with the time picker.
14 |
15 | .PARAMETER OnChange
16 | A script block to call when the time is changed. The $EventData variable contains the currently selected time.
17 |
18 | .PARAMETER Value
19 | The current value of the time picker.
20 |
21 | .PARAMETER Locale
22 | Change the language of the time picker.
23 |
24 | .EXAMPLE
25 | Creates a new time picker
26 |
27 | New-UDTimePicker -Id 'timePicker'
28 | #>
29 | param(
30 | [Parameter()]
31 | [string]$Id = [Guid]::NewGuid().ToString(),
32 | [Parameter()]
33 | [string]$Label,
34 | [Parameter()]
35 | [Endpoint]$OnChange,
36 | [Parameter()]
37 | [string]$Value,
38 | [Parameter()]
39 | [ValidateSet("en", "de", 'ru', 'fr')]
40 | [string]$Locale = "en",
41 | [Parameter()]
42 | [string]$ClassName,
43 | [Parameter()]
44 | [Switch]$DisableAmPm
45 | )
46 |
47 | if ($OnChange) {
48 | $OnChange.Register($Id, $PSCmdlet)
49 | }
50 |
51 | @{
52 | id = $Id
53 | type = 'mu-timepicker'
54 | asset = $MUAssetId
55 | isPlugin = $true
56 |
57 | onChange = $OnChange
58 | value = $Value
59 | label = $Label
60 | locale = $Locale.ToLower()
61 | className = $ClassName
62 | ampm = -not ($DisableAmPm.IsPresent)
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Components/card-expand.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import withStyles from '@mui/styles/withStyles';
3 | import classNames from "classnames";
4 | import { ReactInterval } from "react-interval/lib/Component";
5 |
6 | const styles = theme => ({
7 | content: {
8 | display: "flex"
9 | }
10 | });
11 |
12 | class UDCardExpand extends React.Component {
13 | state = {
14 | content: this.props.content,
15 | };
16 |
17 | onLoadData = () => {
18 | if (!this.props.isEndpoint) {
19 | this.setState({
20 | content: this.props.content
21 | })
22 | }
23 | else {
24 | UniversalDashboard.get(
25 | `/api/internal/component/element/${this.props.id}`, data => {
26 | data.error ?
27 | this.setState({ hasError: true, error: data.error, content: data, })
28 | : this.setState({ content: data, loading: false })
29 | }
30 | )
31 | }
32 | }
33 |
34 | componentWillMount = () => {
35 | this.onLoadData();
36 | }
37 |
38 | render() {
39 | const {
40 | className,
41 | classes,
42 | id,
43 | style,
44 | isEndpoint,
45 | refreshInterval,
46 | autoRefresh
47 | } = this.props;
48 |
49 | const { content } = this.state;
50 |
51 | return (
52 |
53 |
57 | {UniversalDashboard.renderComponent(content)}
58 |
59 |
60 | {isEndpoint ?
61 | : null}
65 |
66 |
67 | );
68 | }
69 | }
70 |
71 | export default withStyles(styles)(UDCardExpand);
72 |
--------------------------------------------------------------------------------
/examples/ping-grid.ps1:
--------------------------------------------------------------------------------
1 | Import-Module UniversalDashboard -Force
2 |
3 | # First Let's define a list of servers to monitor
4 | $MonitoredServersSetup = @()
5 | $Cache:MonitoredServers
6 | $Servers = @("www.google.com", "www.github.com", "8.8.8.8","192.168.0.1")
7 |
8 | # Create some powershell objects to feed into our UI / Update Later
9 | $Servers | ForEach-Object{
10 | $ServerObject = [PSCustomObject]@{
11 | Server = $_
12 | LastUp = (Get-Date)
13 | Status = $false
14 | }
15 | $MonitoredServersSetup += $ServerObject
16 | }
17 | $Cache:MonitoredServers = $MonitoredServersSetup
18 |
19 | # Setup new Dashboard with Grid
20 | Get-UDDashboard | Stop-UDDashboard
21 | $Dashboard = New-UDDashboard -Title "PowerShell Ping Buddy" -Content {
22 |
23 | New-UDGrid -Id "PingStatusGrid" -Title "Ping Buddy" -Headers @("Server", "LastUp","Status") -Properties @("Server","LastUp","Status") -Endpoint {
24 | $Cache:MonitoredServers | Out-UDGridData
25 | }
26 |
27 | }
28 |
29 | # New 30 second schedule
30 | $30SecSchedule = New-UDEndpointSchedule -Every 30 -Second
31 |
32 | # New Endpoint to ping the server address of my "MonitoredServers Object - we'll also refresh the UD grid."
33 | $PingEndpoint = New-UDEndpoint -Schedule $30SecSchedule -Endpoint {
34 |
35 | $Cache:MonitoredServers | ForEach-Object{
36 | $pingResult = Test-NetConnection $_.Server
37 | if($pingResult.PingSucceeded -eq $true)
38 | {
39 | $_.Status = $true
40 | $_.LastUp = (Get-Date)
41 | }
42 | else {
43 | $_.Status = $false
44 | }
45 | }
46 |
47 | Sync-UDElement -Id "PingStatusGrid" -Broadcast
48 |
49 | }
50 |
51 | Get-UDDashboard -Name 'PowerShell Ping Buddy' | Stop-UDDashboard
52 | Start-UDDashboard -Dashboard $Dashboard -Port 10000 -Endpoint $PingEndpoint
53 | Start http://localhost:10000
--------------------------------------------------------------------------------
/src/Scripts/tooltip.ps1:
--------------------------------------------------------------------------------
1 | function New-UDTooltip {
2 | <#
3 | .SYNOPSIS
4 | A tooltip component.
5 |
6 | .DESCRIPTION
7 | A tooltip component. Tooltips can be placed over an other component to display a popup when the user hovers over the nested component.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER Place
13 | Where to place the tooltip.
14 |
15 | .PARAMETER Type
16 | The type of tooltip.
17 |
18 | .PARAMETER Effect
19 | An effect to apply to the tooltip.
20 |
21 | .PARAMETER TooltipContent
22 | Content to display within the tooltip.
23 |
24 | .PARAMETER Content
25 | Content that activates the tooltip when hovered.
26 |
27 | .EXAMPLE
28 | A simple tooltip.
29 |
30 | New-UDTooltip -Content {
31 | New-UDTypography -Text 'Hover me'
32 | } -TooltipContent {
33 | New-UDTypography -Text 'I'm a tooltip'
34 | }
35 | #>
36 | param(
37 | [Parameter()]
38 | [string]$Id = [Guid]::NewGuid(),
39 | [Parameter()]
40 | [ValidateSet("top", "bottom", "left", "right")]
41 | [string]$Place = "top",
42 | [Parameter()]
43 | [ValidateSet("dark", "success", "warning", "error", "info", "light")]
44 | [string]$Type = "dark",
45 | [Parameter()]
46 | [ValidateSet("float", "solid")]
47 | [string]$Effect,
48 | [Parameter(Mandatory)]
49 | [ScriptBlock]$TooltipContent,
50 | [Parameter(Mandatory)]
51 | [ScriptBlock]$Content
52 | )
53 |
54 | @{
55 | type = "ud-tooltip"
56 | tooltipType = $Type
57 | effect = $Effect
58 | place = $Place
59 | id = $Id
60 | tooltipContent = New-UDErrorBoundary -Content $TooltipContent
61 | content = New-UDErrorBoundary -Content $Content
62 | }
63 | }
--------------------------------------------------------------------------------
/src/Scripts/layout.ps1:
--------------------------------------------------------------------------------
1 | function New-UDLayout {
2 | <#
3 | .SYNOPSIS
4 | Create a layout based on the number of components within the layout.
5 |
6 | .DESCRIPTION
7 | Create a layout based on the number of components within the layout. The component wraps New-UDRow and New-UDColumn to automatically layout components in the content.
8 |
9 | .PARAMETER Columns
10 | The number of columns in this layout.
11 |
12 | .PARAMETER Content
13 | The content for this layout.
14 |
15 | .EXAMPLE
16 | New-UDLayout -Columns 2 -Content {
17 | New-UDTypography "Row 1, Col 1"
18 | New-UDTypography "Row 1, Col 2"
19 | New-UDTypography "Row 2, Col 1"
20 | New-UDTypography "Row 2, Col 2"
21 | }
22 | #>
23 | param(
24 | [Parameter(Mandatory = $true)]
25 | [ValidateRange(1, 12)]
26 | [int]$Columns,
27 | [Parameter(Mandatory = $true)]
28 | [ScriptBlock]$Content
29 | )
30 |
31 | $Components = $Content.Invoke()
32 |
33 | if ($Columns -eq 1)
34 | {
35 | $LargeColumnSize = 12
36 | $MediumColumnSize = 12
37 | $SmallColumnSize = 12
38 | }
39 | else
40 | {
41 | $LargeColumnSize = 12 / $Columns
42 | $MediumColumnSize = 12 / ($Columns / 2)
43 | $SmallColumnSize = 12
44 | }
45 |
46 | $Offset = 0
47 | $ComponentCount = ($Components | Measure-Object).Count
48 |
49 | while ($Offset -lt $ComponentCount) {
50 | $ColumnObjects = $Components | Select-Object -Skip $Offset -First $Columns | ForEach-Object {
51 | New-UDColumn -SmallSize $SmallColumnSize -MediumSize $MediumColumnSize -LargeSize $LargeColumnSize -Content {
52 | $_
53 | }
54 | }
55 |
56 | New-UDRow -Columns {
57 | $ColumnObjects
58 | }
59 |
60 | $Offset += $Columns
61 | }
62 | }
--------------------------------------------------------------------------------
/src/Components/map/heatmap-layer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import HeatmapLayer from './HeatmapLayer.js';
3 | import {isGuid} from './utils';
4 |
5 | export default class UDHeatmap extends React.Component {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | this.state = {
11 | points: props.points
12 | }
13 | }
14 |
15 | componentWillMount() {
16 | if (!isGuid(this.props.id)) {
17 | this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this));
18 | }
19 | }
20 |
21 | onIncomingEvent(eventName, event) {
22 | if (event.type === "removeElement") {
23 | this.props.onRemove(event.componentId);
24 | }
25 |
26 | if (event.type === "clearElement") {
27 | this.setState({
28 | points: []
29 | })
30 | }
31 |
32 | if (event.type === "addElement") {
33 | var points = this.state.points.concat(event.elements);
34 |
35 | this.setState({
36 | points
37 | })
38 | }
39 | }
40 |
41 | render() {
42 | return (
43 | m[1]}
48 | latitudeExtractor={m => m[0]}
49 | intensityExtractor={m => parseFloat(m[2])}
50 | //max={this.props.maxIntensity}
51 | //radius={this.props.radius}
52 | maxZoom={this.props.maxZoom}
53 | minOpacity={this.props.minOpacity}
54 | //blur={this.props.blur}
55 | gradient={this.props.gradient}
56 | onReportBounds={this.props.onReportBounds}
57 | />
58 | )
59 | }
60 | }
--------------------------------------------------------------------------------
/src/Components/map/UDLayerControlBaseLayer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { LayersControl } from 'react-leaflet';
3 | import { isGuid } from './utils';
4 |
5 | export default class UDLayerControlBaseLayer extends React.Component {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | this.state = {
11 | content: props.content
12 | }
13 | }
14 |
15 | componentWillMount() {
16 | if (!isGuid(this.props.id)) {
17 | this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this));
18 | }
19 | }
20 |
21 | onRemoveChild(id) {
22 | var content = this.state.content;
23 | if (!Array.isArray(content)) {
24 | content = [content];
25 | }
26 |
27 | content = content.filter(x => x.id !== id);
28 |
29 | this.setState({
30 | content
31 | })
32 | }
33 |
34 | onIncomingEvent(eventName, event) {
35 | if (event.type === "removeElement") {
36 | this.props.onRemove(this.props.id);
37 | }
38 |
39 | if (event.type === "addElement") {
40 | var content = this.state.content;
41 | if (!Array.isArray(content)) {
42 | content = [content];
43 | }
44 |
45 | content = content.concat(event.elements);
46 |
47 | this.setState({
48 | content
49 | })
50 | }
51 | }
52 |
53 | render() {
54 | var content = this.state.content;
55 | if (!Array.isArray(content)) {
56 | content = [content];
57 | }
58 |
59 | return
60 | {content.map(x => {
61 | x.onRemove = this.onRemoveChild.bind(this);
62 | return UniversalDashboard.renderComponent(x);
63 | })}
64 |
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Components/datepicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 | import { FormContext } from './form';
4 | import 'date-fns';
5 |
6 | import AdapterDateFns from '@mui/lab/AdapterDateFns';
7 | import LocalizationProvider from '@mui/lab/LocalizationProvider';
8 | import DatePicker from '@mui/lab/DatePicker';
9 | import TextField from "@mui/material/TextField";
10 |
11 |
12 | import frLocale from "date-fns/locale/fr";
13 | import deLocale from "date-fns/locale/de";
14 | import enLocale from "date-fns/locale/en-US";
15 | import ruLocale from "date-fns/locale/ru";
16 |
17 | const locales = {
18 | "fr": frLocale,
19 | "de": deLocale,
20 | "en": enLocale,
21 | "ru": ruLocale
22 | }
23 |
24 | const DatePickerWithContext = (props) => {
25 | return (
26 |
27 | {
28 | ({ onFieldChange }) =>
29 | }
30 |
31 | )
32 | }
33 |
34 | const DateTimePicker = (props) => {
35 |
36 | const onChange = (value) => {
37 | props.onFieldChange({ id: props.id, value });
38 | props.setState({ value });
39 | if (props.onChange) {
40 | props.onChange(value);
41 | }
42 | }
43 |
44 | useEffect(() => {
45 | props.onFieldChange({ id: props.id, value: props.value });
46 | return () => { }
47 | }, true)
48 |
49 | return (
50 |
51 | onChange(value)}
55 | renderInput={(params) => }
56 | />
57 |
58 | )
59 | }
60 |
61 | export default withComponentFeatures(DatePickerWithContext);
--------------------------------------------------------------------------------
/src/Scripts/icon-button.ps1:
--------------------------------------------------------------------------------
1 | function New-UDIconButton {
2 | <#
3 | .SYNOPSIS
4 | Creates a button with an icon.
5 |
6 | .DESCRIPTION
7 | Creates a button with an icon.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER Icon
13 | The icon to display within the button.
14 |
15 | .PARAMETER OnClick
16 | A script block to execute when the button is clicked.
17 |
18 | .PARAMETER Disabled
19 | Whether the button is disabled.
20 |
21 | .PARAMETER Href
22 | A link to navigate to when the button is clicked.
23 |
24 | .PARAMETER Style
25 | The CSS sytle to apply to the icon.
26 |
27 | .EXAMPLE
28 | Creates an icon button with a user icon with a custom style.
29 |
30 | New-UDIconButton -Icon (New-UDIcon -Icon user -Size sm -Style @{color = '#000'}) -Id 'iconButtonContent'
31 | #>
32 | param(
33 | [Parameter()]
34 | [string]$Id = ([Guid]::NewGuid()).ToString(),
35 | [Parameter ()]
36 | [PSTypeName('UniversalDashboard.Icon')]$Icon,
37 | [Parameter ()]
38 | [Endpoint] $OnClick,
39 | [Parameter ()]
40 | [Switch] $Disabled,
41 | [Parameter ()]
42 | [string] $Href,
43 | [Parameter ()]
44 | [Hashtable] $Style,
45 | [Parameter()]
46 | [string]$ClassName
47 |
48 |
49 | )
50 |
51 | End {
52 | if ($OnClick) {
53 | $OnClick.Register($Id, $PSCmdlet)
54 | }
55 |
56 | @{
57 | type = 'mu-icon-button'
58 | isPlugin = $true
59 | assetId = $MUAssetId
60 | id = $Id
61 | disabled = $Disabled.IsPresent
62 | style = $Style
63 | onClick = $OnClick
64 | icon = $Icon
65 | href = $Href
66 | className = $ClassName
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/src/Components/map/UDLayerControlOverlay.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { LayersControl } from 'react-leaflet';
4 | import { isGuid } from './utils';
5 |
6 |
7 | export class UDLayerControlOverlay extends React.Component {
8 |
9 | constructor(props) {
10 | super(props);
11 |
12 | this.state = {
13 | content: props.content
14 | }
15 | }
16 |
17 | componentWillMount() {
18 | if (!isGuid(this.props.id)) {
19 | this.pubSubToken = UniversalDashboard.subscribe(this.props.id, this.onIncomingEvent.bind(this));
20 | }
21 | }
22 |
23 | onRemoveChild(id) {
24 | var content = this.state.content;
25 | if (!Array.isArray(content)) {
26 | content = [content];
27 | }
28 |
29 | content = content.filter(x => x.id !== id);
30 |
31 | this.setState({
32 | content
33 | })
34 | }
35 |
36 | onIncomingEvent(eventName, event) {
37 | if (event.type === "removeElement") {
38 | this.props.onRemove(this.props.id);
39 | }
40 |
41 | if (event.type === "addElement") {
42 | var content = this.state.content;
43 | if (!Array.isArray(content)) {
44 | content = [content];
45 | }
46 |
47 | content = content.concat(event.elements);
48 |
49 | this.setState({
50 | content
51 | })
52 | }
53 | }
54 |
55 | render() {
56 | var content = this.state.content;
57 | if (!Array.isArray(content)) {
58 | content = [content];
59 | }
60 |
61 | return
62 | {content.map(x => {
63 | x.onRemove = this.onRemoveChild.bind(this);
64 | x.onReportBounds = this.props.onReportBounds;
65 | return UniversalDashboard.renderComponent(x);
66 | })}
67 |
68 | }
69 | }
--------------------------------------------------------------------------------
/src/app/polyfills.js:
--------------------------------------------------------------------------------
1 | if (typeof Object.assign != 'function') {
2 | Object.assign = function(target) {
3 | 'use strict';
4 | if (target == null) {
5 | throw new TypeError('Cannot convert undefined or null to object');
6 | }
7 |
8 | target = Object(target);
9 | for (var index = 1; index < arguments.length; index++) {
10 | var source = arguments[index];
11 | if (source != null) {
12 | for (var key in source) {
13 | if (Object.prototype.hasOwnProperty.call(source, key)) {
14 | target[key] = source[key];
15 | }
16 | }
17 | }
18 | }
19 | return target;
20 | };
21 | }
22 |
23 | if (!String.prototype.startsWith) {
24 | String.prototype.startsWith = function(searchString, position){
25 | return this.substr(position || 0, searchString.length) === searchString;
26 | };
27 | }
28 |
29 | if (!Array.prototype.fill) {
30 | Object.defineProperty(Array.prototype, 'fill', {
31 | value: function(value) {
32 |
33 | // Steps 1-2.
34 | if (this == null) {
35 | throw new TypeError('this is null or not defined');
36 | }
37 |
38 | var O = Object(this);
39 |
40 | // Steps 3-5.
41 | var len = O.length >>> 0;
42 |
43 | // Steps 6-7.
44 | var start = arguments[1];
45 | var relativeStart = start >> 0;
46 |
47 | // Step 8.
48 | var k = relativeStart < 0 ?
49 | Math.max(len + relativeStart, 0) :
50 | Math.min(relativeStart, len);
51 |
52 | // Steps 9-10.
53 | var end = arguments[2];
54 | var relativeEnd = end === undefined ?
55 | len : end >> 0;
56 |
57 | // Step 11.
58 | var final = relativeEnd < 0 ?
59 | Math.max(len + relativeEnd, 0) :
60 | Math.min(relativeEnd, len);
61 |
62 | // Step 12.
63 | while (k < final) {
64 | O[k] = value;
65 | k++;
66 | }
67 |
68 | // Step 13.
69 | return O;
70 | }
71 | });
72 | }
--------------------------------------------------------------------------------
/src/Scripts/interactive/Show-Modal.ps1:
--------------------------------------------------------------------------------
1 | function Show-UDModal {
2 | <#
3 | .SYNOPSIS
4 | Show a modal.
5 |
6 | .DESCRIPTION
7 | Show a modal.
8 |
9 | .PARAMETER FullScreen
10 | Create a full screen modal.
11 |
12 | .PARAMETER Footer
13 | The footer components for the modal.
14 |
15 | .PARAMETER Header
16 | The header components for the modal.
17 |
18 | .PARAMETER Content
19 | The content of the modal.
20 |
21 | .PARAMETER Persistent
22 | Whether the modal can be closed by clicking outside of it.
23 |
24 | .PARAMETER FullWidth
25 | Whether the modal is full width.
26 |
27 | .PARAMETER MaxWidth
28 | The max width of the modal.
29 |
30 | .EXAMPLE
31 | New-UDButton -Text 'Click Me' -OnClick {
32 | Show-UDModal -Content {
33 | New-UDTypography -Text "Hello World"
34 | }
35 | }
36 | #>
37 | param(
38 | [Parameter()]
39 | [Switch]$FullScreen,
40 | [Parameter()]
41 | [ScriptBlock]$Footer = {},
42 | [Parameter()]
43 | [ScriptBlock]$Header = {},
44 | [Parameter()]
45 | [ScriptBlock]$Content = {},
46 | [Parameter()]
47 | [Switch]$Persistent,
48 | [Parameter()]
49 | [Switch]$FullWidth,
50 | [Parameter()]
51 | [ValidateSet("xs", "sm", "md", "lg", "xl")]
52 | [string]$MaxWidth
53 | )
54 |
55 | $Modal = @{
56 | dismissible = -not $Persistent.IsPresent
57 | maxWidth = $MaxWidth
58 | fullWidth = $FullWidth.IsPresent
59 | fullScreen = $FullScreen.IsPresent
60 | }
61 |
62 | if ($null -ne $Footer) {
63 | $Modal['footer'] = & $Footer
64 | }
65 |
66 | if ($null -ne $Header) {
67 | $Modal['header'] = & $Header
68 | }
69 |
70 | if ($null -ne $Content) {
71 | $Modal['content'] = & $Content
72 | }
73 |
74 | $DashboardHub.SendWebSocketMessage($ConnectionId, "showModal", $modal)
75 | }
76 |
--------------------------------------------------------------------------------
/src/Components/checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import Checkbox from "@mui/material/Checkbox";
3 | import CheckBoxIcon from "@mui/icons-material/CheckBox"
4 | import CheckBoxIconBlank from "@mui/icons-material/CheckBoxOutlineBlank"
5 | import FormControlLabel from "@mui/material/FormControlLabel"
6 | import classNames from "classnames";
7 | import { FormContext } from './form';
8 | import { withComponentFeatures } from 'universal-dashboard';
9 |
10 | const UDCheckboxWithContext = (props) => {
11 | return (
12 |
13 | {
14 | ({ onFieldChange }) =>
15 | }
16 |
17 | );
18 | }
19 |
20 | const UDCheckbox = (props) => {
21 | const onChange = (event) => {
22 | props.onFieldChange({ id: props.id, value: event.target.checked });
23 |
24 | props.setState({ checked: event.target.checked })
25 |
26 | if (props.onChange) {
27 | props.onChange(event.target.checked)
28 | }
29 | }
30 |
31 | useEffect(() => {
32 | props.onFieldChange({ id: props.id, value: props.checked });
33 | return () => { }
34 | }, true)
35 |
36 | return (
37 | onChange(event)}
45 | value={props.checked}
46 | style={!props.disabled ? { ...props.style } : { color: null }}
47 | color={props.color}
48 | icon={props.icon ? props.render(props.icon) : }
49 | checkedIcon={props.checkedIcon ? props.render(props.checkedIcon) : }
50 | />
51 | }
52 | label={!props.label ? '' : props.label}
53 | labelPlacement={props.labelPlacement}
54 | />
55 | )
56 | }
57 |
58 | export default withComponentFeatures(UDCheckboxWithContext);
--------------------------------------------------------------------------------
/src/Components/timepicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 | import { FormContext } from './form';
4 | import TextField from "@mui/material/TextField";
5 | import TimePicker from '@mui/lab/TimePicker';
6 | import AdapterDateFns from '@mui/lab/AdapterDateFns';
7 | import LocalizationProvider from '@mui/lab/LocalizationProvider';
8 |
9 | import 'date-fns';
10 | import frLocale from "date-fns/locale/fr";
11 | import deLocale from "date-fns/locale/de";
12 | import enLocale from "date-fns/locale/en-US";
13 | import ruLocale from "date-fns/locale/ru";
14 |
15 | const locales = {
16 | "fr": frLocale,
17 | "de": deLocale,
18 | "en": enLocale,
19 | "ru": ruLocale
20 | }
21 |
22 | const TimePickerWithContext = props => {
23 | return (
24 |
25 | {
26 | ({ onFieldChange }) =>
27 | }
28 |
29 | )
30 | }
31 |
32 | const UdTimePicker = (props) => {
33 |
34 | const onChange = (value) => {
35 | props.onFieldChange({ id: props.id, value });
36 | props.setState({ value });
37 | if (props.onChange) {
38 | props.onChange(value);
39 | }
40 | }
41 |
42 | var value = props.value;
43 | if (value === "") {
44 | value = null;
45 | }
46 |
47 | useEffect(() => {
48 | props.onFieldChange({ id: props.id, value: props.value });
49 | return () => { }
50 | }, true)
51 |
52 | return (
53 |
54 | onChange(value)}
58 | renderInput={(params) => }
59 | />
60 |
61 | )
62 | }
63 |
64 | export default withComponentFeatures(TimePickerWithContext);
--------------------------------------------------------------------------------
/src/classes/Cmdlets/NewNivoChartAxisOptionsCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Management.Automation;
3 |
4 | namespace UniversalDashboard.Enterprise
5 | {
6 | [Cmdlet(VerbsCommon.New, "UDNivoChartAxisOptions")]
7 | public class NewNivoChartAxisOptionsCommand : PSCmdlet
8 | {
9 | private readonly Dictionary _values = new Dictionary();
10 |
11 | [Parameter]
12 | [ValidateSet("top", "right", "bottom", "left")]
13 | public string Position
14 | {
15 | set
16 | {
17 | _values.Add("orient", value);
18 | }
19 | }
20 |
21 |
22 | [Parameter]
23 | public int TickSize
24 | {
25 | set
26 | {
27 | _values.Add("tickSize", value);
28 | }
29 | }
30 |
31 | [Parameter]
32 | public int TickPadding
33 | {
34 | set
35 | {
36 | _values.Add("tickPadding", value);
37 | }
38 | }
39 |
40 | [Parameter]
41 | public int TickRotation
42 | {
43 | set
44 | {
45 | _values.Add("tickRotation", value);
46 | }
47 | }
48 |
49 | [Parameter]
50 | public string Legend
51 | {
52 | set
53 | {
54 | _values.Add("legend", value);
55 | }
56 | }
57 |
58 | [Parameter]
59 | [ValidateSet("center", "start", "end")]
60 | public string LegendPosition
61 | {
62 | set
63 | {
64 | _values.Add("legendPosition", value);
65 | }
66 | }
67 |
68 | [Parameter]
69 | public int LegendOffset
70 | {
71 | set
72 | {
73 | _values.Add("legendOffset", value);
74 | }
75 | }
76 |
77 | protected override void EndProcessing() {
78 | WriteObject(_values);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Components/code-editor.jsx:
--------------------------------------------------------------------------------
1 | import { KeyCode } from 'monaco-editor';
2 | import React from 'react';
3 | import MonacoEditor, { MonacoDiffEditor } from 'react-monaco-editor';
4 | import ReactResizeDetector from 'react-resize-detector';
5 | import { withComponentFeatures } from 'universal-dashboard'
6 | import { saveAs } from 'file-saver';
7 |
8 | var editor;
9 |
10 | const UDMonacoEditor = props => {
11 | const onChange = (newValue) => {
12 | props.setState({ code: newValue })
13 | }
14 |
15 | const editorDidMount = (editor, monaco) => {
16 | editor = editor;
17 | if (props.canSave) {
18 | editor.onKeyDown = (e) => {
19 | console.log(e);
20 | if (e.ctrlKey && e.keyCode === KeyCode.KEY_S) {
21 | e.preventDefault();
22 | saveAs(new Blob([props.code], { type: 'text/plain' }), `${props.id}.${props.extension}`);
23 | }
24 | }
25 | }
26 | }
27 |
28 | const onResize = () => {
29 | if (editor) {
30 | editor.layout();
31 | }
32 | }
33 |
34 | var control = props.original ?
35 |
36 | :
47 |
48 | ;
58 |
59 | if (props.autosize) {
60 | return (
61 |
62 | <>
63 | {control}
64 | >
65 |
66 | )
67 | }
68 |
69 | return (
70 | <>
71 | {control}
72 | >
73 | );
74 | }
75 |
76 | export default withComponentFeatures(UDMonacoEditor)
--------------------------------------------------------------------------------
/src/Scripts/paper.ps1:
--------------------------------------------------------------------------------
1 | function New-UDPaper {
2 | <#
3 | .SYNOPSIS
4 | Creates a paper.
5 |
6 | .DESCRIPTION
7 | Creates a paper. Paper is used to highlight content within a page.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER Children
13 | The content of this paper element.
14 |
15 | .PARAMETER Width
16 | The width of this paper.
17 |
18 | .PARAMETER Height
19 | The height of this paper.
20 |
21 | .PARAMETER Square
22 | Whether this paper is square.
23 |
24 | .PARAMETER Style
25 | The CSS style to apply to this paper.
26 |
27 | .PARAMETER Elevation
28 | The elevation of this paper.
29 |
30 | .EXAMPLE
31 | Creates paper with a heading, custom style and an elevation of 4.
32 |
33 | New-UDPaper -Children {
34 | New-UDHeading -Text "hi" -Id 'hi'
35 | } -Style @{
36 | backgroundColor = '#90caf9'
37 | } -Id 'paperElevation' -Elevation 4
38 |
39 | .NOTES
40 | General notes
41 | #>
42 | param(
43 | [Parameter()][string]$Id = ([Guid]::NewGuid()).ToString(),
44 | [Parameter()][Alias("Content")][ScriptBlock]$Children,
45 | [Parameter()][string]$Width = '500',
46 | [Parameter()][string]$Height = '500',
47 | [Parameter()][Switch]$Square,
48 | [Parameter()][Hashtable]$Style,
49 | [Parameter()][int]$Elevation,
50 | [Parameter()]
51 | [string]$ClassName
52 | )
53 |
54 | End {
55 | $c = New-UDErrorBoundary -Content $Children
56 |
57 | @{
58 | type = 'mu-paper'
59 | isPlugin = $true
60 | assetId = $MUAssetId
61 |
62 | id = $Id
63 | width = $Width
64 | children = $c
65 | height = $Height
66 | square = $Square.IsPresent
67 | style = $Style
68 | elevation = $Elevation
69 | className = $ClassName
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/examples/input-dynamic-pages.ps1:
--------------------------------------------------------------------------------
1 | $Page = New-UDPage -Url "/script/:id" -Endpoint {
2 | param($id)
3 |
4 | $PS1File = Invoke-WebRequest -Uri https://raw.githubusercontent.com/PoshCode/poshcode.github.io/data/scripts/$id.ps1
5 |
6 | New-UDCard -Title "Script $Id" -Text $PS1File.Content -Language powershell -Links @(
7 | New-UDLink -Text "Visit on GitHub" -Url "https://github.com/PoshCode/poshcode.github.io/blob/data/scripts/$Id.ps1"
8 | )
9 | }
10 |
11 | $entry = New-UDPage -Url "/entry/:id" -Endpoint {
12 | param($id)
13 |
14 | $PS1File = Invoke-WebRequest -Uri https://raw.githubusercontent.com/PoshCode/poshcode.github.io/data/scripts/$id.md
15 |
16 | New-UDCard -Title "Script $Id" -Text $PS1File.Content -Language powershell -Links @(
17 | New-UDLink -Text "Visit on GitHub" -Url "https://github.com/PoshCode/poshcode.github.io/blob/data/scripts/$Id.md"
18 | New-UDLink -Text "View Raw Script" -Url "/script/$Id"
19 | )
20 | }
21 |
22 | $HomePage = New-UDPage -Name "Home" -Content {
23 | New-UDCard -Title "About PoshCode.org Archive" -Text "This dashboard allows you to enter a PoshCode.org document ID and view the contents of it. Enter a value between 1000 and 4198." -Links @(
24 | New-UDLink -Text "Visit on GitHub" -Url "https://github.com/PoshCode/poshcode.github.io/"
25 |
26 | )
27 | New-UDInput -Title "PoshCode.org Archive" -Endpoint {
28 | param($Id, [bool]$JustShowScript)
29 |
30 | if ($Id -lt 1000 -or $Id -gt 4198) {
31 | New-UDInputAction -Toast "$Id is not a valid PoshCode.org Archive Id!"
32 | } else {
33 | if ($JustShowScript) {
34 | New-UDInputAction -RedirectUrl "/script/$Id"
35 | }
36 | else {
37 | New-UDInputAction -RedirectUrl "/entry/$Id"
38 | }
39 | }
40 | }
41 | }
42 |
43 | $Dashboard = New-UDDashboard -Title "PoshCode.org Archive" -Pages @(
44 | $HomePage,
45 | $entry
46 | $Page
47 | )
48 |
49 | Start-UDDashboard -Dashboard $Dashboard -Port "10000"
50 | Start-Process http://localhost:10000
51 |
--------------------------------------------------------------------------------
/src/app/basics/lazy-element.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {getApiPath, getDashboardId } from './../config.jsx'
3 | import renderComponent from './../services/render-service.jsx';
4 |
5 | export default class LazyElement extends React.Component {
6 | constructor() {
7 | super();
8 |
9 | this.state = {
10 | loading: true,
11 | error: ""
12 | }
13 | }
14 | componentWillMount() {
15 |
16 | if (this.props.component.type === 'error')
17 | {
18 | return;
19 | }
20 |
21 | const dashboardId = getDashboardId();
22 |
23 | var script = document.createElement('script');
24 | script.onload = function() {
25 | this.setState({loading:false});
26 | }.bind(this)
27 | script.src = getApiPath() + `/api/internal/javascript/${this.props.component.assetId}?dashboardId=${dashboardId}`;
28 | document.head.appendChild(script);
29 | }
30 |
31 | componentDidCatch(e) {
32 | this.setState({
33 | error: e
34 | })
35 | }
36 |
37 | render() {
38 | if (this.state.loading) {
39 | return ;
40 | }
41 |
42 | if (this.props.component.type === 'error')
43 | {
44 | return {this.props.component.message}
45 | }
46 |
47 | if (this.state.error !== "") {
48 | return renderComponent({
49 | type: 'error',
50 | errorRecords: [
51 | {
52 | message: `There was an error rendering component of type ${this.props.component.type}. ${this.state.error}`
53 | }
54 | ]
55 | });
56 | }
57 |
58 | var element = renderComponent(this.props.component, this.props.history, true);
59 |
60 | if (element == null) {
61 | return renderComponent({
62 | type: 'error',
63 | message: `Component not registered: ${this.props.component.type}`
64 | });
65 | }
66 |
67 | return element;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Components/table/components/baseTableHead.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TableCell, TableHead, TableRow, TableSortLabel } from "@mui/material";
3 | import { setCellPadding } from "../v2/utilities";
4 |
5 | export default function BaseTableHead({ headerGroups, columns, expandable }) {
6 |
7 |
8 | function setTableSortLabel(column) {
9 | return column.id !== "selection" && column.getSortByToggleProps ? (
10 |
14 | ) : null;
15 | }
16 |
17 | return (
18 |
19 | {headerGroups.map((headerGroup) => (
20 |
21 |
22 | {expandable && }
23 | {headerGroup.headers.filter((column, index) => !column.hidden).map((column, index) => (
24 |
34 | {column.render("Header")}
35 | {setTableSortLabel(column)}
36 |
37 | ))}
38 |
39 | column.showFilter) && "none",
42 | }}
43 | >
44 | {expandable && }
45 | {headerGroup.headers.filter((column, index) => !column.hidden).map((column, index) => (
46 |
47 | {column.showFilter ? column.render("Filter") : null}
48 |
49 | ))}
50 |
51 |
52 | ))}
53 |
54 | );
55 | }
--------------------------------------------------------------------------------
/src/Scripts/timeline.ps1:
--------------------------------------------------------------------------------
1 | function New-UDTimeline {
2 | param(
3 | [Parameter()]
4 | [string]$Id = ([Guid]::NewGuid()).ToString(),
5 | [Parameter()]
6 | [Alias("Content")]
7 | [scriptblock]$Children,
8 | [Parameter()]
9 | [ValidateSet("right", 'left', 'alternate')]
10 | [string]$Position = "right"
11 | )
12 |
13 | @{
14 | isPlugin = $true
15 | assetId = $MUAssetId
16 | type = "mu-timeline"
17 | id = $id
18 |
19 | children = & $Children
20 | position = $Position.ToLower()
21 | }
22 | }
23 |
24 | function New-UDTimelineItem {
25 | param(
26 | [Parameter()]
27 | [ScriptBlock]$Content = {},
28 | [Parameter()]
29 | [ScriptBlock]$OppositeContent = {},
30 | [Parameter()]
31 | [Hashtable]$Icon,
32 | [Parameter()]
33 | [ValidateSet("error", 'grey', 'info', 'inherit', 'primary', 'secondary', 'success', 'warning')]
34 | [string]$Color = 'grey',
35 | [Parameter()]
36 | [ValidateSet('filled', 'outlined')]
37 | [string]$Variant = 'filled'
38 | )
39 |
40 | @{
41 | content = & $Content
42 | oppositeContent = & $OppositeContent
43 | icon = $icon
44 | color = $Color.ToLower()
45 | variant = $Variant.ToLower()
46 | }
47 | }
48 |
49 | <#
50 |
51 | New-UDTimeline -Children {
52 | New-UDTimelineItem -Content {
53 | 'Breakfast'
54 | } -OppositeContent {
55 | '7:45 AM'
56 | }
57 | New-UDTimelineItem -Content {
58 | 'Welcome Message'
59 | } -OppositeContent {
60 | '9:00 AM'
61 | }
62 | New-UDTimelineItem -Content {
63 | 'State of the Shell'
64 | } -OppositeContent {
65 | '9:30 AM'
66 | }
67 | New-UDTimelineItem -Content {
68 | 'General Session'
69 | } -OppositeContent {
70 | '11:00 AM'
71 | }
72 | }
73 |
74 | #>
--------------------------------------------------------------------------------
/src/Components/menu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withComponentFeatures } from 'universal-dashboard';
3 | import Button from '@mui/material/Button';
4 | import Menu from '@mui/material/Menu';
5 | import MenuItem from '@mui/material/MenuItem';
6 | import Typography from '@mui/material/Typography';
7 | import UDIcon from './icon';
8 |
9 | function UDMenu(props) {
10 | const [anchorEl, setAnchorEl] = React.useState(null);
11 |
12 | const onChange = (value) => {
13 | setAnchorEl(null);
14 | props.setState({ value });
15 |
16 | if (props.onChange) {
17 | props.onChange(value)
18 | }
19 | }
20 |
21 | const handleClick = (event) => {
22 | setAnchorEl(event.currentTarget);
23 | };
24 |
25 | const handleClose = () => {
26 | setAnchorEl(null);
27 | };
28 |
29 | let children = props.children;
30 | if (!children) { children = [] }
31 | if (!Array.isArray(children)) { children = [children] }
32 |
33 | let icon;
34 | if (props.icon) {
35 | icon =
36 | }
37 |
38 | return (
39 |
40 |
44 |
53 |
54 | );
55 | }
56 |
57 | function UDMenuItem(props) {
58 | let icon;
59 | if (props.icon) {
60 | icon =
61 | }
62 |
63 | return (
64 |
68 | )
69 | }
70 |
71 | export default withComponentFeatures(UDMenu);
72 |
73 |
--------------------------------------------------------------------------------
/src/Scripts/datetime.ps1:
--------------------------------------------------------------------------------
1 | function New-UDDateTime {
2 | <#
3 | .SYNOPSIS
4 | This date and time component is used for formatting dates and times using the user's browser settings.
5 |
6 | .DESCRIPTION
7 | This date and time component is used for formatting dates and times using the user's browser settings. Since Universal Dashboard PowerShell scripts run within the server, the date and time settings of the user's system are not taken into account. This component formats date and time within the client's browser to take into account their locale and time zone.
8 |
9 | .PARAMETER Id
10 | The ID of this component.
11 |
12 | .PARAMETER InputObject
13 | The date and time object to format.
14 |
15 | .PARAMETER Format
16 | The format of the date and time. This component uses Day.JS. You can learn more about formatting options on their documentation: https://day.js.org/docs/en/display/format
17 |
18 | .PARAMETER LocalizedFormat
19 | The localized format for the date and time. Use this format if you would like to take the user's browser locale and time zone settings into account.
20 |
21 | .EXAMPLE
22 | Formats a date and time using the format 'DD/MM/YYYY'
23 |
24 | New-UDDateTime -InputObject (Get-Date) -Format 'DD/MM/YYYY'
25 | #>
26 | [CmdletBinding(DefaultParameterSetName = "LocalizedFormat")]
27 | param(
28 | [Parameter()]
29 | [string]$Id = [Guid]::NewGuid(),
30 | [Parameter(Mandatory, Position = 0)]
31 | [string]$InputObject,
32 | [Parameter(ParameterSetName = "Format")]
33 | [string]$Format = "DD/MM/YYYY",
34 | [Parameter(ParameterSetName = "LocalizedFormat")]
35 | [ValidateSet("LT", "LTS", "L", "LL", "LLL", "LLLL", "l", "ll", "lll", "llll")]
36 | [string]$LocalizedFormat = "LLL"
37 | )
38 |
39 | $f = $Format
40 | if ($PSCmdlet.ParameterSetName -eq 'LocalizedFormat')
41 | {
42 | $f = $LocalizedFormat
43 | }
44 |
45 | @{
46 | type = 'mu-datetime'
47 | id = $Id
48 | isPlugin = $true
49 | assetId = $MUAssetId
50 |
51 | inputObject = $InputObject
52 | format = $f
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Components/framework/ud-modal.jsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 |
3 | import Dialog from '@mui/material/Dialog';
4 | import DialogTitle from '@mui/material/DialogTitle';
5 | import DialogContent from '@mui/material/DialogContent';
6 | import DialogActions from '@mui/material/DialogActions';
7 |
8 | export default function Modal(props) {
9 |
10 | const [openToken, setOpenToken] = useState();
11 | const [closeToken, setCloseToken] = useState();
12 | const [open, setOpen] = useState(false);
13 | const [modalOptions, setModalOptions] = useState({});
14 |
15 | useEffect(() => {
16 | setOpenToken(UniversalDashboard.subscribe('modal.open', (x, props) => {
17 | setModalOptions(props);
18 | setOpen(true);
19 | }));
20 |
21 | setCloseToken(UniversalDashboard.subscribe('modal.close', () => {
22 | setOpen(false);
23 | setModalOptions({});
24 | }));
25 |
26 | return () => {
27 | UniversalDashboard.unsubscribe(openToken);
28 | UniversalDashboard.unsubscribe(closeToken);
29 | }
30 | }, [true]);
31 |
32 | var header = null;
33 | if (modalOptions.header != null) {
34 | header = setOpen(false)}>
35 | {UniversalDashboard.renderComponent(modalOptions.header)}
36 |
37 | }
38 |
39 | var content = null;
40 | if (modalOptions.content != null) {
41 | content =
42 | {UniversalDashboard.renderComponent(modalOptions.content)}
43 |
44 | }
45 |
46 | var footer = null;
47 | if (modalOptions.footer != null) {
48 | footer = {UniversalDashboard.renderComponent(modalOptions.footer)}
49 | }
50 |
51 | return (
52 |
57 | )
58 | }
--------------------------------------------------------------------------------
/examples/tree-view/filesystem.ps1:
--------------------------------------------------------------------------------
1 | $Dashboard = New-UdDashboard -Title "Tree View" -Content {
2 | New-UDRow -Columns {
3 | New-UDColumn -Size 3 -Content {
4 | $Root = New-UDTreeNode -Name 'FileSystem' -Id 'FileSystem'
5 | New-UDTreeView -ActiveBackgroundColor '#DFE8E4' -Node $Root -OnNodeClicked {
6 | param($Body)
7 |
8 | $Depth = [int]$Obj.NodeId
9 |
10 | if ($Obj.NodeId -eq 'FileSystem') {
11 | Get-PSDrive -PSProvider FileSystem | ForEach-Object {
12 | New-UDTreeNode -Name $_.Root -Id $_.Root -Icon hdd_o
13 | }
14 | } else {
15 |
16 | Get-ChildItem -Path $Obj.NodeId | ForEach-Object {
17 | if ($Obj.NodeId -eq $_.FullName) {
18 | return;
19 | }
20 |
21 | if ($_.PSIsContainer) {
22 | New-UDTreeNode -Name $_.Name -Id $_.FullName -Icon folder
23 | } else {
24 | New-UDTreeNode -Name $_.Name -Id $_.FullName -Icon file_text
25 | }
26 |
27 | } | ConvertTo-JsonEx
28 |
29 | Set-UDElement -Id "properties" -Content {
30 | New-UDGrid -Title $Obj.NodeId -Headers @("Name", "Value") -Properties @("Name", "Value") -Endpoint {
31 | $Obj = $ArgumentList[0]
32 | (Get-ItemProperty -Path $Obj.NodeId).PSObject.Properties | ForEach-Object {
33 | [PSCustomObject]@{
34 | Name = $_.Name
35 | Value = if ( $_.Value -eq $null) { "" } else { $_.Value.ToString() }
36 | }
37 | } | Out-UDGridData
38 | } -ArgumentList $Obj
39 | }
40 | }
41 | }
42 | }
43 | New-UDColumn -Size 9 -Content {
44 | New-UDElement -Tag "div" -Id "properties" -Content {}
45 | }
46 | }
47 |
48 |
49 | }
50 |
51 | $Server = Start-UDDashboard -Port 10001 -Dashboard $dashboard
--------------------------------------------------------------------------------
/docs/working-on-a-component.md:
--------------------------------------------------------------------------------
1 | # Working on a Component
2 |
3 | This document is intended to help you add a feature or fix a bug in a component.
4 |
5 | ## Step 1: Build Universal Dashboard
6 |
7 | Run the build script in VS Code using the 'Build Debug' task or by running the build script manually.
8 |
9 | ```
10 | .\src\build.ps1 -Configuration Debug -NoHelp
11 | ```
12 |
13 | You won't have to do this every time you want to edit a component.
14 |
15 | ## Step 2: Start the Materialize Webpack Dev Server
16 |
17 | You can start the Materialize Webpack Dev Server by running the 'Start Materialize Webpack Dev Server' task in VS Code or by running npm directly.
18 |
19 | ```
20 | cd .\src\UniversalDashboard.Materialize
21 | npm run dev
22 | ```
23 |
24 | ## Step 3: Modify the component
25 |
26 | The component code is in the `Components` folder and the PowerShell script is in the `Scripts` folder for the components.
27 |
28 | ```
29 | cd .\src\UniversalDashboard.Materialize\Components
30 | cd .\src\UniversalDashboard.Materialize\Scripts
31 | ```
32 |
33 | ## Step 4: Validate the Component
34 |
35 | You should write a test case that reproduces the bug or uses the new feature in the `.\src\UniversalDashboard.Materialize\Tests` folder. Once you have the test stubbed out, you can then run the test.
36 |
37 | An example test stub would look like this.
38 |
39 | ```
40 | Context "Floating" {
41 | Set-TestDashboard {
42 | New-UDButton -Text "Click Me" -Id "button" -Floating
43 | }
44 |
45 | It "is floating" {
46 |
47 | }
48 | }
49 | ```
50 |
51 | When you run the test, the browser will open and the button will quickly be shown. If you wish to leave the browser open so that you can work on your test or component, use `Wait-Debugger` to pause the test.
52 |
53 | ```
54 | Context "Floating" {
55 | Set-TestDashboard {
56 | New-UDButton -Text "Click Me" -Id "button" -Floating
57 | }
58 |
59 | It "is floating" {
60 | Wait-Debugger
61 | }
62 | }
63 | ```
64 |
65 | You can now edit the JSX file for the component and the webpack dev server will automatically recompile the component.
66 |
67 | If you edit the PowerShell script, you will have to restart your test script.
68 |
--------------------------------------------------------------------------------
/src/Scripts/progress.ps1:
--------------------------------------------------------------------------------
1 | function New-UDProgress {
2 | <#
3 | .SYNOPSIS
4 | Creates a progress dialog.
5 |
6 | .DESCRIPTION
7 | Creates a progress dialog. Progress dialogs can show both determinate and indeterminate progress. They can also be circular or linear.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER PercentComplete
13 | The percent complete for the progress.
14 |
15 | .PARAMETER BackgroundColor
16 | The background color.
17 |
18 | .PARAMETER ProgressColor
19 | The progress bar color.
20 |
21 | .PARAMETER Circular
22 | Whether the progress is circular.
23 |
24 | .PARAMETER Size
25 | The size of the progress.
26 |
27 | .EXAMPLE
28 | Creates a progress bar at 75%.
29 |
30 | New-UDProgress -PercentComplete 75
31 | #>
32 | [CmdletBinding(DefaultParameterSetName = "indeterminate")]
33 | param(
34 | [Parameter()]
35 | [string]$Id = [Guid]::NewGuid().ToString(),
36 | [Parameter(ParameterSetName = "determinate")]
37 | [ValidateRange(0, 100)]
38 | $PercentComplete,
39 | [Parameter(ParameterSetName = "indeterminate")]
40 | [Parameter(ParameterSetName = "determinate")]
41 | [UniversalDashboard.Models.DashboardColor]$BackgroundColor,
42 | [Parameter()]
43 | [Alias("Color")]
44 | [UniversalDashboard.Models.DashboardColor]$ProgressColor,
45 | [Parameter(ParameterSetName = 'circular')]
46 | [Switch]$Circular,
47 | [Parameter(ParameterSetName = 'circular')]
48 | [ValidateSet('small', 'medium', 'large')]
49 | [string]$Size
50 | )
51 |
52 | End {
53 | @{
54 | id = $Id
55 | assetId = $MUAssetId
56 | isPlugin = $true
57 | type = "mu-progress"
58 |
59 | variant = $PSCmdlet.ParameterSetName
60 | percentComplete = $PercentComplete
61 | backgroundColor = $BackgroundColor.HtmlColor
62 | progressColor = $ProgressColor.HtmlColor
63 | circular = $Circular.IsPresent
64 | color = $Color
65 | size = $Size
66 | }
67 | }
68 |
69 |
70 | }
--------------------------------------------------------------------------------
/src/Scripts/drawer.ps1:
--------------------------------------------------------------------------------
1 | function New-UDDrawer {
2 | <#
3 | .SYNOPSIS
4 | Creates a new drawer.
5 |
6 | .DESCRIPTION
7 | Creates a new drawer. A drawer is a navigational component that is typically used for navigating between pages. It can be used with New-UDAppBar to provide a custom nav bar.
8 |
9 | .PARAMETER Id
10 | The ID of the component. It defaults to a random GUID.
11 |
12 | .PARAMETER Children
13 | Navgiation controls to show within the drawer. Use New-UDList and New-UDListItem to generate links within the drawer.
14 |
15 | .PARAMETER Variant
16 | The type of drawer. Valid values include "persistent", "permanent", "temporary"
17 |
18 | .PARAMETER Anchor
19 | Where to anchor the drawer. Valid values incldue "left", "right", "top", "bottom"
20 |
21 | .EXAMPLE
22 | Creates a custom navbar using New-UDDrawer
23 |
24 | $Drawer = New-UDDrawer -Id 'drawer' -Children {
25 | New-UDList -Content {
26 | New-UDListItem -Id 'lstHome' -Label 'Home' -OnClick {
27 | Set-TestData 'Home'
28 | } -Content {
29 | New-UDListItem -Id 'lstNested' -Label 'Nested' -OnClick {
30 | Set-TestData 'Nested'
31 | }
32 | }
33 | }
34 | }
35 |
36 | New-UDElement -Tag 'main' -Content {
37 | New-UDAppBar -Children { New-UDTypography -Text 'Hello' -Paragraph } -Position relative -Drawer $Drawer
38 | }
39 | #>
40 | param(
41 | [Parameter()]
42 | [string]$Id = [Guid]::NewGuid(),
43 | [Parameter()]
44 | [Alias("Content")]
45 | [ScriptBlock]$Children,
46 | [ValidateSet("persistent", "permanent", "temporary")]
47 | [string]$Variant = "temporary",
48 | [ValidateSet("left", "right", "top", "bottom")]
49 | [string]$Anchor = "left",
50 | [Parameter()]
51 | [string]$ClassName
52 | )
53 |
54 | try {
55 | $c = & $Children
56 | }
57 | catch {
58 | $c = New-UDError -Message $_
59 | }
60 |
61 | @{
62 | type = 'mu-drawer'
63 | id = $Id
64 | isPlugin = $true
65 | assetId = $MUAssetId
66 | children = $c
67 | variant = $Variant.ToLower()
68 | anchor = $Anchor.ToLower()
69 | className = $ClassName
70 | }
71 | }
--------------------------------------------------------------------------------