Merge branch 'feature/canvas-fixes-4' into 'release'

Canvas Fixes 4

See merge request theappsmith/internal-tools-client!41
This commit is contained in:
Nikhil Nandagopal 2019-10-03 09:26:08 +00:00
commit 9c872c7384
21 changed files with 215 additions and 67 deletions

View File

@ -46,6 +46,7 @@
"react-dom": "^16.7.0",
"react-netlify-identity": "^0.1.9",
"react-redux": "^6.0.0",
"react-rnd": "^10.1.1",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-scripts": "^3.1.1",

View File

@ -111,8 +111,10 @@ export type WidgetDelete = {
export type WidgetResize = {
widgetId: string;
width: number; // delta/diff
height: number; // delta/diff
leftColumn: number;
rightColumn: number;
topRow: number;
bottomRow: number;
};
export const updateWidget = (

View File

@ -24,9 +24,9 @@ export type Theme = {
};
export const theme: Theme = {
radii: [0, 4, 8, 10, 20],
radii: [0, 4, 8, 10, 20, 50],
fontSizes: [0, 10, 12, 14, 16, 18, 24, 28, 32, 48, 64],
spaces: [0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24],
spaces: [0, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24],
fontWeights: [0, 400, 500, 700],
colors: {
primary: Colors.GREEN,

View File

@ -48,6 +48,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
FETCH_PAGE_ERROR: "FETCH_PAGE_ERROR",
SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR",
FETCH_WIDGET_CARDS_ERROR: "FETCH_WIDGET_CARDS_ERROR",
WIDGET_OPERATION_ERROR: "WIDGET_OPERATION_ERROR",
};
export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes];

View File

@ -22,6 +22,7 @@ export interface BaseStyle {
export interface ComponentProps {
widgetId: string;
widgetName?: string;
style: BaseStyle;
}

View File

@ -24,6 +24,18 @@ export const Container = styled("div")<ContainerProps>`
left: 0;
top: 0;
width: 100%;
padding: ${props => props.theme.spaces[8]}px ${props =>
props.theme.spaces[1]}px ${props => props.theme.spaces[1]}px;
&:after {
content: "${props => props.widgetName}";
position: absolute;
left: ${props => props.theme.spaces[1]}px;
top: ${props => props.theme.spaces[1]}px;
font-size: ${props => props.theme.fontSizes[2]}px;
color: ${props => props.theme.colors.containerBorder};
text-align: left;
width: 100%;
}
`;
export const FocusContext: Context<{

View File

@ -18,6 +18,7 @@ const DraggableWrapper = styled.div<{ show: boolean }>`
&:hover > div {
display: block;
}
display: block;
`;
const DragHandle = styled.div`
@ -86,6 +87,10 @@ const DraggableComponent = (props: DraggableComponentProps) => {
top: props.style
? props.style.yPosition + props.style.yPositionUnit
: 0,
minWidth:
props.style.componentWidth + (props.style.widthUnit || "px"),
minHeight:
props.style.componentHeight + (props.style.heightUnit || "px"),
}}
>
<DragHandle className="control" ref={drag}>

View File

@ -35,7 +35,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
accept: Object.values(WidgetFactory.getWidgetTypes()),
drop(widget: WidgetProps & Partial<WidgetConfigProps>, monitor) {
// Make sure we're dropping in this container.
if (isOver && monitor.canDrop()) {
if (isOver) {
props.updateWidget &&
props.updateWidget(
...widgetOperationParams(
@ -52,13 +52,17 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
},
// Collect isOver for ui transforms when hovering over this component
collect: monitor => ({
isOver: !!monitor.isOver({ shallow: true }),
isOver:
(monitor.isOver({ shallow: true }) &&
props.widgetId !== monitor.getItem().widgetId) ||
(monitor.isOver() && props.widgetId !== monitor.getItem().widgetId),
}),
// Only allow drop if the drag object is directly over this component
// As opposed to the drag object being over a child component, or outside the component bounds
// Also only if the dropzone does not overlap any existing children
canDrop: (widget, monitor) => {
if (monitor.isOver({ shallow: true })) {
// Check if the draggable is the same as the dropTarget
if (isOver) {
return noCollision(
monitor.getClientOffset() as XYCoord,
props.snapColumnSpace,
@ -85,7 +89,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
<div
ref={drop}
style={{
position: "relative",
position: "absolute",
left: props.style.xPosition + props.style.xPositionUnit,
height: props.style.componentHeight,
width: props.style.componentWidth,

View File

@ -2,6 +2,7 @@ import React from "react";
import { XYCoord } from "react-dnd";
import styled from "styled-components";
import { snapToGrid } from "../utils/helpers";
import { theme } from "../constants/DefaultTheme";
const DropZoneWrapper = styled.div`
position: absolute;
@ -57,7 +58,7 @@ export const DropZone = (props: DropZoneProps) => {
width: wrapperProps.width + "px",
top: wrapperProps.top + "px",
height: wrapperProps.height + "px",
background: props.canDrop ? "blue" : "red",
background: props.canDrop ? theme.colors.hover : theme.colors.error,
}}
/>
);

View File

@ -1,12 +1,13 @@
import React, { useContext } from "react";
import styled from "styled-components";
import { Resizable, ResizeDirection } from "re-resizable";
import { Rnd } from "react-rnd";
import { XYCoord } from "react-dnd";
import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget";
import { ContainerProps, ParentBoundsContext } from "./ContainerComponent";
export type ResizableComponentProps = WidgetProps & ContainerProps;
const ResizableContainer = styled(Resizable)`
const ResizableContainer = styled(Rnd)`
position: relative;
z-index: 10;
border: ${props => {
@ -16,52 +17,77 @@ const ResizableContainer = styled(Resizable)`
&:before {
content: "";
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
width: ${props => props.theme.spaces[2]}px;
height: ${props => props.theme.spaces[2]}px;
border-radius: ${props => props.theme.radii[5]}%;
z-index: 9;
background: ${props => props.theme.colors.containerBorder};
}
&:after {
right: -4px;
top: 50%;
right: -${props => props.theme.spaces[1]}px;
top: calc(50% - ${props => props.theme.spaces[1]}px);
}
&:before {
left: calc(50%);
top: calc(100% - 4px);
left: calc(50% - ${props => props.theme.spaces[1]}px);
bottom: -${props => props.theme.spaces[1]}px;
}
`;
export const ResizableComponent = (props: ResizableComponentProps) => {
const { boundingParent } = useContext(ParentBoundsContext);
let bounds = "body";
if (boundingParent && boundingParent.current) {
bounds = "." + boundingParent.current.className.split(" ")[1];
}
const updateSize = (
e: Event,
dir: ResizeDirection,
dir: any,
ref: any,
delta: { width: number; height: number },
position: XYCoord,
) => {
const leftColumn = props.leftColumn + position.x / props.parentColumnSpace;
const topRow = props.topRow + position.y / props.parentRowSpace;
const rightColumn =
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace;
const bottomRow =
props.bottomRow + (delta.height + position.y) / props.parentRowSpace;
props.updateWidget &&
props.updateWidget(WidgetOperations.RESIZE, props.widgetId, delta);
props.updateWidget(WidgetOperations.RESIZE, props.widgetId, {
leftColumn,
rightColumn,
topRow,
bottomRow,
});
};
return (
<ResizableContainer
position={{
x: 0,
y: 0,
}}
size={{
width: props.style.componentWidth as number,
height: props.style.componentHeight as number,
}}
disableDragging
minWidth={props.parentColumnSpace}
minHeight={props.parentRowSpace}
style={{ ...props.style }}
onResizeStop={updateSize}
grid={[props.parentColumnSpace, props.parentRowSpace]}
bounds={boundingParent ? boundingParent.current || undefined : "window"}
enable={{
top: false,
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
bounds={bounds}
enableResizing={{
top: true,
right: true,
bottom: true,
left: false,
left: true,
topRight: false,
topLeft: false,
bottomRight: false,
bottomRight: true,
bottomLeft: false,
}}
>

View File

@ -19,7 +19,7 @@ const CardsPaneWrapper = styled.div`
const CardsWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: ${props => props.theme.spaces[2]}px;
grid-gap: ${props => props.theme.spaces[1]}px;
justify-items: stretch;
align-items: stretch;
`;

View File

@ -105,15 +105,14 @@ const mapStateToProps = (state: AppState): EditorReduxState => {
state.ui.editor.pageWidgetId,
state.entities,
);
const configs = state.entities.widgetConfig.config;
const cards = state.ui.editor.cards;
const groups: string[] = Object.keys(cards);
groups.forEach((group: string) => {
cards[group] = cards[group].map((widget: WidgetCardProps) => ({
...widget,
...configs[widget.type],
}));
cards[group] = cards[group].map((widget: WidgetCardProps) => {
const { rows, columns } = state.entities.widgetConfig.config[widget.type];
return { ...widget, rows, columns };
});
});
return {

View File

@ -24,6 +24,7 @@ export function* validateResponse(response: ApiResponse) {
export function* errorSaga(errorAction: ReduxAction<{ error: any }>) {
// Just a pass through for now.
// Add procedures to customize errors here
console.log(errorAction.payload.error);
yield put({
type: ReduxActionTypes.REPORT_ERROR,
payload: {

View File

@ -1,5 +1,6 @@
import {
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxAction,
} from "../constants/ReduxActionConstants";
import {
@ -9,13 +10,18 @@ import {
WidgetDelete,
} from "../actions/pageActions";
import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer";
import { getWidgets, getWidget, getWidgetParent } from "./selectors";
import {
getWidgets,
getWidget,
getWidgetParent,
getDefaultWidgetConfig,
} from "./selectors";
import {
generateWidgetProps,
updateWidgetSize,
updateWidgetPosition,
} from "../utils/WidgetPropsUtils";
import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects";
import { getNextWidgetName } from "../utils/AppsmithUtils";
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
try {
@ -31,7 +37,7 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
} = addChildAction.payload;
const widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
const widgets = yield select(getWidgets);
const defaultWidgetConfig = yield select(getDefaultWidgetConfig, type);
const childWidget = generateWidgetProps(
widget,
type,
@ -41,6 +47,8 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
rows,
parentRowSpace,
parentColumnSpace,
getNextWidgetName(type, widgets),
defaultWidgetConfig,
);
widgets[childWidget.widgetId] = childWidget;
if (widget && widget.children) {
@ -51,11 +59,13 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets },
});
} catch (err) {
} catch (error) {
yield put({
type: ReduxActionTypes.WIDGET_OPERATION_ERROR,
action: ReduxActionTypes.WIDGET_ADD_CHILD,
...err,
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGET_ADD_CHILD,
error,
},
});
}
}
@ -74,12 +84,13 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets },
});
} catch (err) {
console.log(err);
} catch (error) {
yield put({
type: ReduxActionTypes.WIDGET_OPERATION_ERROR,
action: ReduxActionTypes.WIDGET_DELETE,
...err,
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGET_DELETE,
error,
},
});
}
}
@ -97,7 +108,7 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
// Replace widget with update widget props
widgets[widgetId] = widget;
// If the parent has changed i.e parentWidgetId is not parent.widgetId
if (parent.widgetId !== parentWidgetId) {
if (parent.widgetId !== parentWidgetId && widgetId !== parentWidgetId) {
// Remove from the previous parent
parent.children = parent.children.filter(
(child: string) => child !== widgetId,
@ -110,34 +121,44 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets },
});
} catch (err) {
} catch (error) {
yield put({
type: ReduxActionTypes.WIDGET_OPERATION_ERROR,
action: ReduxActionTypes.WIDGET_MOVE,
...err,
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGET_MOVE,
error,
},
});
}
}
export function* resizeSaga(resizeAction: ReduxAction<WidgetResize>) {
try {
const { widgetId, height, width } = resizeAction.payload;
const {
widgetId,
leftColumn,
rightColumn,
topRow,
bottomRow,
} = resizeAction.payload;
let widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
const widgets = yield select(getWidgets);
widget = updateWidgetSize(widget, height, width);
widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow };
widgets[widgetId] = widget;
yield put({
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets },
});
} catch (err) {
} catch (error) {
yield put({
type: ReduxActionTypes.WIDGET_OPERATION_ERROR,
action: ReduxActionTypes.WIDGET_RESIZE,
...err,
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGET_RESIZE,
error,
},
});
}
}

View File

@ -1,7 +1,7 @@
import { AppState } from "../reducers";
import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer";
import { WidgetProps } from "../widgets/BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
export const getWidgets = (
state: AppState,
): { [widgetId: string]: FlattenedWidgetProps } => {
@ -35,3 +35,14 @@ export const getWidgetParent = (
widget.children.indexOf(widgetId) > -1,
);
};
export const getDefaultWidgetConfig = (
state: AppState,
type: WidgetType,
): Partial<WidgetProps> => {
const configs = state.entities.widgetConfig.config;
const widgetConfig = { ...configs[type] };
delete widgetConfig.rows;
delete widgetConfig.columns;
return widgetConfig;
};

View File

@ -12,6 +12,8 @@ import FontFaceObserver from "fontfaceobserver";
import PropertyControlRegistry from "./PropertyControlRegistry";
import WidgetBuilderRegistry from "./WidgetRegistry";
import { Property } from "../api/ActionAPI";
import { WidgetType } from "../constants/WidgetConstants";
import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer";
import _ from "lodash";
export const createReducer = (
@ -60,3 +62,29 @@ export const mapToPropList = (map: Record<string, string>): Property[] => {
return { key: key, value: value };
});
};
export const getNextWidgetName = (
type: WidgetType,
widgets: {
[id: string]: FlattenedWidgetProps;
},
) => {
const prefix = type
.split("_")
.filter(token => token !== "WIDGET")
.join("")
.toLowerCase();
const usedIndices: number[] = Object.values(widgets).map(widget => {
if (widget.type === type) {
const ind = widget.widgetName
? parseInt(widget.widgetName.split(prefix)[1], 10)
: 0;
return Number.isNaN(ind) ? 0 : ind;
}
return 0;
}) as number[];
const lastIndex = Math.max(...usedIndices);
return prefix + (lastIndex + 1);
};

View File

@ -71,7 +71,10 @@ export const isDropZoneOccupied = (
) => {
if (occupied) {
occupied = occupied.filter(widgetDetails => {
return widgetDetails.id !== widget.widgetId;
return (
widgetDetails.id !== widget.widgetId &&
widgetDetails.parentId !== widget.widgetId
);
});
for (let i = 0; i < occupied.length; i++) {
if (areIntersecting(occupied[i], offset)) {
@ -211,6 +214,8 @@ export const generateWidgetProps = (
rows: number,
parentRowSpace: number,
parentColumnSpace: number,
widgetName: string,
widgetConfig: Partial<WidgetProps>,
): ContainerWidgetProps<WidgetProps> => {
if (parent && parent.snapColumns && parent.snapRows) {
const sizes = {
@ -230,10 +235,11 @@ export const generateWidgetProps = (
};
}
return {
...widgetConfig,
type,
executeAction: () => {},
widgetId: generateReactKey(),
widgetName: generateReactKey(), //TODO: figure out what this is to populate appropriately
widgetName: widgetName || generateReactKey(), //TODO: figure out what this is to populate appropriately
isVisible: true,
parentColumnSpace,
parentRowSpace,

View File

@ -14,6 +14,7 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
<ButtonComponent
style={this.getPositionStyle()}
widgetId={this.props.widgetId}
widgetName={this.props.widgetName}
key={this.props.widgetId}
text={this.props.text}
onClick={() => {

View File

@ -35,9 +35,10 @@ class ContainerWidget extends BaseWidget<
super.componentDidUpdate(previousProps);
let snapColumnSpace = this.state.snapColumnSpace;
if (this.state.componentWidth)
snapColumnSpace =
snapColumnSpace = Math.floor(
this.state.componentWidth /
(this.props.snapColumns || DEFAULT_GRID_COLUMNS);
(this.props.snapColumns || DEFAULT_GRID_COLUMNS),
);
if (this.state.snapColumnSpace !== snapColumnSpace) {
this.setState({
snapColumnSpace,
@ -67,6 +68,7 @@ class ContainerWidget extends BaseWidget<
}}
isRoot={!this.props.parentId}
orientation={this.props.orientation || "VERTICAL"}
widgetName={this.props.widgetName}
>
{_.map(this.props.children, this.renderChildWidget)}
</ContainerComponent>
@ -77,6 +79,7 @@ class ContainerWidget extends BaseWidget<
return this.props.children
? this.props.children.map(child => ({
id: child.widgetId,
parentId: this.props.widgetId,
left: child.leftColumn,
top: child.topRow,
bottom: child.bottomRow,
@ -87,7 +90,6 @@ class ContainerWidget extends BaseWidget<
getCanvasView() {
const style = this.getPositionStyle();
const occupiedSpaces = this.getOccupiedSpaces();
const renderDraggableComponent = (
<DraggableComponent
style={{ ...style, xPosition: 0, yPosition: 0 }}
@ -139,6 +141,7 @@ export type OccupiedSpace = {
top: number;
bottom: number;
id: string;
parentId?: string;
};
export default ContainerWidget;

View File

@ -10,6 +10,7 @@ class SpinnerWidget extends BaseWidget<SpinnerWidgetProps, WidgetState> {
<SpinnerComponent
style={this.getPositionStyle()}
widgetId={this.props.widgetId}
widgetName={this.props.widgetName}
key={this.props.widgetId}
size={this.props.size}
value={this.props.value}

View File

@ -3031,7 +3031,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
classnames@^2.2:
classnames@^2.2, classnames@^2.2.5:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@ -9576,6 +9576,13 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
re-resizable@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.1.0.tgz#ba4ece505b48f05691446d57837151349d7575e8"
integrity sha512-Jj9zdYW6SnUto8pmH4b/3Kms/PKPv9CuWE70W1IuUIR1HlrEibgsqhbUe8BYDRBTuagH1gav09806k7TieUeSA==
dependencies:
fast-memoize "^2.5.1"
re-resizable@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.0.0.tgz#84258f098b0dde214a39ca6d9ca9959aeefbc26d"
@ -9661,6 +9668,14 @@ react-dom@^16.7.0:
prop-types "^15.6.2"
scheduler "^0.15.0"
react-draggable@4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.0.3.tgz#6b9f76f66431c47b9070e9b805bbc520df8ca481"
integrity sha512-4vD6zms+9QGeZ2RQXzlUBw8PBYUXy+dzYX5r22idjp9YwQKIIvD/EojL0rbjS1GK4C3P0rAJnmKa8gDQYWUDyA==
dependencies:
classnames "^2.2.5"
prop-types "^15.6.0"
react-error-overlay@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f"
@ -9708,6 +9723,15 @@ react-redux@^6.0.0:
prop-types "^15.7.2"
react-is "^16.8.2"
react-rnd@^10.1.1:
version "10.1.1"
resolved "https://registry.yarnpkg.com/react-rnd/-/react-rnd-10.1.1.tgz#4f4d9d28c46a6060acb64600df7a88490862a324"
integrity sha512-KwNUbNd4Kg2DTLdw/Eb8dSC3T5nMRQIRfyyVjdoLT85hKXpeCkMRb2zo4BHCzkgdgCbFGgYLDmj1qRH5DUkTGA==
dependencies:
re-resizable "6.1.0"
react-draggable "4.0.3"
tslib "1.10.0"
react-router-dom@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
@ -11650,16 +11674,16 @@ tsdx@^0.6.0:
tslib "^1.9.3"
typescript "^3.4.5"
tslib@1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
tslib@1.9.3, tslib@~1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
tsutils@^3.17.1, tsutils@^3.7.0:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"