polished#position JavaScript Examples

The following examples show how to use polished#position. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: search.js    From guitar-book with MIT License 6 votes vote down vote up
Container = styled.div({
  flexGrow: 1,
  marginRight: 40,
  color: colors.text2,
  position: 'relative',
  zIndex: 1,
  [breakpoints.md]: {
    marginRight: 0
  }
})
Example #2
Source File: search.js    From guitar-book with MIT License 6 votes vote down vote up
SuggestionBox = styled.div({
  width: '100%',
  overflowY: 'auto',
  maxWidth: '100%',
  minWidth: 'auto',
  marginTop: 14,
  position: 'absolute',
  background: 'white',
  borderRadius,
  boxShadow,
  maxHeight: `calc(100vh - ${HEADER_HEIGHT}px - 32px)`,
  padding: 0,
  border
})
Example #3
Source File: search.js    From guitar-book with MIT License 6 votes vote down vote up
Overlay = styled.div(
  position('fixed', 0),
  props =>
    !props.visible && {
      opacity: 0,
      visibility: 'hidden'
    },
  {
    backgroundColor: transparentize(0.5, colors.text2),
    transitionProperty: 'opacity, visibility',
    transitionDuration: '150ms',
    transitionTimingFunction: 'ease-in-out',
    zIndex: 1
  }
)
Example #4
Source File: search.js    From guitar-book with MIT License 5 votes vote down vote up
verticalAlign = css({
  position: 'absolute',
  top: '50%',
  transform: 'translateY(-50%)'
})
Example #5
Source File: flip-editor-tools.js    From idena-web with MIT License 5 votes vote down vote up
export function ColorPicker({visible, color, onChange}) {
  const colorPickerRef = useRef()
  const colors = [
    ['ffffff', 'd2d4d9e0', '96999edd', '53565cdd'],
    ['ff6666dd', 'ff60e7dd', 'a066ffdd', '578fffdd'],
    ['0cbdd0dd', '27d980dd', 'ffd763dd', 'ffa366dd'],
  ]

  useClickOutside(colorPickerRef, () => {
    onChange(color)
  })

  return (
    <div
      style={{
        display: `${visible ? '' : 'none'}`,
      }}
    >
      <Box css={position('relative')} ref={colorPickerRef}>
        <Absolute top={0} right={rem(40)} zIndex={100}>
          <Menu>
            {colors.map((row, i) => (
              <Flex key={i} css={{marginLeft: rem(10), marginRight: rem(10)}}>
                {row.map((c, j) => {
                  const showColor = c === 'ffffff' ? '#d2d4d9' : `#${c}`
                  const circleStyle = {
                    padding: rem(1),
                    border: `${color === c ? '1px' : '0px'} solid ${showColor}`,
                    borderRadius: '50%',
                    fontSize: theme.fontSizes.large,
                  }
                  return (
                    <IconButton
                      key={`${j}${j}`}
                      icon={
                        c === 'ffffff' ? (
                          <FiCircle color={showColor} style={circleStyle} />
                        ) : (
                          <FaCircle color={showColor} style={circleStyle} />
                        )
                      }
                      onClick={() => {
                        if (onChange) {
                          onChange(c)
                        }
                      }}
                    ></IconButton>
                  )
                })}
              </Flex>
            ))}
          </Menu>
        </Absolute>
      </Box>
    </div>
  )
}
Example #6
Source File: flip-editor-tools.js    From idena-web with MIT License 4 votes vote down vote up
export function ArrowHint({hint, leftHanded}) {
  return (
    <ChakraBox position="relative">
      <ChakraBox position="absolute" bottom="86px" zIndex={90}>
        {leftHanded && (
          <div>
            <div
              style={{
                minWidth: rem(24),
                minHeight: rem(40),
                borderLeft: `2px solid ${theme.colors.primary}`,
                borderTop: `2px solid ${theme.colors.primary}`,
              }}
            />
            <div
              style={{
                position: 'absolute',
                left: '-5px',
                width: 0,
                height: 0,
                borderTop: `6px solid transparent`,
                borderLeft: `6px solid transparent`,
                borderRight: `6px solid transparent`,
                borderBottom: 0,
                borderTopColor: `${theme.colors.primary}`,
              }}
            />
            <div
              style={{
                position: 'absolute',
                left: '30px',
                top: '-25px',
                minWidth: '75px',
                color: `${theme.colors.muted}`,
                fontWeight: `${theme.fontWeights.normal}`,
              }}
            >
              {hint}
            </div>
          </div>
        )}

        {!leftHanded && (
          <div>
            <div
              style={{
                minWidth: rem(24),
                minHeight: rem(40),
                borderRight: `2px solid ${theme.colors.primary}`,
                borderTop: `2px solid ${theme.colors.primary}`,
              }}
            />
            <div
              style={{
                position: 'absolute',
                left: rem(16),
                width: 0,
                height: 0,
                marginLeft: '0px',
                borderLeft: `6px solid transparent`,
                borderRight: `6px solid transparent`,
                borderTop: `6px solid transparent`,
                borderBottom: 0,
                borderTopColor: `${theme.colors.primary}`,
              }}
            />
            <div
              style={{
                position: 'absolute',
                left: '-58px',
                top: '-25px',
                minWidth: rem(52, theme.fontSizes.base),
                width: rem(52, theme.fontSizes.base),
                color: `${theme.colors.muted}`,
                fontWeight: `${theme.fontWeights.normal}`,
              }}
            >
              {hint}
            </div>
          </div>
        )}
      </ChakraBox>
    </ChakraBox>
  )
}
Example #7
Source File: flip-editor-tools.js    From idena-web with MIT License 4 votes vote down vote up
export function EditorContextMenu({
  x,
  y,
  onClose,
  onCopy,
  onPaste,
  onDelete,
  onClear,
  onBringOnTop,
}) {
  const {t} = useTranslation()

  const contextMenuRef = useRef()
  useClickOutside(contextMenuRef, () => {
    onClose()
  })

  return (
    <Box>
      <Flex>
        <Box css={position('relative')}>
          <Box ref={contextMenuRef}>
            <Absolute top={y} left={x} zIndex={100}>
              <Menu>
                <MenuItem
                  disabled={!onCopy}
                  onClick={() => {
                    onCopy()
                    onClose()
                  }}
                  icon={<CopyIcon boxSize={5} />}
                >
                  {`${t('Copy')} (Ctrl/Cmd+C)`}
                </MenuItem>

                <MenuItem
                  disabled={!onPaste}
                  onClick={() => {
                    onPaste()
                    onClose()
                  }}
                  icon={<ClipboardIcon boxSize={5} />}
                >
                  {`${t('Paste image')} (Ctrl/Cmd+V)`}
                </MenuItem>

                {onBringOnTop && (
                  <MenuItem
                    onClick={() => {
                      onBringOnTop()
                      onClose()
                    }}
                  >
                    {`${t('Bring on top')} `}
                  </MenuItem>
                )}

                <MenuItem
                  disabled={!onDelete}
                  onClick={() => {
                    onDelete()
                    onClose()
                  }}
                  danger
                  icon={<DeleteIcon boxSize={5} color="red.500" />}
                >
                  {`${t('Delete')} `}
                </MenuItem>

                {onClear && (
                  <MenuItem
                    onClick={() => {
                      onClear()
                      onClose()
                    }}
                    icon={<BasketIcon boxSize={5} color="red.500" />}
                  >
                    {`${t('Clear')} `}
                  </MenuItem>
                )}
              </Menu>
            </Absolute>
          </Box>
        </Box>
      </Flex>
    </Box>
  )
}
Example #8
Source File: flip-editor-tools.js    From idena-web with MIT License 4 votes vote down vote up
export function ImageEraseEditor({
  url,
  brushWidth,
  imageObjectProps,
  onDone,
  onChanging,
  isDone,
}) {
  const canvasRef = useRef()
  const [isMouseDown, setIsMouseDown] = useState(false)

  useEffect(() => {
    if (isDone && onDone) {
      if (canvasRef.current) {
        onDone(canvasRef.current.toDataURL())
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isDone])

  const handleMouseMove = useCallback(
    e => {
      const ctx = canvasRef.current && canvasRef.current.getContext('2d')

      const x = e.nativeEvent.offsetX
      const y = e.nativeEvent.offsetY
      if (ctx && isMouseDown) {
        onChanging()
        ctx.globalCompositeOperation = 'destination-out'

        ctx.beginPath()
        ctx.arc(x, y, brushWidth / 2, 0, 2 * Math.PI)
        ctx.fill()
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [canvasRef, isMouseDown]
  )

  const handleMouseDown = () => {
    setIsMouseDown(true)

    onChanging()
  }

  const handleMouseUp = () => {
    setIsMouseDown(false)
  }

  useEffect(() => {
    let ignore = false

    async function init() {
      if (!ignore && canvasRef.current) {
        let img = new Image()
        img.setAttribute('crossOrigin', 'anonymous')
        img.src = url
        img.onload = function() {
          const width =
            img.width * ((imageObjectProps && imageObjectProps.scaleX) || 1)
          const height =
            img.height * ((imageObjectProps && imageObjectProps.scaleY) || 1)
          canvasRef.current.width = width
          canvasRef.current.height = height

          const ctx = canvasRef.current.getContext('2d')
          ctx.drawImage(img, 0, 0, width, height)

          img = null
        }
      }
    }
    init()
    return () => {
      ignore = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [canvasRef])

  const left =
    imageObjectProps &&
    imageObjectProps.x -
      (imageObjectProps.width * imageObjectProps.scaleX) / 2 +
      1
  const top =
    imageObjectProps &&
    imageObjectProps.y -
      (imageObjectProps.height * imageObjectProps.scaleY) / 2 +
      1

  const angle = (imageObjectProps && imageObjectProps.angle) || 0

  return (
    <Box css={position('relative')}>
      <Absolute
        top={0}
        left={0}
        zIndex={100}
        width="442px"
        css={{
          height: '333px',
          paddingTop: '0.5px',
          paddingLeft: '0.5px',
          overflow: 'hidden',
        }}
      >
        <Box
          style={{
            width: '100%',
            height: '100%',
            cursor: 'crosshair',
            borderRadius: rem(12),
          }}
          onMouseDown={handleMouseDown}
          onMouseUp={handleMouseUp}
        >
          <canvas
            style={{
              background: 'transparent',
              position: 'absolute',
              left: `${left}px`,
              top: `${top}px`,
              transform: `rotate(${angle}deg)`,
            }}
            ref={canvasRef}
            onMouseMove={e => handleMouseMove(e)}
          ></canvas>
        </Box>
      </Absolute>
    </Box>
  )
}
Example #9
Source File: flip-editor.js    From idena-web with MIT License 4 votes vote down vote up
export default function FlipEditor({
  idx = 0,
  src,
  visible,
  onChange,
  onChanging,
}) {
  const {t} = useTranslation()
  const toast = useToast()

  const [blankImage, setBlankImage] = useState(BLANK_IMAGE_DATAURL)

  // Button menu
  const [isInsertImageMenuOpen, setInsertImageMenuOpen] = useState(false)
  const insertMenuRef = [useRef(), useRef(), useRef(), useRef()]
  // Context menu
  const [showContextMenu, setShowContextMenu] = useState(false)
  const [contextMenuCursor, setContextMenuCursor] = useState({x: 0, y: 0})

  useClickOutside(insertMenuRef[idx], () => {
    setInsertImageMenuOpen(false)
  })

  const [bottomMenuPanel, setBottomMenuPanel] = useState(BottomMenu.Main)
  const [rightMenuPanel, setRightMenuPanel] = useState(RightMenu.None)

  const [brush, setBrush] = useState(20)
  const [brushColor, setBrushColor] = useState('ff6666dd')
  const [showColorPicker, setShowColorPicker] = useState(false)
  const [showArrowHint, setShowArrowHint] = useState(!src && idx === 0)

  // Editors
  const editorRefs = useRef([
    createRef(),
    createRef(),
    createRef(),
    createRef(),
  ])
  const uploaderRef = useRef()
  const [editors, setEditors] = useState([null, null, null, null])
  const setEditor = (k, e) => {
    if (e) {
      setEditors([...editors.slice(0, k), e, ...editors.slice(k + 1)])
    }
  }

  const [isSelectionCreated, setIsSelectionCreated] = useState(null)
  const [activeObjectUrl, setActiveObjectUrl] = useState(null)
  const [activeObjectId, setActiveObjectId] = useState(null)

  // Postponed onChange() triggering
  const NOCHANGES = 0
  const NEWCHANGES = 1
  const CHANGED = 5

  const [changesCnt, setChangesCnt] = useState(NOCHANGES)
  const handleOnChanging = useCallback(() => {
    if (changesCnt === -1) return
    onChanging(idx)
    if (!changesCnt) setChangesCnt(1)
  }, [changesCnt, idx, onChanging])

  const handleOnChanged = useCallback(() => {
    setChangesCnt(CHANGED)
  }, [])

  useInterval(() => {
    if (changesCnt >= NEWCHANGES) {
      setShowArrowHint(false)
      setChangesCnt(changesCnt + 1)
    }
    if (changesCnt >= CHANGED) {
      setChangesCnt(NOCHANGES)
      const url = editors[idx].toDataURL()
      onChange(url)
    }
  }, 200)

  const [insertImageMode, setInsertImageMode] = useState(0)

  const setImageUrl = useCallback(
    (data, onDone = null) => {
      const {url, insertMode, customEditor} = data
      const nextInsertMode = insertMode || insertImageMode
      const editor = customEditor || editors[idx]

      if (!editor) return

      if (!url) {
        editor.loadImageFromURL(blankImage, 'blank').then(() => {
          setChangesCnt(NOCHANGES)
          onChange(null)
        })
        return
      }

      if (nextInsertMode === INSERT_OBJECT_IMAGE) {
        setChangesCnt(NOCHANGES)

        let replaceObjectProps
        if (data.replaceObjectId) {
          replaceObjectProps = editors[
            idx
          ].getObjectProperties(data.replaceObjectId, ['left', 'top', 'angle'])
          editors[idx].execute('removeObject', data.replaceObjectId)
        }
        Jimp.read(url).then(image => {
          image.getBase64Async('image/png').then(async nextUrl => {
            const resizedNextUrl = await imageResizeSoft(
              nextUrl,
              IMAGE_WIDTH,
              IMAGE_HEIGHT
            )
            editor.addImageObject(resizedNextUrl).then(objectProps => {
              if (data.replaceObjectId) {
                editors[idx].setObjectPropertiesQuietly(
                  objectProps.id,
                  replaceObjectProps
                )
              }

              handleOnChanged()
              setActiveObjectId(objectProps.id)
              setActiveObjectUrl(resizedNextUrl)

              if (onDone) onDone()

              if (editors[idx]._graphics) {
                editors[idx]._graphics.renderAll()
              }
            })
          })
        })
      }

      if (nextInsertMode === INSERT_BACKGROUND_IMAGE) {
        editor.loadImageFromURL(blankImage, 'blank').then(() => {
          editor.addImageObject(url).then(objectProps => {
            const {id} = objectProps
            const {width, height} = editor.getObjectProperties(id, [
              'left',
              'top',
              'width',
              'height',
            ])
            const {newWidth, newHeight} = resizing(
              width,
              height,
              IMAGE_WIDTH,
              IMAGE_HEIGHT,
              false
            )
            editor.setObjectPropertiesQuietly(id, {
              left: IMAGE_WIDTH / 2 + Math.random() * 200 - 400,
              top: IMAGE_HEIGHT / 2 + Math.random() * 200 - 400,
              width: newWidth * 10,
              height: newHeight * 10,
              opacity: 0.5,
            })
            editor.loadImageFromURL(editor.toDataURL(), 'BlurBkgd').then(() => {
              editor.addImageObject(url).then(objectProps2 => {
                const {id: id2} = objectProps2

                editor.setObjectPropertiesQuietly(id2, {
                  left: IMAGE_WIDTH / 2,
                  top: IMAGE_HEIGHT / 2,
                  scaleX: newWidth / width,
                  scaleY: newHeight / height,
                })
                editor.loadImageFromURL(editor.toDataURL(), 'Bkgd').then(() => {
                  editor.clearUndoStack()
                  editor.clearRedoStack()
                  handleOnChanged()
                  if (onDone) onDone()

                  if (editors[idx]._graphics) {
                    editors[idx]._graphics.renderAll()
                  }
                })
              })
            })
          })
        })
      }
    },
    [blankImage, editors, handleOnChanged, idx, insertImageMode, onChange]
  )

  const [showImageSearch, setShowImageSearch] = React.useState()

  // File upload handling
  const handleUpload = e => {
    e.preventDefault()
    const file = e.target.files[0]
    if (!file || !file.type.startsWith('image')) {
      return
    }
    const reader = new FileReader()
    reader.addEventListener('loadend', async re => {
      const url = await imageResizeSoft(
        re.target.result,
        IMAGE_WIDTH,
        IMAGE_HEIGHT
      )
      setImageUrl({url})
      setInsertImageMode(0)
    })
    reader.readAsDataURL(file)
    e.target.value = ''
  }

  const handleImageFromClipboard = async (
    insertMode = INSERT_BACKGROUND_IMAGE
  ) => {
    const list = await navigator.clipboard.read()
    let type
    const item = list.find(listItem =>
      listItem.types.some(itemType => {
        if (itemType.startsWith('image/')) {
          type = itemType
          return true
        }
        return false
      })
    )
    const blob = item && (await item.getType(type))

    if (blob) {
      const reader = new FileReader()
      reader.addEventListener('loadend', async re => {
        setImageUrl({url: re.target.result, insertMode})
      })
      reader.readAsDataURL(blob)
    }
  }

  const {addNotification} = useNotificationDispatch()

  const handleOnCopy = () => {
    const url = activeObjectUrl || (editors[idx] && editors[idx].toDataURL())
    if (url) {
      writeImageURLToClipboard(url).then(() =>
        addNotification({
          title: t('Copied'),
        })
      )
    }
  }

  const handleOnPaste = () => {
    handleImageFromClipboard()
  }

  const handleUndo = () => {
    if (editors[idx]) {
      editors[idx].undo().then(() => {
        setChangesCnt(NOCHANGES)
        handleOnChanged()
      })
    }
  }

  const handleRedo = () => {
    if (editors[idx]) {
      editors[idx].redo().then(() => {
        setChangesCnt(NOCHANGES)
        handleOnChanged()
      })
    }
  }

  const handleOnDelete = () => {
    if (editors[idx]) {
      editors[idx].removeActiveObject()
      setChangesCnt(NOCHANGES)
      handleOnChanged()
    }
  }

  const handleOnClear = () => {
    if (rightMenuPanel === RightMenu.Erase) {
      setRightMenuPanel(RightMenu.None)
    }
    setImageUrl({url: null})
  }

  if (visible) {
    mousetrap.bind(['command+v', 'ctrl+v'], function(e) {
      handleOnPaste()
      e.stopImmediatePropagation()
      return false
    })

    mousetrap.bind(['command+c', 'ctrl+c'], function(e) {
      handleOnCopy()
      e.stopImmediatePropagation()
      return false
    })

    mousetrap.bind(['command+z', 'ctrl+z'], function(e) {
      handleUndo()
      e.stopImmediatePropagation()
      return false
    })

    mousetrap.bind(['shift+ctrl+z', 'shift+command+z'], function(e) {
      handleRedo()
      e.stopImmediatePropagation()
      return false
    })
  }

  function getEditorInstance() {
    const editor =
      editorRefs.current[idx] &&
      editorRefs.current[idx].current &&
      editorRefs.current[idx].current.getInstance()
    return editor
  }

  function getEditorActiveObjectId(editor) {
    const objId =
      editor &&
      editor._graphics &&
      editor._graphics._canvas &&
      editor._graphics._canvas._activeObject &&
      editor._graphics._canvas._activeObject.__fe_id
    return objId
  }

  function getEditorObjectUrl(editor, objId) {
    const obj =
      objId && editor && editor._graphics && editor._graphics._objects[objId]
    const url = obj && obj._element && obj._element.src

    return url
  }

  function getEditorObjectProps(editor, objId) {
    const obj =
      objId && editor && editor._graphics && editor._graphics._objects[objId]
    if (obj) {
      return {
        x: obj.translateX,
        y: obj.translateY,
        width: obj.width,
        height: obj.height,
        angle: obj.angle,
        scaleX: obj.scaleX,
        scaleY: obj.scaleY,
      }
    }
    return null
  }

  // init editor
  React.useEffect(() => {
    const updateEvents = e => {
      if (!e) return
      e.on({
        mousedown() {
          setShowContextMenu(false)

          const editor = getEditorInstance()
          const objId = getEditorActiveObjectId(editor)
          const url = getEditorObjectUrl(editor, objId)

          setActiveObjectId(objId)
          setActiveObjectUrl(url)

          if (e.getDrawingMode() === 'FREE_DRAWING') {
            setChangesCnt(NOCHANGES)
          }
        },
      })

      e.on({
        objectMoved() {
          handleOnChanging()
        },
      })
      e.on({
        objectRotated() {
          handleOnChanging()
        },
      })
      e.on({
        objectScaled() {
          handleOnChanging()
        },
      })
      e.on({
        undoStackChanged() {
          const editor = getEditorInstance()
          const objId = getEditorActiveObjectId(editor)
          const url = getEditorObjectUrl(editor, objId)

          setActiveObjectId(objId)
          setActiveObjectUrl(url)

          handleOnChanging()
        },
      })
      e.on({
        redoStackChanged() {
          const editor = getEditorInstance()
          const objId = getEditorActiveObjectId(editor)
          const url = getEditorObjectUrl(editor, objId)

          setActiveObjectId(objId)
          setActiveObjectUrl(url)

          handleOnChanging()
        },
      })
      e.on({
        objectActivated() {
          //
        },
      })

      e.on({
        selectionCreated() {
          setIsSelectionCreated(true)
        },
      })

      e.on({
        selectionCleared() {
          setIsSelectionCreated(false)
        },
      })
    }

    async function initEditor() {
      const data = await imageResize(
        BLANK_IMAGE_DATAURL,
        IMAGE_WIDTH,
        IMAGE_HEIGHT,
        false
      )
      setBlankImage(data)

      const containerEl = document.querySelectorAll(
        '.tui-image-editor-canvas-container'
      )[idx]

      const containerCanvas = document.querySelectorAll('.lower-canvas')[idx]

      if (containerEl) {
        containerEl.parentElement.style.height = rem(328)
        containerEl.addEventListener('contextmenu', e => {
          setContextMenuCursor({x: e.layerX, y: e.layerY})
          setShowContextMenu(true)
          setRightMenuPanel(RightMenu.None)
          if (editors[idx]) {
            editors[idx].stopDrawingMode()
          }
          e.preventDefault()
        })
      }

      if (containerCanvas) {
        containerCanvas.style.borderRadius = rem(8)
      }

      const newEditor =
        editorRefs.current[idx] &&
        editorRefs.current[idx].current &&
        editorRefs.current[idx].current.getInstance()

      if (newEditor) {
        if (!editors[idx]) {
          setEditor(idx, newEditor)
          newEditor.setBrush({width: brush, color: brushColor})

          if (src) {
            newEditor.loadImageFromURL(src, 'src').then(() => {
              newEditor.clearUndoStack()
              newEditor.clearRedoStack()
              updateEvents(newEditor)
            })
          } else {
            newEditor.loadImageFromURL(blankImage, 'blank').then(() => {
              newEditor.clearUndoStack()
              newEditor.clearRedoStack()
              updateEvents(newEditor)
            })
          }
        }
      }
    }

    initEditor()

    return () => {
      mousetrap.reset()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [editorRefs, src, idx])

  React.useEffect(() => {
    if (showImageSearch || !visible) {
      editorRefs.current[idx].current.getInstance().discardSelection()
    }
  }, [idx, showImageSearch, visible])

  const leftArrowPortalRef = React.useRef()
  const rightArrowPortalRef = React.useRef()

  return (
    <div
      style={{
        display: `${visible ? '' : 'none'}`,
      }}
    >
      <Flex>
        <Box>
          {(bottomMenuPanel === BottomMenu.Erase ||
            rightMenuPanel === RightMenu.Erase) && (
            <ImageEraseEditor
              url={activeObjectUrl}
              isDone={bottomMenuPanel !== BottomMenu.Erase}
              brushWidth={brush}
              imageObjectProps={getEditorObjectProps(
                editors[idx],
                activeObjectId
              )}
              onChanging={() => {
                if (editors[idx] && activeObjectId) {
                  setChangesCnt(NOCHANGES)
                  editors[idx].setObjectPropertiesQuietly(activeObjectId, {
                    opacity: 0,
                  })
                }
              }}
              onDone={url => {
                if (url) {
                  if (editors[idx] && activeObjectId) {
                    setChangesCnt(NOCHANGES)
                    editors[idx].setObjectPropertiesQuietly(activeObjectId, {
                      opacity: 1,
                    })
                  }

                  setImageUrl(
                    {
                      url,
                      insertMode: INSERT_OBJECT_IMAGE,
                      replaceObjectId: activeObjectId,
                    },
                    () => {
                      setRightMenuPanel(RightMenu.None)
                    }
                  )
                }
              }}
            />
          )}

          {showContextMenu && (
            <EditorContextMenu
              x={contextMenuCursor.x}
              y={contextMenuCursor.y}
              onClose={() => {
                setShowContextMenu(false)
              }}
              onCopy={() => {
                handleOnCopy()
              }}
              onPaste={async () =>
                handleImageFromClipboard(INSERT_OBJECT_IMAGE)
              }
              onDelete={
                activeObjectId || isSelectionCreated ? handleOnDelete : null
              }
            />
          )}

          <ChakraBox
            h={rem(IMAGE_HEIGHT)}
            w={rem(IMAGE_WIDTH)}
            border="1px"
            borderColor="brandGray.016"
            rounded="lg"
          >
            <ImageEditor
              key={idx}
              ref={editorRefs.current[idx]}
              cssMaxHeight={IMAGE_HEIGHT}
              cssMaxWidth={IMAGE_WIDTH}
              selectionStyle={{
                cornerSize: 8,
                rotatingPointOffset: 20,
                lineWidth: '1',
                cornerColor: theme.colors.white,
                cornerStrokeColor: theme.colors.primary,
                transparentCorners: false,
                borderColor: theme.colors.primary,
              }}
              usageStatistics={false}
            />
          </ChakraBox>

          {bottomMenuPanel === BottomMenu.Main && (
            <Stack isInline align="center" spacing={3} mt={6}>
              <FlipEditorIcon
                tooltip={t('Search on web')}
                icon={<SearchIcon />}
                onClick={() => {
                  if (rightMenuPanel === RightMenu.Erase) {
                    setRightMenuPanel(RightMenu.None)
                  }
                  setInsertImageMode(INSERT_BACKGROUND_IMAGE)
                  setShowImageSearch(true)
                }}
              />

              {showArrowHint && (
                <Portal containerRef={leftArrowPortalRef}>
                  <ArrowHint
                    hint={t('Start from uploading an image')}
                    leftHanded
                  />
                </Portal>
              )}

              <Box ref={leftArrowPortalRef}>
                <FlipEditorIcon
                  tooltip={t('Select file')}
                  icon={<FolderIcon />}
                  onClick={() => {
                    if (rightMenuPanel === RightMenu.Erase) {
                      setRightMenuPanel(RightMenu.None)
                    }
                    setInsertImageMode(INSERT_BACKGROUND_IMAGE)
                    uploaderRef.current.click()
                  }}
                />
              </Box>

              <VisuallyHidden>
                <input
                  id="file"
                  type="file"
                  accept="image/*"
                  ref={uploaderRef}
                  onChange={handleUpload}
                />
              </VisuallyHidden>

              <FlipEditorIcon
                tooltip={t('Add image')}
                icon={<AddImageIcon />}
                onClick={() => {
                  if (rightMenuPanel === RightMenu.Erase) {
                    setRightMenuPanel(RightMenu.None)
                  }
                  editors[idx].stopDrawingMode()
                  setRightMenuPanel(RightMenu.None)
                  setInsertImageMenuOpen(!isInsertImageMenuOpen)
                }}
              />

              <FlipEditorToolbarDivider />

              <FlipEditorIcon
                icon={<UndoIcon />}
                tooltip={`${t('Undo')} (Ctrl/Cmd+Z})`}
                isDisabled={editors[idx] && editors[idx].isEmptyUndoStack()}
                onClick={handleUndo}
              />
              <FlipEditorIcon
                icon={<RedoIcon />}
                tooltip={`${t('Redo')} (Ctrl/Cmd+Shift+Z})`}
                isDisabled={editors[idx] && editors[idx].isEmptyUndoStack()}
                onClick={handleRedo}
              />

              <FlipEditorToolbarDivider />

              <FlipEditorIcon
                tooltip={t('Crop image')}
                icon={<CropIcon />}
                isDisabled={src === null}
                onClick={() => {
                  editors[idx].startDrawingMode('CROPPER')
                  if (rightMenuPanel === RightMenu.Erase) {
                    setRightMenuPanel(RightMenu.None)
                  }
                  setBottomMenuPanel(BottomMenu.Crop)
                }}
              />

              {showArrowHint && (
                <Portal containerRef={rightArrowPortalRef}>
                  <ArrowHint hint={t('Or start drawing')} />
                </Portal>
              )}

              <ChakraBox ref={rightArrowPortalRef}>
                <FlipEditorIcon
                  tooltip={t('Draw')}
                  isActive={rightMenuPanel === RightMenu.FreeDrawing}
                  icon={<DrawIcon />}
                  onClick={() => {
                    setShowArrowHint(false)
                    const editor = editors[idx]
                    if (editor.getDrawingMode() === 'FREE_DRAWING') {
                      setRightMenuPanel(RightMenu.None)
                      editor.stopDrawingMode()
                    } else {
                      setRightMenuPanel(RightMenu.FreeDrawing)
                      editor.startDrawingMode('FREE_DRAWING')
                    }
                  }}
                />
              </ChakraBox>

              <FlipEditorIcon
                isDisabled={!activeObjectUrl}
                tooltip={
                  activeObjectUrl ? t('Erase') : t('Select image to erase')
                }
                isActive={rightMenuPanel === RightMenu.Erase}
                icon={<EraserIcon />}
                onClick={() => {
                  if (rightMenuPanel === RightMenu.Erase) {
                    setRightMenuPanel(RightMenu.None)
                    setBottomMenuPanel(BottomMenu.Main)
                  } else {
                    setRightMenuPanel(RightMenu.Erase)
                    setBottomMenuPanel(BottomMenu.Erase)
                  }
                }}
              />

              <FlipEditorToolbarDivider />

              <FlipEditorIcon
                tooltip={t('Clear')}
                icon={<BasketIcon />}
                color="red.500"
                _hover={{color: 'red.500'}}
                onClick={handleOnClear}
              />
            </Stack>
          )}

          {bottomMenuPanel === BottomMenu.Crop && (
            <ApplyChangesBottomPanel
              label={t('Crop image')}
              onCancel={() => {
                setBottomMenuPanel(BottomMenu.Main)
                setRightMenuPanel(RightMenu.None)
                if (editors[idx]) {
                  editors[idx].stopDrawingMode()
                }
              }}
              onDone={() => {
                setBottomMenuPanel(BottomMenu.Main)
                if (editors[idx]) {
                  const {width, height} = editors[idx].getCropzoneRect()
                  if (width < 1 || height < 1) {
                    editors[idx].stopDrawingMode()
                  } else {
                    editors[idx]
                      .crop(editors[idx].getCropzoneRect())
                      .then(() => {
                        editors[idx].stopDrawingMode()
                        setRightMenuPanel(RightMenu.None)
                        setImageUrl({
                          url: editors[idx].toDataURL(),
                          insertMode: INSERT_BACKGROUND_IMAGE,
                          customEditor: editors[idx],
                        })
                      })
                  }
                }
              }}
            />
          )}

          {bottomMenuPanel === BottomMenu.Erase && (
            <ApplyChangesBottomPanel
              label={t('Erase')}
              onCancel={() => {
                if (editors[idx] && activeObjectId) {
                  setChangesCnt(NOCHANGES)
                  editors[idx].setObjectPropertiesQuietly(activeObjectId, {
                    opacity: 1,
                  })
                }

                setBottomMenuPanel(BottomMenu.Main)
                setRightMenuPanel(RightMenu.None)
              }}
              onDone={() => {
                setBottomMenuPanel(BottomMenu.Main)
              }}
            />
          )}

          <Box>
            <Flex>
              <Box css={position('relative')}>
                {isInsertImageMenuOpen && (
                  <Box ref={insertMenuRef[idx]}>
                    <Absolute top="-11.4em" right="-17em" zIndex={100}>
                      <Menu>
                        <MenuItem
                          onClick={async () => {
                            setInsertImageMenuOpen(false)
                            setInsertImageMode(INSERT_OBJECT_IMAGE)
                            setShowImageSearch(true)
                          }}
                          disabled={false}
                          icon={<SearchIcon boxSize={5} name="search" />}
                        >
                          {t('Search on web')}
                        </MenuItem>
                        <MenuItem
                          onClick={async () => {
                            setInsertImageMenuOpen(false)
                            setInsertImageMode(INSERT_OBJECT_IMAGE)
                            uploaderRef.current.click()
                          }}
                          disabled={false}
                          icon={<FolderIcon boxSize={5} name="folder" />}
                        >
                          {t('Select file')}
                        </MenuItem>
                        <MenuItem
                          onClick={async () => {
                            setInsertImageMenuOpen(false)
                            handleImageFromClipboard(INSERT_OBJECT_IMAGE)
                          }}
                          disabled={false}
                          danger={false}
                          icon={<ClipboardIcon boxSize={5} />}
                        >
                          {t('Paste image')}
                        </MenuItem>
                      </Menu>
                    </Absolute>
                  </Box>
                )}
              </Box>
            </Flex>
          </Box>
        </Box>

        {(rightMenuPanel === RightMenu.FreeDrawing ||
          rightMenuPanel === RightMenu.Erase) && (
          <Stack align="center" ml={6}>
            <ColorPicker
              color={brushColor}
              visible={showColorPicker}
              onChange={c => {
                setShowColorPicker(false)
                setBrushColor(c)
                if (!editors[idx]) return
                const nextColor = `#${c}`
                editors[idx].setBrush({width: brush, color: nextColor})
              }}
            />

            {rightMenuPanel === RightMenu.FreeDrawing && (
              <>
                <ChakraBox
                  bg={`#${brushColor}`}
                  border="1px"
                  borderColor="brandGray.016"
                  rounded="full"
                  boxSize={4}
                  onClick={() => setShowColorPicker(!showColorPicker)}
                />

                <Divider borderColor="gray.100" w={6} />
              </>
            )}

            <Brushes
              brush={brush}
              onChange={b => {
                setBrush(b)
                if (!editors[idx]) return
                editors[idx].setBrush({width: b, color: brushColor})
              }}
            ></Brushes>
          </Stack>
        )}
      </Flex>
      <ImageSearchDialog
        isOpen={showImageSearch}
        onPick={url => {
          if (visible) {
            setImageUrl({url})
          }
          setInsertImageMode(0)
          setShowImageSearch(false)
        }}
        onClose={() => {
          setShowImageSearch(false)
        }}
        onError={error =>
          toast({
            // eslint-disable-next-line react/display-name
            render: () => <Toast title={error} status="error" />,
          })
        }
      />
    </div>
  )
}