Skip to content

Filter navigation UI #31419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { hash } from "@ember/helper";
import { cached, tracked } from "@glimmer/tracking";
import { fn, hash } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { and } from "truth-helpers";
import BulkSelectToggle from "discourse/components/bulk-select-toggle";
import DButton from "discourse/components/d-button";
import Tags from "discourse/components/discovery/filter-navigation/tags";
import Form from "discourse/components/form";
import PluginOutlet from "discourse/components/plugin-outlet";
import bodyClass from "discourse/helpers/body-class";
import icon from "discourse/helpers/d-icon";
import discourseDebounce from "discourse/lib/debounce";
import { bind } from "discourse/lib/decorators";
import { resettableTracked } from "discourse/lib/tracked-tools";
import { i18n } from "discourse-i18n";
import and from "truth-helpers/helpers/and";

export default class DiscoveryFilterNavigation extends Component {
@service site;
Expand All @@ -21,11 +22,77 @@ export default class DiscoveryFilterNavigation extends Component {
@tracked copyClass = "btn-default";
@resettableTracked newQueryString = this.args.queryString;

availableSorts = [
"likes",
"op_likes",
"views",
"posts",
"activity",
"posters",
"category",
"created",
];

filterPlaceholder = i18n("form_templates.filter_placeholder");

@cached
get formData() {
return {
query: "",
tags: ["foo"],
};
}

@bind
updateQueryString(string) {
this.newQueryString = string;
}

@action
formatDate(set, moment) {
set(moment.format("YYYY-MM-DD"));
}

@action
updateFilter(data) {
let queryParts = [];

// if (data.categories?.length) {
// const categoryNames = data.categories
// .map((category) => {
// // need to account for category names with spaces
// return category.name.replace(/ /g, "-");
// })
// .join(",");
// queryParts.push(`categories:${categoryNames}`);
// }

if (data.tags?.length) {
const tagNames = data.tags.join(",");
queryParts.push(`tags:${tagNames}`);
}

// if (data.created_by?.length) {
// const createdByUsernames = data.created_by.join(",");
// queryParts.push(`created-by:${createdByUsernames}`);
// }

// if (data.created_before?.length) {
// queryParts.push(`created-before:${data.created_before}`);
// }

// if (data.created_after?.length) {
// queryParts.push(`created-after:${data.created_after}`);
// }

// if (data.order?.length) {
// queryParts.push(`order:${data.order}`);
// }

const queryString = queryParts.join(" ");
this.args.updateTopicsListQueryParams(queryString);
}

@action
clearInput() {
this.newQueryString = "";
Expand All @@ -42,6 +109,11 @@ export default class DiscoveryFilterNavigation extends Component {
discourseDebounce(this._restoreButton, 3000);
}

@action
toggleExpanded() {
this.filterExpanded = !this.filterExpanded;
}

@bind
_restoreButton() {
if (this.isDestroying || this.isDestroyed) {
Expand All @@ -63,15 +135,32 @@ export default class DiscoveryFilterNavigation extends Component {
{{/if}}

<div class="topic-query-filter__input">
{{icon "filter" class="topic-query-filter__icon"}}
<Input
class="topic-query-filter__filter-term"
@value={{this.newQueryString}}
@enter={{action @updateTopicsListQueryParams this.newQueryString}}
@type="text"
id="queryStringInput"
autocomplete="off"
/>
<Form
@onSubmit={{this.updateFilter}}
@data={{this.formData}}
as |form data|
>
<form.Field @name="query" @title="Query" as |field|>
<field.Input />
</form.Field>

<form.Field @name="tags" @title="Tags" @format="full" as |field|>
<field.Custom>
<Tags
@query={{data.query}}
@tags={{data.tags}}
@onChange={{field.set}}
/>
</field.Custom>

</form.Field>

<form.Actions>
<form.Submit @label="form_templates.filter" />
</form.Actions>
</Form>

{{!-- {{icon "filter" class="topic-query-filter__icon"}} --}}
{{! EXPERIMENTAL OUTLET - don't use because it will be removed soon }}
<PluginOutlet
@name="below-filter-input"
Expand All @@ -81,24 +170,6 @@ export default class DiscoveryFilterNavigation extends Component {
}}
/>
</div>
{{#if this.newQueryString}}
<div class="topic-query-filter__controls">
<DButton
@icon="xmark"
@action={{this.clearInput}}
@disabled={{unless this.newQueryString "true"}}
/>

{{#if this.discoveryFilter.q}}
<DButton
@icon={{this.copyIcon}}
@action={{this.copyQueryString}}
@disabled={{unless this.newQueryString "true"}}
class={{this.copyClass}}
/>
{{/if}}
</div>
{{/if}}
</div>
</section>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import AsyncContent from "discourse/components/async-content";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";

export default class DiscoveryFilterNavigationTags extends Component {
iteration = 1;

get query() {
return this.args.query || "";
}

@action
async loadTags() {
const data = { q: this.query || "" };
const request = await ajax("/tags/filter/search", { data });
const tagsMap = new Map();
request.results.forEach((result) => {
tagsMap.set(result.name, {
name: result.name,
selected: this.args.tags?.includes?.(result.name) || false,
});
});
this.args.tags?.forEach((tagName) => {
if (tagsMap.has(tagName)) {
tagsMap.get(tagName).selected = true;
} else {
tagsMap.set(tagName, {
name: tagName,
selected: true,
});
}
});
return Array.from(tagsMap.values()).sort((a, b) =>
a.name.localeCompare(b.name)
);
}

@action
toggleTag(tag) {
const selectedTags = [...(this.args.tags || [])];

if (tag.selected) {
const index = selectedTags.indexOf(tag.name);
if (index !== -1) {
selectedTags.splice(index, 1);
}
} else {
if (!selectedTags.includes(tag.name)) {
selectedTags.push(tag.name);
}
}

this.args.onChange?.(selectedTags);
}

<template>
<AsyncContent @asyncData={{this.loadTags}}>
<:loading></:loading>
<:content as |tags|>
<div class="filter-navigation__tags-list">
{{#each tags as |tag|}}
<DButton
@action={{fn this.toggleTag tag}}
class={{concatClass (if tag.selected "btn-primary")}}
>{{tag.name}}</DButton>
{{/each}}
</div>
</:content>
</AsyncContent>
</template>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@ import FKObject from "discourse/form-kit/components/fk/object";
import element from "discourse/helpers/element";

export default class FKCollection extends Component {
constructor() {
super(...arguments);

this.args.onRegisterApi?.({
remove: this.remove,
add: this.add,
});
}

@action
remove(index) {
this.args.remove(this.name, index);
}

@action
add(value) {
this.args.add(this.name, value);
}

get collectionData() {
return this.args.data.get(this.name);
}
Expand All @@ -27,8 +41,12 @@ export default class FKCollection extends Component {

<template>
{{#let (element this.tagName) as |Wrapper|}}
{{#each this.collectionData key="index" as |data index|}}
<Wrapper class="form-kit__collection">
<Wrapper
class="form-kit__collection"
@onRegisterApi={{@onRegisterApi}}
...attributes
>
{{#each this.collectionData key="index" as |data index|}}
{{yield
(hash
Field=(component
Expand Down Expand Up @@ -67,12 +85,14 @@ export default class FKCollection extends Component {
remove=@remove
)
remove=this.remove
add=this.add
)
index
(get this.collectionData index)
}}
</Wrapper>
{{/each}}

{{/each}}
</Wrapper>
{{/let}}
</template>
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class FKForm extends Component {
unregisterField: instance.unregisterField,
triggerRevalidationFor: instance.triggerRevalidationFor,
remove: instance.remove,
add: instance.addItemToCollection,
};

return curryComponent(klass, baseArguments, getOwner(this));
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/common/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@
@import "emoji-picker";
@import "filter-input";
@import "dropdown-menu";
@import "filter-navigation-form";
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.filter-navigation__form {
display: flex;
flex-direction: column;
}

.filter-navigation__tags-list {
display: flex !important;
gap: 0.5rem;
flex-wrap: wrap;
padding-block: 1rem;
width: 100%;

.form-kit__collection {
display: contents;

.form-kit__container-content {
width: auto;
}

.form-kit__field-custom {
min-width: auto;

.form-kit__control-custom {
width: auto;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
width: 100%;
}

.form-kit__control-custom > * {
.form-kit__control-custom > div {
width: 100% !important;
}
}
2 changes: 2 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5099,6 +5099,8 @@ en:
other: "The input must be more than %{count}."
pattern_mismatch: "Please match the requested format."
bad_input: "Please enter a valid input."
filter: "Filter"
filter_placeholder: "Filter topics..."

table_builder:
title: "Table Builder"
Expand Down
Loading