diff --git a/web/package.json b/web/package.json index 1f2b6e3..29bc599 100644 --- a/web/package.json +++ b/web/package.json @@ -28,6 +28,7 @@ "react-router-dom": "^6.3.0", "react-select": "^5.3.2", "react-table": "^7.8.0", + "react-textarea-autosize": "^8.3.4", "react-tooltip": "^5.5.2", "stacktracey": "^2.1.8", "vite-plugin-pwa": "^0.14.6", diff --git a/web/src/components/inputs/input.tsx b/web/src/components/inputs/input.tsx index bbdf4cd..8a14066 100644 --- a/web/src/components/inputs/input.tsx +++ b/web/src/components/inputs/input.tsx @@ -3,12 +3,14 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +import { useEffect } from "react"; import { Field, FieldProps, useFormikContext } from "formik"; -import { classNames } from "@utils"; import { EyeIcon, EyeSlashIcon, CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/solid"; +import TextareaAutosize from "react-textarea-autosize"; + import { useToggle } from "@hooks/hooks"; import { CustomTooltip } from "@components/tooltips/CustomTooltip"; -import { useEffect } from "react"; +import { classNames } from "@utils"; type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; @@ -234,6 +236,147 @@ export const RegexField = ({ ); }; +export const RegexTextAreaField = ({ + name, + defaultValue, + label, + placeholder, + columns, + autoComplete = "off", + useRegex, + hidden, + tooltip, + disabled +}: RegexFieldProps) => { + const validRegex = (pattern: string) => { + + // Check for unsupported lookahead and lookbehind assertions + if (/\(\?<=|\(\?/.test(pattern)) { + return false; + } + + // Check for unsupported recursive patterns + if (/\(\?(R|0)\)/.test(pattern)) { + return false; + } + + // Check for unsupported possessive quantifiers + if (/[*+?]{1}\+|\{[0-9]+,[0-9]*\}\+/.test(pattern)) { + return false; + } + + // Check for unsupported control verbs + if (/\\g { + let error = ""; + + if (!validRegex(val)) { + error = "Invalid regex"; + } + + return error; + }; + + const { validateForm } = useFormikContext(); + useEffect(() => { + if (useRegex) { + validateForm(); + } + }, [useRegex]); + + return ( + + ); +}; + interface TextAreaProps { name: string; defaultValue?: string; @@ -244,6 +387,7 @@ interface TextAreaProps { autoComplete?: string; hidden?: boolean; disabled?: boolean; + tooltip?: JSX.Element; } export const TextArea = ({ @@ -255,6 +399,7 @@ export const TextArea = ({ rows, autoComplete, hidden, + tooltip, disabled }: TextAreaProps) => (
{label && ( -
); +interface TextAreaAutoResizeProps { + name: string; + defaultValue?: string; + label?: string; + placeholder?: string; + columns?: COL_WIDTHS; + rows?: number; + autoComplete?: string; + hidden?: boolean; + disabled?: boolean; + tooltip?: JSX.Element; + +} + +export const TextAreaAutoResize = ({ + name, + defaultValue, + label, + placeholder, + columns, + rows, + autoComplete, + hidden, + tooltip, + disabled +}: TextAreaAutoResizeProps) => ( + +); + + interface PasswordFieldProps { name: string; label?: string; diff --git a/web/src/screens/filters/details.tsx b/web/src/screens/filters/details.tsx index f21d6c5..ea584e6 100644 --- a/web/src/screens/filters/details.tsx +++ b/web/src/screens/filters/details.tsx @@ -11,6 +11,7 @@ import { Form, Formik, FormikValues, useFormikContext } from "formik"; import { z } from "zod"; import { toFormikValidationSchema } from "zod-formik-adapter"; import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/solid"; +import TextareaAutosize from "react-textarea-autosize"; import { CODECS_OPTIONS, @@ -46,7 +47,8 @@ import DEBUG from "@components/debug"; import Toast from "@components/notifications/Toast"; import { DeleteModal } from "@components/modals"; import { TitleSubtitle } from "@components/headings"; -import { TextArea } from "@components/inputs/input"; +import { RegexTextAreaField, TextArea } from "@components/inputs/input"; +import { TextAreaAutoResize } from "../../components/inputs/input"; import { FilterActions } from "./action"; import { filterKeys } from "./list"; @@ -460,8 +462,7 @@ export function MoviesTv() { return (
-

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#tvmovies
} /> - +

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#tvmovies
} />

This field takes a range of years and/or comma separated single years.

https://autobrr.com/filters#tvmovies} />
@@ -508,8 +509,8 @@ export function Music({ values }: AdvancedProps) { return (
-

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#music
} /> -

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#music
} /> +

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#music
} /> +

You can use basic filtering like wildcards * or replace single characters with ?

https://autobrr.com/filters#music} />

This field takes a range of years and/or comma separated single years.

https://autobrr.com/filters#music} /> @@ -567,8 +568,8 @@ export function Advanced({ values }: AdvancedProps) {
-

This field has full regex support (Golang flavour).

https://autobrr.com/filters#advanced

Remember to tick Use Regex below if using more than * and ?.

} /> -

This field has full regex support (Golang flavour).

https://autobrr.com/filters#advanced

Remember to tick Use Regex below if using more than * and ?.

} /> +

This field has full regex support (Golang flavour).

https://autobrr.com/filters#advanced

Remember to tick Use Regex below if using more than * and ?.

} /> +

This field has full regex support (Golang flavour).

https://autobrr.com/filters#advanced

Remember to tick Use Regex below if using more than * and ?.

} /> {values.match_releases ? ( -

Comma separated list of release groups to match.

https://autobrr.com/filters#advanced} /> -

Comma separated list of release groups to ignore (takes priority over Match releases).

https://autobrr.com/filters#advanced} /> +

Comma separated list of release groups to match.

https://autobrr.com/filters#advanced} /> +

Comma separated list of release groups to ignore (takes priority over Match releases).

https://autobrr.com/filters#advanced} />
-

Comma separated list of categories to match.

https://autobrr.com/filters/categories} /> -

Comma separated list of categories to ignore (takes priority over Match releases).

https://autobrr.com/filters/categories} /> +

Comma separated list of categories to match.

https://autobrr.com/filters/categories} /> +

Comma separated list of categories to ignore (takes priority over Match releases).

https://autobrr.com/filters/categories} /> -

Comma separated list of tags to match.

https://autobrr.com/filters#advanced} /> +

Comma separated list of tags to match.

https://autobrr.com/filters#advanced} />

Logic used to match except tags.

https://autobrr.com/filters#advanced} />
-

Comma separated list of uploaders to match.

https://autobrr.com/filters#advanced} /> -

Comma separated list of uploaders to ignore (takes priority over Match releases).

https://autobrr.com/filters#advanced} /> +

Comma separated list of uploaders to match.

https://autobrr.com/filters#advanced} /> +

Comma separated list of uploaders to ignore (takes priority over Match releases).

https://autobrr.com/filters#advanced} />
diff --git a/web/yarn.lock b/web/yarn.lock index 34f85b9..6fa503c 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1238,7 +1238,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.8.4": version: 7.21.5 resolution: "@babel/runtime@npm:7.21.5" dependencies: @@ -5805,6 +5805,19 @@ __metadata: languageName: node linkType: hard +"react-textarea-autosize@npm:^8.3.4": + version: 8.3.4 + resolution: "react-textarea-autosize@npm:8.3.4" + dependencies: + "@babel/runtime": ^7.10.2 + use-composed-ref: ^1.3.0 + use-latest: ^1.2.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 87360d4392276d4e87511a73be9b0634b8bcce8f4f648cf659334d993f25ad3d4062f468f1e1944fc614123acae4299580aad00b760c6a96cec190e076f847f5 + languageName: node + linkType: hard + "react-tooltip@npm:^5.5.2": version: 5.11.1 resolution: "react-tooltip@npm:5.11.1" @@ -6829,7 +6842,16 @@ __metadata: languageName: node linkType: hard -"use-isomorphic-layout-effect@npm:^1.1.2": +"use-composed-ref@npm:^1.3.0": + version: 1.3.0 + resolution: "use-composed-ref@npm:1.3.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: f771cbadfdc91e03b7ab9eb32d0fc0cc647755711801bf507e891ad38c4bbc5f02b2509acadf9c965ec9c5f2f642fd33bdfdfb17b0873c4ad0a9b1f5e5e724bf + languageName: node + linkType: hard + +"use-isomorphic-layout-effect@npm:^1.1.1, use-isomorphic-layout-effect@npm:^1.1.2": version: 1.1.2 resolution: "use-isomorphic-layout-effect@npm:1.1.2" peerDependencies: @@ -6841,6 +6863,20 @@ __metadata: languageName: node linkType: hard +"use-latest@npm:^1.2.1": + version: 1.2.1 + resolution: "use-latest@npm:1.2.1" + dependencies: + use-isomorphic-layout-effect: ^1.1.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ed3f2ddddf6f21825e2ede4c2e0f0db8dcce5129802b69d1f0575fc1b42380436e8c76a6cd885d4e9aa8e292e60fb8b959c955f33c6a9123b83814a1a1875367 + languageName: node + linkType: hard + "use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" @@ -6966,6 +7002,7 @@ __metadata: react-router-dom: ^6.3.0 react-select: ^5.3.2 react-table: ^7.8.0 + react-textarea-autosize: ^8.3.4 react-tooltip: ^5.5.2 stacktracey: ^2.1.8 tailwindcss: ^3.1.3