feat: added visibility toggle with autoHide option
ref: N25B-450
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
MarkerType, getOutgoers
|
MarkerType, getOutgoers
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
|
import warningStyles from './visualProgrammingUI/components/WarningSidebar.module.css'
|
||||||
import {type CSSProperties, useEffect, useState} from "react";
|
import {type CSSProperties, useEffect, useState} from "react";
|
||||||
import {useShallow} from 'zustand/react/shallow';
|
import {useShallow} from 'zustand/react/shallow';
|
||||||
import orderPhaseNodeArray from "../../utils/orderPhaseNodes.ts";
|
import orderPhaseNodeArray from "../../utils/orderPhaseNodes.ts";
|
||||||
@@ -114,8 +115,6 @@ const VisProgUI = () => {
|
|||||||
}
|
}
|
||||||
},[edges, registerWarning, unregisterWarning])
|
},[edges, registerWarning, unregisterWarning])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.innerEditorContainer} round-lg border-lg flex-row`} style={({'--flow-zoom': zoom} as CSSProperties)}>
|
<div className={`${styles.innerEditorContainer} round-lg border-lg flex-row`} style={({'--flow-zoom': zoom} as CSSProperties)}>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -152,10 +151,13 @@ const VisProgUI = () => {
|
|||||||
|
|
||||||
<button onClick={() => redo()}>Redo</button>
|
<button onClick={() => redo()}>Redo</button>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
<Panel position="center-right" className={warningStyles.warningsSidebar}>
|
||||||
|
<WarningsSidebar/>
|
||||||
|
</Panel>
|
||||||
<Controls/>
|
<Controls/>
|
||||||
<Background/>
|
<Background/>
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
<WarningsSidebar/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -221,7 +223,6 @@ const checkPhaseChain = (): boolean => {
|
|||||||
|
|
||||||
const next = outgoingPhases.map(node => checkForCompleteChain(node.id))
|
const next = outgoingPhases.map(node => checkForCompleteChain(node.id))
|
||||||
.find(result => result);
|
.find(result => result);
|
||||||
console.log(next);
|
|
||||||
return !!next;
|
return !!next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +247,6 @@ function VisProgPage() {
|
|||||||
// however this would cause unneeded updates
|
// however this would cause unneeded updates
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [severityIndex]);
|
}, [severityIndex]);
|
||||||
console.log(severityIndex);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<VisualProgrammingUI/>
|
<VisualProgrammingUI/>
|
||||||
|
|||||||
@@ -1,12 +1,51 @@
|
|||||||
.warnings-sidebar {
|
.warnings-sidebar {
|
||||||
min-width: 320px;
|
min-width: auto;
|
||||||
max-width: 320px;
|
max-width: 340px;
|
||||||
width: 320px;
|
margin-right: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: canvas;
|
background: canvas;
|
||||||
border-left: 2px solid CanvasText;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warnings-toggle-bar {
|
||||||
|
background-color: ButtonFace;
|
||||||
|
justify-items: center;
|
||||||
|
align-content: center;
|
||||||
|
width: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warnings-toggle-bar.error:first-child:has(.arrow-right){
|
||||||
|
background-color: hsl(from red h s 75%);
|
||||||
|
}
|
||||||
|
.warnings-toggle-bar.warning:first-child:has(.arrow-right) {
|
||||||
|
background-color: hsl(from orange h s 75%);
|
||||||
|
}
|
||||||
|
.warnings-toggle-bar.info:first-child:has(.arrow-right) {
|
||||||
|
background-color: hsl(from steelblue h s 75%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warnings-toggle-bar:hover {
|
||||||
|
background-color: GrayText !important ;
|
||||||
|
.arrow-left {
|
||||||
|
border-right-color: ButtonFace;
|
||||||
|
transition: transform 0.15s ease-in-out;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
.arrow-right {
|
||||||
|
border-left-color: ButtonFace;
|
||||||
|
transition: transform 0.15s ease-in-out;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.warnings-content {
|
||||||
|
width: 320px;
|
||||||
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border-left: 2px solid CanvasText;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warnings-header {
|
.warnings-header {
|
||||||
@@ -45,12 +84,14 @@
|
|||||||
|
|
||||||
.warning-group-header {
|
.warning-group-header {
|
||||||
background: ButtonFace;
|
background: ButtonFace;
|
||||||
padding: 4px;
|
padding: 6px;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warnings-list {
|
.warnings-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
min-height: 0;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warnings-empty {
|
.warnings-empty {
|
||||||
@@ -131,4 +172,32 @@
|
|||||||
.warning-item .description {
|
.warning-item .description {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auto-hide {
|
||||||
|
background-color: Canvas;
|
||||||
|
border-top: 2px solid CanvasText;
|
||||||
|
margin-top: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* arrows for toggleBar */
|
||||||
|
.arrow-right {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 0.5rem solid transparent;
|
||||||
|
border-bottom: 0.5rem solid transparent;
|
||||||
|
border-left: 0.6rem solid GrayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-left {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 0.5rem solid transparent;
|
||||||
|
border-bottom: 0.5rem solid transparent;
|
||||||
|
border-right: 0.6rem solid GrayText;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,23 +11,64 @@ import styles from "./WarningSidebar.module.css";
|
|||||||
|
|
||||||
export function WarningsSidebar() {
|
export function WarningsSidebar() {
|
||||||
const warnings = useFlowStore.getState().getWarnings();
|
const warnings = useFlowStore.getState().getWarnings();
|
||||||
|
const [hide, setHide] = useState(false);
|
||||||
const [severityFilter, setSeverityFilter] = useState<WarningSeverity | 'ALL'>('ALL');
|
const [severityFilter, setSeverityFilter] = useState<WarningSeverity | 'ALL'>('ALL');
|
||||||
|
const [autoHide, setAutoHide] = useState(false);
|
||||||
|
|
||||||
|
// let autohide change hide status only when autohide is toggled
|
||||||
|
// and allow for user to change the hide state even if autohide is enabled
|
||||||
|
const hasWarnings = warnings.length > 0;
|
||||||
|
useEffect(() => {
|
||||||
|
if (autoHide) {
|
||||||
|
setHide(!hasWarnings);
|
||||||
|
}
|
||||||
|
}, [autoHide, hasWarnings]);
|
||||||
|
|
||||||
useEffect(() => {}, [warnings]);
|
|
||||||
const filtered = severityFilter === 'ALL'
|
const filtered = severityFilter === 'ALL'
|
||||||
? warnings
|
? warnings
|
||||||
: warnings.filter(w => w.severity === severityFilter);
|
: warnings.filter(w => w.severity === severityFilter);
|
||||||
|
|
||||||
return (
|
|
||||||
<aside id="warningSidebar" className={styles.warningsSidebar}>
|
|
||||||
<WarningsHeader
|
|
||||||
severityFilter={severityFilter}
|
|
||||||
onChange={setSeverityFilter}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<WarningsList warnings={filtered} />
|
const summary = warningSummary();
|
||||||
|
// Finds the first key where the count > 0
|
||||||
|
const getHighestSeverity = () => {
|
||||||
|
if (summary.error > 0) return styles.error;
|
||||||
|
if (summary.warning > 0) return styles.warning;
|
||||||
|
if (summary.info > 0) return styles.info;
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className={`flex-row`} >
|
||||||
|
<div
|
||||||
|
className={`${styles.warningsToggleBar} ${getHighestSeverity()}`}
|
||||||
|
onClick={() => setHide(!hide)}
|
||||||
|
title={"toggle warnings"}
|
||||||
|
>
|
||||||
|
<div className={`${hide ? styles.arrowRight : styles.arrowLeft}`}></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="warningSidebar"
|
||||||
|
className={styles.warningsContent}
|
||||||
|
style={hide ? {display: "none"} : {display: "flex"}}
|
||||||
|
>
|
||||||
|
<WarningsHeader
|
||||||
|
severityFilter={severityFilter}
|
||||||
|
onChange={setSeverityFilter}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<WarningsList warnings={filtered} />
|
||||||
|
<div className={styles.autoHide}>
|
||||||
|
<input
|
||||||
|
id="autoHideSwitch"
|
||||||
|
type={"checkbox"}
|
||||||
|
checked={autoHide}
|
||||||
|
onChange={(e) => setAutoHide(e.target.checked)}
|
||||||
|
/><label>Hide if there are no warnings</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +84,6 @@ function WarningsHeader({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.warningsHeader}>
|
<div className={styles.warningsHeader}>
|
||||||
<h3>Warnings</h3>
|
<h3>Warnings</h3>
|
||||||
|
|
||||||
<div className={styles.severityTabs}>
|
<div className={styles.severityTabs}>
|
||||||
{(['ALL', 'ERROR', 'WARNING', 'INFO'] as const).map(severity => (
|
{(['ALL', 'ERROR', 'WARNING', 'INFO'] as const).map(severity => (
|
||||||
<button
|
<button
|
||||||
@@ -71,7 +111,6 @@ function WarningsList(props: { warnings: EditorWarning[] }) {
|
|||||||
global: props.warnings.filter(w => w.scope.id === globalWarning),
|
global: props.warnings.filter(w => w.scope.id === globalWarning),
|
||||||
other: props.warnings.filter(w => w.scope.id !== globalWarning),
|
other: props.warnings.filter(w => w.scope.id !== globalWarning),
|
||||||
}
|
}
|
||||||
console.log(splitWarnings);
|
|
||||||
if (props.warnings.length === 0) {
|
if (props.warnings.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.warningsEmpty}>
|
<div className={styles.warningsEmpty}>
|
||||||
@@ -80,10 +119,9 @@ function WarningsList(props: { warnings: EditorWarning[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div style={{overflowY: 'auto'}}>
|
<div className={styles.warningsList}>
|
||||||
<div className={"warningGroup"}>
|
|
||||||
<div className={styles.warningGroupHeader}>global:</div>
|
<div className={styles.warningGroupHeader}>global:</div>
|
||||||
<div className={styles.warningsList}>
|
<div className={styles.warningsGroup}>
|
||||||
{splitWarnings.global.map((warning) => (
|
{splitWarnings.global.map((warning) => (
|
||||||
<WarningListItem warning={warning} key={`${warning.scope.id}|${warning.type}` + (warning.scope.handleId
|
<WarningListItem warning={warning} key={`${warning.scope.id}|${warning.type}` + (warning.scope.handleId
|
||||||
? `:${warning.scope.handleId}`
|
? `:${warning.scope.handleId}`
|
||||||
@@ -92,10 +130,8 @@ function WarningsList(props: { warnings: EditorWarning[] }) {
|
|||||||
))}
|
))}
|
||||||
{splitWarnings.global.length === 0 && "No global warnings!"}
|
{splitWarnings.global.length === 0 && "No global warnings!"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className={"warningGroup"}>
|
|
||||||
<div className={styles.warningGroupHeader}>other:</div>
|
<div className={styles.warningGroupHeader}>other:</div>
|
||||||
<div className={styles.warningsList}>
|
<div className={styles.warningsGroup}>
|
||||||
{splitWarnings.other.map((warning) => (
|
{splitWarnings.other.map((warning) => (
|
||||||
<WarningListItem warning={warning} key={`${warning.scope.id}|${warning.type}` + (warning.scope.handleId
|
<WarningListItem warning={warning} key={`${warning.scope.id}|${warning.type}` + (warning.scope.handleId
|
||||||
? `:${warning.scope.handleId}`
|
? `:${warning.scope.handleId}`
|
||||||
@@ -104,7 +140,6 @@ function WarningsList(props: { warnings: EditorWarning[] }) {
|
|||||||
))}
|
))}
|
||||||
{splitWarnings.other.length === 0 && "No other warnings!"}
|
{splitWarnings.other.length === 0 && "No other warnings!"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user