mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-28 09:51:06 -06:00
Merge pull request #1107 from psjostrom/SDA-2402-Annotate-Screenshot-Tool
feat: SDA-2532 Work on state for annotate and adding color picker
This commit is contained in:
commit
8ea3d6ae74
52
.vscode/launch.json
vendored
52
.vscode/launch.json
vendored
@ -47,7 +47,31 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mana",
|
"name": "build corp",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
|
||||||
|
},
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--url=https://corporate.symphony.com"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"ELECTRON_DEBUGGING": "true",
|
||||||
|
"ELECTRON_DEV": "true"
|
||||||
|
},
|
||||||
|
"outputCapture": "std",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outFiles": [
|
||||||
|
"${workspaceFolder}/lib/**/*.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mana local",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
@ -68,7 +92,31 @@
|
|||||||
"outFiles": [
|
"outFiles": [
|
||||||
"${workspaceFolder}/lib/**/*.js"
|
"${workspaceFolder}/lib/**/*.js"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"name": "build mana local",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.exe"
|
||||||
|
},
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--url=https://local-dev.symphony.com:9090"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"ELECTRON_DEBUGGING": "true",
|
||||||
|
"ELECTRON_DEV": "true"
|
||||||
|
},
|
||||||
|
"outputCapture": "std",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outFiles": [
|
||||||
|
"${workspaceFolder}/lib/**/*.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
|
10
.vscode/tasks.json
vendored
Normal file
10
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npx run-s compile browserify"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -150,6 +150,7 @@
|
|||||||
"typescript": "3.9.7"
|
"typescript": "3.9.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/lazy-brush": "^1.0.0",
|
||||||
"archiver": "3.1.1",
|
"archiver": "3.1.1",
|
||||||
"async.map": "0.5.2",
|
"async.map": "0.5.2",
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
@ -160,6 +161,7 @@
|
|||||||
"ffi-napi": "3.0.0",
|
"ffi-napi": "3.0.0",
|
||||||
"filesize": "6.1.0",
|
"filesize": "6.1.0",
|
||||||
"image-size": "^0.9.3",
|
"image-size": "^0.9.3",
|
||||||
|
"lazy-brush": "^1.0.1",
|
||||||
"react": "16.13.0",
|
"react": "16.13.0",
|
||||||
"react-dom": "16.13.0",
|
"react-dom": "16.13.0",
|
||||||
"ref-napi": "1.4.3",
|
"ref-napi": "1.4.3",
|
||||||
|
@ -12,6 +12,11 @@ exports[`Snipping Tool should render correctly 1`] = `
|
|||||||
<button
|
<button
|
||||||
className="ActionButton"
|
className="ActionButton"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"border": "2px solid rgba(0, 142, 255, 1)",
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="../renderer/assets/snip-draw.svg"
|
src="../renderer/assets/snip-draw.svg"
|
||||||
@ -45,7 +50,9 @@ exports[`Snipping Tool should render correctly 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
;
|
||||||
<main>
|
<main>
|
||||||
|
<div>
|
||||||
<img
|
<img
|
||||||
alt="Screen snippet"
|
alt="Screen snippet"
|
||||||
className="SnippetImage"
|
className="SnippetImage"
|
||||||
@ -53,6 +60,7 @@ exports[`Snipping Tool should render correctly 1`] = `
|
|||||||
src="Screen-Snippet"
|
src="Screen-Snippet"
|
||||||
width={800}
|
width={800}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<button
|
<button
|
||||||
|
66
src/renderer/components/color-picker-pill.tsx
Normal file
66
src/renderer/components/color-picker-pill.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export interface IColorPickerPillProps {
|
||||||
|
availableColors: IColor[];
|
||||||
|
onChange: (color: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IColor {
|
||||||
|
rgbaColor: string; // Should be provided as a rgba string i.e. 'rgba(255, 0, 0, 0.3)'
|
||||||
|
outline?: string; // Should be provided as a rgba string i.e. 'rgba(255, 0, 0, 0.3)'
|
||||||
|
chosen?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorPickerPill = (props: IColorPickerPillProps) => {
|
||||||
|
const getChosenColor = (colors: IColor[]) => {
|
||||||
|
return colors.find((color) => color.chosen === true);
|
||||||
|
};
|
||||||
|
const chosenColor = getChosenColor(props.availableColors);
|
||||||
|
|
||||||
|
const ColorDot = (color: IColor) => {
|
||||||
|
const isChosenColor = color === chosenColor;
|
||||||
|
const hasOutline = !!color.outline;
|
||||||
|
const border = 'solid 1px ' + color.outline;
|
||||||
|
|
||||||
|
const getWidthAndHeight = () => {
|
||||||
|
if (isChosenColor) {
|
||||||
|
return hasOutline ? '22px' : '24px';
|
||||||
|
}
|
||||||
|
return hasOutline ? '6px' : '8px';
|
||||||
|
};
|
||||||
|
|
||||||
|
const widthAndHeight = getWidthAndHeight();
|
||||||
|
|
||||||
|
const chooseColor = () => {
|
||||||
|
props.onChange(color.rgbaColor);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={color.rgbaColor}
|
||||||
|
className='enclosingCircle'
|
||||||
|
onClick={chooseColor}
|
||||||
|
data-testid={'colorDot ' + color.rgbaColor}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: color.rgbaColor,
|
||||||
|
width: widthAndHeight,
|
||||||
|
height: widthAndHeight,
|
||||||
|
cursor: 'pointer',
|
||||||
|
border: hasOutline ? border : undefined,
|
||||||
|
}}
|
||||||
|
className='colorDot'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='colorPicker'>
|
||||||
|
{props.availableColors.map((color) => ColorDot(color))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColorPickerPill;
|
@ -1,25 +1,81 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { i18n } from '../../common/i18n-preload';
|
import { i18n } from '../../common/i18n-preload';
|
||||||
|
import ColorPickerPill, { IColor } from './color-picker-pill';
|
||||||
|
|
||||||
const { useState, useEffect } = React;
|
const { useState, useCallback, useRef, useEffect } = React;
|
||||||
|
|
||||||
interface IProps {
|
enum Tool {
|
||||||
drawEnabled: boolean;
|
pen = 'PEN',
|
||||||
highlightEnabled: boolean;
|
highlight = 'HIGHLIGHT',
|
||||||
eraseEnabled: boolean;
|
eraser = 'ERASER',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPath {
|
||||||
|
points: IPoint[];
|
||||||
|
color: string;
|
||||||
|
strokeWidth: number;
|
||||||
|
shouldShow: boolean;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPoint {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISvgPath {
|
||||||
|
svgPath: string;
|
||||||
|
key: string;
|
||||||
|
strokeWidth: number;
|
||||||
|
color: string;
|
||||||
|
shouldShow: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const availablePenColors: IColor[] = [
|
||||||
|
{ rgbaColor: 'rgba(0, 0, 40, 1)' },
|
||||||
|
{ rgbaColor: 'rgba(0, 142, 255, 1)' },
|
||||||
|
{ rgbaColor: 'rgba(38, 196, 58, 1)' },
|
||||||
|
{ rgbaColor: 'rgba(246, 178, 2, 1)' },
|
||||||
|
{ rgbaColor: 'rgba(255, 255, 255, 1)', outline: 'rgba(0, 0, 0, 1)' },
|
||||||
|
];
|
||||||
|
const availableHighlightColors: IColor[] = [
|
||||||
|
{ rgbaColor: 'rgba(0, 142, 255, 0.64)' },
|
||||||
|
{ rgbaColor: 'rgba(38, 196, 58, 0.64)' },
|
||||||
|
{ rgbaColor: 'rgba(246, 178, 2, 0.64)' },
|
||||||
|
{ rgbaColor: 'rgba(233, 0, 0, 0.64)' },
|
||||||
|
];
|
||||||
const SNIPPING_TOOL_NAMESPACE = 'ScreenSnippet';
|
const SNIPPING_TOOL_NAMESPACE = 'ScreenSnippet';
|
||||||
|
|
||||||
const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEnabled, eraseEnabled}) => {
|
const SnippingTool = () => {
|
||||||
|
// State and ref preparation functions
|
||||||
|
|
||||||
const [screenSnippet, setScreenSnippet] = useState('Screen-Snippet');
|
const [screenSnippet, setScreenSnippet] = useState('Screen-Snippet');
|
||||||
const [imageDimensions, setImageDimensions] = useState({height: 600, width: 800});
|
const [imageDimensions, setImageDimensions] = useState({
|
||||||
|
height: 600,
|
||||||
|
width: 800,
|
||||||
|
});
|
||||||
|
const [paths, setPaths] = useState<IPath[]>([]);
|
||||||
|
const [chosenTool, setChosenTool] = useState(Tool.pen);
|
||||||
|
const [annotateAreaLocation, setAnnotateAreaLocation] = useState({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
const [penColor, setPenColor] = useState('rgba(0, 142, 255, 1)');
|
||||||
|
const [highlightColor, setHighlightColor] = useState(
|
||||||
|
'rgba(0, 142, 255, 0.64)',
|
||||||
|
);
|
||||||
|
const [
|
||||||
|
shouldRenderHighlightColorPicker,
|
||||||
|
setShouldRenderHighlightColorPicker,
|
||||||
|
] = useState(false);
|
||||||
|
const [shouldRenderPenColorPicker, setShouldRenderPenColorPicker] = useState(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const getSnipImageData = (_event, {snipImage, height, width}) => {
|
const getSnipImageData = ({ }, { snipImage, height, width }) => {
|
||||||
setScreenSnippet(snipImage);
|
setScreenSnippet(snipImage);
|
||||||
setImageDimensions({height, width});
|
setImageDimensions({ height, width });
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcRenderer.on('snipping-tool-data', getSnipImageData);
|
ipcRenderer.on('snipping-tool-data', getSnipImageData);
|
||||||
@ -30,32 +86,114 @@ const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEn
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const annotateRef = useCallback((domNode) => {
|
||||||
|
if (domNode) {
|
||||||
|
setAnnotateAreaLocation(domNode.getBoundingClientRect());
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Hook that alerts clicks outside of the passed ref
|
||||||
|
const useClickOutsideExaminer = (
|
||||||
|
colorPickerRf: React.RefObject<HTMLDivElement>,
|
||||||
|
penRf: React.RefObject<HTMLButtonElement>,
|
||||||
|
highlightRf: React.RefObject<HTMLButtonElement>,
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (
|
||||||
|
!colorPickerRf?.current?.contains(event.target as Node) &&
|
||||||
|
!penRf?.current?.contains(event.target as Node) &&
|
||||||
|
!highlightRf?.current?.contains(event.target as Node)
|
||||||
|
) {
|
||||||
|
setShouldRenderHighlightColorPicker(false);
|
||||||
|
setShouldRenderPenColorPicker(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [colorPickerRf, penRf, highlightRf]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const colorPickerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const penRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const highlightRef = useRef<HTMLButtonElement>(null);
|
||||||
|
useClickOutsideExaminer(colorPickerRef, penRef, highlightRef);
|
||||||
|
|
||||||
|
// State mutating functions
|
||||||
|
|
||||||
|
const penColorChosen = (color: string) => {
|
||||||
|
setPenColor(color);
|
||||||
|
setShouldRenderPenColorPicker(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const highlightColorChosen = (color: string) => {
|
||||||
|
setHighlightColor(color);
|
||||||
|
setShouldRenderHighlightColorPicker(false);
|
||||||
|
};
|
||||||
|
|
||||||
const usePen = () => {
|
const usePen = () => {
|
||||||
// setTool("pen");
|
setChosenTool(Tool.pen);
|
||||||
// setShouldRenderPenColorPicker(shouldRenderPenColorPicker => !shouldRenderPenColorPicker);
|
setShouldRenderPenColorPicker(!shouldRenderPenColorPicker);
|
||||||
// setShouldRenderHighlightColorPicker(false);
|
setShouldRenderHighlightColorPicker(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useHighlight = () => {
|
const useHighlight = () => {
|
||||||
// setTool("highlight");
|
setChosenTool(Tool.highlight);
|
||||||
// setShouldRenderHighlightColorPicker(shouldRenderHighlightColorPicker => !shouldRenderHighlightColorPicker);
|
setShouldRenderHighlightColorPicker(!shouldRenderHighlightColorPicker);
|
||||||
// setShouldRenderPenColorPicker(false);
|
setShouldRenderPenColorPicker(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useEraser = () => {
|
const useEraser = () => {
|
||||||
// setTool("eraser");
|
setChosenTool(Tool.eraser);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
// const updPaths = [...paths];
|
const updPaths = [...paths];
|
||||||
// updPaths.map((p) => {
|
updPaths.map((p) => {
|
||||||
// p.shouldShow = false;
|
p.shouldShow = false;
|
||||||
// return p;
|
return p;
|
||||||
// });
|
});
|
||||||
// setPaths(updPaths);
|
setPaths(updPaths);
|
||||||
};
|
};
|
||||||
|
|
||||||
const done = () => {
|
// Utility functions
|
||||||
|
|
||||||
|
const getMousePosition = (e: React.MouseEvent) => {
|
||||||
|
return {
|
||||||
|
x: e.pageX - annotateAreaLocation.left,
|
||||||
|
y: e.pageY - annotateAreaLocation.top,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const markChosenColor = (colors: IColor[], chosenColor: string) => {
|
||||||
|
return colors.map((color) => {
|
||||||
|
if (color.rgbaColor === chosenColor) {
|
||||||
|
return { ...color, chosen: true };
|
||||||
|
} else {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBorderStyle = (tool: Tool) => {
|
||||||
|
if (chosenTool !== tool) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (chosenTool === Tool.pen) {
|
||||||
|
return { border: '2px solid ' + penColor };
|
||||||
|
} else if (chosenTool === Tool.highlight) {
|
||||||
|
return { border: '2px solid ' + highlightColor };
|
||||||
|
} else if (chosenTool === Tool.eraser) {
|
||||||
|
return { border: '2px solid #008EFF' };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const done = (e) => {
|
||||||
|
getMousePosition(e);
|
||||||
ipcRenderer.send('upload-snippet', screenSnippet);
|
ipcRenderer.send('upload-snippet', screenSnippet);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,45 +202,64 @@ const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEn
|
|||||||
<header>
|
<header>
|
||||||
<div className='DrawActions'>
|
<div className='DrawActions'>
|
||||||
<button
|
<button
|
||||||
className={
|
style={getBorderStyle(Tool.pen)}
|
||||||
drawEnabled ? 'ActionButtonSelected' : 'ActionButton'
|
className='ActionButton'
|
||||||
}
|
|
||||||
onClick={usePen}
|
onClick={usePen}
|
||||||
>
|
>
|
||||||
<img src='../renderer/assets/snip-draw.svg' />
|
<img src='../renderer/assets/snip-draw.svg' />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={
|
style={getBorderStyle(Tool.highlight)}
|
||||||
highlightEnabled
|
className='ActionButton'
|
||||||
? 'ActionButtonSelected'
|
|
||||||
: 'ActionButton'
|
|
||||||
}
|
|
||||||
onClick={useHighlight}
|
onClick={useHighlight}
|
||||||
>
|
>
|
||||||
<img src='../renderer/assets/snip-highlight.svg' />
|
<img src='../renderer/assets/snip-highlight.svg' />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={
|
style={getBorderStyle(Tool.eraser)}
|
||||||
eraseEnabled
|
className='ActionButton'
|
||||||
? 'ActionButtonSelected'
|
|
||||||
: 'ActionButton'
|
|
||||||
}
|
|
||||||
onClick={useEraser}
|
onClick={useEraser}
|
||||||
>
|
>
|
||||||
<img src='../renderer/assets/snip-erase.svg' />
|
<img src='../renderer/assets/snip-erase.svg' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className='ClearActions'>
|
<div className='ClearActions'>
|
||||||
<button
|
<button className='ClearButton' onClick={clear}>
|
||||||
className='ClearButton'
|
|
||||||
onClick={clear}
|
|
||||||
>
|
|
||||||
{i18n.t('Clear', SNIPPING_TOOL_NAMESPACE)()}
|
{i18n.t('Clear', SNIPPING_TOOL_NAMESPACE)()}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>;
|
||||||
|
|
||||||
|
{
|
||||||
|
shouldRenderPenColorPicker && (
|
||||||
|
<div style={{ marginTop: '64px', position: 'absolute', left: '50%' }} ref={colorPickerRef}>
|
||||||
|
<div style={{ position: 'relative', left: '-50%' }}>
|
||||||
|
<ColorPickerPill
|
||||||
|
availableColors={markChosenColor(availablePenColors, penColor)}
|
||||||
|
onChange={penColorChosen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
shouldRenderHighlightColorPicker && (
|
||||||
|
<div style={{ marginTop: '64px', position: 'absolute', left: '50%' }} ref={colorPickerRef}>
|
||||||
|
<div style={{ position: 'relative', left: '-50%' }}>
|
||||||
|
<ColorPickerPill
|
||||||
|
availableColors={markChosenColor(
|
||||||
|
availableHighlightColors,
|
||||||
|
highlightColor,
|
||||||
|
)}
|
||||||
|
onChange={highlightColorChosen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<div ref={annotateRef}>
|
||||||
<img
|
<img
|
||||||
src={screenSnippet}
|
src={screenSnippet}
|
||||||
width={imageDimensions.width}
|
width={imageDimensions.width}
|
||||||
@ -110,6 +267,7 @@ const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEn
|
|||||||
className='SnippetImage'
|
className='SnippetImage'
|
||||||
alt={i18n.t('Screen snippet', SNIPPING_TOOL_NAMESPACE)()}
|
alt={i18n.t('Screen snippet', SNIPPING_TOOL_NAMESPACE)()}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
@ -117,7 +275,7 @@ const SnippingTool: React.FunctionComponent<IProps> = ({drawEnabled, highlightEn
|
|||||||
{i18n.t('Done', SNIPPING_TOOL_NAMESPACE)()}
|
{i18n.t('Done', SNIPPING_TOOL_NAMESPACE)()}
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,40 +47,27 @@ body {
|
|||||||
max-height: 48px;
|
max-height: 48px;
|
||||||
|
|
||||||
.ActionButton {
|
.ActionButton {
|
||||||
margin-left: 24px;
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 15px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ActionButtonSelected {
|
|
||||||
margin-left: 24px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px 0;
|
|
||||||
border: 2px solid #008eff;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 2px solid white;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
&:focus {
|
||||||
width: 24px;
|
outline: none;
|
||||||
height: 24px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,3 +167,41 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.colorPicker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 4px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: fit-content;
|
||||||
|
height: 40px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 4px 16px rgba(15, 27, 36, 0.14), 0px 4px 8px rgba(15, 27, 36, 0.26);
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enclosingCircle {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex: none;
|
||||||
|
order: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
margin: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorDot {
|
||||||
|
border-radius: 50%;
|
||||||
|
flex: none;
|
||||||
|
order: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #000028;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user