From e7b9833cc577c9fa6b0980c9f2b39c3435d8a41d Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Thu, 31 Mar 2022 17:05:59 +0200 Subject: [PATCH 01/30] feat(drawzone): Gestion tactille MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changements apportés : - Gestion des événements tactilles dans la drawzone --- package.json | 6 +- src/draw-zone/components.tsx | 277 +++++ src/draw-zone/hooks.ts | 1248 ++++++++++++++++++++ src/draw-zone/index.stories.tsx | 44 +- src/draw-zone/index.tsx | 1909 +------------------------------ src/draw-zone/state.ts | 101 ++ src/draw-zone/types.ts | 97 ++ src/hooks.ts | 21 +- yarn.lock | 2 +- 9 files changed, 1794 insertions(+), 1911 deletions(-) create mode 100644 src/draw-zone/components.tsx create mode 100644 src/draw-zone/hooks.ts create mode 100644 src/draw-zone/state.ts create mode 100644 src/draw-zone/types.ts diff --git a/package.json b/package.json index b8b231f..d14202e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.26", + "version": "0.0.27-beta1", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ @@ -26,7 +26,8 @@ "dependencies": { "@svgdotjs/svg.draggable.js": "^3.0.2", "@svgdotjs/svg.js": "^3.0.16", - "interactjs": "^1.9.20" + "interactjs": "^1.9.20", + "lodash": "^4.17.21" }, "peerDependencies": { "react": "^17.0.2", @@ -45,6 +46,7 @@ "@storybook/builder-webpack5": "^6.5.8", "@storybook/manager-webpack5": "^6.5.8", "@storybook/react": "^6.5.8", + "@types/lodash": "^4.14.182", "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.2", diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx new file mode 100644 index 0000000..dd56c13 --- /dev/null +++ b/src/draw-zone/components.tsx @@ -0,0 +1,277 @@ +import React, { + ReactNode, + useCallback, + useContext, + useEffect, + useLayoutEffect, + useReducer, + useRef, + useState, +} from 'react' +import { usePointerPosition } from '../hooks' +import { useDraw } from './hooks' +import { DrawZoneContext, drawZoneInitialState, drawZoneReducer } from './state' +import { + ChangedElement, + DrawZoneMode, + DrawZoneShape, + DrawZoneState, + DrawZoneStateActionType, + SizeMode, +} from './types' + +export interface DrawZoneProps { + readonly children?: React.ReactNode + readonly mode?: DrawZoneMode + readonly shape: DrawZoneShape + readonly sizeMode?: SizeMode + readonly src: string + readonly elements: Partial[] + readonly initialRect?: ChangedElement + readonly onChange: (elements: ChangedElement[]) => void + readonly remove: (id: string) => void + readonly onInitialRectChange?: ( + arg: Pick, + ) => void +} + +export default function DrawZone({ + children, + mode = 'draw', + shape, + sizeMode = 'auto', + src, + elements, + onChange, + remove, + initialRect, + onInitialRectChange, +}: DrawZoneProps) { + const { + state: { + scale, + isMarkerShown, + positionTop, + positionLeft, + redraw, + logicalScale, + }, + dispatch, + } = useContext(DrawZoneContext) + const svgRef = useRef(null) + const containerRef = useRef(null) + const { svg, draw, originalSize } = useDraw(svgRef, src, { + onChange, + remove, + mode, + shape, + drawOnMouseDown: true, + initialRect, + onInitialRectChange, + }) + const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) + const [forceRedraw, setForceRedraw] = useState(false) + + const setScale = useCallback( + (scale: number) => { + dispatch({ + type: DrawZoneStateActionType.SET_SCALE, + payload: scale, + }) + }, + [dispatch], + ) + + useEffect(() => { + dispatch({ + type: DrawZoneStateActionType.SET_ORIGINAL_SIZE, + payload: originalSize, + }) + dispatch({ + type: DrawZoneStateActionType.FORCE_REDRAW, + }) + }, [originalSize]) + + useEffect(() => { + if (sizeMode === 'auto') { + setScale(logicalScale) + } else if (containerRef.current && originalSize) { + const rect = containerRef.current.getBoundingClientRect() + + const minWidth = Math.min(rect.width, originalSize.width) + const minHeight = Math.min(rect.height, originalSize.height) + + if ( + originalSize.width <= minWidth && + originalSize.height <= minHeight + ) { + const maxWidth = Math.max(rect.width, originalSize.width) + const maxHeight = Math.max(rect.height, originalSize.height) + + const coef = maxWidth / minWidth + const coef2 = maxHeight / minHeight + + if (originalSize.height * coef <= maxHeight) { + setScale(coef * logicalScale) + } else if (originalSize.width * coef2 <= maxWidth) { + setScale(coef2 * logicalScale) + } else { + setScale(logicalScale) + } + } else if ( + minWidth < originalSize.width || + minHeight < originalSize.height + ) { + setScale( + Math.min( + minWidth / originalSize.width, + minHeight / originalSize.height, + ) * logicalScale, + ) + } + } + }, [containerRef, originalSize, sizeMode, logicalScale]) + + useEffect(() => { + const { current } = svgRef + const { current: container } = containerRef + + if (current && container) { + const handlePointerEnter = () => { + setCanMarkerBeVisible(true) + } + const handlePointerLeave = () => { + setCanMarkerBeVisible(false) + } + + current.addEventListener('pointerenter', handlePointerEnter) + current.addEventListener('pointerleave', handlePointerLeave) + + return () => { + current.removeEventListener('pointerenter', handlePointerEnter) + current.removeEventListener('pointerleave', handlePointerLeave) + } + } + }, []) + + useEffect(() => { + if (svgRef.current) { + svgRef.current.style.top = `${positionTop}px` + svgRef.current.style.left = `${positionLeft}px` + svgRef.current.style.transform = 'none' + } + }, [positionTop, positionLeft]) + + useEffect(() => { + setForceRedraw(true) + }, [mode, redraw]) + + useLayoutEffect(() => { + if (svg) { + if ( + elements.length !== + svg.children().filter((c) => !c.attr('data-draw-ignore')) + .length || + forceRedraw + ) { + svg.clear() + elements.forEach((element) => draw(element as ChangedElement)) + + if (forceRedraw) setForceRedraw(false) + return + } + } + }, [svg, elements, forceRedraw, scale]) + + return ( +
+
+ {canMarkerBeVisible && isMarkerShown && ( + + )} + {children} +
+
+ ) +} + +type MarkerProps = { + readonly src: string + readonly svgRef: React.RefObject +} +function Marker({ src, svgRef }: MarkerProps): JSX.Element { + const { clientX, clientY } = usePointerPosition() + + const width = svgRef.current?.getBoundingClientRect().width + const height = svgRef.current?.getBoundingClientRect().height + const left = clientX - (svgRef.current?.getBoundingClientRect().left || 0) + const top = clientY - (svgRef.current?.getBoundingClientRect().top || 0) + + return ( + <> +
+
+ + ) +} + +export type DrawZoneContainerProps = Partial & { + readonly children: ReactNode +} +export function DrawZoneContainer({ + children, + ...props +}: DrawZoneContainerProps) { + const [state, dispatch] = useReducer(drawZoneReducer, { + ...drawZoneInitialState, + ...props, + }) + + return ( + + {children} + + ) +} diff --git a/src/draw-zone/hooks.ts b/src/draw-zone/hooks.ts new file mode 100644 index 0000000..f1a9070 --- /dev/null +++ b/src/draw-zone/hooks.ts @@ -0,0 +1,1248 @@ +import { + ArrayXY, + Circle, + FillData, + LinkedHTMLElement, + PointArrayAlias, + Polygon, + Polyline, + Rect, + SVG, + StrokeData, + Svg, + Use, +} from '@svgdotjs/svg.js' +import interact from 'interactjs' +import { + useCallback, + useContext, + useEffect, + useLayoutEffect, + useState, +} from 'react' +import { uuid4 } from '../helpers' +import { isTouchDevice } from '../utils' +import { DrawZoneContext } from './state' +import { + ChangedElement, + DrawZoneMode, + DrawZoneShape, + DrawZoneState, + DrawZoneStateActionType, + Point, + Size, +} from './types' + +const xns = 'http://www.w3.org/1999/xlink' +const blue = '#2BB1FD' +const defaultStroke = { color: '#fff', width: 2, opacity: 1 } +const defaultFill = { color: '#000', opacity: 0 } + +function getAbsoluteCoordinates(svg: Svg, points: Point[]) { + const svgRect = svg.node.getBoundingClientRect() + + return points.map(({ x, y }) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })) +} + +export function useDrawZone() { + const { state, dispatch } = useContext(DrawZoneContext) + + const zoomIn = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.ZOOM_IN }) + }, [dispatch]) + + const zoomOut = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.ZOOM_OUT }) + }, [dispatch]) + + const reset = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.RESET }) + }, [dispatch]) + + const toggleMarker = useCallback(() => { + if (state.isMarkerShown) { + dispatch({ type: DrawZoneStateActionType.HIDE_MARKER }) + } else { + dispatch({ type: DrawZoneStateActionType.SHOW_MARKER }) + } + }, [dispatch, state.isMarkerShown]) + + const disable = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.DISABLE }) + }, [dispatch]) + + const enable = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.ENABLE }) + }, [dispatch]) + + const redraw = useCallback(() => { + dispatch({ type: DrawZoneStateActionType.FORCE_REDRAW }) + }, [dispatch]) + + return { + ...(state as DrawZoneState), + zoomIn, + zoomOut, + reset, + toggleMarker, + disable, + enable, + redraw, + } +} + +export function useDraw( + ref: React.RefObject, + src: string, + props: { + readonly onChange: (elements: Array) => void + readonly remove: (id: string) => void + readonly mode: DrawZoneMode + readonly shape: DrawZoneShape + readonly drawOnMouseDown?: boolean + readonly initialRect?: ChangedElement + readonly onInitialRectChange?: ( + arg: Pick, + ) => void + }, +) { + const { + state: { isMarkerShown, isDisabled, scale, positionTop, positionLeft }, + dispatch, + } = useContext(DrawZoneContext) + const [svg, setSvg] = useState() + const [originalSize, setOriginalSize] = useState() + let startPosition: Point | null + let overlayRect: Rect | undefined + let overlayRect2: Rect | undefined + let poly: Polygon | undefined + let tmpPoly: Polyline | undefined + let tmpPoints: Array | undefined + let dragging: boolean + + const convertForOnChange = useCallback( + function convertForOnChange(): ChangedElement[] { + if (!svg) { + return [] + } + + const svgRect = svg.node.getBoundingClientRect() + + return svg + .children() + .filter((e) => !e.attr('data-draw-ignore')) + .map((elt) => { + const elementRect = elt.node.getBoundingClientRect() + + const rect: ChangedElement['rect'] = { + height: (elementRect.height / svgRect.height) * 100, + width: (elementRect.width / svgRect.width) * 100, + x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, + y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, + } + + const polygon = elt as Polygon + const points: Point[] = getAbsoluteCoordinates( + svg, + polygon + .plot() + .map((p) => ({ + x: p[0], + y: p[1], + })) + .filter( + (_, index) => + props.shape === 'poly' || index % 2 === 0, + ), + ) + + const result = { + points, + rect, + label: elt.data('label'), + selected: elt.data('selected') as boolean, + id: elt.data('id'), + color: elt.data('color'), + } + + return result + }) + }, + [svg], + ) + + function onChange() { + props.onChange(convertForOnChange()) + } + + function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { + if (event.defaultPrevented) return false + if (event.key === 'Delete' && this.closest('svg')) { + event.preventDefault() + props.remove(this.dataset['id'] as string) + + return true + } + + return false + } + + function onEscKeyPress(this: SVGElement, event: KeyboardEvent): boolean { + if (event.defaultPrevented) return false + if (event.key === 'Escape' && this.closest('svg')) { + event.preventDefault() + ;(this as unknown as LinkedHTMLElement).instance.fire('deselect') + + return true + } + + return false + } + + function endPolyDrawing(event: Event) { + if (!svg || !poly) return + + const svgRect = svg.node.getBoundingClientRect() + const plot = poly.plot() + + if (plot.length < 3) return + + event.preventDefault() + + poly.remove() + poly = undefined + + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + startPosition = null + + const newPoly = drawPoly({ + points: plot.map(([x, y]) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })), + }) + + window.removeEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.removeEventListener('keyup', onEnterKeyPress, { capture: true }) + + if (newPoly) { + newPoly.fire('select') + } + + onChange() + } + + function onEnterKeyPress(this: Window, event: KeyboardEvent) { + if (event.defaultPrevented) return + if (event.key === 'Enter') { + event.preventDefault() + + endPolyDrawing(event) + } + } + + function onAbortPathDrawing(this: Window, event: KeyboardEvent) { + if (event.defaultPrevented) return + if (event.key === 'Escape') { + event.preventDefault() + + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + if (poly) { + poly.remove() + poly = undefined + } + + startPosition = null + + window.removeEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.removeEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + } + } + + const preventDrag = (event: DragEvent) => { + event.preventDefault() + } + + function drawRect({ + points, + disabled = isDisabled, + stroke = { ...defaultStroke }, + fill = { ...defaultFill }, + + label, + id = null, + }: { + points: Point[] + disabled?: boolean + stroke?: StrokeData + fill?: FillData + label?: string + id?: string | null + }) { + const minX = Math.min(points[0].x, points[1].x) + const minY = Math.min(points[0].y, points[1].y) + const maxX = Math.max(points[0].x, points[1].x) + const maxY = Math.max(points[0].y, points[1].y) + + const newPoints: Point[] = [ + { x: minX, y: minY }, + { x: maxX, y: minY }, + { x: maxX, y: maxY }, + { x: minX, y: maxY }, + ] + + return drawPoly({ + points: newPoints, + disabled, + stroke, + fill, + label, + id, + }) + } + + function drawPoly({ + points, + disabled = isDisabled, + stroke = { ...defaultStroke }, + fill = { ...defaultFill }, + label, + id = null, + }: { + points: Point[] + disabled?: boolean + stroke?: StrokeData + fill?: FillData + label?: string + id?: string | null + }) { + if (!svg || !points || points.length < 2) { + return + } + const svgRect = svg.node.getBoundingClientRect() + + const svgWidth = svgRect.width || originalSize?.width || 0 + const svgHeight = svgRect.height || originalSize?.height || 0 + + const poly = svg.polygon( + points.map( + (point) => [point.x * svgWidth, point.y * svgHeight] as ArrayXY, + ), + ) + + poly.fill(fill) + poly.stroke(stroke) + poly.css('touch-action', 'none') // silence interactjs warning. + + const circles: Circle[] = [] + const handles: Use[] = [] + let rootMatrix: DOMMatrix + + function polyDelKeyPress(ev: KeyboardEvent) { + const result = onDelKeyPress.call(poly.node, ev) + + if (result) { + window.removeEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + } + } + function polyEscKeyPress(ev: KeyboardEvent) { + const result = onEscKeyPress.call(poly.node, ev) + + if (result) { + window.removeEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + } + } + + function makeHandlesGrabbable(svg: Svg) { + interact('.point-handle') + .draggable({ + onstart: function (event) { + svg.css('cursor', 'grabbing') + event.target.instance.css('cursor', 'grabbing') + }, + onmove: function (event) { + const i = event.target.getAttribute('data-index') | 0 + const point = poly.node.points.getItem(i) + + point.x += event.dx / rootMatrix.a + point.y += event.dy / rootMatrix.d + + if (props.shape === 'rect') { + switch (i) { + case 0: // top left + handles[0].x(point.x) + handles[0].y(point.y) + handles[1].y(point.y) + handles[3].x(point.x) + break + case 1: // top right + handles[1].x(point.x) + handles[1].y(point.y) + handles[2].x(point.x) + handles[0].y(point.y) + break + case 2: // bottom right + handles[2].x(point.x) + handles[2].y(point.y) + handles[3].y(point.y) + handles[1].x(point.x) + break + case 3: // bottom left + handles[3].x(point.x) + handles[3].y(point.y) + handles[0].x(point.x) + handles[2].y(point.y) + break + } + + const newPlot = handles.map( + (h) => + [Number(h.x()), Number(h.y())] as ArrayXY, + ) + poly.plot(newPlot) + } else { + event.target.x.baseVal.value = point.x + event.target.y.baseVal.value = point.y + } + }, + onend: function (event) { + event.target.instance.css('cursor', 'grab') + svg.css('cursor', 'crosshair') + const index = Number( + event.target.getAttribute('data-index') || 0, + ) + + if (props.shape === 'poly') { + const currentPlot = [...poly.plot()] + + const newPlot: ArrayXY[] = [ + ...currentPlot.slice(0, index), + [ + Number(event.target.getAttribute('x')), + Number(event.target.getAttribute('y')), + ] as ArrayXY, + ...currentPlot.slice(index + 1), + ] + + poly.plot(newPlot) + } + + svg.node.setAttribute('class', '') + + onChange() + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + elementRect: { + top: 0, + left: 0, + bottom: 1, + right: 1, + }, + }), + ], + }) + .styleCursor(false) + } + + // Custom events. + poly.on('select', () => { + // Deselect all + svg.each(function (this: Svg) { + this.fire('deselect', { inst: poly }) + }) + poly.stroke({ color: blue }) + poly.data('selected', true) + window.addEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + window.addEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + + if (!disabled) { + handles.forEach((h) => h.remove()) + handles.length = 0 + circles.forEach((c) => c.remove()) + circles.length = 0 + rootMatrix = svg.node.getScreenCTM() as DOMMatrix + + for (let i = 0; i < poly.node.points.numberOfItems; i++) { + const point = poly.node.points.getItem(i) + + const handleId = `point-handle-${i}` + + const circle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(isTouchDevice ? 14 : 7) + .center(0, 0) + .fill({ opacity: 1, color: blue }) + .stroke({ width: 1, color: '#fff' }) + .id(handleId) + + const mouseenter = () => { + circle.scale(1.4) + + circle.on('mouseleave', mouseleave) + circle.off('mouseenter', mouseenter) + } + const mouseleave = () => { + circle.scale(5 / 7) + + circle.on('mouseenter', mouseenter) + circle.off('mouseleave', mouseleave) + } + + circle.on('mouseenter', mouseenter) + + const handle = svg + .use(circle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.push(circle) + handles.push(handle) + } + + makeHandlesGrabbable(svg) + + document.addEventListener('dragstart', preventDrag) + } + + onChange() + }) + poly.on('deselect', (e) => { + if ((e as CustomEvent).detail?.inst === poly) return + poly.stroke(stroke) + poly.data('selected', false) + + interact('.point-handle').unset() + handles.forEach((h) => h.remove()) + handles.length = 0 + circles.forEach((circle) => circle.remove()) + circles.length = 0 + document.removeEventListener('dragstart', preventDrag) + + window.removeEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + window.removeEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + + onChange() + }) + + if (!disabled) { + poly.css('cursor', 'move') + + interact(poly.node).draggable({ + listeners: { + start() { + interact('.point-handle').unset() + handles.forEach((handle) => { + handle.remove() + }) + handles.length = 0 + circles.forEach((circle) => { + circle.remove() + }) + circles.length = 0 + }, + move(event) { + const x = parseFloat(event.target.instance.x()) + const y = parseFloat(event.target.instance.y()) + + event.target.instance.x(x + event.dx) + event.target.instance.y(y + event.dy) + + onChange() + }, + end() { + for ( + let i = 0; + i < poly.node.points.numberOfItems; + i++ + ) { + const point = poly.node.points.getItem(i) + + const handleId = `point-handle-${i}` + + const newCircle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(isTouchDevice ? 14 : 7) + .center(0, 0) + .fill({ opacity: 1, color: blue }) + .stroke({ width: 1, color: '#fff' }) + .id(handleId) + + const mouseenter = () => { + newCircle.scale(1.4) + + newCircle.on('mouseleave', mouseleave) + newCircle.off('mouseenter', mouseenter) + } + const mouseleave = () => { + newCircle.scale(5 / 7) + + newCircle.on('mouseenter', mouseenter) + newCircle.off('mouseleave', mouseleave) + } + + newCircle.on('mouseenter', mouseenter) + + const handle = svg + .use(newCircle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.push(newCircle) + handles.push(handle) + } + + makeHandlesGrabbable(svg) + }, + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, + }), + ], + cursorChecker: (action, interactable, element, interacting) => { + switch (action.axis) { + case 'x': + return 'ew-resize' + case 'y': + return 'ns-resize' + default: + return interacting ? 'grabbing' : 'move' + } + }, + }) + } + + poly.data('disabled', disabled) + poly.data('id', id || uuid4()) + poly.data('label', label) + + if (defaultStroke.color !== stroke.color) + poly.data('color', stroke.color) + + return poly + } + + function drawPoint(svg: Svg, x: number, y: number): Circle { + const delta = isTouchDevice ? 6 : 3 + + const point = svg + .circle(isTouchDevice ? 12 : 6) + .center(0, 0) + .fill({ opacity: 1, color: '#f06' }) + .stroke({ width: 1, color: '#fff' }) + .attr('data-draw-ignore', true) + .move(x - delta, y - delta) + + const mouseenter = () => { + point.scale(1.4) + + point.on('mouseleave', mouseleave) + point.off('mouseenter', mouseenter) + } + const mouseleave = () => { + point.scale(5 / 7) + + point.on('mouseenter', mouseenter) + point.off('mouseleave', mouseleave) + } + + point.on('mouseenter', mouseenter) + point.on('pointerdown', function (event) { + event.preventDefault() + event.stopPropagation() + + endPolyDrawing(event) + }) + + return point + } + + const draw = ({ + points: inputPoints, + id, + color, + label, + }: ChangedElement) => { + const fill = color ? { ...defaultFill, color } : defaultFill + const stroke = color ? { ...defaultStroke, color } : defaultStroke + + const points = + inputPoints?.map( + (point) => + ({ + x: point.x, + y: point.y, + } as Point), + ) ?? [] + + if (points.length === 2) drawRect({ points, id, fill, stroke, label }) + else drawPoly({ points, id, fill, stroke, label }) + } + + function onPointerDown(e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg) return + + if (!svg.node.contains(e.target as Node)) return + + if ( + svg.node.contains(e.target as Node) && + svg.node !== e.target && + !startPosition + ) { + const element = e.target as unknown as LinkedHTMLElement + + element.instance.fire('select') + return + } else if (e.target === svg.node) { + svg.each(function (this: Svg) { + this.fire('deselect') + }) + onChange() + } + + if (props.mode === 'draw' && !isDisabled) { + const svgRect = svg.node.getBoundingClientRect() + + if (props.shape === 'poly' && !isDisabled) { + if (!tmpPoints) { + startPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + tmpPoints = [ + drawPoint(svg, startPosition.x, startPosition.y), + ] + + window.addEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.addEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + } else if (startPosition && Array.isArray(tmpPoints)) { + const currentPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + const prev = poly + ? [...poly.plot()] + : [[startPosition.x, startPosition.y] as ArrayXY] + + if (poly) { + poly.remove() + poly = undefined + } + + const start = prev[0] + if ( + prev.length > 2 && + Math.abs(currentPosition.x - start[0]) <= 10 && + Math.abs(currentPosition.y - start[1]) <= 10 + ) { + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + startPosition = null + + const poly = drawPoly({ + points: prev.map(([x, y]) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })), + }) + + window.removeEventListener( + 'keyup', + onAbortPathDrawing, + { + capture: true, + }, + ) + window.removeEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + + poly?.fire('select') + + onChange() + } else { + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + poly = svg + .polygon(plotline) + .fill({ + color: '#f06', + opacity: 0, + }) + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + }) + .attr('data-draw-ignore', true) + + tmpPoints.push( + drawPoint( + svg, + currentPosition.x, + currentPosition.y, + ), + ) + + tmpPoints.forEach((p) => p.front()) + + startPosition = currentPosition + } + } + } else { + startPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (!overlayRect || !overlayRect2) { + overlayRect = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ + color: '#000', + width: 2, + opacity: 0.7, + dasharray: '5,5', + }) + overlayRect2 = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ + color: '#fff', + width: 2, + opacity: 0.7, + dasharray: '5,5', + dashoffset: 5, + }) + } + + overlayRect.move( + startPosition.x / svgRect.width, + startPosition.y / svgRect.height, + ) + + overlayRect2.move( + startPosition.x / svgRect.width, + startPosition.y / svgRect.height, + ) + + e.preventDefault() + } + } else if (props.mode === 'move') { + startPosition = { + x: e.clientX, + y: e.clientY, + } + + dragging = true + + svg.css({ + cursor: 'grabbing', + }) + + e.preventDefault() + } + } + + function onPointerMove(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg || !startPosition) return + + if (props.mode === 'draw' && !isDisabled) { + if (props.shape === 'poly' && !isTouchDevice) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition: Point = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + const prev = poly + ? [...poly.plot()] + : [[startPosition.x, startPosition.y] as ArrayXY] + + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + tmpPoly = svg + .polyline(plotline) + .fill('none') + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + dasharray: '5,5', + }) + .attr('data-draw-ignore', true) + + if (Array.isArray(tmpPoints)) { + tmpPoints.forEach((point) => point.front()) + } + } else if (overlayRect && overlayRect2) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + const minX = + Math.min(startPosition.x, currentPosition.x) / svgRect.width + const minY = + Math.min(startPosition.y, currentPosition.y) / + svgRect.height + const maxX = + Math.max(startPosition.x, currentPosition.x) / svgRect.width + const maxY = + Math.max(startPosition.y, currentPosition.y) / + svgRect.height + + const width = Math.abs(maxX - minX) + const height = Math.abs(maxY - minY) + + overlayRect.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect.width(`${width * 100}%`) + overlayRect.height(`${height * 100}%`) + overlayRect2.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect2.width(`${width * 100}%`) + overlayRect2.height(`${height * 100}%`) + } + } else if (props.mode === 'move' && dragging) { + if (!svg.node.contains(e.target as Node)) { + dragging = false + return + } + const parent = svg.parent() + + if (!parent) return + + const currentPosition = { + x: e.clientX, + y: e.clientY, + } + const translationX = currentPosition.x - startPosition.x + const translationY = currentPosition.y - startPosition.y + + parent.css( + 'transform', + `translate3d(${translationX}px, ${translationY}px, 0px)`, + ) + } + } + + function onPointerUp(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg) return + + if (props.mode === 'draw' && props.shape === 'rect' && !isDisabled) { + if (!startPosition) { + return + } + + if (overlayRect || overlayRect2) { + overlayRect?.remove() + overlayRect = undefined + overlayRect2?.remove() + overlayRect2 = undefined + } + + // Prevent drawing new rect on rect dragend... + if ((e.target as Node | null)?.parentNode === svg.node) { + startPosition = null + return + } + + const svgRect = svg.node.getBoundingClientRect() + const currentPosition = { + x: + Math.max(Math.min(e.clientX, svgRect.right), svgRect.left) - + svgRect.left, + y: + Math.max(Math.min(e.clientY, svgRect.bottom), svgRect.top) - + svgRect.top, + } + + let label = undefined + // Prevent adding very small rects (mis-clicks). + if (Math.abs(currentPosition.x - startPosition.x) <= 2) { + const elements = convertForOnChange() + let lastRect = elements[elements.length - 1] + + if (props.initialRect) { + lastRect = props.initialRect + } + + label = lastRect?.label + + if (props.drawOnMouseDown && lastRect && lastRect.rect) { + currentPosition.x = Math.min( + startPosition.x + + (lastRect.rect.width * svgRect.width) / 100, + svgRect.width, + ) + currentPosition.y = Math.min( + startPosition.y + + (lastRect.rect.height * svgRect.height) / 100, + svgRect.height, + ) + } else { + startPosition = null + return + } + } + + const newRect = drawRect({ + points: [ + { + x: + Math.min(startPosition.x, currentPosition.x) / + svgRect.width, + y: + Math.min(startPosition.y, currentPosition.y) / + svgRect.height, + }, + { + x: + Math.max(startPosition.x, currentPosition.x) / + svgRect.width, + y: + Math.max(startPosition.y, currentPosition.y) / + svgRect.height, + }, + ], + label: label, + }) + + if (newRect && props.onInitialRectChange) { + const elementRect = newRect.node.getBoundingClientRect() + const rect: ChangedElement['rect'] = { + height: (elementRect.height / svgRect.height) * 100, + width: (elementRect.width / svgRect.width) * 100, + x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, + y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, + } + props.onInitialRectChange({ + rect: rect, + label: newRect.data('label'), + id: newRect.data('id'), + }) + } + + setTimeout(() => { + newRect?.fire('select') + onChange() + }, 50) + } else if (props.mode === 'move' && dragging) { + const parent = svg.parent() + + if (parent) { + const grandParent = parent.parent() + + if (grandParent) { + const parentRect = grandParent.node.getBoundingClientRect() + const svgRect = parent.node.getBoundingClientRect() + + dispatch({ + type: DrawZoneStateActionType.SET_POSITION, + payload: { + top: svgRect.top - parentRect.top, + left: svgRect.left - parentRect.left, + }, + }) + + svg.css({ cursor: 'grab' }) + + dragging = false + } + } + } + + if ( + props.mode !== 'draw' || + (props.mode === 'draw' && props.shape !== 'poly') + ) + startPosition = null + } + + useEffect(() => { + if (!ref.current) { + return + } + + const image = new Image() + image.onload = () => { + setOriginalSize({ + width: image.naturalWidth, + height: image.naturalHeight, + }) + } + image.src = src + ref.current.style.background = `url('${src}') center center / 100% 100% no-repeat` + ref.current.style.top = `${positionTop}px` + ref.current.style.left = `${positionLeft}px` + + if (svg) { + svg.node.remove() + } + + const newSvg = SVG().addTo(ref.current).size('100%', '100%').attr({ + 'xmlns:xlink': xns, + }) + + setSvg(newSvg) + }, [ref, src]) + + useEffect(() => { + if (ref.current && originalSize && scale) { + ref.current.style.width = `${originalSize.width * scale}px` + + ref.current.style.height = `${originalSize.height * scale}px` + + if (svg) { + dispatch({ + type: DrawZoneStateActionType.FORCE_REDRAW, + }) + } + } + }, [ref, originalSize, scale]) + + useLayoutEffect(() => { + if (!svg) { + return + } + + svg.css({ + cursor: + props.mode === 'move' && !isDisabled + ? 'grab' + : props.mode === 'none' && !isMarkerShown + ? 'normal' + : 'crosshair', + position: 'absolute', + top: '0', + left: '0', + }) + + const parent = svg.parent() + + if (parent) { + parent.css({ + position: 'relative', + userSelect: 'none', + transform: 'none', + }) + } + + svg.on('pointerdown', onPointerDown as unknown as EventListener) + window.addEventListener('pointerup', onPointerUp) + window.addEventListener('pointermove', onPointerMove) + + return () => { + svg.off('pointerdown', onPointerDown as unknown as EventListener) + window.removeEventListener('pointerup', onPointerUp) + window.removeEventListener('pointermove', onPointerMove) + } + }, [svg, props.mode, props.initialRect]) + + return { + svg, + draw, + originalSize, + } +} diff --git a/src/draw-zone/index.stories.tsx b/src/draw-zone/index.stories.tsx index 80e443e..ba993be 100644 --- a/src/draw-zone/index.stories.tsx +++ b/src/draw-zone/index.stories.tsx @@ -8,10 +8,15 @@ export default { decorators: [ (Story: FunctionComponent) => ( - +
+ +
), ], + parameters: { + layout: 'fullscreen', + }, } export function Default() { @@ -54,7 +59,7 @@ export function Default() { }, [originalSize]) return ( -
+ <>
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - /> -
-
+ setElements(elements)} + remove={(id) => + setElements((el) => el.filter((e) => e.id !== id)) + } + /> + ) } @@ -146,6 +148,7 @@ export function Rects() { setElements((el) => el.filter((e) => e.id !== id)) } mode={move ? 'move' : 'draw'} + shape="rect" > {elements.map((element, index) => { const elem = element as ChangedElement @@ -293,7 +296,8 @@ export function Polygons() { remove={(id) => setElements((el) => el.filter((e) => e.id !== id)) } - mode={move ? 'move' : 'path'} + mode={move ? 'move' : 'draw'} + shape="poly" > {elements.map((element, index) => { const elem = element as ChangedElement @@ -420,7 +424,8 @@ export function ScaleIn() { remove={(id) => setElements((el) => el.filter((e) => e.id !== id)) } - mode={move ? 'move' : 'path'} + mode={move ? 'move' : 'draw'} + shape="poly" sizeMode="fit" > {elements.map((element, index) => { @@ -547,6 +552,7 @@ export function ScaleOut() { setElements((el) => el.filter((e) => e.id !== id)) } mode={move ? 'move' : 'draw'} + shape="rect" sizeMode="fit" > {elements.map((element, index) => { @@ -673,6 +679,7 @@ export function ScaleInRect() { setElements((el) => el.filter((e) => e.id !== id)) } mode={move ? 'move' : 'draw'} + shape="rect" sizeMode="fit" > {elements.map((element, index) => { @@ -766,6 +773,7 @@ export function None() { setElements((el) => el.filter((e) => e.id !== id)) } mode={move ? 'move' : 'none'} + shape="rect" sizeMode="fit" />
diff --git a/src/draw-zone/index.tsx b/src/draw-zone/index.tsx index 8a92e16..7f0620b 100644 --- a/src/draw-zone/index.tsx +++ b/src/draw-zone/index.tsx @@ -1,1886 +1,27 @@ -import React, { - Dispatch, - ReactNode, - createContext, - useCallback, - useContext, - useEffect, - useLayoutEffect, - useReducer, - useRef, - useState, -} from 'react' +import DrawZone, { + DrawZoneContainer, + DrawZoneContainerProps, + DrawZoneProps, +} from './components' +import { useDrawZone } from './hooks' import { - ArrayXY, - Circle, - FillData, - PointArrayAlias, - Polygon, - Polyline, - Rect, - SVG, - StrokeData, - Svg, - Use, -} from '@svgdotjs/svg.js' -import '@svgdotjs/svg.draggable.js' -import interact from 'interactjs' -import { DraggableOptions } from '@interactjs/types/index' -import { uuid4 } from '../helpers' -import { useMousePosition } from '../hooks' -import { isTouchDevice } from '../utils' -import { LinkedHTMLElement } from '@svgdotjs/svg.js' - -export type DrawZoneMode = 'draw' | 'path' | 'move' | 'none' -export type SizeMode = 'auto' | 'fit' - -export interface Size { - readonly width: number - readonly height: number -} -export interface Point { - readonly x: number - readonly y: number -} -export interface ChangedElement { - readonly id: string - readonly selected?: boolean - readonly points: Point[] - readonly label: string - readonly rect: { - readonly height: number - readonly width: number - readonly x: number - readonly y: number - } - readonly color?: string -} - -export type DrawZoneState = { - readonly scale: number - readonly isMarkerShown: boolean - readonly isDisabled: boolean - readonly originalSize: Size | undefined -} - -export const MAX_SCALE = 4 -export const SCALE_STEP = 0.25 - -type DrawZoneStateInternal = DrawZoneState & { - readonly logicalScale: number - readonly positionTop: number - readonly positionLeft: number - readonly redraw: boolean -} - -enum DrawZoneStateActionType { - RESET, - SET_SCALE, - ZOOM_IN, - ZOOM_OUT, - CHANGE_MODE, - CHANGE_SIZE_MODE, - SHOW_MARKER, - HIDE_MARKER, - DISABLE, - ENABLE, - SET_ORIGINAL_SIZE, - SET_POSITION, - FORCE_REDRAW, -} - -type DrawZoneStateAction = - | { readonly type: DrawZoneStateActionType.RESET } - | { - readonly type: DrawZoneStateActionType.SET_SCALE - readonly payload: number - } - | { - readonly type: DrawZoneStateActionType.ZOOM_IN - } - | { - readonly type: DrawZoneStateActionType.ZOOM_OUT - } - | { - readonly type: DrawZoneStateActionType.SHOW_MARKER - } - | { - readonly type: DrawZoneStateActionType.HIDE_MARKER - } - | { - readonly type: DrawZoneStateActionType.DISABLE - } - | { - readonly type: DrawZoneStateActionType.ENABLE - } - | { - readonly type: DrawZoneStateActionType.SET_ORIGINAL_SIZE - readonly payload: Size | undefined - } - | { - readonly type: DrawZoneStateActionType.SET_POSITION - readonly payload: { - readonly top: number - readonly left: number - } - } - | { - readonly type: DrawZoneStateActionType.FORCE_REDRAW - } - -const drawZoneInitialState: DrawZoneStateInternal = { - scale: 1, - isMarkerShown: false, - isDisabled: false, - originalSize: undefined, - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - redraw: false, -} - -const drawZoneReducer = ( - state: DrawZoneStateInternal, - action: DrawZoneStateAction, -): DrawZoneStateInternal => { - switch (action.type) { - case DrawZoneStateActionType.RESET: - return { - ...state, - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - } - case DrawZoneStateActionType.SET_SCALE: - return { - ...state, - scale: action.payload, - } - case DrawZoneStateActionType.ZOOM_IN: - return { - ...state, - logicalScale: Math.min( - state.logicalScale + SCALE_STEP, - MAX_SCALE, - ), - } - case DrawZoneStateActionType.ZOOM_OUT: - return { - ...state, - logicalScale: Math.max( - SCALE_STEP, - state.logicalScale - SCALE_STEP, - ), - } - case DrawZoneStateActionType.SHOW_MARKER: - return { - ...state, - isMarkerShown: true, - } - case DrawZoneStateActionType.HIDE_MARKER: - return { - ...state, - isMarkerShown: false, - } - case DrawZoneStateActionType.DISABLE: - return { - ...state, - isDisabled: true, - } - case DrawZoneStateActionType.ENABLE: - return { - ...state, - isDisabled: false, - } - case DrawZoneStateActionType.SET_ORIGINAL_SIZE: - return { - ...state, - originalSize: action.payload, - } - case DrawZoneStateActionType.SET_POSITION: - return { - ...state, - positionTop: action.payload.top, - positionLeft: action.payload.left, - } - case DrawZoneStateActionType.FORCE_REDRAW: - return { - ...state, - redraw: !state.redraw, - } - default: - return state - } -} - -const DrawZoneContext = createContext<{ - readonly state: DrawZoneStateInternal - readonly dispatch: Dispatch -}>({ - state: drawZoneInitialState, - dispatch: () => undefined, -}) - -export function useDrawZone() { - const { state, dispatch } = useContext(DrawZoneContext) - - const zoomIn = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ZOOM_IN }) - }, [dispatch]) - - const zoomOut = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ZOOM_OUT }) - }, [dispatch]) - - const reset = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.RESET }) - }, [dispatch]) - - const toggleMarker = useCallback(() => { - if (state.isMarkerShown) { - dispatch({ type: DrawZoneStateActionType.HIDE_MARKER }) - } else { - dispatch({ type: DrawZoneStateActionType.SHOW_MARKER }) - } - }, [dispatch, state.isMarkerShown]) - - const disable = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.DISABLE }) - }, [dispatch]) - - const enable = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ENABLE }) - }, [dispatch]) - - const redraw = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.FORCE_REDRAW }) - }, [dispatch]) - - return { - ...(state as DrawZoneState), - zoomIn, - zoomOut, - reset, - toggleMarker, - disable, - enable, - redraw, - } -} - -export type DrawZoneContainerProps = Partial & { - readonly children: ReactNode -} -export function DrawZoneContainer({ - children, - ...props -}: DrawZoneContainerProps) { - const [state, dispatch] = useReducer(drawZoneReducer, { - ...drawZoneInitialState, - ...props, - }) - - return ( - - {children} - - ) -} - -const xns = 'http://www.w3.org/1999/xlink' -const blue = '#2BB1FD' -const defaultStroke = { color: '#fff', width: 2, opacity: 1 } -const defaultFill = { color: '#000', opacity: 0 } - -function getRectCoords(rect: Rect) { - const bbox = rect.bbox() - return [ - { x: bbox.x, y: bbox.y }, - { x: bbox.x, y: bbox.y2 }, - { x: bbox.x2, y: bbox.y2 }, - { x: bbox.x2, y: bbox.y }, - ] -} - -function useDraw( - ref: React.RefObject, - src: string, - props: { - onChange: (elements: Array) => void - remove: (id: string) => void - mode: DrawZoneMode - drawOnMouseDown?: boolean - initialRect?: ChangedElement - onInitialRectChange?: ( - arg: Pick, - ) => void - }, -) { - const { - state: { isMarkerShown, isDisabled, scale, positionTop, positionLeft }, - dispatch, - } = useContext(DrawZoneContext) - const [svg, setSvg] = useState() - const [originalSize, setOriginalSize] = useState() - let startPosition: Point | null - let overlayRect: Rect | undefined - let overlayRect2: Rect | undefined - let poly: Polygon | undefined - let tmpPoly: Polyline | undefined - let tmpPoints: Array | undefined - let dragging: boolean - - function getAbsoluteCoordinates(points: Point[]) { - if (!svg) return points - const svgRect = svg.node.getBoundingClientRect() - - return points.map(({ x, y }) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })) - } - - const convertForOnChange = useCallback( - function convertForOnChange(): ChangedElement[] { - if (!svg) { - return [] - } - - const svgRect = svg.node.getBoundingClientRect() - - return svg - .children() - .filter((e) => !e.attr('data-draw-ignore')) - .map((elt) => { - const elementRect = elt.node.getBoundingClientRect() - - const rect: ChangedElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } - - let points: Point[] - - if (elt instanceof Polygon) { - const polygon = elt as Polygon - - points = getAbsoluteCoordinates( - polygon.plot().map((p) => ({ - x: p[0], - y: p[1], - })), - ) - } else { - const box = elt.bbox() - points = getAbsoluteCoordinates([ - { - x: box.x, - y: box.y, - }, - { - x: box.x2, - y: box.y2, - }, - ]) - } - - const result = { - points, - rect, - label: elt.data('label'), - selected: elt.data('selected') as boolean, - id: elt.data('id'), - color: elt.data('color'), - } - - return result - }) - }, - [svg], - ) - - function onChange() { - props.onChange(convertForOnChange()) - } - - function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Delete' && this.closest('svg')) { - event.preventDefault() - props.remove(this.dataset['id'] as string) - - return true - } - - return false - } - - function onEscKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Escape' && this.closest('svg')) { - event.preventDefault() - ;(this as unknown as LinkedHTMLElement).instance.fire('deselect') - - return true - } - - return false - } - - function endPolyDrawing(event: Event) { - if (!svg || !poly) return - - const svgRect = svg.node.getBoundingClientRect() - const plot = poly.plot() - - if (plot.length < 3) return - - event.preventDefault() - - poly.remove() - poly = undefined - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const newPoly = drawPoly({ - points: plot.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { capture: true }) - - if (newPoly) { - newPoly.fire('select') - } - - onChange() - } - - function onEnterKeyPress(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Enter') { - event.preventDefault() - - endPolyDrawing(event) - } - } - - function onAbortPathDrawing(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Escape') { - event.preventDefault() - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - if (poly) { - poly.remove() - poly = undefined - } - - startPosition = null - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - } - } - - const preventDrag = (event: DragEvent) => { - event.preventDefault() - } - - function drawRect({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, - - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: StrokeData - fill?: FillData - label?: string - id?: string | null - }) { - if (!svg || !points || points.length !== 2) { - return - } - - const rect = svg.rect(0, 0) - - rect.move(`${points[0].x * 100}%`, `${points[0].y * 100}%`) - - rect.width(`${Math.abs(points[1].x - points[0].x) * 100}%`) - - rect.height(`${Math.abs(points[1].y - points[0].y) * 100}%`) - rect.fill(fill) - rect.stroke(stroke) - rect.css('touch-action', 'none') // silence interactjs warning. - - function rectDelKeyPress(ev: KeyboardEvent) { - const result = onDelKeyPress.call(rect.node, ev) - - if (result) { - window.removeEventListener('keyup', rectDelKeyPress, { - capture: true, - }) - } - } - function rectEscKeyPress(ev: KeyboardEvent) { - const result = onEscKeyPress.call(rect.node, ev) - - if (result) { - window.removeEventListener('keyup', rectEscKeyPress, { - capture: true, - }) - } - } - - let circle: Circle | undefined - const handles: Use[] = [] - - // Custom events. - rect.on('select', () => { - // Deselect all - - svg.each(function (this: Svg) { - this.fire('deselect', { inst: rect }) - }) - rect.stroke({ color: blue }) - rect.data('selected', true) - - const coords = getRectCoords(rect) - - if (!disabled) { - handles.forEach((h) => h.remove()) - handles.length = 0 - circle?.remove() - circle = undefined - window.addEventListener('keyup', rectDelKeyPress, { - capture: true, - }) - window.addEventListener('keyup', rectEscKeyPress, { - capture: true, - }) - - circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(7) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .id('point-handle') - - for (let i = 0; i < coords.length; i++) { - const point = coords[i] - - const handle = svg - .use(circle as Circle) - .attr('href', '#point-handle', xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - .back() - - handles.push(handle) - } - - document.addEventListener('dragstart', preventDrag) - } - - onChange() - }) - rect.on('deselect', (e) => { - if ((e as CustomEvent).detail?.inst === rect) return - rect.stroke(stroke) - rect.data('selected', false) - - interact('.point-handle').unset() - handles.forEach((h) => h.remove()) - handles.length = 0 - circle?.remove() - circle = undefined - document.removeEventListener('dragstart', preventDrag) - - window.removeEventListener('keyup', rectDelKeyPress, { - capture: true, - }) - window.removeEventListener('keyup', rectEscKeyPress, { - capture: true, - }) - - onChange() - }) - - if (!disabled) { - rect.css('cursor', 'move') - - interact(rect.node) - .resizable({ - edges: { left: true, right: true, bottom: true, top: true }, - listeners: { - move(event) { - const svgRect = svg.node.getBoundingClientRect() - - event.target.instance.width( - `${(event.rect.width / svgRect.width) * 100}%`, - ) - event.target.instance.height( - `${ - (event.rect.height / svgRect.height) * 100 - }%`, - ) - - // translate when resizing from top or left edges - const x = - (parseFloat(event.target.instance.x()) / 100) * - svgRect.width - const y = - (parseFloat(event.target.instance.y()) / 100) * - svgRect.height - event.target.instance.x( - `${ - ((x + event.deltaRect.left) / - svgRect.width) * - 100 - }%`, - ) - event.target.instance.y( - `${ - ((y + event.deltaRect.top) / - svgRect.height) * - 100 - }%`, - ) - - const coords = getRectCoords(event.target.instance) - handles.forEach((h) => { - h.x(coords[h.data('index')].x) - h.y(coords[h.data('index')].y) - }) - - onChange() - }, - }, - modifiers: [ - interact.modifiers.restrictEdges({ - outer: 'parent', - }), - interact.modifiers.restrictSize({ - min: { width: 20, height: 20 }, - }), - ], - }) - .draggable({ - listeners: { - start(event) { - event.target.instance.fire('select') - handles.forEach((h) => h.hide()) - rect.css('cursor', 'grabbing') - }, - move(event) { - rect.css('cursor', 'grabbing') - const svgRect = svg.node.getBoundingClientRect() - - const x = - (parseFloat(event.target.instance.x()) / 100) * - svgRect.width - const y = - (parseFloat(event.target.instance.y()) / 100) * - svgRect.height - - event.target.instance.x( - `${((x + event.dx) / svgRect.width) * 100}%`, - ) - event.target.instance.y( - `${((y + event.dy) / svgRect.height) * 100}%`, - ) - - onChange() - }, - end(event) { - const coords = getRectCoords(event.target.instance) - handles.forEach((h) => { - h.x(coords[h.data('index')].x) - h.y(coords[h.data('index')].y) - }) - - handles.forEach((h) => h.show()) - - rect.css('cursor', 'move') - - onChange() - }, - }, - modifiers: [ - interact.modifiers.restrictRect({ - restriction: 'parent', - }), - ], - cursorChecker: ( - action, - interactable, - element, - interacting, - ) => { - switch (action.axis) { - case 'x': - return 'ew-resize' - case 'y': - return 'ns-resize' - default: - return interacting ? 'grabbing' : 'move' - } - }, - }) - } - - rect.data('disabled', disabled) - rect.data('id', id || uuid4()) - rect.data('label', label) - - if (stroke.color !== defaultStroke.color) - rect.data('color', stroke.color) - - return rect - } - - function drawPoly({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: { color: string; width: number; opacity: number } - fill?: { color: string; opacity: number } - label?: string - id?: string | null - }) { - if (!svg || !points || points.length < 2) { - return - } - const svgRect = svg.node.getBoundingClientRect() - - const svgWidth = svgRect.width || originalSize?.width || 0 - const svgHeight = svgRect.height || originalSize?.height || 0 - - const poly = svg.polygon( - points.map( - (point) => [point.x * svgWidth, point.y * svgHeight] as ArrayXY, - ), - ) - - poly.fill(fill) - poly.stroke(stroke) - poly.css('touch-action', 'none') // silence interactjs warning. - - const circles: Circle[] = [] - const handles: Use[] = [] - let rootMatrix: DOMMatrix - - function polyDelKeyPress(ev: KeyboardEvent) { - const result = onDelKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - } - } - function polyEscKeyPress(ev: KeyboardEvent) { - const result = onEscKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - } - } - - function makeHandlesGrabbable(svg: Svg) { - interact('.point-handle') - .draggable({ - onstart: function (event) { - svg.css('cursor', 'grabbing') - event.target.instance.css('cursor', 'grabbing') - }, - onmove: function (event) { - const i = event.target.getAttribute('data-index') | 0 - const point = poly.node.points.getItem(i) - - point.x += event.dx / rootMatrix.a - point.y += event.dy / rootMatrix.d - - event.target.x.baseVal.value = point.x - event.target.y.baseVal.value = point.y - }, - onend: function (event) { - event.target.instance.css('cursor', 'grab') - svg.css('cursor', 'crosshair') - const index = Number( - event.target.getAttribute('data-index') || 0, - ) - - const currentPlot = [...poly.plot()] - - const newPlot: ArrayXY[] = [ - ...currentPlot.slice(0, index), - [ - Number(event.target.getAttribute('x')), - Number(event.target.getAttribute('y')), - ] as ArrayXY, - ...currentPlot.slice(index + 1), - ] - - poly.plot(newPlot) - - svg.node.setAttribute('class', '') - onChange() - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - elementRect: { - top: 0, - left: 0, - bottom: 1, - right: 1, - }, - }), - ], - } as DraggableOptions) - .styleCursor(false) - } - - // Custom events. - poly.on('select', () => { - // Deselect all - svg.each(function (this: Svg) { - this.fire('deselect', { inst: poly }) - }) - poly.stroke({ color: blue }) - poly.data('selected', true) - window.addEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.addEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - - if (!disabled) { - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((c) => c.remove()) - circles.length = 0 - rootMatrix = svg.node.getScreenCTM() as DOMMatrix - - for (let i = 0; i < poly.node.points.numberOfItems; i++) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(7) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .id(handleId) - - const mouseenter = () => { - circle.scale(1.4) - - circle.on('mouseleave', mouseleave) - circle.off('mouseenter', mouseenter) - } - const mouseleave = () => { - circle.scale(5 / 7) - - circle.on('mouseenter', mouseenter) - circle.off('mouseleave', mouseleave) - } - - circle.on('mouseenter', mouseenter) - - const handle = svg - .use(circle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(circle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) - - document.addEventListener('dragstart', preventDrag) - } - - onChange() - }) - poly.on('deselect', (e) => { - if ((e as CustomEvent).detail?.inst === poly) return - poly.stroke(stroke) - poly.data('selected', false) - - interact('.point-handle').unset() - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((circle) => circle.remove()) - circles.length = 0 - document.removeEventListener('dragstart', preventDrag) - - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - - onChange() - }) - - if (!disabled) { - poly.css('cursor', 'move') - - interact(poly.node).draggable({ - listeners: { - start() { - interact('.point-handle').unset() - handles.forEach((handle) => { - handle.remove() - }) - handles.length = 0 - circles.forEach((circle) => { - circle.remove() - }) - circles.length = 0 - }, - move(event) { - const x = parseFloat(event.target.instance.x()) - const y = parseFloat(event.target.instance.y()) - - event.target.instance.x(x + event.dx) - event.target.instance.y(y + event.dy) - - onChange() - }, - end() { - for ( - let i = 0; - i < poly.node.points.numberOfItems; - i++ - ) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const newCircle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(7) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .id(handleId) - - const mouseenter = () => { - newCircle.scale(1.4) - - newCircle.on('mouseleave', mouseleave) - newCircle.off('mouseenter', mouseenter) - } - const mouseleave = () => { - newCircle.scale(5 / 7) - - newCircle.on('mouseenter', mouseenter) - newCircle.off('mouseleave', mouseleave) - } - - newCircle.on('mouseenter', mouseenter) - - const handle = svg - .use(newCircle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(newCircle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) - }, - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, - }), - ], - cursorChecker: (action, interactable, element, interacting) => { - switch (action.axis) { - case 'x': - return 'ew-resize' - case 'y': - return 'ns-resize' - default: - return interacting ? 'grabbing' : 'move' - } - }, - }) - } - - poly.data('disabled', disabled) - poly.data('id', id || uuid4()) - poly.data('label', label) - - if (defaultStroke.color !== stroke.color) - poly.data('color', stroke.color) - - return poly - } - - function drawPoint(svg: Svg, x: number, y: number): Circle { - const point = svg - .circle(6) - .center(0, 0) - .fill({ opacity: 1, color: '#f06' }) - .stroke({ width: 1, color: '#fff' }) - .attr('data-draw-ignore', true) - .move(x - 3, y - 3) - - const mouseenter = () => { - point.scale(1.4) - - point.on('mouseleave', mouseleave) - point.off('mouseenter', mouseenter) - } - const mouseleave = () => { - point.scale(5 / 7) - - point.on('mouseenter', mouseenter) - point.off('mouseleave', mouseleave) - } - - point.on('mouseenter', mouseenter) - point.on('click', function (event) { - event.preventDefault() - event.stopPropagation() - - endPolyDrawing(event) - }) - - return point - } - - const draw = ({ - points: inputPoints, - id, - color, - label, - }: ChangedElement) => { - const fill = color ? { ...defaultFill, color } : defaultFill - const stroke = color ? { ...defaultStroke, color } : defaultStroke - - const points = - inputPoints?.map( - (point) => - ({ - x: point.x, - y: point.y, - } as Point), - ) ?? [] - - if (points.length === 2) drawRect({ points, id, fill, stroke, label }) - else drawPoly({ points, id, fill, stroke, label }) - } - - function onMouseDown(e: globalThis.MouseEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (!svg.node.contains(e.target as Node)) return - - if ( - svg.node.contains(e.target as Node) && - svg.node !== e.target && - !startPosition - ) { - const element = e.target as unknown as LinkedHTMLElement - - element.instance.fire('select') - return - } else if (e.target === svg.node) { - svg.each(function (this: Svg) { - this.fire('deselect') - }) - onChange() - } - - if (props.mode === 'draw' && !isDisabled) { - const svgRect = svg.node.getBoundingClientRect() - - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (!overlayRect || !overlayRect2) { - overlayRect = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#000', - width: 2, - opacity: 0.7, - dasharray: '5,5', - }) - overlayRect2 = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#fff', - width: 2, - opacity: 0.7, - dasharray: '5,5', - dashoffset: 5, - }) - } - - overlayRect.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - overlayRect2.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - e.preventDefault() - } else if (props.mode === 'path' && !isDisabled) { - const svgRect = svg.node.getBoundingClientRect() - - if (!tmpPoly) { - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - tmpPoints = [drawPoint(svg, startPosition.x, startPosition.y)] - - window.addEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.addEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - } else if (startPosition && tmpPoly) { - const currentPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - if (poly) { - poly.remove() - poly = undefined - } - - const start = prev[0] - if ( - prev.length > 2 && - Math.abs(currentPosition.x - start[0]) <= 10 && - Math.abs(currentPosition.y - start[1]) <= 10 - ) { - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const poly = drawPoly({ - points: prev.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - - poly?.fire('select') - - onChange() - } else { - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - poly = svg - .polygon(plotline) - .fill({ - color: '#f06', - opacity: 0, - }) - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - }) - .attr('data-draw-ignore', true) - - if (Array.isArray(tmpPoints)) { - tmpPoints.push( - drawPoint( - svg, - currentPosition.x, - currentPosition.y, - ), - ) - - tmpPoints.forEach((p) => p.front()) - } - - startPosition = currentPosition - } - } - } else if (props.mode === 'move') { - startPosition = { - x: e.clientX, - y: e.clientY, - } - - dragging = true - - svg.css({ - cursor: 'grabbing', - }) - - e.preventDefault() - } - } - - function onMouseMove(this: Window, e: globalThis.MouseEvent) { - if (e.defaultPrevented) return - if (!svg || !startPosition) return - - if (props.mode === 'draw' && !isDisabled) { - if (overlayRect && overlayRect2) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition = { - x: - Math.max( - Math.min(e.clientX, svgRect.right), - svgRect.left, - ) - svgRect.left, - y: - Math.max( - Math.min(e.clientY, svgRect.bottom), - svgRect.top, - ) - svgRect.top, - } - - const minX = - Math.min(startPosition.x, currentPosition.x) / svgRect.width - const minY = - Math.min(startPosition.y, currentPosition.y) / - svgRect.height - const maxX = - Math.max(startPosition.x, currentPosition.x) / svgRect.width - const maxY = - Math.max(startPosition.y, currentPosition.y) / - svgRect.height - - const width = Math.abs(maxX - minX) - const height = Math.abs(maxY - minY) - - overlayRect.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect.width(`${width * 100}%`) - overlayRect.height(`${height * 100}%`) - overlayRect2.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect2.width(`${width * 100}%`) - overlayRect2.height(`${height * 100}%`) - } - } else if (props.mode === 'move' && dragging) { - if (!svg.node.contains(e.target as Node)) { - dragging = false - return - } - const parent = svg.parent() - - if (!parent) return - - const currentPosition = { - x: e.clientX, - y: e.clientY, - } - const translationX = currentPosition.x - startPosition.x - const translationY = currentPosition.y - startPosition.y - - parent.css( - 'transform', - `translate3d(${translationX}px, ${translationY}px, 0px)`, - ) - } else if (props.mode === 'path') { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition: Point = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - tmpPoly = svg - .polyline(plotline) - .fill('none') - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - dasharray: '5,5', - }) - .attr('data-draw-ignore', true) - - if (Array.isArray(tmpPoints)) { - tmpPoints.forEach((point) => point.front()) - } - } - } - - function onMouseUp(this: Window, e: globalThis.MouseEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (props.mode === 'draw' && !isDisabled) { - if (!startPosition) { - return - } - - if (overlayRect || overlayRect2) { - overlayRect?.remove() - overlayRect = undefined - overlayRect2?.remove() - overlayRect2 = undefined - } - - // Prevent drawing new rect on rect dragend... - if ((e.target as Node | null)?.parentNode === svg.node) { - startPosition = null - return - } - - const svgRect = svg.node.getBoundingClientRect() - const currentPosition = { - x: - Math.max(Math.min(e.clientX, svgRect.right), svgRect.left) - - svgRect.left, - y: - Math.max(Math.min(e.clientY, svgRect.bottom), svgRect.top) - - svgRect.top, - } - - let label = undefined - // Prevent adding very small rects (mis-clicks). - if (Math.abs(currentPosition.x - startPosition.x) <= 2) { - const elements = convertForOnChange() - let lastRect = elements[elements.length - 1] - - if (props.initialRect) { - lastRect = props.initialRect - } - - label = lastRect?.label - - if (props.drawOnMouseDown && lastRect && lastRect.rect) { - currentPosition.x = Math.min( - startPosition.x + - (lastRect.rect.width * svgRect.width) / 100, - svgRect.width, - ) - currentPosition.y = Math.min( - startPosition.y + - (lastRect.rect.height * svgRect.height) / 100, - svgRect.height, - ) - } else { - startPosition = null - return - } - } - - const newRect = drawRect({ - points: [ - { - x: - Math.min(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.min(startPosition.y, currentPosition.y) / - svgRect.height, - }, - { - x: - Math.max(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.max(startPosition.y, currentPosition.y) / - svgRect.height, - }, - ], - label: label, - }) - - if (newRect && props.onInitialRectChange) { - const elementRect = newRect.node.getBoundingClientRect() - const rect: ChangedElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } - props.onInitialRectChange({ - rect: rect, - label: newRect.data('label'), - id: newRect.data('id'), - }) - } - - setTimeout(() => { - newRect?.fire('select') - onChange() - }, 50) - } else if (props.mode === 'move' && dragging) { - const parent = svg.parent() - - if (parent) { - const grandParent = parent.parent() - - if (grandParent) { - const parentRect = grandParent.node.getBoundingClientRect() - const svgRect = parent.node.getBoundingClientRect() - - dispatch({ - type: DrawZoneStateActionType.SET_POSITION, - payload: { - top: svgRect.top - parentRect.top, - left: svgRect.left - parentRect.left, - }, - }) - - svg.css({ cursor: 'grab' }) - - dragging = false - } - } - } - - if (props.mode !== 'path') startPosition = null - } - - useEffect(() => { - if (!ref.current) { - return - } - - const image = new Image() - image.onload = () => { - setOriginalSize({ - width: image.naturalWidth, - height: image.naturalHeight, - }) - } - image.src = src - ref.current.style.background = `url('${src}') center center / 100% 100% no-repeat` - ref.current.style.top = `${positionTop}px` - ref.current.style.left = `${positionLeft}px` - - if (svg) { - svg.node.remove() - } - - const newSvg = SVG().addTo(ref.current).size('100%', '100%').attr({ - 'xmlns:xlink': xns, - }) - - setSvg(newSvg) - }, [ref, src]) - - useEffect(() => { - if (ref.current && originalSize && scale) { - ref.current.style.width = `${originalSize.width * scale}px` - - ref.current.style.height = `${originalSize.height * scale}px` - - if (svg) { - dispatch({ - type: DrawZoneStateActionType.FORCE_REDRAW, - }) - } - } - }, [ref, originalSize, scale]) - - useLayoutEffect(() => { - if (!svg) { - return - } - - svg.css({ - cursor: - props.mode === 'move' && !isDisabled - ? 'grab' - : props.mode === 'none' && !isMarkerShown - ? 'normal' - : 'crosshair', - position: 'absolute', - top: '0', - left: '0', - }) - - const parent = svg.parent() - - if (parent) { - parent.css({ - position: 'relative', - userSelect: 'none', - transform: 'none', - }) - } - - svg.on('mousedown', onMouseDown as unknown as EventListener) - window.addEventListener('mouseup', onMouseUp) - window.addEventListener('mousemove', onMouseMove) - - return () => { - svg.off('mousedown', onMouseDown as unknown as EventListener) - window.removeEventListener('mouseup', onMouseUp) - window.removeEventListener('mousemove', onMouseMove) - } - }, [svg, props.mode, props.initialRect]) - - return { - svg, - draw, - originalSize, - } -} - -export interface DrawZoneProps { - readonly children?: React.ReactNode - readonly mode?: DrawZoneMode - readonly sizeMode?: SizeMode - readonly src: string - readonly elements: Partial[] - readonly initialRect?: ChangedElement - readonly onChange: (elements: ChangedElement[]) => void - readonly remove: (id: string) => void - readonly onInitialRectChange?: ( - arg: Pick, - ) => void - readonly drawOnMouseDown?: bool -} - -export default function DrawZone({ - children, - mode = 'draw', - sizeMode = 'auto', - src, - elements, - onChange, - remove, - initialRect, - onInitialRectChange, - drawOnMouseDown, -}: DrawZoneProps) { - const { - state: { - scale, - isMarkerShown, - positionTop, - positionLeft, - redraw, - logicalScale, - }, - dispatch, - } = useContext(DrawZoneContext) - const svgRef = useRef(null) - const containerRef = useRef(null) - const { svg, draw, originalSize } = useDraw(svgRef, src, { - onChange, - remove, - mode, - drawOnMouseDown, - initialRect, - onInitialRectChange, - }) - const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) - const [forceRedraw, setForceRedraw] = useState(false) - - const setScale = useCallback( - (scale: number) => { - dispatch({ - type: DrawZoneStateActionType.SET_SCALE, - payload: scale, - }) - }, - [dispatch], - ) - - useEffect(() => { - dispatch({ - type: DrawZoneStateActionType.SET_ORIGINAL_SIZE, - payload: originalSize, - }) - dispatch({ - type: DrawZoneStateActionType.FORCE_REDRAW, - }) - }, [originalSize]) - - useEffect(() => { - if (sizeMode === 'auto') { - setScale(logicalScale) - } else if (containerRef.current && originalSize) { - const rect = containerRef.current.getBoundingClientRect() - - const minWidth = Math.min(rect.width, originalSize.width) - const minHeight = Math.min(rect.height, originalSize.height) - - if ( - originalSize.width <= minWidth && - originalSize.height <= minHeight - ) { - const maxWidth = Math.max(rect.width, originalSize.width) - const maxHeight = Math.max(rect.height, originalSize.height) - - const coef = maxWidth / minWidth - const coef2 = maxHeight / minHeight - - if (originalSize.height * coef <= maxHeight) { - setScale(coef * logicalScale) - } else if (originalSize.width * coef2 <= maxWidth) { - setScale(coef2 * logicalScale) - } else { - setScale(logicalScale) - } - } else if ( - minWidth < originalSize.width || - minHeight < originalSize.height - ) { - setScale( - Math.min( - minWidth / originalSize.width, - minHeight / originalSize.height, - ) * logicalScale, - ) - } - } - }, [containerRef, originalSize, sizeMode, logicalScale]) - - useEffect(() => { - if (isTouchDevice) return - - const handleMouseEnter = () => setCanMarkerBeVisible(true) - const handleMouseLeave = () => setCanMarkerBeVisible(false) - - if (svgRef.current) { - svgRef.current.addEventListener('mouseenter', handleMouseEnter) - svgRef.current.addEventListener('mouseleave', handleMouseLeave) - } - - return () => { - if (svgRef.current) { - svgRef.current.removeEventListener( - 'mouseenter', - handleMouseEnter, - ) - svgRef.current.removeEventListener( - 'mouseleave', - handleMouseLeave, - ) - } - } - }, []) - - useEffect(() => { - if (svgRef.current) { - svgRef.current.style.top = `${positionTop}px` - svgRef.current.style.left = `${positionLeft}px` - svgRef.current.style.transform = 'none' - } - }, [positionTop, positionLeft]) - - useEffect(() => { - setForceRedraw(true) - }, [mode, redraw]) - - useLayoutEffect(() => { - if (svg) { - if ( - elements.length !== - svg.children().filter((c) => !c.attr('data-draw-ignore')) - .length || - forceRedraw - ) { - svg.clear() - elements.forEach((element) => draw(element as ChangedElement)) - - if (forceRedraw) setForceRedraw(false) - return - } - } - }, [svg, elements, forceRedraw, scale]) - - return ( -
-
- {canMarkerBeVisible && isMarkerShown && ( - - )} - {children} -
-
- ) -} - -type MarkerProps = { - readonly src: string - readonly svgRef: React.RefObject -} -function Marker({ src, svgRef }: MarkerProps): JSX.Element { - const { clientX, clientY } = useMousePosition() - - const width = svgRef.current?.getBoundingClientRect().width - const height = svgRef.current?.getBoundingClientRect().height - const left = clientX - (svgRef.current?.getBoundingClientRect().left || 0) - const top = clientY - (svgRef.current?.getBoundingClientRect().top || 0) - - return ( - <> -
-
- - ) + ChangedElement, + DrawZoneMode, + DrawZoneState, + Point, + Size, + SizeMode, +} from './types' + +export default DrawZone +export { DrawZoneContainer, useDrawZone } +export type { + DrawZoneContainerProps, + DrawZoneProps, + ChangedElement, + DrawZoneMode, + DrawZoneState, + Point, + Size, + SizeMode, } diff --git a/src/draw-zone/state.ts b/src/draw-zone/state.ts new file mode 100644 index 0000000..02dfc7c --- /dev/null +++ b/src/draw-zone/state.ts @@ -0,0 +1,101 @@ +import { Dispatch, createContext } from 'react' +import { + DrawZoneStateAction, + DrawZoneStateActionType, + DrawZoneStateInternal, + MAX_SCALE, + SCALE_STEP, +} from './types' + +export const drawZoneInitialState: DrawZoneStateInternal = { + scale: 1, + isMarkerShown: false, + isDisabled: false, + originalSize: undefined, + logicalScale: 1, + positionTop: 0, + positionLeft: 0, + redraw: false, +} + +export function drawZoneReducer( + state: DrawZoneStateInternal, + action: DrawZoneStateAction, +): DrawZoneStateInternal { + switch (action.type) { + case DrawZoneStateActionType.RESET: + return { + ...state, + logicalScale: 1, + positionTop: 0, + positionLeft: 0, + } + case DrawZoneStateActionType.SET_SCALE: + return { + ...state, + scale: action.payload, + } + case DrawZoneStateActionType.ZOOM_IN: + return { + ...state, + logicalScale: Math.min( + state.logicalScale + SCALE_STEP, + MAX_SCALE, + ), + } + case DrawZoneStateActionType.ZOOM_OUT: + return { + ...state, + logicalScale: Math.max( + SCALE_STEP, + state.logicalScale - SCALE_STEP, + ), + } + case DrawZoneStateActionType.SHOW_MARKER: + return { + ...state, + isMarkerShown: true, + } + case DrawZoneStateActionType.HIDE_MARKER: + return { + ...state, + isMarkerShown: false, + } + case DrawZoneStateActionType.DISABLE: + return { + ...state, + isDisabled: true, + } + case DrawZoneStateActionType.ENABLE: + return { + ...state, + isDisabled: false, + } + case DrawZoneStateActionType.SET_ORIGINAL_SIZE: + return { + ...state, + originalSize: action.payload, + } + case DrawZoneStateActionType.SET_POSITION: + return { + ...state, + positionTop: action.payload.top, + positionLeft: action.payload.left, + } + case DrawZoneStateActionType.FORCE_REDRAW: + return { + ...state, + redraw: !state.redraw, + } + default: + return state + } +} + +export const DrawZoneContext = createContext<{ + readonly state: DrawZoneStateInternal + readonly dispatch: Dispatch +}>({ + state: drawZoneInitialState, + dispatch: () => undefined, +}) diff --git a/src/draw-zone/types.ts b/src/draw-zone/types.ts new file mode 100644 index 0000000..7463c95 --- /dev/null +++ b/src/draw-zone/types.ts @@ -0,0 +1,97 @@ +export type DrawZoneMode = 'draw' | 'move' | 'none' +export type DrawZoneShape = 'rect' | 'poly' +export type SizeMode = 'auto' | 'fit' + +export interface Size { + readonly width: number + readonly height: number +} +export interface Point { + readonly x: number + readonly y: number +} +export interface ChangedElement { + readonly id: string + readonly selected?: boolean + readonly points: Point[] + readonly label: string + readonly rect: { + readonly height: number + readonly width: number + readonly x: number + readonly y: number + } + readonly color?: string +} + +export type DrawZoneState = { + readonly scale: number + readonly isMarkerShown: boolean + readonly isDisabled: boolean + readonly originalSize: Size | undefined +} + +export const MAX_SCALE = 4 +export const SCALE_STEP = 0.25 + +export type DrawZoneStateInternal = DrawZoneState & { + readonly logicalScale: number + readonly positionTop: number + readonly positionLeft: number + readonly redraw: boolean +} + +export enum DrawZoneStateActionType { + RESET, + SET_SCALE, + ZOOM_IN, + ZOOM_OUT, + CHANGE_MODE, + CHANGE_SIZE_MODE, + SHOW_MARKER, + HIDE_MARKER, + DISABLE, + ENABLE, + SET_ORIGINAL_SIZE, + SET_POSITION, + FORCE_REDRAW, +} + +export type DrawZoneStateAction = + | { readonly type: DrawZoneStateActionType.RESET } + | { + readonly type: DrawZoneStateActionType.SET_SCALE + readonly payload: number + } + | { + readonly type: DrawZoneStateActionType.ZOOM_IN + } + | { + readonly type: DrawZoneStateActionType.ZOOM_OUT + } + | { + readonly type: DrawZoneStateActionType.SHOW_MARKER + } + | { + readonly type: DrawZoneStateActionType.HIDE_MARKER + } + | { + readonly type: DrawZoneStateActionType.DISABLE + } + | { + readonly type: DrawZoneStateActionType.ENABLE + } + | { + readonly type: DrawZoneStateActionType.SET_ORIGINAL_SIZE + readonly payload: Size | undefined + } + | { + readonly type: DrawZoneStateActionType.SET_POSITION + readonly payload: { + readonly top: number + readonly left: number + } + } + | { + readonly type: DrawZoneStateActionType.FORCE_REDRAW + } diff --git a/src/hooks.ts b/src/hooks.ts index 840d359..ee95798 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,9 +1,9 @@ import { useEffect, useRef, useState } from 'react' -export function useMousePosition() { +export function usePointerPosition() { const [position, setPosition] = useState({ clientX: 0, clientY: 0 }) - const updatePosition = (e: MouseEvent) => { + const updatePosition = (e: PointerEvent) => { const { clientX, clientY } = e setPosition({ clientX, clientY }) @@ -11,12 +11,21 @@ export function useMousePosition() { useAnimationFrame(() => { if (!global.window) return - global.window.addEventListener('mousemove', updatePosition, false) - global.window.addEventListener('mouseenter', updatePosition, false) + + global.window.addEventListener('pointerenter', updatePosition, false) + global.window.addEventListener('pointermove', updatePosition, false) return () => { - global.window.removeEventListener('mousemove', updatePosition) - global.window.removeEventListener('mouseenter', updatePosition) + global.window.removeEventListener( + 'pointermove', + updatePosition, + false, + ) + global.window.removeEventListener( + 'pointerenter', + updatePosition, + false, + ) } }) diff --git a/yarn.lock b/yarn.lock index 1cac690..b1979ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3306,7 +3306,7 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== -"@types/lodash@^4.14.167": +"@types/lodash@^4.14.167", "@types/lodash@^4.14.182": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== From 2775cc41bbc91aaad7903b55a536a003112b21bf Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Fri, 17 Jun 2022 09:36:09 +0200 Subject: [PATCH 02/30] Retours PR --- package.json | 4 +--- src/draw-zone/hooks.ts | 24 ++++++++++++++---------- yarn.lock | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index d14202e..a194bba 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "dependencies": { "@svgdotjs/svg.draggable.js": "^3.0.2", "@svgdotjs/svg.js": "^3.0.16", - "interactjs": "^1.9.20", - "lodash": "^4.17.21" + "interactjs": "^1.9.20" }, "peerDependencies": { "react": "^17.0.2", @@ -46,7 +45,6 @@ "@storybook/builder-webpack5": "^6.5.8", "@storybook/manager-webpack5": "^6.5.8", "@storybook/react": "^6.5.8", - "@types/lodash": "^4.14.182", "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.2", diff --git a/src/draw-zone/hooks.ts b/src/draw-zone/hooks.ts index f1a9070..0cbd0b3 100644 --- a/src/draw-zone/hooks.ts +++ b/src/draw-zone/hooks.ts @@ -38,6 +38,10 @@ const blue = '#2BB1FD' const defaultStroke = { color: '#fff', width: 2, opacity: 1 } const defaultFill = { color: '#000', opacity: 0 } +const CIRCLE_SCALE_IN = 1.4 +const CIRCLE_SCALE_OUT = 1 / CIRCLE_SCALE_IN +const CIRCLE_SIZE = isTouchDevice ? 14 : 7 + function getAbsoluteCoordinates(svg: Svg, points: Point[]) { const svgRect = svg.node.getBoundingClientRect() @@ -507,20 +511,20 @@ export function useDraw( const circle = svg .defs() .attr('data-draw-ignore', true) - .circle(isTouchDevice ? 14 : 7) + .circle(CIRCLE_SIZE) .center(0, 0) .fill({ opacity: 1, color: blue }) .stroke({ width: 1, color: '#fff' }) .id(handleId) const mouseenter = () => { - circle.scale(1.4) + circle.scale(CIRCLE_SCALE_IN) circle.on('mouseleave', mouseleave) circle.off('mouseenter', mouseenter) } const mouseleave = () => { - circle.scale(5 / 7) + circle.scale(CIRCLE_SCALE_OUT) circle.on('mouseenter', mouseenter) circle.off('mouseleave', mouseleave) @@ -615,20 +619,20 @@ export function useDraw( const newCircle = svg .defs() .attr('data-draw-ignore', true) - .circle(isTouchDevice ? 14 : 7) + .circle(CIRCLE_SIZE) .center(0, 0) .fill({ opacity: 1, color: blue }) .stroke({ width: 1, color: '#fff' }) .id(handleId) const mouseenter = () => { - newCircle.scale(1.4) + newCircle.scale(CIRCLE_SCALE_IN) newCircle.on('mouseleave', mouseleave) newCircle.off('mouseenter', mouseenter) } const mouseleave = () => { - newCircle.scale(5 / 7) + newCircle.scale(CIRCLE_SCALE_OUT) newCircle.on('mouseenter', mouseenter) newCircle.off('mouseleave', mouseleave) @@ -689,10 +693,10 @@ export function useDraw( } function drawPoint(svg: Svg, x: number, y: number): Circle { - const delta = isTouchDevice ? 6 : 3 + const delta = CIRCLE_SIZE / 2 const point = svg - .circle(isTouchDevice ? 12 : 6) + .circle(CIRCLE_SIZE) .center(0, 0) .fill({ opacity: 1, color: '#f06' }) .stroke({ width: 1, color: '#fff' }) @@ -700,13 +704,13 @@ export function useDraw( .move(x - delta, y - delta) const mouseenter = () => { - point.scale(1.4) + point.scale(CIRCLE_SCALE_IN) point.on('mouseleave', mouseleave) point.off('mouseenter', mouseenter) } const mouseleave = () => { - point.scale(5 / 7) + point.scale(CIRCLE_SCALE_OUT) point.on('mouseenter', mouseenter) point.off('mouseleave', mouseleave) diff --git a/yarn.lock b/yarn.lock index b1979ca..1cac690 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3306,7 +3306,7 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== -"@types/lodash@^4.14.167", "@types/lodash@^4.14.182": +"@types/lodash@^4.14.167": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== From fc36e4fd55adb6fd373dc6c25fc04161cee2a5d3 Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Fri, 17 Jun 2022 09:41:21 +0200 Subject: [PATCH 03/30] Fix prop drawOnMouseDown --- src/draw-zone/components.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index dd56c13..f54c086 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -33,6 +33,7 @@ export interface DrawZoneProps { readonly onInitialRectChange?: ( arg: Pick, ) => void + readonly drawOnMouseDown?: boolean } export default function DrawZone({ @@ -46,6 +47,7 @@ export default function DrawZone({ remove, initialRect, onInitialRectChange, + drawOnMouseDown, }: DrawZoneProps) { const { state: { @@ -65,7 +67,7 @@ export default function DrawZone({ remove, mode, shape, - drawOnMouseDown: true, + drawOnMouseDown, initialRect, onInitialRectChange, }) From cd584c4f1c9c2c385610957352f25c114c6d5a57 Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Fri, 17 Jun 2022 10:47:36 +0200 Subject: [PATCH 04/30] clean deps --- package.json | 13 +- yarn.lock | 710 +++++++++++++++++++++++++-------------------------- 2 files changed, 358 insertions(+), 365 deletions(-) diff --git a/package.json b/package.json index a194bba..ed9eeac 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "prepare": "install-peers" }, "dependencies": { - "@svgdotjs/svg.draggable.js": "^3.0.2", "@svgdotjs/svg.js": "^3.0.16", "interactjs": "^1.9.20" }, @@ -39,12 +38,12 @@ "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-typescript": "^8.3.0", - "@storybook/addon-actions": "^6.5.8", - "@storybook/addon-essentials": "^6.5.8", - "@storybook/addon-links": "^6.5.8", - "@storybook/builder-webpack5": "^6.5.8", - "@storybook/manager-webpack5": "^6.5.8", - "@storybook/react": "^6.5.8", + "@storybook/addon-actions": "^6.5.9", + "@storybook/addon-essentials": "^6.5.9", + "@storybook/addon-links": "^6.5.9", + "@storybook/builder-webpack5": "^6.5.9", + "@storybook/manager-webpack5": "^6.5.9", + "@storybook/react": "^6.5.9", "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.2", diff --git a/yarn.lock b/yarn.lock index 1cac690..54d0d1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2294,18 +2294,18 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@storybook/addon-actions@6.5.8", "@storybook/addon-actions@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.8.tgz#c1f544acc016a4a0d5dcb373535dc991b5737c5f" - integrity sha512-9ciR1iWBTzQNBDlq0iQs9+TV7gng+FbQxW5mHNxNvT9SxY1dt02wCPHZeVE/5la61wBXZs/zpEepZA93VzVBDw== - dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" +"@storybook/addon-actions@6.5.9", "@storybook/addon-actions@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.9.tgz#d50d65631403e1a5b680961429d9c0d7bd383e68" + integrity sha512-wDYm3M1bN+zcYZV3Q24M03b/P8DDpvj1oSoY6VLlxDAi56h8qZB/voeIS2I6vWXOB79C5tbwljYNQO0GsufS0g== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.8" + "@storybook/theming" "6.5.9" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2319,18 +2319,18 @@ util-deprecate "^1.0.2" uuid-browser "^3.1.0" -"@storybook/addon-backgrounds@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.8.tgz#d7ab67d7bf98f7866b8f95cd92da068f00997b74" - integrity sha512-pvlP5ZVVfd2sWzgCqG/f6RJX/h9648znYbzaLQ4Z6whQIFobP3H3/cj9k/RTy3uXg5vC0IWDHSEaCXgin2sW1Q== +"@storybook/addon-backgrounds@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.9.tgz#a9579fc9d73f783a768c6c6ceb97193c5a1ee708" + integrity sha512-9k+GiY5aiANLOct34ar29jqgdi5ZpCqpZ86zPH0GsEC6ifH6nzP4trLU0vFUe260XDCvB4g8YaI7JZKPhozERg== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.8" + "@storybook/theming" "6.5.9" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" @@ -2338,47 +2338,47 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-controls@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.8.tgz#779407655d990f88e1d3bb734cf116bc419ab6ba" - integrity sha512-fB6p5DgVHUnJKUzOlT2mtvaSCincnO+vuYLyf++f+l4BlYK1Es9HNl/puaRoMgdW+LoGJjXPTIMcMByeHVIt6Q== +"@storybook/addon-controls@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.9.tgz#8f6ef939c87b3dbad98f8bda7e124f0b34f668d2" + integrity sha512-VvjkgK32bGURKyWU2No6Q2B0RQZjLZk8D3neVNCnrWxwrl1G82StegxjRPn/UZm9+MZVPvTvI46nj1VdgOktnw== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-common" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/node-logger" "6.5.8" - "@storybook/store" "6.5.8" - "@storybook/theming" "6.5.8" + "@storybook/node-logger" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" core-js "^3.8.2" lodash "^4.17.21" ts-dedent "^2.0.0" -"@storybook/addon-docs@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.8.tgz#0b6c379cd7b9f2544aa78fdf520939f8f9bd9ecc" - integrity sha512-pAvWwh5YCrsW9nHCrd5BpFigvqn92JisX0aEnwAqKC9B1AW1LxhdPn1o9CQCeszQGaq163RA6AzkCejvAqhtUQ== +"@storybook/addon-docs@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.9.tgz#32b27fb298624afd738c1371a764d7ff4831fe6d" + integrity sha512-9lwOZyiOJFUgGd9ADVfcgpels5o0XOXqGMeVLuzT1160nopbZjNjo/3+YLJ0pyHRPpMJ4rmq2+vxRQR6PVRgPg== dependencies: "@babel/plugin-transform-react-jsx" "^7.12.12" "@babel/preset-env" "^7.12.11" "@jest/transform" "^26.6.2" "@mdx-js/react" "^1.6.22" - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.8" + "@storybook/docs-tools" "6.5.9" "@storybook/mdx1-csf" "^0.0.1" - "@storybook/node-logger" "6.5.8" - "@storybook/postinstall" "6.5.8" - "@storybook/preview-web" "6.5.8" - "@storybook/source-loader" "6.5.8" - "@storybook/store" "6.5.8" - "@storybook/theming" "6.5.8" + "@storybook/node-logger" "6.5.9" + "@storybook/postinstall" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/source-loader" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" babel-loader "^8.0.0" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -2390,37 +2390,37 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-essentials@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.8.tgz#db89307cb8445c49d23ecaa346516b3793baa28a" - integrity sha512-K/Aw/GLugCz5/S3c2tz5lnfC8aN6dSoQQDr8xaMDcBlT9h/xZ1l4jQQnx/mvY/qEvXtexBF41DE6ROWGKSZeSg== - dependencies: - "@storybook/addon-actions" "6.5.8" - "@storybook/addon-backgrounds" "6.5.8" - "@storybook/addon-controls" "6.5.8" - "@storybook/addon-docs" "6.5.8" - "@storybook/addon-measure" "6.5.8" - "@storybook/addon-outline" "6.5.8" - "@storybook/addon-toolbars" "6.5.8" - "@storybook/addon-viewport" "6.5.8" - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/node-logger" "6.5.8" +"@storybook/addon-essentials@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.9.tgz#32ba63acba4d153f4cf6ac33cbbf14b87d260788" + integrity sha512-V9ThjKQsde4A2Es20pLFBsn0MWx2KCJuoTcTsANP4JDcbvEmj8UjbDWbs8jAU+yzJT5r+CI6NoWmQudv12ZOgw== + dependencies: + "@storybook/addon-actions" "6.5.9" + "@storybook/addon-backgrounds" "6.5.9" + "@storybook/addon-controls" "6.5.9" + "@storybook/addon-docs" "6.5.9" + "@storybook/addon-measure" "6.5.9" + "@storybook/addon-outline" "6.5.9" + "@storybook/addon-toolbars" "6.5.9" + "@storybook/addon-viewport" "6.5.9" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" core-js "^3.8.2" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-links@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.5.8.tgz#ec4b640bf89dbb653592e6f05968ba771bb10b4a" - integrity sha512-UxzEPnT1PLPsuy3tt+o3UJTbI4Kihec+/yzpzzGKreUnCC8EWTsOhtMQAVVit2t4BMl4vNrxKO0mKSc8saT3Gw== +"@storybook/addon-links@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.5.9.tgz#91cbca0c044796badf2498723fdd10dacea5748b" + integrity sha512-4BYC7pkxL3NLRnEgTA9jpIkObQKril+XFj1WtmY/lngF90vvK0Kc/TtvTA2/5tSgrHfxEuPevIdxMIyLJ4ejWQ== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.8" + "@storybook/router" "6.5.9" "@types/qs" "^6.9.5" core-js "^3.8.2" global "^4.4.0" @@ -2429,95 +2429,95 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-measure@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.8.tgz#af4c0d1ec4394d89f79593561a898612d4c52177" - integrity sha512-zpNAt1XwBLnQ3OjCfj63J7vC2WCTyAjvbGVAsUkpQb21vr/e3sPFQZPKGwio85SYjIX7AJ+Oi28mbEwWzS8wFA== +"@storybook/addon-measure@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.9.tgz#f949d4f5f4025c839634114365f1399ea04bd0ae" + integrity sha512-0aA22wD0CIEUccsEbWkckCOXOwr4VffofMH1ToVCOeqBoyLOMB0gxFubESeprqM54CWsYh2DN1uujgD6508cwA== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" -"@storybook/addon-outline@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.8.tgz#e6a464ef63b67067e162e13e07d7d3629313f080" - integrity sha512-/bEjYTVJNM5QEiguS5nVQlerl5NhgOod1zLExnkchc8+FTJC58Vy7CRfzr2iaIMuf1QRPqBwSIy6ZqLJOdUfnQ== +"@storybook/addon-outline@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.9.tgz#6ce9b3fb77e6a1a59607d7657c359c69f26cf6dd" + integrity sha512-oJ1DK3BDJr6aTlZc9axfOxV1oDkZO7hOohgUQDaKO1RZrSpyQsx2ViK2X6p/W7JhFJHKh7rv+nGCaVlLz3YIZA== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-toolbars@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.8.tgz#533b7f4bb925d353c9f826d5b92d6801b7418faa" - integrity sha512-16eRbbtn4/cH1xU8JlPZRdShwUwSsPcqpyH1JNl+rgYQ6SaSNq3aO/jDFeQe93guSD0YPRWHz8dKtn6OxVeozQ== +"@storybook/addon-toolbars@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.9.tgz#feedfdac08482d43bb1f3cc00840d80322c5eace" + integrity sha512-6JFQNHYVZUwp17p5rppc+iQJ2QOIWPTF+ni1GMMThjc84mzXs2+899Sf1aPFTvrFJTklmT+bPX6x4aUTouVa1w== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/theming" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/theming" "6.5.9" core-js "^3.8.2" regenerator-runtime "^0.13.7" -"@storybook/addon-viewport@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.8.tgz#3184a6ef12582af6f2361af27f404dffee0b32ae" - integrity sha512-MTpZWkBWNPH55iNHK4tBNKTdew5xKfoNvOj0pZn1rYDHlylMTlq7aoccwRjjK2jZeHHNnb1rm6ZkQDjmYu0Tcw== - dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" - "@storybook/theming" "6.5.8" +"@storybook/addon-viewport@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.9.tgz#fc390ccebea56d2e874ed2fda085c09fe04dd240" + integrity sha512-thKS+iw6M7ueDQQ7M66STZ5rgtJKliAcIX6UCopo0Ffh4RaRYmX6MCjqtvBKk8joyXUvm9SpWQemJD9uBQrjgw== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/theming" "6.5.9" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" prop-types "^15.7.2" regenerator-runtime "^0.13.7" -"@storybook/addons@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.8.tgz#c529a2989830a09d26308277a3e356228479053d" - integrity sha512-L3LmbsYP9tDHHvpr/yv8YuEkzym7SXp/jZ0km31tpG3EuZmgGu7MXPrZ2ymEw4PkAhQzztgRr23VTfKobGUojA== +"@storybook/addons@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.9.tgz#5a9d7395c579a9cbc44dfc122362fb3c95dfb9d5" + integrity sha512-adwdiXg+mntfPocLc1KXjZXyLgGk7Aac699Fwe+OUYPEC5tW347Rm/kFatcE556d42o5czcRiq3ZSIGWnm9ieQ== dependencies: - "@storybook/api" "6.5.8" - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.8" - "@storybook/theming" "6.5.8" + "@storybook/router" "6.5.9" + "@storybook/theming" "6.5.9" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.8.tgz#8bd400b8a5b18ec6bff5d35a898593d9c1c630f9" - integrity sha512-/MueV+wLCvy9gFA3ih4g7QYjDmn14i+D2ydonfaEC7R+agFGXxXwJGPKkz3yBNrRpNkBwcbY9mAmv8lE2AqgqQ== +"@storybook/api@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9" + integrity sha512-9ylztnty4Y+ALU/ehW3BML9czjCAFsWvrwuCi6UgcwNjswwjSX3VRLhfD1KT3pl16ho//95LgZ0LnSwROCcPOA== dependencies: - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.8" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.8" + "@storybook/theming" "6.5.9" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2529,28 +2529,28 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/builder-webpack4@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.8.tgz#adfb4b91bd442c2f256d93649d50ca138c30663a" - integrity sha512-4/CVp/AlOxCeWZ/DF1TVS/TuzHao4l9KCq7DhL+utFEVl9c/dpgoZXc0Gy2FfHa2RXHKckrH/VUfV2KQk4TNSw== +"@storybook/builder-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.9.tgz#4b37e1fa23a25aa4bfeaba640e5d318fcd511f95" + integrity sha512-YOeA4++9uRZ8Hog1wC60yjaxBOiI1FRQNtax7b9E7g+kP8UlSCPCGcv4gls9hFmzbzTOPfQTWnToA9Oa6jzRVw== dependencies: "@babel/core" "^7.12.10" - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/channel-postmessage" "6.5.8" - "@storybook/channels" "6.5.8" - "@storybook/client-api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/core-events" "6.5.8" - "@storybook/node-logger" "6.5.8" - "@storybook/preview-web" "6.5.8" - "@storybook/router" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.8" - "@storybook/theming" "6.5.8" - "@storybook/ui" "6.5.8" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" autoprefixer "^9.8.6" @@ -2582,27 +2582,27 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.2.2" -"@storybook/builder-webpack5@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.8.tgz#8d1e366c4bc2c8bec49849073031cfd195682309" - integrity sha512-bc7LSGzOqTUImejsfjWAHEHwBreoPQKS6pfnWYkjKMvfvWOwlHSAxwOSM5DyS4cvpcpMDG8yBJNz2QcvXFVLxA== +"@storybook/builder-webpack5@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.9.tgz#30b4e08622daff104bcccd015d3ee7902f99dd99" + integrity sha512-NUVZ4Qci6HWPuoH8U/zQkdBO5soGgu7QYrGC/LWU0tRfmmZxkjr7IUU14ppDpGPYgx3r7jkaQI1J/E1YEmSCWQ== dependencies: "@babel/core" "^7.12.10" - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/channel-postmessage" "6.5.8" - "@storybook/channels" "6.5.8" - "@storybook/client-api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/core-events" "6.5.8" - "@storybook/node-logger" "6.5.8" - "@storybook/preview-web" "6.5.8" - "@storybook/router" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.8" - "@storybook/theming" "6.5.8" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" "@types/node" "^14.0.10 || ^16.0.0" babel-loader "^8.0.0" babel-plugin-named-exports-order "^0.0.2" @@ -2626,51 +2626,51 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.4.1" -"@storybook/channel-postmessage@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.8.tgz#989af9a55eba391b64487640f6b5b0cfd1fde2f9" - integrity sha512-6IkIKk+UMYKk05vN8gWHvvOV/EZNXpQG/5gesGDALjkCyvRmcktHak1a9tHpoihZ3L7/gDwXOZraCZmuy8vBcQ== +"@storybook/channel-postmessage@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.9.tgz#9cf4530f0364cee0d5e58f92d6fb5ce98e10257b" + integrity sha512-pX/0R8UW7ezBhCrafRaL20OvMRcmESYvQQCDgjqSzJyHkcG51GOhsd6Ge93eJ6QvRMm9+w0Zs93N2VKjVtz0Qw== dependencies: - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" core-js "^3.8.2" global "^4.4.0" qs "^6.10.0" telejson "^6.0.8" -"@storybook/channel-websocket@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.8.tgz#722743619f86ebc9325bbff026acf6eba13b21ca" - integrity sha512-lAtvgO0FWsyS3u7uFbsGIYp2aSWJfWU/LOtc3x1K5c84JJAd9fncYkyZMwP1gMbdNgYxJoxe8HXtVtfeNegPuQ== +"@storybook/channel-websocket@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.9.tgz#6b7a0127fec58ee5be4f6aebcf460adc564f2f34" + integrity sha512-xtHvSNwuOhkgALwVshKWsoFhDmuvcosdYfxcfFGEiYKXIu46tRS5ZXmpmgEC/0JAVkVoFj5nL8bV7IY5np6oaA== dependencies: - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" core-js "^3.8.2" global "^4.4.0" telejson "^6.0.8" -"@storybook/channels@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.8.tgz#e85ffd2076813b67336b1b9b274c37aa5f1b80e0" - integrity sha512-fNql1lEIvWlI1NiRtwFMWOOvfW6qxgeSP6xoqiAJ0b+QYegEFG9UxJDuEvVHq++S81FulgQ5U+p+5R9XSV19tQ== +"@storybook/channels@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79" + integrity sha512-FvGA35nV38UPXWOl9ERapFTJaxwSTamQ339s2Ev7E9riyRG+GRkgTWzf5kECJgS1PAYKd/7m/RqKJT9BVv6A5g== dependencies: core-js "^3.8.2" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.8.tgz#cf1b3d4a71a3a156636a9b6b51acc4239b259d86" - integrity sha512-mdU+qQ4+T2OUbEnl+3MWRKxEPju/EOIUg66hMgmif8c5u7YFYBFulUMUYLICMjll8Jlu+37+g+qO3K2eEz6CEw== +"@storybook/client-api@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.9.tgz#3e4a8ec1d277fd81325c5d959c553161a85fa182" + integrity sha512-pc7JKJoWLesixUKvG2nV36HukUuYoGRyAgD3PpIV7qSBS4JixqZ3VAHFUtqV1UzfOSQTovLSl4a0rIRnpie6gA== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/channel-postmessage" "6.5.8" - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.8" + "@storybook/store" "6.5.9" "@types/qs" "^6.9.5" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" @@ -2685,44 +2685,45 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.8.tgz#551f818c4448ef6e6adf9c3ad256e1ae61e2d75c" - integrity sha512-dH6HSaVuOIMHy1+rpsqcD3SJxVZEEbuEtsNpdUGwLJaIuduhUJJpM2xQfUW0siZDyrgwoa+znll+G0YNUbv7sg== +"@storybook/client-logger@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014" + integrity sha512-DOHL6p0uiDd3gV/Sb2FR+Vh6OiPrrf8BrA06uvXWsMRIIvEEvnparxv9EvPg7FlmUX0T3nq7d3juwjx4F8Wbcg== dependencies: core-js "^3.8.2" global "^4.4.0" -"@storybook/components@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.8.tgz#eb18cfcc4e88b7f4081b20b8a81b92d7ae9eaeac" - integrity sha512-YE+LZ1/GXoqertxodsf+L9ehcohbICRAxgE/iNqc7MZfk95SD3XRSUbxhCpGe8QTIZJpzs1tK4LFZ3Fg5w/+Lg== +"@storybook/components@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.9.tgz#97e07ffe11ab76c01ccee380888991bd161f75b2" + integrity sha512-BhfX980O9zn/1J4FNMeDo8ZvL1m5Ml3T4HRpfYmEBnf8oW5b5BeF6S2K2cwFStZRjWqm1feUcwNpZxCBVMkQnQ== dependencies: - "@storybook/client-logger" "6.5.8" + "@storybook/client-logger" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.8" + "@storybook/theming" "6.5.9" "@types/react-syntax-highlighter" "11.0.5" core-js "^3.8.2" + memoizerific "^1.11.3" qs "^6.10.0" react-syntax-highlighter "^15.4.5" regenerator-runtime "^0.13.7" util-deprecate "^1.0.2" -"@storybook/core-client@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.8.tgz#4be4ce024a74b51c0650d346f1f3a11a0f921952" - integrity sha512-8x8qKQ2clvpfDcoWrNBmQ8Xt9z/i32TFIBp4PEZMcbB7eqo517nzfllLiXDipiJgO7BGxKtY5CRHQ9pAU9G27A== - dependencies: - "@storybook/addons" "6.5.8" - "@storybook/channel-postmessage" "6.5.8" - "@storybook/channel-websocket" "6.5.8" - "@storybook/client-api" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" +"@storybook/core-client@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.9.tgz#ea6035d1c90d2c68e860e3cf629979491856cd88" + integrity sha512-LY0QbhShowO+PQx3gao3wdVjpKMH1AaSLmuI95FrcjoMmSXGf96jVLKQp9mJRGeHIsAa93EQBYuCihZycM3Kbg== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channel-websocket" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/preview-web" "6.5.8" - "@storybook/store" "6.5.8" - "@storybook/ui" "6.5.8" + "@storybook/preview-web" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/ui" "6.5.9" airbnb-js-shims "^2.2.1" ansi-to-html "^0.6.11" core-js "^3.8.2" @@ -2734,10 +2735,10 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/core-common@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.8.tgz#6972c7109477914f9ed72bedbe3769cec0eb8c56" - integrity sha512-ELGKLMx1d0oEA2LT+fsmo85X2RNE1EO+It7B1bw//g7jyf1hmZ7t3lXMZUCqt7eml1qy1N72LDkfmmU+H9H6ww== +"@storybook/core-common@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.9.tgz#7ca8258ea2634b1d64695c1e4262f71cc7457989" + integrity sha512-NxOK0mrOCo0TWZ7Npc5HU66EKoRHlrtg18/ZixblLDWQMIqY9XCck8K1kJ8QYpYCHla+aHIsYUArFe2vhlEfZA== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -2761,7 +2762,7 @@ "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" "@babel/register" "^7.12.1" - "@storybook/node-logger" "6.5.8" + "@storybook/node-logger" "6.5.9" "@storybook/semver" "^7.3.2" "@types/node" "^14.0.10 || ^16.0.0" "@types/pretty-hrtime" "^1.0.0" @@ -2790,30 +2791,30 @@ util-deprecate "^1.0.2" webpack "4" -"@storybook/core-events@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.8.tgz#bc405d37d0ed1a6eafa2682fc085b2a7a992233c" - integrity sha512-lzG4Lg65WFYvjs2k/E3CP4+eyPexEGrDyRMO9Pbj9H9x+eosYptauEbT/wXF83bmUWZKLWWVUAZX7hDcxBO8cw== +"@storybook/core-events@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637" + integrity sha512-tXt7a3ZvJOCeEKpNa/B5rQM5VI7UJLlOh3IHOImWn4HqoBRrZvbourmac+PRZAtXpos0h3c6554Hjapj/Sny5Q== dependencies: core-js "^3.8.2" -"@storybook/core-server@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.8.tgz#3ef53c4b0437e83c310f9e545607a0b1ce512ab6" - integrity sha512-ti7+MW1xzD9O0JLwgZTwulxhJx5YGPNu+hRpGhJSjKrqGX1h6K6ilmkBSHvyLqpiE+F4mxvqb5Rx3KBIEdEgbw== +"@storybook/core-server@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.9.tgz#749a881c1a81d7cf1a69f3782c06a7f0c39a505c" + integrity sha512-YeePGUrd5fQPvGzMhowh124KrcZURFpFXg1VB0Op3ESqCIsInoMZeObci4Gc+binMXC7vcv7aw3EwSLU37qJzQ== dependencies: "@discoveryjs/json-ext" "^0.5.3" - "@storybook/builder-webpack4" "6.5.8" - "@storybook/core-client" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/builder-webpack4" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/csf-tools" "6.5.8" - "@storybook/manager-webpack4" "6.5.8" - "@storybook/node-logger" "6.5.8" + "@storybook/csf-tools" "6.5.9" + "@storybook/manager-webpack4" "6.5.9" + "@storybook/node-logger" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.8" - "@storybook/telemetry" "6.5.8" + "@storybook/store" "6.5.9" + "@storybook/telemetry" "6.5.9" "@types/node" "^14.0.10 || ^16.0.0" "@types/node-fetch" "^2.5.7" "@types/pretty-hrtime" "^1.0.0" @@ -2848,18 +2849,18 @@ ws "^8.2.3" x-default-browser "^0.4.0" -"@storybook/core@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.8.tgz#cbe6acaf587e9ef66963bb104a067c213aeea990" - integrity sha512-+Fv4n1E5N4Avty9GcRbz4vB2IWH//se2OUU+RTT3vneCOGjyus5bj0Or6GU5wef5UGuvHF78mHg/frhWpguzsw== +"@storybook/core@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.9.tgz#da4f237391d99aed1228323f24b335cafbdf3499" + integrity sha512-Mt3TTQnjQt2/pa60A+bqDsAOrYpohapdtt4DDZEbS8h0V6u11KyYYh3w7FCySlL+sPEyogj63l5Ec76Jah3l2w== dependencies: - "@storybook/core-client" "6.5.8" - "@storybook/core-server" "6.5.8" + "@storybook/core-client" "6.5.9" + "@storybook/core-server" "6.5.9" -"@storybook/csf-tools@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.8.tgz#5788aa1e862ee613d11d6528f27c8e267e4dd3a7" - integrity sha512-4VrjIMxKcp29OFSMDub52aQOMP4EvtZ5eWZkPeORRNQoJsnQaxhF9GGf71QdSaAQZhMoxdvmpA47ehrFk8Rnfw== +"@storybook/csf-tools@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.9.tgz#8e01df2305b53e228229f0b45ada3720e6e42a1c" + integrity sha512-RAdhsO2XmEDyWy0qNQvdKMLeIZAuyfD+tYlUwBHRU6DbByDucvwgMOGy5dF97YNJFmyo93EUYJzXjUrJs3U1LQ== dependencies: "@babel/core" "^7.12.10" "@babel/generator" "^7.12.11" @@ -2890,33 +2891,33 @@ dependencies: lodash "^4.17.15" -"@storybook/docs-tools@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.8.tgz#6189fd20fafa9eb2aa4a19e311d65295992dd105" - integrity sha512-CWMW+3LSstvQmHKV5ggPR1beQZTpwCXEhfysZ9u4Yp/4fcoDIuQ7DTOK5uNFynGCGl1FG3lATriEOhEZ3bZCvQ== +"@storybook/docs-tools@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.9.tgz#5ff304f881e972ce14923a5ffcfed3f052094889" + integrity sha512-UoTaXLvec8x+q+4oYIk/t8DBju9C3ZTGklqOxDIt+0kS3TFAqEgI3JhKXqQOXgN5zDcvLVSxi8dbVAeSxk2ktA== dependencies: "@babel/core" "^7.12.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.8" + "@storybook/store" "6.5.9" core-js "^3.8.2" doctrine "^3.0.0" lodash "^4.17.21" regenerator-runtime "^0.13.7" -"@storybook/manager-webpack4@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.8.tgz#9e4d3dcebac9bd0b07abd630632dc15130ae03f1" - integrity sha512-qW5/L3cJHvtNi5ylDxObALZWaAHMsWQlPP8GRxm95NHpff4CfRo/qs7puY9ZeLmJSic0KchoHEH/8AScflLOgA== +"@storybook/manager-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.9.tgz#c75d2cced4550c8a786f00b0e57b203d613e706c" + integrity sha512-49LZlHqWc7zj9tQfOOANixPYmLxqWTTZceA6DSXnKd9xDiO2Gl23Y+l/CSPXNZGDB8QFAwpimwqyKJj/NLH45A== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.5.8" - "@storybook/core-client" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/node-logger" "6.5.8" - "@storybook/theming" "6.5.8" - "@storybook/ui" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" babel-loader "^8.0.0" @@ -2944,20 +2945,20 @@ webpack-dev-middleware "^3.7.3" webpack-virtual-modules "^0.2.2" -"@storybook/manager-webpack5@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.8.tgz#36e97096506073c837716897495fffe4a616fe83" - integrity sha512-foW/ZvTqGZAl4TfcfGKdS3RlaBDDAgEjUCbCaVShlZRshZ8tzWBVu3JQFqbPVGslH89T5qp9DUYoN/SJqTUpcg== +"@storybook/manager-webpack5@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.9.tgz#ce9dd6ea6298ab426b111f170c23deea7085ba08" + integrity sha512-J1GamphSsaZLNBEhn1awgxzOS8KfvzrHtVlAm2VHwW7j1E1DItROFJhGCgduYYuBiN9eqm+KIYrxcr6cRuoolQ== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.5.8" - "@storybook/core-client" "6.5.8" - "@storybook/core-common" "6.5.8" - "@storybook/node-logger" "6.5.8" - "@storybook/theming" "6.5.8" - "@storybook/ui" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" "@types/node" "^14.0.10 || ^16.0.0" babel-loader "^8.0.0" case-sensitive-paths-webpack-plugin "^2.3.0" @@ -2999,10 +3000,10 @@ prettier ">=2.2.1 <=2.3.0" ts-dedent "^2.0.0" -"@storybook/node-logger@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.8.tgz#d1244c846da5a296a7432444f8682f77853fb81e" - integrity sha512-BHdkSipgjnfsh4FRYbV2R0npM5gVx9JLRsDQ0KiTolRpN4SU98kT/6885zb9jZg6I0EY+UG9Qdr3fvL9VLpY1g== +"@storybook/node-logger@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" + integrity sha512-nZZNZG2Wtwv6Trxi3FrnIqUmB55xO+X/WQGPT5iKlqNjdRIu/T72mE7addcp4rbuWCQfZUhcDDGpBOwKtBxaGg== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.1.0" @@ -3010,24 +3011,24 @@ npmlog "^5.0.1" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.8.tgz#e803fd5652eea68e7da81f6d70e46d9db9c633c1" - integrity sha512-Z6zQnBOaMj+gHtF1XPMpwTIxYRCmh6eNirrJLrkPk5c+fKXtw6+vNCbmPvsyTGxGEHnyn/tYwe1fvwJTHDctUw== +"@storybook/postinstall@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.9.tgz#a5a2565808e9d7bc310e78c279b09ce337fe3457" + integrity sha512-KQBupK+FMRrtSt8IL0MzCZ/w9qbd25Yxxp/+ajfWgZTRgsWgVFOqcDyMhS16eNbBp5qKIBCBDXfEF+/mK8HwQQ== dependencies: core-js "^3.8.2" -"@storybook/preview-web@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.8.tgz#b1c86d56e99032c02ba47bab628c36d76a0385b6" - integrity sha512-jEEAgvTVZfFA0B20mRJfLW6dPA5mG5PxUJtjMx6wH4Yw4+i3Sld/U63hTRt7ktpKdrcu4lX9E+PuaRLPq7S2kg== +"@storybook/preview-web@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.9.tgz#557d919e6df50d66259521aa36ebf4055bbd236e" + integrity sha512-4eMrO2HJyZUYyL/j+gUaDvry6iGedshwT5MQqe7J9FaA+Q2pNARQRB1X53f410w7S4sObRmYIAIluWPYdWym9w== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/channel-postmessage" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.8" + "@storybook/store" "6.5.9" ansi-to-html "^0.6.11" core-js "^3.8.2" global "^4.4.0" @@ -3052,24 +3053,24 @@ react-docgen-typescript "^2.1.1" tslib "^2.0.0" -"@storybook/react@^6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.8.tgz#8a9e15416a18ceb5c11c89ffe9f38357d442efa9" - integrity sha512-LdObfhhPb9gAFBtRNb3awYJe1qMiYeda1ppkj0ZvccbV04YrmbW5bzYvfOCvU6D34ugbQJhJyWuvraO/0EJK6w== +"@storybook/react@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.9.tgz#687ec1f6b785822a392b7ac115b61800f69fb7cd" + integrity sha512-Rp+QaTQAzxJhwuzJXVd49mnIBLQRlF8llTxPT2YoGHdrGkku/zl/HblQ6H2yzEf15367VyzaAv/BpLsO9Jlfxg== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.10" "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" - "@storybook/addons" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core" "6.5.8" - "@storybook/core-common" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core" "6.5.9" + "@storybook/core-common" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.8" - "@storybook/node-logger" "6.5.8" + "@storybook/docs-tools" "6.5.9" + "@storybook/node-logger" "6.5.9" "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.8" + "@storybook/store" "6.5.9" "@types/estree" "^0.0.51" "@types/node" "^14.14.20 || ^16.0.0" "@types/webpack-env" "^1.16.0" @@ -3093,13 +3094,15 @@ util-deprecate "^1.0.2" webpack ">=4.43.0 <6.0.0" -"@storybook/router@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.8.tgz#241e3842bdd174e0eeeb9d1de6adb2975615884d" - integrity sha512-tseNJpZ2ZzVYowjekUMpGJVVRMrwOkttieD9mRbHrhh+2n7b+SoMKnuLi3ow0xeOyPL8ZDng2FgRjQzQHXA5Sw== +"@storybook/router@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8" + integrity sha512-G2Xp/2r8vU2O34eelE+G5VbEEVFDeHcCURrVJEROh6dq2asFJAPbzslVXSeCqgOTNLSpRDJ2NcN5BckkNqmqJg== dependencies: - "@storybook/client-logger" "6.5.8" + "@storybook/client-logger" "6.5.9" core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" regenerator-runtime "^0.13.7" "@storybook/semver@^7.3.2": @@ -3110,13 +3113,13 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.8.tgz#106694fe821f5e42abc4e8e653e5f268a043e470" - integrity sha512-3bVxXKE2o6lS4WGga/S7WwgITxPQ96qsY+pQ1I7A+e4/cKSmZxlVWM9qfMW2ScmHTVoZE0Ujsmn6DWftxzCyrQ== +"@storybook/source-loader@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.9.tgz#7b6f065c6a6108c4b4ca7e45bfd78707373d84ac" + integrity sha512-H03nFKaP6borfWMTTa9igBA+Jm2ph+FoVJImWC/X+LAmLSJYYSXuqSgmiZ/DZvbjxS4k8vccE2HXogne1IvaRA== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/client-logger" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" estraverse "^5.2.0" @@ -3126,14 +3129,14 @@ prettier ">=2.2.1 <=2.3.0" regenerator-runtime "^0.13.7" -"@storybook/store@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.8.tgz#528bf9a2decf5121045fb0a19b8caf795b4a5647" - integrity sha512-5rhGjN/O0oLebRv947B0vgapq48qBBBYYOgq4krRUYU2ecS6LUgtAHR/kTa324o9aBO8cnIXHH78jZcSvMiJlQ== +"@storybook/store@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.9.tgz#dc9963fc013636569082bd8f7200804866373735" + integrity sha512-80pcDTcCwK6wUA63aWOp13urI77jfipIVee9mpVvbNyfrNN8kGv1BS0z/JHDxuV6rC4g7LG1fb+BurR0yki7BA== dependencies: - "@storybook/addons" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/core-events" "6.5.8" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -3147,13 +3150,13 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/telemetry@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.8.tgz#2acf9734292d421f84f7f2ac6c17d887c2cb0df4" - integrity sha512-QnAhYF8CwcjC1bT2PK7Zqvo6E42TPl0MY6JS+H6qSZU/BmYeS0It8ZURNfPsA/OzVVLHUkQs96CisKh3N0WWaw== +"@storybook/telemetry@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.9.tgz#8e1e0d4a89fc2387620045e5ea96c109d16a7247" + integrity sha512-JluoHCRhHAr4X0eUNVBSBi1JIBA92404Tu1TPdbN7x6gCZxHXXPTSUTAnspXp/21cTdMhY2x+kfZQ8fmlGK4MQ== dependencies: - "@storybook/client-logger" "6.5.8" - "@storybook/core-common" "6.5.8" + "@storybook/client-logger" "6.5.9" + "@storybook/core-common" "6.5.9" chalk "^4.1.0" core-js "^3.8.2" detect-package-manager "^2.0.1" @@ -3165,45 +3168,36 @@ read-pkg-up "^7.0.1" regenerator-runtime "^0.13.7" -"@storybook/theming@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.8.tgz#240a3c83ea53a9c7b1f3a2c47d6324f38ecb8771" - integrity sha512-1VaKHqj38Ls1bJwDpg3+aEOlvVib/DCFkP6WYrP/AQtNAzxiyw5WkaoRlTVJZvCdu5TxjpG4O6/Ai5TI9QftIg== +"@storybook/theming@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa" + integrity sha512-KM0AMP5jMQPAdaO8tlbFCYqx9uYM/hZXGSVUhznhLYu7bhNAIK7ZVmXxyE/z/khM++8eUHzRoZGiO/cwCkg9Xw== dependencies: - "@storybook/client-logger" "6.5.8" + "@storybook/client-logger" "6.5.9" core-js "^3.8.2" + memoizerific "^1.11.3" regenerator-runtime "^0.13.7" -"@storybook/ui@6.5.8": - version "6.5.8" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.8.tgz#acd8dadd221c68f65c4963c4ab86b4a757860591" - integrity sha512-rL09kxgY9pCVbxr/VUK4b5FL5VbALfciZR+50sNT1EcTDb9k0OPeqx7a4Ptc+KNkgyPdSTxUGvhzVqH5PYrhZQ== - dependencies: - "@storybook/addons" "6.5.8" - "@storybook/api" "6.5.8" - "@storybook/channels" "6.5.8" - "@storybook/client-logger" "6.5.8" - "@storybook/components" "6.5.8" - "@storybook/core-events" "6.5.8" - "@storybook/router" "6.5.8" +"@storybook/ui@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.9.tgz#41e59279323cccc0d613974ec9782d797220c8a7" + integrity sha512-ryuPxJgtbb0gPXKGgGAUC+Z185xGAd1IvQ0jM5fJ0SisHXI8jteG3RaWhntOehi9qCg+64Vv6eH/cj9QYNHt1Q== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.8" + "@storybook/theming" "6.5.9" core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" regenerator-runtime "^0.13.7" resolve-from "^5.0.0" -"@svgdotjs/svg.draggable.js@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.2.tgz#a8d935ce495a40ab575e8bc11406ae61ebb4f455" - integrity sha512-yqCJe/w1q3IK51zRiqB42rsvQHyX5oqg/Eck02TDRhRv+pQ7sSHJfdyljiGkqoFW4i/1sIpnqYmiLDeM12Nhsw== - dependencies: - "@svgdotjs/svg.js" "^3.0.10" - -"@svgdotjs/svg.js@^3.0.10": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@svgdotjs/svg.js/-/svg.js-3.1.1.tgz#3cf6a59e6d8708cd76d38b26e7d682dacb2f3670" - integrity sha512-73FggAUBS+zuHhJOMZiAsuE5qpwA4pmWUbLuvof2g3YnWEc3QhXA3tjqZlJJukBobSA23a/avf1Vb1U1QbER1Q== - "@svgdotjs/svg.js@^3.0.16": version "3.1.2" resolved "https://registry.yarnpkg.com/@svgdotjs/svg.js/-/svg.js-3.1.2.tgz#acd62ebc5a6ef5b3a70e4a3912ce643a150c56e7" From 213f6aef94d6b8a0406003e13d54b5622ad8c071 Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Mon, 20 Jun 2022 15:30:12 +0200 Subject: [PATCH 05/30] fix touch actions --- package.json | 2 +- src/draw-zone/components.tsx | 1 + src/draw-zone/hooks.ts | 213 ++++++++++------------------------- yarn.lock | 18 +-- 4 files changed, 73 insertions(+), 161 deletions(-) diff --git a/package.json b/package.json index ed9eeac..5e5c089 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@svgdotjs/svg.js": "^3.0.16", - "interactjs": "^1.9.20" + "interactjs": "^1.10.14" }, "peerDependencies": { "react": "^17.0.2", diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index f54c086..3855cbd 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -194,6 +194,7 @@ export default function DrawZone({ position: 'relative', pointerEvents: 'auto', touchAction: 'none', + userSelect: 'none', }} ref={containerRef} > diff --git a/src/draw-zone/hooks.ts b/src/draw-zone/hooks.ts index 0cbd0b3..dbf6e5d 100644 --- a/src/draw-zone/hooks.ts +++ b/src/draw-zone/hooks.ts @@ -38,9 +38,7 @@ const blue = '#2BB1FD' const defaultStroke = { color: '#fff', width: 2, opacity: 1 } const defaultFill = { color: '#000', opacity: 0 } -const CIRCLE_SCALE_IN = 1.4 -const CIRCLE_SCALE_OUT = 1 / CIRCLE_SCALE_IN -const CIRCLE_SIZE = isTouchDevice ? 14 : 7 +const CIRCLE_SIZE = isTouchDevice ? 22 : 10 function getAbsoluteCoordinates(svg: Svg, points: Point[]) { const svgRect = svg.node.getBoundingClientRect() @@ -365,9 +363,9 @@ export function useDraw( poly.stroke(stroke) poly.css('touch-action', 'none') // silence interactjs warning. + let rootMatrix: DOMMatrix const circles: Circle[] = [] const handles: Use[] = [] - let rootMatrix: DOMMatrix function polyDelKeyPress(ev: KeyboardEvent) { const result = onDelKeyPress.call(poly.node, ev) @@ -443,6 +441,7 @@ export function useDraw( onend: function (event) { event.target.instance.css('cursor', 'grab') svg.css('cursor', 'crosshair') + const index = Number( event.target.getAttribute('data-index') || 0, ) @@ -469,18 +468,64 @@ export function useDraw( modifiers: [ interact.modifiers.restrict({ restriction: 'parent', - elementRect: { - top: 0, - left: 0, - bottom: 1, - right: 1, - }, }), ], }) .styleCursor(false) } + function cleanHandles() { + interact('.point-handle').unset() + handles.forEach((h) => h.remove()) + handles.length = 0 + circles.forEach((circle) => circle.remove()) + circles.length = 0 + document.removeEventListener('dragstart', preventDrag) + } + + function createHandles(svg: Svg) { + rootMatrix = svg.node.getScreenCTM() as DOMMatrix + + for (let i = 0; i < poly.node.points.numberOfItems; i++) { + const point = poly.node.points.getItem(i) + + const handleId = `point-handle-${i}` + + const circle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: blue }) + .stroke({ width: 1, color: '#fff' }) + .css('touch-action', 'none') // silence interactjs warning. + .id(handleId) + + const handle = svg + .use(circle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.push(circle) + handles.push(handle) + } + + makeHandlesGrabbable(svg) + + document.addEventListener('dragstart', preventDrag) + } + // Custom events. poly.on('select', () => { // Deselect all @@ -497,64 +542,8 @@ export function useDraw( }) if (!disabled) { - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((c) => c.remove()) - circles.length = 0 - rootMatrix = svg.node.getScreenCTM() as DOMMatrix - - for (let i = 0; i < poly.node.points.numberOfItems; i++) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .id(handleId) - - const mouseenter = () => { - circle.scale(CIRCLE_SCALE_IN) - - circle.on('mouseleave', mouseleave) - circle.off('mouseenter', mouseenter) - } - const mouseleave = () => { - circle.scale(CIRCLE_SCALE_OUT) - - circle.on('mouseenter', mouseenter) - circle.off('mouseleave', mouseleave) - } - - circle.on('mouseenter', mouseenter) - - const handle = svg - .use(circle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(circle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) - - document.addEventListener('dragstart', preventDrag) + cleanHandles() + createHandles(svg) } onChange() @@ -564,12 +553,7 @@ export function useDraw( poly.stroke(stroke) poly.data('selected', false) - interact('.point-handle').unset() - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((circle) => circle.remove()) - circles.length = 0 - document.removeEventListener('dragstart', preventDrag) + cleanHandles() window.removeEventListener('keyup', polyDelKeyPress, { capture: true, @@ -587,15 +571,7 @@ export function useDraw( interact(poly.node).draggable({ listeners: { start() { - interact('.point-handle').unset() - handles.forEach((handle) => { - handle.remove() - }) - handles.length = 0 - circles.forEach((circle) => { - circle.remove() - }) - circles.length = 0 + cleanHandles() }, move(event) { const x = parseFloat(event.target.instance.x()) @@ -607,60 +583,7 @@ export function useDraw( onChange() }, end() { - for ( - let i = 0; - i < poly.node.points.numberOfItems; - i++ - ) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const newCircle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .id(handleId) - - const mouseenter = () => { - newCircle.scale(CIRCLE_SCALE_IN) - - newCircle.on('mouseleave', mouseleave) - newCircle.off('mouseenter', mouseenter) - } - const mouseleave = () => { - newCircle.scale(CIRCLE_SCALE_OUT) - - newCircle.on('mouseenter', mouseenter) - newCircle.off('mouseleave', mouseleave) - } - - newCircle.on('mouseenter', mouseenter) - - const handle = svg - .use(newCircle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(newCircle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) + createHandles(svg) }, }, modifiers: [ @@ -701,22 +624,10 @@ export function useDraw( .fill({ opacity: 1, color: '#f06' }) .stroke({ width: 1, color: '#fff' }) .attr('data-draw-ignore', true) + .addClass('tmp-point') .move(x - delta, y - delta) + .css('touch-action', 'none') // silence interactjs warning. - const mouseenter = () => { - point.scale(CIRCLE_SCALE_IN) - - point.on('mouseleave', mouseleave) - point.off('mouseenter', mouseenter) - } - const mouseleave = () => { - point.scale(CIRCLE_SCALE_OUT) - - point.on('mouseenter', mouseenter) - point.off('mouseleave', mouseleave) - } - - point.on('mouseenter', mouseenter) point.on('pointerdown', function (event) { event.preventDefault() event.stopPropagation() diff --git a/yarn.lock b/yarn.lock index 54d0d1e..d7b6baa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2059,10 +2059,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@interactjs/types@1.10.13": - version "1.10.13" - resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.13.tgz#0ebcc9ab582cb27e84ccdd2a7a33c87076cd511e" - integrity sha512-VdsUBs2YSdrgB2ZuL2UL0WW3fH6XuINjdk7XE9K+r64X9k2IC3nQm7pIg7HjO3iiLtuBNUVEGy+eJwU1G8t/Lg== +"@interactjs/types@1.10.14": + version "1.10.14" + resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.14.tgz#0466d8f3c3681554a55bb09fc5d4ccc3ef4e5cf5" + integrity sha512-ds6wAI9aDzojzr7UBvifUJIgogCL1ZL0nfBrXDv5GVMaW7gBglqejaeiQ1a0cRiHII/8p8s5AwPhhODTDM8RCQ== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -7337,12 +7337,12 @@ install-peers-cli@^2.2.0: commander "^2.20.0" executioner "^2.0.1" -interactjs@^1.9.20: - version "1.10.13" - resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.13.tgz#6a501fb447c1fcda8917b1f59e4384bd4f70adbb" - integrity sha512-wvzcTBjB4gGITQvWGAEKVTfgs/oyxDRas+j948W/E8B/46Ogac1jsLHWvaM1Qka5GqvLKAqABocY7jx4T7VDNg== +interactjs@^1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.14.tgz#9357fa8053ec00fb0533f3da47b646a742c23151" + integrity sha512-UhwZhuFqmKLSJ1o04LlvFMdxohwvuMjVrQC96TI8/dEhF/dS47Tqm3Po5i4yKXaCMIqI8qdvmyfeIWdGLYSClA== dependencies: - "@interactjs/types" "1.10.13" + "@interactjs/types" "1.10.14" internal-slot@^1.0.3: version "1.0.3" From 0704a670b1e0fbaa60bfc41f1ed3d7777a51c335 Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Mon, 20 Jun 2022 15:40:03 +0200 Subject: [PATCH 06/30] 0.0.27-beta2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e5c089..b566674 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta1", + "version": "0.0.27-beta2", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ From 9d0de94d48e7f29266da9af2bf7bc8b63bfa016f Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Mon, 20 Jun 2022 16:25:38 +0200 Subject: [PATCH 07/30] 0.0.27-beta3 --- package.json | 2 +- src/draw-zone/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b566674..cfe2800 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta2", + "version": "0.0.27-beta3", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/types.ts b/src/draw-zone/types.ts index 7463c95..a7be463 100644 --- a/src/draw-zone/types.ts +++ b/src/draw-zone/types.ts @@ -1,5 +1,5 @@ export type DrawZoneMode = 'draw' | 'move' | 'none' -export type DrawZoneShape = 'rect' | 'poly' +export type DrawZoneShape = 'rect' | 'poly' | 'none' export type SizeMode = 'auto' | 'fit' export interface Size { From a1ae41583041c4297b188765bae5b9bd1a415d88 Mon Sep 17 00:00:00 2001 From: Maxime Baumann Date: Mon, 20 Jun 2022 16:35:29 +0200 Subject: [PATCH 08/30] 0.0.27-beta4 --- package.json | 2 +- src/draw-zone/index.tsx | 2 ++ src/index.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index cfe2800..0c5a6ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta3", + "version": "0.0.27-beta4", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/index.tsx b/src/draw-zone/index.tsx index 7f0620b..3c321a0 100644 --- a/src/draw-zone/index.tsx +++ b/src/draw-zone/index.tsx @@ -7,6 +7,7 @@ import { useDrawZone } from './hooks' import { ChangedElement, DrawZoneMode, + DrawZoneShape, DrawZoneState, Point, Size, @@ -20,6 +21,7 @@ export type { DrawZoneProps, ChangedElement, DrawZoneMode, + DrawZoneShape, DrawZoneState, Point, Size, diff --git a/src/index.ts b/src/index.ts index 1ddad70..57be4e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ export type { DrawZoneContainerProps, DrawZoneProps, DrawZoneMode, + DrawZoneShape, SizeMode, DrawZoneState, } from './draw-zone' From db0665a982a35fbf09036978ac8843807e08b58c Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Tue, 26 Jul 2022 09:52:29 +0200 Subject: [PATCH 09/30] Upgrade deps --- package.json | 10 +- yarn.lock | 1744 +++++++++++++++++++++++++------------------------- 2 files changed, 877 insertions(+), 877 deletions(-) diff --git a/package.json b/package.json index 0c5a6ec..d57bede 100644 --- a/package.json +++ b/package.json @@ -20,19 +20,19 @@ "release": "yarn build && yarn publish", "clean": "rm -rf dist/", "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook", - "prepare": "install-peers" + "build-storybook": "build-storybook" }, "dependencies": { "@svgdotjs/svg.js": "^3.0.16", "interactjs": "^1.10.14" }, "peerDependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" }, "devDependencies": { "@babel/core": "^7.15.0", + "@babel/helper-get-function-arity": "^7.16.7", "@babel/preset-env": "^7.18.2", "@babel/preset-typescript": "^7.17.12", "@rollup/plugin-commonjs": "^20.0.0", @@ -56,6 +56,8 @@ "eslint-plugin-unused-imports": "^2.0.0", "install-peers-cli": "^2.2.0", "prettier": "^2.6.2", + "react": "^17", + "react-dom": "^17", "rollup": "^2.56.3", "rollup-plugin-cleaner": "^1.0.0", "rollup-plugin-peer-deps-external": "^2.2.4", diff --git a/yarn.lock b/yarn.lock index d7b6baa..65d2da6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,31 +17,31 @@ dependencies: "@babel/highlight" "^7.14.5" -"@babel/code-frame@^7.14.5": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== +"@babel/code-frame@^7.14.5", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.0" + "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7": +"@babel/code-frame@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.7": +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.15.0", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + +"@babel/compat-data@^7.14.7": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== -"@babel/compat-data@^7.15.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" - integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== - -"@babel/compat-data@^7.16.0", "@babel/compat-data@^7.17.10": +"@babel/compat-data@^7.16.0": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== @@ -90,20 +90,20 @@ source-map "^0.5.0" "@babel/core@^7.15.0": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.2.tgz#87b2fcd7cce9becaa7f5acebdc4f09f3dd19d876" - integrity sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" + integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== dependencies: "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.0" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.9" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -119,16 +119,16 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.15.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" - integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== +"@babel/generator@^7.15.0", "@babel/generator@^7.18.2", "@babel/generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" + integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.18.9" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" - source-map "^0.5.0" -"@babel/generator@^7.16.0", "@babel/generator@^7.18.2": +"@babel/generator@^7.16.0": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== @@ -137,19 +137,12 @@ "@jridgewell/gen-mapping" "^0.3.0" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" - integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== +"@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.16.7", "@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": version "7.14.5" @@ -159,15 +152,25 @@ "@babel/helper-explode-assignable-expression" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== + dependencies: + "@babel/compat-data" "^7.18.8" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": +"@babel/helper-compilation-targets@^7.14.5": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz#973df8cbd025515f3ff25db0c05efc704fa79818" integrity sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A== @@ -187,16 +190,6 @@ browserslist "^4.16.6" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" - semver "^6.3.0" - "@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.0.tgz#c9a137a4d137b2d0e2c649acf536d7ba1a76c0f7" @@ -209,34 +202,26 @@ "@babel/helper-replace-supers" "^7.15.0" "@babel/helper-split-export-declaration" "^7.14.5" -"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" - integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== +"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" + integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== +"@babel/helper-create-regexp-features-plugin@^7.14.5", "@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" + integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - regexpu-core "^4.7.1" - -"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" - integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" "@babel/helper-define-polyfill-provider@^0.1.5": version "0.1.5" @@ -266,24 +251,22 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== +"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" + integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" - integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== +"@babel/helper-environment-visitor@^7.18.2", "@babel/helper-environment-visitor@^7.18.6", "@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== "@babel/helper-explode-assignable-expression@^7.14.5": version "7.14.5" @@ -292,23 +275,22 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.14.5": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" - integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== +"@babel/helper-function-name@^7.14.5", "@babel/helper-function-name@^7.17.9", "@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== dependencies: - "@babel/helper-get-function-arity" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/types" "^7.16.0" + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-function-name@^7.16.0", "@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": +"@babel/helper-function-name@^7.16.0": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== @@ -316,21 +298,21 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" -"@babel/helper-get-function-arity@^7.16.0": +"@babel/helper-get-function-arity@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.14.5": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" - integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== +"@babel/helper-hoist-variables@^7.14.5", "@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.18.6" -"@babel/helper-hoist-variables@^7.16.0", "@babel/helper-hoist-variables@^7.16.7": +"@babel/helper-hoist-variables@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== @@ -344,19 +326,26 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-member-expression-to-functions@^7.16.0", "@babel/helper-member-expression-to-functions@^7.17.7": +"@babel/helper-member-expression-to-functions@^7.16.0": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== dependencies: "@babel/types" "^7.17.0" -"@babel/helper-module-imports@^7.12.13": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" - integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== +"@babel/helper-member-expression-to-functions@^7.17.7", "@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.18.9" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" "@babel/helper-module-imports@^7.14.5": version "7.16.0" @@ -365,7 +354,7 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.16.7": +"@babel/helper-module-imports@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== @@ -400,19 +389,19 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-optimise-call-expression@^7.14.5": version "7.16.0" @@ -421,27 +410,29 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-optimise-call-expression@^7.16.0", "@babel/helper-optimise-call-expression@^7.16.7": +"@babel/helper-optimise-call-expression@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: "@babel/types" "^7.16.7" +"@babel/helper-optimise-call-expression@^7.16.7", "@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-plugin-utils@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" + integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== "@babel/helper-remap-async-to-generator@^7.14.5": version "7.14.5" @@ -452,14 +443,15 @@ "@babel/helper-wrap-function" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== +"@babel/helper-remap-async-to-generator@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-wrap-function" "^7.16.8" - "@babel/types" "^7.16.8" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-replace-supers@^7.14.5": version "7.15.0" @@ -481,7 +473,7 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helper-replace-supers@^7.16.0", "@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": +"@babel/helper-replace-supers@^7.16.0": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== @@ -492,6 +484,17 @@ "@babel/traverse" "^7.18.2" "@babel/types" "^7.18.2" +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + "@babel/helper-simple-access@^7.14.8": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" @@ -499,13 +502,20 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-simple-access@^7.16.0", "@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": +"@babel/helper-simple-access@^7.16.0": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== dependencies: "@babel/types" "^7.18.2" +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" @@ -513,41 +523,41 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.18.9" -"@babel/helper-split-export-declaration@^7.14.5": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" - integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== +"@babel/helper-split-export-declaration@^7.14.5", "@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.18.6" -"@babel/helper-split-export-declaration@^7.16.0", "@babel/helper-split-export-declaration@^7.16.7": +"@babel/helper-split-export-declaration@^7.16.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": +"@babel/helper-validator-identifier@^7.14.5": version "7.15.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== -"@babel/helper-validator-identifier@^7.15.7", "@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7", "@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== -"@babel/helper-validator-option@^7.14.5", "@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.14.5", "@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== "@babel/helper-wrap-function@^7.14.5": version "7.14.5" @@ -559,15 +569,15 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== +"@babel/helper-wrap-function@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz#ae1feddc6ebbaa2fd79346b77821c3bd73a39646" + integrity sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ== dependencies: - "@babel/helper-function-name" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" + "@babel/helper-function-name" "^7.18.9" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helpers@^7.12.5": version "7.15.3" @@ -587,14 +597,14 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== +"@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/highlight@^7.14.5": version "7.16.0" @@ -605,12 +615,12 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.16.0", "@babel/highlight@^7.16.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" - integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== +"@babel/highlight@^7.16.7", "@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" @@ -619,22 +629,27 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862" integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA== -"@babel/parser@^7.14.5", "@babel/parser@^7.15.0": +"@babel/parser@^7.14.5": version "7.16.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac" integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw== -"@babel/parser@^7.16.0", "@babel/parser@^7.16.7", "@babel/parser@^7.18.0": +"@babel/parser@^7.15.0", "@babel/parser@^7.18.0", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" + integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== + +"@babel/parser@^7.16.0": version "7.18.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef" integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" - integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": version "7.14.5" @@ -645,14 +660,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" "@babel/plugin-proposal-optional-chaining" "^7.14.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" - integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" "@babel/plugin-proposal-async-generator-functions@^7.14.9": version "7.14.9" @@ -663,13 +678,14 @@ "@babel/helper-remap-async-to-generator" "^7.14.5" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-async-generator-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" - integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== +"@babel/plugin-proposal-async-generator-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" + integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.14.5": @@ -680,13 +696,13 @@ "@babel/helper-create-class-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-class-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" - integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-class-static-block@^7.14.5": version "7.14.5" @@ -697,13 +713,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-class-static-block@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" - integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-decorators@^7.12.12": @@ -723,12 +739,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-default-from@^7.12.1": @@ -747,12 +763,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-export-namespace-from@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" - integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.14.5": @@ -763,12 +779,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" - integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.14.5": @@ -779,12 +795,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" - integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": @@ -795,12 +811,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" - integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.14.5": @@ -811,12 +827,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@7.12.1": @@ -839,16 +855,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.14.5" -"@babel/plugin-proposal-object-rest-spread@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" - integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== +"@babel/plugin-proposal-object-rest-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" + integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-proposal-optional-catch-binding@^7.14.5": version "7.14.5" @@ -858,12 +874,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.14.5": @@ -875,13 +891,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" - integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.14.5": @@ -892,15 +908,15 @@ "@babel/helper-create-class-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-private-methods@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" - integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-private-property-in-object@^7.12.1", "@babel/plugin-proposal-private-property-in-object@^7.17.12": +"@babel/plugin-proposal-private-property-in-object@^7.12.1": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== @@ -920,7 +936,17 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== @@ -928,13 +954,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" - integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -992,12 +1018,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-import-assertions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" - integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== +"@babel/plugin-syntax-import-assertions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" + integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -1083,12 +1109,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" - integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== +"@babel/plugin-syntax-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.14.5": version "7.14.5" @@ -1097,12 +1123,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-arrow-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== +"@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-async-to-generator@^7.14.5": version "7.14.5" @@ -1113,14 +1139,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-remap-async-to-generator" "^7.14.5" -"@babel/plugin-transform-async-to-generator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" - integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" "@babel/plugin-transform-block-scoped-functions@^7.14.5": version "7.14.5" @@ -1129,12 +1155,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.14.5": version "7.15.3" @@ -1143,12 +1169,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-block-scoping@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" - integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== +"@babel/plugin-transform-block-scoping@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" + integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.14.9": version "7.14.9" @@ -1163,18 +1189,18 @@ "@babel/helper-split-export-declaration" "^7.14.5" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" - integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" +"@babel/plugin-transform-classes@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" + integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.14.5": @@ -1184,12 +1210,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-computed-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" - integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.14.7": version "7.14.7" @@ -1198,14 +1224,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-destructuring@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" - integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== +"@babel/plugin-transform-destructuring@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" + integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": +"@babel/plugin-transform-dotall-regex@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== @@ -1213,13 +1239,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-dotall-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-duplicate-keys@^7.14.5": version "7.14.5" @@ -1228,12 +1254,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-duplicate-keys@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" - integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-exponentiation-operator@^7.14.5": version "7.14.5" @@ -1243,13 +1269,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-flow-strip-types@^7.14.5": version "7.14.5" @@ -1266,12 +1292,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-for-of@^7.18.1": - version "7.18.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" - integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== +"@babel/plugin-transform-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-function-name@^7.14.5": version "7.14.5" @@ -1281,14 +1307,14 @@ "@babel/helper-function-name" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-literals@^7.14.5": version "7.14.5" @@ -1297,12 +1323,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-literals@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" - integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.14.5": version "7.14.5" @@ -1311,12 +1337,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-amd@^7.14.5": version "7.14.5" @@ -1327,13 +1353,13 @@ "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-amd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" - integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== +"@babel/plugin-transform-modules-amd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" + integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.15.0": @@ -1346,14 +1372,14 @@ "@babel/helper-simple-access" "^7.14.8" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" - integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== +"@babel/plugin-transform-modules-commonjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.18.2" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.14.5": @@ -1367,15 +1393,15 @@ "@babel/helper-validator-identifier" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.18.0": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.4.tgz#3d6fd9868c735cce8f38d6ae3a407fb7e61e6d46" - integrity sha512-lH2UaQaHVOAeYrUUuZ8i38o76J/FnO8vu21OE+tD1MyP9lxdZoSfz+pDbWkq46GogUrdrMz3tiz/FYGB+bVThg== +"@babel/plugin-transform-modules-systemjs@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" + integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-validator-identifier" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-umd@^7.14.5": @@ -1386,13 +1412,13 @@ "@babel/helper-module-transforms" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-modules-umd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" - integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-named-capturing-groups-regex@^7.14.9": version "7.14.9" @@ -1401,13 +1427,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.14.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" - integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== +"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" + integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-new-target@^7.14.5": version "7.14.5" @@ -1416,12 +1442,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-new-target@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz#10842cd605a620944e81ea6060e9e65c265742e3" - integrity sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w== +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-object-super@^7.14.5": version "7.14.5" @@ -1431,13 +1457,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-replace-supers" "^7.14.5" -"@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" "@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.14.5": version "7.14.5" @@ -1446,12 +1472,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-parameters@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" - integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== +"@babel/plugin-transform-parameters@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" + integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-property-literals@^7.14.5": version "7.14.5" @@ -1460,12 +1486,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-display-name@^7.14.5": version "7.15.1" @@ -1507,12 +1533,12 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-regenerator@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" - integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" regenerator-transform "^0.15.0" "@babel/plugin-transform-reserved-words@^7.14.5": @@ -1522,12 +1548,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-reserved-words@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" - integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.14.5": version "7.14.5" @@ -1536,12 +1562,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.14.6": version "7.14.6" @@ -1551,13 +1577,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" -"@babel/plugin-transform-spread@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" - integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== +"@babel/plugin-transform-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" + integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-transform-sticky-regex@^7.14.5": version "7.14.5" @@ -1566,12 +1592,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.14.5": version "7.14.5" @@ -1580,12 +1606,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-template-literals@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" - integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typeof-symbol@^7.14.5": version "7.14.5" @@ -1594,12 +1620,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typeof-symbol@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" - integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.15.0": version "7.15.0" @@ -1610,14 +1636,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript" "^7.14.5" -"@babel/plugin-transform-typescript@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf" - integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw== +"@babel/plugin-transform-typescript@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" + integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-typescript" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-typescript" "^7.18.6" "@babel/plugin-transform-unicode-escapes@^7.14.5": version "7.14.5" @@ -1626,12 +1652,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== +"@babel/plugin-transform-unicode-escapes@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" + integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-unicode-regex@^7.14.5": version "7.14.5" @@ -1641,13 +1667,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.12.11": version "7.15.0" @@ -1729,37 +1755,37 @@ semver "^6.3.0" "@babel/preset-env@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" - integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-async-generator-functions" "^7.17.12" - "@babel/plugin-proposal-class-properties" "^7.17.12" - "@babel/plugin-proposal-class-static-block" "^7.18.0" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.17.12" - "@babel/plugin-proposal-json-strings" "^7.17.12" - "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.18.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-private-methods" "^7.17.12" - "@babel/plugin-proposal-private-property-in-object" "^7.17.12" - "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff" + integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg== + dependencies: + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.18.9" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.17.12" + "@babel/plugin-syntax-import-assertions" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1769,43 +1795,43 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.17.12" - "@babel/plugin-transform-async-to-generator" "^7.17.12" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.17.12" - "@babel/plugin-transform-classes" "^7.17.12" - "@babel/plugin-transform-computed-properties" "^7.17.12" - "@babel/plugin-transform-destructuring" "^7.18.0" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.17.12" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.18.1" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.17.12" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.18.0" - "@babel/plugin-transform-modules-commonjs" "^7.18.2" - "@babel/plugin-transform-modules-systemjs" "^7.18.0" - "@babel/plugin-transform-modules-umd" "^7.18.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" - "@babel/plugin-transform-new-target" "^7.17.12" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.18.0" - "@babel/plugin-transform-reserved-words" "^7.17.12" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.17.12" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.18.2" - "@babel/plugin-transform-typeof-symbol" "^7.17.12" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.18.9" + "@babel/plugin-transform-classes" "^7.18.9" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.18.9" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.18.9" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.18.9" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.2" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" + "@babel/types" "^7.18.9" + babel-plugin-polyfill-corejs2 "^0.3.1" + babel-plugin-polyfill-corejs3 "^0.5.2" + babel-plugin-polyfill-regenerator "^0.3.1" core-js-compat "^3.22.1" semver "^6.3.0" @@ -1862,13 +1888,13 @@ "@babel/plugin-transform-typescript" "^7.15.0" "@babel/preset-typescript@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" - integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-typescript" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" "@babel/register@^7.12.1": version "7.15.3" @@ -1882,24 +1908,24 @@ source-map-support "^0.5.16" "@babel/runtime-corejs3@^7.10.2": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.9.tgz#3d02d0161f0fbf3ada8e88159375af97690f4055" - integrity sha512-WxYHHUWF2uZ7Hp1K+D1xQgbgkGUfA+5UPOegEXGt2Y5SMog/rYCVaifLZDbw8UkNXozEqqrZTy6bglL7xTaCOw== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae" + integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A== dependencies: core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6": version "7.15.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.16.3": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== +"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== dependencies: regenerator-runtime "^0.13.4" @@ -1928,16 +1954,16 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/template@^7.16.0", "@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.16.0", "@babel/template@^7.16.7", "@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" -"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5": +"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.14.5": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98" integrity sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw== @@ -1952,6 +1978,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" + integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.9" + "@babel/types" "^7.18.9" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.15.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.0.tgz#965df6c6bfc0a958c1e739284d3c9fa4a6e3c45b" @@ -1967,7 +2009,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.16.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2": +"@babel/traverse@^7.16.0": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.2.tgz#b77a52604b5cc836a9e1e08dca01cba67a12d2e8" integrity sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA== @@ -1983,7 +2025,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.14.9", "@babel/types@^7.2.0", "@babel/types@^7.4.4": +"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.14.9", "@babel/types@^7.2.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ== @@ -1991,20 +2033,12 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@babel/types@^7.14.5", "@babel/types@^7.15.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== +"@babel/types@^7.14.5", "@babel/types@^7.15.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.18.2", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" + integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" "@base2/pretty-print-object@1.0.1": @@ -2059,10 +2093,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@interactjs/types@1.10.14": - version "1.10.14" - resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.14.tgz#0466d8f3c3681554a55bb09fc5d4ccc3ef4e5cf5" - integrity sha512-ds6wAI9aDzojzr7UBvifUJIgogCL1ZL0nfBrXDv5GVMaW7gBglqejaeiQ1a0cRiHII/8p8s5AwPhhODTDM8RCQ== +"@interactjs/types@1.10.17": + version "1.10.17" + resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.17.tgz#1649de06d9ead790c81ecece76736b852bdfc77e" + integrity sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2120,24 +2154,24 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== dependencies: - "@jridgewell/set-array" "^1.0.0" + "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": version "0.3.2" @@ -2148,11 +2182,11 @@ "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.7": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== @@ -2160,6 +2194,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@mdx-js/mdx@^1.6.22": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -3459,13 +3501,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.21.0": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz#fdf59c905354139046b41b3ed95d1609913d0758" - integrity sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz#cae1967b1e569e6171bbc6bec2afa4e0c8efccfe" + integrity sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ== dependencies: - "@typescript-eslint/scope-manager" "5.27.1" - "@typescript-eslint/type-utils" "5.27.1" - "@typescript-eslint/utils" "5.27.1" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/type-utils" "5.31.0" + "@typescript-eslint/utils" "5.31.0" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -3474,121 +3516,75 @@ tsutils "^3.21.0" "@typescript-eslint/experimental-utils@^5.3.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.21.0.tgz#489275ca792f5de7e0d1f4be1f15576ea56b6ca2" - integrity sha512-mzF6ert/6iQoESV0z9v5/mEaJRKL4fv68rHoZ6exM38xjxkw4MNx54B7ferrnMTM/GIRKLDaJ3JPRi+Dxa5Hlg== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.31.0.tgz#f2b23ebeebd31358ce44cf5be7c3411127627b84" + integrity sha512-Yiar0ggNPyOsvrslJBdOo3jc3wjI6NnmWOQBA8WhR54YPbVqTNLuuHC6zxEt8FIgMozerxRlAncwznEjK+cJVA== dependencies: - "@typescript-eslint/utils" "5.21.0" + "@typescript-eslint/utils" "5.31.0" "@typescript-eslint/parser@^5.21.0": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639" - integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.31.0.tgz#7f42d7dcc68a0a6d80a0f3d9a65063aee7bb8d2c" + integrity sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw== dependencies: - "@typescript-eslint/scope-manager" "5.27.1" - "@typescript-eslint/types" "5.27.1" - "@typescript-eslint/typescript-estree" "5.27.1" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/typescript-estree" "5.31.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.21.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz#a4b7ed1618f09f95e3d17d1c0ff7a341dac7862e" - integrity sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ== - dependencies: - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/visitor-keys" "5.21.0" - -"@typescript-eslint/scope-manager@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d" - integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg== +"@typescript-eslint/scope-manager@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606" + integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg== dependencies: - "@typescript-eslint/types" "5.27.1" - "@typescript-eslint/visitor-keys" "5.27.1" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/visitor-keys" "5.31.0" -"@typescript-eslint/type-utils@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.27.1.tgz#369f695199f74c1876e395ebea202582eb1d4166" - integrity sha512-+UC1vVUWaDHRnC2cQrCJ4QtVjpjjCgjNFpg8b03nERmkHv9JV9X5M19D7UFMd+/G7T/sgFwX2pGmWK38rqyvXw== +"@typescript-eslint/type-utils@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz#70a0b7201360b5adbddb0c36080495aa08f6f3d9" + integrity sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w== dependencies: - "@typescript-eslint/utils" "5.27.1" + "@typescript-eslint/utils" "5.31.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.21.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.21.0.tgz#8cdb9253c0dfce3f2ab655b9d36c03f72e684017" - integrity sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA== +"@typescript-eslint/types@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a" + integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g== -"@typescript-eslint/types@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1" - integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg== - -"@typescript-eslint/typescript-estree@5.21.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz#9f0c233e28be2540eaed3df050f0d54fb5aa52de" - integrity sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg== +"@typescript-eslint/typescript-estree@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882" + integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw== dependencies: - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/visitor-keys" "5.21.0" - debug "^4.3.2" - globby "^11.0.4" - is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808" - integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw== - dependencies: - "@typescript-eslint/types" "5.27.1" - "@typescript-eslint/visitor-keys" "5.27.1" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/visitor-keys" "5.31.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.21.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.21.0.tgz#51d7886a6f0575e23706e5548c7e87bce42d7c18" - integrity sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q== - dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.21.0" - "@typescript-eslint/types" "5.21.0" - "@typescript-eslint/typescript-estree" "5.21.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/utils@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.27.1.tgz#b4678b68a94bc3b85bf08f243812a6868ac5128f" - integrity sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w== +"@typescript-eslint/utils@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd" + integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.27.1" - "@typescript-eslint/types" "5.27.1" - "@typescript-eslint/typescript-estree" "5.27.1" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/typescript-estree" "5.31.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.21.0": - version "5.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz#453fb3662409abaf2f8b1f65d515699c888dd8ae" - integrity sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA== - dependencies: - "@typescript-eslint/types" "5.21.0" - eslint-visitor-keys "^3.0.0" - -"@typescript-eslint/visitor-keys@5.27.1": - version "5.27.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af" - integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ== +"@typescript-eslint/visitor-keys@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54" + integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg== dependencies: - "@typescript-eslint/types" "5.27.1" + "@typescript-eslint/types" "5.31.0" eslint-visitor-keys "^3.3.0" "@webassemblyjs/ast@1.11.1": @@ -3900,11 +3896,16 @@ acorn@^7.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: +acorn@^8.4.1, acorn@^8.5.0: version "8.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.7.1: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -4114,7 +4115,7 @@ array-includes@^3.0.3: get-intrinsic "^1.1.1" is-string "^1.0.5" -array-includes@^3.1.4, array-includes@^3.1.5: +array-includes@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== @@ -4218,7 +4219,7 @@ assign-symbols@^1.0.0: ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== ast-types@^0.14.2: version "0.14.2" @@ -4260,10 +4261,10 @@ autoprefixer@^9.8.6: postcss "^7.0.32" postcss-value-parser "^4.1.0" -axe-core@^4.3.5: - version "4.4.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" - integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axe-core@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" + integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== axobject-query@^2.2.0: version "2.2.0" @@ -4351,13 +4352,13 @@ babel-plugin-polyfill-corejs2@^0.2.2: "@babel/helper-define-polyfill-provider" "^0.2.2" semver "^6.1.1" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +babel-plugin-polyfill-corejs2@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" + integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.1.0: @@ -4376,12 +4377,12 @@ babel-plugin-polyfill-corejs3@^0.2.2: "@babel/helper-define-polyfill-provider" "^0.2.2" core-js-compat "^3.14.0" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +babel-plugin-polyfill-corejs3@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" babel-plugin-polyfill-regenerator@^0.2.2: @@ -4391,7 +4392,7 @@ babel-plugin-polyfill-regenerator@^0.2.2: dependencies: "@babel/helper-define-polyfill-provider" "^0.2.2" -babel-plugin-polyfill-regenerator@^0.3.0: +babel-plugin-polyfill-regenerator@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== @@ -4639,7 +4640,7 @@ browserslist@^4.12.0, browserslist@^4.16.8: escalade "^3.1.1" node-releases "^1.1.75" -browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.20.2, browserslist@^4.20.3: +browserslist@^4.14.5: version "4.20.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== @@ -4650,6 +4651,16 @@ browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.20.2, browserslist@^ node-releases "^2.0.5" picocolors "^1.0.0" +browserslist@^4.16.6, browserslist@^4.20.2, browserslist@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" + integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== + dependencies: + caniuse-lite "^1.0.30001366" + electron-to-chromium "^1.4.188" + node-releases "^2.0.6" + update-browserslist-db "^1.0.4" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -4827,11 +4838,16 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001349: +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001251: version "1.0.30001352" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz" integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA== +caniuse-lite@^1.0.30001349, caniuse-lite@^1.0.30001366: + version "1.0.30001370" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz#0a30d4f20d38b9e108cc5ae7cc62df9fe66cd5ba" + integrity sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -5213,17 +5229,17 @@ core-js-compat@^3.14.0, core-js-compat@^3.16.0, core-js-compat@^3.8.1: semver "7.0.0" core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.22.8" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.8.tgz#46fa34ce1ddf742acd7f95f575f66bbb21e05d62" - integrity sha512-pQnwg4xtuvc2Bs/5zYQPaEYYSuTxsF7LBWF0SvnVhthZo/Qe+rJpcEekrdNK5DWwDJ0gv0oI9NNX5Mppdy0ctg== + version "3.24.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.0.tgz#885958fac38bf3f4464a90f2663b4620f6aee6e3" + integrity sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg== dependencies: - browserslist "^4.20.3" + browserslist "^4.21.2" semver "7.0.0" core-js-pure@^3.20.2: - version "3.22.3" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.3.tgz#181d1b6321fb29fe99c16a1f28beb840ab84ad36" - integrity sha512-oN88zz7nmKROMy8GOjs+LN+0LedIvbMdnB5XsTlhcOg1WGARt9l0LFg0zohdoFmCsEZ1h2ZbSQ6azj3M+vhzwQ== + version "3.24.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.0.tgz#10eeb90dbf0d670a6b22b081aecc7deb2faec7e1" + integrity sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA== core-js-pure@^3.8.1: version "3.20.1" @@ -5428,7 +5444,7 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -damerau-levenshtein@^1.0.7: +damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== @@ -5707,10 +5723,10 @@ electron-to-chromium@^1.3.811: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.890.tgz#e7143b659f73dc4d0512d1ae4baeb0fb9e7bc835" integrity sha512-VWlVXSkv0cA/OOehrEyqjUTHwV8YXCPTfPvbtoeU2aHR21vI4Ejh5aC4AxUwOmbLbBgb6Gd3URZahoCxtBqCYQ== -electron-to-chromium@^1.4.147: - version "1.4.151" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.151.tgz#d1c09dd3a06cb81ef03a3bbbff6905827c33ab4b" - integrity sha512-XaG2LpZi9fdiWYOqJh0dJy4SlVywCvpgYXhzOlZTp4JqSKqxn5URqOjbm9OMYB3aInA2GuHQiem1QUOc1yT0Pw== +electron-to-chromium@^1.4.147, electron-to-chromium@^1.4.188: + version "1.4.200" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.200.tgz#6e4c5266106688965b4ea7caa11f0dd315586854" + integrity sha512-nPyI7oHc8T64oSqRXrAt99gNMpk0SAgPHw/o+hkNKyb5+bcdnFtZcSO9FUJES5cVkVZvo8u4qiZ1gQILl8UXsA== elliptic@^6.5.3: version "6.5.4" @@ -5949,32 +5965,33 @@ eslint-config-prettier@^8.5.0: integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== eslint-plugin-jsx-a11y@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" - integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== + version "6.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== dependencies: - "@babel/runtime" "^7.16.3" + "@babel/runtime" "^7.18.9" aria-query "^4.2.2" - array-includes "^3.1.4" + array-includes "^3.1.5" ast-types-flow "^0.0.7" - axe-core "^4.3.5" + axe-core "^4.4.3" axobject-query "^2.2.0" - damerau-levenshtein "^1.0.7" + damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.2.1" + jsx-ast-utils "^3.3.2" language-tags "^1.0.5" - minimatch "^3.0.4" + minimatch "^3.1.2" + semver "^6.3.0" eslint-plugin-react-hooks@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad" - integrity sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw== + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.29.4: - version "7.30.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz#8e7b1b2934b8426ac067a0febade1b13bd7064e3" - integrity sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A== + version "7.30.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" + integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== dependencies: array-includes "^3.1.5" array.prototype.flatmap "^1.3.0" @@ -5992,9 +6009,9 @@ eslint-plugin-react@^7.29.4: string.prototype.matchall "^4.0.7" eslint-plugin-storybook@^0.5.12: - version "0.5.12" - resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.12.tgz#b84d38400b91a9abdf15cd2c81644bff27861a96" - integrity sha512-ojuNKnrZFrQpm5N5Lp8UR0VEn4HtLjlNn6nxQAYlmTsEXNigtId1XPuMbXAsvFcEmv3RTb5l+9tZgkhSURfACg== + version "0.5.13" + resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.13.tgz#dee4056eaa59c886a9ae78fb7b2ee0ed5fad4366" + integrity sha512-82x3FH1VAi68Awu1VEjn/hLkzFZsOP8ItcC5/uGF9WszIrj6n7Q3MZD57oE26k3aKwuPfFtAPnSjFnaBkBab+g== dependencies: "@storybook/csf" "^0.0.1" "@typescript-eslint/experimental-utils" "^5.3.0" @@ -6048,15 +6065,15 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: +eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.14.0: - version "8.17.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21" - integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw== + version "8.20.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b" + integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA== dependencies: "@eslint/eslintrc" "^1.3.0" "@humanwhocodes/config-array" "^0.9.2" @@ -6499,9 +6516,9 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + version "3.2.6" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" + integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== flush-write-stream@^1.0.0: version "1.1.1" @@ -6847,9 +6864,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.15.0: - version "13.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== dependencies: type-fest "^0.20.2" @@ -6872,7 +6889,7 @@ globby@^11.0.2: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.0.4, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -7338,11 +7355,11 @@ install-peers-cli@^2.2.0: executioner "^2.0.1" interactjs@^1.10.14: - version "1.10.14" - resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.14.tgz#9357fa8053ec00fb0533f3da47b646a742c23151" - integrity sha512-UhwZhuFqmKLSJ1o04LlvFMdxohwvuMjVrQC96TI8/dEhF/dS47Tqm3Po5i4yKXaCMIqI8qdvmyfeIWdGLYSClA== + version "1.10.17" + resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.17.tgz#aed66a63020cd092236133f9149e6448dc405d72" + integrity sha512-grjHJgnWkCoQLmAlk2yalNd1r0ztUhXLJNVjSOfWn1wfNNgU2tx1cDEkro9WYerDNC9UG3MZTeD4O6zOM5gbIA== dependencies: - "@interactjs/types" "1.10.14" + "@interactjs/types" "1.10.17" internal-slot@^1.0.3: version "1.0.3" @@ -7471,7 +7488,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0, is-core-module@^2.8.1: +is-core-module@^2.2.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -7941,7 +7958,7 @@ jsesc@^2.5.1: jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-parse-better-errors@^1.0.2: version "1.0.2" @@ -7998,20 +8015,12 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -"jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz#e624f259143b9062c92b6413ff92a164c80d3ccb" - integrity sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q== - dependencies: - array-includes "^3.1.4" - object.assign "^4.1.2" - -jsx-ast-utils@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz#6ab1e52c71dfc0c0707008a91729a9491fe9f76c" - integrity sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw== +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" + integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== dependencies: - array-includes "^3.1.4" + array-includes "^3.1.5" object.assign "^4.1.2" junk@^3.1.0: @@ -8061,14 +8070,14 @@ klona@^2.0.4: integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== language-subtag-registry@~0.3.2: - version "0.3.21" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" - integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== language-tags@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== dependencies: language-subtag-registry "~0.3.2" @@ -8168,7 +8177,7 @@ locate-path@^6.0.0: lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.merge@^4.6.2: version "4.6.2" @@ -8185,7 +8194,7 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -8797,10 +8806,10 @@ node-releases@^1.1.75: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== +node-releases@^2.0.5, node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" @@ -9573,9 +9582,9 @@ prelude-ls@~1.1.2: integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== prettier@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== pretty-error@^2.1.1: version "2.1.2" @@ -9838,6 +9847,15 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" +react-dom@^17: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + react-element-to-jsx-string@^14.3.4: version "14.3.4" resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8" @@ -9882,6 +9900,14 @@ react-syntax-highlighter@^15.4.5: prismjs "^1.27.0" refractor "^3.6.0" +react@^17: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -9980,14 +10006,7 @@ regenerate-unicode-properties@^10.0.1: dependencies: regenerate "^1.4.2" -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0, regenerate@^1.4.2: +regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== @@ -10041,22 +10060,10 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.0.1" @@ -10065,23 +10072,11 @@ regexpu-core@^5.0.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -regjsgen@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - regjsgen@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== -regjsparser@^0.6.4: - version "0.6.9" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" - integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== - dependencies: - jsesc "~0.5.0" - regjsparser@^0.8.2: version "0.8.4" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" @@ -10231,7 +10226,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.3.2: +resolve@^1.10.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -10239,6 +10234,15 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.3.2: is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.14.2: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.17.0, resolve@^1.19.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -10249,12 +10253,13 @@ resolve@^1.17.0, resolve@^1.19.0: supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" ret@~0.1.10: version "0.1.15" @@ -10301,9 +10306,9 @@ rollup-plugin-peer-deps-external@^2.2.4: integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g== rollup@^2.56.3: - version "2.75.6" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.6.tgz#ac4dc8600f95942a0180f61c7c9d6200e374b439" - integrity sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA== + version "2.77.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.1.tgz#63463ebdbc04232fc42630ec72d137cd4400975d" + integrity sha512-GhutNJrvTYD6s1moo+kyq7lD9DeR5HDyXo4bDFlDSkepC9kVKY+KK/NSZFzCmeXeia3kEzVuToQmHRdugyZHxw== optionalDependencies: fsevents "~2.3.2" @@ -10368,6 +10373,14 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" @@ -11344,9 +11357,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^4.6.4: - version "4.7.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d" - integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA== + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== uglify-js@^3.1.4: version "3.14.5" @@ -11376,24 +11389,11 @@ unherit@^1.0.4: inherits "^2.0.0" xtend "^4.0.0" -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - unicode-match-property-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" @@ -11402,21 +11402,11 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - unicode-match-property-value-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" @@ -11546,6 +11536,14 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" + integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -11946,7 +11944,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.0: version "3.0.3" From 1066194fe967de4b8573acad87c25e1d9b6e482c Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Tue, 26 Jul 2022 10:48:00 +0200 Subject: [PATCH 10/30] Add projects utils and hooks that can be public --- package.json | 3 +- src/hooks.ts | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/utils.ts | 125 ++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d57bede..b29ae7c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ }, "dependencies": { "@svgdotjs/svg.js": "^3.0.16", - "interactjs": "^1.10.14" + "interactjs": "^1.10.14", + "lodash": "^4.17.21" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0", diff --git a/src/hooks.ts b/src/hooks.ts index ee95798..db5cb88 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,4 +1,18 @@ -import { useEffect, useRef, useState } from 'react' +import { debounce, isEqual } from 'lodash' +import { + DependencyList, + Dispatch, + EffectCallback, + MutableRefObject, + Ref, + SetStateAction, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { getRandomId } from './utils' export function usePointerPosition() { const [position, setPosition] = useState({ clientX: 0, clientY: 0 }) @@ -55,3 +69,237 @@ export function useAnimationFrame(callback: CallableFunction) { } }, []) } + +export function useCombinedRefs( + ...refs: Array | MutableRefObject> +) { + const targetRef = useRef(null) + + useEffect(() => { + refs.forEach((ref) => { + if (!ref) return + + if (typeof ref === 'function') { + ref(targetRef.current) + } else { + ;(ref as MutableRefObject).current = + targetRef.current + } + }) + }, [refs]) + + return targetRef +} + +export function useFullScreen() { + const [isFullScreen, setIsFullScreen] = useState( + window.matchMedia('(display-mode: fullscreen)').matches, + ) + + const open = useCallback(() => { + document.documentElement.requestFullscreen() + }, []) + + const close = useCallback(() => { + document.exitFullscreen() + }, []) + + const handleChange = useCallback(() => { + setIsFullScreen( + window.matchMedia('(display-mode: fullscreen)').matches || + (document.fullscreenElement !== null && + document.fullscreenElement !== undefined), + ) + }, []) + + useEffect(() => { + window.addEventListener('fullscreenchange', handleChange) + + return () => { + window.removeEventListener('fullscreenchange', handleChange) + } + }, []) + + return { isFullScreen, open, close } +} + +export function useLocalStorage( + key: string, + initialValue: T, +): [T, Dispatch>] { + const getValue = useCallback( + (item: string | null) => { + if (initialValue instanceof Map) { + return item + ? (new Map( + JSON.parse(item), + ) as unknown as T) + : initialValue + } + + return item ? JSON.parse(item) : initialValue + }, + [initialValue], + ) + + const [value, setValue] = useState(() => { + const item = localStorage.getItem(key) + + return getValue(item) + }) + + const onStorageUpdate = useCallback( + (e: StorageEvent) => { + if (e.key === key) { + const newValue = getValue(e.newValue) + + if (!isEqual(newValue, value)) { + setValue(newValue) + } + } + }, + [getValue, key, value], + ) + + useEffect(() => { + window.addEventListener('storage', onStorageUpdate) + + return () => { + window.removeEventListener('storage', onStorageUpdate) + } + }, [onStorageUpdate]) + + useEffect(() => { + if (value instanceof Map) { + localStorage.setItem( + key, + JSON.stringify(Array.from(value.entries())), + ) + } else { + localStorage.setItem(key, JSON.stringify(value)) + } + }, [key, value]) + + return [value, setValue] +} + +export function usePrevious( + value: T, +): MutableRefObject['current'] { + const ref = useRef() + + useEffect(() => { + ref.current = value + }, [value]) + + return ref.current +} + +export function useDebounce(delay: number) { + const [debouncedValue, setDebouncedValue] = useState() + + const debounceHandler = useMemo( + () => debounce((value: T) => setDebouncedValue(value), delay), + [], + ) + + useEffect(() => { + return () => { + debounceHandler.cancel() + } + }, [debounceHandler]) + + return [debouncedValue, debounceHandler] +} + +export function useMount(callback: EffectCallback) { + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(callback, []) +} + +export function useUnmount(callback: EffectCallback) { + const callbackRef = useRef(callback) + + callbackRef.current = callback + + useEffect(() => { + return () => { + callbackRef.current() + } + }, []) +} + +export function useUpdateEffect( + callback: EffectCallback, + deps?: DependencyList, +) { + const isFirstMount = useRef(false) + + useUnmount(() => { + isFirstMount.current = false + }) + + useEffect(() => { + if (isFirstMount.current) { + return callback() + } else { + isFirstMount.current = true + } + }, deps) +} + +export function useSetState(initState: T) { + const [state, setState] = useState(initState) + + const setMergeState = useCallback( + (value: SetStateAction>) => { + setState((prevValue) => { + const newValue = + typeof value === 'function' + ? ( + value as ( + prevState: T | Partial, + ) => T | Partial + )(prevValue) + : value + return newValue ? { ...prevValue, ...newValue } : prevValue + }) + }, + [], + ) + + return [state, setMergeState] +} + +export function useKeyPress( + targetKey: string, + handler: (event: KeyboardEvent) => void, +) { + const handlerRef = useRef(handler) + handlerRef.current = handler + + useEffect(() => { + const handleDown = (event: KeyboardEvent) => { + if (event.key === targetKey) { + handlerRef.current.call(window, event) + } + } + + window.addEventListener('keydown', handleDown) + + return () => { + window.removeEventListener('keydown', handleDown) + } + }, [targetKey, handler]) +} + +export function useId() { + const id = useRef(getRandomId()) + + return id.current +} + +export function useIdOrDefault(id?: string) { + const defaultId = useId() + return id || defaultId +} diff --git a/src/utils.ts b/src/utils.ts index 1b3100a..2d3555a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,128 @@ export const isTouchDevice = global.window && ('ontouchstart' in global.window || navigator.maxTouchPoints > 0) + +export function getRandomId() { + const arr = new Uint32Array(1) + global.crypto.getRandomValues(arr) + return arr[0].toString(36) +} + +export function getRandomNumber(min: number, max: number) { + const arr = new Uint32Array(1) + global.crypto.getRandomValues(arr) + const random = arr[0] + const range = max - min + return Math.floor((random / (0xffffffff + 1)) * range + min) +} + +export function groupBy>( + arr: T[], + key: string | number, +) { + return arr.reduce((acc, cur) => { + const value = acc.get(cur[key]) + + if (!value) { + acc.set(cur[key], [cur]) + } else { + acc.set(cur[key], [...value, cur]) + } + + return acc + }, new Map>()) +} + +export function isReactMouseEvent( + event: React.SyntheticEvent, +): event is React.SyntheticEvent { + return event.nativeEvent instanceof MouseEvent +} + +export function isReactKeyboardEvent( + event: React.SyntheticEvent, +): event is React.SyntheticEvent { + return event.nativeEvent instanceof KeyboardEvent +} + +export function numberToHex(number: number): string { + const hex = number.toString(16) + return hex.length === 1 ? `0${hex}` : hex +} + +export function bgrToHex(b: number, g: number, r: number) { + return `#${numberToHex(r)}${numberToHex(g)}${numberToHex(b)}` +} + +export function hexToBgr(hex: string): [number, number, number] | null { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) + return result + ? [ + parseInt(result[3], 16), + parseInt(result[2], 16), + parseInt(result[1], 16), + ] + : null +} + +export async function copyToClipboard(text: string) { + if (!navigator.clipboard) { + const textarea = document.createElement('textarea') + textarea.value = text + textarea.setAttribute('readonly', '') + textarea.style.position = 'fixed' + textarea.style.top = '0' + textarea.style.left = '0' + document.body.appendChild(textarea) + + textarea.focus() + textarea.select() + + try { + const success = document.execCommand('copy') + + if (!success) { + throw new Error('unable to copy') + } + } finally { + document.body.removeChild(textarea) + } + + return + } + + return await navigator.clipboard.writeText(text) +} + +const ipv4Regex = new RegExp( + '^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$', +) +export function isIPV4(ip: string) { + return ipv4Regex.test(ip) +} + +export function downloadFile(blob: Blob, filename: string) { + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + link.href = url + link.setAttribute('download', filename) + document.body.appendChild(link) + link.click() + document.body.removeChild(link) +} + +export function isNumberKeyEvent(event: KeyboardEvent): number | false { + const key = event.key + + const number = parseInt(key, 10) + if (number >= 0 && number <= 9) { + return number + } + + return false +} + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} From 9550bbf84483f20ba1a72a44d4cb502915cc2561 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Tue, 26 Jul 2022 10:53:55 +0200 Subject: [PATCH 11/30] 0.0.27-beta5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b29ae7c..16ed203 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta4", + "version": "0.0.27-beta5", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ From f5ec5ba94e512d530cb58b9dde3615c857568cdf Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Tue, 26 Jul 2022 10:57:36 +0200 Subject: [PATCH 12/30] fix exports --- src/helpers.ts | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hooks.ts | 2 +- src/index.ts | 6 +-- src/utils.ts | 125 ------------------------------------------------- 4 files changed, 129 insertions(+), 129 deletions(-) diff --git a/src/helpers.ts b/src/helpers.ts index 8ff2336..d37ecff 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -30,3 +30,128 @@ export function truncate( return str } } + +export function getRandomId() { + const arr = new Uint32Array(1) + global.crypto.getRandomValues(arr) + return arr[0].toString(36) +} + +export function getRandomNumber(min: number, max: number) { + const arr = new Uint32Array(1) + global.crypto.getRandomValues(arr) + const random = arr[0] + const range = max - min + return Math.floor((random / (0xffffffff + 1)) * range + min) +} + +export function groupBy>( + arr: T[], + key: string | number, +) { + return arr.reduce((acc, cur) => { + const value = acc.get(cur[key]) + + if (!value) { + acc.set(cur[key], [cur]) + } else { + acc.set(cur[key], [...value, cur]) + } + + return acc + }, new Map>()) +} + +export function isReactMouseEvent( + event: React.SyntheticEvent, +): event is React.SyntheticEvent { + return event.nativeEvent instanceof MouseEvent +} + +export function isReactKeyboardEvent( + event: React.SyntheticEvent, +): event is React.SyntheticEvent { + return event.nativeEvent instanceof KeyboardEvent +} + +export function numberToHex(number: number): string { + const hex = number.toString(16) + return hex.length === 1 ? `0${hex}` : hex +} + +export function bgrToHex(b: number, g: number, r: number) { + return `#${numberToHex(r)}${numberToHex(g)}${numberToHex(b)}` +} + +export function hexToBgr(hex: string): [number, number, number] | null { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) + return result + ? [ + parseInt(result[3], 16), + parseInt(result[2], 16), + parseInt(result[1], 16), + ] + : null +} + +export async function copyToClipboard(text: string) { + if (!navigator.clipboard) { + const textarea = document.createElement('textarea') + textarea.value = text + textarea.setAttribute('readonly', '') + textarea.style.position = 'fixed' + textarea.style.top = '0' + textarea.style.left = '0' + document.body.appendChild(textarea) + + textarea.focus() + textarea.select() + + try { + const success = document.execCommand('copy') + + if (!success) { + throw new Error('unable to copy') + } + } finally { + document.body.removeChild(textarea) + } + + return + } + + return await navigator.clipboard.writeText(text) +} + +const ipv4Regex = new RegExp( + '^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$', +) +export function isIPV4(ip: string) { + return ipv4Regex.test(ip) +} + +export function downloadFile(blob: Blob, filename: string) { + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + link.href = url + link.setAttribute('download', filename) + document.body.appendChild(link) + link.click() + document.body.removeChild(link) +} + +export function isNumberKeyEvent(event: KeyboardEvent): number | false { + const key = event.key + + const number = parseInt(key, 10) + if (number >= 0 && number <= 9) { + return number + } + + return false +} + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} diff --git a/src/hooks.ts b/src/hooks.ts index db5cb88..9bba485 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -12,7 +12,7 @@ import { useRef, useState, } from 'react' -import { getRandomId } from './utils' +import { getRandomId } from './helpers' export function usePointerPosition() { const [position, setPosition] = useState({ clientX: 0, clientY: 0 }) diff --git a/src/index.ts b/src/index.ts index 57be4e8..12e4768 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,6 @@ export type { } from './draw-zone' export { default as ErrorBoundary } from './error-boundary' -export * as helpers from './helpers' -export * as hooks from './hooks' -export * as utils from './utils' +export * from './helpers' +export * from './hooks' +export * from './utils' diff --git a/src/utils.ts b/src/utils.ts index 2d3555a..1b3100a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,128 +1,3 @@ export const isTouchDevice = global.window && ('ontouchstart' in global.window || navigator.maxTouchPoints > 0) - -export function getRandomId() { - const arr = new Uint32Array(1) - global.crypto.getRandomValues(arr) - return arr[0].toString(36) -} - -export function getRandomNumber(min: number, max: number) { - const arr = new Uint32Array(1) - global.crypto.getRandomValues(arr) - const random = arr[0] - const range = max - min - return Math.floor((random / (0xffffffff + 1)) * range + min) -} - -export function groupBy>( - arr: T[], - key: string | number, -) { - return arr.reduce((acc, cur) => { - const value = acc.get(cur[key]) - - if (!value) { - acc.set(cur[key], [cur]) - } else { - acc.set(cur[key], [...value, cur]) - } - - return acc - }, new Map>()) -} - -export function isReactMouseEvent( - event: React.SyntheticEvent, -): event is React.SyntheticEvent { - return event.nativeEvent instanceof MouseEvent -} - -export function isReactKeyboardEvent( - event: React.SyntheticEvent, -): event is React.SyntheticEvent { - return event.nativeEvent instanceof KeyboardEvent -} - -export function numberToHex(number: number): string { - const hex = number.toString(16) - return hex.length === 1 ? `0${hex}` : hex -} - -export function bgrToHex(b: number, g: number, r: number) { - return `#${numberToHex(r)}${numberToHex(g)}${numberToHex(b)}` -} - -export function hexToBgr(hex: string): [number, number, number] | null { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) - return result - ? [ - parseInt(result[3], 16), - parseInt(result[2], 16), - parseInt(result[1], 16), - ] - : null -} - -export async function copyToClipboard(text: string) { - if (!navigator.clipboard) { - const textarea = document.createElement('textarea') - textarea.value = text - textarea.setAttribute('readonly', '') - textarea.style.position = 'fixed' - textarea.style.top = '0' - textarea.style.left = '0' - document.body.appendChild(textarea) - - textarea.focus() - textarea.select() - - try { - const success = document.execCommand('copy') - - if (!success) { - throw new Error('unable to copy') - } - } finally { - document.body.removeChild(textarea) - } - - return - } - - return await navigator.clipboard.writeText(text) -} - -const ipv4Regex = new RegExp( - '^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$', -) -export function isIPV4(ip: string) { - return ipv4Regex.test(ip) -} - -export function downloadFile(blob: Blob, filename: string) { - const url = window.URL.createObjectURL(blob) - const link = document.createElement('a') - - link.href = url - link.setAttribute('download', filename) - document.body.appendChild(link) - link.click() - document.body.removeChild(link) -} - -export function isNumberKeyEvent(event: KeyboardEvent): number | false { - const key = event.key - - const number = parseInt(key, 10) - if (number >= 0 && number <= 9) { - return number - } - - return false -} - -export function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)) -} From 2c5703e4a9288f569e57d3dd1192fedcd508167f Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Tue, 26 Jul 2022 10:58:26 +0200 Subject: [PATCH 13/30] v0.0.27-beta6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16ed203..c07e82d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta5", + "version": "0.0.27-beta6", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ From 7b1fa3f3aa72274122ae9b92fced15cc0c1c7513 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Wed, 3 Aug 2022 16:31:13 +0200 Subject: [PATCH 14/30] v0.0.27-beta.7 - Fix typings --- package.json | 2 +- src/hooks.ts | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index c07e82d..40f4400 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta6", + "version": "0.0.27-beta.7", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/hooks.ts b/src/hooks.ts index 9bba485..255b954 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,4 +1,4 @@ -import { debounce, isEqual } from 'lodash' +import { DebouncedFunc, debounce, isEqual } from 'lodash' import { DependencyList, Dispatch, @@ -183,9 +183,7 @@ export function useLocalStorage( return [value, setValue] } -export function usePrevious( - value: T, -): MutableRefObject['current'] { +export function usePrevious(value: T): T | undefined { const ref = useRef() useEffect(() => { @@ -195,8 +193,13 @@ export function usePrevious( return ref.current } -export function useDebounce(delay: number) { - const [debouncedValue, setDebouncedValue] = useState() +export function useDebounce( + delay: number, + initialValue?: T, +): [T | undefined, DebouncedFunc<(value: T) => void>] { + const [debouncedValue, setDebouncedValue] = useState( + initialValue, + ) const debounceHandler = useMemo( () => debounce((value: T) => setDebouncedValue(value), delay), @@ -248,7 +251,9 @@ export function useUpdateEffect( }, deps) } -export function useSetState(initState: T) { +export function useSetState( + initState: T, +): [T, Dispatch>>] { const [state, setState] = useState(initState) const setMergeState = useCallback( From 398c1ca672717078c7b6dd1bd7239452e06acaed Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 12 Aug 2022 14:36:49 +0200 Subject: [PATCH 15/30] Update storybook --- package.json | 14 +- yarn.lock | 791 +++++++++++++++++++++++---------------------------- 2 files changed, 359 insertions(+), 446 deletions(-) diff --git a/package.json b/package.json index 40f4400..901d89a 100644 --- a/package.json +++ b/package.json @@ -39,12 +39,12 @@ "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-typescript": "^8.3.0", - "@storybook/addon-actions": "^6.5.9", - "@storybook/addon-essentials": "^6.5.9", - "@storybook/addon-links": "^6.5.9", - "@storybook/builder-webpack5": "^6.5.9", - "@storybook/manager-webpack5": "^6.5.9", - "@storybook/react": "^6.5.9", + "@storybook/addon-actions": "^6.5.10", + "@storybook/addon-essentials": "^6.5.10", + "@storybook/addon-links": "^6.5.10", + "@storybook/builder-webpack5": "^6.5.10", + "@storybook/manager-webpack5": "^6.5.10", + "@storybook/react": "^6.5.10", "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.2", @@ -53,7 +53,7 @@ "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.5.0", - "eslint-plugin-storybook": "^0.5.12", + "eslint-plugin-storybook": "^0.6.4", "eslint-plugin-unused-imports": "^2.0.0", "install-peers-cli": "^2.2.0", "prettier": "^2.6.2", diff --git a/yarn.lock b/yarn.lock index 65d2da6..24b80d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1915,7 +1915,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6": version "7.15.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== @@ -2336,18 +2336,18 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@storybook/addon-actions@6.5.9", "@storybook/addon-actions@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.9.tgz#d50d65631403e1a5b680961429d9c0d7bd383e68" - integrity sha512-wDYm3M1bN+zcYZV3Q24M03b/P8DDpvj1oSoY6VLlxDAi56h8qZB/voeIS2I6vWXOB79C5tbwljYNQO0GsufS0g== +"@storybook/addon-actions@6.5.10", "@storybook/addon-actions@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.10.tgz#83ec807a899e0412cf98037647f256c45cc32bf5" + integrity sha512-vpCnEu81fmtYzOf0QsRYoDuf9wXgVVl2VysE1dWRebRhIUDU0JurrthTnw322e38D4FzaoNGqZE7wnBYBohzZA== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.9" + "@storybook/theming" "6.5.10" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2361,18 +2361,18 @@ util-deprecate "^1.0.2" uuid-browser "^3.1.0" -"@storybook/addon-backgrounds@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.9.tgz#a9579fc9d73f783a768c6c6ceb97193c5a1ee708" - integrity sha512-9k+GiY5aiANLOct34ar29jqgdi5ZpCqpZ86zPH0GsEC6ifH6nzP4trLU0vFUe260XDCvB4g8YaI7JZKPhozERg== +"@storybook/addon-backgrounds@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.10.tgz#9ab2d2165fe35d265d9d6013fc174fa8528a272f" + integrity sha512-5uzQda3dh891h7BL8e9Ymk7BI+QgkkzDJXuA4mHjOXfIiD3S3efhJI8amXuBC2ZpIr6zmVit0MqZVyoVve46cQ== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.9" + "@storybook/theming" "6.5.10" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" @@ -2380,47 +2380,47 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-controls@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.9.tgz#8f6ef939c87b3dbad98f8bda7e124f0b34f668d2" - integrity sha512-VvjkgK32bGURKyWU2No6Q2B0RQZjLZk8D3neVNCnrWxwrl1G82StegxjRPn/UZm9+MZVPvTvI46nj1VdgOktnw== +"@storybook/addon-controls@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.10.tgz#275ddcd0f4dc1a107777b425417a8f252f52a91e" + integrity sha512-lC2y3XcolmQAJwFurIyGrynAHPWmfNtTCdu3rQBTVGwyxCoNwdOOeC2jV0BRqX2+CW6OHzJr9frNWXPSaZ8c4w== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-common" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-common" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/node-logger" "6.5.9" - "@storybook/store" "6.5.9" - "@storybook/theming" "6.5.9" + "@storybook/node-logger" "6.5.10" + "@storybook/store" "6.5.10" + "@storybook/theming" "6.5.10" core-js "^3.8.2" lodash "^4.17.21" ts-dedent "^2.0.0" -"@storybook/addon-docs@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.9.tgz#32b27fb298624afd738c1371a764d7ff4831fe6d" - integrity sha512-9lwOZyiOJFUgGd9ADVfcgpels5o0XOXqGMeVLuzT1160nopbZjNjo/3+YLJ0pyHRPpMJ4rmq2+vxRQR6PVRgPg== +"@storybook/addon-docs@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.10.tgz#dde18b5659e8033651e139a231a7f69306433b92" + integrity sha512-1kgjo3f0vL6GN8fTwLL05M/q/kDdzvuqwhxPY/v5hubFb3aQZGr2yk9pRBaLAbs4bez0yG0ASXcwhYnrEZUppg== dependencies: "@babel/plugin-transform-react-jsx" "^7.12.12" "@babel/preset-env" "^7.12.11" "@jest/transform" "^26.6.2" "@mdx-js/react" "^1.6.22" - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.9" + "@storybook/docs-tools" "6.5.10" "@storybook/mdx1-csf" "^0.0.1" - "@storybook/node-logger" "6.5.9" - "@storybook/postinstall" "6.5.9" - "@storybook/preview-web" "6.5.9" - "@storybook/source-loader" "6.5.9" - "@storybook/store" "6.5.9" - "@storybook/theming" "6.5.9" + "@storybook/node-logger" "6.5.10" + "@storybook/postinstall" "6.5.10" + "@storybook/preview-web" "6.5.10" + "@storybook/source-loader" "6.5.10" + "@storybook/store" "6.5.10" + "@storybook/theming" "6.5.10" babel-loader "^8.0.0" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -2432,37 +2432,37 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-essentials@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.9.tgz#32ba63acba4d153f4cf6ac33cbbf14b87d260788" - integrity sha512-V9ThjKQsde4A2Es20pLFBsn0MWx2KCJuoTcTsANP4JDcbvEmj8UjbDWbs8jAU+yzJT5r+CI6NoWmQudv12ZOgw== - dependencies: - "@storybook/addon-actions" "6.5.9" - "@storybook/addon-backgrounds" "6.5.9" - "@storybook/addon-controls" "6.5.9" - "@storybook/addon-docs" "6.5.9" - "@storybook/addon-measure" "6.5.9" - "@storybook/addon-outline" "6.5.9" - "@storybook/addon-toolbars" "6.5.9" - "@storybook/addon-viewport" "6.5.9" - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/node-logger" "6.5.9" +"@storybook/addon-essentials@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.10.tgz#d56f0f972e3bd5eae6c79b2126f510c5c020b62d" + integrity sha512-PT2aiR4vgAyB0pl3HNBUa4/a7NDRxASxAazz7zt9ZDirkipDKfxwdcLeRoJzwSngVDWEhuz5/paN5x4eNp4Hww== + dependencies: + "@storybook/addon-actions" "6.5.10" + "@storybook/addon-backgrounds" "6.5.10" + "@storybook/addon-controls" "6.5.10" + "@storybook/addon-docs" "6.5.10" + "@storybook/addon-measure" "6.5.10" + "@storybook/addon-outline" "6.5.10" + "@storybook/addon-toolbars" "6.5.10" + "@storybook/addon-viewport" "6.5.10" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/node-logger" "6.5.10" core-js "^3.8.2" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-links@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.5.9.tgz#91cbca0c044796badf2498723fdd10dacea5748b" - integrity sha512-4BYC7pkxL3NLRnEgTA9jpIkObQKril+XFj1WtmY/lngF90vvK0Kc/TtvTA2/5tSgrHfxEuPevIdxMIyLJ4ejWQ== +"@storybook/addon-links@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.5.10.tgz#f66568fbc84b942032ac2de85f799d69fcf77922" + integrity sha512-r3WzYIPz7WjHiaPObC2Tg6bHuZRBb/Kt/X+Eitw+jTqBel7ksvkO36tn81q8Eyj61qIdNQmUWAaX/0aewT0kLA== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.9" + "@storybook/router" "6.5.10" "@types/qs" "^6.9.5" core-js "^3.8.2" global "^4.4.0" @@ -2471,95 +2471,95 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-measure@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.9.tgz#f949d4f5f4025c839634114365f1399ea04bd0ae" - integrity sha512-0aA22wD0CIEUccsEbWkckCOXOwr4VffofMH1ToVCOeqBoyLOMB0gxFubESeprqM54CWsYh2DN1uujgD6508cwA== +"@storybook/addon-measure@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.10.tgz#afac72a15d927f9f2119e2218017d757a8c8c6a4" + integrity sha512-ss7L1H5K5hXygDIoVwj+QyVXbve5V67x7CofLiLCgQYuJzfO16+sPGjiTGWMpTb4ijox2uKWnTkpilt5bCjXgw== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" -"@storybook/addon-outline@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.9.tgz#6ce9b3fb77e6a1a59607d7657c359c69f26cf6dd" - integrity sha512-oJ1DK3BDJr6aTlZc9axfOxV1oDkZO7hOohgUQDaKO1RZrSpyQsx2ViK2X6p/W7JhFJHKh7rv+nGCaVlLz3YIZA== +"@storybook/addon-outline@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.10.tgz#a49164697344de1bd11d35a5ce21e59afc0dd19c" + integrity sha512-AjdaeQ+/iBKmGrAqRW4niwMB6AkgGnYmSzVs5Cf6F/Sb4Dp+vzgLNOwLABD9qs8Ri8dvHl5J4QpVwQKUhYZaOQ== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-toolbars@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.9.tgz#feedfdac08482d43bb1f3cc00840d80322c5eace" - integrity sha512-6JFQNHYVZUwp17p5rppc+iQJ2QOIWPTF+ni1GMMThjc84mzXs2+899Sf1aPFTvrFJTklmT+bPX6x4aUTouVa1w== +"@storybook/addon-toolbars@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.10.tgz#750e6c7fa50a54dac7fe5df7b7c239fb02a4456c" + integrity sha512-S0Ljc6Wv+bPbx2e0iTveJ6bBDqjsemu+FZD4qDLsHreoI7DAcqyrF5Def1l8xNohixIVpx8dQpYXRtyzNlXekg== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/theming" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/theming" "6.5.10" core-js "^3.8.2" regenerator-runtime "^0.13.7" -"@storybook/addon-viewport@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.9.tgz#fc390ccebea56d2e874ed2fda085c09fe04dd240" - integrity sha512-thKS+iw6M7ueDQQ7M66STZ5rgtJKliAcIX6UCopo0Ffh4RaRYmX6MCjqtvBKk8joyXUvm9SpWQemJD9uBQrjgw== - dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" - "@storybook/theming" "6.5.9" +"@storybook/addon-viewport@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.10.tgz#4c6151d7e8177b07df8dcb4c61e842dac949215b" + integrity sha512-RFMd+4kZljyuJjR9OJ2bFXHrSG7VTi5FDZYWEU+4W1sBxzC+JhnVnUP+HJH3gUxEFIRQC5neRzwWRE9RUUoALQ== + dependencies: + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" + "@storybook/theming" "6.5.10" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" prop-types "^15.7.2" regenerator-runtime "^0.13.7" -"@storybook/addons@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.9.tgz#5a9d7395c579a9cbc44dfc122362fb3c95dfb9d5" - integrity sha512-adwdiXg+mntfPocLc1KXjZXyLgGk7Aac699Fwe+OUYPEC5tW347Rm/kFatcE556d42o5czcRiq3ZSIGWnm9ieQ== +"@storybook/addons@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.10.tgz#bff2f8fb8453e9df04fa6dbc41341fd05f4cdeba" + integrity sha512-VD4tBCQ23PkSeDoxuHcKy0RfhIs3oMYjBacOZx7d0bvOzK9WjPyvE2ysDAh7r/ceqnwmWHAScIpE+I1RU7gl+g== dependencies: - "@storybook/api" "6.5.9" - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/api" "6.5.10" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.9" - "@storybook/theming" "6.5.9" + "@storybook/router" "6.5.10" + "@storybook/theming" "6.5.10" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9" - integrity sha512-9ylztnty4Y+ALU/ehW3BML9czjCAFsWvrwuCi6UgcwNjswwjSX3VRLhfD1KT3pl16ho//95LgZ0LnSwROCcPOA== +"@storybook/api@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.10.tgz#215623844648f0da2ac646fdcdd1345c2e1a8490" + integrity sha512-AkmgSPNEGdKp4oZA4KQ+RJsacw7GwfvjsVDnCkcXqS9zmSr/RNL0fhpcd60KKkmx/hGKPTDFpK3ZayxDrJ/h4A== dependencies: - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.9" + "@storybook/router" "6.5.10" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.9" + "@storybook/theming" "6.5.10" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2571,28 +2571,28 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/builder-webpack4@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.9.tgz#4b37e1fa23a25aa4bfeaba640e5d318fcd511f95" - integrity sha512-YOeA4++9uRZ8Hog1wC60yjaxBOiI1FRQNtax7b9E7g+kP8UlSCPCGcv4gls9hFmzbzTOPfQTWnToA9Oa6jzRVw== +"@storybook/builder-webpack4@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.10.tgz#79e95323577a37349ab3c81193fa249ac5c50173" + integrity sha512-AoKjsCNoQQoZXYwBDxO8s+yVEd5FjBJAaysEuUTHq2fb81jwLrGcEOo6hjw4jqfugZQIzYUEjPazlvubS78zpw== dependencies: "@babel/core" "^7.12.10" - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/channel-postmessage" "6.5.9" - "@storybook/channels" "6.5.9" - "@storybook/client-api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/core-events" "6.5.9" - "@storybook/node-logger" "6.5.9" - "@storybook/preview-web" "6.5.9" - "@storybook/router" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/channel-postmessage" "6.5.10" + "@storybook/channels" "6.5.10" + "@storybook/client-api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/core-events" "6.5.10" + "@storybook/node-logger" "6.5.10" + "@storybook/preview-web" "6.5.10" + "@storybook/router" "6.5.10" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.9" - "@storybook/theming" "6.5.9" - "@storybook/ui" "6.5.9" + "@storybook/store" "6.5.10" + "@storybook/theming" "6.5.10" + "@storybook/ui" "6.5.10" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" autoprefixer "^9.8.6" @@ -2624,27 +2624,27 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.2.2" -"@storybook/builder-webpack5@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.9.tgz#30b4e08622daff104bcccd015d3ee7902f99dd99" - integrity sha512-NUVZ4Qci6HWPuoH8U/zQkdBO5soGgu7QYrGC/LWU0tRfmmZxkjr7IUU14ppDpGPYgx3r7jkaQI1J/E1YEmSCWQ== +"@storybook/builder-webpack5@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.10.tgz#de78a8a262410bbe65eecc62f2beaed1fe44dd5d" + integrity sha512-Hcsm/TzGRXHndgQCftt+pzI7GQJRqAv8A8ie5b3aFcodhJfK0qzZsQD4Y4ZWxXh1I/xe5t74Kl2qUJ40PX+geA== dependencies: "@babel/core" "^7.12.10" - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/channel-postmessage" "6.5.9" - "@storybook/channels" "6.5.9" - "@storybook/client-api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/core-events" "6.5.9" - "@storybook/node-logger" "6.5.9" - "@storybook/preview-web" "6.5.9" - "@storybook/router" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/channel-postmessage" "6.5.10" + "@storybook/channels" "6.5.10" + "@storybook/client-api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/core-events" "6.5.10" + "@storybook/node-logger" "6.5.10" + "@storybook/preview-web" "6.5.10" + "@storybook/router" "6.5.10" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.9" - "@storybook/theming" "6.5.9" + "@storybook/store" "6.5.10" + "@storybook/theming" "6.5.10" "@types/node" "^14.0.10 || ^16.0.0" babel-loader "^8.0.0" babel-plugin-named-exports-order "^0.0.2" @@ -2668,51 +2668,51 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.4.1" -"@storybook/channel-postmessage@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.9.tgz#9cf4530f0364cee0d5e58f92d6fb5ce98e10257b" - integrity sha512-pX/0R8UW7ezBhCrafRaL20OvMRcmESYvQQCDgjqSzJyHkcG51GOhsd6Ge93eJ6QvRMm9+w0Zs93N2VKjVtz0Qw== +"@storybook/channel-postmessage@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.10.tgz#be8971b4b7f91b664bb2c6965fdfb073d541a03e" + integrity sha512-t9PTA0UzFvYa3IlOfpBOolfrRMPTjUMIeCQ6FNyM0aj5GqLKSvoQzP8NeoRpIrvyf6ljFKKdaMaZ3fiCvh45ag== dependencies: - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" core-js "^3.8.2" global "^4.4.0" qs "^6.10.0" telejson "^6.0.8" -"@storybook/channel-websocket@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.9.tgz#6b7a0127fec58ee5be4f6aebcf460adc564f2f34" - integrity sha512-xtHvSNwuOhkgALwVshKWsoFhDmuvcosdYfxcfFGEiYKXIu46tRS5ZXmpmgEC/0JAVkVoFj5nL8bV7IY5np6oaA== +"@storybook/channel-websocket@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.10.tgz#bd1316a9b555229b215e5054a76b57c503dd8adc" + integrity sha512-RTXMZbMWCS3xU+4GVIdfnUXsKcwg/WTozy88/5OxaKjGw6KgRedqLAQJKJ6Y5XlnwIcWelirkHj/COwTTXhbPg== dependencies: - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" core-js "^3.8.2" global "^4.4.0" telejson "^6.0.8" -"@storybook/channels@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79" - integrity sha512-FvGA35nV38UPXWOl9ERapFTJaxwSTamQ339s2Ev7E9riyRG+GRkgTWzf5kECJgS1PAYKd/7m/RqKJT9BVv6A5g== +"@storybook/channels@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.10.tgz#fca5b0d1ea8d30b022e805301ed436407c867ac4" + integrity sha512-lo26YZ6kWpHXLhuHJF4P/bICY7jD/rXEZqReKtGOSk1Lv99/xvG6pqmcy3hWLf3v3Dy/8otjRPSR7izFVIIZgQ== dependencies: core-js "^3.8.2" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.9.tgz#3e4a8ec1d277fd81325c5d959c553161a85fa182" - integrity sha512-pc7JKJoWLesixUKvG2nV36HukUuYoGRyAgD3PpIV7qSBS4JixqZ3VAHFUtqV1UzfOSQTovLSl4a0rIRnpie6gA== +"@storybook/client-api@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.10.tgz#0bc3f68ce014ce1ffd560472a893ba04be370f09" + integrity sha512-3wBWZl3NvMFgMovgEh+euiARAT2FXzpvTF4Q1gerGMNNDlrGxHnFvSuy4FHg/irtOGLa4yLz43ULFbYtpKw0Lg== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/channel-postmessage" "6.5.9" - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/channel-postmessage" "6.5.10" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.9" + "@storybook/store" "6.5.10" "@types/qs" "^6.9.5" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" @@ -2727,45 +2727,43 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014" - integrity sha512-DOHL6p0uiDd3gV/Sb2FR+Vh6OiPrrf8BrA06uvXWsMRIIvEEvnparxv9EvPg7FlmUX0T3nq7d3juwjx4F8Wbcg== +"@storybook/client-logger@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.10.tgz#cfea823a5b8444409daa74f854c5d05367986b34" + integrity sha512-/xA0MHOevXev68hyLMQw8Qo8KczSIdXOxliAgrycMTkDmw5eKeA8TP7B8zP3wGuq/e3MrdD9/8MWhb/IQBNC3w== dependencies: core-js "^3.8.2" global "^4.4.0" -"@storybook/components@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.9.tgz#97e07ffe11ab76c01ccee380888991bd161f75b2" - integrity sha512-BhfX980O9zn/1J4FNMeDo8ZvL1m5Ml3T4HRpfYmEBnf8oW5b5BeF6S2K2cwFStZRjWqm1feUcwNpZxCBVMkQnQ== +"@storybook/components@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.10.tgz#268e1269bc3d262f7dcec13f96c3b844919687b8" + integrity sha512-9OhgB8YQfGwOKjo/N96N5mrtJ6qDVVoEM1zuhea32tJUd2eYf0aSWpryA9VnOM0V1q/8DAoCg5rPBMYWMBU5uw== dependencies: - "@storybook/client-logger" "6.5.9" + "@storybook/client-logger" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.9" - "@types/react-syntax-highlighter" "11.0.5" + "@storybook/theming" "6.5.10" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" - react-syntax-highlighter "^15.4.5" regenerator-runtime "^0.13.7" util-deprecate "^1.0.2" -"@storybook/core-client@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.9.tgz#ea6035d1c90d2c68e860e3cf629979491856cd88" - integrity sha512-LY0QbhShowO+PQx3gao3wdVjpKMH1AaSLmuI95FrcjoMmSXGf96jVLKQp9mJRGeHIsAa93EQBYuCihZycM3Kbg== - dependencies: - "@storybook/addons" "6.5.9" - "@storybook/channel-postmessage" "6.5.9" - "@storybook/channel-websocket" "6.5.9" - "@storybook/client-api" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" +"@storybook/core-client@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.10.tgz#90c86923236c8efff33d454a0dc552f6df4346b1" + integrity sha512-THsIjNrOrampTl0Lgfjvfjk1JnktKb4CQLOM80KpQb4cjDqorBjJmErzUkUQ2y3fXvrDmQ/kUREkShET4XEdtA== + dependencies: + "@storybook/addons" "6.5.10" + "@storybook/channel-postmessage" "6.5.10" + "@storybook/channel-websocket" "6.5.10" + "@storybook/client-api" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/preview-web" "6.5.9" - "@storybook/store" "6.5.9" - "@storybook/ui" "6.5.9" + "@storybook/preview-web" "6.5.10" + "@storybook/store" "6.5.10" + "@storybook/ui" "6.5.10" airbnb-js-shims "^2.2.1" ansi-to-html "^0.6.11" core-js "^3.8.2" @@ -2777,10 +2775,10 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/core-common@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.9.tgz#7ca8258ea2634b1d64695c1e4262f71cc7457989" - integrity sha512-NxOK0mrOCo0TWZ7Npc5HU66EKoRHlrtg18/ZixblLDWQMIqY9XCck8K1kJ8QYpYCHla+aHIsYUArFe2vhlEfZA== +"@storybook/core-common@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.10.tgz#6b93449548b0890f5c68d89f0ca78e092026182c" + integrity sha512-Bx+VKkfWdrAmD8T51Sjq/mMhRaiapBHcpG4cU5bc3DMbg+LF2/yrgqv/cjVu+m5gHAzYCac5D7gqzBgvG7Myww== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -2804,7 +2802,7 @@ "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" "@babel/register" "^7.12.1" - "@storybook/node-logger" "6.5.9" + "@storybook/node-logger" "6.5.10" "@storybook/semver" "^7.3.2" "@types/node" "^14.0.10 || ^16.0.0" "@types/pretty-hrtime" "^1.0.0" @@ -2833,30 +2831,30 @@ util-deprecate "^1.0.2" webpack "4" -"@storybook/core-events@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637" - integrity sha512-tXt7a3ZvJOCeEKpNa/B5rQM5VI7UJLlOh3IHOImWn4HqoBRrZvbourmac+PRZAtXpos0h3c6554Hjapj/Sny5Q== +"@storybook/core-events@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.10.tgz#66d87c8ea18db8e448018a16a3d0198ddbcbc683" + integrity sha512-EVb1gO1172klVIAABLOoigFMx0V88uctY0K/qVCO8n6v+wd2+0Ccn63kl+gTxsAC3WZ8XhXh9q2w5ImHklVECw== dependencies: core-js "^3.8.2" -"@storybook/core-server@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.9.tgz#749a881c1a81d7cf1a69f3782c06a7f0c39a505c" - integrity sha512-YeePGUrd5fQPvGzMhowh124KrcZURFpFXg1VB0Op3ESqCIsInoMZeObci4Gc+binMXC7vcv7aw3EwSLU37qJzQ== +"@storybook/core-server@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.10.tgz#ada3d647833c02cb8c742281c1f314ff866f96f8" + integrity sha512-jqwpA0ccA8X5ck4esWBid04+cEIVqirdAcqJeNb9IZAD+bRreO4Im8ilzr7jc5AmQ9fkqHs2NByFKh9TITp8NQ== dependencies: "@discoveryjs/json-ext" "^0.5.3" - "@storybook/builder-webpack4" "6.5.9" - "@storybook/core-client" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/builder-webpack4" "6.5.10" + "@storybook/core-client" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/csf-tools" "6.5.9" - "@storybook/manager-webpack4" "6.5.9" - "@storybook/node-logger" "6.5.9" + "@storybook/csf-tools" "6.5.10" + "@storybook/manager-webpack4" "6.5.10" + "@storybook/node-logger" "6.5.10" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.9" - "@storybook/telemetry" "6.5.9" + "@storybook/store" "6.5.10" + "@storybook/telemetry" "6.5.10" "@types/node" "^14.0.10 || ^16.0.0" "@types/node-fetch" "^2.5.7" "@types/pretty-hrtime" "^1.0.0" @@ -2891,18 +2889,18 @@ ws "^8.2.3" x-default-browser "^0.4.0" -"@storybook/core@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.9.tgz#da4f237391d99aed1228323f24b335cafbdf3499" - integrity sha512-Mt3TTQnjQt2/pa60A+bqDsAOrYpohapdtt4DDZEbS8h0V6u11KyYYh3w7FCySlL+sPEyogj63l5Ec76Jah3l2w== +"@storybook/core@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.10.tgz#15ec8be85943251e25c2c24e80e20dcacc4fed65" + integrity sha512-K86yYa0tYlMxADlwQTculYvPROokQau09SCVqpsLg3wJCTvYFL4+SIqcYoyBSbFmHOdnYbJgPydjN33MYLiOZQ== dependencies: - "@storybook/core-client" "6.5.9" - "@storybook/core-server" "6.5.9" + "@storybook/core-client" "6.5.10" + "@storybook/core-server" "6.5.10" -"@storybook/csf-tools@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.9.tgz#8e01df2305b53e228229f0b45ada3720e6e42a1c" - integrity sha512-RAdhsO2XmEDyWy0qNQvdKMLeIZAuyfD+tYlUwBHRU6DbByDucvwgMOGy5dF97YNJFmyo93EUYJzXjUrJs3U1LQ== +"@storybook/csf-tools@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.10.tgz#ae6f1ebd4951e8978c8fe3e08ddd2bd269bf922b" + integrity sha512-H77kZQEisu7+skzeIbNZwmE09OqLjwJTeFhLN1pcjxKVa30LEI3pBHcNBxVKqgxl+Yg3KkB7W/ArLO2N+i2ohw== dependencies: "@babel/core" "^7.12.10" "@babel/generator" "^7.12.11" @@ -2933,33 +2931,33 @@ dependencies: lodash "^4.17.15" -"@storybook/docs-tools@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.9.tgz#5ff304f881e972ce14923a5ffcfed3f052094889" - integrity sha512-UoTaXLvec8x+q+4oYIk/t8DBju9C3ZTGklqOxDIt+0kS3TFAqEgI3JhKXqQOXgN5zDcvLVSxi8dbVAeSxk2ktA== +"@storybook/docs-tools@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.10.tgz#30baa62c1ca3a18b13625b6b305e23e39f404416" + integrity sha512-/bvYgOO+CxMEcHifkjJg0A60OTGOhcjGxnsB1h0gJuxMrqA/7Qwc108bFmPiX0eiD1BovFkZLJV4O6OY7zP5Vw== dependencies: "@babel/core" "^7.12.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.9" + "@storybook/store" "6.5.10" core-js "^3.8.2" doctrine "^3.0.0" lodash "^4.17.21" regenerator-runtime "^0.13.7" -"@storybook/manager-webpack4@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.9.tgz#c75d2cced4550c8a786f00b0e57b203d613e706c" - integrity sha512-49LZlHqWc7zj9tQfOOANixPYmLxqWTTZceA6DSXnKd9xDiO2Gl23Y+l/CSPXNZGDB8QFAwpimwqyKJj/NLH45A== +"@storybook/manager-webpack4@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.10.tgz#41bae252b863484f293954ef2d2dc80bf3e028f1" + integrity sha512-N/TlNDhuhARuFipR/ZJ/xEVESz23iIbCsZ4VNehLHm8PpiGlQUehk+jMjWmz5XV0bJItwjRclY+CU3GjZKblfQ== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.5.9" - "@storybook/core-client" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/node-logger" "6.5.9" - "@storybook/theming" "6.5.9" - "@storybook/ui" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/core-client" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/node-logger" "6.5.10" + "@storybook/theming" "6.5.10" + "@storybook/ui" "6.5.10" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" babel-loader "^8.0.0" @@ -2987,20 +2985,20 @@ webpack-dev-middleware "^3.7.3" webpack-virtual-modules "^0.2.2" -"@storybook/manager-webpack5@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.9.tgz#ce9dd6ea6298ab426b111f170c23deea7085ba08" - integrity sha512-J1GamphSsaZLNBEhn1awgxzOS8KfvzrHtVlAm2VHwW7j1E1DItROFJhGCgduYYuBiN9eqm+KIYrxcr6cRuoolQ== +"@storybook/manager-webpack5@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.10.tgz#dff8e53615906284b68df013935e8a6234ddaafe" + integrity sha512-uRo+6e5MiVOtyFVMYIKVqvpDveCjHyzXBfetSYR7rKEZoaDMEnLLiuF7DIH12lzxwmzCJ1gIc4lf5HFiTMNkgw== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.5.9" - "@storybook/core-client" "6.5.9" - "@storybook/core-common" "6.5.9" - "@storybook/node-logger" "6.5.9" - "@storybook/theming" "6.5.9" - "@storybook/ui" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/core-client" "6.5.10" + "@storybook/core-common" "6.5.10" + "@storybook/node-logger" "6.5.10" + "@storybook/theming" "6.5.10" + "@storybook/ui" "6.5.10" "@types/node" "^14.0.10 || ^16.0.0" babel-loader "^8.0.0" case-sensitive-paths-webpack-plugin "^2.3.0" @@ -3042,10 +3040,10 @@ prettier ">=2.2.1 <=2.3.0" ts-dedent "^2.0.0" -"@storybook/node-logger@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" - integrity sha512-nZZNZG2Wtwv6Trxi3FrnIqUmB55xO+X/WQGPT5iKlqNjdRIu/T72mE7addcp4rbuWCQfZUhcDDGpBOwKtBxaGg== +"@storybook/node-logger@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.10.tgz#bce4c04009c4b62d6d2fb617176d7ef0084e9e89" + integrity sha512-bYswXIKV7Stru8vYfkjUMNN8UhF7Qg7NRsUvG5Djt5lLIae1XmUIgnH40mU/nW4X4BSfcR9MKxsSsngvn2WmQg== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.1.0" @@ -3053,24 +3051,24 @@ npmlog "^5.0.1" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.9.tgz#a5a2565808e9d7bc310e78c279b09ce337fe3457" - integrity sha512-KQBupK+FMRrtSt8IL0MzCZ/w9qbd25Yxxp/+ajfWgZTRgsWgVFOqcDyMhS16eNbBp5qKIBCBDXfEF+/mK8HwQQ== +"@storybook/postinstall@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.10.tgz#b25378da036bce7b318c6732733aa5ad43449f37" + integrity sha512-xqUdpnFHYkn8MgtV+QztvIsRWa6jQUk7QT1Mu17Y0S7PbslNGsuskRPHenHhACXBJF+TM86R+4BaAhnVYTmElw== dependencies: core-js "^3.8.2" -"@storybook/preview-web@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.9.tgz#557d919e6df50d66259521aa36ebf4055bbd236e" - integrity sha512-4eMrO2HJyZUYyL/j+gUaDvry6iGedshwT5MQqe7J9FaA+Q2pNARQRB1X53f410w7S4sObRmYIAIluWPYdWym9w== +"@storybook/preview-web@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.10.tgz#81bf5d3f5fca9e26099c057206bd8e684225989b" + integrity sha512-sTC/o5gkvALOtcNgtApGKGN9EavvSxRHBeBh+5BQjV2qQ8ap+26RsfUizNBECAa2Jrn4osaDYn9HRhJLFL69WA== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/channel-postmessage" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/channel-postmessage" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.9" + "@storybook/store" "6.5.10" ansi-to-html "^0.6.11" core-js "^3.8.2" global "^4.4.0" @@ -3095,24 +3093,24 @@ react-docgen-typescript "^2.1.1" tslib "^2.0.0" -"@storybook/react@^6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.9.tgz#687ec1f6b785822a392b7ac115b61800f69fb7cd" - integrity sha512-Rp+QaTQAzxJhwuzJXVd49mnIBLQRlF8llTxPT2YoGHdrGkku/zl/HblQ6H2yzEf15367VyzaAv/BpLsO9Jlfxg== +"@storybook/react@^6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.10.tgz#6e9f5cf5e4c81d966774c08c87fb2414052db454" + integrity sha512-m8S1qQrwA7pDGwdKEvL6LV3YKvSzVUY297Fq+xcTU3irnAy4sHDuFoLqV6Mi1510mErK1r8+rf+0R5rEXB219g== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.10" "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" - "@storybook/addons" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core" "6.5.9" - "@storybook/core-common" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core" "6.5.10" + "@storybook/core-common" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.9" - "@storybook/node-logger" "6.5.9" + "@storybook/docs-tools" "6.5.10" + "@storybook/node-logger" "6.5.10" "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.9" + "@storybook/store" "6.5.10" "@types/estree" "^0.0.51" "@types/node" "^14.14.20 || ^16.0.0" "@types/webpack-env" "^1.16.0" @@ -3136,12 +3134,12 @@ util-deprecate "^1.0.2" webpack ">=4.43.0 <6.0.0" -"@storybook/router@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8" - integrity sha512-G2Xp/2r8vU2O34eelE+G5VbEEVFDeHcCURrVJEROh6dq2asFJAPbzslVXSeCqgOTNLSpRDJ2NcN5BckkNqmqJg== +"@storybook/router@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.10.tgz#b0c342e080c1d2b5344603bc43a6c75734a4a879" + integrity sha512-O+vNW/eEpYFF8eCg5jZjNQ6q2DKQVxqDRPCy9pJdEbvavMDZn6AFYgVK+VJe5F4211WW2yncOu922xObCxXJYg== dependencies: - "@storybook/client-logger" "6.5.9" + "@storybook/client-logger" "6.5.10" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" @@ -3155,13 +3153,13 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.9.tgz#7b6f065c6a6108c4b4ca7e45bfd78707373d84ac" - integrity sha512-H03nFKaP6borfWMTTa9igBA+Jm2ph+FoVJImWC/X+LAmLSJYYSXuqSgmiZ/DZvbjxS4k8vccE2HXogne1IvaRA== +"@storybook/source-loader@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.10.tgz#f62b4c7b1933976a20913ddc149d55026ef4c872" + integrity sha512-1RxxRumpjs8VUUwES9LId+cuNQnixhZAcwCxd6jaKkTZbjiQCtAhXX6DBTjJGV1u/JnCsqEp5b1wB8j/EioNHw== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/client-logger" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/client-logger" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" estraverse "^5.2.0" @@ -3171,14 +3169,14 @@ prettier ">=2.2.1 <=2.3.0" regenerator-runtime "^0.13.7" -"@storybook/store@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.9.tgz#dc9963fc013636569082bd8f7200804866373735" - integrity sha512-80pcDTcCwK6wUA63aWOp13urI77jfipIVee9mpVvbNyfrNN8kGv1BS0z/JHDxuV6rC4g7LG1fb+BurR0yki7BA== +"@storybook/store@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.10.tgz#85df17a8d57af0cba3934b3c6046537e2bca9abd" + integrity sha512-RswrSYh2IiKkytFPxP9AvP+hekjrvHK2ILvyDk2ZgduCN4n5ivsekOb+N3M2t+dq1eLuW9or5n2T4OWwAwjxxQ== dependencies: - "@storybook/addons" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/core-events" "6.5.9" + "@storybook/addons" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/core-events" "6.5.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -3192,13 +3190,13 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/telemetry@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.9.tgz#8e1e0d4a89fc2387620045e5ea96c109d16a7247" - integrity sha512-JluoHCRhHAr4X0eUNVBSBi1JIBA92404Tu1TPdbN7x6gCZxHXXPTSUTAnspXp/21cTdMhY2x+kfZQ8fmlGK4MQ== +"@storybook/telemetry@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.10.tgz#742b05a55dfe8470ce4cb371f3f3f2c02f96e816" + integrity sha512-+M5HILDFS8nDumLxeSeAwi1MTzIuV6UWzV4yB2wcsEXOBTdplcl9oYqFKtlst78oOIdGtpPYxYfivDlqxC2K4g== dependencies: - "@storybook/client-logger" "6.5.9" - "@storybook/core-common" "6.5.9" + "@storybook/client-logger" "6.5.10" + "@storybook/core-common" "6.5.10" chalk "^4.1.0" core-js "^3.8.2" detect-package-manager "^2.0.1" @@ -3210,30 +3208,30 @@ read-pkg-up "^7.0.1" regenerator-runtime "^0.13.7" -"@storybook/theming@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa" - integrity sha512-KM0AMP5jMQPAdaO8tlbFCYqx9uYM/hZXGSVUhznhLYu7bhNAIK7ZVmXxyE/z/khM++8eUHzRoZGiO/cwCkg9Xw== +"@storybook/theming@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.10.tgz#052100979c1270fc8f60653c1a13a6f047318109" + integrity sha512-BvTQBBcSEwKKcsVmF+Ol6v0RIQUr+bxP7gb10wtfBd23mZTEFA0C1N5FnZr/dDeiBKG1pvf1UKvoYA731y0BsA== dependencies: - "@storybook/client-logger" "6.5.9" + "@storybook/client-logger" "6.5.10" core-js "^3.8.2" memoizerific "^1.11.3" regenerator-runtime "^0.13.7" -"@storybook/ui@6.5.9": - version "6.5.9" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.9.tgz#41e59279323cccc0d613974ec9782d797220c8a7" - integrity sha512-ryuPxJgtbb0gPXKGgGAUC+Z185xGAd1IvQ0jM5fJ0SisHXI8jteG3RaWhntOehi9qCg+64Vv6eH/cj9QYNHt1Q== - dependencies: - "@storybook/addons" "6.5.9" - "@storybook/api" "6.5.9" - "@storybook/channels" "6.5.9" - "@storybook/client-logger" "6.5.9" - "@storybook/components" "6.5.9" - "@storybook/core-events" "6.5.9" - "@storybook/router" "6.5.9" +"@storybook/ui@6.5.10": + version "6.5.10" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.10.tgz#f56095a1a39ae5a203f2ac7f3dba86341a5927d5" + integrity sha512-6iaoaRAiTqB1inTw35vao+5hjcDE0Qa0A3a9ZIeNa6yHvpB1k0lO/N/0PMrRdVvySYpXVD1iry4z4QYdo1rU+w== + dependencies: + "@storybook/addons" "6.5.10" + "@storybook/api" "6.5.10" + "@storybook/channels" "6.5.10" + "@storybook/client-logger" "6.5.10" + "@storybook/components" "6.5.10" + "@storybook/core-events" "6.5.10" + "@storybook/router" "6.5.10" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.9" + "@storybook/theming" "6.5.10" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" @@ -3402,32 +3400,11 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== -"@types/prop-types@*": - version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" - integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== - "@types/qs@^6.9.5": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/react-syntax-highlighter@11.0.5": - version "11.0.5" - resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" - integrity sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg== - dependencies: - "@types/react" "*" - -"@types/react@*": - version "17.0.19" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.19.tgz#8f2a85e8180a43b57966b237d26a29481dacc991" - integrity sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -3435,11 +3412,6 @@ dependencies: "@types/node" "*" -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -5427,11 +5399,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -csstype@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" - integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -6008,14 +5975,15 @@ eslint-plugin-react@^7.29.4: semver "^6.3.0" string.prototype.matchall "^4.0.7" -eslint-plugin-storybook@^0.5.12: - version "0.5.13" - resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.13.tgz#dee4056eaa59c886a9ae78fb7b2ee0ed5fad4366" - integrity sha512-82x3FH1VAi68Awu1VEjn/hLkzFZsOP8ItcC5/uGF9WszIrj6n7Q3MZD57oE26k3aKwuPfFtAPnSjFnaBkBab+g== +eslint-plugin-storybook@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.4.tgz#4b4f077fcc0cf3b3f4ebc730426497e814e38e49" + integrity sha512-wxwbAZqlgjj6MbS/llY7wnXCCUsuFcLB1XnahbgBtjmsFUggpiECt01Dt8huaKvriVXg2w4d7Ye+GzA00rdcKg== dependencies: "@storybook/csf" "^0.0.1" "@typescript-eslint/experimental-utils" "^5.3.0" requireindex "^1.1.0" + ts-dedent "^2.2.0" eslint-plugin-unused-imports@^2.0.0: version "2.0.0" @@ -6375,13 +6343,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fault@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" - integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== - dependencies: - format "^0.2.0" - fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -6582,11 +6543,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -format@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" - integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -7111,11 +7067,6 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -highlight.js@^10.4.1, highlight.js@~10.7.0: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -8216,14 +8167,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowlight@^1.17.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" - integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== - dependencies: - fault "^1.0.0" - highlight.js "~10.7.0" - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -9607,16 +9550,6 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= -prismjs@^1.27.0: - version "1.28.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6" - integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw== - -prismjs@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" - integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9889,17 +9822,6 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-syntax-highlighter@^15.4.5: - version "15.5.0" - resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20" - integrity sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg== - dependencies: - "@babel/runtime" "^7.3.1" - highlight.js "^10.4.1" - lowlight "^1.17.0" - prismjs "^1.27.0" - refractor "^3.6.0" - react@^17: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" @@ -9990,15 +9912,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -refractor@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" - integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== - dependencies: - hastscript "^6.0.0" - parse-entities "^2.0.0" - prismjs "~1.27.0" - regenerate-unicode-properties@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" @@ -11270,7 +11183,7 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -ts-dedent@^2.0.0: +ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== From 7b738f96ac6d9976ea8648707313ba924713e575 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Wed, 24 Aug 2022 16:49:11 +0200 Subject: [PATCH 16/30] WIP rework drawzone --- package.json | 8 +- src/DrawZone2/components.tsx | 645 +++++++++++++++++ src/DrawZone2/constants.ts | 2 + src/DrawZone2/hooks.ts | 1152 +++++++++++++++++++++++++++++++ src/DrawZone2/index.stories.tsx | 171 +++++ src/DrawZone2/index.tsx | 26 + src/DrawZone2/state.ts | 37 + src/DrawZone2/types.ts | 62 ++ src/draw-zone/components.tsx | 15 +- src/draw-zone/hooks.ts | 85 ++- src/draw-zone/index.stories.tsx | 190 ++++- src/helpers.ts | 4 +- src/hooks.ts | 8 +- src/index.ts | 1 + src/types.ts | 1 + yarn.lock | 31 + 16 files changed, 2377 insertions(+), 61 deletions(-) create mode 100644 src/DrawZone2/components.tsx create mode 100644 src/DrawZone2/constants.ts create mode 100644 src/DrawZone2/hooks.ts create mode 100644 src/DrawZone2/index.stories.tsx create mode 100644 src/DrawZone2/index.tsx create mode 100644 src/DrawZone2/state.ts create mode 100644 src/DrawZone2/types.ts create mode 100644 src/types.ts diff --git a/package.json b/package.json index 901d89a..1eeabda 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.7", + "version": "0.0.27-beta.8", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ @@ -28,8 +28,8 @@ "lodash": "^4.17.21" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0", + "react-dom": "^17.0.0" }, "devDependencies": { "@babel/core": "^7.15.0", @@ -45,6 +45,8 @@ "@storybook/builder-webpack5": "^6.5.10", "@storybook/manager-webpack5": "^6.5.10", "@storybook/react": "^6.5.10", + "@types/react": "^17", + "@types/react-dom": "^17", "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.2", diff --git a/src/DrawZone2/components.tsx b/src/DrawZone2/components.tsx new file mode 100644 index 0000000..439fcb4 --- /dev/null +++ b/src/DrawZone2/components.tsx @@ -0,0 +1,645 @@ +import React, { + PropsWithChildren, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { useId, usePointerPosition, useSetState } from '../hooks' +import { MAX_SCALE, SCALE_STEP } from './constants' +import { + useControls, + useDrawZone2, + useDrawZone2PrivateState, + useLoadImage, +} from './hooks' +import { + DrawZone2Context, + DrawZone2ControlsContext, + DrawZone2PrivateContext, +} from './state' +import { + DrawZone2Mode, + DrawZone2PrivateState, + DrawZone2Shape, + DrawZone2State, + DrawZoneElement, + DrawZoneFitMode, + PictureLoadingState, + Point, +} from './types' +import interact from 'interactjs' + +const xns = 'http://www.w3.org/1999/xlink' + +type DrawZone2Props = PropsWithChildren<{ + readonly src: string + readonly disabled?: boolean +}> +export default function DrawZone2({ children, disabled, src }: DrawZone2Props) { + return ( + + + {children} + + + ) +} + +type DrawZone2ContextProviderProps = PropsWithChildren<{ + readonly disabled?: boolean + readonly src: string +}> +function DrawZone2ContextProvider({ + children, + disabled, + src, +}: DrawZone2ContextProviderProps) { + const { status, pictureSize } = useLoadImage(src) + const [state, setState] = useSetState({ + src, + pictureSize, + disabled, + }) + + useEffect(() => { + setState({ src }) + }, [setState, src]) + + useEffect(() => { + setState({ pictureSize }) + }, [pictureSize, setState]) + + if (status === PictureLoadingState.Error) + throw new Error('Failed to load image') + + return ( + + {children} + + ) +} + +function DrawZone2PrivateContextProvider({ + children, +}: PropsWithChildren) { + const [move, setMove] = useState(false) + const [markerVisible, setMarkerVisible] = useState(false) + const [hideContent, setHideContent] = useState(false) + const [state, setState] = useSetState({ + logicalScale: 1, + positionLeft: 0, + positionTop: 0, + redraw: false, + scale: 1, + }) + + const zoomIn = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.min( + (prev.logicalScale as number) + SCALE_STEP, + MAX_SCALE, + ), + })) + }, [setState]) + + const zoomOut = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.max( + SCALE_STEP, + (prev.logicalScale as number) - SCALE_STEP, + ), + })) + }, [setState]) + + const reset = useCallback(() => { + setState({ + logicalScale: 1, + positionTop: 0, + positionLeft: 0, + }) + }, [setState]) + + const toggleContent = useCallback(() => { + setHideContent((prev) => !prev) + }, []) + + const toggleMarker = useCallback(() => { + setMarkerVisible((prev) => !prev) + }, []) + + const toggleMove = useCallback(() => { + setMove((prev) => !prev) + }, []) + + const redraw = useCallback(() => { + setState((prev) => ({ redraw: !prev.redraw })) + }, [setState]) + + const setPosition = useCallback( + (top: number, left: number) => { + setState({ + positionTop: top, + positionLeft: left, + }) + }, + [setState], + ) + + const setScale = useCallback( + (scale: number) => { + setState({ scale }) + }, + [setState], + ) + + const finaleState = useMemo( + () => ({ + ...state, + setPosition, + setScale, + }), + [setPosition, setScale, state], + ) + + return ( + + + {children} + + + ) +} + +type DrawZone2EditorProps = PropsWithChildren<{ + readonly elements: DrawZoneElement[] + readonly fitMode: DrawZoneFitMode + readonly mode: DrawZone2Mode + readonly onChange: (elements: DrawZoneElement[]) => void + readonly shape: DrawZone2Shape + + readonly drawOnMouseDown?: boolean + readonly initialRect?: DrawZoneElement + readonly onInitialRectChange?: ( + arg: Pick, + ) => void +}> +export function DrawZone2Editor({ + children, + elements, + fitMode, + mode, + onChange, + shape, + drawOnMouseDown, + initialRect, + onInitialRectChange, +}: DrawZone2EditorProps) { + const { src, pictureSize } = useDrawZone2() + const { markerVisible } = useControls() + const { positionTop, positionLeft, logicalScale, scale, setScale } = + useDrawZone2PrivateState() + const svgRef = useRef(null) + const svgContainerRef = useRef(null) + const containerRef = useRef(null) + const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) + + useEffect(() => { + if (fitMode === 'auto') { + setScale(logicalScale) + } else if (containerRef.current && pictureSize) { + const rect = containerRef.current.getBoundingClientRect() + + const minWidth = Math.min(rect.width, pictureSize.width) + const minHeight = Math.min(rect.height, pictureSize.height) + + if ( + pictureSize.width <= minWidth && + pictureSize.height <= minHeight + ) { + const maxWidth = Math.max(rect.width, pictureSize.width) + const maxHeight = Math.max(rect.height, pictureSize.height) + + const coef = maxWidth / minWidth + const coef2 = maxHeight / minHeight + + if (pictureSize.height * coef <= maxHeight) { + setScale(coef * logicalScale) + } else if (pictureSize.width * coef2 <= maxWidth) { + setScale(coef2 * logicalScale) + } else { + setScale(logicalScale) + } + } else if ( + minWidth < pictureSize.width || + minHeight < pictureSize.height + ) { + setScale( + Math.min( + minWidth / pictureSize.width, + minHeight / pictureSize.height, + ) * logicalScale, + ) + } + } + }, [containerRef, pictureSize, fitMode, logicalScale, setScale]) + + useEffect(() => { + const { current: svg } = svgRef + const { current } = svgContainerRef + const { current: container } = containerRef + + if (current && container && svg) { + const handlePointerEnter = () => { + setCanMarkerBeVisible(true) + } + const handlePointerLeave = () => { + setCanMarkerBeVisible(false) + } + + current.addEventListener('pointerenter', handlePointerEnter) + current.addEventListener('pointerleave', handlePointerLeave) + + return () => { + current.removeEventListener('pointerenter', handlePointerEnter) + current.removeEventListener('pointerleave', handlePointerLeave) + } + } + }, []) + + useEffect(() => { + if (svgContainerRef.current) { + svgContainerRef.current.style.top = `${positionTop}px` + svgContainerRef.current.style.left = `${positionLeft}px` + svgContainerRef.current.style.transform = 'none' + } + }, [positionTop, positionLeft]) + + const localOnChange = useCallback( + (elements: DrawZoneElement[]) => { + onChange( + elements.map((element) => { + const minX = Math.min(...element.points.map(({ x }) => x)) + const minY = Math.min(...element.points.map(({ y }) => y)) + const maxX = Math.max(...element.points.map(({ x }) => x)) + const maxY = Math.max(...element.points.map(({ y }) => y)) + + const rect: DrawZoneElement['rect'] = { + height: maxY - minY, + width: maxX - minX, + x: minX, + y: minY, + } + + return { + ...element, + rect, + points: element.points.filter( + (_, index) => shape === 'poly' || index % 2 === 0, + ), + } + }), + ) + }, + [onChange, shape], + ) + + return ( +
+
+ + {canMarkerBeVisible && markerVisible && ( + + )} + {children} +
+
+ ) +} + +type SvgZoneProps = { + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function SvgZone({ elements, onChange }: SvgZoneProps) { + const id = useId() + const ref = useRef(null) + + /* + useEffect(() => { + const svg = SVG(ref.current) as Svg + + console.log('PSYC--SVG', svg, ref.current) + }, []) + */ + + return ( + + {elements.map((element) => ( + + ))} + + ) +} + +type DrawElementProps = { + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawElement({ element, elements, onChange }: DrawElementProps) { + if (element.points?.length === 2) { + return ( + + ) + } + + return null +} + +type DrawRectElementProps = { + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawRectElement({ + element, + elements, + onChange, +}: DrawRectElementProps) { + const minX = useMemo( + () => Math.min(element.points[0].x, element.points[1].x), + [element.points], + ) + const minY = useMemo( + () => Math.min(element.points[0].y, element.points[1].y), + [element.points], + ) + const maxX = useMemo( + () => Math.max(element.points[0].x, element.points[1].x), + [element.points], + ) + const maxY = useMemo( + () => Math.max(element.points[0].y, element.points[1].y), + [element.points], + ) + + const newPoints: Point[] = useMemo( + () => [ + { x: minX, y: minY }, + { x: maxX, y: minY }, + { x: maxX, y: maxY }, + { x: minX, y: maxY }, + ], + [maxX, maxY, minX, minY], + ) + + const newElement = useMemo( + () => ({ + ...element, + points: newPoints, + }), + [element, newPoints], + ) + + return ( + + ) +} + +type DrawPolygonElementProps = { + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawPolygonElement({ + element, + elements, + onChange, +}: DrawPolygonElementProps) { + const ref = useRef(null) + //const [interactInstance, setInteractInstance] = useState() + const { scale } = useDrawZone2PrivateState() + + const path = element.points + .map((point) => [point.x * scale, point.y * scale].join(',')) + .join(' ') + + const onClick = useCallback(() => { + if (!element.selected) { + const elem = elements.find((elem) => elem.id === element.id) + const index = elements.findIndex((e) => e === elem) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + selected: true, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + onChange(newElements) + } + }, [elements, element, onChange]) + + useEffect(() => { + const { current } = ref + if (!element.selected || !current) return + + const instance = interact(ref.current as SVGPolygonElement).draggable({ + listeners: { + start() { + // TODO: Remove handles + }, + move(event) { + const dx = event.dx + const dy = event.dy + + const pointsCount = current.points.numberOfItems + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + point.x = point.x + dx + point.y = point.y + dy + } + }, + end() { + // TODO: Handles + const pointsCount = current.points.numberOfItems + const points = new Array() + + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + points.push({ + x: point.x / scale, + y: point.y / scale, + }) + } + + const index = elements.findIndex( + (elem) => elem.id === element.id, + ) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + points: points, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + onChange(newElements) + }, + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, + }), + ], + cursorChecker: (action, interactable, element, interacting) => { + switch (action.axis) { + case 'x': + return 'ew-resize' + case 'y': + return 'ns-resize' + default: + return interacting ? 'grabbing' : 'move' + } + }, + }) + + return () => { + instance.unset() + } + }, [element, elements, onChange, scale]) + + return ( + + ) +} + +function unSelectElement(element: DrawZoneElement): DrawZoneElement { + if (element.selected) + return { + ...element, + selected: true, + } + + return element +} + +type MarkerProps = { + readonly src: string | null + readonly svgRef: React.RefObject +} +function Marker({ src, svgRef }: MarkerProps): JSX.Element { + const { clientX, clientY } = usePointerPosition() + + const width = svgRef.current?.getBoundingClientRect().width + const height = svgRef.current?.getBoundingClientRect().height + const left = clientX - (svgRef.current?.getBoundingClientRect().left || 0) + const top = clientY - (svgRef.current?.getBoundingClientRect().top || 0) + + return ( + <> +
+
+ + ) +} diff --git a/src/DrawZone2/constants.ts b/src/DrawZone2/constants.ts new file mode 100644 index 0000000..5db3157 --- /dev/null +++ b/src/DrawZone2/constants.ts @@ -0,0 +1,2 @@ +export const MAX_SCALE = 4 +export const SCALE_STEP = 0.25 diff --git a/src/DrawZone2/hooks.ts b/src/DrawZone2/hooks.ts new file mode 100644 index 0000000..3b01fd2 --- /dev/null +++ b/src/DrawZone2/hooks.ts @@ -0,0 +1,1152 @@ +import { + ArrayXY, + Circle, + FillData, + LinkedHTMLElement, + PointArrayAlias, + Polygon, + Polyline, + Rect, + SVG, + StrokeData, + Svg, + Use, +} from '@svgdotjs/svg.js' +import { + useCallback, + useContext, + useEffect, + useLayoutEffect, + useState, +} from 'react' +import { isTouchDevice } from '../utils' +import { bgrToHex, uuid4 } from '../helpers' +import { + DrawZone2Context, + DrawZone2ControlsContext, + DrawZone2PrivateContext, +} from './state' +import { + DrawZone2Mode, + DrawZone2Shape, + DrawZoneElement, + PictureLoadingState, + Point, + Size, +} from './types' +import interact from 'interactjs' + +export function useLoadImage(src: string) { + const [status, setStatus] = useState( + PictureLoadingState.Idle, + ) + const [pictureSize, setPictureSize] = useState({ + width: 0, + height: 0, + }) + + useEffect(() => { + setStatus(PictureLoadingState.Loading) + + async function preloadImage(src: string) { + return new Promise((resolve, reject) => { + const image = new Image() + image.onload = resolve + image.onerror = reject + image.src = src + }) + } + + preloadImage(src) + .then(function afterLoad(this: GlobalEventHandlers, ev: Event) { + const target = ev.target as HTMLImageElement + const { width, height } = target + + setPictureSize({ + width, + height, + }) + setStatus(PictureLoadingState.Done) + }) + .catch(() => { + setStatus(PictureLoadingState.Error) + }) + }, [src]) + + return { status, pictureSize } +} + +export function useDrawZone2() { + return useContext(DrawZone2Context) +} +export function useControls() { + return useContext(DrawZone2ControlsContext) +} +export function useDrawZone2PrivateState() { + return useContext(DrawZone2PrivateContext) +} + +const xns = 'http://www.w3.org/1999/xlink' +const blue = '#2BB1FD' +const defaultStroke = { color: '#fff', width: 2, opacity: 1 } +const defaultFill = { color: '#000', opacity: 0 } + +const CIRCLE_SIZE = isTouchDevice ? 22 : 10 + +function getAbsoluteCoordinates(svg: Svg, points: Point[]) { + const svgRect = svg.node.getBoundingClientRect() + + return points.map(({ x, y }) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })) +} + +export function useDraw( + ref: React.RefObject, + onChange: (elements: DrawZoneElement[]) => void, + mode: DrawZone2Mode, + shape: DrawZone2Shape, + drawOnMouseDown?: boolean, + initialRect?: DrawZoneElement, + onInitialRectChange?: ( + arg: Pick, + ) => void, +) { + const { disabled: isDisabled, pictureSize, src } = useDrawZone2() + const { positionLeft, positionTop, scale, setPosition } = + useDrawZone2PrivateState() + const { move, markerVisible, redraw } = useControls() + const [svg, setSvg] = useState() + + let startPosition: Point | null + let overlayRect: Rect | undefined + let overlayRect2: Rect | undefined + let poly: Polygon | undefined + let tmpPoly: Polyline | undefined + let tmpPoints: Array | undefined + let dragging: boolean + + const convertForOnChange = useCallback( + function convertForOnChange(): DrawZoneElement[] { + if (!svg) { + return [] + } + + const svgRect = svg.node.getBoundingClientRect() + + return svg + .children() + .filter((e) => !e.attr('data-draw-ignore')) + .map((elt) => { + const elementRect = elt.node.getBoundingClientRect() + + const rect: DrawZoneElement['rect'] = { + height: (elementRect.height / svgRect.height) * 100, + width: (elementRect.width / svgRect.width) * 100, + x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, + y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, + } + + const polygon = elt as Polygon + const points: Point[] = getAbsoluteCoordinates( + svg, + polygon + .plot() + .map((p) => ({ + x: p[0], + y: p[1], + })) + .filter( + (_, index) => + shape === 'poly' || index % 2 === 0, + ), + ) + + const result = { + points, + rect, + label: elt.data('label'), + selected: elt.data('selected') as boolean, + id: elt.data('id'), + color: elt.data('color'), + } + + return result + }) + }, + [shape, svg], + ) + + const innerOnChange = useCallback( + function innerOnChange() { + onChange(convertForOnChange()) + }, + [convertForOnChange, onChange], + ) + + /* + function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { + if (event.defaultPrevented) return false + if (event.key === 'Delete' && this.closest('svg')) { + event.preventDefault() + remove(this.dataset['id'] as string) + + return true + } + + return false + } + + function onEscKeyPress(this: SVGElement, event: KeyboardEvent): boolean { + if (event.defaultPrevented) return false + if (event.key === 'Escape' && this.closest('svg')) { + event.preventDefault() + ;(this as unknown as LinkedHTMLElement).instance.fire('deselect') + + return true + } + + return false + } + + function endPolyDrawing(event: Event) { + if (!svg || !poly) return + + const svgRect = svg.node.getBoundingClientRect() + const plot = poly.plot() + + if (plot.length < 3) return + + event.preventDefault() + + poly.remove() + poly = undefined + + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + startPosition = null + + const newPoly = drawPoly({ + points: plot.map(([x, y]) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })), + }) + + window.removeEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.removeEventListener('keyup', onEnterKeyPress, { capture: true }) + + if (newPoly) { + newPoly.fire('select') + } + + innerOnChange() + } + + function onEnterKeyPress(this: Window, event: KeyboardEvent) { + if (event.defaultPrevented) return + if (event.key === 'Enter') { + event.preventDefault() + + endPolyDrawing(event) + } + } + + function onAbortPathDrawing(this: Window, event: KeyboardEvent) { + if (event.defaultPrevented) return + if (event.key === 'Escape') { + event.preventDefault() + + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + if (poly) { + poly.remove() + poly = undefined + } + + startPosition = null + + window.removeEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.removeEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + } + } + */ + + const preventDrag = useCallback((event: DragEvent) => { + event.preventDefault() + }, []) + + function drawRect({ + points, + disabled = isDisabled, + stroke = { ...defaultStroke }, + fill = { ...defaultFill }, + + label, + id = null, + }: { + points: Point[] + disabled?: boolean + stroke?: StrokeData + fill?: FillData + label?: string + id?: string | null + }) { + const minX = Math.min(points[0].x, points[1].x) + const minY = Math.min(points[0].y, points[1].y) + const maxX = Math.max(points[0].x, points[1].x) + const maxY = Math.max(points[0].y, points[1].y) + + const newPoints: Point[] = [ + { x: minX, y: minY }, + { x: maxX, y: minY }, + { x: maxX, y: maxY }, + { x: minX, y: maxY }, + ] + + return drawPoly({ + points: newPoints, + disabled, + stroke, + fill, + label, + id, + }) + } + + function drawPoly({ + points, + disabled = isDisabled, + stroke = { ...defaultStroke }, + fill = { ...defaultFill }, + label, + id = null, + }: { + points: Point[] + disabled?: boolean + stroke?: StrokeData + fill?: FillData + label?: string + id?: string | null + }) { + if (!svg || !points || points.length < 2) { + return + } + const svgRect = svg.node.getBoundingClientRect() + + const svgWidth = svgRect.width || pictureSize?.width || 0 + const svgHeight = svgRect.height || pictureSize?.height || 0 + + const poly = svg.polygon( + points.map( + (point) => [point.x * svgWidth, point.y * svgHeight] as ArrayXY, + ), + ) + + poly.fill(fill) + poly.stroke(stroke) + poly.css('touch-action', 'none') // silence interactjs warning. + + let rootMatrix: DOMMatrix + const circles: Circle[] = [] + const handles: Use[] = [] + + /* + function polyDelKeyPress(ev: KeyboardEvent) { + const result = onDelKeyPress.call(poly.node, ev) + + if (result) { + window.removeEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + } + } + function polyEscKeyPress(ev: KeyboardEvent) { + const result = onEscKeyPress.call(poly.node, ev) + + if (result) { + window.removeEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + } + } + */ + + function makeHandlesGrabbable(svg: Svg) { + interact('.point-handle') + .draggable({ + onstart: function (event) { + svg.css('cursor', 'grabbing') + event.target.instance.css('cursor', 'grabbing') + }, + onmove: function (event) { + const i = event.target.getAttribute('data-index') | 0 + const point = poly.node.points.getItem(i) + + point.x += event.dx / rootMatrix.a + point.y += event.dy / rootMatrix.d + + if (shape === 'rect') { + switch (i) { + case 0: // top left + handles[0].x(point.x) + handles[0].y(point.y) + handles[1].y(point.y) + handles[3].x(point.x) + break + case 1: // top right + handles[1].x(point.x) + handles[1].y(point.y) + handles[2].x(point.x) + handles[0].y(point.y) + break + case 2: // bottom right + handles[2].x(point.x) + handles[2].y(point.y) + handles[3].y(point.y) + handles[1].x(point.x) + break + case 3: // bottom left + handles[3].x(point.x) + handles[3].y(point.y) + handles[0].x(point.x) + handles[2].y(point.y) + break + } + + const newPlot = handles.map( + (h) => + [Number(h.x()), Number(h.y())] as ArrayXY, + ) + poly.plot(newPlot) + } else { + event.target.x.baseVal.value = point.x + event.target.y.baseVal.value = point.y + } + }, + onend: function (event) { + event.target.instance.css('cursor', 'grab') + svg.css('cursor', 'crosshair') + + const index = Number( + event.target.getAttribute('data-index') || 0, + ) + + if (shape === 'poly') { + const currentPlot = [...poly.plot()] + + const newPlot: ArrayXY[] = [ + ...currentPlot.slice(0, index), + [ + Number(event.target.getAttribute('x')), + Number(event.target.getAttribute('y')), + ] as ArrayXY, + ...currentPlot.slice(index + 1), + ] + + poly.plot(newPlot) + } + + svg.node.setAttribute('class', '') + + innerOnChange() + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + }), + ], + }) + .styleCursor(false) + } + + function cleanHandles() { + interact('.point-handle').unset() + handles.forEach((h) => h.remove()) + handles.length = 0 + circles.forEach((circle) => circle.remove()) + circles.length = 0 + document.removeEventListener('dragstart', preventDrag) + } + + function createHandles(svg: Svg) { + rootMatrix = svg.node.getScreenCTM() as DOMMatrix + + for (let i = 0; i < poly.node.points.numberOfItems; i++) { + const point = poly.node.points.getItem(i) + + const handleId = `point-handle-${i}` + + const circle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: blue }) + .stroke({ width: 1, color: '#fff' }) + .css('touch-action', 'none') // silence interactjs warning. + .id(handleId) + + const handle = svg + .use(circle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.push(circle) + handles.push(handle) + } + + makeHandlesGrabbable(svg) + + document.addEventListener('dragstart', preventDrag) + } + + // Custom events. + poly.on('select', () => { + // Deselect all + svg.each(function (this: Svg) { + this.fire('deselect', { inst: poly }) + }) + poly.stroke({ color: blue }) + poly.data('selected', true) + /* + window.addEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + window.addEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + */ + + if (!disabled) { + cleanHandles() + createHandles(svg) + } + + innerOnChange() + }) + poly.on('deselect', (e) => { + if ((e as CustomEvent).detail?.inst === poly) return + poly.stroke(stroke) + poly.data('selected', false) + + cleanHandles() + + /* + window.removeEventListener('keyup', polyDelKeyPress, { + capture: true, + }) + window.removeEventListener('keyup', polyEscKeyPress, { + capture: true, + }) + */ + + innerOnChange() + }) + + if (!disabled) { + poly.css('cursor', 'move') + + interact(poly.node).draggable({ + listeners: { + start() { + cleanHandles() + }, + move(event) { + const x = parseFloat(event.target.instance.x()) + const y = parseFloat(event.target.instance.y()) + + event.target.instance.x(x + event.dx) + event.target.instance.y(y + event.dy) + + innerOnChange() + }, + end() { + createHandles(svg) + }, + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, + }), + ], + cursorChecker: (action, interactable, element, interacting) => { + switch (action.axis) { + case 'x': + return 'ew-resize' + case 'y': + return 'ns-resize' + default: + return interacting ? 'grabbing' : 'move' + } + }, + }) + } + + poly.data('disabled', disabled) + poly.data('id', id || uuid4()) + poly.data('label', label) + + if (defaultStroke.color !== stroke.color) + poly.data('color', stroke.color) + + return poly + } + + function drawPoint(svg: Svg, x: number, y: number): Circle { + const delta = CIRCLE_SIZE / 2 + + const point = svg + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: '#f06' }) + .stroke({ width: 1, color: '#fff' }) + .attr('data-draw-ignore', true) + .addClass('tmp-point') + .move(x - delta, y - delta) + .css('touch-action', 'none') // silence interactjs warning. + + point.on('pointerdown', function (event) { + event.preventDefault() + event.stopPropagation() + + //endPolyDrawing(event) + }) + + return point + } + + function draw({ id, points, color, label }: DrawZoneElement) { + const hexColor = color ? bgrToHex(...color) : null + + const fill = hexColor + ? { ...defaultFill, color: hexColor } + : defaultFill + const stroke = hexColor + ? { ...defaultStroke, color: hexColor } + : defaultStroke + + if (points.length === 2) drawRect({ points, id, fill, stroke, label }) + else drawPoly({ points, id, fill, stroke, label }) + } + + function onPointerDown(e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg) return + + if (!svg.node.contains(e.target as Node)) return + + if ( + svg.node.contains(e.target as Node) && + svg.node !== e.target && + !startPosition + ) { + const element = e.target as unknown as LinkedHTMLElement + + element.instance.fire('select') + return + } else if (e.target === svg.node) { + svg.each(function (this: Svg) { + this.fire('deselect') + }) + innerOnChange() + } + + if (move) { + startPosition = { + x: e.clientX, + y: e.clientY, + } + + dragging = true + + svg.css({ + cursor: 'grabbing', + }) + + e.preventDefault() + } else if (mode === 'draw' && !isDisabled) { + const svgRect = svg.node.getBoundingClientRect() + + if (shape === 'poly' && !isDisabled) { + if (!tmpPoints) { + startPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + tmpPoints = [ + drawPoint(svg, startPosition.x, startPosition.y), + ] + + /* + window.addEventListener('keyup', onAbortPathDrawing, { + capture: true, + }) + window.addEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + */ + } else if (startPosition && Array.isArray(tmpPoints)) { + const currentPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + const prev = poly + ? [...poly.plot()] + : [[startPosition.x, startPosition.y] as ArrayXY] + + if (poly) { + poly.remove() + poly = undefined + } + + const start = prev[0] + if ( + prev.length > 2 && + Math.abs(currentPosition.x - start[0]) <= 10 && + Math.abs(currentPosition.y - start[1]) <= 10 + ) { + if (tmpPoints && tmpPoints.length > 0) { + tmpPoints.forEach((p) => p.remove()) + tmpPoints = undefined + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + startPosition = null + + const poly = drawPoly({ + points: prev.map(([x, y]) => ({ + x: x / svgRect.width, + y: y / svgRect.height, + })), + }) + + /* + window.removeEventListener( + 'keyup', + onAbortPathDrawing, + { + capture: true, + }, + ) + window.removeEventListener('keyup', onEnterKeyPress, { + capture: true, + }) + */ + + poly?.fire('select') + + innerOnChange() + } else { + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + poly = svg + .polygon(plotline) + .fill({ + color: '#f06', + opacity: 0, + }) + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + }) + .attr('data-draw-ignore', true) + + tmpPoints.push( + drawPoint( + svg, + currentPosition.x, + currentPosition.y, + ), + ) + + tmpPoints.forEach((p) => p.front()) + + startPosition = currentPosition + } + } + } else { + startPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (!overlayRect || !overlayRect2) { + overlayRect = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ + color: '#000', + width: 2, + opacity: 0.7, + dasharray: '5,5', + }) + overlayRect2 = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ + color: '#fff', + width: 2, + opacity: 0.7, + dasharray: '5,5', + dashoffset: 5, + }) + } + + overlayRect.move( + startPosition.x / svgRect.width, + startPosition.y / svgRect.height, + ) + + overlayRect2.move( + startPosition.x / svgRect.width, + startPosition.y / svgRect.height, + ) + + e.preventDefault() + } + } + } + + function onPointerMove(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg || !startPosition) return + + if (move && dragging) { + if (!svg.node.contains(e.target as Node)) { + dragging = false + return + } + const parent = svg.parent() + + if (!parent) return + + const currentPosition = { + x: e.clientX, + y: e.clientY, + } + const translationX = currentPosition.x - startPosition.x + const translationY = currentPosition.y - startPosition.y + + parent.css( + 'transform', + `translate3d(${translationX}px, ${translationY}px, 0px)`, + ) + } else if (mode === 'draw' && !isDisabled) { + if (shape === 'poly' && !isTouchDevice) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition: Point = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (tmpPoly) { + tmpPoly.remove() + tmpPoly = undefined + } + + const prev = poly + ? [...poly.plot()] + : [[startPosition.x, startPosition.y] as ArrayXY] + + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + tmpPoly = svg + .polyline(plotline) + .fill('none') + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + dasharray: '5,5', + }) + .attr('data-draw-ignore', true) + + if (Array.isArray(tmpPoints)) { + tmpPoints.forEach((point) => point.front()) + } + } else if (overlayRect && overlayRect2) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + const minX = + Math.min(startPosition.x, currentPosition.x) / svgRect.width + const minY = + Math.min(startPosition.y, currentPosition.y) / + svgRect.height + const maxX = + Math.max(startPosition.x, currentPosition.x) / svgRect.width + const maxY = + Math.max(startPosition.y, currentPosition.y) / + svgRect.height + + const width = Math.abs(maxX - minX) + const height = Math.abs(maxY - minY) + + overlayRect.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect.width(`${width * 100}%`) + overlayRect.height(`${height * 100}%`) + overlayRect2.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect2.width(`${width * 100}%`) + overlayRect2.height(`${height * 100}%`) + } + } + } + + function onPointerUp(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + if (!svg) return + + if (move && dragging) { + const parent = svg.parent() + + if (parent) { + const grandParent = parent.parent() + + if (grandParent) { + const parentRect = grandParent.node.getBoundingClientRect() + const svgRect = parent.node.getBoundingClientRect() + + setPosition( + svgRect.top - parentRect.top, + svgRect.left - parentRect.left, + ) + + svg.css({ cursor: 'grab' }) + + dragging = false + } + } + } else if (mode === 'draw' && shape === 'rect' && !isDisabled) { + if (!startPosition) { + return + } + + if (overlayRect || overlayRect2) { + overlayRect?.remove() + overlayRect = undefined + overlayRect2?.remove() + overlayRect2 = undefined + } + + // Prevent drawing new rect on rect dragend... + if ((e.target as Node | null)?.parentNode === svg.node) { + startPosition = null + return + } + + const svgRect = svg.node.getBoundingClientRect() + const currentPosition = { + x: + Math.max(Math.min(e.clientX, svgRect.right), svgRect.left) - + svgRect.left, + y: + Math.max(Math.min(e.clientY, svgRect.bottom), svgRect.top) - + svgRect.top, + } + + let label = undefined + // Prevent adding very small rects (mis-clicks). + if (Math.abs(currentPosition.x - startPosition.x) <= 2) { + const elements = convertForOnChange() + let lastRect = elements[elements.length - 1] + + if (initialRect) { + lastRect = initialRect + } + + label = lastRect?.label + + if (drawOnMouseDown && lastRect && lastRect.rect) { + currentPosition.x = Math.min( + startPosition.x + + (lastRect.rect.width * svgRect.width) / 100, + svgRect.width, + ) + currentPosition.y = Math.min( + startPosition.y + + (lastRect.rect.height * svgRect.height) / 100, + svgRect.height, + ) + } else { + startPosition = null + return + } + } + + const newRect = drawRect({ + points: [ + { + x: + Math.min(startPosition.x, currentPosition.x) / + svgRect.width, + y: + Math.min(startPosition.y, currentPosition.y) / + svgRect.height, + }, + { + x: + Math.max(startPosition.x, currentPosition.x) / + svgRect.width, + y: + Math.max(startPosition.y, currentPosition.y) / + svgRect.height, + }, + ], + label: label, + }) + + if (newRect && onInitialRectChange) { + const elementRect = newRect.node.getBoundingClientRect() + const rect: DrawZoneElement['rect'] = { + height: (elementRect.height / svgRect.height) * 100, + width: (elementRect.width / svgRect.width) * 100, + x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, + y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, + } + onInitialRectChange({ + rect: rect, + label: newRect.data('label'), + id: newRect.data('id'), + }) + } + + setTimeout(() => { + newRect?.fire('select') + innerOnChange() + }, 50) + } + + if (!move && (mode !== 'draw' || (mode === 'draw' && shape !== 'poly'))) + startPosition = null + } + + useEffect(() => { + if (!ref.current) { + return + } + + ref.current.style.background = `url('${src}') center center / 100% 100% no-repeat` + ref.current.style.top = `${positionTop}px` + ref.current.style.left = `${positionLeft}px` + + if (svg) { + svg.node.remove() + } + + const newSvg = SVG().addTo(ref.current).size('100%', '100%').attr({ + 'xmlns:xlink': xns, + }) + + setSvg(newSvg) + }, [ref, src]) + + useEffect(() => { + if (ref.current && pictureSize && scale) { + ref.current.style.width = `${pictureSize.width * scale}px` + + ref.current.style.height = `${pictureSize.height * scale}px` + + if (svg) { + redraw() + } + } + }, [ref, pictureSize, scale]) + + useLayoutEffect(() => { + if (!svg) { + return + } + + svg.css({ + cursor: + move && !isDisabled + ? 'grab' + : mode === 'none' && !markerVisible + ? 'normal' + : 'crosshair', + position: 'absolute', + top: '0', + left: '0', + }) + + const parent = svg.parent() + + if (parent) { + parent.css({ + position: 'relative', + userSelect: 'none', + transform: 'none', + }) + } + + svg.on('pointerdown', onPointerDown as unknown as EventListener) + window.addEventListener('pointerup', onPointerUp) + window.addEventListener('pointermove', onPointerMove) + + return () => { + svg.off('pointerdown', onPointerDown as unknown as EventListener) + window.removeEventListener('pointerup', onPointerUp) + window.removeEventListener('pointermove', onPointerMove) + } + }, [svg, mode, initialRect]) + + return { svg, draw } +} diff --git a/src/DrawZone2/index.stories.tsx b/src/DrawZone2/index.stories.tsx new file mode 100644 index 0000000..f625709 --- /dev/null +++ b/src/DrawZone2/index.stories.tsx @@ -0,0 +1,171 @@ +import React, { + FunctionComponent, + useCallback, + useEffect, + useMemo, + useState, +} from 'react' + +import DrawZone2, { DrawZone2Editor, useControls, useDrawZone2 } from '.' +import type { DrawZoneElement } from '.' +import { isEmpty } from 'lodash' + +export default { + title: 'Components/DrawZone2', + component: DrawZone2, + decorators: [ + (Story: FunctionComponent) => ( +
+ +
+ ), + ], + parameters: { + layout: 'fullscreen', + }, +} + +function Controls() { + const { + contentHidden, + markerVisible, + move, + reset, + toggleContent, + toggleMarker, + toggleMove, + zoomIn, + zoomOut, + } = useControls() + return ( +
+ + + + + + +
+ ) +} + +interface StoryZoneElement extends DrawZoneElement { + readonly metadata: Record +} + +type EditorProps = { + readonly initialElements: StoryZoneElement[] +} +function Editor({ initialElements }: EditorProps) { + const { pictureSize } = useDrawZone2() + const [elements, setElements] = useState>([]) + + useEffect(() => { + if (pictureSize) { + setElements(initialElements) + } + }, [pictureSize, initialElements]) + + const buildMetas = useCallback( + (elem: DrawZoneElement) => { + if (!pictureSize) return {} + + return { + width: Math.floor( + (elem.rect.width * pictureSize.width) / 100, + ).toString(), + height: Math.floor( + (elem.rect.height * pictureSize.height) / 100, + ).toString(), + diagonal: Math.floor( + Math.hypot( + (elem.rect.height * pictureSize.height) / 100, + (elem.rect.width * pictureSize.width) / 100, + ), + ).toString(), + area: Math.floor( + ((elem.rect.width * pictureSize.width) / 100) * + ((elem.rect.height * pictureSize.height) / 100), + ).toString(), + } as Record + }, + [pictureSize], + ) + + const onChange = useCallback( + (elements: DrawZoneElement[]) => { + const newElements = elements.map((elem) => { + return { + ...elem, + metadata: buildMetas(elem), + } + }) + + setElements([...newElements]) + }, + [buildMetas, setElements], + ) + + const element = useMemo( + () => elements.find(({ selected }) => selected), + [elements], + ) + + return ( + <> +
+
+                    {JSON.stringify(elements)}
+                
+
+
+ {element && !isEmpty(element.metadata) && ( +
+                        {JSON.stringify(element.metadata)}
+                    
+ )} +
+ +
+ +
+ + ) +} + +export function Default() { + return ( + + + + + ) +} diff --git a/src/DrawZone2/index.tsx b/src/DrawZone2/index.tsx new file mode 100644 index 0000000..f5fc317 --- /dev/null +++ b/src/DrawZone2/index.tsx @@ -0,0 +1,26 @@ +import DrawZone2, { DrawZone2Editor } from './components' +import {} from './types' +import type { + DrawZone2Mode, + DrawZone2Shape, + DrawZone2State, + DrawZoneElement, + DrawZoneFitMode, + Point, + Rect, + Size, +} from './types' +import { useControls, useDrawZone2, useLoadImage } from './hooks' + +export default DrawZone2 +export { DrawZone2, DrawZone2Editor, useControls, useDrawZone2, useLoadImage } +export type { + Point, + Rect, + Size, + DrawZoneElement, + DrawZone2State, + DrawZone2Mode, + DrawZone2Shape, + DrawZoneFitMode, +} diff --git a/src/DrawZone2/state.ts b/src/DrawZone2/state.ts new file mode 100644 index 0000000..b16dc38 --- /dev/null +++ b/src/DrawZone2/state.ts @@ -0,0 +1,37 @@ +import { noop } from 'lodash' +import { createContext } from 'react' +import { + DrawZone2Controls, + DrawZone2PrivateStateContext, + DrawZone2State, +} from './types' + +export const DrawZone2Context = createContext({ + src: '', + pictureSize: { + height: 0, + width: 0, + }, +}) +export const DrawZone2ControlsContext = createContext({ + contentHidden: false, + markerVisible: false, + move: false, + redraw: noop, + reset: noop, + toggleContent: noop, + toggleMarker: noop, + toggleMove: noop, + zoomIn: noop, + zoomOut: noop, +}) +export const DrawZone2PrivateContext = + createContext({ + logicalScale: 1, + positionLeft: 0, + positionTop: 0, + redraw: false, + scale: 1, + setScale: noop, + setPosition: noop, + }) diff --git a/src/DrawZone2/types.ts b/src/DrawZone2/types.ts new file mode 100644 index 0000000..f814b1a --- /dev/null +++ b/src/DrawZone2/types.ts @@ -0,0 +1,62 @@ +import { BGR } from 'src/types' + +export type DrawZone2Mode = 'draw' | 'none' +export type DrawZone2Shape = 'rect' | 'poly' | 'none' +export type DrawZoneFitMode = 'auto' | 'fit' + +export interface Size { + readonly width: number + readonly height: number +} +export interface Point { + readonly x: number + readonly y: number +} +export interface Rect extends Size, Point {} + +export interface DrawZoneElement { + readonly id: string | undefined + readonly selected?: boolean + readonly points: Point[] + readonly label: string + readonly color?: BGR + readonly rect: Rect +} + +export type DrawZone2State = { + readonly src: string + readonly pictureSize: Size + readonly disabled?: boolean +} + +export type DrawZone2PrivateState = { + readonly scale: number + readonly logicalScale: number + readonly positionTop: number + readonly positionLeft: number + readonly redraw: boolean +} +export type DrawZone2PrivateStateContext = DrawZone2PrivateState & { + readonly setPosition: (top: number, left: number) => void + readonly setScale: (scale: number) => void +} + +export enum PictureLoadingState { + Idle, + Loading, + Error, + Done, +} + +export type DrawZone2Controls = { + readonly contentHidden: boolean + readonly markerVisible: boolean + readonly move: boolean + readonly redraw: () => void + readonly reset: () => void + readonly toggleContent: () => void + readonly toggleMarker: () => void + readonly toggleMove: () => void + readonly zoomIn: () => void + readonly zoomOut: () => void +} diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 3855cbd..4e472d7 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -17,6 +17,7 @@ import { DrawZoneShape, DrawZoneState, DrawZoneStateActionType, + Size, SizeMode, } from './types' @@ -62,7 +63,9 @@ export default function DrawZone({ } = useContext(DrawZoneContext) const svgRef = useRef(null) const containerRef = useRef(null) - const { svg, draw, originalSize } = useDraw(svgRef, src, { + const { svg, draw, originalSize } = useDraw( + svgRef, + src, onChange, remove, mode, @@ -70,7 +73,7 @@ export default function DrawZone({ drawOnMouseDown, initialRect, onInitialRectChange, - }) + ) const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) const [forceRedraw, setForceRedraw] = useState(false) @@ -87,12 +90,12 @@ export default function DrawZone({ useEffect(() => { dispatch({ type: DrawZoneStateActionType.SET_ORIGINAL_SIZE, - payload: originalSize, + payload: { ...originalSize } as Size, }) dispatch({ type: DrawZoneStateActionType.FORCE_REDRAW, }) - }, [originalSize]) + }, [dispatch, originalSize]) useEffect(() => { if (sizeMode === 'auto') { @@ -132,7 +135,7 @@ export default function DrawZone({ ) } } - }, [containerRef, originalSize, sizeMode, logicalScale]) + }, [containerRef, originalSize, sizeMode, logicalScale, setScale]) useEffect(() => { const { current } = svgRef @@ -183,7 +186,7 @@ export default function DrawZone({ return } } - }, [svg, elements, forceRedraw, scale]) + }, [svg, elements, forceRedraw, scale, draw]) return (
, src: string, - props: { - readonly onChange: (elements: Array) => void - readonly remove: (id: string) => void - readonly mode: DrawZoneMode - readonly shape: DrawZoneShape - readonly drawOnMouseDown?: boolean - readonly initialRect?: ChangedElement - readonly onInitialRectChange?: ( - arg: Pick, - ) => void - }, + onChange: (elements: Array) => void, + remove: (id: string) => void, + mode: DrawZoneMode, + shape: DrawZoneShape, + drawOnMouseDown?: boolean, + initialRect?: ChangedElement, + onInitialRectChange?: ( + arg: Pick, + ) => void, ) { const { state: { isMarkerShown, isDisabled, scale, positionTop, positionLeft }, @@ -157,7 +155,7 @@ export function useDraw( })) .filter( (_, index) => - props.shape === 'poly' || index % 2 === 0, + shape === 'poly' || index % 2 === 0, ), ) @@ -176,15 +174,15 @@ export function useDraw( [svg], ) - function onChange() { - props.onChange(convertForOnChange()) + function innerOnChange() { + onChange(convertForOnChange()) } function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { if (event.defaultPrevented) return false if (event.key === 'Delete' && this.closest('svg')) { event.preventDefault() - props.remove(this.dataset['id'] as string) + remove(this.dataset['id'] as string) return true } @@ -245,7 +243,7 @@ export function useDraw( newPoly.fire('select') } - onChange() + innerOnChange() } function onEnterKeyPress(this: Window, event: KeyboardEvent) { @@ -400,7 +398,7 @@ export function useDraw( point.x += event.dx / rootMatrix.a point.y += event.dy / rootMatrix.d - if (props.shape === 'rect') { + if (shape === 'rect') { switch (i) { case 0: // top left handles[0].x(point.x) @@ -446,7 +444,7 @@ export function useDraw( event.target.getAttribute('data-index') || 0, ) - if (props.shape === 'poly') { + if (shape === 'poly') { const currentPlot = [...poly.plot()] const newPlot: ArrayXY[] = [ @@ -463,7 +461,7 @@ export function useDraw( svg.node.setAttribute('class', '') - onChange() + innerOnChange() }, modifiers: [ interact.modifiers.restrict({ @@ -546,7 +544,7 @@ export function useDraw( createHandles(svg) } - onChange() + innerOnChange() }) poly.on('deselect', (e) => { if ((e as CustomEvent).detail?.inst === poly) return @@ -562,7 +560,7 @@ export function useDraw( capture: true, }) - onChange() + innerOnChange() }) if (!disabled) { @@ -580,7 +578,7 @@ export function useDraw( event.target.instance.x(x + event.dx) event.target.instance.y(y + event.dy) - onChange() + innerOnChange() }, end() { createHandles(svg) @@ -679,13 +677,13 @@ export function useDraw( svg.each(function (this: Svg) { this.fire('deselect') }) - onChange() + innerOnChange() } - if (props.mode === 'draw' && !isDisabled) { + if (mode === 'draw' && !isDisabled) { const svgRect = svg.node.getBoundingClientRect() - if (props.shape === 'poly' && !isDisabled) { + if (shape === 'poly' && !isDisabled) { if (!tmpPoints) { startPosition = { x: e.clientX - svgRect.left, @@ -755,7 +753,7 @@ export function useDraw( poly?.fire('select') - onChange() + innerOnChange() } else { const plotline: PointArrayAlias = [ ...prev, @@ -823,7 +821,7 @@ export function useDraw( e.preventDefault() } - } else if (props.mode === 'move') { + } else if (mode === 'move') { startPosition = { x: e.clientX, y: e.clientY, @@ -843,8 +841,8 @@ export function useDraw( if (e.defaultPrevented) return if (!svg || !startPosition) return - if (props.mode === 'draw' && !isDisabled) { - if (props.shape === 'poly' && !isTouchDevice) { + if (mode === 'draw' && !isDisabled) { + if (shape === 'poly' && !isTouchDevice) { const svgRect = svg.node.getBoundingClientRect() const currentPosition: Point = { @@ -918,7 +916,7 @@ export function useDraw( overlayRect2.width(`${width * 100}%`) overlayRect2.height(`${height * 100}%`) } - } else if (props.mode === 'move' && dragging) { + } else if (mode === 'move' && dragging) { if (!svg.node.contains(e.target as Node)) { dragging = false return @@ -945,7 +943,7 @@ export function useDraw( if (e.defaultPrevented) return if (!svg) return - if (props.mode === 'draw' && props.shape === 'rect' && !isDisabled) { + if (mode === 'draw' && shape === 'rect' && !isDisabled) { if (!startPosition) { return } @@ -979,13 +977,13 @@ export function useDraw( const elements = convertForOnChange() let lastRect = elements[elements.length - 1] - if (props.initialRect) { - lastRect = props.initialRect + if (initialRect) { + lastRect = initialRect } label = lastRect?.label - if (props.drawOnMouseDown && lastRect && lastRect.rect) { + if (drawOnMouseDown && lastRect && lastRect.rect) { currentPosition.x = Math.min( startPosition.x + (lastRect.rect.width * svgRect.width) / 100, @@ -1024,7 +1022,7 @@ export function useDraw( label: label, }) - if (newRect && props.onInitialRectChange) { + if (newRect && onInitialRectChange) { const elementRect = newRect.node.getBoundingClientRect() const rect: ChangedElement['rect'] = { height: (elementRect.height / svgRect.height) * 100, @@ -1032,7 +1030,7 @@ export function useDraw( x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, } - props.onInitialRectChange({ + onInitialRectChange({ rect: rect, label: newRect.data('label'), id: newRect.data('id'), @@ -1041,9 +1039,9 @@ export function useDraw( setTimeout(() => { newRect?.fire('select') - onChange() + innerOnChange() }, 50) - } else if (props.mode === 'move' && dragging) { + } else if (mode === 'move' && dragging) { const parent = svg.parent() if (parent) { @@ -1068,10 +1066,7 @@ export function useDraw( } } - if ( - props.mode !== 'draw' || - (props.mode === 'draw' && props.shape !== 'poly') - ) + if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) startPosition = null } @@ -1124,9 +1119,9 @@ export function useDraw( svg.css({ cursor: - props.mode === 'move' && !isDisabled + mode === 'move' && !isDisabled ? 'grab' - : props.mode === 'none' && !isMarkerShown + : mode === 'none' && !isMarkerShown ? 'normal' : 'crosshair', position: 'absolute', @@ -1153,7 +1148,7 @@ export function useDraw( window.removeEventListener('pointerup', onPointerUp) window.removeEventListener('pointermove', onPointerMove) } - }, [svg, props.mode, props.initialRect]) + }, [svg, mode, initialRect]) return { svg, diff --git a/src/draw-zone/index.stories.tsx b/src/draw-zone/index.stories.tsx index ba993be..d99d7e2 100644 --- a/src/draw-zone/index.stories.tsx +++ b/src/draw-zone/index.stories.tsx @@ -1,4 +1,11 @@ -import React, { FunctionComponent, useEffect, useState } from 'react' +import { isEmpty } from 'lodash' +import React, { + FunctionComponent, + useCallback, + useEffect, + useMemo, + useState, +} from 'react' import DrawZone, { ChangedElement, DrawZoneContainer, useDrawZone } from '.' @@ -233,7 +240,6 @@ export function Polygons() { { x: 200, y: 50 }, ], }, - */ { id: 'poly2', points: [ @@ -244,6 +250,7 @@ export function Polygons() { ], color: '#00ff00', }, + */ ] const { isMarkerShown, @@ -416,7 +423,7 @@ export function ScaleIn() { }} > setElements(elements) @@ -670,7 +677,7 @@ export function ScaleInRect() { }} > setElements(elements) @@ -764,7 +771,7 @@ export function None() { }} > setElements(elements) @@ -780,3 +787,176 @@ export function None() {
) } + +export function BugRepro() { + const originalElements = [ + { + id: 'rect1', + points: [ + { x: 125, y: 94 }, + { x: 250, y: 1 }, + ], + color: '#00ff00', + }, + ] + const { + isMarkerShown, + originalSize, + zoomIn, + zoomOut, + toggleMarker, + reset, + } = useDrawZone() + const [move, setMove] = useState(false) + const [elements, setElements] = useState>>([]) + + useEffect(() => { + if (originalSize) { + setElements( + originalElements.map( + (element) => + ({ + ...element, + points: element.points.map((point) => ({ + x: point.x / originalSize.width, + y: point.y / originalSize.height, + })), + } as ChangedElement), + ), + ) + } + }, [originalSize]) + + const buildMetas = useCallback( + (elem: ChangedElement) => { + return { + width: Math.floor( + (elem.rect.width * originalSize.width) / 100, + ).toString(), + height: Math.floor( + (elem.rect.height * originalSize.height) / 100, + ).toString(), + diagonal: Math.floor( + Math.hypot( + (elem.rect.height * originalSize.height) / 100, + (elem.rect.width * originalSize.width) / 100, + ), + ).toString(), + area: Math.floor( + ((elem.rect.width * originalSize.width) / 100) * + ((elem.rect.height * originalSize.height) / 100), + ).toString(), + } + }, + [originalSize], + ) + + const onChange = useCallback( + (elements: ChangedElement[]) => { + const newElements = elements.map((elem) => { + return { + ...elem, + metadata: buildMetas(elem), + } + }) + + setElements([...newElements]) + }, + [buildMetas, setElements], + ) + + const element = useMemo( + () => elements.find(({ selected }) => selected), + [elements], + ) + + return ( +
+
+ + + + + +
+
+ {element && !isEmpty(element.metadata) && ( +
{JSON.stringify(element.metadata)}
+ )} +
+
+ + setElements((el) => el.filter((e) => e.id !== id)) + } + mode={move ? 'move' : 'draw'} + shape="rect" + sizeMode="fit" + > + {elements.map((element, index) => { + const elem = element as ChangedElement + + if (!elem.selected) return null + + return ( +
+ +
+ ) + })} +
+
+
+ ) +} diff --git a/src/helpers.ts b/src/helpers.ts index d37ecff..9706994 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,3 +1,5 @@ +import { BGR } from "./types" + export function uuid4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( /[xy]/g, @@ -83,7 +85,7 @@ export function bgrToHex(b: number, g: number, r: number) { return `#${numberToHex(r)}${numberToHex(g)}${numberToHex(b)}` } -export function hexToBgr(hex: string): [number, number, number] | null { +export function hexToBgr(hex: string): BGR | null { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? [ diff --git a/src/hooks.ts b/src/hooks.ts index 255b954..83b52e7 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -267,7 +267,13 @@ export function useSetState( ) => T | Partial )(prevValue) : value - return newValue ? { ...prevValue, ...newValue } : prevValue + const finalValue = newValue + ? { ...prevValue, ...newValue } + : prevValue + + if (isEqual(finalValue, prevValue)) return prevValue + + return finalValue }) }, [], diff --git a/src/index.ts b/src/index.ts index 12e4768..20b1755 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,3 +21,4 @@ export { default as ErrorBoundary } from './error-boundary' export * from './helpers' export * from './hooks' export * from './utils' +export type { BGR } from './types' diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..fb47528 --- /dev/null +++ b/src/types.ts @@ -0,0 +1 @@ +export type BGR = [blue: number, green: number, red: number] diff --git a/yarn.lock b/yarn.lock index 24b80d0..837f90e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3400,11 +3400,32 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + "@types/qs@^6.9.5": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/react-dom@^17": + version "17.0.17" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" + integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== + dependencies: + "@types/react" "^17" + +"@types/react@^17": + version "17.0.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.48.tgz#a4532a8b91d7b27b8768b6fc0c3bccb760d15a6c" + integrity sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -3412,6 +3433,11 @@ dependencies: "@types/node" "*" +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -5399,6 +5425,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +csstype@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" From 230e74ad01caf7adf14f6d42c6718014ccb5f461 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 25 Aug 2022 12:32:25 +0200 Subject: [PATCH 17/30] Draw & move rects --- src/DrawZone2/components.tsx | 682 ++++++++++++++++++++++---------- src/DrawZone2/constants.ts | 10 + src/DrawZone2/hooks.ts | 168 ++++---- src/DrawZone2/index.stories.tsx | 36 +- src/DrawZone2/index.tsx | 7 +- src/DrawZone2/state.ts | 38 +- src/DrawZone2/types.ts | 34 +- 7 files changed, 622 insertions(+), 353 deletions(-) diff --git a/src/DrawZone2/components.tsx b/src/DrawZone2/components.tsx index 439fcb4..a11909c 100644 --- a/src/DrawZone2/components.tsx +++ b/src/DrawZone2/components.tsx @@ -7,212 +7,100 @@ import React, { useState, } from 'react' import { useId, usePointerPosition, useSetState } from '../hooks' -import { MAX_SCALE, SCALE_STEP } from './constants' -import { - useControls, - useDrawZone2, - useDrawZone2PrivateState, - useLoadImage, -} from './hooks' -import { - DrawZone2Context, - DrawZone2ControlsContext, - DrawZone2PrivateContext, -} from './state' +import { DRAW_ZONE_2_INITIAL_STATE } from './constants' +import { useControls, useLoadImage } from './hooks' +import { DrawZone2Context } from './state' import { DrawZone2Mode, - DrawZone2PrivateState, DrawZone2Shape, DrawZone2State, DrawZoneElement, DrawZoneFitMode, PictureLoadingState, Point, + Size, } from './types' import interact from 'interactjs' +import { Rect, SVG, Svg } from '@svgdotjs/svg.js' +import { uuid4 } from '../helpers' + +import type { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' -type DrawZone2Props = PropsWithChildren<{ - readonly src: string - readonly disabled?: boolean -}> -export default function DrawZone2({ children, disabled, src }: DrawZone2Props) { +export function DrawZone2Container({ children }: PropsWithChildren) { + const [state, setState] = useSetState({ + ...DRAW_ZONE_2_INITIAL_STATE, + }) return ( - - - {children} - - + + {children} + ) } -type DrawZone2ContextProviderProps = PropsWithChildren<{ +type DrawZone2Props = PropsWithChildren<{ readonly disabled?: boolean + readonly drawOnMouseDown?: boolean + readonly elements: DrawZoneElement[] + readonly fitMode: DrawZoneFitMode + readonly initialRect?: DrawZoneElement + readonly mode: DrawZone2Mode + readonly shape: DrawZone2Shape readonly src: string + readonly onChange: (elements: DrawZoneElement[]) => void + readonly onInitialRectChange?: ( + arg: Pick, + ) => void }> -function DrawZone2ContextProvider({ - children, - disabled, - src, -}: DrawZone2ContextProviderProps) { +export default function DrawZone2({ children, src, ...props }: DrawZone2Props) { const { status, pictureSize } = useLoadImage(src) - const [state, setState] = useSetState({ - src, - pictureSize, - disabled, - }) - - useEffect(() => { - setState({ src }) - }, [setState, src]) - - useEffect(() => { - setState({ pictureSize }) - }, [pictureSize, setState]) if (status === PictureLoadingState.Error) throw new Error('Failed to load image') + if (status !== PictureLoadingState.Done) return null + return ( - + {children} - + ) } -function DrawZone2PrivateContextProvider({ - children, -}: PropsWithChildren) { - const [move, setMove] = useState(false) - const [markerVisible, setMarkerVisible] = useState(false) - const [hideContent, setHideContent] = useState(false) - const [state, setState] = useSetState({ - logicalScale: 1, - positionLeft: 0, - positionTop: 0, - redraw: false, - scale: 1, - }) +function filterPoints(shape: DrawZone2Shape) { + return function filter(_: Point, index: number, arr: Point[]): boolean { + if (arr.length === 2) return true - const zoomIn = useCallback(() => { - setState((prev) => ({ - logicalScale: Math.min( - (prev.logicalScale as number) + SCALE_STEP, - MAX_SCALE, - ), - })) - }, [setState]) - - const zoomOut = useCallback(() => { - setState((prev) => ({ - logicalScale: Math.max( - SCALE_STEP, - (prev.logicalScale as number) - SCALE_STEP, - ), - })) - }, [setState]) - - const reset = useCallback(() => { - setState({ - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - }) - }, [setState]) - - const toggleContent = useCallback(() => { - setHideContent((prev) => !prev) - }, []) - - const toggleMarker = useCallback(() => { - setMarkerVisible((prev) => !prev) - }, []) - - const toggleMove = useCallback(() => { - setMove((prev) => !prev) - }, []) - - const redraw = useCallback(() => { - setState((prev) => ({ redraw: !prev.redraw })) - }, [setState]) - - const setPosition = useCallback( - (top: number, left: number) => { - setState({ - positionTop: top, - positionLeft: left, - }) - }, - [setState], - ) - - const setScale = useCallback( - (scale: number) => { - setState({ scale }) - }, - [setState], - ) - - const finaleState = useMemo( - () => ({ - ...state, - setPosition, - setScale, - }), - [setPosition, setScale, state], - ) - - return ( - - - {children} - - - ) + return shape === 'poly' || index % 2 === 0 + } } -type DrawZone2EditorProps = PropsWithChildren<{ - readonly elements: DrawZoneElement[] - readonly fitMode: DrawZoneFitMode - readonly mode: DrawZone2Mode - readonly onChange: (elements: DrawZoneElement[]) => void - readonly shape: DrawZone2Shape - - readonly drawOnMouseDown?: boolean - readonly initialRect?: DrawZoneElement - readonly onInitialRectChange?: ( - arg: Pick, - ) => void -}> -export function DrawZone2Editor({ +type DrawZone2InnerProps = DrawZone2Props & { + readonly pictureSize: Size +} +function DrawZone2Inner({ children, + disabled, + drawOnMouseDown, elements, fitMode, + initialRect, mode, - onChange, + pictureSize, shape, - drawOnMouseDown, - initialRect, + src, + onChange, onInitialRectChange, -}: DrawZone2EditorProps) { - const { src, pictureSize } = useDrawZone2() - const { markerVisible } = useControls() - const { positionTop, positionLeft, logicalScale, scale, setScale } = - useDrawZone2PrivateState() +}: DrawZone2InnerProps) { + const { + markerVisible, + positionTop, + positionLeft, + logicalScale, + scale, + setScale, + } = useControls() const svgRef = useRef(null) const svgContainerRef = useRef(null) const containerRef = useRef(null) @@ -308,9 +196,7 @@ export function DrawZone2Editor({ return { ...element, rect, - points: element.points.filter( - (_, index) => shape === 'poly' || index % 2 === 0, - ), + points: element.points.filter(filterPoints(shape)), } }), ) @@ -334,6 +220,7 @@ export function DrawZone2Editor({
- + {canMarkerBeVisible && markerVisible && ( )} @@ -352,20 +245,338 @@ export function DrawZone2Editor({ } type SvgZoneProps = { + readonly disabled?: boolean readonly elements: DrawZoneElement[] + readonly mode: DrawZone2Mode + readonly shape: DrawZone2Shape readonly onChange: (elements: DrawZoneElement[]) => void } -function SvgZone({ elements, onChange }: SvgZoneProps) { +function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { const id = useId() const ref = useRef(null) + const startPosition = useRef() + const dragging = useRef(false) + const overlayRect = useRef() + const overlayRect2 = useRef() + const { move, scale, setPosition } = useControls() + + const unselectElements = useCallback(() => { + onChange(elements.map(unSelectElement)) + }, [elements, onChange]) - /* useEffect(() => { - const svg = SVG(ref.current) as Svg + const { current } = ref - console.log('PSYC--SVG', svg, ref.current) - }, []) - */ + if (!current) return + + const svg = SVG(current) as Svg + + function onPointerDown(e: globalThis.PointerEvent) { + if (!svg.node.contains(e.target as Node)) return + + if (svg.node.contains(e.target as Node) && svg.node !== e.target) { + return + } else if (e.target === svg.node) { + e.preventDefault() + e.stopImmediatePropagation() + + unselectElements() + } + + if (move) { + e.preventDefault() + e.stopImmediatePropagation() + + startPosition.current = { + x: e.clientX, + y: e.clientY, + } + + dragging.current = true + + return + } + + if (mode !== 'draw' || disabled) return + + const svgRect = svg.node.getBoundingClientRect() + + if (shape === 'rect') { + startPosition.current = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (!overlayRect.current || !overlayRect2.current) { + overlayRect.current = svg + .rect(0, 0) + .fill({ opacity: 0 }) + .stroke({ + color: '#000', + width: 2, + opacity: 0.7, + dasharray: '5,5', + }) + overlayRect2.current = svg + .rect(0, 0) + .fill({ opacity: 0 }) + .stroke({ + color: '#fff', + width: 2, + opacity: 0.7, + dasharray: '5,5', + dashoffset: 5, + }) + } + + overlayRect.current.move( + startPosition.current.x / svgRect.width, + startPosition.current.y / svgRect.height, + ) + + overlayRect2.current.move( + startPosition.current.x / svgRect.width, + startPosition.current.y / svgRect.height, + ) + + e.preventDefault() + } + } + + function onPointerMove(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented || !startPosition.current) return + + if (move && dragging.current) { + if (!svg.node.contains(e.target as Node)) { + dragging.current = false + return + } + + const parent = svg.parent() + + if (!parent) return + + const currentPosition = { + x: e.clientX, + y: e.clientY, + } + const translationX = currentPosition.x - startPosition.current.x + const translationY = currentPosition.y - startPosition.current.y + + parent.css( + 'transform', + `translate3d(${translationX}px, ${translationY}px, 0px)`, + ) + + e.preventDefault() + e.stopImmediatePropagation() + + return + } + + if (mode !== 'draw' || disabled) return + + const svgRect = svg.node.getBoundingClientRect() + + if ( + shape === 'rect' && + overlayRect.current && + overlayRect2.current + ) { + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + const minX = + Math.min(startPosition.current.x, currentPosition.x) / + svgRect.width + const minY = + Math.min(startPosition.current.y, currentPosition.y) / + svgRect.height + const maxX = + Math.max(startPosition.current.x, currentPosition.x) / + svgRect.width + const maxY = + Math.max(startPosition.current.y, currentPosition.y) / + svgRect.height + + const width = Math.abs(maxX - minX) + const height = Math.abs(maxY - minY) + + overlayRect.current.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect.current.width(`${width * 100}%`) + overlayRect.current.height(`${height * 100}%`) + overlayRect2.current.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect2.current.width(`${width * 100}%`) + overlayRect2.current.height(`${height * 100}%`) + } + } + + function onPointerUp(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + + if (move && dragging.current) { + const parent = svg.parent() + + if (!parent) return + const grandParent = parent.parent() + + if (!grandParent) return + + const parentRect = grandParent.node.getBoundingClientRect() + const svgRect = parent.node.getBoundingClientRect() + + setPosition( + svgRect.top - parentRect.top, + svgRect.left - parentRect.left, + ) + + svg.css({ cursor: 'grab' }) + + dragging.current = false + + return + } + + if (mode === 'draw' && shape === 'rect' && !disabled) { + if (!startPosition.current) return + + if (overlayRect.current || overlayRect2.current) { + overlayRect.current?.remove() + overlayRect.current = undefined + overlayRect2.current?.remove() + overlayRect2.current = undefined + } + + // Prevent drawing new rect on rect dragend... + if ((e.target as Node | null)?.parentNode === svg.node) { + startPosition.current = undefined + return + } + + const svgRect = svg.node.getBoundingClientRect() + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + const label = '' + /* + if (Math.abs(currentPosition.x - startPosition.current.x) <= 2) { + let lastRect = elements[elements.length - 1] + + if (initialRect) { + lastRect = initialRect + } + + label = lastRect?.label + + if (drawOnMouseDown && lastRect && lastRect.rect) { + currentPosition.x = Math.min( + startPosition.current.x + + (lastRect.rect.width * svgRect.width) / 100, + svgRect.width, + ) + currentPosition.y = Math.min( + startPosition.current.y + + (lastRect.rect.height * svgRect.height) / 100, + svgRect.height, + ) + } else { + startPosition.current = undefined + return + } + } + */ + + const xMin = + Math.min(startPosition.current.x, currentPosition.x) / scale + const yMin = + Math.min(startPosition.current.y, currentPosition.y) / scale + const xMax = + Math.max(startPosition.current.x, currentPosition.x) / scale + const yMax = + Math.max(startPosition.current.y, currentPosition.y) / scale + + const width = xMax - xMin + const height = yMax - yMin + + const newElement: DrawZoneElement = { + id: uuid4(), + label, + points: [ + { + x: xMin, + y: yMin, + }, + { + x: xMax, + y: yMax, + }, + ], + rect: { + height, + width, + x: xMin, + y: yMin, + }, + selected: true, + } + + /* + if (onInitialRectChange) { + onInitialRectChange({ + rect: rect, + label: newRect.data('label'), + id: newRect.data('id'), + }) + } + */ + + onChange([...elements, newElement]) + } + + if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) + startPosition.current = undefined + } + + svg.on('pointerdown', onPointerDown as unknown as EventListener) + window.addEventListener('pointerup', onPointerUp) + window.addEventListener('pointermove', onPointerMove) + + return () => { + svg.off('pointerdown', onPointerDown as unknown as EventListener) + window.removeEventListener('pointerup', onPointerUp) + window.removeEventListener('pointermove', onPointerMove) + } + }, [ + disabled, + elements, + mode, + move, + onChange, + scale, + setPosition, + shape, + unselectElements, + ]) return ( + + + ) +} + +type SvgElementsProps = { + readonly disabled?: boolean + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function SvgElements({ disabled, elements, onChange }: SvgElementsProps) { + return ( + <> {elements.map((element) => ( ))} - + ) } type DrawElementProps = { + readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] readonly onChange: (elements: DrawZoneElement[]) => void } -function DrawElement({ element, elements, onChange }: DrawElementProps) { +function DrawElement({ + disabled, + element, + elements, + onChange, +}: DrawElementProps) { if (element.points?.length === 2) { return ( void } function DrawRectElement({ + disabled, element, elements, onChange, @@ -455,6 +693,7 @@ function DrawRectElement({ return ( void } function DrawPolygonElement({ + disabled, element, elements, onChange, }: DrawPolygonElementProps) { const ref = useRef(null) - //const [interactInstance, setInteractInstance] = useState() - const { scale } = useDrawZone2PrivateState() + const { scale } = useControls() + const instance = useRef() const path = element.points .map((point) => [point.x * scale, point.y * scale].join(',')) .join(' ') - const onClick = useCallback(() => { - if (!element.selected) { - const elem = elements.find((elem) => elem.id === element.id) - const index = elements.findIndex((e) => e === elem) - const newElements = [ - ...elements.slice(0, index).map(unSelectElement), - { - ...element, - selected: true, - }, - ...elements.slice(index + 1).map(unSelectElement), - ] - - onChange(newElements) - } - }, [elements, element, onChange]) - - useEffect(() => { + const setInstance = useCallback(() => { const { current } = ref - if (!element.selected || !current) return + if (!current || instance.current) return - const instance = interact(ref.current as SVGPolygonElement).draggable({ + instance.current = interact(current as SVGPolygonElement).draggable({ listeners: { start() { // TODO: Remove handles @@ -540,6 +764,7 @@ function DrawPolygonElement({ { ...element, points: points, + selected: true, }, ...elements.slice(index + 1).map(unSelectElement), ] @@ -550,7 +775,12 @@ function DrawPolygonElement({ modifiers: [ interact.modifiers.restrict({ restriction: 'parent', - elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, + elementRect: { + top: 0, + left: 0, + bottom: 1, + right: 1, + }, }), ], cursorChecker: (action, interactable, element, interacting) => { @@ -564,18 +794,54 @@ function DrawPolygonElement({ } }, }) + }, [element, elements, onChange, scale]) - return () => { - instance.unset() + const onPointerDown: React.PointerEventHandler = + useCallback( + (event) => { + if (disabled || event.defaultPrevented) return + + if (!element.selected) { + const elem = elements.find((elem) => elem.id === element.id) + const index = elements.findIndex((e) => e === elem) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + selected: true, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + setInstance() + + onChange(newElements) + } + }, + [disabled, element, elements, onChange, setInstance], + ) + + useEffect(() => { + if (element.selected) { + setInstance() + + } else { + instance.current?.unset() + instance.current = undefined } - }, [element, elements, onChange, scale]) + }, [element.selected, setInstance]) + + const stroke = useMemo( + () => (element.selected ? '#2BB1FD' : '#00ff00'), + [element.selected], + ) return ( @@ -586,7 +852,7 @@ function unSelectElement(element: DrawZoneElement): DrawZoneElement { if (element.selected) return { ...element, - selected: true, + selected: false, } return element diff --git a/src/DrawZone2/constants.ts b/src/DrawZone2/constants.ts index 5db3157..b1c4d69 100644 --- a/src/DrawZone2/constants.ts +++ b/src/DrawZone2/constants.ts @@ -1,2 +1,12 @@ export const MAX_SCALE = 4 export const SCALE_STEP = 0.25 + +export const DRAW_ZONE_2_INITIAL_STATE = { + contentHidden: false, + logicalScale: 1, + markerVisible: false, + move: false, + positionLeft: 0, + positionTop: 0, + scale: 1, +} diff --git a/src/DrawZone2/hooks.ts b/src/DrawZone2/hooks.ts index 3b01fd2..262da21 100644 --- a/src/DrawZone2/hooks.ts +++ b/src/DrawZone2/hooks.ts @@ -1,40 +1,23 @@ -import { - ArrayXY, - Circle, - FillData, - LinkedHTMLElement, - PointArrayAlias, - Polygon, - Polyline, - Rect, - SVG, - StrokeData, - Svg, - Use, -} from '@svgdotjs/svg.js' -import { - useCallback, - useContext, - useEffect, - useLayoutEffect, - useState, -} from 'react' -import { isTouchDevice } from '../utils' -import { bgrToHex, uuid4 } from '../helpers' -import { - DrawZone2Context, - DrawZone2ControlsContext, - DrawZone2PrivateContext, -} from './state' -import { - DrawZone2Mode, - DrawZone2Shape, - DrawZoneElement, - PictureLoadingState, - Point, - Size, -} from './types' -import interact from 'interactjs' +import { useCallback, useContext, useEffect, useState } from 'react' +import { DrawZone2Context } from './state' +import { PictureLoadingState, Size } from './types' +import { MAX_SCALE, SCALE_STEP } from './constants' +import { memoize } from 'lodash' + +async function preloadImage(src: string): Promise { + const ev = await new Promise((resolve, reject) => { + const image = new Image() + image.onload = resolve + image.onerror = reject + image.src = src + }) + + const target = ev.target as HTMLImageElement + const { width, height } = target + return { width, height } +} + +const preloadImageMemo = memoize(preloadImage) export function useLoadImage(src: string) { const [status, setStatus] = useState( @@ -48,27 +31,13 @@ export function useLoadImage(src: string) { useEffect(() => { setStatus(PictureLoadingState.Loading) - async function preloadImage(src: string) { - return new Promise((resolve, reject) => { - const image = new Image() - image.onload = resolve - image.onerror = reject - image.src = src - }) - } - - preloadImage(src) - .then(function afterLoad(this: GlobalEventHandlers, ev: Event) { - const target = ev.target as HTMLImageElement - const { width, height } = target - - setPictureSize({ - width, - height, - }) + preloadImageMemo(src) + .then(function afterLoad(value: Size) { + setPictureSize(value) setStatus(PictureLoadingState.Done) }) .catch(() => { + preloadImageMemo.cache.delete(src) setStatus(PictureLoadingState.Error) }) }, [src]) @@ -76,16 +45,78 @@ export function useLoadImage(src: string) { return { status, pictureSize } } -export function useDrawZone2() { - return useContext(DrawZone2Context) -} export function useControls() { - return useContext(DrawZone2ControlsContext) -} -export function useDrawZone2PrivateState() { - return useContext(DrawZone2PrivateContext) + const { state, setState } = useContext(DrawZone2Context) + + const zoomIn = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.min( + (prev.logicalScale as number) + SCALE_STEP, + MAX_SCALE, + ), + })) + }, [setState]) + + const zoomOut = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.max( + SCALE_STEP, + (prev.logicalScale as number) - SCALE_STEP, + ), + })) + }, [setState]) + + const reset = useCallback(() => { + setState({ + logicalScale: 1, + positionTop: 0, + positionLeft: 0, + }) + }, [setState]) + + const toggleContent = useCallback(() => { + setState((prev) => ({ contentHidden: !prev.contentHidden })) + }, [setState]) + + const toggleMarker = useCallback(() => { + setState((prev) => ({ markerVisible: !prev.markerVisible })) + }, [setState]) + + const toggleMove = useCallback(() => { + setState((prev) => ({ move: !prev.move })) + }, [setState]) + + const setPosition = useCallback( + (top: number, left: number) => { + setState({ + positionTop: top, + positionLeft: left, + }) + }, + [setState], + ) + + const setScale = useCallback( + (scale: number) => { + setState({ scale }) + }, + [setState], + ) + + return { + ...state, + zoomIn, + zoomOut, + reset, + toggleContent, + toggleMarker, + toggleMove, + setPosition, + setScale, + } } +/* const xns = 'http://www.w3.org/1999/xlink' const blue = '#2BB1FD' const defaultStroke = { color: '#fff', width: 2, opacity: 1 } @@ -293,7 +324,7 @@ export function useDraw( }) } } - */ + * const preventDrag = useCallback((event: DragEvent) => { event.preventDefault() @@ -393,7 +424,7 @@ export function useDraw( }) } } - */ + * function makeHandlesGrabbable(svg: Svg) { interact('.point-handle') @@ -550,7 +581,7 @@ export function useDraw( window.addEventListener('keyup', polyEscKeyPress, { capture: true, }) - */ + * if (!disabled) { cleanHandles() @@ -573,7 +604,7 @@ export function useDraw( window.removeEventListener('keyup', polyEscKeyPress, { capture: true, }) - */ + * innerOnChange() }) @@ -721,7 +752,7 @@ export function useDraw( window.addEventListener('keyup', onEnterKeyPress, { capture: true, }) - */ + * } else if (startPosition && Array.isArray(tmpPoints)) { const currentPosition = { x: e.clientX - svgRect.left, @@ -773,7 +804,7 @@ export function useDraw( window.removeEventListener('keyup', onEnterKeyPress, { capture: true, }) - */ + * poly?.fire('select') @@ -1150,3 +1181,4 @@ export function useDraw( return { svg, draw } } +*/ diff --git a/src/DrawZone2/index.stories.tsx b/src/DrawZone2/index.stories.tsx index f625709..153f13b 100644 --- a/src/DrawZone2/index.stories.tsx +++ b/src/DrawZone2/index.stories.tsx @@ -6,9 +6,10 @@ import React, { useState, } from 'react' -import DrawZone2, { DrawZone2Editor, useControls, useDrawZone2 } from '.' +import DrawZone2, { DrawZone2Container, useControls, useLoadImage } from '.' import type { DrawZoneElement } from '.' import { isEmpty } from 'lodash' +import { PictureLoadingState } from './types' export default { title: 'Components/DrawZone2', @@ -59,16 +60,15 @@ interface StoryZoneElement extends DrawZoneElement { type EditorProps = { readonly initialElements: StoryZoneElement[] + readonly src: string } -function Editor({ initialElements }: EditorProps) { - const { pictureSize } = useDrawZone2() +function Editor({ initialElements, src }: EditorProps) { + const { status, pictureSize } = useLoadImage(src) const [elements, setElements] = useState>([]) useEffect(() => { - if (pictureSize) { - setElements(initialElements) - } - }, [pictureSize, initialElements]) + setElements(initialElements) + }, [initialElements]) const buildMetas = useCallback( (elem: DrawZoneElement) => { @@ -137,13 +137,16 @@ function Editor({ initialElements }: EditorProps) { height: '500px', }} > - + {status === PictureLoadingState.Done && ( + + )}
) @@ -151,9 +154,10 @@ function Editor({ initialElements }: EditorProps) { export function Default() { return ( - + - + ) } diff --git a/src/DrawZone2/index.tsx b/src/DrawZone2/index.tsx index f5fc317..72014c7 100644 --- a/src/DrawZone2/index.tsx +++ b/src/DrawZone2/index.tsx @@ -1,5 +1,4 @@ -import DrawZone2, { DrawZone2Editor } from './components' -import {} from './types' +import DrawZone2, { DrawZone2Container } from './components' import type { DrawZone2Mode, DrawZone2Shape, @@ -10,10 +9,10 @@ import type { Rect, Size, } from './types' -import { useControls, useDrawZone2, useLoadImage } from './hooks' +import { useControls, useLoadImage } from './hooks' export default DrawZone2 -export { DrawZone2, DrawZone2Editor, useControls, useDrawZone2, useLoadImage } +export { DrawZone2Container, DrawZone2, useControls, useLoadImage } export type { Point, Rect, diff --git a/src/DrawZone2/state.ts b/src/DrawZone2/state.ts index b16dc38..c840305 100644 --- a/src/DrawZone2/state.ts +++ b/src/DrawZone2/state.ts @@ -1,37 +1,9 @@ import { noop } from 'lodash' import { createContext } from 'react' -import { - DrawZone2Controls, - DrawZone2PrivateStateContext, - DrawZone2State, -} from './types' +import { DRAW_ZONE_2_INITIAL_STATE } from './constants' +import { DrawZone2StateContext } from './types' -export const DrawZone2Context = createContext({ - src: '', - pictureSize: { - height: 0, - width: 0, - }, +export const DrawZone2Context = createContext({ + state: { ...DRAW_ZONE_2_INITIAL_STATE }, + setState: noop, }) -export const DrawZone2ControlsContext = createContext({ - contentHidden: false, - markerVisible: false, - move: false, - redraw: noop, - reset: noop, - toggleContent: noop, - toggleMarker: noop, - toggleMove: noop, - zoomIn: noop, - zoomOut: noop, -}) -export const DrawZone2PrivateContext = - createContext({ - logicalScale: 1, - positionLeft: 0, - positionTop: 0, - redraw: false, - scale: 1, - setScale: noop, - setPosition: noop, - }) diff --git a/src/DrawZone2/types.ts b/src/DrawZone2/types.ts index f814b1a..e5341d3 100644 --- a/src/DrawZone2/types.ts +++ b/src/DrawZone2/types.ts @@ -1,3 +1,4 @@ +import { Dispatch, SetStateAction } from 'react' import { BGR } from 'src/types' export type DrawZone2Mode = 'draw' | 'none' @@ -24,21 +25,19 @@ export interface DrawZoneElement { } export type DrawZone2State = { - readonly src: string - readonly pictureSize: Size - readonly disabled?: boolean -} - -export type DrawZone2PrivateState = { - readonly scale: number + readonly contentHidden: boolean readonly logicalScale: number + readonly markerVisible: boolean + readonly move: boolean readonly positionTop: number readonly positionLeft: number - readonly redraw: boolean + readonly scale: number } -export type DrawZone2PrivateStateContext = DrawZone2PrivateState & { - readonly setPosition: (top: number, left: number) => void - readonly setScale: (scale: number) => void +export type DrawZone2StateContext = { + readonly state: DrawZone2State + readonly setState: Dispatch< + SetStateAction> + > } export enum PictureLoadingState { @@ -47,16 +46,3 @@ export enum PictureLoadingState { Error, Done, } - -export type DrawZone2Controls = { - readonly contentHidden: boolean - readonly markerVisible: boolean - readonly move: boolean - readonly redraw: () => void - readonly reset: () => void - readonly toggleContent: () => void - readonly toggleMarker: () => void - readonly toggleMove: () => void - readonly zoomIn: () => void - readonly zoomOut: () => void -} From 327aecc2bd40ecb0d1e6eef314e59f1c4f243e57 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 25 Aug 2022 15:12:32 +0200 Subject: [PATCH 18/30] Draw poly --- src/DrawZone2/components.tsx | 351 +++++++++++++++++++++++++++++--- src/DrawZone2/index.stories.tsx | 36 +++- 2 files changed, 353 insertions(+), 34 deletions(-) diff --git a/src/DrawZone2/components.tsx b/src/DrawZone2/components.tsx index a11909c..361457e 100644 --- a/src/DrawZone2/components.tsx +++ b/src/DrawZone2/components.tsx @@ -21,12 +21,23 @@ import { Size, } from './types' import interact from 'interactjs' -import { Rect, SVG, Svg } from '@svgdotjs/svg.js' +import { + ArrayXY, + Circle, + PointArrayAlias, + Polygon, + Polyline, + Rect, + SVG, + Svg, +} from '@svgdotjs/svg.js' import { uuid4 } from '../helpers' +import { isTouchDevice } from '../utils' import type { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' +const CIRCLE_SIZE = isTouchDevice ? 22 : 10 export function DrawZone2Container({ children }: PropsWithChildren) { const [state, setState] = useSetState({ @@ -41,10 +52,10 @@ export function DrawZone2Container({ children }: PropsWithChildren) { type DrawZone2Props = PropsWithChildren<{ readonly disabled?: boolean - readonly drawOnMouseDown?: boolean + readonly drawOnPointerDown?: boolean readonly elements: DrawZoneElement[] readonly fitMode: DrawZoneFitMode - readonly initialRect?: DrawZoneElement + readonly initialRect?: Pick readonly mode: DrawZone2Mode readonly shape: DrawZone2Shape readonly src: string @@ -82,7 +93,7 @@ type DrawZone2InnerProps = DrawZone2Props & { function DrawZone2Inner({ children, disabled, - drawOnMouseDown, + drawOnPointerDown, elements, fitMode, initialRect, @@ -230,10 +241,13 @@ function DrawZone2Inner({ > {canMarkerBeVisible && markerVisible && ( @@ -246,24 +260,117 @@ function DrawZone2Inner({ type SvgZoneProps = { readonly disabled?: boolean + readonly drawOnPointerDown?: boolean readonly elements: DrawZoneElement[] + readonly initialRect?: Pick readonly mode: DrawZone2Mode readonly shape: DrawZone2Shape readonly onChange: (elements: DrawZoneElement[]) => void + readonly onInitialRectChange?: ( + arg: Pick, + ) => void } -function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { +function SvgZone({ + disabled, + drawOnPointerDown, + elements, + initialRect, + mode, + shape, + onChange, + onInitialRectChange, +}: SvgZoneProps) { const id = useId() const ref = useRef(null) const startPosition = useRef() const dragging = useRef(false) const overlayRect = useRef() const overlayRect2 = useRef() + const polyPoints = useRef() + const polygon = useRef() + const polyline = useRef() const { move, scale, setPosition } = useControls() const unselectElements = useCallback(() => { onChange(elements.map(unSelectElement)) }, [elements, onChange]) + const pointOnPointerDown = useCallback( + (event: Event) => { + if (!polygon.current) return + + event.preventDefault() + event.stopPropagation() + + const plot = polygon.current.plot() + + if (plot.length < 3) return + + polygon.current.remove() + polygon.current = undefined + + if (polyPoints.current && polyPoints.current.length > 0) { + polyPoints.current.forEach((p) => p.remove()) + polyPoints.current = undefined + } + + polyline.current?.remove() + polyline.current = undefined + + startPosition.current = undefined + + const points = plot.map(([x, y]) => ({ + x: x / scale, + y: y / scale, + })) + + const xList = points.map(({ x }) => x) + const yList = points.map(({ y }) => y) + + const xMin = Math.min(...xList) + const yMin = Math.min(...yList) + const xMax = Math.max(...xList) + const yMax = Math.max(...yList) + + const newElement: DrawZoneElement = { + id: uuid4(), + label: '', + points, + rect: { + height: yMax - yMin, + width: xMax - xMin, + x: xMin, + y: yMin, + }, + selected: true, + } + + onChange([...elements.map(unSelectElement), newElement]) + }, + [elements, onChange, scale], + ) + + const drawPoint = useCallback( + function drawPoint(svg: Svg, x: number, y: number): Circle { + const delta = CIRCLE_SIZE / 2 + + const point = svg + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: '#f06' }) + .stroke({ width: 1, color: '#fff' }) + .attr('data-draw-ignore', true) + .addClass('tmp-point') + .move(x - delta, y - delta) + .css('touch-action', 'none') // silence interactjs warning. + + point.on('pointerdown', pointOnPointerDown) + + return point + }, + [pointOnPointerDown], + ) + useEffect(() => { const { current } = ref @@ -274,7 +381,21 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { function onPointerDown(e: globalThis.PointerEvent) { if (!svg.node.contains(e.target as Node)) return - if (svg.node.contains(e.target as Node) && svg.node !== e.target) { + if ( + svg.node.contains(e.target as Node) && + svg.node !== e.target && + !move && + !startPosition.current + ) { + console.log( + 'PSYC--HERE', + svg.node.contains(e.target as Node), + svg.node !== e.target, + svg.node, + e.target, + move, + startPosition.current, + ) return } else if (e.target === svg.node) { e.preventDefault() @@ -302,6 +423,9 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { const svgRect = svg.node.getBoundingClientRect() if (shape === 'rect') { + e.preventDefault() + e.stopImmediatePropagation() + startPosition.current = { x: e.clientX - svgRect.left, y: e.clientY - svgRect.top, @@ -341,12 +465,131 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { e.preventDefault() } + + if (shape === 'poly') { + e.preventDefault() + e.stopImmediatePropagation() + + if (!polyPoints.current?.length) { + startPosition.current = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + polyPoints.current = [ + drawPoint( + svg, + startPosition.current.x, + startPosition.current.y, + ), + ] + } else if (startPosition.current) { + const currentPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + const prev = polygon.current + ? [...polygon.current.plot()] + : [ + [ + startPosition.current.x, + startPosition.current.y, + ] as ArrayXY, + ] + + polygon.current?.remove() + polygon.current = undefined + + const start = prev[0] + if ( + prev.length > 2 && + Math.abs(currentPosition.x - start[0]) <= 10 && + Math.abs(currentPosition.y - start[1]) <= 10 + ) { + if ( + polyPoints.current && + polyPoints.current.length > 0 + ) { + polyPoints.current.forEach((p) => p.remove()) + polyPoints.current = undefined + } + + if (polyline.current) { + polyline.current.remove() + polyline.current = undefined + } + + startPosition.current = undefined + + const points = prev.map(([x, y]) => ({ + x: x / scale, + y: y / scale, + })) + + const xList = points.map(({ x }) => x) + const yList = points.map(({ y }) => y) + + const xMin = Math.min(...xList) + const yMin = Math.min(...yList) + const xMax = Math.max(...xList) + const yMax = Math.max(...yList) + + const newElement: DrawZoneElement = { + id: uuid4(), + label: '', + points, + rect: { + height: yMax - yMin, + width: xMax - xMin, + x: xMin, + y: yMin, + }, + selected: true, + } + + onChange([...elements.map(unSelectElement), newElement]) + } else { + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + polygon.current = svg + .polyline(plotline) + .fill({ + color: '#f06', + opacity: 0, + }) + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + }) + .attr('data-draw-ignore', true) + + polyPoints.current.push( + drawPoint( + svg, + currentPosition.x, + currentPosition.y, + ), + ) + + polyPoints.current.forEach((p) => p.front()) + + startPosition.current = currentPosition + } + } + } } function onPointerMove(this: Window, e: globalThis.PointerEvent) { if (e.defaultPrevented || !startPosition.current) return - if (move && dragging.current) { + if (move) { + if (!dragging.current) return if (!svg.node.contains(e.target as Node)) { dragging.current = false return @@ -419,12 +662,51 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { overlayRect2.current.width(`${width * 100}%`) overlayRect2.current.height(`${height * 100}%`) } + + if (shape === 'poly' && !isTouchDevice) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition: Point = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + polyline.current?.remove() + + const prev = polygon.current + ? [...polygon.current.plot()] + : [ + [ + startPosition.current.x, + startPosition.current.y, + ] as ArrayXY, + ] + + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + polyline.current = svg.polyline(plotline).fill('none').stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + dasharray: '5,5', + }) + + if (Array.isArray(polyPoints.current)) { + polyPoints.current.forEach((p) => p.front()) + } + } } function onPointerUp(this: Window, e: globalThis.PointerEvent) { if (e.defaultPrevented) return - if (move && dragging.current) { + if (move) { + if (!dragging.current) return false + const parent = svg.parent() if (!parent) return @@ -477,18 +759,22 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { ) - svgRect.top, } - const label = '' - /* - if (Math.abs(currentPosition.x - startPosition.current.x) <= 2) { - let lastRect = elements[elements.length - 1] + let label = '' + if ( + Math.abs(currentPosition.x - startPosition.current.x) <= 2 + ) { + let lastRect: Pick< + DrawZoneElement, + 'id' | 'label' | 'rect' + > = elements[elements.length - 1] if (initialRect) { lastRect = initialRect } - + label = lastRect?.label - - if (drawOnMouseDown && lastRect && lastRect.rect) { + + if (drawOnPointerDown && lastRect && lastRect.rect) { currentPosition.x = Math.min( startPosition.current.x + (lastRect.rect.width * svgRect.width) / 100, @@ -504,7 +790,6 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { return } } - */ const xMin = Math.min(startPosition.current.x, currentPosition.x) / scale @@ -540,17 +825,15 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { selected: true, } - /* if (onInitialRectChange) { onInitialRectChange({ - rect: rect, - label: newRect.data('label'), - id: newRect.data('id'), + rect: newElement.rect, + label: newElement.label, + id: newElement.id, }) } - */ - onChange([...elements, newElement]) + onChange([...elements.map(unSelectElement), newElement]) } if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) @@ -568,10 +851,14 @@ function SvgZone({ disabled, elements, mode, shape, onChange }: SvgZoneProps) { } }, [ disabled, + drawOnPointerDown, + drawPoint, elements, + initialRect, mode, move, onChange, + onInitialRectChange, scale, setPosition, shape, @@ -641,7 +928,14 @@ function DrawElement({ ) } - return null + return ( + + ) } type DrawRectElementProps = { @@ -714,7 +1008,7 @@ function DrawPolygonElement({ onChange, }: DrawPolygonElementProps) { const ref = useRef(null) - const { scale } = useControls() + const { move, scale } = useControls() const instance = useRef() const path = element.points @@ -799,7 +1093,7 @@ function DrawPolygonElement({ const onPointerDown: React.PointerEventHandler = useCallback( (event) => { - if (disabled || event.defaultPrevented) return + if (disabled || event.defaultPrevented || move) return if (!element.selected) { const elem = elements.find((elem) => elem.id === element.id) @@ -818,18 +1112,17 @@ function DrawPolygonElement({ onChange(newElements) } }, - [disabled, element, elements, onChange, setInstance], + [disabled, element, elements, move, onChange, setInstance], ) useEffect(() => { - if (element.selected) { + if (element.selected && !move) { setInstance() - } else { instance.current?.unset() instance.current = undefined } - }, [element.selected, setInstance]) + }, [element.selected, move, setInstance]) const stroke = useMemo( () => (element.selected ? '#2BB1FD' : '#00ff00'), diff --git a/src/DrawZone2/index.stories.tsx b/src/DrawZone2/index.stories.tsx index 153f13b..25e7a52 100644 --- a/src/DrawZone2/index.stories.tsx +++ b/src/DrawZone2/index.stories.tsx @@ -9,7 +9,7 @@ import React, { import DrawZone2, { DrawZone2Container, useControls, useLoadImage } from '.' import type { DrawZoneElement } from '.' import { isEmpty } from 'lodash' -import { PictureLoadingState } from './types' +import { DrawZone2Shape, PictureLoadingState } from './types' export default { title: 'Components/DrawZone2', @@ -60,9 +60,10 @@ interface StoryZoneElement extends DrawZoneElement { type EditorProps = { readonly initialElements: StoryZoneElement[] + readonly shape: DrawZone2Shape readonly src: string } -function Editor({ initialElements, src }: EditorProps) { +function Editor({ initialElements, shape, src }: EditorProps) { const { status, pictureSize } = useLoadImage(src) const [elements, setElements] = useState>([]) @@ -72,7 +73,7 @@ function Editor({ initialElements, src }: EditorProps) { const buildMetas = useCallback( (elem: DrawZoneElement) => { - if (!pictureSize) return {} + if (!pictureSize || shape !== 'rect') return {} return { width: Math.floor( @@ -93,7 +94,7 @@ function Editor({ initialElements, src }: EditorProps) { ).toString(), } as Record }, - [pictureSize], + [pictureSize, shape], ) const onChange = useCallback( @@ -144,7 +145,7 @@ function Editor({ initialElements, src }: EditorProps) { fitMode="fit" mode="draw" onChange={onChange} - shape="rect" + shape={shape} /> )}
@@ -158,6 +159,7 @@ export function Default() { ) } + +export function Poly() { + return ( + + + + + ) +} From ac33b3bb0fcb1d1df6e325a1cb7a7f53a7631057 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 25 Aug 2022 15:40:54 +0200 Subject: [PATCH 19/30] WIP Resize --- src/DrawZone2/components.tsx | 225 ++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 19 deletions(-) diff --git a/src/DrawZone2/components.tsx b/src/DrawZone2/components.tsx index 361457e..8f64473 100644 --- a/src/DrawZone2/components.tsx +++ b/src/DrawZone2/components.tsx @@ -30,15 +30,20 @@ import { Rect, SVG, Svg, + Use, } from '@svgdotjs/svg.js' import { uuid4 } from '../helpers' import { isTouchDevice } from '../utils' -import type { Interactable } from '@interactjs/types' +import { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' const CIRCLE_SIZE = isTouchDevice ? 22 : 10 +const preventDrag = (event: DragEvent) => { + event.preventDefault() +} + export function DrawZone2Container({ children }: PropsWithChildren) { const [state, setState] = useSetState({ ...DRAW_ZONE_2_INITIAL_STATE, @@ -387,15 +392,6 @@ function SvgZone({ !move && !startPosition.current ) { - console.log( - 'PSYC--HERE', - svg.node.contains(e.target as Node), - svg.node !== e.target, - svg.node, - e.target, - move, - startPosition.current, - ) return } else if (e.target === svg.node) { e.preventDefault() @@ -878,6 +874,7 @@ function SvgZone({ @@ -887,9 +884,15 @@ function SvgZone({ type SvgElementsProps = { readonly disabled?: boolean readonly elements: DrawZoneElement[] + readonly shape: DrawZone2Shape readonly onChange: (elements: DrawZoneElement[]) => void } -function SvgElements({ disabled, elements, onChange }: SvgElementsProps) { +function SvgElements({ + disabled, + elements, + shape, + onChange, +}: SvgElementsProps) { return ( <> {elements.map((element) => ( @@ -898,6 +901,7 @@ function SvgElements({ disabled, elements, onChange }: SvgElementsProps) { disabled={disabled} elements={elements} element={element} + shape={shape} onChange={onChange} /> ))} @@ -909,12 +913,14 @@ type DrawElementProps = { readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] + readonly shape: DrawZone2Shape readonly onChange: (elements: DrawZoneElement[]) => void } function DrawElement({ disabled, element, elements, + shape, onChange, }: DrawElementProps) { if (element.points?.length === 2) { @@ -934,6 +940,7 @@ function DrawElement({ element={element} elements={elements} onChange={onChange} + shape={shape} /> ) } @@ -990,6 +997,7 @@ function DrawRectElement({ disabled={disabled} element={newElement} elements={elements} + shape="rect" onChange={onChange} /> ) @@ -999,30 +1007,204 @@ type DrawPolygonElementProps = { readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] + readonly shape: DrawZone2Shape readonly onChange: (elements: DrawZoneElement[]) => void } function DrawPolygonElement({ disabled, element, elements, + shape, onChange, }: DrawPolygonElementProps) { const ref = useRef(null) const { move, scale } = useControls() const instance = useRef() + const handlesInstance = useRef() + const handles = useRef([]) + const circles = useRef([]) const path = element.points .map((point) => [point.x * scale, point.y * scale].join(',')) .join(' ') - const setInstance = useCallback(() => { + const cleanHandles = useCallback(() => { + handlesInstance.current?.unset() + handlesInstance.current = undefined + + handles.current.forEach((h) => h.remove()) + handles.current.length = 0 + + circles.current.forEach((h) => h.remove()) + circles.current.length = 0 + + document.removeEventListener('dragstart', preventDrag) + }, []) + + const makeHandlesGrabbable = useCallback(() => { + const { current } = ref + + if (!current) return + + const parent = current.parentNode as SVGSVGElement + const svg = (parent as unknown as Record).instance + const rootMatrix = parent.getScreenCTM() as DOMMatrix + + handlesInstance.current?.unset() + + handlesInstance.current = interact('.point-handle') + .draggable({ + onstart: function (event) { + svg.css('cursor', 'grabbing') + event.target.instance.css('cursor', 'grabbing') + }, + onmove: function (event) { + const i = event.target.getAttribute('data-index') | 0 + const point = current.points.getItem(i) + + point.x += event.dx / rootMatrix.a + point.y += event.dy / rootMatrix.d + + if (shape === 'rect') { + switch (i) { + case 0: // top left + handles.current[0].x(point.x) + handles.current[0].y(point.y) + handles.current[1].y(point.y) + handles.current[3].x(point.x) + break + case 1: // top right + handles.current[1].x(point.x) + handles.current[1].y(point.y) + handles.current[2].x(point.x) + handles.current[0].y(point.y) + break + case 2: // bottom right + handles.current[2].x(point.x) + handles.current[2].y(point.y) + handles.current[3].y(point.y) + handles.current[1].x(point.x) + break + case 3: // bottom left + handles.current[3].x(point.x) + handles.current[3].y(point.y) + handles.current[0].x(point.x) + handles.current[2].y(point.y) + break + } + + const newPlot = handles.current.map( + (h) => [Number(h.x()), Number(h.y())] as ArrayXY, + ) + + const pointsCount = current.points.numberOfItems + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + point.x = newPlot[i][0] + point.y = newPlot[i][1] + } + } else { + event.target.x.baseVal.value = point.x + event.target.y.baseVal.value = point.y + } + }, + onend: function (event) { + /* + event.target.instance.css('cursor', 'grab') + svg.css('cursor', 'crosshair') + + const index = Number( + event.target.getAttribute('data-index') || 0, + ) + + if (shape === 'poly') { + const currentPlot = [...poly.plot()] + + const newPlot: ArrayXY[] = [ + ...currentPlot.slice(0, index), + [ + Number(event.target.getAttribute('x')), + Number(event.target.getAttribute('y')), + ] as ArrayXY, + ...currentPlot.slice(index + 1), + ] + + poly.plot(newPlot) + } + + svg.node.setAttribute('class', '') + + //innerOnChange() + */ + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + }), + ], + }) + .styleCursor(false) + }, [shape]) + + const createHandles = useCallback(() => { + const { current } = ref + + if (!current) return + + const svg = (current.parentNode as unknown as Record) + .instance + + for (let i = 0; i < current.points.numberOfItems; i++) { + const point = current.points.getItem(i) + + const handleId = `point-handle-${i}` + + const circle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: '#0000FF' }) + .stroke({ width: 1, color: '#fff' }) + .css('touch-action', 'none') // silence interactjs warning. + .id(handleId) + + const handle = svg + .use(circle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.current.push(circle) + handles.current.push(handle) + } + + makeHandlesGrabbable() + + document.addEventListener('dragstart', preventDrag) + }, [makeHandlesGrabbable]) + + const setupInteract = useCallback(() => { const { current } = ref if (!current || instance.current) return + createHandles() + instance.current = interact(current as SVGPolygonElement).draggable({ listeners: { start() { - // TODO: Remove handles + cleanHandles() }, move(event) { const dx = event.dx @@ -1037,7 +1219,7 @@ function DrawPolygonElement({ } }, end() { - // TODO: Handles + createHandles() const pointsCount = current.points.numberOfItems const points = new Array() @@ -1088,7 +1270,7 @@ function DrawPolygonElement({ } }, }) - }, [element, elements, onChange, scale]) + }, [cleanHandles, createHandles, element, elements, onChange, scale]) const onPointerDown: React.PointerEventHandler = useCallback( @@ -1107,22 +1289,24 @@ function DrawPolygonElement({ ...elements.slice(index + 1).map(unSelectElement), ] - setInstance() + setupInteract() onChange(newElements) } }, - [disabled, element, elements, move, onChange, setInstance], + [disabled, element, elements, move, onChange, setupInteract], ) useEffect(() => { if (element.selected && !move) { - setInstance() + setupInteract() } else { instance.current?.unset() instance.current = undefined + + cleanHandles() } - }, [element.selected, move, setInstance]) + }, [cleanHandles, element.selected, move, setupInteract]) const stroke = useMemo( () => (element.selected ? '#2BB1FD' : '#00ff00'), @@ -1137,6 +1321,9 @@ function DrawPolygonElement({ stroke={stroke} strokeOpacity={1} strokeWidth={2} + style={{ + touchAction: 'none', // silence interactjs warning. + }} /> ) } From d2d856fd6308fb0538760d898cfc6f73195c0fa7 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 25 Aug 2022 16:43:22 +0200 Subject: [PATCH 20/30] v0.0.27-beta.9 --- package.json | 2 +- src/DrawZone2/components.tsx | 1391 --------------------- src/DrawZone2/hooks.ts | 1184 ------------------ src/DrawZone2/index.stories.tsx | 201 --- src/DrawZone2/index.tsx | 25 - src/DrawZone2/state.ts | 9 - src/DrawZone2/types.ts | 48 - src/draw-zone/components.tsx | 1381 ++++++++++++++++++-- src/{DrawZone2 => draw-zone}/constants.ts | 2 +- src/draw-zone/hooks.ts | 1227 ++---------------- src/draw-zone/index.stories.tsx | 1008 ++------------- src/draw-zone/index.tsx | 31 +- src/draw-zone/state.ts | 106 +- src/draw-zone/types.ts | 101 +- src/index.ts | 15 +- 15 files changed, 1532 insertions(+), 5199 deletions(-) delete mode 100644 src/DrawZone2/components.tsx delete mode 100644 src/DrawZone2/hooks.ts delete mode 100644 src/DrawZone2/index.stories.tsx delete mode 100644 src/DrawZone2/index.tsx delete mode 100644 src/DrawZone2/state.ts delete mode 100644 src/DrawZone2/types.ts rename src/{DrawZone2 => draw-zone}/constants.ts (82%) diff --git a/package.json b/package.json index 1eeabda..756ce86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.8", + "version": "0.0.27-beta.9", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/DrawZone2/components.tsx b/src/DrawZone2/components.tsx deleted file mode 100644 index 8f64473..0000000 --- a/src/DrawZone2/components.tsx +++ /dev/null @@ -1,1391 +0,0 @@ -import React, { - PropsWithChildren, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { useId, usePointerPosition, useSetState } from '../hooks' -import { DRAW_ZONE_2_INITIAL_STATE } from './constants' -import { useControls, useLoadImage } from './hooks' -import { DrawZone2Context } from './state' -import { - DrawZone2Mode, - DrawZone2Shape, - DrawZone2State, - DrawZoneElement, - DrawZoneFitMode, - PictureLoadingState, - Point, - Size, -} from './types' -import interact from 'interactjs' -import { - ArrayXY, - Circle, - PointArrayAlias, - Polygon, - Polyline, - Rect, - SVG, - Svg, - Use, -} from '@svgdotjs/svg.js' -import { uuid4 } from '../helpers' -import { isTouchDevice } from '../utils' - -import { Interactable } from '@interactjs/types' - -const xns = 'http://www.w3.org/1999/xlink' -const CIRCLE_SIZE = isTouchDevice ? 22 : 10 - -const preventDrag = (event: DragEvent) => { - event.preventDefault() -} - -export function DrawZone2Container({ children }: PropsWithChildren) { - const [state, setState] = useSetState({ - ...DRAW_ZONE_2_INITIAL_STATE, - }) - return ( - - {children} - - ) -} - -type DrawZone2Props = PropsWithChildren<{ - readonly disabled?: boolean - readonly drawOnPointerDown?: boolean - readonly elements: DrawZoneElement[] - readonly fitMode: DrawZoneFitMode - readonly initialRect?: Pick - readonly mode: DrawZone2Mode - readonly shape: DrawZone2Shape - readonly src: string - readonly onChange: (elements: DrawZoneElement[]) => void - readonly onInitialRectChange?: ( - arg: Pick, - ) => void -}> -export default function DrawZone2({ children, src, ...props }: DrawZone2Props) { - const { status, pictureSize } = useLoadImage(src) - - if (status === PictureLoadingState.Error) - throw new Error('Failed to load image') - - if (status !== PictureLoadingState.Done) return null - - return ( - - {children} - - ) -} - -function filterPoints(shape: DrawZone2Shape) { - return function filter(_: Point, index: number, arr: Point[]): boolean { - if (arr.length === 2) return true - - return shape === 'poly' || index % 2 === 0 - } -} - -type DrawZone2InnerProps = DrawZone2Props & { - readonly pictureSize: Size -} -function DrawZone2Inner({ - children, - disabled, - drawOnPointerDown, - elements, - fitMode, - initialRect, - mode, - pictureSize, - shape, - src, - onChange, - onInitialRectChange, -}: DrawZone2InnerProps) { - const { - markerVisible, - positionTop, - positionLeft, - logicalScale, - scale, - setScale, - } = useControls() - const svgRef = useRef(null) - const svgContainerRef = useRef(null) - const containerRef = useRef(null) - const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) - - useEffect(() => { - if (fitMode === 'auto') { - setScale(logicalScale) - } else if (containerRef.current && pictureSize) { - const rect = containerRef.current.getBoundingClientRect() - - const minWidth = Math.min(rect.width, pictureSize.width) - const minHeight = Math.min(rect.height, pictureSize.height) - - if ( - pictureSize.width <= minWidth && - pictureSize.height <= minHeight - ) { - const maxWidth = Math.max(rect.width, pictureSize.width) - const maxHeight = Math.max(rect.height, pictureSize.height) - - const coef = maxWidth / minWidth - const coef2 = maxHeight / minHeight - - if (pictureSize.height * coef <= maxHeight) { - setScale(coef * logicalScale) - } else if (pictureSize.width * coef2 <= maxWidth) { - setScale(coef2 * logicalScale) - } else { - setScale(logicalScale) - } - } else if ( - minWidth < pictureSize.width || - minHeight < pictureSize.height - ) { - setScale( - Math.min( - minWidth / pictureSize.width, - minHeight / pictureSize.height, - ) * logicalScale, - ) - } - } - }, [containerRef, pictureSize, fitMode, logicalScale, setScale]) - - useEffect(() => { - const { current: svg } = svgRef - const { current } = svgContainerRef - const { current: container } = containerRef - - if (current && container && svg) { - const handlePointerEnter = () => { - setCanMarkerBeVisible(true) - } - const handlePointerLeave = () => { - setCanMarkerBeVisible(false) - } - - current.addEventListener('pointerenter', handlePointerEnter) - current.addEventListener('pointerleave', handlePointerLeave) - - return () => { - current.removeEventListener('pointerenter', handlePointerEnter) - current.removeEventListener('pointerleave', handlePointerLeave) - } - } - }, []) - - useEffect(() => { - if (svgContainerRef.current) { - svgContainerRef.current.style.top = `${positionTop}px` - svgContainerRef.current.style.left = `${positionLeft}px` - svgContainerRef.current.style.transform = 'none' - } - }, [positionTop, positionLeft]) - - const localOnChange = useCallback( - (elements: DrawZoneElement[]) => { - onChange( - elements.map((element) => { - const minX = Math.min(...element.points.map(({ x }) => x)) - const minY = Math.min(...element.points.map(({ y }) => y)) - const maxX = Math.max(...element.points.map(({ x }) => x)) - const maxY = Math.max(...element.points.map(({ y }) => y)) - - const rect: DrawZoneElement['rect'] = { - height: maxY - minY, - width: maxX - minX, - x: minX, - y: minY, - } - - return { - ...element, - rect, - points: element.points.filter(filterPoints(shape)), - } - }), - ) - }, - [onChange, shape], - ) - - return ( -
-
- - {canMarkerBeVisible && markerVisible && ( - - )} - {children} -
-
- ) -} - -type SvgZoneProps = { - readonly disabled?: boolean - readonly drawOnPointerDown?: boolean - readonly elements: DrawZoneElement[] - readonly initialRect?: Pick - readonly mode: DrawZone2Mode - readonly shape: DrawZone2Shape - readonly onChange: (elements: DrawZoneElement[]) => void - readonly onInitialRectChange?: ( - arg: Pick, - ) => void -} -function SvgZone({ - disabled, - drawOnPointerDown, - elements, - initialRect, - mode, - shape, - onChange, - onInitialRectChange, -}: SvgZoneProps) { - const id = useId() - const ref = useRef(null) - const startPosition = useRef() - const dragging = useRef(false) - const overlayRect = useRef() - const overlayRect2 = useRef() - const polyPoints = useRef() - const polygon = useRef() - const polyline = useRef() - const { move, scale, setPosition } = useControls() - - const unselectElements = useCallback(() => { - onChange(elements.map(unSelectElement)) - }, [elements, onChange]) - - const pointOnPointerDown = useCallback( - (event: Event) => { - if (!polygon.current) return - - event.preventDefault() - event.stopPropagation() - - const plot = polygon.current.plot() - - if (plot.length < 3) return - - polygon.current.remove() - polygon.current = undefined - - if (polyPoints.current && polyPoints.current.length > 0) { - polyPoints.current.forEach((p) => p.remove()) - polyPoints.current = undefined - } - - polyline.current?.remove() - polyline.current = undefined - - startPosition.current = undefined - - const points = plot.map(([x, y]) => ({ - x: x / scale, - y: y / scale, - })) - - const xList = points.map(({ x }) => x) - const yList = points.map(({ y }) => y) - - const xMin = Math.min(...xList) - const yMin = Math.min(...yList) - const xMax = Math.max(...xList) - const yMax = Math.max(...yList) - - const newElement: DrawZoneElement = { - id: uuid4(), - label: '', - points, - rect: { - height: yMax - yMin, - width: xMax - xMin, - x: xMin, - y: yMin, - }, - selected: true, - } - - onChange([...elements.map(unSelectElement), newElement]) - }, - [elements, onChange, scale], - ) - - const drawPoint = useCallback( - function drawPoint(svg: Svg, x: number, y: number): Circle { - const delta = CIRCLE_SIZE / 2 - - const point = svg - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: '#f06' }) - .stroke({ width: 1, color: '#fff' }) - .attr('data-draw-ignore', true) - .addClass('tmp-point') - .move(x - delta, y - delta) - .css('touch-action', 'none') // silence interactjs warning. - - point.on('pointerdown', pointOnPointerDown) - - return point - }, - [pointOnPointerDown], - ) - - useEffect(() => { - const { current } = ref - - if (!current) return - - const svg = SVG(current) as Svg - - function onPointerDown(e: globalThis.PointerEvent) { - if (!svg.node.contains(e.target as Node)) return - - if ( - svg.node.contains(e.target as Node) && - svg.node !== e.target && - !move && - !startPosition.current - ) { - return - } else if (e.target === svg.node) { - e.preventDefault() - e.stopImmediatePropagation() - - unselectElements() - } - - if (move) { - e.preventDefault() - e.stopImmediatePropagation() - - startPosition.current = { - x: e.clientX, - y: e.clientY, - } - - dragging.current = true - - return - } - - if (mode !== 'draw' || disabled) return - - const svgRect = svg.node.getBoundingClientRect() - - if (shape === 'rect') { - e.preventDefault() - e.stopImmediatePropagation() - - startPosition.current = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (!overlayRect.current || !overlayRect2.current) { - overlayRect.current = svg - .rect(0, 0) - .fill({ opacity: 0 }) - .stroke({ - color: '#000', - width: 2, - opacity: 0.7, - dasharray: '5,5', - }) - overlayRect2.current = svg - .rect(0, 0) - .fill({ opacity: 0 }) - .stroke({ - color: '#fff', - width: 2, - opacity: 0.7, - dasharray: '5,5', - dashoffset: 5, - }) - } - - overlayRect.current.move( - startPosition.current.x / svgRect.width, - startPosition.current.y / svgRect.height, - ) - - overlayRect2.current.move( - startPosition.current.x / svgRect.width, - startPosition.current.y / svgRect.height, - ) - - e.preventDefault() - } - - if (shape === 'poly') { - e.preventDefault() - e.stopImmediatePropagation() - - if (!polyPoints.current?.length) { - startPosition.current = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - polyPoints.current = [ - drawPoint( - svg, - startPosition.current.x, - startPosition.current.y, - ), - ] - } else if (startPosition.current) { - const currentPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - const prev = polygon.current - ? [...polygon.current.plot()] - : [ - [ - startPosition.current.x, - startPosition.current.y, - ] as ArrayXY, - ] - - polygon.current?.remove() - polygon.current = undefined - - const start = prev[0] - if ( - prev.length > 2 && - Math.abs(currentPosition.x - start[0]) <= 10 && - Math.abs(currentPosition.y - start[1]) <= 10 - ) { - if ( - polyPoints.current && - polyPoints.current.length > 0 - ) { - polyPoints.current.forEach((p) => p.remove()) - polyPoints.current = undefined - } - - if (polyline.current) { - polyline.current.remove() - polyline.current = undefined - } - - startPosition.current = undefined - - const points = prev.map(([x, y]) => ({ - x: x / scale, - y: y / scale, - })) - - const xList = points.map(({ x }) => x) - const yList = points.map(({ y }) => y) - - const xMin = Math.min(...xList) - const yMin = Math.min(...yList) - const xMax = Math.max(...xList) - const yMax = Math.max(...yList) - - const newElement: DrawZoneElement = { - id: uuid4(), - label: '', - points, - rect: { - height: yMax - yMin, - width: xMax - xMin, - x: xMin, - y: yMin, - }, - selected: true, - } - - onChange([...elements.map(unSelectElement), newElement]) - } else { - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - polygon.current = svg - .polyline(plotline) - .fill({ - color: '#f06', - opacity: 0, - }) - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - }) - .attr('data-draw-ignore', true) - - polyPoints.current.push( - drawPoint( - svg, - currentPosition.x, - currentPosition.y, - ), - ) - - polyPoints.current.forEach((p) => p.front()) - - startPosition.current = currentPosition - } - } - } - } - - function onPointerMove(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented || !startPosition.current) return - - if (move) { - if (!dragging.current) return - if (!svg.node.contains(e.target as Node)) { - dragging.current = false - return - } - - const parent = svg.parent() - - if (!parent) return - - const currentPosition = { - x: e.clientX, - y: e.clientY, - } - const translationX = currentPosition.x - startPosition.current.x - const translationY = currentPosition.y - startPosition.current.y - - parent.css( - 'transform', - `translate3d(${translationX}px, ${translationY}px, 0px)`, - ) - - e.preventDefault() - e.stopImmediatePropagation() - - return - } - - if (mode !== 'draw' || disabled) return - - const svgRect = svg.node.getBoundingClientRect() - - if ( - shape === 'rect' && - overlayRect.current && - overlayRect2.current - ) { - const currentPosition = { - x: - Math.max( - Math.min(e.clientX, svgRect.right), - svgRect.left, - ) - svgRect.left, - y: - Math.max( - Math.min(e.clientY, svgRect.bottom), - svgRect.top, - ) - svgRect.top, - } - - const minX = - Math.min(startPosition.current.x, currentPosition.x) / - svgRect.width - const minY = - Math.min(startPosition.current.y, currentPosition.y) / - svgRect.height - const maxX = - Math.max(startPosition.current.x, currentPosition.x) / - svgRect.width - const maxY = - Math.max(startPosition.current.y, currentPosition.y) / - svgRect.height - - const width = Math.abs(maxX - minX) - const height = Math.abs(maxY - minY) - - overlayRect.current.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect.current.width(`${width * 100}%`) - overlayRect.current.height(`${height * 100}%`) - overlayRect2.current.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect2.current.width(`${width * 100}%`) - overlayRect2.current.height(`${height * 100}%`) - } - - if (shape === 'poly' && !isTouchDevice) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition: Point = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - polyline.current?.remove() - - const prev = polygon.current - ? [...polygon.current.plot()] - : [ - [ - startPosition.current.x, - startPosition.current.y, - ] as ArrayXY, - ] - - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - polyline.current = svg.polyline(plotline).fill('none').stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - dasharray: '5,5', - }) - - if (Array.isArray(polyPoints.current)) { - polyPoints.current.forEach((p) => p.front()) - } - } - } - - function onPointerUp(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - - if (move) { - if (!dragging.current) return false - - const parent = svg.parent() - - if (!parent) return - const grandParent = parent.parent() - - if (!grandParent) return - - const parentRect = grandParent.node.getBoundingClientRect() - const svgRect = parent.node.getBoundingClientRect() - - setPosition( - svgRect.top - parentRect.top, - svgRect.left - parentRect.left, - ) - - svg.css({ cursor: 'grab' }) - - dragging.current = false - - return - } - - if (mode === 'draw' && shape === 'rect' && !disabled) { - if (!startPosition.current) return - - if (overlayRect.current || overlayRect2.current) { - overlayRect.current?.remove() - overlayRect.current = undefined - overlayRect2.current?.remove() - overlayRect2.current = undefined - } - - // Prevent drawing new rect on rect dragend... - if ((e.target as Node | null)?.parentNode === svg.node) { - startPosition.current = undefined - return - } - - const svgRect = svg.node.getBoundingClientRect() - const currentPosition = { - x: - Math.max( - Math.min(e.clientX, svgRect.right), - svgRect.left, - ) - svgRect.left, - y: - Math.max( - Math.min(e.clientY, svgRect.bottom), - svgRect.top, - ) - svgRect.top, - } - - let label = '' - if ( - Math.abs(currentPosition.x - startPosition.current.x) <= 2 - ) { - let lastRect: Pick< - DrawZoneElement, - 'id' | 'label' | 'rect' - > = elements[elements.length - 1] - - if (initialRect) { - lastRect = initialRect - } - - label = lastRect?.label - - if (drawOnPointerDown && lastRect && lastRect.rect) { - currentPosition.x = Math.min( - startPosition.current.x + - (lastRect.rect.width * svgRect.width) / 100, - svgRect.width, - ) - currentPosition.y = Math.min( - startPosition.current.y + - (lastRect.rect.height * svgRect.height) / 100, - svgRect.height, - ) - } else { - startPosition.current = undefined - return - } - } - - const xMin = - Math.min(startPosition.current.x, currentPosition.x) / scale - const yMin = - Math.min(startPosition.current.y, currentPosition.y) / scale - const xMax = - Math.max(startPosition.current.x, currentPosition.x) / scale - const yMax = - Math.max(startPosition.current.y, currentPosition.y) / scale - - const width = xMax - xMin - const height = yMax - yMin - - const newElement: DrawZoneElement = { - id: uuid4(), - label, - points: [ - { - x: xMin, - y: yMin, - }, - { - x: xMax, - y: yMax, - }, - ], - rect: { - height, - width, - x: xMin, - y: yMin, - }, - selected: true, - } - - if (onInitialRectChange) { - onInitialRectChange({ - rect: newElement.rect, - label: newElement.label, - id: newElement.id, - }) - } - - onChange([...elements.map(unSelectElement), newElement]) - } - - if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) - startPosition.current = undefined - } - - svg.on('pointerdown', onPointerDown as unknown as EventListener) - window.addEventListener('pointerup', onPointerUp) - window.addEventListener('pointermove', onPointerMove) - - return () => { - svg.off('pointerdown', onPointerDown as unknown as EventListener) - window.removeEventListener('pointerup', onPointerUp) - window.removeEventListener('pointermove', onPointerMove) - } - }, [ - disabled, - drawOnPointerDown, - drawPoint, - elements, - initialRect, - mode, - move, - onChange, - onInitialRectChange, - scale, - setPosition, - shape, - unselectElements, - ]) - - return ( - - - - ) -} - -type SvgElementsProps = { - readonly disabled?: boolean - readonly elements: DrawZoneElement[] - readonly shape: DrawZone2Shape - readonly onChange: (elements: DrawZoneElement[]) => void -} -function SvgElements({ - disabled, - elements, - shape, - onChange, -}: SvgElementsProps) { - return ( - <> - {elements.map((element) => ( - - ))} - - ) -} - -type DrawElementProps = { - readonly disabled?: boolean - readonly element: DrawZoneElement - readonly elements: DrawZoneElement[] - readonly shape: DrawZone2Shape - readonly onChange: (elements: DrawZoneElement[]) => void -} -function DrawElement({ - disabled, - element, - elements, - shape, - onChange, -}: DrawElementProps) { - if (element.points?.length === 2) { - return ( - - ) - } - - return ( - - ) -} - -type DrawRectElementProps = { - readonly disabled?: boolean - readonly element: DrawZoneElement - readonly elements: DrawZoneElement[] - readonly onChange: (elements: DrawZoneElement[]) => void -} -function DrawRectElement({ - disabled, - element, - elements, - onChange, -}: DrawRectElementProps) { - const minX = useMemo( - () => Math.min(element.points[0].x, element.points[1].x), - [element.points], - ) - const minY = useMemo( - () => Math.min(element.points[0].y, element.points[1].y), - [element.points], - ) - const maxX = useMemo( - () => Math.max(element.points[0].x, element.points[1].x), - [element.points], - ) - const maxY = useMemo( - () => Math.max(element.points[0].y, element.points[1].y), - [element.points], - ) - - const newPoints: Point[] = useMemo( - () => [ - { x: minX, y: minY }, - { x: maxX, y: minY }, - { x: maxX, y: maxY }, - { x: minX, y: maxY }, - ], - [maxX, maxY, minX, minY], - ) - - const newElement = useMemo( - () => ({ - ...element, - points: newPoints, - }), - [element, newPoints], - ) - - return ( - - ) -} - -type DrawPolygonElementProps = { - readonly disabled?: boolean - readonly element: DrawZoneElement - readonly elements: DrawZoneElement[] - readonly shape: DrawZone2Shape - readonly onChange: (elements: DrawZoneElement[]) => void -} -function DrawPolygonElement({ - disabled, - element, - elements, - shape, - onChange, -}: DrawPolygonElementProps) { - const ref = useRef(null) - const { move, scale } = useControls() - const instance = useRef() - const handlesInstance = useRef() - const handles = useRef([]) - const circles = useRef([]) - - const path = element.points - .map((point) => [point.x * scale, point.y * scale].join(',')) - .join(' ') - - const cleanHandles = useCallback(() => { - handlesInstance.current?.unset() - handlesInstance.current = undefined - - handles.current.forEach((h) => h.remove()) - handles.current.length = 0 - - circles.current.forEach((h) => h.remove()) - circles.current.length = 0 - - document.removeEventListener('dragstart', preventDrag) - }, []) - - const makeHandlesGrabbable = useCallback(() => { - const { current } = ref - - if (!current) return - - const parent = current.parentNode as SVGSVGElement - const svg = (parent as unknown as Record).instance - const rootMatrix = parent.getScreenCTM() as DOMMatrix - - handlesInstance.current?.unset() - - handlesInstance.current = interact('.point-handle') - .draggable({ - onstart: function (event) { - svg.css('cursor', 'grabbing') - event.target.instance.css('cursor', 'grabbing') - }, - onmove: function (event) { - const i = event.target.getAttribute('data-index') | 0 - const point = current.points.getItem(i) - - point.x += event.dx / rootMatrix.a - point.y += event.dy / rootMatrix.d - - if (shape === 'rect') { - switch (i) { - case 0: // top left - handles.current[0].x(point.x) - handles.current[0].y(point.y) - handles.current[1].y(point.y) - handles.current[3].x(point.x) - break - case 1: // top right - handles.current[1].x(point.x) - handles.current[1].y(point.y) - handles.current[2].x(point.x) - handles.current[0].y(point.y) - break - case 2: // bottom right - handles.current[2].x(point.x) - handles.current[2].y(point.y) - handles.current[3].y(point.y) - handles.current[1].x(point.x) - break - case 3: // bottom left - handles.current[3].x(point.x) - handles.current[3].y(point.y) - handles.current[0].x(point.x) - handles.current[2].y(point.y) - break - } - - const newPlot = handles.current.map( - (h) => [Number(h.x()), Number(h.y())] as ArrayXY, - ) - - const pointsCount = current.points.numberOfItems - for (let i = 0; i < pointsCount; i++) { - const point = current.points.getItem(i) - - point.x = newPlot[i][0] - point.y = newPlot[i][1] - } - } else { - event.target.x.baseVal.value = point.x - event.target.y.baseVal.value = point.y - } - }, - onend: function (event) { - /* - event.target.instance.css('cursor', 'grab') - svg.css('cursor', 'crosshair') - - const index = Number( - event.target.getAttribute('data-index') || 0, - ) - - if (shape === 'poly') { - const currentPlot = [...poly.plot()] - - const newPlot: ArrayXY[] = [ - ...currentPlot.slice(0, index), - [ - Number(event.target.getAttribute('x')), - Number(event.target.getAttribute('y')), - ] as ArrayXY, - ...currentPlot.slice(index + 1), - ] - - poly.plot(newPlot) - } - - svg.node.setAttribute('class', '') - - //innerOnChange() - */ - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - }), - ], - }) - .styleCursor(false) - }, [shape]) - - const createHandles = useCallback(() => { - const { current } = ref - - if (!current) return - - const svg = (current.parentNode as unknown as Record) - .instance - - for (let i = 0; i < current.points.numberOfItems; i++) { - const point = current.points.getItem(i) - - const handleId = `point-handle-${i}` - - const circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: '#0000FF' }) - .stroke({ width: 1, color: '#fff' }) - .css('touch-action', 'none') // silence interactjs warning. - .id(handleId) - - const handle = svg - .use(circle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.current.push(circle) - handles.current.push(handle) - } - - makeHandlesGrabbable() - - document.addEventListener('dragstart', preventDrag) - }, [makeHandlesGrabbable]) - - const setupInteract = useCallback(() => { - const { current } = ref - if (!current || instance.current) return - - createHandles() - - instance.current = interact(current as SVGPolygonElement).draggable({ - listeners: { - start() { - cleanHandles() - }, - move(event) { - const dx = event.dx - const dy = event.dy - - const pointsCount = current.points.numberOfItems - for (let i = 0; i < pointsCount; i++) { - const point = current.points.getItem(i) - - point.x = point.x + dx - point.y = point.y + dy - } - }, - end() { - createHandles() - const pointsCount = current.points.numberOfItems - const points = new Array() - - for (let i = 0; i < pointsCount; i++) { - const point = current.points.getItem(i) - - points.push({ - x: point.x / scale, - y: point.y / scale, - }) - } - - const index = elements.findIndex( - (elem) => elem.id === element.id, - ) - const newElements = [ - ...elements.slice(0, index).map(unSelectElement), - { - ...element, - points: points, - selected: true, - }, - ...elements.slice(index + 1).map(unSelectElement), - ] - - onChange(newElements) - }, - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - elementRect: { - top: 0, - left: 0, - bottom: 1, - right: 1, - }, - }), - ], - cursorChecker: (action, interactable, element, interacting) => { - switch (action.axis) { - case 'x': - return 'ew-resize' - case 'y': - return 'ns-resize' - default: - return interacting ? 'grabbing' : 'move' - } - }, - }) - }, [cleanHandles, createHandles, element, elements, onChange, scale]) - - const onPointerDown: React.PointerEventHandler = - useCallback( - (event) => { - if (disabled || event.defaultPrevented || move) return - - if (!element.selected) { - const elem = elements.find((elem) => elem.id === element.id) - const index = elements.findIndex((e) => e === elem) - const newElements = [ - ...elements.slice(0, index).map(unSelectElement), - { - ...element, - selected: true, - }, - ...elements.slice(index + 1).map(unSelectElement), - ] - - setupInteract() - - onChange(newElements) - } - }, - [disabled, element, elements, move, onChange, setupInteract], - ) - - useEffect(() => { - if (element.selected && !move) { - setupInteract() - } else { - instance.current?.unset() - instance.current = undefined - - cleanHandles() - } - }, [cleanHandles, element.selected, move, setupInteract]) - - const stroke = useMemo( - () => (element.selected ? '#2BB1FD' : '#00ff00'), - [element.selected], - ) - - return ( - - ) -} - -function unSelectElement(element: DrawZoneElement): DrawZoneElement { - if (element.selected) - return { - ...element, - selected: false, - } - - return element -} - -type MarkerProps = { - readonly src: string | null - readonly svgRef: React.RefObject -} -function Marker({ src, svgRef }: MarkerProps): JSX.Element { - const { clientX, clientY } = usePointerPosition() - - const width = svgRef.current?.getBoundingClientRect().width - const height = svgRef.current?.getBoundingClientRect().height - const left = clientX - (svgRef.current?.getBoundingClientRect().left || 0) - const top = clientY - (svgRef.current?.getBoundingClientRect().top || 0) - - return ( - <> -
-
- - ) -} diff --git a/src/DrawZone2/hooks.ts b/src/DrawZone2/hooks.ts deleted file mode 100644 index 262da21..0000000 --- a/src/DrawZone2/hooks.ts +++ /dev/null @@ -1,1184 +0,0 @@ -import { useCallback, useContext, useEffect, useState } from 'react' -import { DrawZone2Context } from './state' -import { PictureLoadingState, Size } from './types' -import { MAX_SCALE, SCALE_STEP } from './constants' -import { memoize } from 'lodash' - -async function preloadImage(src: string): Promise { - const ev = await new Promise((resolve, reject) => { - const image = new Image() - image.onload = resolve - image.onerror = reject - image.src = src - }) - - const target = ev.target as HTMLImageElement - const { width, height } = target - return { width, height } -} - -const preloadImageMemo = memoize(preloadImage) - -export function useLoadImage(src: string) { - const [status, setStatus] = useState( - PictureLoadingState.Idle, - ) - const [pictureSize, setPictureSize] = useState({ - width: 0, - height: 0, - }) - - useEffect(() => { - setStatus(PictureLoadingState.Loading) - - preloadImageMemo(src) - .then(function afterLoad(value: Size) { - setPictureSize(value) - setStatus(PictureLoadingState.Done) - }) - .catch(() => { - preloadImageMemo.cache.delete(src) - setStatus(PictureLoadingState.Error) - }) - }, [src]) - - return { status, pictureSize } -} - -export function useControls() { - const { state, setState } = useContext(DrawZone2Context) - - const zoomIn = useCallback(() => { - setState((prev) => ({ - logicalScale: Math.min( - (prev.logicalScale as number) + SCALE_STEP, - MAX_SCALE, - ), - })) - }, [setState]) - - const zoomOut = useCallback(() => { - setState((prev) => ({ - logicalScale: Math.max( - SCALE_STEP, - (prev.logicalScale as number) - SCALE_STEP, - ), - })) - }, [setState]) - - const reset = useCallback(() => { - setState({ - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - }) - }, [setState]) - - const toggleContent = useCallback(() => { - setState((prev) => ({ contentHidden: !prev.contentHidden })) - }, [setState]) - - const toggleMarker = useCallback(() => { - setState((prev) => ({ markerVisible: !prev.markerVisible })) - }, [setState]) - - const toggleMove = useCallback(() => { - setState((prev) => ({ move: !prev.move })) - }, [setState]) - - const setPosition = useCallback( - (top: number, left: number) => { - setState({ - positionTop: top, - positionLeft: left, - }) - }, - [setState], - ) - - const setScale = useCallback( - (scale: number) => { - setState({ scale }) - }, - [setState], - ) - - return { - ...state, - zoomIn, - zoomOut, - reset, - toggleContent, - toggleMarker, - toggleMove, - setPosition, - setScale, - } -} - -/* -const xns = 'http://www.w3.org/1999/xlink' -const blue = '#2BB1FD' -const defaultStroke = { color: '#fff', width: 2, opacity: 1 } -const defaultFill = { color: '#000', opacity: 0 } - -const CIRCLE_SIZE = isTouchDevice ? 22 : 10 - -function getAbsoluteCoordinates(svg: Svg, points: Point[]) { - const svgRect = svg.node.getBoundingClientRect() - - return points.map(({ x, y }) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })) -} - -export function useDraw( - ref: React.RefObject, - onChange: (elements: DrawZoneElement[]) => void, - mode: DrawZone2Mode, - shape: DrawZone2Shape, - drawOnMouseDown?: boolean, - initialRect?: DrawZoneElement, - onInitialRectChange?: ( - arg: Pick, - ) => void, -) { - const { disabled: isDisabled, pictureSize, src } = useDrawZone2() - const { positionLeft, positionTop, scale, setPosition } = - useDrawZone2PrivateState() - const { move, markerVisible, redraw } = useControls() - const [svg, setSvg] = useState() - - let startPosition: Point | null - let overlayRect: Rect | undefined - let overlayRect2: Rect | undefined - let poly: Polygon | undefined - let tmpPoly: Polyline | undefined - let tmpPoints: Array | undefined - let dragging: boolean - - const convertForOnChange = useCallback( - function convertForOnChange(): DrawZoneElement[] { - if (!svg) { - return [] - } - - const svgRect = svg.node.getBoundingClientRect() - - return svg - .children() - .filter((e) => !e.attr('data-draw-ignore')) - .map((elt) => { - const elementRect = elt.node.getBoundingClientRect() - - const rect: DrawZoneElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } - - const polygon = elt as Polygon - const points: Point[] = getAbsoluteCoordinates( - svg, - polygon - .plot() - .map((p) => ({ - x: p[0], - y: p[1], - })) - .filter( - (_, index) => - shape === 'poly' || index % 2 === 0, - ), - ) - - const result = { - points, - rect, - label: elt.data('label'), - selected: elt.data('selected') as boolean, - id: elt.data('id'), - color: elt.data('color'), - } - - return result - }) - }, - [shape, svg], - ) - - const innerOnChange = useCallback( - function innerOnChange() { - onChange(convertForOnChange()) - }, - [convertForOnChange, onChange], - ) - - /* - function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Delete' && this.closest('svg')) { - event.preventDefault() - remove(this.dataset['id'] as string) - - return true - } - - return false - } - - function onEscKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Escape' && this.closest('svg')) { - event.preventDefault() - ;(this as unknown as LinkedHTMLElement).instance.fire('deselect') - - return true - } - - return false - } - - function endPolyDrawing(event: Event) { - if (!svg || !poly) return - - const svgRect = svg.node.getBoundingClientRect() - const plot = poly.plot() - - if (plot.length < 3) return - - event.preventDefault() - - poly.remove() - poly = undefined - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const newPoly = drawPoly({ - points: plot.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { capture: true }) - - if (newPoly) { - newPoly.fire('select') - } - - innerOnChange() - } - - function onEnterKeyPress(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Enter') { - event.preventDefault() - - endPolyDrawing(event) - } - } - - function onAbortPathDrawing(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Escape') { - event.preventDefault() - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - if (poly) { - poly.remove() - poly = undefined - } - - startPosition = null - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - } - } - * - - const preventDrag = useCallback((event: DragEvent) => { - event.preventDefault() - }, []) - - function drawRect({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, - - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: StrokeData - fill?: FillData - label?: string - id?: string | null - }) { - const minX = Math.min(points[0].x, points[1].x) - const minY = Math.min(points[0].y, points[1].y) - const maxX = Math.max(points[0].x, points[1].x) - const maxY = Math.max(points[0].y, points[1].y) - - const newPoints: Point[] = [ - { x: minX, y: minY }, - { x: maxX, y: minY }, - { x: maxX, y: maxY }, - { x: minX, y: maxY }, - ] - - return drawPoly({ - points: newPoints, - disabled, - stroke, - fill, - label, - id, - }) - } - - function drawPoly({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: StrokeData - fill?: FillData - label?: string - id?: string | null - }) { - if (!svg || !points || points.length < 2) { - return - } - const svgRect = svg.node.getBoundingClientRect() - - const svgWidth = svgRect.width || pictureSize?.width || 0 - const svgHeight = svgRect.height || pictureSize?.height || 0 - - const poly = svg.polygon( - points.map( - (point) => [point.x * svgWidth, point.y * svgHeight] as ArrayXY, - ), - ) - - poly.fill(fill) - poly.stroke(stroke) - poly.css('touch-action', 'none') // silence interactjs warning. - - let rootMatrix: DOMMatrix - const circles: Circle[] = [] - const handles: Use[] = [] - - /* - function polyDelKeyPress(ev: KeyboardEvent) { - const result = onDelKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - } - } - function polyEscKeyPress(ev: KeyboardEvent) { - const result = onEscKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - } - } - * - - function makeHandlesGrabbable(svg: Svg) { - interact('.point-handle') - .draggable({ - onstart: function (event) { - svg.css('cursor', 'grabbing') - event.target.instance.css('cursor', 'grabbing') - }, - onmove: function (event) { - const i = event.target.getAttribute('data-index') | 0 - const point = poly.node.points.getItem(i) - - point.x += event.dx / rootMatrix.a - point.y += event.dy / rootMatrix.d - - if (shape === 'rect') { - switch (i) { - case 0: // top left - handles[0].x(point.x) - handles[0].y(point.y) - handles[1].y(point.y) - handles[3].x(point.x) - break - case 1: // top right - handles[1].x(point.x) - handles[1].y(point.y) - handles[2].x(point.x) - handles[0].y(point.y) - break - case 2: // bottom right - handles[2].x(point.x) - handles[2].y(point.y) - handles[3].y(point.y) - handles[1].x(point.x) - break - case 3: // bottom left - handles[3].x(point.x) - handles[3].y(point.y) - handles[0].x(point.x) - handles[2].y(point.y) - break - } - - const newPlot = handles.map( - (h) => - [Number(h.x()), Number(h.y())] as ArrayXY, - ) - poly.plot(newPlot) - } else { - event.target.x.baseVal.value = point.x - event.target.y.baseVal.value = point.y - } - }, - onend: function (event) { - event.target.instance.css('cursor', 'grab') - svg.css('cursor', 'crosshair') - - const index = Number( - event.target.getAttribute('data-index') || 0, - ) - - if (shape === 'poly') { - const currentPlot = [...poly.plot()] - - const newPlot: ArrayXY[] = [ - ...currentPlot.slice(0, index), - [ - Number(event.target.getAttribute('x')), - Number(event.target.getAttribute('y')), - ] as ArrayXY, - ...currentPlot.slice(index + 1), - ] - - poly.plot(newPlot) - } - - svg.node.setAttribute('class', '') - - innerOnChange() - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - }), - ], - }) - .styleCursor(false) - } - - function cleanHandles() { - interact('.point-handle').unset() - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((circle) => circle.remove()) - circles.length = 0 - document.removeEventListener('dragstart', preventDrag) - } - - function createHandles(svg: Svg) { - rootMatrix = svg.node.getScreenCTM() as DOMMatrix - - for (let i = 0; i < poly.node.points.numberOfItems; i++) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .css('touch-action', 'none') // silence interactjs warning. - .id(handleId) - - const handle = svg - .use(circle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(circle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) - - document.addEventListener('dragstart', preventDrag) - } - - // Custom events. - poly.on('select', () => { - // Deselect all - svg.each(function (this: Svg) { - this.fire('deselect', { inst: poly }) - }) - poly.stroke({ color: blue }) - poly.data('selected', true) - /* - window.addEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.addEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - * - - if (!disabled) { - cleanHandles() - createHandles(svg) - } - - innerOnChange() - }) - poly.on('deselect', (e) => { - if ((e as CustomEvent).detail?.inst === poly) return - poly.stroke(stroke) - poly.data('selected', false) - - cleanHandles() - - /* - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - * - - innerOnChange() - }) - - if (!disabled) { - poly.css('cursor', 'move') - - interact(poly.node).draggable({ - listeners: { - start() { - cleanHandles() - }, - move(event) { - const x = parseFloat(event.target.instance.x()) - const y = parseFloat(event.target.instance.y()) - - event.target.instance.x(x + event.dx) - event.target.instance.y(y + event.dy) - - innerOnChange() - }, - end() { - createHandles(svg) - }, - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, - }), - ], - cursorChecker: (action, interactable, element, interacting) => { - switch (action.axis) { - case 'x': - return 'ew-resize' - case 'y': - return 'ns-resize' - default: - return interacting ? 'grabbing' : 'move' - } - }, - }) - } - - poly.data('disabled', disabled) - poly.data('id', id || uuid4()) - poly.data('label', label) - - if (defaultStroke.color !== stroke.color) - poly.data('color', stroke.color) - - return poly - } - - function drawPoint(svg: Svg, x: number, y: number): Circle { - const delta = CIRCLE_SIZE / 2 - - const point = svg - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: '#f06' }) - .stroke({ width: 1, color: '#fff' }) - .attr('data-draw-ignore', true) - .addClass('tmp-point') - .move(x - delta, y - delta) - .css('touch-action', 'none') // silence interactjs warning. - - point.on('pointerdown', function (event) { - event.preventDefault() - event.stopPropagation() - - //endPolyDrawing(event) - }) - - return point - } - - function draw({ id, points, color, label }: DrawZoneElement) { - const hexColor = color ? bgrToHex(...color) : null - - const fill = hexColor - ? { ...defaultFill, color: hexColor } - : defaultFill - const stroke = hexColor - ? { ...defaultStroke, color: hexColor } - : defaultStroke - - if (points.length === 2) drawRect({ points, id, fill, stroke, label }) - else drawPoly({ points, id, fill, stroke, label }) - } - - function onPointerDown(e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (!svg.node.contains(e.target as Node)) return - - if ( - svg.node.contains(e.target as Node) && - svg.node !== e.target && - !startPosition - ) { - const element = e.target as unknown as LinkedHTMLElement - - element.instance.fire('select') - return - } else if (e.target === svg.node) { - svg.each(function (this: Svg) { - this.fire('deselect') - }) - innerOnChange() - } - - if (move) { - startPosition = { - x: e.clientX, - y: e.clientY, - } - - dragging = true - - svg.css({ - cursor: 'grabbing', - }) - - e.preventDefault() - } else if (mode === 'draw' && !isDisabled) { - const svgRect = svg.node.getBoundingClientRect() - - if (shape === 'poly' && !isDisabled) { - if (!tmpPoints) { - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - tmpPoints = [ - drawPoint(svg, startPosition.x, startPosition.y), - ] - - /* - window.addEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.addEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - * - } else if (startPosition && Array.isArray(tmpPoints)) { - const currentPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - if (poly) { - poly.remove() - poly = undefined - } - - const start = prev[0] - if ( - prev.length > 2 && - Math.abs(currentPosition.x - start[0]) <= 10 && - Math.abs(currentPosition.y - start[1]) <= 10 - ) { - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const poly = drawPoly({ - points: prev.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - /* - window.removeEventListener( - 'keyup', - onAbortPathDrawing, - { - capture: true, - }, - ) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - * - - poly?.fire('select') - - innerOnChange() - } else { - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - poly = svg - .polygon(plotline) - .fill({ - color: '#f06', - opacity: 0, - }) - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - }) - .attr('data-draw-ignore', true) - - tmpPoints.push( - drawPoint( - svg, - currentPosition.x, - currentPosition.y, - ), - ) - - tmpPoints.forEach((p) => p.front()) - - startPosition = currentPosition - } - } - } else { - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (!overlayRect || !overlayRect2) { - overlayRect = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#000', - width: 2, - opacity: 0.7, - dasharray: '5,5', - }) - overlayRect2 = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#fff', - width: 2, - opacity: 0.7, - dasharray: '5,5', - dashoffset: 5, - }) - } - - overlayRect.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - overlayRect2.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - e.preventDefault() - } - } - } - - function onPointerMove(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg || !startPosition) return - - if (move && dragging) { - if (!svg.node.contains(e.target as Node)) { - dragging = false - return - } - const parent = svg.parent() - - if (!parent) return - - const currentPosition = { - x: e.clientX, - y: e.clientY, - } - const translationX = currentPosition.x - startPosition.x - const translationY = currentPosition.y - startPosition.y - - parent.css( - 'transform', - `translate3d(${translationX}px, ${translationY}px, 0px)`, - ) - } else if (mode === 'draw' && !isDisabled) { - if (shape === 'poly' && !isTouchDevice) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition: Point = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - tmpPoly = svg - .polyline(plotline) - .fill('none') - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - dasharray: '5,5', - }) - .attr('data-draw-ignore', true) - - if (Array.isArray(tmpPoints)) { - tmpPoints.forEach((point) => point.front()) - } - } else if (overlayRect && overlayRect2) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition = { - x: - Math.max( - Math.min(e.clientX, svgRect.right), - svgRect.left, - ) - svgRect.left, - y: - Math.max( - Math.min(e.clientY, svgRect.bottom), - svgRect.top, - ) - svgRect.top, - } - - const minX = - Math.min(startPosition.x, currentPosition.x) / svgRect.width - const minY = - Math.min(startPosition.y, currentPosition.y) / - svgRect.height - const maxX = - Math.max(startPosition.x, currentPosition.x) / svgRect.width - const maxY = - Math.max(startPosition.y, currentPosition.y) / - svgRect.height - - const width = Math.abs(maxX - minX) - const height = Math.abs(maxY - minY) - - overlayRect.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect.width(`${width * 100}%`) - overlayRect.height(`${height * 100}%`) - overlayRect2.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect2.width(`${width * 100}%`) - overlayRect2.height(`${height * 100}%`) - } - } - } - - function onPointerUp(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (move && dragging) { - const parent = svg.parent() - - if (parent) { - const grandParent = parent.parent() - - if (grandParent) { - const parentRect = grandParent.node.getBoundingClientRect() - const svgRect = parent.node.getBoundingClientRect() - - setPosition( - svgRect.top - parentRect.top, - svgRect.left - parentRect.left, - ) - - svg.css({ cursor: 'grab' }) - - dragging = false - } - } - } else if (mode === 'draw' && shape === 'rect' && !isDisabled) { - if (!startPosition) { - return - } - - if (overlayRect || overlayRect2) { - overlayRect?.remove() - overlayRect = undefined - overlayRect2?.remove() - overlayRect2 = undefined - } - - // Prevent drawing new rect on rect dragend... - if ((e.target as Node | null)?.parentNode === svg.node) { - startPosition = null - return - } - - const svgRect = svg.node.getBoundingClientRect() - const currentPosition = { - x: - Math.max(Math.min(e.clientX, svgRect.right), svgRect.left) - - svgRect.left, - y: - Math.max(Math.min(e.clientY, svgRect.bottom), svgRect.top) - - svgRect.top, - } - - let label = undefined - // Prevent adding very small rects (mis-clicks). - if (Math.abs(currentPosition.x - startPosition.x) <= 2) { - const elements = convertForOnChange() - let lastRect = elements[elements.length - 1] - - if (initialRect) { - lastRect = initialRect - } - - label = lastRect?.label - - if (drawOnMouseDown && lastRect && lastRect.rect) { - currentPosition.x = Math.min( - startPosition.x + - (lastRect.rect.width * svgRect.width) / 100, - svgRect.width, - ) - currentPosition.y = Math.min( - startPosition.y + - (lastRect.rect.height * svgRect.height) / 100, - svgRect.height, - ) - } else { - startPosition = null - return - } - } - - const newRect = drawRect({ - points: [ - { - x: - Math.min(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.min(startPosition.y, currentPosition.y) / - svgRect.height, - }, - { - x: - Math.max(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.max(startPosition.y, currentPosition.y) / - svgRect.height, - }, - ], - label: label, - }) - - if (newRect && onInitialRectChange) { - const elementRect = newRect.node.getBoundingClientRect() - const rect: DrawZoneElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } - onInitialRectChange({ - rect: rect, - label: newRect.data('label'), - id: newRect.data('id'), - }) - } - - setTimeout(() => { - newRect?.fire('select') - innerOnChange() - }, 50) - } - - if (!move && (mode !== 'draw' || (mode === 'draw' && shape !== 'poly'))) - startPosition = null - } - - useEffect(() => { - if (!ref.current) { - return - } - - ref.current.style.background = `url('${src}') center center / 100% 100% no-repeat` - ref.current.style.top = `${positionTop}px` - ref.current.style.left = `${positionLeft}px` - - if (svg) { - svg.node.remove() - } - - const newSvg = SVG().addTo(ref.current).size('100%', '100%').attr({ - 'xmlns:xlink': xns, - }) - - setSvg(newSvg) - }, [ref, src]) - - useEffect(() => { - if (ref.current && pictureSize && scale) { - ref.current.style.width = `${pictureSize.width * scale}px` - - ref.current.style.height = `${pictureSize.height * scale}px` - - if (svg) { - redraw() - } - } - }, [ref, pictureSize, scale]) - - useLayoutEffect(() => { - if (!svg) { - return - } - - svg.css({ - cursor: - move && !isDisabled - ? 'grab' - : mode === 'none' && !markerVisible - ? 'normal' - : 'crosshair', - position: 'absolute', - top: '0', - left: '0', - }) - - const parent = svg.parent() - - if (parent) { - parent.css({ - position: 'relative', - userSelect: 'none', - transform: 'none', - }) - } - - svg.on('pointerdown', onPointerDown as unknown as EventListener) - window.addEventListener('pointerup', onPointerUp) - window.addEventListener('pointermove', onPointerMove) - - return () => { - svg.off('pointerdown', onPointerDown as unknown as EventListener) - window.removeEventListener('pointerup', onPointerUp) - window.removeEventListener('pointermove', onPointerMove) - } - }, [svg, mode, initialRect]) - - return { svg, draw } -} -*/ diff --git a/src/DrawZone2/index.stories.tsx b/src/DrawZone2/index.stories.tsx deleted file mode 100644 index 25e7a52..0000000 --- a/src/DrawZone2/index.stories.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React, { - FunctionComponent, - useCallback, - useEffect, - useMemo, - useState, -} from 'react' - -import DrawZone2, { DrawZone2Container, useControls, useLoadImage } from '.' -import type { DrawZoneElement } from '.' -import { isEmpty } from 'lodash' -import { DrawZone2Shape, PictureLoadingState } from './types' - -export default { - title: 'Components/DrawZone2', - component: DrawZone2, - decorators: [ - (Story: FunctionComponent) => ( -
- -
- ), - ], - parameters: { - layout: 'fullscreen', - }, -} - -function Controls() { - const { - contentHidden, - markerVisible, - move, - reset, - toggleContent, - toggleMarker, - toggleMove, - zoomIn, - zoomOut, - } = useControls() - return ( -
- - - - - - -
- ) -} - -interface StoryZoneElement extends DrawZoneElement { - readonly metadata: Record -} - -type EditorProps = { - readonly initialElements: StoryZoneElement[] - readonly shape: DrawZone2Shape - readonly src: string -} -function Editor({ initialElements, shape, src }: EditorProps) { - const { status, pictureSize } = useLoadImage(src) - const [elements, setElements] = useState>([]) - - useEffect(() => { - setElements(initialElements) - }, [initialElements]) - - const buildMetas = useCallback( - (elem: DrawZoneElement) => { - if (!pictureSize || shape !== 'rect') return {} - - return { - width: Math.floor( - (elem.rect.width * pictureSize.width) / 100, - ).toString(), - height: Math.floor( - (elem.rect.height * pictureSize.height) / 100, - ).toString(), - diagonal: Math.floor( - Math.hypot( - (elem.rect.height * pictureSize.height) / 100, - (elem.rect.width * pictureSize.width) / 100, - ), - ).toString(), - area: Math.floor( - ((elem.rect.width * pictureSize.width) / 100) * - ((elem.rect.height * pictureSize.height) / 100), - ).toString(), - } as Record - }, - [pictureSize, shape], - ) - - const onChange = useCallback( - (elements: DrawZoneElement[]) => { - const newElements = elements.map((elem) => { - return { - ...elem, - metadata: buildMetas(elem), - } - }) - - setElements([...newElements]) - }, - [buildMetas, setElements], - ) - - const element = useMemo( - () => elements.find(({ selected }) => selected), - [elements], - ) - - return ( - <> -
-
-                    {JSON.stringify(elements)}
-                
-
-
- {element && !isEmpty(element.metadata) && ( -
-                        {JSON.stringify(element.metadata)}
-                    
- )} -
- -
- {status === PictureLoadingState.Done && ( - - )} -
- - ) -} - -export function Default() { - return ( - - - - - ) -} - -export function Poly() { - return ( - - - - - ) -} diff --git a/src/DrawZone2/index.tsx b/src/DrawZone2/index.tsx deleted file mode 100644 index 72014c7..0000000 --- a/src/DrawZone2/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import DrawZone2, { DrawZone2Container } from './components' -import type { - DrawZone2Mode, - DrawZone2Shape, - DrawZone2State, - DrawZoneElement, - DrawZoneFitMode, - Point, - Rect, - Size, -} from './types' -import { useControls, useLoadImage } from './hooks' - -export default DrawZone2 -export { DrawZone2Container, DrawZone2, useControls, useLoadImage } -export type { - Point, - Rect, - Size, - DrawZoneElement, - DrawZone2State, - DrawZone2Mode, - DrawZone2Shape, - DrawZoneFitMode, -} diff --git a/src/DrawZone2/state.ts b/src/DrawZone2/state.ts deleted file mode 100644 index c840305..0000000 --- a/src/DrawZone2/state.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { noop } from 'lodash' -import { createContext } from 'react' -import { DRAW_ZONE_2_INITIAL_STATE } from './constants' -import { DrawZone2StateContext } from './types' - -export const DrawZone2Context = createContext({ - state: { ...DRAW_ZONE_2_INITIAL_STATE }, - setState: noop, -}) diff --git a/src/DrawZone2/types.ts b/src/DrawZone2/types.ts deleted file mode 100644 index e5341d3..0000000 --- a/src/DrawZone2/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Dispatch, SetStateAction } from 'react' -import { BGR } from 'src/types' - -export type DrawZone2Mode = 'draw' | 'none' -export type DrawZone2Shape = 'rect' | 'poly' | 'none' -export type DrawZoneFitMode = 'auto' | 'fit' - -export interface Size { - readonly width: number - readonly height: number -} -export interface Point { - readonly x: number - readonly y: number -} -export interface Rect extends Size, Point {} - -export interface DrawZoneElement { - readonly id: string | undefined - readonly selected?: boolean - readonly points: Point[] - readonly label: string - readonly color?: BGR - readonly rect: Rect -} - -export type DrawZone2State = { - readonly contentHidden: boolean - readonly logicalScale: number - readonly markerVisible: boolean - readonly move: boolean - readonly positionTop: number - readonly positionLeft: number - readonly scale: number -} -export type DrawZone2StateContext = { - readonly state: DrawZone2State - readonly setState: Dispatch< - SetStateAction> - > -} - -export enum PictureLoadingState { - Idle, - Loading, - Error, - Done, -} diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 4e472d7..2bfd33f 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -1,147 +1,174 @@ import React, { - ReactNode, + PropsWithChildren, useCallback, - useContext, useEffect, - useLayoutEffect, - useReducer, + useMemo, useRef, useState, } from 'react' -import { usePointerPosition } from '../hooks' -import { useDraw } from './hooks' -import { DrawZoneContext, drawZoneInitialState, drawZoneReducer } from './state' +import { useId, usePointerPosition, useSetState } from '../hooks' +import { DRAW_ZONE_INITIAL_STATE } from './constants' +import { useControls, useLoadImage } from './hooks' +import { DrawZone2Context } from './state' import { - ChangedElement, + DrawZoneElement, + DrawZoneFitMode, DrawZoneMode, DrawZoneShape, DrawZoneState, - DrawZoneStateActionType, + PictureLoadingState, + Point, Size, - SizeMode, } from './types' +import interact from 'interactjs' +import { + ArrayXY, + Circle, + PointArrayAlias, + Polygon, + Polyline, + Rect, + SVG, + Svg, + Use, +} from '@svgdotjs/svg.js' +import { uuid4 } from '../helpers' +import { isTouchDevice } from '../utils' + +import { Interactable } from '@interactjs/types' + +const xns = 'http://www.w3.org/1999/xlink' +const CIRCLE_SIZE = isTouchDevice ? 22 : 10 +const blue = '#2BB1FD' + +const preventDrag = (event: DragEvent) => { + event.preventDefault() +} + +export function DrawZoneContainer({ children }: PropsWithChildren) { + const [state, setState] = useSetState({ + ...DRAW_ZONE_INITIAL_STATE, + }) + return ( + + {children} + + ) +} -export interface DrawZoneProps { - readonly children?: React.ReactNode - readonly mode?: DrawZoneMode +type DrawZoneProps = PropsWithChildren<{ + readonly disabled?: boolean + readonly drawOnPointerDown?: boolean + readonly elements: DrawZoneElement[] + readonly fitMode: DrawZoneFitMode + readonly initialRect?: Pick + readonly mode: DrawZoneMode readonly shape: DrawZoneShape - readonly sizeMode?: SizeMode readonly src: string - readonly elements: Partial[] - readonly initialRect?: ChangedElement - readonly onChange: (elements: ChangedElement[]) => void - readonly remove: (id: string) => void + readonly onChange: (elements: DrawZoneElement[]) => void readonly onInitialRectChange?: ( - arg: Pick, + arg: Pick, ) => void - readonly drawOnMouseDown?: boolean +}> +export default function DrawZone({ children, src, ...props }: DrawZoneProps) { + const { status, pictureSize } = useLoadImage(src) + + if (status === PictureLoadingState.Error) + throw new Error('Failed to load image') + + if (status !== PictureLoadingState.Done) return null + + return ( + + {children} + + ) } -export default function DrawZone({ +function filterPoints(shape: DrawZoneShape) { + return function filter(_: Point, index: number, arr: Point[]): boolean { + if (arr.length === 2) return true + + return shape === 'poly' || index % 2 === 0 + } +} + +type DrawZoneInnerProps = DrawZoneProps & { + readonly pictureSize: Size +} +function DrawZoneInner({ children, - mode = 'draw', + disabled, + drawOnPointerDown, + elements, + fitMode, + initialRect, + mode, + pictureSize, shape, - sizeMode = 'auto', src, - elements, onChange, - remove, - initialRect, onInitialRectChange, - drawOnMouseDown, -}: DrawZoneProps) { +}: DrawZoneInnerProps) { const { - state: { - scale, - isMarkerShown, - positionTop, - positionLeft, - redraw, - logicalScale, - }, - dispatch, - } = useContext(DrawZoneContext) - const svgRef = useRef(null) + markerVisible, + positionTop, + positionLeft, + logicalScale, + scale, + setScale, + } = useControls() + const svgRef = useRef(null) + const svgContainerRef = useRef(null) const containerRef = useRef(null) - const { svg, draw, originalSize } = useDraw( - svgRef, - src, - onChange, - remove, - mode, - shape, - drawOnMouseDown, - initialRect, - onInitialRectChange, - ) const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) - const [forceRedraw, setForceRedraw] = useState(false) - - const setScale = useCallback( - (scale: number) => { - dispatch({ - type: DrawZoneStateActionType.SET_SCALE, - payload: scale, - }) - }, - [dispatch], - ) - - useEffect(() => { - dispatch({ - type: DrawZoneStateActionType.SET_ORIGINAL_SIZE, - payload: { ...originalSize } as Size, - }) - dispatch({ - type: DrawZoneStateActionType.FORCE_REDRAW, - }) - }, [dispatch, originalSize]) useEffect(() => { - if (sizeMode === 'auto') { + if (fitMode === 'auto') { setScale(logicalScale) - } else if (containerRef.current && originalSize) { + } else if (containerRef.current && pictureSize) { const rect = containerRef.current.getBoundingClientRect() - const minWidth = Math.min(rect.width, originalSize.width) - const minHeight = Math.min(rect.height, originalSize.height) + const minWidth = Math.min(rect.width, pictureSize.width) + const minHeight = Math.min(rect.height, pictureSize.height) if ( - originalSize.width <= minWidth && - originalSize.height <= minHeight + pictureSize.width <= minWidth && + pictureSize.height <= minHeight ) { - const maxWidth = Math.max(rect.width, originalSize.width) - const maxHeight = Math.max(rect.height, originalSize.height) + const maxWidth = Math.max(rect.width, pictureSize.width) + const maxHeight = Math.max(rect.height, pictureSize.height) const coef = maxWidth / minWidth const coef2 = maxHeight / minHeight - if (originalSize.height * coef <= maxHeight) { + if (pictureSize.height * coef <= maxHeight) { setScale(coef * logicalScale) - } else if (originalSize.width * coef2 <= maxWidth) { + } else if (pictureSize.width * coef2 <= maxWidth) { setScale(coef2 * logicalScale) } else { setScale(logicalScale) } } else if ( - minWidth < originalSize.width || - minHeight < originalSize.height + minWidth < pictureSize.width || + minHeight < pictureSize.height ) { setScale( Math.min( - minWidth / originalSize.width, - minHeight / originalSize.height, + minWidth / pictureSize.width, + minHeight / pictureSize.height, ) * logicalScale, ) } } - }, [containerRef, originalSize, sizeMode, logicalScale, setScale]) + }, [containerRef, pictureSize, fitMode, logicalScale, setScale]) useEffect(() => { - const { current } = svgRef + const { current: svg } = svgRef + const { current } = svgContainerRef const { current: container } = containerRef - if (current && container) { + if (current && container && svg) { const handlePointerEnter = () => { setCanMarkerBeVisible(true) } @@ -160,33 +187,39 @@ export default function DrawZone({ }, []) useEffect(() => { - if (svgRef.current) { - svgRef.current.style.top = `${positionTop}px` - svgRef.current.style.left = `${positionLeft}px` - svgRef.current.style.transform = 'none' + if (svgContainerRef.current) { + svgContainerRef.current.style.top = `${positionTop}px` + svgContainerRef.current.style.left = `${positionLeft}px` + svgContainerRef.current.style.transform = 'none' } }, [positionTop, positionLeft]) - useEffect(() => { - setForceRedraw(true) - }, [mode, redraw]) + const localOnChange = useCallback( + (elements: DrawZoneElement[]) => { + onChange( + elements.map((element) => { + const minX = Math.min(...element.points.map(({ x }) => x)) + const minY = Math.min(...element.points.map(({ y }) => y)) + const maxX = Math.max(...element.points.map(({ x }) => x)) + const maxY = Math.max(...element.points.map(({ y }) => y)) - useLayoutEffect(() => { - if (svg) { - if ( - elements.length !== - svg.children().filter((c) => !c.attr('data-draw-ignore')) - .length || - forceRedraw - ) { - svg.clear() - elements.forEach((element) => draw(element as ChangedElement)) + const rect: DrawZoneElement['rect'] = { + height: maxY - minY, + width: maxX - minX, + x: minX, + y: minY, + } - if (forceRedraw) setForceRedraw(false) - return - } - } - }, [svg, elements, forceRedraw, scale, draw]) + return { + ...element, + rect, + points: element.points.filter(filterPoints(shape)), + } + }), + ) + }, + [onChange, shape], + ) return (
-
- {canMarkerBeVisible && isMarkerShown && ( - +
+ + {canMarkerBeVisible && markerVisible && ( + )} {children}
@@ -211,8 +264,1089 @@ export default function DrawZone({ ) } +type SvgZoneProps = { + readonly disabled?: boolean + readonly drawOnPointerDown?: boolean + readonly elements: DrawZoneElement[] + readonly initialRect?: Pick + readonly mode: DrawZoneMode + readonly shape: DrawZoneShape + readonly onChange: (elements: DrawZoneElement[]) => void + readonly onInitialRectChange?: ( + arg: Pick, + ) => void +} +function SvgZone({ + disabled, + drawOnPointerDown, + elements, + initialRect, + mode, + shape, + onChange, + onInitialRectChange, +}: SvgZoneProps) { + const id = useId() + const ref = useRef(null) + const startPosition = useRef() + const dragging = useRef(false) + const overlayRect = useRef() + const overlayRect2 = useRef() + const polyPoints = useRef() + const polygon = useRef() + const polyline = useRef() + const { move, scale, setPosition } = useControls() + + const unselectElements = useCallback(() => { + onChange(elements.map(unSelectElement)) + }, [elements, onChange]) + + const pointOnPointerDown = useCallback( + (event: Event) => { + if (!polygon.current) return + + event.preventDefault() + event.stopPropagation() + + const plot = polygon.current.plot() + + if (plot.length < 3) return + + polygon.current.remove() + polygon.current = undefined + + if (polyPoints.current && polyPoints.current.length > 0) { + polyPoints.current.forEach((p) => p.remove()) + polyPoints.current = undefined + } + + polyline.current?.remove() + polyline.current = undefined + + startPosition.current = undefined + + const points = plot.map(([x, y]) => ({ + x: x / scale, + y: y / scale, + })) + + const xList = points.map(({ x }) => x) + const yList = points.map(({ y }) => y) + + const xMin = Math.min(...xList) + const yMin = Math.min(...yList) + const xMax = Math.max(...xList) + const yMax = Math.max(...yList) + + const newElement: DrawZoneElement = { + id: uuid4(), + label: '', + points, + rect: { + height: yMax - yMin, + width: xMax - xMin, + x: xMin, + y: yMin, + }, + selected: true, + } + + onChange([...elements.map(unSelectElement), newElement]) + }, + [elements, onChange, scale], + ) + + const drawPoint = useCallback( + function drawPoint(svg: Svg, x: number, y: number): Circle { + const delta = CIRCLE_SIZE / 2 + + const point = svg + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: '#f06' }) + .stroke({ width: 1, color: '#fff' }) + .attr('data-draw-ignore', true) + .addClass('tmp-point') + .move(x - delta, y - delta) + .css('touch-action', 'none') // silence interactjs warning. + + point.on('pointerdown', pointOnPointerDown) + + return point + }, + [pointOnPointerDown], + ) + + useEffect(() => { + const { current } = ref + + if (!current) return + + const svg = SVG(current) as Svg + + function onPointerDown(e: globalThis.PointerEvent) { + if (!svg.node.contains(e.target as Node)) return + + if ( + svg.node.contains(e.target as Node) && + svg.node !== e.target && + !move && + !startPosition.current + ) { + return + } else if (e.target === svg.node) { + e.preventDefault() + e.stopImmediatePropagation() + + unselectElements() + } + + if (move) { + e.preventDefault() + e.stopImmediatePropagation() + + startPosition.current = { + x: e.clientX, + y: e.clientY, + } + + dragging.current = true + + return + } + + if (mode !== 'draw' || disabled) return + + const svgRect = svg.node.getBoundingClientRect() + + if (shape === 'rect') { + e.preventDefault() + e.stopImmediatePropagation() + + startPosition.current = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + if (!overlayRect.current || !overlayRect2.current) { + overlayRect.current = svg + .rect(0, 0) + .fill({ opacity: 0 }) + .stroke({ + color: '#000', + width: 2, + opacity: 0.7, + dasharray: '5,5', + }) + overlayRect2.current = svg + .rect(0, 0) + .fill({ opacity: 0 }) + .stroke({ + color: '#fff', + width: 2, + opacity: 0.7, + dasharray: '5,5', + dashoffset: 5, + }) + } + + overlayRect.current.move( + startPosition.current.x / svgRect.width, + startPosition.current.y / svgRect.height, + ) + + overlayRect2.current.move( + startPosition.current.x / svgRect.width, + startPosition.current.y / svgRect.height, + ) + + e.preventDefault() + } + + if (shape === 'poly') { + e.preventDefault() + e.stopImmediatePropagation() + + if (!polyPoints.current?.length) { + startPosition.current = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + polyPoints.current = [ + drawPoint( + svg, + startPosition.current.x, + startPosition.current.y, + ), + ] + } else if (startPosition.current) { + const currentPosition = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + const prev = polygon.current + ? [...polygon.current.plot()] + : [ + [ + startPosition.current.x, + startPosition.current.y, + ] as ArrayXY, + ] + + polygon.current?.remove() + polygon.current = undefined + + const start = prev[0] + if ( + prev.length > 2 && + Math.abs(currentPosition.x - start[0]) <= 10 && + Math.abs(currentPosition.y - start[1]) <= 10 + ) { + if ( + polyPoints.current && + polyPoints.current.length > 0 + ) { + polyPoints.current.forEach((p) => p.remove()) + polyPoints.current = undefined + } + + if (polyline.current) { + polyline.current.remove() + polyline.current = undefined + } + + startPosition.current = undefined + + const points = prev.map(([x, y]) => ({ + x: x / scale, + y: y / scale, + })) + + const xList = points.map(({ x }) => x) + const yList = points.map(({ y }) => y) + + const xMin = Math.min(...xList) + const yMin = Math.min(...yList) + const xMax = Math.max(...xList) + const yMax = Math.max(...yList) + + const newElement: DrawZoneElement = { + id: uuid4(), + label: '', + points, + rect: { + height: yMax - yMin, + width: xMax - xMin, + x: xMin, + y: yMin, + }, + selected: true, + } + + onChange([...elements.map(unSelectElement), newElement]) + } else { + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + polygon.current = svg + .polyline(plotline) + .fill({ + color: '#f06', + opacity: 0, + }) + .stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + }) + .attr('data-draw-ignore', true) + + polyPoints.current.push( + drawPoint( + svg, + currentPosition.x, + currentPosition.y, + ), + ) + + polyPoints.current.forEach((p) => p.front()) + + startPosition.current = currentPosition + } + } + } + } + + function onPointerMove(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented || !startPosition.current) return + + if (move) { + if (!dragging.current) return + if (!svg.node.contains(e.target as Node)) { + dragging.current = false + return + } + + const parent = svg.parent() + + if (!parent) return + + const currentPosition = { + x: e.clientX, + y: e.clientY, + } + const translationX = currentPosition.x - startPosition.current.x + const translationY = currentPosition.y - startPosition.current.y + + parent.css( + 'transform', + `translate3d(${translationX}px, ${translationY}px, 0px)`, + ) + + e.preventDefault() + e.stopImmediatePropagation() + + return + } + + if (mode !== 'draw' || disabled) return + + const svgRect = svg.node.getBoundingClientRect() + + if ( + shape === 'rect' && + overlayRect.current && + overlayRect2.current + ) { + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + const minX = + Math.min(startPosition.current.x, currentPosition.x) / + svgRect.width + const minY = + Math.min(startPosition.current.y, currentPosition.y) / + svgRect.height + const maxX = + Math.max(startPosition.current.x, currentPosition.x) / + svgRect.width + const maxY = + Math.max(startPosition.current.y, currentPosition.y) / + svgRect.height + + const width = Math.abs(maxX - minX) + const height = Math.abs(maxY - minY) + + overlayRect.current.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect.current.width(`${width * 100}%`) + overlayRect.current.height(`${height * 100}%`) + overlayRect2.current.move(`${minX * 100}%`, `${minY * 100}%`) + overlayRect2.current.width(`${width * 100}%`) + overlayRect2.current.height(`${height * 100}%`) + } + + if (shape === 'poly' && !isTouchDevice) { + const svgRect = svg.node.getBoundingClientRect() + + const currentPosition: Point = { + x: e.clientX - svgRect.left, + y: e.clientY - svgRect.top, + } + + polyline.current?.remove() + + const prev = polygon.current + ? [...polygon.current.plot()] + : [ + [ + startPosition.current.x, + startPosition.current.y, + ] as ArrayXY, + ] + + const plotline: PointArrayAlias = [ + ...prev, + [currentPosition.x, currentPosition.y], + ] + + polyline.current = svg.polyline(plotline).fill('none').stroke({ + color: '#f06', + width: 1, + linecap: 'round', + linejoin: 'round', + dasharray: '5,5', + }) + + if (Array.isArray(polyPoints.current)) { + polyPoints.current.forEach((p) => p.front()) + } + } + } + + function onPointerUp(this: Window, e: globalThis.PointerEvent) { + if (e.defaultPrevented) return + + if (move) { + if (!dragging.current) return false + + const parent = svg.parent() + + if (!parent) return + const grandParent = parent.parent() + + if (!grandParent) return + + const parentRect = grandParent.node.getBoundingClientRect() + const svgRect = parent.node.getBoundingClientRect() + + setPosition( + svgRect.top - parentRect.top, + svgRect.left - parentRect.left, + ) + + svg.css({ cursor: 'grab' }) + + dragging.current = false + + return + } + + if (mode === 'draw' && shape === 'rect' && !disabled) { + if (!startPosition.current) return + + if (overlayRect.current || overlayRect2.current) { + overlayRect.current?.remove() + overlayRect.current = undefined + overlayRect2.current?.remove() + overlayRect2.current = undefined + } + + // Prevent drawing new rect on rect dragend... + if ((e.target as Node | null)?.parentNode === svg.node) { + startPosition.current = undefined + return + } + + const svgRect = svg.node.getBoundingClientRect() + const currentPosition = { + x: + Math.max( + Math.min(e.clientX, svgRect.right), + svgRect.left, + ) - svgRect.left, + y: + Math.max( + Math.min(e.clientY, svgRect.bottom), + svgRect.top, + ) - svgRect.top, + } + + let label = '' + if ( + Math.abs(currentPosition.x - startPosition.current.x) <= 2 + ) { + let lastRect: Pick< + DrawZoneElement, + 'id' | 'label' | 'rect' + > = elements[elements.length - 1] + + if (initialRect) { + lastRect = initialRect + } + + label = lastRect?.label + + if (drawOnPointerDown && lastRect && lastRect.rect) { + currentPosition.x = Math.min( + startPosition.current.x + + (lastRect.rect.width * svgRect.width) / 100, + svgRect.width, + ) + currentPosition.y = Math.min( + startPosition.current.y + + (lastRect.rect.height * svgRect.height) / 100, + svgRect.height, + ) + } else { + startPosition.current = undefined + return + } + } + + const xMin = + Math.min(startPosition.current.x, currentPosition.x) / scale + const yMin = + Math.min(startPosition.current.y, currentPosition.y) / scale + const xMax = + Math.max(startPosition.current.x, currentPosition.x) / scale + const yMax = + Math.max(startPosition.current.y, currentPosition.y) / scale + + const width = xMax - xMin + const height = yMax - yMin + + const newElement: DrawZoneElement = { + id: uuid4(), + label, + points: [ + { + x: xMin, + y: yMin, + }, + { + x: xMax, + y: yMax, + }, + ], + rect: { + height, + width, + x: xMin, + y: yMin, + }, + selected: true, + } + + if (onInitialRectChange) { + onInitialRectChange({ + rect: newElement.rect, + label: newElement.label, + id: newElement.id, + }) + } + + onChange([...elements.map(unSelectElement), newElement]) + } + + if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) + startPosition.current = undefined + } + + svg.on('pointerdown', onPointerDown as unknown as EventListener) + window.addEventListener('pointerup', onPointerUp) + window.addEventListener('pointermove', onPointerMove) + + return () => { + svg.off('pointerdown', onPointerDown as unknown as EventListener) + window.removeEventListener('pointerup', onPointerUp) + window.removeEventListener('pointermove', onPointerMove) + } + }, [ + disabled, + drawOnPointerDown, + drawPoint, + elements, + initialRect, + mode, + move, + onChange, + onInitialRectChange, + scale, + setPosition, + shape, + unselectElements, + ]) + + return ( + + + + ) +} + +type SvgElementsProps = { + readonly disabled?: boolean + readonly elements: DrawZoneElement[] + readonly shape: DrawZoneShape + readonly onChange: (elements: DrawZoneElement[]) => void +} +function SvgElements({ + disabled, + elements, + shape, + onChange, +}: SvgElementsProps) { + return ( + <> + {elements.map((element) => ( + + ))} + + ) +} + +type DrawElementProps = { + readonly disabled?: boolean + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly shape: DrawZoneShape + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawElement({ + disabled, + element, + elements, + shape, + onChange, +}: DrawElementProps) { + if (element.points?.length === 2) { + return ( + + ) + } + + return ( + + ) +} + +type DrawRectElementProps = { + readonly disabled?: boolean + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawRectElement({ + disabled, + element, + elements, + onChange, +}: DrawRectElementProps) { + const minX = useMemo( + () => Math.min(element.points[0].x, element.points[1].x), + [element.points], + ) + const minY = useMemo( + () => Math.min(element.points[0].y, element.points[1].y), + [element.points], + ) + const maxX = useMemo( + () => Math.max(element.points[0].x, element.points[1].x), + [element.points], + ) + const maxY = useMemo( + () => Math.max(element.points[0].y, element.points[1].y), + [element.points], + ) + + const newPoints: Point[] = useMemo( + () => [ + { x: minX, y: minY }, + { x: maxX, y: minY }, + { x: maxX, y: maxY }, + { x: minX, y: maxY }, + ], + [maxX, maxY, minX, minY], + ) + + const newElement = useMemo( + () => ({ + ...element, + points: newPoints, + }), + [element, newPoints], + ) + + return ( + + ) +} + +type DrawPolygonElementProps = { + readonly disabled?: boolean + readonly element: DrawZoneElement + readonly elements: DrawZoneElement[] + readonly shape: DrawZoneShape + readonly onChange: (elements: DrawZoneElement[]) => void +} +function DrawPolygonElement({ + disabled, + element, + elements, + shape, + onChange, +}: DrawPolygonElementProps) { + const ref = useRef(null) + const { move, scale } = useControls() + const instance = useRef() + const handlesInstance = useRef() + const handles = useRef([]) + const circles = useRef([]) + + const path = element.points + .map((point) => [point.x * scale, point.y * scale].join(',')) + .join(' ') + + const cleanHandles = useCallback(() => { + handlesInstance.current?.unset() + handlesInstance.current = undefined + + handles.current.forEach((h) => h.remove()) + handles.current.length = 0 + + circles.current.forEach((h) => h.remove()) + circles.current.length = 0 + + document.removeEventListener('dragstart', preventDrag) + }, []) + + const makeHandlesGrabbable = useCallback(() => { + const { current } = ref + + if (!current) return + + const parent = current.parentNode as SVGSVGElement + const svg = (parent as unknown as Record).instance + const rootMatrix = parent.getScreenCTM() as DOMMatrix + + handlesInstance.current?.unset() + + handlesInstance.current = interact('.point-handle') + .draggable({ + onstart: function (event) { + svg.css('cursor', 'grabbing') + event.target.instance.css('cursor', 'grabbing') + }, + onmove: function (event) { + const i = event.target.getAttribute('data-index') | 0 + const point = current.points.getItem(i) + + point.x += event.dx / rootMatrix.a + point.y += event.dy / rootMatrix.d + + if (shape === 'rect') { + switch (i) { + case 0: // top left + handles.current[0].x(point.x) + handles.current[0].y(point.y) + handles.current[1].y(point.y) + handles.current[3].x(point.x) + break + case 1: // top right + handles.current[1].x(point.x) + handles.current[1].y(point.y) + handles.current[2].x(point.x) + handles.current[0].y(point.y) + break + case 2: // bottom right + handles.current[2].x(point.x) + handles.current[2].y(point.y) + handles.current[3].y(point.y) + handles.current[1].x(point.x) + break + case 3: // bottom left + handles.current[3].x(point.x) + handles.current[3].y(point.y) + handles.current[0].x(point.x) + handles.current[2].y(point.y) + break + } + + const newPlot = handles.current.map( + (h) => [Number(h.x()), Number(h.y())] as ArrayXY, + ) + + const pointsCount = current.points.numberOfItems + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + point.x = newPlot[i][0] + point.y = newPlot[i][1] + } + } else { + event.target.x.baseVal.value = point.x + event.target.y.baseVal.value = point.y + } + }, + onend: function (event) { + event.target.instance.css('cursor', 'grab') + svg.css('cursor', 'crosshair') + + svg.node.setAttribute('class', '') + + const pointsCount = current.points.numberOfItems + const points = new Array() + + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + points.push({ + x: point.x / scale, + y: point.y / scale, + }) + } + + const index = elements.findIndex( + (elem) => elem.id === element.id, + ) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + points: points, + selected: true, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + onChange(newElements) + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + }), + ], + }) + .styleCursor(false) + }, [element, elements, onChange, scale, shape]) + + const createHandles = useCallback(() => { + const { current } = ref + + if (!current) return + + const svg = (current.parentNode as unknown as Record) + .instance + + for (let i = 0; i < current.points.numberOfItems; i++) { + const point = current.points.getItem(i) + + const handleId = `point-handle-${i}` + + const circle = svg + .defs() + .attr('data-draw-ignore', true) + .circle(CIRCLE_SIZE) + .center(0, 0) + .fill({ opacity: 1, color: blue }) + .stroke({ width: 1, color: '#fff' }) + .css('touch-action', 'none') // silence interactjs warning. + .id(handleId) + + const handle = svg + .use(circle as Circle) + .attr('href', `#${handleId}`, xns) + .addClass('point-handle') + .data('draw-ignore', true) + .x(point.x) + .y(point.y) + .data('index', i) + + handle + .on('mousedown', function mousedown(event) { + event.preventDefault() + event.stopPropagation() + }) + .css('cursor', 'grab') + + circles.current.push(circle) + handles.current.push(handle) + } + + makeHandlesGrabbable() + + document.addEventListener('dragstart', preventDrag) + }, [makeHandlesGrabbable]) + + const setupInteract = useCallback(() => { + const { current } = ref + if (!current) return + + createHandles() + + instance.current = interact(current as SVGPolygonElement).draggable({ + listeners: { + start() { + cleanHandles() + }, + move(event) { + const dx = event.dx + const dy = event.dy + + const pointsCount = current.points.numberOfItems + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + point.x = point.x + dx + point.y = point.y + dy + } + }, + end() { + createHandles() + const pointsCount = current.points.numberOfItems + const points = new Array() + + for (let i = 0; i < pointsCount; i++) { + const point = current.points.getItem(i) + + points.push({ + x: point.x / scale, + y: point.y / scale, + }) + } + + const index = elements.findIndex( + (elem) => elem.id === element.id, + ) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + points: points, + selected: true, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + onChange(newElements) + }, + }, + modifiers: [ + interact.modifiers.restrict({ + restriction: 'parent', + elementRect: { + top: 0, + left: 0, + bottom: 1, + right: 1, + }, + }), + ], + cursorChecker: (action, interactable, element, interacting) => { + switch (action.axis) { + case 'x': + return 'ew-resize' + case 'y': + return 'ns-resize' + default: + return interacting ? 'grabbing' : 'move' + } + }, + }) + }, [cleanHandles, createHandles, element, elements, onChange, scale]) + + const onPointerDown: React.PointerEventHandler = + useCallback( + (event) => { + if (disabled || event.defaultPrevented || move) return + + if (!element.selected) { + const elem = elements.find((elem) => elem.id === element.id) + const index = elements.findIndex((e) => e === elem) + const newElements = [ + ...elements.slice(0, index).map(unSelectElement), + { + ...element, + selected: true, + }, + ...elements.slice(index + 1).map(unSelectElement), + ] + + setupInteract() + + onChange(newElements) + } + }, + [disabled, element, elements, move, onChange, setupInteract], + ) + + useEffect(() => { + if (element.selected && !move) { + setupInteract() + } else { + instance.current?.unset() + instance.current = undefined + } + + return () => { + cleanHandles() + } + }, [cleanHandles, element.selected, move, setupInteract]) + + const stroke = useMemo( + () => (element.selected ? blue : '#00ff00'), + [element.selected], + ) + + return ( + + ) +} + +function unSelectElement(element: DrawZoneElement): DrawZoneElement { + if (element.selected) + return { + ...element, + selected: false, + } + + return element +} + type MarkerProps = { - readonly src: string + readonly src: string | null readonly svgRef: React.RefObject } function Marker({ src, svgRef }: MarkerProps): JSX.Element { @@ -262,22 +1396,3 @@ function Marker({ src, svgRef }: MarkerProps): JSX.Element { ) } - -export type DrawZoneContainerProps = Partial & { - readonly children: ReactNode -} -export function DrawZoneContainer({ - children, - ...props -}: DrawZoneContainerProps) { - const [state, dispatch] = useReducer(drawZoneReducer, { - ...drawZoneInitialState, - ...props, - }) - - return ( - - {children} - - ) -} diff --git a/src/DrawZone2/constants.ts b/src/draw-zone/constants.ts similarity index 82% rename from src/DrawZone2/constants.ts rename to src/draw-zone/constants.ts index b1c4d69..4add4a1 100644 --- a/src/DrawZone2/constants.ts +++ b/src/draw-zone/constants.ts @@ -1,7 +1,7 @@ export const MAX_SCALE = 4 export const SCALE_STEP = 0.25 -export const DRAW_ZONE_2_INITIAL_STATE = { +export const DRAW_ZONE_INITIAL_STATE = { contentHidden: false, logicalScale: 1, markerVisible: false, diff --git a/src/draw-zone/hooks.ts b/src/draw-zone/hooks.ts index 414b02d..c502c35 100644 --- a/src/draw-zone/hooks.ts +++ b/src/draw-zone/hooks.ts @@ -1,1158 +1,117 @@ -import { - ArrayXY, - Circle, - FillData, - LinkedHTMLElement, - PointArrayAlias, - Polygon, - Polyline, - Rect, - SVG, - StrokeData, - Svg, - Use, -} from '@svgdotjs/svg.js' -import interact from 'interactjs' -import { - useCallback, - useContext, - useEffect, - useLayoutEffect, - useState, -} from 'react' -import { uuid4 } from '../helpers' -import { isTouchDevice } from '../utils' -import { DrawZoneContext } from './state' -import { - ChangedElement, - DrawZoneMode, - DrawZoneShape, - DrawZoneState, - DrawZoneStateActionType, - Point, - Size, -} from './types' - -const xns = 'http://www.w3.org/1999/xlink' -const blue = '#2BB1FD' -const defaultStroke = { color: '#fff', width: 2, opacity: 1 } -const defaultFill = { color: '#000', opacity: 0 } - -const CIRCLE_SIZE = isTouchDevice ? 22 : 10 - -function getAbsoluteCoordinates(svg: Svg, points: Point[]) { - const svgRect = svg.node.getBoundingClientRect() - - return points.map(({ x, y }) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })) -} - -export function useDrawZone() { - const { state, dispatch } = useContext(DrawZoneContext) - - const zoomIn = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ZOOM_IN }) - }, [dispatch]) - - const zoomOut = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ZOOM_OUT }) - }, [dispatch]) - - const reset = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.RESET }) - }, [dispatch]) - - const toggleMarker = useCallback(() => { - if (state.isMarkerShown) { - dispatch({ type: DrawZoneStateActionType.HIDE_MARKER }) - } else { - dispatch({ type: DrawZoneStateActionType.SHOW_MARKER }) - } - }, [dispatch, state.isMarkerShown]) - - const disable = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.DISABLE }) - }, [dispatch]) - - const enable = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.ENABLE }) - }, [dispatch]) - - const redraw = useCallback(() => { - dispatch({ type: DrawZoneStateActionType.FORCE_REDRAW }) - }, [dispatch]) +import { useCallback, useContext, useEffect, useState } from 'react' +import { DrawZone2Context } from './state' +import { PictureLoadingState, Size } from './types' +import { MAX_SCALE, SCALE_STEP } from './constants' +import { memoize } from 'lodash' + +async function preloadImage(src: string): Promise { + const ev = await new Promise((resolve, reject) => { + const image = new Image() + image.onload = resolve + image.onerror = reject + image.src = src + }) - return { - ...(state as DrawZoneState), - zoomIn, - zoomOut, - reset, - toggleMarker, - disable, - enable, - redraw, - } + const target = ev.target as HTMLImageElement + const { width, height } = target + return { width, height } } -export function useDraw( - ref: React.RefObject, - src: string, - onChange: (elements: Array) => void, - remove: (id: string) => void, - mode: DrawZoneMode, - shape: DrawZoneShape, - drawOnMouseDown?: boolean, - initialRect?: ChangedElement, - onInitialRectChange?: ( - arg: Pick, - ) => void, -) { - const { - state: { isMarkerShown, isDisabled, scale, positionTop, positionLeft }, - dispatch, - } = useContext(DrawZoneContext) - const [svg, setSvg] = useState() - const [originalSize, setOriginalSize] = useState() - let startPosition: Point | null - let overlayRect: Rect | undefined - let overlayRect2: Rect | undefined - let poly: Polygon | undefined - let tmpPoly: Polyline | undefined - let tmpPoints: Array | undefined - let dragging: boolean - - const convertForOnChange = useCallback( - function convertForOnChange(): ChangedElement[] { - if (!svg) { - return [] - } - - const svgRect = svg.node.getBoundingClientRect() - - return svg - .children() - .filter((e) => !e.attr('data-draw-ignore')) - .map((elt) => { - const elementRect = elt.node.getBoundingClientRect() - - const rect: ChangedElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } +const preloadImageMemo = memoize(preloadImage) - const polygon = elt as Polygon - const points: Point[] = getAbsoluteCoordinates( - svg, - polygon - .plot() - .map((p) => ({ - x: p[0], - y: p[1], - })) - .filter( - (_, index) => - shape === 'poly' || index % 2 === 0, - ), - ) - - const result = { - points, - rect, - label: elt.data('label'), - selected: elt.data('selected') as boolean, - id: elt.data('id'), - color: elt.data('color'), - } - - return result - }) - }, - [svg], +export function useLoadImage(src: string) { + const [status, setStatus] = useState( + PictureLoadingState.Idle, ) + const [pictureSize, setPictureSize] = useState({ + width: 0, + height: 0, + }) - function innerOnChange() { - onChange(convertForOnChange()) - } - - function onDelKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Delete' && this.closest('svg')) { - event.preventDefault() - remove(this.dataset['id'] as string) - - return true - } - - return false - } - - function onEscKeyPress(this: SVGElement, event: KeyboardEvent): boolean { - if (event.defaultPrevented) return false - if (event.key === 'Escape' && this.closest('svg')) { - event.preventDefault() - ;(this as unknown as LinkedHTMLElement).instance.fire('deselect') - - return true - } - - return false - } - - function endPolyDrawing(event: Event) { - if (!svg || !poly) return - - const svgRect = svg.node.getBoundingClientRect() - const plot = poly.plot() - - if (plot.length < 3) return - - event.preventDefault() - - poly.remove() - poly = undefined - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const newPoly = drawPoly({ - points: plot.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.removeEventListener('keyup', onEnterKeyPress, { capture: true }) - - if (newPoly) { - newPoly.fire('select') - } - - innerOnChange() - } - - function onEnterKeyPress(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Enter') { - event.preventDefault() - - endPolyDrawing(event) - } - } - - function onAbortPathDrawing(this: Window, event: KeyboardEvent) { - if (event.defaultPrevented) return - if (event.key === 'Escape') { - event.preventDefault() - - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - if (poly) { - poly.remove() - poly = undefined - } - - startPosition = null + useEffect(() => { + setStatus(PictureLoadingState.Loading) - window.removeEventListener('keyup', onAbortPathDrawing, { - capture: true, + preloadImageMemo(src) + .then(function afterLoad(value: Size) { + setPictureSize(value) + setStatus(PictureLoadingState.Done) }) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, + .catch(() => { + preloadImageMemo.cache.delete(src) + setStatus(PictureLoadingState.Error) }) - } - } - - const preventDrag = (event: DragEvent) => { - event.preventDefault() - } - - function drawRect({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, + }, [src]) - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: StrokeData - fill?: FillData - label?: string - id?: string | null - }) { - const minX = Math.min(points[0].x, points[1].x) - const minY = Math.min(points[0].y, points[1].y) - const maxX = Math.max(points[0].x, points[1].x) - const maxY = Math.max(points[0].y, points[1].y) - - const newPoints: Point[] = [ - { x: minX, y: minY }, - { x: maxX, y: minY }, - { x: maxX, y: maxY }, - { x: minX, y: maxY }, - ] - - return drawPoly({ - points: newPoints, - disabled, - stroke, - fill, - label, - id, - }) - } - - function drawPoly({ - points, - disabled = isDisabled, - stroke = { ...defaultStroke }, - fill = { ...defaultFill }, - label, - id = null, - }: { - points: Point[] - disabled?: boolean - stroke?: StrokeData - fill?: FillData - label?: string - id?: string | null - }) { - if (!svg || !points || points.length < 2) { - return - } - const svgRect = svg.node.getBoundingClientRect() + return { status, pictureSize } +} - const svgWidth = svgRect.width || originalSize?.width || 0 - const svgHeight = svgRect.height || originalSize?.height || 0 +export function useControls() { + const { state, setState } = useContext(DrawZone2Context) - const poly = svg.polygon( - points.map( - (point) => [point.x * svgWidth, point.y * svgHeight] as ArrayXY, + const zoomIn = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.min( + (prev.logicalScale as number) + SCALE_STEP, + MAX_SCALE, ), - ) - - poly.fill(fill) - poly.stroke(stroke) - poly.css('touch-action', 'none') // silence interactjs warning. - - let rootMatrix: DOMMatrix - const circles: Circle[] = [] - const handles: Use[] = [] - - function polyDelKeyPress(ev: KeyboardEvent) { - const result = onDelKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - } - } - function polyEscKeyPress(ev: KeyboardEvent) { - const result = onEscKeyPress.call(poly.node, ev) - - if (result) { - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - } - } - - function makeHandlesGrabbable(svg: Svg) { - interact('.point-handle') - .draggable({ - onstart: function (event) { - svg.css('cursor', 'grabbing') - event.target.instance.css('cursor', 'grabbing') - }, - onmove: function (event) { - const i = event.target.getAttribute('data-index') | 0 - const point = poly.node.points.getItem(i) - - point.x += event.dx / rootMatrix.a - point.y += event.dy / rootMatrix.d - - if (shape === 'rect') { - switch (i) { - case 0: // top left - handles[0].x(point.x) - handles[0].y(point.y) - handles[1].y(point.y) - handles[3].x(point.x) - break - case 1: // top right - handles[1].x(point.x) - handles[1].y(point.y) - handles[2].x(point.x) - handles[0].y(point.y) - break - case 2: // bottom right - handles[2].x(point.x) - handles[2].y(point.y) - handles[3].y(point.y) - handles[1].x(point.x) - break - case 3: // bottom left - handles[3].x(point.x) - handles[3].y(point.y) - handles[0].x(point.x) - handles[2].y(point.y) - break - } - - const newPlot = handles.map( - (h) => - [Number(h.x()), Number(h.y())] as ArrayXY, - ) - poly.plot(newPlot) - } else { - event.target.x.baseVal.value = point.x - event.target.y.baseVal.value = point.y - } - }, - onend: function (event) { - event.target.instance.css('cursor', 'grab') - svg.css('cursor', 'crosshair') - - const index = Number( - event.target.getAttribute('data-index') || 0, - ) - - if (shape === 'poly') { - const currentPlot = [...poly.plot()] - - const newPlot: ArrayXY[] = [ - ...currentPlot.slice(0, index), - [ - Number(event.target.getAttribute('x')), - Number(event.target.getAttribute('y')), - ] as ArrayXY, - ...currentPlot.slice(index + 1), - ] - - poly.plot(newPlot) - } - - svg.node.setAttribute('class', '') - - innerOnChange() - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - }), - ], - }) - .styleCursor(false) - } - - function cleanHandles() { - interact('.point-handle').unset() - handles.forEach((h) => h.remove()) - handles.length = 0 - circles.forEach((circle) => circle.remove()) - circles.length = 0 - document.removeEventListener('dragstart', preventDrag) - } - - function createHandles(svg: Svg) { - rootMatrix = svg.node.getScreenCTM() as DOMMatrix - - for (let i = 0; i < poly.node.points.numberOfItems; i++) { - const point = poly.node.points.getItem(i) - - const handleId = `point-handle-${i}` - - const circle = svg - .defs() - .attr('data-draw-ignore', true) - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) - .css('touch-action', 'none') // silence interactjs warning. - .id(handleId) - - const handle = svg - .use(circle as Circle) - .attr('href', `#${handleId}`, xns) - .addClass('point-handle') - .data('draw-ignore', true) - .x(point.x) - .y(point.y) - .data('index', i) - - handle - .on('mousedown', function mousedown(event) { - event.preventDefault() - event.stopPropagation() - }) - .css('cursor', 'grab') - - circles.push(circle) - handles.push(handle) - } - - makeHandlesGrabbable(svg) - - document.addEventListener('dragstart', preventDrag) - } - - // Custom events. - poly.on('select', () => { - // Deselect all - svg.each(function (this: Svg) { - this.fire('deselect', { inst: poly }) - }) - poly.stroke({ color: blue }) - poly.data('selected', true) - window.addEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.addEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - - if (!disabled) { - cleanHandles() - createHandles(svg) - } - - innerOnChange() - }) - poly.on('deselect', (e) => { - if ((e as CustomEvent).detail?.inst === poly) return - poly.stroke(stroke) - poly.data('selected', false) - - cleanHandles() - - window.removeEventListener('keyup', polyDelKeyPress, { - capture: true, - }) - window.removeEventListener('keyup', polyEscKeyPress, { - capture: true, - }) - - innerOnChange() - }) + })) + }, [setState]) - if (!disabled) { - poly.css('cursor', 'move') - - interact(poly.node).draggable({ - listeners: { - start() { - cleanHandles() - }, - move(event) { - const x = parseFloat(event.target.instance.x()) - const y = parseFloat(event.target.instance.y()) - - event.target.instance.x(x + event.dx) - event.target.instance.y(y + event.dy) - - innerOnChange() - }, - end() { - createHandles(svg) - }, - }, - modifiers: [ - interact.modifiers.restrict({ - restriction: 'parent', - elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, - }), - ], - cursorChecker: (action, interactable, element, interacting) => { - switch (action.axis) { - case 'x': - return 'ew-resize' - case 'y': - return 'ns-resize' - default: - return interacting ? 'grabbing' : 'move' - } - }, - }) - } - - poly.data('disabled', disabled) - poly.data('id', id || uuid4()) - poly.data('label', label) - - if (defaultStroke.color !== stroke.color) - poly.data('color', stroke.color) - - return poly - } - - function drawPoint(svg: Svg, x: number, y: number): Circle { - const delta = CIRCLE_SIZE / 2 - - const point = svg - .circle(CIRCLE_SIZE) - .center(0, 0) - .fill({ opacity: 1, color: '#f06' }) - .stroke({ width: 1, color: '#fff' }) - .attr('data-draw-ignore', true) - .addClass('tmp-point') - .move(x - delta, y - delta) - .css('touch-action', 'none') // silence interactjs warning. - - point.on('pointerdown', function (event) { - event.preventDefault() - event.stopPropagation() - - endPolyDrawing(event) - }) - - return point - } - - const draw = ({ - points: inputPoints, - id, - color, - label, - }: ChangedElement) => { - const fill = color ? { ...defaultFill, color } : defaultFill - const stroke = color ? { ...defaultStroke, color } : defaultStroke - - const points = - inputPoints?.map( - (point) => - ({ - x: point.x, - y: point.y, - } as Point), - ) ?? [] - - if (points.length === 2) drawRect({ points, id, fill, stroke, label }) - else drawPoly({ points, id, fill, stroke, label }) - } - - function onPointerDown(e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (!svg.node.contains(e.target as Node)) return - - if ( - svg.node.contains(e.target as Node) && - svg.node !== e.target && - !startPosition - ) { - const element = e.target as unknown as LinkedHTMLElement - - element.instance.fire('select') - return - } else if (e.target === svg.node) { - svg.each(function (this: Svg) { - this.fire('deselect') - }) - innerOnChange() - } - - if (mode === 'draw' && !isDisabled) { - const svgRect = svg.node.getBoundingClientRect() - - if (shape === 'poly' && !isDisabled) { - if (!tmpPoints) { - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - tmpPoints = [ - drawPoint(svg, startPosition.x, startPosition.y), - ] - - window.addEventListener('keyup', onAbortPathDrawing, { - capture: true, - }) - window.addEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - } else if (startPosition && Array.isArray(tmpPoints)) { - const currentPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - if (poly) { - poly.remove() - poly = undefined - } - - const start = prev[0] - if ( - prev.length > 2 && - Math.abs(currentPosition.x - start[0]) <= 10 && - Math.abs(currentPosition.y - start[1]) <= 10 - ) { - if (tmpPoints && tmpPoints.length > 0) { - tmpPoints.forEach((p) => p.remove()) - tmpPoints = undefined - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - startPosition = null - - const poly = drawPoly({ - points: prev.map(([x, y]) => ({ - x: x / svgRect.width, - y: y / svgRect.height, - })), - }) - - window.removeEventListener( - 'keyup', - onAbortPathDrawing, - { - capture: true, - }, - ) - window.removeEventListener('keyup', onEnterKeyPress, { - capture: true, - }) - - poly?.fire('select') - - innerOnChange() - } else { - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - poly = svg - .polygon(plotline) - .fill({ - color: '#f06', - opacity: 0, - }) - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - }) - .attr('data-draw-ignore', true) - - tmpPoints.push( - drawPoint( - svg, - currentPosition.x, - currentPosition.y, - ), - ) - - tmpPoints.forEach((p) => p.front()) - - startPosition = currentPosition - } - } - } else { - startPosition = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (!overlayRect || !overlayRect2) { - overlayRect = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#000', - width: 2, - opacity: 0.7, - dasharray: '5,5', - }) - overlayRect2 = svg.rect(0, 0).fill({ opacity: 0 }).stroke({ - color: '#fff', - width: 2, - opacity: 0.7, - dasharray: '5,5', - dashoffset: 5, - }) - } - - overlayRect.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - overlayRect2.move( - startPosition.x / svgRect.width, - startPosition.y / svgRect.height, - ) - - e.preventDefault() - } - } else if (mode === 'move') { - startPosition = { - x: e.clientX, - y: e.clientY, - } - - dragging = true - - svg.css({ - cursor: 'grabbing', - }) - - e.preventDefault() - } - } - - function onPointerMove(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg || !startPosition) return - - if (mode === 'draw' && !isDisabled) { - if (shape === 'poly' && !isTouchDevice) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition: Point = { - x: e.clientX - svgRect.left, - y: e.clientY - svgRect.top, - } - - if (tmpPoly) { - tmpPoly.remove() - tmpPoly = undefined - } - - const prev = poly - ? [...poly.plot()] - : [[startPosition.x, startPosition.y] as ArrayXY] - - const plotline: PointArrayAlias = [ - ...prev, - [currentPosition.x, currentPosition.y], - ] - - tmpPoly = svg - .polyline(plotline) - .fill('none') - .stroke({ - color: '#f06', - width: 1, - linecap: 'round', - linejoin: 'round', - dasharray: '5,5', - }) - .attr('data-draw-ignore', true) - - if (Array.isArray(tmpPoints)) { - tmpPoints.forEach((point) => point.front()) - } - } else if (overlayRect && overlayRect2) { - const svgRect = svg.node.getBoundingClientRect() - - const currentPosition = { - x: - Math.max( - Math.min(e.clientX, svgRect.right), - svgRect.left, - ) - svgRect.left, - y: - Math.max( - Math.min(e.clientY, svgRect.bottom), - svgRect.top, - ) - svgRect.top, - } - - const minX = - Math.min(startPosition.x, currentPosition.x) / svgRect.width - const minY = - Math.min(startPosition.y, currentPosition.y) / - svgRect.height - const maxX = - Math.max(startPosition.x, currentPosition.x) / svgRect.width - const maxY = - Math.max(startPosition.y, currentPosition.y) / - svgRect.height - - const width = Math.abs(maxX - minX) - const height = Math.abs(maxY - minY) - - overlayRect.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect.width(`${width * 100}%`) - overlayRect.height(`${height * 100}%`) - overlayRect2.move(`${minX * 100}%`, `${minY * 100}%`) - overlayRect2.width(`${width * 100}%`) - overlayRect2.height(`${height * 100}%`) - } - } else if (mode === 'move' && dragging) { - if (!svg.node.contains(e.target as Node)) { - dragging = false - return - } - const parent = svg.parent() - - if (!parent) return - - const currentPosition = { - x: e.clientX, - y: e.clientY, - } - const translationX = currentPosition.x - startPosition.x - const translationY = currentPosition.y - startPosition.y - - parent.css( - 'transform', - `translate3d(${translationX}px, ${translationY}px, 0px)`, - ) - } - } - - function onPointerUp(this: Window, e: globalThis.PointerEvent) { - if (e.defaultPrevented) return - if (!svg) return - - if (mode === 'draw' && shape === 'rect' && !isDisabled) { - if (!startPosition) { - return - } - - if (overlayRect || overlayRect2) { - overlayRect?.remove() - overlayRect = undefined - overlayRect2?.remove() - overlayRect2 = undefined - } - - // Prevent drawing new rect on rect dragend... - if ((e.target as Node | null)?.parentNode === svg.node) { - startPosition = null - return - } - - const svgRect = svg.node.getBoundingClientRect() - const currentPosition = { - x: - Math.max(Math.min(e.clientX, svgRect.right), svgRect.left) - - svgRect.left, - y: - Math.max(Math.min(e.clientY, svgRect.bottom), svgRect.top) - - svgRect.top, - } - - let label = undefined - // Prevent adding very small rects (mis-clicks). - if (Math.abs(currentPosition.x - startPosition.x) <= 2) { - const elements = convertForOnChange() - let lastRect = elements[elements.length - 1] - - if (initialRect) { - lastRect = initialRect - } - - label = lastRect?.label - - if (drawOnMouseDown && lastRect && lastRect.rect) { - currentPosition.x = Math.min( - startPosition.x + - (lastRect.rect.width * svgRect.width) / 100, - svgRect.width, - ) - currentPosition.y = Math.min( - startPosition.y + - (lastRect.rect.height * svgRect.height) / 100, - svgRect.height, - ) - } else { - startPosition = null - return - } - } - - const newRect = drawRect({ - points: [ - { - x: - Math.min(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.min(startPosition.y, currentPosition.y) / - svgRect.height, - }, - { - x: - Math.max(startPosition.x, currentPosition.x) / - svgRect.width, - y: - Math.max(startPosition.y, currentPosition.y) / - svgRect.height, - }, - ], - label: label, - }) - - if (newRect && onInitialRectChange) { - const elementRect = newRect.node.getBoundingClientRect() - const rect: ChangedElement['rect'] = { - height: (elementRect.height / svgRect.height) * 100, - width: (elementRect.width / svgRect.width) * 100, - x: ((elementRect.x - svgRect.x) / svgRect.width) * 100, - y: ((elementRect.y - svgRect.y) / svgRect.height) * 100, - } - onInitialRectChange({ - rect: rect, - label: newRect.data('label'), - id: newRect.data('id'), - }) - } - - setTimeout(() => { - newRect?.fire('select') - innerOnChange() - }, 50) - } else if (mode === 'move' && dragging) { - const parent = svg.parent() - - if (parent) { - const grandParent = parent.parent() - - if (grandParent) { - const parentRect = grandParent.node.getBoundingClientRect() - const svgRect = parent.node.getBoundingClientRect() - - dispatch({ - type: DrawZoneStateActionType.SET_POSITION, - payload: { - top: svgRect.top - parentRect.top, - left: svgRect.left - parentRect.left, - }, - }) - - svg.css({ cursor: 'grab' }) - - dragging = false - } - } - } - - if (mode !== 'draw' || (mode === 'draw' && shape !== 'poly')) - startPosition = null - } - - useEffect(() => { - if (!ref.current) { - return - } - - const image = new Image() - image.onload = () => { - setOriginalSize({ - width: image.naturalWidth, - height: image.naturalHeight, - }) - } - image.src = src - ref.current.style.background = `url('${src}') center center / 100% 100% no-repeat` - ref.current.style.top = `${positionTop}px` - ref.current.style.left = `${positionLeft}px` - - if (svg) { - svg.node.remove() - } - - const newSvg = SVG().addTo(ref.current).size('100%', '100%').attr({ - 'xmlns:xlink': xns, - }) - - setSvg(newSvg) - }, [ref, src]) - - useEffect(() => { - if (ref.current && originalSize && scale) { - ref.current.style.width = `${originalSize.width * scale}px` - - ref.current.style.height = `${originalSize.height * scale}px` - - if (svg) { - dispatch({ - type: DrawZoneStateActionType.FORCE_REDRAW, - }) - } - } - }, [ref, originalSize, scale]) - - useLayoutEffect(() => { - if (!svg) { - return - } + const zoomOut = useCallback(() => { + setState((prev) => ({ + logicalScale: Math.max( + SCALE_STEP, + (prev.logicalScale as number) - SCALE_STEP, + ), + })) + }, [setState]) - svg.css({ - cursor: - mode === 'move' && !isDisabled - ? 'grab' - : mode === 'none' && !isMarkerShown - ? 'normal' - : 'crosshair', - position: 'absolute', - top: '0', - left: '0', + const reset = useCallback(() => { + setState({ + logicalScale: 1, + positionTop: 0, + positionLeft: 0, }) + }, [setState]) - const parent = svg.parent() + const toggleContent = useCallback(() => { + setState((prev) => ({ contentHidden: !prev.contentHidden })) + }, [setState]) - if (parent) { - parent.css({ - position: 'relative', - userSelect: 'none', - transform: 'none', + const toggleMarker = useCallback(() => { + setState((prev) => ({ markerVisible: !prev.markerVisible })) + }, [setState]) + + const toggleMove = useCallback(() => { + setState((prev) => ({ move: !prev.move })) + }, [setState]) + + const setPosition = useCallback( + (top: number, left: number) => { + setState({ + positionTop: top, + positionLeft: left, }) - } - - svg.on('pointerdown', onPointerDown as unknown as EventListener) - window.addEventListener('pointerup', onPointerUp) - window.addEventListener('pointermove', onPointerMove) + }, + [setState], + ) - return () => { - svg.off('pointerdown', onPointerDown as unknown as EventListener) - window.removeEventListener('pointerup', onPointerUp) - window.removeEventListener('pointermove', onPointerMove) - } - }, [svg, mode, initialRect]) + const setScale = useCallback( + (scale: number) => { + setState({ scale }) + }, + [setState], + ) return { - svg, - draw, - originalSize, + ...state, + zoomIn, + zoomOut, + reset, + toggleContent, + toggleMarker, + toggleMove, + setPosition, + setScale, } } diff --git a/src/draw-zone/index.stories.tsx b/src/draw-zone/index.stories.tsx index d99d7e2..dd5751e 100644 --- a/src/draw-zone/index.stories.tsx +++ b/src/draw-zone/index.stories.tsx @@ -1,4 +1,3 @@ -import { isEmpty } from 'lodash' import React, { FunctionComponent, useCallback, @@ -7,18 +6,18 @@ import React, { useState, } from 'react' -import DrawZone, { ChangedElement, DrawZoneContainer, useDrawZone } from '.' +import DrawZone, { DrawZoneContainer, useControls, useLoadImage } from '.' +import type { DrawZoneElement } from '.' +import { DrawZoneShape, PictureLoadingState } from './types' export default { title: 'Components/DrawZone', component: DrawZone, decorators: [ (Story: FunctionComponent) => ( - -
- -
- +
+ +
), ], parameters: { @@ -26,833 +25,79 @@ export default { }, } -export function Default() { - const originalElements = [ - { - id: 'rect1', - points: [ - { x: 125, y: 94 }, - { x: 250, y: 1 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, - toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - - return ( - <> -
- - - - - -
- setElements(elements)} - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - /> - - ) -} - -export function Rects() { - const originalElements = [ - { - id: 'rect1', - points: [ - { x: 125, y: 94 }, - { x: 250, y: 1 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, - toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - - return ( -
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="rect" - > - {elements.map((element, index) => { - const elem = element as ChangedElement - - if (!elem.selected) return null - - return ( -
- -
- ) - })} -
-
-
- ) -} - -export function Polygons() { - const originalElements = [ - /* - { - id: 'poly2', - points: [ - { x: 0, y: 0 }, - { x: 125, y: 125 }, - { x: 62.5, y: 250 }, - ], - }, - { - id: 'poly3', - points: [ - { x: 25, y: 25 }, - { x: 100, y: 100 }, - { x: 62.5, y: 200 }, - ], - }, - { - id: 'poly1', - points: [ - { x: 50, y: 75 }, - { x: 50, y: 212.5 }, - { x: 150, y: 200 }, - { x: 200, y: 50 }, - ], - }, - { - id: 'poly2', - points: [ - { x: 37, y: 26 }, - { x: 125, y: 188 }, - { x: 250, y: 188 }, - { x: 250, y: 94 }, - ], - color: '#00ff00', - }, - */ - ] +function Controls() { const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, - toggleMarker, + contentHidden, + markerVisible, + move, reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - - return ( -
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="poly" - > - {elements.map((element, index) => { - const elem = element as ChangedElement - - if (!elem.selected) return null - - return ( -
- -
- ) - })} -
-
-
- ) -} - -export function ScaleIn() { - const originalElements = [ - { - id: 'poly2', - points: [ - { x: 2520, y: 1410 }, - { x: 1450, y: 3760 }, - { x: 3550, y: 3580 }, - { x: 3420, y: 1930 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, + toggleContent, toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - - return ( -
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="poly" - sizeMode="fit" - > - {elements.map((element, index) => { - const elem = element as ChangedElement - - if (!elem.selected) return null - - return ( -
- -
- ) - })} -
-
-
- ) -} - -export function ScaleOut() { - const originalElements = [ - { - id: 'rect1', - points: [ - { x: 125, y: 94 }, - { x: 250, y: 1 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, + toggleMove, zoomIn, zoomOut, - toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - + } = useControls() return (
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="rect" - sizeMode="fit" - > - {elements.map((element, index) => { - const elem = element as ChangedElement - - if (!elem.selected) return null - - return ( -
- -
- ) - })} -
-
+ + + + + +
) } -export function ScaleInRect() { - const originalElements = [ - { - id: 'rect1', - points: [ - { x: 125, y: 94 }, - { x: 250, y: 1 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, - toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) - - return ( -
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="rect" - sizeMode="fit" - > - {elements.map((element, index) => { - const elem = element as ChangedElement - - if (!elem.selected) return null - - return ( -
- -
- ) - })} -
-
-
- ) +interface StoryZoneElement extends DrawZoneElement { + readonly metadata: Record } -export function None() { - const { isMarkerShown, zoomIn, zoomOut, toggleMarker, reset } = - useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) - - return ( -
-
- - - - - -
-
- - setElements(elements) - } - remove={(id) => - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'none'} - shape="rect" - sizeMode="fit" - /> -
-
- ) +type EditorProps = { + readonly initialElements: StoryZoneElement[] + readonly shape: DrawZoneShape + readonly src: string } - -export function BugRepro() { - const originalElements = [ - { - id: 'rect1', - points: [ - { x: 125, y: 94 }, - { x: 250, y: 1 }, - ], - color: '#00ff00', - }, - ] - const { - isMarkerShown, - originalSize, - zoomIn, - zoomOut, - toggleMarker, - reset, - } = useDrawZone() - const [move, setMove] = useState(false) - const [elements, setElements] = useState>>([]) +function Editor({ initialElements, shape, src }: EditorProps) { + const { status, pictureSize } = useLoadImage(src) + const [elements, setElements] = useState>([]) useEffect(() => { - if (originalSize) { - setElements( - originalElements.map( - (element) => - ({ - ...element, - points: element.points.map((point) => ({ - x: point.x / originalSize.width, - y: point.y / originalSize.height, - })), - } as ChangedElement), - ), - ) - } - }, [originalSize]) + setElements(initialElements) + }, [initialElements]) const buildMetas = useCallback( - (elem: ChangedElement) => { + (elem: DrawZoneElement) => { + if (!pictureSize || shape !== 'rect') return {} + return { width: Math.floor( - (elem.rect.width * originalSize.width) / 100, + (elem.rect.width * pictureSize.width) / 100, ).toString(), height: Math.floor( - (elem.rect.height * originalSize.height) / 100, + (elem.rect.height * pictureSize.height) / 100, ).toString(), diagonal: Math.floor( Math.hypot( - (elem.rect.height * originalSize.height) / 100, - (elem.rect.width * originalSize.width) / 100, + (elem.rect.height * pictureSize.height) / 100, + (elem.rect.width * pictureSize.width) / 100, ), ).toString(), area: Math.floor( - ((elem.rect.width * originalSize.width) / 100) * - ((elem.rect.height * originalSize.height) / 100), + ((elem.rect.width * pictureSize.width) / 100) * + ((elem.rect.height * pictureSize.height) / 100), ).toString(), - } + } as Record }, - [originalSize], + [pictureSize, shape], ) const onChange = useCallback( - (elements: ChangedElement[]) => { + (elements: DrawZoneElement[]) => { const newElements = elements.map((elem) => { return { ...elem, @@ -871,23 +116,36 @@ export function BugRepro() { ) return ( -
-
- - - - - -
-
- {element && !isEmpty(element.metadata) && ( -
{JSON.stringify(element.metadata)}
+ <> +
+ Current item : + {element && ( + <> + +
+                            {JSON.stringify(element.metadata)}
+                        
+ )}
+
- - setElements((el) => el.filter((e) => e.id !== id)) - } - mode={move ? 'move' : 'draw'} - shape="rect" - sizeMode="fit" - > - {elements.map((element, index) => { - const elem = element as ChangedElement + {status === PictureLoadingState.Done && ( + + )} +
+ + ) +} - if (!elem.selected) return null +export function Default() { + return ( + + + + + ) +} - return ( -
- -
- ) - })} - -
-
+export function Poly() { + return ( + + + + ) } diff --git a/src/draw-zone/index.tsx b/src/draw-zone/index.tsx index 3c321a0..bff2114 100644 --- a/src/draw-zone/index.tsx +++ b/src/draw-zone/index.tsx @@ -1,29 +1,32 @@ -import DrawZone, { - DrawZoneContainer, - DrawZoneContainerProps, - DrawZoneProps, -} from './components' -import { useDrawZone } from './hooks' -import { - ChangedElement, +import DrawZone, { DrawZoneContainer } from './components' +import type { + DrawZoneElement, + DrawZoneFitMode, DrawZoneMode, DrawZoneShape, DrawZoneState, + PictureLoadingState, Point, + Rect, Size, - SizeMode, } from './types' +import { useControls, useLoadImage } from './hooks' export default DrawZone -export { DrawZoneContainer, useDrawZone } +export { + DrawZoneContainer as DrawZoneContainer, + DrawZone, + useControls, + useLoadImage, +} export type { - DrawZoneContainerProps, - DrawZoneProps, - ChangedElement, + DrawZoneElement, + DrawZoneFitMode, DrawZoneMode, DrawZoneShape, DrawZoneState, + PictureLoadingState, Point, + Rect, Size, - SizeMode, } diff --git a/src/draw-zone/state.ts b/src/draw-zone/state.ts index 02dfc7c..0e0cb2a 100644 --- a/src/draw-zone/state.ts +++ b/src/draw-zone/state.ts @@ -1,101 +1,9 @@ -import { Dispatch, createContext } from 'react' -import { - DrawZoneStateAction, - DrawZoneStateActionType, - DrawZoneStateInternal, - MAX_SCALE, - SCALE_STEP, -} from './types' +import { noop } from 'lodash' +import { createContext } from 'react' +import { DRAW_ZONE_INITIAL_STATE } from './constants' +import { DrawZoneStateContext } from './types' -export const drawZoneInitialState: DrawZoneStateInternal = { - scale: 1, - isMarkerShown: false, - isDisabled: false, - originalSize: undefined, - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - redraw: false, -} - -export function drawZoneReducer( - state: DrawZoneStateInternal, - action: DrawZoneStateAction, -): DrawZoneStateInternal { - switch (action.type) { - case DrawZoneStateActionType.RESET: - return { - ...state, - logicalScale: 1, - positionTop: 0, - positionLeft: 0, - } - case DrawZoneStateActionType.SET_SCALE: - return { - ...state, - scale: action.payload, - } - case DrawZoneStateActionType.ZOOM_IN: - return { - ...state, - logicalScale: Math.min( - state.logicalScale + SCALE_STEP, - MAX_SCALE, - ), - } - case DrawZoneStateActionType.ZOOM_OUT: - return { - ...state, - logicalScale: Math.max( - SCALE_STEP, - state.logicalScale - SCALE_STEP, - ), - } - case DrawZoneStateActionType.SHOW_MARKER: - return { - ...state, - isMarkerShown: true, - } - case DrawZoneStateActionType.HIDE_MARKER: - return { - ...state, - isMarkerShown: false, - } - case DrawZoneStateActionType.DISABLE: - return { - ...state, - isDisabled: true, - } - case DrawZoneStateActionType.ENABLE: - return { - ...state, - isDisabled: false, - } - case DrawZoneStateActionType.SET_ORIGINAL_SIZE: - return { - ...state, - originalSize: action.payload, - } - case DrawZoneStateActionType.SET_POSITION: - return { - ...state, - positionTop: action.payload.top, - positionLeft: action.payload.left, - } - case DrawZoneStateActionType.FORCE_REDRAW: - return { - ...state, - redraw: !state.redraw, - } - default: - return state - } -} - -export const DrawZoneContext = createContext<{ - readonly state: DrawZoneStateInternal - readonly dispatch: Dispatch -}>({ - state: drawZoneInitialState, - dispatch: () => undefined, +export const DrawZone2Context = createContext({ + state: { ...DRAW_ZONE_INITIAL_STATE }, + setState: noop, }) diff --git a/src/draw-zone/types.ts b/src/draw-zone/types.ts index a7be463..98cd62a 100644 --- a/src/draw-zone/types.ts +++ b/src/draw-zone/types.ts @@ -1,6 +1,9 @@ -export type DrawZoneMode = 'draw' | 'move' | 'none' +import { Dispatch, SetStateAction } from 'react' +import { BGR } from 'src/types' + +export type DrawZoneMode = 'draw' | 'none' export type DrawZoneShape = 'rect' | 'poly' | 'none' -export type SizeMode = 'auto' | 'fit' +export type DrawZoneFitMode = 'auto' | 'fit' export interface Size { readonly width: number @@ -10,88 +13,36 @@ export interface Point { readonly x: number readonly y: number } -export interface ChangedElement { - readonly id: string +export interface Rect extends Size, Point {} + +export interface DrawZoneElement { + readonly id: string | undefined readonly selected?: boolean readonly points: Point[] readonly label: string - readonly rect: { - readonly height: number - readonly width: number - readonly x: number - readonly y: number - } - readonly color?: string + readonly color?: BGR + readonly rect: Rect } export type DrawZoneState = { - readonly scale: number - readonly isMarkerShown: boolean - readonly isDisabled: boolean - readonly originalSize: Size | undefined -} - -export const MAX_SCALE = 4 -export const SCALE_STEP = 0.25 - -export type DrawZoneStateInternal = DrawZoneState & { + readonly contentHidden: boolean readonly logicalScale: number + readonly markerVisible: boolean + readonly move: boolean readonly positionTop: number readonly positionLeft: number - readonly redraw: boolean + readonly scale: number } - -export enum DrawZoneStateActionType { - RESET, - SET_SCALE, - ZOOM_IN, - ZOOM_OUT, - CHANGE_MODE, - CHANGE_SIZE_MODE, - SHOW_MARKER, - HIDE_MARKER, - DISABLE, - ENABLE, - SET_ORIGINAL_SIZE, - SET_POSITION, - FORCE_REDRAW, +export type DrawZoneStateContext = { + readonly state: DrawZoneState + readonly setState: Dispatch< + SetStateAction> + > } -export type DrawZoneStateAction = - | { readonly type: DrawZoneStateActionType.RESET } - | { - readonly type: DrawZoneStateActionType.SET_SCALE - readonly payload: number - } - | { - readonly type: DrawZoneStateActionType.ZOOM_IN - } - | { - readonly type: DrawZoneStateActionType.ZOOM_OUT - } - | { - readonly type: DrawZoneStateActionType.SHOW_MARKER - } - | { - readonly type: DrawZoneStateActionType.HIDE_MARKER - } - | { - readonly type: DrawZoneStateActionType.DISABLE - } - | { - readonly type: DrawZoneStateActionType.ENABLE - } - | { - readonly type: DrawZoneStateActionType.SET_ORIGINAL_SIZE - readonly payload: Size | undefined - } - | { - readonly type: DrawZoneStateActionType.SET_POSITION - readonly payload: { - readonly top: number - readonly left: number - } - } - | { - readonly type: DrawZoneStateActionType.FORCE_REDRAW - } +export enum PictureLoadingState { + Idle, + Loading, + Error, + Done, +} diff --git a/src/index.ts b/src/index.ts index 20b1755..888ba58 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,18 +3,19 @@ export { default as DrawZone, DrawZoneContainer, // Hooks - useDrawZone, + useControls, + useLoadImage, } from './draw-zone' export type { - Size, - Point, - ChangedElement, - DrawZoneContainerProps, - DrawZoneProps, + DrawZoneElement, + DrawZoneFitMode, DrawZoneMode, DrawZoneShape, - SizeMode, DrawZoneState, + PictureLoadingState, + Point, + Rect, + Size, } from './draw-zone' export { default as ErrorBoundary } from './error-boundary' From 9994c09b1f478b27bd1fb67912711d7b7e3be75f Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 25 Aug 2022 17:04:41 +0200 Subject: [PATCH 21/30] v0.0.27-beta.10 --- package.json | 2 +- src/draw-zone/components.tsx | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 756ce86..c02054a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.9", + "version": "0.0.27-beta.10", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 2bfd33f..2b3a483 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -111,6 +111,7 @@ function DrawZoneInner({ onInitialRectChange, }: DrawZoneInnerProps) { const { + contentHidden, markerVisible, positionTop, positionLeft, @@ -118,7 +119,6 @@ function DrawZoneInner({ scale, setScale, } = useControls() - const svgRef = useRef(null) const svgContainerRef = useRef(null) const containerRef = useRef(null) const [canMarkerBeVisible, setCanMarkerBeVisible] = useState(false) @@ -164,11 +164,10 @@ function DrawZoneInner({ }, [containerRef, pictureSize, fitMode, logicalScale, setScale]) useEffect(() => { - const { current: svg } = svgRef const { current } = svgContainerRef const { current: container } = containerRef - if (current && container && svg) { + if (current && container) { const handlePointerEnter = () => { setCanMarkerBeVisible(true) } @@ -196,6 +195,8 @@ function DrawZoneInner({ const localOnChange = useCallback( (elements: DrawZoneElement[]) => { + if (contentHidden) return + onChange( elements.map((element) => { const minX = Math.min(...element.points.map(({ x }) => x)) @@ -218,7 +219,7 @@ function DrawZoneInner({ }), ) }, - [onChange, shape], + [contentHidden, onChange, shape], ) return ( @@ -245,19 +246,19 @@ function DrawZoneInner({ height: `${pictureSize?.height * scale}px`, }} > + {canMarkerBeVisible && markerVisible && ( + + )} - {canMarkerBeVisible && markerVisible && ( - - )} {children}
@@ -295,7 +296,7 @@ function SvgZone({ const polyPoints = useRef() const polygon = useRef() const polyline = useRef() - const { move, scale, setPosition } = useControls() + const { contentHidden, move, scale, setPosition } = useControls() const unselectElements = useCallback(() => { onChange(elements.map(unSelectElement)) @@ -387,6 +388,8 @@ function SvgZone({ function onPointerDown(e: globalThis.PointerEvent) { if (!svg.node.contains(e.target as Node)) return + if (contentHidden) return + if ( svg.node.contains(e.target as Node) && svg.node !== e.target && @@ -847,6 +850,7 @@ function SvgZone({ window.removeEventListener('pointermove', onPointerMove) } }, [ + contentHidden, disabled, drawOnPointerDown, drawPoint, From c7d5a8814271030f174be46ccba02a015f431f2a Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 07:39:32 +0200 Subject: [PATCH 22/30] v0.0.27-beta.11 --- package.json | 2 +- src/draw-zone/components.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c02054a..97f0f51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.10", + "version": "0.0.27-beta.11", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 2b3a483..3927d54 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -1332,6 +1332,7 @@ function DrawPolygonElement({ stroke={stroke} strokeOpacity={1} strokeWidth={2} + fillOpacity={0} style={{ touchAction: 'none', // silence interactjs warning. }} From 5148829133d9c2a350dd3568602f9bdfa28ccf91 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 08:33:41 +0200 Subject: [PATCH 23/30] v0.0.27-beta.12 --- package.json | 2 +- src/draw-zone/components.tsx | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 97f0f51..563ef10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.11", + "version": "0.0.27-beta.12", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 3927d54..4eca601 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -900,16 +900,17 @@ function SvgElements({ }: SvgElementsProps) { return ( <> - {elements.map((element) => ( - - ))} + {shape !== 'none' && + elements.map((element) => ( + + ))} ) } From 6e994a6999b58705770d5488d14f5b88a94cd860 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 09:30:01 +0200 Subject: [PATCH 24/30] v0.0.27-beta.13 --- package.json | 2 +- src/draw-zone/components.tsx | 14 ++++++---- src/draw-zone/index.stories.tsx | 45 ++++++++++++--------------------- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 563ef10..258af9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.12", + "version": "0.0.27-beta.13", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 4eca601..d6de69e 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -199,10 +199,14 @@ function DrawZoneInner({ onChange( elements.map((element) => { - const minX = Math.min(...element.points.map(({ x }) => x)) - const minY = Math.min(...element.points.map(({ y }) => y)) - const maxX = Math.max(...element.points.map(({ x }) => x)) - const maxY = Math.max(...element.points.map(({ y }) => y)) + const points = element.points.map(({x, y}) => ({ + x: Math.round(x), + y: Math.round(y) + })) + const minX = Math.min(...points.map(({ x }) => x)) + const minY = Math.min(...points.map(({ y }) => y)) + const maxX = Math.max(...points.map(({ x }) => x)) + const maxY = Math.max(...points.map(({ y }) => y)) const rect: DrawZoneElement['rect'] = { height: maxY - minY, @@ -214,7 +218,7 @@ function DrawZoneInner({ return { ...element, rect, - points: element.points.filter(filterPoints(shape)), + points: points.filter(filterPoints(shape)), } }), ) diff --git a/src/draw-zone/index.stories.tsx b/src/draw-zone/index.stories.tsx index dd5751e..c364fdc 100644 --- a/src/draw-zone/index.stories.tsx +++ b/src/draw-zone/index.stories.tsx @@ -6,9 +6,9 @@ import React, { useState, } from 'react' -import DrawZone, { DrawZoneContainer, useControls, useLoadImage } from '.' +import DrawZone, { DrawZoneContainer, useControls } from '.' import type { DrawZoneElement } from '.' -import { DrawZoneShape, PictureLoadingState } from './types' +import { DrawZoneShape } from './types' export default { title: 'Components/DrawZone', @@ -63,7 +63,6 @@ type EditorProps = { readonly src: string } function Editor({ initialElements, shape, src }: EditorProps) { - const { status, pictureSize } = useLoadImage(src) const [elements, setElements] = useState>([]) useEffect(() => { @@ -72,28 +71,18 @@ function Editor({ initialElements, shape, src }: EditorProps) { const buildMetas = useCallback( (elem: DrawZoneElement) => { - if (!pictureSize || shape !== 'rect') return {} + if (shape !== 'rect') return {} return { - width: Math.floor( - (elem.rect.width * pictureSize.width) / 100, - ).toString(), - height: Math.floor( - (elem.rect.height * pictureSize.height) / 100, - ).toString(), + width: elem.rect.width.toString(), + height: elem.rect.height.toString(), diagonal: Math.floor( - Math.hypot( - (elem.rect.height * pictureSize.height) / 100, - (elem.rect.width * pictureSize.width) / 100, - ), - ).toString(), - area: Math.floor( - ((elem.rect.width * pictureSize.width) / 100) * - ((elem.rect.height * pictureSize.height) / 100), + Math.hypot(elem.rect.height, elem.rect.width), ).toString(), + area: Math.floor(elem.rect.width * elem.rect.height).toString(), } as Record }, - [pictureSize, shape], + [shape], ) const onChange = useCallback( @@ -153,16 +142,14 @@ function Editor({ initialElements, shape, src }: EditorProps) { height: '500px', }} > - {status === PictureLoadingState.Done && ( - - )} +
) From c972f198924d95a0fc864dfd211ca3aad036992c Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 11:18:04 +0200 Subject: [PATCH 25/30] v0.0.27-beta.14 --- package.json | 2 +- src/draw-zone/components.tsx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 258af9a..8bb390f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.13", + "version": "0.0.27-beta.14", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index d6de69e..6f4eb3d 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -32,7 +32,7 @@ import { Svg, Use, } from '@svgdotjs/svg.js' -import { uuid4 } from '../helpers' +import { bgrToHex, uuid4 } from '../helpers' import { isTouchDevice } from '../utils' import { Interactable } from '@interactjs/types' @@ -199,9 +199,9 @@ function DrawZoneInner({ onChange( elements.map((element) => { - const points = element.points.map(({x, y}) => ({ + const points = element.points.map(({ x, y }) => ({ x: Math.round(x), - y: Math.round(y) + y: Math.round(y), })) const minX = Math.min(...points.map(({ x }) => x)) const minY = Math.min(...points.map(({ y }) => y)) @@ -1325,8 +1325,13 @@ function DrawPolygonElement({ }, [cleanHandles, element.selected, move, setupInteract]) const stroke = useMemo( - () => (element.selected ? blue : '#00ff00'), - [element.selected], + () => + element.selected + ? blue + : element.color + ? bgrToHex(...element.color) + : '#ffffff', + [element.selected, element.color], ) return ( From 993e683dbf37b7e519b5586c4f2afa3f2430c838 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 14:16:23 +0200 Subject: [PATCH 26/30] v0.0.27-beta.15 --- package.json | 2 +- src/draw-zone/components.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8bb390f..585bfff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.14", + "version": "0.0.27-beta.15", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 6f4eb3d..0e80d48 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -38,7 +38,7 @@ import { isTouchDevice } from '../utils' import { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' -const CIRCLE_SIZE = isTouchDevice ? 22 : 10 +const CIRCLE_SIZE = isTouchDevice ? 32 : 10 const blue = '#2BB1FD' const preventDrag = (event: DragEvent) => { From bffcdbf6f45a59909b23f847a4dfa8bddfb9dc4d Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 14:24:42 +0200 Subject: [PATCH 27/30] v0.0.27-beta.16 --- package.json | 2 +- src/draw-zone/components.tsx | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 585bfff..3458e6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.15", + "version": "0.0.27-beta.16", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 0e80d48..c8e14b2 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -38,7 +38,8 @@ import { isTouchDevice } from '../utils' import { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' -const CIRCLE_SIZE = isTouchDevice ? 32 : 10 +const CIRCLE_SIZE = 10 +const CIRCLE_BORDER_SIZE = (isTouchDevice ? 33 : 11) - CIRCLE_SIZE const blue = '#2BB1FD' const preventDrag = (event: DragEvent) => { @@ -369,7 +370,11 @@ function SvgZone({ .circle(CIRCLE_SIZE) .center(0, 0) .fill({ opacity: 1, color: '#f06' }) - .stroke({ width: 1, color: '#fff' }) + .stroke({ + width: CIRCLE_BORDER_SIZE, + color: '#fff', + opacity: 0.3, + }) .attr('data-draw-ignore', true) .addClass('tmp-point') .move(x - delta, y - delta) @@ -1180,7 +1185,11 @@ function DrawPolygonElement({ .circle(CIRCLE_SIZE) .center(0, 0) .fill({ opacity: 1, color: blue }) - .stroke({ width: 1, color: '#fff' }) + .stroke({ + width: CIRCLE_BORDER_SIZE, + color: '#fff', + opacity: 0.3, + }) .css('touch-action', 'none') // silence interactjs warning. .id(handleId) From b2ee205a449cd29b447fceb5e5d305a6ce7aecc1 Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 14:34:02 +0200 Subject: [PATCH 28/30] v0.0.27-beta.17 --- package.json | 2 +- src/draw-zone/components.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3458e6e..419c9b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.16", + "version": "0.0.27-beta.17", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index c8e14b2..2c42262 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -39,7 +39,7 @@ import { Interactable } from '@interactjs/types' const xns = 'http://www.w3.org/1999/xlink' const CIRCLE_SIZE = 10 -const CIRCLE_BORDER_SIZE = (isTouchDevice ? 33 : 11) - CIRCLE_SIZE +const CIRCLE_BORDER_SIZE = (isTouchDevice ? 47 : 11) - CIRCLE_SIZE const blue = '#2BB1FD' const preventDrag = (event: DragEvent) => { From 36c2a27f6006ffbbc3752258705490bfac43958d Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Fri, 26 Aug 2022 17:45:58 +0200 Subject: [PATCH 29/30] v0.0.27 --- package.json | 2 +- src/draw-zone/components.tsx | 6 +++--- src/draw-zone/hooks.ts | 4 ++-- src/draw-zone/state.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 419c9b9..724868b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@psycle/repsycle", - "version": "0.0.27-beta.17", + "version": "0.0.27", "description": "Psycle Research front-end toolkit", "author": "Psycle Research", "keywords": [ diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index 2c42262..bb57986 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -9,7 +9,7 @@ import React, { import { useId, usePointerPosition, useSetState } from '../hooks' import { DRAW_ZONE_INITIAL_STATE } from './constants' import { useControls, useLoadImage } from './hooks' -import { DrawZone2Context } from './state' +import { DrawZoneContext } from './state' import { DrawZoneElement, DrawZoneFitMode, @@ -51,9 +51,9 @@ export function DrawZoneContainer({ children }: PropsWithChildren) { ...DRAW_ZONE_INITIAL_STATE, }) return ( - + {children} - + ) } diff --git a/src/draw-zone/hooks.ts b/src/draw-zone/hooks.ts index c502c35..f94d5e2 100644 --- a/src/draw-zone/hooks.ts +++ b/src/draw-zone/hooks.ts @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useState } from 'react' -import { DrawZone2Context } from './state' +import { DrawZoneContext } from './state' import { PictureLoadingState, Size } from './types' import { MAX_SCALE, SCALE_STEP } from './constants' import { memoize } from 'lodash' @@ -46,7 +46,7 @@ export function useLoadImage(src: string) { } export function useControls() { - const { state, setState } = useContext(DrawZone2Context) + const { state, setState } = useContext(DrawZoneContext) const zoomIn = useCallback(() => { setState((prev) => ({ diff --git a/src/draw-zone/state.ts b/src/draw-zone/state.ts index 0e0cb2a..31908ee 100644 --- a/src/draw-zone/state.ts +++ b/src/draw-zone/state.ts @@ -3,7 +3,7 @@ import { createContext } from 'react' import { DRAW_ZONE_INITIAL_STATE } from './constants' import { DrawZoneStateContext } from './types' -export const DrawZone2Context = createContext({ +export const DrawZoneContext = createContext({ state: { ...DRAW_ZONE_INITIAL_STATE }, setState: noop, }) From 6b003180ce99b21cfb494a7c5d56bbd5c27821bf Mon Sep 17 00:00:00 2001 From: Maxime BAUMANN Date: Thu, 15 Sep 2022 14:34:23 +0200 Subject: [PATCH 30/30] fix none mode --- src/draw-zone/components.tsx | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/draw-zone/components.tsx b/src/draw-zone/components.tsx index bb57986..e8a92a6 100644 --- a/src/draw-zone/components.tsx +++ b/src/draw-zone/components.tsx @@ -413,6 +413,8 @@ function SvgZone({ unselectElements() } + if (mode !== 'draw' || disabled) return + if (move) { e.preventDefault() e.stopImmediatePropagation() @@ -427,8 +429,6 @@ function SvgZone({ return } - if (mode !== 'draw' || disabled) return - const svgRect = svg.node.getBoundingClientRect() if (shape === 'rect') { @@ -597,6 +597,8 @@ function SvgZone({ function onPointerMove(this: Window, e: globalThis.PointerEvent) { if (e.defaultPrevented || !startPosition.current) return + if (mode !== 'draw' || disabled) return + if (move) { if (!dragging.current) return if (!svg.node.contains(e.target as Node)) { @@ -626,8 +628,6 @@ function SvgZone({ return } - if (mode !== 'draw' || disabled) return - const svgRect = svg.node.getBoundingClientRect() if ( @@ -888,6 +888,7 @@ function SvgZone({ @@ -898,12 +899,14 @@ function SvgZone({ type SvgElementsProps = { readonly disabled?: boolean readonly elements: DrawZoneElement[] + readonly mode: DrawZoneMode readonly shape: DrawZoneShape readonly onChange: (elements: DrawZoneElement[]) => void } function SvgElements({ disabled, elements, + mode, shape, onChange, }: SvgElementsProps) { @@ -916,6 +919,7 @@ function SvgElements({ disabled={disabled} elements={elements} element={element} + mode={mode} shape={shape} onChange={onChange} /> @@ -928,6 +932,7 @@ type DrawElementProps = { readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] + readonly mode: DrawZoneMode readonly shape: DrawZoneShape readonly onChange: (elements: DrawZoneElement[]) => void } @@ -935,6 +940,7 @@ function DrawElement({ disabled, element, elements, + mode, shape, onChange, }: DrawElementProps) { @@ -944,6 +950,7 @@ function DrawElement({ disabled={disabled} element={element} elements={elements} + mode={mode} onChange={onChange} /> ) @@ -954,8 +961,9 @@ function DrawElement({ disabled={disabled} element={element} elements={elements} - onChange={onChange} + mode={mode} shape={shape} + onChange={onChange} /> ) } @@ -964,12 +972,14 @@ type DrawRectElementProps = { readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] + readonly mode: DrawZoneMode readonly onChange: (elements: DrawZoneElement[]) => void } function DrawRectElement({ disabled, element, elements, + mode, onChange, }: DrawRectElementProps) { const minX = useMemo( @@ -1012,6 +1022,7 @@ function DrawRectElement({ disabled={disabled} element={newElement} elements={elements} + mode={mode} shape="rect" onChange={onChange} /> @@ -1022,6 +1033,7 @@ type DrawPolygonElementProps = { readonly disabled?: boolean readonly element: DrawZoneElement readonly elements: DrawZoneElement[] + readonly mode: DrawZoneMode readonly shape: DrawZoneShape readonly onChange: (elements: DrawZoneElement[]) => void } @@ -1029,6 +1041,7 @@ function DrawPolygonElement({ disabled, element, elements, + mode, shape, onChange, }: DrawPolygonElementProps) { @@ -1222,6 +1235,8 @@ function DrawPolygonElement({ const { current } = ref if (!current) return + if (mode !== 'draw') return + createHandles() instance.current = interact(current as SVGPolygonElement).draggable({