mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Marker time input (#242)
* Use duration input for marker time * Allow reset to current time * Validate input
This commit is contained in:
127
ui/v2/src/components/Shared/DurationInput.tsx
Normal file
127
ui/v2/src/components/Shared/DurationInput.tsx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import React, { FunctionComponent, useState, useEffect } from "react";
|
||||||
|
import { InputGroup, ButtonGroup, Button, IInputGroupProps, HTMLInputProps, ControlGroup } from "@blueprintjs/core";
|
||||||
|
import { TextUtils } from "../../utils/text";
|
||||||
|
import { FIXED, NUMERIC_INPUT } from "@blueprintjs/core/lib/esm/common/classes";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
disabled?: boolean
|
||||||
|
numericValue: number
|
||||||
|
onValueChange(valueAsNumber: number): void
|
||||||
|
onReset?(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DurationInput: FunctionComponent<HTMLInputProps & IProps> = (props: IProps) => {
|
||||||
|
const [value, setValue] = useState<string>(secondsToString(props.numericValue));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(secondsToString(props.numericValue));
|
||||||
|
}, [props.numericValue]);
|
||||||
|
|
||||||
|
function secondsToString(seconds : number) {
|
||||||
|
let ret = TextUtils.secondsToTimestamp(seconds);
|
||||||
|
|
||||||
|
if (ret.startsWith("00:")) {
|
||||||
|
ret = ret.substr(3);
|
||||||
|
|
||||||
|
if (ret.startsWith("0")) {
|
||||||
|
ret = ret.substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToSeconds(v : string) {
|
||||||
|
if (!v) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let splits = v.split(":");
|
||||||
|
|
||||||
|
if (splits.length > 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let seconds = 0;
|
||||||
|
let factor = 1;
|
||||||
|
while(splits.length > 0) {
|
||||||
|
let thisSplit = splits.pop();
|
||||||
|
if (thisSplit == undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let thisInt = parseInt(thisSplit, 10);
|
||||||
|
if (isNaN(thisInt)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds += factor * thisInt;
|
||||||
|
factor *= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function increment() {
|
||||||
|
let seconds = stringToSeconds(value);
|
||||||
|
seconds += 1;
|
||||||
|
props.onValueChange(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrement() {
|
||||||
|
let seconds = stringToSeconds(value);
|
||||||
|
seconds -= 1;
|
||||||
|
props.onValueChange(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderButtons() {
|
||||||
|
return (
|
||||||
|
<ButtonGroup
|
||||||
|
vertical={true}
|
||||||
|
className={FIXED}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon="chevron-up"
|
||||||
|
disabled={props.disabled}
|
||||||
|
onClick={() => increment()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon="chevron-down"
|
||||||
|
disabled={props.disabled}
|
||||||
|
onClick={() => decrement()}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReset() {
|
||||||
|
if (props.onReset) {
|
||||||
|
props.onReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderReset() {
|
||||||
|
if (props.onReset) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
icon="time"
|
||||||
|
onClick={() => onReset()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ControlGroup className={NUMERIC_INPUT}>
|
||||||
|
<InputGroup
|
||||||
|
disabled={props.disabled}
|
||||||
|
value={value}
|
||||||
|
onChange={(e : any) => setValue(e.target.value)}
|
||||||
|
onBlur={() => props.onValueChange(stringToSeconds(value))}
|
||||||
|
placeholder="hh:mm:ss"
|
||||||
|
rightElement={maybeRenderReset()}
|
||||||
|
/>
|
||||||
|
{renderButtons()}
|
||||||
|
</ControlGroup>
|
||||||
|
)
|
||||||
|
};
|
||||||
@@ -19,6 +19,7 @@ import { MarkerTitleSuggest } from "../../select/MarkerTitleSuggest";
|
|||||||
import { WallPanel } from "../../Wall/WallPanel";
|
import { WallPanel } from "../../Wall/WallPanel";
|
||||||
import { SceneHelpers } from "../helpers";
|
import { SceneHelpers } from "../helpers";
|
||||||
import { ErrorUtils } from "../../../utils/errors";
|
import { ErrorUtils } from "../../../utils/errors";
|
||||||
|
import { DurationInput } from "../../Shared/DurationInput";
|
||||||
|
|
||||||
interface ISceneMarkersPanelProps {
|
interface ISceneMarkersPanelProps {
|
||||||
scene: GQL.SceneDataFragment;
|
scene: GQL.SceneDataFragment;
|
||||||
@@ -148,14 +149,10 @@ export const SceneMarkersPanel: FunctionComponent<ISceneMarkersPanelProps> = (pr
|
|||||||
}
|
}
|
||||||
function renderSecondsField(fieldProps: FieldProps<IFormFields>) {
|
function renderSecondsField(fieldProps: FieldProps<IFormFields>) {
|
||||||
return (
|
return (
|
||||||
<NumericInput
|
<DurationInput
|
||||||
placeholder="Seconds"
|
onValueChange={(s) => fieldProps.form.setFieldValue("seconds", s)}
|
||||||
fill={true}
|
onReset={() => fieldProps.form.setFieldValue("seconds", Math.round(jwplayer.getPosition()))}
|
||||||
allowNumericCharactersOnly={true}
|
numericValue={fieldProps.field.value}
|
||||||
name={fieldProps.field.name}
|
|
||||||
onValueChange={(_, s) => fieldProps.form.setFieldValue("seconds", s)}
|
|
||||||
onBlur={fieldProps.field.onBlur}
|
|
||||||
value={fieldProps.field.value}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -197,7 +194,7 @@ export const SceneMarkersPanel: FunctionComponent<ISceneMarkersPanelProps> = (pr
|
|||||||
<FormGroup label="Scene Marker Title" labelFor="title" className="column is-full">
|
<FormGroup label="Scene Marker Title" labelFor="title" className="column is-full">
|
||||||
<Field name="title" render={renderTitleField} />
|
<Field name="title" render={renderTitleField} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup label="Seconds" labelFor="seconds" className="column is-half">
|
<FormGroup label="Time" labelFor="seconds" className="column is-half">
|
||||||
<Field name="seconds" render={renderSecondsField} />
|
<Field name="seconds" render={renderSecondsField} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup label="Primary Tag" labelFor="primaryTagId" className="column is-half">
|
<FormGroup label="Primary Tag" labelFor="primaryTagId" className="column is-half">
|
||||||
@@ -228,11 +225,11 @@ export const SceneMarkersPanel: FunctionComponent<ISceneMarkersPanelProps> = (pr
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Collapse isOpen={isEditorOpen}>
|
<Collapse isOpen={isEditorOpen}>
|
||||||
<Formik
|
{isEditorOpen ? <Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
render={renderFormFields}
|
render={renderFormFields}
|
||||||
/>
|
/> : undefined}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user