Skip to content

Commit

Permalink
Add and,or and not conditions to the Compendium Browser trait f…
Browse files Browse the repository at this point in the history
…ilter (foundryvtt#6436)
  • Loading branch information
In3luki authored Feb 18, 2023
1 parent 8170818 commit a39014d
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 38 deletions.
8 changes: 6 additions & 2 deletions src/module/actor/character/sheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,9 @@ class CharacterSheetPF2e extends CreatureSheetPF2e<CharacterPF2e> {
if (checkboxesFilterCodes.includes("feattype-class")) checkboxesFilterCodes.push("feattype-archetype");

const feattype: string[] = [];
const traits: string[] = [];
const traits: { values: string[]; conjunction?: "and" | "or" } = {
values: [],
};
for (const filterCode of checkboxesFilterCodes) {
const [filterType, value] = filterCode.split("-");
if (!(filterType && value)) {
Expand All @@ -783,7 +785,9 @@ class CharacterSheetPF2e extends CreatureSheetPF2e<CharacterPF2e> {
if (filterType === "feattype") {
feattype.push(value);
} else if (filterType === "traits") {
traits.push(value);
traits.values.push(value);
} else if (filterType === "conjunction" && (value === "and" || value === "or")) {
traits.conjunction = value;
}
}

Expand Down
46 changes: 40 additions & 6 deletions src/module/apps/compendium-browser/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Progress } from "./progress";
import { PhysicalItemPF2e } from "@item/physical";
import { KitPF2e } from "@item/kit";
import { ErrorPF2e, isObject, objectHasKey } from "@util";
import { ErrorPF2e, htmlQueryAll, isObject, objectHasKey } from "@util";
import { LocalizePF2e } from "@system/localize";
import * as browserTabs from "./tabs";
import { TabData, PackInfo, TabName, BrowserTab, SortDirection } from "./data";
Expand Down Expand Up @@ -364,13 +364,13 @@ class CompendiumBrowser extends Application {
} else if (
currentTab.filterData.multiselects &&
objectHasKey(currentTab.filterData.multiselects, mappedFilterType) &&
Array.isArray(filterValue)
Array.isArray(filterValue.values)
) {
// Multiselects
// A convoluted cast is necessary here to not get an infered type of MultiSelectData<PhysicalItem> since MultiSelectData is not exported
const multiselects = (currentTab.filterData.multiselects as BaseFilterData["multiselects"])!;
const multiselect = multiselects[mappedFilterType];
for (const value of filterValue) {
for (const value of filterValue.values) {
const option = multiselect.options.find((opt) => opt.value === value);
if (option) {
multiselect.selected.push(option);
Expand All @@ -380,6 +380,9 @@ class CompendiumBrowser extends Application {
);
}
}
if (filterValue.conjunction === "and" || filterValue.conjunction === "or") {
multiselect.conjunction = filterValue.conjunction;
}
} else if (
currentTab.filterData.ranges &&
objectHasKey(currentTab.filterData.ranges, mappedFilterType) &&
Expand Down Expand Up @@ -654,7 +657,7 @@ class CompendiumBrowser extends Application {
if (!multiselect) continue;
const data = multiselects[filterName];

new Tagify(multiselect, {
const tagify = new Tagify(multiselect, {
enforceWhitelist: true,
keepInvalidTags: false,
editTags: false,
Expand All @@ -667,10 +670,28 @@ class CompendiumBrowser extends Application {
searchKeys: ["label"],
},
whitelist: data.options,
transformTag(tagData) {
const selected = data.selected.find((s) => s.value === tagData.value);
if (selected?.not) {
(tagData as unknown as { class: string }).class = "conjunction-not";
}
},
});

multiselect.addEventListener("change", () => {
const selections: unknown = JSON.parse(multiselect.value || "[]");
tagify.on("click", (event) => {
const target = event.detail.event.target as HTMLElement;
if (!target) return;

const value = event.detail.data.value;
const selected = data.selected.find((s) => s.value === value);
if (selected) {
const current = !!selected.not;
selected.not = !current;
this.render();
}
});
tagify.on("change", (event) => {
const selections: unknown = JSON.parse(event.detail.value || "[]");
const isValid =
Array.isArray(selections) &&
selections.every(
Expand All @@ -683,6 +704,19 @@ class CompendiumBrowser extends Application {
this.render();
}
});

for (const element of htmlQueryAll<HTMLInputElement>(
container,
`input[name=${filterName}-filter-conjunction]`
)) {
element.addEventListener("change", () => {
const value = element.value;
if (value === "and" || value === "or") {
data.conjunction = value;
this.render();
}
});
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/module/apps/compendium-browser/tabs/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,8 @@ export class CompendiumBrowserActionTab extends CompendiumBrowserTab {
const { checkboxes, multiselects } = this.filterData;

// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}
// Source
if (checkboxes.source.selected.length) {
if (!checkboxes.source.selected.includes(entry.source)) return false;
Expand All @@ -98,6 +96,7 @@ export class CompendiumBrowserActionTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
22 changes: 21 additions & 1 deletion src/module/apps/compendium-browser/tabs/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CompendiumBrowser } from "..";
import { BaseFilterData, CheckboxOptions, CompendiumBrowserIndexData, RangesData } from "./data";
import { BaseFilterData, CheckboxOptions, CompendiumBrowserIndexData, MultiselectData, RangesData } from "./data";
import { ErrorPF2e, sluggify } from "@util";
import { TabName } from "../data";
import MiniSearch from "minisearch";
Expand Down Expand Up @@ -90,6 +90,26 @@ export abstract class CompendiumBrowserTab {
return true;
}

protected filterTraits(
traits: string[],
selected: MultiselectData["selected"],
condition: MultiselectData["conjunction"]
): boolean {
const selectedTraits = selected.filter((s) => !s.not).map((s) => s.value);
const notTraits = selected.filter((t) => t.not).map((s) => s.value);
if (selectedTraits.length || notTraits.length) {
if (notTraits.some((t) => traits.includes(t))) {
return false;
}
const fullfilled =
condition === "and"
? selectedTraits.every((t) => traits.includes(t))
: selectedTraits.some((t) => traits.includes(t));
if (!fullfilled) return false;
}
return true;
}

async renderResults(start: number): Promise<HTMLLIElement[]> {
if (!this.templatePath) {
throw ErrorPF2e(`Tab "${this.tabName}" has no valid template path.`);
Expand Down
5 changes: 2 additions & 3 deletions src/module/apps/compendium-browser/tabs/bestiary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,8 @@ export class CompendiumBrowserBestiaryTab extends CompendiumBrowserTab {
if (!checkboxes.alignments.selected.includes(entry.alignment)) return false;
}
// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}
// Source
if (checkboxes.source.selected.length) {
if (!checkboxes.source.selected.includes(entry.source)) return false;
Expand Down Expand Up @@ -153,6 +151,7 @@ export class CompendiumBrowserBestiaryTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
12 changes: 6 additions & 6 deletions src/module/apps/compendium-browser/tabs/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ interface CheckboxData {

interface MultiselectData<T extends string = string> {
label: string;
conjunction: "and" | "or";
options: { label: string; value: T }[];
selected: { label: string; value: T }[];
selected: { label: string; not?: boolean; value: T }[];
}

interface SelectData {
Expand Down Expand Up @@ -116,12 +117,15 @@ interface RenderResultListOptions {
// Models used for initializing filters
interface BaseInitialFilters {
searchText?: string;
traits?: {
values: string[];
conjunction?: MultiselectData["conjunction"];
};
orderBy?: SortByOption;
orderDirection?: SortDirection;
}

interface InitialActionFilters extends BaseInitialFilters {
traits?: string[];
source?: string[];
orderBy?: CommonSortByOption;
}
Expand All @@ -131,7 +135,6 @@ interface InitialBestiaryFilters extends BaseInitialFilters {
rarity?: string[];
sizes?: string[];
source?: string[];
traits?: string[];
orderBy?: CommonSortByOption;
}

Expand All @@ -153,7 +156,6 @@ interface InitialFeatFilters extends BaseInitialFilters {
skills?: string[];
rarity?: string[];
source?: string[];
traits?: string[];
level?: { min?: number; max?: number };
orderBy?: CommonSortByOption;
}
Expand All @@ -162,7 +164,6 @@ interface InitialHazardFilters extends BaseInitialFilters {
complexity?: string[];
rarity?: string[];
source?: string[];
traits?: string[];
levelRange?: { min?: number; max?: number };
orderBy?: CommonSortByOption;
}
Expand All @@ -176,7 +177,6 @@ interface InitialSpellFilters extends BaseInitialFilters {
school?: string[];
source?: string[];
traditions?: string[];
traits?: string[];
orderBy?: CommonSortByOption;
}

Expand Down
8 changes: 2 additions & 6 deletions src/module/apps/compendium-browser/tabs/equipment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,21 +167,16 @@ export class CompendiumBrowserEquipmentTab extends CompendiumBrowserTab {
) {
return false;
}

// Weapon categories
if (
checkboxes.weaponTypes.selected.length > 0 &&
!this.arrayIncludes(checkboxes.weaponTypes.selected, [entry.category, entry.group])
) {
return false;
}

// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}

// Source
if (checkboxes.source.selected.length > 0 && !checkboxes.source.selected.includes(entry.source)) {
return false;
Expand Down Expand Up @@ -248,6 +243,7 @@ export class CompendiumBrowserEquipmentTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
6 changes: 2 additions & 4 deletions src/module/apps/compendium-browser/tabs/feat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,8 @@ export class CompendiumBrowserFeatTab extends CompendiumBrowserTab {
if (!this.arrayIncludes(checkboxes.skills.selected, entry.skills)) return false;
}
// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}

// Source
if (checkboxes.source.selected.length) {
if (!checkboxes.source.selected.includes(entry.source)) return false;
Expand Down Expand Up @@ -173,6 +170,7 @@ export class CompendiumBrowserFeatTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
5 changes: 2 additions & 3 deletions src/module/apps/compendium-browser/tabs/hazard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,8 @@ export class CompendiumBrowserHazardTab extends CompendiumBrowserTab {
if (!checkboxes.complexity.selected.includes(entry.complexity)) return false;
}
// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}
// Source
if (checkboxes.source.selected.length) {
if (!checkboxes.source.selected.includes(entry.source)) return false;
Expand Down Expand Up @@ -132,6 +130,7 @@ export class CompendiumBrowserHazardTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
5 changes: 2 additions & 3 deletions src/module/apps/compendium-browser/tabs/spell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,8 @@ export class CompendiumBrowserSpellTab extends CompendiumBrowserTab {
if (!this.arrayIncludes(checkboxes.traditions.selected, entry.traditions)) return false;
}
// Traits
const selectedTraits = multiselects.traits.selected.map((s) => s.value);
if (selectedTraits.length > 0 && !selectedTraits.every((t) => entry.traits.includes(t))) {
if (!this.filterTraits(entry.traits, multiselects.traits.selected, multiselects.traits.conjunction))
return false;
}
// School
if (checkboxes.school.selected.length) {
if (!checkboxes.school.selected.includes(entry.school)) return false;
Expand Down Expand Up @@ -225,6 +223,7 @@ export class CompendiumBrowserSpellTab extends CompendiumBrowserTab {
},
multiselects: {
traits: {
conjunction: "and",
label: "PF2E.BrowserFilterTraits",
options: [],
selected: [],
Expand Down
7 changes: 7 additions & 0 deletions src/styles/_tags.scss
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ tags.tags.paizo-style {
}
}

.conjunction-not {
.tagify__tag-text {
text-decoration: line-through;
text-decoration-color: var(--color-text-trait);
}
}

// In lieu of remove button
tag[readonly=true] {
padding-right: 2px;
Expand Down
6 changes: 6 additions & 0 deletions src/styles/packs/_tab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
dd {
width: 82%;
}

.filter-conjunction {
input[type="radio"] {
margin: 0 5px 0 3px;
}
}
}

.rangecontainer {
Expand Down
4 changes: 4 additions & 0 deletions static/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,10 @@
"BrowserFilterCategory": "Feat Types",
"BrowserFilterClass": "Classes",
"BrowserFilterComplexity": "Hazard Complexities",
"BrowserFilterConjunction": {
"AndLabel": "All of above",
"OrLabel": "Any of above"
},
"BrowserFilterConsumable": "Consumable Type",
"BrowserFilterInventoryTypes": "Inventory Types",
"BrowserFilterLevels": "Levels",
Expand Down
7 changes: 6 additions & 1 deletion static/templates/actors/character/tabs/feats.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
<div class="feat-section" data-category-id="{{section.id}}">
<h3 class="header">
{{localize section.label}}
<button type="button" class="feat-browse" data-type="feat" data-filter="feattype-{{section.id}},{{section.featFilter}}">
<button
type="button"
class="feat-browse"
data-type="feat"
data-filter="feattype-{{section.id}},{{section.featFilter}}{{#if (eq section.id "class")}},conjunction-or{{/if}}"
>
<i class="fas fa-fw fa-search"></i>{{localize "PF2E.BrowseLabel"}}
</button>
</h3>
Expand Down
Loading

0 comments on commit a39014d

Please sign in to comment.