mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Fix various issues with v2.5 UI (#390)
* Fix navbar collapse breakpoint * Fix list filter colors and height * Make styling similar to v2 * Fix scene card zoom and orientation * Keep p tag even without details * Fix custom css * Default settings tab to tasks * Fix flickering progress bar. Fix percentage. * Fix unsetting studio * Fix scene gallery select * Don't hide edit on small devices * Fix log dropdown style * Use monospace for custom css input
This commit is contained in:
@@ -107,7 +107,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
|
|
||||||
function renderSortByOptions() {
|
function renderSortByOptions() {
|
||||||
return props.filter.sortByOptions.map(option => (
|
return props.filter.sortByOptions.map(option => (
|
||||||
<Dropdown.Item onClick={onChangeSortBy} key={option}>
|
<Dropdown.Item onClick={onChangeSortBy} key={option} className="bg-secondary text-white">
|
||||||
{option}
|
{option}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
));
|
));
|
||||||
@@ -186,7 +186,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
function renderSelectAll() {
|
function renderSelectAll() {
|
||||||
if (props.onSelectAll) {
|
if (props.onSelectAll) {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Item key="select-all" onClick={() => onSelectAll()}>
|
<Dropdown.Item key="select-all" className="bg-secondary text-white" onClick={() => onSelectAll()}>
|
||||||
Select All
|
Select All
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
);
|
);
|
||||||
@@ -196,7 +196,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
function renderSelectNone() {
|
function renderSelectNone() {
|
||||||
if (props.onSelectNone) {
|
if (props.onSelectNone) {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Item key="select-none" onClick={() => onSelectNone()}>
|
<Dropdown.Item key="select-none" className="bg-secondary text-white" onClick={() => onSelectNone()}>
|
||||||
Select None
|
Select None
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
);
|
);
|
||||||
@@ -209,7 +209,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
if (props.otherOperations) {
|
if (props.otherOperations) {
|
||||||
props.otherOperations.forEach(o => {
|
props.otherOperations.forEach(o => {
|
||||||
options.push(
|
options.push(
|
||||||
<Dropdown.Item key={o.text} onClick={o.onClick}>
|
<Dropdown.Item key={o.text} className="bg-secondary text-white" onClick={o.onClick}>
|
||||||
{o.text}
|
{o.text}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
);
|
);
|
||||||
@@ -222,7 +222,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
<Dropdown.Toggle variant="secondary" id="more-menu">
|
<Dropdown.Toggle variant="secondary" id="more-menu">
|
||||||
<Icon icon="ellipsis-h" />
|
<Icon icon="ellipsis-h" />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu>{options}</Dropdown.Menu>
|
<Dropdown.Menu className="bg-secondary text-white">{options}</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -259,13 +259,13 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
defaultValue={props.filter.searchTerm}
|
defaultValue={props.filter.searchTerm}
|
||||||
onChange={onChangeQuery}
|
onChange={onChangeQuery}
|
||||||
className="filter-item col-5 col-sm-2"
|
className="filter-item col-5 col-sm-2 bg-secondary text-white border-secondary"
|
||||||
/>
|
/>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
onChange={onChangePageSize}
|
onChange={onChangePageSize}
|
||||||
value={props.filter.itemsPerPage.toString()}
|
value={props.filter.itemsPerPage.toString()}
|
||||||
className="filter-item col-1 d-none d-sm-inline"
|
className="btn-secondary filter-item col-1 d-none d-sm-inline"
|
||||||
>
|
>
|
||||||
{PAGE_SIZE_OPTIONS.map(s => (
|
{PAGE_SIZE_OPTIONS.map(s => (
|
||||||
<option value={s} key={s}>
|
<option value={s} key={s}>
|
||||||
@@ -278,7 +278,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
<Dropdown.Toggle split variant="secondary" id="more-menu">
|
<Dropdown.Toggle split variant="secondary" id="more-menu">
|
||||||
{props.filter.sortBy}
|
{props.filter.sortBy}
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu>{renderSortByOptions()}</Dropdown.Menu>
|
<Dropdown.Menu className="bg-secondary text-white">{renderSortByOptions()}</Dropdown.Menu>
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
overlay={
|
overlay={
|
||||||
<Tooltip id="sort-direction-tooltip">
|
<Tooltip id="sort-direction-tooltip">
|
||||||
|
|||||||
@@ -73,20 +73,20 @@ export const MainNavbar: React.FC = () => {
|
|||||||
variant="dark"
|
variant="dark"
|
||||||
bg="dark"
|
bg="dark"
|
||||||
className="top-nav"
|
className="top-nav"
|
||||||
expand="sm"
|
expand="md"
|
||||||
>
|
>
|
||||||
<Navbar.Brand as="div" className="order-1 order-sm-0">
|
<Navbar.Brand as="div" className="order-1 order-md-0">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<Button className="minimal brand-link d-none d-sm-inline-block">
|
<Button className="minimal brand-link d-none d-md-inline-block">
|
||||||
Stash
|
Stash
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="minimal brand-icon d-inline d-sm-none">
|
<Button className="minimal brand-icon d-inline d-md-none">
|
||||||
<img src="favicon.ico" alt="" />
|
<img src="favicon.ico" alt="" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</Navbar.Brand>
|
</Navbar.Brand>
|
||||||
<Navbar.Toggle className="order-0" />
|
<Navbar.Toggle className="order-0" />
|
||||||
<Navbar.Collapse className="order-3 order-sm-1">
|
<Navbar.Collapse className="order-3 order-md-1">
|
||||||
<Nav className="mr-md-auto">
|
<Nav className="mr-md-auto">
|
||||||
{menuItems.map(i => (
|
{menuItems.map(i => (
|
||||||
<Nav.Link eventKey={i.href} as="div" key={i.href}>
|
<Nav.Link eventKey={i.href} as="div" key={i.href}>
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
|
className="text-input"
|
||||||
value={url ?? ""}
|
value={url ?? ""}
|
||||||
readOnly={!isEditing}
|
readOnly={!isEditing}
|
||||||
plaintext={!isEditing}
|
plaintext={!isEditing}
|
||||||
|
|||||||
@@ -235,11 +235,9 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
: TextUtils.fileNameFromPath(props.scene.path)}
|
: TextUtils.fileNameFromPath(props.scene.path)}
|
||||||
</h5>
|
</h5>
|
||||||
<span>{props.scene.date}</span>
|
<span>{props.scene.date}</span>
|
||||||
{props.scene.details && (
|
<p>
|
||||||
<p>
|
{props.scene.details && TextUtils.truncate(props.scene.details, 100, "... (continued)")}
|
||||||
{TextUtils.truncate(props.scene.details, 100, "... (continued)")}
|
</p>
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{maybeRenderPopoverButtonGroup()}
|
{maybeRenderPopoverButtonGroup()}
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ export const Scene: React.FC = () => {
|
|||||||
<Tab
|
<Tab
|
||||||
eventKey="scene-edit-panel"
|
eventKey="scene-edit-panel"
|
||||||
title="Edit"
|
title="Edit"
|
||||||
tabClassName="d-none d-sm-block"
|
|
||||||
>
|
>
|
||||||
<SceneEditPanel
|
<SceneEditPanel
|
||||||
scene={scene}
|
scene={scene}
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
}
|
}
|
||||||
value={url}
|
value={url}
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
|
className="text-input"
|
||||||
/>
|
/>
|
||||||
{maybeRenderScrapeButton()}
|
{maybeRenderScrapeButton()}
|
||||||
</td>
|
</td>
|
||||||
@@ -318,7 +319,8 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
title: "Date",
|
title: "Date",
|
||||||
value: date,
|
value: date,
|
||||||
isEditing: true,
|
isEditing: true,
|
||||||
onChange: setDate
|
onChange: setDate,
|
||||||
|
placeholder: "YYYY-MM-DD"
|
||||||
})}
|
})}
|
||||||
{TableUtils.renderHtmlSelect({
|
{TableUtils.renderHtmlSelect({
|
||||||
title: "Rating",
|
title: "Rating",
|
||||||
@@ -342,7 +344,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<td>Studio</td>
|
<td>Studio</td>
|
||||||
<td>
|
<td>
|
||||||
<StudioSelect
|
<StudioSelect
|
||||||
onSelect={items => items.length && setStudioId(items[0]?.id)}
|
onSelect={items => setStudioId(items.length > 0 ? items[0]?.id : undefined)}
|
||||||
ids={studioId ? [studioId] : []}
|
ids={studioId ? [studioId] : []}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@@ -377,7 +379,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Form.Label>Details</Form.Label>
|
<Form.Label>Details</Form.Label>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="textarea"
|
as="textarea"
|
||||||
className="scene-description"
|
className="scene-description text-input"
|
||||||
onChange={(newValue: React.FormEvent<HTMLTextAreaElement>) =>
|
onChange={(newValue: React.FormEvent<HTMLTextAreaElement>) =>
|
||||||
setDetails(newValue.currentTarget.value)
|
setDetails(newValue.currentTarget.value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { SettingsTasksPanel } from "./SettingsTasksPanel/SettingsTasksPanel";
|
|||||||
export const Settings: React.FC = () => {
|
export const Settings: React.FC = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const defaultTab = queryString.parse(location.search).tab ?? "configuration";
|
const defaultTab = queryString.parse(location.search).tab ?? "tasks";
|
||||||
|
|
||||||
const onSelect = (val: string) => history.push(`?tab=${val}`);
|
const onSelect = (val: string) => history.push(`?tab=${val}`);
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export const Settings: React.FC = () => {
|
|||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
>
|
>
|
||||||
<Row>
|
<Row>
|
||||||
<Col sm={2}>
|
<Col sm={3} md={2}>
|
||||||
<Nav variant="pills" className="flex-column">
|
<Nav variant="pills" className="flex-column">
|
||||||
<Nav.Item>
|
<Nav.Item>
|
||||||
<Nav.Link eventKey="configuration">Configuration</Nav.Link>
|
<Nav.Link eventKey="configuration">Configuration</Nav.Link>
|
||||||
@@ -43,7 +43,7 @@ export const Settings: React.FC = () => {
|
|||||||
<hr className="d-sm-none" />
|
<hr className="d-sm-none" />
|
||||||
</Nav>
|
</Nav>
|
||||||
</Col>
|
</Col>
|
||||||
<Col sm={10}>
|
<Col sm={9} md={10}>
|
||||||
<Tab.Content>
|
<Tab.Content>
|
||||||
<Tab.Pane eventKey="configuration">
|
<Tab.Pane eventKey="configuration">
|
||||||
<SettingsConfigurationPanel />
|
<SettingsConfigurationPanel />
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="database-path">
|
<Form.Group id="database-path">
|
||||||
<h6>Database Path</h6>
|
<h6>Database Path</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
defaultValue={databasePath}
|
defaultValue={databasePath}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
setDatabasePath(e.currentTarget.value)
|
setDatabasePath(e.currentTarget.value)
|
||||||
@@ -187,7 +187,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="generated-path">
|
<Form.Group id="generated-path">
|
||||||
<h6>Generated Path</h6>
|
<h6>Generated Path</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
defaultValue={generatedPath}
|
defaultValue={generatedPath}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
setGeneratedPath(e.currentTarget.value)
|
setGeneratedPath(e.currentTarget.value)
|
||||||
@@ -206,7 +206,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
excludes.map((regexp, i) => (
|
excludes.map((regexp, i) => (
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
value={regexp}
|
value={regexp}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
excludeRegexChanged(i, e.currentTarget.value)
|
excludeRegexChanged(i, e.currentTarget.value)
|
||||||
@@ -226,15 +226,13 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Button className="minimal" onClick={() => excludeAddRegex()}>
|
<Button className="minimal" onClick={() => excludeAddRegex()}>
|
||||||
<Icon icon="plus" />
|
<Icon icon="plus" />
|
||||||
</Button>
|
</Button>
|
||||||
<Form.Text>
|
<Form.Text className="text-muted">
|
||||||
|
Regexps of files/paths to exclude from Scan and add to Clean
|
||||||
<a
|
<a
|
||||||
href="https://github.com/stashapp/stash/wiki/Exclude-file-configuration"
|
href="https://github.com/stashapp/stash/wiki/Exclude-file-configuration"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<span>
|
|
||||||
Regexps of files/paths to exclude from Scan and add to Clean
|
|
||||||
</span>
|
|
||||||
<Icon icon="question-circle" />
|
<Icon icon="question-circle" />
|
||||||
</a>
|
</a>
|
||||||
</Form.Text>
|
</Form.Text>
|
||||||
@@ -248,7 +246,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="transcode-size">
|
<Form.Group id="transcode-size">
|
||||||
<h6>Maximum transcode size</h6>
|
<h6>Maximum transcode size</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 input-control"
|
||||||
as="select"
|
as="select"
|
||||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||||
setMaxTranscodeSize(translateQuality(event.currentTarget.value))
|
setMaxTranscodeSize(translateQuality(event.currentTarget.value))
|
||||||
@@ -268,7 +266,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="streaming-transcode-size">
|
<Form.Group id="streaming-transcode-size">
|
||||||
<h6>Maximum streaming transcode size</h6>
|
<h6>Maximum streaming transcode size</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 input-control"
|
||||||
as="select"
|
as="select"
|
||||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||||
setMaxStreamingTranscodeSize(
|
setMaxStreamingTranscodeSize(
|
||||||
@@ -296,7 +294,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="username">
|
<Form.Group id="username">
|
||||||
<h6>Username</h6>
|
<h6>Username</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
defaultValue={username}
|
defaultValue={username}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
setUsername(e.currentTarget.value)
|
setUsername(e.currentTarget.value)
|
||||||
@@ -309,7 +307,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="password">
|
<Form.Group id="password">
|
||||||
<h6>Password</h6>
|
<h6>Password</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
type="password"
|
type="password"
|
||||||
defaultValue={password}
|
defaultValue={password}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
@@ -328,7 +326,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="log-file">
|
<Form.Group id="log-file">
|
||||||
<h6>Log file</h6>
|
<h6>Log file</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input"
|
||||||
defaultValue={logFile}
|
defaultValue={logFile}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
setLogFile(e.currentTarget.value)
|
setLogFile(e.currentTarget.value)
|
||||||
@@ -356,7 +354,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
<Form.Group id="log-level">
|
<Form.Group id="log-level">
|
||||||
<h6>Log Level</h6>
|
<h6>Log Level</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 input-control"
|
||||||
as="select"
|
as="select"
|
||||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||||
setLogLevel(event.currentTarget.value)
|
setLogLevel(event.currentTarget.value)
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4>User Interface</h4>
|
<h4>User Interface</h4>
|
||||||
<Form.Group controlId="language" className="row">
|
<Form.Group controlId="language">
|
||||||
<Form.Label className="col-2">Language</Form.Label>
|
<h6>Language</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
className="col-4"
|
className="col-4 input-control"
|
||||||
value={language}
|
value={language}
|
||||||
onChange={(e: React.FormEvent<HTMLSelectElement>) =>
|
onChange={(e: React.FormEvent<HTMLSelectElement>) =>
|
||||||
setLanguage(e.currentTarget.value)
|
setLanguage(e.currentTarget.value)
|
||||||
@@ -72,7 +72,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||||||
</Form.Control>
|
</Form.Control>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Label>Scene / Marker Wall</Form.Label>
|
<h5>Scene / Marker Wall</h5>
|
||||||
<Form.Check
|
<Form.Check
|
||||||
id="wall-show-title"
|
id="wall-show-title"
|
||||||
checked={wallShowTitle}
|
checked={wallShowTitle}
|
||||||
@@ -104,19 +104,20 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||||||
|
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<h5>Scene Player</h5>
|
<h5>Scene Player</h5>
|
||||||
<Form.Check
|
<Form.Group id="auto-start-video">
|
||||||
id="auto-start-video"
|
<Form.Check
|
||||||
checked={autostartVideo}
|
checked={autostartVideo}
|
||||||
label="Auto-start video"
|
label="Auto-start video"
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setAutostartVideo(!autostartVideo);
|
setAutostartVideo(!autostartVideo);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Group id="max-loop-duration">
|
<Form.Group id="max-loop-duration">
|
||||||
<Form.Label>Maximum loop duration</Form.Label>
|
<h6>Maximum loop duration</h6>
|
||||||
<DurationInput
|
<DurationInput
|
||||||
className="col col-sm-4"
|
className="row col col-4"
|
||||||
numericValue={maximumLoopDuration}
|
numericValue={maximumLoopDuration}
|
||||||
onValueChange={duration => setMaximumLoopDuration(duration)}
|
onValueChange={duration => setMaximumLoopDuration(duration)}
|
||||||
/>
|
/>
|
||||||
@@ -145,7 +146,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||||||
setCSS(e.currentTarget.value)
|
setCSS(e.currentTarget.value)
|
||||||
}
|
}
|
||||||
rows={16}
|
rows={16}
|
||||||
className="col col-sm-6"
|
className="col col-sm-6 text-input code"
|
||||||
></Form.Control>
|
></Form.Control>
|
||||||
<Form.Text className="text-muted">
|
<Form.Text className="text-muted">
|
||||||
Page must be reloaded for changes to take effect.
|
Page must be reloaded for changes to take effect.
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export const SettingsLogsPanel: React.FC = () => {
|
|||||||
<Form.Row id="log-level">
|
<Form.Row id="log-level">
|
||||||
<Form.Label className="col-6 col-sm-2">Log Level</Form.Label>
|
<Form.Label className="col-6 col-sm-2">Log Level</Form.Label>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="col-6 col-sm-2"
|
className="col-6 col-sm-2 input-control"
|
||||||
as="select"
|
as="select"
|
||||||
defaultValue={logLevel}
|
defaultValue={logLevel}
|
||||||
onChange={event => setLogLevel(event.currentTarget.value)}
|
onChange={event => setLogLevel(event.currentTarget.value)}
|
||||||
|
|||||||
@@ -167,8 +167,8 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<h5>Status: {status}</h5>
|
<h5>Status: {status}</h5>
|
||||||
{status !== "Idle" ? (
|
{!!status && status !== "Idle" ? (
|
||||||
<ProgressBar now={progress} label={`${progress}%`} />
|
<ProgressBar animated now={progress} label={`${progress.toFixed(0)}%`} />
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
)}
|
)}
|
||||||
@@ -183,7 +183,7 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
{renderImportAlert()}
|
{renderImportAlert()}
|
||||||
{renderCleanAlert()}
|
{renderCleanAlert()}
|
||||||
|
|
||||||
<h5>Running Jobs</h5>
|
<h4>Running Jobs</h4>
|
||||||
|
|
||||||
{renderJobStatus()}
|
{renderJobStatus()}
|
||||||
|
|
||||||
|
|||||||
@@ -31,3 +31,7 @@
|
|||||||
.log-time {
|
.log-time {
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#configuration-tabs-tabpane-about .table {
|
||||||
|
width: initial;
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
|||||||
<Form.Group className={`duration-input ${props.className}`}>
|
<Form.Group className={`duration-input ${props.className}`}>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
className="duration-control"
|
className="duration-control text-input"
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
|
|||||||
@@ -73,16 +73,20 @@ export const SceneGallerySelect: React.FC<ISceneGallerySelect> = props => {
|
|||||||
|
|
||||||
const onChange = (selectedItems: ValueType<Option>) => {
|
const onChange = (selectedItems: ValueType<Option>) => {
|
||||||
const selectedItem = getSelectedValues(selectedItems)[0];
|
const selectedItem = getSelectedValues(selectedItems)[0];
|
||||||
props.onSelect(galleries.find(g => g.id === selectedItem.value));
|
props.onSelect(selectedItem ? galleries.find(g => g.id === selectedItem) : undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialId = props.initialId ? [props.initialId] : [];
|
const selectedOptions: Option[] = props.initialId
|
||||||
|
? items.filter(item => props.initialId?.indexOf(item.value) !== -1)
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
|
className="input-control"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
items={items}
|
items={items}
|
||||||
initialIds={initialId}
|
selectedOptions={selectedOptions}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -207,10 +211,15 @@ export const StudioSelect: React.FC<IFilterProps> = props => {
|
|||||||
const { data, loading } = StashService.useAllStudiosForFilter();
|
const { data, loading } = StashService.useAllStudiosForFilter();
|
||||||
|
|
||||||
const normalizedData = data?.allStudios ?? [];
|
const normalizedData = data?.allStudios ?? [];
|
||||||
const items: Option[] = normalizedData.map(item => ({
|
|
||||||
|
const items = (normalizedData.length > 0
|
||||||
|
? [{ name: "None", id: "0" }, ...normalizedData]
|
||||||
|
: []
|
||||||
|
).map(item => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name
|
label: item.name
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const placeholder = props.noSelectionString ?? "Select studio...";
|
const placeholder = props.noSelectionString ?? "Select studio...";
|
||||||
const selectedOptions: Option[] = props.ids
|
const selectedOptions: Option[] = props.ids
|
||||||
? items.filter(item => props.ids?.indexOf(item.value) !== -1)
|
? items.filter(item => props.ids?.indexOf(item.value) !== -1)
|
||||||
@@ -358,6 +367,7 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
|||||||
options,
|
options,
|
||||||
value: selectedOptions,
|
value: selectedOptions,
|
||||||
className,
|
className,
|
||||||
|
classNamePrefix: "react-select",
|
||||||
onChange,
|
onChange,
|
||||||
isMulti,
|
isMulti,
|
||||||
isClearable,
|
isClearable,
|
||||||
|
|||||||
@@ -11,23 +11,32 @@ export class StashService {
|
|||||||
public static client: ApolloClient<NormalizedCacheObject>;
|
public static client: ApolloClient<NormalizedCacheObject>;
|
||||||
private static cache: InMemoryCache;
|
private static cache: InMemoryCache;
|
||||||
|
|
||||||
public static initialize() {
|
public static getPlatformURL(ws? : boolean) {
|
||||||
const platformUrl = new URL(window.location.origin);
|
const platformUrl = new URL(window.location.origin);
|
||||||
const wsPlatformUrl = new URL(window.location.origin);
|
|
||||||
wsPlatformUrl.protocol = "ws:";
|
|
||||||
|
|
||||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||||
platformUrl.port = "9999"; // TODO: Hack. Development expects port 9999
|
platformUrl.port = "9999"; // TODO: Hack. Development expects port 9999
|
||||||
wsPlatformUrl.port = "9999";
|
|
||||||
|
|
||||||
if (process.env.REACT_APP_HTTPS === "true") {
|
if (process.env.REACT_APP_HTTPS === "true") {
|
||||||
platformUrl.protocol = "https:";
|
platformUrl.protocol = "https:";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ws) {
|
||||||
|
platformUrl.protocol = "ws:";
|
||||||
|
}
|
||||||
|
|
||||||
|
return platformUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static initialize() {
|
||||||
|
const platformUrl = StashService.getPlatformURL();
|
||||||
|
const wsPlatformUrl = StashService.getPlatformURL(true);
|
||||||
|
|
||||||
if (platformUrl.protocol === "https:") {
|
if (platformUrl.protocol === "https:") {
|
||||||
wsPlatformUrl.protocol = "wss:";
|
wsPlatformUrl.protocol = "wss:";
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${platformUrl.toString().slice(0, -1)}/graphql`;
|
const url = `${platformUrl.toString().slice(0, -1)}/graphql`;
|
||||||
const wsUrl = `${wsPlatformUrl.toString().slice(0, -1)}/graphql`;
|
const wsUrl = `${wsPlatformUrl.toString().slice(0, -1)}/graphql`;
|
||||||
|
|
||||||
|
|||||||
5192
ui/v2.5/src/core/generated-graphql.tsx
Normal file
5192
ui/v2.5/src/core/generated-graphql.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,14 +23,38 @@ body {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 4rem 0 0 0;
|
padding: 4rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) {
|
a {
|
||||||
min-width: 845px;
|
color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, .code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-control, .text-input {
|
||||||
|
color: $text-color;
|
||||||
|
border: 0;
|
||||||
|
box-shadow:0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0), inset 0 0 0 1px rgba(16, 22, 26, 0.3), inset 0 1px 1px rgba(16, 22, 26, 0.4);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
color: $text-color;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: 0 0 0 1px $primary, 0 0 0 1px $primary, 0 0 0 3px rgba(19,124,189,.3), inset 0 0 0 1px rgba(16,22,26,.3), inset 0 1px 1px rgba(16,22,26,.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
.text-input, .text-input:focus {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
background-color: $textfield-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-control, .input-control:focus {
|
||||||
|
background-color: $secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-list a {
|
||||||
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-list table {
|
.table-list table {
|
||||||
@@ -56,40 +80,84 @@ code {
|
|||||||
|
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
.zoom-0 {
|
.zoom-0 {
|
||||||
width: 15rem;
|
width: 240px;
|
||||||
|
|
||||||
|
.scene-card-video {
|
||||||
|
max-height: 180px;
|
||||||
|
}
|
||||||
|
.previewable.portrait {
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-1 {
|
.zoom-1 {
|
||||||
width: 20rem;
|
width: 320px;
|
||||||
|
|
||||||
|
.scene-card-video {
|
||||||
|
max-height: 240px;
|
||||||
|
}
|
||||||
|
.previewable.portrait {
|
||||||
|
height: 240px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-2 {
|
.zoom-2 {
|
||||||
width: 30rem;
|
width: 480px;
|
||||||
|
|
||||||
|
.scene-card-video {
|
||||||
|
max-height: 360px;
|
||||||
|
}
|
||||||
|
.previewable.portrait {
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-3 {
|
.zoom-3 {
|
||||||
width: 40rem;
|
width: 640px;
|
||||||
|
|
||||||
|
.scene-card-video {
|
||||||
|
max-height: 480px;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
height: 480px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-card-video {
|
.scene-card-video {
|
||||||
height: auto;
|
height: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.zoom-0 {
|
/* this is a bit of a hack, because we can't supply direct class names
|
||||||
height: 11.25rem;
|
to the react-select controls */
|
||||||
|
div.react-select__control {
|
||||||
|
background-color: $secondary;
|
||||||
|
border-color: $secondary;
|
||||||
|
color: $text-color;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.react-select__single-value, .react-select__input {
|
||||||
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-1 {
|
.react-select__multi-value {
|
||||||
height: 15rem;
|
background-color: $muted-gray;
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.react-select__menu {
|
||||||
|
background-color: $secondary;
|
||||||
|
color: $text-color;
|
||||||
|
|
||||||
|
.react-select__option {
|
||||||
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-2 {
|
.react-select__option--is-focused {
|
||||||
height: 22.5rem;
|
background-color: #8a9ba826;
|
||||||
}
|
cursor: pointer;
|
||||||
|
|
||||||
.zoom-3 {
|
|
||||||
height: 30rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +223,8 @@ code {
|
|||||||
.operation-container {
|
.operation-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 10px auto;
|
align-items: center;
|
||||||
|
margin: 0 auto 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-item,
|
.filter-item,
|
||||||
@@ -294,6 +363,10 @@ code {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fa-icon {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ import { StashService } from "./core/StashService";
|
|||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import * as serviceWorker from "./serviceWorker";
|
import * as serviceWorker from "./serviceWorker";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render((
|
||||||
|
<>
|
||||||
|
<link rel="stylesheet" type="text/css" href={StashService.getPlatformURL() + "css"}/>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ApolloProvider client={StashService.initialize()!}>
|
<ApolloProvider client={StashService.initialize()!}>
|
||||||
<App />
|
<App />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
</BrowserRouter>,
|
</BrowserRouter>
|
||||||
document.getElementById("root")
|
</>
|
||||||
|
), document.getElementById("root")
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ $pre-color: $text-color;
|
|||||||
$navbar-dark-color: rgb(245, 248, 250);
|
$navbar-dark-color: rgb(245, 248, 250);
|
||||||
$popover-bg: $secondary;
|
$popover-bg: $secondary;
|
||||||
$dark-text: #182026;
|
$dark-text: #182026;
|
||||||
|
$textfield-bg: rgba(16,22,26,.3);
|
||||||
|
|
||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
@import "node_modules/bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
@@ -129,10 +130,6 @@ hr {
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover {
|
.popover {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const renderInputGroup = (options: {
|
|||||||
<td>{options.title}</td>
|
<td>{options.title}</td>
|
||||||
<td>
|
<td>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
|
className="text-input"
|
||||||
readOnly={!options.isEditing}
|
readOnly={!options.isEditing}
|
||||||
plaintext={!options.isEditing}
|
plaintext={!options.isEditing}
|
||||||
defaultValue={options.value}
|
defaultValue={options.value}
|
||||||
@@ -87,6 +88,7 @@ const renderHtmlSelect = (options: {
|
|||||||
<td>
|
<td>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
|
className="input-control"
|
||||||
disabled={!options.isEditing}
|
disabled={!options.isEditing}
|
||||||
plaintext={!options.isEditing}
|
plaintext={!options.isEditing}
|
||||||
value={options.value?.toString()}
|
value={options.value?.toString()}
|
||||||
|
|||||||
Reference in New Issue
Block a user