mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
New patchable performer page components (#5897)
This commit is contained in:
parent
d9a316d083
commit
c66ef42480
8 changed files with 263 additions and 213 deletions
|
|
@ -46,6 +46,7 @@ import { AliasList } from "src/components/Shared/DetailsPage/AliasList";
|
|||
import { HeaderImage } from "src/components/Shared/DetailsPage/HeaderImage";
|
||||
import { LightboxLink } from "src/hooks/Lightbox/LightboxLink";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { ILightboxImage } from "src/hooks/Lightbox/types";
|
||||
|
||||
interface IProps {
|
||||
performer: GQL.PerformerDataFragment;
|
||||
|
|
@ -201,6 +202,34 @@ const PerformerTabs: React.FC<{
|
|||
);
|
||||
};
|
||||
|
||||
interface IPerformerHeaderImageProps {
|
||||
activeImage: string | null | undefined;
|
||||
collapsed: boolean;
|
||||
encodingImage: boolean;
|
||||
lightboxImages: ILightboxImage[];
|
||||
performer: GQL.PerformerDataFragment;
|
||||
}
|
||||
|
||||
const PerformerHeaderImage: React.FC<IPerformerHeaderImageProps> =
|
||||
PatchComponent(
|
||||
"PerformerHeaderImage",
|
||||
({ encodingImage, activeImage, lightboxImages, performer }) => {
|
||||
return (
|
||||
<HeaderImage encodingImage={encodingImage}>
|
||||
{!!activeImage && (
|
||||
<LightboxLink images={lightboxImages}>
|
||||
<DetailImage
|
||||
className="performer"
|
||||
src={activeImage}
|
||||
alt={performer.name}
|
||||
/>
|
||||
</LightboxLink>
|
||||
)}
|
||||
</HeaderImage>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const PerformerPage: React.FC<IProps> = PatchComponent(
|
||||
"PerformerPage",
|
||||
({ performer, tabKey }) => {
|
||||
|
|
@ -364,18 +393,13 @@ const PerformerPage: React.FC<IProps> = PatchComponent(
|
|||
show={enableBackgroundImage && !isEditing}
|
||||
/>
|
||||
<div className="detail-container">
|
||||
<HeaderImage encodingImage={encodingImage}>
|
||||
{!!activeImage && (
|
||||
<LightboxLink images={lightboxImages}>
|
||||
<DetailImage
|
||||
className="performer"
|
||||
src={activeImage}
|
||||
alt={performer.name}
|
||||
/>
|
||||
</LightboxLink>
|
||||
)}
|
||||
</HeaderImage>
|
||||
|
||||
<PerformerHeaderImage
|
||||
activeImage={activeImage}
|
||||
collapsed={collapsed}
|
||||
encodingImage={encodingImage}
|
||||
lightboxImages={lightboxImages}
|
||||
performer={performer}
|
||||
/>
|
||||
<div className="row">
|
||||
<div className="performer-head col">
|
||||
<DetailTitle
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { cloneDeep } from "@apollo/client/utilities";
|
|||
import { Icon } from "./Icon";
|
||||
import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import cx from "classnames";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
const maxFieldNameLength = 64;
|
||||
|
||||
|
|
@ -90,76 +91,85 @@ const CustomFieldInput: React.FC<{
|
|||
onChange: (field: string, value: unknown) => void;
|
||||
isNew?: boolean;
|
||||
error?: string;
|
||||
}> = ({ field, value, onChange, isNew = false, error }) => {
|
||||
const intl = useIntl();
|
||||
const [currentField, setCurrentField] = useState(field);
|
||||
const [currentValue, setCurrentValue] = useState(value as string);
|
||||
}> = PatchComponent(
|
||||
"CustomFieldInput",
|
||||
({ field, value, onChange, isNew = false, error }) => {
|
||||
const intl = useIntl();
|
||||
const [currentField, setCurrentField] = useState(field);
|
||||
const [currentValue, setCurrentValue] = useState(value as string);
|
||||
|
||||
const fieldRef = useRef<HTMLInputElement>(null);
|
||||
const valueRef = useRef<HTMLInputElement>(null);
|
||||
const fieldRef = useRef<HTMLInputElement>(null);
|
||||
const valueRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentField(field);
|
||||
setCurrentValue(value as string);
|
||||
}, [field, value]);
|
||||
useEffect(() => {
|
||||
setCurrentField(field);
|
||||
setCurrentValue(value as string);
|
||||
}, [field, value]);
|
||||
|
||||
function onBlur() {
|
||||
onChange(currentField, convertCustomValue(currentValue));
|
||||
}
|
||||
function onBlur() {
|
||||
onChange(currentField, convertCustomValue(currentValue));
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
onChange("", "");
|
||||
}
|
||||
function onDelete() {
|
||||
onChange("", "");
|
||||
}
|
||||
|
||||
return (
|
||||
<FormGroup>
|
||||
<Row className={cx("custom-fields-row", { "custom-fields-new": isNew })}>
|
||||
<Col sm={3} xl={2} className="custom-fields-field">
|
||||
{isNew ? (
|
||||
<>
|
||||
return (
|
||||
<FormGroup>
|
||||
<Row
|
||||
className={cx("custom-fields-row", { "custom-fields-new": isNew })}
|
||||
>
|
||||
<Col sm={3} xl={2} className="custom-fields-field">
|
||||
{isNew ? (
|
||||
<>
|
||||
<Form.Control
|
||||
ref={fieldRef}
|
||||
className="input-control"
|
||||
type="text"
|
||||
value={currentField ?? ""}
|
||||
placeholder={intl.formatMessage({
|
||||
id: "custom_fields.field",
|
||||
})}
|
||||
onChange={(event) =>
|
||||
setCurrentField(event.currentTarget.value)
|
||||
}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Form.Label title={currentField}>{currentField}</Form.Label>
|
||||
)}
|
||||
</Col>
|
||||
<Col sm={9} xl={7}>
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
ref={fieldRef}
|
||||
ref={valueRef}
|
||||
className="input-control"
|
||||
type="text"
|
||||
value={currentField ?? ""}
|
||||
placeholder={intl.formatMessage({ id: "custom_fields.field" })}
|
||||
onChange={(event) => setCurrentField(event.currentTarget.value)}
|
||||
value={(currentValue as string) ?? ""}
|
||||
placeholder={currentField}
|
||||
onChange={(event) => setCurrentValue(event.currentTarget.value)}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Form.Label title={currentField}>{currentField}</Form.Label>
|
||||
)}
|
||||
</Col>
|
||||
<Col sm={9} xl={7}>
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
ref={valueRef}
|
||||
className="input-control"
|
||||
type="text"
|
||||
value={(currentValue as string) ?? ""}
|
||||
placeholder={currentField}
|
||||
onChange={(event) => setCurrentValue(event.currentTarget.value)}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
{!isNew && (
|
||||
<Button
|
||||
className="custom-fields-remove"
|
||||
variant="danger"
|
||||
onClick={() => onDelete()}
|
||||
>
|
||||
<Icon icon={faMinus} />
|
||||
</Button>
|
||||
)}
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Control.Feedback type="invalid">{error}</Form.Control.Feedback>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
<InputGroup.Append>
|
||||
{!isNew && (
|
||||
<Button
|
||||
className="custom-fields-remove"
|
||||
variant="danger"
|
||||
onClick={() => onDelete()}
|
||||
>
|
||||
<Icon icon={faMinus} />
|
||||
</Button>
|
||||
)}
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Control.Feedback type="invalid">{error}</Form.Control.Feedback>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
interface ICustomField {
|
||||
field: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useLayoutEffect, useRef } from "react";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { remToPx } from "src/utils/units";
|
||||
|
||||
const DEFAULT_WIDTH = Math.round(remToPx(30));
|
||||
|
|
@ -6,34 +7,37 @@ const DEFAULT_WIDTH = Math.round(remToPx(30));
|
|||
// Props used by the <img> element
|
||||
type IDetailImageProps = JSX.IntrinsicElements["img"];
|
||||
|
||||
export const DetailImage = (props: IDetailImageProps) => {
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
export const DetailImage = PatchComponent(
|
||||
"DetailImage",
|
||||
(props: IDetailImageProps) => {
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
|
||||
function fixWidth() {
|
||||
const img = imgRef.current;
|
||||
if (!img) return;
|
||||
function fixWidth() {
|
||||
const img = imgRef.current;
|
||||
if (!img) return;
|
||||
|
||||
// prevent SVG's w/o intrinsic size from rendering as 0x0
|
||||
if (img.naturalWidth === 0) {
|
||||
// If the naturalWidth is zero, it means the image either hasn't loaded yet
|
||||
// or we're on Firefox and it is an SVG w/o an intrinsic size.
|
||||
// So set the width to our fallback width.
|
||||
img.setAttribute("width", String(DEFAULT_WIDTH));
|
||||
} else {
|
||||
// If we have a `naturalWidth`, this could either be the actual intrinsic width
|
||||
// of the image, or the image is an SVG w/o an intrinsic size and we're on Chrome or Safari,
|
||||
// which seem to return a size calculated in some browser-specific way.
|
||||
// Worse yet, once rendered, Safari will then return the value of `img.width` as `img.naturalWidth`,
|
||||
// so we need to clone the image to disconnect it from the DOM, and then get the `naturalWidth` of the clone,
|
||||
// in order to always return the same `naturalWidth` for a given src.
|
||||
const i = img.cloneNode() as HTMLImageElement;
|
||||
img.setAttribute("width", String(i.naturalWidth || DEFAULT_WIDTH));
|
||||
// prevent SVG's w/o intrinsic size from rendering as 0x0
|
||||
if (img.naturalWidth === 0) {
|
||||
// If the naturalWidth is zero, it means the image either hasn't loaded yet
|
||||
// or we're on Firefox and it is an SVG w/o an intrinsic size.
|
||||
// So set the width to our fallback width.
|
||||
img.setAttribute("width", String(DEFAULT_WIDTH));
|
||||
} else {
|
||||
// If we have a `naturalWidth`, this could either be the actual intrinsic width
|
||||
// of the image, or the image is an SVG w/o an intrinsic size and we're on Chrome or Safari,
|
||||
// which seem to return a size calculated in some browser-specific way.
|
||||
// Worse yet, once rendered, Safari will then return the value of `img.width` as `img.naturalWidth`,
|
||||
// so we need to clone the image to disconnect it from the DOM, and then get the `naturalWidth` of the clone,
|
||||
// in order to always return the same `naturalWidth` for a given src.
|
||||
const i = img.cloneNode() as HTMLImageElement;
|
||||
img.setAttribute("width", String(i.naturalWidth || DEFAULT_WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
fixWidth();
|
||||
}, [props.src]);
|
||||
|
||||
return <img ref={imgRef} onLoad={() => fixWidth()} {...props} />;
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
fixWidth();
|
||||
}, [props.src]);
|
||||
|
||||
return <img ref={imgRef} onLoad={() => fixWidth()} {...props} />;
|
||||
};
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { PropsWithChildren } from "react";
|
||||
import { LoadingIndicator } from "../LoadingIndicator";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
export const HeaderImage: React.FC<
|
||||
PropsWithChildren<{
|
||||
encodingImage: boolean;
|
||||
}>
|
||||
> = ({ encodingImage, children }) => {
|
||||
> = PatchComponent("HeaderImage", ({ encodingImage, children }) => {
|
||||
return (
|
||||
<div className="detail-header-image">
|
||||
{encodingImage ? (
|
||||
|
|
@ -18,4 +19,4 @@ export const HeaderImage: React.FC<
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { useIntl } from "react-intl";
|
|||
import { ModalComponent } from "./Modal";
|
||||
import { Icon } from "./Icon";
|
||||
import { faFile, faLink } from "@fortawesome/free-solid-svg-icons";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
interface IImageInput {
|
||||
isEditing: boolean;
|
||||
|
|
@ -24,122 +25,119 @@ function acceptExtensions(acceptSVG: boolean = false) {
|
|||
return `.jpg,.jpeg,.png,.webp,.gif${acceptSVG ? ",.svg" : ""}`;
|
||||
}
|
||||
|
||||
export const ImageInput: React.FC<IImageInput> = ({
|
||||
isEditing,
|
||||
text,
|
||||
onImageChange,
|
||||
onImageURL,
|
||||
acceptSVG = false,
|
||||
}) => {
|
||||
const [isShowDialog, setIsShowDialog] = useState(false);
|
||||
const [url, setURL] = useState("");
|
||||
const intl = useIntl();
|
||||
export const ImageInput: React.FC<IImageInput> = PatchComponent(
|
||||
"ImageInput",
|
||||
({ isEditing, text, onImageChange, onImageURL, acceptSVG = false }) => {
|
||||
const [isShowDialog, setIsShowDialog] = useState(false);
|
||||
const [url, setURL] = useState("");
|
||||
const intl = useIntl();
|
||||
|
||||
if (!isEditing) return <div />;
|
||||
if (!isEditing) return <div />;
|
||||
|
||||
if (!onImageURL) {
|
||||
// just return the file input
|
||||
return (
|
||||
<Form.Label className="image-input">
|
||||
<Button variant="secondary">
|
||||
{text ?? intl.formatMessage({ id: "actions.browse_for_image" })}
|
||||
</Button>
|
||||
<Form.Control
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
accept={acceptExtensions(acceptSVG)}
|
||||
/>
|
||||
</Form.Label>
|
||||
);
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
setURL("");
|
||||
setIsShowDialog(true);
|
||||
}
|
||||
|
||||
function onConfirmURL() {
|
||||
if (!onImageURL) {
|
||||
return;
|
||||
// just return the file input
|
||||
return (
|
||||
<Form.Label className="image-input">
|
||||
<Button variant="secondary">
|
||||
{text ?? intl.formatMessage({ id: "actions.browse_for_image" })}
|
||||
</Button>
|
||||
<Form.Control
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
accept={acceptExtensions(acceptSVG)}
|
||||
/>
|
||||
</Form.Label>
|
||||
);
|
||||
}
|
||||
|
||||
setIsShowDialog(false);
|
||||
onImageURL(url);
|
||||
}
|
||||
function showDialog() {
|
||||
setURL("");
|
||||
setIsShowDialog(true);
|
||||
}
|
||||
|
||||
function onConfirmURL() {
|
||||
if (!onImageURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsShowDialog(false);
|
||||
onImageURL(url);
|
||||
}
|
||||
|
||||
function renderDialog() {
|
||||
return (
|
||||
<ModalComponent
|
||||
show={!!isShowDialog}
|
||||
onHide={() => setIsShowDialog(false)}
|
||||
header={intl.formatMessage({ id: "dialogs.set_image_url_title" })}
|
||||
accept={{
|
||||
onClick: onConfirmURL,
|
||||
text: intl.formatMessage({ id: "actions.confirm" }),
|
||||
}}
|
||||
>
|
||||
<div className="dialog-content">
|
||||
<Form.Group controlId="url" as={Row}>
|
||||
<Form.Label column xs={3}>
|
||||
{intl.formatMessage({ id: "url" })}
|
||||
</Form.Label>
|
||||
<Col xs={9}>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setURL(event.currentTarget.value)
|
||||
}
|
||||
value={url}
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
</div>
|
||||
</ModalComponent>
|
||||
);
|
||||
}
|
||||
|
||||
const popover = (
|
||||
<Popover id="set-image-popover">
|
||||
<Popover.Content>
|
||||
<>
|
||||
<div>
|
||||
<Form.Label className="image-input">
|
||||
<Button variant="secondary">
|
||||
<Icon icon={faFile} className="fa-fw" />
|
||||
<span>{intl.formatMessage({ id: "actions.from_file" })}</span>
|
||||
</Button>
|
||||
<Form.Control
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
accept={acceptExtensions(acceptSVG)}
|
||||
/>
|
||||
</Form.Label>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="minimal" onClick={showDialog}>
|
||||
<Icon icon={faLink} className="fa-fw" />
|
||||
<span>{intl.formatMessage({ id: "actions.from_url" })}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
function renderDialog() {
|
||||
return (
|
||||
<ModalComponent
|
||||
show={!!isShowDialog}
|
||||
onHide={() => setIsShowDialog(false)}
|
||||
header={intl.formatMessage({ id: "dialogs.set_image_url_title" })}
|
||||
accept={{
|
||||
onClick: onConfirmURL,
|
||||
text: intl.formatMessage({ id: "actions.confirm" }),
|
||||
}}
|
||||
>
|
||||
<div className="dialog-content">
|
||||
<Form.Group controlId="url" as={Row}>
|
||||
<Form.Label column xs={3}>
|
||||
{intl.formatMessage({ id: "url" })}
|
||||
</Form.Label>
|
||||
<Col xs={9}>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setURL(event.currentTarget.value)
|
||||
}
|
||||
value={url}
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
</div>
|
||||
</ModalComponent>
|
||||
<>
|
||||
{renderDialog()}
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement="top"
|
||||
overlay={popover}
|
||||
rootClose
|
||||
>
|
||||
<Button variant="secondary" className="mr-2">
|
||||
{text ?? intl.formatMessage({ id: "actions.set_image" })}
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const popover = (
|
||||
<Popover id="set-image-popover">
|
||||
<Popover.Content>
|
||||
<>
|
||||
<div>
|
||||
<Form.Label className="image-input">
|
||||
<Button variant="secondary">
|
||||
<Icon icon={faFile} className="fa-fw" />
|
||||
<span>{intl.formatMessage({ id: "actions.from_file" })}</span>
|
||||
</Button>
|
||||
<Form.Control
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
accept={acceptExtensions(acceptSVG)}
|
||||
/>
|
||||
</Form.Label>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="minimal" onClick={showDialog}>
|
||||
<Icon icon={faLink} className="fa-fw" />
|
||||
<span>{intl.formatMessage({ id: "actions.from_url" })}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderDialog()}
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement="top"
|
||||
overlay={popover}
|
||||
rootClose
|
||||
>
|
||||
<Button variant="secondary" className="mr-2">
|
||||
{text ?? intl.formatMessage({ id: "actions.set_image" })}
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
|
|
|||
|
|
@ -149,7 +149,9 @@ Returns `void`.
|
|||
- `CompressedPerformerDetailsPanel`
|
||||
- `ConstantSetting`
|
||||
- `CountrySelect`
|
||||
- `CustomFieldInput`
|
||||
- `DateInput`
|
||||
- `DetailImage`
|
||||
- `ExternalLinkButtons`
|
||||
- `ExternalLinksButton`
|
||||
- `FolderSelect`
|
||||
|
|
@ -162,9 +164,12 @@ Returns `void`.
|
|||
- `GalleryIDSelect`
|
||||
- `GallerySelect`
|
||||
- `GallerySelect.sort`
|
||||
- `HeaderImage`
|
||||
- `HoverPopover`
|
||||
- `Icon`
|
||||
- `ImageDetailPanel`
|
||||
- `ImageInput`
|
||||
- `LightboxLink`
|
||||
- `LoadingIndicator`
|
||||
- `ModalSetting`
|
||||
- `GroupIDSelect`
|
||||
|
|
@ -186,6 +191,7 @@ Returns `void`.
|
|||
- `PerformerSelect.sort`
|
||||
- `PerformerGalleriesPanel`
|
||||
- `PerformerGroupsPanel`
|
||||
- `PerformerHeaderImage`
|
||||
- `PerformerImagesPanel`
|
||||
- `PerformerScenesPanel`
|
||||
- `PluginRoutes`
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import { PropsWithChildren } from "react";
|
|||
import { useLightbox } from "./hooks";
|
||||
import { ILightboxImage } from "./types";
|
||||
import { Button } from "react-bootstrap";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
export const LightboxLink: React.FC<
|
||||
PropsWithChildren<{ images?: ILightboxImage[] | undefined; index?: number }>
|
||||
> = ({ images, index, children }) => {
|
||||
> = PatchComponent("LightboxLink", ({ images, index, children }) => {
|
||||
const showLightbox = useLightbox();
|
||||
|
||||
if (!images || images.length === 0) {
|
||||
|
|
@ -20,4 +21,4 @@ export const LightboxLink: React.FC<
|
|||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
6
ui/v2.5/src/pluginApi.d.ts
vendored
6
ui/v2.5/src/pluginApi.d.ts
vendored
|
|
@ -694,12 +694,18 @@ declare namespace PluginApi {
|
|||
PerformerAppearsWithPanel: React.FC<any>;
|
||||
PerformerGalleriesPanel: React.FC<any>;
|
||||
PerformerGroupsPanel: React.FC<any>;
|
||||
PerformerHeaderImage: React.FC<any>;
|
||||
PerformerScenesPanel: React.FC<any>;
|
||||
PerformerImagesPanel: React.FC<any>;
|
||||
TabTitleCounter: React.FC<any>;
|
||||
PerformerCard: React.FC<any>;
|
||||
ExternalLinkButtons: React.FC<any>;
|
||||
ExternalLinksButton: React.FC<any>;
|
||||
CustomFieldInput: React.FC<any>;
|
||||
ImageInput: React.FC<any>;
|
||||
DetailImage: React.FC<any>;
|
||||
HeaderImage: React.FC<any>;
|
||||
LightboxLink: React.FC<any>;
|
||||
"PerformerCard.Popovers": React.FC<any>;
|
||||
"PerformerCard.Details": React.FC<any>;
|
||||
"PerformerCard.Overlays": React.FC<any>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue