Various UI fixes (#1502)

* Set/unset existing ids when moving to/from set
* Refactor rating banner
* Fix overlapping in multi set
* Prevent UI crash on bad hierarchical input value
This commit is contained in:
WithoutPants
2021-06-16 12:17:54 +10:00
committed by GitHub
parent b55715775d
commit 45f4a5ba81
14 changed files with 95 additions and 160 deletions

View File

@@ -27,10 +27,12 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
setPerformerMode, setPerformerMode,
] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add); ] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
const [performerIds, setPerformerIds] = useState<string[]>(); const [performerIds, setPerformerIds] = useState<string[]>();
const [existingPerformerIds, setExistingPerformerIds] = useState<string[]>();
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>( const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(
GQL.BulkUpdateIdMode.Add GQL.BulkUpdateIdMode.Add
); );
const [tagIds, setTagIds] = useState<string[]>(); const [tagIds, setTagIds] = useState<string[]>();
const [existingTagIds, setExistingTagIds] = useState<string[]>();
const [organized, setOrganized] = useState<boolean | undefined>(); const [organized, setOrganized] = useState<boolean | undefined>();
const [updateGalleries] = useBulkGalleryUpdate(); const [updateGalleries] = useBulkGalleryUpdate();
@@ -279,16 +281,11 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
setRating(updateRating); setRating(updateRating);
setStudioId(updateStudioID); setStudioId(updateStudioID);
if (performerMode === GQL.BulkUpdateIdMode.Set) { setExistingPerformerIds(updatePerformerIds);
setPerformerIds(updatePerformerIds); setExistingTagIds(updateTagIds);
}
if (tagMode === GQL.BulkUpdateIdMode.Set) {
setTagIds(updateTagIds);
}
setOrganized(updateOrganized); setOrganized(updateOrganized);
}, [props.selected, performerMode, tagMode]); }, [props.selected]);
useEffect(() => { useEffect(() => {
if (checkboxRef.current) { if (checkboxRef.current) {
@@ -301,12 +298,15 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
ids: string[] | undefined ids: string[] | undefined
) { ) {
let mode = GQL.BulkUpdateIdMode.Add; let mode = GQL.BulkUpdateIdMode.Add;
let existingIds: string[] | undefined = [];
switch (type) { switch (type) {
case "performers": case "performers":
mode = performerMode; mode = performerMode;
existingIds = existingPerformerIds;
break; break;
case "tags": case "tags":
mode = tagMode; mode = tagMode;
existingIds = existingTagIds;
break; break;
} }
@@ -314,8 +314,7 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
<MultiSet <MultiSet
type={type} type={type}
disabled={isUpdating} disabled={isUpdating}
onUpdate={(items) => { onUpdate={(itemIDs) => {
const itemIDs = items.map((i) => i.id);
switch (type) { switch (type) {
case "performers": case "performers":
setPerformerIds(itemIDs); setPerformerIds(itemIDs);
@@ -335,6 +334,7 @@ export const EditGalleriesDialog: React.FC<IListOperationProps> = (
break; break;
} }
}} }}
existingIds={existingIds ?? []}
ids={ids ?? []} ids={ids ?? []}
mode={mode} mode={mode}
/> />

View File

@@ -7,6 +7,7 @@ import { useConfiguration } from "src/core/StashService";
import { GridCard, HoverPopover, Icon, TagLink } from "src/components/Shared"; import { GridCard, HoverPopover, Icon, TagLink } from "src/components/Shared";
import { TextUtils } from "src/utils"; import { TextUtils } from "src/utils";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
import { RatingBanner } from "../Shared/RatingBanner";
interface IProps { interface IProps {
gallery: GQL.SlimGalleryDataFragment; gallery: GQL.SlimGalleryDataFragment;
@@ -114,21 +115,6 @@ export const GalleryCard: React.FC<IProps> = (props) => {
} }
} }
function maybeRenderRatingBanner() {
if (!props.gallery.rating) {
return;
}
return (
<div
className={`rating-banner ${
props.gallery.rating ? `rating-${props.gallery.rating}` : ""
}`}
>
RATING: {props.gallery.rating}
</div>
);
}
return ( return (
<GridCard <GridCard
className={`gallery-card zoom-${props.zoomIndex}`} className={`gallery-card zoom-${props.zoomIndex}`}
@@ -148,7 +134,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
src={`${props.gallery.cover.paths.thumbnail}`} src={`${props.gallery.cover.paths.thumbnail}`}
/> />
) : undefined} ) : undefined}
{maybeRenderRatingBanner()} <RatingBanner rating={props.gallery.rating} />
</> </>
} }
overlays={maybeRenderSceneStudioOverlay()} overlays={maybeRenderSceneStudioOverlay()}

View File

@@ -27,10 +27,12 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
setPerformerMode, setPerformerMode,
] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add); ] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
const [performerIds, setPerformerIds] = useState<string[]>(); const [performerIds, setPerformerIds] = useState<string[]>();
const [existingPerformerIds, setExistingPerformerIds] = useState<string[]>();
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>( const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(
GQL.BulkUpdateIdMode.Add GQL.BulkUpdateIdMode.Add
); );
const [tagIds, setTagIds] = useState<string[]>(); const [tagIds, setTagIds] = useState<string[]>();
const [existingTagIds, setExistingTagIds] = useState<string[]>();
const [organized, setOrganized] = useState<boolean | undefined>(); const [organized, setOrganized] = useState<boolean | undefined>();
const [updateImages] = useBulkImageUpdate(); const [updateImages] = useBulkImageUpdate();
@@ -275,13 +277,8 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
setRating(updateRating); setRating(updateRating);
setStudioId(updateStudioID); setStudioId(updateStudioID);
if (performerMode === GQL.BulkUpdateIdMode.Set) { setExistingPerformerIds(updatePerformerIds);
setPerformerIds(updatePerformerIds); setExistingTagIds(updateTagIds);
}
if (tagMode === GQL.BulkUpdateIdMode.Set) {
setTagIds(updateTagIds);
}
setOrganized(updateOrganized); setOrganized(updateOrganized);
}, [props.selected, performerMode, tagMode]); }, [props.selected, performerMode, tagMode]);
@@ -296,12 +293,15 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
ids: string[] | undefined ids: string[] | undefined
) { ) {
let mode = GQL.BulkUpdateIdMode.Add; let mode = GQL.BulkUpdateIdMode.Add;
let existingIds: string[] | undefined = [];
switch (type) { switch (type) {
case "performers": case "performers":
mode = performerMode; mode = performerMode;
existingIds = existingPerformerIds;
break; break;
case "tags": case "tags":
mode = tagMode; mode = tagMode;
existingIds = existingTagIds;
break; break;
} }
@@ -309,8 +309,7 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
<MultiSet <MultiSet
type={type} type={type}
disabled={isUpdating} disabled={isUpdating}
onUpdate={(items) => { onUpdate={(itemIDs) => {
const itemIDs = items.map((i) => i.id);
switch (type) { switch (type) {
case "performers": case "performers":
setPerformerIds(itemIDs); setPerformerIds(itemIDs);
@@ -330,6 +329,7 @@ export const EditImagesDialog: React.FC<IListOperationProps> = (
break; break;
} }
}} }}
existingIds={existingIds ?? []}
ids={ids ?? []} ids={ids ?? []}
mode={mode} mode={mode}
/> />

View File

@@ -6,6 +6,7 @@ import { Icon, TagLink, HoverPopover, SweatDrops } from "src/components/Shared";
import { TextUtils } from "src/utils"; import { TextUtils } from "src/utils";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
import { GridCard } from "../Shared/GridCard"; import { GridCard } from "../Shared/GridCard";
import { RatingBanner } from "../Shared/RatingBanner";
interface IImageCardProps { interface IImageCardProps {
image: GQL.SlimImageDataFragment; image: GQL.SlimImageDataFragment;
@@ -18,21 +19,6 @@ interface IImageCardProps {
export const ImageCard: React.FC<IImageCardProps> = ( export const ImageCard: React.FC<IImageCardProps> = (
props: IImageCardProps props: IImageCardProps
) => { ) => {
function maybeRenderRatingBanner() {
if (!props.image.rating) {
return;
}
return (
<div
className={`rating-banner ${
props.image.rating ? `rating-${props.image.rating}` : ""
}`}
>
RATING: {props.image.rating}
</div>
);
}
function maybeRenderTagPopoverButton() { function maybeRenderTagPopoverButton() {
if (props.image.tags.length <= 0) return; if (props.image.tags.length <= 0) return;
@@ -130,7 +116,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
src={props.image.paths.thumbnail ?? ""} src={props.image.paths.thumbnail ?? ""}
/> />
</div> </div>
{maybeRenderRatingBanner()} <RatingBanner rating={props.image.rating} />
</> </>
} }
popovers={maybeRenderPopoverButtonGroup()} popovers={maybeRenderPopoverButtonGroup()}

View File

@@ -2,6 +2,7 @@ import React, { FunctionComponent } from "react";
import { FormattedPlural } from "react-intl"; import { FormattedPlural } from "react-intl";
import * as GQL from "src/core/generated-graphql"; import * as GQL from "src/core/generated-graphql";
import { GridCard } from "src/components/Shared"; import { GridCard } from "src/components/Shared";
import { RatingBanner } from "../Shared/RatingBanner";
interface IProps { interface IProps {
movie: GQL.MovieDataFragment; movie: GQL.MovieDataFragment;
@@ -12,21 +13,6 @@ interface IProps {
} }
export const MovieCard: FunctionComponent<IProps> = (props: IProps) => { export const MovieCard: FunctionComponent<IProps> = (props: IProps) => {
function maybeRenderRatingBanner() {
if (!props.movie.rating) {
return;
}
return (
<div
className={`rating-banner ${
props.movie.rating ? `rating-${props.movie.rating}` : ""
}`}
>
RATING: {props.movie.rating}
</div>
);
}
function maybeRenderSceneNumber() { function maybeRenderSceneNumber() {
if (!props.sceneIndex) { if (!props.sceneIndex) {
return ( return (
@@ -57,7 +43,7 @@ export const MovieCard: FunctionComponent<IProps> = (props: IProps) => {
alt={props.movie.name ?? ""} alt={props.movie.name ?? ""}
src={props.movie.front_image_path ?? ""} src={props.movie.front_image_path ?? ""}
/> />
{maybeRenderRatingBanner()} <RatingBanner rating={props.movie.rating} />
</> </>
} }
details={maybeRenderSceneNumber()} details={maybeRenderSceneNumber()}

View File

@@ -25,6 +25,7 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
GQL.BulkUpdateIdMode.Add GQL.BulkUpdateIdMode.Add
); );
const [tagIds, setTagIds] = useState<string[]>(); const [tagIds, setTagIds] = useState<string[]>();
const [existingTagIds, setExistingTagIds] = useState<string[]>();
const [favorite, setFavorite] = useState<boolean | undefined>(); const [favorite, setFavorite] = useState<boolean | undefined>();
const [updatePerformers] = useBulkPerformerUpdate(getPerformerInput()); const [updatePerformers] = useBulkPerformerUpdate(getPerformerInput());
@@ -178,9 +179,7 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
} }
}); });
if (tagMode === GQL.BulkUpdateIdMode.Set) { setExistingTagIds(updateTagIds);
setTagIds(updateTagIds);
}
setFavorite(updateFavorite); setFavorite(updateFavorite);
setRating(updateRating); setRating(updateRating);
}, [props.selected, tagMode]); }, [props.selected, tagMode]);
@@ -191,42 +190,6 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
} }
}, [favorite, checkboxRef]); }, [favorite, checkboxRef]);
function renderMultiSelect(
type: "performers" | "tags",
ids: string[] | undefined
) {
let mode = GQL.BulkUpdateIdMode.Add;
switch (type) {
case "tags":
mode = tagMode;
break;
}
return (
<MultiSet
type={type}
disabled={isUpdating}
onUpdate={(items) => {
const itemIDs = items.map((i) => i.id);
switch (type) {
case "tags":
setTagIds(itemIDs);
break;
}
}}
onSetMode={(newMode) => {
switch (type) {
case "tags":
setTagMode(newMode);
break;
}
}}
ids={ids ?? []}
mode={mode}
/>
);
}
function cycleFavorite() { function cycleFavorite() {
if (favorite) { if (favorite) {
setFavorite(undefined); setFavorite(undefined);
@@ -271,7 +234,15 @@ export const EditPerformersDialog: React.FC<IListOperationProps> = (
<Form.Label> <Form.Label>
<FormattedMessage id="tags" /> <FormattedMessage id="tags" />
</Form.Label> </Form.Label>
{renderMultiSelect("tags", tagIds)} <MultiSet
type="tags"
disabled={isUpdating}
onUpdate={(itemIDs) => setTagIds(itemIDs)}
onSetMode={(newMode) => setTagMode(newMode)}
existingIds={existingTagIds ?? []}
ids={tagIds ?? []}
mode={tagMode}
/>
</Form.Group> </Form.Group>
<Form.Group controlId="favorite"> <Form.Group controlId="favorite">

View File

@@ -27,10 +27,12 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
setPerformerMode, setPerformerMode,
] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add); ] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
const [performerIds, setPerformerIds] = useState<string[]>(); const [performerIds, setPerformerIds] = useState<string[]>();
const [existingPerformerIds, setExistingPerformerIds] = useState<string[]>();
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>( const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(
GQL.BulkUpdateIdMode.Add GQL.BulkUpdateIdMode.Add
); );
const [tagIds, setTagIds] = useState<string[]>(); const [tagIds, setTagIds] = useState<string[]>();
const [existingTagIds, setExistingTagIds] = useState<string[]>();
const [organized, setOrganized] = useState<boolean | undefined>(); const [organized, setOrganized] = useState<boolean | undefined>();
const [updateScenes] = useBulkSceneUpdate(getSceneInput()); const [updateScenes] = useBulkSceneUpdate(getSceneInput());
@@ -271,13 +273,8 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
setRating(updateRating); setRating(updateRating);
setStudioId(updateStudioID); setStudioId(updateStudioID);
if (performerMode === GQL.BulkUpdateIdMode.Set) { setExistingPerformerIds(updatePerformerIds);
setPerformerIds(updatePerformerIds); setExistingTagIds(updateTagIds);
}
if (tagMode === GQL.BulkUpdateIdMode.Set) {
setTagIds(updateTagIds);
}
setOrganized(updateOrganized); setOrganized(updateOrganized);
}, [props.selected, performerMode, tagMode]); }, [props.selected, performerMode, tagMode]);
@@ -292,12 +289,15 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
ids: string[] | undefined ids: string[] | undefined
) { ) {
let mode = GQL.BulkUpdateIdMode.Add; let mode = GQL.BulkUpdateIdMode.Add;
let existingIds: string[] | undefined = [];
switch (type) { switch (type) {
case "performers": case "performers":
mode = performerMode; mode = performerMode;
existingIds = existingPerformerIds;
break; break;
case "tags": case "tags":
mode = tagMode; mode = tagMode;
existingIds = existingTagIds;
break; break;
} }
@@ -305,8 +305,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
<MultiSet <MultiSet
type={type} type={type}
disabled={isUpdating} disabled={isUpdating}
onUpdate={(items) => { onUpdate={(itemIDs) => {
const itemIDs = items.map((i) => i.id);
switch (type) { switch (type) {
case "performers": case "performers":
setPerformerIds(itemIDs); setPerformerIds(itemIDs);
@@ -327,6 +326,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
} }
}} }}
ids={ids ?? []} ids={ids ?? []}
existingIds={existingIds ?? []}
mode={mode} mode={mode}
/> />
); );
@@ -404,7 +404,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
<Form.Group controlId="tags"> <Form.Group controlId="tags">
<Form.Label> <Form.Label>
<FormattedMessage id="performers" /> <FormattedMessage id="tags" />
</Form.Label> </Form.Label>
{renderMultiSelect("tags", tagIds)} {renderMultiSelect("tags", tagIds)}
</Form.Group> </Form.Group>

View File

@@ -15,6 +15,7 @@ import { TextUtils } from "src/utils";
import { SceneQueue } from "src/models/sceneQueue"; import { SceneQueue } from "src/models/sceneQueue";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
import { GridCard } from "../Shared/GridCard"; import { GridCard } from "../Shared/GridCard";
import { RatingBanner } from "../Shared/RatingBanner";
interface IScenePreviewProps { interface IScenePreviewProps {
isPortrait: boolean; isPortrait: boolean;
@@ -89,21 +90,6 @@ export const SceneCard: React.FC<ISceneCardProps> = (
missingStudioImage || missingStudioImage ||
(config?.data?.configuration.interface.showStudioAsText ?? false); (config?.data?.configuration.interface.showStudioAsText ?? false);
function maybeRenderRatingBanner() {
if (!props.scene.rating) {
return;
}
return (
<div
className={`rating-banner ${
props.scene.rating ? `rating-${props.scene.rating}` : ""
}`}
>
RATING: {props.scene.rating}
</div>
);
}
function maybeRenderSceneSpecsOverlay() { function maybeRenderSceneSpecsOverlay() {
return ( return (
<div className="scene-specs-overlay"> <div className="scene-specs-overlay">
@@ -333,7 +319,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
config.data?.configuration?.interface?.soundOnPreview ?? false config.data?.configuration?.interface?.soundOnPreview ?? false
} }
/> />
{maybeRenderRatingBanner()} <RatingBanner rating={props.scene.rating} />
{maybeRenderSceneSpecsOverlay()} {maybeRenderSceneSpecsOverlay()}
</> </>
} }

View File

@@ -13,10 +13,11 @@ type ValidTypes =
interface IMultiSetProps { interface IMultiSetProps {
type: "performers" | "studios" | "tags"; type: "performers" | "studios" | "tags";
existingIds?: string[];
ids?: string[]; ids?: string[];
mode: GQL.BulkUpdateIdMode; mode: GQL.BulkUpdateIdMode;
disabled?: boolean; disabled?: boolean;
onUpdate: (items: ValidTypes[]) => void; onUpdate: (ids: string[]) => void;
onSetMode: (mode: GQL.BulkUpdateIdMode) => void; onSetMode: (mode: GQL.BulkUpdateIdMode) => void;
} }
@@ -31,7 +32,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (
]; ];
function onUpdate(items: ValidTypes[]) { function onUpdate(items: ValidTypes[]) {
props.onUpdate(items); props.onUpdate(items.map((i) => i.id));
} }
function getModeText(mode: GQL.BulkUpdateIdMode) { function getModeText(mode: GQL.BulkUpdateIdMode) {
@@ -51,13 +52,32 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (
} }
} }
function onSetMode(mode: GQL.BulkUpdateIdMode) {
if (mode === props.mode) {
return;
}
// if going to Set, set the existing ids
if (mode === GQL.BulkUpdateIdMode.Set && props.existingIds) {
props.onUpdate(props.existingIds);
// if going from Set, wipe the ids
} else if (
mode !== GQL.BulkUpdateIdMode.Set &&
props.mode === GQL.BulkUpdateIdMode.Set
) {
props.onUpdate([]);
}
props.onSetMode(mode);
}
function renderModeButton(mode: GQL.BulkUpdateIdMode) { function renderModeButton(mode: GQL.BulkUpdateIdMode) {
return ( return (
<Button <Button
variant="primary" variant="primary"
active={props.mode === mode} active={props.mode === mode}
size="sm" size="sm"
onClick={() => props.onSetMode(mode)} onClick={() => onSetMode(mode)}
disabled={props.disabled} disabled={props.disabled}
> >
{getModeText(mode)} {getModeText(mode)}
@@ -66,7 +86,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (
} }
return ( return (
<div> <div className="multi-set">
<ButtonGroup className="button-group-above"> <ButtonGroup className="button-group-above">
{modes.map((m) => renderModeButton(m))} {modes.map((m) => renderModeButton(m))}
</ButtonGroup> </ButtonGroup>

View File

@@ -0,0 +1,15 @@
import React from "react";
import { FormattedMessage } from "react-intl";
interface IProps {
rating?: number | null;
}
export const RatingBanner: React.FC<IProps> = ({ rating }) =>
rating ? (
<div className={`rating-banner rating-${rating}`}>
<FormattedMessage id="rating" />: {rating}
</div>
) : (
<></>
);

View File

@@ -76,11 +76,9 @@
} }
} }
.multi-set > div.input-group-prepend + div { // z-index gets set on button groups for some reason
flex: 1 1; .multi-set .btn-group > button.btn {
margin-bottom: 0; z-index: auto;
min-width: 0;
position: relative;
} }
.folder-list { .folder-list {

View File

@@ -5,6 +5,7 @@ import { NavUtils } from "src/utils";
import { GridCard } from "src/components/Shared"; import { GridCard } from "src/components/Shared";
import { ButtonGroup } from "react-bootstrap"; import { ButtonGroup } from "react-bootstrap";
import { PopoverCountButton } from "../Shared/PopoverCountButton"; import { PopoverCountButton } from "../Shared/PopoverCountButton";
import { RatingBanner } from "../Shared/RatingBanner";
interface IProps { interface IProps {
studio: GQL.StudioDataFragment; studio: GQL.StudioDataFragment;
@@ -45,21 +46,6 @@ function maybeRenderChildren(studio: GQL.StudioDataFragment) {
} }
} }
function maybeRenderRatingBanner(studio: GQL.StudioDataFragment) {
if (!studio.rating) {
return;
}
return (
<div
className={`rating-banner ${
studio.rating ? `rating-${studio.rating}` : ""
}`}
>
RATING: {studio.rating}
</div>
);
}
export const StudioCard: React.FC<IProps> = ({ export const StudioCard: React.FC<IProps> = ({
studio, studio,
hideParent, hideParent,
@@ -135,7 +121,7 @@ export const StudioCard: React.FC<IProps> = ({
<> <>
{maybeRenderParent(studio, hideParent)} {maybeRenderParent(studio, hideParent)}
{maybeRenderChildren(studio)} {maybeRenderChildren(studio)}
{maybeRenderRatingBanner(studio)} <RatingBanner rating={studio.rating} />
{maybeRenderPopoverButtonGroup()} {maybeRenderPopoverButtonGroup()}
</> </>
} }

View File

@@ -362,6 +362,7 @@ div.dropdown-menu {
position: absolute; position: absolute;
text-align: center; text-align: center;
text-size-adjust: none; text-size-adjust: none;
text-transform: uppercase;
top: 14px; top: 14px;
transform: rotate(-36deg); transform: rotate(-36deg);
} }

View File

@@ -341,14 +341,14 @@ export abstract class IHierarchicalLabeledIdCriterion extends Criterion<IHierarc
protected toCriterionInput(): HierarchicalMultiCriterionInput { protected toCriterionInput(): HierarchicalMultiCriterionInput {
return { return {
value: this.value.items.map((v) => v.id), value: (this.value.items ?? []).map((v) => v.id),
modifier: this.modifier, modifier: this.modifier,
depth: this.value.depth, depth: this.value.depth,
}; };
} }
public getLabelValue(): string { public getLabelValue(): string {
const labels = this.value.items.map((v) => v.label).join(", "); const labels = (this.value.items ?? []).map((v) => v.label).join(", ");
if (this.value.depth === 0) { if (this.value.depth === 0) {
return labels; return labels;