diff --git a/ivette/src/dome/renderer/graph/diagram.tsx b/ivette/src/dome/renderer/graph/diagram.tsx index 30efb2c70e9d063fed519971090af887f3e86c4b..88e04b89e4be87ea6c7c0ee3857fb0d156d8aca1 100644 --- a/ivette/src/dome/renderer/graph/diagram.tsx +++ b/ivette/src/dome/renderer/graph/diagram.tsx @@ -26,6 +26,7 @@ import { classes } from 'dome/misc/utils'; import { Size } from 'react-virtualized'; import * as d3 from 'd3-graphviz'; import AutoSizer from 'react-virtualized-auto-sizer'; +import './style.css'; /* -------------------------------------------------------------------------- */ /* --- Graph Specifications --- */ @@ -33,13 +34,27 @@ import AutoSizer from 'react-virtualized-auto-sizer'; export type Direction = 'LR' | 'TD'; -export type Cell = string | { label: string, port: string }; +export type Color = + | 'white' + | 'grey' + | 'dark' + | 'primary' + | 'selected' + | 'green' + | 'orange' + | 'red' + | 'yellow' + | 'blue' + | 'pink'; export type Shape = | 'point' | 'box' | 'diamond' | 'hexagon' | 'circle' | 'ellipse' | 'note' | 'tab' | 'folder'; + +export type Cell = string | { label: string, port: string }; + export type Box = Cell | Box[]; export interface Node { @@ -49,9 +64,12 @@ export interface Node { label?: string; /** Node tooltip */ title?: string; + /** Node color (filled background) */ + color?: Color; /** - * Shape. Box shapes alternate LR and TD directions. - * Initial direction is orthogonal to the graph direction. */ + * Shape. Nested boxes alternate LR and TD directions. Initial direction is + * orthogonal to the graph direction. Node label is ignored for box layout. + */ shape?: Shape | Box[]; } @@ -100,6 +118,38 @@ export interface DiagramProps { } +/* -------------------------------------------------------------------------- */ +/* --- Color Model --- */ +/* -------------------------------------------------------------------------- */ + +const BGCOLOR = { + 'white': '#fff', + 'grey': '#ccc', + 'dark': '#666', + 'primary': 'dodgerblue', + 'selected': 'deepskyblue', + 'green': 'lime', + 'orange': '#ffa700', + 'red': 'red', + 'yellow': 'yellow', + 'blue': 'cyan', + 'pink': 'hotpink', +}; + +const FGCOLOR = { + 'white': 'black', + 'grey': 'black', + 'dark': 'white', + 'primary': 'white', + 'selected': 'black', + 'green': 'black', + 'orange': 'black', + 'red': 'white', + 'yellow': 'black', + 'blue': 'black', + 'pink': 'white', +}; + /* -------------------------------------------------------------------------- */ /* --- Dot Model --- */ /* -------------------------------------------------------------------------- */ @@ -172,8 +222,11 @@ class DotModel { .attr('label', n.label) .attr('shape', n.shape); } + const color = n.color ?? 'white'; this .attr('tooltip', n.title) + .attr('fontcolor', FGCOLOR[color]) + .attr('fillcolor', BGCOLOR[color]) .println('];'); } @@ -210,14 +263,16 @@ interface GraphvizProps extends DiagramProps { size: Size } function GraphvizView(props: GraphvizProps): JSX.Element { // --- Model Generation - const { direction = 'LR', nodes, edges } = props; + const { + direction = 'LR', nodes, edges + } = props; const model = React.useMemo(() => { const dot = new DotModel(); dot .attr('rankdir', direction) .attr('bgcolor', 'none') .attr('width', 0.5) - .println(); + .println('node [ style="filled" ];'); nodes.concat().sort(byNode).forEach(n => dot.node(n)); edges.concat().sort(byEdge).forEach(e => dot.edge(e)); return dot.flush(); @@ -235,7 +290,7 @@ function GraphvizView(props: GraphvizProps): JSX.Element { React.useEffect(() => { d3.graphviz(`#${id}`, { useWorker: false, - fit: false, zoom: true, width, height, + fit: true, zoom: true, width, height, }).renderDot(model); }, [id, model, width, height]); @@ -258,7 +313,7 @@ export function Diagram(props: DiagramProps): JSX.Element { {display && ( <AutoSizer> {(size: Size) => ( - <div className={className}> + <div className={className} style={size}> <GraphvizView size={size} {...props} /> </div> )} diff --git a/ivette/src/dome/renderer/graph/style copy.css b/ivette/src/dome/renderer/graph/style copy.css new file mode 100644 index 0000000000000000000000000000000000000000..ad640de4533911ff329e887669ac8c19cf4bf3e6 --- /dev/null +++ b/ivette/src/dome/renderer/graph/style copy.css @@ -0,0 +1,213 @@ +/* -------------------------------------------------------------------------- */ +/* --- Main Dome Styles --- */ +/* -------------------------------------------------------------------------- */ + +* { + user-select: none; + box-sizing: border-box; + margin: 0 ; + padding: 0 ; +} + +body { + color: var(--text); + background: var(--background-softer); + overflow: hidden ; + position: fixed ; + font-family: sans-serif ; + font-size: 13px ; + top: 0 ; + bottom: 0 ; + left: 0 ; + right: 0 ; +} + +#app { + width: 100% ; + height: 100% ; +} + +/* -------------------------------------------------------------------------- */ +/* --- Frame Colors --- */ +/* -------------------------------------------------------------------------- */ + +.dome-erased { + display: none !important; +} + +.dome-hidden { + visibility: hidden !important; +} + +.dome-positionned { + position: relative; +} + +.dome-color-frame { + fill: var(--text-discrete) ; + color: var(--text-discrete) ; + border-color: var(--border) ; + background: var(--background-intense) ; +} + +.dome-color-dragzone { + opacity: 0.0 ; + background: transparent ; + transition: opacity .1s linear 0.1s , background .1s linear 0.1s ; +} + +.dome-color-dragzone:hover { + background: var(--grid-layout-holder) ; + opacity: 0.4 ; + transition: opacity .1s linear 0.1s , background .1s linear 0.1s ; +} + +.dome-color-dragging { + background: var(--grid-layout-target) ; + opacity: 0.5 ; + transition: opacity .1s linear 0.1s , background .1s linear 0.1s ; +} + +div.dome-dragged { + background: var(--grid-layout-holder) ; + border: none ; +} + +.dome-dragging * { + cursor: move ; +} + +/* -------------------------------------------------------------------------- */ +/* --- Text Properties --- */ +/* -------------------------------------------------------------------------- */ + +.dome-text-label { + font-family: sans-serif ; + user-select: none ; + white-space: nowrap ; + text-overflow: ellipsis ; +} + +.dome-text-title { + font-family: sans-serif ; + font-size: larger ; + font-weight: bolder ; + user-select: none ; + white-space: nowrap ; + text-overflow: ellipsis ; +} + +.dome-text-descr { + font-weight: lighter ; + font-family: sans-serif ; + font-size: smaller ; + user-select: none ; + white-space: normal ; + text-overflow: ellipsis ; +} + +.dome-text-data { + cursor: text ; + user-select: text ; + font-family: sans-serif ; + white-space: nowrap ; + text-overflow: clip ; +} + +.dome-text-code { + cursor: text ; + user-select: text ; + font-family: 'Andale mono', monospace ; + font-size: 9pt ; + white-space: nowrap ; + text-overflow: clip ; +} + +.dome-text-cell { + cursor: default ; + user-select: text ; + font-family: 'Andale mono', monospace ; + font-size: 9pt ; + white-space: nowrap ; + text-overflow: clip ; +} + +.dome-text-item { + cursor: default ; + user-select: none ; + font-family: 'Andale mono', monospace ; + font-size: 9pt ; + white-space: nowrap ; + text-overflow: clip ; +} + +/* -------------------------------------------------------------------------- */ +/* --- Theme-compliant Scrollbars --- */ +/* -------------------------------------------------------------------------- */ + +::-webkit-scrollbar { + width: 14px; + height: 14px; +} + +::-webkit-scrollbar-track { + background: var(--background-intense); +} + +::-webkit-scrollbar-thumb { + background-color: var(--info-text-discrete); + border-radius: 20px; + border: 3px solid var(--background-intense); +} + +::-webkit-scrollbar-corner { + background-color: var(--background-profound); + background: var(--background-profound); + color: var(--background-profound); +} + +/* -------------------------------------------------------------------------- */ +/* --- Theme-compliant Input Widgets --- */ +/* -------------------------------------------------------------------------- */ + +input[type="search"]::placeholder { + font-style: italic; + color: var(--text-discrete); +} + +input[type="text"]::placeholder { + font-style: italic; + color: var(--text-discrete); +} + +input[type="text"] { + vertical-align: middle; + margin: 2px 4px 2px 0px; + background-color: var(--background-interaction); + border: var(--border); + border-radius: 2px; +} + +input:focus-visible { + outline: none; + box-shadow: 0px 0px 1px 1px var(--border); +} + +input[type="checkbox"] { + appearance: none; + width: 13px; + height: 13px; + border: 1px solid var(--border); + border-radius: 2px; + content: ""; + font-size: 12px; + color: var(--text); + background-clip: content-box; + padding: 1px; +} + +input[type="checkbox"]:checked { + background-color: var(--checked-element); +} + +/* -------------------------------------------------------------------------- */ diff --git a/ivette/src/dome/renderer/graph/style.css b/ivette/src/dome/renderer/graph/style.css new file mode 100644 index 0000000000000000000000000000000000000000..c8818cb37fc1b12a09f3fc820acec6e7e6a256a0 --- /dev/null +++ b/ivette/src/dome/renderer/graph/style.css @@ -0,0 +1,7 @@ +/* -------------------------------------------------------------------------- */ +/* --- Graph Styles --- */ +/* -------------------------------------------------------------------------- */ + +.dome-xDiagram { + background: #eee; +} diff --git a/ivette/src/sandbox/dotdiagram.tsx b/ivette/src/sandbox/dotdiagram.tsx index 89f60932474c1cf8bd61fb42edc2c966a36da83f..921acdf25e4409d4d5c2ada1bae8406df58fce31 100644 --- a/ivette/src/sandbox/dotdiagram.tsx +++ b/ivette/src/sandbox/dotdiagram.tsx @@ -37,18 +37,37 @@ import { registerSandbox } from 'ivette'; const nodes: Node[] = [ { id: 'A' }, { id: 'B', shape: 'diamond' }, - { id: 'R', + { + id: 'R', shape: [ { label: 'C', port: 'c' }, - [ 'D1', 'D2'], + ['D1', 'D2'], { label: 'E', port: 'e' }, ] - } + }, + { id: 'white', color: 'white' }, + { id: 'grey', color: 'grey' }, + { id: 'dark', color: 'dark' }, + { id: 'primary', color: 'primary' }, + { id: 'selected', color: 'selected' }, + { id: 'green', color: 'green' }, + { id: 'orange', color: 'orange' }, + { id: 'red', color: 'red' }, + { id: 'yellow', color: 'yellow' }, + { id: 'blue', color: 'blue' }, + { id: 'pink', color: 'pink' }, ]; const edges: Edge[] = [ { source: 'A', target: 'R', targetPort: 'c' }, { source: 'R', target: 'B', sourcePort: 'e' }, + { source: 'primary', target: 'selected' }, + { source: 'white', target: 'grey' }, + { source: 'grey', target: 'dark' }, + { source: 'green', target: 'orange' }, + { source: 'orange', target: 'red' }, + { source: 'yellow', target: 'pink' }, + { source: 'pink', target: 'blue' } ]; function DiagramSample(): JSX.Element {