Better rendering of buttons for node on graph
This commit is contained in:
parent
05fbd5a9f4
commit
37f9986b02
|
|
@ -19,7 +19,7 @@ import ReactFlow, {
|
|||
Edge,
|
||||
BackgroundVariant
|
||||
} from 'reactflow';
|
||||
import { App } from 'obsidian';
|
||||
import { App, setIcon } from 'obsidian';
|
||||
import { Dialog } from 'src/utils/Dialog';
|
||||
|
||||
// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что esbuild настроен на обработку CSS импортов.
|
||||
|
|
@ -66,21 +66,17 @@ interface BaseNodeWrapperProps {
|
|||
* @returns {React.ReactElement} Элемент React.
|
||||
*/
|
||||
const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable, children }) => {
|
||||
// ----------------------------------------------- Styles --------------------------------------------------------------------
|
||||
// Стили для кнопки удаления. Вынесены для переиспользования.
|
||||
const deleteButtonStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
bottom: '-30px', // Размещаем под узлом
|
||||
left: '5px',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
fontSize: '1em',
|
||||
color: 'var(--text-muted)', // Используем цвет текста Obsidian
|
||||
padding: '0 5px',
|
||||
opacity: data.isSelected ? 1 : 0, // Показываем только если узел выделен
|
||||
transition: 'opacity 0.2s ease-in-out'
|
||||
};
|
||||
const deleteIconRef = React.useRef<HTMLSpanElement>(null);
|
||||
const regenerateIconRef = React.useRef<HTMLSpanElement>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (deleteIconRef.current) {
|
||||
setIcon(deleteIconRef.current, 'trash-2');
|
||||
}
|
||||
if (regenerateIconRef.current) {
|
||||
setIcon(regenerateIconRef.current, 'refresh-cw');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// ----------------------------------------------- Event Handlers ------------------------------------------------------------
|
||||
/**
|
||||
|
|
@ -109,6 +105,7 @@ const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable,
|
|||
}
|
||||
};
|
||||
// ----------------------------------------------- Render --------------------------------------------------------------------
|
||||
|
||||
return React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
|
|
@ -123,37 +120,28 @@ const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable,
|
|||
isConnectable: isConnectable
|
||||
}),
|
||||
children, // Здесь будет отображаться специфическое содержимое узла
|
||||
data.isSelected &&
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
position: 'absolute',
|
||||
bottom: '-30px',
|
||||
left: '5px',
|
||||
display: 'flex',
|
||||
gap: '5px'
|
||||
}
|
||||
className: 'node-toolbar' // Применяем класс для тулбара
|
||||
},
|
||||
React.createElement(
|
||||
'button',
|
||||
{
|
||||
style: deleteButtonStyle,
|
||||
onClick: handleNodeDelete,
|
||||
'aria-label': 'Удалить узел',
|
||||
className: 'node-delete-button'
|
||||
className: 'node-action-button node-delete-button' // Применяем классы для кнопки
|
||||
},
|
||||
'🗑️'
|
||||
React.createElement('span', { ref: deleteIconRef }) // Элемент для иконки удаления
|
||||
),
|
||||
React.createElement(
|
||||
'button',
|
||||
{
|
||||
style: deleteButtonStyle,
|
||||
onClick: handleRegenerate,
|
||||
'aria-label': 'Регенерировать',
|
||||
className: 'node-regenerate-button'
|
||||
className: 'node-action-button node-regenerate-button' // Применяем классы для кнопки
|
||||
},
|
||||
'🔄'
|
||||
React.createElement('span', { ref: regenerateIconRef }) // Элемент для иконки регенерации
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -842,22 +842,67 @@
|
|||
}
|
||||
|
||||
|
||||
/* Стиль для кнопок внутри узла (например, кнопки удаления) */
|
||||
.react-flow__node button.node-delete-button {
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
color: var(--text-error); /* Цвет для кнопки удаления */
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
padding: 3px 6px;
|
||||
margin-top: 5px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
/* Node Toolbar: Контейнер для кнопок действий */
|
||||
.react-flow__node .node-toolbar {
|
||||
position: absolute;
|
||||
top: 4px; /* Отступ сверху */
|
||||
right: 3px; /* <-- ИЗМЕНЕНО: Сдвинуто на 4px правее */
|
||||
z-index: 10; /* Чтобы кнопки были поверх содержимого узла */
|
||||
display: flex;
|
||||
flex-direction: column; /* Вертикальное расположение кнопок */
|
||||
gap: 1px; /* Отступ между кнопками */
|
||||
background-color: transparent; /* Прозрачный фон */
|
||||
padding: 0px; /* Нет внутреннего отступа */
|
||||
pointer-events: none; /* Изначально игнорировать события мыши */
|
||||
opacity: 0; /* Изначально скрыт */
|
||||
transition: opacity 0.2s ease-in-out; /* Плавное появление/исчезновение */
|
||||
}
|
||||
|
||||
.react-flow__node button.node-delete-button:hover {
|
||||
background-color: var(--background-modifier-error-hover);
|
||||
color: var(--text-on-accent);
|
||||
/* Показать toolbar при наведении на узел */
|
||||
.react-flow__node:hover .node-toolbar {
|
||||
pointer-events: auto; /* Разрешить события мыши при наведении */
|
||||
opacity: 1; /* Полностью показать */
|
||||
}
|
||||
|
||||
/* Стили для отдельных кнопок действий внутри узла */
|
||||
.react-flow__node .node-action-button {
|
||||
background-color: var(--background-secondary-alt); /* Фон как у вторичных кнопок */
|
||||
border: 1px solid var(--background-modifier-border); /* Граница */
|
||||
border-radius: 4px; /* Скругленные углы */
|
||||
padding: 4px; /* <-- ИЗМЕНЕНО: Отступ для соответствия новому размеру иконок */
|
||||
height: 16px; /* <-- ИЗМЕНЕНО: Уменьшенная высота */
|
||||
width: 16px; /* <-- ИЗМЕНЕНО: Уменьшенная ширина */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted); /* Цвет текста/иконки */
|
||||
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
||||
box-shadow: var(--shadow-s); /* Небольшая тень */
|
||||
flex-shrink: 0; /* Чтобы кнопки не сжимались */
|
||||
}
|
||||
|
||||
.react-flow__node .node-action-button:hover {
|
||||
background-color: var(--background-secondary); /* <-- ИЗМЕНЕНО: Более плотный фон при наведении */
|
||||
color: var(--text-normal); /* <-- ИЗМЕНЕНО: Цвет иконки при наведении */
|
||||
border-color: var(--background-modifier-border); /* <-- ИЗМЕНЕНО: Цвет границы при наведении */
|
||||
}
|
||||
|
||||
/* Удаляем старые стили, которые конфликтуют или больше не нужны */
|
||||
.react-flow__node button.node-delete-button,
|
||||
.react-flow__node button.node-regenerate-button {
|
||||
position: static; /* Сбрасываем абсолютное позиционирование */
|
||||
bottom: auto; /* Сбрасываем позицию */
|
||||
left: auto; /* Сбрасываем позицию */
|
||||
margin-top: 0; /* Сбрасываем внешний отступ */
|
||||
font-size: 1em; /* Размер шрифта, если используется текст */
|
||||
}
|
||||
|
||||
/* Убеждаемся, что SVG иконка внутри кнопки получает цвет */
|
||||
.react-flow__node .node-action-button svg {
|
||||
width: 8px; /* <-- ИЗМЕНЕНО: Размер иконки уменьшен в 2 раза */
|
||||
height: 8px; /* <-- ИЗМЕНЕНО: Размер иконки уменьшен в 2 раза */
|
||||
stroke: currentColor; /* Использовать текущий цвет текста для иконки */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -323,8 +323,13 @@ export class GraphView extends ItemView {
|
|||
}
|
||||
const data = await response.json();
|
||||
|
||||
// Чтобы не раздувать отправляемые эндпоинтом данные
|
||||
const nodesWithGraphId = data.graph_nodes ?
|
||||
data.graph_nodes.map((node: any) => ({ ...node, graph_id: graphId })) :
|
||||
[];
|
||||
|
||||
this.graphData = {
|
||||
nodes: data.graph_nodes || [],
|
||||
nodes: nodesWithGraphId,
|
||||
edges: data.graph_edges || []
|
||||
};
|
||||
this.currentNode = data.current_node_id;
|
||||
|
|
|
|||
62
styles.css
62
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user