Component demo · Operator screens and process graphics
Pipe
index.htm as it is written with the smart-industrial package
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>Pipe Overview Demo</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="node_modules/smart-industrial/source/styles/smart.default.css" />
<link rel="stylesheet" type="text/css" href="node_modules/smart-industrial/source/styles/smart.industrial-elements.css" />
<link rel="stylesheet" type="text/css" href="node_modules/smart-industrial/source/styles/smart.industrial.css" />
<link rel="stylesheet" type="text/css" href="node_modules/smart-industrial/source/styles/smart.industrial-dark.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body class="viewport" theme="industrial">
<div class="smart-demo-container">
<section id="overview">
<h2>Smart.Pipe displays a section of process line between two symbols on a mimic diagram.</h2>
<p>
A pipe is a component rather than a drawn line because it carries state: whether the line is flowing, in which direction, and whether it is in service. A flowing pipe animates a dashed line in the direction of flow. When the operating system asks for reduced motion, the line stands still and chevrons along it point the way the flow goes.
</p>
</section>
<section>
<h2>Idle and flowing pipes</h2>
<div class="grid two">
<div class="cell">
<smart-pipe class="h" id="p1"></smart-pipe>
<span>idle</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p2" flowing></smart-pipe>
<span>flowing</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p3" flowing reverse></smart-pipe>
<span>flowing, reversed</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p4" flowing thickness="7"></smart-pipe>
<span>thickness 7</span>
</div>
</div>
</section>
<section>
<h2>Vertical runs</h2>
<div class="grid vertical">
<div class="cell"><smart-pipe class="v" orientation="vertical"></smart-pipe><span>idle</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" flowing></smart-pipe><span>flowing</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" flowing reverse></smart-pipe><span>reversed</span></div>
</div>
</section>
<section>
<h2>Elbows</h2>
<p>Each elbow enters from one edge and leaves through the centre, so two elbows form a corner.</p>
<div class="grid four">
<div class="cell"><smart-pipe class="b" elbow="topLeft" flowing></smart-pipe><span>topLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="topRight" flowing></smart-pipe><span>topRight</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomLeft" flowing></smart-pipe><span>bottomLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomRight" flowing></smart-pipe><span>bottomRight</span></div>
</div>
</section>
<section>
<h2>A run on a grid</h2>
<p>
Pipe segments stretch to fill their grid cell, so a run is built by placing segments on the page layout. Snapping to a grid keeps the connections aligned when the page is resized.
</p>
<div class="loop">
<smart-pipe id="l1" elbow="topRight" flowing style="grid-column: 1; grid-row: 1;"></smart-pipe>
<smart-pipe id="l2" flowing style="grid-column: 2; grid-row: 1;"></smart-pipe>
<smart-pipe id="l3" elbow="topLeft" flowing reverse style="grid-column: 3; grid-row: 1;"></smart-pipe>
<smart-pipe id="l4" orientation="vertical" flowing style="grid-column: 3; grid-row: 2;"></smart-pipe>
<smart-pipe id="l5" elbow="bottomLeft" flowing style="grid-column: 3; grid-row: 3;"></smart-pipe>
<smart-pipe id="l6" flowing reverse style="grid-column: 2; grid-row: 3;"></smart-pipe>
<smart-pipe id="l7" elbow="bottomRight" flowing reverse style="grid-column: 1; grid-row: 3;"></smart-pipe>
<smart-pipe id="l8" orientation="vertical" flowing reverse style="grid-column: 1; grid-row: 2;"></smart-pipe>
</div>
<div class="demo-actions">
<button type="button" id="toggleFlow">Stop the flow</button>
</div>
</section>
</div>
<!-- scripts -->
<script type="module" src="node_modules/smart-industrial/source/modules/smart.pipe.js"></script>
<script type="module" src="index.js"></script>
<script>
/* Shown once every element has rendered, so nothing moves on the way in (styles/demos.css). */
addEventListener('load', function () {
var all = Array.prototype.slice.call(document.querySelectorAll('*')),
ready = function () { document.body.classList.add('ready'); };
Promise.all(all.map(function (e) { return e.localName.indexOf('smart-') === 0 ? customElements.whenDefined(e.localName) : null; })).then(function () {
return Promise.all(all.filter(function (e) { return typeof e.whenRendered === 'function' && !e.isRendered; }).map(function (e) { return e.whenRendered(); }));
}).then(function () { requestAnimationFrame(function () { requestAnimationFrame(ready); }); }, ready);
});
</script>
</body>
</html>
index.js
/// <reference path="../../../source/typescript/smart.elements.d.ts" />
window.onload = function () {
const loop = document.querySelectorAll('.loop smart-pipe'),
button = document.getElementById('toggleFlow');
button.addEventListener('click', function () {
const flowing = !loop[0].flowing;
for (let i = 0; i < loop.length; i++) {
loop[i].flowing = flowing;
}
button.textContent = flowing ? 'Stop the flow' : 'Start the flow';
});
};
styles.css
.smart-demo-container {
max-width: 900px;
}
section {
margin-bottom: 34px;
}
section h2 {
font-size: 16px;
font-weight: 500;
margin: 0 0 8px;
}
section p {
color: var(--smart-text-secondary-color);
margin: 0 0 14px;
max-width: 74ch;
}
.grid {
display: grid;
gap: 20px 26px;
}
.two {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.four {
grid-template-columns: repeat(auto-fit, minmax(120px, 140px));
}
.vertical {
grid-template-columns: repeat(auto-fit, minmax(90px, 110px));
}
.cell {
display: flex;
flex-direction: column;
gap: 8px;
}
.cell span {
color: var(--smart-text-tertiary-color);
font-size: 11px;
}
.h {
width: 100%;
height: 24px;
}
.v {
width: 60px;
height: 110px;
}
.b {
width: 100%;
height: 70px;
}
/* A closed loop built from four elbows and four straights. */
.loop {
display: grid;
grid-template-columns: 90px minmax(120px, 1fr) 90px;
grid-template-rows: 60px 70px 60px;
max-width: 460px;
}
.loop smart-pipe {
width: 100%;
height: 100%;
}
.demo-actions {
margin-top: 16px;
}
.demo-actions button {
background: transparent;
border: 1px solid var(--smart-border);
border-radius: var(--smart-border-radius);
color: var(--smart-text-secondary-color);
cursor: pointer;
font-family: inherit;
font-size: 11px;
letter-spacing: .06em;
padding: 7px 14px;
text-transform: uppercase;
}
.demo-actions button:hover {
background: var(--smart-ui-state-hover);
border-color: var(--smart-ui-state-border-hover);
color: var(--smart-ui-state-color-hover);
}
/* ── Demo chrome, shared by the Industrial demos ─────────────────────────
Headings, prose, buttons and notes are the same on every demo page. This block
is appended by scripts and comes last, so it wins over the rules above. */
.smart-demo-container {
padding: 4px 0 32px;
}
.smart-demo-container section {
margin-bottom: 36px;
}
.smart-demo-container section h2 {
margin: 0 0 8px;
font-size: 17px;
font-weight: 600;
line-height: 1.35;
color: var(--smart-text-color);
}
.smart-demo-container #overview h2 {
margin-bottom: 10px;
font-size: 20px;
max-width: 78ch;
}
.smart-demo-container section > p {
margin: 0 0 14px;
max-width: 84ch;
font-size: 14px;
line-height: 1.55;
color: var(--smart-text-secondary-color);
}
.smart-demo-container code {
padding: 1px 5px;
border-radius: 2px;
background: var(--smart-ui-state-hover);
font-family: Consolas, "Courier New", monospace;
font-size: 12px;
}
.smart-demo-container .demo-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 10px;
margin-top: 12px;
}
.smart-demo-container .demo-actions label {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--smart-text-color);
}
.smart-demo-container .demo-actions button,
.smart-demo-container .demo-button {
min-height: 30px;
padding: 0 12px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-industrial-panel-background, var(--smart-surface));
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
text-transform: none;
cursor: pointer;
}
.smart-demo-container .demo-actions button:hover,
.smart-demo-container .demo-button:hover {
border-color: var(--smart-ui-state-border-hover);
background: var(--smart-ui-state-hover);
color: var(--smart-ui-state-color-hover);
}
.smart-demo-container .demo-actions button.demo-button-primary,
.smart-demo-container .demo-button-primary {
border-color: var(--smart-primary);
background: var(--smart-primary);
color: var(--smart-primary-color, #fff);
}
.smart-demo-container .demo-actions button[disabled],
.smart-demo-container .demo-button[disabled] {
opacity: 0.5;
cursor: default;
}
.smart-demo-container .demo-actions select,
.smart-demo-container .demo-actions input[type="text"],
.smart-demo-container .demo-actions input[type="number"] {
min-height: 30px;
padding: 2px 6px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-background);
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
}
.smart-demo-container .hint,
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
margin: 10px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
font-family: Consolas, "Courier New", monospace;
}
.smart-demo-container .log {
list-style: none;
margin: 10px 0 0;
padding: 0;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .log li {
padding: 2px 0;
}
The same demo in Angular, generated from the HTML demo. The frameworks guide has the project setup.
angular/app.component.html
<div class="angular-root" theme="industrial">
<div class="smart-demo-container">
<section id="overview">
<h2>Smart.Pipe displays a section of process line between two symbols on a mimic diagram.</h2>
<p>
A pipe is a component rather than a drawn line because it carries state: whether the line is flowing, in which direction, and whether it is in service. A flowing pipe animates a dashed line in the direction of flow. When the operating system asks for reduced motion, the line stands still and chevrons along it point the way the flow goes.
</p>
</section>
<section>
<h2>Idle and flowing pipes</h2>
<div class="grid two">
<div class="cell">
<smart-pipe class="h" id="p1"></smart-pipe>
<span>idle</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p2" [flowing]="true"></smart-pipe>
<span>flowing</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p3" [flowing]="true" [reverse]="true"></smart-pipe>
<span>flowing, reversed</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p4" [flowing]="true" [thickness]="7"></smart-pipe>
<span>thickness 7</span>
</div>
</div>
</section>
<section>
<h2>Vertical runs</h2>
<div class="grid vertical">
<div class="cell"><smart-pipe class="v" orientation="vertical"></smart-pipe><span>idle</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" [flowing]="true"></smart-pipe><span>flowing</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" [flowing]="true" [reverse]="true"></smart-pipe><span>reversed</span></div>
</div>
</section>
<section>
<h2>Elbows</h2>
<p>Each elbow enters from one edge and leaves through the centre, so two elbows form a corner.</p>
<div class="grid four">
<div class="cell"><smart-pipe class="b" elbow="topLeft" [flowing]="true"></smart-pipe><span>topLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="topRight" [flowing]="true"></smart-pipe><span>topRight</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomLeft" [flowing]="true"></smart-pipe><span>bottomLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomRight" [flowing]="true"></smart-pipe><span>bottomRight</span></div>
</div>
</section>
<section>
<h2>A run on a grid</h2>
<p>
Pipe segments stretch to fill their grid cell, so a run is built by placing segments on the page layout. Snapping to a grid keeps the connections aligned when the page is resized.
</p>
<div class="loop">
<smart-pipe id="l1" elbow="topRight" [flowing]="true" style="grid-column: 1; grid-row: 1;"></smart-pipe>
<smart-pipe id="l2" [flowing]="true" style="grid-column: 2; grid-row: 1;"></smart-pipe>
<smart-pipe id="l3" elbow="topLeft" [flowing]="true" [reverse]="true" style="grid-column: 3; grid-row: 1;"></smart-pipe>
<smart-pipe id="l4" orientation="vertical" [flowing]="true" style="grid-column: 3; grid-row: 2;"></smart-pipe>
<smart-pipe id="l5" elbow="bottomLeft" [flowing]="true" style="grid-column: 3; grid-row: 3;"></smart-pipe>
<smart-pipe id="l6" [flowing]="true" [reverse]="true" style="grid-column: 2; grid-row: 3;"></smart-pipe>
<smart-pipe id="l7" elbow="bottomRight" [flowing]="true" [reverse]="true" style="grid-column: 1; grid-row: 3;"></smart-pipe>
<smart-pipe id="l8" orientation="vertical" [flowing]="true" [reverse]="true" style="grid-column: 1; grid-row: 2;"></smart-pipe>
</div>
<div class="demo-actions">
<button type="button" id="toggleFlow">Stop the flow</button>
</div>
</section>
</div>
</div>
angular/app.component.ts
import { Component, AfterViewInit, OnDestroy } from '@angular/core';
import { PipeModule } from 'smart-industrial/angular/pipe';
import { runDemo } from './demo';
/**
* Smart.UI Industrial - Pipe in Angular.
*
* The template is the HTML demo's markup: the elements are the wrappers, so what is
* written on them is checked against their inputs. demo.ts drives them the way the
* plant's data would, once every element in the view has rendered.
*/
@Component({
selector: 'app-root',
standalone: true,
imports: [PipeModule],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent implements AfterViewInit, OnDestroy {
private unmounted = false;
ngAfterViewInit(): void {
const root = document.querySelector('.angular-root') as HTMLElement,
pending: Promise<void>[] = [];
root.querySelectorAll('*').forEach((element: any) => {
if (typeof element.whenRendered === 'function' && !element.isRendered) {
pending.push(element.whenRendered());
}
});
Promise.all(pending).then(() => {
if (!this.unmounted) {
runDemo();
}
});
}
ngOnDestroy(): void {
this.unmounted = true;
}
}
angular/demo.ts
// @ts-nocheck
/* The HTML demo's script, as it was written: page code that feeds the elements, not a typed
API. The types are on the wrappers in app.component.ts. */
export function runDemo(): void {
const loop = document.querySelectorAll('.loop smart-pipe'),
button = document.getElementById('toggleFlow');
button.addEventListener('click', function () {
const flowing = !loop[0].flowing;
for (let i = 0; i < loop.length; i++) {
loop[i].flowing = flowing;
}
button.textContent = flowing ? 'Stop the flow' : 'Start the flow';
});
}
angular/app.component.css
.smart-demo-container {
max-width: 900px;
}
section {
margin-bottom: 34px;
}
section h2 {
font-size: 16px;
font-weight: 500;
margin: 0 0 8px;
}
section p {
color: var(--smart-text-secondary-color);
margin: 0 0 14px;
max-width: 74ch;
}
.grid {
display: grid;
gap: 20px 26px;
}
.two {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.four {
grid-template-columns: repeat(auto-fit, minmax(120px, 140px));
}
.vertical {
grid-template-columns: repeat(auto-fit, minmax(90px, 110px));
}
.cell {
display: flex;
flex-direction: column;
gap: 8px;
}
.cell span {
color: var(--smart-text-tertiary-color);
font-size: 11px;
}
.h {
width: 100%;
height: 24px;
}
.v {
width: 60px;
height: 110px;
}
.b {
width: 100%;
height: 70px;
}
/* A closed loop built from four elbows and four straights. */
.loop {
display: grid;
grid-template-columns: 90px minmax(120px, 1fr) 90px;
grid-template-rows: 60px 70px 60px;
max-width: 460px;
}
.loop smart-pipe {
width: 100%;
height: 100%;
}
.demo-actions {
margin-top: 16px;
}
.demo-actions button {
background: transparent;
border: 1px solid var(--smart-border);
border-radius: var(--smart-border-radius);
color: var(--smart-text-secondary-color);
cursor: pointer;
font-family: inherit;
font-size: 11px;
letter-spacing: .06em;
padding: 7px 14px;
text-transform: uppercase;
}
.demo-actions button:hover {
background: var(--smart-ui-state-hover);
border-color: var(--smart-ui-state-border-hover);
color: var(--smart-ui-state-color-hover);
}
/* ── Demo chrome, shared by the Industrial demos ─────────────────────────
Headings, prose, buttons and notes are the same on every demo page. This block
is appended by scripts and comes last, so it wins over the rules above. */
.smart-demo-container {
padding: 4px 0 32px;
}
.smart-demo-container section {
margin-bottom: 36px;
}
.smart-demo-container section h2 {
margin: 0 0 8px;
font-size: 17px;
font-weight: 600;
line-height: 1.35;
color: var(--smart-text-color);
}
.smart-demo-container #overview h2 {
margin-bottom: 10px;
font-size: 20px;
max-width: 78ch;
}
.smart-demo-container section > p {
margin: 0 0 14px;
max-width: 84ch;
font-size: 14px;
line-height: 1.55;
color: var(--smart-text-secondary-color);
}
.smart-demo-container code {
padding: 1px 5px;
border-radius: 2px;
background: var(--smart-ui-state-hover);
font-family: Consolas, "Courier New", monospace;
font-size: 12px;
}
.smart-demo-container .demo-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 10px;
margin-top: 12px;
}
.smart-demo-container .demo-actions label {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--smart-text-color);
}
.smart-demo-container .demo-actions button,
.smart-demo-container .demo-button {
min-height: 30px;
padding: 0 12px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-industrial-panel-background, var(--smart-surface));
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
text-transform: none;
cursor: pointer;
}
.smart-demo-container .demo-actions button:hover,
.smart-demo-container .demo-button:hover {
border-color: var(--smart-ui-state-border-hover);
background: var(--smart-ui-state-hover);
color: var(--smart-ui-state-color-hover);
}
.smart-demo-container .demo-actions button.demo-button-primary,
.smart-demo-container .demo-button-primary {
border-color: var(--smart-primary);
background: var(--smart-primary);
color: var(--smart-primary-color, #fff);
}
.smart-demo-container .demo-actions button[disabled],
.smart-demo-container .demo-button[disabled] {
opacity: 0.5;
cursor: default;
}
.smart-demo-container .demo-actions select,
.smart-demo-container .demo-actions input[type="text"],
.smart-demo-container .demo-actions input[type="number"] {
min-height: 30px;
padding: 2px 6px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-background);
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
}
.smart-demo-container .hint,
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
margin: 10px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
font-family: Consolas, "Courier New", monospace;
}
.smart-demo-container .log {
list-style: none;
margin: 10px 0 0;
padding: 0;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .log li {
padding: 2px 0;
}
The same demo in React, generated from the HTML demo. The frameworks guide has the project setup.
react/App.jsx
import 'smart-industrial/source/styles/smart.default.css';
import 'smart-industrial/source/styles/smart.industrial-elements.css';
import 'smart-industrial/source/styles/smart.industrial.css';
import 'smart-industrial/source/styles/smart.industrial-dark.css';
import './App.css';
import React from 'react';
import { Pipe } from 'smart-industrial/react/pipe';
/* The demo logic is the HTML demo's: it feeds the elements the way a plant's data would. */
const App = () => {
const root = React.useRef(null);
React.useEffect(() => {
/* The elements render after React has mounted them, and the demo's logic - written
for a page's load event - reads their parts, so it waits for every element in the
tree to have rendered first. */
const pending = Array.from(root.current.querySelectorAll('*'))
.filter((element) => typeof element.whenRendered === 'function' && !element.isRendered)
.map((element) => element.whenRendered());
let unmounted = false;
Promise.all(pending).then(() => {
if (unmounted) {
return;
}
const loop = document.querySelectorAll('.loop smart-pipe'),
button = document.getElementById('toggleFlow');
button.addEventListener('click', function () {
const flowing = !loop[0].flowing;
for (let i = 0; i < loop.length; i++) {
loop[i].flowing = flowing;
}
button.textContent = flowing ? 'Stop the flow' : 'Start the flow';
});
});
return () => {
unmounted = true;
};
}, []);
return (
<div className="react-root" ref={root} theme="industrial">
<div className="smart-demo-container">
<section id="overview">
<h2>Smart.Pipe displays a section of process line between two symbols on a mimic diagram.</h2>
<p>A pipe is a component rather than a drawn line because it carries state: whether the line is flowing, in which direction, and whether it is in service. A flowing pipe animates a dashed line in the direction of flow. When the operating system asks for reduced motion, the line stands still and chevrons along it point the way the flow goes.</p>
</section>
<section>
<h2>Idle and flowing pipes</h2>
<div className="grid two">
<div className="cell">
<Pipe className="h" id="p1"></Pipe>
<span>idle</span>
</div>
<div className="cell">
<Pipe className="h" id="p2" flowing></Pipe>
<span>flowing</span>
</div>
<div className="cell">
<Pipe className="h" id="p3" flowing reverse></Pipe>
<span>flowing, reversed</span>
</div>
<div className="cell">
<Pipe className="h" id="p4" flowing thickness={7}></Pipe>
<span>thickness 7</span>
</div>
</div>
</section>
<section>
<h2>Vertical runs</h2>
<div className="grid vertical">
<div className="cell">
<Pipe className="v" orientation="vertical"></Pipe>
<span>idle</span>
</div>
<div className="cell">
<Pipe className="v" orientation="vertical" flowing></Pipe>
<span>flowing</span>
</div>
<div className="cell">
<Pipe className="v" orientation="vertical" flowing reverse></Pipe>
<span>reversed</span>
</div>
</div>
</section>
<section>
<h2>Elbows</h2>
<p>Each elbow enters from one edge and leaves through the centre, so two elbows form a corner.</p>
<div className="grid four">
<div className="cell">
<Pipe className="b" elbow="topLeft" flowing></Pipe>
<span>topLeft</span>
</div>
<div className="cell">
<Pipe className="b" elbow="topRight" flowing></Pipe>
<span>topRight</span>
</div>
<div className="cell">
<Pipe className="b" elbow="bottomLeft" flowing></Pipe>
<span>bottomLeft</span>
</div>
<div className="cell">
<Pipe className="b" elbow="bottomRight" flowing></Pipe>
<span>bottomRight</span>
</div>
</div>
</section>
<section>
<h2>A run on a grid</h2>
<p>Pipe segments stretch to fill their grid cell, so a run is built by placing segments on the page layout. Snapping to a grid keeps the connections aligned when the page is resized.</p>
<div className="loop">
<Pipe id="l1" elbow="topRight" flowing style={{ gridColumn: "1", gridRow: "1" }}></Pipe>
<Pipe id="l2" flowing style={{ gridColumn: "2", gridRow: "1" }}></Pipe>
<Pipe id="l3" elbow="topLeft" flowing reverse style={{ gridColumn: "3", gridRow: "1" }}></Pipe>
<Pipe id="l4" orientation="vertical" flowing style={{ gridColumn: "3", gridRow: "2" }}></Pipe>
<Pipe id="l5" elbow="bottomLeft" flowing style={{ gridColumn: "3", gridRow: "3" }}></Pipe>
<Pipe id="l6" flowing reverse style={{ gridColumn: "2", gridRow: "3" }}></Pipe>
<Pipe id="l7" elbow="bottomRight" flowing reverse style={{ gridColumn: "1", gridRow: "3" }}></Pipe>
<Pipe id="l8" orientation="vertical" flowing reverse style={{ gridColumn: "1", gridRow: "2" }}></Pipe>
</div>
<div className="demo-actions">
<button type="button" id="toggleFlow">Stop the flow</button>
</div>
</section>
</div>
</div>
);
};
export default App;
react/App.css
.smart-demo-container {
max-width: 900px;
}
section {
margin-bottom: 34px;
}
section h2 {
font-size: 16px;
font-weight: 500;
margin: 0 0 8px;
}
section p {
color: var(--smart-text-secondary-color);
margin: 0 0 14px;
max-width: 74ch;
}
.grid {
display: grid;
gap: 20px 26px;
}
.two {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.four {
grid-template-columns: repeat(auto-fit, minmax(120px, 140px));
}
.vertical {
grid-template-columns: repeat(auto-fit, minmax(90px, 110px));
}
.cell {
display: flex;
flex-direction: column;
gap: 8px;
}
.cell span {
color: var(--smart-text-tertiary-color);
font-size: 11px;
}
.h {
width: 100%;
height: 24px;
}
.v {
width: 60px;
height: 110px;
}
.b {
width: 100%;
height: 70px;
}
/* A closed loop built from four elbows and four straights. */
.loop {
display: grid;
grid-template-columns: 90px minmax(120px, 1fr) 90px;
grid-template-rows: 60px 70px 60px;
max-width: 460px;
}
.loop smart-pipe {
width: 100%;
height: 100%;
}
.demo-actions {
margin-top: 16px;
}
.demo-actions button {
background: transparent;
border: 1px solid var(--smart-border);
border-radius: var(--smart-border-radius);
color: var(--smart-text-secondary-color);
cursor: pointer;
font-family: inherit;
font-size: 11px;
letter-spacing: .06em;
padding: 7px 14px;
text-transform: uppercase;
}
.demo-actions button:hover {
background: var(--smart-ui-state-hover);
border-color: var(--smart-ui-state-border-hover);
color: var(--smart-ui-state-color-hover);
}
/* ── Demo chrome, shared by the Industrial demos ─────────────────────────
Headings, prose, buttons and notes are the same on every demo page. This block
is appended by scripts and comes last, so it wins over the rules above. */
.smart-demo-container {
padding: 4px 0 32px;
}
.smart-demo-container section {
margin-bottom: 36px;
}
.smart-demo-container section h2 {
margin: 0 0 8px;
font-size: 17px;
font-weight: 600;
line-height: 1.35;
color: var(--smart-text-color);
}
.smart-demo-container #overview h2 {
margin-bottom: 10px;
font-size: 20px;
max-width: 78ch;
}
.smart-demo-container section > p {
margin: 0 0 14px;
max-width: 84ch;
font-size: 14px;
line-height: 1.55;
color: var(--smart-text-secondary-color);
}
.smart-demo-container code {
padding: 1px 5px;
border-radius: 2px;
background: var(--smart-ui-state-hover);
font-family: Consolas, "Courier New", monospace;
font-size: 12px;
}
.smart-demo-container .demo-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 10px;
margin-top: 12px;
}
.smart-demo-container .demo-actions label {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--smart-text-color);
}
.smart-demo-container .demo-actions button,
.smart-demo-container .demo-button {
min-height: 30px;
padding: 0 12px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-industrial-panel-background, var(--smart-surface));
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
text-transform: none;
cursor: pointer;
}
.smart-demo-container .demo-actions button:hover,
.smart-demo-container .demo-button:hover {
border-color: var(--smart-ui-state-border-hover);
background: var(--smart-ui-state-hover);
color: var(--smart-ui-state-color-hover);
}
.smart-demo-container .demo-actions button.demo-button-primary,
.smart-demo-container .demo-button-primary {
border-color: var(--smart-primary);
background: var(--smart-primary);
color: var(--smart-primary-color, #fff);
}
.smart-demo-container .demo-actions button[disabled],
.smart-demo-container .demo-button[disabled] {
opacity: 0.5;
cursor: default;
}
.smart-demo-container .demo-actions select,
.smart-demo-container .demo-actions input[type="text"],
.smart-demo-container .demo-actions input[type="number"] {
min-height: 30px;
padding: 2px 6px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-background);
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
}
.smart-demo-container .hint,
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
margin: 10px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
font-family: Consolas, "Courier New", monospace;
}
.smart-demo-container .log {
list-style: none;
margin: 10px 0 0;
padding: 0;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .log li {
padding: 2px 0;
}
The same demo in Vue, generated from the HTML demo. The frameworks guide has the project setup.
vue/App.vue
<template>
<div class="vue-root" theme="industrial" v-pre>
<div class="smart-demo-container">
<section id="overview">
<h2>Smart.Pipe displays a section of process line between two symbols on a mimic diagram.</h2>
<p>
A pipe is a component rather than a drawn line because it carries state: whether the line is flowing, in which direction, and whether it is in service. A flowing pipe animates a dashed line in the direction of flow. When the operating system asks for reduced motion, the line stands still and chevrons along it point the way the flow goes.
</p>
</section>
<section>
<h2>Idle and flowing pipes</h2>
<div class="grid two">
<div class="cell">
<smart-pipe class="h" id="p1"></smart-pipe>
<span>idle</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p2" flowing></smart-pipe>
<span>flowing</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p3" flowing reverse></smart-pipe>
<span>flowing, reversed</span>
</div>
<div class="cell">
<smart-pipe class="h" id="p4" flowing thickness="7"></smart-pipe>
<span>thickness 7</span>
</div>
</div>
</section>
<section>
<h2>Vertical runs</h2>
<div class="grid vertical">
<div class="cell"><smart-pipe class="v" orientation="vertical"></smart-pipe><span>idle</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" flowing></smart-pipe><span>flowing</span></div>
<div class="cell"><smart-pipe class="v" orientation="vertical" flowing reverse></smart-pipe><span>reversed</span></div>
</div>
</section>
<section>
<h2>Elbows</h2>
<p>Each elbow enters from one edge and leaves through the centre, so two elbows form a corner.</p>
<div class="grid four">
<div class="cell"><smart-pipe class="b" elbow="topLeft" flowing></smart-pipe><span>topLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="topRight" flowing></smart-pipe><span>topRight</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomLeft" flowing></smart-pipe><span>bottomLeft</span></div>
<div class="cell"><smart-pipe class="b" elbow="bottomRight" flowing></smart-pipe><span>bottomRight</span></div>
</div>
</section>
<section>
<h2>A run on a grid</h2>
<p>
Pipe segments stretch to fill their grid cell, so a run is built by placing segments on the page layout. Snapping to a grid keeps the connections aligned when the page is resized.
</p>
<div class="loop">
<smart-pipe id="l1" elbow="topRight" flowing style="grid-column: 1; grid-row: 1;"></smart-pipe>
<smart-pipe id="l2" flowing style="grid-column: 2; grid-row: 1;"></smart-pipe>
<smart-pipe id="l3" elbow="topLeft" flowing reverse style="grid-column: 3; grid-row: 1;"></smart-pipe>
<smart-pipe id="l4" orientation="vertical" flowing style="grid-column: 3; grid-row: 2;"></smart-pipe>
<smart-pipe id="l5" elbow="bottomLeft" flowing style="grid-column: 3; grid-row: 3;"></smart-pipe>
<smart-pipe id="l6" flowing reverse style="grid-column: 2; grid-row: 3;"></smart-pipe>
<smart-pipe id="l7" elbow="bottomRight" flowing reverse style="grid-column: 1; grid-row: 3;"></smart-pipe>
<smart-pipe id="l8" orientation="vertical" flowing reverse style="grid-column: 1; grid-row: 2;"></smart-pipe>
</div>
<div class="demo-actions">
<button type="button" id="toggleFlow">Stop the flow</button>
</div>
</section>
</div>
</div>
</template>
<script>
import { onMounted } from "vue";
import "smart-industrial/source/styles/smart.default.css";
import "smart-industrial/source/styles/smart.industrial-elements.css";
import "smart-industrial/source/styles/smart.industrial.css";
import "smart-industrial/source/styles/smart.industrial-dark.css";
import "smart-industrial/source/modules/smart.pipe.js";
export default {
name: "app",
setup() {
onMounted(() => {
/* The elements render after Vue has mounted them, and the demo's logic - written for
a page's load event - reads their parts, so it waits for every element in the tree
to have rendered first. */
const pending = Array.from(document.querySelector(".vue-root").querySelectorAll("*"))
.filter((element) => typeof element.whenRendered === "function" && !element.isRendered)
.map((element) => element.whenRendered());
Promise.all(pending).then(() => {
const loop = document.querySelectorAll('.loop smart-pipe'),
button = document.getElementById('toggleFlow');
button.addEventListener('click', function () {
const flowing = !loop[0].flowing;
for (let i = 0; i < loop.length; i++) {
loop[i].flowing = flowing;
}
button.textContent = flowing ? 'Stop the flow' : 'Start the flow';
});
});
});
}
};
</script>
<style>
.smart-demo-container {
max-width: 900px;
}
section {
margin-bottom: 34px;
}
section h2 {
font-size: 16px;
font-weight: 500;
margin: 0 0 8px;
}
section p {
color: var(--smart-text-secondary-color);
margin: 0 0 14px;
max-width: 74ch;
}
.grid {
display: grid;
gap: 20px 26px;
}
.two {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.four {
grid-template-columns: repeat(auto-fit, minmax(120px, 140px));
}
.vertical {
grid-template-columns: repeat(auto-fit, minmax(90px, 110px));
}
.cell {
display: flex;
flex-direction: column;
gap: 8px;
}
.cell span {
color: var(--smart-text-tertiary-color);
font-size: 11px;
}
.h {
width: 100%;
height: 24px;
}
.v {
width: 60px;
height: 110px;
}
.b {
width: 100%;
height: 70px;
}
/* A closed loop built from four elbows and four straights. */
.loop {
display: grid;
grid-template-columns: 90px minmax(120px, 1fr) 90px;
grid-template-rows: 60px 70px 60px;
max-width: 460px;
}
.loop smart-pipe {
width: 100%;
height: 100%;
}
.demo-actions {
margin-top: 16px;
}
.demo-actions button {
background: transparent;
border: 1px solid var(--smart-border);
border-radius: var(--smart-border-radius);
color: var(--smart-text-secondary-color);
cursor: pointer;
font-family: inherit;
font-size: 11px;
letter-spacing: .06em;
padding: 7px 14px;
text-transform: uppercase;
}
.demo-actions button:hover {
background: var(--smart-ui-state-hover);
border-color: var(--smart-ui-state-border-hover);
color: var(--smart-ui-state-color-hover);
}
/* ── Demo chrome, shared by the Industrial demos ─────────────────────────
Headings, prose, buttons and notes are the same on every demo page. This block
is appended by scripts and comes last, so it wins over the rules above. */
.smart-demo-container {
padding: 4px 0 32px;
}
.smart-demo-container section {
margin-bottom: 36px;
}
.smart-demo-container section h2 {
margin: 0 0 8px;
font-size: 17px;
font-weight: 600;
line-height: 1.35;
color: var(--smart-text-color);
}
.smart-demo-container #overview h2 {
margin-bottom: 10px;
font-size: 20px;
max-width: 78ch;
}
.smart-demo-container section > p {
margin: 0 0 14px;
max-width: 84ch;
font-size: 14px;
line-height: 1.55;
color: var(--smart-text-secondary-color);
}
.smart-demo-container code {
padding: 1px 5px;
border-radius: 2px;
background: var(--smart-ui-state-hover);
font-family: Consolas, "Courier New", monospace;
font-size: 12px;
}
.smart-demo-container .demo-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 10px;
margin-top: 12px;
}
.smart-demo-container .demo-actions label {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--smart-text-color);
}
.smart-demo-container .demo-actions button,
.smart-demo-container .demo-button {
min-height: 30px;
padding: 0 12px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-industrial-panel-background, var(--smart-surface));
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
text-transform: none;
cursor: pointer;
}
.smart-demo-container .demo-actions button:hover,
.smart-demo-container .demo-button:hover {
border-color: var(--smart-ui-state-border-hover);
background: var(--smart-ui-state-hover);
color: var(--smart-ui-state-color-hover);
}
.smart-demo-container .demo-actions button.demo-button-primary,
.smart-demo-container .demo-button-primary {
border-color: var(--smart-primary);
background: var(--smart-primary);
color: var(--smart-primary-color, #fff);
}
.smart-demo-container .demo-actions button[disabled],
.smart-demo-container .demo-button[disabled] {
opacity: 0.5;
cursor: default;
}
.smart-demo-container .demo-actions select,
.smart-demo-container .demo-actions input[type="text"],
.smart-demo-container .demo-actions input[type="number"] {
min-height: 30px;
padding: 2px 6px;
border: 1px solid var(--smart-border);
border-radius: 3px;
background: var(--smart-background);
color: var(--smart-text-color);
font: inherit;
font-size: 13px;
}
.smart-demo-container .hint,
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
margin: 10px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .demo-log,
.smart-demo-container .demo-counts {
font-family: Consolas, "Courier New", monospace;
}
.smart-demo-container .log {
list-style: none;
margin: 10px 0 0;
padding: 0;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: var(--smart-text-secondary-color);
opacity: 1;
}
.smart-demo-container .log li {
padding: 2px 0;
}
</style>
What the demo shows
- Idle and flowing pipes
- Vertical runs
- Elbows
- A run on a grid