mirror of
https://github.com/discourse/discourse.git
synced 2026-08-09 12:38:21 -05:00
Introduces select-kit
* renames `select-box-kit` into `select-kit`
* introduces `single-select` and `multi-select` as base components
* introduces {{search-advanced-category-chooser}} as a better component for selecting category in advanced search
* improves events handling in select-kit
* recreates color selection inputs using {{multi-select}} and a custom {{selected-color}} component
* replaces category-selector by a component using select-kit and based on multi-select
* improves positioning of wrapper
* removes the need for offscreen, and instead use `select-kit-header` as a base focus point for all select-kit based components
* introduces a formal plugin api for select-kit based components
* introduces a formal pattern for loading and updating select-kit based components:
```
computeValue()
computeContent()
mutateValue()
```
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBox.extend({
|
||||
pluginApiIdentifiers: ["admin-agree-flag-dropdown"],
|
||||
classNames: ["agree-flag", "admin-agree-flag-dropdown"],
|
||||
adminTools: Ember.inject.service(),
|
||||
nameProperty: "label",
|
||||
allowInitialValueMutation: false,
|
||||
headerIcon: "thumbs-o-up",
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = `${I18n.t("admin.flags.agree")}...`;
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("adminTools", "post.user")
|
||||
spammerDetails(adminTools, user) {
|
||||
return adminTools.spammerDetails(user);
|
||||
},
|
||||
|
||||
canDeleteSpammer: Ember.computed.and("spammerDetails.canDelete", "post.flaggedForSpam"),
|
||||
|
||||
computeContent() {
|
||||
const content = [];
|
||||
const post = this.get("post");
|
||||
const canDeleteSpammer = this.get("canDeleteSpammer");
|
||||
|
||||
if (post.user_deleted) {
|
||||
content.push({
|
||||
icon: "eye",
|
||||
id: "confirm-agree-restore",
|
||||
action: () => this.send("perform", "restore"),
|
||||
label: I18n.t("admin.flags.agree_flag_restore_post"),
|
||||
description: I18n.t("admin.flags.agree_flag_restore_post_title")
|
||||
});
|
||||
} else {
|
||||
if (!post.get("postHidden")) {
|
||||
content.push({
|
||||
icon: "eye-slash",
|
||||
action: () => this.send("perform", "hide"),
|
||||
id: "confirm-agree-hide",
|
||||
label: I18n.t("admin.flags.agree_flag_hide_post"),
|
||||
description: I18n.t("admin.flags.agree_flag_hide_post_title")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
content.push({
|
||||
icon: "thumbs-o-up",
|
||||
id: "confirm-agree-keep",
|
||||
description: I18n.t('admin.flags.agree_flag_title'),
|
||||
action: () => this.send("perform", "keep"),
|
||||
label: I18n.t("admin.flags.agree_flag"),
|
||||
});
|
||||
|
||||
if (canDeleteSpammer) {
|
||||
content.push({
|
||||
title: I18n.t("admin.flags.delete_spammer_title"),
|
||||
icon: "exclamation-triangle",
|
||||
id: "delete-spammer",
|
||||
action: () => this.send("deleteSpammer"),
|
||||
label: I18n.t("admin.flags.delete_spammer"),
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
const computedContentItem = this.get("computedContent").findBy("value", value);
|
||||
Ember.get(computedContentItem, "originalContent.action")();
|
||||
},
|
||||
|
||||
actions: {
|
||||
deleteSpammer() {
|
||||
let spammerDetails = this.get("spammerDetails");
|
||||
this.attrs.removeAfter(spammerDetails.deleteUser());
|
||||
},
|
||||
|
||||
perform(action) {
|
||||
let flaggedPost = this.get("post");
|
||||
this.attrs.removeAfter(flaggedPost.agreeFlags(action));
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBox.extend({
|
||||
classNames: ["delete-flag", "admin-delete-flag-dropdown"],
|
||||
adminTools: Ember.inject.service(),
|
||||
nameProperty: "label",
|
||||
headerIcon: "trash-o",
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t("admin.flags.delete");
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("adminTools", "post.user")
|
||||
spammerDetails(adminTools, user) {
|
||||
return adminTools.spammerDetails(user);
|
||||
},
|
||||
|
||||
canDeleteSpammer: Ember.computed.and("spammerDetails.canDelete", "post.flaggedForSpam"),
|
||||
|
||||
computeContent() {
|
||||
const content = [];
|
||||
const canDeleteSpammer = this.get("canDeleteSpammer");
|
||||
|
||||
content.push({
|
||||
icon: "external-link",
|
||||
id: "delete-defer",
|
||||
action: () => this.send("deletePostDeferFlag"),
|
||||
label: I18n.t("admin.flags.delete_post_defer_flag"),
|
||||
description: I18n.t("admin.flags.delete_post_defer_flag_title"),
|
||||
});
|
||||
|
||||
content.push({
|
||||
icon: "thumbs-o-up",
|
||||
id: "delete-agree",
|
||||
action: () => this.send("deletePostAgreeFlag"),
|
||||
label: I18n.t("admin.flags.delete_post_agree_flag"),
|
||||
description: I18n.t("admin.flags.delete_post_agree_flag_title"),
|
||||
});
|
||||
|
||||
if (canDeleteSpammer) {
|
||||
content.push({
|
||||
title: I18n.t("admin.flags.delete_post_agree_flag_title"),
|
||||
icon: "exclamation-triangle",
|
||||
id: "delete-spammer",
|
||||
action: () => this.send("deleteSpammer"),
|
||||
label: I18n.t("admin.flags.delete_spammer")
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
const computedContentItem = this.get("computedContent").findBy("value", value);
|
||||
Ember.get(computedContentItem, "originalContent.action")();
|
||||
},
|
||||
|
||||
actions: {
|
||||
deleteSpammer() {
|
||||
let spammerDetails = this.get("spammerDetails");
|
||||
this.attrs.removeAfter(spammerDetails.deleteUser());
|
||||
},
|
||||
|
||||
deletePostDeferFlag() {
|
||||
let flaggedPost = this.get('post');
|
||||
this.attrs.removeAfter(flaggedPost.deferFlags(true));
|
||||
},
|
||||
|
||||
deletePostAgreeFlag() {
|
||||
let flaggedPost = this.get('post');
|
||||
this.attrs.removeAfter(flaggedPost.agreeFlags('delete'));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
const { makeArray } = Ember;
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["admin-group-selector"],
|
||||
classNames: "admin-group-selector",
|
||||
selected: null,
|
||||
available: null,
|
||||
allowAny: false,
|
||||
|
||||
computeValues() {
|
||||
return makeArray(this.get("selected"))
|
||||
.map(s => this.valueForContentItem(s));
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
return makeArray(this.get("available"));
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, name) {
|
||||
let computedContent = this.baseComputedContentItem(contentItem, name);
|
||||
computedContent.locked = contentItem.automatic;
|
||||
return computedContent;
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
if (values.length > this.get("selected").length) {
|
||||
const newValues = values
|
||||
.filter(v => !this.get("selected")
|
||||
.map(s => this.valueForContentItem(s))
|
||||
.includes(v));
|
||||
|
||||
newValues.forEach(value => {
|
||||
const actionContext = this.get("available")
|
||||
.findBy(this.get("valueAttribute"), parseInt(value, 10));
|
||||
|
||||
this.triggerAction({ action: "groupAdded", actionContext });
|
||||
});
|
||||
} else if (values.length < this.get("selected").length) {
|
||||
const selected = this.get("selected")
|
||||
.filter(s => !values.includes(this.valueForContentItem(s)));
|
||||
|
||||
selected.forEach(s => {
|
||||
this.triggerAction({
|
||||
action: "groupRemoved",
|
||||
actionContext: this.valueForContentItem(s)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["categories-admin-dropdown"],
|
||||
classNames: "categories-admin-dropdown",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
headerIcon: ["bars", "caret-down"],
|
||||
|
||||
autoHighlight() {},
|
||||
|
||||
computeContent() {
|
||||
const items = [
|
||||
{
|
||||
id: "create",
|
||||
name: I18n.t("category.create"),
|
||||
description: I18n.t("category.create_long"),
|
||||
icon: "plus"
|
||||
}
|
||||
];
|
||||
|
||||
const includeReorder = this.get("siteSettings.fixed_category_positions");
|
||||
if (includeReorder) {
|
||||
items.push({
|
||||
id: "reorder",
|
||||
name: I18n.t("categories.reorder.title"),
|
||||
description: I18n.t("categories.reorder.title_long"),
|
||||
icon: "random"
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
this.get(value)();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import Category from "discourse/models/category";
|
||||
const { get, isNone, isEmpty } = Ember;
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["category-chooser"],
|
||||
classNames: "category-chooser",
|
||||
filterable: true,
|
||||
castInteger: true,
|
||||
allowUncategorized: false,
|
||||
rowComponent: "category-row",
|
||||
noneRowComponent: "none-category-row",
|
||||
allowSubCategories: true,
|
||||
|
||||
filterComputedContent(computedContent, computedValue, filter) {
|
||||
if (isEmpty(filter)) { return computedContent; }
|
||||
|
||||
const _matchFunction = (f, text) => {
|
||||
return text.toLowerCase().indexOf(f) > -1;
|
||||
};
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
|
||||
return computedContent.filter(c => {
|
||||
const category = Category.findById(get(c, "value"));
|
||||
const text = get(c, "name");
|
||||
if (category && category.get("parentCategory")) {
|
||||
const categoryName = category.get("parentCategory.name");
|
||||
return _matchFunction(lowerFilter, text) || _matchFunction(lowerFilter, categoryName);
|
||||
} else {
|
||||
return _matchFunction(lowerFilter, text);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@computed("rootNone", "rootNoneLabel")
|
||||
none(rootNone, rootNoneLabel) {
|
||||
if (this.siteSettings.allow_uncategorized_topics || this.get("allowUncategorized")) {
|
||||
if (!isNone(rootNone)) {
|
||||
return rootNoneLabel || "category.none";
|
||||
} else {
|
||||
return Category.findUncategorized();
|
||||
}
|
||||
} else {
|
||||
return "category.choose";
|
||||
}
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_bindComposerResizing() {
|
||||
this.appEvents.on("composer:resized", this, this.applyDirection);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindComposerResizing() {
|
||||
this.appEvents.off("composer:resized");
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
|
||||
let scopedCategoryId = this.get("scopedCategoryId");
|
||||
if (scopedCategoryId) {
|
||||
const scopedCat = Category.findById(scopedCategoryId);
|
||||
scopedCategoryId = scopedCat.get("parent_category_id") || scopedCat.get("id");
|
||||
}
|
||||
|
||||
const excludeCategoryId = this.get("excludeCategoryId");
|
||||
|
||||
return categories.filter(c => {
|
||||
const categoryId = this.valueForContentItem(c);
|
||||
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && get(c, "parent_category_id") !== scopedCategoryId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.get("allowSubCategories") === false && c.get("parentCategory") ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((this.get("allowUncategorized") === false && get(c, "isUncategorizedCategory")) || excludeCategoryId === categoryId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get(c, "permission") === PermissionType.FULL;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["category-notifications-button"],
|
||||
classNames: "category-notifications-button",
|
||||
isHidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
|
||||
i18nPrefix: "category.notifications",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
mutateValue(value) {
|
||||
this.get("category").setNotification(value);
|
||||
},
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) {
|
||||
return [iconForSelectedDetails, "caret-down"];
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import Category from "discourse/models/category";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/category-row",
|
||||
classNames: "category-row",
|
||||
displayCategoryDescription: true,
|
||||
|
||||
@computed("computedContent.value", "computedContent.name")
|
||||
category(value, name) {
|
||||
if (Ember.isEmpty(value)) {
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get("name") === name) {
|
||||
return uncat;
|
||||
}
|
||||
} else {
|
||||
return Category.findById(parseInt(value, 10));
|
||||
}
|
||||
},
|
||||
|
||||
@computed("category")
|
||||
badgeForCategory(category) {
|
||||
return categoryBadgeHTML(category, {
|
||||
link: false,
|
||||
allowUncategorized: true,
|
||||
hideParent: true
|
||||
}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategory")
|
||||
badgeForParentCategory(parentCategory) {
|
||||
return categoryBadgeHTML(parentCategory, {link: false}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategoryid")
|
||||
parentCategory(parentCategoryId) {
|
||||
return Category.findById(parentCategoryId);
|
||||
},
|
||||
|
||||
@computed("parentCategoryid")
|
||||
hasParentCategory(parentCategoryid) {
|
||||
return !Ember.isNone(parentCategoryid);
|
||||
},
|
||||
|
||||
@computed("category")
|
||||
parentCategoryid(category) {
|
||||
return category.get("parent_category_id");
|
||||
},
|
||||
|
||||
topicCount: Ember.computed.alias("category.topic_count"),
|
||||
|
||||
@computed("options.displayCategoryDescription", "category.description")
|
||||
hasDescription(displayCategoryDescription, description) {
|
||||
return displayCategoryDescription && description && description !== "null";
|
||||
},
|
||||
|
||||
@computed("category.description")
|
||||
description(description) {
|
||||
return `${description.substr(0, 200)}${description.length > 200 ? '…' : ''}`;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["category-selector"],
|
||||
classNames: "category-selector",
|
||||
filterable: true,
|
||||
allowAny: false,
|
||||
rowComponent: "category-row",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: "multi-select/selected-category"
|
||||
}));
|
||||
|
||||
this.set("rowComponentOptions", Ember.Object.create({
|
||||
displayCategoryDescription: false
|
||||
}));
|
||||
},
|
||||
|
||||
computeValues() {
|
||||
return Ember.makeArray(this.get("categories")).map(c => c.id);
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
this.set("categories", values.map(v => Category.findById(v)));
|
||||
},
|
||||
|
||||
filterComputedContent(computedContent, computedValues, filter) {
|
||||
const regex = new RegExp(filter.toLowerCase(), 'i');
|
||||
return computedContent.filter(category => Ember.get(category, "name").match(regex));
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
const blacklist = Ember.makeArray(this.get("blacklist"));
|
||||
return Category.list().filter(category => !blacklist.includes(category));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import SingleSelectComponent from "select-kit/components/single-select";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SingleSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["combo-box"],
|
||||
classNames: "combobox combo-box",
|
||||
autoFilterable: true,
|
||||
headerComponent: "combo-box/combo-box-header",
|
||||
|
||||
caretUpIcon: "caret-up",
|
||||
caretDownIcon: "caret-down",
|
||||
clearable: false,
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComboBoxOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
||||
caretUpIcon: this.get("caretUpIcon"),
|
||||
caretDownIcon: this.get("caretDownIcon"),
|
||||
clearable: this.get("clearable"),
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/combo-box/combo-box-header",
|
||||
classNames: "combo-box-header",
|
||||
|
||||
clearable: Ember.computed.alias("options.clearable"),
|
||||
caretUpIcon: Ember.computed.alias("options.caretUpIcon"),
|
||||
caretDownIcon: Ember.computed.alias("options.caretDownIcon"),
|
||||
|
||||
@computed("isExpanded", "caretUpIcon", "caretDownIcon")
|
||||
caretIcon(isExpanded, caretUpIcon, caretDownIcon) {
|
||||
return isExpanded === true ? caretUpIcon : caretDownIcon;
|
||||
},
|
||||
|
||||
@computed("clearable", "computedContent.hasSelection")
|
||||
shouldDisplayClearableButton(clearable, hasSelection) {
|
||||
return clearable === true && hasSelection === true;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import SingleSelectComponent from "select-kit/components/single-select";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SingleSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["dropdown-select-box"],
|
||||
classNames: "dropdown-select-box",
|
||||
verticalOffset: 3,
|
||||
fullWidthOnMobile: true,
|
||||
filterable: false,
|
||||
autoFilterable: false,
|
||||
headerComponent: "dropdown-select-box/dropdown-select-box-header",
|
||||
rowComponent: "dropdown-select-box/dropdown-select-box-row",
|
||||
showFullTitle: true,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setDropdownSelectBoxComponentOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
||||
showFullTitle: this.get("showFullTitle")
|
||||
});
|
||||
},
|
||||
|
||||
didClickOutside() {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
this.close();
|
||||
},
|
||||
|
||||
didSelect() {
|
||||
this._super();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/dropdown-select-box/dropdown-select-box-header",
|
||||
classNames: "dropdown-select-box-header",
|
||||
tagName: "button",
|
||||
|
||||
classNameBindings: ["btnClassName"],
|
||||
|
||||
@computed("options.showFullTitle")
|
||||
btnClassName(showFullTitle) {
|
||||
return `btn ${showFullTitle ? 'btn-icon-text' : 'no-text btn-icon'}`;
|
||||
}
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/dropdown-select-box/dropdown-select-box-row",
|
||||
classNames: "dropdown-select-box-row",
|
||||
|
||||
name: Ember.computed.alias("computedContent.name"),
|
||||
description: Ember.computed.alias("computedContent.originalContent.description")
|
||||
});
|
||||
@@ -0,0 +1,167 @@
|
||||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { CLOSE_STATUS_TYPE } from "discourse/controllers/edit-topic-timer";
|
||||
import DatetimeMixin from "select-kit/components/future-date-input-selector/mixin";
|
||||
|
||||
const TIMEFRAME_BASE = {
|
||||
enabled: () => true,
|
||||
when: () => null,
|
||||
icon: 'briefcase',
|
||||
displayWhen: true,
|
||||
};
|
||||
|
||||
function buildTimeframe(opts) {
|
||||
return jQuery.extend({}, TIMEFRAME_BASE, opts);
|
||||
}
|
||||
|
||||
export const TIMEFRAMES = [
|
||||
buildTimeframe({
|
||||
id: 'later_today',
|
||||
format: "h a",
|
||||
enabled: opts => opts.canScheduleToday,
|
||||
when: (time) => time.hour(18).minute(0),
|
||||
icon: 'moon-o'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "tomorrow",
|
||||
format: "ddd, h a",
|
||||
when: (time, timeOfDay) => time.add(1, 'day').hour(timeOfDay).minute(0),
|
||||
icon: 'sun-o'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "later_this_week",
|
||||
format: "ddd, h a",
|
||||
enabled: opts => !opts.canScheduleToday && opts.day < 4,
|
||||
when: (time, timeOfDay) => time.add(2, 'day').hour(timeOfDay).minute(0),
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "this_weekend",
|
||||
format: "ddd, h a",
|
||||
enabled: opts => opts.day < 5 && opts.includeWeekend,
|
||||
when: (time, timeOfDay) => time.day(6).hour(timeOfDay).minute(0),
|
||||
icon: 'bed'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "next_week",
|
||||
format: "ddd, h a",
|
||||
enabled: opts => opts.day !== 7,
|
||||
when: (time, timeOfDay) => time.add(1, 'week').day(1).hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "two_weeks",
|
||||
format: "MMM D",
|
||||
when: (time, timeOfDay) => time.add(2, 'week').hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "next_month",
|
||||
format: "MMM D",
|
||||
enabled: opts => opts.now.date() !== moment().endOf("month").date(),
|
||||
when: (time, timeOfDay) => time.add(1, 'month').startOf('month').hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "three_months",
|
||||
format: "MMM D",
|
||||
enabled: opts => opts.includeFarFuture,
|
||||
when: (time, timeOfDay) => time.add(3, 'month').startOf('month').hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "six_months",
|
||||
format: "MMM D",
|
||||
enabled: opts => opts.includeFarFuture,
|
||||
when: (time, timeOfDay) => time.add(6, 'month').startOf('month').hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "one_year",
|
||||
format: "MMM D",
|
||||
enabled: opts => opts.includeFarFuture,
|
||||
when: (time, timeOfDay) => time.add(1, 'year').startOf('day').hour(timeOfDay).minute(0),
|
||||
icon: 'briefcase'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "forever",
|
||||
enabled: opts => opts.includeFarFuture,
|
||||
when: (time, timeOfDay) => time.add(1000, 'year').hour(timeOfDay).minute(0),
|
||||
icon: 'gavel',
|
||||
displayWhen: false
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "pick_date_and_time",
|
||||
icon: 'calendar-plus-o'
|
||||
}),
|
||||
buildTimeframe({
|
||||
id: "set_based_on_last_post",
|
||||
enabled: opts => opts.includeBasedOnLastPost,
|
||||
icon: 'clock-o'
|
||||
}),
|
||||
];
|
||||
|
||||
let _timeframeById = null;
|
||||
export function timeframeDetails(id) {
|
||||
if (!_timeframeById) {
|
||||
_timeframeById = {};
|
||||
TIMEFRAMES.forEach(t => _timeframeById[t.id] = t);
|
||||
}
|
||||
return _timeframeById[id];
|
||||
}
|
||||
|
||||
export const FORMAT = "YYYY-MM-DD HH:mm";
|
||||
|
||||
export default ComboBoxComponent.extend(DatetimeMixin, {
|
||||
pluginApiIdentifiers: ["future-date-input-selector"],
|
||||
classNames: ["future-date-input-selector"],
|
||||
isCustom: Ember.computed.equal("value", "pick_date_and_time"),
|
||||
clearable: true,
|
||||
rowComponent: "future-date-input-selector/future-date-input-selector-row",
|
||||
headerComponent: "future-date-input-selector/future-date-input-selector-header",
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.datetime = this._computeDatetimeForValue(this.get("computedValue"));
|
||||
content.name = this.get("selectedComputedContent.name") || content.name;
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
content.icons = this._computeIconsForValue(this.get("computedValue"));
|
||||
return content;
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, name) {
|
||||
let item = this.baseComputedContentItem(contentItem, name);
|
||||
item.datetime = this._computeDatetimeForValue(contentItem.id);
|
||||
item.icons = this._computeIconsForValue(contentItem.id);
|
||||
return item;
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
let now = moment();
|
||||
let opts = {
|
||||
now,
|
||||
day: now.day(),
|
||||
includeWeekend: this.get('includeWeekend'),
|
||||
includeFarFuture: this.get('includeFarFuture'),
|
||||
includeBasedOnLastPost: this.get("statusType") === CLOSE_STATUS_TYPE,
|
||||
canScheduleToday: (24 - now.hour()) > 6,
|
||||
};
|
||||
|
||||
return TIMEFRAMES.filter(tf => tf.enabled(opts)).map(tf => {
|
||||
return {
|
||||
id: tf.id,
|
||||
name: I18n.t(`topic.auto_update_input.${tf.id}`)
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
if (this.get("isCustom")) return;
|
||||
let input = null;
|
||||
const { time } = this._updateAt(value);
|
||||
|
||||
if (time && !Ember.isEmpty(value)) {
|
||||
input = time.format(FORMAT);
|
||||
}
|
||||
|
||||
this.setProperties({ input, value });
|
||||
},
|
||||
});
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import ComboBoxHeaderComponent from "select-kit/components/combo-box/combo-box-header";
|
||||
|
||||
export default ComboBoxHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/future-date-input-selector/future-date-input-selector-header",
|
||||
classNames: "future-date-input-selector-header"
|
||||
});
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/future-date-input-selector/future-date-input-selector-row",
|
||||
classNames: "future-date-input-selector-row"
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
import { timeframeDetails } from 'select-kit/components/future-date-input-selector';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
_computeIconsForValue(value) {
|
||||
let {icon} = this._updateAt(value);
|
||||
|
||||
if (icon) {
|
||||
return icon.split(",");
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
|
||||
_computeDatetimeForValue(value) {
|
||||
if (Ember.isNone(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let {time} = this._updateAt(value);
|
||||
if (time) {
|
||||
let details = timeframeDetails(value);
|
||||
if (!details.displayWhen) {
|
||||
time = null;
|
||||
}
|
||||
if (time && details.format) {
|
||||
return time.format(details.format);
|
||||
}
|
||||
}
|
||||
return time;
|
||||
},
|
||||
|
||||
_updateAt(selection) {
|
||||
let details = timeframeDetails(selection);
|
||||
|
||||
if (details) {
|
||||
return {
|
||||
time: details.when(moment(), this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18),
|
||||
icon: details.icon
|
||||
};
|
||||
}
|
||||
|
||||
return { time: moment() };
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["grouo-notifications-button"],
|
||||
classNames: ["group-notifications-button"],
|
||||
i18nPrefix: "groups.notifications",
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
mutateValue(value) {
|
||||
this.get("group").setNotification(value, this.get("user.id"));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["list-setting"],
|
||||
classNames: "list-setting",
|
||||
tokenSeparator: "|",
|
||||
settingValue: "",
|
||||
choices: null,
|
||||
filterable: true,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (!Ember.isNone(this.get("settingName"))) {
|
||||
this.set("nameProperty", this.get("settingName"));
|
||||
}
|
||||
|
||||
if (this.get("nameProperty").indexOf("color") > -1) {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: "multi-select/selected-color"
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
let content;
|
||||
if (Ember.isNone(this.get("choices"))) {
|
||||
content = this.get("settingValue").split(this.get("tokenSeparator"));;
|
||||
} else {
|
||||
content = this.get("choices");
|
||||
}
|
||||
|
||||
return Ember.makeArray(content).filter(c => c);
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
this.set("settingValue", values.join(this.get("tokenSeparator")));
|
||||
},
|
||||
|
||||
computeValues() {
|
||||
return this.get("settingValue")
|
||||
.split(this.get("tokenSeparator"))
|
||||
.filter(c => c);
|
||||
},
|
||||
|
||||
_handleTabOnKeyDown(event) {
|
||||
if (this.$highlightedRow().length === 1) {
|
||||
this._super(event);
|
||||
} else {
|
||||
this.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,255 @@
|
||||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone, isEmpty, makeArray } = Ember;
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["multi-select"],
|
||||
classNames: "multi-select",
|
||||
headerComponent: "multi-select/multi-select-header",
|
||||
filterComponent: null,
|
||||
headerText: "select_kit.default_header_text",
|
||||
allowAny: true,
|
||||
allowInitialValueMutation: false,
|
||||
autoFilterable: true,
|
||||
selectedNameComponent: "multi-select/selected-name",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.set("computedValues", []);
|
||||
if (isNone(this.get("values"))) { this.set("values", []); }
|
||||
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: this.get("selectedNameComponent")
|
||||
}));
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_setChoicesMaxWidth() {
|
||||
const width = this.$body().outerWidth(false);
|
||||
this.$(".choices").css({ maxWidth: width, width });
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_compute() {
|
||||
Ember.run.scheduleOnce("afterRender", () => {
|
||||
this.willComputeAttributes();
|
||||
let content = this._beforeWillComputeContent(this.get("content"));
|
||||
content = this.willComputeContent(content);
|
||||
let values = this._beforeWillComputeValues(this.get("values"));
|
||||
content = this.computeContent(content);
|
||||
content = this._beforeDidComputeContent(content);
|
||||
values = this.willComputeValues(values);
|
||||
values = this.computeValues(values);
|
||||
values = this._beforeDidComputeValues(values);
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
this.didComputeContent(content);
|
||||
this.didComputeValues(values);
|
||||
this.didComputeAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
@computed("filter", "shouldDisplayCreateRow")
|
||||
createRowComputedContent(filter, shouldDisplayCreateRow) {
|
||||
if (shouldDisplayCreateRow === true) {
|
||||
let content = this.createContentFromInput(filter);
|
||||
return this.computeContentItem(content, { created: true });
|
||||
}
|
||||
},
|
||||
|
||||
@computed("filter", "computedValues")
|
||||
shouldDisplayCreateRow(filter, computedValues) {
|
||||
return this._super() && !computedValues.includes(filter);
|
||||
},
|
||||
|
||||
_beforeWillComputeValues(values) {
|
||||
return values.map(v => this._castInteger(v === "" ? null : v));
|
||||
},
|
||||
willComputeValues(values) { return values; },
|
||||
computeValues(values) { return values; },
|
||||
_beforeDidComputeValues(values) {
|
||||
this.setProperties({ computedValues: values });
|
||||
return values;
|
||||
},
|
||||
didComputeValues(values) { return values; },
|
||||
|
||||
mutateAttributes() {
|
||||
Ember.run.next(() => {
|
||||
this.mutateContent(this.get("computedContent"));
|
||||
this.mutateValues(this.get("computedValues"));
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
});
|
||||
},
|
||||
mutateValues(computedValues) { this.set("values", computedValues); },
|
||||
|
||||
filterComputedContent(computedContent, computedValues, filter) {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("computedContent.[]", "computedValues.[]", "filter")
|
||||
filteredComputedContent(computedContent, computedValues, filter) {
|
||||
computedContent = computedContent.filter(c => {
|
||||
return !computedValues.includes(get(c, "value"));
|
||||
});
|
||||
|
||||
if (this.get("shouldFilter") === true) {
|
||||
computedContent = this.filterComputedContent(computedContent, computedValues, filter);
|
||||
}
|
||||
|
||||
return computedContent.slice(0, this.get("limitMatches"));
|
||||
},
|
||||
|
||||
baseHeaderComputedContent() {
|
||||
return {
|
||||
selectedComputedContents: this.get("selectedComputedContents")
|
||||
};
|
||||
},
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return I18n.t("select_kit.create", { content: rowComponent.get("computedContent.name")});
|
||||
};
|
||||
},
|
||||
|
||||
didPressBackspace(event) {
|
||||
this.expand();
|
||||
this.keyDown(event);
|
||||
this._destroyEvent(event);
|
||||
},
|
||||
|
||||
didPressEscape(event) {
|
||||
const $highlighted = this.$(".selected-name.is-highlighted");
|
||||
if ($highlighted.length > 0) {
|
||||
$highlighted.removeClass("is-highlighted");
|
||||
}
|
||||
|
||||
this._super(event);
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
if (!isEmpty(this.get("filter"))) return;
|
||||
|
||||
const keyCode = event.keyCode || event.which;
|
||||
const $filterInput = this.$filterInput();
|
||||
|
||||
// select all choices
|
||||
if (this.get("hasSelection") && event.metaKey === true && keyCode === 65) {
|
||||
this.$(".choices .selected-name:not(.is-locked)").addClass("is-highlighted");
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear selection when multiple
|
||||
if (this.$(".selected-name.is-highlighted").length >= 1 && keyCode === this.keys.BACKSPACE) {
|
||||
const highlightedComputedContents = [];
|
||||
$.each(this.$(".selected-name.is-highlighted"), (i, el) => {
|
||||
const computedContent = this._findComputedContentItemByGuid($(el).attr("data-guid"));
|
||||
if (!Ember.isNone(computedContent)) { highlightedComputedContents.push(computedContent); }
|
||||
});
|
||||
this.send("onDeselect", highlightedComputedContents);
|
||||
return;
|
||||
}
|
||||
|
||||
// try to remove last item from the list
|
||||
if (keyCode === this.keys.BACKSPACE) {
|
||||
let $lastSelectedValue = $(this.$(".choices .selected-name:not(.is-locked)").last());
|
||||
|
||||
if ($lastSelectedValue.length === 0) { return; }
|
||||
|
||||
if ($filterInput.not(":visible") && $lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($filterInput.val() === "") {
|
||||
if ($filterInput.is(":focus")) {
|
||||
if ($lastSelectedValue.length > 0) { $lastSelectedValue.click(); }
|
||||
} else {
|
||||
if ($lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
} else {
|
||||
$filterInput.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed("computedValues.[]", "computedContent.[]")
|
||||
selectedComputedContents(computedValues, computedContent) {
|
||||
const selected = [];
|
||||
computedValues.forEach(v => selected.push(computedContent.findBy("value", v)) );
|
||||
return selected;
|
||||
},
|
||||
|
||||
@computed("selectedComputedContents.[]")
|
||||
hasSelection(selectedComputedContents) { return !Ember.isEmpty(selectedComputedContents); },
|
||||
|
||||
autoHighlight() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
if (this.get("renderedBodyOnce") === false) { return; }
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
if (isEmpty(this.get("filteredComputedContent"))) {
|
||||
if (this.get("createRowComputedContent")) {
|
||||
this.send("onHighlight", this.get("createRowComputedContent"));
|
||||
} else if (this.get("noneRowComputedContent") && this.get("hasSelection") === true) {
|
||||
this.send("onHighlight", this.get("noneRowComputedContent"));
|
||||
}
|
||||
} else {
|
||||
this.send("onHighlight", this.get("filteredComputedContent.firstObject"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
didSelect() {
|
||||
this.focus();
|
||||
this.autoHighlight();
|
||||
},
|
||||
|
||||
didDeselect() {
|
||||
this.focus();
|
||||
this.autoHighlight();
|
||||
},
|
||||
|
||||
validateComputedContentItem(computedContentItem) {
|
||||
return !this.get("computedValues").includes(computedContentItem.value);
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClear() {
|
||||
this.get("selectedComputedContents").forEach(selectedComputedContent => {
|
||||
this.send("onDeselect", selectedComputedContent);
|
||||
});
|
||||
},
|
||||
|
||||
onCreate(computedContentItem) {
|
||||
if (this.validateComputedContentItem(computedContentItem)) {
|
||||
this.get("computedContent").pushObject(computedContentItem);
|
||||
this.send("onSelect", computedContentItem);
|
||||
}
|
||||
},
|
||||
|
||||
onSelect(computedContentItem) {
|
||||
this.willSelect(computedContentItem);
|
||||
this.get("computedValues").pushObject(computedContentItem.value);
|
||||
Ember.run.next(() => this.mutateAttributes());
|
||||
Ember.run.schedule("afterRender", () => this.didSelect(computedContentItem));
|
||||
},
|
||||
|
||||
onDeselect(rowComputedContentItems) {
|
||||
rowComputedContentItems = Ember.makeArray(rowComputedContentItems);
|
||||
const generatedComputedContents = this._filterRemovableComputedContents(makeArray(rowComputedContentItems));
|
||||
this.willDeselect(rowComputedContentItems);
|
||||
this.get("computedValues").removeObjects(rowComputedContentItems.map(r => r.value));
|
||||
this.get("computedContent").removeObjects(generatedComputedContents);
|
||||
Ember.run.next(() => this.mutateAttributes());
|
||||
Ember.run.schedule("afterRender", () => this.didDeselect(rowComputedContentItems));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
attributeBindings: ["names:data-name"],
|
||||
classNames: "multi-select-header",
|
||||
layoutName: "select-kit/templates/components/multi-select/multi-select-header",
|
||||
selectedNameComponent: Ember.computed.alias("options.selectedNameComponent"),
|
||||
|
||||
@on("didRender")
|
||||
_positionFilter() {
|
||||
if (this.get("shouldDisplayFilter") === false) { return; }
|
||||
|
||||
const $filter = this.$(".filter");
|
||||
$filter.width(0);
|
||||
|
||||
const leftHeaderOffset = this.$().offset().left;
|
||||
const leftFilterOffset = $filter.offset().left;
|
||||
const offset = leftFilterOffset - leftHeaderOffset;
|
||||
const width = this.$().outerWidth(false);
|
||||
const availableSpace = width - offset;
|
||||
const $choices = $filter.parent(".choices");
|
||||
const parentRightPadding = parseInt($choices.css("padding-right") , 10);
|
||||
$filter.width(availableSpace - parentRightPadding * 4);
|
||||
},
|
||||
|
||||
@computed("computedContent.selectedComputedContents.[]")
|
||||
names(selectedComputedContents) {
|
||||
return Ember.makeArray(selectedComputedContents).map(sc => sc.name).join(",");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import SelectedNameComponent from "select-kit/components/multi-select/selected-name";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
export default SelectedNameComponent.extend({
|
||||
classNames: "selected-category",
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-category",
|
||||
|
||||
@computed("content.originalContent")
|
||||
badge(category) {
|
||||
return categoryBadgeHTML(category, {allowUncategorized: true, link: false}).htmlSafe();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import SelectedNameComponent from "select-kit/components/multi-select/selected-name";
|
||||
|
||||
export default SelectedNameComponent.extend({
|
||||
classNames: "selected-color",
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-color",
|
||||
|
||||
didRender() {
|
||||
const name = this.get("content.name");
|
||||
this.$(".color-preview").css("background", `#${name}`.htmlSafe());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
attributeBindings: [
|
||||
"tabindex",
|
||||
"content.name:data-name",
|
||||
"content.value:data-value",
|
||||
"guid:data-guid"
|
||||
],
|
||||
classNames: ["selected-name", "choice"],
|
||||
classNameBindings: ["isHighlighted", "isLocked"],
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-name",
|
||||
tagName: "li",
|
||||
tabindex: -1,
|
||||
|
||||
@computed("content")
|
||||
guid(content) { return Ember.guidFor(content); },
|
||||
|
||||
isLocked: Ember.computed("content.locked", function() {
|
||||
return this.getWithDefault("content.locked", false);
|
||||
}),
|
||||
|
||||
click() {
|
||||
if (this.get("isLocked") === true) { return false; }
|
||||
this.toggleProperty("isHighlighted");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import CategoryRowComponent from "select-kit/components/category-row";
|
||||
|
||||
export default CategoryRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/category-row",
|
||||
classNames: "none category-row",
|
||||
|
||||
click() {
|
||||
this.sendAction("onClear");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import { default as computed, on } from "ember-addons/ember-computed-decorators";
|
||||
import { buttonDetails } from "discourse/lib/notification-levels";
|
||||
import { allLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: "notifications-button",
|
||||
nameProperty: "key",
|
||||
fullWidthOnMobile: true,
|
||||
content: allLevels,
|
||||
collectionHeight: "auto",
|
||||
castInteger: true,
|
||||
autofilterable: false,
|
||||
filterable: false,
|
||||
rowComponent: "notifications-button/notifications-button-row",
|
||||
allowInitialValueMutation: false,
|
||||
i18nPrefix: "",
|
||||
i18nPostfix: "",
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) { return iconForSelectedDetails; },
|
||||
|
||||
@computed("selectedDetails.icon")
|
||||
iconForSelectedDetails(icon) { return icon; },
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t(`${this.get("i18nPrefix")}.${this.get("selectedDetails.key")}.title`);
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setNotificationsButtonComponentOptions() {
|
||||
this.get("rowComponentOptions").setProperties({
|
||||
i18nPrefix: this.get("i18nPrefix"),
|
||||
i18nPostfix: this.get("i18nPostfix")
|
||||
});
|
||||
},
|
||||
|
||||
@computed("computedValue")
|
||||
selectedDetails(computedValue) {
|
||||
return buttonDetails(computedValue);
|
||||
}
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import DropdownSelectBoxRoxComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
|
||||
import { buttonDetails } from "discourse/lib/notification-levels";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default DropdownSelectBoxRoxComponent.extend({
|
||||
classNames: "notifications-button-row",
|
||||
|
||||
i18nPrefix: Ember.computed.alias("options.i18nPrefix"),
|
||||
i18nPostfix: Ember.computed.alias("options.i18nPostfix"),
|
||||
|
||||
@computed("computedContent.value", "i18nPrefix")
|
||||
title(value, prefix) {
|
||||
const key = buttonDetails(value).key;
|
||||
return I18n.t(`${prefix}.${key}.title`);
|
||||
},
|
||||
|
||||
@computed("computedContent.name", "computedContent.originalContent.icon")
|
||||
icon(contentName, icon) {
|
||||
return iconHTML(icon, { class: contentName.dasherize() });
|
||||
},
|
||||
|
||||
@computed("_start")
|
||||
description(_start) {
|
||||
return Handlebars.escapeExpression(I18n.t(`${_start}.description`));
|
||||
},
|
||||
|
||||
@computed("_start")
|
||||
name(_start) {
|
||||
return Handlebars.escapeExpression(I18n.t(`${_start}.title`));
|
||||
},
|
||||
|
||||
@computed("i18nPrefix", "i18nPostfix", "computedContent.name")
|
||||
_start(prefix, postfix, contentName) {
|
||||
return `${prefix}.${contentName}${postfix}`;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
pluginApiIdentifiers: ["pinned-button"],
|
||||
descriptionKey: "help",
|
||||
classNames: "pinned-button",
|
||||
classNameBindings: ["isHidden"],
|
||||
layoutName: "select-kit/templates/components/pinned-button",
|
||||
|
||||
@computed("topic.pinned_globally", "pinned")
|
||||
reasonText(pinnedGlobally, pinned) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const pinnedKey = pinned ? `pinned${globally}` : "unpinned";
|
||||
const key = `topic_statuses.${pinnedKey}.help`;
|
||||
return I18n.t(key);
|
||||
},
|
||||
|
||||
@computed("pinned", "topic.deleted", "topic.unpinned")
|
||||
isHidden(pinned, deleted, unpinned) {
|
||||
return deleted || (!pinned && !unpinned);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["pinned-options"],
|
||||
classNames: "pinned-options",
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
autoHighlight() {},
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
const pinnedGlobally = this.get("topic.pinned_globally");
|
||||
const pinned = this.get("computedValue");
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
const title = I18n.t(`topic_statuses.${state}.title`);
|
||||
|
||||
content.name = `${title}${iconHTML("caret-down")}`.htmlSafe();
|
||||
content.dataName = title;
|
||||
content.icon = `thumb-tack ${state === "unpinned" ? "unpinned" : null}`;
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("init")
|
||||
_setContent() {
|
||||
const globally = this.get("topic.pinned_globally") ? "_globally" : "";
|
||||
|
||||
this.set("content", [
|
||||
{
|
||||
id: "pinned",
|
||||
name: I18n.t("topic_statuses.pinned" + globally + ".title"),
|
||||
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
|
||||
icon: "thumb-tack"
|
||||
},
|
||||
{
|
||||
id: "unpinned",
|
||||
name: I18n.t("topic_statuses.unpinned.title"),
|
||||
icon: "thumb-tack unpinned",
|
||||
description: I18n.t('topic_statuses.unpinned.help'),
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (value === "unpinned") {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import CategoryChooserComponent from "select-kit/components/category-chooser";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default CategoryChooserComponent.extend({
|
||||
pluginApiIdentifiers: ["advanced-search-category-chooser"],
|
||||
rootNone: true,
|
||||
rootNoneLabel: "category.all",
|
||||
allowUncategorized: true,
|
||||
clearable: true,
|
||||
|
||||
mutateValue(value) {
|
||||
if (value) {
|
||||
this.set("value", Category.findById(value));
|
||||
} else {
|
||||
this.set("value", null);
|
||||
}
|
||||
},
|
||||
|
||||
computeValue(category) { if (category) return category.id; }
|
||||
});
|
||||
@@ -0,0 +1,239 @@
|
||||
const { isNone, run, makeArray } = Ember;
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import UtilsMixin from "select-kit/mixins/utils";
|
||||
import DomHelpersMixin from "select-kit/mixins/dom-helpers";
|
||||
import EventsMixin from "select-kit/mixins/events";
|
||||
import PluginApiMixin from "select-kit/mixins/plugin-api";
|
||||
import { applyContentPluginApiCallbacks } from "select-kit/mixins/plugin-api";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixin, EventsMixin, {
|
||||
pluginApiIdentifiers: ["select-kit"],
|
||||
layoutName: "select-kit/templates/components/select-kit",
|
||||
classNames: ["select-kit", "select-box-kit"],
|
||||
classNameBindings: [
|
||||
"isFocused",
|
||||
"isExpanded",
|
||||
"isDisabled",
|
||||
"isHidden",
|
||||
"isAbove",
|
||||
"isBelow",
|
||||
"isLeftAligned",
|
||||
"isRightAligned"
|
||||
],
|
||||
isDisabled: false,
|
||||
isExpanded: false,
|
||||
isFocused: false,
|
||||
isHidden: false,
|
||||
renderedBodyOnce: false,
|
||||
renderedFilterOnce: false,
|
||||
tabindex: 0,
|
||||
scrollableParentSelector: ".modal-body",
|
||||
none: null,
|
||||
highlightedValue: null,
|
||||
noContentLabel: "select_kit.no_content",
|
||||
valueAttribute: "id",
|
||||
nameProperty: "name",
|
||||
autoFilterable: false,
|
||||
filterable: false,
|
||||
filter: "",
|
||||
filterPlaceholder: "select_kit.filter_placeholder",
|
||||
filterIcon: "search",
|
||||
headerIcon: null,
|
||||
rowComponent: "select-kit/select-kit-row",
|
||||
rowComponentOptions: null,
|
||||
noneRowComponent: "select-kit/select-kit-none-row",
|
||||
createRowComponent: "select-kit/select-kit-create-row",
|
||||
filterComponent: "select-kit/select-kit-filter",
|
||||
headerComponent: "select-kit/select-kit-header",
|
||||
headerComponentOptions: null,
|
||||
headerComputedContent: null,
|
||||
collectionComponent: "select-kit/select-kit-collection",
|
||||
collectionHeight: 200,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
fullWidthOnMobile: false,
|
||||
castInteger: false,
|
||||
allowAny: false,
|
||||
allowInitialValueMutation: false,
|
||||
content: null,
|
||||
computedContent: null,
|
||||
limitMatches: 100,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.noneValue = "__none__";
|
||||
this._previousScrollParentOverflow = "auto";
|
||||
this._previousCSSContext = {};
|
||||
this.set("headerComponentOptions", Ember.Object.create());
|
||||
this.set("rowComponentOptions", Ember.Object.create());
|
||||
this.set("computedContent", []);
|
||||
|
||||
if ($(window).outerWidth(false) <= 420) {
|
||||
this.setProperties({ filterable: false, autoFilterable: false });
|
||||
}
|
||||
},
|
||||
|
||||
willComputeAttributes() {},
|
||||
didComputeAttributes() {},
|
||||
|
||||
_beforeWillComputeContent(content) { return makeArray(content); },
|
||||
willComputeContent(content) { return content; },
|
||||
computeContent(content) { return content; },
|
||||
_beforeDidComputeContent(content) {
|
||||
content = applyContentPluginApiCallbacks(this.get("pluginApiIdentifiers"), content);
|
||||
|
||||
const existingCreatedComputedContent = this.get("computedContent").filterBy("created", true);
|
||||
this.setProperties({
|
||||
computedContent: content.map(c => this.computeContentItem(c)).concat(existingCreatedComputedContent)
|
||||
});
|
||||
return content;
|
||||
},
|
||||
didComputeContent() {},
|
||||
|
||||
mutateAttributes() {
|
||||
run.next(() => {
|
||||
this.mutateContent(this.get("computedContent"));
|
||||
this.mutateValue(this.get("computedValue"));
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
});
|
||||
},
|
||||
mutateContent() {},
|
||||
mutateValue(computedValue) { this.set("value", computedValue); },
|
||||
|
||||
computeHeaderContent() {
|
||||
return this.baseHeaderComputedContent();
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, options) {
|
||||
return this.baseComputedContentItem(contentItem, options);
|
||||
},
|
||||
|
||||
baseComputedContentItem(contentItem, options) {
|
||||
let originalContent;
|
||||
options = options || {};
|
||||
const name = options.name;
|
||||
|
||||
if (typeof contentItem === "string" || typeof contentItem === "number") {
|
||||
originalContent = {};
|
||||
originalContent[this.get("valueAttribute")] = contentItem;
|
||||
originalContent[this.get("nameProperty")] = name || contentItem;
|
||||
} else {
|
||||
originalContent = contentItem;
|
||||
}
|
||||
|
||||
return {
|
||||
value: this._castInteger(this.valueForContentItem(contentItem)),
|
||||
name: name || this._nameForContent(contentItem),
|
||||
locked: false,
|
||||
created: options.created || false,
|
||||
originalContent
|
||||
};
|
||||
},
|
||||
|
||||
@computed("shouldFilter", "allowAny", "filter")
|
||||
shouldDisplayFilter(shouldFilter, allowAny, filter) {
|
||||
if (shouldFilter === true) return true;
|
||||
if (allowAny === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "filteredComputedContent.[]")
|
||||
shouldDisplayNoContentRow(filter, filteredComputedContent) {
|
||||
return filter.length > 0 && filteredComputedContent.length === 0;
|
||||
},
|
||||
|
||||
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
|
||||
shouldFilter(filter, filterable, autoFilterable, renderedFilterOnce) {
|
||||
if (renderedFilterOnce === true && filterable === true) return true;
|
||||
if (filterable === true) return true;
|
||||
if (autoFilterable === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "computedContent")
|
||||
shouldDisplayCreateRow(filter, computedContent) {
|
||||
if (computedContent.map(c => c.value).includes(filter)) return false;
|
||||
if (this.get("allowAny") === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "shouldDisplayCreateRow")
|
||||
createRowComputedContent(filter, shouldDisplayCreateRow) {
|
||||
if (shouldDisplayCreateRow === true) {
|
||||
let content = this.createContentFromInput(filter);
|
||||
return this.computeContentItem(content, { created: true });
|
||||
}
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow() { return () => null; },
|
||||
|
||||
@computed
|
||||
templateForNoneRow() { return () => null; },
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return I18n.t("select_box.create", {
|
||||
content: rowComponent.get("computedContent.name")
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@computed("none")
|
||||
noneRowComputedContent(none) {
|
||||
if (isNone(none)) { return null; }
|
||||
|
||||
switch (typeof none) {
|
||||
case "string":
|
||||
return this.computeContentItem(this.noneValue, { name: I18n.t(none) });
|
||||
default:
|
||||
return this.computeContentItem(none);
|
||||
}
|
||||
},
|
||||
|
||||
createContentFromInput(input) { return input; },
|
||||
|
||||
willSelect() {
|
||||
this.clearFilter();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
didSelect() {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
},
|
||||
|
||||
willDeselect() {
|
||||
this.clearFilter();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
didDeselect() {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
},
|
||||
|
||||
clearFilter() {
|
||||
this.$filterInput().val("");
|
||||
this.setProperties({ filter: "" });
|
||||
},
|
||||
|
||||
actions: {
|
||||
onToggle() {
|
||||
this.get("isExpanded") === true ? this.collapse() : this.expand();
|
||||
},
|
||||
|
||||
onHighlight(rowComputedContent) {
|
||||
this.set("highlightedValue", rowComputedContent.value);
|
||||
},
|
||||
|
||||
onFilter(filter) {
|
||||
this.setProperties({
|
||||
highlightedValue: null,
|
||||
renderedFilterOnce: true,
|
||||
filter
|
||||
});
|
||||
this.autoHighlight();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-collection",
|
||||
classNames: ["select-kit-collection", "select-box-kit-collection"],
|
||||
tagName: "ul"
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: "create",
|
||||
|
||||
click() {
|
||||
this.sendAction("onCreate", this.get("computedContent"));
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-filter",
|
||||
classNames: ["select-kit-filter", "select-box-kit-filter"],
|
||||
classNameBindings: ["isFocused", "isHidden"],
|
||||
isHidden: Ember.computed.not("shouldDisplayFilter")
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-header",
|
||||
classNames: ["select-kit-header", "select-box-kit-header"],
|
||||
classNameBindings: ["isFocused"],
|
||||
attributeBindings: [
|
||||
"dataName:data-name",
|
||||
"tabindex",
|
||||
"ariaLabel:aria-label",
|
||||
"ariaHasPopup:aria-haspopup",
|
||||
"title"
|
||||
],
|
||||
|
||||
ariaHasPopup: true,
|
||||
|
||||
ariaLabel: Ember.computed.alias("title"),
|
||||
|
||||
name: Ember.computed.alias("computedContent.name"),
|
||||
|
||||
@computed("computedContent.icon", "computedContent.icons")
|
||||
icons(icon, icons) {
|
||||
return Ember.makeArray(icon).concat(icons).filter(i => !Ember.isEmpty(i));
|
||||
},
|
||||
|
||||
@computed("computedContent.dataName", "name")
|
||||
dataName(dataName, name) { return dataName || name; },
|
||||
|
||||
@computed("computedContent.title", "name")
|
||||
title(title, name) { return title || name; },
|
||||
|
||||
click() {
|
||||
this.sendAction("onToggle");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: "none",
|
||||
|
||||
click() {
|
||||
this.sendAction("onClear");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
const { run, isPresent, makeArray, isEmpty } = Ember;
|
||||
import UtilsMixin from "select-kit/mixins/utils";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, {
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: ["select-kit-row", "select-box-kit-row"],
|
||||
tagName: "li",
|
||||
tabIndex: -1,
|
||||
attributeBindings: [
|
||||
"tabIndex",
|
||||
"title",
|
||||
"computedContent.value:data-value",
|
||||
"computedContent.name:data-name"
|
||||
],
|
||||
classNameBindings: ["isHighlighted", "isSelected"],
|
||||
|
||||
@computed("computedContent.title", "computedContent.name")
|
||||
title(title, name) { return title || name; },
|
||||
|
||||
@computed("templateForRow")
|
||||
template(templateForRow) { return templateForRow(this); },
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setSelectionState() {
|
||||
const contentValue = this.get("computedContent.value");
|
||||
|
||||
this.set("isSelected", this.get("computedValue") === contentValue);
|
||||
this.set("isHighlighted", this.get("highlightedValue") === contentValue);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_clearDebounce() {
|
||||
const hoverDebounce = this.get("hoverDebounce");
|
||||
if (isPresent(hoverDebounce)) { run.cancel(hoverDebounce); }
|
||||
},
|
||||
|
||||
@computed("computedContent.icon", "computedContent.icons", "computedContent.originalContent.icon")
|
||||
icons(icon, icons, originalIcon) {
|
||||
return makeArray(icon)
|
||||
.concat(icons)
|
||||
.concat(makeArray(originalIcon))
|
||||
.filter(i => !isEmpty(i));
|
||||
},
|
||||
|
||||
mouseEnter() {
|
||||
this.set("hoverDebounce", run.debounce(this, this._sendOnHighlightAction, 32));
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("onSelect", this.get("computedContent"));
|
||||
},
|
||||
|
||||
_sendOnHighlightAction() {
|
||||
this.sendAction("onHighlight", this.get("computedContent"));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,168 @@
|
||||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone, isEmpty, isPresent } = Ember;
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["single-select"],
|
||||
classNames: "single-select",
|
||||
computedValue: null,
|
||||
value: null,
|
||||
allowInitialValueMutation: true,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (this.get("allowInitialValueMutation") === true) {
|
||||
const none = isNone(this.get("none"));
|
||||
const emptyValue = isEmpty(this.get("value"));
|
||||
if (none && emptyValue) {
|
||||
if (!isEmpty(this.get("content"))) {
|
||||
const value = this.valueForContentItem(this.get("content.firstObject"));
|
||||
Ember.run.next(() => this.mutateValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_compute() {
|
||||
Ember.run.scheduleOnce("afterRender", () => {
|
||||
this.willComputeAttributes();
|
||||
let content = this._beforeWillComputeContent(this.get("content"));
|
||||
content = this.willComputeContent(content);
|
||||
let value = this._beforeWillComputeValue(this.get("value"));
|
||||
content = this.computeContent(content);
|
||||
content = this._beforeDidComputeContent(content);
|
||||
value = this.willComputeValue(value);
|
||||
value = this.computeValue(value);
|
||||
value = this._beforeDidComputeValue(value);
|
||||
this.didComputeContent(content);
|
||||
this.didComputeValue(value);
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
this.didComputeAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
_beforeWillComputeValue(value) {
|
||||
switch (typeof value) {
|
||||
case "string":
|
||||
case "number":
|
||||
return this._castInteger(value === "" ? null : value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
},
|
||||
willComputeValue(value) { return value; },
|
||||
computeValue(value) { return value; },
|
||||
_beforeDidComputeValue(value) {
|
||||
if (!isEmpty(this.get("content")) && isNone(value) && isNone(this.get("none"))) {
|
||||
value = this.valueForContentItem(get(this.get("content"), "firstObject"));
|
||||
}
|
||||
|
||||
this.setProperties({ computedValue: value });
|
||||
return value;
|
||||
},
|
||||
didComputeValue(value) { return value; },
|
||||
|
||||
filterComputedContent(computedContent, computedValue, filter) {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
baseHeaderComputedContent() {
|
||||
return {
|
||||
icons: Ember.makeArray(this.getWithDefault("headerIcon", [])),
|
||||
name: this.get("selectedComputedContent.name") || this.get("noneRowComputedContent.name")
|
||||
};
|
||||
},
|
||||
|
||||
@computed("computedContent.[]", "computedValue", "filter", "shouldFilter")
|
||||
filteredComputedContent(computedContent, computedValue, filter, shouldFilter) {
|
||||
if (shouldFilter === true) {
|
||||
computedContent = this.filterComputedContent(computedContent, computedValue, filter);
|
||||
}
|
||||
|
||||
return computedContent.slice(0, this.get("limitMatches"));
|
||||
},
|
||||
|
||||
@computed("computedValue", "computedContent.[]")
|
||||
selectedComputedContent(computedValue, computedContent) {
|
||||
if (isNone(computedValue) || isNone(computedContent)) { return null; }
|
||||
return computedContent.findBy("value", computedValue);
|
||||
},
|
||||
|
||||
@computed("selectedComputedContent")
|
||||
hasSelection(selectedComputedContent) {
|
||||
return selectedComputedContent !== this.get("noneRowComputedContent") &&
|
||||
!Ember.isNone(selectedComputedContent);
|
||||
},
|
||||
|
||||
@computed("filter", "computedValue")
|
||||
shouldDisplayCreateRow(filter, computedValue) {
|
||||
return this._super() && computedValue !== filter;
|
||||
},
|
||||
|
||||
autoHighlight() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
const filteredComputedContent = this.get("filteredComputedContent");
|
||||
const displayCreateRow = this.get("shouldDisplayCreateRow");
|
||||
const none = this.get("noneRowComputedContent");
|
||||
|
||||
if (this.get("hasSelection") && isEmpty(this.get("filter"))) {
|
||||
this.send("onHighlight", this.get("selectedComputedContent"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNone(this.get("highlightedValue")) && !isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", get(filteredComputedContent, "firstObject"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (displayCreateRow === true && isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", this.get("createRowComputedContent"));
|
||||
}
|
||||
else if (!isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", get(filteredComputedContent, "firstObject"));
|
||||
}
|
||||
else if (isEmpty(filteredComputedContent) && isPresent(none) && displayCreateRow === false) {
|
||||
this.send("onHighlight", none);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
validateComputedContentItem(computedContentItem) {
|
||||
return this.get("computedValue") !== computedContentItem.value;
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClear() {
|
||||
this.send("onDeselect", this.get("selectedComputedContent"));
|
||||
},
|
||||
|
||||
onCreate(computedContentItem) {
|
||||
if (this.validateComputedContentItem(computedContentItem)) {
|
||||
this.get("computedContent").pushObject(computedContentItem);
|
||||
this.send("onSelect", computedContentItem);
|
||||
}
|
||||
},
|
||||
|
||||
onSelect(rowComputedContentItem) {
|
||||
this.willSelect(rowComputedContentItem);
|
||||
this.set("computedValue", rowComputedContentItem.value);
|
||||
this.mutateAttributes();
|
||||
Ember.run.schedule("afterRender", () => this.didSelect(rowComputedContentItem));
|
||||
},
|
||||
|
||||
onDeselect(rowComputedContentItem) {
|
||||
this.willDeselect(rowComputedContentItem);
|
||||
this.set("computedValue", null);
|
||||
this.mutateAttributes();
|
||||
Ember.run.schedule("afterRender", () => this.didDeselect(rowComputedContentItem));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["tag-notifications-button"],
|
||||
classNames: "tag-notifications-button",
|
||||
i18nPrefix: "tagging.notifications",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
mutateValue(value) {
|
||||
this.sendAction("action", value);
|
||||
},
|
||||
|
||||
computeValue() {
|
||||
return this.get("notificationLevel");
|
||||
},
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) {
|
||||
return [iconForSelectedDetails, "caret-down"];
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["topic-footer-mobile-dropdown"],
|
||||
classNames: "topic-footer-mobile-dropdown",
|
||||
filterable: false,
|
||||
autoFilterable: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t("topic.controls");
|
||||
return content;
|
||||
},
|
||||
|
||||
computeContent(content) {
|
||||
const topic = this.get("topic");
|
||||
const details = topic.get("details");
|
||||
|
||||
if (details.get("can_invite_to")) {
|
||||
content.push({ id: "invite", icon: "users", name: I18n.t("topic.invite_reply.title") });
|
||||
}
|
||||
|
||||
if (topic.get("bookmarked")) {
|
||||
content.push({ id: "bookmark", icon: "bookmark", name: I18n.t("bookmarked.clear_bookmarks") });
|
||||
} else {
|
||||
content.push({ id: "bookmark", icon: "bookmark", name: I18n.t("bookmarked.title") });
|
||||
}
|
||||
|
||||
content.push({ id: "share", icon: "link", name: I18n.t("topic.share.title") });
|
||||
|
||||
if (details.get("can_flag_topic")) {
|
||||
content.push({ id: "flag", icon: "flag", name: I18n.t("topic.flag_topic.title") });
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
autoHighlight() {},
|
||||
|
||||
mutateValue(value) {
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (!topic.get("id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refresh = () => this.send("onDeselect", value);
|
||||
|
||||
switch(value) {
|
||||
case "invite":
|
||||
this.attrs.showInvite();
|
||||
refresh();
|
||||
break;
|
||||
case "bookmark":
|
||||
topic.toggleBookmark().then(() => refresh() );
|
||||
break;
|
||||
case "share":
|
||||
this.appEvents.trigger("share:url", topic.get("shareUrl"), $("#topic-footer-buttons"));
|
||||
refresh();
|
||||
break;
|
||||
case "flag":
|
||||
this.attrs.showFlagTopic();
|
||||
refresh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/topic-notifications-button",
|
||||
classNames: "topic-notifications-button",
|
||||
showFullTitle: true,
|
||||
appendReason: true
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { topicLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["topic-notifications-options"],
|
||||
classNames: "topic-notifications-options",
|
||||
content: topicLevels,
|
||||
i18nPrefix: "topic.notifications",
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
@on("didInsertElement")
|
||||
_bindGlobalLevelChanged() {
|
||||
this.appEvents.on("topic-notifications-button:changed", (msg) => {
|
||||
if (msg.type === "notification") {
|
||||
if (this.get("computedValue") !== msg.id) {
|
||||
this.get("topic.details").updateNotifications(msg.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindGlobalLevelChanged() {
|
||||
this.appEvents.off("topic-notifications-button:changed");
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
if (value !== this.get("value")) {
|
||||
this.get("topic.details").updateNotifications(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user