mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Responsive styles for portrait orientation phones
This commit is contained in:
@@ -49,7 +49,7 @@
|
||||
"function-whitespace-after": "always",
|
||||
"length-zero-no-unit": true,
|
||||
"max-empty-lines": 1,
|
||||
"max-nesting-depth": 3,
|
||||
"max-nesting-depth": 4,
|
||||
"max-line-length": 100,
|
||||
"media-feature-colon-space-after": "always",
|
||||
"media-feature-colon-space-before": "never",
|
||||
|
||||
@@ -22,7 +22,7 @@ export const App: React.FC = () => (
|
||||
<ErrorBoundary>
|
||||
<ToastProvider>
|
||||
<MainNavbar />
|
||||
<div className="main">
|
||||
<div className="main container-fluid">
|
||||
<Switch>
|
||||
<Route exact path="/" component={Stats} />
|
||||
<Route path="/scenes" component={Scenes} />
|
||||
|
||||
@@ -64,13 +64,18 @@ export const MainNavbar: React.FC = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Navbar fixed="top" variant="dark" bg="dark">
|
||||
<Navbar fixed="top" variant="dark" bg="dark" className="top-nav">
|
||||
<Navbar.Brand as="div">
|
||||
<Link to="/">
|
||||
<Button className="minimal">Stash</Button>
|
||||
<Button className="minimal brand-link d-none d-sm-inline-block">
|
||||
Stash
|
||||
</Button>
|
||||
<Button className="minimal brand-icon d-inline d-sm-none">
|
||||
<img src="http://192.168.1.65:9999/favicon.ico" alt="" />
|
||||
</Button>
|
||||
</Link>
|
||||
</Navbar.Brand>
|
||||
<Nav className="mr-auto">
|
||||
<Nav className="mr-md-auto">
|
||||
{menuItems.map(i => (
|
||||
<LinkContainer
|
||||
activeClassName="active"
|
||||
@@ -80,13 +85,15 @@ export const MainNavbar: React.FC = () => {
|
||||
>
|
||||
<Button className="minimal">
|
||||
<Icon icon={i.icon} />
|
||||
<span>{i.text}</span>
|
||||
<span className="d-none d-sm-inline">{i.text}</span>
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
))}
|
||||
</Nav>
|
||||
<Nav>
|
||||
<div className="d-none d-sm-block">
|
||||
{newButton}
|
||||
</div>
|
||||
<LinkContainer exact to="/settings">
|
||||
<Button className="minimal">
|
||||
<Icon icon="cog" />
|
||||
|
||||
@@ -134,7 +134,7 @@ export const ParserInput: React.FC<IParserInputProps> = (props: IParserInputProp
|
||||
<InputGroup.Append>
|
||||
<DropdownButton id="parser-field-select" title="Add Field">
|
||||
{validFields.map(item => (
|
||||
<Dropdown.Item onSelect={() => addParserField(item)}>
|
||||
<Dropdown.Item key={item.field} onSelect={() => addParserField(item)}>
|
||||
<span>{item.field}</span>
|
||||
<span className="ml-auto">{item.helperText}</span>
|
||||
</Dropdown.Item>
|
||||
@@ -184,7 +184,7 @@ export const ParserInput: React.FC<IParserInputProps> = (props: IParserInputProp
|
||||
<Form.Group>
|
||||
<DropdownButton variant="secondary" id="recipe-select" title="Select Parser Recipe">
|
||||
{builtInRecipes.map(item => (
|
||||
<Dropdown.Item onSelect={() => setParserRecipe(item)}>
|
||||
<Dropdown.Item key={item.pattern} onSelect={() => setParserRecipe(item)}>
|
||||
<span>{item.pattern}</span>
|
||||
<span className="mr-auto">{item.description}</span>
|
||||
</Dropdown.Item>
|
||||
|
||||
@@ -20,7 +20,8 @@ export const ShowFields = (props: IShowFieldsProps) => {
|
||||
}
|
||||
|
||||
const fieldRows = [...props.fields.entries()].map(([label, enabled]) => (
|
||||
<div
|
||||
<Button
|
||||
className="minimal d-block"
|
||||
key={label}
|
||||
onClick={() => {
|
||||
handleClick(label);
|
||||
@@ -28,7 +29,7 @@ export const ShowFields = (props: IShowFieldsProps) => {
|
||||
>
|
||||
<Icon icon={enabled ? "check" : "times"} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
</Button>
|
||||
));
|
||||
|
||||
return (
|
||||
|
||||
@@ -174,7 +174,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="database-path">
|
||||
<h6>Database Path</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
defaultValue={databasePath}
|
||||
onChange={(e: any) => setDatabasePath(e.target.value)}
|
||||
/>
|
||||
@@ -186,7 +186,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="generated-path">
|
||||
<h6>Generated Path</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
defaultValue={generatedPath}
|
||||
onChange={(e: any) => setGeneratedPath(e.target.value)}
|
||||
/>
|
||||
@@ -203,7 +203,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
&& excludes.map((regexp, i) => (
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
value={regexp}
|
||||
onChange={(e: any) =>
|
||||
excludeRegexChanged(i, e.target.value)
|
||||
@@ -246,7 +246,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="transcode-size">
|
||||
<h6>Maximum transcode size</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
as="select"
|
||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||
setMaxTranscodeSize(translateQuality(event.currentTarget.value))
|
||||
@@ -266,7 +266,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="streaming-transcode-size">
|
||||
<h6>Maximum streaming transcode size</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
as="select"
|
||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||
setMaxStreamingTranscodeSize(
|
||||
@@ -294,7 +294,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="username">
|
||||
<h6>Username</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
defaultValue={username}
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
setUsername(e.currentTarget.value)
|
||||
@@ -307,7 +307,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="password">
|
||||
<h6>Password</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
type="password"
|
||||
defaultValue={password}
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
@@ -326,7 +326,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="log-file">
|
||||
<h6>Log file</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
defaultValue={logFile}
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
setLogFile(e.currentTarget.value)
|
||||
@@ -354,7 +354,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
<Form.Group id="log-level">
|
||||
<h6>Log Level</h6>
|
||||
<Form.Control
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
as="select"
|
||||
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
|
||||
setLogLevel(event.currentTarget.value)
|
||||
|
||||
@@ -103,7 +103,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||
<Form.Group id="max-loop-duration">
|
||||
<Form.Label>Maximum loop duration</Form.Label>
|
||||
<DurationInput
|
||||
className="col-4"
|
||||
className="col col-sm-4"
|
||||
numericValue={maximumLoopDuration}
|
||||
onValueChange={duration => setMaximumLoopDuration(duration)}
|
||||
/>
|
||||
@@ -129,7 +129,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||
value={css}
|
||||
onChange={(e: any) => setCSS(e.target.value)}
|
||||
rows={16}
|
||||
className="col-6"
|
||||
className="col col-sm-6"
|
||||
></Form.Control>
|
||||
<Form.Text className="text-muted">
|
||||
Page must be reloaded for changes to take effect.
|
||||
|
||||
@@ -40,7 +40,7 @@ const LogElement: React.FC<ILogElementProps> = ({ logEntry }) => {
|
||||
<div className="row">
|
||||
<span className="log-time">{logEntry.time}</span>
|
||||
<span className={`${levelClass(logEntry.level)}`}>{level}</span>
|
||||
<span className="col-9">{logEntry.message}</span>
|
||||
<span className="col col-sm-9">{logEntry.message}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -99,9 +99,9 @@ export const SettingsLogsPanel: React.FC = () => {
|
||||
<>
|
||||
<h4>Logs</h4>
|
||||
<Form.Row id="log-level">
|
||||
<Form.Label className="col-2">Log Level</Form.Label>
|
||||
<Form.Label className="col-6 col-sm-2">Log Level</Form.Label>
|
||||
<Form.Control
|
||||
className="col-2"
|
||||
className="col-6 col-sm-2"
|
||||
as="select"
|
||||
defaultValue={logLevel}
|
||||
onChange={event => setLogLevel(event.currentTarget.value)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const DetailsEditNavbar: React.FC<IProps> = (props: IProps) => {
|
||||
function renderDeleteButton() {
|
||||
if (props.isNew || props.isEditing) return;
|
||||
return (
|
||||
<Button variant="danger" className="delete" onClick={() => setIsDeleteAlertOpen(true)}>
|
||||
<Button variant="danger" className="delete d-none d-sm-block" onClick={() => setIsDeleteAlertOpen(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -65,7 +65,7 @@ export const FolderSelect: React.FC<IProps> = (props: IProps) => {
|
||||
<ul className="folder-list">
|
||||
{selectableDirectories.map(path => {
|
||||
return (
|
||||
<li className="folder-item">
|
||||
<li key={path} className="folder-item">
|
||||
<Button
|
||||
variant="link"
|
||||
key={path}
|
||||
|
||||
@@ -10,8 +10,9 @@ export const Stats: React.FC = () => {
|
||||
if (error) return <span>error.message</span>;
|
||||
|
||||
return (
|
||||
<div className="w-75 m-auto">
|
||||
<nav className="w-75 m-auto d-flex flex-row">
|
||||
<div className="mt-5">
|
||||
<div className="col col-sm-8 m-sm-auto">
|
||||
<nav className="col col-sm-8 m-sm-auto row">
|
||||
<div className="flex-grow-1">
|
||||
<div>
|
||||
<p className="heading">Scenes</p>
|
||||
@@ -45,11 +46,8 @@ export const Stats: React.FC = () => {
|
||||
</nav>
|
||||
|
||||
<h5>Notes</h5>
|
||||
<pre>
|
||||
{`
|
||||
This is still an early version, some things are still a work in progress.
|
||||
`}
|
||||
</pre>
|
||||
<em>This is still an early version, some things are still a work in progress.</em>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ export const Studio: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className={cx('studio-details', { 'col-4': !isNew, 'col-8': isNew})}>
|
||||
<div className={cx('studio-details', { 'col ml-sm-5': !isNew, 'col-8': isNew})}>
|
||||
{ isNew && <h2>Add Studio</h2> }
|
||||
<img className="logo" alt={name} src={imagePreview} />
|
||||
<Table id="performer-details" style={{ width: "100%" }}>
|
||||
@@ -180,7 +180,7 @@ export const Studio: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
{ !isNew && (
|
||||
<div className="col-8">
|
||||
<div className="col-12 col-sm-8">
|
||||
<StudioScenesPanel studio={studio} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
.studio-details {
|
||||
padding-left: 4rem;
|
||||
|
||||
.logo {
|
||||
margin: 4rem 0;
|
||||
width: 100%;
|
||||
|
||||
@@ -93,6 +93,10 @@
|
||||
position: relative;
|
||||
width: 20%;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
video,
|
||||
img {
|
||||
height: 100%;
|
||||
|
||||
@@ -104,7 +104,7 @@ export const WallPanel: React.FC<IWallPanelProps> = (
|
||||
return (
|
||||
<>
|
||||
<div className={`wall-overlay ${overlayClassName}`} />
|
||||
<div className="wall grid">
|
||||
<div className="wall grid row">
|
||||
{maybeRenderScenes()}
|
||||
{maybeRenderSceneMarkers()}
|
||||
</div>
|
||||
|
||||
@@ -228,7 +228,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
if (props.onChangeZoom) {
|
||||
return (
|
||||
<Form.Control
|
||||
className="zoom-slider col-1"
|
||||
className="zoom-slider col-1 d-none d-sm-block"
|
||||
type="range"
|
||||
min={0}
|
||||
max={3}
|
||||
@@ -249,14 +249,14 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
placeholder="Search..."
|
||||
defaultValue={props.filter.searchTerm}
|
||||
onChange={onChangeQuery}
|
||||
className="filter-item"
|
||||
className="filter-item col-5 col-sm-2"
|
||||
style={{ width: "inherit" }}
|
||||
/>
|
||||
<Form.Control
|
||||
as="select"
|
||||
onChange={onChangePageSize}
|
||||
value={props.filter.itemsPerPage.toString()}
|
||||
className="filter-item col-1"
|
||||
className="filter-item col-1 d-none d-sm-inline"
|
||||
>
|
||||
{PAGE_SIZE_OPTIONS.map(s => (
|
||||
<option value={s} key={s}>{s}</option>
|
||||
@@ -298,13 +298,13 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
editingCriterion={editingCriterion}
|
||||
/>
|
||||
|
||||
<ButtonGroup className="filter-item">
|
||||
<ButtonGroup className="filter-item d-none d-sm-inline-flex">
|
||||
{renderDisplayModeOptions()}
|
||||
</ButtonGroup>
|
||||
|
||||
{maybeRenderZoom()}
|
||||
|
||||
<ButtonGroup className="filter-item">{renderMore()}</ButtonGroup>
|
||||
<ButtonGroup className="filter-item d-none d-sm-inline-flex">{renderMore()}</ButtonGroup>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
|
||||
@@ -37,9 +37,20 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
i => startPage + i
|
||||
);
|
||||
|
||||
const calculatePageClass = (buttonPage:number) => {
|
||||
if(pages.length <= 4) return '';
|
||||
|
||||
if(currentPage === 1 && buttonPage <= 4) return '';
|
||||
const maxPage = pages[pages.length - 1];
|
||||
if(currentPage === maxPage && buttonPage > (maxPage - 3)) return '';
|
||||
if(Math.abs(buttonPage - currentPage) <= 1) return '';
|
||||
return 'd-none d-sm-block'
|
||||
}
|
||||
|
||||
const pageButtons = pages.map((page: number) => (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={calculatePageClass(page)}
|
||||
key={page}
|
||||
active={currentPage === page}
|
||||
onClick={() => onChangePage(page)}
|
||||
@@ -54,9 +65,11 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
return (
|
||||
<ButtonGroup className="filter-container pagination">
|
||||
<Button variant="secondary" disabled={currentPage === 1} onClick={() => onChangePage(1)}>
|
||||
First
|
||||
<span className="d-none d-sm-inline">First</span>
|
||||
<span className="d-inline d-sm-none">《</span>
|
||||
</Button>
|
||||
<Button
|
||||
className="d-none d-sm-block"
|
||||
variant="secondary"
|
||||
disabled={currentPage === 1}
|
||||
onClick={() => onChangePage(currentPage - 1)}
|
||||
@@ -65,6 +78,7 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
</Button>
|
||||
{pageButtons}
|
||||
<Button
|
||||
className="d-none d-sm-block"
|
||||
variant="secondary"
|
||||
disabled={currentPage === totalPages}
|
||||
onClick={() => onChangePage(currentPage + 1)}
|
||||
@@ -76,7 +90,8 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
disabled={currentPage === totalPages}
|
||||
onClick={() => onChangePage(totalPages)}
|
||||
>
|
||||
Last
|
||||
<span className="d-none d-sm-inline">Last</span>
|
||||
<span className="d-inline d-sm-none">》</span>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
||||
@@ -164,7 +164,7 @@ export const Performer: React.FC = () => {
|
||||
}
|
||||
|
||||
const renderIcons = () => (
|
||||
<span className="name-icons">
|
||||
<span className="name-icons d-block d-sm-inline">
|
||||
<Button
|
||||
className={cx('minimal', performer.favorite ? "favorite" : "not-favorite")}
|
||||
onClick={() => setFavorite(!performer.favorite)}
|
||||
@@ -217,13 +217,19 @@ export const Performer: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div id="performer-page" className="row">
|
||||
<div className="image-container col-4 offset-1">
|
||||
<div className="image-container col-sm-4 offset-sm-1 d-none d-sm-block">
|
||||
<Button variant="link" onClick={() => setLightboxIsOpen(true)}>
|
||||
<img className="performer" src={imagePreview} alt="Performer" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-6">
|
||||
<div className="performer-head">
|
||||
<div className="col col-sm-6">
|
||||
<div className="row">
|
||||
<div className="image-container col-6 d-block d-sm-none">
|
||||
<Button variant="link" onClick={() => setLightboxIsOpen(true)}>
|
||||
<img className="performer" src={imagePreview} alt="Performer" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="performer-head col-6 col-sm-12">
|
||||
<h2>
|
||||
{performer.name}
|
||||
{renderIcons()}
|
||||
@@ -231,6 +237,7 @@ export const Performer: React.FC = () => {
|
||||
{maybeRenderAliases()}
|
||||
{maybeRenderAge()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="performer-body">
|
||||
<div className="performer-tabs">{renderTabs()}</div>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ export const PerformerList: React.FC = () => {
|
||||
}
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="grid row">
|
||||
{result.data.findPerformers.performers.map(p => (
|
||||
<PerformerCard key={p.id} performer={p} />
|
||||
))}
|
||||
|
||||
@@ -188,7 +188,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
>
|
||||
<Form.Control
|
||||
type="checkbox"
|
||||
className="card-select"
|
||||
className="card-select d-none d-sm-block"
|
||||
checked={props.selected}
|
||||
onChange={() => props.onSelectedChanged(!props.selected, shiftKey)}
|
||||
onClick={(event: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
|
||||
|
||||
@@ -53,7 +53,7 @@ export const PrimaryTags: React.FC<IPrimaryTags> = ({ sceneMarkers, onClickMarke
|
||||
});
|
||||
|
||||
return (
|
||||
<Card className="primary-card col-3" key={id}>
|
||||
<Card className="primary-card col-12 col-sm-3" key={id}>
|
||||
<h3>{primaries[id].name}</h3>
|
||||
<Card.Body className="primary-card-body">
|
||||
{ markers }
|
||||
|
||||
@@ -55,7 +55,7 @@ export const Scene: React.FC = () => {
|
||||
timestamp={timestamp}
|
||||
autoplay={autoplay}
|
||||
/>
|
||||
<div id="details-container">
|
||||
<div id="details-container" className="col col-sm-9 m-sm-auto">
|
||||
<Tabs id="scene-tabs" mountOnEnter>
|
||||
<Tab eventKey="scene-details-panel" title="Details">
|
||||
<SceneDetailPanel scene={scene} />
|
||||
@@ -83,7 +83,7 @@ export const Scene: React.FC = () => {
|
||||
<Tab className="file-info-panel" eventKey="scene-file-info-panel" title="File Info">
|
||||
<SceneFileInfoPanel scene={scene} />
|
||||
</Tab>
|
||||
<Tab eventKey="scene-edit-panel" title="Edit">
|
||||
<Tab eventKey="scene-edit-panel" title="Edit" tabClassName="d-none d-sm-block">
|
||||
<SceneEditPanel
|
||||
scene={scene}
|
||||
onUpdate={newScene => setScene(newScene)}
|
||||
|
||||
@@ -34,9 +34,9 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = props => {
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<h1 className="col scene-header">
|
||||
<h3 className="col scene-header text-truncate">
|
||||
{props.scene.title ?? TextUtils.fileNameFromPath(props.scene.path)}
|
||||
</h1>
|
||||
</h3>
|
||||
<div className="col-6 scene-details">
|
||||
<h4>{props.scene.date ?? ''}</h4>
|
||||
{props.scene.rating ? <h6>Rating: {props.scene.rating}</h6> : ""}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import { Table } from "react-bootstrap";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { TextUtils } from "src/utils";
|
||||
|
||||
@@ -12,10 +11,10 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
) => {
|
||||
function renderChecksum() {
|
||||
return (
|
||||
<tr>
|
||||
<td>Checksum</td>
|
||||
<td>{props.scene.checksum}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Checksum</span>
|
||||
<span className="col-8 text-truncate">{props.scene.checksum}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,25 +23,25 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
scene: { path }
|
||||
} = props;
|
||||
return (
|
||||
<tr>
|
||||
<td>Path</td>
|
||||
<td>
|
||||
<div className="row">
|
||||
<span className="col-4">Path</span>
|
||||
<span className="col-8 text-truncate">
|
||||
<a href={`file://${path}`}>{`file://${props.scene.path}`}</a>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderStream() {
|
||||
return (
|
||||
<tr>
|
||||
<td>Stream</td>
|
||||
<td>
|
||||
<div className="row">
|
||||
<span className="col-4">Stream</span>
|
||||
<span className="col-8 text-truncate">
|
||||
<a href={props.scene.paths.stream ?? ""}>
|
||||
{props.scene.paths.stream}
|
||||
</a>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,12 +50,12 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>File Size</td>
|
||||
<td>
|
||||
<div className="row">
|
||||
<span className="col-4">File Size</span>
|
||||
<span className="col-8 text-truncate">
|
||||
{TextUtils.fileSize(parseInt(props.scene.file.size ?? "0", 10))}
|
||||
</td>
|
||||
</tr>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,10 +64,12 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Duration</td>
|
||||
<td>{TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0)}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Duration</span>
|
||||
<span className="col-8 text-truncate">
|
||||
{TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,12 +78,12 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Dimensions</td>
|
||||
<td>
|
||||
<div className="row">
|
||||
<span className="col-4">Dimensions</span>
|
||||
<span className="col-8 text-truncate">
|
||||
{props.scene.file.width} x {props.scene.file.height}
|
||||
</td>
|
||||
</tr>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,22 +92,22 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Frame Rate</td>
|
||||
<td>{props.scene.file.framerate} frames per second</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Frame Rate</span>
|
||||
<span className="col-8 text-truncate">{props.scene.file.framerate} frames per second</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderBitRate() {
|
||||
function renderbitrate() {
|
||||
if (props.scene.file.bitrate === undefined) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Bit Rate</td>
|
||||
<td>{TextUtils.bitRate(props.scene.file.bitrate ?? 0)}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Bit Rate</span>
|
||||
<span className="col-8 text-truncate">{TextUtils.bitRate(props.scene.file.bitrate ?? 0)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,10 +116,10 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Video Codec</td>
|
||||
<td>{props.scene.file.video_codec}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Video Codec</span>
|
||||
<span className="col-8 text-truncate">{props.scene.file.video_codec}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -127,10 +128,10 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Audio Codec</td>
|
||||
<td>{props.scene.file.audio_codec}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Audio Codec</span>
|
||||
<span className="col-8 text-truncate">{props.scene.file.audio_codec}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -139,17 +140,15 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>Downloaded From</td>
|
||||
<td>{props.scene.url}</td>
|
||||
</tr>
|
||||
<div className="row">
|
||||
<span className="col-4">Downloaded From</span>
|
||||
<span className="col-8 text-truncate">{props.scene.url}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table>
|
||||
<tbody>
|
||||
<div className="container scene-file-info">
|
||||
{renderChecksum()}
|
||||
{renderPath()}
|
||||
{renderStream()}
|
||||
@@ -157,12 +156,10 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
{renderDuration()}
|
||||
{renderDimensions()}
|
||||
{renderFrameRate()}
|
||||
{renderBitRate()}
|
||||
{renderbitrate()}
|
||||
{renderVideoCodec()}
|
||||
{renderAudioCodec()}
|
||||
{renderUrl()}
|
||||
</tbody>
|
||||
</Table>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -51,11 +51,13 @@ export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => onOpenEditor()}>Create Marker</Button>
|
||||
<div className="container">
|
||||
<PrimaryTags
|
||||
sceneMarkers={props.scene.scene_markers ?? []}
|
||||
onClickMarker={onClickMarker}
|
||||
onEdit={onOpenEditor}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<WallPanel
|
||||
sceneMarkers={props.scene.scene_markers}
|
||||
|
||||
@@ -194,7 +194,7 @@ export class ScenePlayerImpl extends React.Component<
|
||||
return (
|
||||
<ReactJWPlayer
|
||||
playerId={JWUtils.playerID}
|
||||
playerScript="http://localhost:9999/jwplayer/jwplayer.js"
|
||||
playerScript="http://192.168.1.65:9999/jwplayer/jwplayer.js"
|
||||
customProps={config}
|
||||
onReady={this.onReady}
|
||||
onSeeked={this.onSeeked}
|
||||
@@ -205,8 +205,8 @@ export class ScenePlayerImpl extends React.Component<
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<HotKeys keyMap={KeyMap} handlers={this.KeyHandlers}>
|
||||
<div id="jwplayer-container">
|
||||
<HotKeys keyMap={KeyMap} handlers={this.KeyHandlers} className="row">
|
||||
<div id="jwplayer-container" className="w-100 col-sm-9 m-sm-auto no-gutter" >
|
||||
{this.renderPlayer()}
|
||||
<ScenePlayerScrubber
|
||||
scene={this.props.scene}
|
||||
|
||||
@@ -357,7 +357,7 @@ export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="scrubber-wrapper">
|
||||
<div className="scrubber-wrapper d-none d-sm-block">
|
||||
<Button
|
||||
variant="link"
|
||||
className="scrubber-button"
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
}
|
||||
|
||||
.file-info-panel {
|
||||
td {
|
||||
padding: .4rem;
|
||||
div {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
margin: 0;
|
||||
padding: $pt-navbar-height 0 0 0;
|
||||
|
||||
@media (min-width: 600px) {
|
||||
min-width: 845px;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
@@ -21,6 +25,12 @@ code {
|
||||
margin: $pt-grid-size $pt-grid-size 0 0;
|
||||
padding: 0;
|
||||
|
||||
.performer-card,
|
||||
.studio-card {
|
||||
min-width: 185px;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
&.wall {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -44,8 +54,8 @@ code {
|
||||
.card {
|
||||
margin: 0 0 10px 10px;
|
||||
overflow: hidden;
|
||||
width: 20rem;
|
||||
|
||||
@media (min-width: 576px) {
|
||||
&.zoom-0 {
|
||||
width: 15rem;
|
||||
|
||||
@@ -93,6 +103,7 @@ code {
|
||||
height: 30rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-select {
|
||||
margin-top: -12px;
|
||||
@@ -314,11 +325,6 @@ video.preview.portrait {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#details-container {
|
||||
margin: 10px auto;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.pre {
|
||||
white-space: pre-line;
|
||||
}
|
||||
@@ -527,3 +533,35 @@ video.preview.portrait {
|
||||
}
|
||||
}
|
||||
/* stylelint-enable */
|
||||
|
||||
.brand-icon {
|
||||
padding: 3px 6px;
|
||||
|
||||
img {
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
@media (max-width: 576px) {
|
||||
a {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.nav-tabs {
|
||||
border: none;
|
||||
margin: auto;
|
||||
|
||||
.nav-link {
|
||||
border: none;
|
||||
padding: 8px;
|
||||
|
||||
&.active {
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user