Skip to content

Commit

Permalink
tool wip
Browse files Browse the repository at this point in the history
  • Loading branch information
c298lee committed Jan 31, 2025
1 parent 0f25452 commit 20de538
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 86 deletions.
23 changes: 23 additions & 0 deletions packages/feedback/src/screenshot/components/CropIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { VNode, h as hType } from 'preact';

interface FactoryParams {
h: typeof hType;
}

export default function CropIconFactory({
h, // eslint-disable-line @typescript-eslint/no-unused-vars
}: FactoryParams) {
return function CropIcon(): VNode {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.25 12.5H12.5M12.5 12.5H4.50001C3.94773 12.5 3.50001 12.0523 3.50001 11.5V3.50002M12.5 12.5L12.5 4.50002C12.5 3.94773 12.0523 3.50002 11.5 3.50002H3.50001M12.5 12.5L12.5 15.25M3.50001 3.50002V0.750031M3.50001 3.50002H0.75"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
}
199 changes: 118 additions & 81 deletions packages/feedback/src/screenshot/components/ScreenshotEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-
import type * as Hooks from 'preact/hooks';
import { DOCUMENT, WINDOW } from '../../constants';
import CropCornerFactory from './CropCorner';
import CropIconFactory from './CropIcon';
import PenIconFactory from './PenIcon';
import { createScreenshotInputStyles } from './ScreenshotInput.css';
import { useTakeScreenshotFactory } from './useTakeScreenshot';
Expand Down Expand Up @@ -75,6 +76,7 @@ export function ScreenshotEditorFactory({
const useTakeScreenshot = useTakeScreenshotFactory({ hooks });
const CropCorner = CropCornerFactory({ h });
const PenIcon = PenIconFactory({ h });
const CropIcon = CropIconFactory({ h });

return function ScreenshotEditor({ onError }: Props): VNode {
const styles = hooks.useMemo(() => ({ __html: createScreenshotInputStyles(options.styleNonce).innerText }), []);
Expand All @@ -86,6 +88,7 @@ export function ScreenshotEditorFactory({
const [croppingRect, setCroppingRect] = hooks.useState<Box>({ startX: 0, startY: 0, endX: 0, endY: 0 });
const [confirmCrop, setConfirmCrop] = hooks.useState(false);
const [isResizing, setIsResizing] = hooks.useState(false);
const [isCropping, setIsCropping] = hooks.useState(false);
const [isAnnotating, setIsAnnotating] = hooks.useState(false);

hooks.useEffect(() => {
Expand Down Expand Up @@ -142,6 +145,10 @@ export function ScreenshotEditorFactory({
const croppingBox = constructRect(croppingRect);
ctx.clearRect(0, 0, imageDimensions.width, imageDimensions.height);

if (!isCropping) {
return;
}

// draw gray overlay around the selection
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, imageDimensions.width, imageDimensions.height);
Expand All @@ -154,7 +161,7 @@ export function ScreenshotEditorFactory({
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1;
ctx.strokeRect(croppingBox.x + 3, croppingBox.y + 3, croppingBox.width - 6, croppingBox.height - 6);
}, [croppingRect]);
}, [croppingRect, isCropping]);

function onGrabButton(e: Event, corner: string): void {
setIsAnnotating(false);
Expand Down Expand Up @@ -398,102 +405,132 @@ export function ScreenshotEditorFactory({
return (
<div class="editor">
<style nonce={options.styleNonce} dangerouslySetInnerHTML={styles} />
{options._experiments.annotations && (
<div class="editor__tool-container">
<button
class="editor__pen-tool"
style={{
background: isAnnotating
? 'var(--button-primary-background, var(--accent-background))'
: 'var(--button-background, var(--background))',
color: isAnnotating
? 'var(--button-primary-foreground, var(--accent-foreground))'
: 'var(--button-foreground, var(--foreground))',
}}
onClick={e => {
e.preventDefault();
setIsAnnotating(!isAnnotating);
}}
>
<PenIcon />
</button>
</div>
)}
<div class="editor__canvas-container" ref={canvasContainerRef}>
<div class="editor__crop-container" style={{ zIndex: isAnnotating ? 1 : 2 }} ref={cropContainerRef}>
<div class="editor__image-container">
<div class="editor__canvas-container" ref={canvasContainerRef}>
<div class="editor__crop-container" style={{ zIndex: isAnnotating ? 1 : 2 }} ref={cropContainerRef}>
<canvas
onMouseDown={onDragStart}
style={{ cursor: confirmCrop ? 'move' : 'auto' }}
ref={croppingRef}
></canvas>
{isCropping && (
<div>
<CropCorner
left={croppingRect.startX - CROP_BUTTON_BORDER}
top={croppingRect.startY - CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="top-left"
></CropCorner>
<CropCorner
left={croppingRect.endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
top={croppingRect.startY - CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="top-right"
></CropCorner>
<CropCorner
left={croppingRect.startX - CROP_BUTTON_BORDER}
top={croppingRect.endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="bottom-left"
></CropCorner>
<CropCorner
left={croppingRect.endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
top={croppingRect.endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="bottom-right"
></CropCorner>
</div>
)}
<div
style={{
left: Math.max(0, croppingRect.endX - 191),
top: Math.max(0, croppingRect.endY + 8),
display: confirmCrop ? 'flex' : 'none',
}}
class="editor__crop-btn-group"
>
<button
onClick={e => {
e.preventDefault();
if (croppingRef.current) {
setCroppingRect({
startX: 0,
startY: 0,
endX: croppingRef.current.width / DPI,
endY: croppingRef.current.height / DPI,
});
}
setConfirmCrop(false);
}}
class="btn btn--default"
>
{options.cancelButtonLabel}
</button>
<button
onClick={e => {
e.preventDefault();
applyCrop();
setConfirmCrop(false);
}}
class="btn btn--primary"
>
{options.confirmButtonLabel}
</button>
</div>
</div>

<canvas
onMouseDown={onDragStart}
style={{ cursor: confirmCrop ? 'move' : 'auto' }}
ref={croppingRef}
class="editor__annotation"
onMouseDown={onAnnotateStart}
style={{ zIndex: isAnnotating ? '2' : '1' }}
ref={annotatingRef}
></canvas>
<CropCorner
left={croppingRect.startX - CROP_BUTTON_BORDER}
top={croppingRect.startY - CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="top-left"
></CropCorner>
<CropCorner
left={croppingRect.endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
top={croppingRect.startY - CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="top-right"
></CropCorner>
<CropCorner
left={croppingRect.startX - CROP_BUTTON_BORDER}
top={croppingRect.endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="bottom-left"
></CropCorner>
<CropCorner
left={croppingRect.endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
top={croppingRect.endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER}
onGrabButton={onGrabButton}
corner="bottom-right"
></CropCorner>
<div
style={{
left: Math.max(0, croppingRect.endX - 191),
top: Math.max(0, croppingRect.endY + 8),
display: confirmCrop ? 'flex' : 'none',
}}
class="editor__crop-btn-group"
>
</div>
</div>
{options._experiments.annotations && (
<div class="editor__tool-container">
<div />
<div class="editor__tool-bar">
<button
class="editor__tool"
style={{
background: isAnnotating
? 'var(--button-primary-background, var(--accent-background))'
: 'var(--button-background, var(--background))',
color: isAnnotating
? 'var(--button-primary-foreground, var(--accent-foreground))'
: 'var(--button-foreground, var(--foreground))',
}}
onClick={e => {
e.preventDefault();
if (croppingRef.current) {
setCroppingRect({
startX: 0,
startY: 0,
endX: croppingRef.current.width / DPI,
endY: croppingRef.current.height / DPI,
});
}
setConfirmCrop(false);
setIsAnnotating(!isAnnotating);
setIsCropping(false);
}}
class="btn btn--default"
>
{options.cancelButtonLabel}
<CropIcon />
</button>
<button
class="editor__tool"
style={{
background: isCropping
? 'var(--button-primary-background, var(--accent-background))'
: 'var(--button-background, var(--background))',
color: isCropping
? 'var(--button-primary-foreground, var(--accent-foreground))'
: 'var(--button-foreground, var(--foreground))',
}}
onClick={e => {
e.preventDefault();
applyCrop();
setConfirmCrop(false);
setIsCropping(!isCropping);
setIsAnnotating(false);
}}
class="btn btn--primary"
>
{options.confirmButtonLabel}
<PenIcon />
</button>
</div>
<div />
</div>
<canvas
class="editor__annotation"
onMouseDown={onAnnotateStart}
style={{ zIndex: isAnnotating ? '2' : '1' }}
ref={annotatingRef}
></canvas>
</div>
)}
</div>
);
};
Expand Down
19 changes: 14 additions & 5 deletions packages/feedback/src/screenshot/components/ScreenshotInput.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ export function createScreenshotInputStyles(styleNonce?: string): HTMLStyleEleme

style.textContent = `
.editor {
display: flex;
flex-grow: 1;
flex-direction: column;
}
.editor__image-container {
padding: 10px;
padding-top: 65px;
padding-bottom: 65px;
flex-grow: 1;
position: relative;
height: 100%;
border-radius: var(--menu-border-radius, 6px);
background-color: ${surface200};
background-image: repeating-linear-gradient(
Expand Down Expand Up @@ -90,13 +96,16 @@ export function createScreenshotInputStyles(styleNonce?: string): HTMLStyleEleme
border-top: none;
}
.editor__tool-container {
position: absolute;
padding: 10px 0px;
top: 0;
padding-top: 10px;
display: flex;
justify-content: space-between;
}
.editor__pen-tool {
.editor__tool-bar {
height: 30px;
display: flex;
gap: 8px;
}
.editor__tool {
justify-content: center;
align-items: center;
border: var(--button-border, var(--border));
Expand Down

0 comments on commit 20de538

Please sign in to comment.