Use RatingSystem control in RatingFilter (#3133)

* Use RatingSystem control in RatingFilter
* Improve styling for rating on performer page
This commit is contained in:
WithoutPants
2022-11-17 10:10:40 +11:00
committed by GitHub
parent 3660bf2d1a
commit f0bf780c2e
9 changed files with 92 additions and 121 deletions

View File

@@ -216,11 +216,7 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
} }
if (criterion instanceof RatingCriterion) { if (criterion instanceof RatingCriterion) {
return ( return (
<RatingFilter <RatingFilter criterion={criterion} onValueChanged={onValueChanged} />
criterion={criterion}
onValueChanged={onValueChanged}
configuration={config}
/>
); );
} }
if ( if (
@@ -307,12 +303,32 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
); );
} }
function isValid() {
if (criterion.criterionOption.type === "none") {
return false;
}
if (criterion instanceof RatingCriterion) {
switch (criterion.modifier) {
case CriterionModifier.Equals:
case CriterionModifier.NotEquals:
case CriterionModifier.LessThan:
return !!criterion.value.value;
case CriterionModifier.Between:
case CriterionModifier.NotBetween:
return criterion.value.value < (criterion.value.value2 ?? 0);
}
}
return true;
}
const title = !editingCriterion const title = !editingCriterion
? intl.formatMessage({ id: "search_filter.add_filter" }) ? intl.formatMessage({ id: "search_filter.add_filter" })
: intl.formatMessage({ id: "search_filter.update_filter" }); : intl.formatMessage({ id: "search_filter.update_filter" });
return ( return (
<> <>
<Modal show onHide={() => onCancel()}> <Modal show onHide={() => onCancel()} className="add-filter-dialog">
<Modal.Header>{title}</Modal.Header> <Modal.Header>{title}</Modal.Header>
<Modal.Body> <Modal.Body>
<div className="dialog-content"> <div className="dialog-content">
@@ -322,10 +338,7 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
</div> </div>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button <Button onClick={onAddFilter} disabled={!isValid()}>
onClick={onAddFilter}
disabled={criterion.criterionOption.type === "none"}
>
{title} {title}
</Button> </Button>
</Modal.Footer> </Modal.Footer>

View File

@@ -1,135 +1,61 @@
import React, { useRef } from "react"; import React from "react";
import { Form } from "react-bootstrap"; import { FormattedMessage } from "react-intl";
import { useIntl } from "react-intl";
import { CriterionModifier } from "../../../core/generated-graphql"; import { CriterionModifier } from "../../../core/generated-graphql";
import { INumberValue } from "../../../models/list-filter/types"; import { INumberValue } from "../../../models/list-filter/types";
import { Criterion } from "../../../models/list-filter/criteria/criterion"; import { Criterion } from "../../../models/list-filter/criteria/criterion";
import { import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
convertFromRatingFormat,
convertToRatingFormat,
defaultRatingSystemOptions,
} from "src/utils/rating";
import * as GQL from "src/core/generated-graphql";
import { IUIConfig } from "src/core/config";
interface IDurationFilterProps { interface IRatingFilterProps {
criterion: Criterion<INumberValue>; criterion: Criterion<INumberValue>;
onValueChanged: (value: INumberValue) => void; onValueChanged: (value: INumberValue) => void;
configuration: GQL.ConfigDataFragment | undefined;
} }
export const RatingFilter: React.FC<IDurationFilterProps> = ({ export const RatingFilter: React.FC<IRatingFilterProps> = ({
criterion, criterion,
onValueChanged, onValueChanged,
configuration,
}) => { }) => {
const intl = useIntl(); function getRatingSystem(field: "value" | "value2") {
const ratingSystem = const defaultValue = field === "value" ? 0 : undefined;
(configuration?.ui as IUIConfig)?.ratingSystemOptions ??
defaultRatingSystemOptions;
const valueStage = useRef<INumberValue>(criterion.value); return (
<div>
function onChanged( <RatingSystem
event: React.ChangeEvent<HTMLInputElement>, value={criterion.value[field]}
property: "value" | "value2" onSetRating={(value) => {
) { onValueChanged({
const value = parseInt(event.target.value, 10); ...criterion.value,
valueStage.current[property] = !Number.isNaN(value) [field]: value ?? defaultValue,
? convertFromRatingFormat(value, ratingSystem.type) });
: 0; }}
valueRequired
/>
</div>
);
} }
function onBlurInput() {
onValueChanged(valueStage.current);
}
let equalsControl: JSX.Element | null = null;
if ( if (
criterion.modifier === CriterionModifier.Equals || criterion.modifier === CriterionModifier.Equals ||
criterion.modifier === CriterionModifier.NotEquals criterion.modifier === CriterionModifier.NotEquals ||
) {
equalsControl = (
<Form.Group>
<Form.Control
className="btn-secondary"
type="number"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChanged(e, "value")
}
onBlur={onBlurInput}
defaultValue={
convertToRatingFormat(criterion.value?.value, ratingSystem) ?? ""
}
placeholder={intl.formatMessage({ id: "criterion.value" })}
/>
</Form.Group>
);
}
let lowerControl: JSX.Element | null = null;
if (
criterion.modifier === CriterionModifier.GreaterThan || criterion.modifier === CriterionModifier.GreaterThan ||
criterion.modifier === CriterionModifier.Between || criterion.modifier === CriterionModifier.LessThan
criterion.modifier === CriterionModifier.NotBetween
) { ) {
lowerControl = ( return getRatingSystem("value");
<Form.Group>
<Form.Control
className="btn-secondary"
type="number"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChanged(e, "value")
}
onBlur={onBlurInput}
defaultValue={
convertToRatingFormat(criterion.value?.value, ratingSystem) ?? ""
}
placeholder={intl.formatMessage({ id: "criterion.greater_than" })}
/>
</Form.Group>
);
} }
let upperControl: JSX.Element | null = null;
if ( if (
criterion.modifier === CriterionModifier.LessThan ||
criterion.modifier === CriterionModifier.Between || criterion.modifier === CriterionModifier.Between ||
criterion.modifier === CriterionModifier.NotBetween criterion.modifier === CriterionModifier.NotBetween
) { ) {
upperControl = ( return (
<Form.Group> <div className="rating-filter">
<Form.Control {getRatingSystem("value")}
className="btn-secondary" <span className="and-divider">
type="number" <FormattedMessage id="between_and" />
onChange={(e: React.ChangeEvent<HTMLInputElement>) => </span>
onChanged( {getRatingSystem("value2")}
e, </div>
criterion.modifier === CriterionModifier.LessThan
? "value"
: "value2"
)
}
onBlur={onBlurInput}
defaultValue={
convertToRatingFormat(
criterion.modifier === CriterionModifier.LessThan
? criterion.value?.value
: criterion.value?.value2,
ratingSystem
) ?? ""
}
placeholder={intl.formatMessage({ id: "criterion.less_than" })}
/>
</Form.Group>
); );
} }
return ( return <></>;
<>
{equalsControl}
{lowerControl}
{upperControl}
</>
);
}; };

View File

@@ -107,3 +107,12 @@ input[type="range"].zoom-slider {
} }
} }
} }
.add-filter-dialog .rating-stars {
font-size: 1.3em;
margin-left: 0.25em;
}
.rating-filter .and-divider {
margin-left: 0.5em;
}

View File

@@ -41,6 +41,10 @@
} }
} }
} }
.rating-number .form-control {
width: inherit;
}
} }
.alias { .alias {

View File

@@ -17,6 +17,7 @@ export interface IRatingStarsProps {
onSetRating?: (value?: number) => void; onSetRating?: (value?: number) => void;
disabled?: boolean; disabled?: boolean;
precision: RatingStarPrecision; precision: RatingStarPrecision;
valueRequired?: boolean;
} }
export const RatingStars: React.FC<IRatingStarsProps> = ( export const RatingStars: React.FC<IRatingStarsProps> = (
@@ -62,7 +63,15 @@ export const RatingStars: React.FC<IRatingStarsProps> = (
) { ) {
const f = newToggleFraction(); const f = newToggleFraction();
if (!f) { if (!f) {
newRating = undefined; if (props.valueRequired) {
if (fraction) {
newRating = stars + 1;
} else {
newRating = stars;
}
} else {
newRating = undefined;
}
} else if (fraction) { } else if (fraction) {
// we're toggling from an existing fraction so use the stars value // we're toggling from an existing fraction so use the stars value
newRating = stars + f; newRating = stars + f;
@@ -143,10 +152,17 @@ export const RatingStars: React.FC<IRatingStarsProps> = (
if (hoverRating) { if (hoverRating) {
if (hoverRating === stars && precision === 1) { if (hoverRating === stars && precision === 1) {
if (props.valueRequired) {
return { rating: r, fraction: 0 };
}
// unsetting // unsetting
return undefined; return undefined;
} }
if (hoverRating === stars + 1 && fraction && fraction === precision) { if (hoverRating === stars + 1 && fraction && fraction === precision) {
if (props.valueRequired) {
return { rating: r, fraction: 0 };
}
// unsetting // unsetting
return undefined; return undefined;
} }

View File

@@ -13,6 +13,7 @@ export interface IRatingSystemProps {
value?: number; value?: number;
onSetRating?: (value?: number) => void; onSetRating?: (value?: number) => void;
disabled?: boolean; disabled?: boolean;
valueRequired?: boolean;
} }
export const RatingSystem: React.FC<IRatingSystemProps> = ( export const RatingSystem: React.FC<IRatingSystemProps> = (
@@ -32,6 +33,7 @@ export const RatingSystem: React.FC<IRatingSystemProps> = (
precision={ precision={
ratingSystemOptions.starPrecision ?? defaultRatingStarPrecision ratingSystemOptions.starPrecision ?? defaultRatingStarPrecision
} }
valueRequired={props.valueRequired}
/> />
); );
} }

View File

@@ -127,6 +127,7 @@
"birth_year": "Birth Year", "birth_year": "Birth Year",
"birthdate": "Birthdate", "birthdate": "Birthdate",
"bitrate": "Bit Rate", "bitrate": "Bit Rate",
"between_and": "and",
"captions": "Captions", "captions": "Captions",
"career_length": "Career Length", "career_length": "Career Length",
"component_tagger": { "component_tagger": {

View File

@@ -342,7 +342,7 @@ export class NullNumberCriterionOption extends CriterionOption {
CriterionModifier.IsNull, CriterionModifier.IsNull,
CriterionModifier.NotNull, CriterionModifier.NotNull,
], ],
defaultModifier: CriterionModifier.GreaterThan, defaultModifier: CriterionModifier.Equals,
inputType: "number", inputType: "number",
}); });
} }

View File

@@ -42,11 +42,11 @@ export class RatingCriterion extends Criterion<INumberValue> {
this.modifier === CriterionModifier.Between || this.modifier === CriterionModifier.Between ||
this.modifier === CriterionModifier.NotBetween this.modifier === CriterionModifier.NotBetween
) { ) {
return `${convertToRatingFormat(value, this.ratingSystem)}, ${ return `${convertToRatingFormat(value, this.ratingSystem) ?? 0}, ${
convertToRatingFormat(value2, this.ratingSystem) ?? 0 convertToRatingFormat(value2, this.ratingSystem) ?? 0
}`; }`;
} else { } else {
return `${convertToRatingFormat(value, this.ratingSystem)}`; return `${convertToRatingFormat(value, this.ratingSystem) ?? 0}`;
} }
} }