Upgraded the TaskNameDisplayPanel to be more reusable.
This commit is contained in:
parent
7ee7776c78
commit
74c105a42e
@ -63,3 +63,28 @@
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-tasks-grid {
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
|
||||
> .workflow-tasks-list-host {
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.task-list-container--expand-to-content {
|
||||
width: max-content;
|
||||
|
||||
.selectable-list {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.task-name-display-panel {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.task-name-display-panel__name {
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,18 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.validation-error-icon--reserve-space {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 1rem;
|
||||
width: 1rem;
|
||||
|
||||
.error-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.task-name-display-panel--wrap {
|
||||
white-space: normal;
|
||||
|
||||
|
||||
@ -4,16 +4,26 @@ import React from "react";
|
||||
|
||||
interface ValidationErrorIconProps {
|
||||
visible: boolean;
|
||||
reserveSpace?: boolean;
|
||||
}
|
||||
|
||||
const ValidationErrorIcon: React.FC<ValidationErrorIconProps> = ({
|
||||
visible,
|
||||
reserveSpace = false,
|
||||
}) => {
|
||||
if (!visible) return null;
|
||||
if (!visible && !reserveSpace) return null;
|
||||
|
||||
const wrapperClassName = reserveSpace
|
||||
? "validation-error-icon validation-error-icon--reserve-space"
|
||||
: "validation-error-icon";
|
||||
|
||||
return (
|
||||
<span className="error-icon">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||
<span className={wrapperClassName} aria-hidden={!visible}>
|
||||
{visible && (
|
||||
<span className="error-icon">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@ -93,7 +93,7 @@ const TaskList: React.FC<TaskListProps> = ({
|
||||
const sortedTasks = sortTasksTopologically(tasks);
|
||||
|
||||
return (
|
||||
<div className="task-list-container">
|
||||
<div className="task-list-container task-list-container--expand-to-content">
|
||||
<div className="task-list-header">
|
||||
<AddTaskButton tasksMetadata={tasksMetadata} onAdd={handleAddTask} />
|
||||
</div>
|
||||
@ -111,6 +111,7 @@ const TaskList: React.FC<TaskListProps> = ({
|
||||
showValidationErrorIcon={
|
||||
validTasksList[x.config.guid as string] === false
|
||||
}
|
||||
reserveValidationErrorIconSpace={true}
|
||||
/>
|
||||
);
|
||||
} else return <></>;
|
||||
|
||||
@ -11,14 +11,19 @@ export interface TaskNameDisplayPanel {
|
||||
task: TaskDefinition;
|
||||
showValidationErrorIcon: boolean;
|
||||
allowWordWrap?: boolean;
|
||||
reserveValidationErrorIconSpace?: boolean;
|
||||
}
|
||||
|
||||
const TaskNameDisplayPanel: React.FC<TaskNameDisplayPanel> = ({
|
||||
task,
|
||||
showValidationErrorIcon = false,
|
||||
allowWordWrap = false,
|
||||
reserveValidationErrorIconSpace = false,
|
||||
}) => {
|
||||
const [tasksMetadata, setTasksMetadata] = React.useState<TaskMetadata[]>([]);
|
||||
const [isNameTruncated, setIsNameTruncated] = React.useState(false);
|
||||
const taskNameRef = React.useRef<HTMLSpanElement | null>(null);
|
||||
const taskName = task.config.name as string;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTaskMetadata = async () => {
|
||||
@ -42,13 +47,52 @@ const TaskNameDisplayPanel: React.FC<TaskNameDisplayPanel> = ({
|
||||
? "task-name-display-panel task-name-display-panel--wrap"
|
||||
: "task-name-display-panel";
|
||||
|
||||
const updateTruncationState = React.useCallback(() => {
|
||||
const el = taskNameRef.current;
|
||||
|
||||
if (!el || allowWordWrap) {
|
||||
setIsNameTruncated(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsNameTruncated(el.scrollWidth > el.clientWidth);
|
||||
}, [allowWordWrap]);
|
||||
|
||||
useEffect(() => {
|
||||
updateTruncationState();
|
||||
}, [taskName, allowWordWrap, updateTruncationState]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = taskNameRef.current;
|
||||
if (!el || allowWordWrap || typeof ResizeObserver === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
updateTruncationState();
|
||||
});
|
||||
|
||||
observer.observe(el);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [allowWordWrap, updateTruncationState]);
|
||||
|
||||
return (
|
||||
<div className={panelClassName}>
|
||||
<FontAwesomeStringIcon icon={meta?.icon} />
|
||||
<span className="task-name-display-panel__name">
|
||||
{task.config.name as string}
|
||||
<span
|
||||
ref={taskNameRef}
|
||||
className="task-name-display-panel__name"
|
||||
title={isNameTruncated ? taskName : undefined}
|
||||
>
|
||||
{taskName}
|
||||
</span>
|
||||
{<ValidationErrorIcon visible={showValidationErrorIcon} />}
|
||||
<ValidationErrorIcon
|
||||
visible={showValidationErrorIcon}
|
||||
reserveSpace={reserveValidationErrorIconSpace}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -248,8 +248,8 @@ const TasksTab: React.FC<TasksTabProps> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="two-column-grid no-scroll">
|
||||
<div className="fit-content-width`">
|
||||
<div className="two-column-grid no-scroll workflow-tasks-grid">
|
||||
<div className="fit-content-width workflow-tasks-list-host">
|
||||
<TaskList
|
||||
tasks={tasks}
|
||||
validTasksList={taskValidation}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user