Introduces select-kit
* renames `select-box-kit` into `select-kit` * introduces `single-select` and `multi-select` as base components * introduces {{search-advanced-category-chooser}} as a better component for selecting category in advanced search * improves events handling in select-kit * recreates color selection inputs using {{multi-select}} and a custom {{selected-color}} component * replaces category-selector by a component using select-kit and based on multi-select * improves positioning of wrapper * removes the need for offscreen, and instead use `select-kit-header` as a base focus point for all select-kit based components * introduces a formal plugin api for select-kit based components * introduces a formal pattern for loading and updating select-kit based components: ``` computeValue() computeContent() mutateValue() ```
This commit is contained in:
parent
edc4b30f82
commit
39f3dbd945
12
.eslintrc
12
.eslintrc
|
@ -42,12 +42,12 @@
|
|||
"invisible":true,
|
||||
"asyncRender":true,
|
||||
"selectDropdown":true,
|
||||
"selectBox":true,
|
||||
"expandSelectBoxKit":true,
|
||||
"collapseSelectBoxKit":true,
|
||||
"selectBoxKitSelectRow":true,
|
||||
"selectBoxKitSelectNoneRow":true,
|
||||
"selectBoxKitFillInFilter":true,
|
||||
"selectKit":true,
|
||||
"expandSelectKit":true,
|
||||
"collapseSelectKit":true,
|
||||
"selectKitSelectRow":true,
|
||||
"selectKitSelectNoneRow":true,
|
||||
"selectKitFillInFilter":true,
|
||||
"asyncTestDiscourse":true,
|
||||
"fixture":true,
|
||||
"find":true,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{{category-selector categories=selectedCategories blacklist=selectedCategories}}
|
||||
{{category-selector categories=selectedCategories}}
|
||||
<div class='desc'>{{{unbound setting.description}}}</div>
|
||||
{{setting-validation-message message=validationMessage}}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
<div class='filters'>
|
||||
<div>
|
||||
<label>{{d-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.categories_filter'}}</label>
|
||||
{{category-selector categories=model.categories blacklist=model.categories}}
|
||||
{{category-selector categories=model.categories}}
|
||||
<div class="instructions">{{i18n 'admin.web_hooks.categories_filter_instructions'}}</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//= require ./ember-addons/ember-computed-decorators
|
||||
//= require ./ember-addons/fmt
|
||||
//= require_tree ./discourse-common
|
||||
//= require_tree ./select-box-kit
|
||||
//= require_tree ./select-kit
|
||||
//= require ./discourse
|
||||
//= require ./deprecated
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ export function renderIcon(renderType, id, params) {
|
|||
let rendererForType = renderer[renderType];
|
||||
|
||||
if (rendererForType) {
|
||||
let result = rendererForType(REPLACEMENTS[id] || id, params || {});
|
||||
const icon = { id, replacementId: REPLACEMENTS[id] };
|
||||
let result = rendererForType(icon, params || {});
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
@ -68,8 +69,14 @@ export function registerIconRenderer(renderer) {
|
|||
}
|
||||
|
||||
// Support for font awesome icons
|
||||
function faClasses(id, params) {
|
||||
let classNames = `fa fa-${id} d-icon d-icon-${id}`;
|
||||
function faClasses(icon, params) {
|
||||
let classNames;
|
||||
if (typeof icon.replacementId !== "undefined") {
|
||||
classNames = `fa fa-${icon.replacementId} d-icon ${icon.id}`;
|
||||
} else {
|
||||
classNames = `fa fa-${icon.id} d-icon d-${icon.id}`;
|
||||
}
|
||||
|
||||
if (params) {
|
||||
if (params.modifier) { classNames += " fa-" + params.modifier; }
|
||||
if (params['class']) { classNames += ' ' + params['class']; }
|
||||
|
@ -81,9 +88,9 @@ function faClasses(id, params) {
|
|||
registerIconRenderer({
|
||||
name: 'font-awesome',
|
||||
|
||||
string(id, params) {
|
||||
string(icon, params) {
|
||||
let tagName = params.tagName || 'i';
|
||||
let html = `<${tagName} class='${faClasses(id, params)}'`;
|
||||
let html = `<${tagName} class='${faClasses(icon, params)}'`;
|
||||
if (params.title) { html += ` title='${I18n.t(params.title)}'`; }
|
||||
if (params.label) { html += " aria-hidden='true'"; }
|
||||
html += `></${tagName}>`;
|
||||
|
@ -93,11 +100,11 @@ registerIconRenderer({
|
|||
return html;
|
||||
},
|
||||
|
||||
node(id, params) {
|
||||
node(icon, params) {
|
||||
let tagName = params.tagName || 'i';
|
||||
|
||||
const properties = {
|
||||
className: faClasses(id, params),
|
||||
className: faClasses(icon, params),
|
||||
attributes: { "aria-hidden": true }
|
||||
};
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import Category from 'discourse/models/category';
|
||||
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import { findRawTemplate } from 'discourse/lib/raw-templates';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
@observes('categories')
|
||||
_update() {
|
||||
if (this.get('canReceiveUpdates') === 'true')
|
||||
this._initializeAutocomplete({updateData: true});
|
||||
},
|
||||
|
||||
@on('didInsertElement')
|
||||
_initializeAutocomplete(opts) {
|
||||
const self = this,
|
||||
regexp = new RegExp(`href=['\"]${Discourse.getURL('/c/')}([^'\"]+)`);
|
||||
|
||||
this.$('input').autocomplete({
|
||||
items: this.get('categories'),
|
||||
single: this.get('single'),
|
||||
allowAny: false,
|
||||
updateData: (opts && opts.updateData) ? opts.updateData : false,
|
||||
dataSource(term) {
|
||||
return Category.list().filter(category => {
|
||||
const regex = new RegExp(term, 'i');
|
||||
return category.get('name').match(regex) &&
|
||||
!_.contains(self.get('blacklist') || [], category) &&
|
||||
!_.contains(self.get('categories'), category) ;
|
||||
});
|
||||
},
|
||||
onChangeItems(items) {
|
||||
const categories = _.map(items, link => {
|
||||
const slug = link.match(regexp)[1];
|
||||
return Category.findSingleBySlug(slug);
|
||||
});
|
||||
Em.run.next(() => {
|
||||
let existingCategory = _.isArray(self.get('categories')) ? self.get('categories') : [self.get('categories')];
|
||||
const result = _.intersection(existingCategory.map(itm => itm.id), categories.map(itm => itm.id));
|
||||
if (result.length !== categories.length || existingCategory.length !== categories.length)
|
||||
self.set('categories', categories);
|
||||
});
|
||||
},
|
||||
template: findRawTemplate('category-selector-autocomplete'),
|
||||
transformComplete(category) {
|
||||
return categoryBadgeHTML(category, {allowUncategorized: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
FORMAT,
|
||||
} from "select-box-kit/components/future-date-input-selector";
|
||||
} from "select-kit/components/future-date-input-selector";
|
||||
|
||||
import { PUBLISH_TO_CATEGORY_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
|
||||
|
|
|
@ -185,18 +185,18 @@ export default Em.Component.extend({
|
|||
const userInput = Discourse.Category.findBySlug(subcategories[1], subcategories[0]);
|
||||
if ((!existingInput && userInput)
|
||||
|| (existingInput && userInput && existingInput.id !== userInput.id))
|
||||
this.set('searchedTerms.category', [userInput]);
|
||||
this.set('searchedTerms.category', userInput);
|
||||
} else
|
||||
if (isNaN(subcategories)) {
|
||||
const userInput = Discourse.Category.findSingleBySlug(subcategories[0]);
|
||||
if ((!existingInput && userInput)
|
||||
|| (existingInput && userInput && existingInput.id !== userInput.id))
|
||||
this.set('searchedTerms.category', [userInput]);
|
||||
this.set('searchedTerms.category', userInput);
|
||||
} else {
|
||||
const userInput = Discourse.Category.findById(subcategories[0]);
|
||||
if ((!existingInput && userInput)
|
||||
|| (existingInput && userInput && existingInput.id !== userInput.id))
|
||||
this.set('searchedTerms.category', [userInput]);
|
||||
this.set('searchedTerms.category', userInput);
|
||||
}
|
||||
} else
|
||||
this.set('searchedTerms.category', '');
|
||||
|
@ -303,11 +303,11 @@ export default Em.Component.extend({
|
|||
|
||||
const slugCategoryMatches = (match.length !== 0) ? match[0].match(REGEXP_CATEGORY_SLUG) : null;
|
||||
const idCategoryMatches = (match.length !== 0) ? match[0].match(REGEXP_CATEGORY_ID) : null;
|
||||
if (categoryFilter && categoryFilter[0]) {
|
||||
const id = categoryFilter[0].id;
|
||||
const slug = categoryFilter[0].slug;
|
||||
if (categoryFilter[0].parentCategory) {
|
||||
const parentSlug = categoryFilter[0].parentCategory.slug;
|
||||
if (categoryFilter) {
|
||||
const id = categoryFilter.id;
|
||||
const slug = categoryFilter.slug;
|
||||
if (categoryFilter.parentCategory) {
|
||||
const parentSlug = categoryFilter.parentCategory.slug;
|
||||
if (slugCategoryMatches)
|
||||
searchTerm = searchTerm.replace(slugCategoryMatches[0], `#${parentSlug}:${slug}`);
|
||||
else if (idCategoryMatches)
|
||||
|
|
|
@ -12,6 +12,7 @@ export default MountWidget.extend(Docking, {
|
|||
buildArgs() {
|
||||
let attrs = {
|
||||
topic: this.get('topic'),
|
||||
notificationLevel: this.get('notificationLevel'),
|
||||
topicTrackingState: this.topicTrackingState,
|
||||
enteredIndex: this.get('enteredIndex'),
|
||||
dockAt: this.dockAt,
|
||||
|
|
|
@ -21,6 +21,7 @@ import { attachAdditionalPanel } from 'discourse/widgets/header';
|
|||
import { registerIconRenderer, replaceIcon } from 'discourse-common/lib/icon-library';
|
||||
import { addNavItem } from 'discourse/models/nav-item';
|
||||
import { replaceFormatter } from 'discourse/lib/utilities';
|
||||
import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = '0.8.12';
|
||||
|
@ -589,6 +590,21 @@ class PluginApi {
|
|||
formatUsername(fn) {
|
||||
replaceFormatter(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Access SelectKit plugin api
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* modifySelectKit("topic-footer-mobile-dropdown").appendContent(() => [{
|
||||
* name: "discourse",
|
||||
* id: 1
|
||||
* }])
|
||||
*/
|
||||
modifySelectKit(pluginApiKey) {
|
||||
return modifySelectKit(pluginApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
let _pluginv01;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<input class='category-selector' type='text' name='categories'>
|
|
@ -9,7 +9,7 @@
|
|||
{{navigation-bar navItems=navItems filterMode=filterMode category=category}}
|
||||
|
||||
{{#if showCategoryNotifications}}
|
||||
{{category-notifications-button category=category}}
|
||||
{{category-notifications-button value=category.notification_level category=category}}
|
||||
{{/if}}
|
||||
|
||||
{{create-topic-button
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
{{category-chooser
|
||||
none="category.none"
|
||||
value=category.parent_category_id
|
||||
excludeCategoryId=category.id
|
||||
categories=parentCategories
|
||||
allowSubCategories=false
|
||||
allowUncategorized=false}}
|
||||
{{/if}}
|
||||
</section>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div class="control-group pull-left">
|
||||
<label class="control-label" for="search-in-category">{{i18n "search.advanced.in_category.label"}}</label>
|
||||
<div class="controls">
|
||||
{{category-selector categories=searchedTerms.category single="true" canReceiveUpdates="true"}}
|
||||
{{search-advanced-category-chooser value=searchedTerms.category}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -74,10 +74,10 @@
|
|||
args=(hash topic=topic)
|
||||
connectorTagName="span"}}
|
||||
|
||||
{{pinned-button topic=topic}}
|
||||
{{pinned-button pinned=topic.pinned topic=topic}}
|
||||
</div>
|
||||
|
||||
{{topic-notifications-button topic=topic}}
|
||||
{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}
|
||||
|
||||
{{plugin-outlet name="after-topic-footer-buttons"
|
||||
args=(hash topic=topic)
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
|
||||
{{topic-timeline
|
||||
topic=model
|
||||
notificationLevel=model.details.notification_level
|
||||
prevEvent=info.prevEvent
|
||||
fullscreen=info.topicProgressExpanded
|
||||
enteredIndex=enteredIndex
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
{{i18n "topic.unsubscribe.change_notification_state"}}
|
||||
</p>
|
||||
|
||||
{{topic-notifications-button topic=model}}
|
||||
{{topic-notifications-button notificationLevel=model.details.notification_level topic=model}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
{{/if}}
|
||||
|
||||
{{#if isGroup}}
|
||||
{{group-notifications-button group=group user=model}}
|
||||
{{group-notifications-button value=group.group_user.notification_level group=group user=model}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
export default class ComponentConnector {
|
||||
constructor(widget, componentName, opts) {
|
||||
constructor(widget, componentName, opts, trackedProperties) {
|
||||
this.widget = widget;
|
||||
this.opts = opts;
|
||||
this.componentName = componentName;
|
||||
this.trackedProperties = trackedProperties || [];
|
||||
}
|
||||
|
||||
init() {
|
||||
const $elem = $('<div style="display: inline-block;" class="widget-component-connector"></div>');
|
||||
const $elem = $('<div style="display: inline-flex;" class="widget-component-connector"></div>');
|
||||
const elem = $elem[0];
|
||||
const { opts, widget, componentName } = this;
|
||||
|
||||
|
@ -29,7 +30,18 @@ export default class ComponentConnector {
|
|||
return elem;
|
||||
}
|
||||
|
||||
update() { }
|
||||
update(prev) {
|
||||
let shouldInit = false;
|
||||
this.trackedProperties.forEach(prop => {
|
||||
if (prev.opts[prop] !== this.opts[prop]) {
|
||||
shouldInit = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldInit === true) return this.init();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ComponentConnector.prototype.type = 'Widget';
|
||||
|
|
|
@ -291,7 +291,7 @@ createWidget('timeline-footer-controls', {
|
|||
|
||||
html(attrs) {
|
||||
const controls = [];
|
||||
const { currentUser, fullScreen, topic } = attrs;
|
||||
const { currentUser, fullScreen, topic, notificationLevel } = attrs;
|
||||
|
||||
if (currentUser && !fullScreen) {
|
||||
if (topic.get('details.can_create_post')) {
|
||||
|
@ -315,12 +315,13 @@ createWidget('timeline-footer-controls', {
|
|||
|
||||
if (currentUser) {
|
||||
controls.push(new ComponentConnector(this,
|
||||
'topic-notifications-button',
|
||||
'topic-notifications-options',
|
||||
{
|
||||
value: notificationLevel,
|
||||
topic,
|
||||
appendReason: false,
|
||||
showFullTitle: false
|
||||
}
|
||||
},
|
||||
["value"]
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import MultiComboBoxComponent from "select-box-kit/components/multi-combo-box";
|
||||
|
||||
export default MultiComboBoxComponent.extend({
|
||||
classNames: "admin-group-selector",
|
||||
selected: null,
|
||||
available: null,
|
||||
allowAny: false,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super();
|
||||
|
||||
this.set("value", this.get("selected").map(s => this._valueForContent(s)));
|
||||
this.set("content", this.get("available"));
|
||||
},
|
||||
|
||||
formatRowContent(content) {
|
||||
let formatedContent = this._super(content);
|
||||
formatedContent.locked = content.automatic;
|
||||
return formatedContent;
|
||||
},
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super();
|
||||
|
||||
this.set("highlightedValue", null);
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.autoHighlightFunction();
|
||||
});
|
||||
},
|
||||
|
||||
selectValuesFunction(values) {
|
||||
values.forEach(value => {
|
||||
this.triggerAction({
|
||||
action: "groupAdded",
|
||||
actionContext: this.get("content").findBy("id", parseInt(value, 10))
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
deselectValuesFunction(values) {
|
||||
values.forEach(value => {
|
||||
this.triggerAction({ action: "groupRemoved", actionContext: value });
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,131 +0,0 @@
|
|||
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,
|
||||
|
||||
filteredContentFunction(computedContent, computedValue, filter) {
|
||||
if (isEmpty(filter)) { return computedContent; }
|
||||
|
||||
const _matchFunction = (f, text) => {
|
||||
return text.toLowerCase().indexOf(f) > -1;
|
||||
};
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
|
||||
return computedContent.filter(c => {
|
||||
const category = Category.findById(get(c, "value"));
|
||||
const text = get(c, "name");
|
||||
if (category && category.get("parentCategory")) {
|
||||
const categoryName = category.get("parentCategory.name");
|
||||
return _matchFunction(lowerFilter, text) || _matchFunction(lowerFilter, categoryName);
|
||||
} else {
|
||||
return _matchFunction(lowerFilter, text);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@computed("rootNone", "rootNoneLabel")
|
||||
none(rootNone, rootNoneLabel) {
|
||||
if (this.siteSettings.allow_uncategorized_topics || this.get("allowUncategorized")) {
|
||||
if (!isNone(rootNone)) {
|
||||
return rootNoneLabel || "category.none";
|
||||
} else {
|
||||
return Category.findUncategorized();
|
||||
}
|
||||
} else {
|
||||
return "category.choose";
|
||||
}
|
||||
},
|
||||
|
||||
@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;
|
||||
}
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
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",
|
||||
|
||||
selectValueFunction(value) {
|
||||
this.get("category").setNotification(value);
|
||||
}
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
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();
|
||||
}
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
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() {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
this.close();
|
||||
},
|
||||
|
||||
didSelectValue() {
|
||||
this._super();
|
||||
|
||||
this.blur();
|
||||
}
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
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",
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
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")
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
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); }
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
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); }
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
import MultiComboBoxComponent from "select-box-kit/components/multi-combo-box";
|
||||
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default MultiComboBoxComponent.extend({
|
||||
classNames: "list-setting",
|
||||
tokenSeparator: "|",
|
||||
settingValue: "",
|
||||
choices: null,
|
||||
filterable: true,
|
||||
|
||||
init() {
|
||||
const valuesFromString = this.get("settingValue").split(this.get("tokenSeparator"));
|
||||
this.set("value", valuesFromString.reject(v => Ember.isEmpty(v)));
|
||||
|
||||
if (Ember.isNone(this.get("choices"))) {
|
||||
this.set("content", valuesFromString);
|
||||
} else {
|
||||
this.set("content", this.get("choices"));
|
||||
}
|
||||
|
||||
if (!Ember.isNone(this.get("settingName"))) {
|
||||
this.set("nameProperty", this.get("settingName"));
|
||||
}
|
||||
|
||||
if (Ember.isEmpty(this.get("content"))) {
|
||||
this.set("rowComponent", null);
|
||||
this.set("noContentLabel", null);
|
||||
}
|
||||
|
||||
this._super();
|
||||
|
||||
if (this.get("nameProperty").indexOf("color") > -1) {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: "multi-combo-box/selected-color"
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
@observes("value.[]")
|
||||
setSettingValue() {
|
||||
this.set("settingValue", this.get("value").join(this.get("tokenSeparator")));
|
||||
},
|
||||
|
||||
@observes("content.[]")
|
||||
setChoices() { this.set("choices", this.get("content")); },
|
||||
|
||||
_handleTabOnKeyDown(event) {
|
||||
if (this.$highlightedRow().length === 1) {
|
||||
this._super(event);
|
||||
} else {
|
||||
this.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,240 +0,0 @@
|
|||
import SelectBoxKitComponent from "select-box-kit/components/select-box-kit";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone, isEmpty } = Ember;
|
||||
|
||||
export default SelectBoxKitComponent.extend({
|
||||
classNames: "multi-combo-box",
|
||||
headerComponent: "multi-combo-box/multi-combo-box-header",
|
||||
filterComponent: null,
|
||||
headerText: "select_box.default_header_text",
|
||||
allowAny: true,
|
||||
allowValueMutation: false,
|
||||
autoSelectFirst: false,
|
||||
autoFilterable: true,
|
||||
selectedNameComponent: "multi-combo-box/selected-name",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (isNone(this.get("value"))) { this.set("value", []); }
|
||||
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: this.get("selectedNameComponent")
|
||||
}));
|
||||
},
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return I18n.t("select_box.create", { content: rowComponent.get("content.name")});
|
||||
};
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
const $filterInput = this.$filterInput();
|
||||
|
||||
if (this.get("isFocused") === true && this.get("isExpanded") === false && keyCode === this.keys.BACKSPACE) {
|
||||
this.expand();
|
||||
return;
|
||||
}
|
||||
|
||||
// select all choices
|
||||
if (event.metaKey === true && keyCode === 65 && isEmpty(this.get("filter"))) {
|
||||
this.$(".choices .selected-name:not(.is-locked)").addClass("is-highlighted");
|
||||
return;
|
||||
}
|
||||
|
||||
// clear selection when multiple
|
||||
if (Ember.isEmpty(this.get("filter")) && this.$(".selected-name.is-highlighted").length >= 1 && keyCode === this.keys.BACKSPACE) {
|
||||
const highlightedValues = [];
|
||||
$.each(this.$(".selected-name.is-highlighted"), (i, el) => {
|
||||
highlightedValues.push($(el).attr("data-value"));
|
||||
});
|
||||
|
||||
this.send("onDeselect", highlightedValues);
|
||||
return;
|
||||
}
|
||||
|
||||
// try to remove last item from the list
|
||||
if (Ember.isEmpty(this.get("filter")) && keyCode === this.keys.BACKSPACE) {
|
||||
let $lastSelectedValue = $(this.$(".choices .selected-name:not(.is-locked)").last());
|
||||
|
||||
if ($lastSelectedValue.length === 0) { return; }
|
||||
|
||||
if ($lastSelectedValue.hasClass("is-highlighted") || $(document.activeElement).is($lastSelectedValue)) {
|
||||
this.send("onDeselect", this.get("selectedContent.lastObject.value"));
|
||||
$filterInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($filterInput.not(":visible") && $lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($filterInput.val() === "") {
|
||||
if ($filterInput.is(":focus")) {
|
||||
if ($lastSelectedValue.length > 0) { $lastSelectedValue.click(); }
|
||||
} else {
|
||||
if ($lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
} else {
|
||||
$filterInput.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed("value.[]")
|
||||
computedValue(value) { return value.map(v => this._castInteger(v)); },
|
||||
|
||||
@computed("value.[]", "computedContent.[]")
|
||||
selectedContent(value, computedContent) {
|
||||
const contents = [];
|
||||
value.forEach(v => {
|
||||
const content = computedContent.findBy("value", v);
|
||||
if (!isNone(content)) { contents.push(content); }
|
||||
});
|
||||
return contents;
|
||||
},
|
||||
|
||||
filteredContentFunction(computedContent, computedValue, filter) {
|
||||
computedContent = computedContent.filter(c => {
|
||||
return !computedValue.includes(get(c, "value"));
|
||||
});
|
||||
|
||||
if (isEmpty(filter)) { return computedContent; }
|
||||
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
willCreateContent() {
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
|
||||
didCreateContent() {
|
||||
this.clearFilter();
|
||||
this.autoHighlightFunction();
|
||||
},
|
||||
|
||||
createContentFunction(input) {
|
||||
if (!this.get("content").includes(input)) {
|
||||
this.get("content").pushObject(input);
|
||||
this.get("value").pushObject(input);
|
||||
}
|
||||
},
|
||||
|
||||
deselectValuesFunction(values) {
|
||||
const contents = this._computeRemovableContentsForValues(values);
|
||||
this.get("value").removeObjects(values);
|
||||
this.get("content").removeObjects(contents);
|
||||
},
|
||||
|
||||
highlightValueFunction(value) {
|
||||
this.set("highlightedValue", value);
|
||||
},
|
||||
|
||||
selectValuesFunction(values) {
|
||||
this.get("value").pushObjects(values);
|
||||
},
|
||||
|
||||
willSelectValues() {
|
||||
this.expand();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
|
||||
didSelectValues() {
|
||||
this.focus();
|
||||
this.clearFilter();
|
||||
this.autoHighlightFunction();
|
||||
},
|
||||
|
||||
willDeselectValues() {
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
|
||||
didDeselectValues() {
|
||||
this.autoHighlightFunction();
|
||||
},
|
||||
|
||||
willHighlightValue() {},
|
||||
|
||||
didHighlightValue() {},
|
||||
|
||||
autoHighlightFunction() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
if (this.get("renderedBodyOnce") === false) { return; }
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
if (isEmpty(this.get("filteredContent"))) {
|
||||
if (!isEmpty(this.get("filter"))) {
|
||||
this.send("onHighlight", this.get("filter"));
|
||||
} else if (this.get("none") && !isEmpty(this.get("selectedContent"))) {
|
||||
this.send("onHighlight", this.noneValue);
|
||||
}
|
||||
} else {
|
||||
this.send("onHighlight", this.get("filteredContent.firstObject.value"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClearSelection() {
|
||||
const values = this.get("selectedContent").map(c => get(c, "value"));
|
||||
this.send("onDeselect", values);
|
||||
},
|
||||
|
||||
onHighlight(value) {
|
||||
value = this._originalValueForValue(value);
|
||||
this.willHighlightValue(value);
|
||||
this.set("highlightedValue", value);
|
||||
this.highlightValueFunction(value);
|
||||
this.didHighlightValue(value);
|
||||
},
|
||||
|
||||
onCreateContent(input) {
|
||||
this.willCreateContent(input);
|
||||
this.createContentFunction(input);
|
||||
this.didCreateContent(input);
|
||||
},
|
||||
|
||||
onSelect(values) {
|
||||
values = Ember.makeArray(values).map(v => this._originalValueForValue(v));
|
||||
this.willSelectValues(values);
|
||||
this.selectValuesFunction(values);
|
||||
this.didSelectValues(values);
|
||||
},
|
||||
|
||||
onDeselect(values) {
|
||||
values = Ember.makeArray(this._computeRemovableValues(values));
|
||||
this.willDeselectValues(values);
|
||||
this.deselectValuesFunction(values);
|
||||
this.didSelectValues(values);
|
||||
}
|
||||
},
|
||||
|
||||
_computeRemovableContentsForValues(values) {
|
||||
const removableContents = [];
|
||||
values.forEach(v => {
|
||||
if (!this.get("_initialValues").includes(v)) {
|
||||
const content = this._contentForValue(v);
|
||||
if (!isNone(content)) { removableContents.push(content); }
|
||||
}
|
||||
});
|
||||
return removableContents;
|
||||
},
|
||||
|
||||
_computeRemovableValues(values) {
|
||||
return Ember.makeArray(values)
|
||||
.map(v => this._originalValueForValue(v))
|
||||
.filter(v => {
|
||||
return get(this._computedContentForValue(v), "locked") !== true;
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
import SelectedNameComponent from "select-box-kit/components/multi-combo-box/selected-name";
|
||||
|
||||
export default SelectedNameComponent.extend({
|
||||
didRender() {
|
||||
const name = this.get("content.name");
|
||||
this.$().css("border-bottom", Handlebars.Utils.escapeExpression(`7px solid #${name}`));
|
||||
}
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
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);
|
||||
}
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
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"
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (value === "unpinned") {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
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();
|
||||
},
|
||||
});
|
|
@ -1,328 +0,0 @@
|
|||
const { get, isNone, isEmpty, isPresent } = Ember;
|
||||
import { on } 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,
|
||||
renderedBodyOnce: false,
|
||||
renderedFilterOnce: 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",
|
||||
rowComponentOptions: null,
|
||||
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",
|
||||
headerComponentOptions: null,
|
||||
collectionComponent: "select-box-kit/select-box-kit-collection",
|
||||
collectionHeight: 200,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
fullWidthOnMobile: false,
|
||||
castInteger: false,
|
||||
allowAny: false,
|
||||
allowValueMutation: true,
|
||||
autoSelectFirst: true,
|
||||
content: null,
|
||||
_initialValues: null,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.noneValue = "__none__";
|
||||
this._previousScrollParentOverflow = "auto";
|
||||
this._previousCSSContext = {};
|
||||
this.set("headerComponentOptions", Ember.Object.create());
|
||||
this.set("rowComponentOptions", Ember.Object.create());
|
||||
|
||||
if ($(window).outerWidth(false) <= 420) {
|
||||
this.setProperties({ filterable: false, autoFilterable: false });
|
||||
}
|
||||
|
||||
if (isNone(this.get("content"))) { this.set("content", []); }
|
||||
this.set("value", this._castInteger(this.get("value")));
|
||||
|
||||
this.setInitialValues();
|
||||
},
|
||||
|
||||
setInitialValues() {
|
||||
this.set("_initialValues", this.getWithDefault("content", []).map((c) => {
|
||||
return this._valueForContent(c);
|
||||
}));
|
||||
},
|
||||
|
||||
@computed("computedContent.[]", "computedValue.[]", "filter")
|
||||
filteredContent(computedContent, computedValue, filter) {
|
||||
return this.filteredContentFunction(computedContent, computedValue, filter);
|
||||
},
|
||||
|
||||
filteredContentFunction(computedContent, computedValue, filter) {
|
||||
if (isEmpty(filter)) { return computedContent; }
|
||||
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
formatRowContent(content) {
|
||||
let originalContent;
|
||||
|
||||
if (typeof content === "string" || typeof content === "number") {
|
||||
originalContent = {};
|
||||
originalContent[this.get("valueAttribute")] = content;
|
||||
originalContent[this.get("nameProperty")] = content;
|
||||
} else {
|
||||
originalContent = content;
|
||||
}
|
||||
|
||||
return {
|
||||
value: this._castInteger(this._valueForContent(content)),
|
||||
name: this._nameForContent(content),
|
||||
locked: false,
|
||||
originalContent
|
||||
};
|
||||
},
|
||||
|
||||
formatContents(contents) {
|
||||
return contents.map(content => this.formatRowContent(content));
|
||||
},
|
||||
|
||||
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
|
||||
shouldDisplayFilter(filter, filterable, autoFilterable, renderedFilterOnce) {
|
||||
if (renderedFilterOnce === true || filterable === true) { return true; }
|
||||
if (filter.length > 0 && autoFilterable === true) { return true; }
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter")
|
||||
shouldDisplayCreateRow(filter) {
|
||||
if (this.get("allowAny") === true && filter.length > 0) { return true; }
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "shouldDisplayCreateRow")
|
||||
createRowContent(filter, shouldDisplayCreateRow) {
|
||||
if (shouldDisplayCreateRow === true && !this.get("value").includes(filter)) {
|
||||
return Ember.Object.create({ value: filter, name: filter });
|
||||
}
|
||||
},
|
||||
|
||||
@computed("content.[]", "value.[]")
|
||||
computedContent(content) {
|
||||
this._mutateValue();
|
||||
return this.formatContents(content || []);
|
||||
},
|
||||
|
||||
@computed("value", "none", "computedContent.firstObject.value")
|
||||
computedValue(value, none, firstContentValue) {
|
||||
if (isNone(value) && isNone(none) && this.get("autoSelectFirst") === true) {
|
||||
return firstContentValue;
|
||||
}
|
||||
|
||||
return 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: this.noneValue });
|
||||
default:
|
||||
return this.formatRowContent(none);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("computedValue", "computedContent.[]")
|
||||
selectedContent(computedValue, computedContent) {
|
||||
if (isNone(computedValue)) { return []; }
|
||||
return [ computedContent.findBy("value", computedValue) ];
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_setupResizeListener() {
|
||||
$(window).on("resize.select-box-kit", () => this.collapse() );
|
||||
},
|
||||
|
||||
|
||||
autoHighlightFunction() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
const filteredContent = this.get("filteredContent");
|
||||
const display = this.get("shouldDisplayCreateRow");
|
||||
const none = this.get("computedNone");
|
||||
|
||||
if (isNone(this.get("highlightedValue")) && !isEmpty(filteredContent)) {
|
||||
this.send("onHighlight", get(filteredContent, "firstObject.value"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (display === true && isEmpty(filteredContent)) {
|
||||
this.send("onHighlight", this.get("filter"));
|
||||
}
|
||||
else if (!isEmpty(filteredContent)) {
|
||||
this.send("onHighlight", get(filteredContent, "firstObject.value"));
|
||||
}
|
||||
else if (isEmpty(filteredContent) && isPresent(none) && display === false) {
|
||||
this.send("onHighlight", get(none, "value"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
willFilterContent() {
|
||||
this.expand();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
didFilterContent() {
|
||||
this.set("renderedFilterOnce", true);
|
||||
this.autoHighlightFunction();
|
||||
},
|
||||
|
||||
willCreateContent() { },
|
||||
createContentFunction(input) {
|
||||
this.get("content").pushObject(input);
|
||||
this.send("onSelect", input);
|
||||
},
|
||||
didCreateContent() {
|
||||
this.clearFilter();
|
||||
this.autoHighlightFunction();
|
||||
},
|
||||
|
||||
willHighlightValue() {},
|
||||
highlightValueFunction(value) {
|
||||
this.set("highlightedValue", value);
|
||||
},
|
||||
didHighlightValue() {},
|
||||
|
||||
willSelectValue() {
|
||||
this.clearFilter();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
selectValueFunction(value) {
|
||||
this.set("value", value);
|
||||
},
|
||||
didSelectValue() {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
},
|
||||
|
||||
willDeselectValue() {
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
unsetValueFunction() {
|
||||
this.set("value", null);
|
||||
},
|
||||
didDeselectValue() {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onToggle() {
|
||||
this.get("isExpanded") === true ? this.collapse() : this.expand();
|
||||
},
|
||||
|
||||
onClearSelection() {
|
||||
this.send("onDeselect", this.get("value"));
|
||||
},
|
||||
|
||||
onHighlight(value) {
|
||||
value = this._originalValueForValue(value);
|
||||
this.willHighlightValue(value);
|
||||
this.set("highlightedValue", value);
|
||||
this.highlightValueFunction(value);
|
||||
this.didHighlightValue(value);
|
||||
},
|
||||
|
||||
onCreateContent(input) {
|
||||
this.willCreateContent(input);
|
||||
this.createContentFunction(input);
|
||||
this.didCreateContent(input);
|
||||
},
|
||||
|
||||
onSelect(value) {
|
||||
if (value === "") { value = null; }
|
||||
this.willSelectValue(value);
|
||||
this.selectValueFunction(value);
|
||||
this.didSelectValue(value);
|
||||
},
|
||||
|
||||
onDeselect(value) {
|
||||
value = this._originalValueForValue(value);
|
||||
this.willDeselectValue(value);
|
||||
this.unsetValueFunction(value);
|
||||
this.didSelectValue(value);
|
||||
},
|
||||
|
||||
onFilterChange(_filter) {
|
||||
this.willFilterContent(_filter);
|
||||
this.set("filter", _filter);
|
||||
this.didFilterContent(_filter);
|
||||
},
|
||||
},
|
||||
|
||||
clearFilter() {
|
||||
this.$filterInput().val("");
|
||||
this.setProperties({ filter: "" });
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_mutateValue() {
|
||||
if (this.get("allowValueMutation") !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const none = isNone(this.get("none"));
|
||||
const emptyValue = isEmpty(this.get("value"));
|
||||
|
||||
if (none && emptyValue) {
|
||||
Ember.run.scheduleOnce("sync", () => {
|
||||
if (!isEmpty(this.get("computedContent"))) {
|
||||
const firstValue = this.get("computedContent.firstObject.value");
|
||||
this.set("value", firstValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
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"
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
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"));
|
||||
},
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
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"); }
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
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");
|
||||
}
|
||||
});
|
|
@ -1,70 +0,0 @@
|
|||
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",
|
||||
tabIndex: -1,
|
||||
attributeBindings: [
|
||||
"tabIndex",
|
||||
"title",
|
||||
"content.value:data-value",
|
||||
"content.name:data-name"
|
||||
],
|
||||
classNameBindings: ["isHighlighted", "isSelected"],
|
||||
clicked: false,
|
||||
|
||||
@computed("content.originalContent.title", "content.name")
|
||||
title(title, name) {
|
||||
return title || name;
|
||||
},
|
||||
|
||||
@computed("templateForRow")
|
||||
template(templateForRow) { return templateForRow(this); },
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setSelectionState() {
|
||||
const contentValue = this.get("content.value");
|
||||
|
||||
this.set("isSelected", this.get("value") === contentValue);
|
||||
this.set("isHighlighted", this.get("highlightedValue") === 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._sendOnSelectAction();
|
||||
},
|
||||
|
||||
_sendOnSelectAction() {
|
||||
if (this.get("clicked") === false) {
|
||||
this.set("clicked", true);
|
||||
this.sendAction("onSelect", this.get("content.value"));
|
||||
}
|
||||
},
|
||||
|
||||
_sendOnHighlightAction() {
|
||||
this.sendAction("onHighlight", this.get("content.value"));
|
||||
}
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
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",
|
||||
|
||||
selectValueFunction(value) {
|
||||
this.sendAction("action", value);
|
||||
}
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
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();
|
||||
}
|
||||
});
|
|
@ -1,222 +0,0 @@
|
|||
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("mousedown.select-box-kit")
|
||||
.off("touchstart.select-box-kit");
|
||||
|
||||
this.$offscreenInput()
|
||||
.off("focus.select-box-kit")
|
||||
.off("focusin.select-box-kit")
|
||||
.off("blur.select-box-kit")
|
||||
.off("keypress.select-box-kit")
|
||||
.off("keydown.select-box-kit");
|
||||
|
||||
this.$filterInput()
|
||||
.off("change.select-box-kit")
|
||||
.off("keypress.select-box-kit")
|
||||
.off("keydown.select-box-kit");
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
$(document)
|
||||
.on("mousedown.select-box-kit, touchstart.select-box-kit", event => {
|
||||
if (Ember.isNone(this.get("element"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.get("element").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;
|
||||
|
||||
if (keyCode === this.keys.TAB) { this._handleTabOnKeyDown(event); }
|
||||
if (keyCode === this.keys.ESC) { this._handleEscOnKeyDown(event); }
|
||||
if (keyCode === this.keys.UP || keyCode === this.keys.DOWN) {
|
||||
this._handleArrowKey(keyCode, event);
|
||||
}
|
||||
if (keyCode === this.keys.BACKSPACE) {
|
||||
this.expand();
|
||||
|
||||
if (this.$filterInput().is(":visible")) {
|
||||
this.$filterInput().focus().trigger(event).trigger("change");
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.on("keypress.select-box-kit", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
switch (keyCode) {
|
||||
case this.keys.ENTER:
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.expand();
|
||||
} else if (this.$highlightedRow().length === 1) {
|
||||
this.$highlightedRow().click();
|
||||
}
|
||||
return false;
|
||||
case this.keys.BACKSPACE:
|
||||
return event;
|
||||
}
|
||||
|
||||
if (this._isSpecialKey(keyCode) === false && event.metaKey === false) {
|
||||
this.expand();
|
||||
|
||||
if (this.get("filterable") === true || this.get("autoFilterable")) {
|
||||
this.set("renderedFilterOnce", true);
|
||||
}
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$filterInput()
|
||||
.focus()
|
||||
.val(this.$filterInput().val() + String.fromCharCode(keyCode));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.$filterInput()
|
||||
.on("change.select-box-kit", (event) => {
|
||||
this.send("onFilterChange", $(event.target).val());
|
||||
})
|
||||
.on("keydown.select-box-kit", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (keyCode === this.keys.TAB) { this._handleTabOnKeyDown(event); }
|
||||
if (keyCode === this.keys.ESC) { this._handleEscOnKeyDown(event); }
|
||||
if (keyCode === this.keys.UP || keyCode === this.keys.DOWN) {
|
||||
this._handleArrowKey(keyCode, event);
|
||||
}
|
||||
})
|
||||
.on("keypress.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 (keyCode === this.keys.TAB && this.get("isExpanded") === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isSpecialKey(keyCode) === true) {
|
||||
this.$offscreenInput().focus().trigger(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
_handleEscOnKeyDown(event) {
|
||||
this.unfocus();
|
||||
this._killEvent(event);
|
||||
},
|
||||
|
||||
_handleTabOnKeyDown(event) {
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.unfocus();
|
||||
return true;
|
||||
} else if (this.$highlightedRow().length === 1) {
|
||||
this._killEvent(event);
|
||||
this.$highlightedRow().click();
|
||||
this.focus();
|
||||
} else {
|
||||
this.unfocus();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_handleArrowKey(keyCode, event) {
|
||||
if (this.get("isExpanded") === false) { this.expand(); }
|
||||
this._killEvent(event);
|
||||
const $rows = this.$rows();
|
||||
|
||||
if ($rows.length <= 0) { return; }
|
||||
if ($rows.length === 1) {
|
||||
this._rowSelection($rows, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const direction = keyCode === 38 ? -1 : 1;
|
||||
|
||||
Ember.run.throttle(this, this._moveHighlight, direction, $rows, 32);
|
||||
},
|
||||
|
||||
_moveHighlight(direction, $rows) {
|
||||
const currentIndex = $rows.index(this.$highlightedRow());
|
||||
let nextIndex = currentIndex + direction;
|
||||
|
||||
if (nextIndex < 0) {
|
||||
nextIndex = $rows.length - 1;
|
||||
} else if (nextIndex >= $rows.length) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
this._rowSelection($rows, nextIndex);
|
||||
},
|
||||
|
||||
_rowSelection($rows, nextIndex) {
|
||||
const highlightableValue = $rows.eq(nextIndex).attr("data-value");
|
||||
const $highlightableRow = this.$findRowByValue(highlightableValue);
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
$highlightableRow.trigger("mouseover").focus();
|
||||
this.focus();
|
||||
});
|
||||
},
|
||||
|
||||
_isSpecialKey(keyCode) {
|
||||
return _.values(this.keys).includes(keyCode);
|
||||
},
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
{{#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"}}
|
|
@ -1,18 +0,0 @@
|
|||
<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>
|
|
@ -1,13 +0,0 @@
|
|||
<ul class="choices">
|
||||
{{#each selectedContent as |selectedContent|}}
|
||||
{{component selectedNameComponent onDeselect=onDeselect content=selectedContent}}
|
||||
{{/each}}
|
||||
<li class="filter">
|
||||
{{component "select-box-kit/select-box-kit-filter"
|
||||
onFilterChange=onFilterChange
|
||||
shouldDisplayFilter=shouldDisplayFilter
|
||||
isFocused=isFocused
|
||||
filter=filter
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
|
@ -1,9 +0,0 @@
|
|||
<span class="name">
|
||||
{{#unless isLocked}}
|
||||
<span class="delete-icon" {{action onDeselect content.value bubbles=false}}>
|
||||
{{d-icon "times"}}
|
||||
</span>
|
||||
{{/unless}}
|
||||
|
||||
{{content.name}}
|
||||
</span>
|
|
@ -1,7 +0,0 @@
|
|||
{{#if icon}}
|
||||
{{{icon}}}
|
||||
{{/if}}
|
||||
|
||||
<span class="selected-name" title={{selectedName}}>
|
||||
{{{selectedName}}}
|
||||
</span>
|
|
@ -1,9 +0,0 @@
|
|||
{{#if template}}
|
||||
{{{template}}}
|
||||
{{else}}
|
||||
{{#if icon}}
|
||||
{{{icon}}}
|
||||
{{/if}}
|
||||
|
||||
<span class="name">{{content.name}}</span>
|
||||
{{/if}}
|
|
@ -1,21 +1,18 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import DropdownSelectBox from "select-box-kit/components/dropdown-select-box";
|
||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBox.extend({
|
||||
headerText: "admin.flags.agree",
|
||||
headerIcon: "thumbs-o-up",
|
||||
pluginApiIdentifiers: ["admin-agree-flag-dropdown"],
|
||||
classNames: ["agree-flag", "admin-agree-flag-dropdown"],
|
||||
adminTools: Ember.inject.service(),
|
||||
nameProperty: "label",
|
||||
allowInitialValueMutation: false,
|
||||
headerIcon: "thumbs-o-up",
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setAdminAgreeDropdownOptions() {
|
||||
this.get('headerComponentOptions').setProperties({
|
||||
selectedName: `${I18n.t(this.get("headerText"))} ...`,
|
||||
icon: iconHTML("thumbs-o-up")
|
||||
});
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = `${I18n.t("admin.flags.agree")}...`;
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("adminTools", "post.user")
|
||||
|
@ -25,9 +22,10 @@ export default DropdownSelectBox.extend({
|
|||
|
||||
canDeleteSpammer: Ember.computed.and("spammerDetails.canDelete", "post.flaggedForSpam"),
|
||||
|
||||
@computed("post", "canDeleteSpammer")
|
||||
content(post, canDeleteSpammer) {
|
||||
computeContent() {
|
||||
const content = [];
|
||||
const post = this.get("post");
|
||||
const canDeleteSpammer = this.get("canDeleteSpammer");
|
||||
|
||||
if (post.user_deleted) {
|
||||
content.push({
|
||||
|
@ -70,8 +68,9 @@ export default DropdownSelectBox.extend({
|
|||
return content;
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
Ember.get(this._contentForValue(value), "action")();
|
||||
mutateValue(value) {
|
||||
const computedContentItem = this.get("computedContent").findBy("value", value);
|
||||
Ember.get(computedContentItem, "originalContent.action")();
|
||||
},
|
||||
|
||||
actions: {
|
|
@ -1,20 +1,16 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import DropdownSelectBox from "select-box-kit/components/dropdown-select-box";
|
||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default DropdownSelectBox.extend({
|
||||
headerText: "admin.flags.delete",
|
||||
classNames: ["delete-flag", "admin-delete-flag-dropdown"],
|
||||
adminTools: Ember.inject.service(),
|
||||
nameProperty: "label",
|
||||
headerIcon: "trash-o",
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setAdminDeleteDropdownOptions() {
|
||||
this.get('headerComponentOptions').setProperties({
|
||||
selectedName: `${I18n.t(this.get("headerText"))} ...`,
|
||||
icon: iconHTML("trash-o")
|
||||
});
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t("admin.flags.delete");
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("adminTools", "post.user")
|
||||
|
@ -24,9 +20,9 @@ export default DropdownSelectBox.extend({
|
|||
|
||||
canDeleteSpammer: Ember.computed.and("spammerDetails.canDelete", "post.flaggedForSpam"),
|
||||
|
||||
@computed("post", "canDeleteSpammer")
|
||||
content(post, canDeleteSpammer) {
|
||||
computeContent() {
|
||||
const content = [];
|
||||
const canDeleteSpammer = this.get("canDeleteSpammer");
|
||||
|
||||
content.push({
|
||||
icon: "external-link",
|
||||
|
@ -57,8 +53,9 @@ export default DropdownSelectBox.extend({
|
|||
return content;
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
Ember.get(this._contentForValue(value), "action")();
|
||||
mutateValue(value) {
|
||||
const computedContentItem = this.get("computedContent").findBy("value", value);
|
||||
Ember.get(computedContentItem, "originalContent.action")();
|
||||
},
|
||||
|
||||
actions: {
|
|
@ -0,0 +1,51 @@
|
|||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
const { makeArray } = Ember;
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["admin-group-selector"],
|
||||
classNames: "admin-group-selector",
|
||||
selected: null,
|
||||
available: null,
|
||||
allowAny: false,
|
||||
|
||||
computeValues() {
|
||||
return makeArray(this.get("selected"))
|
||||
.map(s => this.valueForContentItem(s));
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
return makeArray(this.get("available"));
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, name) {
|
||||
let computedContent = this.baseComputedContentItem(contentItem, name);
|
||||
computedContent.locked = contentItem.automatic;
|
||||
return computedContent;
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
if (values.length > this.get("selected").length) {
|
||||
const newValues = values
|
||||
.filter(v => !this.get("selected")
|
||||
.map(s => this.valueForContentItem(s))
|
||||
.includes(v));
|
||||
|
||||
newValues.forEach(value => {
|
||||
const actionContext = this.get("available")
|
||||
.findBy(this.get("valueAttribute"), parseInt(value, 10));
|
||||
|
||||
this.triggerAction({ action: "groupAdded", actionContext });
|
||||
});
|
||||
} else if (values.length < this.get("selected").length) {
|
||||
const selected = this.get("selected")
|
||||
.filter(s => !values.includes(this.valueForContentItem(s)));
|
||||
|
||||
selected.forEach(s => {
|
||||
this.triggerAction({
|
||||
action: "groupRemoved",
|
||||
actionContext: this.valueForContentItem(s)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,21 +1,15 @@
|
|||
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";
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["categories-admin-dropdown"],
|
||||
classNames: "categories-admin-dropdown",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
headerIcon: ["bars", "caret-down"],
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComponentOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
||||
shouldDisplaySelectedName: false,
|
||||
icon: `${iconHTML('bars')}${iconHTML('caret-down')}`.htmlSafe(),
|
||||
});
|
||||
},
|
||||
autoHighlight() {},
|
||||
|
||||
@computed
|
||||
content() {
|
||||
computeContent() {
|
||||
const items = [
|
||||
{
|
||||
id: "create",
|
||||
|
@ -38,8 +32,7 @@ export default DropdownSelectBoxComponent.extend({
|
|||
return items;
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
mutateValue(value) {
|
||||
this.get(value)();
|
||||
this.set("value", null);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,92 @@
|
|||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import Category from "discourse/models/category";
|
||||
const { get, isNone, isEmpty } = Ember;
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["category-chooser"],
|
||||
classNames: "category-chooser",
|
||||
filterable: true,
|
||||
castInteger: true,
|
||||
allowUncategorized: false,
|
||||
rowComponent: "category-row",
|
||||
noneRowComponent: "none-category-row",
|
||||
allowSubCategories: true,
|
||||
|
||||
filterComputedContent(computedContent, computedValue, filter) {
|
||||
if (isEmpty(filter)) { return computedContent; }
|
||||
|
||||
const _matchFunction = (f, text) => {
|
||||
return text.toLowerCase().indexOf(f) > -1;
|
||||
};
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
|
||||
return computedContent.filter(c => {
|
||||
const category = Category.findById(get(c, "value"));
|
||||
const text = get(c, "name");
|
||||
if (category && category.get("parentCategory")) {
|
||||
const categoryName = category.get("parentCategory.name");
|
||||
return _matchFunction(lowerFilter, text) || _matchFunction(lowerFilter, categoryName);
|
||||
} else {
|
||||
return _matchFunction(lowerFilter, text);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@computed("rootNone", "rootNoneLabel")
|
||||
none(rootNone, rootNoneLabel) {
|
||||
if (this.siteSettings.allow_uncategorized_topics || this.get("allowUncategorized")) {
|
||||
if (!isNone(rootNone)) {
|
||||
return rootNoneLabel || "category.none";
|
||||
} else {
|
||||
return Category.findUncategorized();
|
||||
}
|
||||
} else {
|
||||
return "category.choose";
|
||||
}
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_bindComposerResizing() {
|
||||
this.appEvents.on("composer:resized", this, this.applyDirection);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_unbindComposerResizing() {
|
||||
this.appEvents.off("composer:resized");
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
|
||||
let scopedCategoryId = this.get("scopedCategoryId");
|
||||
if (scopedCategoryId) {
|
||||
const scopedCat = Category.findById(scopedCategoryId);
|
||||
scopedCategoryId = scopedCat.get("parent_category_id") || scopedCat.get("id");
|
||||
}
|
||||
|
||||
const excludeCategoryId = this.get("excludeCategoryId");
|
||||
|
||||
return categories.filter(c => {
|
||||
const categoryId = this.valueForContentItem(c);
|
||||
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && get(c, "parent_category_id") !== scopedCategoryId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.get("allowSubCategories") === false && c.get("parentCategory") ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((this.get("allowUncategorized") === false && get(c, "isUncategorizedCategory")) || excludeCategoryId === categoryId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get(c, "permission") === PermissionType.FULL;
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["category-notifications-button"],
|
||||
classNames: "category-notifications-button",
|
||||
isHidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
|
||||
i18nPrefix: "category.notifications",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
mutateValue(value) {
|
||||
this.get("category").setNotification(value);
|
||||
},
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) {
|
||||
return [iconForSelectedDetails, "caret-down"];
|
||||
}
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import Category from "discourse/models/category";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/category-row",
|
||||
classNames: "category-row",
|
||||
displayCategoryDescription: true,
|
||||
|
||||
@computed("computedContent.value", "computedContent.name")
|
||||
category(value, name) {
|
||||
if (Ember.isEmpty(value)) {
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get("name") === name) {
|
||||
return uncat;
|
||||
}
|
||||
} else {
|
||||
return Category.findById(parseInt(value, 10));
|
||||
}
|
||||
},
|
||||
|
||||
@computed("category")
|
||||
badgeForCategory(category) {
|
||||
return categoryBadgeHTML(category, {
|
||||
link: false,
|
||||
allowUncategorized: true,
|
||||
hideParent: true
|
||||
}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategory")
|
||||
badgeForParentCategory(parentCategory) {
|
||||
return categoryBadgeHTML(parentCategory, {link: false}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategoryid")
|
||||
parentCategory(parentCategoryId) {
|
||||
return Category.findById(parentCategoryId);
|
||||
},
|
||||
|
||||
@computed("parentCategoryid")
|
||||
hasParentCategory(parentCategoryid) {
|
||||
return !Ember.isNone(parentCategoryid);
|
||||
},
|
||||
|
||||
@computed("category")
|
||||
parentCategoryid(category) {
|
||||
return category.get("parent_category_id");
|
||||
},
|
||||
|
||||
topicCount: Ember.computed.alias("category.topic_count"),
|
||||
|
||||
@computed("options.displayCategoryDescription", "category.description")
|
||||
hasDescription(displayCategoryDescription, description) {
|
||||
return displayCategoryDescription && description && description !== "null";
|
||||
},
|
||||
|
||||
@computed("category.description")
|
||||
description(description) {
|
||||
return `${description.substr(0, 200)}${description.length > 200 ? '…' : ''}`;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["category-selector"],
|
||||
classNames: "category-selector",
|
||||
filterable: true,
|
||||
allowAny: false,
|
||||
rowComponent: "category-row",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: "multi-select/selected-category"
|
||||
}));
|
||||
|
||||
this.set("rowComponentOptions", Ember.Object.create({
|
||||
displayCategoryDescription: false
|
||||
}));
|
||||
},
|
||||
|
||||
computeValues() {
|
||||
return Ember.makeArray(this.get("categories")).map(c => c.id);
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
this.set("categories", values.map(v => Category.findById(v)));
|
||||
},
|
||||
|
||||
filterComputedContent(computedContent, computedValues, filter) {
|
||||
const regex = new RegExp(filter.toLowerCase(), 'i');
|
||||
return computedContent.filter(category => Ember.get(category, "name").match(regex));
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
const blacklist = Ember.makeArray(this.get("blacklist"));
|
||||
return Category.list().filter(category => !blacklist.includes(category));
|
||||
}
|
||||
});
|
|
@ -1,7 +1,8 @@
|
|||
import SelectBoxKitComponent from "select-box-kit/components/select-box-kit";
|
||||
import SingleSelectComponent from "select-kit/components/single-select";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectBoxKitComponent.extend({
|
||||
export default SingleSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["combo-box"],
|
||||
classNames: "combobox combo-box",
|
||||
autoFilterable: true,
|
||||
headerComponent: "combo-box/combo-box-header",
|
||||
|
@ -10,6 +11,12 @@ export default SelectBoxKitComponent.extend({
|
|||
caretDownIcon: "caret-down",
|
||||
clearable: false,
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setComboBoxOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
|
@ -0,0 +1,21 @@
|
|||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/combo-box/combo-box-header",
|
||||
classNames: "combo-box-header",
|
||||
|
||||
clearable: Ember.computed.alias("options.clearable"),
|
||||
caretUpIcon: Ember.computed.alias("options.caretUpIcon"),
|
||||
caretDownIcon: Ember.computed.alias("options.caretDownIcon"),
|
||||
|
||||
@computed("isExpanded", "caretUpIcon", "caretDownIcon")
|
||||
caretIcon(isExpanded, caretUpIcon, caretDownIcon) {
|
||||
return isExpanded === true ? caretUpIcon : caretDownIcon;
|
||||
},
|
||||
|
||||
@computed("clearable", "computedContent.hasSelection")
|
||||
shouldDisplayClearableButton(clearable, hasSelection) {
|
||||
return clearable === true && hasSelection === true;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import SingleSelectComponent from "select-kit/components/single-select";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SingleSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["dropdown-select-box"],
|
||||
classNames: "dropdown-select-box",
|
||||
verticalOffset: 3,
|
||||
fullWidthOnMobile: true,
|
||||
filterable: false,
|
||||
autoFilterable: false,
|
||||
headerComponent: "dropdown-select-box/dropdown-select-box-header",
|
||||
rowComponent: "dropdown-select-box/dropdown-select-box-row",
|
||||
showFullTitle: true,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setDropdownSelectBoxComponentOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
||||
showFullTitle: this.get("showFullTitle")
|
||||
});
|
||||
},
|
||||
|
||||
didClickOutside() {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
this.close();
|
||||
},
|
||||
|
||||
didSelect() {
|
||||
this._super();
|
||||
this.close();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/dropdown-select-box/dropdown-select-box-header",
|
||||
classNames: "dropdown-select-box-header",
|
||||
tagName: "button",
|
||||
|
||||
classNameBindings: ["btnClassName"],
|
||||
|
||||
@computed("options.showFullTitle")
|
||||
btnClassName(showFullTitle) {
|
||||
return `btn ${showFullTitle ? 'btn-icon-text' : 'no-text btn-icon'}`;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/dropdown-select-box/dropdown-select-box-row",
|
||||
classNames: "dropdown-select-box-row",
|
||||
|
||||
name: Ember.computed.alias("computedContent.name"),
|
||||
description: Ember.computed.alias("computedContent.originalContent.description")
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import ComboBoxComponent from "select-box-kit/components/combo-box";
|
||||
import ComboBoxComponent from "select-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";
|
||||
import DatetimeMixin from "select-kit/components/future-date-input-selector/mixin";
|
||||
|
||||
const TIMEFRAME_BASE = {
|
||||
enabled: () => true,
|
||||
|
@ -112,14 +111,30 @@ export function timeframeDetails(id) {
|
|||
export const FORMAT = "YYYY-MM-DD HH:mm";
|
||||
|
||||
export default ComboBoxComponent.extend(DatetimeMixin, {
|
||||
pluginApiIdentifiers: ["future-date-input-selector"],
|
||||
classNames: ["future-date-input-selector"],
|
||||
isCustom: Ember.computed.equal("value", "pick_date_and_time"),
|
||||
clearable: true,
|
||||
rowComponent: "future-date-input-selector/future-date-input-selector-row",
|
||||
headerComponent: "future-date-input-selector/future-date-input-selector-header",
|
||||
|
||||
@computed
|
||||
content() {
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.datetime = this._computeDatetimeForValue(this.get("computedValue"));
|
||||
content.name = this.get("selectedComputedContent.name") || content.name;
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
content.icons = this._computeIconsForValue(this.get("computedValue"));
|
||||
return content;
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, name) {
|
||||
let item = this.baseComputedContentItem(contentItem, name);
|
||||
item.datetime = this._computeDatetimeForValue(contentItem.id);
|
||||
item.icons = this._computeIconsForValue(contentItem.id);
|
||||
return item;
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
let now = moment();
|
||||
let opts = {
|
||||
now,
|
||||
|
@ -138,21 +153,15 @@ export default ComboBoxComponent.extend(DatetimeMixin, {
|
|||
});
|
||||
},
|
||||
|
||||
@observes("value")
|
||||
_updateInput() {
|
||||
mutateValue(value) {
|
||||
if (this.get("isCustom")) return;
|
||||
let input = null;
|
||||
const { time } = this.get("updateAt");
|
||||
const { time } = this._updateAt(value);
|
||||
|
||||
if (time && !Ember.isEmpty(this.get("value"))) {
|
||||
if (time && !Ember.isEmpty(value)) {
|
||||
input = time.format(FORMAT);
|
||||
}
|
||||
|
||||
this.set("input", input);
|
||||
this.setProperties({ input, value });
|
||||
},
|
||||
|
||||
@computed("value")
|
||||
updateAt(value) {
|
||||
return this._updateAt(value);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import ComboBoxHeaderComponent from "select-kit/components/combo-box/combo-box-header";
|
||||
|
||||
export default ComboBoxHeaderComponent.extend({
|
||||
layoutName: "select-kit/templates/components/future-date-input-selector/future-date-input-selector-header",
|
||||
classNames: "future-date-input-selector-header"
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/future-date-input-selector/future-date-input-selector-row",
|
||||
classNames: "future-date-input-selector-row"
|
||||
});
|
|
@ -1,16 +1,15 @@
|
|||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
|
||||
import { timeframeDetails } from 'select-box-kit/components/future-date-input-selector';
|
||||
import { timeframeDetails } from 'select-kit/components/future-date-input-selector';
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
_computeIconForValue(value) {
|
||||
_computeIconsForValue(value) {
|
||||
let {icon} = this._updateAt(value);
|
||||
|
||||
if (icon) {
|
||||
return icon.split(",").map(i => iconHTML(i)).join(" ");
|
||||
return icon.split(",");
|
||||
}
|
||||
|
||||
return null;
|
||||
return [];
|
||||
},
|
||||
|
||||
_computeDatetimeForValue(value) {
|
||||
|
@ -20,7 +19,6 @@ export default Ember.Mixin.create({
|
|||
|
||||
let {time} = this._updateAt(value);
|
||||
if (time) {
|
||||
|
||||
let details = timeframeDetails(value);
|
||||
if (!details.displayWhen) {
|
||||
time = null;
|
||||
|
@ -34,6 +32,7 @@ export default Ember.Mixin.create({
|
|||
|
||||
_updateAt(selection) {
|
||||
let details = timeframeDetails(selection);
|
||||
|
||||
if (details) {
|
||||
return {
|
||||
time: details.when(moment(), this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18),
|
|
@ -1,11 +1,12 @@
|
|||
import NotificationOptionsComponent from "select-box-kit/components/notifications-button";
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["grouo-notifications-button"],
|
||||
classNames: ["group-notifications-button"],
|
||||
value: Ember.computed.alias("group.group_user.notification_level"),
|
||||
i18nPrefix: "groups.notifications",
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
selectValueFunction(value) {
|
||||
mutateValue(value) {
|
||||
this.get("group").setNotification(value, this.get("user.id"));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
import MultiSelectComponent from "select-kit/components/multi-select";
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["list-setting"],
|
||||
classNames: "list-setting",
|
||||
tokenSeparator: "|",
|
||||
settingValue: "",
|
||||
choices: null,
|
||||
filterable: true,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (!Ember.isNone(this.get("settingName"))) {
|
||||
this.set("nameProperty", this.get("settingName"));
|
||||
}
|
||||
|
||||
if (this.get("nameProperty").indexOf("color") > -1) {
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: "multi-select/selected-color"
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
computeContent() {
|
||||
let content;
|
||||
if (Ember.isNone(this.get("choices"))) {
|
||||
content = this.get("settingValue").split(this.get("tokenSeparator"));;
|
||||
} else {
|
||||
content = this.get("choices");
|
||||
}
|
||||
|
||||
return Ember.makeArray(content).filter(c => c);
|
||||
},
|
||||
|
||||
mutateValues(values) {
|
||||
this.set("settingValue", values.join(this.get("tokenSeparator")));
|
||||
},
|
||||
|
||||
computeValues() {
|
||||
return this.get("settingValue")
|
||||
.split(this.get("tokenSeparator"))
|
||||
.filter(c => c);
|
||||
},
|
||||
|
||||
_handleTabOnKeyDown(event) {
|
||||
if (this.$highlightedRow().length === 1) {
|
||||
this._super(event);
|
||||
} else {
|
||||
this.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,255 @@
|
|||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone, isEmpty, makeArray } = Ember;
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["multi-select"],
|
||||
classNames: "multi-select",
|
||||
headerComponent: "multi-select/multi-select-header",
|
||||
filterComponent: null,
|
||||
headerText: "select_kit.default_header_text",
|
||||
allowAny: true,
|
||||
allowInitialValueMutation: false,
|
||||
autoFilterable: true,
|
||||
selectedNameComponent: "multi-select/selected-name",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.set("computedValues", []);
|
||||
if (isNone(this.get("values"))) { this.set("values", []); }
|
||||
|
||||
this.set("headerComponentOptions", Ember.Object.create({
|
||||
selectedNameComponent: this.get("selectedNameComponent")
|
||||
}));
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_setChoicesMaxWidth() {
|
||||
const width = this.$body().outerWidth(false);
|
||||
this.$(".choices").css({ maxWidth: width, width });
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_compute() {
|
||||
Ember.run.scheduleOnce("afterRender", () => {
|
||||
this.willComputeAttributes();
|
||||
let content = this._beforeWillComputeContent(this.get("content"));
|
||||
content = this.willComputeContent(content);
|
||||
let values = this._beforeWillComputeValues(this.get("values"));
|
||||
content = this.computeContent(content);
|
||||
content = this._beforeDidComputeContent(content);
|
||||
values = this.willComputeValues(values);
|
||||
values = this.computeValues(values);
|
||||
values = this._beforeDidComputeValues(values);
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
this.didComputeContent(content);
|
||||
this.didComputeValues(values);
|
||||
this.didComputeAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
@computed("filter", "shouldDisplayCreateRow")
|
||||
createRowComputedContent(filter, shouldDisplayCreateRow) {
|
||||
if (shouldDisplayCreateRow === true) {
|
||||
let content = this.createContentFromInput(filter);
|
||||
return this.computeContentItem(content, { created: true });
|
||||
}
|
||||
},
|
||||
|
||||
@computed("filter", "computedValues")
|
||||
shouldDisplayCreateRow(filter, computedValues) {
|
||||
return this._super() && !computedValues.includes(filter);
|
||||
},
|
||||
|
||||
_beforeWillComputeValues(values) {
|
||||
return values.map(v => this._castInteger(v === "" ? null : v));
|
||||
},
|
||||
willComputeValues(values) { return values; },
|
||||
computeValues(values) { return values; },
|
||||
_beforeDidComputeValues(values) {
|
||||
this.setProperties({ computedValues: values });
|
||||
return values;
|
||||
},
|
||||
didComputeValues(values) { return values; },
|
||||
|
||||
mutateAttributes() {
|
||||
Ember.run.next(() => {
|
||||
this.mutateContent(this.get("computedContent"));
|
||||
this.mutateValues(this.get("computedValues"));
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
});
|
||||
},
|
||||
mutateValues(computedValues) { this.set("values", computedValues); },
|
||||
|
||||
filterComputedContent(computedContent, computedValues, filter) {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("computedContent.[]", "computedValues.[]", "filter")
|
||||
filteredComputedContent(computedContent, computedValues, filter) {
|
||||
computedContent = computedContent.filter(c => {
|
||||
return !computedValues.includes(get(c, "value"));
|
||||
});
|
||||
|
||||
if (this.get("shouldFilter") === true) {
|
||||
computedContent = this.filterComputedContent(computedContent, computedValues, filter);
|
||||
}
|
||||
|
||||
return computedContent.slice(0, this.get("limitMatches"));
|
||||
},
|
||||
|
||||
baseHeaderComputedContent() {
|
||||
return {
|
||||
selectedComputedContents: this.get("selectedComputedContents")
|
||||
};
|
||||
},
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return I18n.t("select_kit.create", { content: rowComponent.get("computedContent.name")});
|
||||
};
|
||||
},
|
||||
|
||||
didPressBackspace(event) {
|
||||
this.expand();
|
||||
this.keyDown(event);
|
||||
this._destroyEvent(event);
|
||||
},
|
||||
|
||||
didPressEscape(event) {
|
||||
const $highlighted = this.$(".selected-name.is-highlighted");
|
||||
if ($highlighted.length > 0) {
|
||||
$highlighted.removeClass("is-highlighted");
|
||||
}
|
||||
|
||||
this._super(event);
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
if (!isEmpty(this.get("filter"))) return;
|
||||
|
||||
const keyCode = event.keyCode || event.which;
|
||||
const $filterInput = this.$filterInput();
|
||||
|
||||
// select all choices
|
||||
if (this.get("hasSelection") && event.metaKey === true && keyCode === 65) {
|
||||
this.$(".choices .selected-name:not(.is-locked)").addClass("is-highlighted");
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear selection when multiple
|
||||
if (this.$(".selected-name.is-highlighted").length >= 1 && keyCode === this.keys.BACKSPACE) {
|
||||
const highlightedComputedContents = [];
|
||||
$.each(this.$(".selected-name.is-highlighted"), (i, el) => {
|
||||
const computedContent = this._findComputedContentItemByGuid($(el).attr("data-guid"));
|
||||
if (!Ember.isNone(computedContent)) { highlightedComputedContents.push(computedContent); }
|
||||
});
|
||||
this.send("onDeselect", highlightedComputedContents);
|
||||
return;
|
||||
}
|
||||
|
||||
// try to remove last item from the list
|
||||
if (keyCode === this.keys.BACKSPACE) {
|
||||
let $lastSelectedValue = $(this.$(".choices .selected-name:not(.is-locked)").last());
|
||||
|
||||
if ($lastSelectedValue.length === 0) { return; }
|
||||
|
||||
if ($filterInput.not(":visible") && $lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($filterInput.val() === "") {
|
||||
if ($filterInput.is(":focus")) {
|
||||
if ($lastSelectedValue.length > 0) { $lastSelectedValue.click(); }
|
||||
} else {
|
||||
if ($lastSelectedValue.length > 0) {
|
||||
$lastSelectedValue.click();
|
||||
} else {
|
||||
$filterInput.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed("computedValues.[]", "computedContent.[]")
|
||||
selectedComputedContents(computedValues, computedContent) {
|
||||
const selected = [];
|
||||
computedValues.forEach(v => selected.push(computedContent.findBy("value", v)) );
|
||||
return selected;
|
||||
},
|
||||
|
||||
@computed("selectedComputedContents.[]")
|
||||
hasSelection(selectedComputedContents) { return !Ember.isEmpty(selectedComputedContents); },
|
||||
|
||||
autoHighlight() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (this.get("isExpanded") === false) { return; }
|
||||
if (this.get("renderedBodyOnce") === false) { return; }
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
if (isEmpty(this.get("filteredComputedContent"))) {
|
||||
if (this.get("createRowComputedContent")) {
|
||||
this.send("onHighlight", this.get("createRowComputedContent"));
|
||||
} else if (this.get("noneRowComputedContent") && this.get("hasSelection") === true) {
|
||||
this.send("onHighlight", this.get("noneRowComputedContent"));
|
||||
}
|
||||
} else {
|
||||
this.send("onHighlight", this.get("filteredComputedContent.firstObject"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
didSelect() {
|
||||
this.focus();
|
||||
this.autoHighlight();
|
||||
},
|
||||
|
||||
didDeselect() {
|
||||
this.focus();
|
||||
this.autoHighlight();
|
||||
},
|
||||
|
||||
validateComputedContentItem(computedContentItem) {
|
||||
return !this.get("computedValues").includes(computedContentItem.value);
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClear() {
|
||||
this.get("selectedComputedContents").forEach(selectedComputedContent => {
|
||||
this.send("onDeselect", selectedComputedContent);
|
||||
});
|
||||
},
|
||||
|
||||
onCreate(computedContentItem) {
|
||||
if (this.validateComputedContentItem(computedContentItem)) {
|
||||
this.get("computedContent").pushObject(computedContentItem);
|
||||
this.send("onSelect", computedContentItem);
|
||||
}
|
||||
},
|
||||
|
||||
onSelect(computedContentItem) {
|
||||
this.willSelect(computedContentItem);
|
||||
this.get("computedValues").pushObject(computedContentItem.value);
|
||||
Ember.run.next(() => this.mutateAttributes());
|
||||
Ember.run.schedule("afterRender", () => this.didSelect(computedContentItem));
|
||||
},
|
||||
|
||||
onDeselect(rowComputedContentItems) {
|
||||
rowComputedContentItems = Ember.makeArray(rowComputedContentItems);
|
||||
const generatedComputedContents = this._filterRemovableComputedContents(makeArray(rowComputedContentItems));
|
||||
this.willDeselect(rowComputedContentItems);
|
||||
this.get("computedValues").removeObjects(rowComputedContentItems.map(r => r.value));
|
||||
this.get("computedContent").removeObjects(generatedComputedContents);
|
||||
Ember.run.next(() => this.mutateAttributes());
|
||||
Ember.run.schedule("afterRender", () => this.didDeselect(rowComputedContentItems));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,11 +1,11 @@
|
|||
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";
|
||||
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
|
||||
|
||||
export default SelectBoxKitHeaderComponent.extend({
|
||||
export default SelectKitHeaderComponent.extend({
|
||||
attributeBindings: ["names:data-name"],
|
||||
classNames: "multi-combo-box-header",
|
||||
layoutName: "select-box-kit/templates/components/multi-combo-box/multi-combo-box-header",
|
||||
classNames: "multi-select-header",
|
||||
layoutName: "select-kit/templates/components/multi-select/multi-select-header",
|
||||
selectedNameComponent: Ember.computed.alias("options.selectedNameComponent"),
|
||||
|
||||
@on("didRender")
|
||||
|
@ -25,6 +25,8 @@ export default SelectBoxKitHeaderComponent.extend({
|
|||
$filter.width(availableSpace - parentRightPadding * 4);
|
||||
},
|
||||
|
||||
@computed("selectedContent.[]")
|
||||
names(selectedContent) { return selectedContent.map(sc => sc.name).join(","); }
|
||||
@computed("computedContent.selectedComputedContents.[]")
|
||||
names(selectedComputedContents) {
|
||||
return Ember.makeArray(selectedComputedContents).map(sc => sc.name).join(",");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
import SelectedNameComponent from "select-kit/components/multi-select/selected-name";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
export default SelectedNameComponent.extend({
|
||||
classNames: "selected-category",
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-category",
|
||||
|
||||
@computed("content.originalContent")
|
||||
badge(category) {
|
||||
return categoryBadgeHTML(category, {allowUncategorized: true, link: false}).htmlSafe();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import SelectedNameComponent from "select-kit/components/multi-select/selected-name";
|
||||
|
||||
export default SelectedNameComponent.extend({
|
||||
classNames: "selected-color",
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-color",
|
||||
|
||||
didRender() {
|
||||
const name = this.get("content.name");
|
||||
this.$(".color-preview").css("background", `#${name}`.htmlSafe());
|
||||
}
|
||||
});
|
|
@ -1,18 +1,27 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
attributeBindings: ["tabindex","content.name:data-name", "content.value:data-value"],
|
||||
classNames: "selected-name",
|
||||
attributeBindings: [
|
||||
"tabindex",
|
||||
"content.name:data-name",
|
||||
"content.value:data-value",
|
||||
"guid:data-guid"
|
||||
],
|
||||
classNames: ["selected-name", "choice"],
|
||||
classNameBindings: ["isHighlighted", "isLocked"],
|
||||
layoutName: "select-box-kit/templates/components/multi-combo-box/selected-name",
|
||||
layoutName: "select-kit/templates/components/multi-select/selected-name",
|
||||
tagName: "li",
|
||||
tabindex: -1,
|
||||
|
||||
@computed("content")
|
||||
guid(content) { return Ember.guidFor(content); },
|
||||
|
||||
isLocked: Ember.computed("content.locked", function() {
|
||||
return this.getWithDefault("content.locked", false);
|
||||
}),
|
||||
|
||||
click() {
|
||||
if (this.get("isLocked") === true) { return false; }
|
||||
|
||||
this.toggleProperty("isHighlighted");
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import CategoryRowComponent from "select-kit/components/category-row";
|
||||
|
||||
export default CategoryRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/category-row",
|
||||
classNames: "none category-row",
|
||||
|
||||
click() {
|
||||
this.sendAction("onClear");
|
||||
}
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import DropdownSelectBoxComponent from "select-box-kit/components/dropdown-select-box";
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import { default as computed, on } from "ember-addons/ember-computed-decorators";
|
||||
import { buttonDetails } from "discourse/lib/notification-levels";
|
||||
import { allLevels } from "discourse/lib/notification-levels";
|
||||
|
@ -9,24 +9,29 @@ export default DropdownSelectBoxComponent.extend({
|
|||
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",
|
||||
|
||||
allowInitialValueMutation: false,
|
||||
i18nPrefix: "",
|
||||
i18nPostfix: "",
|
||||
showFullTitle: true,
|
||||
|
||||
@on("didReceiveAttrs", "didUpdateAttrs")
|
||||
_setComponentOptions() {
|
||||
this.get("headerComponentOptions").setProperties({
|
||||
i18nPrefix: this.get("i18nPrefix"),
|
||||
showFullTitle: this.get("showFullTitle"),
|
||||
});
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) { return iconForSelectedDetails; },
|
||||
|
||||
@computed("selectedDetails.icon")
|
||||
iconForSelectedDetails(icon) { return icon; },
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t(`${this.get("i18nPrefix")}.${this.get("selectedDetails.key")}.title`);
|
||||
content.hasSelection = this.get("hasSelection");
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setNotificationsButtonComponentOptions() {
|
||||
this.get("rowComponentOptions").setProperties({
|
||||
i18nPrefix: this.get("i18nPrefix"),
|
||||
i18nPostfix: this.get("i18nPostfix")
|
|
@ -1,4 +1,4 @@
|
|||
import DropdownSelectBoxRoxComponent from "select-box-kit/components/dropdown-select-box/dropdown-select-box-row";
|
||||
import DropdownSelectBoxRoxComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
|
||||
import { buttonDetails } from "discourse/lib/notification-levels";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
@ -9,13 +9,13 @@ export default DropdownSelectBoxRoxComponent.extend({
|
|||
i18nPrefix: Ember.computed.alias("options.i18nPrefix"),
|
||||
i18nPostfix: Ember.computed.alias("options.i18nPostfix"),
|
||||
|
||||
@computed("content.value", "i18nPrefix")
|
||||
@computed("computedContent.value", "i18nPrefix")
|
||||
title(value, prefix) {
|
||||
const key = buttonDetails(value).key;
|
||||
return I18n.t(`${prefix}.${key}.title`);
|
||||
},
|
||||
|
||||
@computed("content.name", "content.originalContent.icon")
|
||||
@computed("computedContent.name", "computedContent.originalContent.icon")
|
||||
icon(contentName, icon) {
|
||||
return iconHTML(icon, { class: contentName.dasherize() });
|
||||
},
|
||||
|
@ -30,7 +30,7 @@ export default DropdownSelectBoxRoxComponent.extend({
|
|||
return Handlebars.escapeExpression(I18n.t(`${_start}.title`));
|
||||
},
|
||||
|
||||
@computed("i18nPrefix", "i18nPostfix", "content.name")
|
||||
@computed("i18nPrefix", "i18nPostfix", "computedContent.name")
|
||||
_start(prefix, postfix, contentName) {
|
||||
return `${prefix}.${contentName}${postfix}`;
|
||||
},
|
|
@ -1,12 +1,13 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
pluginApiIdentifiers: ["pinned-button"],
|
||||
descriptionKey: "help",
|
||||
classNames: "pinned-button",
|
||||
classNameBindings: ["isHidden"],
|
||||
layoutName: "select-box-kit/templates/components/pinned-button",
|
||||
layoutName: "select-kit/templates/components/pinned-button",
|
||||
|
||||
@computed("topic.pinned_globally", "topic.pinned")
|
||||
@computed("topic.pinned_globally", "pinned")
|
||||
reasonText(pinnedGlobally, pinned) {
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const pinnedKey = pinned ? `pinned${globally}` : "unpinned";
|
||||
|
@ -14,7 +15,7 @@ export default Ember.Component.extend({
|
|||
return I18n.t(key);
|
||||
},
|
||||
|
||||
@computed("topic.pinned", "topic.deleted", "topic.unpinned")
|
||||
@computed("pinned", "topic.deleted", "topic.unpinned")
|
||||
isHidden(pinned, deleted, unpinned) {
|
||||
return deleted || (!pinned && !unpinned);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["pinned-options"],
|
||||
classNames: "pinned-options",
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
autoHighlight() {},
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
const pinnedGlobally = this.get("topic.pinned_globally");
|
||||
const pinned = this.get("computedValue");
|
||||
const globally = pinnedGlobally ? "_globally" : "";
|
||||
const state = pinned ? `pinned${globally}` : "unpinned";
|
||||
const title = I18n.t(`topic_statuses.${state}.title`);
|
||||
|
||||
content.name = `${title}${iconHTML("caret-down")}`.htmlSafe();
|
||||
content.dataName = title;
|
||||
content.icon = `thumb-tack ${state === "unpinned" ? "unpinned" : null}`;
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("init")
|
||||
_setContent() {
|
||||
const globally = this.get("topic.pinned_globally") ? "_globally" : "";
|
||||
|
||||
this.set("content", [
|
||||
{
|
||||
id: "pinned",
|
||||
name: I18n.t("topic_statuses.pinned" + globally + ".title"),
|
||||
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
|
||||
icon: "thumb-tack"
|
||||
},
|
||||
{
|
||||
id: "unpinned",
|
||||
name: I18n.t("topic_statuses.unpinned.title"),
|
||||
icon: "thumb-tack unpinned",
|
||||
description: I18n.t('topic_statuses.unpinned.help'),
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
mutateValue(value) {
|
||||
const topic = this.get("topic");
|
||||
|
||||
if (value === "unpinned") {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import CategoryChooserComponent from "select-kit/components/category-chooser";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default CategoryChooserComponent.extend({
|
||||
pluginApiIdentifiers: ["advanced-search-category-chooser"],
|
||||
rootNone: true,
|
||||
rootNoneLabel: "category.all",
|
||||
allowUncategorized: true,
|
||||
clearable: true,
|
||||
|
||||
mutateValue(value) {
|
||||
if (value) {
|
||||
this.set("value", Category.findById(value));
|
||||
} else {
|
||||
this.set("value", null);
|
||||
}
|
||||
},
|
||||
|
||||
computeValue(category) { if (category) return category.id; }
|
||||
});
|
|
@ -0,0 +1,239 @@
|
|||
const { isNone, run, makeArray } = Ember;
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import UtilsMixin from "select-kit/mixins/utils";
|
||||
import DomHelpersMixin from "select-kit/mixins/dom-helpers";
|
||||
import EventsMixin from "select-kit/mixins/events";
|
||||
import PluginApiMixin from "select-kit/mixins/plugin-api";
|
||||
import { applyContentPluginApiCallbacks } from "select-kit/mixins/plugin-api";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixin, EventsMixin, {
|
||||
pluginApiIdentifiers: ["select-kit"],
|
||||
layoutName: "select-kit/templates/components/select-kit",
|
||||
classNames: ["select-kit", "select-box-kit"],
|
||||
classNameBindings: [
|
||||
"isFocused",
|
||||
"isExpanded",
|
||||
"isDisabled",
|
||||
"isHidden",
|
||||
"isAbove",
|
||||
"isBelow",
|
||||
"isLeftAligned",
|
||||
"isRightAligned"
|
||||
],
|
||||
isDisabled: false,
|
||||
isExpanded: false,
|
||||
isFocused: false,
|
||||
isHidden: false,
|
||||
renderedBodyOnce: false,
|
||||
renderedFilterOnce: false,
|
||||
tabindex: 0,
|
||||
scrollableParentSelector: ".modal-body",
|
||||
none: null,
|
||||
highlightedValue: null,
|
||||
noContentLabel: "select_kit.no_content",
|
||||
valueAttribute: "id",
|
||||
nameProperty: "name",
|
||||
autoFilterable: false,
|
||||
filterable: false,
|
||||
filter: "",
|
||||
filterPlaceholder: "select_kit.filter_placeholder",
|
||||
filterIcon: "search",
|
||||
headerIcon: null,
|
||||
rowComponent: "select-kit/select-kit-row",
|
||||
rowComponentOptions: null,
|
||||
noneRowComponent: "select-kit/select-kit-none-row",
|
||||
createRowComponent: "select-kit/select-kit-create-row",
|
||||
filterComponent: "select-kit/select-kit-filter",
|
||||
headerComponent: "select-kit/select-kit-header",
|
||||
headerComponentOptions: null,
|
||||
headerComputedContent: null,
|
||||
collectionComponent: "select-kit/select-kit-collection",
|
||||
collectionHeight: 200,
|
||||
verticalOffset: 0,
|
||||
horizontalOffset: 0,
|
||||
fullWidthOnMobile: false,
|
||||
castInteger: false,
|
||||
allowAny: false,
|
||||
allowInitialValueMutation: false,
|
||||
content: null,
|
||||
computedContent: null,
|
||||
limitMatches: 100,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.noneValue = "__none__";
|
||||
this._previousScrollParentOverflow = "auto";
|
||||
this._previousCSSContext = {};
|
||||
this.set("headerComponentOptions", Ember.Object.create());
|
||||
this.set("rowComponentOptions", Ember.Object.create());
|
||||
this.set("computedContent", []);
|
||||
|
||||
if ($(window).outerWidth(false) <= 420) {
|
||||
this.setProperties({ filterable: false, autoFilterable: false });
|
||||
}
|
||||
},
|
||||
|
||||
willComputeAttributes() {},
|
||||
didComputeAttributes() {},
|
||||
|
||||
_beforeWillComputeContent(content) { return makeArray(content); },
|
||||
willComputeContent(content) { return content; },
|
||||
computeContent(content) { return content; },
|
||||
_beforeDidComputeContent(content) {
|
||||
content = applyContentPluginApiCallbacks(this.get("pluginApiIdentifiers"), content);
|
||||
|
||||
const existingCreatedComputedContent = this.get("computedContent").filterBy("created", true);
|
||||
this.setProperties({
|
||||
computedContent: content.map(c => this.computeContentItem(c)).concat(existingCreatedComputedContent)
|
||||
});
|
||||
return content;
|
||||
},
|
||||
didComputeContent() {},
|
||||
|
||||
mutateAttributes() {
|
||||
run.next(() => {
|
||||
this.mutateContent(this.get("computedContent"));
|
||||
this.mutateValue(this.get("computedValue"));
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
});
|
||||
},
|
||||
mutateContent() {},
|
||||
mutateValue(computedValue) { this.set("value", computedValue); },
|
||||
|
||||
computeHeaderContent() {
|
||||
return this.baseHeaderComputedContent();
|
||||
},
|
||||
|
||||
computeContentItem(contentItem, options) {
|
||||
return this.baseComputedContentItem(contentItem, options);
|
||||
},
|
||||
|
||||
baseComputedContentItem(contentItem, options) {
|
||||
let originalContent;
|
||||
options = options || {};
|
||||
const name = options.name;
|
||||
|
||||
if (typeof contentItem === "string" || typeof contentItem === "number") {
|
||||
originalContent = {};
|
||||
originalContent[this.get("valueAttribute")] = contentItem;
|
||||
originalContent[this.get("nameProperty")] = name || contentItem;
|
||||
} else {
|
||||
originalContent = contentItem;
|
||||
}
|
||||
|
||||
return {
|
||||
value: this._castInteger(this.valueForContentItem(contentItem)),
|
||||
name: name || this._nameForContent(contentItem),
|
||||
locked: false,
|
||||
created: options.created || false,
|
||||
originalContent
|
||||
};
|
||||
},
|
||||
|
||||
@computed("shouldFilter", "allowAny", "filter")
|
||||
shouldDisplayFilter(shouldFilter, allowAny, filter) {
|
||||
if (shouldFilter === true) return true;
|
||||
if (allowAny === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "filteredComputedContent.[]")
|
||||
shouldDisplayNoContentRow(filter, filteredComputedContent) {
|
||||
return filter.length > 0 && filteredComputedContent.length === 0;
|
||||
},
|
||||
|
||||
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
|
||||
shouldFilter(filter, filterable, autoFilterable, renderedFilterOnce) {
|
||||
if (renderedFilterOnce === true && filterable === true) return true;
|
||||
if (filterable === true) return true;
|
||||
if (autoFilterable === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "computedContent")
|
||||
shouldDisplayCreateRow(filter, computedContent) {
|
||||
if (computedContent.map(c => c.value).includes(filter)) return false;
|
||||
if (this.get("allowAny") === true && filter.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("filter", "shouldDisplayCreateRow")
|
||||
createRowComputedContent(filter, shouldDisplayCreateRow) {
|
||||
if (shouldDisplayCreateRow === true) {
|
||||
let content = this.createContentFromInput(filter);
|
||||
return this.computeContentItem(content, { created: true });
|
||||
}
|
||||
},
|
||||
|
||||
@computed
|
||||
templateForRow() { return () => null; },
|
||||
|
||||
@computed
|
||||
templateForNoneRow() { return () => null; },
|
||||
|
||||
@computed("filter")
|
||||
templateForCreateRow() {
|
||||
return (rowComponent) => {
|
||||
return I18n.t("select_box.create", {
|
||||
content: rowComponent.get("computedContent.name")
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@computed("none")
|
||||
noneRowComputedContent(none) {
|
||||
if (isNone(none)) { return null; }
|
||||
|
||||
switch (typeof none) {
|
||||
case "string":
|
||||
return this.computeContentItem(this.noneValue, { name: I18n.t(none) });
|
||||
default:
|
||||
return this.computeContentItem(none);
|
||||
}
|
||||
},
|
||||
|
||||
createContentFromInput(input) { return input; },
|
||||
|
||||
willSelect() {
|
||||
this.clearFilter();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
didSelect() {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
},
|
||||
|
||||
willDeselect() {
|
||||
this.clearFilter();
|
||||
this.set("highlightedValue", null);
|
||||
},
|
||||
didDeselect() {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
},
|
||||
|
||||
clearFilter() {
|
||||
this.$filterInput().val("");
|
||||
this.setProperties({ filter: "" });
|
||||
},
|
||||
|
||||
actions: {
|
||||
onToggle() {
|
||||
this.get("isExpanded") === true ? this.collapse() : this.expand();
|
||||
},
|
||||
|
||||
onHighlight(rowComputedContent) {
|
||||
this.set("highlightedValue", rowComputedContent.value);
|
||||
},
|
||||
|
||||
onFilter(filter) {
|
||||
this.setProperties({
|
||||
highlightedValue: null,
|
||||
renderedFilterOnce: true,
|
||||
filter
|
||||
});
|
||||
this.autoHighlight();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-collection",
|
||||
classNames: ["select-kit-collection", "select-box-kit-collection"],
|
||||
tagName: "ul"
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: "create",
|
||||
|
||||
click() {
|
||||
this.sendAction("onCreate", this.get("computedContent"));
|
||||
},
|
||||
});
|
|
@ -1,6 +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",
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-filter",
|
||||
classNames: ["select-kit-filter", "select-box-kit-filter"],
|
||||
classNameBindings: ["isFocused", "isHidden"],
|
||||
isHidden: Ember.computed.not("shouldDisplayFilter")
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-header",
|
||||
classNames: ["select-kit-header", "select-box-kit-header"],
|
||||
classNameBindings: ["isFocused"],
|
||||
attributeBindings: [
|
||||
"dataName:data-name",
|
||||
"tabindex",
|
||||
"ariaLabel:aria-label",
|
||||
"ariaHasPopup:aria-haspopup",
|
||||
"title"
|
||||
],
|
||||
|
||||
ariaHasPopup: true,
|
||||
|
||||
ariaLabel: Ember.computed.alias("title"),
|
||||
|
||||
name: Ember.computed.alias("computedContent.name"),
|
||||
|
||||
@computed("computedContent.icon", "computedContent.icons")
|
||||
icons(icon, icons) {
|
||||
return Ember.makeArray(icon).concat(icons).filter(i => !Ember.isEmpty(i));
|
||||
},
|
||||
|
||||
@computed("computedContent.dataName", "name")
|
||||
dataName(dataName, name) { return dataName || name; },
|
||||
|
||||
@computed("computedContent.title", "name")
|
||||
title(title, name) { return title || name; },
|
||||
|
||||
click() {
|
||||
this.sendAction("onToggle");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||
|
||||
export default SelectKitRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: "none",
|
||||
|
||||
click() {
|
||||
this.sendAction("onClear");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
const { run, isPresent, makeArray, isEmpty } = Ember;
|
||||
import UtilsMixin from "select-kit/mixins/utils";
|
||||
|
||||
export default Ember.Component.extend(UtilsMixin, {
|
||||
layoutName: "select-kit/templates/components/select-kit/select-kit-row",
|
||||
classNames: ["select-kit-row", "select-box-kit-row"],
|
||||
tagName: "li",
|
||||
tabIndex: -1,
|
||||
attributeBindings: [
|
||||
"tabIndex",
|
||||
"title",
|
||||
"computedContent.value:data-value",
|
||||
"computedContent.name:data-name"
|
||||
],
|
||||
classNameBindings: ["isHighlighted", "isSelected"],
|
||||
|
||||
@computed("computedContent.title", "computedContent.name")
|
||||
title(title, name) { return title || name; },
|
||||
|
||||
@computed("templateForRow")
|
||||
template(templateForRow) { return templateForRow(this); },
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setSelectionState() {
|
||||
const contentValue = this.get("computedContent.value");
|
||||
|
||||
this.set("isSelected", this.get("computedValue") === contentValue);
|
||||
this.set("isHighlighted", this.get("highlightedValue") === contentValue);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_clearDebounce() {
|
||||
const hoverDebounce = this.get("hoverDebounce");
|
||||
if (isPresent(hoverDebounce)) { run.cancel(hoverDebounce); }
|
||||
},
|
||||
|
||||
@computed("computedContent.icon", "computedContent.icons", "computedContent.originalContent.icon")
|
||||
icons(icon, icons, originalIcon) {
|
||||
return makeArray(icon)
|
||||
.concat(icons)
|
||||
.concat(makeArray(originalIcon))
|
||||
.filter(i => !isEmpty(i));
|
||||
},
|
||||
|
||||
mouseEnter() {
|
||||
this.set("hoverDebounce", run.debounce(this, this._sendOnHighlightAction, 32));
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("onSelect", this.get("computedContent"));
|
||||
},
|
||||
|
||||
_sendOnHighlightAction() {
|
||||
this.sendAction("onHighlight", this.get("computedContent"));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,168 @@
|
|||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
const { get, isNone, isEmpty, isPresent } = Ember;
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["single-select"],
|
||||
classNames: "single-select",
|
||||
computedValue: null,
|
||||
value: null,
|
||||
allowInitialValueMutation: true,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
if (this.get("allowInitialValueMutation") === true) {
|
||||
const none = isNone(this.get("none"));
|
||||
const emptyValue = isEmpty(this.get("value"));
|
||||
if (none && emptyValue) {
|
||||
if (!isEmpty(this.get("content"))) {
|
||||
const value = this.valueForContentItem(this.get("content.firstObject"));
|
||||
Ember.run.next(() => this.mutateValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_compute() {
|
||||
Ember.run.scheduleOnce("afterRender", () => {
|
||||
this.willComputeAttributes();
|
||||
let content = this._beforeWillComputeContent(this.get("content"));
|
||||
content = this.willComputeContent(content);
|
||||
let value = this._beforeWillComputeValue(this.get("value"));
|
||||
content = this.computeContent(content);
|
||||
content = this._beforeDidComputeContent(content);
|
||||
value = this.willComputeValue(value);
|
||||
value = this.computeValue(value);
|
||||
value = this._beforeDidComputeValue(value);
|
||||
this.didComputeContent(content);
|
||||
this.didComputeValue(value);
|
||||
this.set("headerComputedContent", this.computeHeaderContent());
|
||||
this.didComputeAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
_beforeWillComputeValue(value) {
|
||||
switch (typeof value) {
|
||||
case "string":
|
||||
case "number":
|
||||
return this._castInteger(value === "" ? null : value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
},
|
||||
willComputeValue(value) { return value; },
|
||||
computeValue(value) { return value; },
|
||||
_beforeDidComputeValue(value) {
|
||||
if (!isEmpty(this.get("content")) && isNone(value) && isNone(this.get("none"))) {
|
||||
value = this.valueForContentItem(get(this.get("content"), "firstObject"));
|
||||
}
|
||||
|
||||
this.setProperties({ computedValue: value });
|
||||
return value;
|
||||
},
|
||||
didComputeValue(value) { return value; },
|
||||
|
||||
filterComputedContent(computedContent, computedValue, filter) {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
return computedContent.filter(c => {
|
||||
return get(c, "name").toLowerCase().indexOf(lowerFilter) > -1;
|
||||
});
|
||||
},
|
||||
|
||||
baseHeaderComputedContent() {
|
||||
return {
|
||||
icons: Ember.makeArray(this.getWithDefault("headerIcon", [])),
|
||||
name: this.get("selectedComputedContent.name") || this.get("noneRowComputedContent.name")
|
||||
};
|
||||
},
|
||||
|
||||
@computed("computedContent.[]", "computedValue", "filter", "shouldFilter")
|
||||
filteredComputedContent(computedContent, computedValue, filter, shouldFilter) {
|
||||
if (shouldFilter === true) {
|
||||
computedContent = this.filterComputedContent(computedContent, computedValue, filter);
|
||||
}
|
||||
|
||||
return computedContent.slice(0, this.get("limitMatches"));
|
||||
},
|
||||
|
||||
@computed("computedValue", "computedContent.[]")
|
||||
selectedComputedContent(computedValue, computedContent) {
|
||||
if (isNone(computedValue) || isNone(computedContent)) { return null; }
|
||||
return computedContent.findBy("value", computedValue);
|
||||
},
|
||||
|
||||
@computed("selectedComputedContent")
|
||||
hasSelection(selectedComputedContent) {
|
||||
return selectedComputedContent !== this.get("noneRowComputedContent") &&
|
||||
!Ember.isNone(selectedComputedContent);
|
||||
},
|
||||
|
||||
@computed("filter", "computedValue")
|
||||
shouldDisplayCreateRow(filter, computedValue) {
|
||||
return this._super() && computedValue !== filter;
|
||||
},
|
||||
|
||||
autoHighlight() {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (!isNone(this.get("highlightedValue"))) { return; }
|
||||
|
||||
const filteredComputedContent = this.get("filteredComputedContent");
|
||||
const displayCreateRow = this.get("shouldDisplayCreateRow");
|
||||
const none = this.get("noneRowComputedContent");
|
||||
|
||||
if (this.get("hasSelection") && isEmpty(this.get("filter"))) {
|
||||
this.send("onHighlight", this.get("selectedComputedContent"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNone(this.get("highlightedValue")) && !isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", get(filteredComputedContent, "firstObject"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (displayCreateRow === true && isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", this.get("createRowComputedContent"));
|
||||
}
|
||||
else if (!isEmpty(filteredComputedContent)) {
|
||||
this.send("onHighlight", get(filteredComputedContent, "firstObject"));
|
||||
}
|
||||
else if (isEmpty(filteredComputedContent) && isPresent(none) && displayCreateRow === false) {
|
||||
this.send("onHighlight", none);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
validateComputedContentItem(computedContentItem) {
|
||||
return this.get("computedValue") !== computedContentItem.value;
|
||||
},
|
||||
|
||||
actions: {
|
||||
onClear() {
|
||||
this.send("onDeselect", this.get("selectedComputedContent"));
|
||||
},
|
||||
|
||||
onCreate(computedContentItem) {
|
||||
if (this.validateComputedContentItem(computedContentItem)) {
|
||||
this.get("computedContent").pushObject(computedContentItem);
|
||||
this.send("onSelect", computedContentItem);
|
||||
}
|
||||
},
|
||||
|
||||
onSelect(rowComputedContentItem) {
|
||||
this.willSelect(rowComputedContentItem);
|
||||
this.set("computedValue", rowComputedContentItem.value);
|
||||
this.mutateAttributes();
|
||||
Ember.run.schedule("afterRender", () => this.didSelect(rowComputedContentItem));
|
||||
},
|
||||
|
||||
onDeselect(rowComputedContentItem) {
|
||||
this.willDeselect(rowComputedContentItem);
|
||||
this.set("computedValue", null);
|
||||
this.mutateAttributes();
|
||||
Ember.run.schedule("afterRender", () => this.didDeselect(rowComputedContentItem));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["tag-notifications-button"],
|
||||
classNames: "tag-notifications-button",
|
||||
i18nPrefix: "tagging.notifications",
|
||||
showFullTitle: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
mutateValue(value) {
|
||||
this.sendAction("action", value);
|
||||
},
|
||||
|
||||
computeValue() {
|
||||
return this.get("notificationLevel");
|
||||
},
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) {
|
||||
return [iconForSelectedDetails, "caret-down"];
|
||||
}
|
||||
});
|
|
@ -1,24 +1,21 @@
|
|||
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";
|
||||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
headerText: "topic.controls",
|
||||
pluginApiIdentifiers: ["topic-footer-mobile-dropdown"],
|
||||
classNames: "topic-footer-mobile-dropdown",
|
||||
filterable: false,
|
||||
autoFilterable: false,
|
||||
allowValueMutation: false,
|
||||
autoSelectFirst: false,
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setTopicFooterMobileDropdownOptions() {
|
||||
this.get("headerComponentOptions")
|
||||
.set("selectedName", I18n.t(this.get("headerText")));
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
content.name = I18n.t("topic.controls");
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("topic", "topic.details", "value")
|
||||
content(topic, details) {
|
||||
const content = [];
|
||||
computeContent(content) {
|
||||
const topic = this.get("topic");
|
||||
const details = topic.get("details");
|
||||
|
||||
if (details.get("can_invite_to")) {
|
||||
content.push({ id: "invite", icon: "users", name: I18n.t("topic.invite_reply.title") });
|
||||
|
@ -39,16 +36,15 @@ export default ComboBoxComponent.extend({
|
|||
return content;
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
autoHighlight() {},
|
||||
|
||||
mutateValue(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.send("onDeselect", value);
|
||||
|
||||
switch(value) {
|
|
@ -1,5 +1,5 @@
|
|||
export default Ember.Component.extend({
|
||||
layoutName: "select-box-kit/templates/components/topic-notifications-button",
|
||||
layoutName: "select-kit/templates/components/topic-notifications-button",
|
||||
classNames: "topic-notifications-button",
|
||||
showFullTitle: true,
|
||||
appendReason: true
|
|
@ -1,12 +1,13 @@
|
|||
import NotificationOptionsComponent from "select-box-kit/components/notifications-button";
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { topicLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
pluginApiIdentifiers: ["topic-notifications-options"],
|
||||
classNames: "topic-notifications-options",
|
||||
content: topicLevels,
|
||||
i18nPrefix: "topic.notifications",
|
||||
value: Ember.computed.alias("topic.details.notification_level"),
|
||||
allowInitialValueMutation: false,
|
||||
|
||||
@on("didInsertElement")
|
||||
_bindGlobalLevelChanged() {
|
||||
|
@ -24,11 +25,9 @@ export default NotificationOptionsComponent.extend({
|
|||
this.appEvents.off("topic-notifications-button:changed");
|
||||
},
|
||||
|
||||
selectValueFunction(value) {
|
||||
mutateValue(value) {
|
||||
if (value !== this.get("value")) {
|
||||
this.get("topic.details").updateNotifications(value);
|
||||
}
|
||||
|
||||
this.set("value", value);
|
||||
}
|
||||
});
|
|
@ -4,13 +4,12 @@ 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";
|
||||
this.wrapperSelector = ".select-box-kit-wrapper";
|
||||
this.filterInputSelector = ".filter-input";
|
||||
this.rowSelector = ".select-kit-row";
|
||||
this.collectionSelector = ".select-kit-collection";
|
||||
this.headerSelector = ".select-kit-header";
|
||||
this.bodySelector = ".select-kit-body";
|
||||
this.wrapperSelector = ".select-kit-wrapper";
|
||||
},
|
||||
|
||||
$findRowByValue(value) { return this.$(`${this.rowSelector}[data-value='${value}']`); },
|
||||
|
@ -34,45 +33,50 @@ export default Ember.Mixin.create({
|
|||
|
||||
$selectedRow() { return this.$rows().filter(".is-selected"); },
|
||||
|
||||
$offscreenInput() { return this.$(this.offscreenInputSelector); },
|
||||
|
||||
$filterInput() { return this.$(this.filterInputSelector); },
|
||||
|
||||
@on("didRender")
|
||||
_ajustPosition() {
|
||||
$(`.select-box-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
this.$collection().css("max-height", this.get("collectionHeight"));
|
||||
this._applyFixedPosition();
|
||||
this._applyDirection();
|
||||
this._positionWrapper();
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_setupResizeListener() {
|
||||
$(window).on("resize.select-kit", () => this.collapse() );
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_clearState() {
|
||||
$(window).off("resize.select-box-kit");
|
||||
$(`.select-box-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
$(window).off("resize.select-kit");
|
||||
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
},
|
||||
|
||||
// make sure we don’t propagate a click outside component
|
||||
// to avoid closing a modal containing the component for example
|
||||
click(event) { this._killEvent(event); },
|
||||
click(event) {
|
||||
this._destroyEvent(event);
|
||||
},
|
||||
|
||||
// use to collapse and remove focus
|
||||
close() {
|
||||
this.collapse();
|
||||
close(event) {
|
||||
this.collapse(event);
|
||||
this.setProperties({ isFocused: false });
|
||||
},
|
||||
|
||||
// force the component in a known default state
|
||||
focus() {
|
||||
Ember.run.schedule("afterRender", () => this.$offscreenInput().select() );
|
||||
Ember.run.schedule("afterRender", () => this.$header().focus() );
|
||||
},
|
||||
|
||||
expand() {
|
||||
if (this.get("isExpanded") === true) { return; }
|
||||
expand(event) {
|
||||
if (this.get("isExpanded") === true) return;
|
||||
this.setProperties({ isExpanded: true, renderedBodyOnce: true, isFocused: true });
|
||||
this.focus();
|
||||
this.autoHighlightFunction();
|
||||
this.focus(event);
|
||||
this.autoHighlight();
|
||||
},
|
||||
|
||||
collapse() {
|
||||
|
@ -80,35 +84,18 @@ export default Ember.Mixin.create({
|
|||
Ember.run.schedule("afterRender", () => this._removeFixedPosition() );
|
||||
},
|
||||
|
||||
// make sure we close/unfocus the component when clicked outside
|
||||
clickOutside(event) {
|
||||
if ($(event.target).parents(".select-box-kit").length === 1) {
|
||||
this.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.unfocus();
|
||||
return;
|
||||
},
|
||||
|
||||
// lose focus of the component in two steps
|
||||
// first collapase and keep focus and then remove focus
|
||||
unfocus() {
|
||||
this.set("highlightedValue", null);
|
||||
|
||||
// first collapse and keep focus and then remove focus
|
||||
unfocus(event) {
|
||||
if (this.get("isExpanded") === true) {
|
||||
this.collapse();
|
||||
this.focus();
|
||||
this.collapse(event);
|
||||
this.focus(event);
|
||||
} else {
|
||||
this.close();
|
||||
this.close(event);
|
||||
}
|
||||
},
|
||||
|
||||
blur() {
|
||||
Ember.run.schedule("afterRender", () => this.$offscreenInput().blur() );
|
||||
},
|
||||
|
||||
_killEvent(event) {
|
||||
_destroyEvent(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
@ -119,8 +106,8 @@ export default Ember.Mixin.create({
|
|||
const dHeader = $(".d-header")[0];
|
||||
const dHeaderBounds = dHeader ? dHeader.getBoundingClientRect() : {top: 0, height: 0};
|
||||
const dHeaderHeight = dHeaderBounds.top + dHeaderBounds.height;
|
||||
const headerHeight = this.$header().outerHeight(false);
|
||||
const headerWidth = this.$header().outerWidth(false);
|
||||
const componentHeight = this.$().outerHeight(false);
|
||||
const componentWidth = this.$().outerWidth(false);
|
||||
const bodyHeight = this.$body().outerHeight(false);
|
||||
const windowWidth = $(window).width();
|
||||
const windowHeight = $(window).height();
|
||||
|
@ -145,7 +132,7 @@ export default Ember.Mixin.create({
|
|||
options.left = bodyWidth + this.get("horizontalOffset");
|
||||
} else {
|
||||
this.setProperties({ isLeftAligned: false, isRightAligned: true });
|
||||
options.right = - (bodyWidth - headerWidth + this.get("horizontalOffset"));
|
||||
options.right = - (bodyWidth - componentWidth + this.get("horizontalOffset"));
|
||||
}
|
||||
} else {
|
||||
const horizontalSpacing = boundingRect.left;
|
||||
|
@ -160,15 +147,15 @@ export default Ember.Mixin.create({
|
|||
}
|
||||
}
|
||||
|
||||
const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight;
|
||||
const hasBelowSpace = windowHeight - offsetBottom - componentHeight > 0;
|
||||
const hasAboveSpace = offsetTop - componentHeight - dHeaderHeight > 0;
|
||||
const fullHeight = this.get("verticalOffset") + bodyHeight + componentHeight;
|
||||
const hasBelowSpace = windowHeight - offsetBottom - fullHeight > 0;
|
||||
const hasAboveSpace = offsetTop - fullHeight - dHeaderHeight > 0;
|
||||
if (hasBelowSpace || (!hasBelowSpace && !hasAboveSpace)) {
|
||||
this.setProperties({ isBelow: true, isAbove: false });
|
||||
options.top = headerHeight + this.get("verticalOffset");
|
||||
options.top = componentHeight + this.get("verticalOffset") - 2;
|
||||
} else {
|
||||
this.setProperties({ isBelow: false, isAbove: true });
|
||||
options.bottom = headerHeight + this.get("verticalOffset");
|
||||
options.bottom = componentHeight + this.get("verticalOffset") - 1;
|
||||
}
|
||||
|
||||
this.$body().css(options);
|
||||
|
@ -182,7 +169,7 @@ export default Ember.Mixin.create({
|
|||
|
||||
const width = this.$().outerWidth(false);
|
||||
const height = this.$().outerHeight(false);
|
||||
const $placeholder = $(`<div class='select-box-kit-fixed-placeholder-${this.elementId}'></div>`);
|
||||
const $placeholder = $(`<div class='select-kit-fixed-placeholder-${this.elementId}'></div>`);
|
||||
|
||||
this._previousScrollParentOverflow = this._previousScrollParentOverflow || scrollableParent.css("overflow");
|
||||
scrollableParent.css({ overflow: "hidden" });
|
||||
|
@ -212,7 +199,7 @@ export default Ember.Mixin.create({
|
|||
},
|
||||
|
||||
_removeFixedPosition() {
|
||||
$(`.select-box-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
|
||||
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
|
||||
|
||||
|
@ -236,11 +223,11 @@ export default Ember.Mixin.create({
|
|||
},
|
||||
|
||||
_positionWrapper() {
|
||||
const headerHeight = this.$header().outerHeight(false);
|
||||
const componentHeight = this.$().outerHeight(false);
|
||||
|
||||
this.$(this.wrapperSelector).css({
|
||||
width: this.$().outerWidth(false),
|
||||
height: headerHeight + this.$body().outerHeight(false)
|
||||
width: this.$().outerWidth(false) - 2,
|
||||
height: componentHeight + this.$body().outerHeight(false)
|
||||
});
|
||||
},
|
||||
});
|
|
@ -0,0 +1,233 @@
|
|||
export default Ember.Mixin.create({
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this.keys = {
|
||||
TAB: 9,
|
||||
ENTER: 13,
|
||||
ESC: 27,
|
||||
UP: 38,
|
||||
DOWN: 40,
|
||||
BACKSPACE: 8,
|
||||
};
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
|
||||
$(document)
|
||||
.off("mousedown.select-kit")
|
||||
.off("touchstart.select-kit");
|
||||
|
||||
this.$header()
|
||||
.off("focus.select-kit")
|
||||
.off("blur.select-kit")
|
||||
.off("keypress.select-kit")
|
||||
.off("keydown.select-kit");
|
||||
|
||||
this.$filterInput()
|
||||
.off("change.select-kit")
|
||||
.off("keydown.select-kit")
|
||||
.off("focus.select-kit")
|
||||
.off("focusin.select-kit");
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
$(document)
|
||||
.on("mousedown.select-kit, touchstart.select-kit", event => {
|
||||
if (Ember.isNone(this.get("element"))) return;
|
||||
if (this.get("element").contains(event.target)) return;
|
||||
|
||||
this.didClickOutside(event);
|
||||
});
|
||||
|
||||
this.$header()
|
||||
.on("blur.select-kit", () => {
|
||||
if (this.get("isExpanded") === false && this.get("isFocused") === true) {
|
||||
this.close();
|
||||
}
|
||||
})
|
||||
.on("focus.select-kit", (event) => {
|
||||
this.set("isFocused", true);
|
||||
this._destroyEvent(event);
|
||||
})
|
||||
.on("keydown.select-kit", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (document.activeElement !== this.$header()[0]) return event;
|
||||
|
||||
if (keyCode === this.keys.TAB) this.tabFromHeader(event);
|
||||
if (keyCode === this.keys.BACKSPACE) this.backspaceFromHeader(event);
|
||||
if (keyCode === this.keys.ESC) this.escapeFromHeader(event);
|
||||
if (keyCode === this.keys.ENTER) this.enterFromHeader(event);
|
||||
if ([this.keys.UP, this.keys.DOWN].includes(keyCode)) this.upAndDownFromHeader(event);
|
||||
return event;
|
||||
})
|
||||
.on("keypress.select-kit", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (keyCode === this.keys.ENTER) { return true; }
|
||||
|
||||
this.expand(event);
|
||||
|
||||
if (this.get("filterable") === true || this.get("autoFilterable")) {
|
||||
this.set("renderedFilterOnce", true);
|
||||
}
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
let newVal = this.$filterInput().val();
|
||||
|
||||
const start = this.$filterInput()[0].selectionStart;
|
||||
const end = this.$filterInput()[0].selectionEnd;
|
||||
if (!Ember.isNone(start) && !Ember.isNone(end)) {
|
||||
newVal = newVal.substr(0, start) +
|
||||
String.fromCharCode(keyCode) +
|
||||
newVal.substr(end, newVal.length);
|
||||
} else {
|
||||
newVal = newVal + String.fromCharCode(keyCode);
|
||||
}
|
||||
|
||||
this.$filterInput().focus().val(newVal);
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$filterInput()
|
||||
.on("change.select-kit", (event) => {
|
||||
this.send("onFilter", $(event.target).val());
|
||||
})
|
||||
.on("focus.select-kit focusin.select-kit", (event) => {
|
||||
this.set("isFocused", true);
|
||||
this._destroyEvent(event);
|
||||
})
|
||||
.on("keydown.select-kit", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
||||
if (keyCode === this.keys.TAB) this.tabFromFilter(event);
|
||||
if (keyCode === this.keys.ESC) this.escapeFromFilter(event);
|
||||
if (keyCode === this.keys.ENTER) this.enterFromFilter(event);
|
||||
if ([this.keys.UP, this.keys.DOWN].includes(keyCode)) this.upAndDownFromFilter(event);
|
||||
});
|
||||
},
|
||||
|
||||
didPressTab(event) {
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.unfocus(event);
|
||||
} else if (this.$highlightedRow().length === 1) {
|
||||
this._destroyEvent(event);
|
||||
Ember.run.throttle(this, this._rowClick, this.$highlightedRow(), 150, 150, true);
|
||||
this.focus(event);
|
||||
} else {
|
||||
this._destroyEvent(event);
|
||||
this.unfocus(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
didPressEscape(event) {
|
||||
this._destroyEvent(event);
|
||||
this.unfocus(event);
|
||||
},
|
||||
|
||||
didPressUpAndDownArrows(event) {
|
||||
this._destroyEvent(event);
|
||||
|
||||
const keyCode = event.keyCode || event.which;
|
||||
const $rows = this.$rows();
|
||||
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.expand(event);
|
||||
|
||||
if (this.$selectedRow().length === 1) {
|
||||
this._highlightRow(this.$selectedRow());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($rows.length <= 0) { return; }
|
||||
if ($rows.length === 1) {
|
||||
this._rowSelection($rows, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const direction = keyCode === 38 ? -1 : 1;
|
||||
|
||||
Ember.run.throttle(this, this._moveHighlight, direction, $rows, 32);
|
||||
},
|
||||
|
||||
didPressBackspace(event) {
|
||||
this._destroyEvent(event);
|
||||
|
||||
this.expand(event);
|
||||
|
||||
if (this.$filterInput().is(":visible")) {
|
||||
this.$filterInput().focus().trigger(event).trigger("change");
|
||||
}
|
||||
},
|
||||
|
||||
didPressEnter(event) {
|
||||
this._destroyEvent(event);
|
||||
|
||||
if (this.get("isExpanded") === false) {
|
||||
this.expand(event);
|
||||
} else if (this.$highlightedRow().length === 1) {
|
||||
Ember.run.throttle(this, this._rowClick, this.$highlightedRow(), 150, true);
|
||||
}
|
||||
},
|
||||
|
||||
didClickOutside(event) {
|
||||
if ($(event.target).parents(".select-kit").length === 1) {
|
||||
this.close(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.unfocus(event);
|
||||
return;
|
||||
},
|
||||
|
||||
tabFromHeader(event) { this.didPressTab(event); },
|
||||
tabFromFilter(event) { this.didPressTab(event); },
|
||||
|
||||
escapeFromHeader(event) { this.didPressEscape(event); },
|
||||
escapeFromFilter(event) { this.didPressEscape(event); },
|
||||
|
||||
upAndDownFromHeader(event) { this.didPressUpAndDownArrows(event); },
|
||||
upAndDownFromFilter(event) { this.didPressUpAndDownArrows(event); },
|
||||
|
||||
backspaceFromHeader(event) { this.didPressBackspace(event); },
|
||||
|
||||
enterFromHeader(event) { this.didPressEnter(event); },
|
||||
enterFromFilter(event) { this.didPressEnter(event); },
|
||||
|
||||
_moveHighlight(direction, $rows) {
|
||||
const currentIndex = $rows.index(this.$highlightedRow());
|
||||
let nextIndex = currentIndex + direction;
|
||||
|
||||
if (nextIndex < 0) {
|
||||
nextIndex = $rows.length - 1;
|
||||
} else if (nextIndex >= $rows.length) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
this._rowSelection($rows, nextIndex);
|
||||
},
|
||||
|
||||
_rowClick($row) { $row.click(); },
|
||||
|
||||
_rowSelection($rows, nextIndex) {
|
||||
const highlightableValue = $rows.eq(nextIndex).attr("data-value");
|
||||
const $highlightableRow = this.$findRowByValue(highlightableValue);
|
||||
this._highlightRow($highlightableRow);
|
||||
},
|
||||
|
||||
_highlightRow($row) {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
$row.trigger("mouseover").focus();
|
||||
this.focus();
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
let _appendContentCallbacks = {};
|
||||
function appendContent(pluginApiIdentifiers, contentFunction) {
|
||||
if (Ember.isNone(_appendContentCallbacks[pluginApiIdentifiers])) {
|
||||
_appendContentCallbacks[pluginApiIdentifiers] = [];
|
||||
}
|
||||
|
||||
_appendContentCallbacks[pluginApiIdentifiers].push(contentFunction);
|
||||
}
|
||||
|
||||
let _prependContentCallbacks = {};
|
||||
function prependContent(pluginApiIdentifiers, contentFunction) {
|
||||
if (Ember.isNone(_prependContentCallbacks[pluginApiIdentifiers])) {
|
||||
_prependContentCallbacks[pluginApiIdentifiers] = [];
|
||||
}
|
||||
|
||||
_prependContentCallbacks[pluginApiIdentifiers].push(contentFunction);
|
||||
}
|
||||
|
||||
let _modifyContentCallbacks = {};
|
||||
function modifyContent(pluginApiIdentifiers, contentFunction) {
|
||||
if (Ember.isNone(_modifyContentCallbacks[pluginApiIdentifiers])) {
|
||||
_modifyContentCallbacks[pluginApiIdentifiers] = [];
|
||||
}
|
||||
|
||||
_modifyContentCallbacks[pluginApiIdentifiers].push(contentFunction);
|
||||
}
|
||||
|
||||
export function applyContentPluginApiCallbacks(identifiers, content) {
|
||||
identifiers.forEach((key) => {
|
||||
(_prependContentCallbacks[key] || []).forEach((c) => {
|
||||
content = c().concat(content);
|
||||
});
|
||||
(_appendContentCallbacks[key] || []).forEach((c) => {
|
||||
content = content.concat(c());
|
||||
});
|
||||
(_modifyContentCallbacks[key] || []).forEach((c) => {
|
||||
content = c(content);
|
||||
});
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
export function modifySelectKit(pluginApiIdentifiers) {
|
||||
return {
|
||||
appendContent: (content) => {
|
||||
appendContent(pluginApiIdentifiers, () => {return content;} );
|
||||
return modifySelectKit(pluginApiIdentifiers);
|
||||
},
|
||||
prependContent: (content) => {
|
||||
prependContent(pluginApiIdentifiers, () => {return content;} );
|
||||
return modifySelectKit(pluginApiIdentifiers);
|
||||
},
|
||||
modifyContent: (callback) => {
|
||||
modifyContent(pluginApiIdentifiers, callback);
|
||||
return modifySelectKit(pluginApiIdentifiers);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function clearCallbacks() {
|
||||
_appendContentCallbacks = {};
|
||||
_prependContentCallbacks = {};
|
||||
_modifyContentCallbacks = {};
|
||||
}
|
||||
|
||||
const EMPTY_ARRAY = Object.freeze([]);
|
||||
export default Ember.Mixin.create({
|
||||
concatenatedProperties: ["pluginApiIdentifiers"],
|
||||
pluginApiIdentifiers: EMPTY_ARRAY
|
||||
});
|
|
@ -1,6 +1,16 @@
|
|||
const { get, isNone } = Ember;
|
||||
const { get, isNone, guidFor } = Ember;
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
valueForContentItem(content) {
|
||||
switch (typeof content) {
|
||||
case "string":
|
||||
case "number":
|
||||
return content;
|
||||
default:
|
||||
return get(content, this.get("valueAttribute"));
|
||||
}
|
||||
},
|
||||
|
||||
_nameForContent(content) {
|
||||
if (isNone(content)) {
|
||||
return null;
|
||||
|
@ -25,37 +35,13 @@ export default Ember.Mixin.create({
|
|||
return value;
|
||||
},
|
||||
|
||||
_valueForContent(content) {
|
||||
switch (typeof content) {
|
||||
case "string":
|
||||
case "number":
|
||||
return content;
|
||||
default:
|
||||
return get(content, this.get("valueAttribute"));
|
||||
}
|
||||
},
|
||||
|
||||
_contentForValue(value) {
|
||||
return this.get("content").find(c => {
|
||||
if (this._valueForContent(c) === value) { return true; }
|
||||
});
|
||||
},
|
||||
|
||||
_computedContentForValue(value) {
|
||||
const searchedValue = value.toString();
|
||||
_findComputedContentItemByGuid(guid) {
|
||||
return this.get("computedContent").find(c => {
|
||||
if (c.value.toString() === searchedValue) { return true; }
|
||||
return guidFor(c) === guid;
|
||||
});
|
||||
},
|
||||
|
||||
_originalValueForValue(value) {
|
||||
if (isNone(value)) { return null; }
|
||||
if (value === this.noneValue) { return this.noneValue; }
|
||||
|
||||
const computedContent = this._computedContentForValue(value);
|
||||
|
||||
if (isNone(computedContent)) { return value; }
|
||||
|
||||
return get(computedContent.originalContent, this.get("valueAttribute"));
|
||||
},
|
||||
_filterRemovableComputedContents(computedContent) {
|
||||
return computedContent.filter(c => c.created === true);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
{{#if category}}
|
||||
{{#if hasParentCategory}}
|
||||
<div class="category-status">
|
||||
{{badgeForParentCategory}} {{badgeForCategory}}
|
||||
<span class="topic-count">× {{topicCount}}</span>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="category-status">
|
||||
{{badgeForCategory}}
|
||||
<span class="topic-count">× {{topicCount}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasDescription}}
|
||||
<div class="category-desc">{{description}}</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{computedContent.name}}
|
||||
{{/if}}
|
|
@ -0,0 +1,13 @@
|
|||
{{#each icons as |icon|}} {{d-icon icon}} {{/each}}
|
||||
|
||||
<span class="selected-name" title={{title}}>
|
||||
{{{name}}}
|
||||
</span>
|
||||
|
||||
{{#if shouldDisplayClearableButton}}
|
||||
<button class="btn-clear" {{action onClear bubbles=false}}>
|
||||
{{d-icon 'times'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
|
@ -0,0 +1,7 @@
|
|||
{{#each icons as |icon|}} {{d-icon icon}} {{/each}}
|
||||
|
||||
{{#if options.showFullTitle}}
|
||||
<span class="d-button-label selected-name">
|
||||
{{name}}
|
||||
</span>
|
||||
{{/if}}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue