diff --git a/ui/v2.5/.stylelintrc b/ui/v2.5/.stylelintrc index 0cd6917bb..341c692b5 100644 --- a/ui/v2.5/.stylelintrc +++ b/ui/v2.5/.stylelintrc @@ -74,9 +74,9 @@ except: ["after-single-line-comment", "first-nested" ], ignore: ["after-comment"], }], - "selector-max-id": 0, - "selector-max-type": 1, - "selector-class-pattern": "^(\\.*[A-Z]*[a-z]+)+(-[a-z]+)*$", + "selector-max-id": 1, + "selector-max-type": 2, + "selector-class-pattern": "^(\\.*[A-Z]*[a-z]+)+(-[a-z0-9]+)*$", "selector-combinator-space-after": "always", "selector-combinator-space-before": "always", "selector-list-comma-newline-after": "always", diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index f10df123b..6c5d272d3 100755 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -13,7 +13,7 @@ import { Settings } from "./components/Settings/Settings"; import { Stats } from "./components/Stats"; import Studios from "./components/Studios/Studios"; import { TagList } from "./components/Tags/TagList"; -import { SceneFilenameParser } from "./components/scenes/SceneFilenameParser"; +import { SceneFilenameParser } from "./components/SceneFilenameParser/SceneFilenameParser"; library.add(fas); diff --git a/ui/v2.5/src/components/SceneFilenameParser/ParserField.ts b/ui/v2.5/src/components/SceneFilenameParser/ParserField.ts new file mode 100644 index 000000000..ece22625b --- /dev/null +++ b/ui/v2.5/src/components/SceneFilenameParser/ParserField.ts @@ -0,0 +1,67 @@ + +export class ParserField { + public field: string; + public helperText?: string; + + constructor(field: string, helperText?: string) { + this.field = field; + this.helperText = helperText; + } + + public getFieldPattern() { + return `{${this.field}}`; + } + + static Title = new ParserField("title"); + static Ext = new ParserField("ext", "File extension"); + + static I = new ParserField("i", "Matches any ignored word"); + static D = new ParserField("d", "Matches any delimiter (.-_)"); + + static Performer = new ParserField("performer"); + static Studio = new ParserField("studio"); + static Tag = new ParserField("tag"); + + // date fields + static Date = new ParserField("date", "YYYY-MM-DD"); + static YYYY = new ParserField("yyyy", "Year"); + static YY = new ParserField("yy", "Year (20YY)"); + static MM = new ParserField("mm", "Two digit month"); + static DD = new ParserField("dd", "Two digit date"); + static YYYYMMDD = new ParserField("yyyymmdd"); + static YYMMDD = new ParserField("yymmdd"); + static DDMMYYYY = new ParserField("ddmmyyyy"); + static DDMMYY = new ParserField("ddmmyy"); + static MMDDYYYY = new ParserField("mmddyyyy"); + static MMDDYY = new ParserField("mmddyy"); + + static validFields = [ + ParserField.Title, + ParserField.Ext, + ParserField.D, + ParserField.I, + ParserField.Performer, + ParserField.Studio, + ParserField.Tag, + ParserField.Date, + ParserField.YYYY, + ParserField.YY, + ParserField.MM, + ParserField.DD, + ParserField.YYYYMMDD, + ParserField.YYMMDD, + ParserField.DDMMYYYY, + ParserField.DDMMYY, + ParserField.MMDDYYYY, + ParserField.MMDDYY + ]; + + static fullDateFields = [ + ParserField.YYYYMMDD, + ParserField.YYMMDD, + ParserField.DDMMYYYY, + ParserField.DDMMYY, + ParserField.MMDDYYYY, + ParserField.MMDDYY + ]; +} diff --git a/ui/v2.5/src/components/SceneFilenameParser/ParserInput.tsx b/ui/v2.5/src/components/SceneFilenameParser/ParserInput.tsx new file mode 100644 index 000000000..da410ab52 --- /dev/null +++ b/ui/v2.5/src/components/SceneFilenameParser/ParserInput.tsx @@ -0,0 +1,221 @@ +import React, { useState } from 'react'; +import { + Button, + Dropdown, + DropdownButton, + Form, + InputGroup, +} from 'react-bootstrap'; +import { ParserField } from './ParserField'; +import { ShowFields } from './ShowFields'; + +const builtInRecipes = [ + { + pattern: "{title}", + ignoreWords: [], + whitespaceCharacters: "", + capitalizeTitle: false, + description: "Filename" + }, + { + pattern: "{title}.{ext}", + ignoreWords: [], + whitespaceCharacters: "", + capitalizeTitle: false, + description: "Without extension" + }, + { + pattern: "{}.{yy}.{mm}.{dd}.{title}.XXX.{}.{ext}", + ignoreWords: [], + whitespaceCharacters: ".", + capitalizeTitle: true, + description: "" + }, + { + pattern: "{}.{yy}.{mm}.{dd}.{title}.{ext}", + ignoreWords: [], + whitespaceCharacters: ".", + capitalizeTitle: true, + description: "" + }, + { + pattern: "{title}.XXX.{}.{ext}", + ignoreWords: [], + whitespaceCharacters: ".", + capitalizeTitle: true, + description: "" + }, + { + pattern: "{}.{yy}.{mm}.{dd}.{title}.{i}.{ext}", + ignoreWords: ["cz", "fr"], + whitespaceCharacters: ".", + capitalizeTitle: true, + description: "Foreign language" + } +]; + +export interface IParserInput { + pattern: string; + ignoreWords: string[]; + whitespaceCharacters: string; + capitalizeTitle: boolean; + page: number; + pageSize: number; + findClicked: boolean; +} + +interface IParserRecipe { + pattern: string; + ignoreWords: string[]; + whitespaceCharacters: string; + capitalizeTitle: boolean; + description: string; +} + +interface IParserInputProps { + input: IParserInput; + onFind: (input: IParserInput) => void; + onPageSizeChanged: (newSize: number) => void; + showFields: Map; + setShowFields: (fields: Map) => void; +} + +export const ParserInput: React.FC = (props: IParserInputProps) => { + const [pattern, setPattern] = useState(props.input.pattern); + const [ignoreWords, setIgnoreWords] = useState( + props.input.ignoreWords.join(" ") + ); + const [whitespaceCharacters, setWhitespaceCharacters] = useState( + props.input.whitespaceCharacters + ); + const [capitalizeTitle, setCapitalizeTitle] = useState( + props.input.capitalizeTitle + ); + + function onFind() { + props.onFind({ + pattern, + ignoreWords: ignoreWords.split(" "), + whitespaceCharacters, + capitalizeTitle, + page: 1, + pageSize: props.input.pageSize, + findClicked: props.input.findClicked + }); + } + + function setParserRecipe(recipe: IParserRecipe) { + setPattern(recipe.pattern); + setIgnoreWords(recipe.ignoreWords.join(" ")); + setWhitespaceCharacters(recipe.whitespaceCharacters); + setCapitalizeTitle(recipe.capitalizeTitle); + } + + const validFields = [new ParserField("", "Wildcard")].concat( + ParserField.validFields + ); + + function addParserField(field: ParserField) { + setPattern(pattern + field.getFieldPattern()); + } + + const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"]; + + return ( + + + Filename Pattern + + setPattern(newValue.target.value)} + value={pattern} + /> + + + {validFields.map(item => ( + addParserField(item)}> + {item.field} + {item.helperText} + + ))} + + + + Use '\\' to escape literal {} characters + + + + Ignored words + setIgnoreWords(newValue.target.value)} + value={ignoreWords} + /> + Matches with {"{i}"} + + +
Title
+ + Whitespace characters: + + setWhitespaceCharacters(newValue.target.value) + } + value={whitespaceCharacters} + /> + + These characters will be replaced with whitespace in the title + + + + Capitalize title + setCapitalizeTitle(!capitalizeTitle)} + /> + + + {/* TODO - mapping stuff will go here */} + + + + {builtInRecipes.map(item => ( + setParserRecipe(item)}> + {item.pattern} + {item.description} + + ))} + + + + + props.setShowFields(fields)} + /> + + + + + + props.onPageSizeChanged(parseInt(event.target.value, 10)) + } + defaultValue={props.input.pageSize} + className="col-1 filter-item" + > + {PAGE_SIZE_OPTIONS.map(val => ( + + ))} + + +
+ ); +} diff --git a/ui/v2.5/src/components/scenes/SceneFilenameParser.tsx b/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx similarity index 71% rename from ui/v2.5/src/components/scenes/SceneFilenameParser.tsx rename to ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx index 8db9d8a34..e677c923e 100644 --- a/ui/v2.5/src/components/scenes/SceneFilenameParser.tsx +++ b/ui/v2.5/src/components/SceneFilenameParser/SceneFilenameParser.tsx @@ -5,19 +5,18 @@ import { Badge, Button, Card, - Collapse, - Dropdown, - DropdownButton, Form, Table } from "react-bootstrap"; import _ from "lodash"; import { StashService } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; -import { FilterSelect, Icon, StudioSelect, LoadingIndicator } from "src/components/Shared"; +import { FilterSelect, StudioSelect, LoadingIndicator } from "src/components/Shared"; import { TextUtils } from "src/utils"; import { useToast } from "src/hooks"; import { Pagination } from "../list/Pagination"; +import { IParserInput, ParserInput } from './ParserInput'; +import { ParserField } from './ParserField'; class ParserResult { public value: GQL.Maybe = null; @@ -37,72 +36,6 @@ class ParserResult { } } -class ParserField { - public field: string; - public helperText?: string; - - constructor(field: string, helperText?: string) { - this.field = field; - this.helperText = helperText; - } - - public getFieldPattern() { - return `{${this.field}}`; - } - - static Title = new ParserField("title"); - static Ext = new ParserField("ext", "File extension"); - - static I = new ParserField("i", "Matches any ignored word"); - static D = new ParserField("d", "Matches any delimiter (.-_)"); - - static Performer = new ParserField("performer"); - static Studio = new ParserField("studio"); - static Tag = new ParserField("tag"); - - // date fields - static Date = new ParserField("date", "YYYY-MM-DD"); - static YYYY = new ParserField("yyyy", "Year"); - static YY = new ParserField("yy", "Year (20YY)"); - static MM = new ParserField("mm", "Two digit month"); - static DD = new ParserField("dd", "Two digit date"); - static YYYYMMDD = new ParserField("yyyymmdd"); - static YYMMDD = new ParserField("yymmdd"); - static DDMMYYYY = new ParserField("ddmmyyyy"); - static DDMMYY = new ParserField("ddmmyy"); - static MMDDYYYY = new ParserField("mmddyyyy"); - static MMDDYY = new ParserField("mmddyy"); - - static validFields = [ - ParserField.Title, - ParserField.Ext, - ParserField.D, - ParserField.I, - ParserField.Performer, - ParserField.Studio, - ParserField.Tag, - ParserField.Date, - ParserField.YYYY, - ParserField.YY, - ParserField.MM, - ParserField.DD, - ParserField.YYYYMMDD, - ParserField.YYMMDD, - ParserField.DDMMYYYY, - ParserField.DDMMYY, - ParserField.MMDDYYYY, - ParserField.MMDDYY - ]; - - static fullDateFields = [ - ParserField.YYYYMMDD, - ParserField.YYMMDD, - ParserField.DDMMYYYY, - ParserField.DDMMYY, - ParserField.MMDDYYYY, - ParserField.MMDDYY - ]; -} class SceneParserResult { public id: string; public filename: string; @@ -219,69 +152,6 @@ class SceneParserResult { } } -interface IParserInput { - pattern: string; - ignoreWords: string[]; - whitespaceCharacters: string; - capitalizeTitle: boolean; - page: number; - pageSize: number; - findClicked: boolean; -} - -interface IParserRecipe { - pattern: string; - ignoreWords: string[]; - whitespaceCharacters: string; - capitalizeTitle: boolean; - description: string; -} - -const builtInRecipes = [ - { - pattern: "{title}", - ignoreWords: [], - whitespaceCharacters: "", - capitalizeTitle: false, - description: "Filename" - }, - { - pattern: "{title}.{ext}", - ignoreWords: [], - whitespaceCharacters: "", - capitalizeTitle: false, - description: "Without extension" - }, - { - pattern: "{}.{yy}.{mm}.{dd}.{title}.XXX.{}.{ext}", - ignoreWords: [], - whitespaceCharacters: ".", - capitalizeTitle: true, - description: "" - }, - { - pattern: "{}.{yy}.{mm}.{dd}.{title}.{ext}", - ignoreWords: [], - whitespaceCharacters: ".", - capitalizeTitle: true, - description: "" - }, - { - pattern: "{title}.XXX.{}.{ext}", - ignoreWords: [], - whitespaceCharacters: ".", - capitalizeTitle: true, - description: "" - }, - { - pattern: "{}.{yy}.{mm}.{dd}.{title}.{i}.{ext}", - ignoreWords: ["cz", "fr"], - whitespaceCharacters: ".", - capitalizeTitle: true, - description: "Foreign language" - } -]; - const initialParserInput = { pattern: "{title}.{ext}", ignoreWords: [], @@ -518,181 +388,6 @@ export const SceneFilenameParser: React.FC = () => { setAllStudioSet(selected); } - interface IShowFieldsProps { - fields: Map; - onShowFieldsChanged: (fields: Map) => void; - } - - function ShowFields(props: IShowFieldsProps) { - const [open, setOpen] = useState(false); - - function handleClick(label: string) { - const copy = new Map(props.fields); - copy.set(label, !props.fields.get(label)); - props.onShowFieldsChanged(copy); - } - - const fieldRows = [...props.fields.entries()].map(([label, enabled]) => ( -
{ - handleClick(label); - }} - > - - {label} -
- )); - - return ( -
-
setOpen(!open)}> - - Display fields -
- -
{fieldRows}
-
-
- ); - } - - interface IParserInputProps { - input: IParserInput; - onFind: (input: IParserInput) => void; - } - - function ParserInput(props: IParserInputProps) { - const [pattern, setPattern] = useState(props.input.pattern); - const [ignoreWords, setIgnoreWords] = useState( - props.input.ignoreWords.join(" ") - ); - const [whitespaceCharacters, setWhitespaceCharacters] = useState( - props.input.whitespaceCharacters - ); - const [capitalizeTitle, setCapitalizeTitle] = useState( - props.input.capitalizeTitle - ); - - function onFind() { - props.onFind({ - pattern, - ignoreWords: ignoreWords.split(" "), - whitespaceCharacters, - capitalizeTitle, - page: 1, - pageSize: props.input.pageSize, - findClicked: props.input.findClicked - }); - } - - function setParserRecipe(recipe: IParserRecipe) { - setPattern(recipe.pattern); - setIgnoreWords(recipe.ignoreWords.join(" ")); - setWhitespaceCharacters(recipe.whitespaceCharacters); - setCapitalizeTitle(recipe.capitalizeTitle); - } - - const validFields = [new ParserField("", "Wildcard")].concat( - ParserField.validFields - ); - - function addParserField(field: ParserField) { - setPattern(pattern + field.getFieldPattern()); - } - - const PAGE_SIZE_OPTIONS = ["20", "40", "60", "120"]; - - return ( - - - setPattern(newValue.target.value)} - value={pattern} - /> - - {validFields.map(item => ( - addParserField(item)}> - {item.field} - {item.helperText} - - ))} - -
Use '\\' to escape literal {} characters
-
- - - Ignored words:: - setIgnoreWords(newValue.target.value)} - value={ignoreWords} - /> -
Matches with {"{i}"}
-
- - -
Title
- Whitespace characters: - - setWhitespaceCharacters(newValue.target.value) - } - value={whitespaceCharacters} - /> - - Capitalize title - setCapitalizeTitle(!capitalizeTitle)} - /> - -
- These characters will be replaced with whitespace in the title -
-
- - {/* TODO - mapping stuff will go here */} - - - - {builtInRecipes.map(item => ( - setParserRecipe(item)}> - {item.pattern} - {item.description} - - ))} - - - - - setShowFields(fields)} - /> - - - - - - onPageSizeChanged(parseInt(event.target.value, 10)) - } - defaultValue={props.input.pageSize} - className="filter-item" - > - {PAGE_SIZE_OPTIONS.map(val => ( - - ))} - - -
- ); - } - interface ISceneParserFieldProps { parserResult: ParserResult; className?: string; @@ -1062,7 +757,13 @@ export const SceneFilenameParser: React.FC = () => { return (

Scene Filename Parser

- onFindClicked(input)} /> + onFindClicked(input)} + onPageSizeChanged={onPageSizeChanged} + showFields={showFields} + setShowFields={setShowFields} + /> {isLoading && } {renderTable()} diff --git a/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx b/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx new file mode 100644 index 000000000..d010424ed --- /dev/null +++ b/ui/v2.5/src/components/SceneFilenameParser/ShowFields.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { + Button, + Collapse +} from 'react-bootstrap'; +import { Icon } from 'src/components/Shared'; + +interface IShowFieldsProps { + fields: Map; + onShowFieldsChanged: (fields: Map) => void; +} + +export const ShowFields = (props: IShowFieldsProps) => { + const [open, setOpen] = useState(false); + + function handleClick(label: string) { + const copy = new Map(props.fields); + copy.set(label, !props.fields.get(label)); + props.onShowFieldsChanged(copy); + } + + const fieldRows = [...props.fields.entries()].map(([label, enabled]) => ( +
{ + handleClick(label); + }} + > + + {label} +
+ )); + + return ( +
+ + +
{fieldRows}
+
+
+ ); +} diff --git a/ui/v2.5/src/components/Shared/DurationInput.tsx b/ui/v2.5/src/components/Shared/DurationInput.tsx index b85f399ec..269b6e576 100644 --- a/ui/v2.5/src/components/Shared/DurationInput.tsx +++ b/ui/v2.5/src/components/Shared/DurationInput.tsx @@ -62,9 +62,10 @@ export const DurationInput: React.FC = (props: IProps) => { } return ( - + setValue(e.target.value)} diff --git a/ui/v2.5/src/components/Shared/Select.tsx b/ui/v2.5/src/components/Shared/Select.tsx index 92dfc7c0b..8ddbacada 100644 --- a/ui/v2.5/src/components/Shared/Select.tsx +++ b/ui/v2.5/src/components/Shared/Select.tsx @@ -239,9 +239,11 @@ export const TagSelect: React.FC = props => { const Toast = useToast(); const placeholder = props.noSelectionString ?? "Select tags..."; + const selectedTags = props.ids ?? selectedIds; + const tags = data?.allTags ?? []; const selected = tags - .filter(tag => selectedIds.indexOf(tag.id) !== -1) + .filter(tag => selectedTags.indexOf(tag.id) !== -1) .map(tag => ({ value: tag.id, label: tag.name })); const items: Option[] = tags.map(item => ({ value: item.id, diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index 3a5b6cf9f..dcb227ff7 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -40,12 +40,21 @@ } } -.duration-button { - border-bottom-left-radius: 0; - border-top-left-radius: 0; - line-height: 10px; - margin-left: 0 !important; - padding: 1px 7px; +.duration-input { + .duration-control { + min-width: 3rem; + } + + .duration-button { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + line-height: 10px; + padding: 1px 7px; + } + + .btn + .btn { + margin-left: 0; + } } .folder-list { diff --git a/ui/v2.5/src/components/Tags/styles.scss b/ui/v2.5/src/components/Tags/styles.scss index 54652f2d8..bad2b2247 100644 --- a/ui/v2.5/src/components/Tags/styles.scss +++ b/ui/v2.5/src/components/Tags/styles.scss @@ -1,4 +1,3 @@ - #tag-list-container { display: flex; flex-direction: column; @@ -24,5 +23,3 @@ text-decoration: underline; } } - - diff --git a/ui/v2.5/src/components/Wall/Wall.scss b/ui/v2.5/src/components/Wall/Wall.scss index 78c4218a1..5e19ad64f 100644 --- a/ui/v2.5/src/components/Wall/Wall.scss +++ b/ui/v2.5/src/components/Wall/Wall.scss @@ -85,17 +85,19 @@ z-index: -1; } -.wall-item video, -.wall-item img { - height: 100%; - object-fit: contain; - width: 100%; -} +.wall { + .wall-item { + line-height: 0; + overflow: visible; + padding: 0; + position: relative; + width: 20%; -.wall-item { - line-height: 0; - overflow: visible; - padding: 0 !important; - position: relative; - width: 20%; + video, + img { + height: 100%; + object-fit: contain; + width: 100%; + } + } } diff --git a/ui/v2.5/src/components/list/ListFilter.tsx b/ui/v2.5/src/components/list/ListFilter.tsx index 89395db35..bc94ec803 100644 --- a/ui/v2.5/src/components/list/ListFilter.tsx +++ b/ui/v2.5/src/components/list/ListFilter.tsx @@ -228,7 +228,7 @@ export const ListFilter: React.FC = ( if (props.onChangeZoom) { return ( = ( ); @@ -117,7 +117,7 @@ export const SceneCard: React.FC = ( ); @@ -135,7 +135,7 @@ export const SceneCard: React.FC = ( ); diff --git a/ui/v2.5/src/components/scenes/ScenePlayer/ScenePlayerScrubber.scss b/ui/v2.5/src/components/scenes/ScenePlayer/ScenePlayerScrubber.scss index a0c931f32..2cffd375f 100644 --- a/ui/v2.5/src/components/scenes/ScenePlayer/ScenePlayerScrubber.scss +++ b/ui/v2.5/src/components/scenes/ScenePlayer/ScenePlayerScrubber.scss @@ -128,4 +128,3 @@ display: inline-block; width: 100%; } - diff --git a/ui/v2.5/src/components/scenes/SceneSelectedOptions.tsx b/ui/v2.5/src/components/scenes/SceneSelectedOptions.tsx index 9160767e9..3c8735100 100644 --- a/ui/v2.5/src/components/scenes/SceneSelectedOptions.tsx +++ b/ui/v2.5/src/components/scenes/SceneSelectedOptions.tsx @@ -180,58 +180,50 @@ export const SceneSelectedOptions: React.FC = ( return ret; } - function updateScenesEditState(state: GQL.SlimSceneDataFragment[]) { + useEffect(() => { + const state = props.selected; let updateRating = ""; - let updateStudioId: string|undefined; + let updateStudioID: string|undefined; let updatePerformerIds: string[] = []; let updateTagIds: string[] = []; let first = true; state.forEach((scene: GQL.SlimSceneDataFragment) => { - const thisRating = scene.rating?.toString() ?? ""; - const thisStudio = scene?.studio?.id; + const sceneRating = scene.rating?.toString() ?? ""; + const sceneStudioID = scene?.studio?.id; + const scenePerformerIDs = (scene.performers ?? []).map(p => p.id).sort(); + const sceneTagIDs = (scene.tags ?? []).map(p => p.id).sort(); if (first) { - updateRating = thisRating; - updateStudioId = thisStudio; - updatePerformerIds = scene.performers - ? scene.performers.map(p => p.id).sort() - : []; - updateTagIds = scene.tags ? scene.tags.map(p => p.id).sort() : []; + updateRating = sceneRating; + updateStudioID = sceneStudioID; + updatePerformerIds = scenePerformerIDs; + updateTagIds = sceneTagIDs; first = false; } else { - if (rating !== thisRating) { + if (sceneRating !== updateRating) { updateRating = ""; } - if (studioId !== thisStudio) { - updateStudioId = undefined; + if (sceneStudioID !== updateStudioID) { + updateStudioID = undefined; } - const perfIds = scene.performers - ? scene.performers.map(p => p.id).sort() - : []; - const tIds = scene.tags ? scene.tags.map(t => t.id).sort() : []; - - if (!_.isEqual(performerIds, perfIds)) { + if (!_.isEqual(scenePerformerIDs, updatePerformerIds)) { updatePerformerIds = []; } - - if (!_.isEqual(tagIds, tIds)) { + if (!_.isEqual(sceneTagIDs, updateTagIds)) { updateTagIds = []; } } }); setRating(updateRating); - setStudioId(updateStudioId); + setStudioId(updateStudioID); setPerformerIds(updatePerformerIds); setTagIds(updateTagIds); - } - - useEffect(() => { - updateScenesEditState(props.selected); setIsLoading(false); }, [props.selected]); + function renderMultiSelect( type: "performers" | "tags", ids: string[] | undefined diff --git a/ui/v2.5/src/components/scenes/styles.scss b/ui/v2.5/src/components/scenes/styles.scss index a00fe78ff..7b3a91688 100644 --- a/ui/v2.5/src/components/scenes/styles.scss +++ b/ui/v2.5/src/components/scenes/styles.scss @@ -88,6 +88,7 @@ width: 100%; } } + #details { min-height: 150px; } @@ -104,4 +105,3 @@ overflow-y: auto; } } - diff --git a/ui/v2.5/src/index.scss b/ui/v2.5/src/index.scss index aeffadf23..98eaf3af5 100755 --- a/ui/v2.5/src/index.scss +++ b/ui/v2.5/src/index.scss @@ -1,22 +1,9 @@ @import "styles/theme"; @import "styles/range"; @import "styles/scrollbars"; -@import "styles/variables"; @import "./components/**/*.scss"; body { - font-family: - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Oxygen, - Ubuntu, - Cantarell, - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; margin: 0; @@ -70,6 +57,7 @@ code { max-height: 11.25rem; } } + &.zoom-1 { width: 20rem; @@ -81,6 +69,7 @@ code { height: 15rem; } } + &.zoom-2 { width: 30rem; @@ -92,6 +81,7 @@ code { height: 22.5rem; } } + &.zoom-3 { width: 40rem; @@ -156,7 +146,7 @@ video.preview.portrait { } .tag-item { - background-color: #bfccd6; + background-color: $muted-gray; color: #182026; font-size: 12px; font-weight: 400; @@ -214,7 +204,7 @@ video.preview.portrait { } .rating-5 { - background: #FF2F39; + background: #ff2f39; } .rating-4 { @@ -224,6 +214,7 @@ video.preview.portrait { .rating-3 { background: $orange1; } + .rating-2 { background: $sepia1; } @@ -250,7 +241,7 @@ video.preview.portrait { .scene-specs-overlay { bottom: 1rem; - color: #f5f8fa; + color: $text-color; display: block; font-weight: 400; letter-spacing: -.03rem; @@ -274,7 +265,7 @@ video.preview.portrait { background-position: right top; background-repeat: no-repeat; background-size: contain; - color: #f5f8fa; + color: $text-color; display: inline-block; height: 100%; letter-spacing: -.03rem; @@ -332,20 +323,13 @@ video.preview.portrait { white-space: pre-line; } -.studio { - .image { - background-position: center !important; - background-repeat: no-repeat !important; - background-size: contain !important; - height: 100px; - } -} - +/* stylelint-disable selector-class-pattern */ .react-photo-gallery--gallery { img { object-fit: contain; } } +/* stylelint-enable selector-class-pattern */ #parser-container { margin: 10px auto; @@ -413,11 +397,11 @@ video.preview.portrait { } .main { - color: #f5f8fa; + color: $text-color; } .table { - color: #f5f8fa; + color: $text-color; width: inherit; } @@ -425,7 +409,7 @@ video.preview.portrait { border: none; } -.table-striped tbody tr:nth-child(odd) td { +.table-striped tr:nth-child(odd) td { background: rgba(92, 112, 128, .15); } @@ -455,7 +439,7 @@ video.preview.portrait { .button-link { background-color: transparent; border-width: 0; - color: #48aff0; + color: $link-color; cursor: pointer; display: inline; padding: 0; @@ -467,7 +451,7 @@ video.preview.portrait { .scrubber-button { background-color: transparent; - color: #48aff0; + color: $link-color; } .scrubber-button:hover { @@ -532,6 +516,7 @@ video.preview.portrait { padding-bottom: 2rem; } +/* stylelint-disable selector-class-pattern */ .svg-inline--fa { margin: 0 .4rem; } @@ -541,3 +526,4 @@ video.preview.portrait { margin: 0; } } +/* stylelint-enable */ diff --git a/ui/v2.5/src/styles/_theme.scss b/ui/v2.5/src/styles/_theme.scss index 610088cc7..9309a7dcc 100644 --- a/ui/v2.5/src/styles/_theme.scss +++ b/ui/v2.5/src/styles/_theme.scss @@ -2,6 +2,7 @@ /* Blueprint dark theme */ $secondary: #394b59; +$muted-gray: #bfccd6; $theme-colors: ( primary: #137cbd, @@ -13,7 +14,7 @@ $theme-colors: ( ); $body-bg: #202b33; -$text-muted: #bfccd6; +$text-muted: $muted-gray; $link-color: #48aff0; $link-hover-color: #48aff0; $text-color: #f5f8fa; @@ -23,10 +24,19 @@ $popover-bg: $secondary; @import "node_modules/bootstrap/scss/bootstrap"; +$red1: #a82a2a; +$orange1: #a66321; +$sepia1: #63411e; +$dark-gray2: #202b33; +$dark-gray5: #394b59; + +$pt-grid-size: 10px; +$pt-navbar-height: 4rem; + .btn.active:not(.disabled), .btn.active.minimal:not(.disabled) { background-color: rgba(138, 155, 168, .3); - color: #f5f8fa; + color: $text-color; } a.minimal, diff --git a/ui/v2.5/src/styles/_variables.scss b/ui/v2.5/src/styles/_variables.scss deleted file mode 100644 index 8254ac00a..000000000 --- a/ui/v2.5/src/styles/_variables.scss +++ /dev/null @@ -1,8 +0,0 @@ -$red1: #a82a2a; -$orange1: #a66321; -$sepia1: #63411e; -$dark-gray2: #202b33; -$dark-gray5: #394b59; - -$pt-grid-size: 10px; -$pt-navbar-height: 4rem;