Skip to content

Commit ab1e44b

Browse files
Merge pull request #23 from jaseci-labs/rotate-arrow
feat[feature]: Added element rotating feature and enhanced line/arrow elements capabilities
2 parents 095b103 + 55539c3 commit ab1e44b

File tree

9 files changed

+1056
-115
lines changed

9 files changed

+1056
-115
lines changed

components/Canvas.cl.jac

Lines changed: 211 additions & 24 deletions
Large diffs are not rendered by default.

components/layout/Sidebar.cl.jac

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import from "...constants.colors" { PRESET_COLORS }
22
import from "...constants.fonts" { FONTS, FONT_SIZES }
3-
import from "...constants.tools" { LINE_STYLES }
3+
import from "...constants.tools" { LINE_STYLES, CURVE_STYLES }
44

55
def:pub Sidebar(props: dict) -> JsxElement {
66
currentTool = props.currentTool or "select";
@@ -10,6 +10,7 @@ def:pub Sidebar(props: dict) -> JsxElement {
1010
currentFontSize = props.currentFontSize or 24;
1111
currentFontFamily = props.currentFontFamily or "Virgil";
1212
currentLineStyle = props.currentLineStyle or "solid";
13+
currentCurveStyle = props.currentCurveStyle or "curved";
1314
currentFillColor = props.currentFillColor or "transparent";
1415

1516
selectedElement = props.selectedElement;
@@ -19,13 +20,15 @@ def:pub Sidebar(props: dict) -> JsxElement {
1920
onFontSizeChange = props.onFontSizeChange;
2021
onFontFamilyChange = props.onFontFamilyChange;
2122
onLineStyleChange = props.onLineStyleChange;
23+
onCurveStyleChange = props.onCurveStyleChange;
2224
onFillColorChange = props.onFillColorChange;
2325
onUpdateElement = props.onUpdateElement;
2426

2527
presetColors = PRESET_COLORS();
2628
fontSizes = FONT_SIZES();
2729
fonts = FONTS();
2830
lineStyles = LINE_STYLES();
31+
curveStyles = CURVE_STYLES();
2932

3033
# Determine if we're editing an existing element or setting defaults for new ones
3134
hasSelection = selectedElement and selectedElement.element;
@@ -41,6 +44,7 @@ def:pub Sidebar(props: dict) -> JsxElement {
4144
showFont = False;
4245
showFontSize = False;
4346
showLineStyle = False;
47+
showCurveStyle = False;
4448
showFillColor = False;
4549
showShapeTextColor = False;
4650

@@ -50,6 +54,7 @@ def:pub Sidebar(props: dict) -> JsxElement {
5054
activeFontSize = currentFontSize;
5155
activeFontFamily = currentFontFamily;
5256
activeLineStyle = currentLineStyle;
57+
activeCurveStyle = currentCurveStyle;
5358
activeFillColor = currentFillColor;
5459
activeShapeTextColor = currentColor;
5560

@@ -75,6 +80,12 @@ def:pub Sidebar(props: dict) -> JsxElement {
7580
if elType != "freehand" {
7681
showLineStyle = True;
7782
}
83+
if elType == "line" or elType == "arrow" {
84+
showCurveStyle = True;
85+
selRawCurve = sel.curveStyle or "curved";
86+
# Map legacy "straight" to "sharp" for display
87+
activeCurveStyle = (selRawCurve == "straight") and "sharp" or selRawCurve;
88+
}
7889
activeColor = sel.color;
7990
activeStrokeWidth = sel.strokeWidth;
8091
activeOpacity = sel.opacity or 1.0;
@@ -111,14 +122,17 @@ def:pub Sidebar(props: dict) -> JsxElement {
111122
if currentTool != "freehand" {
112123
showLineStyle = True;
113124
}
125+
if currentTool == "line" or currentTool == "arrow" {
126+
showCurveStyle = True;
127+
}
114128
if currentTool == "rectangle" or currentTool == "circle" or currentTool == "diamond" {
115129
showFillColor = True;
116130
}
117131
}
118132
}
119133

120134
# Hide sidebar entirely when nothing to show
121-
hasContent = showColor or showStroke or showOpacity or showFont or showFontSize or showLineStyle or showFillColor or hasSelection;
135+
hasContent = showColor or showStroke or showOpacity or showFont or showFontSize or showLineStyle or showCurveStyle or showFillColor or hasSelection;
122136
if not hasContent {
123137
return None;
124138
}
@@ -181,6 +195,13 @@ def:pub Sidebar(props: dict) -> JsxElement {
181195
}
182196
}
183197

198+
def handleCurveStyleChange(style: str) -> None {
199+
onCurveStyleChange(style);
200+
if hasSelection {
201+
onUpdateElement({"curveStyle": style});
202+
}
203+
}
204+
184205
def handleFillColorChange(color: str) -> None {
185206
onFillColorChange(color);
186207
if hasSelection {
@@ -319,6 +340,28 @@ def:pub Sidebar(props: dict) -> JsxElement {
319340
</div>
320341
</div> or None}
321342
343+
{showCurveStyle and <div>
344+
<span className="sidebar-label">Arrow Type</span>
345+
<div className="flex items-center gap-1 mt-1.5">
346+
{[
347+
<button
348+
key={cs.id}
349+
onClick={lambda -> None { handleCurveStyleChange(cs.id); }}
350+
title={cs.label}
351+
className={"flex-1 h-9 rounded-lg flex flex-col items-center justify-center gap-0.5 transition-all duration-150 " + (activeCurveStyle == cs.id and "bg-orange-50 ring-1 ring-orange-200" or "bg-gray-50 hover:bg-gray-100")}
352+
>
353+
<svg width="32" height="16" viewBox="0 0 32 16">
354+
{cs.id == "sharp" and <path d="M2 14 L16 4 L30 14" stroke={activeCurveStyle == cs.id and "#ea580c" or "#9ca3af"} strokeWidth="1.8" fill="none" strokeLinecap="round" strokeLinejoin="round" /> or None}
355+
{cs.id == "curved" and <path d="M2 14 Q16 2 30 14" stroke={activeCurveStyle == cs.id and "#ea580c" or "#9ca3af"} strokeWidth="1.8" fill="none" strokeLinecap="round" /> or None}
356+
{cs.id == "elbow" and <path d="M2 14 L2 8 Q2 4 6 4 L30 4" stroke={activeCurveStyle == cs.id and "#ea580c" or "#9ca3af"} strokeWidth="1.8" fill="none" strokeLinecap="round" strokeLinejoin="round" /> or None}
357+
</svg>
358+
<span className={"text-[9px] font-medium " + (activeCurveStyle == cs.id and "text-orange-600" or "text-gray-400")}>{cs.label}</span>
359+
</button>
360+
for cs in curveStyles
361+
]}
362+
</div>
363+
</div> or None}
364+
322365
{showOpacity and <div>
323366
<div className="flex items-center justify-between">
324367
<span className="sidebar-label">Opacity</span>

constants/tools.cl.jac

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ def:pub LINE_STYLES() -> list {
3232
{"id": "dotted", "label": "Dotted", "dash": [3, 6]}
3333
];
3434
}
35+
36+
def:pub CURVE_STYLES() -> list {
37+
return [
38+
{"id": "sharp", "label": "Sharp"},
39+
{"id": "curved", "label": "Curved"},
40+
{"id": "elbow", "label": "Elbow"}
41+
];
42+
}

hooks/useSelection.cl.jac

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,6 @@ def:pub useSelection() -> dict {
9797
stopResizing();
9898
}
9999

100-
def addToSelection(elementInfo: dict) -> None {
101-
newList = selectedElements.slice();
102-
newList.push(elementInfo);
103-
selectedElements = newList;
104-
selectedElement = None;
105-
}
106-
107100
def removeFromSelection(id: str) -> None {
108101
newList = [sel for sel in selectedElements if sel.id != id];
109102
selectedElements = newList;
@@ -214,7 +207,6 @@ def:pub useSelection() -> dict {
214207
"stopDragging": stopDragging,
215208
"stopResizing": stopResizing,
216209
"clearSelection": clearSelection,
217-
"addToSelection": addToSelection,
218210
"removeFromSelection": removeFromSelection,
219211
"startBoxSelection": startBoxSelection,
220212
"updateBoxSelection": updateBoxSelection,

main.jac

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ cl {
1717
has currentFontFamily: str = "Virgil";
1818
has currentOpacity: float = 1.0;
1919
has currentLineStyle: str = "solid";
20+
has currentCurveStyle: str = "curved";
2021
has currentFillColor: str = "transparent";
2122

2223
# Selection state (updated by Canvas via callback)
@@ -58,6 +59,7 @@ cl {
5859
currentFontSize={currentFontSize}
5960
currentFontFamily={currentFontFamily}
6061
currentLineStyle={currentLineStyle}
62+
currentCurveStyle={currentCurveStyle}
6163
currentFillColor={currentFillColor}
6264
selectedElement={selectedElement}
6365
onColorChange={lambda color: any -> None { currentColor = color; }}
@@ -66,6 +68,7 @@ cl {
6668
onFontSizeChange={lambda size: any -> None { currentFontSize = size; }}
6769
onFontFamilyChange={lambda font: any -> None { currentFontFamily = font; }}
6870
onLineStyleChange={lambda style: any -> None { currentLineStyle = style; }}
71+
onCurveStyleChange={lambda style: any -> None { currentCurveStyle = style; }}
6972
onFillColorChange={lambda color: any -> None { currentFillColor = color; }}
7073
onUpdateElement={handleUpdateElement}
7174
/>
@@ -80,6 +83,7 @@ cl {
8083
currentFontFamily={currentFontFamily}
8184
currentOpacity={currentOpacity}
8285
currentLineStyle={currentLineStyle}
86+
currentCurveStyle={currentCurveStyle}
8387
currentFillColor={currentFillColor}
8488
clearCanvasRef={clearCanvasRef}
8589
addElementRef={addElementRef}

0 commit comments

Comments
 (0)