Refactoring Properties component
This commit is contained in:
@@ -5,11 +5,19 @@
|
|||||||
using `_id={WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}{get_unique_id()}`
|
using `_id={WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}{get_unique_id()}`
|
||||||
|
|
||||||
| Name | value |
|
| Name | value |
|
||||||
|-----------------|--------------------|
|
|----------------------------------|--------------------------------|
|
||||||
| Canvas | `c_{self._id}` |
|
| Canvas | `c_{self._id}` |
|
||||||
| Designer | `d_{self._id}` |
|
| Designer | `d_{self._id}` |
|
||||||
| Error Message | `err_{self._id}` |
|
| Error Message | `err_{self._id}` |
|
||||||
| Properties | `p_{self._id}` |
|
| Properties | `p_{self._id}` |
|
||||||
|
| Properties Input Section | `pi_{self._id}` |
|
||||||
|
| Properties Output Section | `po_{self._id}` |
|
||||||
|
| Properties Properties Section | `pp_{self._id}` |
|
||||||
|
| Properties Properties drag top | `ppt_{self._id}` |
|
||||||
|
| Properties Properties drag left | `ppl_{self._id}` |
|
||||||
|
| Properties Properties drag right | `ppr_{self._id}` |
|
||||||
| Spliter | `s_{self._id}` |
|
| Spliter | `s_{self._id}` |
|
||||||
| Top element | `t_{self._id}` |
|
| Top element | `t_{self._id}` |
|
||||||
|
| Form for properties | `f_{self._id}_{component_id}` |
|
||||||
|
| Form for output properties | `fo_{self._id}_{component_id}` |
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,14 @@ def post(session, _id: str, designer_height: int):
|
|||||||
return instance.set_designer_height(designer_height)
|
return instance.set_designer_height(designer_height)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.UpdatePropertiesLayout)
|
||||||
|
def post(session, _id: str, input_width: int, properties_width: int, output_width: int):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.UpdatePropertiesLayout} with args {debug_session(session)}, {_id=}, {input_width=}, {properties_width=}, {output_width=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
return instance.update_properties_layout(input_width, properties_width, output_width)
|
||||||
|
|
||||||
|
|
||||||
@rt(Routes.SelectComponent)
|
@rt(Routes.SelectComponent)
|
||||||
def post(session, _id: str, component_id: str):
|
def post(session, _id: str, component_id: str):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
function bindWorkflowDesigner(elementId) {
|
function bindWorkflowDesigner(elementId) {
|
||||||
bindWorkflowDesignerToolbox(elementId)
|
bindWorkflowDesignerToolbox(elementId)
|
||||||
bindWorkflowDesignerSplitter(elementId)
|
bindWorkflowDesignerSplitter(elementId)
|
||||||
|
bindWorkflowProperties(elementId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindWorkflowDesignerToolbox(elementId) {
|
function bindWorkflowDesignerToolbox(elementId) {
|
||||||
@@ -612,3 +613,153 @@ function bindWorkflowDesignerSplitter(elementId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bindWorkflowProperties(elementId) {
|
||||||
|
let isDragging = false;
|
||||||
|
let isResizing = false;
|
||||||
|
let startX = 0;
|
||||||
|
let startWidths = {};
|
||||||
|
let resizeType = '';
|
||||||
|
|
||||||
|
console.debug("Binding Properties component for "+ elementId)
|
||||||
|
properties_component = document.getElementById(`p_${elementId}`);
|
||||||
|
if (properties_component == null) {
|
||||||
|
console.error(`'Component ' p_${elementId}' is not found !' `)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const totalWidth = properties_component.getBoundingClientRect().width
|
||||||
|
console.debug("totalWidth", totalWidth)
|
||||||
|
|
||||||
|
const minPropertiesWidth = Math.floor(totalWidth * 0.2); // 20% minimum
|
||||||
|
|
||||||
|
const inputSection = document.getElementById(`pi_${elementId}`);
|
||||||
|
const propertiesSection = document.getElementById(`pp_${elementId}`);
|
||||||
|
const outputSection = document.getElementById(`po_${elementId}`);
|
||||||
|
const dragHandle = document.getElementById(`ppt_${elementId}`);
|
||||||
|
const leftHandle = document.getElementById(`ppl_${elementId}`);
|
||||||
|
const rightHandle = document.getElementById(`ppr_${elementId}`);
|
||||||
|
|
||||||
|
// Drag and drop for moving properties section
|
||||||
|
dragHandle.addEventListener('mousedown', (e) => {
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startWidths = {
|
||||||
|
input: parseInt(inputSection.style.width),
|
||||||
|
properties: parseInt(propertiesSection.style.width),
|
||||||
|
output: parseInt(outputSection.style.width)
|
||||||
|
};
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Left resize handle
|
||||||
|
leftHandle.addEventListener('mousedown', (e) => {
|
||||||
|
isResizing = true;
|
||||||
|
resizeType = 'left';
|
||||||
|
startX = e.clientX;
|
||||||
|
startWidths = {
|
||||||
|
input: parseInt(inputSection.style.width),
|
||||||
|
properties: parseInt(propertiesSection.style.width),
|
||||||
|
output: parseInt(outputSection.style.width)
|
||||||
|
};
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Right resize handle
|
||||||
|
rightHandle.addEventListener('mousedown', (e) => {
|
||||||
|
isResizing = true;
|
||||||
|
resizeType = 'right';
|
||||||
|
startX = e.clientX;
|
||||||
|
startWidths = {
|
||||||
|
input: parseInt(inputSection.style.width),
|
||||||
|
properties: parseInt(propertiesSection.style.width),
|
||||||
|
output: parseInt(outputSection.style.width)
|
||||||
|
};
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mouse move
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
if (isDragging) {
|
||||||
|
const deltaX = e.clientX - startX;
|
||||||
|
let newInputWidth = startWidths.input + deltaX;
|
||||||
|
let newOutputWidth = startWidths.output - deltaX;
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
if (newInputWidth < 0) {
|
||||||
|
newInputWidth = 0;
|
||||||
|
newOutputWidth = totalWidth - startWidths.properties;
|
||||||
|
}
|
||||||
|
if (newOutputWidth < 0) {
|
||||||
|
newOutputWidth = 0;
|
||||||
|
newInputWidth = totalWidth - startWidths.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputSection.style.width = newInputWidth + 'px';
|
||||||
|
outputSection.style.width = newOutputWidth + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isResizing) {
|
||||||
|
const deltaX = e.clientX - startX;
|
||||||
|
let newInputWidth = startWidths.input;
|
||||||
|
let newPropertiesWidth = startWidths.properties;
|
||||||
|
let newOutputWidth = startWidths.output;
|
||||||
|
|
||||||
|
if (resizeType === 'left') {
|
||||||
|
newInputWidth = startWidths.input + deltaX;
|
||||||
|
newPropertiesWidth = startWidths.properties - deltaX;
|
||||||
|
|
||||||
|
if (newInputWidth < 0) {
|
||||||
|
newInputWidth = 0;
|
||||||
|
newPropertiesWidth = startWidths.input + startWidths.properties;
|
||||||
|
}
|
||||||
|
if (newPropertiesWidth < minPropertiesWidth) {
|
||||||
|
newPropertiesWidth = minPropertiesWidth;
|
||||||
|
newInputWidth = totalWidth - minPropertiesWidth - startWidths.output;
|
||||||
|
}
|
||||||
|
} else if (resizeType === 'right') {
|
||||||
|
newPropertiesWidth = startWidths.properties + deltaX;
|
||||||
|
newOutputWidth = startWidths.output - deltaX;
|
||||||
|
|
||||||
|
if (newOutputWidth < 0) {
|
||||||
|
newOutputWidth = 0;
|
||||||
|
newPropertiesWidth = startWidths.properties + startWidths.output;
|
||||||
|
}
|
||||||
|
if (newPropertiesWidth < minPropertiesWidth) {
|
||||||
|
newPropertiesWidth = minPropertiesWidth;
|
||||||
|
newOutputWidth = totalWidth - startWidths.input - minPropertiesWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputSection.style.width = newInputWidth + 'px';
|
||||||
|
propertiesSection.style.width = newPropertiesWidth + 'px';
|
||||||
|
outputSection.style.width = newOutputWidth + 'px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mouse up
|
||||||
|
document.addEventListener('mouseup', () => {
|
||||||
|
if (isDragging || isResizing) {
|
||||||
|
// Send HTMX request with new dimensions
|
||||||
|
const currentWidths = {
|
||||||
|
input_width: parseInt(inputSection.style.width),
|
||||||
|
properties_width: parseInt(propertiesSection.style.width),
|
||||||
|
output_width: parseInt(outputSection.style.width)
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
htmx.ajax('POST', '/workflows/update-properties-layout', {
|
||||||
|
target: `#${elementId}`,
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
swap: "outerHTML",
|
||||||
|
values: { _id: elementId, ...currentWidths }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('HTMX request failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
isResizing = false;
|
||||||
|
resizeType = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -9,10 +9,11 @@ from components.BaseComponent import BaseComponent
|
|||||||
from components.undo_redo.constants import UndoRedoAttrs
|
from components.undo_redo.constants import UndoRedoAttrs
|
||||||
from components.workflows.assets.icons import icon_play, icon_pause, icon_stop, icon_refresh
|
from components.workflows.assets.icons import icon_play, icon_pause, icon_stop, icon_refresh
|
||||||
from components.workflows.commands import WorkflowDesignerCommandManager
|
from components.workflows.commands import WorkflowDesignerCommandManager
|
||||||
|
from components.workflows.components.WorkflowDesignerProperties import WorkflowDesignerProperties
|
||||||
from components.workflows.components.WorkflowPlayer import WorkflowPlayer
|
from components.workflows.components.WorkflowPlayer import WorkflowPlayer
|
||||||
from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID, ProcessorTypes
|
from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID, ProcessorTypes
|
||||||
from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \
|
from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \
|
||||||
Connection, WorkflowsDesignerDbManager, ComponentState
|
Connection, WorkflowsDesignerDbManager, ComponentState, WorkflowsDesignerState
|
||||||
from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons, mk_icon
|
from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons, mk_icon
|
||||||
from core.instance_manager import InstanceManager
|
from core.instance_manager import InstanceManager
|
||||||
from core.jira import JiraRequestTypes, DEFAULT_SEARCH_FIELDS
|
from core.jira import JiraRequestTypes, DEFAULT_SEARCH_FIELDS
|
||||||
@@ -65,9 +66,10 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
self._designer_settings = designer_settings
|
self._designer_settings = designer_settings
|
||||||
self._db = WorkflowsDesignerDbManager(session, settings_manager)
|
self._db = WorkflowsDesignerDbManager(session, settings_manager)
|
||||||
self._undo_redo = ComponentsInstancesHelper.get_undo_redo(session)
|
self._undo_redo = ComponentsInstancesHelper.get_undo_redo(session)
|
||||||
self._state = self._db.load_state(key)
|
self._state: WorkflowsDesignerState = self._db.load_state(key)
|
||||||
self._boundaries = boundaries
|
self._boundaries = boundaries
|
||||||
self.commands = WorkflowDesignerCommandManager(self)
|
self.commands = WorkflowDesignerCommandManager(self)
|
||||||
|
self.properties = WorkflowDesignerProperties(self._session, f"{self._id}", self)
|
||||||
|
|
||||||
workflow_name = self._designer_settings.workflow_name
|
workflow_name = self._designer_settings.workflow_name
|
||||||
self._player = InstanceManager.get(self._session,
|
self._player = InstanceManager.get(self._session,
|
||||||
@@ -83,7 +85,10 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
def set_boundaries(self, boundaries: dict):
|
def set_boundaries(self, boundaries: dict):
|
||||||
self._boundaries = boundaries
|
self._boundaries = boundaries
|
||||||
|
|
||||||
def get_state(self):
|
def get_boundaries(self):
|
||||||
|
return self._boundaries
|
||||||
|
|
||||||
|
def get_state(self) -> WorkflowsDesignerState:
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def get_db(self):
|
def get_db(self):
|
||||||
@@ -106,6 +111,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
|
|
||||||
def refresh_state(self):
|
def refresh_state(self):
|
||||||
self._state = self._db.load_state(self._key)
|
self._state = self._db.load_state(self._key)
|
||||||
|
self.properties.update_layout()
|
||||||
return self.__ft__(oob=True)
|
return self.__ft__(oob=True)
|
||||||
|
|
||||||
def add_component(self, component_type, x, y):
|
def add_component(self, component_type, x, y):
|
||||||
@@ -140,7 +146,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
undo_redo_attrs = UndoRedoAttrs(f"Move Component '{component.title}'", on_undo=self.refresh_state)
|
undo_redo_attrs = UndoRedoAttrs(f"Move Component '{component.title}'", on_undo=self.refresh_state)
|
||||||
self._db.save_state(self._key, self._state, undo_redo_attrs) # update db
|
self._db.save_state(self._key, self._state, undo_redo_attrs) # update db
|
||||||
|
|
||||||
return self.refresh_designer(), self.refresh_properties(True), self._undo_redo.refresh()
|
return self.refresh_designer(), self.properties.refresh(), self._undo_redo.refresh()
|
||||||
|
|
||||||
def delete_component(self, component_id):
|
def delete_component(self, component_id):
|
||||||
# Remove component
|
# Remove component
|
||||||
@@ -192,6 +198,16 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
self._db.save_state(self._key, self._state, undo_redo_attrs)
|
self._db.save_state(self._key, self._state, undo_redo_attrs)
|
||||||
return self.__ft__(), self._undo_redo.refresh() # refresh the whole component
|
return self.__ft__(), self._undo_redo.refresh() # refresh the whole component
|
||||||
|
|
||||||
|
def update_properties_layout(self, input_width, properties_width, output_width):
|
||||||
|
self._state.properties_input_width = input_width
|
||||||
|
self._state.properties_properties_width = properties_width
|
||||||
|
self._state.properties_output_width = output_width
|
||||||
|
self.properties.update_layout()
|
||||||
|
|
||||||
|
undo_redo_attrs = UndoRedoAttrs(f"Resize Properties", on_undo=lambda: self.refresh_state())
|
||||||
|
self._db.save_state(self._key, self._state, undo_redo_attrs)
|
||||||
|
return self.__ft__(), self._undo_redo.refresh() # refresh the whole component
|
||||||
|
|
||||||
def select_component(self, component_id):
|
def select_component(self, component_id):
|
||||||
if component_id in self._state.components:
|
if component_id in self._state.components:
|
||||||
self._state.selected_component_id = component_id
|
self._state.selected_component_id = component_id
|
||||||
@@ -273,7 +289,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
def __ft__(self, oob=False):
|
def __ft__(self, oob=False):
|
||||||
return Div(
|
return Div(
|
||||||
H1(f"{self._designer_settings.workflow_name}", cls="text-xl font-bold"),
|
H1(f"{self._designer_settings.workflow_name}", cls="text-xl font-bold"),
|
||||||
P("Drag components from the toolbox to the canvas to create your workflow.", cls="text-sm mb-6"),
|
# P("Drag components from the toolbox to the canvas to create your workflow.", cls="text-sm"),
|
||||||
Div(
|
Div(
|
||||||
self._mk_media(),
|
self._mk_media(),
|
||||||
# self._mk_refresh_button(),
|
# self._mk_refresh_button(),
|
||||||
@@ -365,6 +381,11 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _mk_elements(self):
|
def _mk_elements(self):
|
||||||
|
if len(self._state.components) == 0:
|
||||||
|
return Div("Drag components from the toolbox to the canvas to create your workflow.",
|
||||||
|
cls="flex items-center justify-center h-full w-full"
|
||||||
|
)
|
||||||
|
|
||||||
return Div(
|
return Div(
|
||||||
# Render connections
|
# Render connections
|
||||||
*[NotStr(self._mk_connection_svg(conn)) for conn in self._state.connections],
|
*[NotStr(self._mk_connection_svg(conn)) for conn in self._state.connections],
|
||||||
@@ -436,6 +457,17 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
|
|
||||||
return Div('Not defined yet !')
|
return Div('Not defined yet !')
|
||||||
|
|
||||||
|
def _mk_properties_output(self, component):
|
||||||
|
return Div(
|
||||||
|
"Output name",
|
||||||
|
Input(type="input",
|
||||||
|
name="output_name",
|
||||||
|
placeholder="data",
|
||||||
|
value=component.properties.get("output_name", None),
|
||||||
|
cls="input w-xs"),
|
||||||
|
cls="join"
|
||||||
|
)
|
||||||
|
|
||||||
def _mk_properties_details(self, component_id, allow_component_selection=False):
|
def _mk_properties_details(self, component_id, allow_component_selection=False):
|
||||||
def _mk_header():
|
def _mk_header():
|
||||||
return Div(
|
return Div(
|
||||||
@@ -469,11 +501,32 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
return Div(
|
return Div(
|
||||||
Form(
|
Form(
|
||||||
_mk_header(),
|
_mk_header(),
|
||||||
|
Div(
|
||||||
|
Input(type="radio", name=f"pt_{self._id}", cls="tab", aria_label="Properties", checked="checked"),
|
||||||
|
Div(
|
||||||
_mk_select(),
|
_mk_select(),
|
||||||
self._mk_processor_properties(component, selected_processor_name),
|
self._mk_processor_properties(component, selected_processor_name),
|
||||||
|
cls="tab-content"
|
||||||
|
),
|
||||||
|
|
||||||
|
Input(type="radio", name=f"pt_{self._id}", cls="tab", aria_label="Inputs"),
|
||||||
|
Div(
|
||||||
|
"Inputs",
|
||||||
|
cls="tab-content"
|
||||||
|
),
|
||||||
|
|
||||||
|
Input(type="radio", name=f"pt_{self._id}", cls="tab", aria_label="Output"),
|
||||||
|
Div(
|
||||||
|
self._mk_properties_output(component),
|
||||||
|
cls="tab-content"
|
||||||
|
),
|
||||||
|
|
||||||
|
cls="tabs tabs-border"
|
||||||
|
),
|
||||||
mk_dialog_buttons(cls="mt-4",
|
mk_dialog_buttons(cls="mt-4",
|
||||||
on_ok=self.commands.save_properties(component_id),
|
on_ok=self.commands.save_properties(component_id),
|
||||||
on_cancel=self.commands.cancel_properties(component_id)),
|
on_cancel=self.commands.cancel_properties(component_id)),
|
||||||
|
|
||||||
cls="font-mono text-sm",
|
cls="font-mono text-sm",
|
||||||
id=f"f_{self._id}_{component_id}",
|
id=f"f_{self._id}_{component_id}",
|
||||||
),
|
),
|
||||||
@@ -481,13 +534,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _mk_properties(self, oob=False):
|
def _mk_properties(self, oob=False):
|
||||||
return Div(
|
return self.properties
|
||||||
self._mk_properties_details(self._state.selected_component_id),
|
|
||||||
cls="p-2 bg-base-100 rounded-lg border",
|
|
||||||
style=f"height:{self._get_properties_height()}px;",
|
|
||||||
hx_swap_oob='true' if oob else None,
|
|
||||||
id=f"p_{self._id}",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _mk_jira_processor_details(self, component):
|
def _mk_jira_processor_details(self, component):
|
||||||
def _mk_option(name):
|
def _mk_option(name):
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
from fasthtml.common import *
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from components.BaseComponent import BaseComponent
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DesignerLayout:
|
||||||
|
input_width: int
|
||||||
|
properties_width: int
|
||||||
|
output_width: int
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowDesignerProperties(BaseComponent):
|
||||||
|
def __init__(self, session, instance_id, owner):
|
||||||
|
super().__init__(session, instance_id)
|
||||||
|
self._owner = owner
|
||||||
|
self._boundaries = self._owner.get_boundaries()
|
||||||
|
self._commands = self._owner.commands
|
||||||
|
self.layout = self.compute_layout()
|
||||||
|
|
||||||
|
def update_layout(self):
|
||||||
|
self.layout = self.compute_layout()
|
||||||
|
|
||||||
|
def compute_layout(self) -> DesignerLayout:
|
||||||
|
if self._owner.get_state().properties_input_width is None:
|
||||||
|
input_width = self._boundaries["width"] // 3
|
||||||
|
properties_width = self._boundaries["width"] // 3
|
||||||
|
output_width = self._boundaries["width"] - input_width - properties_width - 10
|
||||||
|
else:
|
||||||
|
input_width = self._owner.get_state().properties_input_width
|
||||||
|
properties_width = self._owner.get_state().properties_properties_width
|
||||||
|
output_width = self._owner.get_state().properties_output_width
|
||||||
|
|
||||||
|
return DesignerLayout(
|
||||||
|
input_width=input_width,
|
||||||
|
properties_width=properties_width,
|
||||||
|
output_width=output_width
|
||||||
|
)
|
||||||
|
|
||||||
|
def refresh(self, oob=True):
|
||||||
|
return self.__ft__(oob=oob)
|
||||||
|
|
||||||
|
def _mk_input(self):
|
||||||
|
return Div(
|
||||||
|
"Input",
|
||||||
|
id=f"pi_{self._id}",
|
||||||
|
style=f"width: {self.layout.input_width}px; height: {self._boundaries['height']}px; background-color: #f0f0f0; border: 1px solid #ccc; display: inline-block; vertical-align: top; padding: 10px; box-sizing: border-box;"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_output(self):
|
||||||
|
return Div(
|
||||||
|
"Output",
|
||||||
|
id=f"po_{self._id}",
|
||||||
|
style=f"width: {self.layout.output_width}px; height: {self._boundaries['height']}px; background-color: #f0f0f0; border: 1px solid #ccc; display: inline-block; vertical-align: top; padding: 10px; box-sizing: border-box;"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_properties(self):
|
||||||
|
return Div(
|
||||||
|
# Drag handle (20px height)
|
||||||
|
Div(
|
||||||
|
"Properties",
|
||||||
|
id=f"ppt_{self._id}",
|
||||||
|
style="height: 20px; background-color: #ddd; cursor: move; text-align: center; line-height: 20px; font-weight: bold; font-size: 12px; border-bottom: 1px solid #bbb;"
|
||||||
|
),
|
||||||
|
|
||||||
|
# Properties content
|
||||||
|
Div(
|
||||||
|
Input(placeholder="output name", style="width: 100%; margin-bottom: 10px;"),
|
||||||
|
Select(
|
||||||
|
Option("Repository", value="repository"),
|
||||||
|
Option("Jira", value="jira"),
|
||||||
|
style="width: 100%; margin-bottom: 10px;"
|
||||||
|
),
|
||||||
|
Div(
|
||||||
|
Button("Save", **self._commands.on_save(), style="margin-right: 5px; padding: 5px 10px;"),
|
||||||
|
Button("Cancel", **self._commands.on_cancel(), style="padding: 5px 10px;"),
|
||||||
|
style="text-align: center;"
|
||||||
|
),
|
||||||
|
style=f"padding: 10px; height: {self._boundaries['height'] - 20}px; box-sizing: border-box;"
|
||||||
|
),
|
||||||
|
|
||||||
|
# Left resize handle
|
||||||
|
Div(
|
||||||
|
id=f"ppl_{self._id}",
|
||||||
|
style="position: absolute; left: 0; top: 0; width: 5px; height: 100%; cursor: ew-resize; background-color: transparent;"
|
||||||
|
),
|
||||||
|
|
||||||
|
# Right resize handle
|
||||||
|
Div(
|
||||||
|
id=f"ppr_{self._id}",
|
||||||
|
style="position: absolute; right: 0; top: 0; width: 5px; height: 100%; cursor: ew-resize; background-color: transparent;"
|
||||||
|
),
|
||||||
|
|
||||||
|
id=f"pp_{self._id}",
|
||||||
|
style=f"width: {self.layout.properties_width}px; height: {self._boundaries['height']}px; background-color: #f8f8f8; border: 1px solid #ccc; display: inline-block; vertical-align: top; position: relative; box-sizing: border-box;"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_layout(self):
|
||||||
|
return Div(
|
||||||
|
self._mk_input(),
|
||||||
|
self._mk_properties(),
|
||||||
|
self._mk_output(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __ft__(self, oob=False):
|
||||||
|
# return self.render()
|
||||||
|
return Div(
|
||||||
|
self._mk_layout(),
|
||||||
|
style=f"height: {self._boundaries['height']}px; border: 2px solid #333; position: relative; font-family: Arial, sans-serif;",
|
||||||
|
id=f"p_{self._id}",
|
||||||
|
hx_swap_oob='true' if oob else None,
|
||||||
|
)
|
||||||
@@ -25,6 +25,7 @@ class Routes:
|
|||||||
AddConnection = "/add-connection"
|
AddConnection = "/add-connection"
|
||||||
DeleteConnection = "/delete-connection"
|
DeleteConnection = "/delete-connection"
|
||||||
ResizeDesigner = "/resize-designer"
|
ResizeDesigner = "/resize-designer"
|
||||||
|
UpdatePropertiesLayout = "/update-properties-layout"
|
||||||
SaveProperties = "/save-properties"
|
SaveProperties = "/save-properties"
|
||||||
CancelProperties = "/cancel-properties"
|
CancelProperties = "/cancel-properties"
|
||||||
SelectProcessor = "/select-processor"
|
SelectProcessor = "/select-processor"
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ class WorkflowsDesignerState:
|
|||||||
connections: list[Connection] = field(default_factory=list)
|
connections: list[Connection] = field(default_factory=list)
|
||||||
component_counter: int = 0
|
component_counter: int = 0
|
||||||
designer_height: int = 230
|
designer_height: int = 230
|
||||||
|
properties_input_width: int = None
|
||||||
|
properties_properties_width : int = None
|
||||||
|
properties_output_width: int = None
|
||||||
selected_component_id: str | None = None
|
selected_component_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ def test_i_can_render_no_component(designer):
|
|||||||
actual = designer.__ft__()
|
actual = designer.__ft__()
|
||||||
expected = Div(
|
expected = Div(
|
||||||
H1("Workflow Name"),
|
H1("Workflow Name"),
|
||||||
P("Drag components from the toolbox to the canvas to create your workflow."),
|
# P("Drag components from the toolbox to the canvas to create your workflow."),
|
||||||
Div(id=f"t_{designer.get_id()}"), # media + error message
|
Div(id=f"t_{designer.get_id()}"), # media + error message
|
||||||
Div(id=f"d_{designer.get_id()}"), # designer container
|
Div(id=f"d_{designer.get_id()}"), # designer container
|
||||||
Div(cls="wkf-splitter"),
|
Div(cls="wkf-splitter"),
|
||||||
|
|||||||
Reference in New Issue
Block a user