Refactor ItemList code and re-enable viewing sub-tag/studio content (#5080)

* Refactor list filter to use contexts
* Refactor FilteredListToolbar
* Move components into separate files
* Convert ItemList hook into components
* Fix criteria clone functions
* Add toggle for sub-studio content
* Add toggle for sub-tag content
* Make LoadingIndicator height smaller and fade in.
This commit is contained in:
WithoutPants
2024-07-31 16:35:37 +10:00
committed by GitHub
parent 540d72bc44
commit 6a5dc4e774
42 changed files with 1644 additions and 929 deletions

View File

@@ -89,6 +89,15 @@ export abstract class Criterion<V extends CriterionValue> {
this.value = value;
}
public clone(): Criterion<V> {
const newCriterion = new (this.constructor as new (
type: CriterionOption,
value: V
) => Criterion<V>)(this.criterionOption, this.value);
newCriterion.modifier = this.modifier;
return newCriterion;
}
public static getModifierLabel(intl: IntlShape, modifier: CriterionModifier) {
const modifierMessageID = modifierMessageIDs[modifier];
@@ -251,6 +260,19 @@ export class ILabeledIdCriterionOption extends CriterionOption {
}
export class ILabeledIdCriterion extends Criterion<ILabeledId[]> {
constructor(type: CriterionOption, value: ILabeledId[] = []) {
super(type, value);
}
public clone(): Criterion<ILabeledId[]> {
const newCriterion = new ILabeledIdCriterion(
this.criterionOption,
this.value.map((v) => ({ ...v }))
);
newCriterion.modifier = this.modifier;
return newCriterion;
}
protected getLabelValue(_intl: IntlShape): string {
return this.value.map((v) => v.label).join(", ");
}
@@ -272,23 +294,33 @@ export class ILabeledIdCriterion extends Criterion<ILabeledId[]> {
return this.value.length > 0;
}
constructor(type: CriterionOption) {
super(type, []);
}
}
export class IHierarchicalLabeledIdCriterion extends Criterion<IHierarchicalLabelValue> {
constructor(type: CriterionOption) {
const value: IHierarchicalLabelValue = {
constructor(
type: CriterionOption,
value: IHierarchicalLabelValue = {
items: [],
excluded: [],
depth: 0,
};
}
) {
super(type, value);
}
public clone(): Criterion<IHierarchicalLabelValue> {
const newCriterion = new IHierarchicalLabeledIdCriterion(
this.criterionOption,
{
...this.value,
items: this.value.items.map((v) => ({ ...v })),
excluded: this.value.excluded.map((v) => ({ ...v })),
}
);
newCriterion.modifier = this.modifier;
return newCriterion;
}
override get modifier(): CriterionModifier {
return this._modifier;
}
@@ -501,8 +533,17 @@ export class StringCriterion extends Criterion<string> {
}
export class MultiStringCriterion extends Criterion<string[]> {
constructor(type: CriterionOption) {
super(type, []);
constructor(type: CriterionOption, value: string[] = []) {
super(type, value);
}
public clone(): Criterion<string[]> {
const newCriterion = new MultiStringCriterion(
this.criterionOption,
this.value.slice()
);
newCriterion.modifier = this.modifier;
return newCriterion;
}
protected getLabelValue(_intl: IntlShape) {

View File

@@ -67,26 +67,48 @@ export class ListFilterModel {
public constructor(
mode: FilterMode,
config?: ConfigDataFragment,
defaultZoomIndex?: number
options?: {
defaultZoomIndex?: number;
defaultSortBy?: string;
defaultSortDir?: SortDirectionEnum;
}
) {
this.mode = mode;
this.config = config;
this.options = getFilterOptions(mode);
const { defaultSortBy, displayModeOptions } = this.options;
this.sortBy = defaultSortBy;
if (this.sortBy === "date") {
this.sortDirection = SortDirectionEnum.Desc;
if (options?.defaultSortBy) {
this.sortBy = options.defaultSortBy;
if (options.defaultSortDir) {
this.sortDirection = options.defaultSortDir;
}
} else {
this.sortBy = defaultSortBy;
if (this.sortBy === "date") {
this.sortDirection = SortDirectionEnum.Desc;
}
}
this.displayMode = displayModeOptions[0];
if (defaultZoomIndex !== undefined) {
this.defaultZoomIndex = defaultZoomIndex;
this.zoomIndex = defaultZoomIndex;
if (options?.defaultZoomIndex !== undefined) {
this.defaultZoomIndex = options.defaultZoomIndex;
this.zoomIndex = options.defaultZoomIndex;
}
}
public clone() {
return Object.assign(new ListFilterModel(this.mode, this.config), this);
const ret = Object.assign(
new ListFilterModel(this.mode, this.config),
this
);
ret.criteria = this.criteria.map((c) => c.clone());
return ret;
}
public empty() {
return new ListFilterModel(this.mode, this.config, {
defaultZoomIndex: this.defaultZoomIndex,
});
}
// returns the number of filters applied
@@ -443,4 +465,44 @@ export class ListFilterModel {
zoom_index: this.zoomIndex,
};
}
public clearCriteria() {
const ret = this.clone();
ret.criteria = [];
ret.currentPage = 1;
return ret;
}
public removeCriterion(type: CriterionType) {
const ret = this.clone();
const c = ret.criteria.find((cc) => cc.criterionOption.type === type);
if (!c) return ret;
const newCriteria = ret.criteria.filter((cc) => {
return cc.getId() !== c.getId();
});
ret.criteria = newCriteria;
ret.currentPage = 1;
return ret;
}
public changePage(page: number) {
const ret = this.clone();
ret.currentPage = page;
return ret;
}
public setZoom(zoomIndex: number) {
const ret = this.clone();
ret.zoomIndex = zoomIndex;
return ret;
}
public setDisplayMode(displayMode: DisplayMode) {
const ret = this.clone();
ret.displayMode = displayMode;
return ret;
}
}