Skip to content

Commit 4471301

Browse files
authored
Add very basic doodle functionality (#2)
* add basic doodlability * tweak css * remove logs and bad comments * small format change * test prettier * test prettier * test prettier * test prettier * pre commit hooks are running * small change to format file * pass linter * so thats how type checking works
1 parent 01a4437 commit 4471301

File tree

14 files changed

+715
-18
lines changed

14 files changed

+715
-18
lines changed

.eslintcache

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/components/toolbar.js":"1","/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/guest.js":"2","/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/toolbar.js":"3","/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/doodle/doodleCanvas.js":"4"},{"size":5021,"mtime":1614880460577,"results":"5","hashOfConfig":"6"},{"size":22213,"mtime":1614880828948,"results":"7","hashOfConfig":"6"},{"size":3684,"mtime":1614881067711,"results":"8","hashOfConfig":"6"},{"size":2861,"mtime":1614880967602,"results":"9","hashOfConfig":"10"},{"filePath":"11","messages":"12","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"zv3vgn",{"filePath":"13","messages":"14","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"15","messages":"16","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"17","messages":"18","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"itfxzx","/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/components/toolbar.js",[],"/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/guest.js",[],"/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/annotator/toolbar.js",[],"/Users/willfuchs/Documents/Cal Poly 20-21/csc 402/client/src/doodle/doodleCanvas.js",[]]

dev-server/documents/html/burns.mustache

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
</style>
2828
</head>
2929
<body>
30+
<div id="main-content">
3031
<pre xml:space="preserve">
3132
Project Gutenberg's Poems And Songs Of Robert Burns, by Robert Burns
3233

@@ -30225,5 +30226,5 @@ subscribe to our email newsletter to hear about new eBooks.
3022530226

3022630227
</pre>
3022730228
{{{ hypothesisScript }}}
30228-
30229+
</div>
3022930230
</body></html>

package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"gulp-replace": "^1.0.0",
5858
"gulp-sourcemaps": "^3.0.0",
5959
"hammerjs": "^2.0.4",
60+
"husky": "=4",
6061
"karma": "^6.0.1",
6162
"karma-browserify": "^8.0.0",
6263
"karma-chai": "^0.1.0",
@@ -66,6 +67,7 @@
6667
"karma-mocha-reporter": "^2.0.4",
6768
"karma-sinon": "^1.0.5",
6869
"katex": "^0.12.0",
70+
"lint-staged": "^10.5.4",
6971
"lodash.debounce": "^4.0.3",
7072
"loose-envify": "^1.4.0",
7173
"mocha": "8.2.1",
@@ -75,7 +77,7 @@
7577
"postcss": "^8.0.3",
7678
"postcss-url": "^10.0.0",
7779
"preact": "^10.4.0",
78-
"prettier": "2.2.1",
80+
"prettier": "^2.2.1",
7981
"puppeteer": "^5.0.0",
8082
"query-string": "^3.0.1",
8183
"redux": "^4.0.1",
@@ -127,5 +129,14 @@
127129
"report-coverage": "codecov -f coverage/coverage-final.json",
128130
"version": "make clean build",
129131
"setup-frontend-shared": "gulp build-frontend-shared"
132+
},
133+
"husky": {
134+
"hooks": {
135+
"pre-commit": "tsc --build src/tsconfig.json && lint-staged"
136+
}
137+
},
138+
"lint-staged": {
139+
"*.js": "eslint --cache --fix",
140+
"*.{js,scss}": "prettier --write"
130141
}
131142
}

src/annotator/components/toolbar.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ ToolbarButton.propTypes = {
7272
* Callback to toggle visibility of highlights in the document.
7373
* @prop {() => any} toggleSidebar -
7474
* Callback to toggle the visibility of the sidebar.
75+
* @prop {() => any} toggleDoodleability -
76+
* Callback to toggle visibility of highlights in the document.
7577
* @prop {import("preact").Ref<HTMLButtonElement>} [toggleSidebarRef] -
7678
* Ref that gets set to the toolbar button for toggling the sidebar.
7779
* This is exposed to enable the drag-to-resize functionality of this
@@ -95,6 +97,7 @@ export default function Toolbar({
9597
showHighlights,
9698
toggleHighlights,
9799
toggleSidebar,
100+
toggleDoodleability,
98101
toggleSidebarRef,
99102
useMinimalControls = false,
100103
}) {
@@ -136,7 +139,7 @@ export default function Toolbar({
136139
<ToolbarButton
137140
label={'New Doodle'}
138141
icon={'doodle'}
139-
onClick={() => {alert("this would create a doodle!")}}
142+
onClick={toggleDoodleability}
140143
/>
141144
</div>
142145
)}
@@ -152,6 +155,7 @@ Toolbar.propTypes = {
152155
showHighlights: propTypes.bool.isRequired,
153156
toggleHighlights: propTypes.func.isRequired,
154157
toggleSidebar: propTypes.func.isRequired,
158+
toggleDoodleability: propTypes.func.isRequired,
155159
toggleSidebarRef: propTypes.any,
156160
useMinimalControls: propTypes.bool,
157161
};

src/annotator/guest.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ export default class Guest extends Delegator {
123123
/** @type {ToolbarController|null} */
124124
this.toolbar = null;
125125

126+
/** TODO add this type back while still passing linter {DoodleController|null}* } */
127+
this.doodleCanvasController = null;
128+
126129
this.adderToolbar = document.createElement('hypothesis-adder');
127130
this.adderToolbar.style.display = 'none';
128131
this.element.appendChild(this.adderToolbar);
@@ -349,6 +352,10 @@ export default class Guest extends Delegator {
349352
crossframe.on('setVisibleHighlights', state => {
350353
this.setVisibleHighlights(state);
351354
});
355+
356+
crossframe.on('setDoodleability', state => {
357+
this.setDoodleability(state);
358+
});
352359
}
353360

354361
destroy() {
@@ -710,4 +717,15 @@ export default class Guest extends Delegator {
710717
this.toolbar.highlightsVisible = shouldShowHighlights;
711718
}
712719
}
720+
721+
/**
722+
* Set whether the document can be doodled on
723+
*
724+
* @param {boolean} shouldBeDoodleable
725+
*/
726+
setDoodleability(shouldBeDoodleable) {
727+
if (this.doodleCanvasController) {
728+
this.doodleCanvasController.doodleable = shouldBeDoodleable;
729+
}
730+
}
713731
}

src/annotator/icons.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ export default {
1515
note: require('../images/icons/note.svg'),
1616
pointer: require('../images/icons/pointer.svg'),
1717
show: require('../images/icons/show.svg'),
18-
doodle: require('../images/icons/doodle.svg')
18+
doodle: require('../images/icons/doodle.svg'),
1919
};

src/annotator/sidebar.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Guest from './guest';
1010
import { ToolbarController } from './toolbar';
1111
import { createShadowRoot } from './util/shadow-root';
1212
import BucketBar from './plugin/bucket-bar';
13+
import { DoodleController } from '../doodle/doodleController';
1314

1415
/**
1516
* @typedef LayoutState
@@ -134,9 +135,18 @@ export default class Sidebar extends Guest {
134135
createAnnotation: () => this.createAnnotation(),
135136
setSidebarOpen: open => (open ? this.show() : this.hide()),
136137
setHighlightsVisible: show => this.setAllVisibleHighlights(show),
138+
setUserCanDoodle: show => this.setAllDoodleability(show),
137139
});
138140
this.toolbar.useMinimalControls = config.theme === 'clean';
139141

142+
this.doodleCanvasController = new DoodleController(
143+
document.getElementById('main-content'),
144+
{
145+
tool: 'pen',
146+
size: 5,
147+
}
148+
);
149+
140150
if (this.frame) {
141151
// If using our own container frame for the sidebar, add the toolbar to it.
142152
this.frame.prepend(toolbarContainer);
@@ -410,4 +420,13 @@ export default class Sidebar extends Guest {
410420
setAllVisibleHighlights(shouldShowHighlights) {
411421
this.crossframe.call('setVisibleHighlights', shouldShowHighlights);
412422
}
423+
424+
/**
425+
* (CreativeNTR) Toggle doodle ability on and off
426+
*
427+
* @param {boolean} shouldBeDoodleable
428+
*/
429+
setAllDoodleability(shouldBeDoodleable) {
430+
this.crossframe.call('setDoodleability', shouldBeDoodleable);
431+
}
413432
}

src/annotator/toolbar.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Toolbar from './components/toolbar';
77
* @prop {() => any} createAnnotation
88
* @prop {(open: boolean) => any} setSidebarOpen
99
* @prop {(visible: boolean) => any} setHighlightsVisible
10+
* @prop {(doodleable: boolean) => any} setUserCanDoodle
1011
*/
1112

1213
/**
@@ -21,7 +22,12 @@ export class ToolbarController {
2122
* @param {ToolbarOptions} options
2223
*/
2324
constructor(container, options) {
24-
const { createAnnotation, setSidebarOpen, setHighlightsVisible } = options;
25+
const {
26+
createAnnotation,
27+
setSidebarOpen,
28+
setHighlightsVisible,
29+
setUserCanDoodle,
30+
} = options;
2531

2632
this._container = container;
2733
this._container.className = 'annotator-toolbar';
@@ -33,11 +39,16 @@ export class ToolbarController {
3339

3440
this._highlightsVisible = false;
3541
this._sidebarOpen = false;
42+
this._doodleable = false;
3643

3744
this._closeSidebar = () => setSidebarOpen(false);
3845
this._toggleSidebar = () => setSidebarOpen(!this._sidebarOpen);
3946
this._toggleHighlights = () =>
4047
setHighlightsVisible(!this._highlightsVisible);
48+
this._toggleDoodleability = () => {
49+
this._doodleable = !this._doodleable;
50+
setUserCanDoodle(this._doodleable);
51+
};
4152
this._createAnnotation = () => {
4253
createAnnotation();
4354
setSidebarOpen(true);
@@ -125,6 +136,7 @@ export class ToolbarController {
125136
toggleSidebar={this._toggleSidebar}
126137
toggleSidebarRef={this._sidebarToggleButton}
127138
useMinimalControls={this.useMinimalControls}
139+
toggleDoodleability={this._toggleDoodleability}
128140
/>,
129141
this._container
130142
);

src/doodle/canvas.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { createElement } from 'preact';
2+
import { useRef, useEffect } from 'preact/hooks';
3+
import propTypes from 'prop-types';
4+
5+
//TODO not sure how to enforce this type...
6+
/**
7+
* @typedef Line
8+
* @property {string} tool - The tool used to draw this line.
9+
* @property {any} points
10+
*/
11+
12+
const Canvas = ({
13+
width,
14+
height,
15+
handleMouseDown,
16+
handleMouseUp,
17+
handleMouseMove,
18+
handleMouseLeave,
19+
lines,
20+
}) => {
21+
const canvasRef = useRef(null);
22+
23+
const drawLine = (ctx, line) => {
24+
if (line.points.length <= 1) {
25+
return;
26+
}
27+
const [[startX, startY], ...rest] = line.points;
28+
// Move to the first point, begin the line
29+
ctx.beginPath();
30+
ctx.moveTo(startX, startY);
31+
32+
if (line.tool === 'pen') {
33+
ctx.globalCompositeOperation = 'source-over';
34+
ctx.lineWidth = line.size;
35+
ctx.strokeStyle = line.color;
36+
} else {
37+
ctx.globalCompositeOperation = 'destination-out';
38+
ctx.lineWidth = 25;
39+
}
40+
41+
// Draw the rest of the lines
42+
for (let [x, y] of rest) {
43+
ctx.lineTo(x, y);
44+
}
45+
46+
ctx.stroke();
47+
ctx.closePath();
48+
};
49+
50+
useEffect(() => {
51+
// for testing
52+
const canvas = canvasRef.current;
53+
const ctx = canvas.getContext('2d');
54+
55+
// Draw all of the lines (reverse order so that erasing works)
56+
for (let i = lines.length - 1; i >= 0; i--) {
57+
drawLine(ctx, lines[i]);
58+
}
59+
}, [lines]);
60+
61+
return (
62+
<canvas
63+
width={width}
64+
height={height}
65+
ref={canvasRef}
66+
onMouseDown={handleMouseDown}
67+
onMouseMove={handleMouseMove}
68+
onMouseUp={handleMouseUp}
69+
onMouseLeave={handleMouseLeave}
70+
/>
71+
);
72+
};
73+
74+
Canvas.propTypes = {
75+
width: propTypes.number.isRequired,
76+
height: propTypes.number.isRequired,
77+
handleMouseDown: propTypes.func.isRequired,
78+
handleMouseUp: propTypes.func.isRequired,
79+
handleMouseMove: propTypes.func.isRequired,
80+
handleMouseLeave: propTypes.func.isRequired,
81+
lines: propTypes.array.isRequired,
82+
};
83+
84+
export { Canvas };

0 commit comments

Comments
 (0)