mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 07:18:06 +08:00
DEV: Convert the entire sidebar to gjs (#26978)
This commit is contained in:
parent
2f95628f87
commit
fcd2293226
|
@ -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 {
|
||||
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 { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
||||
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";
|
||||
|
||||
export default class BackToForum extends Component {
|
||||
|
@ -13,14 +13,13 @@ export default class BackToForum extends Component {
|
|||
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
|
||||
}
|
||||
|
||||
get homepage() {
|
||||
return `discovery.${defaultHomepage()}`;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.shouldDisplay}}
|
||||
<LinkTo @route={{this.homepage}} class="sidebar-sections__back-to-forum">
|
||||
{{dIcon "arrow-left"}}
|
||||
<LinkTo
|
||||
@route="discovery.{{(defaultHomepage)}}"
|
||||
class="sidebar-sections__back-to-forum"
|
||||
>
|
||||
{{icon "arrow-left"}}
|
||||
|
||||
<span>{{i18n "admin.back_to_forum"}}</span>
|
||||
</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 default class SidebarCommonCategoriesSection extends Component {
|
||||
@service topicTrackingState;
|
||||
@service siteSettings;
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@service topicTrackingState;
|
||||
|
||||
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 { service } from "@ember/service";
|
||||
import CustomSection from "./custom-section";
|
||||
|
||||
export default class SidebarCustomSection extends Component {
|
||||
@service currentUser;
|
||||
|
@ -19,4 +20,12 @@ export default class SidebarCustomSection extends Component {
|
|||
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 site;
|
||||
@service siteSettings;
|
||||
|
@ -67,7 +67,7 @@ export default class extends Component {
|
|||
@tracked initialLoad = true;
|
||||
@tracked filteredCategoriesGroupings = [];
|
||||
@tracked filteredCategoryIds = [];
|
||||
|
||||
// TODO: tracked array, no ember array methods
|
||||
@tracked
|
||||
selectedSidebarCategoryIds = [...this.currentUser.sidebar_category_ids];
|
||||
|
||||
|
@ -307,31 +307,31 @@ export default class extends Component {
|
|||
<div class="sidebar-categories-form__loading">
|
||||
{{loadingSpinner size="small"}}
|
||||
</div>
|
||||
{{else if (gt this.filteredCategoriesGroupings.length 0)}}
|
||||
{{else}}
|
||||
{{#each this.filteredCategoriesGroupings as |categories|}}
|
||||
<div
|
||||
class="sidebar-categories-form__row"
|
||||
style={{borderColor (get categories "0.color") "left"}}
|
||||
{{didInsert this.didInsert}}
|
||||
style={{borderColor (get categories "0.color") "left"}}
|
||||
class="sidebar-categories-form__row"
|
||||
>
|
||||
|
||||
{{#each categories as |category|}}
|
||||
<div
|
||||
class="sidebar-categories-form__category-row"
|
||||
data-category-id={{category.id}}
|
||||
data-category-level={{category.level}}
|
||||
class="sidebar-categories-form__category-row"
|
||||
>
|
||||
<label
|
||||
class="sidebar-categories-form__category-label"
|
||||
for={{concat
|
||||
"sidebar-categories-form__input--"
|
||||
category.id
|
||||
}}
|
||||
class="sidebar-categories-form__category-label"
|
||||
>
|
||||
<div class="sidebar-categories-form__category-wrapper">
|
||||
<div class="sidebar-categories-form__category-badge">
|
||||
{{categoryBadge category}}
|
||||
</div>
|
||||
|
||||
{{#unless category.parentCategory}}
|
||||
<div
|
||||
class="sidebar-categories-form__category-description"
|
||||
|
@ -345,11 +345,7 @@ export default class extends Component {
|
|||
</div>
|
||||
|
||||
<Input
|
||||
id={{concat
|
||||
"sidebar-categories-form__input--"
|
||||
category.id
|
||||
}}
|
||||
class="sidebar-categories-form__input"
|
||||
{{on "click" (fn this.toggleCategory category.id)}}
|
||||
@type="checkbox"
|
||||
@checked={{includes
|
||||
this.selectedSidebarCategoryIds
|
||||
|
@ -358,17 +354,21 @@ export default class extends Component {
|
|||
disabled={{not
|
||||
(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>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="sidebar-categories-form__no-categories">
|
||||
{{i18n "sidebar.categories_form_modal.no_categories"}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div class="sidebar-categories-form__no-categories">
|
||||
{{i18n "sidebar.categories_form_modal.no_categories"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
</EditNavigationMenuModal>
|
||||
|
|
|
@ -6,12 +6,13 @@ import { on } from "@ember/modifier";
|
|||
import { action } from "@ember/object";
|
||||
import DButton from "discourse/components/d-button";
|
||||
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-i18n";
|
||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||
|
||||
export default class extends Component {
|
||||
export default class SidebarEditNavigationMenuModal extends Component {
|
||||
@tracked filter = "";
|
||||
@tracked filterDropdownValue = "all";
|
||||
|
||||
|
@ -34,11 +35,6 @@ export default class extends Component {
|
|||
},
|
||||
];
|
||||
|
||||
@action
|
||||
onFilterInput(input) {
|
||||
this.args.onFilterInput(input.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
onFilterDropdownChange(value) {
|
||||
this.filterDropdownValue = value;
|
||||
|
@ -79,18 +75,18 @@ export default class extends Component {
|
|||
<:belowHeader>
|
||||
<div class="sidebar__edit-navigation-menu__filter">
|
||||
<div class="sidebar__edit-navigation-menu__filter-input">
|
||||
{{dIcon
|
||||
{{icon
|
||||
"search"
|
||||
class="sidebar__edit-navigation-menu__filter-input-icon"
|
||||
}}
|
||||
|
||||
<Input
|
||||
class="sidebar__edit-navigation-menu__filter-input-field"
|
||||
placeholder={{@inputFilterPlaceholder}}
|
||||
{{on "input" (withEventValue @onFilterInput)}}
|
||||
@type="text"
|
||||
@value={{this.filter}}
|
||||
{{on "input" this.onFilterInput}}
|
||||
placeholder={{@inputFilterPlaceholder}}
|
||||
autofocus="true"
|
||||
class="sidebar__edit-navigation-menu__filter-input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -113,18 +109,18 @@ export default class extends Component {
|
|||
<:footer>
|
||||
<div class="sidebar__edit-navigation-menu__footer">
|
||||
<DButton
|
||||
@action={{@save}}
|
||||
@label="save"
|
||||
@disabled={{@saving}}
|
||||
@action={{@save}}
|
||||
class="btn-primary sidebar__edit-navigation-menu__save-button"
|
||||
/>
|
||||
|
||||
{{#if @showResetDefaultsButton}}
|
||||
<DButton
|
||||
@icon="undo"
|
||||
@label="sidebar.edit_navigation_modal_form.reset_to_defaults"
|
||||
@disabled={{@saving}}
|
||||
@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"
|
||||
/>
|
||||
{{/if}}
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
import Component from "@glimmer/component";
|
||||
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 didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
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 { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
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 siteSettings;
|
||||
@service store;
|
||||
|
@ -161,4 +170,72 @@ export default class extends Component {
|
|||
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 I18n from "discourse-i18n";
|
||||
|
||||
export default class FilterNoResulsts extends Component {
|
||||
export default class FilterNoResults extends Component {
|
||||
@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 { tracked } from "@glimmer/tracking";
|
||||
import { on } from "@ember/modifier";
|
||||
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 { 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 MoreSectionLink from "./more-section-link";
|
||||
import SectionLinkButton from "./section-link-button";
|
||||
|
||||
export default class SidebarMoreSectionLinks extends Component {
|
||||
@service router;
|
||||
|
@ -93,7 +100,7 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||
}
|
||||
|
||||
#setActiveSectionLink() {
|
||||
const activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
|
||||
this.activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
|
||||
const args = [sectionLink.route];
|
||||
|
||||
if (sectionLink.model) {
|
||||
|
@ -108,7 +115,56 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||
|
||||
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 { tracked } from "@glimmer/tracking";
|
||||
import { fn } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import { defaultHomepage } from "discourse/lib/utilities";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
export default class SwitchPanelButtons extends Component {
|
||||
@service router;
|
||||
@service sidebarState;
|
||||
|
||||
@tracked currentPanel;
|
||||
@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 { bind } from "discourse-common/utils/decorators";
|
||||
import SidebarCustomSection from "../common/custom-sections";
|
||||
|
||||
export default class SidebarUserCustomSections extends SidebarCustomSection {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.messageBus.subscribe("/refresh-sidebar-sections", this._refresh);
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this.messageBus.unsubscribe("/refresh-sidebar-sections");
|
||||
}
|
||||
|
||||
@bind
|
||||
_refresh() {
|
||||
return ajax("/sidebar_sections.json", {}).then((json) => {
|
||||
this.currentUser.set("sidebar_sections", json.sidebar_sections);
|
||||
});
|
||||
async _refresh() {
|
||||
const json = await ajax("/sidebar_sections.json", {});
|
||||
this.currentUser.set("sidebar_sections", json.sidebar_sections);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
import Component from "@glimmer/component";
|
||||
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 routeAction from "discourse/helpers/route-action";
|
||||
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 i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import Section from "../section";
|
||||
import SectionLink from "../section-link";
|
||||
|
||||
export const INBOX = "inbox";
|
||||
export const UNREAD = "unread";
|
||||
|
@ -28,6 +33,8 @@ export default class SidebarUserMessagesSection extends Component {
|
|||
@service currentUser;
|
||||
@service router;
|
||||
|
||||
_pmTopicTrackingStateKey = "messages-section";
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
|
@ -37,21 +44,12 @@ export default class SidebarUserMessagesSection extends Component {
|
|||
this._refreshSectionLinksDisplayState
|
||||
);
|
||||
|
||||
this._pmTopicTrackingStateKey = "messages-section";
|
||||
|
||||
this.pmTopicTrackingState.onStateChange(
|
||||
this._pmTopicTrackingStateKey,
|
||||
this._refreshSectionLinkCounts
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
_refreshSectionLinkCounts() {
|
||||
for (const sectionLink of this.allSectionLinks) {
|
||||
sectionLink.refreshCount();
|
||||
}
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
|
||||
|
@ -67,6 +65,13 @@ export default class SidebarUserMessagesSection extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
_refreshSectionLinkCounts() {
|
||||
for (const sectionLink of this.allSectionLinks) {
|
||||
sectionLink.refreshCount();
|
||||
}
|
||||
}
|
||||
|
||||
_refreshSectionLinksDisplayState() {
|
||||
const currentRouteName = this.router.currentRoute.name;
|
||||
const currentRouteParentName = this.router.currentRoute.parent.name;
|
||||
|
@ -144,4 +149,53 @@ export default class SidebarUserMessagesSection extends Component {
|
|||
...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 { cached } from "@glimmer/tracking";
|
||||
import { array, hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import SidebarEditNavigationMenuTagsModal from "discourse/components/sidebar/edit-navigation-menu/tags-modal";
|
||||
import { hasDefaultSidebarTags } from "discourse/lib/sidebar/helpers";
|
||||
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 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 {
|
||||
@service currentUser;
|
||||
|
@ -36,7 +41,6 @@ export default class SidebarUserTagsSection extends Component {
|
|||
@cached
|
||||
get sectionLinks() {
|
||||
const links = [];
|
||||
|
||||
let tags;
|
||||
|
||||
if (this.currentUser.sidebarTags.length > 0) {
|
||||
|
@ -79,4 +83,51 @@ export default class SidebarUserTagsSection extends Component {
|
|||
showModal() {
|
||||
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 { dasherize } from "@ember/string";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import UserStatusMessage from "discourse/components/user-status-message";
|
||||
import { decorateUsername } from "discourse/helpers/decorate-username-selector";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
|
@ -315,7 +316,7 @@ export default {
|
|||
|
||||
get contentComponent() {
|
||||
if (this.oneOnOneMessage) {
|
||||
return "user-status-message";
|
||||
return UserStatusMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user