Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

186143109 drawing toolbar in new toolbar system #2309

Merged
merged 42 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
06aad85
set up new toolbar in drawing tile
bacalj May 20, 2024
1c15e51
Merge branch 'master' into 186143109-drawing-toolbar-update
bacalj May 20, 2024
0ab0130
individually implemented basic actions
bacalj May 21, 2024
7c67be6
breaking up the button definitions for now
bacalj May 21, 2024
4d56cda
put button definitons in folder, set up openPallette state
bacalj May 21, 2024
d3bc258
wip:getting pallettes working
bacalj May 22, 2024
618e808
delete existing drawing-toolbar file
bacalj May 22, 2024
adff048
base implementation of all buttons
bacalj May 21, 2024
26e927f
Merge branch 'local-try-dynamic-svg-like-vector-palette' into 1861431…
bacalj May 23, 2024
76d3c4d
cleanup merge
bacalj May 23, 2024
e4d2159
allow for up to four rows of stamps
bacalj May 23, 2024
3b73209
actually we can simplifty and not calculate rows at all
bacalj May 23, 2024
fb4a0b9
remove absolute positioning
bacalj May 24, 2024
50e1a90
style simplification, triangle toggle
bacalj May 24, 2024
e44e2c3
example of removing dead button definitions (and all specified button…
bacalj May 24, 2024
bcb27fb
straggling cleanup
bacalj May 24, 2024
cb295dd
more unused buttons clean out, prepare for touchHold, making sure pal…
bacalj May 24, 2024
e9b024b
do not propagate triangle clicks, close color palettes on select stam…
bacalj May 24, 2024
31d052c
Merge branch 'master' into 186143109-drawing-toolbar-update
bacalj May 24, 2024
48266e0
hack jest test so we can deploy
bacalj May 24, 2024
716a72a
less of a hack, but probably not ideal
bacalj May 24, 2024
332c2c3
revert useTouchHold use for now
bacalj May 24, 2024
90df55b
enabled and disabled for action buttons
bacalj May 24, 2024
a1951dd
more cleaning out of dead code
bacalj May 24, 2024
82d5277
use-touch-hold support
bacalj May 25, 2024
2b409a7
cleanup
bacalj May 25, 2024
be9cfd9
handle shared var buttons
bacalj May 26, 2024
2ba7f92
remove unused stamp button, unused svg button component
bacalj May 26, 2024
9564b8d
no need to register button components in shared var reg
bacalj May 26, 2024
fdf2e99
cleanup, file names
bacalj May 26, 2024
4c239fa
use isLightColorRequiringContrastOffset on stroke and fill indicators
bacalj May 26, 2024
e86d37a
implement upload button
bacalj May 26, 2024
7f6b051
settings on vector buttons
bacalj May 26, 2024
df36497
pass correct svg setting strings to avoid warnings
bacalj May 26, 2024
8c577fb
cypress test adjustments, add class to general upload button
bacalj May 27, 2024
55e7bfd
cy test adjustments to match new toolbar show behavior
bacalj May 28, 2024
6fd09ca
remove accidentally committed cy download
bacalj May 28, 2024
7e86707
review fixes
bacalj May 28, 2024
d8ca2a6
restore style import
bacalj May 28, 2024
6ea8e41
pass handleClick to onTouchHold
bacalj May 28, 2024
8036f70
Merge branch 'master' into 186143109-drawing-toolbar-update
bacalj May 28, 2024
ad45db2
update unit configuration documentation
bacalj May 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/functional/tile_tests/arrow_annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ context('Arrow Annotations (Sparrows)', function () {

cy.log("Can create sparrows across two tiles");
clueCanvas.addTile("drawing");
drawToolTile.getDrawToolVector().eq(1).click();
drawToolTile.getDrawToolVector().eq(0).click();
drawToolTile.getDrawTile().eq(1)
.trigger("mousedown", 150, 50)
.trigger("mousemove", 100, 150)
Expand Down
1 change: 1 addition & 0 deletions cypress/e2e/functional/tile_tests/diagram_tool_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ context('Diagram Tool Tile', function () {

// Draw tile and toolbar buttons render
drawTile.getDrawTile().should("exist");
drawTile.getDrawTile().click();
drawTile.getDrawToolNewVariable().should("exist").should("be.enabled");
drawTile.getDrawToolEditVariable().should("exist").should("be.disabled");
drawTile.getDrawToolInsertVariable().should("exist").should("be.disabled");
Expand Down
5 changes: 3 additions & 2 deletions cypress/e2e/functional/tile_tests/drawing_tool_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ context('Draw Tool Tile', function () {
cy.log("change line to arrow");
drawToolTile.getVectorDrawing().children().its("length").should("eq", 1); // Only a line, no arrowheads yet.
drawToolTile.getDrawToolVectorSubmenu().click();
cy.get(".toolbar-palette.vectors .drawing-tool-buttons").should("be.visible");
cy.get(".toolbar-palette.vectors .drawing-tool-buttons div:nth-child(3) button").click();
cy.get(".toolbar-palette.vectors .palette-buttons").should("be.visible");
cy.get(".toolbar-palette.vectors .palette-buttons div:nth-child(3) button").click();
drawToolTile.getVectorDrawing().children().its("length").should("eq", 3); // Now three items in group...
drawToolTile.getVectorDrawing().find("polygon").its("length").should("eq", 2); // including two arrowheads.
// selecting from this submenu activates the vector tool, which de-selects the object.
Expand Down Expand Up @@ -577,6 +577,7 @@ context('Draw Tool Tile', function () {
// Once that's fixed, we should drag that image into the drawing tile.

cy.log("uploads images");
drawToolTile.getDrawTile().click();
const imageFilePath2 = 'image.png';
cy.uploadFile(drawToolTile.getDrawToolUploadImage(), imageFilePath2, 'image/png');
cy.wait(2000);
Expand Down
38 changes: 19 additions & 19 deletions cypress/support/elements/tile/DrawToolTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,61 @@ class DrawToolTile{
return cy.get('.primary-workspace .drawing-tool .object-list.open button.close');
}
getDrawToolSelect(){
return cy.get('.primary-workspace .drawing-tool-button.button-select');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.select');
}
getDrawToolFreehand(){
return cy.get('.primary-workspace .drawing-tool-button.button-line');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.line');
}
getDrawToolVector(){
return cy.get('.primary-workspace .drawing-tool-button.button-vector');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.vector');
}
getDrawToolVectorSubmenu(){
return cy.get('.primary-workspace .drawing-tool-button.button-vector .expand-collapse');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.vector .expand-collapse');
}
getDrawToolRectangle(){
return cy.get('.primary-workspace .drawing-tool-button.button-rectangle');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.rectangle');
}
getDrawToolEllipse(){
return cy.get('.primary-workspace .drawing-tool-button.button-ellipse');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.ellipse');
}
getDrawToolStamp(){
return cy.get('.primary-workspace .drawing-tool-button.button-stamp');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.stamp');
}
getDrawToolStampExpand(){
return cy.get('.primary-workspace .drawing-tool-button.button-stamp .expand-collapse');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.stamp .expand-collapse');
}
getDrawToolText(){
return cy.get('.primary-workspace .drawing-tool-button.button-text');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.text');
}
getDrawToolStrokeColor(){
return cy.get('.primary-workspace .drawing-tool-button.button-stroke-color');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.stroke-color');
}
getDrawToolFillColor(){
return cy.get('.primary-workspace .drawing-tool-button.button-fill-color');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.fill-color');
}
getDrawToolVariable(){
return cy.get('.primary-workspace .drawing-tool-button.button-variable');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.variable');
}
getDrawToolNewVariable(){
return cy.get('.primary-workspace .drawing-tool-button.button-new-variable');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.new-variable');
}
getDrawToolInsertVariable(){
return cy.get('.primary-workspace .drawing-tool-button.button-insert-variable');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.insert-variable');
}
getDrawToolEditVariable(){
return cy.get('.primary-workspace .drawing-tool-button.button-edit-variable');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.edit-variable');
}
getDrawToolUploadImage(){
return cy.get('.primary-workspace .drawing-tool-button.image-upload input');
return cy.get('.primary-workspace .drawing-toolbar .upload-button-input');
}
getDrawToolGroup(){
return cy.get('.primary-workspace .drawing-tool-button.button-group');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.group');
}
getDrawToolUngroup(){
return cy.get('.primary-workspace .drawing-tool-button.button-ungroup');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.ungroup');
}
getDrawToolDelete(){
return cy.get('.primary-workspace .drawing-tool-button.button-delete');
return cy.get('.primary-workspace .drawing-toolbar .toolbar-button.delete');
}

getFreehandDrawing(){
Expand Down
18 changes: 18 additions & 0 deletions src/clue/app-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@
"delete"
]
},
"drawing": {
"tools": [
"select",
"line",
"vector",
"rectangle",
"ellipse",
"stamp",
"stroke-color",
"fill-color",
"text",
"upload",
"group",
"ungroup",
"duplicate",
"delete"
]
},
"graph": {
"emptyPlotIsNumeric": true,
"scalePlotOnValueChange": true,
Expand Down
56 changes: 38 additions & 18 deletions src/components/toolbar/tile-toolbar-button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { PropsWithChildren } from "react";
import { useTouchHold } from "../../hooks/use-touch-hold";
import classNames from "classnames";
import { useTooltipOptions } from "../../hooks/use-tooltip-options";
import { Tooltip } from "react-tippy";
Expand All @@ -17,30 +18,49 @@ export interface TileToolbarButtonProps {
title: string; // user-visible name, used in the tooltip
keyHint?: string, // If set, displayed to the user as the hotkey equivalent
onClick: (e: React.MouseEvent) => void; // Action when clicked
onTouchHold?: (e: React.MouseEvent) => void; // Action when long-pressed
selected?: boolean; // puts button in 'active' state if defined and true
disabled?: boolean; // makes button grey and unclickable if defined and true
extraContent?: JSX.Element; // Additional element added after the button.
}

/**
* A generic, simple button that can go on a tile toolbar.
*/
export const TileToolbarButton =
function({name, title, keyHint, onClick, selected, disabled, children}: PropsWithChildren<TileToolbarButtonProps>) {
export const TileToolbarButton = function ({
name,
title,
keyHint,
onClick,
onTouchHold,
selected,
disabled,
children,
extraContent
}: PropsWithChildren<TileToolbarButtonProps>) {
const tipOptions = useTooltipOptions();
const tooltip = formatTooltip(title, keyHint);

const tipOptions = useTooltipOptions();
const { onTouchStart, onTouchEnd, onMouseDown, onMouseUp, onClick: handleOnClick } = useTouchHold(
() => onTouchHold?.({} as React.MouseEvent),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like either onTouchHold should be changed to not expect an argument, or an actual event argument should be passed.

onClick
);

const tooltip = formatTooltip(title, keyHint);
return (
<Tooltip title={tooltip} {...tipOptions} >
<button
className={classNames('toolbar-button', name, { selected, disabled })}
// TODO: confer with Scott about aria-disabled vs. disabled
disabled={disabled}
onClick={onClick}
onMouseDown={(e) => { e.preventDefault(); }}
>
{children}
</button>
</Tooltip>
);
};
return (
<Tooltip title={tooltip} {...tipOptions}>
<button
className={classNames("toolbar-button", name, { selected, disabled })}
// TODO: confer with Scott about aria-disabled vs. disabled
disabled={disabled}
onClick={handleOnClick}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
>
{children}
</button>
{extraContent}
</Tooltip>
);
};
9 changes: 0 additions & 9 deletions src/components/toolbar/toolbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,6 @@

&.selected {
background-color: $workspace-teal-light-4;

svg {
fill: $workspace-teal-dark-1;
}

}

svg {
fill: $charcoal-dark-1;
}

// Inputs inside a button (eg, for image upload)
Expand Down
61 changes: 61 additions & 0 deletions src/components/toolbar/upload-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { PropsWithChildren, useRef } from "react";
import { TileToolbarButton } from "./tile-toolbar-button";

export interface IUploadButtonComponentProps {
name: string; // a unique internal name used in configuration to identify the button
title: string; // user-visible name, used in the tooltip
keyHint?: string, // If set, displayed to the user as the hotkey equivalent
accept?: string, // MIME types accepted
onUpload: (file: File) => void; // Action when a file is uploaded
selected?: boolean; // puts button in 'active' state if defined and true
disabled?: boolean; // makes button grey and unclickable if defined and true
}

/**
* A TileToolbarButton that is for uploading files.
* It contains a hidden input element, and the toolbar button forwards a click to it.
*/
export const UploadButton =
function({name, title, keyHint, accept, onUpload, selected, disabled, children}:
PropsWithChildren<IUploadButtonComponentProps>) {
const inputRef = useRef<HTMLInputElement>(null);

// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
// Hide the <input> element — we do this because file inputs tend to be ugly, difficult
// to style, and inconsistent in their design across browsers. Opacity is used to hide the file
// input instead of visibility: hidden or display: none, because assistive technology interprets
// the latter two styles to mean the file input isn't interactive.
const hideFileInputStyle = { opacity: 0, width: 1, height: 1, maxWidth: 1, maxHeight: 1 };

const handleFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.currentTarget.files;

Check warning on line 31 in src/components/toolbar/upload-button.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/toolbar/upload-button.tsx#L31

Added line #L31 was not covered by tests
if (files?.length) {
onUpload(files[0]);

Check warning on line 33 in src/components/toolbar/upload-button.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/toolbar/upload-button.tsx#L33

Added line #L33 was not covered by tests
}
};

const input =
<input
ref={inputRef}
type="file"
style={hideFileInputStyle}
accept={accept || "image/png, image/jpeg"}
title={title}
onChange={handleFileInputChange}
className="upload-button-input"
/>;

return (
<TileToolbarButton
name={name}
title={title}
keyHint={keyHint}
disabled={disabled}
selected={selected}
onClick={() => { inputRef.current?.click(); }}
extraContent={input}
>
{children}
</TileToolbarButton>
);
};
101 changes: 101 additions & 0 deletions src/plugins/drawing/action-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useContext } from "react";
import { observer } from "mobx-react";
import { TileToolbarButton } from "../../components/toolbar/tile-toolbar-button";
import { IToolbarButtonComponentProps } from "../../components/toolbar/toolbar-button-manager";
import { DrawingContentModelContext } from "./components/drawing-content-context";
import { OpenPaletteValues } from "./model/drawing-content";
import DeleteIcon from "../../assets/icons/delete/delete-selection-icon.svg";
import GroupObjectsIcon from "./assets/group-objects-icon.svg";
import UngroupObjectsIcon from "./assets/ungroup-objects-icon.svg";
import DuplicateIcon from "./assets/duplicate-icon.svg";

import "./drawing-toolbar.scss";
bacalj marked this conversation as resolved.
Show resolved Hide resolved

export const GroupButton = observer(({ name }: IToolbarButtonComponentProps) => {
const drawingModel = useContext(DrawingContentModelContext);
const enabled = drawingModel.selection.length > 1;

function groupSelection() {
drawingModel.setOpenPalette(OpenPaletteValues.None);
if (drawingModel.selection.length > 1) {
drawingModel.createGroup(drawingModel.selection);
}
}
return (
<TileToolbarButton
name={name} title={"Group"}
onClick={groupSelection}
disabled={!enabled}
>
<GroupObjectsIcon />
</TileToolbarButton>
);
});

export const UngroupButton = observer(({ name }: IToolbarButtonComponentProps) => {
const drawingModel = useContext(DrawingContentModelContext);
const enabled = drawingModel.selection.length > 0
? drawingModel.getSelectedObjects()[0].type === "group"
: false;
bacalj marked this conversation as resolved.
Show resolved Hide resolved

function ungroupSelection() {
drawingModel.setOpenPalette(OpenPaletteValues.None);
if (drawingModel.selection.length > 0) {
drawingModel.ungroupGroups(drawingModel.selection);
}
}
return (
<TileToolbarButton
name={name}
title={"Ungroup"}
onClick={ungroupSelection}
disabled={!enabled}
>
<UngroupObjectsIcon />
</TileToolbarButton>
);
});

// Duplicate
export const DuplicateButton = observer(({ name }: IToolbarButtonComponentProps) => {
const drawingModel = useContext(DrawingContentModelContext);
const enabled = drawingModel.selection.length > 0;

function duplicateSelection() {
drawingModel.setOpenPalette(OpenPaletteValues.None);
drawingModel.duplicateObjects(drawingModel.selection);

Check warning on line 66 in src/plugins/drawing/action-buttons.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/drawing/action-buttons.tsx#L64-L66

Added lines #L64 - L66 were not covered by tests
}

return (
<TileToolbarButton
name={name}
title={"Duplicate"}
onClick={duplicateSelection}
disabled={!enabled}
>
<DuplicateIcon />
</TileToolbarButton>
);
});

// Delete
export const DeleteButton = observer(({ name }: IToolbarButtonComponentProps) => {
const drawingModel = useContext(DrawingContentModelContext);
const enabled = drawingModel.selection.length > 0;

const deleteSelection = () => {
drawingModel.setOpenPalette(OpenPaletteValues.None);
drawingModel.deleteObjects([...drawingModel.selection]);
};

return (
<TileToolbarButton
name={name}
title={"Delete"}
onClick={deleteSelection}
disabled={!enabled}
>
<DeleteIcon />
</TileToolbarButton>
);
});
Loading
Loading