mirror of
https://github.com/strapi/strapi.git
synced 2025-12-12 15:32:42 +00:00
feat(wip): add visual reordering of relations
This commit is contained in:
parent
a11193d13e
commit
6b6c3948aa
@ -515,6 +515,25 @@ const EditViewDataManagerProvider = ({
|
||||
});
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* @typedef Payload
|
||||
* @type {object}
|
||||
* @property {string} name - The name of the field in `modifiedData`
|
||||
* @property {number} oldIndex - The relation's current index
|
||||
* @property {number} newIndex - The relation's new index
|
||||
*
|
||||
*
|
||||
* @type {(payload: Payload) => void}
|
||||
*/
|
||||
const reorderRelation = useCallback(({ name, oldIndex, newIndex }) => {
|
||||
dispatch({
|
||||
type: 'REORDER_RELATION',
|
||||
keys: name.split('.'),
|
||||
oldIndex,
|
||||
newIndex,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const removeComponentFromDynamicZone = useCallback(
|
||||
(dynamicZoneName, index) => {
|
||||
trackUsageRef.current('removeComponentFromDynamicZone');
|
||||
@ -592,6 +611,7 @@ const EditViewDataManagerProvider = ({
|
||||
removeComponentFromDynamicZone,
|
||||
removeComponentFromField,
|
||||
removeRepeatableField,
|
||||
reorderRelation,
|
||||
slug,
|
||||
triggerFormValidation,
|
||||
updateActionAllowedFields,
|
||||
|
||||
@ -172,15 +172,32 @@ const reducer = (state, action) =>
|
||||
const { id } = action;
|
||||
const modifiedDataRelation = get(state, [...path]);
|
||||
|
||||
/**
|
||||
* TODO: before merge make this performant (e.g. 1000 relations === long time)
|
||||
*/
|
||||
const newRelations = modifiedDataRelation.filter((rel) => rel.id !== id);
|
||||
|
||||
set(draftState, path, newRelations);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'REORDER_RELATION': {
|
||||
const { oldIndex, newIndex, keys } = action;
|
||||
const path = ['modifiedData', ...keys];
|
||||
const modifiedDataRelations = get(state, [...path]);
|
||||
|
||||
const currentItem = modifiedDataRelations[oldIndex];
|
||||
|
||||
const newRelations = [...modifiedDataRelations];
|
||||
|
||||
console.log(oldIndex, newIndex);
|
||||
|
||||
newRelations.splice(oldIndex, 1);
|
||||
newRelations.splice(newIndex, 0, currentItem);
|
||||
|
||||
console.log(newRelations);
|
||||
|
||||
set(draftState, path, newRelations);
|
||||
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* This action will be called when you open your entry (first load)
|
||||
* but also every time you press publish.
|
||||
|
||||
@ -20,7 +20,7 @@ import { Relation } from './components/Relation';
|
||||
import { RelationItem } from './components/RelationItem';
|
||||
import { RelationList } from './components/RelationList';
|
||||
import { Option } from './components/Option';
|
||||
import { RELATION_ITEM_HEIGHT } from './constants';
|
||||
import { RELATION_GUTTER, RELATION_ITEM_HEIGHT } from './constants';
|
||||
|
||||
const LinkEllipsis = styled(Link)`
|
||||
white-space: nowrap;
|
||||
@ -65,6 +65,7 @@ const RelationInput = ({
|
||||
onRelationConnect,
|
||||
onRelationLoadMore,
|
||||
onRelationDisconnect,
|
||||
onRelationReorder,
|
||||
onSearchNextPage,
|
||||
onSearch,
|
||||
placeholder,
|
||||
@ -87,9 +88,11 @@ const RelationInput = ({
|
||||
const dynamicListHeight = useMemo(
|
||||
() =>
|
||||
totalNumberOfRelations > numberOfRelationsToDisplay
|
||||
? Math.min(totalNumberOfRelations, numberOfRelationsToDisplay) * RELATION_ITEM_HEIGHT +
|
||||
? Math.min(totalNumberOfRelations, numberOfRelationsToDisplay) *
|
||||
(RELATION_ITEM_HEIGHT + RELATION_GUTTER) +
|
||||
RELATION_ITEM_HEIGHT / 2
|
||||
: Math.min(totalNumberOfRelations, numberOfRelationsToDisplay) * RELATION_ITEM_HEIGHT,
|
||||
: Math.min(totalNumberOfRelations, numberOfRelationsToDisplay) *
|
||||
(RELATION_ITEM_HEIGHT + RELATION_GUTTER),
|
||||
[totalNumberOfRelations, numberOfRelationsToDisplay]
|
||||
);
|
||||
|
||||
@ -143,6 +146,9 @@ const RelationInput = ({
|
||||
};
|
||||
}, [paginatedRelations, relations, numberOfRelationsToDisplay, totalNumberOfRelations]);
|
||||
|
||||
/**
|
||||
* --- ReactSelect Workaround START ---
|
||||
*/
|
||||
/**
|
||||
* This code is being isolated because it's a hack to fix a placement bug in
|
||||
* `react-select` where when the options prop is updated the position of the
|
||||
@ -198,12 +204,21 @@ const RelationInput = ({
|
||||
const handleMenuClose = () => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
/**
|
||||
* --- ReactSelect Workaround END ---
|
||||
*/
|
||||
|
||||
const handleMenuOpen = () => {
|
||||
setIsMenuOpen(true);
|
||||
onSearch();
|
||||
};
|
||||
|
||||
const handleUpdatePositionOfRelation = (newIndex, currentIndex) => {
|
||||
if (onRelationReorder) {
|
||||
onRelationReorder(currentIndex, newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Field error={error} name={name} hint={description} id={id}>
|
||||
<Relation
|
||||
@ -277,18 +292,24 @@ const RelationInput = ({
|
||||
ref={listRef}
|
||||
outerRef={outerListRef}
|
||||
itemCount={totalNumberOfRelations}
|
||||
itemSize={RELATION_ITEM_HEIGHT}
|
||||
itemSize={RELATION_ITEM_HEIGHT + RELATION_GUTTER}
|
||||
itemData={relations}
|
||||
itemKey={(index, listData) => `${listData[index].id}-${listData[index].name}`}
|
||||
innerElementType="ol"
|
||||
>
|
||||
{({ data, index, style }) => {
|
||||
const { publicationState, href, mainField, id } = data[index];
|
||||
const statusColor = publicationState === 'draft' ? 'secondary' : 'success';
|
||||
const canDrag = totalNumberOfRelations > 1;
|
||||
|
||||
return (
|
||||
<RelationItem
|
||||
disabled={disabled}
|
||||
key={`relation-${name}-${id}`}
|
||||
canDrag={canDrag}
|
||||
id={id}
|
||||
index={index}
|
||||
updatePositionOfRelation={handleUpdatePositionOfRelation}
|
||||
endAction={
|
||||
<DisconnectButton
|
||||
data-testid={`remove-relation-${id}`}
|
||||
@ -300,7 +321,11 @@ const RelationInput = ({
|
||||
<Icon width="12px" as={Cross} />
|
||||
</DisconnectButton>
|
||||
}
|
||||
style={style}
|
||||
style={{
|
||||
...style,
|
||||
bottom: style.bottom + RELATION_GUTTER,
|
||||
height: style.height - RELATION_GUTTER,
|
||||
}}
|
||||
>
|
||||
<BoxEllipsis minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>
|
||||
<Tooltip description={mainField ?? `${id}`}>
|
||||
@ -395,6 +420,7 @@ RelationInput.propTypes = {
|
||||
onRelationConnect: PropTypes.func.isRequired,
|
||||
onRelationDisconnect: PropTypes.func.isRequired,
|
||||
onRelationLoadMore: PropTypes.func.isRequired,
|
||||
onRelationReorder: PropTypes.func.isRequired,
|
||||
onSearch: PropTypes.func.isRequired,
|
||||
onSearchNextPage: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { IconButton } from '@strapi/design-system/IconButton';
|
||||
|
||||
import Drag from '@strapi/icons/Drag';
|
||||
|
||||
import { composeRefs } from '../../../utils';
|
||||
|
||||
const ChildrenWrapper = styled(Flex)`
|
||||
width: 100%;
|
||||
@ -10,21 +17,106 @@ const ChildrenWrapper = styled(Flex)`
|
||||
min-width: 0;
|
||||
`;
|
||||
|
||||
export const RelationItem = ({ children, disabled, endAction, style, ...props }) => {
|
||||
const RELATION_ITEM_DRAG_TYPE = 'RelationItem';
|
||||
|
||||
export const RelationItem = ({
|
||||
children,
|
||||
canDrag,
|
||||
disabled,
|
||||
endAction,
|
||||
style,
|
||||
id,
|
||||
index,
|
||||
updatePositionOfRelation,
|
||||
...props
|
||||
}) => {
|
||||
const relationRef = useRef(null);
|
||||
|
||||
const [{ handlerId }, dropRef] = useDrop({
|
||||
accept: RELATION_ITEM_DRAG_TYPE,
|
||||
collect(monitor) {
|
||||
return {
|
||||
handlerId: monitor.getHandlerId(),
|
||||
};
|
||||
},
|
||||
hover(item, monitor) {
|
||||
if (!relationRef.current) {
|
||||
return;
|
||||
}
|
||||
const dragIndex = item.index;
|
||||
const currentIndex = index;
|
||||
|
||||
// Don't replace items with themselves
|
||||
if (dragIndex === currentIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine rectangle on screen
|
||||
const hoverBoundingRect = relationRef.current.getBoundingClientRect();
|
||||
|
||||
// Get vertical middle
|
||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
||||
|
||||
// Determine mouse position
|
||||
const clientOffset = monitor.getClientOffset();
|
||||
|
||||
// Get pixels to the top
|
||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
||||
|
||||
// Only perform the move when the mouse has crossed half of the items height
|
||||
// When dragging downwards, only move when the cursor is below 50%
|
||||
// When dragging upwards, only move when the cursor is above 50%
|
||||
|
||||
// Dragging downwards
|
||||
if (dragIndex < currentIndex && hoverClientY < hoverMiddleY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dragging upwards
|
||||
if (dragIndex > currentIndex && hoverClientY > hoverMiddleY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Time to actually perform the action
|
||||
updatePositionOfRelation(dragIndex, currentIndex);
|
||||
|
||||
item.index = currentIndex;
|
||||
},
|
||||
});
|
||||
|
||||
const [{ isDragging }, dragRef] = useDrag(() => ({
|
||||
type: RELATION_ITEM_DRAG_TYPE,
|
||||
item: { index },
|
||||
canDrag,
|
||||
collect: (monitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const composedRefs = composeRefs(relationRef, dropRef, dragRef);
|
||||
|
||||
const opacity = isDragging ? 0 : 1;
|
||||
|
||||
return (
|
||||
<Box style={style} as="li">
|
||||
<Flex
|
||||
draggable={canDrag}
|
||||
paddingTop={2}
|
||||
paddingBottom={2}
|
||||
paddingLeft={4}
|
||||
paddingLeft={canDrag ? 2 : 4}
|
||||
paddingRight={4}
|
||||
hasRadius
|
||||
borderSize={1}
|
||||
background={disabled ? 'neutral150' : 'neutral0'}
|
||||
borderColor="neutral200"
|
||||
justifyContent="space-between"
|
||||
ref={composedRefs}
|
||||
style={{ opacity }}
|
||||
data-handler-id={handlerId}
|
||||
{...props}
|
||||
>
|
||||
{/* TODO: swap this out for using children when DS is updated */}
|
||||
{canDrag ? <IconButton marginRight={1} aria-label="Drag" noBorder icon={<Drag />} /> : null}
|
||||
<ChildrenWrapper justifyContent="space-between">{children}</ChildrenWrapper>
|
||||
{endAction && <Box paddingLeft={4}>{endAction}</Box>}
|
||||
</Flex>
|
||||
@ -33,15 +125,19 @@ export const RelationItem = ({ children, disabled, endAction, style, ...props })
|
||||
};
|
||||
|
||||
RelationItem.defaultProps = {
|
||||
canDrag: false,
|
||||
disabled: false,
|
||||
endAction: undefined,
|
||||
style: undefined,
|
||||
};
|
||||
|
||||
RelationItem.propTypes = {
|
||||
canDrag: PropTypes.bool,
|
||||
children: PropTypes.node.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
endAction: PropTypes.node,
|
||||
id: PropTypes.number.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
style: PropTypes.shape({
|
||||
height: PropTypes.number,
|
||||
left: PropTypes.number,
|
||||
@ -49,4 +145,5 @@ RelationItem.propTypes = {
|
||||
right: PropTypes.number,
|
||||
width: PropTypes.string,
|
||||
}),
|
||||
updatePositionOfRelation: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { Stack } from '@strapi/design-system/Stack';
|
||||
|
||||
const ShadowBox = styled(Box)`
|
||||
position: relative;
|
||||
@ -37,7 +36,7 @@ const ShadowBox = styled(Box)`
|
||||
export const RelationList = ({ children, overflow, ...props }) => {
|
||||
return (
|
||||
<ShadowBox overflowDirection={overflow} {...props}>
|
||||
<Stack spacing={1}>{children}</Stack>
|
||||
{children}
|
||||
</ShadowBox>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export const RELATION_ITEM_HEIGHT = 50;
|
||||
export const RELATION_GUTTER = 4;
|
||||
|
||||
@ -10,6 +10,7 @@ import { useCMEditViewDataManager, NotAllowedInput } from '@strapi/helper-plugin
|
||||
import { RelationInput } from '../RelationInput';
|
||||
|
||||
import { useRelation } from '../../hooks/useRelation';
|
||||
import { useModifiedDataSelector } from '../../hooks/useModifiedDataSelector';
|
||||
|
||||
import { getTrad } from '../../utils';
|
||||
|
||||
@ -37,10 +38,10 @@ export const RelationInputDataManager = ({
|
||||
targetModel,
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { connectRelation, disconnectRelation, loadRelation, modifiedData, slug, initialData } =
|
||||
const { connectRelation, disconnectRelation, loadRelation, slug, initialData, reorderRelation } =
|
||||
useCMEditViewDataManager();
|
||||
|
||||
const relationsFromModifiedData = get(modifiedData, name) ?? [];
|
||||
const relationsFromModifiedData = useModifiedDataSelector(name, []);
|
||||
|
||||
const currentLastPage = Math.ceil(relationsFromModifiedData.length / RELATIONS_TO_DISPLAY);
|
||||
|
||||
@ -131,6 +132,19 @@ export const RelationInputDataManager = ({
|
||||
search.fetchNextPage();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} currentIndex
|
||||
* @param {number} oldIndex
|
||||
*/
|
||||
const handleRelationReorder = (oldIndex, newIndex) => {
|
||||
reorderRelation({
|
||||
name,
|
||||
newIndex,
|
||||
oldIndex,
|
||||
});
|
||||
};
|
||||
|
||||
if (
|
||||
(!isFieldAllowed && isCreatingEntry) ||
|
||||
(!isCreatingEntry && !isFieldAllowed && !isFieldReadable)
|
||||
@ -194,9 +208,10 @@ export const RelationInputDataManager = ({
|
||||
defaultMessage: 'No relations available',
|
||||
})}
|
||||
numberOfRelationsToDisplay={RELATIONS_TO_DISPLAY}
|
||||
onRelationConnect={(relation) => handleRelationConnect(relation)}
|
||||
onRelationDisconnect={(relation) => handleRelationDisconnect(relation)}
|
||||
onRelationLoadMore={() => handleRelationLoadMore()}
|
||||
onRelationConnect={handleRelationConnect}
|
||||
onRelationDisconnect={handleRelationDisconnect}
|
||||
onRelationLoadMore={handleRelationLoadMore}
|
||||
onRelationReorder={handleRelationReorder}
|
||||
onSearch={(term) => handleSearch(term)}
|
||||
onSearchNextPage={() => handleSearchMore()}
|
||||
placeholder={formatMessage(
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
describe('useModifiedDataSelector', () => {
|
||||
it.todo('should not re render when the same primitive value is returned from the modifiedData');
|
||||
|
||||
it.todo('should not re render when the same object value is returned from the modifiedData');
|
||||
|
||||
it.todo('should not re render when the same array value is returned from the modifiedData');
|
||||
|
||||
it.todo('should re render when a different primitive value is returned from the modifiedData');
|
||||
|
||||
it.todo('should re render when a different object value is returned from the modifiedData');
|
||||
|
||||
it.todo('should re render when a different array value is returned from the modifiedData');
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
||||
|
||||
export const useModifiedDataSelector = (path, defaultValue) => {
|
||||
const { modifiedData } = useCMEditViewDataManager();
|
||||
|
||||
const [value, setValue] = useState(get(modifiedData, path, defaultValue));
|
||||
|
||||
useEffect(() => {
|
||||
const newValue = get(modifiedData, path, defaultValue);
|
||||
|
||||
if (!isEqual(newValue, value)) {
|
||||
setValue(newValue);
|
||||
}
|
||||
}, [modifiedData, path, defaultValue, value]);
|
||||
|
||||
return value;
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @typedef PossibleRef<T>
|
||||
* @type {React.Ref<T> | undefined;}
|
||||
*
|
||||
* @typedef setRef
|
||||
* @type {<T>(ref: PossibleRef<T>, value: T) => React.RefCallback<T>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {setRef}
|
||||
*/
|
||||
const setRef = (ref, value) => {
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref !== null && ref !== undefined) {
|
||||
ref.current = value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility to compose multiple refs together
|
||||
* Accepts callback refs and RefObject(s)
|
||||
*
|
||||
* @type {<T>(...refs: PossibleRef<T>[]) => (node: T) => void}
|
||||
*/
|
||||
export const composeRefs = (...refs) => {
|
||||
return (node) => refs.forEach((ref) => setRef(ref, node));
|
||||
};
|
||||
@ -1,13 +1,20 @@
|
||||
export { default as arrayMoveItem } from './arrayMoveItem';
|
||||
|
||||
export { default as checkIfAttributeIsDisplayable } from './checkIfAttributeIsDisplayable';
|
||||
export { composeRefs } from './composeRefs';
|
||||
export { default as createDefaultForm } from './createDefaultForm';
|
||||
|
||||
export { default as formatLayoutToApi } from './formatLayoutToApi';
|
||||
|
||||
export { default as generatePermissionsObject } from './generatePermissionsObject';
|
||||
export { default as getFieldName } from './getFieldName';
|
||||
export { default as getMaxTempKey } from './getMaxTempKey';
|
||||
export { default as getRequestUrl } from './getRequestUrl';
|
||||
export { default as getTrad } from './getTrad';
|
||||
|
||||
export { default as ItemTypes } from './ItemTypes';
|
||||
|
||||
export { default as mergeMetasWithSchema } from './mergeMetasWithSchema';
|
||||
|
||||
export { default as removeKeyInObject } from './removeKeyInObject';
|
||||
export { default as removePasswordFieldsFromData } from './removePasswordFieldsFromData';
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
import { composeRefs } from '../composeRefs';
|
||||
|
||||
describe('composeRefs', () => {
|
||||
it('given the ref is a function it should call those functions with the node value', () => {
|
||||
const ref1 = jest.fn();
|
||||
const ref2 = jest.fn();
|
||||
const ref3 = jest.fn();
|
||||
const node = 'I am a node';
|
||||
|
||||
const composedRefs = composeRefs(ref1, ref2, ref3);
|
||||
|
||||
composedRefs(node);
|
||||
|
||||
expect(ref1).toHaveBeenCalledWith(node);
|
||||
expect(ref2).toHaveBeenCalledWith(node);
|
||||
expect(ref3).toHaveBeenCalledWith(node);
|
||||
});
|
||||
|
||||
/**
|
||||
* This is difficult because you need to be able to access the
|
||||
* ref.current value from outside the component.
|
||||
*/
|
||||
it.todo('refs as React.useRef');
|
||||
});
|
||||
@ -108,8 +108,8 @@
|
||||
"qs": "6.10.1",
|
||||
"react": "^17.0.2",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dnd": "^14.0.2",
|
||||
"react-dnd-html5-backend": "^14.0.0",
|
||||
"react-dnd": "15.1.2",
|
||||
"react-dnd-html5-backend": "15.1.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-error-boundary": "3.1.1",
|
||||
"react-fast-compare": "^3.2.0",
|
||||
|
||||
77
yarn.lock
77
yarn.lock
@ -3747,20 +3747,30 @@
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
||||
|
||||
"@react-dnd/asap@^4.0.0":
|
||||
"@react-dnd/asap@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651"
|
||||
integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==
|
||||
|
||||
"@react-dnd/asap@4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.1.tgz#5291850a6b58ce6f2da25352a64f1b0674871aab"
|
||||
integrity sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==
|
||||
|
||||
"@react-dnd/invariant@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-2.0.0.tgz#09d2e81cd39e0e767d7da62df9325860f24e517e"
|
||||
integrity sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==
|
||||
"@react-dnd/invariant@3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-3.0.0.tgz#ea55db612b8be3284e87b67f1a1567595cd4c386"
|
||||
integrity sha512-keberJRIqPX15IK3SWS/iO1t/kGETiL1oczKrDitAaMnQ+kpHf81l3MrRmFjvfqcnApE+izEvwM6GsyoIcpsVA==
|
||||
|
||||
"@react-dnd/shallowequal@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
|
||||
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
|
||||
"@react-dnd/invariant@3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-3.0.1.tgz#7e70be19ea21b539e8bf1da28466f4f05df2a4cc"
|
||||
integrity sha512-blqduwV86oiKw2Gr44wbe3pj3Z/OsXirc7ybCv9F/pLAR+Aih8F3rjeJzK0ANgtYKv5lCpkGVoZAeKitKDaD/g==
|
||||
|
||||
"@react-dnd/shallowequal@3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-3.0.1.tgz#8056fe046a8d10a275e321ec0557ae652d7a4d06"
|
||||
integrity sha512-XjDVbs3ZU16CO1h5Q3Ew2RPJqmZBDE/EVf1LYp6ePEffs3V/MX9ZbL5bJr8qiK5SbGmUMuDoaFgyKacYz8prRA==
|
||||
|
||||
"@rushstack/ts-command-line@^4.7.7":
|
||||
version "4.12.1"
|
||||
@ -10123,15 +10133,24 @@ dkim-signer@0.2.2:
|
||||
dependencies:
|
||||
libmime "^2.0.3"
|
||||
|
||||
dnd-core@14.0.1:
|
||||
version "14.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-14.0.1.tgz#76d000e41c494983210fb20a48b835f81a203c2e"
|
||||
integrity sha512-+PVS2VPTgKFPYWo3vAFEA8WPbTf7/xo43TifH9G8S1KqnrQu0o77A3unrF5yOugy4mIz7K5wAVFHUcha7wsz6A==
|
||||
dnd-core@15.1.1:
|
||||
version "15.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-15.1.1.tgz#b4dce2d892be2a7c9ca32ffdd545350be8d52f4f"
|
||||
integrity sha512-Mtj/Sltcx7stVXzeDg4g7roTe/AmzRuIf/FYOxX6F8gULbY54w066BlErBOzQfn9RIJ3gAYLGX7wvVvoBSq7ig==
|
||||
dependencies:
|
||||
"@react-dnd/asap" "^4.0.0"
|
||||
"@react-dnd/invariant" "^2.0.0"
|
||||
"@react-dnd/asap" "4.0.0"
|
||||
"@react-dnd/invariant" "3.0.0"
|
||||
redux "^4.1.1"
|
||||
|
||||
dnd-core@15.1.2:
|
||||
version "15.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-15.1.2.tgz#0983bce555c4985f58b731ffe1faed31e1ea7f6f"
|
||||
integrity sha512-EOec1LyJUuGRFg0LDa55rSRAUe97uNVKVkUo8iyvzQlcECYTuPblVQfRWXWj1OyPseFIeebWpNmKFy0h6BcF1A==
|
||||
dependencies:
|
||||
"@react-dnd/asap" "4.0.1"
|
||||
"@react-dnd/invariant" "3.0.1"
|
||||
redux "^4.1.2"
|
||||
|
||||
dns-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
|
||||
@ -17837,6 +17856,8 @@ path-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
|
||||
integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
path-dirname@^1.0.0:
|
||||
version "1.0.2"
|
||||
@ -18790,21 +18811,21 @@ react-copy-to-clipboard@^5.1.0:
|
||||
copy-to-clipboard "^3.3.1"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-dnd-html5-backend@^14.0.0:
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-14.1.0.tgz#b35a3a0c16dd3a2bfb5eb7ec62cf0c2cace8b62f"
|
||||
integrity sha512-6ONeqEC3XKVf4eVmMTe0oPds+c5B9Foyj8p/ZKLb7kL2qh9COYxiBHv3szd6gztqi/efkmriywLUVlPotqoJyw==
|
||||
react-dnd-html5-backend@15.1.2:
|
||||
version "15.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.2.tgz#85e2c5ad57e87190495756f68f44fd89299062fb"
|
||||
integrity sha512-mem9QbutUF+aA2YC1y47G3ECjnYV/sCYKSnu5Jd7cbg3fLMPAwbnTf/JayYdnCH5l3eg9akD9dQt+cD0UdF8QQ==
|
||||
dependencies:
|
||||
dnd-core "14.0.1"
|
||||
dnd-core "15.1.1"
|
||||
|
||||
react-dnd@^14.0.2:
|
||||
version "14.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-14.0.5.tgz#ecf264e220ae62e35634d9b941502f3fca0185ed"
|
||||
integrity sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A==
|
||||
react-dnd@15.1.2:
|
||||
version "15.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-15.1.2.tgz#211b30fd842326209c63f26f1bdf1bc52eef4f64"
|
||||
integrity sha512-EaSbMD9iFJDY/o48T3c8wn3uWU+2uxfFojhesZN3LhigJoAIvH2iOjxofSA9KbqhAKP6V9P853G6XG8JngKVtA==
|
||||
dependencies:
|
||||
"@react-dnd/invariant" "^2.0.0"
|
||||
"@react-dnd/shallowequal" "^2.0.0"
|
||||
dnd-core "14.0.1"
|
||||
"@react-dnd/invariant" "3.0.1"
|
||||
"@react-dnd/shallowequal" "3.0.1"
|
||||
dnd-core "15.1.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
@ -19338,7 +19359,7 @@ redent@^3.0.0:
|
||||
indent-string "^4.0.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
redux@^4.0.0, redux@^4.0.1, redux@^4.1.1:
|
||||
redux@^4.0.0, redux@^4.0.1, redux@^4.1.1, redux@^4.1.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
|
||||
integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user