JSFiddle - React, Tailwind, and code Playground

by Steve Eberhardt

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/521/fabric.js"></script>
<div id="app"></div>

CSS

/*
  Style the container of the annotation target to use flex boxes 

  The selectors need to be specific enough to override Polaris's '.aws-ui .x' selectors.
*/
.container {
  /*
    The annotation-area-containers also have an inline 'width' style. It needs to be inline so that
    we can use calc() and take into account the side-nav width.
  */
  display: flex;
  flex-flow: column;
  padding: 8px 9px 1px 9px; /* we use left+right padding in the inline 'width' style */
  box-sizing: border-box;
  width: 100%;
  height: 100vh;
}

.container > .main {
  display: flex;
  flex-grow: 1;
  min-height: 0; /* min-height is needed so that resizing the window always adjusts the height */
  min-width: 0; /* min-width is needed so that resizing the window always adjusts the width */
  /* 
    Note: Shrinking the window in ie11 causes the canvas to overlap the header. This is a known
    bug with ie11 and is marked as "won't fix" by the Edge team.
    https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8600549/
  */
}

.container > .main .target {
  display: flex;
  flex: 7;
  min-height: 0; /* min-height is needed so that resizing the window always adjusts the height */
  min-width: 0; /* min-width is needed so that resizing the window always adjusts the width */
}

.container > .main .target > * {
  align-self: flex-start;
  min-height: 0; /* min-height is needed so that resizing the window always adjusts the height */
  min-width: 0; /* min-width is needed so that resizing the window always adjusts the width */
}

.container > .main .canvas-wrapper {
  align-self: auto;
  flex: 1;
  min-height: 0; /* min-height is needed so that resizing the window always adjusts the height */
  min-width: 0; /* min-width is needed so that resizing the window always adjusts the width */
}

.container > .main .category-picker-wrapper {
  flex: 3;
  min-height: 0; /* min-height is needed so that resizing the window always adjusts the height */
  min-width: 0; /* min-width...

React

const src = 'https://i.imgur.com/CIPKVJo.jpg';
  
const onEventStop = (eventName, callback) => {
  let timer;
  window.addEventListener(eventName, () => {
    clearTimeout(timer);
    timer = setTimeout(callback, 250);
  });
};

const constrainViewportToImage = ({
  zoom, canvasViewPort, backgroundWidth, canvasWidth, backgroundHeight, canvasHeight,
}) => {
  const yOffset = 5; // key to see the Y offset of the canvas viewport
  const xOffset = 4; // key to see the X offset of the canvas viewport

  // Ensure canvas viewport is within Y bounds of image
  // The top of the backgroundImage should only move as low as the top of the canvas, and as high as
  // the top of the canvas - the verticalOverflow
  const verticalOverflow = (backgroundHeight * zoom) - canvasHeight;
  if (canvasViewPort[yOffset] >= 0) {
    // Case: Top of the background image moved lower than the top of the canvas
    canvasViewPort[yOffset] = 0;
  } else if (-verticalOverflow > canvasViewPort[yOffset]) {
    // Case: Top of the background image moved higher than the verticalOverflow
    canvasViewPort[yOffset] = -verticalOverflow;
  }

  // Ensure canvas viewport is within X bounds of image
  // The left of the backgroundImage should only move as far to the right as the left of the canvas,
  // and as far left as the left of the canvas - the horizontalOverflow
  const horizontalOverflow = (backgroundWidth * zoom) - canvasWidth;
  if (canvasViewPort[xOffset] >= 0) {
    // Case: Left of the background image moved to the right passed the left side of the canvas
    canvasViewPort[xOffset] = 0;
  } else if (-horizontalOverflow > canvasViewPort[xOffset]) {
    // Case: Left of the background image moved to the left further than the horizontalOverflow
    canvasViewPort[xOffset] = -horizontalOverflow;
  }
};

const generateCrosshair = (mousePosX, mousePosY, canvas) => {
  //changing the cursor type to crosshair
  canvas.defaultCursor = dashedLineConstants.defaultCursor;

  //generate horizontal of...