New patchable performer page components (#5897)

This commit is contained in:
CJ 2025-06-02 17:16:57 -07:00 committed by GitHub
parent d9a316d083
commit c66ef42480
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 263 additions and 213 deletions

View file

@ -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}
<PerformerHeaderImage
activeImage={activeImage}
collapsed={collapsed}
encodingImage={encodingImage}
lightboxImages={lightboxImages}
performer={performer}
/>
</LightboxLink>
)}
</HeaderImage>
<div className="row">
<div className="performer-head col">
<DetailTitle

View file

@ -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,7 +91,9 @@ const CustomFieldInput: React.FC<{
onChange: (field: string, value: unknown) => void;
isNew?: boolean;
error?: string;
}> = ({ field, value, onChange, isNew = false, error }) => {
}> = PatchComponent(
"CustomFieldInput",
({ field, value, onChange, isNew = false, error }) => {
const intl = useIntl();
const [currentField, setCurrentField] = useState(field);
const [currentValue, setCurrentValue] = useState(value as string);
@ -113,7 +116,9 @@ const CustomFieldInput: React.FC<{
return (
<FormGroup>
<Row className={cx("custom-fields-row", { "custom-fields-new": isNew })}>
<Row
className={cx("custom-fields-row", { "custom-fields-new": isNew })}
>
<Col sm={3} xl={2} className="custom-fields-field">
{isNew ? (
<>
@ -122,8 +127,12 @@ const CustomFieldInput: React.FC<{
className="input-control"
type="text"
value={currentField ?? ""}
placeholder={intl.formatMessage({ id: "custom_fields.field" })}
onChange={(event) => setCurrentField(event.currentTarget.value)}
placeholder={intl.formatMessage({
id: "custom_fields.field",
})}
onChange={(event) =>
setCurrentField(event.currentTarget.value)
}
onBlur={onBlur}
/>
</>
@ -159,7 +168,8 @@ const CustomFieldInput: React.FC<{
<Form.Control.Feedback type="invalid">{error}</Form.Control.Feedback>
</FormGroup>
);
};
}
);
interface ICustomField {
field: string;

View file

@ -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,7 +7,9 @@ const DEFAULT_WIDTH = Math.round(remToPx(30));
// Props used by the <img> element
type IDetailImageProps = JSX.IntrinsicElements["img"];
export const DetailImage = (props: IDetailImageProps) => {
export const DetailImage = PatchComponent(
"DetailImage",
(props: IDetailImageProps) => {
const imgRef = useRef<HTMLImageElement>(null);
function fixWidth() {
@ -36,4 +39,5 @@ export const DetailImage = (props: IDetailImageProps) => {
}, [props.src]);
return <img ref={imgRef} onLoad={() => fixWidth()} {...props} />;
};
}
);

View file

@ -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>
);
};
});

View file

@ -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,13 +25,9 @@ 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,
}) => {
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();
@ -142,4 +139,5 @@ export const ImageInput: React.FC<IImageInput> = ({
</OverlayTrigger>
</>
);
};
}
);

View file

@ -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`

View file

@ -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>
);
};
});

View file

@ -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>;