Merge pull request #70 from ente-io/search-auto-complete
Search auto complete
This commit is contained in:
commit
88f06bb655
|
@ -19,6 +19,7 @@
|
|||
"bootstrap": "^4.5.2",
|
||||
"chrono-node": "^2.2.6",
|
||||
"comlink": "^4.3.0",
|
||||
"debounce-promise": "^3.1.2",
|
||||
"exif-js": "^2.3.0",
|
||||
"formik": "^2.1.5",
|
||||
"heic2any": "^0.0.3",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"react-burger-menu": "^3.0.4",
|
||||
"react-dom": "16.13.1",
|
||||
"react-dropzone": "^11.2.4",
|
||||
"react-select": "^4.3.1",
|
||||
"react-top-loading-bar": "^2.0.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.6",
|
||||
|
@ -45,11 +47,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^9.5.3",
|
||||
"@types/debounce-promise": "^3.1.3",
|
||||
"@types/libsodium-wrappers": "^0.7.8",
|
||||
"@types/localforage": "^0.0.34",
|
||||
"@types/node": "^14.6.4",
|
||||
"@types/photoswipe": "^4.1.1",
|
||||
"@types/react": "^16.9.49",
|
||||
"@types/react-select": "^4.0.15",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/react-window-infinite-loader": "^1.0.3",
|
||||
"@types/styled-components": "^5.1.3",
|
||||
|
|
20
src/components/CrossIcon.tsx
Normal file
20
src/components/CrossIcon.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
|
||||
export default function DateIcon(props) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={props.height}
|
||||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
DateIcon.defaultProps = {
|
||||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
};
|
20
src/components/DateIcon.tsx
Normal file
20
src/components/DateIcon.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
|
||||
export default function DateIcon(props) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={props.height}
|
||||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V10h14v10zm-4.5-7a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
DateIcon.defaultProps = {
|
||||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
};
|
22
src/components/LocationIcon.tsx
Normal file
22
src/components/LocationIcon.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export default function LocationIcon(props) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={props.height}
|
||||
viewBox={props.viewBox}
|
||||
width={props.width}
|
||||
>
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zM7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 2.88-2.88 7.19-5 9.88C9.92 16.21 7 11.85 7 9z"></path>
|
||||
<circle cx="12" cy="9" r="2.5"></circle>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
LocationIcon.defaultProps = {
|
||||
height: 24,
|
||||
width: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
};
|
|
@ -1,18 +1,25 @@
|
|||
import { Formik } from 'formik';
|
||||
import { SetCollections, SetFiles } from 'pages/gallery';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Form } from 'react-bootstrap';
|
||||
import styled from 'styled-components';
|
||||
import constants from 'utils/strings/constants';
|
||||
import * as Yup from 'yup';
|
||||
import AsyncSelect from 'react-select/async';
|
||||
import { components } from 'react-select';
|
||||
import debounce from 'debounce-promise';
|
||||
import { File, getLocalFiles } from 'services/fileService';
|
||||
import {
|
||||
Collection,
|
||||
getLocalCollections,
|
||||
getNonEmptyCollections,
|
||||
} from 'services/collectionService';
|
||||
import { parseHumanDate, searchLocation } from 'services/searchService';
|
||||
import { getFilesWithCreationDay, getFilesInsideBbox } from 'utils/search';
|
||||
import { Bbox, parseHumanDate, searchLocation } from 'services/searchService';
|
||||
import {
|
||||
getFilesWithCreationDay,
|
||||
getFilesInsideBbox,
|
||||
getFormattedDate,
|
||||
} from 'utils/search';
|
||||
import constants from 'utils/strings/constants';
|
||||
import LocationIcon from './LocationIcon';
|
||||
import DateIcon from './DateIcon';
|
||||
import CrossIcon from './CrossIcon';
|
||||
|
||||
const Wrapper = styled.div<{ open: boolean }>`
|
||||
background-color: #111;
|
||||
|
@ -32,8 +39,14 @@ const Wrapper = styled.div<{ open: boolean }>`
|
|||
display: ${(props) => (props.open ? 'flex' : 'none')};
|
||||
`;
|
||||
|
||||
interface formValues {
|
||||
searchPhrase: string;
|
||||
enum SuggestionType {
|
||||
DATE,
|
||||
LOCATION,
|
||||
}
|
||||
interface Suggestion {
|
||||
type: SuggestionType;
|
||||
label: string;
|
||||
value: Bbox | Date;
|
||||
}
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
|
@ -63,36 +76,61 @@ export default function SearchBar(props: Props) {
|
|||
}
|
||||
}, [props.isOpen]);
|
||||
|
||||
const searchFiles = async (values: formValues) => {
|
||||
props.loadingBar.current?.continuousStart();
|
||||
const getAutoCompleteSuggestion = async (searchPhrase: string) => {
|
||||
let option = new Array<Suggestion>();
|
||||
if (!searchPhrase?.length) {
|
||||
return option;
|
||||
}
|
||||
const searchedDate = parseHumanDate(searchPhrase);
|
||||
if (searchedDate != null) {
|
||||
option.push({
|
||||
type: SuggestionType.DATE,
|
||||
value: searchedDate,
|
||||
label: getFormattedDate(searchedDate),
|
||||
});
|
||||
}
|
||||
const searchResults = await searchLocation(searchPhrase);
|
||||
option.push(
|
||||
...searchResults.map(
|
||||
(searchResult) =>
|
||||
({
|
||||
type: SuggestionType.LOCATION,
|
||||
value: searchResult.bbox,
|
||||
label: searchResult.placeName,
|
||||
} as Suggestion)
|
||||
)
|
||||
);
|
||||
return option;
|
||||
};
|
||||
|
||||
const getOptions = debounce(getAutoCompleteSuggestion, 100);
|
||||
|
||||
const filterFiles = (selectedOption: Suggestion) => {
|
||||
if (!selectedOption) {
|
||||
return;
|
||||
}
|
||||
let resultFiles: File[] = [];
|
||||
|
||||
const searchedDate = parseHumanDate(values.searchPhrase);
|
||||
if (searchedDate != null) {
|
||||
const filesWithSameDate = getFilesWithCreationDay(
|
||||
allFiles,
|
||||
searchedDate
|
||||
);
|
||||
resultFiles.push(...filesWithSameDate);
|
||||
} else {
|
||||
const bbox = await searchLocation(values.searchPhrase);
|
||||
if (bbox) {
|
||||
switch (selectedOption.type) {
|
||||
case SuggestionType.DATE:
|
||||
const searchedDate = selectedOption.value as Date;
|
||||
const filesWithSameDate = getFilesWithCreationDay(
|
||||
allFiles,
|
||||
searchedDate
|
||||
);
|
||||
resultFiles = filesWithSameDate;
|
||||
break;
|
||||
case SuggestionType.LOCATION:
|
||||
const bbox = selectedOption.value as Bbox;
|
||||
|
||||
const filesTakenAtLocation = getFilesInsideBbox(allFiles, bbox);
|
||||
resultFiles.push(...filesTakenAtLocation);
|
||||
}
|
||||
resultFiles = filesTakenAtLocation;
|
||||
}
|
||||
|
||||
props.setFiles(resultFiles);
|
||||
props.setCollections(
|
||||
getNonEmptyCollections(allCollections, resultFiles)
|
||||
);
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(
|
||||
() => resolve(props.loadingBar.current?.complete()),
|
||||
5000
|
||||
)
|
||||
);
|
||||
};
|
||||
const closeSearchBar = ({ resetForm }) => {
|
||||
props.setOpen(false);
|
||||
|
@ -100,68 +138,110 @@ export default function SearchBar(props: Props) {
|
|||
props.setCollections(allCollections);
|
||||
resetForm();
|
||||
};
|
||||
|
||||
const getIconByType = (type: SuggestionType) =>
|
||||
type === SuggestionType.DATE ? <DateIcon /> : <LocationIcon />;
|
||||
|
||||
const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span style={{ marginRight: '10px' }}>
|
||||
{getIconByType(props.type)}
|
||||
</span>
|
||||
<span>{props.label}</span>
|
||||
</div>
|
||||
);
|
||||
const { Option, SingleValue } = components;
|
||||
const SingleValueWithIcon = (props) => (
|
||||
<SingleValue {...props}>
|
||||
<LabelWithIcon type={props.data.type} label={props.data.label} />
|
||||
</SingleValue>
|
||||
);
|
||||
const OptionWithIcon = (props) => (
|
||||
<Option {...props}>
|
||||
<LabelWithIcon type={props.data.type} label={props.data.label} />
|
||||
</Option>
|
||||
);
|
||||
|
||||
const customStyles = {
|
||||
control: (provided, { isFocused }) => ({
|
||||
...provided,
|
||||
backgroundColor: '#282828',
|
||||
color: '#d1d1d1',
|
||||
borderColor: isFocused && '#2dc262',
|
||||
boxShadow: isFocused && '0 0 3px #2dc262',
|
||||
':hover': {
|
||||
borderColor: '#2dc262',
|
||||
},
|
||||
}),
|
||||
input: (provided) => ({
|
||||
...provided,
|
||||
color: '#d1d1d1',
|
||||
}),
|
||||
menu: (provided) => ({
|
||||
...provided,
|
||||
marginTop: '10px',
|
||||
backgroundColor: '#282828',
|
||||
}),
|
||||
option: (provided, { isFocused }) => ({
|
||||
...provided,
|
||||
backgroundColor: isFocused && '#343434',
|
||||
}),
|
||||
dropdownIndicator: (provided) => ({
|
||||
...provided,
|
||||
display: 'none',
|
||||
}),
|
||||
indicatorSeparator: (provided) => ({
|
||||
...provided,
|
||||
display: 'none',
|
||||
}),
|
||||
clearIndicator: (provided) => ({
|
||||
...provided,
|
||||
':hover': { color: '#d2d2d2' },
|
||||
}),
|
||||
singleValue: (provided, state) => ({
|
||||
...provided,
|
||||
backgroundColor: '#282828',
|
||||
color: '#d1d1d1',
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper open={props.isOpen}>
|
||||
<Formik<formValues>
|
||||
initialValues={{ searchPhrase: '' }}
|
||||
onSubmit={searchFiles}
|
||||
validationSchema={Yup.object().shape({
|
||||
searchPhrase: Yup.string().required(constants.REQUIRED),
|
||||
})}
|
||||
validateOnChange={false}
|
||||
validateOnBlur={false}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
touched,
|
||||
errors,
|
||||
handleChange,
|
||||
handleSubmit,
|
||||
resetForm,
|
||||
}) => (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
maxWidth: '600px',
|
||||
margin: '10px',
|
||||
}}
|
||||
>
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Control
|
||||
type={'search'}
|
||||
placeholder={constants.SEARCH_HINT}
|
||||
value={values.searchPhrase}
|
||||
onChange={handleChange('searchPhrase')}
|
||||
ref={searchBarRef}
|
||||
isInvalid={Boolean(
|
||||
touched.searchPhrase &&
|
||||
errors.searchPhrase
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
margin: '0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => closeSearchBar({ resetForm })}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={25}
|
||||
viewBox={`0 0 25 25`}
|
||||
width={25}
|
||||
>
|
||||
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Formik>
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
maxWidth: '600px',
|
||||
margin: '10px',
|
||||
}}
|
||||
>
|
||||
<AsyncSelect
|
||||
components={{
|
||||
Option: OptionWithIcon,
|
||||
SingleValue: SingleValueWithIcon,
|
||||
}}
|
||||
ref={searchBarRef}
|
||||
placeholder={constants.SEARCH_HINT}
|
||||
loadOptions={getOptions}
|
||||
onChange={filterFiles}
|
||||
isClearable
|
||||
escapeClearsValue
|
||||
styles={customStyles}
|
||||
noOptionsMessage={() => null}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
margin: '0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => closeSearchBar({ resetForm: () => null })}
|
||||
>
|
||||
<CrossIcon />
|
||||
</div>
|
||||
</>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
import React, { Props } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.button<{ open: boolean }>`
|
||||
const Wrapper = styled.button<{ isOpen: boolean }>`
|
||||
border: none;
|
||||
background-color: #404040;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
bottom: 50px;
|
||||
display: ${(props) => (!props.isOpen ? 'block' : 'none')};
|
||||
right: 50px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
`;
|
||||
|
||||
export default function SearchButton(props) {
|
||||
if (props.open) {
|
||||
return <div />;
|
||||
}
|
||||
return (
|
||||
<Wrapper open={props.open} onClick={props.onClick}>
|
||||
<Wrapper isOpen={props.isOpen} onClick={props.onClick}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={props.height}
|
||||
|
|
|
@ -114,7 +114,7 @@ export default function Gallery() {
|
|||
});
|
||||
|
||||
const loadingBar = useRef(null);
|
||||
const [searchView, setSearchView] = useState(false);
|
||||
const [searchMode, setSearchMode] = useState(false);
|
||||
useEffect(() => {
|
||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
||||
if (!key) {
|
||||
|
@ -258,6 +258,7 @@ export default function Gallery() {
|
|||
const updateFiles = (files: File[]) => {
|
||||
setFiles(files);
|
||||
setSinceTime(new Date().getTime());
|
||||
selectCollection(null);
|
||||
};
|
||||
return (
|
||||
<FullScreenDropZone
|
||||
|
@ -292,8 +293,8 @@ export default function Gallery() {
|
|||
attributes={dialogMessage}
|
||||
/>
|
||||
<SearchBar
|
||||
isOpen={searchView}
|
||||
setOpen={setSearchView}
|
||||
isOpen={searchMode}
|
||||
setOpen={setSearchMode}
|
||||
loadingBar={loadingBar}
|
||||
setFiles={updateFiles}
|
||||
setCollections={setCollections}
|
||||
|
@ -355,9 +356,9 @@ export default function Gallery() {
|
|||
isFirstLoad={isFirstLoad}
|
||||
openFileUploader={openFileUploader}
|
||||
loadingBar={loadingBar}
|
||||
searchMode={searchView}
|
||||
searchMode={searchMode}
|
||||
/>
|
||||
{files.length < 30 && (
|
||||
{files.length < 30 && !searchMode && (
|
||||
<Alert
|
||||
variant="success"
|
||||
style={{
|
||||
|
@ -383,8 +384,8 @@ export default function Gallery() {
|
|||
/>
|
||||
) : (
|
||||
<SearchButton
|
||||
open={searchView}
|
||||
onClick={() => setSearchView(true)}
|
||||
isOpen={searchMode}
|
||||
onClick={() => setSearchMode(true)}
|
||||
/>
|
||||
)}
|
||||
</FullScreenDropZone>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import HTTPService from './HTTPService';
|
||||
import * as chrono from 'chrono-node';
|
||||
|
||||
const KM_IN_DEGREE = 0.01;
|
||||
export type Bbox = [number, number, number, number];
|
||||
export interface LocationSearchResponse {
|
||||
placeName: string;
|
||||
bbox: Bbox;
|
||||
}
|
||||
export const getMapboxToken = () => {
|
||||
return process.env.NEXT_PUBLIC_MAPBOX_TOKEN;
|
||||
};
|
||||
|
@ -11,12 +17,25 @@ export function parseHumanDate(humanDate: string) {
|
|||
|
||||
export async function searchLocation(
|
||||
location: string
|
||||
): Promise<[number, number, number, number]> {
|
||||
): Promise<LocationSearchResponse[]> {
|
||||
const resp = await HTTPService.get(
|
||||
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURI(
|
||||
location
|
||||
)}.json`,
|
||||
{ access_token: getMapboxToken(), limit: 1 }
|
||||
{ access_token: getMapboxToken(), limit: 4 }
|
||||
);
|
||||
return resp.data.features.length > 0 && resp.data.features[0].bbox;
|
||||
|
||||
return resp.data.features.length == 0
|
||||
? new Array<LocationSearchResponse>()
|
||||
: resp.data.features.map((feature) => ({
|
||||
placeName: feature.place_name,
|
||||
bbox:
|
||||
feature.bbox ??
|
||||
([
|
||||
feature.center[0] - KM_IN_DEGREE,
|
||||
feature.center[1] - KM_IN_DEGREE,
|
||||
feature.center[0] + KM_IN_DEGREE,
|
||||
feature.center[1] + KM_IN_DEGREE,
|
||||
] as Bbox),
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -33,3 +33,9 @@ export function getFilesWithCreationDay(files: File[], searchedDate: Date) {
|
|||
isSearchedDate(new Date(file.metadata.creationTime / 1000))
|
||||
);
|
||||
}
|
||||
export function getFormattedDate(date: Date) {
|
||||
return new Intl.DateTimeFormat('en-IN', {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}).format(date);
|
||||
}
|
||||
|
|
134
yarn.lock
134
yarn.lock
|
@ -1654,6 +1654,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.13.10":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
|
||||
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.12.13", "@babel/template@^7.7.4":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
|
||||
|
@ -1705,6 +1712,22 @@
|
|||
lodash "^4.17.19"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@emotion/cache@^11.4.0":
|
||||
version "11.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0"
|
||||
integrity sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/sheet" "^1.0.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
stylis "^4.0.3"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||
|
||||
"@emotion/is-prop-valid@^0.8.8":
|
||||
version "0.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
|
||||
|
@ -1717,16 +1740,60 @@
|
|||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||
|
||||
"@emotion/memoize@^0.7.4":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||
|
||||
"@emotion/react@^11.1.1":
|
||||
version "11.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.0.tgz#2465ad7b073a691409b88dfd96dc17097ddad9b7"
|
||||
integrity sha512-4XklWsl9BdtatLoJpSjusXhpKv9YVteYKh9hPKP1Sxl+mswEFoUe0WtmtWjxEjkA51DQ2QRMCNOvKcSlCQ7ivg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@emotion/cache" "^11.4.0"
|
||||
"@emotion/serialize" "^1.0.2"
|
||||
"@emotion/sheet" "^1.0.1"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
hoist-non-react-statics "^3.3.1"
|
||||
|
||||
"@emotion/serialize@^1.0.0", "@emotion/serialize@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
|
||||
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/unitless" "^0.7.5"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.1.tgz#245f54abb02dfd82326e28689f34c27aa9b2a698"
|
||||
integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==
|
||||
|
||||
"@emotion/stylis@^0.8.4":
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
|
||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||
|
||||
"@emotion/unitless@^0.7.4":
|
||||
"@emotion/unitless@^0.7.4", "@emotion/unitless@^0.7.5":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||
|
||||
"@emotion/utils@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
|
||||
integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
|
||||
|
||||
"@emotion/weak-memoize@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
||||
"@jimp/bmp@^0.16.1":
|
||||
version "0.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.1.tgz#6e2da655b2ba22e721df0795423f34e92ef13768"
|
||||
|
@ -2193,6 +2260,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf"
|
||||
integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==
|
||||
|
||||
"@types/debounce-promise@^3.1.3":
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/debounce-promise/-/debounce-promise-3.1.3.tgz#dd0d6b96ee61da0dd4c413e3ea03a425ffa36b3f"
|
||||
integrity sha512-mjcCf//DAUQ6YLQMhqYJAv/+a4BsE1GQFmy1el5K62wLJJmQwGi3TsnshhOFynPpuBF9Gh2Vvb+5ImPi47KaZw==
|
||||
|
||||
"@types/hoist-non-react-statics@*":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
|
@ -2245,7 +2317,24 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
|
||||
"@types/react-transition-group@^4.4.0":
|
||||
"@types/react-dom@*":
|
||||
version "17.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.5.tgz#df44eed5b8d9e0b13bb0cd38e0ea6572a1231227"
|
||||
integrity sha512-ikqukEhH4H9gr4iJCmQVNzTB307kROe3XFfHAOTxOXPOw7lAoEXnM5KWTkzeANGL5Ce6ABfiMl/zJBYNi7ObmQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-select@^4.0.15":
|
||||
version "4.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-4.0.15.tgz#2e6a1cff22c4bbae6c95b8dbee5b5097c12eae54"
|
||||
integrity sha512-GPyBFYGMVFCtF4eg9riodEco+s2mflR10Nd5csx69+bcdvX6Uo9H/jgrIqovBU9yxBppB9DS66OwD6xxgVqOYQ==
|
||||
dependencies:
|
||||
"@emotion/serialize" "^1.0.0"
|
||||
"@types/react" "*"
|
||||
"@types/react-dom" "*"
|
||||
"@types/react-transition-group" "*"
|
||||
|
||||
"@types/react-transition-group@*", "@types/react-transition-group@^4.4.0":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.1.tgz#e1a3cb278df7f47f17b5082b1b3da17170bd44b1"
|
||||
integrity sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==
|
||||
|
@ -3671,6 +3760,11 @@ debounce-fn@^4.0.0:
|
|||
dependencies:
|
||||
mimic-fn "^3.0.0"
|
||||
|
||||
debounce-promise@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/debounce-promise/-/debounce-promise-3.1.2.tgz#320fb8c7d15a344455cd33cee5ab63530b6dc7c5"
|
||||
integrity sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
|
@ -4627,7 +4721,7 @@ hmac-drbg@^1.0.1:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0:
|
||||
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
|
@ -5348,6 +5442,11 @@ media-typer@0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
||||
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
|
||||
|
||||
memoize-one@^5.0.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||
|
||||
memory-fs@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
||||
|
@ -6288,7 +6387,7 @@ prop-types-extra@^1.1.0:
|
|||
react-is "^16.3.2"
|
||||
warning "^4.0.0"
|
||||
|
||||
prop-types@15.7.2, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -6491,6 +6590,13 @@ react-fast-compare@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||
|
||||
react-input-autosize@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-3.0.0.tgz#6b5898c790d4478d69420b55441fcc31d5c50a85"
|
||||
integrity sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==
|
||||
dependencies:
|
||||
prop-types "^15.5.8"
|
||||
|
||||
react-is@16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
@ -6527,12 +6633,25 @@ react-refresh@0.8.3:
|
|||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
|
||||
|
||||
react-select@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-4.3.1.tgz#389fc07c9bc7cf7d3c377b7a05ea18cd7399cb81"
|
||||
integrity sha512-HBBd0dYwkF5aZk1zP81Wx5UsLIIT2lSvAY2JiJo199LjoLHoivjn9//KsmvQMEFGNhe58xyuOITjfxKCcGc62Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
"@emotion/cache" "^11.4.0"
|
||||
"@emotion/react" "^11.1.1"
|
||||
memoize-one "^5.0.0"
|
||||
prop-types "^15.6.0"
|
||||
react-input-autosize "^3.0.0"
|
||||
react-transition-group "^4.3.0"
|
||||
|
||||
react-top-loading-bar@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-top-loading-bar/-/react-top-loading-bar-2.0.1.tgz#c8805ad9c1068766fdd3cadd414e67cfdf1878e9"
|
||||
integrity sha512-wkRlK9Rte4TU817GDcjlsCoDOxrrnvsNvK609FKyio0EIrmmqjQDz5DB8HbN88CHNZBy5Lh/OBALc03ioWFPuQ==
|
||||
|
||||
react-transition-group@^4.4.1:
|
||||
react-transition-group@^4.3.0, react-transition-group@^4.4.1:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
||||
|
@ -7268,6 +7387,11 @@ stylis@3.5.4:
|
|||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
|
||||
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
|
||||
|
||||
stylis@^4.0.3:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
|
||||
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
|
|
Loading…
Reference in a new issue