mirror of
https://github.com/discourse/discourse.git
synced 2024-11-27 05:03:37 +08:00
FEATURE: implements minimum selection for select-kit
This commit is contained in:
parent
cd6a99a027
commit
f0fe16d824
|
@ -15,7 +15,7 @@
|
|||
{{tag-chooser
|
||||
tags=model.parent_tag_name
|
||||
everyTag=true
|
||||
limit=1
|
||||
maximum=1
|
||||
allowCreate=true
|
||||
filterPlaceholder="tagging.groups.parent_tag_placeholder"}}
|
||||
<span class="description">{{i18n 'tagging.groups.parent_tag_description'}}</span>
|
||||
|
|
|
@ -34,12 +34,12 @@ export default ComboBox.extend(Tags, {
|
|||
});
|
||||
});
|
||||
|
||||
this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
|
||||
this.set("maximum", parseInt(this.get("limit") || this.get("maximum") || this.get("siteSettings.max_tags_per_topic")));
|
||||
},
|
||||
|
||||
@computed("hasReachedLimit")
|
||||
caretIcon(hasReachedLimit) {
|
||||
return hasReachedLimit ? null : "plus fa-fw";
|
||||
@computed("hasReachedMaximum")
|
||||
caretIcon(hasReachedMaximum) {
|
||||
return hasReachedMaximum ? null : "plus fa-fw";
|
||||
},
|
||||
|
||||
@computed("tags")
|
||||
|
@ -135,6 +135,12 @@ export default ComboBox.extend(Tags, {
|
|||
content.label = joinedTags;
|
||||
}
|
||||
|
||||
if (!this.get("hasReachedMinimum") && isEmpty(this.get("selection"))) {
|
||||
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
|
||||
const label = I18n.t(key, { count: this.get("minimum") });
|
||||
content.title = content.name = content.label = label;
|
||||
}
|
||||
|
||||
content.title = content.name = content.value = joinedTags;
|
||||
|
||||
return content;
|
||||
|
|
|
@ -140,10 +140,23 @@ export default SelectKitComponent.extend({
|
|||
},
|
||||
|
||||
computeHeaderContent() {
|
||||
return {
|
||||
let content = {
|
||||
title: this.get("title"),
|
||||
selection: this.get("selection")
|
||||
};
|
||||
|
||||
if (this.get("noneLabel")) {
|
||||
if (!this.get("hasSelection")) {
|
||||
content.title = content.name = content.label = I18n.t(this.get("noneLabel"));
|
||||
}
|
||||
} else {
|
||||
if (!this.get("hasReachedMinimum")) {
|
||||
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
|
||||
content.title = content.name = content.label = I18n.t(key, { count: this.get("minimum") });
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed("filter")
|
||||
|
@ -154,7 +167,7 @@ export default SelectKitComponent.extend({
|
|||
},
|
||||
|
||||
validateSelect() {
|
||||
return this._super() && !this.get("hasReachedLimit");
|
||||
return this._super() && !this.get("hasReachedMaximum");
|
||||
},
|
||||
|
||||
@computed("computedValues.[]", "computedContent.[]")
|
||||
|
|
|
@ -25,7 +25,8 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
|||
"isLeftAligned",
|
||||
"isRightAligned",
|
||||
"hasSelection",
|
||||
"hasReachedLimit",
|
||||
"hasReachedMaximum",
|
||||
"hasReachedMinimum",
|
||||
],
|
||||
isDisabled: false,
|
||||
isExpanded: false,
|
||||
|
@ -71,6 +72,10 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
|||
collectionHeader: null,
|
||||
allowAutoSelectFirst: true,
|
||||
highlightedSelection: null,
|
||||
maximum: null,
|
||||
minimum: null,
|
||||
minimumLabel: null,
|
||||
maximumLabel: null,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
@ -188,14 +193,22 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
|||
}
|
||||
},
|
||||
|
||||
validateCreate() { return !this.get("hasReachedLimit"); },
|
||||
validateCreate() { return !this.get("hasReachedMaximum"); },
|
||||
|
||||
validateSelect() { return !this.get("hasReachedLimit"); },
|
||||
validateSelect() { return !this.get("hasReachedMaximum"); },
|
||||
|
||||
@computed("limit", "selection.[]")
|
||||
hasReachedLimit(limit, selection) {
|
||||
if (!limit) return false;
|
||||
return selection.length >= limit;
|
||||
@computed("maximum", "selection.[]")
|
||||
hasReachedMaximum(maximum, selection) {
|
||||
if (!maximum) return false;
|
||||
selection = makeArray(selection);
|
||||
return selection.length >= maximum;
|
||||
},
|
||||
|
||||
@computed("minimum", "selection.[]")
|
||||
hasReachedMinimum(minimum, selection) {
|
||||
if (!minimum) return true;
|
||||
selection = makeArray(selection);
|
||||
return selection.length >= minimum;
|
||||
},
|
||||
|
||||
@computed("shouldFilter", "allowAny", "filter")
|
||||
|
@ -212,10 +225,16 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
|||
}
|
||||
},
|
||||
|
||||
@computed("hasReachedLimit", "limit")
|
||||
maxContentRow(hasReachedLimit, limit) {
|
||||
if (hasReachedLimit) {
|
||||
return I18n.t("select_kit.max_content_reached", { count: limit });
|
||||
@computed("hasReachedMaximum", "hasReachedMinimum", "isExpanded")
|
||||
validationMessage(hasReachedMaximum, hasReachedMinimum) {
|
||||
if (hasReachedMaximum && this.get("maximum")) {
|
||||
const key = this.get("maximumLabel") || "select_kit.max_content_reached";
|
||||
return I18n.t(key, { count: this.get("maximum") });
|
||||
}
|
||||
|
||||
if (!hasReachedMinimum && this.get("minimum")) {
|
||||
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
|
||||
return I18n.t(key, { count: this.get("minimum") });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -227,9 +246,9 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
|||
return false;
|
||||
},
|
||||
|
||||
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedLimit", "isLoading")
|
||||
shouldDisplayCreateRow(computedValue, filter, collectionComputedContent, hasReachedLimit, isLoading) {
|
||||
if (isLoading || hasReachedLimit) return false;
|
||||
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedMaximum", "isLoading")
|
||||
shouldDisplayCreateRow(computedValue, filter, collectionComputedContent, hasReachedMaximum, isLoading) {
|
||||
if (isLoading || hasReachedMaximum) return false;
|
||||
if (collectionComputedContent.map(c => c.value).includes(filter)) return false;
|
||||
if (this.get("allowAny") && filter.length > 0 && this.validateCreate(filter)) return true;
|
||||
return false;
|
||||
|
|
|
@ -91,7 +91,7 @@ export default SelectKitComponent.extend({
|
|||
name: this.get("selection.name") || this.get("noneRowComputedContent.name")
|
||||
};
|
||||
|
||||
if (!this.get("hasSelection") && this.get("noneLabel")) {
|
||||
if (this.get("noneLabel") && !this.get("hasSelection")) {
|
||||
content.title = content.name = I18n.t(this.get("noneLabel"));
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ export default SelectKitComponent.extend({
|
|||
return selection !== this.get("noneRowComputedContent") && !isNone(selection);
|
||||
},
|
||||
|
||||
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedLimit")
|
||||
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedMaximum", "hasReachedMinimum")
|
||||
shouldDisplayCreateRow(computedValue, filter) {
|
||||
return this._super() && computedValue !== filter;
|
||||
},
|
||||
|
|
|
@ -37,7 +37,7 @@ export default MultiSelectComponent.extend(Tags, {
|
|||
});
|
||||
|
||||
if (!this.get("unlimitedTagCount")) {
|
||||
this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
|
||||
this.set("maximum", parseInt(this.get("limit") || this.get("maximum") || this.get("siteSettings.max_tags_per_topic")));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ export default Ember.Mixin.create({
|
|||
},
|
||||
|
||||
validateCreate(term) {
|
||||
if (this.get("hasReachedLimit") || !this.site.get("can_create_tag")) {
|
||||
if (this.get("hasReachedMaximum") || !this.site.get("can_create_tag")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
computedValue=computedValue
|
||||
rowComponentOptions=rowComponentOptions
|
||||
noContentRow=noContentRow
|
||||
maxContentRow=maxContentRow
|
||||
validationMessage=validationMessage
|
||||
}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
|
|
@ -30,26 +30,25 @@
|
|||
}}
|
||||
{{/if}}
|
||||
|
||||
{{#if maxContentRow}}
|
||||
<li class="select-kit-row max-content">
|
||||
{{maxContentRow}}
|
||||
{{#if noContentRow}}
|
||||
<li class="select-kit-row no-content">
|
||||
{{noContentRow}}
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if noContentRow}}
|
||||
<li class="select-kit-row no-content">
|
||||
{{noContentRow}}
|
||||
</li>
|
||||
{{else}}
|
||||
{{#each collectionComputedContent as |computedContent|}}
|
||||
{{component rowComponent
|
||||
computedContent=computedContent
|
||||
highlighted=highlighted
|
||||
computedValue=computedValue
|
||||
templateForRow=templateForRow
|
||||
onClickRow=onClickRow
|
||||
onMouseoverRow=onMouseoverRow
|
||||
options=rowComponentOptions
|
||||
}}
|
||||
{{/each}}
|
||||
{{#if validationMessage}}
|
||||
<div class="validation-message">
|
||||
{{validationMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#each collectionComputedContent as |computedContent|}}
|
||||
{{component rowComponent
|
||||
computedContent=computedContent
|
||||
highlighted=highlighted
|
||||
computedValue=computedValue
|
||||
templateForRow=templateForRow
|
||||
onClickRow=onClickRow
|
||||
onMouseoverRow=onMouseoverRow
|
||||
options=rowComponentOptions
|
||||
}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
computedValue=computedValue
|
||||
rowComponentOptions=rowComponentOptions
|
||||
noContentRow=noContentRow
|
||||
maxContentRow=maxContentRow
|
||||
validationMessage=validationMessage
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -171,11 +171,6 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.max-content {
|
||||
white-space: nowrap;
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
@ -211,6 +206,14 @@
|
|||
padding: 0;
|
||||
max-height: 200px;
|
||||
|
||||
.validation-message {
|
||||
white-space: nowrap;
|
||||
color: $danger;
|
||||
flex: 1 0 auto;
|
||||
margin: 5px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.select-kit-collection {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
|
|
@ -1250,7 +1250,8 @@ en:
|
|||
no_content: No matches found
|
||||
filter_placeholder: Search...
|
||||
create: "Create: '{{content}}'"
|
||||
max_content_reached: "You can only select {{count}} items."
|
||||
max_content_reached: "You can only select {{count}} item(s)."
|
||||
min_content_not_reached: "Select at least {{count}} item(s)."
|
||||
|
||||
emoji_picker:
|
||||
filter_placeholder: Search for emoji
|
||||
|
|
|
@ -160,3 +160,44 @@ componentTest('with limitMatches', {
|
|||
andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('with minimum', {
|
||||
template: '{{multi-select content=content minimum=1}}',
|
||||
|
||||
beforeEach() {
|
||||
this.set('content', ['sam', 'jeff', 'neil']);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
this.get('subject').expand();
|
||||
|
||||
andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item(s).'));
|
||||
|
||||
this.get('subject').selectRowByValue('sam');
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(this.get('subject').header().label(), 'sam');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('with minimumLabel', {
|
||||
template: '{{multi-select content=content minimum=1 minimumLabel="test.minimum"}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' };
|
||||
this.set('content', ['sam', 'jeff', 'neil']);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
this.get('subject').expand();
|
||||
|
||||
andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1'));
|
||||
|
||||
this.get('subject').selectRowByValue('jeff');
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(this.get('subject').header().label(), 'jeff');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -501,3 +501,44 @@ componentTest('with limitMatches', {
|
|||
andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2));
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('with minimum', {
|
||||
template: '{{single-select content=content minimum=1 allowAutoSelectFirst=false}}',
|
||||
|
||||
beforeEach() {
|
||||
this.set('content', ['sam', 'jeff', 'neil']);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
this.get('subject').expand();
|
||||
|
||||
andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item(s).'));
|
||||
|
||||
this.get('subject').selectRowByValue('sam');
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(this.get('subject').header().label(), 'sam');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('with minimumLabel', {
|
||||
template: '{{single-select content=content minimum=1 minimumLabel="test.minimum" allowAutoSelectFirst=false}}',
|
||||
|
||||
beforeEach() {
|
||||
I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' };
|
||||
this.set('content', ['sam', 'jeff', 'neil']);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
this.get('subject').expand();
|
||||
|
||||
andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1'));
|
||||
|
||||
this.get('subject').selectRowByValue('jeff');
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(this.get('subject').header().label(), 'jeff');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -63,6 +63,7 @@ function selectKit(selector) { // eslint-disable-line no-unused-vars
|
|||
return {
|
||||
value: function() { return header.attr('data-value'); },
|
||||
name: function() { return header.attr('data-name'); },
|
||||
label: function() { return header.text().trim(); },
|
||||
icon: function() { return header.find('.icon'); },
|
||||
title: function() { return header.attr('title'); },
|
||||
el: function() { return header; }
|
||||
|
@ -183,6 +184,16 @@ function selectKit(selector) { // eslint-disable-line no-unused-vars
|
|||
return rowHelper(find(selector).find('.select-kit-row.none'));
|
||||
},
|
||||
|
||||
validationMessage: function() {
|
||||
var validationMessage = find(selector).find('.validation-message');
|
||||
|
||||
if (validationMessage.length) {
|
||||
return validationMessage.html().trim();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
selectedRow: function() {
|
||||
return rowHelper(find(selector).find('.select-kit-row.is-selected'));
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user