Skip to content

Commit

Permalink
feat(category): display nested categories
Browse files Browse the repository at this point in the history
  • Loading branch information
anehx authored and czosel committed Aug 23, 2023
1 parent 9310c46 commit ab75a63
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 66 deletions.
13 changes: 5 additions & 8 deletions addon/components/category-nav.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
<nav
class="uk-background-muted uk-border-right"
{{did-insert (perform this.fetchCategories)}}
>
<nav class="uk-background-muted uk-border-right">
<div class="uk-padding-small uk-width-1 uk-text-bold uk-border-bottom">
<div class="uk-text-center" data-test-nav-title>
<span class="category-header">
Expand All @@ -14,21 +11,21 @@
@category={{
hash name=(t "alexandria.category-nav.all-files") color="#e3e3e3"
}}
@selected={{eq @selected undefined}}
@selected={{@selected}}
data-test-all-files
/>
{{#if this.fetchCategories.isRunning}}
{{#if this.categories.isLoading}}
{{#each (range 0 5) as |index|}}
<CategoryNav::Category::Skeleton
@animationDelay={{concat index "00ms"}}
data-test-skeleton-category
/>
{{/each}}
{{else}}
{{#each this.categories as |category|}}
{{#each this.categories.records as |category|}}
<CategoryNav::Category
@category={{category}}
@selected={{eq category.id @selected}}
@selected={{@selected}}
data-test-category
/>
{{/each}}
Expand Down
17 changes: 5 additions & 12 deletions addon/components/category-nav.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { lastValue, task } from "ember-concurrency";
import { query } from "ember-data-resources";

export default class CategoryNavComponent extends Component {
@service store;
@service notification;
@service intl;

@lastValue("fetchCategories") categories;
@task
*fetchCategories() {
try {
return yield this.store.findAll("category");
} catch {
this.notification.danger(
this.intl.t("alexandria.errors.fetch-categories"),
);
}
}
categories = query(this, "category", () => ({
"filter[hasParent]": false,
include: "children",
}));
}
40 changes: 31 additions & 9 deletions addon/components/category-nav/category.hbs
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
<li class="category-nav__category" ...attributes>
<div
class="uk-link-reset {{if @selected "active"}}"
class="uk-link-reset {{if this.isActive "active"}}"
tabindex="0"
data-test-link
{{on "click" this.loadCategory}}
>
<div class="uk-flex uk-flex-middle">
<div>
<FaIcon
@icon="folder"
@size="2x"
class="uk-margin-right"
data-test-icon
{{set-style color=@category.color}}
/>
<div class="uk-margin-right {{if @isSubcategory "uk-margin-left"}}">
{{#if this.isOpen}}
<FaIcon
@prefix="far"
@icon="folder-open"
@fixedWidth={{true}}
data-test-icon
{{set-style color=@category.color}}
/>
{{else}}
<FaIcon
@prefix="far"
@icon="folder"
@fixedWidth={{true}}
data-test-icon
{{set-style color=@category.color}}
/>
{{/if}}
</div>
<div
data-test-name
Expand All @@ -23,4 +33,16 @@
</div>
</div>
</div>
{{#if (and this.expandChildren @category.children.length)}}
<ul>
{{#each @category.children as |child|}}
<CategoryNav::Category
@category={{child}}
@selected={{@selected}}
@isSubcategory={{true}}
data-test-subcategory
/>
{{/each}}
</ul>
{{/if}}
</li>
28 changes: 28 additions & 0 deletions addon/components/category-nav/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,45 @@ import { getOwner } from "@ember/application";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";

export default class CategoryNavCategoryComponent extends Component {
@service documents;
@service router;

@tracked collapseChildren = false;

get isActive() {
if (!this.args.category.id) {
return this.args.selected === undefined;
}

return this.args.selected === this.args.category.id;
}

get isOpen() {
return (
this.args.category.children
?.map((category) => category.id)
.includes(this.args.selected) || this.isActive
);
}

get expandChildren() {
return this.isOpen && !this.collapseChildren;
}

get controllerInstance() {
const applicationInstance = getOwner(this);
return applicationInstance.lookup("controller:application");
}

@action loadCategory() {
if (this.isActive) {
this.collapseChildren = !this.collapseChildren;
return;
}

this.documents.clearDocumentSelection();
this.controllerInstance.resetTagFilter();
this.router.transitionTo(this.router.currentRouteName, {
Expand Down
1 change: 1 addition & 0 deletions addon/components/category-nav/category/skeleton.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="uk-flex uk-flex-middle">
<div>
<FaIcon
@prefix="far"
@icon="folder"
@size="2x"
class="uk-margin-right skeleton-icon"
Expand Down
26 changes: 24 additions & 2 deletions addon/components/document-upload-button.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
class="uk-height-1-1"
uk-form-custom
...attributes
{{did-insert (perform this.fetchCategories)}}
>
<UkButton
@color="primary"
Expand All @@ -25,7 +24,7 @@
/>
{{else}}
<Drop @width="uk-with-medium" as |Item|>
{{#each this.categories as |category|}}
{{#each this.categories.records as |category|}}
<Item uk-form-custom data-test-upload-category>
<input
type="file"
Expand All @@ -35,6 +34,7 @@
{{on "change" (perform this.upload category)}}
/>
<FaIcon
@prefix="far"
@icon="folder"
@size="2x"
class="uk-margin-small-right"
Expand All @@ -43,6 +43,28 @@
/>
{{category.name}}
</Item>
{{#if category.children.length}}
{{#each category.children as |child|}}
<Item class="item--indent" uk-form-custom data-test-upload-category>
<input
type="file"
multiple="multiple"
data-test-input
aria-label="file input"
{{on "change" (perform this.upload child)}}
/>
<FaIcon
@prefix="far"
@icon="folder"
@size="2x"
class="uk-margin-small-right"
data-test-folder-icon
{{set-style color=child.color}}
/>
{{child.name}}
</Item>
{{/each}}
{{/if}}
{{else}}
<span data-test-no-categories>
{{t "alexandria.document-upload-button.no-categories"}}
Expand Down
19 changes: 6 additions & 13 deletions addon/components/document-upload-button.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { task, lastValue } from "ember-concurrency";
import { task } from "ember-concurrency";
import { query } from "ember-data-resources";

export default class DocumentUploadButtonComponent extends Component {
@service notification;
@service intl;
@service store;
@service documents;

@lastValue("fetchCategories") categories;
categories = query(this, "category", () => ({
"filter[hasParent]": false,
include: "children",
}));

@task *upload(category, { target: { files = [] } = {} }) {
try {
Expand All @@ -32,15 +36,4 @@ export default class DocumentUploadButtonComponent extends Component {
);
}
}

@task *fetchCategories() {
try {
return yield this.store.peekAll("category") ||
this.store.findAll("category");
} catch {
this.notification.danger(
this.intl.t("alexandria.errors.fetch-categories"),
);
}
}
}
4 changes: 3 additions & 1 deletion addon/models/category.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { attr, hasMany } from "@ember-data/model";
import { attr, hasMany, belongsTo } from "@ember-data/model";
import { LocalizedModel, localizedAttr } from "ember-localized-model";

export default class CategoryModel extends LocalizedModel {
Expand All @@ -7,5 +7,7 @@ export default class CategoryModel extends LocalizedModel {
@attr color;
@attr metainfo;

@belongsTo("category", { inverse: "children", async: true }) parent;
@hasMany("category", { inverse: "parent", async: true }) children;
@hasMany("document", { inverse: "category", async: true }) documents;
}
4 changes: 4 additions & 0 deletions app/styles/components/_drop.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
background: $global-muted-background;
}

&--indent {
padding-left: 25px;
}

svg {
font-size: 1.2em;
margin-right: 10px;
Expand Down
8 changes: 6 additions & 2 deletions config/icons.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module.exports = function () {
return {
"free-solid-svg-icons": [
"folder",
"ellipsis-v",
"file-download",
"box-open",
Expand All @@ -13,6 +12,11 @@ module.exports = function () {
"chevron-right",
"chevron-left",
],
"free-regular-svg-icons": ["file-alt", "trash-alt"],
"free-regular-svg-icons": [
"folder",
"folder-open",
"file-alt",
"trash-alt",
],
};
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@
"ember-composable-helpers": "^5.0.0",
"ember-concurrency": "^3.1.0",
"ember-data": "^3.28.13 || ^4.12.3",
"ember-data-resources": "^5.0.0",
"ember-engines-router-service": "^0.3.0",
"ember-fetch": "^8.1.2",
"ember-intl": "^5.7.2",
"ember-localized-model": "^3.2.0",
"ember-modifier": "^4.1.0",
"ember-promise-helpers": "^2.0.0",
"ember-resources": "^6.4.0",
"ember-simple-auth-oidc": "^5.1.0",
"ember-truth-helpers": "^3.1.1",
"ember-uikit": "^8.0.0",
Expand Down
4 changes: 3 additions & 1 deletion tests/dummy/mirage/models/category.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Model, hasMany } from "miragejs";
import { Model, belongsTo, hasMany } from "miragejs";

export default Model.extend({
documents: hasMany(),
parent: belongsTo("category"),
children: hasMany("category"),
});
4 changes: 2 additions & 2 deletions tests/integration/components/category-nav/category-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ module("Integration | Component | category-nav/category", function (hooks) {
});

test("it renders an active category", async function (assert) {
this.category = { name: "test", color: "#f00" };
this.category = { id: "test", name: "test", color: "#f00" };
await render(
hbs`<CategoryNav::Category @category={{this.category}} @selected={{true}}/>`,
hbs`<CategoryNav::Category @category={{this.category}} @selected="test"/>`,
);

assert.dom("[data-test-name]").hasText("test");
Expand Down
Loading

0 comments on commit ab75a63

Please sign in to comment.