mirror of
https://github.com/discourse/discourse.git
synced 2025-06-06 03:06:53 +08:00
DEV: Convert the entire sidebar to gjs (#26978)
This commit is contained in:
@ -0,0 +1,47 @@
|
|||||||
|
import Category from "discourse/models/category";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import AllCategoriesSectionLink from "../common/all-categories-section-link";
|
||||||
|
import SidebarCommonCategoriesSection from "../common/categories-section";
|
||||||
|
import Section from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
|
export default class SidebarAnonymousCategoriesSection extends SidebarCommonCategoriesSection {
|
||||||
|
shouldSortCategoriesByDefault =
|
||||||
|
!!this.siteSettings.default_navigation_menu_categories;
|
||||||
|
|
||||||
|
get categories() {
|
||||||
|
if (this.siteSettings.default_navigation_menu_categories) {
|
||||||
|
return Category.findByIds(
|
||||||
|
this.siteSettings.default_navigation_menu_categories
|
||||||
|
.split("|")
|
||||||
|
.map((categoryId) => parseInt(categoryId, 10))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.topSiteCategories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Section
|
||||||
|
@sectionName="categories"
|
||||||
|
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
>
|
||||||
|
{{#each this.sectionLinks as |sectionLink|}}
|
||||||
|
<SectionLink
|
||||||
|
@route={{sectionLink.route}}
|
||||||
|
@title={{sectionLink.title}}
|
||||||
|
@content={{sectionLink.text}}
|
||||||
|
@currentWhen={{sectionLink.currentWhen}}
|
||||||
|
@model={{sectionLink.model}}
|
||||||
|
@prefixType={{sectionLink.prefixType}}
|
||||||
|
@prefixValue={{sectionLink.prefixValue}}
|
||||||
|
@prefixColor={{sectionLink.prefixColor}}
|
||||||
|
data-category-id={{sectionLink.category.id}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<AllCategoriesSectionLink />
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
<Sidebar::Section
|
|
||||||
@sectionName="categories"
|
|
||||||
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
>
|
|
||||||
|
|
||||||
{{#each this.sectionLinks as |sectionLink|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@route={{sectionLink.route}}
|
|
||||||
@title={{sectionLink.title}}
|
|
||||||
@content={{sectionLink.text}}
|
|
||||||
@currentWhen={{sectionLink.currentWhen}}
|
|
||||||
@model={{sectionLink.model}}
|
|
||||||
@prefixType={{sectionLink.prefixType}}
|
|
||||||
@prefixValue={{sectionLink.prefixValue}}
|
|
||||||
@prefixColor={{sectionLink.prefixColor}}
|
|
||||||
data-category-id={{sectionLink.category.id}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<Sidebar::Common::AllCategoriesSectionLink />
|
|
||||||
</Sidebar::Section>
|
|
@ -1,24 +0,0 @@
|
|||||||
import SidebarCommonCategoriesSection from "discourse/components/sidebar/common/categories-section";
|
|
||||||
import Category from "discourse/models/category";
|
|
||||||
|
|
||||||
export default class SidebarAnonymousCategoriesSection extends SidebarCommonCategoriesSection {
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
|
|
||||||
if (!this.siteSettings.default_navigation_menu_categories) {
|
|
||||||
this.shouldSortCategoriesByDefault = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get categories() {
|
|
||||||
if (this.siteSettings.default_navigation_menu_categories) {
|
|
||||||
return Category.findByIds(
|
|
||||||
this.siteSettings.default_navigation_menu_categories
|
|
||||||
.split("|")
|
|
||||||
.map((categoryId) => parseInt(categoryId, 10))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return this.topSiteCategories;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import SidebarCustomSection from "discourse/components/sidebar/common/custom-sections";
|
import SidebarCustomSection from "../common/custom-sections";
|
||||||
|
|
||||||
export default class SidebarAnonymousCustomSections extends SidebarCustomSection {
|
export default class SidebarAnonymousCustomSections extends SidebarCustomSection {
|
||||||
anonymous = true;
|
anonymous = true;
|
@ -0,0 +1,20 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import CategoriesSection from "./categories-section";
|
||||||
|
import CustomSections from "./custom-sections";
|
||||||
|
import TagsSection from "./tags-section";
|
||||||
|
|
||||||
|
export default class SidebarAnonymousSections extends Component {
|
||||||
|
@service siteSettings;
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-sections sidebar-sections-anonymous">
|
||||||
|
<CustomSections @collapsable={{@collapsableSections}} />
|
||||||
|
<CategoriesSection @collapsable={{@collapsableSections}} />
|
||||||
|
|
||||||
|
{{#if this.siteSettings.tagging_enabled}}
|
||||||
|
<TagsSection @collapsable={{@collapsableSections}} />
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
<div class="sidebar-sections sidebar-sections-anonymous">
|
|
||||||
<Sidebar::Anonymous::CustomSections @collapsable={{@collapsableSections}} />
|
|
||||||
<Sidebar::Anonymous::CategoriesSection
|
|
||||||
@collapsable={{@collapsableSections}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if this.siteSettings.tagging_enabled}}
|
|
||||||
<Sidebar::Anonymous::TagsSection @collapsable={{@collapsableSections}} />
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
@ -1,6 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class SidebarAnonymousSections extends Component {
|
|
||||||
@service siteSettings;
|
|
||||||
}
|
|
@ -0,0 +1,61 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { cached } from "@glimmer/tracking";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import AllTagsSectionLink from "../common/all-tags-section-link";
|
||||||
|
import Section from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
|
export default class SidebarAnonymousTagsSection extends Component {
|
||||||
|
@service router;
|
||||||
|
@service topicTrackingState;
|
||||||
|
@service site;
|
||||||
|
|
||||||
|
get displaySection() {
|
||||||
|
return (
|
||||||
|
this.site.anonymous_default_navigation_menu_tags?.length > 0 ||
|
||||||
|
this.site.navigation_menu_site_top_tags?.length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached
|
||||||
|
get sectionLinks() {
|
||||||
|
return (
|
||||||
|
this.site.anonymous_default_navigation_menu_tags ||
|
||||||
|
this.site.navigation_menu_site_top_tags
|
||||||
|
).map((tag) => {
|
||||||
|
return new TagSectionLink({
|
||||||
|
tag,
|
||||||
|
topicTrackingState: this.topicTrackingState,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.displaySection}}
|
||||||
|
<Section
|
||||||
|
@sectionName="tags"
|
||||||
|
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
>
|
||||||
|
|
||||||
|
{{#each this.sectionLinks as |sectionLink|}}
|
||||||
|
<SectionLink
|
||||||
|
@route={{sectionLink.route}}
|
||||||
|
@content={{sectionLink.text}}
|
||||||
|
@title={{sectionLink.title}}
|
||||||
|
@currentWhen={{sectionLink.currentWhen}}
|
||||||
|
@prefixType={{sectionLink.prefixType}}
|
||||||
|
@prefixValue={{sectionLink.prefixValue}}
|
||||||
|
@prefixColor={{sectionLink.prefixColor}}
|
||||||
|
@models={{sectionLink.models}}
|
||||||
|
data-tag-name={{sectionLink.tagName}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<AllTagsSectionLink />
|
||||||
|
</Section>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
{{#if this.displaySection}}
|
|
||||||
<Sidebar::Section
|
|
||||||
@sectionName="tags"
|
|
||||||
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
>
|
|
||||||
|
|
||||||
{{#each this.sectionLinks as |sectionLink|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@route={{sectionLink.route}}
|
|
||||||
@content={{sectionLink.text}}
|
|
||||||
@title={{sectionLink.title}}
|
|
||||||
@currentWhen={{sectionLink.currentWhen}}
|
|
||||||
@prefixType={{sectionLink.prefixType}}
|
|
||||||
@prefixValue={{sectionLink.prefixValue}}
|
|
||||||
@prefixColor={{sectionLink.prefixColor}}
|
|
||||||
@models={{sectionLink.models}}
|
|
||||||
data-tag-name={{sectionLink.tagName}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<Sidebar::Common::AllTagsSectionLink />
|
|
||||||
</Sidebar::Section>
|
|
||||||
{{/if}}
|
|
@ -1,30 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { cached } from "@glimmer/tracking";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
|
|
||||||
|
|
||||||
export default class SidebarAnonymousTagsSection extends Component {
|
|
||||||
@service router;
|
|
||||||
@service topicTrackingState;
|
|
||||||
@service site;
|
|
||||||
|
|
||||||
get displaySection() {
|
|
||||||
return (
|
|
||||||
this.site.anonymous_default_navigation_menu_tags?.length > 0 ||
|
|
||||||
this.site.navigation_menu_site_top_tags?.length > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached
|
|
||||||
get sectionLinks() {
|
|
||||||
return (
|
|
||||||
this.site.anonymous_default_navigation_menu_tags ||
|
|
||||||
this.site.navigation_menu_site_top_tags
|
|
||||||
).map((tag) => {
|
|
||||||
return new TagSectionLink({
|
|
||||||
tag,
|
|
||||||
topicTrackingState: this.topicTrackingState,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,9 @@
|
|||||||
|
import ApiSections from "./api-sections";
|
||||||
|
|
||||||
|
const SidebarApiPanels = <template>
|
||||||
|
<div class="sidebar-sections">
|
||||||
|
<ApiSections @collapsable={{@collapsableSections}} />
|
||||||
|
</div>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarApiPanels;
|
@ -1,3 +0,0 @@
|
|||||||
<div class="sidebar-sections">
|
|
||||||
<Sidebar::ApiSections @collapsable={{@collapsableSections}} />
|
|
||||||
</div>
|
|
@ -0,0 +1,94 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { getOwner, setOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import Section from "./section";
|
||||||
|
import SectionLink from "./section-link";
|
||||||
|
|
||||||
|
export default class SidebarApiSection extends Component {
|
||||||
|
@service sidebarState;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.section = new this.args.sectionConfig();
|
||||||
|
setOwner(this.section, getOwner(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldDisplay() {
|
||||||
|
return (
|
||||||
|
!this.sidebarState.currentPanel.filterable ||
|
||||||
|
this.sidebarState.filter.length === 0 ||
|
||||||
|
this.filteredLinks.length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get filteredLinks() {
|
||||||
|
if (!this.sidebarState.filter) {
|
||||||
|
return this.section.links;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.section.text.toLowerCase().match(this.sidebarState.filter)) {
|
||||||
|
return this.section.links;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.section.links.filter((link) => {
|
||||||
|
return (
|
||||||
|
link.text.toString().toLowerCase().match(this.sidebarState.filter) ||
|
||||||
|
link.keywords.navigation.some((keyword) =>
|
||||||
|
keyword.match(this.sidebarState.filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.shouldDisplay}}
|
||||||
|
<Section
|
||||||
|
@sectionName={{this.section.name}}
|
||||||
|
@headerLinkText={{this.section.text}}
|
||||||
|
@headerLinkTitle={{this.section.title}}
|
||||||
|
@headerActionsIcon={{this.section.actionsIcon}}
|
||||||
|
@headerActions={{this.section.actions}}
|
||||||
|
@willDestroy={{this.section.willDestroy}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
@displaySection={{this.section.displaySection}}
|
||||||
|
@hideSectionHeader={{this.section.hideSectionHeader}}
|
||||||
|
@collapsedByDefault={{this.section.collapsedByDefault}}
|
||||||
|
>
|
||||||
|
{{#each this.filteredLinks key="name" as |link|}}
|
||||||
|
<SectionLink
|
||||||
|
@linkName={{link.name}}
|
||||||
|
@linkClass={{link.classNames}}
|
||||||
|
@route={{link.route}}
|
||||||
|
@model={{link.model}}
|
||||||
|
@query={{link.query}}
|
||||||
|
@models={{link.models}}
|
||||||
|
@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}}
|
||||||
|
@currentWhen={{link.currentWhen}}
|
||||||
|
@didInsert={{link.didInsert}}
|
||||||
|
@willDestroy={{link.willDestroy}}
|
||||||
|
@content={{link.text}}
|
||||||
|
@contentComponent={{component
|
||||||
|
link.contentComponent
|
||||||
|
status=link.contentComponentArgs
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</Section>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,48 +0,0 @@
|
|||||||
{{#if this.shouldDisplay}}
|
|
||||||
<Sidebar::Section
|
|
||||||
@sectionName={{this.section.name}}
|
|
||||||
@headerLinkText={{this.section.text}}
|
|
||||||
@headerLinkTitle={{this.section.title}}
|
|
||||||
@headerActionsIcon={{this.section.actionsIcon}}
|
|
||||||
@headerActions={{this.section.actions}}
|
|
||||||
@willDestroy={{this.section.willDestroy}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
@displaySection={{this.section.displaySection}}
|
|
||||||
@hideSectionHeader={{this.section.hideSectionHeader}}
|
|
||||||
@collapsedByDefault={{this.section.collapsedByDefault}}
|
|
||||||
>
|
|
||||||
{{#each this.filteredLinks key="name" as |link|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@linkName={{link.name}}
|
|
||||||
@linkClass={{link.classNames}}
|
|
||||||
@route={{link.route}}
|
|
||||||
@model={{link.model}}
|
|
||||||
@query={{link.query}}
|
|
||||||
@models={{link.models}}
|
|
||||||
@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}}
|
|
||||||
@currentWhen={{link.currentWhen}}
|
|
||||||
@didInsert={{link.didInsert}}
|
|
||||||
@willDestroy={{link.willDestroy}}
|
|
||||||
@content={{link.text}}
|
|
||||||
@contentComponent={{component
|
|
||||||
link.contentComponent
|
|
||||||
status=link.contentComponentArgs
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</Sidebar::Section>
|
|
||||||
{{/if}}
|
|
@ -1,40 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { getOwner, setOwner } from "@ember/application";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class SidebarApiSection extends Component {
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.section = new this.args.sectionConfig();
|
|
||||||
setOwner(this.section, getOwner(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldDisplay() {
|
|
||||||
if (!this.sidebarState.currentPanel.filterable) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
this.sidebarState.filter.length === 0 || this.filteredLinks.length > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get filteredLinks() {
|
|
||||||
if (!this.sidebarState.filter) {
|
|
||||||
return this.section.links;
|
|
||||||
}
|
|
||||||
if (this.section.text.toLowerCase().match(this.sidebarState.filter)) {
|
|
||||||
return this.section.links;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.section.links.filter((link) => {
|
|
||||||
return (
|
|
||||||
link.text.toString().toLowerCase().match(this.sidebarState.filter) ||
|
|
||||||
link.keywords.navigation.some((keyword) =>
|
|
||||||
keyword.match(this.sidebarState.filter)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import AdminHeader from "./admin-header";
|
||||||
|
import ApiSection from "./api-section";
|
||||||
|
import FilterNoResults from "./filter-no-results";
|
||||||
|
|
||||||
|
export default class SidebarApiSections extends Component {
|
||||||
|
@service sidebarState;
|
||||||
|
|
||||||
|
get sections() {
|
||||||
|
if (this.sidebarState.combinedMode) {
|
||||||
|
return this.sidebarState.panels
|
||||||
|
.filter((panel) => !panel.hidden)
|
||||||
|
.flatMap((panel) => panel.sections);
|
||||||
|
} else {
|
||||||
|
return this.sidebarState.currentPanel.sections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AdminHeader />
|
||||||
|
|
||||||
|
{{#each this.sections as |sectionConfig|}}
|
||||||
|
<ApiSection
|
||||||
|
@sectionConfig={{sectionConfig}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<FilterNoResults />
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<Sidebar::AdminHeader />
|
|
||||||
{{#each this.sections as |sectionConfig|}}
|
|
||||||
<Sidebar::ApiSection
|
|
||||||
@sectionConfig={{sectionConfig}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
<Sidebar::FilterNoResults />
|
|
@ -1,17 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class SidebarApiSections extends Component {
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
get sections() {
|
|
||||||
if (this.sidebarState.combinedMode) {
|
|
||||||
return this.sidebarState.panels
|
|
||||||
.filter((panel) => !panel.hidden)
|
|
||||||
.map((panel) => panel.sections)
|
|
||||||
.flat();
|
|
||||||
} else {
|
|
||||||
return this.sidebarState.currentPanel.sections;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import { LinkTo } from "@ember/routing";
|
|||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
||||||
import { defaultHomepage } from "discourse/lib/utilities";
|
import { defaultHomepage } from "discourse/lib/utilities";
|
||||||
import dIcon from "discourse-common/helpers/d-icon";
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
import i18n from "discourse-common/helpers/i18n";
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
|
||||||
export default class BackToForum extends Component {
|
export default class BackToForum extends Component {
|
||||||
@ -13,14 +13,13 @@ export default class BackToForum extends Component {
|
|||||||
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
get homepage() {
|
|
||||||
return `discovery.${defaultHomepage()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{#if this.shouldDisplay}}
|
{{#if this.shouldDisplay}}
|
||||||
<LinkTo @route={{this.homepage}} class="sidebar-sections__back-to-forum">
|
<LinkTo
|
||||||
{{dIcon "arrow-left"}}
|
@route="discovery.{{(defaultHomepage)}}"
|
||||||
|
class="sidebar-sections__back-to-forum"
|
||||||
|
>
|
||||||
|
{{icon "arrow-left"}}
|
||||||
|
|
||||||
<span>{{i18n "admin.back_to_forum"}}</span>
|
<span>{{i18n "admin.back_to_forum"}}</span>
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
|
const SidebarCommonAllCategoriesSectionLink = <template>
|
||||||
|
<SectionLink
|
||||||
|
@linkName="all-categories"
|
||||||
|
@content={{i18n "sidebar.all_categories"}}
|
||||||
|
@route="discovery.categories"
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue="sidebar.all_categories"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarCommonAllCategoriesSectionLink;
|
@ -1,7 +0,0 @@
|
|||||||
<Sidebar::SectionLink
|
|
||||||
@linkName="all-categories"
|
|
||||||
@content={{i18n "sidebar.all_categories"}}
|
|
||||||
@route="discovery.categories"
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue="sidebar.all_categories"
|
|
||||||
/>
|
|
@ -0,0 +1,14 @@
|
|||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
|
const SidebarCommonAllTagsSectionLink = <template>
|
||||||
|
<SectionLink
|
||||||
|
@linkName="all-tags"
|
||||||
|
@content={{i18n "sidebar.all_tags"}}
|
||||||
|
@route="tags"
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue="list"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarCommonAllTagsSectionLink;
|
@ -1,7 +0,0 @@
|
|||||||
<Sidebar::SectionLink
|
|
||||||
@linkName="all-tags"
|
|
||||||
@content={{i18n "sidebar.all_tags"}}
|
|
||||||
@route="tags"
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue="list"
|
|
||||||
/>
|
|
@ -8,9 +8,9 @@ import Category from "discourse/models/category";
|
|||||||
export const TOP_SITE_CATEGORIES_TO_SHOW = 5;
|
export const TOP_SITE_CATEGORIES_TO_SHOW = 5;
|
||||||
|
|
||||||
export default class SidebarCommonCategoriesSection extends Component {
|
export default class SidebarCommonCategoriesSection extends Component {
|
||||||
@service topicTrackingState;
|
|
||||||
@service siteSettings;
|
|
||||||
@service site;
|
@service site;
|
||||||
|
@service siteSettings;
|
||||||
|
@service topicTrackingState;
|
||||||
|
|
||||||
shouldSortCategoriesByDefault = true;
|
shouldSortCategoriesByDefault = true;
|
||||||
|
|
@ -0,0 +1,109 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import { or } from "truth-helpers";
|
||||||
|
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||||
|
import CommonCommunitySection from "discourse/lib/sidebar/common/community-section/section";
|
||||||
|
import Section from "discourse/lib/sidebar/section";
|
||||||
|
import AdminCommunitySection from "discourse/lib/sidebar/user/community-section/admin-section";
|
||||||
|
import MoreSectionLink from "../more-section-link";
|
||||||
|
import MoreSectionLinks from "../more-section-links";
|
||||||
|
import SectionComponent from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
import SectionLinkButton from "../section-link-button";
|
||||||
|
|
||||||
|
export default class SidebarCustomSection extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
@service navigationMenu;
|
||||||
|
@service site;
|
||||||
|
@service siteSettings;
|
||||||
|
|
||||||
|
@tracked section = this.initialSection;
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
super.willDestroy();
|
||||||
|
this.section.teardown?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
get initialSection() {
|
||||||
|
const opts = {
|
||||||
|
section: this.args.sectionData,
|
||||||
|
owner: getOwner(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.args.sectionData.section_type !== "community") {
|
||||||
|
return new Section(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentUser?.admin) {
|
||||||
|
return new AdminCommunitySection(opts);
|
||||||
|
} else {
|
||||||
|
return new CommonCommunitySection(opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SectionComponent
|
||||||
|
@sectionName={{this.section.slug}}
|
||||||
|
@headerLinkText={{this.section.decoratedTitle}}
|
||||||
|
@indicatePublic={{this.section.indicatePublic}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
@headerActions={{this.section.headerActions}}
|
||||||
|
@headerActionsIcon={{this.section.headerActionIcon}}
|
||||||
|
@hideSectionHeader={{this.section.hideSectionHeader}}
|
||||||
|
class={{this.section.dragCss}}
|
||||||
|
>
|
||||||
|
{{#each this.section.links as |link|}}
|
||||||
|
<SectionLink
|
||||||
|
@badgeText={{link.badgeText}}
|
||||||
|
@content={{replaceEmoji link.text}}
|
||||||
|
@currentWhen={{link.currentWhen}}
|
||||||
|
@href={{or link.value link.href}}
|
||||||
|
@linkClass={{link.linkDragCss}}
|
||||||
|
@linkName={{link.name}}
|
||||||
|
@model={{link.model}}
|
||||||
|
@models={{link.models}}
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue={{link.prefixValue}}
|
||||||
|
@query={{link.query}}
|
||||||
|
@route={{link.route}}
|
||||||
|
@shouldDisplay={{link.shouldDisplay}}
|
||||||
|
@suffixCSSClass={{link.suffixCSSClass}}
|
||||||
|
@suffixType={{link.suffixType}}
|
||||||
|
@suffixValue={{link.suffixValue}}
|
||||||
|
@title={{link.title}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if this.section.moreLinks}}
|
||||||
|
{{#if this.navigationMenu.isDesktopDropdownMode}}
|
||||||
|
{{#each this.section.moreLinks as |sectionLink|}}
|
||||||
|
<MoreSectionLink @sectionLink={{sectionLink}} />
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if this.section.moreSectionButtonAction}}
|
||||||
|
<SectionLinkButton
|
||||||
|
@action={{this.section.moreSectionButtonAction}}
|
||||||
|
@icon={{this.section.moreSectionButtonIcon}}
|
||||||
|
@text={{this.section.moreSectionButtonText}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{else if this.section.moreLinks}}
|
||||||
|
<MoreSectionLinks
|
||||||
|
@sectionLinks={{this.section.moreLinks}}
|
||||||
|
@moreButtonAction={{this.section.moreSectionButtonAction}}
|
||||||
|
@moreButtonText={{this.section.moreSectionButtonText}}
|
||||||
|
@moreButtonIcon={{this.section.moreSectionButtonIcon}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{else if this.section.moreSectionButtonAction}}
|
||||||
|
<SectionLinkButton
|
||||||
|
@action={{this.section.moreSectionButtonAction}}
|
||||||
|
@icon={{this.section.moreSectionButtonIcon}}
|
||||||
|
@text={{this.section.moreSectionButtonText}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</SectionComponent>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
<Sidebar::Section
|
|
||||||
@sectionName={{this.section.slug}}
|
|
||||||
@headerLinkText={{this.section.decoratedTitle}}
|
|
||||||
@indicatePublic={{this.section.indicatePublic}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
@headerActions={{this.section.headerActions}}
|
|
||||||
@headerActionsIcon={{this.section.headerActionIcon}}
|
|
||||||
@hideSectionHeader={{this.section.hideSectionHeader}}
|
|
||||||
class={{this.section.dragCss}}
|
|
||||||
>
|
|
||||||
{{#each this.section.links as |link|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@badgeText={{link.badgeText}}
|
|
||||||
@content={{replace-emoji link.text}}
|
|
||||||
@currentWhen={{link.currentWhen}}
|
|
||||||
@href={{or link.value link.href}}
|
|
||||||
@linkClass={{link.linkDragCss}}
|
|
||||||
@linkName={{link.name}}
|
|
||||||
@model={{link.model}}
|
|
||||||
@models={{link.models}}
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue={{link.prefixValue}}
|
|
||||||
@query={{link.query}}
|
|
||||||
@route={{link.route}}
|
|
||||||
@shouldDisplay={{link.shouldDisplay}}
|
|
||||||
@suffixCSSClass={{link.suffixCSSClass}}
|
|
||||||
@suffixType={{link.suffixType}}
|
|
||||||
@suffixValue={{link.suffixValue}}
|
|
||||||
@title={{link.title}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if this.section.moreLinks}}
|
|
||||||
{{#if this.navigationMenu.isDesktopDropdownMode}}
|
|
||||||
{{#each this.section.moreLinks as |sectionLink|}}
|
|
||||||
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if this.section.moreSectionButtonAction}}
|
|
||||||
<Sidebar::SectionLinkButton
|
|
||||||
@action={{this.section.moreSectionButtonAction}}
|
|
||||||
@icon={{this.section.moreSectionButtonIcon}}
|
|
||||||
@text={{this.section.moreSectionButtonText}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else if this.section.moreLinks}}
|
|
||||||
<Sidebar::MoreSectionLinks
|
|
||||||
@sectionLinks={{this.section.moreLinks}}
|
|
||||||
@moreButtonAction={{this.section.moreSectionButtonAction}}
|
|
||||||
@moreButtonText={{this.section.moreSectionButtonText}}
|
|
||||||
@moreButtonIcon={{this.section.moreSectionButtonIcon}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else if this.section.moreSectionButtonAction}}
|
|
||||||
<Sidebar::SectionLinkButton
|
|
||||||
@action={{this.section.moreSectionButtonAction}}
|
|
||||||
@icon={{this.section.moreSectionButtonIcon}}
|
|
||||||
@text={{this.section.moreSectionButtonText}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</Sidebar::Section>
|
|
@ -1,43 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { getOwner } from "@ember/application";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import CommonCommunitySection from "discourse/lib/sidebar/common/community-section/section";
|
|
||||||
import Section from "discourse/lib/sidebar/section";
|
|
||||||
import AdminCommunitySection from "discourse/lib/sidebar/user/community-section/admin-section";
|
|
||||||
|
|
||||||
export default class SidebarCustomSection extends Component {
|
|
||||||
@service currentUser;
|
|
||||||
@service navigationMenu;
|
|
||||||
@service site;
|
|
||||||
@service siteSettings;
|
|
||||||
|
|
||||||
@tracked section;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.section = this.#initializeSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
this.section.teardown?.();
|
|
||||||
super.willDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
#initializeSection() {
|
|
||||||
let sectionClass = Section;
|
|
||||||
|
|
||||||
switch (this.args.sectionData.section_type) {
|
|
||||||
case "community":
|
|
||||||
sectionClass = this.currentUser?.admin
|
|
||||||
? AdminCommunitySection
|
|
||||||
: CommonCommunitySection;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new sectionClass({
|
|
||||||
section: this.args.sectionData,
|
|
||||||
owner: getOwner(this),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
import CustomSection from "./custom-section";
|
||||||
|
|
||||||
export default class SidebarCustomSection extends Component {
|
export default class SidebarCustomSection extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@ -19,4 +20,12 @@ export default class SidebarCustomSection extends Component {
|
|||||||
return this.currentUser.sidebarSections;
|
return this.currentUser.sidebarSections;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-custom-sections">
|
||||||
|
{{#each this.sections as |section|}}
|
||||||
|
<CustomSection @sectionData={{section}} @collapsable={{@collapsable}} />
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<div class="sidebar-custom-sections">
|
|
||||||
{{#each this.sections as |section|}}
|
|
||||||
<Sidebar::Common::CustomSection
|
|
||||||
@sectionData={{section}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
@ -59,7 +59,7 @@ function applyMode(mode, categories, selectedSidebarCategoryIds) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class extends Component {
|
export default class SidebarEditNavigationMenuCategoriesModal extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@service site;
|
@service site;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
@ -67,7 +67,7 @@ export default class extends Component {
|
|||||||
@tracked initialLoad = true;
|
@tracked initialLoad = true;
|
||||||
@tracked filteredCategoriesGroupings = [];
|
@tracked filteredCategoriesGroupings = [];
|
||||||
@tracked filteredCategoryIds = [];
|
@tracked filteredCategoryIds = [];
|
||||||
|
// TODO: tracked array, no ember array methods
|
||||||
@tracked
|
@tracked
|
||||||
selectedSidebarCategoryIds = [...this.currentUser.sidebar_category_ids];
|
selectedSidebarCategoryIds = [...this.currentUser.sidebar_category_ids];
|
||||||
|
|
||||||
@ -307,31 +307,31 @@ export default class extends Component {
|
|||||||
<div class="sidebar-categories-form__loading">
|
<div class="sidebar-categories-form__loading">
|
||||||
{{loadingSpinner size="small"}}
|
{{loadingSpinner size="small"}}
|
||||||
</div>
|
</div>
|
||||||
{{else if (gt this.filteredCategoriesGroupings.length 0)}}
|
{{else}}
|
||||||
{{#each this.filteredCategoriesGroupings as |categories|}}
|
{{#each this.filteredCategoriesGroupings as |categories|}}
|
||||||
<div
|
<div
|
||||||
class="sidebar-categories-form__row"
|
|
||||||
style={{borderColor (get categories "0.color") "left"}}
|
|
||||||
{{didInsert this.didInsert}}
|
{{didInsert this.didInsert}}
|
||||||
|
style={{borderColor (get categories "0.color") "left"}}
|
||||||
|
class="sidebar-categories-form__row"
|
||||||
>
|
>
|
||||||
|
|
||||||
{{#each categories as |category|}}
|
{{#each categories as |category|}}
|
||||||
<div
|
<div
|
||||||
class="sidebar-categories-form__category-row"
|
|
||||||
data-category-id={{category.id}}
|
data-category-id={{category.id}}
|
||||||
data-category-level={{category.level}}
|
data-category-level={{category.level}}
|
||||||
|
class="sidebar-categories-form__category-row"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="sidebar-categories-form__category-label"
|
|
||||||
for={{concat
|
for={{concat
|
||||||
"sidebar-categories-form__input--"
|
"sidebar-categories-form__input--"
|
||||||
category.id
|
category.id
|
||||||
}}
|
}}
|
||||||
|
class="sidebar-categories-form__category-label"
|
||||||
>
|
>
|
||||||
<div class="sidebar-categories-form__category-wrapper">
|
<div class="sidebar-categories-form__category-wrapper">
|
||||||
<div class="sidebar-categories-form__category-badge">
|
<div class="sidebar-categories-form__category-badge">
|
||||||
{{categoryBadge category}}
|
{{categoryBadge category}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#unless category.parentCategory}}
|
{{#unless category.parentCategory}}
|
||||||
<div
|
<div
|
||||||
class="sidebar-categories-form__category-description"
|
class="sidebar-categories-form__category-description"
|
||||||
@ -345,11 +345,7 @@ export default class extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
id={{concat
|
{{on "click" (fn this.toggleCategory category.id)}}
|
||||||
"sidebar-categories-form__input--"
|
|
||||||
category.id
|
|
||||||
}}
|
|
||||||
class="sidebar-categories-form__input"
|
|
||||||
@type="checkbox"
|
@type="checkbox"
|
||||||
@checked={{includes
|
@checked={{includes
|
||||||
this.selectedSidebarCategoryIds
|
this.selectedSidebarCategoryIds
|
||||||
@ -358,17 +354,21 @@ export default class extends Component {
|
|||||||
disabled={{not
|
disabled={{not
|
||||||
(includes this.filteredCategoryIds category.id)
|
(includes this.filteredCategoryIds category.id)
|
||||||
}}
|
}}
|
||||||
{{on "click" (fn this.toggleCategory category.id)}}
|
id={{concat
|
||||||
|
"sidebar-categories-form__input--"
|
||||||
|
category.id
|
||||||
|
}}
|
||||||
|
class="sidebar-categories-form__input"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="sidebar-categories-form__no-categories">
|
||||||
|
{{i18n "sidebar.categories_form_modal.no_categories"}}
|
||||||
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
|
||||||
<div class="sidebar-categories-form__no-categories">
|
|
||||||
{{i18n "sidebar.categories_form_modal.no_categories"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form>
|
</form>
|
||||||
</EditNavigationMenuModal>
|
</EditNavigationMenuModal>
|
||||||
|
@ -6,12 +6,13 @@ import { on } from "@ember/modifier";
|
|||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import DButton from "discourse/components/d-button";
|
import DButton from "discourse/components/d-button";
|
||||||
import DModal from "discourse/components/d-modal";
|
import DModal from "discourse/components/d-modal";
|
||||||
import dIcon from "discourse-common/helpers/d-icon";
|
import withEventValue from "discourse/helpers/with-event-value";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
import i18n from "discourse-common/helpers/i18n";
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||||
|
|
||||||
export default class extends Component {
|
export default class SidebarEditNavigationMenuModal extends Component {
|
||||||
@tracked filter = "";
|
@tracked filter = "";
|
||||||
@tracked filterDropdownValue = "all";
|
@tracked filterDropdownValue = "all";
|
||||||
|
|
||||||
@ -34,11 +35,6 @@ export default class extends Component {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@action
|
|
||||||
onFilterInput(input) {
|
|
||||||
this.args.onFilterInput(input.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onFilterDropdownChange(value) {
|
onFilterDropdownChange(value) {
|
||||||
this.filterDropdownValue = value;
|
this.filterDropdownValue = value;
|
||||||
@ -79,18 +75,18 @@ export default class extends Component {
|
|||||||
<:belowHeader>
|
<:belowHeader>
|
||||||
<div class="sidebar__edit-navigation-menu__filter">
|
<div class="sidebar__edit-navigation-menu__filter">
|
||||||
<div class="sidebar__edit-navigation-menu__filter-input">
|
<div class="sidebar__edit-navigation-menu__filter-input">
|
||||||
{{dIcon
|
{{icon
|
||||||
"search"
|
"search"
|
||||||
class="sidebar__edit-navigation-menu__filter-input-icon"
|
class="sidebar__edit-navigation-menu__filter-input-icon"
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
class="sidebar__edit-navigation-menu__filter-input-field"
|
{{on "input" (withEventValue @onFilterInput)}}
|
||||||
placeholder={{@inputFilterPlaceholder}}
|
|
||||||
@type="text"
|
@type="text"
|
||||||
@value={{this.filter}}
|
@value={{this.filter}}
|
||||||
{{on "input" this.onFilterInput}}
|
placeholder={{@inputFilterPlaceholder}}
|
||||||
autofocus="true"
|
autofocus="true"
|
||||||
|
class="sidebar__edit-navigation-menu__filter-input-field"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -113,18 +109,18 @@ export default class extends Component {
|
|||||||
<:footer>
|
<:footer>
|
||||||
<div class="sidebar__edit-navigation-menu__footer">
|
<div class="sidebar__edit-navigation-menu__footer">
|
||||||
<DButton
|
<DButton
|
||||||
|
@action={{@save}}
|
||||||
@label="save"
|
@label="save"
|
||||||
@disabled={{@saving}}
|
@disabled={{@saving}}
|
||||||
@action={{@save}}
|
|
||||||
class="btn-primary sidebar__edit-navigation-menu__save-button"
|
class="btn-primary sidebar__edit-navigation-menu__save-button"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{#if @showResetDefaultsButton}}
|
{{#if @showResetDefaultsButton}}
|
||||||
<DButton
|
<DButton
|
||||||
@icon="undo"
|
|
||||||
@label="sidebar.edit_navigation_modal_form.reset_to_defaults"
|
|
||||||
@disabled={{@saving}}
|
|
||||||
@action={{@resetToDefaults}}
|
@action={{@resetToDefaults}}
|
||||||
|
@label="sidebar.edit_navigation_modal_form.reset_to_defaults"
|
||||||
|
@icon="undo"
|
||||||
|
@disabled={{@saving}}
|
||||||
class="btn-flat btn-text sidebar__edit-navigation-menu__reset-defaults-button"
|
class="btn-flat btn-text sidebar__edit-navigation-menu__reset-defaults-button"
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { concat, fn } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
|
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
import { gt, includes, or } from "truth-helpers";
|
||||||
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||||
|
import loadingSpinner from "discourse/helpers/loading-spinner";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
import EditNavigationMenuModal from "./modal";
|
||||||
|
|
||||||
export default class extends Component {
|
export default class SidebarEditNavigationMenuTagsModal extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
@service store;
|
@service store;
|
||||||
@ -161,4 +170,72 @@ export default class extends Component {
|
|||||||
this.saving = false;
|
this.saving = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EditNavigationMenuModal
|
||||||
|
@title="sidebar.tags_form_modal.title"
|
||||||
|
@saving={{this.saving}}
|
||||||
|
@save={{this.save}}
|
||||||
|
@showResetDefaultsButton={{gt
|
||||||
|
this.siteSettings.default_navigation_menu_tags.length
|
||||||
|
0
|
||||||
|
}}
|
||||||
|
@resetToDefaults={{this.resetToDefaults}}
|
||||||
|
@deselectAll={{this.deselectAll}}
|
||||||
|
@deselectAllText={{i18n "sidebar.tags_form_modal.subtitle.text"}}
|
||||||
|
@inputFilterPlaceholder={{i18n
|
||||||
|
"sidebar.tags_form_modal.filter_placeholder"
|
||||||
|
}}
|
||||||
|
@onFilterInput={{this.onFilterInput}}
|
||||||
|
@resetFilter={{this.resetFilter}}
|
||||||
|
@filterSelected={{this.filterSelected}}
|
||||||
|
@filterUnselected={{this.filterUnselected}}
|
||||||
|
@closeModal={{@closeModal}}
|
||||||
|
@loading={{or this.tagsLoading this.disableFiltering}}
|
||||||
|
class="sidebar__edit-navigation-menu__tags-modal"
|
||||||
|
>
|
||||||
|
{{#if this.tagsLoading}}
|
||||||
|
{{loadingSpinner size="large"}}
|
||||||
|
{{else}}
|
||||||
|
<form class="sidebar-tags-form">
|
||||||
|
{{#each this.tags as |tag|}}
|
||||||
|
<div
|
||||||
|
{{didInsert this.didInsertTag}}
|
||||||
|
data-tag-name={{tag.name}}
|
||||||
|
class="sidebar-tags-form__tag"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{{on "click" (fn this.toggleTag tag.name)}}
|
||||||
|
@type="checkbox"
|
||||||
|
@checked={{includes this.selectedTags tag.name}}
|
||||||
|
id={{concat "sidebar-tags-form__input--" tag.name}}
|
||||||
|
class="sidebar-tags-form__input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label
|
||||||
|
for={{concat "sidebar-tags-form__input--" tag.name}}
|
||||||
|
class="sidebar-tags-form__tag-label"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<span class="sidebar-tags-form__tag-label-name">
|
||||||
|
{{tag.name}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="sidebar-tags-form__tag-label-count">
|
||||||
|
({{tag.topic_count}})
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="sidebar-tags-form__no-tags">
|
||||||
|
{{i18n "sidebar.tags_form_modal.no_tags"}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</form>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<ConditionalLoadingSpinner @condition={{this.tags.loadingMore}} />
|
||||||
|
</EditNavigationMenuModal>
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,65 +0,0 @@
|
|||||||
<Sidebar::EditNavigationMenu::Modal
|
|
||||||
@title="sidebar.tags_form_modal.title"
|
|
||||||
@saving={{this.saving}}
|
|
||||||
@save={{this.save}}
|
|
||||||
@showResetDefaultsButton={{gt
|
|
||||||
this.siteSettings.default_navigation_menu_tags.length
|
|
||||||
0
|
|
||||||
}}
|
|
||||||
@resetToDefaults={{this.resetToDefaults}}
|
|
||||||
@deselectAll={{this.deselectAll}}
|
|
||||||
@deselectAllText={{i18n "sidebar.tags_form_modal.subtitle.text"}}
|
|
||||||
@inputFilterPlaceholder={{i18n "sidebar.tags_form_modal.filter_placeholder"}}
|
|
||||||
@onFilterInput={{this.onFilterInput}}
|
|
||||||
@resetFilter={{this.resetFilter}}
|
|
||||||
@filterSelected={{this.filterSelected}}
|
|
||||||
@filterUnselected={{this.filterUnselected}}
|
|
||||||
@closeModal={{@closeModal}}
|
|
||||||
@loading={{or this.tagsLoading this.disableFiltering}}
|
|
||||||
class="sidebar__edit-navigation-menu__tags-modal"
|
|
||||||
>
|
|
||||||
{{#if this.tagsLoading}}
|
|
||||||
{{loading-spinner size="large"}}
|
|
||||||
{{else}}
|
|
||||||
<form class="sidebar-tags-form">
|
|
||||||
{{#if (gt this.tags.length 0)}}
|
|
||||||
{{#each this.tags as |tag|}}
|
|
||||||
<div
|
|
||||||
class="sidebar-tags-form__tag"
|
|
||||||
data-tag-name={{tag.name}}
|
|
||||||
{{did-insert this.didInsertTag}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
id={{concat "sidebar-tags-form__input--" tag.name}}
|
|
||||||
class="sidebar-tags-form__input"
|
|
||||||
@type="checkbox"
|
|
||||||
@checked={{includes this.selectedTags tag.name}}
|
|
||||||
{{on "click" (fn this.toggleTag tag.name)}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label
|
|
||||||
class="sidebar-tags-form__tag-label"
|
|
||||||
for={{concat "sidebar-tags-form__input--" tag.name}}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
<span class="sidebar-tags-form__tag-label-name">
|
|
||||||
{{tag.name}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="sidebar-tags-form__tag-label-count">
|
|
||||||
({{tag.topic_count}})
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<div class="sidebar-tags-form__no-tags">
|
|
||||||
{{i18n "sidebar.tags_form_modal.no_tags"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</form>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.tags.loadingMore}} />
|
|
||||||
</Sidebar::EditNavigationMenu::Modal>
|
|
@ -5,7 +5,7 @@ import i18n from "discourse-common/helpers/i18n";
|
|||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
export default class FilterNoResulsts extends Component {
|
export default class FilterNoResults extends Component {
|
||||||
@service sidebarState;
|
@service sidebarState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ApiPanels from "./api-panels";
|
||||||
|
import Footer from "./footer";
|
||||||
|
import Sections from "./sections";
|
||||||
|
|
||||||
|
export default class SidebarHamburgerDropdown extends Component {
|
||||||
|
@service appEvents;
|
||||||
|
@service currentUser;
|
||||||
|
@service site;
|
||||||
|
@service siteSettings;
|
||||||
|
@service sidebarState;
|
||||||
|
|
||||||
|
@action
|
||||||
|
triggerRenderedAppEvent() {
|
||||||
|
this.appEvents.trigger("sidebar-hamburger-dropdown:rendered");
|
||||||
|
}
|
||||||
|
|
||||||
|
get collapsableSections() {
|
||||||
|
if (
|
||||||
|
this.siteSettings.navigation_menu === "header dropdown" &&
|
||||||
|
!this.args.collapsableSections
|
||||||
|
) {
|
||||||
|
return this.site.mobileView || this.site.narrowDesktopView;
|
||||||
|
} else {
|
||||||
|
this.args.collapsableSections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="hamburger-panel">
|
||||||
|
<div
|
||||||
|
{{didInsert this.triggerRenderedAppEvent}}
|
||||||
|
data-max-width="320"
|
||||||
|
class="revamped menu-panel drop-down"
|
||||||
|
>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="panel-body-contents">
|
||||||
|
<div class="sidebar-hamburger-dropdown">
|
||||||
|
{{#if this.sidebarState.showMainPanel}}
|
||||||
|
<Sections
|
||||||
|
@currentUser={{this.currentUser}}
|
||||||
|
@collapsableSections={{this.collapsableSections}}
|
||||||
|
@panel={{this.sidebarState.currentPanel}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<ApiPanels
|
||||||
|
@currentUser={{this.currentUser}}
|
||||||
|
@collapsableSections={{this.collapsableSections}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
<div class="hamburger-panel">
|
|
||||||
<div
|
|
||||||
class="revamped menu-panel drop-down"
|
|
||||||
data-max-width="320"
|
|
||||||
{{did-insert this.triggerRenderedAppEvent}}
|
|
||||||
>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="panel-body-contents">
|
|
||||||
<div class="sidebar-hamburger-dropdown">
|
|
||||||
{{#if this.sidebarState.showMainPanel}}
|
|
||||||
<Sidebar::Sections
|
|
||||||
@currentUser={{this.currentUser}}
|
|
||||||
@collapsableSections={{this.collapsableSections}}
|
|
||||||
@panel={{this.sidebarState.currentPanel}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<Sidebar::ApiPanels
|
|
||||||
@currentUser={{this.currentUser}}
|
|
||||||
@collapsableSections={{this.collapsableSections}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
<Sidebar::Footer @tagName="" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,27 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class SidebarHamburgerDropdown extends Component {
|
|
||||||
@service appEvents;
|
|
||||||
@service currentUser;
|
|
||||||
@service site;
|
|
||||||
@service siteSettings;
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
@action
|
|
||||||
triggerRenderedAppEvent() {
|
|
||||||
this.appEvents.trigger("sidebar-hamburger-dropdown:rendered");
|
|
||||||
}
|
|
||||||
|
|
||||||
get collapsableSections() {
|
|
||||||
if (
|
|
||||||
this.siteSettings.navigation_menu === "header dropdown" &&
|
|
||||||
!this.args.collapsableSections
|
|
||||||
) {
|
|
||||||
return this.site.mobileView || this.site.narrowDesktopView;
|
|
||||||
} else {
|
|
||||||
this.args.collapsableSections;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
import { or } from "truth-helpers";
|
||||||
|
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||||
|
import SectionLink from "./section-link";
|
||||||
|
|
||||||
|
const SidebarMoreSectionLink = <template>
|
||||||
|
<SectionLink
|
||||||
|
@badgeText={{@sectionLink.badgeText}}
|
||||||
|
@content={{replaceEmoji @sectionLink.text}}
|
||||||
|
@currentWhen={{@sectionLink.currentWhen}}
|
||||||
|
@href={{or @sectionLink.href @sectionLink.value}}
|
||||||
|
@linkName={{@sectionLink.name}}
|
||||||
|
@model={{@sectionLink.model}}
|
||||||
|
@models={{@sectionLink.models}}
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue={{@sectionLink.prefixValue}}
|
||||||
|
@query={{@sectionLink.query}}
|
||||||
|
@route={{@sectionLink.route}}
|
||||||
|
@shouldDisplay={{@sectionLink.shouldDisplay}}
|
||||||
|
@suffixCSSClass={{@sectionLink.suffixCSSClass}}
|
||||||
|
@suffixType={{@sectionLink.suffixType}}
|
||||||
|
@suffixValue={{@sectionLink.suffixValue}}
|
||||||
|
@title={{@sectionLink.title}}
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarMoreSectionLink;
|
@ -1,18 +0,0 @@
|
|||||||
<Sidebar::SectionLink
|
|
||||||
@badgeText={{@sectionLink.badgeText}}
|
|
||||||
@content={{replace-emoji @sectionLink.text}}
|
|
||||||
@currentWhen={{@sectionLink.currentWhen}}
|
|
||||||
@href={{or @sectionLink.href @sectionLink.value}}
|
|
||||||
@linkName={{@sectionLink.name}}
|
|
||||||
@model={{@sectionLink.model}}
|
|
||||||
@models={{@sectionLink.models}}
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue={{@sectionLink.prefixValue}}
|
|
||||||
@query={{@sectionLink.query}}
|
|
||||||
@route={{@sectionLink.route}}
|
|
||||||
@shouldDisplay={{@sectionLink.shouldDisplay}}
|
|
||||||
@suffixCSSClass={{@sectionLink.suffixCSSClass}}
|
|
||||||
@suffixType={{@sectionLink.suffixType}}
|
|
||||||
@suffixValue={{@sectionLink.suffixValue}}
|
|
||||||
@title={{@sectionLink.title}}
|
|
||||||
/>
|
|
@ -1,9 +1,16 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
|
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||||
|
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import MoreSectionLink from "./more-section-link";
|
||||||
|
import SectionLinkButton from "./section-link-button";
|
||||||
|
|
||||||
export default class SidebarMoreSectionLinks extends Component {
|
export default class SidebarMoreSectionLinks extends Component {
|
||||||
@service router;
|
@service router;
|
||||||
@ -93,7 +100,7 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#setActiveSectionLink() {
|
#setActiveSectionLink() {
|
||||||
const activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
|
this.activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
|
||||||
const args = [sectionLink.route];
|
const args = [sectionLink.route];
|
||||||
|
|
||||||
if (sectionLink.model) {
|
if (sectionLink.model) {
|
||||||
@ -108,7 +115,56 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||||||
|
|
||||||
return this.router.isActive(...args) && sectionLink;
|
return this.router.isActive(...args) && sectionLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activeSectionLink = activeSectionLink;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.activeSectionLink}}
|
||||||
|
<MoreSectionLink @sectionLink={{this.activeSectionLink}} />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<li class="sidebar-section-link-wrapper">
|
||||||
|
<button
|
||||||
|
{{on "click" this.toggleSectionLinks}}
|
||||||
|
aria-expanded={{if this.open "true" "false"}}
|
||||||
|
class="sidebar-section-link sidebar-row sidebar-more-section-links-details-summary --link-button"
|
||||||
|
>
|
||||||
|
<span class="sidebar-section-link-prefix icon">
|
||||||
|
{{icon "ellipsis-v"}}
|
||||||
|
</span>
|
||||||
|
<span class="sidebar-section-link-content-text">
|
||||||
|
{{i18n "sidebar.more"}}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{{#if this.open}}
|
||||||
|
<div class="sidebar-more-section-links-details">
|
||||||
|
<div
|
||||||
|
{{didInsert this.registerClickListener}}
|
||||||
|
{{willDestroy this.unregisterClickListener}}
|
||||||
|
class="sidebar-more-section-links-details-content-wrapper"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="sidebar-more-section-links-details-content">
|
||||||
|
<ul class="sidebar-more-section-links-details-content-main">
|
||||||
|
{{#each this.sectionLinks as |sectionLink|}}
|
||||||
|
<MoreSectionLink @sectionLink={{sectionLink}} />
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{#if @moreButtonAction}}
|
||||||
|
<div class="sidebar-more-section-links-details-content-footer">
|
||||||
|
<SectionLinkButton
|
||||||
|
@action={{@moreButtonAction}}
|
||||||
|
@icon={{@moreButtonIcon}}
|
||||||
|
@text={{@moreButtonText}}
|
||||||
|
@name="customize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,48 +0,0 @@
|
|||||||
{{#if this.activeSectionLink}}
|
|
||||||
<Sidebar::MoreSectionLink @sectionLink={{this.activeSectionLink}} />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<li class="sidebar-section-link-wrapper">
|
|
||||||
<button
|
|
||||||
class="sidebar-section-link sidebar-row sidebar-more-section-links-details-summary --link-button"
|
|
||||||
aria-expanded={{if this.open "true" "false"}}
|
|
||||||
{{on "click" this.toggleSectionLinks}}
|
|
||||||
>
|
|
||||||
<span class="sidebar-section-link-prefix icon">
|
|
||||||
{{d-icon "ellipsis-v"}}
|
|
||||||
</span>
|
|
||||||
<span class="sidebar-section-link-content-text">
|
|
||||||
{{i18n "sidebar.more"}}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{{#if this.open}}
|
|
||||||
<div class="sidebar-more-section-links-details">
|
|
||||||
<div
|
|
||||||
class="sidebar-more-section-links-details-content-wrapper"
|
|
||||||
{{did-insert this.registerClickListener}}
|
|
||||||
{{will-destroy this.unregisterClickListener}}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="sidebar-more-section-links-details-content">
|
|
||||||
<ul class="sidebar-more-section-links-details-content-main">
|
|
||||||
{{#each this.sectionLinks as |sectionLink|}}
|
|
||||||
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{#if @moreButtonAction}}
|
|
||||||
<div class="sidebar-more-section-links-details-content-footer">
|
|
||||||
<Sidebar::SectionLinkButton
|
|
||||||
@action={{@moreButtonAction}}
|
|
||||||
@icon={{@moreButtonIcon}}
|
|
||||||
@text={{@moreButtonText}}
|
|
||||||
@name="customize"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
@ -0,0 +1,168 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { fn, hash } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import withEventValue from "discourse/helpers/with-event-value";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import discourseLater from "discourse-common/lib/later";
|
||||||
|
import IconPicker from "select-kit/components/icon-picker";
|
||||||
|
|
||||||
|
export default class SectionFormLink extends Component {
|
||||||
|
@service site;
|
||||||
|
|
||||||
|
@tracked dragCssClass;
|
||||||
|
dragCount = 0;
|
||||||
|
|
||||||
|
isAboveElement(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const target = event.currentTarget;
|
||||||
|
const domRect = target.getBoundingClientRect();
|
||||||
|
return event.offsetY < domRect.height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dragHasStarted(event) {
|
||||||
|
event.dataTransfer.effectAllowed = "move";
|
||||||
|
this.args.setDraggedLinkCallback(this.args.link);
|
||||||
|
this.dragCssClass = "dragging";
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dragOver(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (this.dragCssClass !== "dragging") {
|
||||||
|
if (this.isAboveElement(event)) {
|
||||||
|
this.dragCssClass = "drag-above";
|
||||||
|
} else {
|
||||||
|
this.dragCssClass = "drag-below";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@action
|
||||||
|
dragEnter() {
|
||||||
|
this.dragCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dragLeave() {
|
||||||
|
this.dragCount--;
|
||||||
|
if (
|
||||||
|
this.dragCount === 0 &&
|
||||||
|
(this.dragCssClass === "drag-above" || this.dragCssClass === "drag-below")
|
||||||
|
) {
|
||||||
|
discourseLater(() => {
|
||||||
|
this.dragCssClass = null;
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dropItem(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.dragCount = 0;
|
||||||
|
this.args.reorderCallback(this.args.link, this.isAboveElement(event));
|
||||||
|
this.dragCssClass = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dragEnd() {
|
||||||
|
this.dragCount = 0;
|
||||||
|
this.dragCssClass = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
{{on "dragstart" this.dragHasStarted}}
|
||||||
|
{{on "dragover" this.dragOver}}
|
||||||
|
{{on "dragenter" this.dragEnter}}
|
||||||
|
{{on "dragleave" this.dragLeave}}
|
||||||
|
{{on "dragend" this.dragEnd}}
|
||||||
|
{{on "drop" this.dropItem}}
|
||||||
|
role="row"
|
||||||
|
data-row-id={{@link.objectId}}
|
||||||
|
draggable="true"
|
||||||
|
class={{concatClass
|
||||||
|
"sidebar-section-form-link"
|
||||||
|
"row-wrapper"
|
||||||
|
this.dragCssClass
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{#if this.site.desktopView}}
|
||||||
|
<div class="draggable" data-link-name={{@link.name}}>
|
||||||
|
{{icon "grip-lines"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="input-group" role="cell">
|
||||||
|
<IconPicker
|
||||||
|
@name="icon"
|
||||||
|
@value={{@link.icon}}
|
||||||
|
@options={{hash
|
||||||
|
maximum=1
|
||||||
|
caretDownIcon="caret-down"
|
||||||
|
caretUpIcon="caret-up"
|
||||||
|
icons=@link.icon
|
||||||
|
}}
|
||||||
|
@onlyAvailable={{true}}
|
||||||
|
@onChange={{fn (mut @link.icon)}}
|
||||||
|
aria-label={{i18n "sidebar.sections.custom.links.icon.label"}}
|
||||||
|
class={{@link.iconCssClass}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if @link.invalidIconMessage}}
|
||||||
|
<div class="icon warning" role="alert" aria-live="assertive">
|
||||||
|
{{@link.invalidIconMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group" role="cell">
|
||||||
|
<Input
|
||||||
|
{{on "input" (withEventValue (fn (mut @link.name)))}}
|
||||||
|
@type="text"
|
||||||
|
@value={{@link.name}}
|
||||||
|
name="link-name"
|
||||||
|
aria-label={{i18n "sidebar.sections.custom.links.name.label"}}
|
||||||
|
class={{@link.nameCssClass}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if @link.invalidNameMessage}}
|
||||||
|
<div role="alert" aria-live="assertive" class="name warning">
|
||||||
|
{{@link.invalidNameMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group" role="cell">
|
||||||
|
<Input
|
||||||
|
{{on "input" (withEventValue (fn (mut @link.value)))}}
|
||||||
|
@type="text"
|
||||||
|
@value={{@link.value}}
|
||||||
|
name="link-url"
|
||||||
|
aria-label={{i18n "sidebar.sections.custom.links.value.label"}}
|
||||||
|
class={{@link.valueCssClass}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if @link.invalidValueMessage}}
|
||||||
|
<div role="alert" aria-live="assertive" class="value warning">
|
||||||
|
{{@link.invalidValueMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@icon="trash-alt"
|
||||||
|
@action={{fn @deleteLink @link}}
|
||||||
|
@title="sidebar.sections.custom.links.delete"
|
||||||
|
role="cell"
|
||||||
|
class="btn-flat delete-link"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,80 +0,0 @@
|
|||||||
<div
|
|
||||||
class={{concat-class
|
|
||||||
"sidebar-section-form-link"
|
|
||||||
"row-wrapper"
|
|
||||||
this.dragCssClass
|
|
||||||
}}
|
|
||||||
draggable="true"
|
|
||||||
{{on "dragstart" this.dragHasStarted}}
|
|
||||||
{{on "dragover" this.dragOver}}
|
|
||||||
{{on "dragenter" this.dragEnter}}
|
|
||||||
{{on "dragleave" this.dragLeave}}
|
|
||||||
{{on "dragend" this.dragEnd}}
|
|
||||||
{{on "drop" this.dropItem}}
|
|
||||||
role="row"
|
|
||||||
data-row-id={{@link.objectId}}
|
|
||||||
>
|
|
||||||
{{#if this.site.desktopView}}
|
|
||||||
<div class="draggable" data-link-name={{@link.name}}>
|
|
||||||
{{d-icon "grip-lines"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="input-group" role="cell">
|
|
||||||
<IconPicker
|
|
||||||
@name="icon"
|
|
||||||
@value={{@link.icon}}
|
|
||||||
@options={{hash
|
|
||||||
maximum=1
|
|
||||||
caretDownIcon="caret-down"
|
|
||||||
caretUpIcon="caret-up"
|
|
||||||
icons=@link.icon
|
|
||||||
}}
|
|
||||||
class={{@link.iconCssClass}}
|
|
||||||
@onlyAvailable={{true}}
|
|
||||||
@onChange={{fn (mut @link.icon)}}
|
|
||||||
ariaLabel={{i18n "sidebar.sections.custom.links.icon.label"}}
|
|
||||||
/>
|
|
||||||
{{#if @link.invalidIconMessage}}
|
|
||||||
<div class="icon warning" role="alert" aria-live="assertive">
|
|
||||||
{{@link.invalidIconMessage}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="input-group" role="cell">
|
|
||||||
<Input
|
|
||||||
name="link-name"
|
|
||||||
@type="text"
|
|
||||||
@value={{@link.name}}
|
|
||||||
class={{@link.nameCssClass}}
|
|
||||||
ariaLabel={{i18n "sidebar.sections.custom.links.name.label"}}
|
|
||||||
{{on "input" (with-event-value (fn (mut @link.name)))}}
|
|
||||||
/>
|
|
||||||
{{#if @link.invalidNameMessage}}
|
|
||||||
<div class="name warning" role="alert" aria-live="assertive">
|
|
||||||
{{@link.invalidNameMessage}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="input-group" role="cell">
|
|
||||||
<Input
|
|
||||||
name="link-url"
|
|
||||||
@type="text"
|
|
||||||
@value={{@link.value}}
|
|
||||||
class={{@link.valueCssClass}}
|
|
||||||
ariaLabel={{i18n "sidebar.sections.custom.links.value.label"}}
|
|
||||||
{{on "input" (with-event-value (fn (mut @link.value)))}}
|
|
||||||
/>
|
|
||||||
{{#if @link.invalidValueMessage}}
|
|
||||||
<div class="value warning" role="alert" aria-live="assertive">
|
|
||||||
{{@link.invalidValueMessage}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<DButton
|
|
||||||
@icon="trash-alt"
|
|
||||||
@action={{fn @deleteLink @link}}
|
|
||||||
@title="sidebar.sections.custom.links.delete"
|
|
||||||
class="btn-flat delete-link"
|
|
||||||
role="cell"
|
|
||||||
/>
|
|
||||||
</div>
|
|
@ -1,69 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import discourseLater from "discourse-common/lib/later";
|
|
||||||
|
|
||||||
export default class SectionFormLink extends Component {
|
|
||||||
@service site;
|
|
||||||
@tracked dragCssClass;
|
|
||||||
|
|
||||||
dragCount = 0;
|
|
||||||
|
|
||||||
isAboveElement(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const target = event.currentTarget;
|
|
||||||
const domRect = target.getBoundingClientRect();
|
|
||||||
return event.offsetY < domRect.height / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dragHasStarted(event) {
|
|
||||||
event.dataTransfer.effectAllowed = "move";
|
|
||||||
this.args.setDraggedLinkCallback(this.args.link);
|
|
||||||
this.dragCssClass = "dragging";
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dragOver(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
if (this.dragCssClass !== "dragging") {
|
|
||||||
if (this.isAboveElement(event)) {
|
|
||||||
this.dragCssClass = "drag-above";
|
|
||||||
} else {
|
|
||||||
this.dragCssClass = "drag-below";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
dragEnter() {
|
|
||||||
this.dragCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dragLeave() {
|
|
||||||
this.dragCount--;
|
|
||||||
if (
|
|
||||||
this.dragCount === 0 &&
|
|
||||||
(this.dragCssClass === "drag-above" || this.dragCssClass === "drag-below")
|
|
||||||
) {
|
|
||||||
discourseLater(() => {
|
|
||||||
this.dragCssClass = null;
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dropItem(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.dragCount = 0;
|
|
||||||
this.args.reorderCallback(this.args.link, this.isAboveElement(event));
|
|
||||||
this.dragCssClass = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dragEnd() {
|
|
||||||
this.dragCount = 0;
|
|
||||||
this.dragCssClass = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,21 @@
|
|||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
|
||||||
|
const SidebarSectionHeader = <template>
|
||||||
|
{{#if @collapsable}}
|
||||||
|
<DButton
|
||||||
|
@title="sidebar.toggle_section"
|
||||||
|
@action={{@toggleSectionDisplay}}
|
||||||
|
aria-controls={{@sidebarSectionContentID}}
|
||||||
|
aria-expanded={{if @isExpanded "true" "false"}}
|
||||||
|
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
|
||||||
|
>
|
||||||
|
{{yield}}
|
||||||
|
</DButton>
|
||||||
|
{{else}}
|
||||||
|
<span class="sidebar-section-header">
|
||||||
|
{{yield}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarSectionHeader;
|
@ -1,15 +0,0 @@
|
|||||||
{{#if @collapsable}}
|
|
||||||
<DButton
|
|
||||||
@title="sidebar.toggle_section"
|
|
||||||
@action={{@toggleSectionDisplay}}
|
|
||||||
aria-controls={{@sidebarSectionContentID}}
|
|
||||||
aria-expanded={{if @isExpanded "true" "false"}}
|
|
||||||
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
|
|
||||||
>
|
|
||||||
{{yield}}
|
|
||||||
</DButton>
|
|
||||||
{{else}}
|
|
||||||
<span class="sidebar-section-header">
|
|
||||||
{{yield}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
@ -0,0 +1,22 @@
|
|||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
|
||||||
|
const SidebarSectionLinkButton = <template>
|
||||||
|
<div class="sidebar-section-link-wrapper">
|
||||||
|
<button
|
||||||
|
{{on "click" @action}}
|
||||||
|
type="button"
|
||||||
|
class="sidebar-section-link sidebar-row --link-button"
|
||||||
|
>
|
||||||
|
<span class="sidebar-section-link-prefix icon">
|
||||||
|
{{icon @icon}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="sidebar-section-link-content-text">
|
||||||
|
{{@text}}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarSectionLinkButton;
|
@ -1,15 +0,0 @@
|
|||||||
<div class="sidebar-section-link-wrapper">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="sidebar-section-link sidebar-row --link-button"
|
|
||||||
{{on "click" @action}}
|
|
||||||
>
|
|
||||||
<span class="sidebar-section-link-prefix icon">
|
|
||||||
{{d-icon @icon}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="sidebar-section-link-content-text">
|
|
||||||
{{@text}}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
@ -0,0 +1,74 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { concat } from "@ember/helper";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { eq } from "truth-helpers";
|
||||||
|
import { isHex } from "discourse/components/sidebar/section-link";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
|
||||||
|
export default class SidebarSectionLinkPrefix extends Component {
|
||||||
|
get prefixValue() {
|
||||||
|
if (!this.args.prefixType && !this.args.prefixValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.args.prefixType) {
|
||||||
|
case "span":
|
||||||
|
let hexValues = this.args.prefixValue;
|
||||||
|
|
||||||
|
hexValues = hexValues.reduce((acc, color) => {
|
||||||
|
const hexCode = isHex(color);
|
||||||
|
|
||||||
|
if (hexCode) {
|
||||||
|
acc.push(`#${hexCode} 50%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (hexValues.length === 1) {
|
||||||
|
hexValues.push(hexValues[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hexValues.join(", ");
|
||||||
|
default:
|
||||||
|
return this.args.prefixValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if @prefixType}}
|
||||||
|
<span
|
||||||
|
style={{if @prefixColor (htmlSafe (concat "color: " @prefixColor))}}
|
||||||
|
class={{concatClass
|
||||||
|
"sidebar-section-link-prefix"
|
||||||
|
@prefixType
|
||||||
|
@prefixCSSClass
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{#if (eq @prefixType "image")}}
|
||||||
|
<img src={{this.prefixValue}} class="prefix-image" />
|
||||||
|
{{else if (eq @prefixType "text")}}
|
||||||
|
<span class="prefix-text">
|
||||||
|
{{this.prefixValue}}
|
||||||
|
</span>
|
||||||
|
{{else if (eq @prefixType "icon")}}
|
||||||
|
{{icon this.prefixValue class="prefix-icon"}}
|
||||||
|
{{else if (eq @prefixType "span")}}
|
||||||
|
<span
|
||||||
|
style={{htmlSafe
|
||||||
|
(concat
|
||||||
|
"background: linear-gradient(90deg, " this.prefixValue ")"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
class="prefix-span"
|
||||||
|
></span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if @prefixBadge}}
|
||||||
|
{{icon @prefixBadge class="prefix-badge"}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
{{#if @prefixType}}
|
|
||||||
<span
|
|
||||||
class={{concat-class
|
|
||||||
"sidebar-section-link-prefix"
|
|
||||||
@prefixType
|
|
||||||
@prefixCSSClass
|
|
||||||
}}
|
|
||||||
style={{if @prefixColor (html-safe (concat "color: " @prefixColor))}}
|
|
||||||
>
|
|
||||||
|
|
||||||
{{#if (eq @prefixType "image")}}
|
|
||||||
<img src={{this.prefixValue}} class="prefix-image" />
|
|
||||||
{{else if (eq @prefixType "text")}}
|
|
||||||
<span class="prefix-text">
|
|
||||||
{{this.prefixValue}}
|
|
||||||
</span>
|
|
||||||
{{else if (eq @prefixType "icon")}}
|
|
||||||
{{d-icon this.prefixValue class="prefix-icon"}}
|
|
||||||
{{else if (eq @prefixType "span")}}
|
|
||||||
<span
|
|
||||||
style={{html-safe
|
|
||||||
(concat "background: linear-gradient(90deg, " this.prefixValue ")")
|
|
||||||
}}
|
|
||||||
class="prefix-span"
|
|
||||||
></span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @prefixBadge}}
|
|
||||||
{{d-icon @prefixBadge class="prefix-badge"}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
@ -1,33 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { isHex } from "discourse/components/sidebar/section-link";
|
|
||||||
|
|
||||||
export default class extends Component {
|
|
||||||
get prefixValue() {
|
|
||||||
if (!this.args.prefixType && !this.args.prefixValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.args.prefixType) {
|
|
||||||
case "span":
|
|
||||||
let hexValues = this.args.prefixValue;
|
|
||||||
|
|
||||||
hexValues = hexValues.reduce((acc, color) => {
|
|
||||||
const hexCode = isHex(color);
|
|
||||||
|
|
||||||
if (hexCode) {
|
|
||||||
acc.push(`#${hexCode} 50%`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (hexValues.length === 1) {
|
|
||||||
hexValues.push(hexValues[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hexValues.join(", ");
|
|
||||||
default:
|
|
||||||
return this.args.prefixValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,199 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { hash } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import { eq, or } from "truth-helpers";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
|
import SectionLinkPrefix from "./section-link-prefix";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given string is a valid color hex code.
|
||||||
|
*
|
||||||
|
* @param {String|undefined} input Input string to check if it is a valid color hex code. Can be in the form of "FFFFFF" or "#FFFFFF" or "FFF" or "#FFF".
|
||||||
|
* @returns {String|undefined} Returns the matching color hex code without the leading `#` if it is valid, otherwise returns undefined. Example: "FFFFFF" or "FFF".
|
||||||
|
*/
|
||||||
|
export function isHex(input) {
|
||||||
|
const match = input?.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return match[1];
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default class SectionLink extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.args.didInsert?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
super.willDestroy(...arguments);
|
||||||
|
this.args.willDestroy?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldDisplay() {
|
||||||
|
if (this.args.shouldDisplay === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.args.shouldDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
get linkClass() {
|
||||||
|
let classNames = ["sidebar-section-link", "sidebar-row"];
|
||||||
|
|
||||||
|
if (this.args.linkClass) {
|
||||||
|
classNames.push(this.args.linkClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.args.class) {
|
||||||
|
deprecated("SectionLink's @class arg has been renamed to @linkClass", {
|
||||||
|
id: "discourse.section-link-class-arg",
|
||||||
|
since: "3.2.0.beta4",
|
||||||
|
dropFrom: "3.3.0.beta1",
|
||||||
|
});
|
||||||
|
classNames.push(this.args.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classNames.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
get target() {
|
||||||
|
return this.currentUser?.user_option?.external_links_in_new_tab &&
|
||||||
|
this.isExternal
|
||||||
|
? "_blank"
|
||||||
|
: "_self";
|
||||||
|
}
|
||||||
|
|
||||||
|
get isExternal() {
|
||||||
|
return (
|
||||||
|
this.args.href &&
|
||||||
|
new URL(this.args.href, window.location.href).origin !==
|
||||||
|
window.location.origin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get models() {
|
||||||
|
if (this.args.model) {
|
||||||
|
return [this.args.model];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.args.models) {
|
||||||
|
return this.args.models;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get prefixColor() {
|
||||||
|
const hexCode = isHex(this.args.prefixColor);
|
||||||
|
|
||||||
|
if (hexCode) {
|
||||||
|
return `#${hexCode}`;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.shouldDisplay}}
|
||||||
|
<li
|
||||||
|
data-list-item-name={{@linkName}}
|
||||||
|
class="sidebar-section-link-wrapper"
|
||||||
|
...attributes
|
||||||
|
>
|
||||||
|
{{#if @href}}
|
||||||
|
<a
|
||||||
|
href={{@href}}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target={{this.target}}
|
||||||
|
title={{@title}}
|
||||||
|
data-link-name={{@linkName}}
|
||||||
|
class={{this.linkClass}}
|
||||||
|
>
|
||||||
|
<SectionLinkPrefix
|
||||||
|
@prefixType={{@prefixType}}
|
||||||
|
@prefixValue={{@prefixValue}}
|
||||||
|
@prefixCSSClass={{@prefixCSSClass}}
|
||||||
|
@prefixColor={{this.prefixColor}}
|
||||||
|
@prefixBadge={{@prefixBadge}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="sidebar-section-link-content-text">
|
||||||
|
{{@content}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo
|
||||||
|
@route={{@route}}
|
||||||
|
@query={{or @query (hash)}}
|
||||||
|
@models={{this.models}}
|
||||||
|
@current-when={{@currentWhen}}
|
||||||
|
title={{@title}}
|
||||||
|
data-link-name={{@linkName}}
|
||||||
|
class={{this.linkClass}}
|
||||||
|
>
|
||||||
|
<SectionLinkPrefix
|
||||||
|
@prefixType={{@prefixType}}
|
||||||
|
@prefixValue={{@prefixValue}}
|
||||||
|
@prefixCSSClass={{@prefixCSSClass}}
|
||||||
|
@prefixColor={{this.prefixColor}}
|
||||||
|
@prefixBadge={{@prefixBadge}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class={{concatClass
|
||||||
|
"sidebar-section-link-content-text"
|
||||||
|
@contentCSSClass
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{@content}}
|
||||||
|
<@contentComponent />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{#if @badgeText}}
|
||||||
|
<span class="sidebar-section-link-content-badge">
|
||||||
|
{{@badgeText}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if @suffixValue}}
|
||||||
|
<span
|
||||||
|
class={{concatClass
|
||||||
|
"sidebar-section-link-suffix"
|
||||||
|
@suffixType
|
||||||
|
@suffixCSSClass
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{#if (eq @suffixType "icon")}}
|
||||||
|
{{icon @suffixValue}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if @hoverValue}}
|
||||||
|
<span class="sidebar-section-link-hover">
|
||||||
|
<button
|
||||||
|
{{on "click" @hoverAction}}
|
||||||
|
type="button"
|
||||||
|
title={{@hoverTitle}}
|
||||||
|
class="sidebar-section-hover-button"
|
||||||
|
>
|
||||||
|
{{#if (eq @hoverType "icon")}}
|
||||||
|
{{icon @hoverValue class="hover-icon"}}
|
||||||
|
{{/if}}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
{{#if this.shouldDisplay}}
|
|
||||||
<li
|
|
||||||
class="sidebar-section-link-wrapper"
|
|
||||||
data-list-item-name={{@linkName}}
|
|
||||||
...attributes
|
|
||||||
>
|
|
||||||
{{#if @href}}
|
|
||||||
<a
|
|
||||||
href={{@href}}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
target={{this.target}}
|
|
||||||
title={{@title}}
|
|
||||||
data-link-name={{@linkName}}
|
|
||||||
class={{this.linkClass}}
|
|
||||||
>
|
|
||||||
<Sidebar::SectionLinkPrefix
|
|
||||||
@prefixType={{@prefixType}}
|
|
||||||
@prefixValue={{@prefixValue}}
|
|
||||||
@prefixCSSClass={{@prefixCSSClass}}
|
|
||||||
@prefixColor={{this.prefixColor}}
|
|
||||||
@prefixBadge={{@prefixBadge}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span class="sidebar-section-link-content-text">
|
|
||||||
{{@content}}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<LinkTo
|
|
||||||
@route={{@route}}
|
|
||||||
@query={{or @query (hash)}}
|
|
||||||
@models={{this.models}}
|
|
||||||
@current-when={{@currentWhen}}
|
|
||||||
title={{@title}}
|
|
||||||
data-link-name={{@linkName}}
|
|
||||||
class={{this.linkClass}}
|
|
||||||
>
|
|
||||||
|
|
||||||
<Sidebar::SectionLinkPrefix
|
|
||||||
@prefixType={{@prefixType}}
|
|
||||||
@prefixValue={{@prefixValue}}
|
|
||||||
@prefixCSSClass={{@prefixCSSClass}}
|
|
||||||
@prefixColor={{this.prefixColor}}
|
|
||||||
@prefixBadge={{@prefixBadge}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span
|
|
||||||
class={{concat-class
|
|
||||||
"sidebar-section-link-content-text"
|
|
||||||
@contentCSSClass
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{@content}}
|
|
||||||
{{@contentComponent}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{{#if @badgeText}}
|
|
||||||
<span class="sidebar-section-link-content-badge">
|
|
||||||
{{@badgeText}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @suffixValue}}
|
|
||||||
<span
|
|
||||||
class={{concat-class
|
|
||||||
"sidebar-section-link-suffix"
|
|
||||||
@suffixType
|
|
||||||
@suffixCSSClass
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{#if (eq @suffixType "icon")}}
|
|
||||||
{{d-icon @suffixValue}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @hoverValue}}
|
|
||||||
<span class="sidebar-section-link-hover">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
title={{@hoverTitle}}
|
|
||||||
class="sidebar-section-hover-button"
|
|
||||||
{{on "click" @hoverAction}}
|
|
||||||
>
|
|
||||||
{{#if (eq @hoverType "icon")}}
|
|
||||||
{{d-icon @hoverValue class="hover-icon"}}
|
|
||||||
{{/if}}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
@ -1,96 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a given string is a valid color hex code.
|
|
||||||
*
|
|
||||||
* @param {String|undefined} input Input string to check if it is a valid color hex code. Can be in the form of "FFFFFF" or "#FFFFFF" or "FFF" or "#FFF".
|
|
||||||
* @returns {String|undefined} Returns the matching color hex code without the leading `#` if it is valid, otherwise returns undefined. Example: "FFFFFF" or "FFF".
|
|
||||||
*/
|
|
||||||
export function isHex(input) {
|
|
||||||
const match = input?.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
return match[1];
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default class SectionLink extends Component {
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.args.didInsert?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy(...arguments);
|
|
||||||
this.args.willDestroy?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldDisplay() {
|
|
||||||
if (this.args.shouldDisplay === undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.args.shouldDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
get linkClass() {
|
|
||||||
let classNames = ["sidebar-section-link", "sidebar-row"];
|
|
||||||
|
|
||||||
if (this.args.linkClass) {
|
|
||||||
classNames.push(this.args.linkClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.args.class) {
|
|
||||||
deprecated("SectionLink's @class arg has been renamed to @linkClass", {
|
|
||||||
id: "discourse.section-link-class-arg",
|
|
||||||
since: "3.2.0.beta4",
|
|
||||||
dropFrom: "3.3.0.beta1",
|
|
||||||
});
|
|
||||||
classNames.push(this.args.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
return classNames.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
get target() {
|
|
||||||
return this.currentUser?.user_option?.external_links_in_new_tab &&
|
|
||||||
this.isExternal
|
|
||||||
? "_blank"
|
|
||||||
: "_self";
|
|
||||||
}
|
|
||||||
|
|
||||||
get isExternal() {
|
|
||||||
return (
|
|
||||||
this.args.href &&
|
|
||||||
new URL(this.args.href, window.location.href).origin !==
|
|
||||||
window.location.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get models() {
|
|
||||||
if (this.args.model) {
|
|
||||||
return [this.args.model];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.args.models) {
|
|
||||||
return this.args.models;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get prefixColor() {
|
|
||||||
const hexCode = isHex(this.args.prefixColor);
|
|
||||||
|
|
||||||
if (hexCode) {
|
|
||||||
return `#${hexCode}`;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,9 @@
|
|||||||
|
const SidebarSectionMessage = <template>
|
||||||
|
<div class="sidebar-section-message-wrapper sidebar-row">
|
||||||
|
<div class="sidebar-section-message">
|
||||||
|
{{yield}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarSectionMessage;
|
@ -1,5 +0,0 @@
|
|||||||
<div class="sidebar-section-message-wrapper sidebar-row">
|
|
||||||
<div class="sidebar-section-message">
|
|
||||||
{{yield}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -0,0 +1,177 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { 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 icon from "discourse-common/helpers/d-icon";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||||
|
import DTooltip from "float-kit/components/d-tooltip";
|
||||||
|
import SectionHeader from "./section-header";
|
||||||
|
|
||||||
|
export default class SidebarSection extends Component {
|
||||||
|
@service keyValueStore;
|
||||||
|
@service sidebarState;
|
||||||
|
|
||||||
|
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`;
|
||||||
|
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
super.willDestroy(...arguments);
|
||||||
|
this.args.willDestroy?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCollapsed() {
|
||||||
|
if (!this.args.collapsable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
|
||||||
|
) {
|
||||||
|
return this.args.collapsedByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === "true"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
setExpandedState() {
|
||||||
|
if (this.isCollapsed) {
|
||||||
|
this.sidebarState.collapseSection(this.args.sectionName);
|
||||||
|
} else {
|
||||||
|
this.sidebarState.expandSection(this.args.sectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get displaySectionContent() {
|
||||||
|
return !this.sidebarState.collapsedSections.has(
|
||||||
|
this.collapsedSidebarSectionKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleSectionDisplay() {
|
||||||
|
if (this.displaySectionContent) {
|
||||||
|
this.sidebarState.collapseSection(this.args.sectionName);
|
||||||
|
} else {
|
||||||
|
this.sidebarState.expandSection(this.args.sectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove focus from the toggle, but only on click
|
||||||
|
if (!event.key) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handleMultipleHeaderActions(id) {
|
||||||
|
this.args.headerActions
|
||||||
|
.find((headerAction) => headerAction.id === id)
|
||||||
|
.action();
|
||||||
|
}
|
||||||
|
|
||||||
|
get headerCaretIcon() {
|
||||||
|
return this.displaySectionContent ? "angle-down" : "angle-right";
|
||||||
|
}
|
||||||
|
|
||||||
|
get isSingleHeaderAction() {
|
||||||
|
return this.args.headerActions?.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isMultipleHeaderActions() {
|
||||||
|
return this.args.headerActions?.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get displaySection() {
|
||||||
|
if (this.args.displaySection === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.args.displaySection;
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.displaySection}}
|
||||||
|
<div
|
||||||
|
{{didInsert this.setExpandedState}}
|
||||||
|
data-section-name={{@sectionName}}
|
||||||
|
class="sidebar-section-wrapper sidebar-section"
|
||||||
|
...attributes
|
||||||
|
>
|
||||||
|
{{#unless @hideSectionHeader}}
|
||||||
|
<div class="sidebar-section-header-wrapper sidebar-row">
|
||||||
|
<SectionHeader
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
@sidebarSectionContentID={{this.sidebarSectionContentID}}
|
||||||
|
@toggleSectionDisplay={{this.toggleSectionDisplay}}
|
||||||
|
@isExpanded={{this.displaySectionContent}}
|
||||||
|
>
|
||||||
|
{{#if @collapsable}}
|
||||||
|
<span class="sidebar-section-header-caret">
|
||||||
|
{{icon this.headerCaretIcon}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="sidebar-section-header-text">
|
||||||
|
{{@headerLinkText}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{#if @indicatePublic}}
|
||||||
|
<DTooltip
|
||||||
|
@icon="globe"
|
||||||
|
class="sidebar-section-header-global-indicator"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="sidebar-section-header-global-indicator__content"
|
||||||
|
>
|
||||||
|
{{icon "shield-alt"}}
|
||||||
|
{{i18n "sidebar.sections.global_section"}}
|
||||||
|
</span>
|
||||||
|
</DTooltip>
|
||||||
|
{{/if}}
|
||||||
|
</SectionHeader>
|
||||||
|
|
||||||
|
{{#if this.isSingleHeaderAction}}
|
||||||
|
{{#each @headerActions as |headerAction|}}
|
||||||
|
<button
|
||||||
|
{{on "click" headerAction.action}}
|
||||||
|
type="button"
|
||||||
|
title={{headerAction.title}}
|
||||||
|
class="sidebar-section-header-button"
|
||||||
|
>
|
||||||
|
{{icon @headerActionsIcon}}
|
||||||
|
</button>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.isMultipleHeaderActions}}
|
||||||
|
<DropdownSelectBox
|
||||||
|
@options={{hash
|
||||||
|
icon=@headerActionsIcon
|
||||||
|
placementStrategy="absolute"
|
||||||
|
}}
|
||||||
|
@content={{@headerActions}}
|
||||||
|
@onChange={{this.handleMultipleHeaderActions}}
|
||||||
|
class="sidebar-section-header-dropdown"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if this.displaySectionContent}}
|
||||||
|
<ul
|
||||||
|
id={{this.sidebarSectionContentID}}
|
||||||
|
class="sidebar-section-content"
|
||||||
|
>
|
||||||
|
{{yield}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,73 +0,0 @@
|
|||||||
{{#if this.displaySection}}
|
|
||||||
<div
|
|
||||||
data-section-name={{@sectionName}}
|
|
||||||
class="sidebar-section-wrapper sidebar-section"
|
|
||||||
...attributes
|
|
||||||
{{did-insert this.setExpandedState}}
|
|
||||||
>
|
|
||||||
{{#unless @hideSectionHeader}}
|
|
||||||
<div class="sidebar-section-header-wrapper sidebar-row">
|
|
||||||
<Sidebar::SectionHeader
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
@sidebarSectionContentID={{this.sidebarSectionContentID}}
|
|
||||||
@toggleSectionDisplay={{this.toggleSectionDisplay}}
|
|
||||||
@isExpanded={{this.displaySectionContent}}
|
|
||||||
>
|
|
||||||
{{#if @collapsable}}
|
|
||||||
<span class="sidebar-section-header-caret">
|
|
||||||
{{d-icon this.headerCaretIcon}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<span class="sidebar-section-header-text">
|
|
||||||
{{@headerLinkText}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{{#if @indicatePublic}}
|
|
||||||
<DTooltip
|
|
||||||
@icon="globe"
|
|
||||||
class="sidebar-section-header-global-indicator"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="sidebar-section-header-global-indicator__content"
|
|
||||||
>{{d-icon "shield-alt"}}{{i18n
|
|
||||||
"sidebar.sections.global_section"
|
|
||||||
}}</span>
|
|
||||||
</DTooltip>
|
|
||||||
{{/if}}
|
|
||||||
</Sidebar::SectionHeader>
|
|
||||||
|
|
||||||
{{#if this.isSingleHeaderAction}}
|
|
||||||
{{#each @headerActions as |headerAction|}}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="sidebar-section-header-button"
|
|
||||||
{{on "click" headerAction.action}}
|
|
||||||
title={{headerAction.title}}
|
|
||||||
>
|
|
||||||
{{d-icon @headerActionsIcon}}
|
|
||||||
</button>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.isMultipleHeaderActions}}
|
|
||||||
<DropdownSelectBox
|
|
||||||
@options={{hash
|
|
||||||
icon=@headerActionsIcon
|
|
||||||
placementStrategy="absolute"
|
|
||||||
}}
|
|
||||||
@content={{@headerActions}}
|
|
||||||
@onChange={{this.handleMultipleHeaderActions}}
|
|
||||||
class="sidebar-section-header-dropdown"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if this.displaySectionContent}}
|
|
||||||
<ul class="sidebar-section-content" id={{this.sidebarSectionContentID}}>
|
|
||||||
{{yield}}
|
|
||||||
</ul>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
@ -1,87 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default class SidebarSection extends Component {
|
|
||||||
@service keyValueStore;
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`;
|
|
||||||
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
|
|
||||||
|
|
||||||
get isCollapsed() {
|
|
||||||
if (!this.args.collapsable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
|
|
||||||
) {
|
|
||||||
return this.args.collapsedByDefault;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === "true"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
setExpandedState() {
|
|
||||||
if (this.isCollapsed) {
|
|
||||||
this.sidebarState.collapseSection(this.args.sectionName);
|
|
||||||
} else {
|
|
||||||
this.sidebarState.expandSection(this.args.sectionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get displaySectionContent() {
|
|
||||||
return !this.sidebarState.collapsedSections.has(
|
|
||||||
this.collapsedSidebarSectionKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy(...arguments);
|
|
||||||
this.args.willDestroy?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleSectionDisplay() {
|
|
||||||
if (this.displaySectionContent) {
|
|
||||||
this.sidebarState.collapseSection(this.args.sectionName);
|
|
||||||
} else {
|
|
||||||
this.sidebarState.expandSection(this.args.sectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove focus from the toggle, but only on click
|
|
||||||
if (!event.key) {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
handleMultipleHeaderActions(id) {
|
|
||||||
this.args.headerActions
|
|
||||||
.find((headerAction) => headerAction.id === id)
|
|
||||||
.action();
|
|
||||||
}
|
|
||||||
|
|
||||||
get headerCaretIcon() {
|
|
||||||
return this.displaySectionContent ? "angle-down" : "angle-right";
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSingleHeaderAction() {
|
|
||||||
return this.args.headerActions?.length === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isMultipleHeaderActions() {
|
|
||||||
return this.args.headerActions?.length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displaySection() {
|
|
||||||
if (this.args.displaySection === undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.args.displaySection;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
import AnonymousSections from "./anonymous/sections";
|
||||||
|
import UserSections from "./user/sections";
|
||||||
|
|
||||||
|
const SidebarSections = <template>
|
||||||
|
{{#if @currentUser}}
|
||||||
|
<UserSections
|
||||||
|
@collapsableSections={{@collapsableSections}}
|
||||||
|
@panel={{@panel}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<AnonymousSections @collapsableSections={{@collapsableSections}} />
|
||||||
|
{{/if}}
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default SidebarSections;
|
@ -1,8 +0,0 @@
|
|||||||
{{#if @currentUser}}
|
|
||||||
<Sidebar::User::Sections
|
|
||||||
@collapsableSections={{@collapsableSections}}
|
|
||||||
@panel={{@panel}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<Sidebar::Anonymous::Sections @collapsableSections={{@collapsableSections}} />
|
|
||||||
{{/if}}
|
|
@ -1,13 +1,16 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
import { defaultHomepage } from "discourse/lib/utilities";
|
import { defaultHomepage } from "discourse/lib/utilities";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
|
||||||
export default class SwitchPanelButtons extends Component {
|
export default class SwitchPanelButtons extends Component {
|
||||||
@service router;
|
@service router;
|
||||||
@service sidebarState;
|
@service sidebarState;
|
||||||
|
|
||||||
@tracked currentPanel;
|
@tracked currentPanel;
|
||||||
@tracked isSwitching = false;
|
@tracked isSwitching = false;
|
||||||
|
|
||||||
@ -40,4 +43,17 @@ export default class SwitchPanelButtons extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#each @buttons as |button|}}
|
||||||
|
<DButton
|
||||||
|
@action={{fn this.switchPanel button}}
|
||||||
|
@icon={{button.switchButtonIcon}}
|
||||||
|
@disabled={{this.isSwitching}}
|
||||||
|
@translatedLabel={{button.switchButtonLabel}}
|
||||||
|
data-key={{button.key}}
|
||||||
|
class="btn-default sidebar__panel-switch-button"
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,10 +0,0 @@
|
|||||||
{{#each @buttons as |button|}}
|
|
||||||
<DButton
|
|
||||||
@action={{fn this.switchPanel button}}
|
|
||||||
@icon={{button.switchButtonIcon}}
|
|
||||||
@disabled={{this.isSwitching}}
|
|
||||||
@translatedLabel={{button.switchButtonLabel}}
|
|
||||||
data-key={{button.key}}
|
|
||||||
class="btn-default sidebar__panel-switch-button"
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
@ -0,0 +1,124 @@
|
|||||||
|
import { cached } from "@glimmer/tracking";
|
||||||
|
import { array, hash } from "@ember/helper";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import { hasDefaultSidebarCategories } from "discourse/lib/sidebar/helpers";
|
||||||
|
import Category from "discourse/models/category";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import { debounce } from "discourse-common/utils/decorators";
|
||||||
|
import AllCategoriesSectionLink from "../common/all-categories-section-link";
|
||||||
|
import CommonCategoriesSection from "../common/categories-section";
|
||||||
|
import EditNavigationMenuCategoriesModal from "../edit-navigation-menu/categories-modal";
|
||||||
|
import Section from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
|
export const REFRESH_COUNTS_APP_EVENT_NAME =
|
||||||
|
"sidebar:refresh-categories-section-counts";
|
||||||
|
|
||||||
|
export default class SidebarUserCategoriesSection extends CommonCategoriesSection {
|
||||||
|
@service appEvents;
|
||||||
|
@service currentUser;
|
||||||
|
@service modal;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.callbackId = this.topicTrackingState.onStateChange(() => {
|
||||||
|
this._refreshCounts();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.appEvents.on(REFRESH_COUNTS_APP_EVENT_NAME, this, this._refreshCounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
super.willDestroy(...arguments);
|
||||||
|
|
||||||
|
this.topicTrackingState.offStateChange(this.callbackId);
|
||||||
|
|
||||||
|
this.appEvents.off(
|
||||||
|
REFRESH_COUNTS_APP_EVENT_NAME,
|
||||||
|
this,
|
||||||
|
this._refreshCounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TopicTrackingState changes or plugins can trigger this function so we debounce to ensure we're not refreshing
|
||||||
|
// unnecessarily.
|
||||||
|
@debounce(300)
|
||||||
|
_refreshCounts() {
|
||||||
|
this.sectionLinks.forEach((sectionLink) => sectionLink.refreshCounts());
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached
|
||||||
|
get categories() {
|
||||||
|
if (this.currentUser.sidebarCategoryIds?.length > 0) {
|
||||||
|
return Category.findByIds(this.currentUser.sidebarCategoryIds);
|
||||||
|
} else {
|
||||||
|
return this.topSiteCategories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldDisplayDefaultConfig() {
|
||||||
|
return this.currentUser.admin && !this.hasDefaultSidebarCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasDefaultSidebarCategories() {
|
||||||
|
return hasDefaultSidebarCategories(this.siteSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
showModal() {
|
||||||
|
this.modal.show(EditNavigationMenuCategoriesModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Section
|
||||||
|
@sectionName="categories"
|
||||||
|
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
||||||
|
@headerActions={{array
|
||||||
|
(hash
|
||||||
|
action=this.showModal
|
||||||
|
title=(i18n "sidebar.sections.categories.header_action_title")
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@headerActionsIcon="pencil-alt"
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
>
|
||||||
|
|
||||||
|
{{#each this.sectionLinks as |sectionLink|}}
|
||||||
|
<SectionLink
|
||||||
|
@route={{sectionLink.route}}
|
||||||
|
@query={{sectionLink.query}}
|
||||||
|
@title={{sectionLink.title}}
|
||||||
|
@content={{sectionLink.text}}
|
||||||
|
@currentWhen={{sectionLink.currentWhen}}
|
||||||
|
@model={{sectionLink.model}}
|
||||||
|
@badgeText={{sectionLink.badgeText}}
|
||||||
|
@prefixBadge={{sectionLink.prefixBadge}}
|
||||||
|
@prefixType={{sectionLink.prefixType}}
|
||||||
|
@prefixValue={{sectionLink.prefixValue}}
|
||||||
|
@prefixColor={{sectionLink.prefixColor}}
|
||||||
|
@suffixCSSClass={{sectionLink.suffixCSSClass}}
|
||||||
|
@suffixValue={{sectionLink.suffixValue}}
|
||||||
|
@suffixType={{sectionLink.suffixType}}
|
||||||
|
data-category-id={{sectionLink.category.id}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<AllCategoriesSectionLink />
|
||||||
|
|
||||||
|
{{#if this.shouldDisplayDefaultConfig}}
|
||||||
|
<SectionLink
|
||||||
|
@linkName="configure-default-navigation-menu-categories"
|
||||||
|
@content={{i18n "sidebar.sections.categories.configure_defaults"}}
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue="wrench"
|
||||||
|
@route="adminSiteSettingsCategory"
|
||||||
|
@model="sidebar"
|
||||||
|
@query={{hash filter="default_navigation_menu_categories"}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,49 +0,0 @@
|
|||||||
<Sidebar::Section
|
|
||||||
@sectionName="categories"
|
|
||||||
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
|
|
||||||
@headerActions={{array
|
|
||||||
(hash
|
|
||||||
action=this.showModal
|
|
||||||
title=(i18n "sidebar.sections.categories.header_action_title")
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
@headerActionsIcon="pencil-alt"
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
>
|
|
||||||
|
|
||||||
{{#if (gt this.sectionLinks.length 0)}}
|
|
||||||
{{#each this.sectionLinks as |sectionLink|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@route={{sectionLink.route}}
|
|
||||||
@query={{sectionLink.query}}
|
|
||||||
@title={{sectionLink.title}}
|
|
||||||
@content={{sectionLink.text}}
|
|
||||||
@currentWhen={{sectionLink.currentWhen}}
|
|
||||||
@model={{sectionLink.model}}
|
|
||||||
@badgeText={{sectionLink.badgeText}}
|
|
||||||
@prefixBadge={{sectionLink.prefixBadge}}
|
|
||||||
@prefixType={{sectionLink.prefixType}}
|
|
||||||
@prefixValue={{sectionLink.prefixValue}}
|
|
||||||
@prefixColor={{sectionLink.prefixColor}}
|
|
||||||
@suffixCSSClass={{sectionLink.suffixCSSClass}}
|
|
||||||
@suffixValue={{sectionLink.suffixValue}}
|
|
||||||
@suffixType={{sectionLink.suffixType}}
|
|
||||||
data-category-id={{sectionLink.category.id}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Sidebar::Common::AllCategoriesSectionLink />
|
|
||||||
|
|
||||||
{{#if this.shouldDisplayDefaultConfig}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@linkName="configure-default-navigation-menu-categories"
|
|
||||||
@content={{i18n "sidebar.sections.categories.configure_defaults"}}
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue="wrench"
|
|
||||||
@route="adminSiteSettingsCategory"
|
|
||||||
@model="sidebar"
|
|
||||||
@query={{hash filter="default_navigation_menu_categories"}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</Sidebar::Section>
|
|
@ -1,71 +0,0 @@
|
|||||||
import { cached } from "@glimmer/tracking";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import SidebarCommonCategoriesSection from "discourse/components/sidebar/common/categories-section";
|
|
||||||
import SidebarEditNavigationMenuCategoriesModal from "discourse/components/sidebar/edit-navigation-menu/categories-modal";
|
|
||||||
import { hasDefaultSidebarCategories } from "discourse/lib/sidebar/helpers";
|
|
||||||
import Category from "discourse/models/category";
|
|
||||||
import { debounce } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export const REFRESH_COUNTS_APP_EVENT_NAME =
|
|
||||||
"sidebar:refresh-categories-section-counts";
|
|
||||||
|
|
||||||
export default class SidebarUserCategoriesSection extends SidebarCommonCategoriesSection {
|
|
||||||
@service appEvents;
|
|
||||||
@service currentUser;
|
|
||||||
@service modal;
|
|
||||||
@service router;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
|
|
||||||
this.callbackId = this.topicTrackingState.onStateChange(() => {
|
|
||||||
this._refreshCounts();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.appEvents.on(REFRESH_COUNTS_APP_EVENT_NAME, this, this._refreshCounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy(...arguments);
|
|
||||||
|
|
||||||
this.topicTrackingState.offStateChange(this.callbackId);
|
|
||||||
|
|
||||||
this.appEvents.off(
|
|
||||||
REFRESH_COUNTS_APP_EVENT_NAME,
|
|
||||||
this,
|
|
||||||
this._refreshCounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TopicTrackingState changes or plugins can trigger this function so we debounce to ensure we're not refreshing
|
|
||||||
// unnecessarily.
|
|
||||||
@debounce(300)
|
|
||||||
_refreshCounts() {
|
|
||||||
this.sectionLinks.forEach((sectionLink) => {
|
|
||||||
sectionLink.refreshCounts();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached
|
|
||||||
get categories() {
|
|
||||||
if (this.currentUser.sidebarCategoryIds?.length > 0) {
|
|
||||||
return Category.findByIds(this.currentUser.sidebarCategoryIds);
|
|
||||||
} else {
|
|
||||||
return this.topSiteCategories;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldDisplayDefaultConfig() {
|
|
||||||
return this.currentUser.admin && !this.hasDefaultSidebarCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasDefaultSidebarCategories() {
|
|
||||||
return hasDefaultSidebarCategories(this.siteSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
showModal() {
|
|
||||||
this.modal.show(SidebarEditNavigationMenuCategoriesModal);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +1,21 @@
|
|||||||
import SidebarCustomSection from "discourse/components/sidebar/common/custom-sections";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import SidebarCustomSection from "../common/custom-sections";
|
||||||
|
|
||||||
export default class SidebarUserCustomSections extends SidebarCustomSection {
|
export default class SidebarUserCustomSections extends SidebarCustomSection {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
|
|
||||||
this.messageBus.subscribe("/refresh-sidebar-sections", this._refresh);
|
this.messageBus.subscribe("/refresh-sidebar-sections", this._refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
willDestroy() {
|
willDestroy() {
|
||||||
|
super.willDestroy(...arguments);
|
||||||
this.messageBus.unsubscribe("/refresh-sidebar-sections");
|
this.messageBus.unsubscribe("/refresh-sidebar-sections");
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
_refresh() {
|
async _refresh() {
|
||||||
return ajax("/sidebar_sections.json", {}).then((json) => {
|
const json = await ajax("/sidebar_sections.json", {});
|
||||||
this.currentUser.set("sidebar_sections", json.sidebar_sections);
|
this.currentUser.set("sidebar_sections", json.sidebar_sections);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,15 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { cached } from "@glimmer/tracking";
|
import { cached } from "@glimmer/tracking";
|
||||||
import { getOwner } from "@ember/application";
|
import { array, fn, hash } from "@ember/helper";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
import routeAction from "discourse/helpers/route-action";
|
||||||
import GroupMessageSectionLink from "discourse/lib/sidebar/user/messages-section/group-message-section-link";
|
import GroupMessageSectionLink from "discourse/lib/sidebar/user/messages-section/group-message-section-link";
|
||||||
import PersonalMessageSectionLink from "discourse/lib/sidebar/user/messages-section/personal-message-section-link";
|
import PersonalMessageSectionLink from "discourse/lib/sidebar/user/messages-section/personal-message-section-link";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import Section from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
export const INBOX = "inbox";
|
export const INBOX = "inbox";
|
||||||
export const UNREAD = "unread";
|
export const UNREAD = "unread";
|
||||||
@ -28,6 +33,8 @@ export default class SidebarUserMessagesSection extends Component {
|
|||||||
@service currentUser;
|
@service currentUser;
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
|
_pmTopicTrackingStateKey = "messages-section";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
|
|
||||||
@ -37,21 +44,12 @@ export default class SidebarUserMessagesSection extends Component {
|
|||||||
this._refreshSectionLinksDisplayState
|
this._refreshSectionLinksDisplayState
|
||||||
);
|
);
|
||||||
|
|
||||||
this._pmTopicTrackingStateKey = "messages-section";
|
|
||||||
|
|
||||||
this.pmTopicTrackingState.onStateChange(
|
this.pmTopicTrackingState.onStateChange(
|
||||||
this._pmTopicTrackingStateKey,
|
this._pmTopicTrackingStateKey,
|
||||||
this._refreshSectionLinkCounts
|
this._refreshSectionLinkCounts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
|
||||||
_refreshSectionLinkCounts() {
|
|
||||||
for (const sectionLink of this.allSectionLinks) {
|
|
||||||
sectionLink.refreshCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
willDestroy() {
|
||||||
super.willDestroy(...arguments);
|
super.willDestroy(...arguments);
|
||||||
|
|
||||||
@ -67,6 +65,13 @@ export default class SidebarUserMessagesSection extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
_refreshSectionLinkCounts() {
|
||||||
|
for (const sectionLink of this.allSectionLinks) {
|
||||||
|
sectionLink.refreshCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_refreshSectionLinksDisplayState() {
|
_refreshSectionLinksDisplayState() {
|
||||||
const currentRouteName = this.router.currentRoute.name;
|
const currentRouteName = this.router.currentRoute.name;
|
||||||
const currentRouteParentName = this.router.currentRoute.parent.name;
|
const currentRouteParentName = this.router.currentRoute.parent.name;
|
||||||
@ -144,4 +149,53 @@ export default class SidebarUserMessagesSection extends Component {
|
|||||||
...this.personalMessagesSectionLinks,
|
...this.personalMessagesSectionLinks,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Section
|
||||||
|
@sectionName="messages"
|
||||||
|
@headerActionIcon="plus"
|
||||||
|
@headerActions={{array
|
||||||
|
(hash
|
||||||
|
action=(fn (routeAction "composePrivateMessage") null null)
|
||||||
|
title=(i18n "sidebar.sections.messages.header_action_title")
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@headerActionsIcon="plus"
|
||||||
|
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
>
|
||||||
|
{{#each
|
||||||
|
this.personalMessagesSectionLinks
|
||||||
|
as |personalMessageSectionLink|
|
||||||
|
}}
|
||||||
|
{{#if personalMessageSectionLink.shouldDisplay}}
|
||||||
|
<SectionLink
|
||||||
|
@linkName={{personalMessageSectionLink.name}}
|
||||||
|
@linkClass={{personalMessageSectionLink.class}}
|
||||||
|
@route={{personalMessageSectionLink.route}}
|
||||||
|
@model={{personalMessageSectionLink.model}}
|
||||||
|
@prefixType={{personalMessageSectionLink.prefixType}}
|
||||||
|
@prefixValue={{personalMessageSectionLink.prefixValue}}
|
||||||
|
@currentWhen={{personalMessageSectionLink.currentWhen}}
|
||||||
|
@content={{personalMessageSectionLink.text}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
|
||||||
|
{{#if groupMessageSectionLink.shouldDisplay}}
|
||||||
|
<SectionLink
|
||||||
|
@linkName={{groupMessageSectionLink.name}}
|
||||||
|
@linkClass={{groupMessageSectionLink.class}}
|
||||||
|
@route={{groupMessageSectionLink.route}}
|
||||||
|
@prefixType={{groupMessageSectionLink.prefixType}}
|
||||||
|
@prefixValue={{groupMessageSectionLink.prefixValue}}
|
||||||
|
@models={{groupMessageSectionLink.models}}
|
||||||
|
@currentWhen={{groupMessageSectionLink.currentWhen}}
|
||||||
|
@content={{groupMessageSectionLink.text}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,45 +0,0 @@
|
|||||||
<Sidebar::Section
|
|
||||||
@sectionName="messages"
|
|
||||||
@headerActionIcon="plus"
|
|
||||||
@headerActions={{array
|
|
||||||
(hash
|
|
||||||
action=(fn (route-action "composePrivateMessage") null null)
|
|
||||||
title=(i18n "sidebar.sections.messages.header_action_title")
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
@headerActionsIcon="plus"
|
|
||||||
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
>
|
|
||||||
{{#each this.personalMessagesSectionLinks as |personalMessageSectionLink|}}
|
|
||||||
{{#if personalMessageSectionLink.shouldDisplay}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@linkName={{personalMessageSectionLink.name}}
|
|
||||||
@linkClass={{personalMessageSectionLink.class}}
|
|
||||||
@route={{personalMessageSectionLink.route}}
|
|
||||||
@model={{personalMessageSectionLink.model}}
|
|
||||||
@prefixType={{personalMessageSectionLink.prefixType}}
|
|
||||||
@prefixValue={{personalMessageSectionLink.prefixValue}}
|
|
||||||
@currentWhen={{personalMessageSectionLink.currentWhen}}
|
|
||||||
@content={{personalMessageSectionLink.text}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if (gt this.groupMessagesSectionLinks.length 0)}}
|
|
||||||
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
|
|
||||||
{{#if groupMessageSectionLink.shouldDisplay}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@linkName={{groupMessageSectionLink.name}}
|
|
||||||
@linkClass={{groupMessageSectionLink.class}}
|
|
||||||
@route={{groupMessageSectionLink.route}}
|
|
||||||
@prefixType={{groupMessageSectionLink.prefixType}}
|
|
||||||
@prefixValue={{groupMessageSectionLink.prefixValue}}
|
|
||||||
@models={{groupMessageSectionLink.models}}
|
|
||||||
@currentWhen={{groupMessageSectionLink.currentWhen}}
|
|
||||||
@content={{groupMessageSectionLink.text}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
</Sidebar::Section>
|
|
@ -0,0 +1,28 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ApiSections from "../api-sections";
|
||||||
|
import CategoriesSection from "./categories-section";
|
||||||
|
import CustomSections from "./custom-sections";
|
||||||
|
import MessagesSection from "./messages-section";
|
||||||
|
import TagsSection from "./tags-section";
|
||||||
|
|
||||||
|
export default class SidebarUserSections extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-sections">
|
||||||
|
<CustomSections @collapsable={{@collapsableSections}} />
|
||||||
|
<CategoriesSection @collapsable={{@collapsableSections}} />
|
||||||
|
|
||||||
|
{{#if this.currentUser.display_sidebar_tags}}
|
||||||
|
<TagsSection @collapsable={{@collapsableSections}} />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.currentUser.can_send_private_messages}}
|
||||||
|
<MessagesSection @collapsable={{@collapsableSections}} />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<ApiSections @collapsable={{@collapsableSections}} />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
<div class="sidebar-sections">
|
|
||||||
<Sidebar::User::CustomSections @collapsable={{@collapsableSections}} />
|
|
||||||
<Sidebar::User::CategoriesSection @collapsable={{@collapsableSections}} />
|
|
||||||
|
|
||||||
{{#if this.currentUser.display_sidebar_tags}}
|
|
||||||
<Sidebar::User::TagsSection @collapsable={{@collapsableSections}} />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.enableMessagesSection}}
|
|
||||||
<Sidebar::User::MessagesSection @collapsable={{@collapsableSections}} />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Sidebar::ApiSections @collapsable={{@collapsableSections}} />
|
|
||||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class SidebarUserSections extends Component {
|
|
||||||
@service siteSettings;
|
|
||||||
@service currentUser;
|
|
||||||
@service site;
|
|
||||||
|
|
||||||
get enableMessagesSection() {
|
|
||||||
return this.currentUser?.can_send_private_messages;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,16 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { cached } from "@glimmer/tracking";
|
import { cached } from "@glimmer/tracking";
|
||||||
|
import { array, hash } from "@ember/helper";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import SidebarEditNavigationMenuTagsModal from "discourse/components/sidebar/edit-navigation-menu/tags-modal";
|
import SidebarEditNavigationMenuTagsModal from "discourse/components/sidebar/edit-navigation-menu/tags-modal";
|
||||||
import { hasDefaultSidebarTags } from "discourse/lib/sidebar/helpers";
|
import { hasDefaultSidebarTags } from "discourse/lib/sidebar/helpers";
|
||||||
import PMTagSectionLink from "discourse/lib/sidebar/user/tags-section/pm-tag-section-link";
|
import PMTagSectionLink from "discourse/lib/sidebar/user/tags-section/pm-tag-section-link";
|
||||||
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
|
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
|
||||||
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
|
import AllTagsSectionLink from "../common/all-tags-section-link";
|
||||||
|
import Section from "../section";
|
||||||
|
import SectionLink from "../section-link";
|
||||||
|
|
||||||
export default class SidebarUserTagsSection extends Component {
|
export default class SidebarUserTagsSection extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@ -36,7 +41,6 @@ export default class SidebarUserTagsSection extends Component {
|
|||||||
@cached
|
@cached
|
||||||
get sectionLinks() {
|
get sectionLinks() {
|
||||||
const links = [];
|
const links = [];
|
||||||
|
|
||||||
let tags;
|
let tags;
|
||||||
|
|
||||||
if (this.currentUser.sidebarTags.length > 0) {
|
if (this.currentUser.sidebarTags.length > 0) {
|
||||||
@ -79,4 +83,51 @@ export default class SidebarUserTagsSection extends Component {
|
|||||||
showModal() {
|
showModal() {
|
||||||
this.modal.show(SidebarEditNavigationMenuTagsModal);
|
this.modal.show(SidebarEditNavigationMenuTagsModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Section
|
||||||
|
@sectionName="tags"
|
||||||
|
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
||||||
|
@headerActions={{array
|
||||||
|
(hash
|
||||||
|
action=this.showModal
|
||||||
|
title=(i18n "sidebar.sections.tags.header_action_title")
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@headerActionsIcon="pencil-alt"
|
||||||
|
@collapsable={{@collapsable}}
|
||||||
|
>
|
||||||
|
{{#each this.sectionLinks as |sectionLink|}}
|
||||||
|
<SectionLink
|
||||||
|
@route={{sectionLink.route}}
|
||||||
|
@title={{sectionLink.title}}
|
||||||
|
@content={{sectionLink.text}}
|
||||||
|
@currentWhen={{sectionLink.currentWhen}}
|
||||||
|
@prefixType={{sectionLink.prefixType}}
|
||||||
|
@prefixValue={{sectionLink.prefixValue}}
|
||||||
|
@prefixColor={{sectionLink.prefixColor}}
|
||||||
|
@badgeText={{sectionLink.badgeText}}
|
||||||
|
@models={{sectionLink.models}}
|
||||||
|
@suffixCSSClass={{sectionLink.suffixCSSClass}}
|
||||||
|
@suffixValue={{sectionLink.suffixValue}}
|
||||||
|
@suffixType={{sectionLink.suffixType}}
|
||||||
|
data-tag-name={{sectionLink.tagName}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<AllTagsSectionLink />
|
||||||
|
|
||||||
|
{{#if this.shouldDisplayDefaultConfig}}
|
||||||
|
<SectionLink
|
||||||
|
@linkName="configure-default-navigation-menu-tags"
|
||||||
|
@content={{i18n "sidebar.sections.tags.configure_defaults"}}
|
||||||
|
@prefixType="icon"
|
||||||
|
@prefixValue="wrench"
|
||||||
|
@route="adminSiteSettingsCategory"
|
||||||
|
@model="sidebar"
|
||||||
|
@query={{hash filter="default_navigation_menu_tags"}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
}
|
}
|
@ -1,47 +0,0 @@
|
|||||||
<Sidebar::Section
|
|
||||||
@sectionName="tags"
|
|
||||||
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
|
||||||
@headerActions={{array
|
|
||||||
(hash
|
|
||||||
action=this.showModal
|
|
||||||
title=(i18n "sidebar.sections.tags.header_action_title")
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
@headerActionsIcon="pencil-alt"
|
|
||||||
@collapsable={{@collapsable}}
|
|
||||||
>
|
|
||||||
|
|
||||||
{{#if (gt this.sectionLinks.length 0)}}
|
|
||||||
{{#each this.sectionLinks as |sectionLink|}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@route={{sectionLink.route}}
|
|
||||||
@title={{sectionLink.title}}
|
|
||||||
@content={{sectionLink.text}}
|
|
||||||
@currentWhen={{sectionLink.currentWhen}}
|
|
||||||
@prefixType={{sectionLink.prefixType}}
|
|
||||||
@prefixValue={{sectionLink.prefixValue}}
|
|
||||||
@prefixColor={{sectionLink.prefixColor}}
|
|
||||||
@badgeText={{sectionLink.badgeText}}
|
|
||||||
@models={{sectionLink.models}}
|
|
||||||
@suffixCSSClass={{sectionLink.suffixCSSClass}}
|
|
||||||
@suffixValue={{sectionLink.suffixValue}}
|
|
||||||
@suffixType={{sectionLink.suffixType}}
|
|
||||||
data-tag-name={{sectionLink.tagName}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Sidebar::Common::AllTagsSectionLink />
|
|
||||||
|
|
||||||
{{#if this.shouldDisplayDefaultConfig}}
|
|
||||||
<Sidebar::SectionLink
|
|
||||||
@linkName="configure-default-navigation-menu-tags"
|
|
||||||
@content={{i18n "sidebar.sections.tags.configure_defaults"}}
|
|
||||||
@prefixType="icon"
|
|
||||||
@prefixValue="wrench"
|
|
||||||
@route="adminSiteSettingsCategory"
|
|
||||||
@model="sidebar"
|
|
||||||
@query={{hash filter="default_navigation_menu_tags"}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</Sidebar::Section>
|
|
@ -2,6 +2,7 @@ import { tracked } from "@glimmer/tracking";
|
|||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import UserStatusMessage from "discourse/components/user-status-message";
|
||||||
import { decorateUsername } from "discourse/helpers/decorate-username-selector";
|
import { decorateUsername } from "discourse/helpers/decorate-username-selector";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import { emojiUnescape } from "discourse/lib/text";
|
import { emojiUnescape } from "discourse/lib/text";
|
||||||
@ -315,7 +316,7 @@ export default {
|
|||||||
|
|
||||||
get contentComponent() {
|
get contentComponent() {
|
||||||
if (this.oneOnOneMessage) {
|
if (this.oneOnOneMessage) {
|
||||||
return "user-status-message";
|
return UserStatusMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user