[WIP] select-box-kit refactoring
This commit is contained in:
parent
a9f718fe57
commit
ae1743c61f
|
@ -46,6 +46,7 @@
|
|||
"expandSelectBox":true,
|
||||
"collapseSelectBox":true,
|
||||
"selectBoxSelectRow":true,
|
||||
"selectBoxSelectNoneRow":true,
|
||||
"selectBoxFillInFilter":true,
|
||||
"asyncTestDiscourse":true,
|
||||
"fixture":true,
|
||||
|
|
|
@ -50,5 +50,3 @@ export default Ember.Component.extend({
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -26,9 +26,7 @@
|
|||
{{combo-box name="badge_type_id"
|
||||
value=buffered.badge_type_id
|
||||
content=badgeTypes
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.name"
|
||||
disabled=readOnly}}
|
||||
isDisabled=readOnly}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -36,8 +34,7 @@
|
|||
{{combo-box name="badge_grouping_id"
|
||||
value=buffered.badge_grouping_id
|
||||
content=badgeGroupings
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.displayName"}}
|
||||
nameProperty="name"}}
|
||||
<button {{action "editGroupings"}} class='btn'>{{d-icon 'pencil'}}</button>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{#if editing}}
|
||||
{{#admin-form-row label="admin.user_fields.type"}}
|
||||
{{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}}
|
||||
{{combo-box content=fieldTypes value=buffered.field_type}}
|
||||
{{/admin-form-row}}
|
||||
|
||||
{{#admin-form-row label="admin.user_fields.name"}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{{input value=buffered.path_whitelist placeholder="/blog/.*" enter="save" class="path-whitelist"}}
|
||||
</td>
|
||||
<td>
|
||||
{{category-select-box value=categoryId class="small"}}
|
||||
{{category-chooser value=categoryId class="small"}}
|
||||
</td>
|
||||
<td>
|
||||
{{d-button icon="check" action="save" class="btn-primary" disabled=cantSave}}
|
||||
|
|
|
@ -36,8 +36,7 @@
|
|||
|
||||
<h3>{{i18n "admin.customize.theme.color_scheme"}}</h3>
|
||||
<p>{{i18n "admin.customize.theme.color_scheme_select"}}</p>
|
||||
<p>{{select-box content=colorSchemes
|
||||
textKey="name"
|
||||
<p>{{combo-box content=colorSchemes
|
||||
filterable=true
|
||||
value=colorSchemeId
|
||||
icon="paint-brush"}}
|
||||
|
@ -123,11 +122,8 @@
|
|||
</ul>
|
||||
{{/unless}}
|
||||
{{#if selectableChildThemes}}
|
||||
<p>{{combo-box content=selectableChildThemes
|
||||
nameProperty="name"
|
||||
value=selectedChildThemeId
|
||||
valueAttribute="id"}}
|
||||
|
||||
<p>
|
||||
{{combo-box content=selectableChildThemes value=selectedChildThemeId}}
|
||||
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
|
||||
</p>
|
||||
{{/if}}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</div>
|
||||
|
||||
<div class='control'>
|
||||
{{combo-box content=groups valueAttribute="id" value=groupId none="admin.groups.bulk_select"}}
|
||||
{{combo-box filterable=true content=groups value=groupId none="admin.groups.bulk_select"}}
|
||||
</div>
|
||||
|
||||
<div class='control'>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions nameProperty="name" value=filterActionId none="admin.logs.staff_actions.all"}}
|
||||
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions value=filterActionId none="admin.logs.staff_actions.all"}}
|
||||
{{/if}}
|
||||
|
||||
<div class="pull-right">
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
{{#d-modal-body title="admin.customize.colors.select_base.title"}}
|
||||
{{i18n "admin.customize.colors.select_base.description"}}
|
||||
{{combo-box content=model
|
||||
nameProperty="name"
|
||||
value=selectedBaseThemeId
|
||||
valueAttribute="base_scheme_id"}}
|
||||
{{/d-modal-body}}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
{{i18n 'admin.dashboard.reports.start_date'}} {{date-picker-past value=startDate defaultDate=startDate}}
|
||||
{{i18n 'admin.dashboard.reports.end_date'}} {{date-picker-past value=endDate defaultDate=endDate}}
|
||||
{{#if showCategoryOptions}}
|
||||
{{combo-box valueAttribute="value" content=categoryOptions value=categoryId}}
|
||||
{{combo-box filterable=true valueAttribute="value" content=categoryOptions value=categoryId}}
|
||||
{{/if}}
|
||||
{{#if showGroupOptions}}
|
||||
{{combo-box valueAttribute="value" content=groupOptions value=groupId}}
|
||||
{{combo-box filterable=true valueAttribute="value" content=groupOptions value=groupId}}
|
||||
{{/if}}
|
||||
{{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}}
|
||||
{{d-button action="exportCsv" label="admin.export_csv.button_text" icon="download"}}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<form class="form-horizontal">
|
||||
<div>
|
||||
<label>{{i18n 'admin.badges.badge'}}</label>
|
||||
{{combo-box valueAttribute="id" value=selectedBadgeId content=grantableBadges nameProperty="name"}}
|
||||
{{combo-box filterable=true value=selectedBadgeId content=grantableBadges}}
|
||||
</div>
|
||||
<label>
|
||||
<label>{{i18n 'admin.badges.reason'}}</label>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<section class="details {{unless model.active 'not-activated'}}">
|
||||
|
||||
<div class='user-controls'>
|
||||
{{#if model.canViewProfile}}
|
||||
{{#link-to 'user' model class="btn"}}
|
||||
|
@ -379,7 +378,7 @@
|
|||
<div class='controls'>
|
||||
{{#if model.customGroups}}
|
||||
{{i18n 'admin.groups.primary'}}
|
||||
{{combo-box content=model.customGroups value=model.primary_group_id nameProperty="name" none="admin.groups.no_primary"}}
|
||||
{{combo-box content=model.customGroups value=model.primary_group_id none="admin.groups.no_primary"}}
|
||||
{{/if}}
|
||||
{{#if primaryGroupDirty}}
|
||||
{{d-button icon="check" class="ok" action="savePrimaryGroup"}}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
<label for='content-type'>{{i18n 'admin.web_hooks.content_type'}}</label>
|
||||
{{combo-box content=contentTypes
|
||||
name="content-type"
|
||||
nameProperty="name"
|
||||
valueAttribute="id"
|
||||
value=model.content_type}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//= require ./ember-addons/ember-computed-decorators
|
||||
//= require ./ember-addons/fmt
|
||||
//= require_tree ./discourse-common
|
||||
//= require_tree ./select-box-kit
|
||||
//= require ./discourse
|
||||
//= require ./deprecated
|
||||
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
tagName: 'select',
|
||||
attributeBindings: ['tabindex', 'disabled'],
|
||||
classNames: ['combobox'],
|
||||
valueAttribute: 'id',
|
||||
nameProperty: 'name',
|
||||
|
||||
buildBuffer(buffer) {
|
||||
const nameProperty = this.get('nameProperty');
|
||||
const none = this.get('none');
|
||||
let noneValue = null;
|
||||
|
||||
// Add none option if required
|
||||
if (typeof none === "string") {
|
||||
buffer.push('<option value="">' + I18n.t(none) + "</option>");
|
||||
} else if (typeof none === "object") {
|
||||
noneValue = Em.get(none, this.get('valueAttribute'));
|
||||
buffer.push(`<option value="${noneValue}">${Em.get(none, nameProperty)}</option>`);
|
||||
}
|
||||
|
||||
let selected = this.get('value');
|
||||
if (!Em.isNone(selected)) { selected = selected.toString(); }
|
||||
|
||||
let selectedFound = false;
|
||||
let firstVal = undefined;
|
||||
const content = this.get('content');
|
||||
|
||||
if (content) {
|
||||
let first = true;
|
||||
content.forEach(o => {
|
||||
let val = o[this.get('valueAttribute')];
|
||||
if (typeof val === "undefined") { val = o; }
|
||||
if (!Em.isNone(val)) { val = val.toString(); }
|
||||
|
||||
const selectedText = (val === selected) ? "selected" : "";
|
||||
const name = Handlebars.Utils.escapeExpression(Ember.get(o, nameProperty) || o);
|
||||
|
||||
if (val === selected) {
|
||||
selectedFound = true;
|
||||
}
|
||||
if (first) {
|
||||
firstVal = val;
|
||||
first = false;
|
||||
}
|
||||
buffer.push(`<option ${selectedText} value="${val}">${name}</option>`);
|
||||
});
|
||||
}
|
||||
|
||||
if (!selectedFound && !noneValue) {
|
||||
if (none) {
|
||||
this.set('value', null);
|
||||
} else {
|
||||
this.set('value', firstVal);
|
||||
}
|
||||
}
|
||||
|
||||
Ember.run.scheduleOnce('afterRender', this, this._updateSelect2);
|
||||
},
|
||||
|
||||
@observes('value')
|
||||
valueChanged() {
|
||||
const $combo = this.$(),
|
||||
val = this.get('value');
|
||||
|
||||
if (val !== undefined && val !== null) {
|
||||
$combo.select2('val', val.toString());
|
||||
} else {
|
||||
$combo.select2('val', null);
|
||||
}
|
||||
},
|
||||
|
||||
@observes('content.[]')
|
||||
_rerenderOnChange() {
|
||||
this.rerenderBuffer();
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
// Workaround for https://github.com/emberjs/ember.js/issues/9813
|
||||
// Can be removed when fixed. Without it, the wrong option is selected
|
||||
this.$('option').each((i, o) => o.selected = !!$(o).attr('selected'));
|
||||
|
||||
// observer for item names changing (optional)
|
||||
if (this.get('nameChanges')) {
|
||||
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerenderBuffer);
|
||||
}
|
||||
|
||||
const $elem = this.$();
|
||||
const caps = this.capabilities;
|
||||
const minimumResultsForSearch = this.get('minimumResultsForSearch') || ((caps && caps.isIOS) ? -1 : 5);
|
||||
|
||||
if (!this.get("selectionTemplate") && this.get("selectionIcon")) {
|
||||
this.selectionTemplate = (item) => {
|
||||
let name = Em.get(item, 'text');
|
||||
name = Handlebars.escapeExpression(name);
|
||||
return iconHTML(this.get('selectionIcon')) + name;
|
||||
};
|
||||
}
|
||||
|
||||
const options = {
|
||||
minimumResultsForSearch,
|
||||
width: this.get('width') || 'resolve',
|
||||
allowClear: true
|
||||
};
|
||||
|
||||
if (this.comboTemplate) {
|
||||
options.formatResult = this.comboTemplate.bind(this);
|
||||
}
|
||||
|
||||
if (this.selectionTemplate) {
|
||||
options.formatSelection = this.selectionTemplate.bind(this);
|
||||
}
|
||||
|
||||
$elem.select2(options);
|
||||
|
||||
const castInteger = this.get('castInteger');
|
||||
$elem.on("change", e => {
|
||||
let val = $(e.target).val();
|
||||
if (val && val.length && castInteger) {
|
||||
val = parseInt(val, 10);
|
||||
}
|
||||
Ember.run(() => this.set('value', val));
|
||||
});
|
||||
|
||||
Ember.run.scheduleOnce('afterRender', this, this._triggerChange);
|
||||
},
|
||||
|
||||
_updateSelect2() {
|
||||
this.$().trigger('change.select2');
|
||||
},
|
||||
|
||||
_triggerChange() {
|
||||
this.$().trigger('change');
|
||||
},
|
||||
|
||||
@on('willDestroyElement')
|
||||
_destroyDropdown() {
|
||||
this.$().select2('destroy');
|
||||
}
|
||||
|
||||
}));
|
|
@ -1,49 +0,0 @@
|
|||
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["categories-admin-dropdown"],
|
||||
|
||||
icon: `${iconHTML('bars')}${iconHTML('caret-down')}`.htmlSafe(),
|
||||
|
||||
generatedHeadertext: null,
|
||||
|
||||
@computed
|
||||
content() {
|
||||
const items = [
|
||||
{
|
||||
id: "create",
|
||||
text: 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",
|
||||
text: I18n.t("categories.reorder.title"),
|
||||
description: I18n.t("categories.reorder.title_long"),
|
||||
icon: "random"
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
actionNames: {
|
||||
create: "createCategory",
|
||||
reorder: "reorderCategories"
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
this._super(content);
|
||||
|
||||
this.sendAction(`actionNames.${this.get("value")}`);
|
||||
this.set("value", null);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,88 +0,0 @@
|
|||
import Combobox from 'discourse-common/components/combo-box';
|
||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
import PermissionType from 'discourse/models/permission-type';
|
||||
import Category from 'discourse/models/category';
|
||||
|
||||
export default Combobox.extend({
|
||||
classNames: ['combobox category-combobox'],
|
||||
dataAttributes: ['id', 'description_text'],
|
||||
overrideWidths: true,
|
||||
castInteger: true,
|
||||
|
||||
@computed("scopedCategoryId", "categories")
|
||||
content(scopedCategoryId, categories) {
|
||||
// Always scope to the parent of a category, if present
|
||||
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 = c.get('id');
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get('parent_category_id') !== scopedCategoryId) { return false; }
|
||||
if (c.get('isUncategorizedCategory') || excludeCategoryId === categoryId) { return false; }
|
||||
return c.get('permission') === PermissionType.FULL;
|
||||
});
|
||||
},
|
||||
|
||||
@on("init")
|
||||
@observes("site.sortedCategories")
|
||||
_updateCategories() {
|
||||
if (!this.get('categories')) {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
this.set('categories', categories);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("rootNone", "rootNoneLabel")
|
||||
none(rootNone, rootNoneLabel) {
|
||||
if (this.siteSettings.allow_uncategorized_topics || this.get('allowUncategorized')) {
|
||||
if (rootNone) {
|
||||
return rootNoneLabel || "category.none";
|
||||
} else {
|
||||
return Category.findUncategorized();
|
||||
}
|
||||
} else {
|
||||
return 'category.choose';
|
||||
}
|
||||
},
|
||||
|
||||
comboTemplate(item) {
|
||||
let category;
|
||||
|
||||
// If we have no id, but text with the uncategorized name, we can use that badge.
|
||||
if (Ember.isEmpty(item.id)) {
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get('name') === item.text) {
|
||||
category = uncat;
|
||||
}
|
||||
} else {
|
||||
category = Category.findById(parseInt(item.id,10));
|
||||
}
|
||||
|
||||
if (!category) return item.text;
|
||||
let result = categoryBadgeHTML(category, {link: false, allowUncategorized: true, hideParent: true});
|
||||
const parentCategoryId = category.get('parent_category_id');
|
||||
|
||||
if (parentCategoryId) {
|
||||
result = categoryBadgeHTML(Category.findById(parentCategoryId), {link: false}) + " " + result;
|
||||
}
|
||||
|
||||
result += ` <span class='topic-count'>× ${category.get('topic_count')}</span>`;
|
||||
|
||||
const description = category.get('description');
|
||||
// TODO wtf how can this be null?;
|
||||
if (description && description !== 'null') {
|
||||
result += `<div class="category-desc">${description.substr(0, 200)}${description.length > 200 ? '…' : ''}</div>`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
import NotificationOptionsComponent from "discourse/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: ["category-notifications-button"],
|
||||
|
||||
hidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
|
||||
|
||||
i18nPrefix: "category.notifications",
|
||||
|
||||
value: Em.computed.alias("category.notification_level"),
|
||||
|
||||
@computed("value")
|
||||
icon() {
|
||||
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
|
||||
},
|
||||
|
||||
generatedHeadertext: null,
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
this._super(content);
|
||||
|
||||
this.get("category").setNotification(this.get("value"));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,162 +0,0 @@
|
|||
import SelectBoxComponent from "discourse/components/select-box";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
import { observes, on } from "ember-addons/ember-computed-decorators";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default SelectBoxComponent.extend({
|
||||
classNames: ["category-select-box"],
|
||||
|
||||
selectBoxRowComponent: "category-select-box/category-select-box-row",
|
||||
|
||||
textKey: "name",
|
||||
|
||||
filterable: true,
|
||||
|
||||
castInteger: true,
|
||||
|
||||
clearable: true,
|
||||
|
||||
allowUncategorized: null,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (!Ember.isNone(this.get("categories"))) {
|
||||
this.set("content", this.get("categories"));
|
||||
this._scopeCategories();
|
||||
}
|
||||
|
||||
if (Ember.isNone(this.get("value"))) {
|
||||
if (this.siteSettings.allow_uncategorized_topics && this.get("allowUncategorized") !== false) {
|
||||
this.set("value", Category.findUncategorized().id);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
filterFunction: function(content) {
|
||||
const _matchFunction = (filter, text) => {
|
||||
return text.toLowerCase().indexOf(filter) > -1;
|
||||
};
|
||||
|
||||
return (selectBox) => {
|
||||
const filter = selectBox.get("filter").toLowerCase();
|
||||
return _.filter(content, (c) => {
|
||||
const category = Category.findById(c[selectBox.get("idKey")]);
|
||||
const text = c[selectBox.get("textKey")];
|
||||
if (category && category.get("parentCategory")) {
|
||||
const categoryName = category.get("parentCategory.name");
|
||||
return _matchFunction(filter, text) || _matchFunction(filter, categoryName);
|
||||
} else {
|
||||
return _matchFunction(filter, text);
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@on("init")
|
||||
@observes("selectedContent")
|
||||
_setHeaderText: function() {
|
||||
let headerText;
|
||||
|
||||
if (Ember.isNone(this.get("selectedContent"))) {
|
||||
if (this.siteSettings.allow_uncategorized_topics) {
|
||||
headerText = Ember.get(Category.findUncategorized(), this.get("textKey"));
|
||||
} else {
|
||||
headerText = I18n.t("category.choose").htmlSafe();
|
||||
}
|
||||
} else {
|
||||
headerText = this.get("selectedContent.text");
|
||||
}
|
||||
|
||||
this.set("headerText", headerText);
|
||||
},
|
||||
|
||||
templateForRow: function() {
|
||||
return (rowComponent) => this.rowContentTemplate(rowComponent.get("content"));
|
||||
}.property(),
|
||||
|
||||
@observes("scopedCategoryId", "categories")
|
||||
_scopeCategories() {
|
||||
let scopedCategoryId = this.get("scopedCategoryId");
|
||||
const categories = this.get("categories");
|
||||
|
||||
// Always scope to the parent of a category, if present
|
||||
if (scopedCategoryId) {
|
||||
const scopedCat = Category.findById(scopedCategoryId);
|
||||
scopedCategoryId = scopedCat.get("parent_category_id") || scopedCat.get("id");
|
||||
}
|
||||
|
||||
const excludeCategoryId = this.get("excludeCategoryId");
|
||||
|
||||
const filteredCategories = categories.filter(c => {
|
||||
const categoryId = c.get("id");
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get("parent_category_id") !== scopedCategoryId) { return false; }
|
||||
if (excludeCategoryId === categoryId) { return false; }
|
||||
if (this.get("allowUncategorized") === false && c.get("isUncategorizedCategory")) { return false; }
|
||||
if (this.get("allowUncategorized") !== true) {
|
||||
if (!this.siteSettings.allow_uncategorized_topics && c.get("isUncategorizedCategory")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return c.get("permission") === PermissionType.FULL;
|
||||
});
|
||||
|
||||
this.set("content", filteredCategories);
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_bindComposerResizing() {
|
||||
this.appEvents.on("composer:resized", this, this.applyDirection);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindComposerResizing() {
|
||||
this.appEvents.off("composer:resized");
|
||||
},
|
||||
|
||||
@on("init")
|
||||
@observes("site.sortedCategories")
|
||||
_updateCategories() {
|
||||
if (!this.get("categories")) {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
this.set("categories", categories);
|
||||
}
|
||||
},
|
||||
|
||||
rowContentTemplate(item) {
|
||||
let category;
|
||||
|
||||
// If we have no id, but text with the uncategorized name, we can use that badge.
|
||||
if (Ember.isEmpty(item.id)) {
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get("name") === item.text) {
|
||||
category = uncat;
|
||||
}
|
||||
} else {
|
||||
category = Category.findById(parseInt(item.id,10));
|
||||
}
|
||||
|
||||
if (!category) return item.text;
|
||||
let result = categoryBadgeHTML(category, {link: false, allowUncategorized: true, hideParent: true});
|
||||
const parentCategoryId = category.get("parent_category_id");
|
||||
|
||||
if (parentCategoryId) {
|
||||
result = `<div class="category-status">${categoryBadgeHTML(Category.findById(parentCategoryId), {link: false})} ${result}`;
|
||||
} else {
|
||||
result = `<div class="category-status">${result}`;
|
||||
}
|
||||
|
||||
result += ` <span class="topic-count">× ${category.get("topic_count")}</span></div>`;
|
||||
|
||||
const description = category.get("description");
|
||||
// TODO wtf how can this be null?;
|
||||
if (description && description !== "null") {
|
||||
result += `<div class="category-desc">${description.substr(0, 200)}${description.length > 200 ? '…' : ''}</div>`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import SelectBoxRowComponent from "discourse/components/select-box/select-box-row";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default SelectBoxRowComponent.extend({
|
||||
classNameBindings: ["isUncategorized"],
|
||||
|
||||
@computed("content")
|
||||
isUncategorized(content) {
|
||||
const category = Category.findById(content.id);
|
||||
return category.get("isUncategorizedCategory");
|
||||
}
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import SelectBoxComponent from "discourse/components/select-box";
|
||||
|
||||
export default SelectBoxComponent.extend({
|
||||
classNames: ["dropdown-select-box"],
|
||||
wrapper: false,
|
||||
verticalOffset: 3,
|
||||
collectionHeight: "auto",
|
||||
fullWidthOnMobile: true,
|
||||
selectBoxHeaderComponent: "dropdown-select-box/dropdown-header",
|
||||
|
||||
@computed
|
||||
templateForRow: function() {
|
||||
return (rowComponent) => {
|
||||
let template = "";
|
||||
const content = rowComponent.get("content");
|
||||
|
||||
const icon = rowComponent.icon();
|
||||
if (icon) {
|
||||
template += `<div class="icons">${icon}</div>`;
|
||||
}
|
||||
|
||||
template += `
|
||||
<div class="texts">
|
||||
<span class="title">${Handlebars.escapeExpression(Ember.get(content, this.get("textKey")))}</span>
|
||||
<span class="desc">${Handlebars.escapeExpression(content.description)}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return template;
|
||||
};
|
||||
}
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
import SelectBoxHeaderComponent from "discourse/components/select-box/select-box-header";
|
||||
|
||||
export default SelectBoxHeaderComponent.extend({
|
||||
layoutName: "components/dropdown-select-box/dropdown-header",
|
||||
|
||||
classNames: ["dropdown-header"]
|
||||
});
|
|
@ -1,207 +0,0 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import Combobox from 'discourse-common/components/combo-box';
|
||||
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
|
||||
const LATER_TODAY = 'later_today';
|
||||
const TOMORROW = 'tomorrow';
|
||||
const LATER_THIS_WEEK = 'later_this_week';
|
||||
const THIS_WEEKEND = 'this_weekend';
|
||||
const NEXT_WEEK = 'next_week';
|
||||
const TWO_WEEKS = 'two_weeks';
|
||||
const NEXT_MONTH = 'next_month';
|
||||
const FOREVER = 'forever';
|
||||
|
||||
export const PICK_DATE_AND_TIME = 'pick_date_and_time';
|
||||
export const SET_BASED_ON_LAST_POST = 'set_based_on_last_post';
|
||||
|
||||
export const FORMAT = 'YYYY-MM-DD HH:mm';
|
||||
|
||||
export default Combobox.extend({
|
||||
classNames: ['future-date-input-selector'],
|
||||
isCustom: Ember.computed.equal("value", PICK_DATE_AND_TIME),
|
||||
|
||||
@computed()
|
||||
content() {
|
||||
const selections = [];
|
||||
const now = moment();
|
||||
const canScheduleToday = (24 - now.hour()) > 6;
|
||||
const day = now.day();
|
||||
|
||||
if (canScheduleToday) {
|
||||
selections.push({
|
||||
id: LATER_TODAY,
|
||||
name: I18n.t('topic.auto_update_input.later_today')
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: TOMORROW,
|
||||
name: I18n.t('topic.auto_update_input.tomorrow')
|
||||
});
|
||||
|
||||
if (!canScheduleToday && day < 4) {
|
||||
selections.push({
|
||||
id: LATER_THIS_WEEK,
|
||||
name: I18n.t('topic.auto_update_input.later_this_week')
|
||||
});
|
||||
}
|
||||
|
||||
if (day < 5 && this.get('includeWeekend')) {
|
||||
selections.push({
|
||||
id: THIS_WEEKEND,
|
||||
name: I18n.t('topic.auto_update_input.this_weekend')
|
||||
});
|
||||
}
|
||||
|
||||
if (day !== 7) {
|
||||
selections.push({
|
||||
id: NEXT_WEEK,
|
||||
name: I18n.t('topic.auto_update_input.next_week')
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: TWO_WEEKS,
|
||||
name: I18n.t('topic.auto_update_input.two_weeks')
|
||||
});
|
||||
|
||||
if (moment().endOf('month').date() !== now.date()) {
|
||||
selections.push({
|
||||
id: NEXT_MONTH,
|
||||
name: I18n.t('topic.auto_update_input.next_month')
|
||||
});
|
||||
}
|
||||
|
||||
if (this.get('includeForever')) {
|
||||
selections.push({
|
||||
id: FOREVER,
|
||||
name: I18n.t('topic.auto_update_input.forever')
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: PICK_DATE_AND_TIME,
|
||||
name: I18n.t('topic.auto_update_input.pick_date_and_time')
|
||||
});
|
||||
|
||||
if (this.get('statusType') === CLOSE_STATUS_TYPE) {
|
||||
selections.push({
|
||||
id: SET_BASED_ON_LAST_POST,
|
||||
name: I18n.t('topic.auto_update_input.set_based_on_last_post')
|
||||
});
|
||||
}
|
||||
|
||||
return selections;
|
||||
},
|
||||
|
||||
@observes('value')
|
||||
_updateInput() {
|
||||
if (this.get('isCustom')) return;
|
||||
let input = null;
|
||||
const { time } = this.get('updateAt');
|
||||
|
||||
if (time && !Ember.isEmpty(this.get('value'))) {
|
||||
input = time.format(FORMAT);
|
||||
}
|
||||
|
||||
this.set('input', input);
|
||||
},
|
||||
|
||||
@computed('value')
|
||||
updateAt(value) {
|
||||
return this._updateAt(value);
|
||||
},
|
||||
|
||||
comboTemplate(state) {
|
||||
return this._format(state);
|
||||
},
|
||||
|
||||
selectionTemplate(state) {
|
||||
return this._format(state);
|
||||
},
|
||||
|
||||
_format(state) {
|
||||
let { time, icon } = this._updateAt(state.id);
|
||||
let icons;
|
||||
|
||||
if (icon) {
|
||||
icons = icon.split(',').map(i => iconHTML(i)).join(" ");
|
||||
}
|
||||
|
||||
if (time) {
|
||||
if (state.id === LATER_TODAY) {
|
||||
time = time.format('h a');
|
||||
} else if (state.id === NEXT_MONTH || state.id === TWO_WEEKS) {
|
||||
time = time.format('MMM D');
|
||||
} else {
|
||||
time = time.format('ddd, h a');
|
||||
}
|
||||
}
|
||||
|
||||
let output = "";
|
||||
|
||||
if (!Ember.isEmpty(icons)) {
|
||||
output += `<span class='future-date-input-selector-icons'>${icons}</span>`;
|
||||
}
|
||||
|
||||
output += `<span>${state.text}</span>`;
|
||||
|
||||
if (time && state.id !== FOREVER) {
|
||||
output += `<span class='future-date-input-selector-datetime'>${time}</span>`;
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
_updateAt(selection) {
|
||||
let time = moment();
|
||||
let icon;
|
||||
const timeOfDay = this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18;
|
||||
|
||||
switch(selection) {
|
||||
case LATER_TODAY:
|
||||
time = time.hour(18).minute(0);
|
||||
icon = 'moon-o';
|
||||
break;
|
||||
case TOMORROW:
|
||||
time = time.add(1, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'sun-o';
|
||||
break;
|
||||
case LATER_THIS_WEEK:
|
||||
time = time.add(2, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case THIS_WEEKEND:
|
||||
time = time.day(6).hour(timeOfDay).minute(0);
|
||||
icon = 'bed';
|
||||
break;
|
||||
case NEXT_WEEK:
|
||||
time = time.add(1, 'week').day(1).hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case TWO_WEEKS:
|
||||
time = time.add(2, 'week').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case NEXT_MONTH:
|
||||
time = time.add(1, 'month').startOf('month').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case FOREVER:
|
||||
time = time.add(1000, 'year').hour(timeOfDay).minute(0);
|
||||
icon = 'gavel';
|
||||
break;
|
||||
case PICK_DATE_AND_TIME:
|
||||
time = null;
|
||||
icon = 'calendar-plus-o';
|
||||
break;
|
||||
case SET_BASED_ON_LAST_POST:
|
||||
time = null;
|
||||
icon = 'clock-o';
|
||||
break;
|
||||
}
|
||||
|
||||
return { time, icon };
|
||||
},
|
||||
});
|
|
@ -3,7 +3,7 @@ import {
|
|||
FORMAT,
|
||||
PICK_DATE_AND_TIME,
|
||||
SET_BASED_ON_LAST_POST
|
||||
} from "discourse/components/future-date-input-selector";
|
||||
} from "select-box-kit/components/future-date-input-selector";
|
||||
|
||||
import { PUBLISH_TO_CATEGORY_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ export default NotificationOptionsComponent.extend({
|
|||
i18nPrefix: "groups.notifications",
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
onSelect(content) {
|
||||
this._super(content);
|
||||
|
||||
this.get("group").setNotification(this.get("value"), this.get("user.id"));
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import computed 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"],
|
||||
|
||||
i18nPrefix: "",
|
||||
i18nPostfix: "",
|
||||
textKey: "key",
|
||||
showFullTitle: true,
|
||||
fullWidthOnMobile: true,
|
||||
content: allLevels,
|
||||
|
||||
value: Em.computed.alias("notificationLevel"),
|
||||
|
||||
@computed("selectedDetails")
|
||||
icon(details) {
|
||||
return iconHTML(details.icon, {class: details.key}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("selectedDetails.key", "i18nPrefix")
|
||||
selectedTitle(key, prefix) {
|
||||
return I18n.t(`${prefix}.${key}.title`);
|
||||
},
|
||||
|
||||
@computed("value")
|
||||
selectedDetails(value) {
|
||||
return buttonDetails(value);
|
||||
},
|
||||
|
||||
@computed("selectedTitle", "showFullTitle")
|
||||
generatedHeadertext(selectedTitle, showFullTitle) {
|
||||
return showFullTitle ? selectedTitle : null;
|
||||
},
|
||||
|
||||
@computed
|
||||
titleForRow: function() {
|
||||
return (rowComponent) => {
|
||||
const notificationLevel = rowComponent.get(`content.${this.get("idKey")}`);
|
||||
const details = buttonDetails(notificationLevel);
|
||||
return I18n.t(`${this.get("i18nPrefix")}.${details.key}.title`);
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow: function() {
|
||||
return (rowComponent) => {
|
||||
const content = rowComponent.get("content");
|
||||
const start = `${this.get("i18nPrefix")}.${content.key}${this.get("i18nPostfix")}`;
|
||||
const title = I18n.t(`${start}.title`);
|
||||
const description = I18n.t(`${start}.description`);
|
||||
|
||||
return `
|
||||
<div class="icons">
|
||||
<span class="selection-indicator"></span>
|
||||
${iconHTML(content.icon, { class: content.key.dasherize() })}
|
||||
</div>
|
||||
<div class="texts">
|
||||
<span class="title">${Handlebars.escapeExpression(title)}</span>
|
||||
<span class="desc">${Handlebars.escapeExpression(description)}</span>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
}
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["pinned-options"],
|
||||
|
||||
@computed("topic.pinned")
|
||||
value(pinned) {
|
||||
return pinned ? "pinned" : "unpinned";
|
||||
},
|
||||
|
||||
@observes("topic.pinned")
|
||||
_pinnedChanged() {
|
||||
this.set("value", this.get("topic.pinned") ? "pinned" : "unpinned");
|
||||
},
|
||||
|
||||
@computed("topic.pinned_globally")
|
||||
content(pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
|
||||
return [
|
||||
{
|
||||
id: "pinned",
|
||||
text: I18n.t("topic_statuses.pinned" + globally + ".title"),
|
||||
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
|
||||
icon: "thumb-tack"
|
||||
},
|
||||
{
|
||||
id: "unpinned",
|
||||
text: I18n.t("topic_statuses.unpinned.title"),
|
||||
icon: "thumb-tack",
|
||||
description: I18n.t('topic_statuses.unpinned.help'),
|
||||
iconClass: "unpinned"
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
@computed("topic.pinned", "topic.pinned_globally")
|
||||
icon(pinned, pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
|
||||
return iconHTML(
|
||||
"thumb-tack",
|
||||
{ class: (state === "unpinned" ? "unpinned" : null) }
|
||||
);
|
||||
},
|
||||
|
||||
@computed("topic.pinned", "topic.pinned_globally")
|
||||
generatedHeadertext(pinned, pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
const title = I18n.t(`topic_statuses.${state}.title`);
|
||||
|
||||
return `${title}${iconHTML("caret-down")}`.htmlSafe();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
this._super(content);
|
||||
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (this.get("value") === "unpinned") {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,485 +0,0 @@
|
|||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "components/select-box",
|
||||
classNames: "select-box",
|
||||
classNameBindings: ["expanded:is-expanded", "hidden:is-hidden"],
|
||||
|
||||
expanded: false,
|
||||
focused: false,
|
||||
filterFocused: false,
|
||||
renderBody: false,
|
||||
wrapper: true,
|
||||
hidden: false,
|
||||
tabindex: 0,
|
||||
scrollableParentSelector: ".modal-body",
|
||||
|
||||
caretUpIcon: "caret-up",
|
||||
caretDownIcon: "caret-down",
|
||||
headerText: I18n.t("select_box.default_header_text"),
|
||||
dynamicHeaderText: true,
|
||||
icon: null,
|
||||
clearable: false,
|
||||
|
||||
value: null,
|
||||
highlightedValue: null,
|
||||
selectedContent: null,
|
||||
noContentLabel: I18n.t("select_box.no_content"),
|
||||
clearSelectionLabel: null,
|
||||
|
||||
idKey: "id",
|
||||
textKey: "text",
|
||||
iconKey: "icon",
|
||||
|
||||
filterable: false,
|
||||
filter: "",
|
||||
filterPlaceholder: I18n.t("select_box.filter_placeholder"),
|
||||
filterIcon: "search",
|
||||
|
||||
selectBoxRowComponent: "select-box/select-box-row",
|
||||
selectBoxFilterComponent: "select-box/select-box-filter",
|
||||
selectBoxHeaderComponent: "select-box/select-box-header",
|
||||
selectBoxCollectionComponent: "select-box/select-box-collection",
|
||||
|
||||
collectionHeight: 200,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
fullWidthOnMobile: false,
|
||||
|
||||
castInteger: false,
|
||||
|
||||
click(event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
filterFunction: function(content) {
|
||||
return (selectBox) => {
|
||||
const filter = selectBox.get("filter").toLowerCase();
|
||||
return _.filter(content, (c) => {
|
||||
return c[selectBox.get("textKey")].toLowerCase().indexOf(filter) > -1;
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@computed("textKey")
|
||||
titleForRow(textKey) {
|
||||
return (rowComponent) => {
|
||||
return rowComponent.get(`content.${textKey}`);
|
||||
};
|
||||
},
|
||||
|
||||
@computed("idKey")
|
||||
idForRow(idKey) {
|
||||
return (rowComponent) => {
|
||||
return rowComponent.get(`content.${idKey}`);
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
shouldHighlightRow: function() {
|
||||
return (rowComponent) => {
|
||||
const id = this._castInteger(rowComponent.get(`content.${this.get("idKey")}`));
|
||||
return id === this.get("highlightedValue");
|
||||
};
|
||||
},
|
||||
|
||||
@computed("value", "idKey")
|
||||
shouldSelectRow(value, idKey) {
|
||||
return (rowComponent) => {
|
||||
const id = this._castInteger(rowComponent.get(`content.${idKey}`));
|
||||
return id === value;
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow: function() {
|
||||
return (rowComponent) => {
|
||||
let template = "";
|
||||
|
||||
const icon = rowComponent.icon();
|
||||
if (icon) {
|
||||
template += icon;
|
||||
}
|
||||
|
||||
const text = rowComponent.get(`content.${this.get("textKey")}`);
|
||||
template += `<p class="text">${Handlebars.escapeExpression(text)}</p>`;
|
||||
|
||||
return template;
|
||||
};
|
||||
},
|
||||
|
||||
applyDirection() {
|
||||
this.$().removeClass("is-above is-below is-left-aligned is-right-aligned");
|
||||
let options = { left: "auto", bottom: "auto", left: "auto", top: "auto" };
|
||||
const headerHeight = this.$(".select-box-header").outerHeight(false);
|
||||
const filterHeight = this.$(".select-box-filter").outerHeight(false);
|
||||
const bodyHeight = this.$(".select-box-body").outerHeight(false);
|
||||
const windowWidth = $(window).width();
|
||||
const windowHeight = $(window).height();
|
||||
const boundingRect = this.$()[0].getBoundingClientRect();
|
||||
const offsetTop = boundingRect.top;
|
||||
|
||||
if (this.get("fullWidthOnMobile") && this.site.isMobileDevice) {
|
||||
const margin = 10;
|
||||
const relativeLeft = this.$().offset().left - $(window).scrollLeft();
|
||||
options.left = margin - relativeLeft;
|
||||
options.width = windowWidth - margin * 2;
|
||||
options.maxWidth = options.minWidth = "unset";
|
||||
} else {
|
||||
const offsetLeft = boundingRect.left;
|
||||
const bodyWidth = this.$(".select-box-body").outerWidth(false);
|
||||
const hasRightSpace = (windowWidth - (this.get("horizontalOffset") + offsetLeft + filterHeight + bodyWidth) > 0);
|
||||
|
||||
if (hasRightSpace) {
|
||||
this.$().addClass("is-left-aligned");
|
||||
options.left = this.get("horizontalOffset");
|
||||
} else {
|
||||
this.$().addClass("is-right-aligned");
|
||||
options.right = this.get("horizontalOffset");
|
||||
}
|
||||
}
|
||||
|
||||
const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight;
|
||||
const hasBelowSpace = windowHeight - offsetTop - componentHeight > 0;
|
||||
if (hasBelowSpace) {
|
||||
this.$().addClass("is-below");
|
||||
options.top = headerHeight + this.get("verticalOffset");
|
||||
} else {
|
||||
this.$().addClass("is-above");
|
||||
options.bottom = headerHeight + this.get("verticalOffset");
|
||||
}
|
||||
|
||||
this.$(".select-box-body").css(options);
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
const content = this.getWithDefault("content", []);
|
||||
this.set("content", content);
|
||||
|
||||
if (this.site.isMobileDevice) {
|
||||
this.set("filterable", false);
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
value: this._castInteger(this.get("value")),
|
||||
componentId: this.elementId
|
||||
});
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_removeDocumentListeners: function() {
|
||||
$(document).off("click.select-box");
|
||||
$(window).off("resize.select-box");
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindEvents: function() {
|
||||
this.$(".select-box-offscreen").off(
|
||||
"focusin.select-box",
|
||||
"focusout.select-box",
|
||||
"keydown.select-box"
|
||||
);
|
||||
this.$(".filter-query").off("focusin.select-box", "focusout.select-box");
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_configureSelectBoxDOM: function() {
|
||||
if (this.get("scrollableParent").length === 1) {
|
||||
this._removeFixedPosition();
|
||||
}
|
||||
|
||||
const computedWidth = this.$().outerWidth(false);
|
||||
const computedHeight = this.$().outerHeight(false);
|
||||
|
||||
this.$(".select-box-filter").css("height", computedHeight);
|
||||
|
||||
if (this.get("expanded")) {
|
||||
if (this.get("scrollableParent").length === 1) {
|
||||
this._applyFixedPosition(computedWidth, computedHeight);
|
||||
}
|
||||
|
||||
this.$(".select-box-collection").css("max-height", this.get("collectionHeight"));
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.applyDirection();
|
||||
if (this.get("wrapper")) {
|
||||
this._positionSelectBoxWrapper();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.get("wrapper")) {
|
||||
this.$(".select-box-wrapper").hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (this.get("expanded")) {
|
||||
if ((keyCode === 13 || keyCode === 9) && Ember.isPresent(this.get("highlightedValue"))) {
|
||||
event.preventDefault();
|
||||
this.send("onSelectRow", this.get("highlightedContent"));
|
||||
}
|
||||
|
||||
if (keyCode === 9) {
|
||||
this.set("expanded", false);
|
||||
}
|
||||
|
||||
if (keyCode === 27) {
|
||||
this.set("expanded", false);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (keyCode === 38) {
|
||||
event.preventDefault();
|
||||
const self = this;
|
||||
Ember.run.throttle(self, this._handleUpArrow, 50);
|
||||
}
|
||||
|
||||
if (keyCode === 40) {
|
||||
event.preventDefault();
|
||||
const self = this;
|
||||
Ember.run.throttle(self, this._handleDownArrow, 50);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_setupDocumentListeners: function() {
|
||||
$(document).off("click.select-box");
|
||||
|
||||
$(document)
|
||||
.on("click.select-box", (event) => {
|
||||
if (this.isDestroying || this.isDestroyed) { return; }
|
||||
|
||||
const $element = this.$();
|
||||
const $target = $(event.target);
|
||||
|
||||
if (!$target.closest($element).length) {
|
||||
this.set("expanded", false);
|
||||
}
|
||||
});
|
||||
|
||||
$(window).on("resize.select-box", () => this.set("expanded", false) );
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_bindEvents: function() {
|
||||
this.$(".select-box-offscreen")
|
||||
.on("focusin.select-box", () => this.set("focused", true) )
|
||||
.on("focusout.select-box", () => this.set("focused", false) );
|
||||
|
||||
this.$(".filter-query")
|
||||
.on("focusin.select-box", () => this.set("filterFocused", true) )
|
||||
.on("focusout.select-box", () => this.set("filterFocused", false) );
|
||||
|
||||
this.$(".select-box-offscreen").on("keydown.select-box", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (keyCode === 13 || keyCode === 40) {
|
||||
this.setProperties({ expanded: true, focused: false });
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (keyCode >= 65 && keyCode <= 90) {
|
||||
this.setProperties({ expanded: true, focused: false });
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$(".filter-query").focus().val(String.fromCharCode(keyCode));
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@observes("expanded")
|
||||
_expandedChanged: function() {
|
||||
if (this.get("expanded")) {
|
||||
this.setProperties({ highlightedValue: null, renderBody: true, focused: false });
|
||||
|
||||
if (this.get("filterable")) {
|
||||
Ember.run.schedule("afterRender", () => this.$(".filter-query").focus());
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@computed("value", "content.[]", "idKey")
|
||||
selectedContent(value, content, idKey) {
|
||||
if (Ember.isNone(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return content.find((c) => {
|
||||
return this._castInteger(Ember.get(c, idKey)) === value;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("highlightedValue", "content.[]", "idKey")
|
||||
highlightedContent(highlightedValue, content, idKey) {
|
||||
if (Ember.isNone(highlightedValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return content.find((c) => {
|
||||
return this._castInteger(Ember.get(c, idKey)) === highlightedValue;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("headerText", "selectedContent", "textKey")
|
||||
selectedTitle(headerText, selectedContent, textKey) {
|
||||
if (Ember.isNone(selectedContent)) {
|
||||
return headerText;
|
||||
}
|
||||
|
||||
return selectedContent[textKey];
|
||||
},
|
||||
|
||||
@computed("headerText", "dynamicHeaderText", "selectedContent", "textKey", "clearSelectionLabel")
|
||||
generatedHeadertext(headerText, dynamic, selectedContent, textKey, clearSelectionLabel) {
|
||||
if (dynamic && !Ember.isNone(selectedContent)) {
|
||||
return selectedContent[textKey];
|
||||
}
|
||||
|
||||
if (dynamic && Ember.isNone(selectedContent) && !Ember.isNone(clearSelectionLabel)) {
|
||||
return I18n.t(clearSelectionLabel);
|
||||
}
|
||||
|
||||
return headerText;
|
||||
},
|
||||
|
||||
@computed("content.[]", "filter", "idKey")
|
||||
filteredContent(content, filter, idKey) {
|
||||
let filteredContent;
|
||||
|
||||
if (Ember.isEmpty(filter)) {
|
||||
filteredContent = content;
|
||||
} else {
|
||||
filteredContent = this.filterFunction(content)(this);
|
||||
|
||||
if (!Ember.isEmpty(filteredContent)) {
|
||||
this.set("highlightedValue", filteredContent[0][idKey]);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredContent;
|
||||
},
|
||||
|
||||
@computed("scrollableParentSelector")
|
||||
scrollableParent(scrollableParentSelector) {
|
||||
return this.$().parents(scrollableParentSelector).first();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onToggle() {
|
||||
this.toggleProperty("expanded");
|
||||
},
|
||||
|
||||
onFilterChange(filter) {
|
||||
this.set("filter", filter);
|
||||
},
|
||||
|
||||
onHoverRow(content) {
|
||||
const id = this._castInteger(Ember.get(content, this.get("idKey")));
|
||||
this.set("highlightedValue", id);
|
||||
},
|
||||
|
||||
onSelectRow(content) {
|
||||
this.setProperties({
|
||||
value: this._castInteger(Ember.get(content, this.get("idKey"))),
|
||||
expanded: false
|
||||
});
|
||||
},
|
||||
|
||||
onClearSelection() {
|
||||
this.setProperties({ value: null, expanded: false });
|
||||
}
|
||||
},
|
||||
|
||||
_positionSelectBoxWrapper() {
|
||||
const headerHeight = this.$(".select-box-header").outerHeight(false);
|
||||
|
||||
this.$(".select-box-wrapper").css({
|
||||
width: this.$().width(),
|
||||
display: "block",
|
||||
height: headerHeight + this.$(".select-box-body").outerHeight(false)
|
||||
});
|
||||
},
|
||||
|
||||
_castInteger(id) {
|
||||
if (this.get("castInteger") === true && Ember.isPresent(id)) {
|
||||
return parseInt(id, 10);
|
||||
}
|
||||
|
||||
return id;
|
||||
},
|
||||
|
||||
_applyFixedPosition(width, height) {
|
||||
const $placeholder = $(`<div class='select-box-fixed-placeholder-${this.get("componentId")}' style='vertical-align: middle; height: ${height}px; width: ${width}px; line-height: ${height}px;display:inline-block'></div>`);
|
||||
|
||||
this.$()
|
||||
.before($placeholder)
|
||||
.css({
|
||||
width,
|
||||
position: "fixed",
|
||||
"margin-top": -this.get("scrollableParent").scrollTop(),
|
||||
"margin-left": -width
|
||||
});
|
||||
|
||||
this.get("scrollableParent").on("scroll.select-box", () => this.set("expanded", false) );
|
||||
},
|
||||
|
||||
_removeFixedPosition() {
|
||||
$(`.select-box-fixed-placeholder-${this.get("componentId")}`).remove();
|
||||
this.$().css({
|
||||
top: "auto",
|
||||
left: "auto",
|
||||
"margin-left": "auto",
|
||||
"margin-top": "auto",
|
||||
position: "relative"
|
||||
});
|
||||
|
||||
this.get("scrollableParent").off("scroll.select-box");
|
||||
},
|
||||
|
||||
_handleDownArrow() {
|
||||
this._handleArrow("down");
|
||||
},
|
||||
|
||||
_handleUpArrow() {
|
||||
this._handleArrow("up");
|
||||
},
|
||||
|
||||
_handleArrow(direction) {
|
||||
const content = this.get("filteredContent");
|
||||
const idKey = this.get("idKey");
|
||||
const selectedContent = content.findBy(idKey, this.get("highlightedValue"));
|
||||
const currentIndex = content.indexOf(selectedContent);
|
||||
|
||||
if (direction === "down") {
|
||||
if (currentIndex < 0) {
|
||||
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
|
||||
} else if(currentIndex + 1 < content.length) {
|
||||
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex + 1], idKey)));
|
||||
}
|
||||
} else {
|
||||
if (currentIndex <= 0) {
|
||||
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
|
||||
} else if(currentIndex - 1 < content.length) {
|
||||
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex - 1], idKey)));
|
||||
}
|
||||
}
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
const $highlightedRow = this.$(".select-box-row.is-highlighted");
|
||||
|
||||
if ($highlightedRow.length === 0) { return; }
|
||||
|
||||
const $collection = this.$(".select-box-collection");
|
||||
const rowOffset = $highlightedRow.offset();
|
||||
const bodyOffset = $collection.offset();
|
||||
$collection.scrollTop(rowOffset.top - bodyOffset.top);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: "select-box-collection",
|
||||
|
||||
actions: {
|
||||
onClearSelection() {
|
||||
this.sendAction("onClearSelection");
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: "select-box-filter",
|
||||
|
||||
classNameBindings: ["focused:is-focused"]
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: "select-box-header",
|
||||
|
||||
classNameBindings: ["focused:is-focused"],
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super();
|
||||
|
||||
this._setCaretIcon();
|
||||
},
|
||||
|
||||
click(event) {
|
||||
this.sendAction("onToggle");
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
_setCaretIcon() {
|
||||
if(this.get("expanded")) {
|
||||
this.set("caretIcon", this.get("caretUpIcon"));
|
||||
} else {
|
||||
this.set("caretIcon", this.get("caretDownIcon"));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "components/select-box/select-box-row",
|
||||
|
||||
classNames: "select-box-row",
|
||||
|
||||
tagName: "li",
|
||||
|
||||
attributeBindings: ["title", "id:data-id"],
|
||||
|
||||
classNameBindings: ["isHighlighted:is-highlighted", "isSelected:is-selected"],
|
||||
|
||||
@computed("titleForRow")
|
||||
title(titleForRow) { return titleForRow(this); },
|
||||
|
||||
@computed("idForRow")
|
||||
id(idForRow) { return idForRow(this); },
|
||||
|
||||
@computed("templateForRow")
|
||||
template(templateForRow) { return templateForRow(this); },
|
||||
|
||||
@computed("shouldHighlightRow", "highlightedValue")
|
||||
isHighlighted(shouldHighlightRow) { return shouldHighlightRow(this); },
|
||||
|
||||
@computed("shouldSelectRow", "value")
|
||||
isSelected(shouldSelectRow) { return shouldSelectRow(this); },
|
||||
|
||||
icon() {
|
||||
if (this.get("content.icon")) {
|
||||
const iconName = this.get("content.icon");
|
||||
const iconClass = this.get("content.iconClass");
|
||||
return iconHTML(iconName, { class: iconClass });
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
mouseEnter() {
|
||||
this.sendAction("onHover", this.get("content"));
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("onSelect", this.get("content"));
|
||||
}
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import NotificationOptionsComponent from "discourse/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: ["tag-notifications-button"],
|
||||
|
||||
i18nPrefix: "tagging.notifications",
|
||||
|
||||
@computed("value")
|
||||
icon() {
|
||||
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
|
||||
},
|
||||
|
||||
generatedHeadertext: null,
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
this._super(content);
|
||||
|
||||
this.sendAction("action", this.get("value"));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,78 +0,0 @@
|
|||
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||
import SelectBoxComponent from "discourse/components/select-box";
|
||||
|
||||
export default SelectBoxComponent.extend({
|
||||
textKey: "name",
|
||||
|
||||
headerText: I18n.t("topic.controls"),
|
||||
|
||||
dynamicHeaderText: false,
|
||||
|
||||
collectionHeight: 300,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this._createContent();
|
||||
},
|
||||
|
||||
_createContent() {
|
||||
const 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') });
|
||||
}
|
||||
|
||||
this.set('content', content);
|
||||
},
|
||||
|
||||
@observes('value')
|
||||
_valueChanged() {
|
||||
this._super();
|
||||
|
||||
const value = this.get('value');
|
||||
const topic = this.get('topic');
|
||||
|
||||
// In case it's not a valid topic
|
||||
if (!topic.get('id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
this._createContent();
|
||||
this.set('value', null);
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
layoutName: "components/topic-notifications-button",
|
||||
|
||||
classNames: ["topic-notifications-button"],
|
||||
|
||||
showFullTitle: true,
|
||||
|
||||
appendReason: true,
|
||||
});
|
|
@ -1,52 +0,0 @@
|
|||
import NotificationOptionsComponent from "discourse/components/notifications-button";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { topicLevels, buttonDetails } from "discourse/lib/notification-levels";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: ["topic-notifications-options"],
|
||||
|
||||
content: topicLevels,
|
||||
|
||||
i18nPrefix: "topic.notifications",
|
||||
|
||||
value: Ember.computed.alias("topic.details.notification_level"),
|
||||
|
||||
@on("didInsertElement")
|
||||
_bindGlobalLevelChanged() {
|
||||
this.appEvents.on("topic-notifications-button:changed", (msg) => {
|
||||
if (msg.type === "notification") {
|
||||
if (this.get("value") !== msg.id) {
|
||||
this.get("topic.details").updateNotifications(msg.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindGlobalLevelChanged() {
|
||||
this.appEvents.off("topic-notifications-button:changed");
|
||||
},
|
||||
|
||||
@computed("value", "showFullTitle")
|
||||
generatedHeadertext(value, showFullTitle) {
|
||||
if (showFullTitle) {
|
||||
const details = buttonDetails(value);
|
||||
return I18n.t(`topic.notifications.${details.key}.title`);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelectRow(content) {
|
||||
const notificationLevelId = Ember.get(content, this.get("idKey"));
|
||||
|
||||
if (notificationLevelId !== this.get("value")) {
|
||||
this.get("topic.details").updateNotifications(notificationLevelId);
|
||||
}
|
||||
|
||||
this._super(content);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -147,7 +147,7 @@ function positioningWorkaround($fixedElement) {
|
|||
|
||||
fixedElement.style.top = '0px';
|
||||
|
||||
composingTopic = $('#reply-control .category-select-box').length > 0;
|
||||
composingTopic = $('#reply-control .category-chooser').length > 0;
|
||||
|
||||
const height = calcHeight(composingTopic);
|
||||
fixedElement.style.height = height + "px";
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div class="control-group">
|
||||
<label class="control-label"></label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="id" value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
|
||||
{{combo-box value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<button
|
||||
class="btn {{if text 'btn-icon-text' 'no-text btn-icon'}}"
|
||||
aria-label="{{selectedTitle}}"
|
||||
type="button"
|
||||
title="{{selectedTitle}}">
|
||||
|
||||
{{{icon}}}
|
||||
|
||||
{{#if text}}
|
||||
<span class="d-button-label">{{{text}}}</span>
|
||||
{{/if}}
|
||||
</button>
|
|
@ -19,8 +19,8 @@
|
|||
{{/each}}
|
||||
{{else}}
|
||||
<label>{{i18n 'category.parent'}}</label>
|
||||
{{category-select-box
|
||||
clearSelectionLabel="category.none"
|
||||
{{category-chooser
|
||||
none="category.none"
|
||||
value=category.parent_category_id
|
||||
categories=parentCategories
|
||||
allowUncategorized=false}}
|
||||
|
|
|
@ -61,11 +61,13 @@
|
|||
<section class="field">
|
||||
<label>
|
||||
{{i18n "category.sort_order"}}
|
||||
{{combo-box valueAttribute="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
|
||||
{{#unless isDefaultSortOrder}}
|
||||
{{combo-box valueAttribute="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
|
||||
{{/unless}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
|
||||
{{#unless isDefaultSortOrder}}
|
||||
{{combo-box valueAttribute="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="field num-featured-topics-fields">
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{else if publishToCategory}}
|
||||
<div class="control-group">
|
||||
<label>{{i18n 'topic.topic_status_update.publish_to'}}</label>
|
||||
{{category-select-box
|
||||
{{category-chooser
|
||||
value=topicTimer.category_id
|
||||
excludeCategoryId=excludeCategoryId}}
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
<div class="future-date-input">
|
||||
<div class="control-group">
|
||||
<label>{{displayLabel}}</label>
|
||||
|
||||
{{future-date-input-selector
|
||||
valueAttribute="id"
|
||||
minimumResultsForSearch=-1
|
||||
statusType=statusType
|
||||
value=selection
|
||||
input=input
|
||||
includeWeekend=includeWeekend
|
||||
includeForever=includeForever
|
||||
width="50%"
|
||||
none="topic.auto_update_input.none"}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<span class="edit-title">
|
||||
{{text-field value=buffered.title maxlength=siteSettings.max_topic_title_length}}
|
||||
</span>
|
||||
{{category-select-box value=buffered.category_id}}
|
||||
{{category-chooser value=buffered.category_id}}
|
||||
{{else}}
|
||||
<span class='post-title'>
|
||||
{{i18n "queue.topic"}}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
<input
|
||||
class="select-box-offscreen"
|
||||
type="text"
|
||||
aria-haspopup="true"
|
||||
role="button"
|
||||
aria-labelledby="select-box-input-{{componentId}}"
|
||||
tabindex={{tabindex}}
|
||||
/>
|
||||
|
||||
{{component selectBoxHeaderComponent
|
||||
text=generatedHeadertext
|
||||
selectedTitle=selectedTitle
|
||||
focused=focused
|
||||
caretUpIcon=caretUpIcon
|
||||
caretDownIcon=caretDownIcon
|
||||
onToggle=(action "onToggle")
|
||||
icon=icon
|
||||
expanded=expanded
|
||||
value=value
|
||||
}}
|
||||
|
||||
<div class="select-box-body">
|
||||
{{#if renderBody}}
|
||||
{{#if filterable}}
|
||||
{{component selectBoxFilterComponent
|
||||
onFilterChange=(action "onFilterChange")
|
||||
icon=filterIcon
|
||||
focused=filterFocused
|
||||
placeholder=filterPlaceholder
|
||||
tabindex=tabindex
|
||||
}}
|
||||
{{/if}}
|
||||
|
||||
{{component selectBoxCollectionComponent
|
||||
clearSelectionLabel=clearSelectionLabel
|
||||
filteredContent=filteredContent
|
||||
selectBoxRowComponent=selectBoxRowComponent
|
||||
templateForRow=templateForRow
|
||||
shouldHighlightRow=shouldHighlightRow
|
||||
shouldSelectRow=shouldSelectRow
|
||||
titleForRow=titleForRow
|
||||
idForRow=idForRow
|
||||
onSelectRow=(action "onSelectRow")
|
||||
onHoverRow=(action "onHoverRow")
|
||||
onClearSelection=(action "onClearSelection")
|
||||
noContentLabel=noContentLabel
|
||||
highlightedValue=highlightedValue
|
||||
value=value
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if wrapper}}
|
||||
<div class="select-box-wrapper"></div>
|
||||
{{/if}}
|
|
@ -1,28 +0,0 @@
|
|||
<ul class="collection">
|
||||
{{#if clearSelectionLabel}}
|
||||
<li {{action "onClearSelection" on="click"}} class="select-box-row clear-selection">
|
||||
{{i18n clearSelectionLabel}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#each filteredContent as |content|}}
|
||||
{{component selectBoxRowComponent
|
||||
content=content
|
||||
templateForRow=templateForRow
|
||||
idForRow=idForRow
|
||||
titleForRow=titleForRow
|
||||
shouldHighlightRow=shouldHighlightRow
|
||||
shouldSelectRow=shouldSelectRow
|
||||
highlightedValue=highlightedValue
|
||||
onSelect=onSelectRow
|
||||
onHover=onHoverRow
|
||||
value=value
|
||||
}}
|
||||
{{else}}
|
||||
{{#if noContentLabel}}
|
||||
<li class="select-box-row no-content">
|
||||
{{noContentLabel}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
|
@ -1,9 +0,0 @@
|
|||
{{#if icon}}
|
||||
{{d-icon icon class="icon"}}
|
||||
{{/if}}
|
||||
|
||||
<span class="current-selection" title={{selectedTitle}}>
|
||||
{{text}}
|
||||
</span>
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
|
@ -1 +0,0 @@
|
|||
{{{template}}}
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
{{#if model.showCategoryChooser}}
|
||||
<div class="category-input">
|
||||
{{category-select-box value=model.categoryId scopedCategoryId=scopedCategoryId tabindex="3"}}
|
||||
{{category-chooser value=model.categoryId scopedCategoryId=scopedCategoryId tabindex="3"}}
|
||||
{{popup-input-tip validation=categoryValidation}}
|
||||
</div>
|
||||
{{#if model.archetype.hasOptions}}
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<span class='desc'>
|
||||
{{i18n "search.sort_by"}}
|
||||
</span>
|
||||
{{combo-box value=sortOrder content=sortOrders castInteger="true"}}
|
||||
{{combo-box value=sortOrder content=sortOrders castInteger=true}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<p>{{i18n "topics.bulk.choose_new_category"}}</p>
|
||||
|
||||
<p>{{category-select-box value=newCategoryId}}</p>
|
||||
<p>{{category-chooser value=newCategoryId}}</p>
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{d-button action="changeCategory" label="topics.bulk.change_category"}}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{{text-field value=topicName placeholderKey="composer.title_placeholder" elementId='split-topic-name'}}
|
||||
|
||||
<label>{{i18n 'categories.category'}}</label>
|
||||
{{category-select-box value=categoryId class="small"}}
|
||||
{{category-chooser value=categoryId class="small"}}
|
||||
</form>
|
||||
{{/d-modal-body}}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div class="control-group">
|
||||
<label class="control-label"></label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="id" value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
|
||||
{{combo-box value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<label class="control-label">{{i18n 'user.email_settings'}}</label>
|
||||
<div class='controls controls-dropdown'>
|
||||
<label>{{i18n 'user.email_previous_replies.title'}}</label>
|
||||
{{select-box idKey="value" textKey="name" content=previousRepliesOptions value=model.user_option.email_previous_replies}}
|
||||
{{combo-box valueAttribute="value" content=previousRepliesOptions value=model.user_option.email_previous_replies}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.email_in_reply_to" checked=model.user_option.email_in_reply_to}}
|
||||
{{preference-checkbox labelKey="user.email_private_messages" checked=model.user_option.email_private_messages}}
|
||||
|
@ -25,7 +25,7 @@
|
|||
{{preference-checkbox labelKey="user.email_digests.title" disabled=model.user_option.mailing_list_mode checked=model.user_option.email_digests}}
|
||||
{{#if model.user_option.email_digests}}
|
||||
<div class='controls controls-dropdown'>
|
||||
{{select-box idKey="value" filterable=true textKey="name" content=digestFrequencies value=model.user_option.digest_after_minutes}}
|
||||
{{combo-box valueAttribute="value" filterable=true content=digestFrequencies value=model.user_option.digest_after_minutes}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.include_tl0_in_digests" disabled=model.user_option.mailing_list_mode checked=model.user_option.include_tl0_in_digests}}
|
||||
{{/if}}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="control-group theme">
|
||||
<label class="control-label">{{i18n 'user.theme'}}</label>
|
||||
<div class="controls">
|
||||
{{select-box textKey="name" content=userSelectableThemes value=themeKey}}
|
||||
{{combo-box content=userSelectableThemes value=themeKey}}
|
||||
</div>
|
||||
<div class="controls">
|
||||
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}}
|
||||
|
@ -24,7 +24,7 @@
|
|||
<div class="control-group pref-locale">
|
||||
<label class="control-label">{{i18n 'user.locale.title'}}</label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="value" content=availableLocales value=model.locale none="user.locale.default"}}
|
||||
{{combo-box filterable=true valueAttribute="value" content=availableLocales value=model.locale none="user.locale.default"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.locale.instructions'}}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
|
||||
{{#if showCategoryChooser}}
|
||||
<br>
|
||||
{{category-select-box class="small" value=buffered.category_id}}
|
||||
{{category-chooser class="small" value=buffered.category_id}}
|
||||
{{/if}}
|
||||
|
||||
{{#if canEditTags}}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import DropdownSelectBoxComponent from "select-box-kit/components/dropdown-select-box";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: "categories-admin-dropdown",
|
||||
actionNames: { create: "createCategory", reorder: "reorderCategories" },
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComponentOptions() {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
shouldDisplaySelectedName: false,
|
||||
icon: `${iconHTML('bars')}${iconHTML('caret-down')}`.htmlSafe(),
|
||||
}));
|
||||
},
|
||||
|
||||
@computed
|
||||
content() {
|
||||
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;
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
|
||||
this.sendAction(`actionNames.${value}`);
|
||||
this.set("value", null);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,131 @@
|
|||
import ComboBoxComponent from "select-box-kit/components/combo-box";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
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({
|
||||
classNames: "category-chooser",
|
||||
filterable: true,
|
||||
castInteger: true,
|
||||
allowUncategorized: null,
|
||||
|
||||
filterFunction(computedContent) {
|
||||
const _matchFunction = (filter, text) => {
|
||||
return text.toLowerCase().indexOf(filter) > -1;
|
||||
};
|
||||
|
||||
return (selectBox) => {
|
||||
const filter = selectBox.get("filter").toLowerCase();
|
||||
return _.filter(computedContent, 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(filter, text) || _matchFunction(filter, categoryName);
|
||||
} else {
|
||||
return _matchFunction(filter, 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";
|
||||
}
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow() {
|
||||
return rowComponent => this._rowContentTemplate(rowComponent.get("content"));
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForNoneRow() {
|
||||
return rowComponent => this._rowContentTemplate(rowComponent.get("content"));
|
||||
},
|
||||
|
||||
@computed("scopedCategoryId", "content.[]")
|
||||
computedContent(scopedCategoryId, categories) {
|
||||
// Always scope to the parent of a category, if present
|
||||
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 = get(c, "value");
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && get(c, "originalContent.parent_category_id") !== scopedCategoryId) {
|
||||
return false;
|
||||
}
|
||||
if (get(c, 'originalContent.isUncategorizedCategory') || excludeCategoryId === categoryId) {
|
||||
return false;
|
||||
}
|
||||
return get(c, 'originalContent.permission') === PermissionType.FULL;
|
||||
});
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_bindComposerResizing() {
|
||||
this.appEvents.on("composer:resized", this, this.applyDirection);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindComposerResizing() {
|
||||
this.appEvents.off("composer:resized");
|
||||
},
|
||||
|
||||
@computed("site.sortedCategories")
|
||||
content() {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
return this.formatContents(categories);
|
||||
},
|
||||
|
||||
_rowContentTemplate(content) {
|
||||
let category;
|
||||
|
||||
// If we have no id, but text with the uncategorized name, we can use that badge.
|
||||
if (isEmpty(get(content, "value"))) {
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get("name") === get(content, "name")) {
|
||||
category = uncat;
|
||||
}
|
||||
} else {
|
||||
category = Category.findById(parseInt(get(content, "value"), 10));
|
||||
}
|
||||
|
||||
if (!category) return get(content, "name");
|
||||
let result = categoryBadgeHTML(category, {link: false, allowUncategorized: true, hideParent: true});
|
||||
const parentCategoryId = category.get("parent_category_id");
|
||||
|
||||
if (parentCategoryId) {
|
||||
result = `<div class="category-status">${categoryBadgeHTML(Category.findById(parentCategoryId), {link: false})} ${result}`;
|
||||
} else {
|
||||
result = `<div class="category-status">${result}`;
|
||||
}
|
||||
|
||||
result += ` <span class="topic-count">× ${category.get("topic_count")}</span></div>`;
|
||||
|
||||
const description = category.get("description");
|
||||
// TODO wtf how can this be null?;
|
||||
if (description && description !== "null") {
|
||||
result += `<div class="category-desc">${description.substr(0, 200)}${description.length > 200 ? '…' : ''}</div>`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import NotificationOptionsComponent from "select-box-kit/components/notifications-button";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: "category-notifications-button",
|
||||
isHidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
|
||||
i18nPrefix: "category.notifications",
|
||||
value: Ember.computed.alias("category.notification_level"),
|
||||
headerComponent: "category-notifications-button/category-notifications-button-header",
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
|
||||
this.get("category").setNotification(value);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
import NotificationButtonHeader from "select-box-kit/components/notifications-button/notifications-button-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default NotificationButtonHeader.extend({
|
||||
classNames: "category-notifications-button-header",
|
||||
shouldDisplaySelectedName: false,
|
||||
|
||||
@computed("_selectedDetails.icon", "_selectedDetails.key")
|
||||
icon() {
|
||||
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import SelectBoxKitComponent from "select-box-kit/components/select-box-kit";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectBoxKitComponent.extend({
|
||||
classNames: "combobox combo-box",
|
||||
autoFilterable: true,
|
||||
headerComponent: "combo-box/combo-box-header",
|
||||
|
||||
caretUpIcon: "caret-up",
|
||||
caretDownIcon: "caret-down",
|
||||
clearable: false,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComponentOptions() {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
caretUpIcon: this.get("caretUpIcon"),
|
||||
caretDownIcon: this.get("caretDownIcon"),
|
||||
clearable: this.get("clearable"),
|
||||
}));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
import SelectBoxKitHeaderComponent from "select-box-kit/components/select-box-kit/select-box-kit-header";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectBoxKitHeaderComponent.extend({
|
||||
layoutName: "select-box-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"),
|
||||
selectedName: Ember.computed.alias("options.selectedName"),
|
||||
|
||||
@computed("isExpanded", "caretUpIcon", "caretDownIcon")
|
||||
caretIcon(isExpanded, caretUpIcon, caretDownIcon) {
|
||||
return isExpanded === true ? caretUpIcon : caretDownIcon;
|
||||
},
|
||||
|
||||
@computed("clearable", "selectedContent")
|
||||
shouldDisplayClearableButton(clearable, selectedContent) {
|
||||
return clearable === true && !Ember.isEmpty(selectedContent);
|
||||
},
|
||||
|
||||
@computed("options.selectedName", "selectedContent.firstObject.name", "none.name")
|
||||
selectedName(selectedName, name, noneName) {
|
||||
if (Ember.isPresent(selectedName)) {
|
||||
return selectedName;
|
||||
}
|
||||
|
||||
if (Ember.isNone(name)) {
|
||||
if (Ember.isNone(noneName)) {
|
||||
return this._super();
|
||||
} else {
|
||||
return noneName;
|
||||
}
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import SelectBoxKitComponent from "select-box-kit/components/select-box-kit";
|
||||
|
||||
export default SelectBoxKitComponent.extend({
|
||||
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",
|
||||
|
||||
clickOutside() {
|
||||
this.close();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
this.set("value", value);
|
||||
|
||||
this.blur();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import SelectBoxKitHeaderComponent from "select-box-kit/components/select-box-kit/select-box-kit-header";
|
||||
|
||||
export default SelectBoxKitHeaderComponent.extend({
|
||||
layoutName: "select-box-kit/templates/components/dropdown-select-box/dropdown-select-box-header",
|
||||
classNames: "dropdown-select-box-header",
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import SelectBoxKitRowComponent from "select-box-kit/components/select-box-kit/select-box-kit-row";
|
||||
|
||||
export default SelectBoxKitRowComponent.extend({
|
||||
layoutName: "select-box-kit/templates/components/dropdown-select-box/dropdown-select-box-row",
|
||||
classNames: "dropdown-select-box-row",
|
||||
|
||||
name: Ember.computed.alias("content.name"),
|
||||
description: Ember.computed.alias("content.originalContent.description")
|
||||
});
|
|
@ -0,0 +1,118 @@
|
|||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import ComboBoxComponent from "select-box-kit/components/combo-box";
|
||||
import { CLOSE_STATUS_TYPE } from "discourse/controllers/edit-topic-timer";
|
||||
import DatetimeMixin from "select-box-kit/components/future-date-input-selector/mixin";
|
||||
|
||||
export const LATER_TODAY = "later_today";
|
||||
export const TOMORROW = "tomorrow";
|
||||
export const LATER_THIS_WEEK = "later_this_week";
|
||||
export const THIS_WEEKEND = "this_weekend";
|
||||
export const NEXT_WEEK = "next_week";
|
||||
export const TWO_WEEKS = "two_weeks";
|
||||
export const NEXT_MONTH = "next_month";
|
||||
export const FOREVER = "forever";
|
||||
|
||||
export const PICK_DATE_AND_TIME = "pick_date_and_time";
|
||||
export const SET_BASED_ON_LAST_POST = "set_based_on_last_post";
|
||||
|
||||
export const FORMAT = "YYYY-MM-DD HH:mm";
|
||||
|
||||
export default ComboBoxComponent.extend(DatetimeMixin, {
|
||||
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",
|
||||
|
||||
@computed
|
||||
content() {
|
||||
const selections = [];
|
||||
const now = moment();
|
||||
const canScheduleToday = (24 - now.hour()) > 6;
|
||||
const day = now.day();
|
||||
|
||||
if (canScheduleToday) {
|
||||
selections.push({
|
||||
id: LATER_TODAY,
|
||||
name: I18n.t("topic.auto_update_input.later_today")
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: TOMORROW,
|
||||
name: I18n.t("topic.auto_update_input.tomorrow")
|
||||
});
|
||||
|
||||
if (!canScheduleToday && day < 4) {
|
||||
selections.push({
|
||||
id: LATER_THIS_WEEK,
|
||||
name: I18n.t("topic.auto_update_input.later_this_week")
|
||||
});
|
||||
}
|
||||
|
||||
if (day < 5 && this.get("includeWeekend")) {
|
||||
selections.push({
|
||||
id: THIS_WEEKEND,
|
||||
name: I18n.t("topic.auto_update_input.this_weekend")
|
||||
});
|
||||
}
|
||||
|
||||
if (day !== 7) {
|
||||
selections.push({
|
||||
id: NEXT_WEEK,
|
||||
name: I18n.t("topic.auto_update_input.next_week")
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: TWO_WEEKS,
|
||||
name: I18n.t("topic.auto_update_input.two_weeks")
|
||||
});
|
||||
|
||||
if (moment().endOf("month").date() !== now.date()) {
|
||||
selections.push({
|
||||
id: NEXT_MONTH,
|
||||
name: I18n.t("topic.auto_update_input.next_month")
|
||||
});
|
||||
}
|
||||
|
||||
if (this.get("includeForever")) {
|
||||
selections.push({
|
||||
id: FOREVER,
|
||||
name: I18n.t("topic.auto_update_input.forever")
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: PICK_DATE_AND_TIME,
|
||||
name: I18n.t("topic.auto_update_input.pick_date_and_time")
|
||||
});
|
||||
|
||||
if (this.get("statusType") === CLOSE_STATUS_TYPE) {
|
||||
selections.push({
|
||||
id: SET_BASED_ON_LAST_POST,
|
||||
name: I18n.t("topic.auto_update_input.set_based_on_last_post")
|
||||
});
|
||||
}
|
||||
|
||||
return selections;
|
||||
},
|
||||
|
||||
@observes("value")
|
||||
_updateInput() {
|
||||
if (this.get("isCustom")) return;
|
||||
let input = null;
|
||||
const { time } = this.get("updateAt");
|
||||
|
||||
if (time && !Ember.isEmpty(this.get("value"))) {
|
||||
input = time.format(FORMAT);
|
||||
}
|
||||
|
||||
this.set("input", input);
|
||||
},
|
||||
|
||||
@computed("value")
|
||||
updateAt(value) {
|
||||
return this._updateAt(value);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import ComboBoxHeaderComponent from "select-box-kit/components/combo-box/combo-box-header";
|
||||
import DatetimeMixin from "select-box-kit/components/future-date-input-selector/mixin";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default ComboBoxHeaderComponent.extend(DatetimeMixin, {
|
||||
layoutName: "select-box-kit/templates/components/future-date-input-selector/future-date-input-selector-header",
|
||||
classNames: "future-date-input-selector-header",
|
||||
|
||||
@computed("selectedContent.firstObject.value")
|
||||
datetime(value) { return this._computeDatetimeForValue(value); },
|
||||
|
||||
@computed("selectedContent.firstObject.value")
|
||||
icon(value) { return this._computeIconForValue(value); }
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import SelectBoxKitRowComponent from "select-box-kit/components/select-box-kit/select-box-kit-row";
|
||||
import DatetimeMixin from "select-box-kit/components/future-date-input-selector/mixin";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectBoxKitRowComponent.extend(DatetimeMixin, {
|
||||
layoutName: "select-box-kit/templates/components/future-date-input-selector/future-date-input-selector-row",
|
||||
classNames: "future-date-input-selector-row",
|
||||
|
||||
@computed("content.value")
|
||||
datetime(value) { return this._computeDatetimeForValue(value); },
|
||||
|
||||
@computed("content.value")
|
||||
icon(value) { return this._computeIconForValue(value); }
|
||||
});
|
|
@ -0,0 +1,101 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
import {
|
||||
LATER_TODAY,
|
||||
TOMORROW,
|
||||
LATER_THIS_WEEK,
|
||||
THIS_WEEKEND,
|
||||
NEXT_WEEK,
|
||||
TWO_WEEKS,
|
||||
NEXT_MONTH,
|
||||
FOREVER,
|
||||
PICK_DATE_AND_TIME,
|
||||
SET_BASED_ON_LAST_POST,
|
||||
} from "select-box-kit/components/future-date-input-selector";
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
_computeIconForValue(value) {
|
||||
let {icon} = this._updateAt(value);
|
||||
|
||||
if (icon) {
|
||||
return icon.split(",").map(i => iconHTML(i)).join(" ");
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_computeDatetimeForValue(value) {
|
||||
if (Ember.isNone(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let {time} = this._updateAt(value);
|
||||
|
||||
if (time) {
|
||||
if (value === LATER_TODAY) {
|
||||
time = time.format("h a");
|
||||
} else if (value === NEXT_MONTH || value === TWO_WEEKS) {
|
||||
time = time.format("MMM D");
|
||||
} else {
|
||||
time = time.format("ddd, h a");
|
||||
}
|
||||
}
|
||||
|
||||
if (time && value !== FOREVER) {
|
||||
return time;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_updateAt(selection) {
|
||||
let time = moment();
|
||||
let icon;
|
||||
const timeOfDay = this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18;
|
||||
|
||||
switch(selection) {
|
||||
case LATER_TODAY:
|
||||
time = time.hour(18).minute(0);
|
||||
icon = 'moon-o';
|
||||
break;
|
||||
case TOMORROW:
|
||||
time = time.add(1, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'sun-o';
|
||||
break;
|
||||
case LATER_THIS_WEEK:
|
||||
time = time.add(2, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case THIS_WEEKEND:
|
||||
time = time.day(6).hour(timeOfDay).minute(0);
|
||||
icon = 'bed';
|
||||
break;
|
||||
case NEXT_WEEK:
|
||||
time = time.add(1, 'week').day(1).hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case TWO_WEEKS:
|
||||
time = time.add(2, 'week').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case NEXT_MONTH:
|
||||
time = time.add(1, 'month').startOf('month').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case FOREVER:
|
||||
time = time.add(1000, 'year').hour(timeOfDay).minute(0);
|
||||
icon = 'gavel';
|
||||
break;
|
||||
case PICK_DATE_AND_TIME:
|
||||
time = null;
|
||||
icon = 'calendar-plus-o';
|
||||
break;
|
||||
case SET_BASED_ON_LAST_POST:
|
||||
time = null;
|
||||
icon = 'clock-o';
|
||||
break;
|
||||
}
|
||||
|
||||
return { time, icon };
|
||||
},
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
// Experimental
|
||||
import SelectBoxKitComponent from "select-box-kit/components/select-box-kit";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone } = Ember;
|
||||
|
||||
export default SelectBoxKitComponent.extend({
|
||||
classNames: "multi-combobox",
|
||||
headerComponent: "multi-combo-box/multi-combo-box-header",
|
||||
filterComponent: null,
|
||||
headerText: "select_box.default_header_text",
|
||||
value: [],
|
||||
allowAny: true,
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return `Create: ${rowComponent.get("content.name")}`;
|
||||
};
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
const $filterInput = this.$filterInput();
|
||||
|
||||
if (keyCode === 8) {
|
||||
let $lastSelectedValue = $(this.$(".choices .selected-name").last());
|
||||
|
||||
if ($lastSelectedValue.is(":focus") || $(document.activeElement).is($lastSelectedValue)) {
|
||||
this.send("onDeselect", $lastSelectedValue.data("value"));
|
||||
$filterInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($filterInput.val() === "") {
|
||||
if ($filterInput.is(":focus")) {
|
||||
if ($lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.focus();
|
||||
}
|
||||
} else {
|
||||
if ($lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.focus();
|
||||
} else {
|
||||
$filterInput.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filterInput.focus();
|
||||
this._super(event);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("none")
|
||||
computedNone(none) {
|
||||
if (!isNone(none)) {
|
||||
this.set("none", { name: I18n.t(none), value: "" });
|
||||
}
|
||||
},
|
||||
|
||||
@computed("value.[]")
|
||||
computedValue(value) {
|
||||
return value.map(v => this._castInteger(v));
|
||||
},
|
||||
|
||||
@computed("computedValue.[]", "computedContent.[]")
|
||||
selectedContent(computedValue, computedContent) {
|
||||
const contents = [];
|
||||
computedValue.forEach(cv => {
|
||||
contents.push(computedContent.findBy("value", cv));
|
||||
});
|
||||
return contents;
|
||||
},
|
||||
|
||||
filterFunction(content) {
|
||||
return (selectBox, computedValue) => {
|
||||
const filter = selectBox.get("filter").toLowerCase();
|
||||
return _.filter(content, c => {
|
||||
return !computedValue.includes(get(c, "value")) &&
|
||||
get(c, "name").toLowerCase().indexOf(filter) > -1;
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClearSelection() {
|
||||
this.send("onSelect", []);
|
||||
},
|
||||
|
||||
onSelect(value) {
|
||||
this.setProperties({ filter: "", highlightedValue: null });
|
||||
this.get("value").pushObject(value);
|
||||
},
|
||||
|
||||
onDeselect(value) {
|
||||
this.defaultOnDeselect(value);
|
||||
this.get("value").removeObject(value);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import SelectBoxKitHeaderComponent from "select-box-kit/components/select-box-kit/select-box-kit-header";
|
||||
|
||||
export default SelectBoxKitHeaderComponent.extend({
|
||||
attributeBindings: ["names:data-name"],
|
||||
classNames: "multi-combobox-header",
|
||||
layoutName: "select-box-kit/templates/components/multi-combo-box/multi-combo-box-header",
|
||||
|
||||
@computed("filter", "selectedContent.[]", "isFocused", "selectBoxIsExpanded")
|
||||
shouldDisplayFilterPlaceholder(filter, selectedContent, isFocused) {
|
||||
if (Ember.isEmpty(selectedContent)) {
|
||||
if (filter.length > 0) { return false; }
|
||||
if (isFocused === true) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_positionFilter() {
|
||||
this.$(".filter").width(0);
|
||||
|
||||
const leftHeaderOffset = this.$().offset().left;
|
||||
const leftFilterOffset = this.$(".filter").offset().left;
|
||||
const offset = leftFilterOffset - leftHeaderOffset;
|
||||
const width = this.$().outerWidth(false);
|
||||
const availableSpace = width - offset;
|
||||
|
||||
// TODO: avoid magic number 8
|
||||
// TODO: make sure the filter doesn’t end up being very small
|
||||
this.$(".filter").width(availableSpace - 8);
|
||||
},
|
||||
|
||||
@computed("selectedContent.[]")
|
||||
names(selectedContent) {
|
||||
return selectedContent.map(sc => sc.name).join(",");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import DropdownSelectBoxComponent from "select-box-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",
|
||||
value: Ember.computed.alias("notificationLevel"),
|
||||
castInteger: true,
|
||||
autofilterable: false,
|
||||
filterable: false,
|
||||
rowComponent: "notifications-button/notifications-button-row",
|
||||
headerComponent: "notifications-button/notifications-button-header",
|
||||
|
||||
i18nPrefix: "",
|
||||
i18nPostfix: "",
|
||||
showFullTitle: true,
|
||||
|
||||
@on("didReceiveAttrs", "didUpdateAttrs")
|
||||
_setComponentOptions() {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
i18nPrefix: this.get("i18nPrefix"),
|
||||
showFullTitle: this.get("showFullTitle"),
|
||||
}));
|
||||
|
||||
this.set("rowComponentOptions", Ember.Object.create({
|
||||
i18nPrefix: this.get("i18nPrefix"),
|
||||
i18nPostfix: this.get("i18nPostfix")
|
||||
}));
|
||||
},
|
||||
|
||||
@computed("computedValue")
|
||||
selectedDetails(computedValue) {
|
||||
return buttonDetails(computedValue);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import DropdownSelectBoxHeaderComponent from "select-box-kit/components/dropdown-select-box/dropdown-select-box-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { buttonDetails } from "discourse/lib/notification-levels";
|
||||
|
||||
export default DropdownSelectBoxHeaderComponent.extend({
|
||||
classNames: "notifications-button-header",
|
||||
|
||||
i18nPrefix: Ember.computed.alias("options.i18nPrefix"),
|
||||
shouldDisplaySelectedName: Ember.computed.alias("options.showFullTitle"),
|
||||
|
||||
@computed("_selectedDetails.icon", "_selectedDetails.key")
|
||||
icon(icon, key) {
|
||||
return iconHTML(icon, {class: key}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("_selectedDetails.key", "i18nPrefix")
|
||||
selectedName(key, prefix) {
|
||||
return I18n.t(`${prefix}.${key}.title`);
|
||||
},
|
||||
|
||||
@computed("selectedContent.firstObject.value")
|
||||
_selectedDetails(value) { return buttonDetails(value); }
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
import DropdownSelectBoxRoxComponent from "select-box-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("content.value", "i18nPrefix")
|
||||
title(value, prefix) {
|
||||
const key = buttonDetails(value).key;
|
||||
return I18n.t(`${prefix}.${key}.title`);
|
||||
},
|
||||
|
||||
@computed("content.name", "content.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", "content.name")
|
||||
_start(prefix, postfix, contentName) {
|
||||
return `${prefix}.${contentName}${postfix}`;
|
||||
},
|
||||
});
|
|
@ -2,10 +2,9 @@ import computed from "ember-addons/ember-computed-decorators";
|
|||
|
||||
export default Ember.Component.extend({
|
||||
descriptionKey: "help",
|
||||
|
||||
classNames: ["pinned-button"],
|
||||
|
||||
classNameBindings: ["hidden:is-hidden"],
|
||||
classNames: "pinned-button",
|
||||
classNameBindings: ["isHidden"],
|
||||
layoutName: "select-box-kit/templates/components/pinned-button",
|
||||
|
||||
@computed("topic.pinned_globally", "topic.pinned")
|
||||
reasonText(pinnedGlobally, pinned) {
|
||||
|
@ -16,7 +15,7 @@ export default Ember.Component.extend({
|
|||
},
|
||||
|
||||
@computed("topic.pinned", "topic.deleted", "topic.unpinned")
|
||||
hidden(pinned, deleted, unpinned) {
|
||||
isHidden(pinned, deleted, unpinned) {
|
||||
return deleted || (!pinned && !unpinned);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
import DropdownSelectBoxComponent from "select-box-kit/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: "pinned-options",
|
||||
|
||||
headerComponent: "pinned-options/pinned-options-header",
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComponentOptions() {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
pinned: this.get("topic.pinned"),
|
||||
pinnedGlobally: this.get("topic.pinned_globally")
|
||||
}));
|
||||
},
|
||||
|
||||
@computed("topic.pinned")
|
||||
value(pinned) {
|
||||
return pinned ? "pinned" : "unpinned";
|
||||
},
|
||||
|
||||
@observes("topic.pinned")
|
||||
_pinStateChanged() {
|
||||
this.set("value", this.get("topic.pinned") ? "pinned" : "unpinned");
|
||||
this._setComponentOptions();
|
||||
},
|
||||
|
||||
@computed("topic.pinned_globally")
|
||||
content(pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
|
||||
return [
|
||||
{
|
||||
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",
|
||||
description: I18n.t('topic_statuses.unpinned.help'),
|
||||
iconClass: "unpinned"
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (value === "unpinned") {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import DropdownSelectBoxHeaderComponent from "select-box-kit/components/dropdown-select-box/dropdown-select-box-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default DropdownSelectBoxHeaderComponent.extend({
|
||||
classNames: "pinned-options-header",
|
||||
|
||||
pinnedGlobally: Ember.computed.alias("options.pinnedGlobally"),
|
||||
pinned: Ember.computed.alias("options.pinned"),
|
||||
|
||||
@computed("pinned", "pinnedGlobally")
|
||||
icon(pinned, pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
|
||||
return iconHTML(
|
||||
"thumb-tack",
|
||||
{ class: (state === "unpinned" ? "unpinned" : null) }
|
||||
);
|
||||
},
|
||||
|
||||
@computed("pinned", "pinnedGlobally")
|
||||
selectedName(pinned, pinnedGlobally) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
const title = I18n.t(`topic_statuses.${state}.title`);
|
||||
|
||||
return `${title}${iconHTML("caret-down")}`.htmlSafe();
|
||||
},
|
||||
});
|
|
@ -0,0 +1,440 @@
|
|||
const { get, isNone, isEmpty, isPresent } = Ember;
|
||||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import UtilsMixin from "select-box-kit/mixins/utils";
|
||||
import DomHelpersMixin from "select-box-kit/mixins/dom-helpers";
|
||||
import KeyboardMixin from "select-box-kit/mixins/keyboard";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, DomHelpersMixin, KeyboardMixin, {
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit",
|
||||
classNames: "select-box-kit",
|
||||
classNameBindings: [
|
||||
"isFocused",
|
||||
"isExpanded",
|
||||
"isDisabled",
|
||||
"isHidden",
|
||||
"isAbove",
|
||||
"isBelow",
|
||||
"isLeftAligned",
|
||||
"isRightAligned"
|
||||
],
|
||||
isDisabled: false,
|
||||
isExpanded: false,
|
||||
isFocused: false,
|
||||
isHidden: false,
|
||||
renderBody: false,
|
||||
tabindex: 0,
|
||||
scrollableParentSelector: ".modal-body",
|
||||
value: null,
|
||||
none: null,
|
||||
highlightedValue: null,
|
||||
noContentLabel: "select_box.no_content",
|
||||
valueAttribute: "id",
|
||||
nameProperty: "name",
|
||||
autoFilterable: false,
|
||||
filterable: false,
|
||||
filter: "",
|
||||
filterPlaceholder: "select_box.filter_placeholder",
|
||||
filterIcon: "search",
|
||||
rowComponent: "select-box-kit/select-box-kit-row",
|
||||
noneRowComponent: "select-box-kit/select-box-kit-none-row",
|
||||
createRowComponent: "select-box-kit/select-box-kit-create-row",
|
||||
filterComponent: "select-box-kit/select-box-kit-filter",
|
||||
headerComponent: "select-box-kit/select-box-kit-header",
|
||||
collectionComponent: "select-box-kit/select-box-kit-collection",
|
||||
collectionHeight: 200,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
fullWidthOnMobile: false,
|
||||
castInteger: false,
|
||||
allowAny: false,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if ($(window).outerWidth(false) <= 420) {
|
||||
this.setProperties({ filterable: false, autoFilterable: false });
|
||||
}
|
||||
|
||||
this._previousScrollParentOverflow = "auto";
|
||||
},
|
||||
|
||||
close() {
|
||||
this.setProperties({ isExpanded: false, isFocused: false });
|
||||
},
|
||||
|
||||
focus() {
|
||||
Ember.run.schedule("afterRender", () => this.$offscreenInput().select() );
|
||||
},
|
||||
|
||||
blur() {
|
||||
Ember.run.schedule("afterRender", () => this.$offscreenInput().blur() );
|
||||
},
|
||||
|
||||
clickOutside(event) {
|
||||
if ($(event.target).parents(".select-box-kit").length === 1) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.get("isExpanded") === true) {
|
||||
this.set("isExpanded", false);
|
||||
this.focus();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
|
||||
createFunction(input) {
|
||||
return (selectedBox) => {
|
||||
const formatedContent = selectedBox.formatContent(input);
|
||||
formatedContent.meta.generated = true;
|
||||
return formatedContent;
|
||||
};
|
||||
},
|
||||
|
||||
filterFunction(content) {
|
||||
return selectBox => {
|
||||
const filter = selectBox.get("filter").toLowerCase();
|
||||
return _.filter(content, c => {
|
||||
return get(c, "name").toLowerCase().indexOf(filter) > -1;
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
nameForContent(content) {
|
||||
if (isNone(content)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof content === "object") {
|
||||
return get(content, this.get("nameProperty"));
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
valueForContent(content) {
|
||||
switch (typeof content) {
|
||||
case "string":
|
||||
return this._castInteger(content);
|
||||
default:
|
||||
return this._castInteger(get(content, this.get("valueAttribute")));
|
||||
}
|
||||
},
|
||||
|
||||
formatContent(content) {
|
||||
return {
|
||||
value: this.valueForContent(content),
|
||||
name: this.nameForContent(content),
|
||||
originalContent: content,
|
||||
meta: { generated: false }
|
||||
};
|
||||
},
|
||||
|
||||
formatContents(contents) {
|
||||
return contents.map(content => this.formatContent(content));
|
||||
},
|
||||
|
||||
@computed("filter", "filterable", "autoFilterable")
|
||||
computedFilterable(filter, filterable, autoFilterable) {
|
||||
if (filterable === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filter.length > 0 && autoFilterable === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("computedFilterable", "filter", "allowAny")
|
||||
shouldDisplayCreateRow(computedFilterable, filter, allow) {
|
||||
return computedFilterable === true && filter.length > 0 && allow === true;
|
||||
},
|
||||
|
||||
@computed("filter", "allowAny")
|
||||
createRowContent(filter, allow) {
|
||||
if (allow === true) {
|
||||
return Ember.Object.create({ value: filter, name: filter });
|
||||
}
|
||||
},
|
||||
|
||||
@computed("content.[]")
|
||||
computedContent(content) {
|
||||
return this.formatContents(content || []);
|
||||
},
|
||||
|
||||
@computed("value", "none", "computedContent.firstObject.value")
|
||||
computedValue(value, none, firstContentValue) {
|
||||
if (isNone(value) && isNone(none)) {
|
||||
return this._castInteger(firstContentValue);
|
||||
}
|
||||
|
||||
return this._castInteger(value);
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow() { return () => null; },
|
||||
|
||||
@computed
|
||||
templateForNoneRow() { return () => null; },
|
||||
|
||||
@computed
|
||||
templateForCreateRow() { return () => null; },
|
||||
|
||||
@computed("none")
|
||||
computedNone(none) {
|
||||
if (isNone(none)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (typeof none) {
|
||||
case "string":
|
||||
return Ember.Object.create({ name: I18n.t(none), value: "" });
|
||||
default:
|
||||
return this.formatContent(none);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("computedValue", "computedContent.[]")
|
||||
selectedContent(computedValue, computedContent) {
|
||||
if (isNone(computedValue)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [ computedContent.findBy("value", this._castInteger(computedValue)) ];
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_configureSelectBoxDOM() {
|
||||
if (this.get("isExpanded") === true) {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$collection().css("max-height", this.get("collectionHeight"));
|
||||
this._applyDirection();
|
||||
this._positionWrapper();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_cleanHandlers() {
|
||||
$(window).off(`resize.${this.elementId}`);
|
||||
this._removeFixedPosition();
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_setupResizeListener() {
|
||||
$(window).on(`resize.${this.elementId}`, () => this.set("isExpanded", false) );
|
||||
},
|
||||
|
||||
@observes("filter", "filteredContent.[]", "shouldDisplayCreateRow")
|
||||
_setHighlightedValue() {
|
||||
const filteredContent = this.get("filteredContent");
|
||||
const display = this.get("shouldDisplayCreateRow");
|
||||
const none = this.get("computedNone");
|
||||
|
||||
if (isNone(this.get("highlightedValue")) && !isEmpty(filteredContent)) {
|
||||
this.set("highlightedValue", get(filteredContent, "firstObject.value"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (display === true && isEmpty(filteredContent)) {
|
||||
this.set("highlightedValue", this.get("filter"));
|
||||
}
|
||||
else if (!isEmpty(filteredContent)) {
|
||||
this.set("highlightedValue", get(filteredContent, "firstObject.value"));
|
||||
}
|
||||
else if (isEmpty(filteredContent) && isPresent(none) && display === false) {
|
||||
this.set("highlightedValue", get(none, "value"));
|
||||
}
|
||||
},
|
||||
|
||||
@observes("isExpanded")
|
||||
_isExpandedChanged() {
|
||||
if (this.get("isExpanded") === true) {
|
||||
this._applyFixedPosition();
|
||||
|
||||
this.setProperties({
|
||||
highlightedValue: this.get("computedValue"),
|
||||
renderBody: true,
|
||||
isFocused: true
|
||||
});
|
||||
} else {
|
||||
this._removeFixedPosition();
|
||||
}
|
||||
},
|
||||
|
||||
@computed("filter", "computedFilterable", "computedContent.[]", "computedValue.[]")
|
||||
filteredContent(filter, computedFilterable, computedContent, computedValue) {
|
||||
if (computedFilterable === false) {
|
||||
return computedContent;
|
||||
}
|
||||
|
||||
return this.filterFunction(computedContent)(this, computedValue);
|
||||
},
|
||||
|
||||
@computed("scrollableParentSelector")
|
||||
scrollableParent(scrollableParentSelector) {
|
||||
return this.$().parents(scrollableParentSelector).first();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onToggle() {
|
||||
this.toggleProperty("isExpanded");
|
||||
|
||||
if (this.get("isExpanded") === true) { this.focus(); }
|
||||
},
|
||||
|
||||
onCreateContent(input) {
|
||||
const content = this.createFunction(input)(this);
|
||||
this.get("computedContent").pushObject(content);
|
||||
this.send("onSelect", content.value);
|
||||
},
|
||||
|
||||
onFilterChange(filter) {
|
||||
this.set("filter", filter);
|
||||
},
|
||||
|
||||
onHighlight(value) {
|
||||
this.set("highlightedValue", value);
|
||||
},
|
||||
|
||||
onClearSelection() {
|
||||
this.send("onSelect", null);
|
||||
},
|
||||
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
this.set("value", value);
|
||||
},
|
||||
|
||||
onDeselect() {
|
||||
this.defaultOnDeselect();
|
||||
this.set("value", null);
|
||||
}
|
||||
},
|
||||
|
||||
defaultOnSelect(value) {
|
||||
if (value === "") { value = null; }
|
||||
|
||||
this.setProperties({
|
||||
highlightedValue: null,
|
||||
isExpanded: false,
|
||||
filter: ""
|
||||
});
|
||||
|
||||
this.focus();
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
defaultOnDeselect(value) {
|
||||
const content = this.get("computedContent").findBy("value", value);
|
||||
if (!isNone(content) && get(content, "meta.generated") === true) {
|
||||
this.get("computedContent").removeObject(content);
|
||||
}
|
||||
},
|
||||
|
||||
_applyDirection() {
|
||||
let options = { left: "auto", bottom: "auto", top: "auto" };
|
||||
const headerHeight = this.$header().outerHeight(false);
|
||||
const filterHeight = this.$(".select-box-kit-filter").outerHeight(false);
|
||||
const bodyHeight = this.$body().outerHeight(false);
|
||||
const windowWidth = $(window).width();
|
||||
const windowHeight = $(window).height();
|
||||
const boundingRect = this.$()[0].getBoundingClientRect();
|
||||
const offsetTop = boundingRect.top;
|
||||
|
||||
if (this.get("fullWidthOnMobile") && windowWidth <= 420) {
|
||||
const margin = 10;
|
||||
const relativeLeft = this.$().offset().left - $(window).scrollLeft();
|
||||
options.left = margin - relativeLeft;
|
||||
options.width = windowWidth - margin * 2;
|
||||
options.maxWidth = options.minWidth = "unset";
|
||||
} else {
|
||||
const offsetLeft = boundingRect.left;
|
||||
const bodyWidth = this.$body().outerWidth(false);
|
||||
const hasRightSpace = (windowWidth - (this.get("horizontalOffset") + offsetLeft + filterHeight + bodyWidth) > 0);
|
||||
|
||||
if (hasRightSpace) {
|
||||
this.setProperties({ isLeftAligned: true, isRightAligned: false });
|
||||
options.left = this.get("horizontalOffset");
|
||||
} else {
|
||||
this.setProperties({ isLeftAligned: false, isRightAligned: true });
|
||||
options.right = this.get("horizontalOffset");
|
||||
}
|
||||
}
|
||||
|
||||
const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight;
|
||||
const hasBelowSpace = windowHeight - offsetTop - componentHeight > 0;
|
||||
if (hasBelowSpace) {
|
||||
this.setProperties({ isBelow: true, isAbove: false });
|
||||
options.top = headerHeight + this.get("verticalOffset");
|
||||
} else {
|
||||
this.setProperties({ isBelow: false, isAbove: true });
|
||||
options.bottom = headerHeight + this.get("verticalOffset");
|
||||
}
|
||||
|
||||
this.$body().css(options);
|
||||
},
|
||||
|
||||
_applyFixedPosition() {
|
||||
const width = this.$().outerWidth(false);
|
||||
const height = this.$header().outerHeight(false);
|
||||
|
||||
if (this.get("scrollableParent").length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $placeholder = $(`<div class='select-box-kit-fixed-placeholder-${this.elementId}'></div>`);
|
||||
|
||||
this._previousScrollParentOverflow = this.get("scrollableParent").css("overflow");
|
||||
this.get("scrollableParent").css({ overflow: "hidden" });
|
||||
|
||||
this.$()
|
||||
.before($placeholder.css({
|
||||
display: "inline-block",
|
||||
width,
|
||||
height,
|
||||
"vertical-align": "middle"
|
||||
}))
|
||||
.css({
|
||||
width,
|
||||
direction: $("html").css("direction"),
|
||||
position: "fixed",
|
||||
"margin-top": -this.get("scrollableParent").scrollTop(),
|
||||
"margin-left": -width
|
||||
});
|
||||
},
|
||||
|
||||
_removeFixedPosition() {
|
||||
if (this.get("scrollableParent").length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(`.select-box-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
|
||||
this.$().css({
|
||||
top: "auto",
|
||||
left: "auto",
|
||||
"margin-left": "auto",
|
||||
"margin-top": "auto",
|
||||
position: "relative"
|
||||
});
|
||||
|
||||
this.get("scrollableParent").css({
|
||||
overflow: this._previousScrollParentOverflow
|
||||
});
|
||||
},
|
||||
|
||||
_positionWrapper() {
|
||||
const headerHeight = this.$header().outerHeight(false);
|
||||
|
||||
this.$(".select-box-kit-wrapper").css({
|
||||
width: this.$().width(),
|
||||
height: headerHeight + this.$body().outerHeight(false)
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
export default Ember.Component.extend({
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-collection",
|
||||
classNames: "select-box-kit-collection",
|
||||
tagName: "ul"
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import SelectBoxKitRowComponent from "select-box-kit/components/select-box-kit/select-box-kit-row";
|
||||
|
||||
export default SelectBoxKitRowComponent.extend({
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-row",
|
||||
classNames: "create",
|
||||
|
||||
click() {
|
||||
this.sendAction("onCreateContent", this.get("content.name"));
|
||||
},
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
export default Ember.Component.extend({
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-filter",
|
||||
classNames: "select-box-kit-filter",
|
||||
classNameBindings: ["isFocused", "isHidden"],
|
||||
isHidden: Ember.computed.not("filterable"),
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-header",
|
||||
classNames: "select-box-kit-header",
|
||||
classNameBindings: ["isFocused"],
|
||||
attributeBindings: ["selectedName:data-name"],
|
||||
shouldDisplaySelectedName: true,
|
||||
|
||||
@computed("options.shouldDisplaySelectedName")
|
||||
shouldDisplaySelectedName(should) {
|
||||
if (Ember.isNone(should)) { return true; }
|
||||
return should;
|
||||
},
|
||||
|
||||
@computed("options.selectedName", "selectedContent.firstObject.name")
|
||||
selectedName(optionsSelectedName, firstSelectedContentName) {
|
||||
if (Ember.isNone(optionsSelectedName)) {
|
||||
return firstSelectedContentName;
|
||||
}
|
||||
return optionsSelectedName;
|
||||
},
|
||||
|
||||
@computed("options.icon")
|
||||
icon(optionsIcon) { return optionsIcon; },
|
||||
|
||||
click() { this.sendAction("onToggle"); }
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import SelectBoxKitRowComponent from "select-box-kit/components/select-box-kit/select-box-kit-row";
|
||||
|
||||
export default SelectBoxKitRowComponent.extend({
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-row",
|
||||
classNames: "none",
|
||||
|
||||
click() {
|
||||
this.sendAction("onClearSelection");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
const { run, isPresent } = Ember;
|
||||
import UtilsMixin from "select-box-kit/mixins/utils";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, {
|
||||
layoutName: "select-box-kit/templates/components/select-box-kit/select-box-kit-row",
|
||||
classNames: "select-box-kit-row",
|
||||
tagName: "li",
|
||||
attributeBindings: [
|
||||
"title",
|
||||
"content.value:data-value",
|
||||
"content.name:data-name"
|
||||
],
|
||||
classNameBindings: ["isHighlighted", "isSelected"],
|
||||
|
||||
title: Ember.computed.alias("content.name"),
|
||||
|
||||
@computed("templateForRow")
|
||||
template(templateForRow) { return templateForRow(this); },
|
||||
|
||||
@on("didReceiveAttrs", "didUpdateAttrs")
|
||||
_setSelectionState() {
|
||||
const contentValue = this.get("content.value");
|
||||
this.set("isSelected", this.get("value") === contentValue);
|
||||
this.set("isHighlighted", this._castInteger(this.get("highlightedValue")) === this._castInteger(contentValue));
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_clearDebounce() {
|
||||
const hoverDebounce = this.get("hoverDebounce");
|
||||
|
||||
if (isPresent(hoverDebounce)) {
|
||||
run.cancel(hoverDebounce);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("content.originalContent.icon", "content.originalContent.iconClass")
|
||||
icon(icon, cssClass) {
|
||||
if (icon) {
|
||||
return iconHTML(icon, { class: cssClass });
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
mouseEnter() {
|
||||
this.set("hoverDebounce", run.debounce(this, this._sendOnHighlightAction, 32));
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("onSelect", this.get("content.value"));
|
||||
},
|
||||
|
||||
_sendOnHighlightAction() {
|
||||
this.sendAction("onHighlight", this.get("content.value"));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import NotificationOptionsComponent from "select-box-kit/components/notifications-button";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: "tag-notifications-button",
|
||||
i18nPrefix: "tagging.notifications",
|
||||
showFullTitle: false,
|
||||
headerComponent: "tag-notifications-button/tag-notifications-button-header",
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
this.sendAction("action", value);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
import NotificationButtonHeader from "select-box-kit/components/notifications-button/notifications-button-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default NotificationButtonHeader.extend({
|
||||
classNames: "tag-notifications-button-header",
|
||||
shouldDisplaySelectedName: false,
|
||||
|
||||
@computed("_selectedDetails.icon", "_selectedDetails.key")
|
||||
icon() {
|
||||
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import ComboBoxComponent from "select-box-kit/components/combo-box";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
headerText: "topic.controls",
|
||||
classNames: "topic-footer-mobile-dropdown",
|
||||
filterable: false,
|
||||
autoFilterable: false,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComponentOptions() {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedName: I18n.t(this.get("headerText"))
|
||||
}));
|
||||
},
|
||||
|
||||
@computed("topic", "topic.details", "value")
|
||||
content(topic, details) {
|
||||
const content = [];
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
value = this.defaultOnSelect(value);
|
||||
|
||||
const topic = this.get("topic");
|
||||
|
||||
// In case it"s not a valid topic
|
||||
if (!topic.get("id")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("value", value);
|
||||
|
||||
const refresh = () => this.set("value", null);
|
||||
|
||||
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-box-kit/templates/components/topic-notifications-button",
|
||||
classNames: "topic-notifications-button",
|
||||
showFullTitle: true,
|
||||
appendReason: true
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import NotificationOptionsComponent from "select-box-kit/components/notifications-button";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { topicLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
classNames: "topic-notifications-options",
|
||||
content: topicLevels,
|
||||
i18nPrefix: "topic.notifications",
|
||||
value: Ember.computed.alias("topic.details.notification_level"),
|
||||
|
||||
@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");
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(value) {
|
||||
if (value !== this.get("computedValue")) {
|
||||
this.get("topic.details").updateNotifications(value);
|
||||
}
|
||||
|
||||
this.set("value", value);
|
||||
|
||||
this.defaultOnSelect(value);
|
||||
|
||||
this.blur();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
export default Ember.Mixin.create({
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.offscreenInputSelector = ".select-box-kit-offscreen";
|
||||
this.filterInputSelector = ".select-box-kit-filter-input";
|
||||
this.rowSelector = ".select-box-kit-row";
|
||||
this.collectionSelector = ".select-box-kit-collection";
|
||||
this.headerSelector = ".select-box-kit-header";
|
||||
this.bodySelector = ".select-box-kit-body";
|
||||
},
|
||||
|
||||
$findRowByValue(value) {
|
||||
return this.$(`${this.rowSelector}[data-value='${value}']`);
|
||||
},
|
||||
|
||||
$header() {
|
||||
return this.$(this.headerSelector);
|
||||
},
|
||||
|
||||
$body() {
|
||||
return this.$(this.bodySelector);
|
||||
},
|
||||
|
||||
$collection() {
|
||||
return this.$(this.collectionSelector);
|
||||
},
|
||||
|
||||
$rows() {
|
||||
return this.$(this.rowSelector);
|
||||
},
|
||||
|
||||
$highlightedRow() {
|
||||
return this.$rows().filter(".is-highlighted");
|
||||
},
|
||||
|
||||
$selectedRow() {
|
||||
return this.$rows().filter(".is-selected");
|
||||
},
|
||||
|
||||
$offscreenInput() {
|
||||
return this.$(this.offscreenInputSelector);
|
||||
},
|
||||
|
||||
$filterInput() {
|
||||
return this.$(this.filterInputSelector);
|
||||
},
|
||||
|
||||
_killEvent(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,196 @@
|
|||
const { isEmpty } = Ember;
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.keys = {
|
||||
TAB: 9,
|
||||
ENTER: 13,
|
||||
ESC: 27,
|
||||
SPACE: 32,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40,
|
||||
SHIFT: 16,
|
||||
CTRL: 17,
|
||||
ALT: 18,
|
||||
PAGE_UP: 33,
|
||||
PAGE_DOWN: 34,
|
||||
HOME: 36,
|
||||
END: 35,
|
||||
BACKSPACE: 8
|
||||
};
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
|
||||
$(document).off(
|
||||
"click.select-box-kit mousedown.select-box-kit touchstart.select-box-kit"
|
||||
);
|
||||
|
||||
this.$offscreenInput()
|
||||
.off("focus.select-box-kit focusin.select-box-kit blur.select-box-kit keydown.select-box-kit");
|
||||
|
||||
this.$filterInput().off(`keydown.select-box-kit`);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
$(document).on(
|
||||
"click.select-box-kit mousedown.select-box-kit touchstart.select-box-kit", event => {
|
||||
if (this.$()[0].contains(event.target)) { return; }
|
||||
this.clickOutside(event);
|
||||
});
|
||||
|
||||
this.$offscreenInput()
|
||||
.on(`blur.select-box-kit`, () => {
|
||||
if (this.get("isExpanded") === false && this.get("isFocused") === true) {
|
||||
this.close();
|
||||
}
|
||||
})
|
||||
.on(`focus.select-box-kit`, (event) => {
|
||||
this.set("isFocused", true);
|
||||
this._killEvent(event);
|
||||
})
|
||||
.on(`focusin.select-box-kit`, (event) => {
|
||||
this.set("isFocused", true);
|
||||
this._killEvent(event);
|
||||
})
|
||||
.on(`keydown.select-box-kit`, (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
switch (keyCode) {
|
||||
case this.keys.UP:
|
||||
case this.keys.DOWN:
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.set("isExpanded", true);
|
||||
}
|
||||
|
||||
Ember.run.schedule("actions", () => {
|
||||
this._handleArrowKey(keyCode);
|
||||
});
|
||||
|
||||
this._killEvent(event);
|
||||
|
||||
return;
|
||||
case this.keys.ENTER:
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.set("isExpanded", true);
|
||||
} else {
|
||||
this.send("onSelect", this.$highlightedRow().data("value"));
|
||||
}
|
||||
|
||||
this._killEvent(event);
|
||||
|
||||
return;
|
||||
case this.keys.TAB:
|
||||
if (this.get("isExpanded") === false) {
|
||||
return true;
|
||||
} else {
|
||||
this.send("onSelect", this.$highlightedRow().data("value"));
|
||||
return;
|
||||
}
|
||||
case this.keys.ESC:
|
||||
this.close();
|
||||
this._killEvent(event);
|
||||
return;
|
||||
case this.keys.BACKSPACE:
|
||||
this._killEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isSpecialKey(keyCode) === false && event.metaKey === false) {
|
||||
this.setProperties({
|
||||
isExpanded: true,
|
||||
filter: String.fromCharCode(keyCode)
|
||||
});
|
||||
|
||||
Ember.run.schedule("afterRender", () => this.$filterInput().focus() );
|
||||
}
|
||||
});
|
||||
|
||||
this.$filterInput()
|
||||
.on(`keydown.select-box-kit`, (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if ([
|
||||
this.keys.RIGHT,
|
||||
this.keys.LEFT,
|
||||
this.keys.BACKSPACE,
|
||||
this.keys.SPACE,
|
||||
].includes(keyCode) || event.metaKey === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isSpecialKey(keyCode) === true) {
|
||||
this.$offscreenInput().focus().trigger(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
_handleArrowKey(keyCode) {
|
||||
if (isEmpty(this.get("filteredContent"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
switch (keyCode) {
|
||||
case 38:
|
||||
Ember.run.throttle(this, this._handleUpArrow, 32);
|
||||
break;
|
||||
default:
|
||||
Ember.run.throttle(this, this._handleDownArrow, 32);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_moveHighlight(direction) {
|
||||
const $rows = this.$rows();
|
||||
const currentIndex = $rows.index(this.$highlightedRow());
|
||||
|
||||
let nextIndex = 0;
|
||||
|
||||
if (currentIndex < 0) {
|
||||
nextIndex = 0;
|
||||
} else if (currentIndex + direction < $rows.length) {
|
||||
nextIndex = currentIndex + direction;
|
||||
}
|
||||
|
||||
this._rowSelection($rows, nextIndex);
|
||||
},
|
||||
|
||||
_handleDownArrow() { this._moveHighlight(1); },
|
||||
|
||||
_handleUpArrow() { this._moveHighlight(-1); },
|
||||
|
||||
_rowSelection($rows, nextIndex) {
|
||||
const highlightableValue = $rows.eq(nextIndex).data("value");
|
||||
const $highlightableRow = this.$findRowByValue(highlightableValue);
|
||||
this.send("onHighlight", highlightableValue);
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
const $collection = this.$collection();
|
||||
const currentOffset = $collection.offset().top +
|
||||
$collection.outerHeight(false);
|
||||
const nextBottom = $highlightableRow.offset().top +
|
||||
$highlightableRow.outerHeight(false);
|
||||
const nextOffset = $collection.scrollTop() + nextBottom - currentOffset;
|
||||
|
||||
if (nextIndex === 0) {
|
||||
$collection.scrollTop(0);
|
||||
} else if (nextBottom > currentOffset) {
|
||||
$collection.scrollTop(nextOffset);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_isSpecialKey(keyCode) {
|
||||
return Object.values(this.keys).includes(keyCode);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
export default Ember.Mixin.create({
|
||||
_castInteger(value) {
|
||||
if (this.get("castInteger") === true && Ember.isPresent(value)) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
return Ember.isNone(value) ? value : value.toString();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
{{#if icon}}
|
||||
{{{icon}}}
|
||||
{{/if}}
|
||||
|
||||
<span class="selected-name" title={{selectedName}}>
|
||||
{{{selectedName}}}
|
||||
</span>
|
||||
|
||||
{{#if shouldDisplayClearableButton}}
|
||||
<button class="btn-clear" {{action onClearSelection bubbles=false}}>
|
||||
{{d-icon 'times'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
|
@ -0,0 +1,17 @@
|
|||
<button
|
||||
class="btn {{if shouldDisplaySelectedName 'btn-icon-text' 'no-text btn-icon'}}"
|
||||
aria-label="{{selectedName}}"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
title="{{selectedName}}">
|
||||
|
||||
{{#if icon}}
|
||||
{{{icon}}}
|
||||
{{/if}}
|
||||
|
||||
{{#if shouldDisplaySelectedName}}
|
||||
<span class="d-button-label selected-name">
|
||||
{{selectedName}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</button>
|
|
@ -0,0 +1,15 @@
|
|||
{{#if template}}
|
||||
{{{template}}}
|
||||
{{else}}
|
||||
{{#if icon}}
|
||||
<div class="icons">
|
||||
<span class="selection-indicator"></span>
|
||||
{{{icon}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="texts">
|
||||
<span class="name">{{{name}}}</span>
|
||||
<span class="desc">{{{description}}}</span>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1,23 @@
|
|||
{{#if icon}}
|
||||
<div class="future-date-input-selector-icons">
|
||||
{{{icon}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<span class="selected-name" title={{selectedName}}>
|
||||
{{{selectedName}}}
|
||||
</span>
|
||||
|
||||
{{#if datetime}}
|
||||
<span class="future-date-input-selector-datetime">
|
||||
{{datetime}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if shouldDisplayClearableButton}}
|
||||
<button class="btn-clear" {{action onClearSelection bubbles=false}}>
|
||||
{{d-icon 'times'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
|
@ -0,0 +1,13 @@
|
|||
{{#if icon}}
|
||||
<div class="future-date-input-selector-icons">
|
||||
{{{icon}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<span class="name">{{content.name}}</span>
|
||||
|
||||
{{#if datetime}}
|
||||
<span class="future-date-input-selector-datetime">
|
||||
{{datetime}}
|
||||
</span>
|
||||
{{/if}}
|
|
@ -0,0 +1,30 @@
|
|||
<ul class="choices">
|
||||
{{#each selectedContent as |selectedContent|}}
|
||||
<li tabindex="-1" data-value={{selectedContent.value}} data-name={{selectedContent.name}} class="selected-name">
|
||||
<span class="delete-icon" {{action onDeselect selectedContent.value bubbles=false}}>
|
||||
{{d-icon "times"}}
|
||||
</span>
|
||||
<span class="name">
|
||||
{{selectedContent.name}}
|
||||
</span>
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if shouldDisplayFilterPlaceholder}}
|
||||
<li class="choice-placeholder">
|
||||
{{text}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
<li class="filter">
|
||||
{{input
|
||||
class="select-box-kit-filter-input"
|
||||
key-up=onFilterChange
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck=false
|
||||
value=filter
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,65 @@
|
|||
<input
|
||||
class="select-box-kit-offscreen"
|
||||
type="text"
|
||||
aria-haspopup="true"
|
||||
role="button"
|
||||
aria-labelledby="select-box-kit-offscreen-{{elementId}}"
|
||||
tabindex={{tabindex}}
|
||||
name={{name}}
|
||||
value={{computedValue}}
|
||||
readonly=true
|
||||
/>
|
||||
|
||||
{{component headerComponent
|
||||
none=computedNone
|
||||
isFocused=isFocused
|
||||
isExpanded=isExpanded
|
||||
selectedContent=selectedContent
|
||||
onDeselect=(action "onDeselect")
|
||||
onToggle=(action "onToggle")
|
||||
onFilterChange=(action "onFilterChange")
|
||||
onClearSelection=(action "onClearSelection")
|
||||
options=headerComponentOptions
|
||||
}}
|
||||
|
||||
<div class="select-box-kit-body">
|
||||
{{component filterComponent
|
||||
onFilterChange=(action "onFilterChange")
|
||||
icon=filterIcon
|
||||
filter=filter
|
||||
filterable=computedFilterable
|
||||
isFocused=isFocused
|
||||
placeholder=(i18n filterPlaceholder)
|
||||
tabindex=tabindex
|
||||
}}
|
||||
|
||||
{{#if renderBody}}
|
||||
{{component collectionComponent
|
||||
shouldDisplayCreateRow=shouldDisplayCreateRow
|
||||
none=computedNone
|
||||
createRowContent=createRowContent
|
||||
selectedContent=selectedContent
|
||||
filteredContent=filteredContent
|
||||
rowComponent=rowComponent
|
||||
noneRowComponent=noneRowComponent
|
||||
createRowComponent=createRowComponent
|
||||
iconForRow=iconForRow
|
||||
templateForRow=templateForRow
|
||||
templateForNoneRow=templateForNoneRow
|
||||
templateForCreateRow=templateForCreateRow
|
||||
shouldHighlightRow=shouldHighlightRow
|
||||
shouldSelectRow=shouldSelectRow
|
||||
titleForRow=titleForRow
|
||||
onClearSelection=(action "onClearSelection")
|
||||
onSelect=(action "onSelect")
|
||||
onHighlight=(action "onHighlight")
|
||||
onCreateContent=(action "onCreateContent")
|
||||
noContentLabel=noContentLabel
|
||||
highlightedValue=highlightedValue
|
||||
computedValue=computedValue
|
||||
rowComponentOptions=rowComponentOptions
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="select-box-kit-wrapper"></div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue