mirror of
https://github.com/gradio-app/gradio.git
synced 2025-04-06 12:30:29 +08:00
gradio 2.0 prelaunch
This commit is contained in:
parent
7f135f9d74
commit
857dc3f1be
1209
frontend/package-lock.json
generated
1209
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"audio-react-recorder": "^1.0.4",
|
||||
"classnames": "^2.3.1",
|
||||
"fabric": "^4.5.0",
|
||||
"jspreadsheet-ce": "^4.7.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { DataURLComponentExample } from '../component_example';
|
||||
import Webcam from "react-webcam";
|
||||
// import { SketchField, Tools } from 'react-sketch';
|
||||
import { SketchField, Tools } from '../../vendor/ReactSketch';
|
||||
|
||||
class ImageInput extends React.Component {
|
||||
constructor(props) {
|
||||
@ -65,7 +65,7 @@ class ImageInput extends React.Component {
|
||||
this.sketchKey += 1;
|
||||
}
|
||||
return (<div className="input_image">
|
||||
{/* <div className="image_preview_holder sketch">
|
||||
<div className="image_preview_holder sketch">
|
||||
<SketchField
|
||||
ref={this.sketchRef}
|
||||
key={this.sketchKey}
|
||||
@ -77,7 +77,7 @@ class ImageInput extends React.Component {
|
||||
backgroundColor="white"
|
||||
onChange={this.getSketch}
|
||||
/>
|
||||
</div>*/}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
@ -23,13 +23,13 @@
|
||||
@apply flex-1;
|
||||
}
|
||||
.component_set {
|
||||
@apply bg-gray-100 p-2 rounded flex flex-col gap-2;
|
||||
@apply bg-gray-50 p-2 rounded flex flex-col gap-2;
|
||||
}
|
||||
.panel_header {
|
||||
@apply mb-1 uppercase text-sm font-semibold;
|
||||
}
|
||||
.panel_button {
|
||||
@apply flex-grow p-3 rounded bg-gray-100 hover:bg-gray-200 transition font-semibold focus:outline-none;
|
||||
@apply flex-grow p-3 rounded bg-gray-50 hover:bg-gray-100 transition font-semibold focus:outline-none;
|
||||
}
|
||||
.panel_button.disabled {
|
||||
@apply text-gray-400 cursor-not-allowed;
|
||||
@ -47,7 +47,7 @@
|
||||
@apply rounded-tr-none rounded-br-none;
|
||||
}
|
||||
.panel_button.right_panel_button {
|
||||
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-300;
|
||||
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-200;
|
||||
}
|
||||
.examples {
|
||||
h4 {
|
||||
@ -100,7 +100,7 @@
|
||||
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
|
||||
}
|
||||
.image_preview_holder {
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-300 relative inline-block;
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-200 relative inline-block;
|
||||
}
|
||||
.sketch > div {
|
||||
@apply bg-white;
|
||||
@ -118,7 +118,7 @@
|
||||
.input_radio {
|
||||
@apply flex flex-wrap gap-2;
|
||||
.radio_item {
|
||||
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
}
|
||||
.radio_item.selected {
|
||||
@apply bg-yellow-500 text-white;
|
||||
@ -133,7 +133,7 @@
|
||||
.input_checkbox_group {
|
||||
@apply flex flex-wrap gap-2;
|
||||
.checkbox_item {
|
||||
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
}
|
||||
.checkbox_item.selected {
|
||||
@apply bg-yellow-500 text-white;
|
||||
@ -159,7 +159,7 @@
|
||||
.input_checkbox {
|
||||
@apply flex flex-wrap gap-2;
|
||||
.checkbox_item {
|
||||
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
|
||||
}
|
||||
.checkbox_item.selected {
|
||||
@apply bg-yellow-500 text-white;
|
||||
@ -185,7 +185,7 @@
|
||||
.input_dropdown {
|
||||
@apply inline-block relative;
|
||||
.selector {
|
||||
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded inline-flex items-center;
|
||||
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded inline-flex items-center;
|
||||
}
|
||||
.current {
|
||||
@apply mr-1;
|
||||
@ -212,7 +212,7 @@
|
||||
.input_slider {
|
||||
@apply text-center;
|
||||
.range {
|
||||
@apply w-full appearance-none bg-gray-300 hover:bg-gray-200 transition rounded h-4;
|
||||
@apply w-full appearance-none bg-gray-200 hover:bg-gray-200 transition rounded h-4;
|
||||
}
|
||||
.range::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
@ -245,7 +245,7 @@
|
||||
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
|
||||
}
|
||||
.video_preview_holder {
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-300;
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-200;
|
||||
}
|
||||
.video_preview {
|
||||
@apply w-full h-full object-contain;
|
||||
|
@ -5,10 +5,16 @@
|
||||
.gradio_interface[theme="huggingface"] {
|
||||
@apply container mx-auto my-4;
|
||||
.title {
|
||||
@apply text-center;
|
||||
@apply text-center p-4 text-4xl;
|
||||
}
|
||||
.description {
|
||||
@apply pb-4;
|
||||
}
|
||||
.loading {
|
||||
@apply absolute right-0.5;
|
||||
@apply absolute right-1;
|
||||
}
|
||||
.loading img {
|
||||
@apply h-5;
|
||||
}
|
||||
.panels {
|
||||
@apply flex flex-wrap justify-center gap-4;
|
||||
@ -29,7 +35,7 @@
|
||||
@apply flex gap-4 my-4;
|
||||
}
|
||||
.screenshot_set {
|
||||
@apply flex flex-grow;
|
||||
@apply hidden flex hidden flex-grow;
|
||||
}
|
||||
.panel_button.left_panel_button {
|
||||
@apply rounded-tr-none rounded-br-none;
|
||||
@ -37,6 +43,41 @@
|
||||
.panel_button.right_panel_button {
|
||||
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-300;
|
||||
}
|
||||
.examples {
|
||||
h4 {
|
||||
@apply text-lg font-semibold my-2;
|
||||
}
|
||||
.examples_control {
|
||||
@apply hidden flex hidden gap-2;
|
||||
}
|
||||
.examples_control_left {
|
||||
@apply flex gap-2;
|
||||
}
|
||||
.shortcut {
|
||||
@apply block text-xs;
|
||||
}
|
||||
.examples_control button {
|
||||
@apply bg-gray-100 hover:bg-gray-200 p-2;
|
||||
}
|
||||
.examples_table {
|
||||
@apply table-auto p-2 bg-gray-100 mt-4 rounded;
|
||||
tbody tr {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
thead {
|
||||
@apply border-b-2 border-gray-300;
|
||||
}
|
||||
tbody tr:hover {
|
||||
@apply bg-purple-500 text-white;
|
||||
}
|
||||
tbody tr.selected {
|
||||
@apply font-bold;
|
||||
}
|
||||
td, th {
|
||||
@apply py-2 px-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Input Components */
|
||||
textarea.input_text {
|
||||
@apply w-full p-3 border border-gray-200 rounded-lg shadow-inner outline-none focus:ring-1 focus:ring-inset focus:ring-indigo-200 focus:shadow-inner placeholder-gray-400;
|
||||
@ -51,13 +92,22 @@
|
||||
@apply w-full h-80;
|
||||
.upload_zone {
|
||||
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
|
||||
}
|
||||
}
|
||||
.image_preview_holder {
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-300;
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-300 relative inline-block;
|
||||
}
|
||||
.image_preview {
|
||||
.sketch > div {
|
||||
@apply bg-white;
|
||||
}
|
||||
.image_preview, video {
|
||||
@apply w-full h-full object-contain;
|
||||
}
|
||||
.snapshot {
|
||||
@apply absolute bottom-0 w-full bg-white bg-opacity-90 py-3 font-bold text-lg text-center;
|
||||
}
|
||||
}
|
||||
.input_image_example {
|
||||
@apply h-24;
|
||||
}
|
||||
.input_radio {
|
||||
@apply flex flex-wrap gap-2;
|
||||
@ -182,7 +232,36 @@
|
||||
audio {
|
||||
@apply w-full;
|
||||
}
|
||||
} /* Output Components */
|
||||
}
|
||||
.input_video {
|
||||
@apply w-full h-80;
|
||||
.upload_zone {
|
||||
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
|
||||
}
|
||||
.video_preview_holder {
|
||||
@apply w-full h-full flex justify-center items-center bg-gray-200;
|
||||
}
|
||||
.video_preview {
|
||||
@apply w-full h-full object-contain;
|
||||
}
|
||||
}
|
||||
.input_file {
|
||||
@apply w-full h-80;
|
||||
.upload_zone {
|
||||
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
|
||||
}
|
||||
.file_preview_holder {
|
||||
@apply w-full h-full flex flex-col justify-center items-center relative inline-block;
|
||||
}
|
||||
.file_name {
|
||||
@apply text-6xl p-6;
|
||||
}
|
||||
.file_size {
|
||||
@apply text-2xl p-2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output Components */
|
||||
.output_label {
|
||||
.output_class {
|
||||
@apply hidden;
|
||||
@ -212,7 +291,47 @@
|
||||
}
|
||||
.output_audio {
|
||||
audio {
|
||||
@apply w-full;
|
||||
@apply w-full border-2 border-gray-300 rounded;
|
||||
}
|
||||
}
|
||||
.output_highlightedtext {
|
||||
.category_legend {
|
||||
@apply flex flex-wrap gap-4 mb-2;
|
||||
.category-label {
|
||||
@apply flex gap-2;
|
||||
}
|
||||
.colorbox {
|
||||
@apply w-6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.output_keyvalues {
|
||||
table {
|
||||
@apply bg-white;
|
||||
thead {
|
||||
@apply font-bold;
|
||||
}
|
||||
td, th {
|
||||
@apply p-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.output_video {
|
||||
@apply w-full h-80 object-contain flex justify-center;
|
||||
video {
|
||||
@apply h-full;
|
||||
}
|
||||
}
|
||||
.output_file {
|
||||
@apply w-full h-80;
|
||||
.file_display {
|
||||
@apply w-full h-full flex flex-col justify-center items-center relative inline-block;
|
||||
}
|
||||
.file_name {
|
||||
@apply text-6xl p-6;
|
||||
}
|
||||
.file_size {
|
||||
@apply text-2xl p-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
725
frontend/src/vendor/ReactSketch/components/SketchField/SketchField.jsx
vendored
Normal file
725
frontend/src/vendor/ReactSketch/components/SketchField/SketchField.jsx
vendored
Normal file
@ -0,0 +1,725 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
// @ts-nocheck
|
||||
|
||||
import React, { PureComponent } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import History from "../../history";
|
||||
import { uuid4 } from "../../utils";
|
||||
import Arrow from "./../../tools/Arrow";
|
||||
import Tool from "./../../tools";
|
||||
import DefaultTool from "./../../tools/defaul-tool";
|
||||
import Select from "./../../tools/Select";
|
||||
import Pencil from "./../../tools/Pencil";
|
||||
import Line from "./../../tools/Line";
|
||||
import Rectangle from "./../../tools/Rectangle";
|
||||
import RectangleLabel from "./../../tools/Rectangle/rectangle-label";
|
||||
import Circle from "./../../tools/Circle";
|
||||
import Pan from "./../../tools/Pan";
|
||||
import Highlighter from "./../../tools/Highlighter";
|
||||
import { fabric } from "fabric";
|
||||
|
||||
/**
|
||||
* Sketch Tool based on FabricJS for React Applications
|
||||
*/
|
||||
class SketchField extends PureComponent {
|
||||
static propTypes = {
|
||||
// the color of the line
|
||||
lineColor: PropTypes.string,
|
||||
// The width of the line
|
||||
lineWidth: PropTypes.number,
|
||||
// the fill color of the shape when applicable
|
||||
fillColor: PropTypes.string,
|
||||
// the background color of the sketch
|
||||
backgroundColor: PropTypes.string,
|
||||
// the opacity of the object
|
||||
opacity: PropTypes.number,
|
||||
// number of undo/redo steps to maintain
|
||||
undoSteps: PropTypes.number,
|
||||
// The tool to use, can be pencil, rectangle, circle, brush;
|
||||
tool: PropTypes.string,
|
||||
// image format when calling toDataURL
|
||||
imageFormat: PropTypes.string,
|
||||
// Sketch data for controlling sketch from
|
||||
// outside the component
|
||||
value: PropTypes.object,
|
||||
// Set to true if you wish to force load the given value, even if it is the same
|
||||
forceValue: PropTypes.bool,
|
||||
// Specify some width correction which will be applied on auto resize
|
||||
widthCorrection: PropTypes.number,
|
||||
// Specify some height correction which will be applied on auto resize
|
||||
heightCorrection: PropTypes.number,
|
||||
// Specify action on change
|
||||
onChange: PropTypes.func,
|
||||
// Default initial value
|
||||
defaultValue: PropTypes.object,
|
||||
// Sketch width
|
||||
width: PropTypes.number,
|
||||
// Sketch height
|
||||
height: PropTypes.number,
|
||||
// event object added
|
||||
onObjectAdded: PropTypes.func,
|
||||
// event object modified
|
||||
onObjectModified: PropTypes.func,
|
||||
// event object removed
|
||||
onObjectRemoved: PropTypes.func,
|
||||
// event mouse down
|
||||
onMouseDown: PropTypes.func,
|
||||
// event mouse move
|
||||
onMouseMove: PropTypes.func,
|
||||
// event mouse up
|
||||
onMouseUp: PropTypes.func,
|
||||
// event mouse out
|
||||
onMouseOut: PropTypes.func,
|
||||
// event object move
|
||||
onObjectMoving: PropTypes.func,
|
||||
// event object scale
|
||||
onObjectScaling: PropTypes.func,
|
||||
// event object rotating
|
||||
onObjectRotating: PropTypes.func,
|
||||
// Class name to pass to container div of canvas
|
||||
className: PropTypes.string,
|
||||
// Style options to pass to container div of canvas
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
lineColor: "black",
|
||||
lineWidth: 10,
|
||||
fillColor: "transparent",
|
||||
backgroundColor: "transparent",
|
||||
opacity: 1.0,
|
||||
undoSteps: 25,
|
||||
tool: null,
|
||||
widthCorrection: 0,
|
||||
heightCorrection: 0,
|
||||
forceValue: false,
|
||||
onObjectAdded: () => null,
|
||||
onObjectModified: () => null,
|
||||
onObjectRemoved: () => null,
|
||||
onMouseDown: () => null,
|
||||
onMouseMove: () => null,
|
||||
onMouseUp: () => null,
|
||||
onMouseOut: () => null,
|
||||
onObjectMoving: () => null,
|
||||
onObjectScaling: () => null,
|
||||
onObjectRotating: () => null,
|
||||
};
|
||||
|
||||
state = {
|
||||
action: true,
|
||||
};
|
||||
_initTools = (fabricCanvas) => {
|
||||
this._tools = {};
|
||||
this._tools[Tool.Select] = new Select(fabricCanvas);
|
||||
this._tools[Tool.Pencil] = new Pencil(fabricCanvas);
|
||||
this._tools[Tool.Line] = new Line(fabricCanvas);
|
||||
this._tools[Tool.Arrow] = new Arrow(fabricCanvas);
|
||||
this._tools[Tool.Rectangle] = new Rectangle(fabricCanvas);
|
||||
this._tools[Tool.RectangleLabel] = new RectangleLabel(fabricCanvas);
|
||||
this._tools[Tool.Circle] = new Circle(fabricCanvas);
|
||||
this._tools[Tool.Pan] = new Pan(fabricCanvas);
|
||||
this._tools[Tool.Highlighter] = new Highlighter(fabricCanvas);
|
||||
this._tools[Tool.DefaultTool] = new DefaultTool(fabricCanvas);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable touch Scrolling on Canvas
|
||||
*/
|
||||
enableTouchScroll = () => {
|
||||
let canvas = this._fc;
|
||||
if (canvas.allowTouchScrolling) return;
|
||||
canvas.allowTouchScrolling = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable touch Scrolling on Canvas
|
||||
*/
|
||||
disableTouchScroll = () => {
|
||||
let canvas = this._fc;
|
||||
if (canvas.allowTouchScrolling) {
|
||||
canvas.allowTouchScrolling = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an image as object to the canvas
|
||||
*
|
||||
* @param dataUrl the image url or Data Url
|
||||
* @param options object to pass and change some options when loading image, the format of the object is:
|
||||
*
|
||||
* {
|
||||
* left: <Number: distance from left of canvas>,
|
||||
* top: <Number: distance from top of canvas>,
|
||||
* scale: <Number: initial scale of image>
|
||||
* }
|
||||
*/
|
||||
addImg = (dataUrl, options = {}) => {
|
||||
let canvas = this._fc;
|
||||
fabric.Image.fromURL(dataUrl, (oImg) => {
|
||||
let opts = {
|
||||
left: Math.random() * (canvas.getWidth() - oImg.width * 0.5),
|
||||
top: Math.random() * (canvas.getHeight() - oImg.height * 0.5),
|
||||
scale: 0.5,
|
||||
};
|
||||
Object.assign(opts, options);
|
||||
oImg.scale(opts.scale);
|
||||
oImg.set({
|
||||
left: opts.left,
|
||||
top: opts.top,
|
||||
});
|
||||
canvas.add(oImg);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when an object is added to the canvas
|
||||
*/
|
||||
_onObjectAdded = (e) => {
|
||||
const { onObjectAdded } = this.props;
|
||||
if (!this.state.action) {
|
||||
this.setState({ action: true });
|
||||
return;
|
||||
}
|
||||
let obj = e.target;
|
||||
obj.__version = 1;
|
||||
// record current object state as json and save as originalState
|
||||
let objState = obj.toJSON();
|
||||
obj.__originalState = objState;
|
||||
let state = JSON.stringify(objState);
|
||||
// object, previous state, current state
|
||||
this._history.keep([obj, state, state]);
|
||||
onObjectAdded(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when an object is moving around inside the canvas
|
||||
*/
|
||||
_onObjectMoving = (e) => {
|
||||
const { onObjectMoving } = this.props;
|
||||
onObjectMoving(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when an object is scaling inside the canvas
|
||||
*/
|
||||
_onObjectScaling = (e) => {
|
||||
const { onObjectScaling } = this.props;
|
||||
onObjectScaling(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when an object is rotating inside the canvas
|
||||
*/
|
||||
_onObjectRotating = (e) => {
|
||||
const { onObjectRotating } = this.props;
|
||||
onObjectRotating(e);
|
||||
};
|
||||
|
||||
_onObjectModified = (e) => {
|
||||
const { onObjectModified } = this.props;
|
||||
let obj = e.target;
|
||||
obj.__version += 1;
|
||||
let prevState = JSON.stringify(obj.__originalState);
|
||||
let objState = obj.toJSON();
|
||||
// record current object state as json and update to originalState
|
||||
obj.__originalState = objState;
|
||||
let currState = JSON.stringify(objState);
|
||||
this._history.keep([obj, prevState, currState]);
|
||||
onObjectModified(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when an object is removed from the canvas
|
||||
*/
|
||||
_onObjectRemoved = (e) => {
|
||||
const { onObjectRemoved } = this.props;
|
||||
let obj = e.target;
|
||||
if (obj.__removed) {
|
||||
obj.__version += 1;
|
||||
return;
|
||||
}
|
||||
obj.__version = 0;
|
||||
onObjectRemoved(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when the mouse button is pressed down
|
||||
*/
|
||||
_onMouseDown = (e) => {
|
||||
const { onMouseDown } = this.props;
|
||||
this._selectedTool.doMouseDown(e);
|
||||
onMouseDown(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when the mouse cursor is moving around within the canvas
|
||||
*/
|
||||
_onMouseMove = (e) => {
|
||||
const { onMouseMove } = this.props;
|
||||
this._selectedTool.doMouseMove(e);
|
||||
onMouseMove(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action when the mouse cursor is moving out from the canvas
|
||||
*/
|
||||
_onMouseOut = (e) => {
|
||||
const { onMouseOut } = this.props;
|
||||
this._selectedTool.doMouseOut(e);
|
||||
if (this.props.onChange) {
|
||||
let onChange = this.props.onChange;
|
||||
setTimeout(() => {
|
||||
onChange(e.e);
|
||||
}, 10);
|
||||
}
|
||||
onMouseOut(e);
|
||||
};
|
||||
|
||||
_onMouseUp = (e) => {
|
||||
const { onMouseUp } = this.props;
|
||||
this._selectedTool.doMouseUp(e);
|
||||
// Update the final state to new-generated object
|
||||
// Ignore Path object since it would be created after mouseUp
|
||||
// Assumed the last object in canvas.getObjects() in the newest object
|
||||
if (this.props.tool !== Tool.Pencil) {
|
||||
const canvas = this._fc;
|
||||
const objects = canvas.getObjects();
|
||||
const newObj = objects[objects.length - 1];
|
||||
if (newObj && newObj.__version === 1) {
|
||||
newObj.__originalState = newObj.toJSON();
|
||||
}
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
let onChange = this.props.onChange;
|
||||
setTimeout(() => {
|
||||
onChange(e.e);
|
||||
}, 10);
|
||||
}
|
||||
onMouseUp(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Track the resize of the window and update our state
|
||||
*
|
||||
* @param e the resize event
|
||||
* @private
|
||||
*/
|
||||
_resize = (e, canvasWidth = null, canvasHeight = null) => {
|
||||
if (e) e.preventDefault();
|
||||
let { widthCorrection, heightCorrection } = this.props;
|
||||
let canvas = this._fc;
|
||||
let { offsetWidth, clientHeight } = this._container;
|
||||
let prevWidth = canvasWidth || canvas.getWidth();
|
||||
let prevHeight = canvasHeight || canvas.getHeight();
|
||||
let wfactor = ((offsetWidth - widthCorrection) / prevWidth).toFixed(2);
|
||||
let hfactor = ((clientHeight - heightCorrection) / prevHeight).toFixed(2);
|
||||
canvas.setWidth(offsetWidth - widthCorrection);
|
||||
canvas.setHeight(clientHeight - heightCorrection);
|
||||
if (canvas.backgroundImage) {
|
||||
// Need to scale background images as well
|
||||
let bi = canvas.backgroundImage;
|
||||
bi.width = bi.width * wfactor;
|
||||
bi.height = bi.height * hfactor;
|
||||
}
|
||||
let objects = canvas.getObjects();
|
||||
for (let i in objects) {
|
||||
let obj = objects[i];
|
||||
let scaleX = obj.scaleX;
|
||||
let scaleY = obj.scaleY;
|
||||
let left = obj.left;
|
||||
let top = obj.top;
|
||||
let tempScaleX = scaleX * wfactor;
|
||||
let tempScaleY = scaleY * hfactor;
|
||||
let tempLeft = left * wfactor;
|
||||
let tempTop = top * hfactor;
|
||||
obj.scaleX = tempScaleX;
|
||||
obj.scaleY = tempScaleY;
|
||||
obj.left = tempLeft;
|
||||
obj.top = tempTop;
|
||||
obj.setCoords();
|
||||
}
|
||||
canvas.renderAll();
|
||||
canvas.calcOffset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the background color for this sketch
|
||||
* @param color in rgba or hex format
|
||||
*/
|
||||
_backgroundColor = (color) => {
|
||||
if (!color) return;
|
||||
let canvas = this._fc;
|
||||
canvas.setBackgroundColor(color, () => canvas.renderAll());
|
||||
};
|
||||
|
||||
/**
|
||||
* Zoom the drawing by the factor specified
|
||||
*
|
||||
* The zoom factor is a percentage with regards the original, for example if factor is set to 2
|
||||
* it will double the size whereas if it is set to 0.5 it will half the size
|
||||
*
|
||||
* @param factor the zoom factor
|
||||
*/
|
||||
zoom = (factor) => {
|
||||
let canvas = this._fc;
|
||||
let objects = canvas.getObjects();
|
||||
for (let i in objects) {
|
||||
objects[i].scaleX = objects[i].scaleX * factor;
|
||||
objects[i].scaleY = objects[i].scaleY * factor;
|
||||
objects[i].left = objects[i].left * factor;
|
||||
objects[i].top = objects[i].top * factor;
|
||||
objects[i].setCoords();
|
||||
}
|
||||
canvas.renderAll();
|
||||
canvas.calcOffset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform an undo operation on canvas, if it cannot undo it will leave the canvas intact
|
||||
*/
|
||||
undo = () => {
|
||||
let history = this._history;
|
||||
let [obj, prevState, currState] = history.getCurrent();
|
||||
history.undo();
|
||||
if (obj.__removed) {
|
||||
this.setState({ action: false }, () => {
|
||||
this._fc.add(obj);
|
||||
obj.__version -= 1;
|
||||
obj.__removed = false;
|
||||
});
|
||||
} else if (obj.__version <= 1) {
|
||||
this._fc.remove(obj);
|
||||
} else {
|
||||
obj.__version -= 1;
|
||||
obj.setOptions(JSON.parse(prevState));
|
||||
obj.setCoords();
|
||||
this._fc.renderAll();
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a redo operation on canvas, if it cannot redo it will leave the canvas intact
|
||||
*/
|
||||
redo = () => {
|
||||
let history = this._history;
|
||||
if (history.canRedo()) {
|
||||
let canvas = this._fc;
|
||||
//noinspection Eslint
|
||||
let [obj, prevState, currState] = history.redo();
|
||||
if (obj.__version === 0) {
|
||||
this.setState({ action: false }, () => {
|
||||
canvas.add(obj);
|
||||
obj.__version = 1;
|
||||
});
|
||||
} else {
|
||||
obj.__version += 1;
|
||||
obj.setOptions(JSON.parse(currState));
|
||||
}
|
||||
obj.setCoords();
|
||||
canvas.renderAll();
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegation method to check if we can perform an undo Operation, useful to disable/enable possible buttons
|
||||
*
|
||||
* @returns {*} true if we can undo otherwise false
|
||||
*/
|
||||
canUndo = () => {
|
||||
return this._history.canUndo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegation method to check if we can perform a redo Operation, useful to disable/enable possible buttons
|
||||
*
|
||||
* @returns {*} true if we can redo otherwise false
|
||||
*/
|
||||
canRedo = () => {
|
||||
return this._history.canRedo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
|
||||
*
|
||||
* Available Options are
|
||||
* <table style="width:100%">
|
||||
*
|
||||
* <tr><td><b>Name</b></td><td><b>Type</b></td><td><b>Argument</b></td><td><b>Default</b></td><td><b>Description</b></td></tr>
|
||||
* <tr><td>format</td> <td>String</td> <td><optional></td><td>png</td><td>The format of the output image. Either "jpeg" or "png"</td></tr>
|
||||
* <tr><td>quality</td><td>Number</td><td><optional></td><td>1</td><td>Quality level (0..1). Only used for jpeg.</td></tr>
|
||||
* <tr><td>multiplier</td><td>Number</td><td><optional></td><td>1</td><td>Multiplier to scale by</td></tr>
|
||||
* <tr><td>left</td><td>Number</td><td><optional></td><td></td><td>Cropping left offset. Introduced in v1.2.14</td></tr>
|
||||
* <tr><td>top</td><td>Number</td><td><optional></td><td></td><td>Cropping top offset. Introduced in v1.2.14</td></tr>
|
||||
* <tr><td>width</td><td>Number</td><td><optional></td><td></td><td>Cropping width. Introduced in v1.2.14</td></tr>
|
||||
* <tr><td>height</td><td>Number</td><td><optional></td><td></td><td>Cropping height. Introduced in v1.2.14</td></tr>
|
||||
*
|
||||
* </table>
|
||||
*
|
||||
* @returns {String} URL containing a representation of the object in the format specified by options.format
|
||||
*/
|
||||
toDataURL = (options) => this._fc.toDataURL(options);
|
||||
|
||||
/**
|
||||
* Returns JSON representation of canvas
|
||||
*
|
||||
* @param propertiesToInclude Array <optional> Any properties that you might want to additionally include in the output
|
||||
* @returns {string} JSON string
|
||||
*/
|
||||
toJSON = (propertiesToInclude) => this._fc.toJSON(propertiesToInclude);
|
||||
|
||||
/**
|
||||
* Populates canvas with data from the specified JSON.
|
||||
*
|
||||
* JSON format must conform to the one of fabric.Canvas#toDatalessJSON
|
||||
*
|
||||
* @param json JSON string or object
|
||||
*/
|
||||
fromJSON = (json) => {
|
||||
if (!json) return;
|
||||
let canvas = this._fc;
|
||||
setTimeout(() => {
|
||||
canvas.loadFromJSON(json, () => {
|
||||
if (this.props.tool === Tool.DefaultTool) {
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
}
|
||||
canvas.renderAll();
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the content of the canvas, this will also clear history but will return the canvas content as JSON to be
|
||||
* used as needed in order to undo the clear if possible
|
||||
*
|
||||
* @param propertiesToInclude Array <optional> Any properties that you might want to additionally include in the output
|
||||
* @returns {string} JSON string of the canvas just cleared
|
||||
*/
|
||||
clear = (propertiesToInclude) => {
|
||||
let discarded = this.toJSON(propertiesToInclude);
|
||||
this._fc.clear();
|
||||
this._history.clear();
|
||||
return discarded;
|
||||
};
|
||||
|
||||
hasSelection = () => {
|
||||
let canvas = this._fc;
|
||||
return !!canvas.getActiveObject();
|
||||
};
|
||||
|
||||
clearSelection = () => {
|
||||
let canvas = this._fc;
|
||||
canvas.discardActiveObject();
|
||||
canvas.requestRenderAll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove selected object from the canvas
|
||||
*/
|
||||
removeSelected = () => {
|
||||
let canvas = this._fc;
|
||||
let activeObj = canvas.getActiveObject();
|
||||
if (activeObj) {
|
||||
let selected = [];
|
||||
if (activeObj.type === "activeSelection") {
|
||||
activeObj.forEachObject((obj) => selected.push(obj));
|
||||
} else {
|
||||
selected.push(activeObj);
|
||||
}
|
||||
selected.forEach((obj) => {
|
||||
obj.__removed = true;
|
||||
let objState = obj.toJSON();
|
||||
obj.__originalState = objState;
|
||||
let state = JSON.stringify(objState);
|
||||
this._history.keep([obj, state, state]);
|
||||
canvas.remove(obj);
|
||||
});
|
||||
canvas.discardActiveObject();
|
||||
canvas.requestRenderAll();
|
||||
}
|
||||
};
|
||||
|
||||
copy = () => {
|
||||
let canvas = this._fc;
|
||||
canvas.getActiveObject().clone((cloned) => (this._clipboard = cloned));
|
||||
};
|
||||
|
||||
paste = () => {
|
||||
// clone again, so you can do multiple copies.
|
||||
this._clipboard.clone((clonedObj) => {
|
||||
let canvas = this._fc;
|
||||
canvas.discardActiveObject();
|
||||
clonedObj.set({
|
||||
left: clonedObj.left + 10,
|
||||
top: clonedObj.top + 10,
|
||||
evented: true,
|
||||
});
|
||||
if (clonedObj.type === "activeSelection") {
|
||||
// active selection needs a reference to the canvas.
|
||||
clonedObj.canvas = canvas;
|
||||
clonedObj.forEachObject((obj) => canvas.add(obj));
|
||||
clonedObj.setCoords();
|
||||
} else {
|
||||
canvas.add(clonedObj);
|
||||
}
|
||||
this._clipboard.top += 10;
|
||||
this._clipboard.left += 10;
|
||||
canvas.setActiveObject(clonedObj);
|
||||
canvas.requestRenderAll();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the background from the dataUrl given
|
||||
*
|
||||
* @param dataUrl the dataUrl to be used as a background
|
||||
* @param options
|
||||
*/
|
||||
setBackgroundFromDataUrl = (dataUrl, options = {}) => {
|
||||
let canvas = this._fc;
|
||||
let img = new Image();
|
||||
img.setAttribute("crossOrigin", "anonymous");
|
||||
const { stretched, stretchedX, stretchedY, ...fabricOptions } = options;
|
||||
img.onload = () => {
|
||||
const imgObj = new fabric.Image(img);
|
||||
if (stretched || stretchedX) imgObj.scaleToWidth(canvas.width);
|
||||
if (stretched || stretchedY) imgObj.scaleToHeight(canvas.height);
|
||||
canvas.setBackgroundImage(imgObj, () => canvas.renderAll(), fabricOptions);
|
||||
};
|
||||
img.src = dataUrl;
|
||||
};
|
||||
|
||||
addText = (text, options = {}) => {
|
||||
let canvas = this._fc;
|
||||
let iText = new fabric.IText(text, options);
|
||||
let opts = {
|
||||
left: (canvas.getWidth() - iText.width) * 0.5,
|
||||
top: (canvas.getHeight() - iText.height) * 0.5,
|
||||
};
|
||||
Object.assign(options, opts);
|
||||
iText.set({
|
||||
left: options.left,
|
||||
top: options.top,
|
||||
});
|
||||
|
||||
canvas.add(iText);
|
||||
};
|
||||
|
||||
callEvent = (e, eventFunction) => {
|
||||
if (this._selectedTool) eventFunction(e);
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
let { tool, value, undoSteps, defaultValue, backgroundColor } = this.props;
|
||||
|
||||
let canvas = (this._fc = new fabric.Canvas(
|
||||
this._canvas /*, {
|
||||
preserveObjectStacking: false,
|
||||
renderOnAddRemove: false,
|
||||
skipTargetFind: true
|
||||
}*/
|
||||
));
|
||||
|
||||
this._initTools(canvas);
|
||||
|
||||
// set initial backgroundColor
|
||||
this._backgroundColor(backgroundColor);
|
||||
|
||||
let selectedTool = this._tools[tool];
|
||||
if (selectedTool) selectedTool.configureCanvas(this.props);
|
||||
this._selectedTool = selectedTool;
|
||||
|
||||
// Control resize
|
||||
window.addEventListener("resize", this._resize, false);
|
||||
|
||||
// Initialize History, with maximum number of undo steps
|
||||
this._history = new History(undoSteps);
|
||||
|
||||
// Events binding
|
||||
canvas.on("object:added", (e) => this.callEvent(e, this._onObjectAdded));
|
||||
canvas.on("object:modified", (e) => this.callEvent(e, this._onObjectModified));
|
||||
canvas.on("object:removed", (e) => this.callEvent(e, this._onObjectRemoved));
|
||||
canvas.on("mouse:down", (e) => this.callEvent(e, this._onMouseDown));
|
||||
canvas.on("mouse:move", (e) => this.callEvent(e, this._onMouseMove));
|
||||
canvas.on("mouse:up", (e) => this.callEvent(e, this._onMouseUp));
|
||||
canvas.on("mouse:out", (e) => this.callEvent(e, this._onMouseOut));
|
||||
canvas.on("object:moving", (e) => this.callEvent(e, this._onObjectMoving));
|
||||
canvas.on("object:scaling", (e) => this.callEvent(e, this._onObjectScaling));
|
||||
canvas.on("object:rotating", (e) => this.callEvent(e, this._onObjectRotating));
|
||||
// IText Events fired on Adding Text
|
||||
// canvas.on("text:event:changed", console.log)
|
||||
// canvas.on("text:selection:changed", console.log)
|
||||
// canvas.on("text:editing:entered", console.log)
|
||||
// canvas.on("text:editing:exited", console.log)
|
||||
|
||||
this.disableTouchScroll();
|
||||
|
||||
this._resize();
|
||||
|
||||
// initialize canvas with controlled value if exists
|
||||
(value || defaultValue) && this.fromJSON(value || defaultValue);
|
||||
};
|
||||
|
||||
componentWillUnmount = () => window.removeEventListener("resize", this._resize);
|
||||
|
||||
componentDidUpdate = (prevProps, prevState) => {
|
||||
if (
|
||||
this.props.width !== prevProps.width ||
|
||||
this.props.height !== prevProps.height
|
||||
) {
|
||||
this._resize();
|
||||
}
|
||||
|
||||
if (this.props.tool !== prevProps.tool) {
|
||||
this._selectedTool = this._tools[this.props.tool];
|
||||
//Bring the cursor back to default if it is changed by a tool
|
||||
this._fc.defaultCursor = "default";
|
||||
if (this._selectedTool) {
|
||||
this._selectedTool.configureCanvas(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.backgroundColor !== prevProps.backgroundColor) {
|
||||
this._backgroundColor(this.props.backgroundColor);
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.value !== prevProps.value ||
|
||||
(this.props.value && this.props.forceValue)
|
||||
) {
|
||||
this.fromJSON(this.props.value);
|
||||
}
|
||||
};
|
||||
|
||||
render = () => {
|
||||
let { className, style, width, height } = this.props;
|
||||
|
||||
let canvasDivStyle = Object.assign(
|
||||
{},
|
||||
style ? style : {},
|
||||
width ? { width: width } : {},
|
||||
height ? { height: height } : { height: 512 }
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
ref={(c) => (this._container = c)}
|
||||
style={canvasDivStyle}>
|
||||
<canvas id={uuid4()} ref={(c) => (this._canvas = c)}>
|
||||
Sorry, Canvas HTML5 element is not supported by your browser :(
|
||||
</canvas>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default SketchField;
|
215
frontend/src/vendor/ReactSketch/components/SketchField/SketchField.spec.js
vendored
Normal file
215
frontend/src/vendor/ReactSketch/components/SketchField/SketchField.spec.js
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
/* global expect, describe,it */
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-env node */
|
||||
|
||||
import React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import SketchField from "./index";
|
||||
|
||||
function objectFromDrag(canvas, from = { x: 0, y: 0 }, to = { x: 10, y: 10 }, id) {
|
||||
function MouseEventPositionGenerator(pos = { x: 0, y: 0 }) {
|
||||
const eventX = ["x", "pageX", "screenX", "clientX", "offsetX"];
|
||||
const eventY = ["y", "pageY", "screenY", "clientY", "offsetY"];
|
||||
const generated = {};
|
||||
eventX.forEach((key) => (generated[key] = pos.x));
|
||||
eventY.forEach((key) => (generated[key] = pos.y));
|
||||
return generated;
|
||||
}
|
||||
|
||||
canvas.trigger("mouse:down", { e: MouseEventPositionGenerator(from) });
|
||||
canvas.trigger("mouse:move", { e: MouseEventPositionGenerator(to) });
|
||||
canvas.trigger("mouse:up", { e: MouseEventPositionGenerator(to) });
|
||||
// Get the last object as the last created object
|
||||
const objects = canvas.getObjects();
|
||||
const newObj = objects[objects.length - 1];
|
||||
id && (newObj.id = id);
|
||||
return newObj;
|
||||
}
|
||||
|
||||
describe("SketchField", () => {
|
||||
it("Loads Normally", () => {
|
||||
require("./index");
|
||||
});
|
||||
|
||||
it("Contains canvas tag", () => {
|
||||
const sketch = mount(<SketchField />);
|
||||
expect(sketch.getDOMNode("canvas")).toBeDefined();
|
||||
});
|
||||
|
||||
it("Drag to create rectangle", () => {
|
||||
const sketch = mount(<SketchField tool="rectangle" />);
|
||||
const canvas = sketch.instance()._fc;
|
||||
expect(canvas).toBeDefined();
|
||||
|
||||
const startPt = { x: 10, y: 10 };
|
||||
const endPt = { x: 40, y: 50 };
|
||||
const bounding = {
|
||||
left: startPt.x,
|
||||
top: startPt.y,
|
||||
width: endPt.x - startPt.x,
|
||||
height: endPt.y - startPt.y,
|
||||
};
|
||||
|
||||
// From left-top to right-bottom
|
||||
objectFromDrag(canvas, startPt, endPt);
|
||||
|
||||
// Check the rectangle existed
|
||||
expect(canvas.getObjects()[0]).toBeDefined();
|
||||
const rect1 = canvas.getObjects()[0];
|
||||
expect(rect1.type).toEqual("rect");
|
||||
|
||||
// Check the rectangle dimension
|
||||
expect({
|
||||
left: rect1.left,
|
||||
top: rect1.top,
|
||||
width: rect1.width,
|
||||
height: rect1.height,
|
||||
}).toEqual(bounding);
|
||||
|
||||
canvas.remove(rect1);
|
||||
// From right-bottom to left-top;
|
||||
objectFromDrag(canvas, endPt, startPt);
|
||||
const rect2 = canvas.getObjects()[0];
|
||||
expect(rect2.type).toEqual("rect");
|
||||
|
||||
// Check the rectangle dimension
|
||||
expect({
|
||||
left: rect2.left,
|
||||
top: rect2.top,
|
||||
width: rect2.width,
|
||||
height: rect2.height,
|
||||
}).toEqual(bounding);
|
||||
});
|
||||
|
||||
it("Undo/Redo for multiple rectangles add to canvas", () => {
|
||||
const sketch = mount(<SketchField tool="rectangle" />).instance();
|
||||
const canvas = sketch._fc;
|
||||
expect(canvas).toBeDefined();
|
||||
|
||||
const startPt = { x: 10, y: 10 };
|
||||
const endPt = { x: 40, y: 50 };
|
||||
|
||||
canvas.renderOnAddRemove = false;
|
||||
objectFromDrag(canvas, startPt, endPt, "rect1");
|
||||
objectFromDrag(canvas, startPt, endPt, "rect2");
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1", "rect2"]);
|
||||
|
||||
sketch.undo();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
|
||||
|
||||
sketch.undo();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual([]);
|
||||
|
||||
sketch.redo();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
|
||||
|
||||
objectFromDrag(canvas, startPt, endPt, "rect3");
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1", "rect3"]);
|
||||
|
||||
sketch.undo();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
|
||||
});
|
||||
|
||||
it("Undo/Redo for multiple modification for single rectangle", () => {
|
||||
const sketch = mount(<SketchField tool="rectangle" />).instance();
|
||||
const canvas = sketch._fc;
|
||||
expect(canvas).toBeDefined();
|
||||
|
||||
const startPt = { x: 10, y: 10 };
|
||||
const endPt = { x: 40, y: 50 };
|
||||
|
||||
// [Action1] Add new rectange object and save its state
|
||||
const stateStack = [];
|
||||
const rect = objectFromDrag(canvas, startPt, endPt);
|
||||
stateStack.push(rect.toJSON());
|
||||
|
||||
// [Action2] Change rectangle dimension and save its state
|
||||
rect.set({ width: 50, height: 60 });
|
||||
rect.setCoords();
|
||||
canvas.trigger("object:modified", { target: rect });
|
||||
stateStack.push(rect.toJSON());
|
||||
|
||||
// [Action3] Change the position and save its state
|
||||
rect.set({ left: 20, top: 70 });
|
||||
rect.setCoords();
|
||||
canvas.trigger("object:modified", { target: rect });
|
||||
stateStack.push(rect.toJSON());
|
||||
|
||||
// Undo Action3
|
||||
sketch.undo();
|
||||
void (function () {
|
||||
const obj = canvas.getObjects()[0];
|
||||
expect(obj.toJSON()).toEqual(stateStack[1]);
|
||||
})();
|
||||
|
||||
// Undo Action2
|
||||
sketch.undo();
|
||||
void (function () {
|
||||
const obj = canvas.getObjects()[0];
|
||||
expect(obj.toJSON()).toEqual(stateStack[0]);
|
||||
})();
|
||||
|
||||
// Undo Action1, and then redo Action1
|
||||
sketch.undo();
|
||||
sketch.redo();
|
||||
void (function () {
|
||||
const obj = canvas.getObjects()[0];
|
||||
expect(obj.toJSON()).toEqual(stateStack[0]);
|
||||
})();
|
||||
|
||||
// redo Action2
|
||||
sketch.redo();
|
||||
void (function () {
|
||||
const obj = canvas.getObjects()[0];
|
||||
expect(obj.toJSON()).toEqual(stateStack[1]);
|
||||
})();
|
||||
|
||||
// redo Action3
|
||||
sketch.redo();
|
||||
void (function () {
|
||||
const obj = canvas.getObjects()[0];
|
||||
expect(obj.toJSON()).toEqual(stateStack[2]);
|
||||
})();
|
||||
});
|
||||
|
||||
it("Removes selected object", () => {
|
||||
const sketch = mount(<SketchField tool="rectangle" />).instance();
|
||||
const canvas = sketch._fc;
|
||||
expect(canvas).toBeDefined();
|
||||
|
||||
const startPt = { x: 10, y: 10 };
|
||||
const endPt = { x: 40, y: 50 };
|
||||
|
||||
canvas.renderOnAddRemove = false;
|
||||
objectFromDrag(canvas, startPt, endPt, "rect1");
|
||||
objectFromDrag(canvas, startPt, endPt, "rect2");
|
||||
canvas.setActiveObject(canvas.getObjects()[0]);
|
||||
|
||||
sketch.removeSelected();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect2"]);
|
||||
|
||||
sketch.undo();
|
||||
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect2", "rect1"]);
|
||||
});
|
||||
|
||||
it("Copy/Paste selected object", () => {
|
||||
const sketch = mount(<SketchField tool="rectangle" />).instance();
|
||||
const canvas = sketch._fc;
|
||||
expect(canvas).toBeDefined();
|
||||
|
||||
const startPt = { x: 10, y: 10 };
|
||||
const endPt = { x: 40, y: 50 };
|
||||
|
||||
canvas.renderOnAddRemove = false;
|
||||
objectFromDrag(canvas, startPt, endPt, "rect1");
|
||||
objectFromDrag(canvas, startPt, endPt, "rect2");
|
||||
canvas.setActiveObject(canvas.getObjects()[0]);
|
||||
|
||||
expect(canvas.getObjects().length).toEqual(2);
|
||||
|
||||
sketch.copy();
|
||||
sketch.paste();
|
||||
|
||||
expect(canvas.getObjects().length).toEqual(3);
|
||||
});
|
||||
});
|
4
frontend/src/vendor/ReactSketch/components/SketchField/index.js
vendored
Normal file
4
frontend/src/vendor/ReactSketch/components/SketchField/index.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import SketchField from "./SketchField";
|
||||
|
||||
export { SketchField };
|
||||
export default SketchField;
|
90
frontend/src/vendor/ReactSketch/history/History.spec.js
vendored
Normal file
90
frontend/src/vendor/ReactSketch/history/History.spec.js
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-env node, mocha */
|
||||
|
||||
import History from "./index";
|
||||
|
||||
describe("History", () => {
|
||||
it("Loads Normally", () => {
|
||||
require("./index");
|
||||
});
|
||||
|
||||
it("Undo limit is set", () => {
|
||||
const instance = new History();
|
||||
const instanceWithCustomUndoSteps = new History(15);
|
||||
expect(instance.getUndoLimit()).toEqual(10);
|
||||
expect(instanceWithCustomUndoSteps.getUndoLimit()).toEqual(15);
|
||||
});
|
||||
|
||||
it("Informs if can undo", () => {
|
||||
const instance = new History();
|
||||
expect(instance.canUndo()).toBeFalsy();
|
||||
instance.keep("1");
|
||||
expect(instance.canUndo()).toBeTruthy();
|
||||
instance.keep("2");
|
||||
expect(instance.canUndo()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Can undo/redo object", () => {
|
||||
const instance = new History(15, true);
|
||||
instance.keep("1");
|
||||
instance.keep("2");
|
||||
expect(instance.canUndo()).toBeTruthy();
|
||||
expect(instance.getCurrent()).toEqual("2");
|
||||
expect(instance.undo()).toEqual("1");
|
||||
expect(instance.undo()).toBeNull();
|
||||
expect(instance.getCurrent()).toBeNull();
|
||||
expect(instance.redo()).toEqual("1");
|
||||
expect(instance.getCurrent()).toEqual("1");
|
||||
});
|
||||
|
||||
it("Multiple undo/redo of objects", () => {
|
||||
const instance = new History();
|
||||
instance.keep("1");
|
||||
instance.keep("2");
|
||||
instance.keep("3");
|
||||
instance.keep("4");
|
||||
instance.keep("5");
|
||||
expect(instance.canUndo()).toBeTruthy();
|
||||
expect(instance.undo()).toEqual("4");
|
||||
expect(instance.undo()).toEqual("3");
|
||||
expect(instance.undo()).toEqual("2");
|
||||
expect(instance.undo()).toEqual("1");
|
||||
expect(instance.undo()).toBeNull();
|
||||
expect(instance.redo()).toEqual("1");
|
||||
expect(instance.redo()).toEqual("2");
|
||||
expect(instance.redo()).toEqual("3");
|
||||
expect(instance.redo()).toEqual("4");
|
||||
expect(instance.redo()).toEqual("5");
|
||||
expect(instance.redo()).toBeNull();
|
||||
});
|
||||
|
||||
it("Redo is reset after a keep of a new object", () => {
|
||||
const instance = new History();
|
||||
instance.keep("1");
|
||||
instance.keep("2");
|
||||
instance.keep("3");
|
||||
expect(instance.canUndo()).toBeTruthy();
|
||||
expect(instance.canRedo()).toBeFalsy();
|
||||
expect(instance.undo()).toEqual("2");
|
||||
expect(instance.canRedo()).toBeTruthy();
|
||||
instance.keep("4");
|
||||
expect(instance.canRedo()).toBeFalsy();
|
||||
expect(instance.redo()).toBeNull();
|
||||
});
|
||||
|
||||
it("Can clear history", () => {
|
||||
const instance = new History();
|
||||
instance.keep("1");
|
||||
instance.keep("2");
|
||||
instance.keep("3");
|
||||
expect(instance.undo()).toEqual("2");
|
||||
expect(instance.redo()).toEqual("3");
|
||||
instance.clear();
|
||||
expect(instance.canUndo()).toBeFalsy();
|
||||
expect(instance.canRedo()).toBeFalsy();
|
||||
expect(instance.undo()).toBeNull();
|
||||
expect(instance.redo()).toBeNull();
|
||||
instance.undo();
|
||||
instance.redo();
|
||||
});
|
||||
});
|
135
frontend/src/vendor/ReactSketch/history/index.js
vendored
Normal file
135
frontend/src/vendor/ReactSketch/history/index.js
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Maintains the history of an object
|
||||
*/
|
||||
class History {
|
||||
constructor(undoLimit = 10, debug = false) {
|
||||
this.undoLimit = undoLimit;
|
||||
this.undoList = [];
|
||||
this.redoList = [];
|
||||
this.current = null;
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the limit of undo/redo actions
|
||||
*
|
||||
* @returns {number|*} the undo limit, as it is configured when constructing the history instance
|
||||
*/
|
||||
getUndoLimit() {
|
||||
return this.undoLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Current state
|
||||
*
|
||||
* @returns {null|*}
|
||||
*/
|
||||
getCurrent() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep an object to history
|
||||
*
|
||||
* This method will set the object as current value and will push the previous "current" object to the undo history
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
keep(obj) {
|
||||
try {
|
||||
this.redoList = [];
|
||||
if (this.current) {
|
||||
this.undoList.push(this.current);
|
||||
}
|
||||
if (this.undoList.length > this.undoLimit) {
|
||||
this.undoList.shift();
|
||||
}
|
||||
this.current = obj;
|
||||
} finally {
|
||||
this.print();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the last object, this operation will set the current object to one step back in time
|
||||
*
|
||||
* @returns the new current value after the undo operation, else null if no undo operation was possible
|
||||
*/
|
||||
undo() {
|
||||
try {
|
||||
if (this.current) {
|
||||
this.redoList.push(this.current);
|
||||
if (this.redoList.length > this.undoLimit) {
|
||||
this.redoList.shift();
|
||||
}
|
||||
if (this.undoList.length === 0) this.current = null;
|
||||
}
|
||||
if (this.undoList.length > 0) {
|
||||
this.current = this.undoList.pop();
|
||||
return this.current;
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
this.print();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redo the last object, redo happens only if no keep operations have been performed
|
||||
*
|
||||
* @returns the new current value after the redo operation, or null if no redo operation was possible
|
||||
*/
|
||||
redo() {
|
||||
try {
|
||||
if (this.redoList.length > 0) {
|
||||
if (this.current) this.undoList.push(this.current);
|
||||
this.current = this.redoList.pop();
|
||||
return this.current;
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
this.print();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we can perform a redo operation
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canRedo() {
|
||||
return this.redoList.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we can perform an undo operation
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canUndo() {
|
||||
return this.undoList.length > 0 || this.current !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the history maintained, can be undone
|
||||
*/
|
||||
clear() {
|
||||
this.undoList = [];
|
||||
this.redoList = [];
|
||||
this.current = null;
|
||||
this.print();
|
||||
}
|
||||
|
||||
print() {
|
||||
if (this.debug) {
|
||||
/* eslint-disable no-console */
|
||||
console.log(
|
||||
this.undoList,
|
||||
" -> " + this.current + " <- ",
|
||||
this.redoList.slice(0).reverse()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default History;
|
11
frontend/src/vendor/ReactSketch/index.js
vendored
Normal file
11
frontend/src/vendor/ReactSketch/index.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import "./polyfill";
|
||||
import SketchField from "./components/SketchField";
|
||||
import Tools from "./tools";
|
||||
|
||||
export { SketchField };
|
||||
export { Tools };
|
||||
|
||||
export default {
|
||||
SketchField,
|
||||
Tools,
|
||||
};
|
30
frontend/src/vendor/ReactSketch/polyfill.js
vendored
Normal file
30
frontend/src/vendor/ReactSketch/polyfill.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Object.assign() polyfill for IE11
|
||||
* @see <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign>
|
||||
*/
|
||||
|
||||
if (typeof Object.assign != "function") {
|
||||
Object.defineProperty(Object, "assign", {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
value: function assign(target, varArgs) {
|
||||
"use strict";
|
||||
if (target == null) {
|
||||
throw new TypeError("Cannot convert undefined or null to object");
|
||||
}
|
||||
let to = Object(target);
|
||||
for (let index = 1; index < arguments.length; index++) {
|
||||
let nextSource = arguments[index];
|
||||
if (nextSource != null) {
|
||||
for (let nextKey in nextSource) {
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
},
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
12
frontend/src/vendor/ReactSketch/tools.js
vendored
Normal file
12
frontend/src/vendor/ReactSketch/tools.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
Circle: "circle",
|
||||
Line: "line",
|
||||
Arrow: "arrow",
|
||||
Pencil: "pencil",
|
||||
Rectangle: "rectangle",
|
||||
RectangleLabel: "rectangle-label",
|
||||
Select: "select",
|
||||
Pan: "pan",
|
||||
Highlighter: "highlighter",
|
||||
DefaultTool: "default-tool",
|
||||
};
|
82
frontend/src/vendor/ReactSketch/tools/Arrow/index.js
vendored
Normal file
82
frontend/src/vendor/ReactSketch/tools/Arrow/index.js
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class Index extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
this._width = props.lineWidth;
|
||||
this._color = props.lineColor;
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
this.isDown = true;
|
||||
let canvas = this._canvas;
|
||||
var pointer = canvas.getPointer(o.e);
|
||||
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
|
||||
this.line = new fabric.Line(points, {
|
||||
strokeWidth: this._width,
|
||||
fill: this._color,
|
||||
stroke: this._color,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
selectable: false,
|
||||
evented: false,
|
||||
});
|
||||
|
||||
this.head = new fabric.Triangle({
|
||||
fill: this._color,
|
||||
left: pointer.x,
|
||||
top: pointer.y,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
height: 3 * this._width,
|
||||
width: 3 * this._width,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
angle: 90,
|
||||
});
|
||||
|
||||
canvas.add(this.line);
|
||||
canvas.add(this.head);
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
var pointer = canvas.getPointer(o.e);
|
||||
this.line.set({ x2: pointer.x, y2: pointer.y });
|
||||
this.line.setCoords();
|
||||
|
||||
let x_delta = pointer.x - this.line.x1;
|
||||
let y_delta = pointer.y - this.line.y1;
|
||||
|
||||
this.head.set({
|
||||
left: pointer.x,
|
||||
top: pointer.y,
|
||||
angle: 90 + (Math.atan2(y_delta, x_delta) * 180) / Math.PI,
|
||||
});
|
||||
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
let canvas = this._canvas;
|
||||
|
||||
canvas.remove(this.line);
|
||||
canvas.remove(this.head);
|
||||
let arrow = new fabric.Group([this.line, this.head]);
|
||||
canvas.add(arrow);
|
||||
}
|
||||
|
||||
doMouseOut(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Index;
|
61
frontend/src/vendor/ReactSketch/tools/Circle/index.js
vendored
Normal file
61
frontend/src/vendor/ReactSketch/tools/Circle/index.js
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
import { linearDistance } from "../../utils";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class Circle extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
this._width = props.lineWidth;
|
||||
this._color = props.lineColor;
|
||||
this._fill = props.fillColor;
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
let canvas = this._canvas;
|
||||
this.isDown = true;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
[this.startX, this.startY] = [pointer.x, pointer.y];
|
||||
this.circle = new fabric.Circle({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
originX: "left",
|
||||
originY: "center",
|
||||
strokeWidth: this._width,
|
||||
stroke: this._color,
|
||||
fill: this._fill,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
radius: 1,
|
||||
});
|
||||
canvas.add(this.circle);
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
this.circle.set({
|
||||
radius:
|
||||
linearDistance(
|
||||
{ x: this.startX, y: this.startY },
|
||||
{ x: pointer.x, y: pointer.y }
|
||||
) / 2,
|
||||
angle:
|
||||
(Math.atan2(pointer.y - this.startY, pointer.x - this.startX) * 180) /
|
||||
Math.PI,
|
||||
});
|
||||
this.circle.setCoords();
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Circle;
|
15
frontend/src/vendor/ReactSketch/tools/Highlighter/index.js
vendored
Normal file
15
frontend/src/vendor/ReactSketch/tools/Highlighter/index.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
import { hexToRgbA, colorNameToHex } from "../../utils";
|
||||
|
||||
class Highlighter extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
this._canvas.isDrawingMode = true;
|
||||
this._canvas.freeDrawingBrush.width = props.lineWidth;
|
||||
this._canvas.freeDrawingBrush.color =
|
||||
props.lineColor.indexOf("#") > -1
|
||||
? hexToRgbA(props.lineColor)
|
||||
: hexToRgbA(colorNameToHex(props.lineColor));
|
||||
}
|
||||
}
|
||||
|
||||
export default Highlighter;
|
51
frontend/src/vendor/ReactSketch/tools/Line/index.js
vendored
Normal file
51
frontend/src/vendor/ReactSketch/tools/Line/index.js
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class Line extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
this._width = props.lineWidth;
|
||||
this._color = props.lineColor;
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
this.isDown = true;
|
||||
let canvas = this._canvas;
|
||||
var pointer = canvas.getPointer(o.e);
|
||||
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
|
||||
this.line = new fabric.Line(points, {
|
||||
strokeWidth: this._width,
|
||||
fill: this._color,
|
||||
stroke: this._color,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
selectable: false,
|
||||
evented: false,
|
||||
});
|
||||
canvas.add(this.line);
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
var pointer = canvas.getPointer(o.e);
|
||||
this.line.set({ x2: pointer.x, y2: pointer.y });
|
||||
this.line.setCoords();
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
|
||||
doMouseOut(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Line;
|
41
frontend/src/vendor/ReactSketch/tools/Pan/index.js
vendored
Normal file
41
frontend/src/vendor/ReactSketch/tools/Pan/index.js
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class Pan extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
//Change the cursor to the move grabber
|
||||
canvas.defaultCursor = "move";
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
let canvas = this._canvas;
|
||||
this.isDown = true;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
this.startX = pointer.x;
|
||||
this.startY = pointer.y;
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
|
||||
canvas.relativePan({
|
||||
x: pointer.x - this.startX,
|
||||
y: pointer.y - this.startY,
|
||||
});
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Pan;
|
11
frontend/src/vendor/ReactSketch/tools/Pencil/index.js
vendored
Normal file
11
frontend/src/vendor/ReactSketch/tools/Pencil/index.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
class Pencil extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
this._canvas.isDrawingMode = true;
|
||||
this._canvas.freeDrawingBrush.width = props.lineWidth;
|
||||
this._canvas.freeDrawingBrush.color = props.lineColor;
|
||||
}
|
||||
}
|
||||
|
||||
export default Pencil;
|
64
frontend/src/vendor/ReactSketch/tools/Rectangle/index.js
vendored
Normal file
64
frontend/src/vendor/ReactSketch/tools/Rectangle/index.js
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class Rectangle extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
this._width = props.lineWidth;
|
||||
this._color = props.lineColor;
|
||||
this._fill = props.fillColor;
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
let canvas = this._canvas;
|
||||
this.isDown = true;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
this.startX = pointer.x;
|
||||
this.startY = pointer.y;
|
||||
this.rect = new fabric.Rect({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
width: pointer.x - this.startX,
|
||||
height: pointer.y - this.startY,
|
||||
stroke: this._color,
|
||||
strokeWidth: this._width,
|
||||
fill: this._fill,
|
||||
transparentCorners: false,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
strokeUniform: true,
|
||||
noScaleCache: false,
|
||||
angle: 0,
|
||||
});
|
||||
canvas.add(this.rect);
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
if (this.startX > pointer.x) {
|
||||
this.rect.set({ left: Math.abs(pointer.x) });
|
||||
}
|
||||
if (this.startY > pointer.y) {
|
||||
this.rect.set({ top: Math.abs(pointer.y) });
|
||||
}
|
||||
this.rect.set({ width: Math.abs(this.startX - pointer.x) });
|
||||
this.rect.set({ height: Math.abs(this.startY - pointer.y) });
|
||||
this.rect.setCoords();
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Rectangle;
|
35
frontend/src/vendor/ReactSketch/tools/Rectangle/rectangle-label-object.js
vendored
Normal file
35
frontend/src/vendor/ReactSketch/tools/Rectangle/rectangle-label-object.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class RectangleLabelObject {
|
||||
constructor(canvas, text, rectProps, textProps) {
|
||||
this._canvas = canvas;
|
||||
this._text = text;
|
||||
this._rectObj = new fabric.Rect(rectProps);
|
||||
this._textObj = new fabric.Textbox(text, textProps);
|
||||
canvas.on({ "object:scaling": this.update });
|
||||
canvas.on({ "object:moving": this.update });
|
||||
}
|
||||
|
||||
update = (e) => {
|
||||
//e.target.set({scaleX:1, scaleY:1})
|
||||
if (!this._textObj || !this._rectObj) return;
|
||||
if (e.target === this._rectObj) {
|
||||
this._textObj.set({
|
||||
width: this._rectObj.getScaledWidth(),
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
top: this._rectObj.top - this._textObj.getScaledHeight(),
|
||||
left: this._rectObj.left,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
setText(text) {
|
||||
this._text = text;
|
||||
this._textObj.set({ text });
|
||||
}
|
||||
}
|
||||
|
||||
export default RectangleLabelObject;
|
114
frontend/src/vendor/ReactSketch/tools/Rectangle/rectangle-label.js
vendored
Normal file
114
frontend/src/vendor/ReactSketch/tools/Rectangle/rectangle-label.js
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
import RectangleLabelObject from "./rectangle-label-object";
|
||||
|
||||
class RectangleLabel extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
this._width = props.lineWidth;
|
||||
this._color = props.lineColor;
|
||||
this._fill = props.fillColor;
|
||||
this._textString = props.text;
|
||||
this._maxFontSize = 12;
|
||||
}
|
||||
|
||||
doMouseDown(o) {
|
||||
let canvas = this._canvas;
|
||||
this.isDown = true;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
this.startX = pointer.x;
|
||||
this.startY = pointer.y;
|
||||
this.rectangleLabel = new RectangleLabelObject(
|
||||
canvas,
|
||||
"New drawing",
|
||||
{
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
width: pointer.x - this.startX,
|
||||
height: pointer.y - this.startY,
|
||||
stroke: this._color,
|
||||
strokeWidth: this._width,
|
||||
fill: this._fill,
|
||||
transparentCorners: false,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
strokeUniform: true,
|
||||
noScaleCache: false,
|
||||
angle: 0,
|
||||
},
|
||||
{
|
||||
left: this.startX,
|
||||
top: this.startY - 12,
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
width: pointer.x - this.startX - this._width,
|
||||
height: canvas.height / 3,
|
||||
fontSize: this._maxFontSize,
|
||||
noScaleCache: false,
|
||||
backgroundColor: this._color,
|
||||
transparentCorners: true,
|
||||
hasControls: false,
|
||||
angle: 0,
|
||||
}
|
||||
);
|
||||
|
||||
if (this._objects && this._objects.length > 0)
|
||||
this._objects.push(this.rectangleLabel);
|
||||
else this._objects = [this.rectangleLabel];
|
||||
|
||||
while (this.rectangleLabel._textObj.height > canvas.height / 3) {
|
||||
this.rectangleLabel._textObj.set({
|
||||
fontSize: this.rectangleLabel._textObj.fontSize - 1,
|
||||
top: this.startY - this.rectangleLabel._textObj.fontSize - 12,
|
||||
});
|
||||
}
|
||||
|
||||
canvas.add(this.rectangleLabel._rectObj);
|
||||
canvas.add(this.rectangleLabel._textObj);
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseMove(o) {
|
||||
if (!this.isDown) return;
|
||||
let canvas = this._canvas;
|
||||
let pointer = canvas.getPointer(o.e);
|
||||
if (this.startX > pointer.x) {
|
||||
this.rectangleLabel._rectObj.set({ left: Math.abs(pointer.x) });
|
||||
this.rectangleLabel._textObj.set({ left: Math.abs(pointer.x) });
|
||||
}
|
||||
if (this.startY > pointer.y) {
|
||||
this.rectangleLabel._rectObj.set({ left: Math.abs(pointer.x) });
|
||||
this.rectangleLabel._textObj.set({ top: Math.abs(pointer.y) });
|
||||
}
|
||||
this.rectangleLabel._textObj.setCoords();
|
||||
this.rectangleLabel._rectObj.set({
|
||||
width: Math.abs(this.startX - pointer.x),
|
||||
});
|
||||
this.rectangleLabel._textObj.set({
|
||||
width: this.rectangleLabel._rectObj.getScaledWidth(),
|
||||
});
|
||||
this.rectangleLabel._rectObj.set({
|
||||
height: Math.abs(this.startY - pointer.y),
|
||||
});
|
||||
this.rectangleLabel._rectObj.setCoords();
|
||||
canvas.renderAll();
|
||||
}
|
||||
|
||||
doMouseUp(o) {
|
||||
this.isDown = false;
|
||||
let canvas = this._canvas;
|
||||
|
||||
// var group = new fabric.Group([this.rectangleLabel._rectObj,this.rectangleLabel._textObj]);
|
||||
// canvas.remove(this.rectangleLabel._rectObj);
|
||||
// canvas.remove(this.rectangleLabel._textObj);
|
||||
// canvas.add(group);
|
||||
canvas.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
export default RectangleLabel;
|
16
frontend/src/vendor/ReactSketch/tools/Select/index.js
vendored
Normal file
16
frontend/src/vendor/ReactSketch/tools/Select/index.js
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "../fabrictool";
|
||||
|
||||
class Select extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = false;
|
||||
canvas.selection = true;
|
||||
canvas.forEachObject((o) => {
|
||||
o.selectable = o.evented = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Select;
|
18
frontend/src/vendor/ReactSketch/tools/defaul-tool.js
vendored
Normal file
18
frontend/src/vendor/ReactSketch/tools/defaul-tool.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*eslint no-unused-vars: 0*/
|
||||
|
||||
import FabricCanvasTool from "./fabrictool";
|
||||
|
||||
const fabric = require("fabric").fabric;
|
||||
|
||||
class DefaultTool extends FabricCanvasTool {
|
||||
configureCanvas(props) {
|
||||
let canvas = this._canvas;
|
||||
canvas.isDrawingMode = canvas.selection = false;
|
||||
canvas.forEachObject((o) => (o.selectable = o.evented = false));
|
||||
canvas.discardActiveObject();
|
||||
canvas.defaultCursor = "pointer";
|
||||
canvas.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
export default DefaultTool;
|
22
frontend/src/vendor/ReactSketch/tools/fabrictool.js
vendored
Normal file
22
frontend/src/vendor/ReactSketch/tools/fabrictool.js
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
/**
|
||||
* "Abstract" like base class for a Canvas tool
|
||||
*/
|
||||
class FabricCanvasTool {
|
||||
constructor(canvas) {
|
||||
this._canvas = canvas;
|
||||
}
|
||||
|
||||
configureCanvas(props) {}
|
||||
|
||||
doMouseUp(event) {}
|
||||
|
||||
doMouseDown(event) {}
|
||||
|
||||
doMouseMove(event) {}
|
||||
|
||||
doMouseOut(event) {}
|
||||
}
|
||||
|
||||
export default FabricCanvasTool;
|
23
frontend/src/vendor/ReactSketch/tools/index.js
vendored
Normal file
23
frontend/src/vendor/ReactSketch/tools/index.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
import Arrow from "./Arrow";
|
||||
import Circle from "./Circle";
|
||||
import Highlighter from "./Highlighter";
|
||||
import Line from "./Line";
|
||||
import Pan from "./Pan";
|
||||
import Pencil from "./Pencil";
|
||||
import Rectangle from "./Rectangle";
|
||||
import RectangleLabel from "./Rectangle/rectangle-label";
|
||||
import Select from "./Select/index";
|
||||
import DefaultTool from "./defaul-tool";
|
||||
|
||||
export default {
|
||||
Arrow,
|
||||
Circle,
|
||||
Highlighter,
|
||||
Line,
|
||||
Pan,
|
||||
Pencil,
|
||||
Rectangle,
|
||||
RectangleLabel,
|
||||
Select,
|
||||
DefaultTool,
|
||||
};
|
15
frontend/src/vendor/ReactSketch/utils/Utils.spec.js
vendored
Normal file
15
frontend/src/vendor/ReactSketch/utils/Utils.spec.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/* global expect,describe,it */
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-env node, mocha */
|
||||
|
||||
import { uuid4 } from "../index";
|
||||
|
||||
describe("Utils", () => {
|
||||
it("Loads Normally", () => {
|
||||
require("../index");
|
||||
});
|
||||
|
||||
it("Generates random uuid", () => {
|
||||
expect(uuid4()).toBeDefined();
|
||||
});
|
||||
});
|
242
frontend/src/vendor/ReactSketch/utils/index.js
vendored
Normal file
242
frontend/src/vendor/ReactSketch/utils/index.js
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Determine the mouse position
|
||||
*
|
||||
* @param event the canvas event
|
||||
* @returns *[] tuple of position x,y
|
||||
* @private
|
||||
*/
|
||||
export const pointerPosition = (event) => {
|
||||
event = event || window.event;
|
||||
var target = event.target || event.srcElement,
|
||||
style = target.currentStyle || window.getComputedStyle(target, null),
|
||||
borderLeftWidth = parseInt(style["borderLeftWidth"], 10),
|
||||
borderTopWidth = parseInt(style["borderTopWidth"], 10),
|
||||
rect = target.getBoundingClientRect(),
|
||||
_x = event.clientX - borderLeftWidth - rect.left,
|
||||
_y = event.clientY - borderTopWidth - rect.top,
|
||||
_touchX = event.changedTouches
|
||||
? event.changedTouches[0].clientX - borderLeftWidth - rect.left
|
||||
: null,
|
||||
_touchY = event.changedTouches
|
||||
? event.changedTouches[0].clientY - borderTopWidth - rect.top
|
||||
: null;
|
||||
return [_x || _touchX, _y || _touchY];
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the distance of two x,y points
|
||||
*
|
||||
* @param point1 an object with x,y attributes representing the start point
|
||||
* @param point2 an object with x,y attributes representing the end point
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
export const linearDistance = (point1, point2) => {
|
||||
let xs = point2.x - point1.x;
|
||||
let ys = point2.y - point1.y;
|
||||
return Math.sqrt(xs * xs + ys * ys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a random uuid of the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
* @returns {string}
|
||||
*/
|
||||
export const uuid4 = () => {
|
||||
let uuid = "",
|
||||
ii;
|
||||
for (ii = 0; ii < 32; ii += 1) {
|
||||
switch (ii) {
|
||||
case 8:
|
||||
case 20:
|
||||
uuid += "-";
|
||||
uuid += ((Math.random() * 16) | 0).toString(16);
|
||||
break;
|
||||
case 12:
|
||||
uuid += "-";
|
||||
uuid += "4";
|
||||
break;
|
||||
case 16:
|
||||
uuid += "-";
|
||||
uuid += ((Math.random() * 4) | 8).toString(16);
|
||||
break;
|
||||
default:
|
||||
uuid += ((Math.random() * 16) | 0).toString(16);
|
||||
}
|
||||
}
|
||||
return uuid;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hex is a color code in the form of hex.
|
||||
* Return a rgba equivalent of given hex.
|
||||
* @returns {string}
|
||||
*/
|
||||
export const hexToRgbA = (hex) => {
|
||||
var c;
|
||||
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
|
||||
c = hex.substring(1).split("");
|
||||
if (c.length == 3) {
|
||||
c = [c[0], c[0], c[1], c[1], c[2], c[2]];
|
||||
}
|
||||
c = "0x" + c.join("");
|
||||
return "rgba(" + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(",") + ",0.4)";
|
||||
}
|
||||
throw new Error("Bad Hex");
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color is a color name to convert it to hex
|
||||
* Return hex equivalent of given color name
|
||||
*/
|
||||
export const colorNameToHex = (color) => {
|
||||
var colors = {
|
||||
aliceblue: "#f0f8ff",
|
||||
antiquewhite: "#faebd7",
|
||||
aqua: "#00ffff",
|
||||
aquamarine: "#7fffd4",
|
||||
azure: "#f0ffff",
|
||||
beige: "#f5f5dc",
|
||||
bisque: "#ffe4c4",
|
||||
black: "#000000",
|
||||
blanchedalmond: "#ffebcd",
|
||||
blue: "#0000ff",
|
||||
blueviolet: "#8a2be2",
|
||||
brown: "#a52a2a",
|
||||
burlywood: "#deb887",
|
||||
cadetblue: "#5f9ea0",
|
||||
chartreuse: "#7fff00",
|
||||
chocolate: "#d2691e",
|
||||
coral: "#ff7f50",
|
||||
cornflowerblue: "#6495ed",
|
||||
cornsilk: "#fff8dc",
|
||||
crimson: "#dc143c",
|
||||
cyan: "#00ffff",
|
||||
darkblue: "#00008b",
|
||||
darkcyan: "#008b8b",
|
||||
darkgoldenrod: "#b8860b",
|
||||
darkgray: "#a9a9a9",
|
||||
darkgreen: "#006400",
|
||||
darkkhaki: "#bdb76b",
|
||||
darkmagenta: "#8b008b",
|
||||
darkolivegreen: "#556b2f",
|
||||
darkorange: "#ff8c00",
|
||||
darkorchid: "#9932cc",
|
||||
darkred: "#8b0000",
|
||||
darksalmon: "#e9967a",
|
||||
darkseagreen: "#8fbc8f",
|
||||
darkslateblue: "#483d8b",
|
||||
darkslategray: "#2f4f4f",
|
||||
darkturquoise: "#00ced1",
|
||||
darkviolet: "#9400d3",
|
||||
deeppink: "#ff1493",
|
||||
deepskyblue: "#00bfff",
|
||||
dimgray: "#696969",
|
||||
dodgerblue: "#1e90ff",
|
||||
firebrick: "#b22222",
|
||||
floralwhite: "#fffaf0",
|
||||
forestgreen: "#228b22",
|
||||
fuchsia: "#ff00ff",
|
||||
gainsboro: "#dcdcdc",
|
||||
ghostwhite: "#f8f8ff",
|
||||
gold: "#ffd700",
|
||||
goldenrod: "#daa520",
|
||||
gray: "#808080",
|
||||
green: "#008000",
|
||||
greenyellow: "#adff2f",
|
||||
honeydew: "#f0fff0",
|
||||
hotpink: "#ff69b4",
|
||||
"indianred ": "#cd5c5c",
|
||||
indigo: "#4b0082",
|
||||
ivory: "#fffff0",
|
||||
khaki: "#f0e68c",
|
||||
lavender: "#e6e6fa",
|
||||
lavenderblush: "#fff0f5",
|
||||
lawngreen: "#7cfc00",
|
||||
lemonchiffon: "#fffacd",
|
||||
lightblue: "#add8e6",
|
||||
lightcoral: "#f08080",
|
||||
lightcyan: "#e0ffff",
|
||||
lightgoldenrodyellow: "#fafad2",
|
||||
lightgrey: "#d3d3d3",
|
||||
lightgreen: "#90ee90",
|
||||
lightpink: "#ffb6c1",
|
||||
lightsalmon: "#ffa07a",
|
||||
lightseagreen: "#20b2aa",
|
||||
lightskyblue: "#87cefa",
|
||||
lightslategray: "#778899",
|
||||
lightsteelblue: "#b0c4de",
|
||||
lightyellow: "#ffffe0",
|
||||
lime: "#00ff00",
|
||||
limegreen: "#32cd32",
|
||||
linen: "#faf0e6",
|
||||
magenta: "#ff00ff",
|
||||
maroon: "#800000",
|
||||
mediumaquamarine: "#66cdaa",
|
||||
mediumblue: "#0000cd",
|
||||
mediumorchid: "#ba55d3",
|
||||
mediumpurple: "#9370d8",
|
||||
mediumseagreen: "#3cb371",
|
||||
mediumslateblue: "#7b68ee",
|
||||
mediumspringgreen: "#00fa9a",
|
||||
mediumturquoise: "#48d1cc",
|
||||
mediumvioletred: "#c71585",
|
||||
midnightblue: "#191970",
|
||||
mintcream: "#f5fffa",
|
||||
mistyrose: "#ffe4e1",
|
||||
moccasin: "#ffe4b5",
|
||||
navajowhite: "#ffdead",
|
||||
navy: "#000080",
|
||||
oldlace: "#fdf5e6",
|
||||
olive: "#808000",
|
||||
olivedrab: "#6b8e23",
|
||||
orange: "#ffa500",
|
||||
orangered: "#ff4500",
|
||||
orchid: "#da70d6",
|
||||
palegoldenrod: "#eee8aa",
|
||||
palegreen: "#98fb98",
|
||||
paleturquoise: "#afeeee",
|
||||
palevioletred: "#d87093",
|
||||
papayawhip: "#ffefd5",
|
||||
peachpuff: "#ffdab9",
|
||||
peru: "#cd853f",
|
||||
pink: "#ffc0cb",
|
||||
plum: "#dda0dd",
|
||||
powderblue: "#b0e0e6",
|
||||
purple: "#800080",
|
||||
rebeccapurple: "#663399",
|
||||
red: "#ff0000",
|
||||
rosybrown: "#bc8f8f",
|
||||
royalblue: "#4169e1",
|
||||
saddlebrown: "#8b4513",
|
||||
salmon: "#fa8072",
|
||||
sandybrown: "#f4a460",
|
||||
seagreen: "#2e8b57",
|
||||
seashell: "#fff5ee",
|
||||
sienna: "#a0522d",
|
||||
silver: "#c0c0c0",
|
||||
skyblue: "#87ceeb",
|
||||
slateblue: "#6a5acd",
|
||||
slategray: "#708090",
|
||||
snow: "#fffafa",
|
||||
springgreen: "#00ff7f",
|
||||
steelblue: "#4682b4",
|
||||
tan: "#d2b48c",
|
||||
teal: "#008080",
|
||||
thistle: "#d8bfd8",
|
||||
tomato: "#ff6347",
|
||||
turquoise: "#40e0d0",
|
||||
violet: "#ee82ee",
|
||||
wheat: "#f5deb3",
|
||||
white: "#ffffff",
|
||||
whitesmoke: "#f5f5f5",
|
||||
yellow: "#ffff00",
|
||||
yellowgreen: "#9acd32",
|
||||
};
|
||||
|
||||
if (typeof colors[color.toLowerCase()] != "undefined")
|
||||
return colors[color.toLowerCase()];
|
||||
|
||||
return false;
|
||||
};
|
@ -9,13 +9,13 @@ gradio/external.py
|
||||
gradio/inputs.py
|
||||
gradio/interface.py
|
||||
gradio/interpretation.py
|
||||
gradio/mix.py
|
||||
gradio/networking.py
|
||||
gradio/notebook.py
|
||||
gradio/outputs.py
|
||||
gradio/processing_utils.py
|
||||
gradio/strings.py
|
||||
gradio/test_data.py
|
||||
gradio/transforms.py
|
||||
gradio/tunneling.py
|
||||
gradio/utils.py
|
||||
gradio.egg-info/PKG-INFO
|
||||
@ -27,136 +27,26 @@ gradio/frontend/asset-manifest.json
|
||||
gradio/frontend/index.html
|
||||
gradio/frontend/static/css/2.c6f1eff9.chunk.css
|
||||
gradio/frontend/static/css/2.c6f1eff9.chunk.css.map
|
||||
gradio/frontend/static/css/3.40297d85.chunk.css
|
||||
gradio/frontend/static/css/3.40297d85.chunk.css.map
|
||||
gradio/frontend/static/css/4.e3609929.chunk.css
|
||||
gradio/frontend/static/css/4.e3609929.chunk.css.map
|
||||
gradio/frontend/static/js/2.62a34f98.chunk.js
|
||||
gradio/frontend/static/js/2.62a34f98.chunk.js.LICENSE.txt
|
||||
gradio/frontend/static/js/2.62a34f98.chunk.js.map
|
||||
gradio/frontend/static/js/3.c9f63f14.chunk.js
|
||||
gradio/frontend/static/js/3.c9f63f14.chunk.js.map
|
||||
gradio/frontend/static/js/4.9ae33d51.chunk.js
|
||||
gradio/frontend/static/js/4.9ae33d51.chunk.js.map
|
||||
gradio/frontend/static/js/main.aca3848b.chunk.js
|
||||
gradio/frontend/static/js/main.aca3848b.chunk.js.map
|
||||
gradio/frontend/static/js/runtime-main.88972b1f.js
|
||||
gradio/frontend/static/js/runtime-main.88972b1f.js.map
|
||||
gradio/frontend/static/css/3.9a5b5cea.chunk.css
|
||||
gradio/frontend/static/css/3.9a5b5cea.chunk.css.map
|
||||
gradio/frontend/static/css/4.fae94bce.chunk.css
|
||||
gradio/frontend/static/css/4.fae94bce.chunk.css.map
|
||||
gradio/frontend/static/js/2.0081c136.chunk.js
|
||||
gradio/frontend/static/js/2.0081c136.chunk.js.LICENSE.txt
|
||||
gradio/frontend/static/js/2.0081c136.chunk.js.map
|
||||
gradio/frontend/static/js/3.66c6a8f4.chunk.js
|
||||
gradio/frontend/static/js/3.66c6a8f4.chunk.js.map
|
||||
gradio/frontend/static/js/4.0a51054c.chunk.js
|
||||
gradio/frontend/static/js/4.0a51054c.chunk.js.map
|
||||
gradio/frontend/static/js/main.535df397.chunk.js
|
||||
gradio/frontend/static/js/main.535df397.chunk.js.map
|
||||
gradio/frontend/static/js/runtime-main.86797e02.js
|
||||
gradio/frontend/static/js/runtime-main.86797e02.js.map
|
||||
gradio/frontend/static/media/logo_loading.e93acd82.gif
|
||||
gradio/static/css/gradio.css
|
||||
gradio/static/css/loading.css
|
||||
gradio/static/css/style.css
|
||||
gradio/static/css/interfaces/input/checkbox_group.css
|
||||
gradio/static/css/interfaces/input/dropdown.css
|
||||
gradio/static/css/interfaces/input/file.css
|
||||
gradio/static/css/interfaces/input/image.css
|
||||
gradio/static/css/interfaces/input/microphone.css
|
||||
gradio/static/css/interfaces/input/radio.css
|
||||
gradio/static/css/interfaces/input/sketchpad.css
|
||||
gradio/static/css/interfaces/input/slider.css
|
||||
gradio/static/css/interfaces/input/textbox.css
|
||||
gradio/static/css/interfaces/input/webcam.css
|
||||
gradio/static/css/interfaces/output/audio.css
|
||||
gradio/static/css/interfaces/output/highlighted_text.css
|
||||
gradio/static/css/interfaces/output/html.css
|
||||
gradio/static/css/interfaces/output/image.css
|
||||
gradio/static/css/interfaces/output/json.css
|
||||
gradio/static/css/interfaces/output/key_values.css
|
||||
gradio/static/css/interfaces/output/label.css
|
||||
gradio/static/css/interfaces/output/textbox.css
|
||||
gradio/static/css/vendor/cropper.min.css
|
||||
gradio/static/css/vendor/icons.svg
|
||||
gradio/static/css/vendor/jexcel.min.css
|
||||
gradio/static/css/vendor/jquery-ui.css
|
||||
gradio/static/css/vendor/jsonTree.css
|
||||
gradio/static/css/vendor/jsuites.min.css
|
||||
gradio/static/css/vendor/tui-color-picker.css
|
||||
gradio/static/css/vendor/tui-image-editor.css
|
||||
gradio/static/css/vendor/images/ui-bg_flat_0_aaaaaa_40x100.png
|
||||
gradio/static/css/vendor/images/ui-icons_444444_256x240.png
|
||||
gradio/static/css/vendor/images/ui-icons_555555_256x240.png
|
||||
gradio/static/css/vendor/images/ui-icons_777620_256x240.png
|
||||
gradio/static/css/vendor/images/ui-icons_777777_256x240.png
|
||||
gradio/static/css/vendor/images/ui-icons_cc0000_256x240.png
|
||||
gradio/static/css/vendor/images/ui-icons_ffffff_256x240.png
|
||||
gradio/static/img/logo.png
|
||||
gradio/static/img/logo_error.png
|
||||
gradio/static/img/logo_inline.png
|
||||
gradio/static/img/logo_loading.gif
|
||||
gradio/static/img/logo_mini.png
|
||||
gradio/static/img/logo_only.png
|
||||
gradio/static/img/mic.png
|
||||
gradio/static/img/mic_recording.png
|
||||
gradio/static/img/table.png
|
||||
gradio/static/img/webcam.png
|
||||
gradio/static/img/vendor/icon-a.svg
|
||||
gradio/static/img/vendor/icon-b.svg
|
||||
gradio/static/img/vendor/icon-c.svg
|
||||
gradio/static/img/vendor/icon-d.svg
|
||||
gradio/static/js/all_io.js
|
||||
gradio/static/js/gradio.js
|
||||
gradio/static/js/utils.js
|
||||
gradio/static/js/interfaces/input/audio.js
|
||||
gradio/static/js/interfaces/input/checkbox.js
|
||||
gradio/static/js/interfaces/input/checkbox_group.js
|
||||
gradio/static/js/interfaces/input/dataframe.js
|
||||
gradio/static/js/interfaces/input/dropdown.js
|
||||
gradio/static/js/interfaces/input/file.js
|
||||
gradio/static/js/interfaces/input/image.js
|
||||
gradio/static/js/interfaces/input/microphone.js
|
||||
gradio/static/js/interfaces/input/number.js
|
||||
gradio/static/js/interfaces/input/radio.js
|
||||
gradio/static/js/interfaces/input/sketchpad.js
|
||||
gradio/static/js/interfaces/input/slider.js
|
||||
gradio/static/js/interfaces/input/textbox.js
|
||||
gradio/static/js/interfaces/input/video.js
|
||||
gradio/static/js/interfaces/input/webcam.js
|
||||
gradio/static/js/interfaces/output/audio.js
|
||||
gradio/static/js/interfaces/output/dataframe.js
|
||||
gradio/static/js/interfaces/output/file.js
|
||||
gradio/static/js/interfaces/output/highlighted_text.js
|
||||
gradio/static/js/interfaces/output/html.js
|
||||
gradio/static/js/interfaces/output/image.js
|
||||
gradio/static/js/interfaces/output/json.js
|
||||
gradio/static/js/interfaces/output/key_values.js
|
||||
gradio/static/js/interfaces/output/label.js
|
||||
gradio/static/js/interfaces/output/textbox.js
|
||||
gradio/static/js/interfaces/output/video.js
|
||||
gradio/static/js/vendor/Chart.min.js
|
||||
gradio/static/js/vendor/FileSaver.min.js
|
||||
gradio/static/js/vendor/black-theme.js
|
||||
gradio/static/js/vendor/cropper.min.js
|
||||
gradio/static/js/vendor/fabric.js
|
||||
gradio/static/js/vendor/html2canvas.min.js
|
||||
gradio/static/js/vendor/jexcel.min.js
|
||||
gradio/static/js/vendor/jquery-ui.min.js
|
||||
gradio/static/js/vendor/jquery.min.js
|
||||
gradio/static/js/vendor/jquery.ui.touch-punch.js
|
||||
gradio/static/js/vendor/jsonTree.js
|
||||
gradio/static/js/vendor/jsuites.min.js
|
||||
gradio/static/js/vendor/p5.dom.min.js
|
||||
gradio/static/js/vendor/p5.min.js
|
||||
gradio/static/js/vendor/p5.sound.min.js
|
||||
gradio/static/js/vendor/papaparse.min.js
|
||||
gradio/static/js/vendor/sketchpad.js
|
||||
gradio/static/js/vendor/tui-code-snippet.min.js
|
||||
gradio/static/js/vendor/tui-color-picker.js
|
||||
gradio/static/js/vendor/tui-image-editor.js
|
||||
gradio/static/js/vendor/wavesurfer.min.js
|
||||
gradio/static/js/vendor/webcam.min.js
|
||||
gradio/static/js/vendor/white-theme.js
|
||||
gradio/static/js/vendor/gifcap/Makefile
|
||||
gradio/static/js/vendor/gifcap/configure
|
||||
gradio/static/js/vendor/gifcap/encoder.c
|
||||
gradio/static/js/vendor/gifcap/encoder.js
|
||||
gradio/static/js/vendor/gifcap/encoder.wasm
|
||||
gradio/static/js/vendor/gifcap/gifencoder.js
|
||||
gradio/static/js/vendor/gifcap/quantizer.js
|
||||
gradio/static/js/vendor/gifcap/writer.js
|
||||
gradio/templates/index.html
|
||||
gradio/templates/login.html
|
||||
test/test_demos.py
|
||||
test/test_inputs.py
|
||||
test/test_interfaces.py
|
||||
test/test_interpretation.py
|
||||
test/test_mix.py
|
||||
test/test_outputs.py
|
@ -158,8 +158,8 @@ def get_gradio_interface(model_name, api_key, alias):
|
||||
query_gradio_api.__name__ = alias
|
||||
|
||||
pipeline = {
|
||||
'inputs': [inp[0] for inp in config_info["input_interfaces"]],
|
||||
'outputs': [out[0] for out in config_info["output_interfaces"]],
|
||||
'inputs': [inp[0] for inp in config_info["input_components"]],
|
||||
'outputs': [out[0] for out in config_info["output_components"]],
|
||||
'preprocess': lambda x: {"data": [x]},
|
||||
'postprocess': lambda r: r["data"][0],
|
||||
}
|
||||
|
@ -130,10 +130,10 @@ class Interface:
|
||||
if isinstance(outputs, list):
|
||||
self.output_components = [get_output_instance(i) for i in outputs]
|
||||
else:
|
||||
self.output_interfaces = [get_output_instance(outputs)]
|
||||
self.output_components = [get_output_instance(outputs)]
|
||||
|
||||
if repeat_outputs_per_model:
|
||||
self.output_interfaces *= len(fn)
|
||||
self.output_components *= len(fn)
|
||||
self.predict = fn
|
||||
self.function_names = [func.__name__ for func in fn]
|
||||
self.__name__ = ", ".join(self.function_names)
|
||||
@ -226,10 +226,10 @@ class Interface:
|
||||
repr = "Gradio Interface for: {}".format(", ".join(fn.__name__ for fn in self.predict))
|
||||
repr += "\n" + "-"*len(repr)
|
||||
repr += "\ninputs:"
|
||||
for component in self.input_interfaces:
|
||||
for component in self.input_components:
|
||||
repr += "\n|-{}".format(str(component))
|
||||
repr += "\noutputs:"
|
||||
for component in self.output_interfaces:
|
||||
for component in self.output_components:
|
||||
repr+= "\n|-{}".format(str(component))
|
||||
return repr
|
||||
|
||||
|
@ -18,11 +18,11 @@ class Parallel(Interface):
|
||||
|
||||
for io in interfaces:
|
||||
fns.extend(io.predict)
|
||||
outputs.extend(io.output_interfaces)
|
||||
outputs.extend(io.output_components)
|
||||
|
||||
kwargs = {
|
||||
"fn": fns,
|
||||
"inputs": interfaces[0].input_interfaces,
|
||||
"inputs": interfaces[0].input_components,
|
||||
"outputs": outputs,
|
||||
"repeat_outputs_per_model": False,
|
||||
}
|
||||
@ -47,7 +47,7 @@ class Series(Interface):
|
||||
for idx, io in enumerate(interfaces):
|
||||
# skip preprocessing for first interface since the compound interface will include it
|
||||
if idx > 0:
|
||||
data = [input_interface.preprocess(data[i]) for i, input_interface in enumerate(io.input_interfaces)]
|
||||
data = [input_interface.preprocess(data[i]) for i, input_interface in enumerate(io.input_components)]
|
||||
# run all of predictions sequentially
|
||||
predictions = []
|
||||
for predict_fn in io.predict:
|
||||
@ -56,15 +56,15 @@ class Series(Interface):
|
||||
data = predictions
|
||||
# skip postprocessing for final interface since the compound interface will include it
|
||||
if idx < len(interfaces) - 1:
|
||||
data = [output_interface.postprocess(data[i]) for i, output_interface in enumerate(io.output_interfaces)]
|
||||
data = [output_interface.postprocess(data[i]) for i, output_interface in enumerate(io.output_components)]
|
||||
return data[0]
|
||||
|
||||
connected_fn.__name__ = " => ".join([f[0].__name__ for f in fns])
|
||||
|
||||
kwargs = {
|
||||
"fn": connected_fn,
|
||||
"inputs": interfaces[0].input_interfaces,
|
||||
"outputs": interfaces[-1].output_interfaces,
|
||||
"inputs": interfaces[0].input_components,
|
||||
"outputs": interfaces[-1].output_components,
|
||||
}
|
||||
kwargs.update(options)
|
||||
super().__init__(**kwargs)
|
||||
|
@ -121,24 +121,10 @@ def get_first_available_port(initial, final):
|
||||
)
|
||||
|
||||
|
||||
def home_page(examples=None, path=None):
|
||||
return render_template("index.html",
|
||||
config=app.interface.config,
|
||||
vendor_prefix=(
|
||||
GRADIO_STATIC_ROOT if app.interface.share else ""),
|
||||
input_interfaces=[interface[0]
|
||||
for interface in app.interface.config["input_interfaces"]],
|
||||
output_interfaces=[interface[0]
|
||||
for interface in app.interface.config["output_interfaces"]],
|
||||
css=app.interface.css, examples=examples, path=path
|
||||
)
|
||||
|
||||
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
@login_check
|
||||
def main():
|
||||
return home_page()
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route('/login', methods=["GET", "POST"])
|
||||
@ -155,50 +141,6 @@ def login():
|
||||
return abort(401)
|
||||
|
||||
|
||||
@app.route("/from_dir", methods=["GET"])
|
||||
@login_check
|
||||
def main_from_flagging_dir():
|
||||
return redirect("/from_dir/" + app.interface.flagging_dir)
|
||||
|
||||
|
||||
@app.route("/from_dir/<path:path>", methods=["GET"])
|
||||
@login_check
|
||||
def main_from_dir(path):
|
||||
log_file = os.path.join(path, "log.csv")
|
||||
path_exists = os.path.exists(path)
|
||||
log_file_exists = os.path.exists(log_file)
|
||||
examples_from_folder = isinstance(app.interface.examples, str) and app.interface.examples == path
|
||||
multiple_inputs = len(app.interface.input_interfaces) > 1
|
||||
if path_exists:
|
||||
if not log_file_exists and multiple_inputs:
|
||||
if examples_from_folder:
|
||||
abort(404, "log.csv file required for multiple inputs.")
|
||||
else:
|
||||
return redirect("/")
|
||||
elif examples_from_folder:
|
||||
abort(404, "Examples dir not found")
|
||||
else:
|
||||
return redirect("/")
|
||||
if log_file_exists:
|
||||
if app.interface.encrypt:
|
||||
with open(log_file, "rb") as csvfile:
|
||||
encrypted_csv = csvfile.read()
|
||||
decrypted_csv = encryptor.decrypt(
|
||||
app.interface.encryption_key, encrypted_csv)
|
||||
csv_data = io.StringIO(decrypted_csv.decode())
|
||||
examples = list(csv.reader(csv_data))
|
||||
else:
|
||||
with open(log_file) as logs:
|
||||
examples = list(csv.reader(logs))
|
||||
examples = examples[1:] # remove header
|
||||
else:
|
||||
examples = [[filename] for filename in os.listdir(path)]
|
||||
for i, example in enumerate(examples):
|
||||
for j, (interface, cell) in enumerate(zip(app.interface.input_components + app.interface.output_components, example)):
|
||||
examples[i][j] = interface.restore_flagged(cell)
|
||||
return home_page(examples=examples, path=path)
|
||||
|
||||
|
||||
@app.route("/config/", methods=["GET"])
|
||||
@login_check
|
||||
def config():
|
||||
@ -245,13 +187,13 @@ def score_similarity():
|
||||
raw_input = request.json["data"]
|
||||
|
||||
preprocessed_input = [input_interface.preprocess(raw_input[i])
|
||||
for i, input_interface in enumerate(app.interface.input_interfaces)]
|
||||
for i, input_interface in enumerate(app.interface.input_components)]
|
||||
input_embedding = app.interface.embed(preprocessed_input)
|
||||
scores = list()
|
||||
|
||||
for example in app.interface.examples:
|
||||
preprocessed_example = [iface.preprocess(iface.preprocess_example(example))
|
||||
for iface, example in zip(app.interface.input_interfaces, example)]
|
||||
for iface, example in zip(app.interface.input_components, example)]
|
||||
example_embedding = app.interface.embed(preprocessed_example)
|
||||
scores.append(calculate_similarity(input_embedding, example_embedding))
|
||||
log_feature_analytics('score_similarity')
|
||||
@ -265,13 +207,13 @@ def view_embeddings():
|
||||
if "data" in request.json:
|
||||
raw_input = request.json["data"]
|
||||
preprocessed_input = [input_interface.preprocess(raw_input[i])
|
||||
for i, input_interface in enumerate(app.interface.input_interfaces)]
|
||||
for i, input_interface in enumerate(app.interface.input_components)]
|
||||
sample_embedding.append(app.interface.embed(preprocessed_input))
|
||||
|
||||
example_embeddings = []
|
||||
for example in app.interface.examples:
|
||||
preprocessed_example = [iface.preprocess(iface.preprocess_example(example))
|
||||
for iface, example in zip(app.interface.input_interfaces, example)]
|
||||
for iface, example in zip(app.interface.input_components, example)]
|
||||
example_embedding = app.interface.embed(preprocessed_example)
|
||||
example_embeddings.append(example_embedding)
|
||||
|
||||
@ -291,7 +233,7 @@ def update_embeddings():
|
||||
if "data" in request.json:
|
||||
raw_input = request.json["data"]
|
||||
preprocessed_input = [input_interface.preprocess(raw_input[i])
|
||||
for i, input_interface in enumerate(app.interface.input_interfaces)]
|
||||
for i, input_interface in enumerate(app.interface.input_components)]
|
||||
sample_embedding.append(app.interface.embed(preprocessed_input))
|
||||
sample_embedding_2d = transform_with_pca(
|
||||
app.pca_model, sample_embedding)
|
||||
@ -307,7 +249,7 @@ def predict_examples():
|
||||
for example_id in example_ids:
|
||||
example_set = app.interface.examples[example_id]
|
||||
processed_example_set = [iface.preprocess_example(example)
|
||||
for iface, example in zip(app.interface.input_interfaces, example_set)]
|
||||
for iface, example in zip(app.interface.input_components, example_set)]
|
||||
try:
|
||||
predictions, _ = app.interface.process(processed_example_set)
|
||||
except:
|
||||
@ -321,19 +263,19 @@ def flag_data(input_data, output_data, flag_option=None):
|
||||
flag_path = os.path.join(app.cwd, app.interface.flagging_dir)
|
||||
csv_data = []
|
||||
encryption_key = app.interface.encryption_key if app.interface.encrypt else None
|
||||
for i, interface in enumerate(app.interface.input_interfaces):
|
||||
for i, interface in enumerate(app.interface.input_components):
|
||||
csv_data.append(interface.save_flagged(
|
||||
flag_path, app.interface.config["input_interfaces"][i]["label"], input_data[i], encryption_key))
|
||||
for i, interface in enumerate(app.interface.output_interfaces):
|
||||
flag_path, app.interface.config["input_components"][i]["label"], input_data[i], encryption_key))
|
||||
for i, interface in enumerate(app.interface.output_components):
|
||||
csv_data.append(interface.save_flagged(
|
||||
flag_path, app.interface.config["output_interfaces"][i]["label"], output_data[i], encryption_key))
|
||||
flag_path, app.interface.config["output_components"][i]["label"], output_data[i], encryption_key))
|
||||
if flag_option:
|
||||
csv_data.append(flag_option)
|
||||
|
||||
headers = [interface["label"]
|
||||
for interface in app.interface.config["input_interfaces"]]
|
||||
for interface in app.interface.config["input_components"]]
|
||||
headers += [interface["label"]
|
||||
for interface in app.interface.config["output_interfaces"]]
|
||||
for interface in app.interface.config["output_components"]]
|
||||
if app.interface.flagging_options is not None:
|
||||
headers.append("flag")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user