DEV: Add subsection capabilities to the sidebar API

This commit is contained in:
Sérgio Saquetim 2024-08-12 23:28:22 -03:00
parent 86bb07f619
commit 97bc09b4d1
No known key found for this signature in database
GPG Key ID: B4E3D7F11E793062
5 changed files with 119 additions and 57 deletions

View File

@ -18,43 +18,53 @@ const SidebarApiSection = <template>
@activeLink={{@section.activeLink}}
@expandWhenActive={{@expandWhenActive}}
@scrollActiveLinkIntoView={{@scrollActiveLinkIntoView}}
@level={{@section.level}}
>
{{#each @section.filteredLinks key="name" as |link|}}
{{#each @section.filteredLinks key="name" as |item|}}
{{#if item.links}}
<SidebarApiSection
@section={{item}}
@collapsable={{@collapsable}}
@expandWhenActive={{@expandActiveSection}}
@scrollActiveLinkIntoView={{@scrollActiveLinkIntoView}}
/>
{{else}}
<SectionLink
@linkName={{link.name}}
@linkClass={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@query={{link.query}}
@models={{link.models}}
@currentWhen={{link.currentWhen}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@prefixColor={{link.prefixColor}}
@prefixBadge={{link.prefixBadge}}
@prefixType={{link.prefixType}}
@prefixValue={{link.prefixValue}}
@prefixCSSClass={{link.prefixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@hoverType={{link.hoverType}}
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@linkName={{item.name}}
@linkClass={{item.classNames}}
@route={{item.route}}
@model={{item.model}}
@query={{item.query}}
@models={{item.models}}
@currentWhen={{item.currentWhen}}
@href={{item.href}}
@title={{item.title}}
@contentCSSClass={{item.contentCSSClass}}
@prefixColor={{item.prefixColor}}
@prefixBadge={{item.prefixBadge}}
@prefixType={{item.prefixType}}
@prefixValue={{item.prefixValue}}
@prefixCSSClass={{item.prefixCSSClass}}
@suffixType={{item.suffixType}}
@suffixValue={{item.suffixValue}}
@suffixCSSClass={{item.suffixCSSClass}}
@hoverType={{item.hoverType}}
@hoverValue={{item.hoverValue}}
@hoverAction={{item.hoverAction}}
@hoverTitle={{item.hoverTitle}}
@didInsert={{item.didInsert}}
@willDestroy={{item.willDestroy}}
@content={{item.text}}
@contentComponent={{component
link.contentComponent
status=link.contentComponentArgs
item.contentComponent
status=item.contentComponentArgs
}}
@scrollIntoView={{and
@scrollActiveLinkIntoView
(eq link.name @section.activeLink.name)
(eq item.name @section.activeLink.name)
}}
/>
{{/if}}
{{/each}}
</Section>
{{/if}}

View File

@ -2,6 +2,8 @@ import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { getOwner, setOwner } from "@ember/owner";
import { service } from "@ember/service";
import BaseCustomSidebarSection from "../../lib/sidebar/base-custom-sidebar-section";
import BaseCustomSidebarSectionLink from "../../lib/sidebar/base-custom-sidebar-section-link";
import ApiSection from "./api-section";
import PanelHeader from "./panel-header";
@ -20,20 +22,13 @@ export default class SidebarApiSections extends Component {
sectionConfigs = this.sidebarState.currentPanel.sections;
}
return sectionConfigs.map((Section) => {
const SidebarSection = prepareSidebarSectionClass(Section, this.router);
const sectionInstance = new SidebarSection({
filterable:
!this.sidebarState.combinedMode &&
this.sidebarState.currentPanel.filterable,
return sectionConfigs.map((SectionClass) =>
initializeSection(SectionClass, {
routerService: this.router,
sidebarState: this.sidebarState,
});
setOwner(sectionInstance, getOwner(this));
return sectionInstance;
});
owner: this,
})
);
}
get filteredSections() {
@ -54,15 +49,52 @@ export default class SidebarApiSections extends Component {
</template>
}
function initializeSection(SectionClass, opts) {
const { sidebarState, owner } = opts;
const SidebarSection = prepareSidebarSectionClass(SectionClass, opts);
const sectionInstance = new SidebarSection({
filterable:
!sidebarState.combinedMode && sidebarState.currentPanel.filterable,
sidebarState,
});
setOwner(sectionInstance, getOwner(owner));
return sectionInstance;
}
// extends the class provided for the section to add functionality we don't want to be overridable when defining custom
// sections using the plugin API, like for example the filtering capabilities
function prepareSidebarSectionClass(Section, routerService) {
return class extends Section {
function prepareSidebarSectionClass(SectionClass, opts) {
const { routerService, level = 0 } = opts;
return class extends SectionClass {
#level;
constructor({ filterable, sidebarState }) {
super();
this.filterable = filterable;
this.sidebarState = sidebarState;
this.#level = level;
}
get level() {
return this.#level;
}
@cached
get links() {
return super.links.map((item) => {
return item instanceof BaseCustomSidebarSectionLink
? item
: initializeSection(item, {
...opts,
level: level + 1,
});
});
}
@cached
@ -75,13 +107,19 @@ function prepareSidebarSectionClass(Section, routerService) {
return this.links;
}
return this.links.filter((link) => {
return this.links.filter((item) => {
// subsection
if (item instanceof BaseCustomSidebarSection) {
return item.filteredLinks.length > 0;
}
// standard link
return (
link.text
item.text
.toString()
.toLowerCase()
.match(this.sidebarState.sanitizedFilter) ||
link.keywords.navigation.some((keyword) =>
item.keywords.navigation.some((keyword) =>
keyword.match(this.sidebarState.sanitizedFilter)
)
);

View File

@ -1,11 +1,13 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { concat, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import { isEmpty } from "@ember/utils";
import { gt } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import element from "discourse/helpers/element";
import {
getCollapsedSidebarSectionKey,
getSidebarSectionContentId,
@ -41,6 +43,11 @@ export default class SidebarSection extends Component {
this.args.willDestroy?.();
}
get dynamicElement() {
const tagName = this.args.level > 0 ? "li" : "div";
return element(tagName);
}
get isCollapsed() {
if (!this.args.collapsable) {
return false;
@ -158,17 +165,19 @@ export default class SidebarSection extends Component {
<template>
{{#if this.displaySection}}
<div
<this.dynamicElement
{{didInsert this.setExpandedState}}
data-section-name={{@sectionName}}
class={{concatClass
"sidebar-section"
"sidebar-section-wrapper"
(if (gt @level 0) "sidebar-subsection")
(if
this.displaySectionContent
"sidebar-section--expanded"
"sidebar-section--collapsed"
)
(concat "sidebar-section--level-" @level)
}}
...attributes
>
@ -241,7 +250,7 @@ export default class SidebarSection extends Component {
{{yield}}
</ul>
{{/if}}
</div>
</this.dynamicElement>
{{/if}}
</template>
}

View File

@ -10,6 +10,7 @@ const shortcuts = {
a: <template><a ...attributes>{{yield}}</a></template>,
button: <template><button ...attributes>{{yield}}</button></template>,
td: <template><td ...attributes>{{yield}}</td></template>,
li: <template><li ...attributes>{{yield}}</li></template>,
};
/**

View File

@ -160,3 +160,7 @@
padding-right: 0.25rem;
}
}
.sidebar-subsection {
padding-left: 0.5em;
}