DEV: select-kit 2 (#7998)

This new iteration of select-kit focuses on following best principales and disallowing mutations inside select-kit components. A best effort has been made to avoid breaking changes, however if you content was a flat array, eg: ["foo", "bar"] You will need to set valueProperty=null and nameProperty=null on the component.

Also almost every component should have an `onChange` handler now to decide what to do with the updated data. **select-kit will not mutate your data by itself anymore**
This commit is contained in:
Joffrey JAFFEUX 2020-02-03 14:22:14 +01:00 committed by GitHub
parent 0e2cbee339
commit 0431942f3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
278 changed files with 7566 additions and 6957 deletions

View File

@ -21,13 +21,11 @@ export default Component.extend({
actions: {
penaltyChanged() {
let postAction = this.postAction;
// If we switch to edit mode, jump to the edit textarea
if (postAction === "edit") {
if (this.postAction === "edit") {
scheduleOnce("afterRender", () => {
let elem = this.element;
let body = elem.closest(".modal-body");
const elem = this.element;
const body = elem.closest(".modal-body");
body.scrollTop(body.height());
elem.querySelector(".post-editor").focus();
});

View File

@ -20,6 +20,19 @@ export default Component.extend({
];
},
didInsertElement() {
this._super(...arguments);
schedule("afterRender", () => {
$(this.element.querySelector(".external-url")).keydown(e => {
// enter key
if (e.keyCode === 13) {
this.send("submit");
}
});
});
},
focusPermalink() {
schedule("afterRender", () =>
this.element.querySelector(".permalink-url").focus()
@ -64,19 +77,10 @@ export default Component.extend({
}
);
}
},
onChangePermalinkType(type) {
this.set("permalinkType", type);
}
},
didInsertElement() {
this._super(...arguments);
schedule("afterRender", () => {
$(this.element.querySelector(".external-url")).keydown(e => {
// enter key
if (e.keyCode === 13) {
this.send("submit");
}
});
});
}
});

View File

@ -1,16 +1,15 @@
import discourseComputed from "discourse-common/utils/decorators";
import Component from "@ember/component";
import Category from "discourse/models/category";
import { computed } from "@ember/object";
export default Component.extend({
@discourseComputed("value")
selectedCategories: {
get(value) {
return Category.findByIds(value.split("|"));
},
set(value) {
this.set("value", value.mapBy("id").join("|"));
return value;
selectedCategories: computed("value", function() {
return Category.findByIds(this.value.split("|").filter(Boolean));
}),
actions: {
onChangeSelectedCategories(value) {
this.set("value", (value || []).mapBy("id").join("|"));
}
}
});

View File

@ -0,0 +1,43 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import { makeArray } from "discourse-common/lib/helpers";
export default Component.extend({
tokenSeparator: "|",
createdChoices: null,
settingValue: computed("value", function() {
return this.value
.toString()
.split(this.tokenSeparator)
.filter(Boolean);
}),
settingChoices: computed(
"settingValue",
"setting.choices.[]",
"createdChoices.[]",
function() {
return [
...new Set([
...makeArray(this.settingValue),
...makeArray(this.setting.choices),
...makeArray(this.createdChoices)
])
];
}
),
actions: {
onChangeListSetting(value) {
this.set("value", value.join(this.tokenSeparator));
},
onChangeChoices(choices) {
this.set("createdChoices", [
...new Set([...makeArray(this.createdChoices), ...makeArray(choices)])
]);
}
}
});

View File

@ -1,11 +1,25 @@
import discourseComputed from "discourse-common/utils/decorators";
import { computed } from "@ember/object";
import Component from "@ember/component";
export default Component.extend({
@discourseComputed()
groupChoices() {
return this.site.get("groups").map(g => {
tokenSeparator: "|",
nameProperty: "name",
valueProperty: "id",
groupChoices: computed("site.groups", function() {
return (this.site.groups || []).map(g => {
return { name: g.name, id: g.id.toString() };
});
}),
settingValue: computed("value", function() {
return (this.value || "").split(this.tokenSeparator).filter(Boolean);
}),
actions: {
onChangeGroupListSetting(value) {
this.set("value", value.join(this.tokenSeparator));
}
}
});

View File

@ -1,20 +1,18 @@
import discourseComputed from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
import { empty, alias } from "@ember/object/computed";
import { empty, reads } from "@ember/object/computed";
import Component from "@ember/component";
import { on } from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [":value-list"],
inputInvalid: empty("newValue"),
inputDelimiter: null,
inputType: null,
newValue: "",
collection: null,
values: null,
noneKey: alias("addKey"),
noneKey: reads("addKey"),
@on("didReceiveAttrs")
_setupCollection() {
@ -47,7 +45,7 @@ export default Component.extend({
addValue(newValue) {
if (this.inputInvalid) return;
this.set("newValue", "");
this.set("newValue", null);
this._addValue(newValue);
},
@ -62,12 +60,26 @@ export default Component.extend({
_addValue(value) {
this.collection.addObject(value);
if (this.choices) {
this.set("choices", this.choices.rejectBy("id", value));
} else {
this.set("choices", []);
}
this._saveValues();
},
_removeValue(value) {
const collection = this.collection;
collection.removeObject(value);
this.collection.removeObject(value);
const item = { id: value, name: value };
if (this.choices) {
this.choices.addObject(item);
} else {
this.set("choices", makeArray(item));
}
this._saveValues();
},

View File

@ -1,5 +1,5 @@
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { alias } from "@ember/object/computed";
import { reads } from "@ember/object/computed";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import { popupAjaxError } from "discourse/lib/ajax-error";
@ -10,15 +10,37 @@ export default Controller.extend(bufferedProperty("model"), {
adminBadges: inject(),
saving: false,
savingStatus: "",
badgeTypes: alias("adminBadges.badgeTypes"),
badgeGroupings: alias("adminBadges.badgeGroupings"),
badgeTriggers: alias("adminBadges.badgeTriggers"),
protectedSystemFields: alias("adminBadges.protectedSystemFields"),
readOnly: alias("buffered.system"),
badgeTypes: reads("adminBadges.badgeTypes"),
badgeGroupings: reads("adminBadges.badgeGroupings"),
badgeTriggers: reads("adminBadges.badgeTriggers"),
protectedSystemFields: reads("adminBadges.protectedSystemFields"),
readOnly: reads("buffered.system"),
showDisplayName: propertyNotEqual("name", "displayName"),
init() {
this._super(...arguments);
// this is needed because the model doesnt have default values
// and as we are using a bufferedProperty it's not accessible
// in any other way
Ember.run.next(() => {
if (!this.model.badge_type_id) {
this.model.set("badge_type_id", this.get("badgeTypes.firstObject.id"));
}
if (!this.model.badge_grouping_id) {
this.model.set(
"badge_grouping_id",
this.get("badgeGroupings.firstObject.id")
);
}
if (!this.model.trigger) {
this.model.set("trigger", this.get("badgeTriggers.firstObject.id"));
}
});
},
@discourseComputed("model.query", "buffered.query")
hasQuery(modelQuery, bufferedQuery) {
if (bufferedQuery) {

View File

@ -278,7 +278,7 @@ export default Controller.extend(CanCheckEmails, {
},
resetCustomGroups() {
this.set("customGroupIdsBuffer", null);
this.set("customGroupIdsBuffer", this.model.customGroups.mapBy("id"));
},
savePrimaryGroup() {

View File

@ -5,6 +5,17 @@ import ModalFunctionality from "discourse/mixins/modal-functionality";
export default Controller.extend(ModalFunctionality, {
adminCustomizeColors: inject(),
selectedBaseThemeId: null,
init() {
this._super(...arguments);
const defaultScheme = this.get(
"adminCustomizeColors.baseColorSchemes.0.base_scheme_id"
);
this.set("selectedBaseThemeId", defaultScheme);
},
actions: {
selectBase() {
this.adminCustomizeColors.send(

View File

@ -19,7 +19,7 @@ export default DiscourseRoute.extend({
controller.setProperties({
originalPrimaryGroupId: model.primary_group_id,
availableGroups: this._availableGroups,
customGroupIdsBuffer: null,
customGroupIdsBuffer: model.customGroups.mapBy("id"),
model
});
}

View File

@ -20,7 +20,7 @@
{{/admin-form-row}}
{{#admin-form-row label="admin.api.user_mode"}}
{{combo-box content=userModes value=userMode onSelect=(action "changeUserMode")}}
{{combo-box content=userModes value=userMode onChange=(action "changeUserMode")}}
{{/admin-form-row}}
{{#if showUserSelector}}
@ -28,11 +28,11 @@
{{user-selector single="true"
usernames=model.username
placeholderKey="admin.api.user_placeholder"
}}
}}
{{/admin-form-row}}
{{/if}}
{{#admin-form-row}}
{{d-button icon="check" label="admin.api.save" action=(action "save") class="btn-primary"}}
{{/admin-form-row}}
{{/if}}
</div>
</div>

View File

@ -23,30 +23,35 @@
<div>
<label for="badge_type_id">{{i18n 'admin.badges.badge_type'}}</label>
{{combo-box name="badge_type_id"
value=buffered.badge_type_id
content=badgeTypes
allowInitialValueMutation=true
isDisabled=readOnly}}
{{combo-box
name="badge_type_id"
value=buffered.badge_type_id
content=badgeTypes
onChange=(action (mut buffered.badge_type_id))
isDisabled=readOnly
}}
</div>
<div>
<label for="badge_grouping_id">{{i18n 'admin.badges.badge_grouping'}}</label>
<div class="badge-grouping-control">
{{combo-box name="badge_grouping_id"
{{combo-box
name="badge_grouping_id"
value=buffered.badge_grouping_id
content=badgeGroupings
class="badge-selector"
nameProperty="name"}}
nameProperty="name"
onChange=(action (mut buffered.badge_grouping_id))
}}
{{d-button
class="btn-default"
action=(route-action "editGroupings")
icon="pencil-alt"}}
}}
</div>
</div>
<div>
<label for="description">{{i18n 'admin.badges.description'}}</label>
{{#if buffered.system}}
@ -95,12 +100,13 @@
<div>
<label for="trigger">{{i18n 'admin.badges.trigger'}}</label>
{{combo-box name="trigger"
value=buffered.trigger
content=badgeTriggers
optionValuePath="content.id"
optionLabelPath="content.name"
disabled=readOnly}}
{{combo-box
name="trigger"
value=buffered.trigger
content=badgeTriggers
onChange=(action (mut buffered.trigger))
disabled=readOnly
}}
</div>
{{/if}}
{{/if}}

View File

@ -130,7 +130,10 @@
</span>
<div class="input">
{{date-input date=startDate onChange=(action "onChangeStartDate")}}
{{date-input
date=startDate
onChange=(action "onChangeStartDate")
}}
</div>
</div>
@ -140,7 +143,10 @@
</span>
<div class="input">
{{date-input date=endDate onChange=(action "onChangeEndDate")}}
{{date-input
date=endDate
onChange=(action "onChangeEndDate")
}}
</div>
</div>
{{/if}}

View File

@ -1,6 +1,10 @@
{{#if editing}}
{{#admin-form-row label="admin.user_fields.type"}}
{{combo-box content=fieldTypes value=buffered.field_type}}
{{combo-box
content=fieldTypes
value=buffered.field_type
onChange=(action (mut buffered.field_type))
}}
{{/admin-form-row}}
{{#admin-form-row label="admin.user_fields.name"}}

View File

@ -13,7 +13,11 @@
</td>
<td class="editing-input">
<div class="label">{{i18n "admin.embedding.category"}}</div>
{{category-chooser value=categoryId class="small"}}
{{category-chooser
value=categoryId
class="small"
onChange=(action (mut categoryId))
}}
</td>
<td class="editing-controls">
{{d-button icon="check" action=(action "save") class="btn-primary" disabled=cantSave}}

View File

@ -4,7 +4,11 @@
{{{i18n 'admin.user.penalty_post_actions'}}}
</div>
</label>
{{combo-box value=postAction content=penaltyActions onSelect=(action "penaltyChanged")}}
{{combo-box
value=postAction
content=penaltyActions
onChange=(action "penaltyChanged")
}}
</div>
{{#if editing}}

View File

@ -8,7 +8,11 @@
autocorrect="off"
autocapitalize="off"}}
{{combo-box content=permalinkTypes value=permalinkType}}
{{combo-box
content=permalinkTypes
value=permalinkType
onChange=(action (mut permalinkType))
}}
{{text-field
value=permalink_type_value

View File

@ -1,7 +1,5 @@
{{search-advanced-category-chooser
filterable=true
value=category
castInteger=true
onSelectNone=(action "onChange")
onDeselect=(action "onDeselect")
onSelect=(action "onChange")}}
onChange=(action (mut category))
}}

View File

@ -4,5 +4,5 @@
allowAny=filter.allow_any
value=filter.default
none="admin.dashboard.report_filter_any"
onSelectNone=(action "onChange")
onSelect=(action "onChange")}}
onChange=(action "onChange")
}}

View File

@ -1,10 +1,9 @@
{{combo-box
castInteger=true
filterable=true
valueAttribute="value"
valueProperty="value"
content=groupOptions
value=groupId
allowAny=filter.allow_any
none="admin.dashboard.reports.groups"
onSelectNone=(action "onChange")
onSelect=(action "onChange")}}
onChange=(action "onChange")
}}

View File

@ -1,4 +1,10 @@
<b>{{i18n 'admin.logs.screened_ips.form.label'}}</b>
{{text-field value=ip_address disabled=formSubmitted class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.ip_address" autocorrect="off" autocapitalize="off"}}
{{combo-box content=actionNames value=actionName}}
{{combo-box
content=actionNames
value=actionName
onChange=(action (mut actionName))
}}
{{d-button class="btn-default" action=(action "submit") disabled=formSubmitted label="admin.logs.screened_ips.form.add"}}

View File

@ -1,3 +1,7 @@
{{category-selector categories=selectedCategories}}
{{category-selector
categories=selectedCategories
onChange=(action "onChangeSelectedCategories")
}}
<div class='desc'>{{{unbound setting.description}}}</div>
{{setting-validation-message message=validationMessage}}

View File

@ -1,3 +1,7 @@
{{category-chooser value=value allowUncategorized="true"}}
{{category-chooser
value=value
allowUncategorized=true
onChange=(action (mut value))
}}
{{setting-validation-message message=validationMessage}}
<div class='desc'>{{{unbound setting.description}}}</div>

View File

@ -1,3 +1,11 @@
{{list-setting settingValue=value choices=setting.choices settingName=setting.setting allowAny=allowAny}}
{{list-setting
value=settingValue
settingName=setting.setting
allowAny=allowAny
choices=settingChoices
onChange=(action "onChangeListSetting")
onChangeChoices=(action "onChangeChoices")
}}
{{setting-validation-message message=validationMessage}}
<div class='desc'>{{{unbound setting.description}}}</div>

View File

@ -1,4 +1,15 @@
{{combo-box castInteger=true valueAttribute="value" content=setting.validValues value=value none=setting.allowsNone}}
{{combo-box
valueProperty="value"
content=setting.validValues
value=value
onChange=(action (mut value))
allowAny=setting.allowsNone
}}
{{preview}}
{{setting-validation-message message=validationMessage}}
<div class='desc'>{{{unbound setting.description}}}</div>
<div class='desc'>
{{{unbound setting.description}}}
</div>

View File

@ -1,3 +1,10 @@
{{list-setting settingValue=value choices=groupChoices settingName='name'}}
{{list-setting
value=settingValue
choices=groupChoices
settingName="name"
nameProperty=nameProperty
valueProperty=valueProperty
onChange=(action "onChangeGroupListSetting")
}}
{{setting-validation-message message=validationMessage}}
<div class='desc'>{{{unbound setting.description}}}</div>

View File

@ -2,12 +2,19 @@
<div class='values'>
{{#each collection as |value index|}}
<div class='value' data-index={{index}}>
{{d-button action=(action "removeValue")
actionParam=value
icon="times"
class="btn-default remove-value-btn btn-small"}}
{{d-button
action=(action "removeValue")
actionParam=value
icon="times"
class="remove-value-btn btn-small"
}}
{{input title=value value=value class="value-input" focus-out=(action "changeValue" index)}}
{{input
title=value
value=value
class="value-input"
focus-out=(action "changeValue" index)
}}
</div>
{{/each}}
</div>
@ -15,7 +22,10 @@
{{combo-box
allowAny=true
allowContentReplacement=true
none=noneKey
valueProperty=null
nameProperty=null
value=newValue
content=filteredChoices
onSelect=(action "selectChoice")}}
onChange=(action "selectChoice")
}}

View File

@ -1,8 +1,8 @@
{{combo-box
content=sortedTemplates
valueAttribute="id"
valueProperty="id"
nameProperty="title"
onSelect=(action "selectTemplate")
onChange=(action "selectTemplate")
}}
{{outlet}}

View File

@ -30,7 +30,13 @@
{{/if}}
</div>
{{else}}
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions value=filterActionId none="admin.logs.staff_actions.all" onSelect=(action "filterActionIdChanged")}}
{{i18n "admin.logs.staff_actions.filter"}}
{{combo-box
content=userHistoryActions
value=filterActionId
none="admin.logs.staff_actions.all"
onChange=(action "filterActionIdChanged")
}}
{{/if}}
{{d-button class="btn-default" action=(action "exportStaffActionLogs") label="admin.export_csv.button_text" icon="download"}}

View File

@ -1,16 +1,20 @@
<div>
{{#d-modal-body title="admin.customize.colors.select_base.title"}}
{{i18n "admin.customize.colors.select_base.description"}}
{{combo-box content=model
value=selectedBaseThemeId
allowInitialValueMutation=true
valueAttribute="base_scheme_id"}}
{{combo-box
content=model
value=selectedBaseThemeId
onChange=(action (mut selectedBaseThemeId))
valueProperty="base_scheme_id"
}}
{{/d-modal-body}}
<div class="modal-footer">
{{d-button
class="btn-primary"
action=(action "selectBase")
icon="plus"
label="admin.customize.new"}}
label="admin.customize.new"
}}
</div>
</div>

View File

@ -89,7 +89,12 @@
{{input value=name placeholder=placeholder}}
<div class="label">{{I18n "admin.customize.theme.create_type"}}</div>
{{combo-box valueAttribute="value" content=createTypes value=selectedType}}
{{combo-box
valueProperty="value"
content=createTypes
value=selectedType
onChange=(action (mut selectedType))
}}
</div>
{{/if}}
</div>

View File

@ -8,7 +8,9 @@
label="admin.user.silence_duration"
includeFarFuture=true
clearable=false
input=silenceUntil}}
input=silenceUntil
onChangeInput=(action (mut silenceUntil))
}}
</label>
</div>

View File

@ -9,7 +9,9 @@
label="admin.user.suspend_duration"
includeFarFuture=true
clearable=false
input=suspendUntil}}
input=suspendUntil
onChangeInput=(action (mut suspendUntil))
}}
</label>
</div>
{{suspension-details reason=reason message=message}}

View File

@ -4,4 +4,5 @@
filters=model
reportOptions=reportOptions
showFilteringUI=true
onRefresh=(route-action "onParamsChange")}}
onRefresh=(route-action "onParamsChange")
}}

View File

@ -1,6 +1,11 @@
<div class="admin-title">
{{period-chooser period=period}}
{{combo-box content=searchTypeOptions value=searchType class='search-logs-filter'}}
{{period-chooser period=period onChange=(action (mut period))}}
{{combo-box
content=searchTypeOptions
value=searchType
class="search-logs-filter"
onChange=(action (mut searchType))
}}
</div>
{{#conditional-loading-spinner condition=loading}}

View File

@ -1,6 +1,11 @@
<div class="admin-title">
{{period-chooser period=period}}
{{combo-box content=searchTypeOptions value=searchType class='search-logs-filter'}}
{{period-chooser period=period onChange=(action (mut period))}}
{{combo-box
content=searchTypeOptions
value=searchType
class="search-logs-filter"
onChange=(action (mut searchType))
}}
</div>
<h2>

View File

@ -15,8 +15,13 @@
{{else}}
<form class="form-horizontal">
<div>
<label>{{i18n 'admin.badges.badge'}}</label>
{{combo-box forceEscape=true filterable=true value=selectedBadgeId content=grantableBadges}}
<label>{{i18n 'admin.badges.badge'}}</label>
{{combo-box
filterable=true
value=selectedBadgeId
content=grantableBadges
onChange=(action (mut selectedBadgeId))
}}
</div>
<div>
<label>{{i18n 'admin.badges.reason'}}</label>

View File

@ -298,7 +298,7 @@
</div>
<div class="controls">
{{d-button href="/admin/api/keys" label="admin.api.manage_keys"}}
</div>
</div>
{{/if}}
@ -350,8 +350,9 @@
<div class="value">
{{combo-box
content=site.trustLevels
value=model.trust_level
nameProperty="detailedName"
value=model.trustLevel.id
onChange=(action (mut model.trust_level))
}}
{{#if model.dirty}}
@ -514,9 +515,10 @@
<div class="field">{{i18n "admin.groups.custom"}}</div>
<div class="value">
{{admin-group-selector
selected=model.customGroups
available=availableGroups
buffer=customGroupIdsBuffer}}
content=availableGroups
value=customGroupIdsBuffer
onChange=(action (mut customGroupIdsBuffer))
}}
</div>
{{#if customGroupsDirty}}
<div class="controls">
@ -532,7 +534,9 @@
{{combo-box
content=model.customGroups
value=model.primary_group_id
none="admin.groups.no_primary"}}
none="admin.groups.no_primary"
onChange=(action (mut model.primary_group_id))
}}
</div>
{{#if primaryGroupDirty}}
<div class="controls">

View File

@ -14,9 +14,12 @@
<div>
<label for='content-type'>{{i18n 'admin.web_hooks.content_type'}}</label>
{{combo-box content=contentTypes
name="content-type"
value=model.content_type}}
{{combo-box
content=contentTypes
name="content-type"
value=model.content_type
onChange=(action (mut model.content_type))
}}
</div>
<div>
@ -48,7 +51,10 @@
<div class='filters'>
<div class="filter">
<label>{{d-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.categories_filter'}}</label>
{{category-selector categories=model.categories}}
{{category-selector
categories=model.categories
onChange=(action (mut model.categories))
}}
<div class="instructions">{{i18n 'admin.web_hooks.categories_filter_instructions'}}</div>
</div>
{{#if showTagsFilter}}

View File

@ -637,6 +637,12 @@ registerIconRenderer({
params.title
).replace(/'/g, "&#39;")}'>${html}</span>`;
}
if (params.translatedtitle) {
html = `<span class="svg-icon-title" title='${params.translatedtitle.replace(
/'/g,
"&#39;"
)}'>${html}</span>`;
}
return html;
},

View File

@ -1,13 +1,16 @@
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import { computed } from "@ember/object";
export default DropdownSelectBoxComponent.extend({
classNames: ["auth-token-dropdown"],
headerIcon: "wrench",
allowInitialValueMutation: false,
showFullTitle: false,
computeContent() {
const content = [
selectKitOptions: {
icon: "wrench",
showFullTitle: false
},
content: computed(function() {
return [
{
id: "notYou",
icon: "user-times",
@ -21,12 +24,10 @@ export default DropdownSelectBoxComponent.extend({
description: ""
}
];
return content;
},
}),
actions: {
onSelect(id) {
onChange(id) {
switch (id) {
case "notYou":
this.showToken(this.token);

View File

@ -49,5 +49,22 @@ export default Component.extend(FilterModeMixin, {
noSubcategories,
persistedQueryParams: params
});
},
actions: {
changeCategoryNotificationLevel(notificationLevel) {
this.category.setNotification(notificationLevel);
},
selectCategoryAdminDropdownAction(actionId) {
switch (actionId) {
case "create":
this.createCategory();
break;
case "reorder":
this.reorderCategories();
break;
}
}
}
});

View File

@ -32,8 +32,9 @@ export default Component.extend({
didUpdateAttrs() {
this._super(...arguments);
if (this._picker) {
this._picker.setDate(this.date, true);
if (this._picker && typeof date === "string") {
const [year, month, day] = this.date.split("-").map(x => parseInt(x, 10));
this._picker.setDate(new Date(year, month - 1, day), true);
}
},
@ -84,7 +85,7 @@ export default Component.extend({
this._picker && this._picker.hide();
if (this.onChange) {
this.onChange(moment(value).toDate());
this.onChange(value);
}
},
@ -103,5 +104,11 @@ export default Component.extend({
_opts() {
return null;
},
actions: {
onInput(event) {
this._picker && this._picker.setDate(event.target.value, true);
}
}
});

View File

@ -10,7 +10,8 @@ export default DatePicker.extend({
moment()
.add(1, "day")
.toDate(),
setDefaultDate: !!this.defaultDate
setDefaultDate: !!this.defaultDate,
minDate: this.minDate || moment().toDate()
};
}
});

View File

@ -1,4 +1,4 @@
import { next } from "@ember/runloop";
import { schedule } from "@ember/runloop";
import Component from "@ember/component";
/* global Pikaday:true */
import loadScript from "discourse/lib/load-script";
@ -28,7 +28,7 @@ export default Component.extend({
_loadPikadayPicker(container) {
loadScript("/javascripts/pikaday.js").then(() => {
next(() => {
schedule("afterRender", () => {
const options = {
field: this.element.querySelector(".date-picker"),
container: container || null,

View File

@ -9,6 +9,16 @@ export default buildCategoryPanel("security", {
showPendingGroupChangesAlert: false,
interactedWithDropdowns: false,
@on("init")
_setup() {
this.setProperties({
selectedGroup: this.get("category.availableGroups.firstObject"),
selectedPermission: this.get(
"category.availablePermissions.firstObject.id"
)
});
},
@on("init")
_registerValidator() {
this.registerValidator(() => {
@ -24,8 +34,18 @@ export default buildCategoryPanel("security", {
},
actions: {
onDropdownChange() {
this.set("interactedWithDropdowns", true);
onSelectGroup(selectedGroup) {
this.setProperties({
interactedWithDropdowns: true,
selectedGroup
});
},
onSelectPermission(selectedPermission) {
this.setProperties({
interactedWithDropdowns: true,
selectedPermission
});
},
editPermissions() {
@ -42,11 +62,8 @@ export default buildCategoryPanel("security", {
});
}
this.set(
"selectedGroup",
this.get("category.availableGroups.firstObject")
);
this.setProperties({
selectedGroup: this.get("category.availableGroups.firstObject"),
showPendingGroupChangesAlert: false,
interactedWithDropdowns: false
});

View File

@ -1,5 +1,5 @@
import { isEmpty } from "@ember/utils";
import { alias, equal, or } from "@ember/object/computed";
import { equal, or, readOnly } from "@ember/object/computed";
import { schedule } from "@ember/runloop";
import Component from "@ember/component";
import discourseComputed, {
@ -16,7 +16,7 @@ import {
} from "discourse/controllers/edit-topic-timer";
export default Component.extend({
selection: alias("topicTimer.status_type"),
selection: readOnly("topicTimer.status_type"),
autoOpen: equal("selection", OPEN_STATUS_TYPE),
autoClose: equal("selection", CLOSE_STATUS_TYPE),
autoDelete: equal("selection", DELETE_STATUS_TYPE),
@ -27,16 +27,11 @@ export default Component.extend({
@discourseComputed(
"topicTimer.updateTime",
"loading",
"publishToCategory",
"topicTimer.category_id"
)
saveDisabled(updateTime, loading, publishToCategory, topicTimerCategoryId) {
return (
isEmpty(updateTime) ||
loading ||
(publishToCategory && !topicTimerCategoryId)
);
saveDisabled(updateTime, publishToCategory, topicTimerCategoryId) {
return isEmpty(updateTime) || (publishToCategory && !topicTimerCategoryId);
},
@discourseComputed("topic.visible")
@ -70,5 +65,25 @@ export default Component.extend({
this.set("topicTimer.based_on_last_post", false);
});
}
},
didReceiveAttrs() {
this._super(...arguments);
// TODO: get rid of this hack
schedule("afterRender", () => {
if (!this.get("topicTimer.status_type")) {
this.set(
"topicTimer.status_type",
this.get("timerTypes.firstObject.id")
);
}
});
},
actions: {
onChangeTimerType(value) {
this.set("topicTimer.status_type", value);
}
}
});

View File

@ -45,9 +45,10 @@ export default Component.extend({
const dateTime = moment(`${this.date}${time}`);
if (dateTime.isValid()) {
this.set("input", dateTime.format(FORMAT));
this.attrs.onChangeInput &&
this.attrs.onChangeInput(dateTime.format(FORMAT));
} else {
this.set("input", null);
this.attrs.onChangeInput && this.attrs.onChangeInput(null);
}
},
@ -109,7 +110,10 @@ export default Component.extend({
}
if (isCustom) {
return date || time;
if (date) {
return moment(`${date}${time ? " " + time : ""}`).isAfter(moment());
}
return time;
} else {
return input;
}

View File

@ -1,23 +1,16 @@
import discourseComputed from "discourse-common/utils/decorators";
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import { computed } from "@ember/object";
export default DropdownSelectBoxComponent.extend({
pluginApiIdentifiers: ["group-member-dropdown"],
classNames: "group-member-dropdown",
showFullTitle: false,
allowInitialValueMutation: false,
allowAutoSelectFirst: false,
classNames: ["group-member-dropdown"],
init() {
this._super(...arguments);
this.headerIcon = ["wrench"];
selectKitOptions: {
icon: "wrench",
showFullTitle: false
},
autoHighlight() {},
@discourseComputed("member.owner")
content(isOwner) {
content: computed("member.owner", function() {
const items = [
{
id: "removeMember",
@ -29,8 +22,8 @@ export default DropdownSelectBoxComponent.extend({
}
];
if (this.currentUser && this.currentUser.admin) {
if (isOwner) {
if (this.get("currentUser.admin")) {
if (this.member.owner) {
items.push({
id: "removeOwner",
name: I18n.t("groups.members.remove_owner"),
@ -52,19 +45,5 @@ export default DropdownSelectBoxComponent.extend({
}
return items;
},
mutateValue(id) {
switch (id) {
case "removeMember":
this.removeMember(this.member);
break;
case "makeOwner":
this.makeOwner(this.get("member.username"));
break;
case "removeOwner":
this.removeOwner(this.member);
break;
}
}
})
});

View File

@ -1,7 +1,10 @@
import discourseComputed from "discourse-common/utils/decorators";
import Component from "@ember/component";
import { computed } from "@ember/object";
export default Component.extend({
tokenSeparator: "|",
init() {
this._super(...arguments);
@ -17,6 +20,17 @@ export default Component.extend({
];
},
groupTrustLevel: computed(
"model.grant_trust_level",
"trustLevelOptions",
function() {
return (
this.model.get("grant_trust_level") ||
this.trustLevelOptions.firstObject.value
);
}
),
@discourseComputed("model.visibility_level", "model.public_admission")
disableMembershipRequestSetting(visibility_level, publicAdmission) {
visibility_level = parseInt(visibility_level, 10);
@ -30,5 +44,11 @@ export default Component.extend({
disablePublicSetting(visibility_level, allowMembershipRequests) {
visibility_level = parseInt(visibility_level, 10);
return allowMembershipRequests || visibility_level > 1;
},
actions: {
onChangeEmailDomainsSetting(value) {
this.set("model.emailDomains", value.join(this.tokenSeparator));
}
}
});

View File

@ -0,0 +1,12 @@
export default Ember.Component.extend({
actions: {
onChange(tags) {
this.valueChanged &&
this.valueChanged({
target: {
value: tags
}
});
}
}
});

View File

@ -182,10 +182,13 @@ export default Component.extend({
.finally(() => this.set("updating", false));
},
categoryChanged(category) {
categoryChanged(categoryId) {
let category = Category.findById(categoryId);
if (!category) {
category = Category.findUncategorized();
}
this._updates.category_id = category.id;
},

View File

@ -553,7 +553,6 @@ export default Component.extend({
}
},
@observes("searchedTerms.time.when", "searchedTerms.time.days")
updateSearchTermForPostTime() {
const match = this.filterBlocks(REGEXP_POST_TIME_PREFIX);
const timeDaysFilter = this.get("searchedTerms.time.days");
@ -603,5 +602,28 @@ export default Component.extend({
badgeFinder(term) {
return Badge.findAll({ search: term });
},
actions: {
onChangeWhenTime(time) {
if (time) {
this.set("searchedTerms.time.when", time);
this.updateSearchTermForPostTime();
}
},
onChangeWhenDate(date) {
if (date) {
this.set("searchedTerms.time.days", moment(date).format("YYYY-MM-DD"));
this.updateSearchTermForPostTime();
}
},
onChangeCategory(categoryId) {
if (categoryId) {
this.set("searchedTerms.category", Category.findById(categoryId));
} else {
this.set("searchedTerms.category", null);
}
}
}
});

View File

@ -11,8 +11,8 @@ export default Component.extend({
},
actions: {
updateDestinationCategory(category) {
return this.topic.updateDestinationCategory(category.get("id"));
updateDestinationCategory(categoryId) {
return this.topic.updateDestinationCategory(categoryId);
},
publish() {

View File

@ -1,47 +1,38 @@
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import { computed } from "@ember/object";
export default DropdownSelectBoxComponent.extend({
pluginApiIdentifiers: ["tags-admin-dropdown"],
classNames: "tags-admin-dropdown",
showFullTitle: false,
allowInitialValueMutation: false,
actionsMapping: null,
init() {
this._super(...arguments);
this.headerIcon = ["bars", "caret-down"];
selectKitOptions: {
icons: ["bars", "caret-down"],
showFullTitle: false
},
autoHighlight() {},
computeContent() {
const items = [
content: computed(function() {
return [
{
id: "manageGroups",
name: I18n.t("tagging.manage_groups"),
description: I18n.t("tagging.manage_groups_description"),
icon: "wrench",
__sk_row_type: "noopRow"
icon: "wrench"
},
{
id: "uploadTags",
name: I18n.t("tagging.upload"),
description: I18n.t("tagging.upload_description"),
icon: "upload",
__sk_row_type: "noopRow"
icon: "upload"
},
{
id: "deleteUnusedTags",
name: I18n.t("tagging.delete_unused"),
description: I18n.t("tagging.delete_unused_description"),
icon: "trash-alt",
__sk_row_type: "noopRow"
icon: "trash-alt"
}
];
return items;
},
}),
actions: {
onSelect(id) {

View File

@ -17,15 +17,6 @@ export default Component.extend({
notice: null,
showTopicTimer: null,
rerenderTriggers: [
"topicClosed",
"statusType",
"executeAt",
"basedOnLastPost",
"duration",
"categoryId"
],
@discourseComputed("statusType")
canRemoveTimer(type) {
if (type === REMINDER_TYPE) return true;
@ -38,7 +29,7 @@ export default Component.extend({
},
renderTopicTimer() {
if (!this.executeAt) {
if (!this.executeAt || this.executeAt < moment()) {
this.set("showTopicTimer", null);
return;
}
@ -50,7 +41,6 @@ export default Component.extend({
const statusUpdateAt = moment(this.executeAt);
const duration = moment.duration(statusUpdateAt - moment());
const minutesLeft = duration.asMinutes();
if (minutesLeft > 0) {
let rerenderDelay = 1000;
if (minutesLeft > 2160) {
@ -82,9 +72,11 @@ export default Component.extend({
);
}
this.set("title", `${moment(this.executeAt).format("LLLL")}`.htmlSafe());
this.set("notice", `${I18n.t(this._noticeKey(), options)}`.htmlSafe());
this.set("showTopicTimer", true);
this.setProperties({
title: `${moment(this.executeAt).format("LLLL")}`.htmlSafe(),
notice: `${I18n.t(this._noticeKey(), options)}`.htmlSafe(),
showTopicTimer: true
});
// TODO Sam: concerned this can cause a heavy rerender loop
if (ENV.environment !== "test") {

View File

@ -100,13 +100,19 @@ export default Controller.extend(ModalFunctionality, {
});
}
})
.catch(error => {
popupAjaxError(error);
})
.catch(popupAjaxError)
.finally(() => this.set("loading", false));
},
actions: {
onChangeStatusType(value) {
this.set("topicTimer.status_type", value);
},
onChangeUpdateTime(value) {
this.set("topicTimer.updateTime", value);
},
saveTimer() {
if (!this.get("topicTimer.updateTime")) {
this.flash(

View File

@ -86,6 +86,20 @@ export default Controller.extend({
this.toggleProperty("showActions");
},
actOnGroup(member, actionId) {
switch (actionId) {
case "removeMember":
this.send("removeMember", member);
break;
case "makeOwner":
this.send("makeOwner", member.username);
break;
case "removeOwner":
this.send("removeOwner", member);
break;
}
},
removeMember(user) {
this.model.removeMember(user, this.memberParams);
},

View File

@ -14,6 +14,7 @@ import {
isiPad,
iOSWithVisualViewport
} from "discourse/lib/utilities";
import { computed } from "@ember/object";
const USER_HOMES = {
1: "latest",
@ -76,6 +77,17 @@ export default Controller.extend(PreferencesTabController, {
});
},
homepageId: computed(
"model.user_option.homepage_id",
"userSelectableHome.[]",
function() {
return (
this.model.user_option.homepage_id ||
this.userSelectableHome.firstObject.value
);
}
),
@discourseComputed
titleCountModes() {
return TITLE_COUNT_MODES.map(value => {
@ -195,6 +207,8 @@ export default Controller.extend(PreferencesTabController, {
// Force refresh when leaving this screen
Discourse.set("assetVersion", "forceRefresh");
this.set("textSize", newSize);
}
}
});

View File

@ -155,9 +155,8 @@ export default Controller.extend(BulkTopicSelection, FilterModeMixin, {
});
},
changeTagNotification(id) {
const tagNotification = this.tagNotification;
tagNotification.update({ notification_level: id });
changeTagNotificationLevel(notificationLevel) {
this.tagNotification.update({ notification_level: notificationLevel });
}
}
});

View File

@ -242,12 +242,12 @@ export default Controller.extend(bufferedProperty("model"), {
},
actions: {
topicCategoryChanged(selection) {
this.set("buffered.category_id", selection.value);
topicCategoryChanged(categoryId) {
this.set("buffered.category_id", categoryId);
},
topicTagsChanged({ target }) {
this.set("buffered.tags", target.value);
topicTagsChanged(value) {
this.set("buffered.tags", value);
},
deletePending(pending) {

View File

@ -59,6 +59,9 @@ export default Controller.extend({
},
actions: {
changeGroupNotificationLevel(notificationLevel) {
this.group.setNotification(notificationLevel, this.get("user.id"));
},
archive() {
this.bulkOperation("archive_messages");
},

View File

@ -9,7 +9,7 @@ import CanCheckEmails from "discourse/mixins/can-check-emails";
import User from "discourse/models/user";
import optionalService from "discourse/lib/optional-service";
import { prioritizeNameInUx } from "discourse/lib/settings";
import { set } from "@ember/object";
import { set, computed } from "@ember/object";
export default Controller.extend(CanCheckEmails, {
indexStream: false,
@ -136,6 +136,21 @@ export default Controller.extend(CanCheckEmails, {
}
},
userNotificationLevel: computed(
"currentUser.ignored_ids",
"model.ignored",
"model.muted",
function() {
if (this.get("model.ignored")) {
return "changeToIgnored";
} else if (this.get("model.muted")) {
return "changeToMuted";
} else {
return "changeToNormal";
}
}
),
actions: {
collapseProfile() {
this.set("forceExpand", false);

View File

@ -0,0 +1,8 @@
import { registerUnbound } from "discourse-common/lib/helpers";
registerUnbound(
"component-for-collection",
(collectionIdentifier, selectKit) => {
return selectKit.modifyComponentForCollection(collectionIdentifier);
}
);

View File

@ -0,0 +1,8 @@
import { registerUnbound } from "discourse-common/lib/helpers";
registerUnbound(
"component-for-row",
(collectionForIdentifier, item, selectKit) => {
return selectKit.modifyComponentForRow(collectionForIdentifier, item);
}
);

View File

@ -17,7 +17,7 @@ const DiscourseLocation = EmberObject.extend({
this._super(...arguments);
this.set("location", this.location || window.location);
this.initState();
this.initOptions();
},
/**
@ -25,9 +25,9 @@ const DiscourseLocation = EmberObject.extend({
Used to set state on first call to setURL
@method initState
@method initOptions
*/
initState() {
initOptions() {
const history = this.history || window.history;
if (history && history.scrollRestoration) {
history.scrollRestoration = "manual";

View File

@ -292,6 +292,10 @@ const DiscourseURL = EmberObject.extend({
return this.handleURL(path, opts);
},
routeToUrl(url, opts = {}) {
this.routeTo(Discourse.getURL(url), opts);
},
rewrite(regexp, replacement, opts) {
rewrites.push({ regexp, replacement, opts: opts || {} });
},

View File

@ -120,15 +120,14 @@ const Category = RestModel.extend({
return topicCount > (this.num_featured_topics || 2);
},
@discourseComputed("topic_count", "subcategories")
totalTopicCount(topicCount, subcats) {
let count = topicCount;
if (subcats) {
subcats.forEach(s => {
count += s.get("topic_count");
@discourseComputed("topic_count", "subcategories.[]")
totalTopicCount(topicCount, subcategories) {
if (subcategories) {
subcategories.forEach(subcategory => {
topicCount += subcategory.topic_count;
});
}
return count;
return topicCount;
},
save() {

View File

@ -12,7 +12,9 @@
{{combo-box
value=selectedUserBadgeId
nameProperty="badge.name"
content=selectableUserBadges}}
content=selectableUserBadges
onChange=(action (mut selectedUserBadgeId))
}}
</div>
</div>

View File

@ -1,18 +1,20 @@
{{#each categoryBreadcrumbs as |breadcrumb|}}
{{#if breadcrumb.hasOptions}}
{{category-drop
category=breadcrumb.category
category=breadcrumb.category
categories=breadcrumb.options
options=(hash
parentCategory=breadcrumb.parentCategory
categories=breadcrumb.options
subCategory=breadcrumb.isSubcategory
noSubcategories=breadcrumb.noSubcategories}}
noSubcategories=breadcrumb.noSubcategories
autoFilterable=true
)
}}
{{/if}}
{{/each}}
{{#if siteSettings.tagging_enabled}}
{{tag-drop
currentCategory=category
tagId=tagId}}
{{tag-drop currentCategory=category tagId=tagId}}
{{/if}}
{{plugin-outlet name="bread-crumbs-right" connectorTagName="li" tagName=""}}

View File

@ -3,12 +3,13 @@
{{else}}
{{composer-actions
composerModel=model
options=model.replyOptions
replyOptions=model.replyOptions
canWhisper=canWhisper
openComposer=openComposer
closeComposer=closeComposer
action=model.action
tabindex=tabindex}}
tabindex=tabindex
}}
{{/if}}
<span class="action-title">

View File

@ -5,12 +5,15 @@
{{#each group.buttons as |b|}}
{{#if b.popupMenu}}
{{toolbar-popup-menu-options
onSelect=onPopupMenuAction
content=popupMenuOptions
onChange=onPopupMenuAction
onExpand=(action b.action b)
title=b.title
headerIcon=b.icon
class=b.className
content=popupMenuOptions}}
options=(hash
popupTitle=b.title
icon=b.icon
)
}}
{{else}}
{{d-button
action=b.action

View File

@ -2,14 +2,18 @@
{{#if showCategoryAdmin}}
{{categories-admin-dropdown
create=createCategory
reorder=reorderCategories}}
onChange=(action "selectCategoryAdminDropdownAction")
}}
{{/if}}
{{navigation-bar navItems=navItems filterMode=filterMode category=category}}
{{#if showCategoryNotifications}}
{{category-notifications-button value=category.notification_level category=category}}
{{category-notifications-button
value=category.notification_level
category=category
onChange=(action "changeCategoryNotificationLevel")
}}
{{/if}}
{{plugin-outlet name="before-create-topic-button"

View File

@ -2,4 +2,6 @@
type=inputType
class="date-picker"
placeholder=placeholder
value=value}}
value=value
input=(action "onInput")
}}

View File

@ -17,7 +17,9 @@
excludeCategoryId=category.id
categories=parentCategories
allowSubCategories=true
allowUncategorized=false}}
allowUncategorized=false
onChange=(action (mut category.parent_category_id))
}}
</section>
{{/if}}

View File

@ -22,18 +22,21 @@
{{/unless}}
{{#if editingPermissions}}
{{#if category.availableGroups}}
{{combo-box class="available-groups"
allowInitialValueMutation=true
allowContentReplacement=true
content=category.availableGroups
onSelect=(action "onDropdownChange")
value=selectedGroup}}
{{combo-box allowInitialValueMutation=true
class="permission-selector"
nameProperty="description"
content=category.availablePermissions
onSelect=(action "onDropdownChange")
value=selectedPermission}}
{{combo-box
class="available-groups"
content=category.availableGroups
onChange=(action "onSelectGroup")
value=selectedGroup
valueProperty=null
nameProperty=null
}}
{{combo-box
class="permission-selector"
nameProperty="description"
content=category.availablePermissions
onChange=(action "onSelectPermission")
value=selectedPermission
}}
{{d-button
action=(action "addPermission" selectedGroup selectedPermission)
class="btn-primary add-permission"

View File

@ -33,10 +33,13 @@
{{i18n "category.search_priority.label"}}
</label>
<div class="controls">
{{combo-box valueAttribute="value"
id="category-search-priority"
content=searchPrioritiesOptions
value=category.search_priority}}
{{combo-box
valueProperty="value"
id="category-search-priority"
content=searchPrioritiesOptions
value=category.search_priority
onChange=(action (mut category.search_priority))
}}
</div>
</section>
@ -145,7 +148,7 @@
{{i18n "category.default_view"}}
</label>
<div class="controls">
{{combo-box valueAttribute="value" id="category-default-view" content=availableViews value=category.default_view}}
{{combo-box valueProperty="value" id="category-default-view" content=availableViews value=category.default_view}}
</div>
</section>
@ -154,7 +157,7 @@
{{i18n "category.default_top_period"}}
</label>
<div class="controls">
{{combo-box valueAttribute="value" id="category-default-period" content=availableTopPeriods value=category.default_top_period}}
{{combo-box valueProperty="value" id="category-default-period" content=availableTopPeriods value=category.default_top_period}}
</div>
</section>
@ -163,9 +166,9 @@
{{i18n "category.sort_order"}}
</label>
<div class="controls">
{{combo-box valueAttribute="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
{{combo-box valueProperty="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
{{#unless isDefaultSortOrder}}
{{combo-box castBoolean=true valueAttribute="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
{{combo-box castBoolean=true valueProperty="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
{{/unless}}
</div>
</section>
@ -184,7 +187,7 @@
<label for="subcategory-list-style">
{{i18n "category.subcategory_list_style"}}
</label>
{{combo-box valueAttribute="value" id="subcategory-list-style" content=availableSubcategoryListStyles value=category.subcategory_list_style}}
{{combo-box valueProperty="value" id="subcategory-list-style" content=availableSubcategoryListStyles value=category.subcategory_list_style}}
</section>
{{/if}}

View File

@ -6,12 +6,17 @@
tags=category.allowed_tags
everyTag=true
excludeSynonyms=true
unlimitedTagCount=true}}
unlimitedTagCount=true
onChange=(action (mut category.allowed_tags))
}}
</section>
<section class="field">
<label for="category-allowed-tag-groups">{{i18n 'category.tags_allowed_tag_groups'}}</label>
{{tag-group-chooser id="category-allowed-tag-groups" tagGroups=category.allowed_tag_groups}}
{{tag-group-chooser
id="category-allowed-tag-groups"
tagGroups=category.allowed_tag_groups
}}
{{#link-to 'tagGroups'}}{{i18n 'category.manage_tag_groups_link'}}{{/link-to}}
</section>

View File

@ -1,39 +1,52 @@
<form>
<div class="control-group">
{{combo-box class="timer-type" allowInitialValueMutation=true content=timerTypes value=selection}}
{{combo-box
class="timer-type"
onChange=onChangeStatusType
content=timerTypes
value=selection
}}
</div>
<div>
{{#if showTimeOnly}}
{{future-date-input
input=topicTimer.updateTime
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
basedOnLastPost=topicTimer.based_on_last_post}}
input=(readonly topicTimer.updateTime)
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
basedOnLastPost=topicTimer.based_on_last_post
onChangeInput=onChangeUpdateTime
}}
{{else if publishToCategory}}
<div class="control-group">
<label>{{i18n 'topic.topic_status_update.publish_to'}}</label>
{{category-chooser
value=topicTimer.category_id
excludeCategoryId=excludeCategoryId}}
excludeCategoryId=excludeCategoryId
onChange=(action (mut topicTimer.category_id))
}}
</div>
{{future-date-input
input=topicTimer.updateTime
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
categoryId=topicTimer.category_id
basedOnLastPost=topicTimer.based_on_last_post}}
input=(readonly topicTimer.updateTime)
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
categoryId=topicTimer.category_id
basedOnLastPost=topicTimer.based_on_last_post
onChangeInput=onChangeUpdateTime
}}
{{else if autoClose}}
{{future-date-input
input=topicTimer.updateTime
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
basedOnLastPost=topicTimer.based_on_last_post
lastPostedAt=model.last_posted_at}}
input=topicTimer.updateTime
label="topic.topic_status_update.when"
statusType=selection
includeWeekend=true
basedOnLastPost=topicTimer.based_on_last_post
lastPostedAt=model.last_posted_at
onChangeInput=onChangeUpdateTime
}}
{{/if}}
</div>
</form>

View File

@ -2,21 +2,29 @@
<div class="control-group">
<label>{{displayLabel}}</label>
{{future-date-input-selector
minimumResultsForSearch=-1
statusType=statusType
value=selection
input=input
includeDateTime=includeDateTime
includeWeekend=includeWeekend
includeFarFuture=includeFarFuture
includeMidFuture=includeMidFuture
clearable=clearable
none="topic.auto_update_input.none"}}
minimumResultsForSearch=-1
statusType=statusType
value=(readonly selection)
input=(readonly input)
includeDateTime=includeDateTime
includeWeekend=includeWeekend
includeFarFuture=includeFarFuture
includeMidFuture=includeMidFuture
clearable=clearable
none="topic.auto_update_input.none"
onChangeInput=onChangeInput
onChange=(action (mut selection))
}}
</div>
{{#if displayDateAndTimePicker}}
<div class="control-group">
{{d-icon "calendar-alt"}} {{date-picker-future value=date defaultDate=date}}
{{d-icon "calendar-alt"}}
{{date-picker-future
value=date
defaultDate=date
onSelect=(action (mut date))
}}
</div>
<div class="control-group">
@ -44,11 +52,12 @@
{{#if showTopicStatusInfo}}
<div class="alert alert-info">
{{topic-timer-info
statusType=statusType
executeAt=executeAt
basedOnLastPost=basedOnLastPost
duration=duration
categoryId=categoryId}}
statusType=statusType
executeAt=executeAt
basedOnLastPost=basedOnLastPost
duration=duration
categoryId=categoryId
}}
</div>
{{/if}}
</div>

View File

@ -6,7 +6,10 @@
{{/link-to}}
</li>
{{else}}
{{group-dropdown content=group.extras.visible_group_names value=group.name}}
{{group-dropdown
groups=group.extras.visible_group_names
value=group.name
}}
{{/if}}
{{#each tabs as |tab|}}

View File

@ -3,12 +3,15 @@
<label class="control-label">{{i18n 'admin.groups.manage.interaction.visibility'}}</label>
<label for="visiblity">{{i18n 'admin.groups.manage.interaction.visibility_levels.title'}}</label>
{{combo-box name="alias"
valueAttribute="value"
value=model.visibility_level
content=visibilityLevelOptions
castInteger=true
class="groups-form-visibility-level"}}
{{combo-box
name="alias"
valueProperty="value"
value=model.visibility_level
content=visibilityLevelOptions
castInteger=true
class="groups-form-visibility-level"
onChange=(action (mut model.visibility_level))
}}
<div class="control-instructions">
{{i18n 'admin.groups.manage.interaction.visibility_levels.description'}}
@ -19,7 +22,7 @@
<label for="visiblity">{{i18n 'admin.groups.manage.interaction.members_visibility_levels.title'}}</label>
{{combo-box name="alias"
valueAttribute="value"
valueProperty="value"
value=model.members_visibility_level
content=visibilityLevelOptions
castInteger=true
@ -35,21 +38,27 @@
<label class="control-label">{{i18n 'groups.manage.interaction.posting'}}</label>
<label for="alias">{{i18n 'groups.alias_levels.mentionable'}}</label>
{{combo-box name="alias"
valueAttribute="value"
value=model.mentionable_level
content=aliasLevelOptions
class="groups-form-mentionable-level"}}
{{combo-box
name="alias"
valueProperty="value"
value=model.mentionable_level
content=aliasLevelOptions
class="groups-form-mentionable-level"
onChange=(action (mut model.mentionable_level))
}}
</div>
<div class="control-group">
<label for="alias">{{i18n 'groups.alias_levels.messageable'}}</label>
{{combo-box name="alias"
valueAttribute="value"
value=model.messageable_level
content=aliasLevelOptions
class="groups-form-messageable-level"}}
{{combo-box
name="alias"
valueProperty="value"
value=model.messageable_level
content=aliasLevelOptions
class="groups-form-messageable-level"
onChange=(action (mut model.messageable_level))
}}
</div>
<div class="control-group">

View File

@ -6,9 +6,12 @@
{{i18n 'admin.groups.manage.membership.automatic_membership_email_domains'}}
</label>
{{list-setting name="automatic_membership"
settingValue=model.emailDomains
class="group-form-automatic-membership-automatic"}}
{{list-setting
name="automatic_membership"
settingValue=model.emailDomains
class="group-form-automatic-membership-automatic"
onChange=(action "onChangeEmailDomainsSetting")
}}
<label>
{{input type="checkbox"
@ -26,12 +29,14 @@
<label class="control-label">{{i18n "admin.groups.manage.membership.effects"}}</label>
<label for="grant_trust_level">{{i18n 'admin.groups.manage.membership.trust_levels_title'}}</label>
{{combo-box name="grant_trust_level"
valueAttribute="value"
value=model.grant_trust_level
content=trustLevelOptions
class="groups-form-grant-trust-level"}}
{{combo-box
name="grant_trust_level"
valueProperty="value"
value=groupTrustLevel
content=trustLevelOptions
class="groups-form-grant-trust-level"
onChange=(action (mut model.grant_trust_level))
}}
<label>
{{input type="checkbox"
checked=model.primary_group

View File

@ -1,12 +1,15 @@
{{#if multiple}}
{{dropdown-select-box
headerIcon=bundle.icon
class="reviewable-action-dropdown"
nameProperty="label"
title=bundle.label
content=bundle.actions
onSelect=(action "performById")
disabled=reviewableUpdating}}
onChange=(action "performById")
options=(hash
icon=bundle.icon
disabled=reviewableUpdating
)
}}
{{else}}
{{d-button
class=(concat "reviewable-action " (dasherize first.id) " " first.button_class)

View File

@ -1 +1,4 @@
{{category-chooser value=value onChooseCategory=categoryChanged}}
{{category-chooser
value=value
onChange=categoryChanged
}}

View File

@ -1 +1,4 @@
{{mini-tag-chooser tags=value onChangeTags=valueChanged}}
{{mini-tag-chooser
value=value
onChange=(action "onChange")
}}

View File

@ -27,13 +27,13 @@
{{#each reviewable.editable_fields as |f|}}
<div class='editable-field {{dasherize f.id}}'>
{{component
(concat "reviewable-field-" f.type)
{{component (concat "reviewable-field-" f.type)
tagName=''
value=(editable-value reviewable f.id)
tagCategoryId=reviewable.category.id
valueChanged=(action "valueChanged" f.id)
categoryChanged=(action "categoryChanged")}}
categoryChanged=(action "categoryChanged")
}}
</div>
{{/each}}
</div>

View File

@ -15,7 +15,10 @@
<div class="control-group pull-left">
<label class="control-label" for="search-in-category">{{i18n "search.advanced.in_category.label"}}</label>
<div class="controls">
{{search-advanced-category-chooser value=searchedTerms.category}}
{{search-advanced-category-chooser
value=searchedTerms.category.id
onChange=(action "onChangeCategory")
}}
</div>
</div>
</div>
@ -47,7 +50,9 @@
allowCreate=false
filterPlaceholder=null
everyTag=true
unlimitedTagCount=true}}
unlimitedTagCount=true
onChange=(action (mut searchedTerms.tags))
}}
<section class="field">
<label>{{ input type="checkbox" class="all-tags" checked=searchedTerms.special.all_tags}} {{i18n "search.advanced.filters.all_tags"}} </label>
</section>
@ -68,13 +73,27 @@
<label>{{input type="checkbox" class="in-seen" checked=searchedTerms.special.in.seen}} {{i18n "search.advanced.filters.seen"}}</label>
</section>
{{/if}}
{{combo-box id="in" valueAttribute="value" content=inOptions value=searchedTerms.in none="user.locale.any"}}
{{combo-box
id="in"
valueProperty="value"
content=inOptions
value=searchedTerms.in
none="user.locale.any"
onChange=(action (mut searchedTerms.in))
}}
</div>
</div>
<div class="control-group pull-left">
<label class="control-label" for="search-status-options">{{i18n "search.advanced.statuses.label"}}</label>
<div class="controls">
{{combo-box id="status" valueAttribute="value" content=statusOptions value=searchedTerms.status none="user.locale.any"}}
{{combo-box
id="status"
valueProperty="value"
content=statusOptions
value=searchedTerms.status
none="user.locale.any"
onChange=(action (mut searchedTerms.status))
}}
</div>
</div>
</div>
@ -83,8 +102,18 @@
<div class="control-group pull-left">
<label class="control-label" for="search-post-date">{{i18n "search.advanced.post.time.label"}}</label>
<div class="controls full-search-dates">
{{combo-box id="postTime" valueAttribute="value" content=postTimeOptions value=searchedTerms.time.when}}
{{date-picker value=searchedTerms.time.days id="search-post-date"}}
{{combo-box
id="postTime"
valueProperty="value"
content=postTimeOptions
value=searchedTerms.time.when
onChange=(action "onChangeWhenTime")
}}
{{date-input
date=searchedTerms.time.days
onChange=(action "onChangeWhenDate")
id="search-post-date"
}}
</div>
</div>
<div class="control-group pull-left">

View File

@ -8,7 +8,8 @@
<label>{{i18n "shared_drafts.destination_category"}}</label>
{{category-chooser
value=topic.destination_category_id
onChooseCategory=(action "updateDestinationCategory")}}
onChange=(action "updateDestinationCategory")
}}
</div>
<div class='publish-field'>

View File

@ -54,7 +54,10 @@
</div>
{{#if showNotificationsButton}}
{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}
{{topic-notifications-button
notificationLevel=topic.details.notification_level
topic=topic
}}
{{/if}}
{{plugin-outlet name="after-topic-footer-buttons"

View File

@ -1,6 +1,12 @@
<label class="control-label" for="{{concat 'user-' elementId}}">{{{field.name}}} {{#if field.required}}<span class='required'>*</span>{{/if}}
</label>
<div class='controls'>
{{combo-box id=(concat 'user-' elementId) content=field.options value=value none=noneLabel}}
{{combo-box
id=(concat 'user-' elementId)
content=field.options
value=value
none=noneLabel
onChange=(action (mut value))
}}
<div class="instructions">{{{field.description}}}</div>
</div>

View File

@ -73,16 +73,28 @@
{{#if model.showCategoryChooser}}
<div class="category-input">
{{category-chooser
fullWidthOnMobile=true
value=model.categoryId
scopedCategoryId=scopedCategoryId
tabindex="3"
onChange=(action (mut model.categoryId))
isDisabled=disableCategoryChooser
tabindex="3"}}
options=(hash
scopedCategoryId=scopedCategoryId
)
}}
{{popup-input-tip validation=categoryValidation}}
</div>
{{/if}}
{{#if canEditTags}}
{{mini-tag-chooser tags=model.tags tabindex="4" categoryId=model.categoryId minimum=model.minimumRequiredTags isDisabled=disableTagsChooser}}
{{mini-tag-chooser
value=model.tags
tabindex=4
isDisabled=disableTagsChooser
onChange=(action (mut model.tags))
options=(hash
categoryId=model.categoryId
minimum=model.minimumRequiredTags
)
}}
{{popup-input-tip validation=tagValidation}}
{{/if}}
</div>

View File

@ -55,7 +55,12 @@
<span class='desc'>
{{i18n "search.sort_by"}}
</span>
{{combo-box value=sortOrder content=sortOrders castInteger=true}}
{{combo-box
value=sortOrder
content=sortOrders
castInteger=true
onChange=(action (mut sortOrder))
}}
</div>
</div>
{{/if}}
@ -194,12 +199,18 @@
{{#if site.mobileView}}
{{#if expanded}}
<div class="search-advanced-filters">
{{search-advanced-options searchTerm=searchTerm isExpanded=expanded}}
{{search-advanced-options
searchTerm=searchTerm
isExpanded=expanded
}}
</div>
{{/if}}
{{else}}
<div class="search-advanced-filters">
{{search-advanced-options searchTerm=searchTerm isExpanded=true}}
{{search-advanced-options
searchTerm=searchTerm
isExpanded=true
}}
{{d-button
label="submit"

View File

@ -63,11 +63,13 @@
<td>
{{#if canManageGroup}}
{{group-member-dropdown
removeMember=(action "removeMember")
makeOwner=(action "makeOwner")
removeOwner=(action "removeOwner")
member=m
group=model}}
removeMember=(action "removeMember")
makeOwner=(action "makeOwner")
removeOwner=(action "removeOwner")
member=m
group=model
onChange=(action "actOnGroup" m)
}}
{{/if}}
{{!-- group parameter is used by plugins --}}
</td>

View File

@ -12,12 +12,16 @@
placeholderKey="groups.index.all"
class="groups-header-filters-name no-blur"}}
{{combo-box value=type
content=types
clearable=true
allowAutoSelectFirst=false
noneLabel="groups.index.filter"
class="groups-header-filters-type"}}
{{combo-box
value=type
content=types
class="groups-header-filters-type"
onChange=(action (mut type))
options=(hash
clearable=true
none="groups.index.filter"
)
}}
</div>
</div>

View File

@ -7,9 +7,7 @@
<div class="group-members-manage">
{{#if canManageGroup}}
{{#if currentUser.admin}}
{{group-members-dropdown
showAddMembersModal=(route-action "showAddMembersModal")
showBulkAddModal=(route-action "showBulkAddModal")}}
{{group-members-dropdown onChange=(action "groupMembersDropdown")}}
{{else}}
{{d-button
icon="plus"

View File

@ -4,7 +4,7 @@
{{plugin-outlet name="users-top" connectorTagName='div' args=(hash model=model)}}
<div class='clearfix user-controls'>
{{period-chooser period=period}}
{{period-chooser period=period onChange=(action (mut period))}}
{{text-field value=nameInput placeholderKey="directory.filter_name" class="filter-name no-blur"}}
</div>

View File

@ -24,17 +24,17 @@
{{#if userHasTimezoneSet}}
{{#tap-tile-grid activeTile=selectedReminderType as |grid|}}
{{#if usingMobileDevice}}
<!-- {{tap-tile icon="desktop" text=(i18n "bookmarks.reminders.at_desktop") tileId=reminderTypes.AT_DESKTOP activeTile=grid.activeTile onSelect=(action "selectReminderType")}} -->
<!-- {{tap-tile icon="desktop" text=(i18n "bookmarks.reminders.at_desktop") tileId=reminderTypes.AT_DESKTOP activeTile=grid.activeTile onChange=(action "selectReminderType")}} -->
{{/if}}
{{#if showLaterToday}}
{{tap-tile icon="far-moon" text=laterTodayFormatted tileId=reminderTypes.LATER_TODAY activeTile=grid.activeTile onSelect=(action "selectReminderType")}}
{{tap-tile icon="far-moon" text=laterTodayFormatted tileId=reminderTypes.LATER_TODAY activeTile=grid.activeTile onChange=(action "selectReminderType")}}
{{/if}}
{{tap-tile icon="briefcase" text=nextBusinessDayFormatted tileId=reminderTypes.NEXT_BUSINESS_DAY activeTile=grid.activeTile onSelect=(action "selectReminderType")}}
{{tap-tile icon="far-sun" text=tomorrowFormatted tileId=reminderTypes.TOMORROW activeTile=grid.activeTile onSelect=(action "selectReminderType")}}
{{tap-tile icon="far-clock" text=nextWeekFormatted tileId=reminderTypes.NEXT_WEEK activeTile=grid.activeTile onSelect=(action "selectReminderType")}}
{{tap-tile icon="far-calendar-plus" text=nextMonthFormatted tileId=reminderTypes.NEXT_MONTH activeTile=grid.activeTile onSelect=(action "selectReminderType")}}
<!-- {{tap-tile icon="calendar-alt" text=(I18n "bookmarks.reminders.custom") tileId=reminderTypes.CUSTOM activeTile=grid.activeTile onSelect=(action "selectReminderType")}} -->
{{tap-tile icon="briefcase" text=nextBusinessDayFormatted tileId=reminderTypes.NEXT_BUSINESS_DAY activeTile=grid.activeTile onChange=(action "selectReminderType")}}
{{tap-tile icon="far-sun" text=tomorrowFormatted tileId=reminderTypes.TOMORROW activeTile=grid.activeTile onChange=(action "selectReminderType")}}
{{tap-tile icon="far-clock" text=nextWeekFormatted tileId=reminderTypes.NEXT_WEEK activeTile=grid.activeTile onChange=(action "selectReminderType")}}
{{tap-tile icon="far-calendar-plus" text=nextMonthFormatted tileId=reminderTypes.NEXT_MONTH activeTile=grid.activeTile onChange=(action "selectReminderType")}}
<!-- {{tap-tile icon="calendar-alt" text=(I18n "bookmarks.reminders.custom") tileId=reminderTypes.CUSTOM activeTile=grid.activeTile onChange=(action "selectReminderType")}} -->
{{/tap-tile-grid}}
{{else}}
<div class="alert alert-info">{{{i18n "bookmarks.no_timezone" basePath=basePath }}}</div>

View File

@ -1,6 +1,11 @@
<p>{{i18n "topics.bulk.choose_new_category"}}</p>
<p>{{category-chooser value=newCategoryId}}</p>
<p>
{{category-chooser
value=newCategoryId
onChange=(action (mut newCategoryId))
}}
</p>
{{#conditional-loading-spinner condition=loading}}
{{d-button action=(action "changeCategory") label="topics.bulk.change_category"}}

View File

@ -11,7 +11,7 @@
{{date-picker-past
value=(unbound date)
containerId="date-container"
onSelect=(action (mut date))}}
onChange=(action (mut date))}}
{{input type="time" value=time}}
</form>

Some files were not shown because too many files have changed in this diff Show More