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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
)
}