mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Date picker (#3572)
* Add date picker dependency * Add DateInput component * Add DateInput to edit panels * Add DateInput to DateFilter * Add time to DateInput and add to Timestamp filter * Use calendar icon for button
This commit is contained in:
parent
b602ed2381
commit
b6b275edc8
14 changed files with 458 additions and 73 deletions
|
|
@ -49,6 +49,7 @@
|
||||||
"normalize-url": "^4.5.1",
|
"normalize-url": "^4.5.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^1.6.6",
|
"react-bootstrap": "^1.6.6",
|
||||||
|
"react-datepicker": "^4.10.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-intl": "^6.2.8",
|
"react-intl": "^6.2.8",
|
||||||
|
|
@ -83,6 +84,7 @@
|
||||||
"@types/mousetrap": "^1.6.11",
|
"@types/mousetrap": "^1.6.11",
|
||||||
"@types/node": "^18.13.0",
|
"@types/node": "^18.13.0",
|
||||||
"@types/react": "^17.0.53",
|
"@types/react": "^17.0.53",
|
||||||
|
"@types/react-datepicker": "^4.10.0",
|
||||||
"@types/react-dom": "^17.0.19",
|
"@types/react-dom": "^17.0.19",
|
||||||
"@types/react-helmet": "^6.1.6",
|
"@types/react-helmet": "^6.1.6",
|
||||||
"@types/react-router-bootstrap": "^0.24.5",
|
"@types/react-router-bootstrap": "^0.24.5",
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import { galleryTitle } from "src/core/galleries";
|
||||||
import { useRatingKeybinds } from "src/hooks/keybinds";
|
import { useRatingKeybinds } from "src/hooks/keybinds";
|
||||||
import { ConfigurationContext } from "src/hooks/Config";
|
import { ConfigurationContext } from "src/hooks/Config";
|
||||||
import isEqual from "lodash-es/isEqual";
|
import isEqual from "lodash-es/isEqual";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
gallery: Partial<GQL.GalleryDataFragment>;
|
gallery: Partial<GQL.GalleryDataFragment>;
|
||||||
|
|
@ -458,11 +459,18 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
{renderTextField(
|
<Form.Group controlId="date" as={Row}>
|
||||||
"date",
|
{FormUtils.renderLabel({
|
||||||
intl.formatMessage({ id: "date" }),
|
title: intl.formatMessage({ id: "date" }),
|
||||||
"YYYY-MM-DD"
|
})}
|
||||||
)}
|
<Col xs={9}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.date}
|
||||||
|
onValueChange={(value) => formik.setFieldValue("date", value)}
|
||||||
|
error={formik.errors.date}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
<Form.Group controlId="rating" as={Row}>
|
<Form.Group controlId="rating" as={Row}>
|
||||||
{FormUtils.renderLabel({
|
{FormUtils.renderLabel({
|
||||||
title: intl.formatMessage({ id: "rating" }),
|
title: intl.formatMessage({ id: "rating" }),
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||||
import { useRatingKeybinds } from "src/hooks/keybinds";
|
import { useRatingKeybinds } from "src/hooks/keybinds";
|
||||||
import { ConfigurationContext } from "src/hooks/Config";
|
import { ConfigurationContext } from "src/hooks/Config";
|
||||||
import isEqual from "lodash-es/isEqual";
|
import isEqual from "lodash-es/isEqual";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
image: GQL.ImageDataFragment;
|
image: GQL.ImageDataFragment;
|
||||||
|
|
@ -205,11 +206,18 @@ export const ImageEditPanel: React.FC<IProps> = ({
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
{renderTextField(
|
<Form.Group controlId="date" as={Row}>
|
||||||
"date",
|
{FormUtils.renderLabel({
|
||||||
intl.formatMessage({ id: "date" }),
|
title: intl.formatMessage({ id: "date" }),
|
||||||
"YYYY-MM-DD"
|
})}
|
||||||
)}
|
<Col xs={9}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.date}
|
||||||
|
onValueChange={(value) => formik.setFieldValue("date", value)}
|
||||||
|
error={formik.errors.date}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
<Form.Group controlId="rating" as={Row}>
|
<Form.Group controlId="rating" as={Row}>
|
||||||
{FormUtils.renderLabel({
|
{FormUtils.renderLabel({
|
||||||
title: intl.formatMessage({ id: "rating" }),
|
title: intl.formatMessage({ id: "rating" }),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
|
||||||
import { CriterionModifier } from "../../../core/generated-graphql";
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { IDateValue } from "../../../models/list-filter/types";
|
import { IDateValue } from "../../../models/list-filter/types";
|
||||||
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
interface IDateFilterProps {
|
interface IDateFilterProps {
|
||||||
criterion: Criterion<IDateValue>;
|
criterion: Criterion<IDateValue>;
|
||||||
|
|
@ -18,11 +19,7 @@ export const DateFilter: React.FC<IDateFilterProps> = ({
|
||||||
|
|
||||||
const { value } = criterion;
|
const { value } = criterion;
|
||||||
|
|
||||||
function onChanged(
|
function onChanged(newValue: string, property: "value" | "value2") {
|
||||||
event: React.ChangeEvent<HTMLInputElement>,
|
|
||||||
property: "value" | "value2"
|
|
||||||
) {
|
|
||||||
const newValue = event.target.value;
|
|
||||||
const valueCopy = { ...value };
|
const valueCopy = { ...value };
|
||||||
|
|
||||||
valueCopy[property] = newValue;
|
valueCopy[property] = newValue;
|
||||||
|
|
@ -36,16 +33,10 @@ export const DateFilter: React.FC<IDateFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
equalsControl = (
|
equalsControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
className="btn-secondary"
|
|
||||||
type="text"
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
onChanged(e, "value")
|
|
||||||
}
|
|
||||||
value={value?.value ?? ""}
|
value={value?.value ?? ""}
|
||||||
placeholder={
|
onValueChange={(v) => onChanged(v, "value")}
|
||||||
intl.formatMessage({ id: "criterion.value" }) + " (YYYY-MM-DD)"
|
placeholder={intl.formatMessage({ id: "criterion.value" })}
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
|
|
@ -59,17 +50,10 @@ export const DateFilter: React.FC<IDateFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
lowerControl = (
|
lowerControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
className="btn-secondary"
|
|
||||||
type="text"
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
onChanged(e, "value")
|
|
||||||
}
|
|
||||||
value={value?.value ?? ""}
|
value={value?.value ?? ""}
|
||||||
placeholder={
|
onValueChange={(v) => onChanged(v, "value")}
|
||||||
intl.formatMessage({ id: "criterion.greater_than" }) +
|
placeholder={intl.formatMessage({ id: "criterion.greater_than" })}
|
||||||
" (YYYY-MM-DD)"
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
|
|
@ -83,25 +67,21 @@ export const DateFilter: React.FC<IDateFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
upperControl = (
|
upperControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
className="btn-secondary"
|
|
||||||
type="text"
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
onChanged(
|
|
||||||
e,
|
|
||||||
criterion.modifier === CriterionModifier.LessThan
|
|
||||||
? "value"
|
|
||||||
: "value2"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
value={
|
value={
|
||||||
(criterion.modifier === CriterionModifier.LessThan
|
(criterion.modifier === CriterionModifier.LessThan
|
||||||
? value?.value
|
? value?.value
|
||||||
: value?.value2) ?? ""
|
: value?.value2) ?? ""
|
||||||
}
|
}
|
||||||
placeholder={
|
onValueChange={(v) =>
|
||||||
intl.formatMessage({ id: "criterion.less_than" }) + " (YYYY-MM-DD)"
|
onChanged(
|
||||||
|
v,
|
||||||
|
criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? "value"
|
||||||
|
: "value2"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
placeholder={intl.formatMessage({ id: "criterion.less_than" })}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
|
||||||
import { CriterionModifier } from "../../../core/generated-graphql";
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ITimestampValue } from "../../../models/list-filter/types";
|
import { ITimestampValue } from "../../../models/list-filter/types";
|
||||||
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
interface ITimestampFilterProps {
|
interface ITimestampFilterProps {
|
||||||
criterion: Criterion<ITimestampValue>;
|
criterion: Criterion<ITimestampValue>;
|
||||||
|
|
@ -18,11 +19,7 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
|
|
||||||
const { value } = criterion;
|
const { value } = criterion;
|
||||||
|
|
||||||
function onChanged(
|
function onChanged(newValue: string, property: "value" | "value2") {
|
||||||
event: React.ChangeEvent<HTMLInputElement>,
|
|
||||||
property: "value" | "value2"
|
|
||||||
) {
|
|
||||||
const newValue = event.target.value;
|
|
||||||
const valueCopy = { ...value };
|
const valueCopy = { ...value };
|
||||||
|
|
||||||
valueCopy[property] = newValue;
|
valueCopy[property] = newValue;
|
||||||
|
|
@ -36,7 +33,13 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
equalsControl = (
|
equalsControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
|
value={value?.value ?? ""}
|
||||||
|
onValueChange={(v) => onChanged(v, "value")}
|
||||||
|
placeholder={intl.formatMessage({ id: "criterion.value" })}
|
||||||
|
isTime
|
||||||
|
/>
|
||||||
|
{/* <Form.Control
|
||||||
className="btn-secondary"
|
className="btn-secondary"
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
|
@ -47,7 +50,7 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
intl.formatMessage({ id: "criterion.value" }) +
|
intl.formatMessage({ id: "criterion.value" }) +
|
||||||
" (YYYY-MM-DD HH:MM)"
|
" (YYYY-MM-DD HH:MM)"
|
||||||
}
|
}
|
||||||
/>
|
/> */}
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +63,13 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
lowerControl = (
|
lowerControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
|
value={value?.value ?? ""}
|
||||||
|
onValueChange={(v) => onChanged(v, "value")}
|
||||||
|
placeholder={intl.formatMessage({ id: "criterion.greater_than" })}
|
||||||
|
isTime
|
||||||
|
/>
|
||||||
|
{/* <Form.Control
|
||||||
className="btn-secondary"
|
className="btn-secondary"
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
|
@ -71,7 +80,7 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
intl.formatMessage({ id: "criterion.greater_than" }) +
|
intl.formatMessage({ id: "criterion.greater_than" }) +
|
||||||
" (YYYY-MM-DD HH:MM)"
|
" (YYYY-MM-DD HH:MM)"
|
||||||
}
|
}
|
||||||
/>
|
/> */}
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +93,24 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
) {
|
) {
|
||||||
upperControl = (
|
upperControl = (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Control
|
<DateInput
|
||||||
|
value={
|
||||||
|
(criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? value?.value
|
||||||
|
: value?.value2) ?? ""
|
||||||
|
}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
onChanged(
|
||||||
|
v,
|
||||||
|
criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? "value"
|
||||||
|
: "value2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder={intl.formatMessage({ id: "criterion.less_than" })}
|
||||||
|
isTime
|
||||||
|
/>
|
||||||
|
{/* <Form.Control
|
||||||
className="btn-secondary"
|
className="btn-secondary"
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
|
@ -104,7 +130,7 @@ export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
intl.formatMessage({ id: "criterion.less_than" }) +
|
intl.formatMessage({ id: "criterion.less_than" }) +
|
||||||
" (YYYY-MM-DD HH:MM)"
|
" (YYYY-MM-DD HH:MM)"
|
||||||
}
|
}
|
||||||
/>
|
/> */}
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { MovieScrapeDialog } from "./MovieScrapeDialog";
|
||||||
import { useRatingKeybinds } from "src/hooks/keybinds";
|
import { useRatingKeybinds } from "src/hooks/keybinds";
|
||||||
import { ConfigurationContext } from "src/hooks/Config";
|
import { ConfigurationContext } from "src/hooks/Config";
|
||||||
import isEqual from "lodash-es/isEqual";
|
import isEqual from "lodash-es/isEqual";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
interface IMovieEditPanel {
|
interface IMovieEditPanel {
|
||||||
movie: Partial<GQL.MovieDataFragment>;
|
movie: Partial<GQL.MovieDataFragment>;
|
||||||
|
|
@ -410,11 +411,18 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
{renderTextField(
|
<Form.Group controlId="date" as={Row}>
|
||||||
"date",
|
{FormUtils.renderLabel({
|
||||||
intl.formatMessage({ id: "date" }),
|
title: intl.formatMessage({ id: "date" }),
|
||||||
"YYYY-MM-DD"
|
})}
|
||||||
)}
|
<Col xs={9}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.date}
|
||||||
|
onValueChange={(value) => formik.setFieldValue("date", value)}
|
||||||
|
error={formik.errors.date}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Group controlId="studio" as={Row}>
|
<Form.Group controlId="studio" as={Row}>
|
||||||
{FormUtils.renderLabel({
|
{FormUtils.renderLabel({
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import {
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { StringListInput } from "src/components/Shared/StringListInput";
|
import { StringListInput } from "src/components/Shared/StringListInput";
|
||||||
import isEqual from "lodash-es/isEqual";
|
import isEqual from "lodash-es/isEqual";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
const isScraper = (
|
const isScraper = (
|
||||||
scraper: GQL.Scraper | GQL.StashBox
|
scraper: GQL.Scraper | GQL.StashBox
|
||||||
|
|
@ -927,8 +928,35 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
{renderField("birthdate", { placeholder: "YYYY-MM-DD" })}
|
<Form.Group controlId="birthdate" as={Row}>
|
||||||
{renderField("death_date", { placeholder: "YYYY-MM-DD" })}
|
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||||
|
<FormattedMessage id="birthdate" />
|
||||||
|
</Form.Label>
|
||||||
|
<Col xs={fieldXS} xl={fieldXL}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.birthdate}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
formik.setFieldValue("birthdate", value)
|
||||||
|
}
|
||||||
|
error={formik.errors.birthdate}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group controlId="death_date" as={Row}>
|
||||||
|
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||||
|
<FormattedMessage id="death_date" />
|
||||||
|
</Form.Label>
|
||||||
|
<Col xs={fieldXS} xl={fieldXL}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.death_date}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
formik.setFieldValue("death_date", value)
|
||||||
|
}
|
||||||
|
error={formik.errors.death_date}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Group as={Row}>
|
<Form.Group as={Row}>
|
||||||
<Form.Label column xs={labelXS} xl={labelXL}>
|
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ import { galleryTitle } from "src/core/galleries";
|
||||||
import { useRatingKeybinds } from "src/hooks/keybinds";
|
import { useRatingKeybinds } from "src/hooks/keybinds";
|
||||||
import { lazyComponent } from "src/utils/lazyComponent";
|
import { lazyComponent } from "src/utils/lazyComponent";
|
||||||
import isEqual from "lodash-es/isEqual";
|
import isEqual from "lodash-es/isEqual";
|
||||||
|
import { DateInput } from "src/components/Shared/DateInput";
|
||||||
|
|
||||||
const SceneScrapeDialog = lazyComponent(() => import("./SceneScrapeDialog"));
|
const SceneScrapeDialog = lazyComponent(() => import("./SceneScrapeDialog"));
|
||||||
const SceneQueryModal = lazyComponent(() => import("./SceneQueryModal"));
|
const SceneQueryModal = lazyComponent(() => import("./SceneQueryModal"));
|
||||||
|
|
@ -769,11 +770,20 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
{renderTextField(
|
|
||||||
"date",
|
<Form.Group controlId="date" as={Row}>
|
||||||
intl.formatMessage({ id: "date" }),
|
{FormUtils.renderLabel({
|
||||||
"YYYY-MM-DD"
|
title: intl.formatMessage({ id: "date" }),
|
||||||
)}
|
})}
|
||||||
|
<Col xs={9}>
|
||||||
|
<DateInput
|
||||||
|
value={formik.values.date}
|
||||||
|
onValueChange={(value) => formik.setFieldValue("date", value)}
|
||||||
|
error={formik.errors.date}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
{renderTextField(
|
{renderTextField(
|
||||||
"director",
|
"director",
|
||||||
intl.formatMessage({ id: "director" })
|
intl.formatMessage({ id: "director" })
|
||||||
|
|
|
||||||
105
ui/v2.5/src/components/Shared/DateInput.tsx
Normal file
105
ui/v2.5/src/components/Shared/DateInput.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { faCalendar } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { Button, InputGroup, Form } from "react-bootstrap";
|
||||||
|
import ReactDatePicker from "react-datepicker";
|
||||||
|
import TextUtils from "src/utils/text";
|
||||||
|
import { Icon } from "./Icon";
|
||||||
|
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
value: string | undefined;
|
||||||
|
isTime?: boolean;
|
||||||
|
onValueChange(value: string): void;
|
||||||
|
placeholder?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DateInput: React.FC<IProps> = (props: IProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const date = useMemo(() => {
|
||||||
|
const toDate = props.isTime
|
||||||
|
? TextUtils.stringToFuzzyDateTime
|
||||||
|
: TextUtils.stringToFuzzyDate;
|
||||||
|
if (props.value) {
|
||||||
|
const ret = toDate(props.value);
|
||||||
|
if (!ret || isNaN(ret.getTime())) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}, [props.value, props.isTime]);
|
||||||
|
|
||||||
|
function maybeRenderButton() {
|
||||||
|
if (!props.disabled) {
|
||||||
|
const ShowPickerButton = ({
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
onClick: (
|
||||||
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||||
|
) => void;
|
||||||
|
}) => (
|
||||||
|
<Button variant="secondary" onClick={onClick}>
|
||||||
|
<Icon icon={faCalendar} />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const dateToString = props.isTime
|
||||||
|
? TextUtils.dateTimeToString
|
||||||
|
: TextUtils.dateToString;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactDatePicker
|
||||||
|
selected={date}
|
||||||
|
onChange={(v) => {
|
||||||
|
props.onValueChange(v ? dateToString(v) : "");
|
||||||
|
}}
|
||||||
|
customInput={React.createElement(ShowPickerButton)}
|
||||||
|
showMonthDropdown
|
||||||
|
showYearDropdown
|
||||||
|
scrollableMonthYearDropdown
|
||||||
|
scrollableYearDropdown
|
||||||
|
maxDate={new Date()}
|
||||||
|
yearDropdownItemNumber={100}
|
||||||
|
portalId="date-picker-portal"
|
||||||
|
showTimeSelect={props.isTime}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const placeholderText = intl.formatMessage({
|
||||||
|
id: props.isTime ? "datetime_format" : "date_format",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<InputGroup hasValidation>
|
||||||
|
<Form.Control
|
||||||
|
className="date-input text-input"
|
||||||
|
disabled={props.disabled}
|
||||||
|
value={props.value}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
props.onValueChange(e.currentTarget.value)
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
!props.disabled
|
||||||
|
? props.placeholder
|
||||||
|
? `${props.placeholder} (${placeholderText})`
|
||||||
|
: placeholderText
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
isInvalid={!!props.error}
|
||||||
|
/>
|
||||||
|
<InputGroup.Append>{maybeRenderButton()}</InputGroup.Append>
|
||||||
|
<Form.Control.Feedback type="invalid">
|
||||||
|
{props.error}
|
||||||
|
</Form.Control.Feedback>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -323,3 +323,97 @@ button.collapse-button.btn-primary:not(:disabled):not(.disabled):active {
|
||||||
}
|
}
|
||||||
/* stylelint-enable */
|
/* stylelint-enable */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-input.form-control:focus {
|
||||||
|
// z-index gets set to 3 in input groups
|
||||||
|
z-index: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stylelint-disable */
|
||||||
|
div.react-datepicker {
|
||||||
|
background-color: $body-bg;
|
||||||
|
border-color: $card-bg;
|
||||||
|
color: $text-color;
|
||||||
|
|
||||||
|
.react-datepicker__header,
|
||||||
|
.react-datepicker-time__header {
|
||||||
|
background-color: $secondary;
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__day {
|
||||||
|
color: $text-color;
|
||||||
|
&.react-datepicker__day--disabled {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(138, 155, 168, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.react-datepicker__time-container div.react-datepicker__time {
|
||||||
|
background-color: $body-bg;
|
||||||
|
color: $text-color;
|
||||||
|
|
||||||
|
ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover {
|
||||||
|
background-color: rgba(138, 155, 168, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__day-name {
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the current month with the dropdowns
|
||||||
|
.react-datepicker__current-month {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__triangle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__month-dropdown-container {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__year-dropdown-container {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__month-dropdown-container
|
||||||
|
.react-datepicker__month-read-view,
|
||||||
|
.react-datepicker__year-dropdown-container .react-datepicker__year-read-view {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.944rem;
|
||||||
|
|
||||||
|
// react-datepicker hides these fields when the dropdown is shown
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide the dropdown arrows
|
||||||
|
.react-datepicker__month-dropdown-container
|
||||||
|
.react-datepicker__month-read-view--down-arrow,
|
||||||
|
.react-datepicker__year-dropdown-container
|
||||||
|
.react-datepicker__year-read-view--down-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-datepicker__year-dropdown,
|
||||||
|
.react-datepicker__month-dropdown {
|
||||||
|
background-color: $body-bg;
|
||||||
|
|
||||||
|
.react-datepicker__year-option:hover,
|
||||||
|
.react-datepicker__month-option:hover {
|
||||||
|
background-color: #8a9ba826;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* stylelint-enable */
|
||||||
|
|
||||||
|
#date-picker-portal .react-datepicker-popper {
|
||||||
|
z-index: 1600;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ Once migrated, these files can be deleted. The files can be optionally deleted d
|
||||||
* Added toggleable favorite button to Performer cards. ([#3369](https://github.com/stashapp/stash/pull/3369))
|
* Added toggleable favorite button to Performer cards. ([#3369](https://github.com/stashapp/stash/pull/3369))
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Added date/time pickers for date and timestamp fields. ([#3572](https://github.com/stashapp/stash/pull/3572))
|
||||||
* Added folder browser to path filter UI. ([#3570](https://github.com/stashapp/stash/pull/3570))
|
* Added folder browser to path filter UI. ([#3570](https://github.com/stashapp/stash/pull/3570))
|
||||||
* Include Organized flag in merge dialog. ([#3565](https://github.com/stashapp/stash/pull/3565))
|
* Include Organized flag in merge dialog. ([#3565](https://github.com/stashapp/stash/pull/3565))
|
||||||
* Scene cover generation is now optional during scanning, and can be generated using the Generate task. ([#3187](https://github.com/stashapp/stash/pull/3187))
|
* Scene cover generation is now optional during scanning, and can be generated using the Generate task. ([#3187](https://github.com/stashapp/stash/pull/3187))
|
||||||
|
|
|
||||||
|
|
@ -728,6 +728,8 @@
|
||||||
"custom": "Custom",
|
"custom": "Custom",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"death_date": "Death Date",
|
"death_date": "Death Date",
|
||||||
|
"date_format": "YYYY-MM-DD",
|
||||||
|
"datetime_format": "YYYY-MM-DD HH:MM",
|
||||||
"death_year": "Death Year",
|
"death_year": "Death Year",
|
||||||
"descending": "Descending",
|
"descending": "Descending",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,70 @@ const stringToDate = (dateString: string) => {
|
||||||
return new Date(year, monthIndex, day, 0, 0, 0, 0);
|
return new Date(year, monthIndex, day, 0, 0, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stringToFuzzyDate = (dateString: string) => {
|
||||||
|
if (!dateString) return null;
|
||||||
|
|
||||||
|
const parts = dateString.split("-");
|
||||||
|
// Invalid date string
|
||||||
|
let year = Number(parts[0]);
|
||||||
|
if (isNaN(year)) year = new Date().getFullYear();
|
||||||
|
let monthIndex = 0;
|
||||||
|
if (parts.length > 1) {
|
||||||
|
monthIndex = Math.max(0, Number(parts[1]) - 1);
|
||||||
|
if (monthIndex > 11 || isNaN(monthIndex)) monthIndex = 0;
|
||||||
|
}
|
||||||
|
let day = 1;
|
||||||
|
if (parts.length > 2) {
|
||||||
|
day = Number(parts[2]);
|
||||||
|
if (day > 31 || isNaN(day)) day = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(year, monthIndex, day, 0, 0, 0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stringToFuzzyDateTime = (dateString: string) => {
|
||||||
|
if (!dateString) return null;
|
||||||
|
|
||||||
|
const dateTime = dateString.split(" ");
|
||||||
|
|
||||||
|
let date: Date | null = null;
|
||||||
|
if (dateTime.length > 0) {
|
||||||
|
date = stringToFuzzyDate(dateTime[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!date) {
|
||||||
|
date = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateTime.length > 1) {
|
||||||
|
const timeParts = dateTime[1].split(":");
|
||||||
|
if (date && timeParts.length > 0) {
|
||||||
|
date.setHours(Number(timeParts[0]));
|
||||||
|
}
|
||||||
|
if (date && timeParts.length > 1) {
|
||||||
|
date.setMinutes(Number(timeParts[1]));
|
||||||
|
}
|
||||||
|
if (date && timeParts.length > 2) {
|
||||||
|
date.setSeconds(Number(timeParts[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
function dateToString(date: Date) {
|
||||||
|
return `${date.getFullYear()}-${(date.getMonth() + 1)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateTimeToString(date: Date) {
|
||||||
|
return `${dateToString(date)} ${date
|
||||||
|
.getHours()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
const getAge = (dateString?: string | null, fromDateString?: string | null) => {
|
const getAge = (dateString?: string | null, fromDateString?: string | null) => {
|
||||||
if (!dateString) return 0;
|
if (!dateString) return 0;
|
||||||
|
|
||||||
|
|
@ -355,6 +419,10 @@ const TextUtils = {
|
||||||
secondsToTimestamp,
|
secondsToTimestamp,
|
||||||
fileNameFromPath,
|
fileNameFromPath,
|
||||||
stringToDate,
|
stringToDate,
|
||||||
|
stringToFuzzyDate,
|
||||||
|
stringToFuzzyDateTime,
|
||||||
|
dateToString,
|
||||||
|
dateTimeToString,
|
||||||
age: getAge,
|
age: getAge,
|
||||||
bitRate,
|
bitRate,
|
||||||
resolution,
|
resolution,
|
||||||
|
|
|
||||||
|
|
@ -2139,7 +2139,7 @@
|
||||||
tslib "^2.4.1"
|
tslib "^2.4.1"
|
||||||
webcrypto-core "^1.7.4"
|
webcrypto-core "^1.7.4"
|
||||||
|
|
||||||
"@popperjs/core@^2.11.6":
|
"@popperjs/core@^2.11.6", "@popperjs/core@^2.9.2":
|
||||||
version "2.11.6"
|
version "2.11.6"
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
|
||||||
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||||
|
|
@ -2339,6 +2339,16 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
|
|
||||||
|
"@types/react-datepicker@^4.10.0":
|
||||||
|
version "4.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.10.0.tgz#fcb0e6a7787491bf2f37fbda2b537062608a0056"
|
||||||
|
integrity sha512-Cq+ks20vBIU6XN67TbkCHu8M7V46Y6vJrKE2n+8q/GfueJyWWTIKeC3Z7cz/d+qxGDq/VCrqA929R0U4lNuztg==
|
||||||
|
dependencies:
|
||||||
|
"@popperjs/core" "^2.9.2"
|
||||||
|
"@types/react" "*"
|
||||||
|
date-fns "^2.0.1"
|
||||||
|
react-popper "^2.2.5"
|
||||||
|
|
||||||
"@types/react-dom@^17.0.19":
|
"@types/react-dom@^17.0.19":
|
||||||
version "17.0.19"
|
version "17.0.19"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492"
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492"
|
||||||
|
|
@ -3303,7 +3313,7 @@ chardet@^0.7.0:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
classnames@^2.2.5, classnames@^2.3.1, classnames@^2.3.2:
|
classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
|
|
@ -3559,6 +3569,11 @@ dataloader@2.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0"
|
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0"
|
||||||
integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==
|
integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==
|
||||||
|
|
||||||
|
date-fns@^2.0.1, date-fns@^2.24.0:
|
||||||
|
version "2.29.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
||||||
|
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
||||||
|
|
||||||
debounce@^1.2.0:
|
debounce@^1.2.0:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
||||||
|
|
@ -6449,6 +6464,18 @@ react-bootstrap@^1.6.6:
|
||||||
uncontrollable "^7.2.1"
|
uncontrollable "^7.2.1"
|
||||||
warning "^4.0.3"
|
warning "^4.0.3"
|
||||||
|
|
||||||
|
react-datepicker@^4.10.0:
|
||||||
|
version "4.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.10.0.tgz#3f386ac5873dac5ea56544e51cdc01109938796c"
|
||||||
|
integrity sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==
|
||||||
|
dependencies:
|
||||||
|
"@popperjs/core" "^2.9.2"
|
||||||
|
classnames "^2.2.6"
|
||||||
|
date-fns "^2.24.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-onclickoutside "^6.12.2"
|
||||||
|
react-popper "^2.3.0"
|
||||||
|
|
||||||
react-dom@^17.0.2:
|
react-dom@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||||
|
|
@ -6463,6 +6490,11 @@ react-fast-compare@^2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||||
|
|
||||||
|
react-fast-compare@^3.0.1:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.1.tgz#53933d9e14f364281d6cba24bfed7a4afb808b5f"
|
||||||
|
integrity sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg==
|
||||||
|
|
||||||
react-fast-compare@^3.1.1:
|
react-fast-compare@^3.1.1:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||||
|
|
@ -6504,6 +6536,11 @@ react-lifecycles-compat@^3.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
|
react-onclickoutside@^6.12.2:
|
||||||
|
version "6.12.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz#8e6cf80c7d17a79f2c908399918158a7b02dda01"
|
||||||
|
integrity sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==
|
||||||
|
|
||||||
react-overlays@^5.1.2:
|
react-overlays@^5.1.2:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b"
|
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b"
|
||||||
|
|
@ -6526,6 +6563,14 @@ react-photo-gallery@^8.0.0:
|
||||||
prop-types "~15.7.2"
|
prop-types "~15.7.2"
|
||||||
resize-observer-polyfill "^1.5.0"
|
resize-observer-polyfill "^1.5.0"
|
||||||
|
|
||||||
|
react-popper@^2.2.5, react-popper@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
|
||||||
|
integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==
|
||||||
|
dependencies:
|
||||||
|
react-fast-compare "^3.0.1"
|
||||||
|
warning "^4.0.2"
|
||||||
|
|
||||||
react-refresh@^0.14.0:
|
react-refresh@^0.14.0:
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
||||||
|
|
@ -7957,7 +8002,7 @@ vite@^4.1.1:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
warning@^4.0.0, warning@^4.0.3:
|
warning@^4.0.0, warning@^4.0.2, warning@^4.0.3:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||||
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue