discourse/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js

402 lines
9.6 KiB
JavaScript

import Controller from "@ember/controller";
import EmberObject, { action } from "@ember/object";
import { gt, or } from "@ember/object/computed";
import { next } from "@ember/runloop";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import I18n from "I18n";
export const BAR_CHART_TYPE = "bar";
export const PIE_CHART_TYPE = "pie";
export const REGULAR_POLL_TYPE = "regular";
export const NUMBER_POLL_TYPE = "number";
export const MULTIPLE_POLL_TYPE = "multiple";
const ALWAYS_POLL_RESULT = "always";
const VOTE_POLL_RESULT = "on_vote";
const CLOSED_POLL_RESULT = "on_close";
const STAFF_POLL_RESULT = "staff_only";
export default Controller.extend(ModalFunctionality, {
showAdvanced: false,
pollType: REGULAR_POLL_TYPE,
pollTitle: "",
pollOptions: null,
pollOptionsText: null,
pollMin: 1,
pollMax: 2,
pollStep: 1,
pollGroups: null,
pollAutoClose: null,
pollResult: ALWAYS_POLL_RESULT,
chartType: BAR_CHART_TYPE,
publicPoll: null,
onShow() {
this.setProperties({
showAdvanced: false,
pollType: REGULAR_POLL_TYPE,
pollTitle: null,
pollOptions: [EmberObject.create({ value: "" })],
pollOptionsText: "",
pollMin: 1,
pollMax: 2,
pollStep: 1,
pollGroups: null,
pollAutoClose: null,
pollResult: ALWAYS_POLL_RESULT,
chartType: BAR_CHART_TYPE,
publicPoll: false,
});
},
@discourseComputed
pollResults() {
const options = [
{
name: I18n.t("poll.ui_builder.poll_result.always"),
value: ALWAYS_POLL_RESULT,
},
{
name: I18n.t("poll.ui_builder.poll_result.vote"),
value: VOTE_POLL_RESULT,
},
{
name: I18n.t("poll.ui_builder.poll_result.closed"),
value: CLOSED_POLL_RESULT,
},
];
if (this.get("currentUser.staff")) {
options.push({
name: I18n.t("poll.ui_builder.poll_result.staff"),
value: STAFF_POLL_RESULT,
});
}
return options;
},
@discourseComputed("pollType")
isRegular(pollType) {
return pollType === REGULAR_POLL_TYPE;
},
@discourseComputed("pollType")
isNumber(pollType) {
return pollType === NUMBER_POLL_TYPE;
},
@discourseComputed("pollType")
isMultiple(pollType) {
return pollType === MULTIPLE_POLL_TYPE;
},
showNumber: or("showAdvanced", "isNumber"),
@discourseComputed("pollOptions.@each.value")
pollOptionsCount(pollOptions) {
return (pollOptions || []).filter((option) => option.value.length > 0)
.length;
},
@discourseComputed("site.groups")
siteGroups(groups) {
// prevents group "everyone" to be listed
return groups.filter((g) => g.id !== 0);
},
@discourseComputed("chartType", "pollType")
isPie(chartType, pollType) {
return pollType !== NUMBER_POLL_TYPE && chartType === PIE_CHART_TYPE;
},
canRemoveOption: gt("pollOptions.length", 1),
@observes("pollType", "pollOptionsCount")
_setPollMinMax() {
if (this.isMultiple) {
if (
this.pollMin <= 0 ||
this.pollMin >= this.pollMax ||
this.pollMin >= this.pollOptionsCount
) {
this.set("pollMin", this.pollOptionsCount > 0 ? 1 : 0);
}
if (
this.pollMax <= 0 ||
this.pollMin >= this.pollMax ||
this.pollMax > this.pollOptionsCount
) {
this.set("pollMax", this.pollOptionsCount);
}
} else if (this.isNumber) {
this.set("pollMax", this.siteSettings.poll_maximum_options);
}
},
@discourseComputed(
"pollType",
"pollResult",
"publicPoll",
"pollTitle",
"pollOptions.@each.value",
"pollMin",
"pollMax",
"pollStep",
"pollGroups",
"pollAutoClose",
"chartType"
)
pollOutput(
pollType,
pollResult,
publicPoll,
pollTitle,
pollOptions,
pollMin,
pollMax,
pollStep,
pollGroups,
pollAutoClose,
chartType
) {
let pollHeader = "[poll";
let output = "";
const match = this.toolbarEvent
.getText()
.match(/\[poll(\s+name=[^\s\]]+)*.*\]/gim);
if (match) {
pollHeader += ` name=poll${match.length + 1}`;
}
let step = pollStep;
if (step < 1) {
step = 1;
}
if (pollType) {
pollHeader += ` type=${pollType}`;
}
if (pollResult) {
pollHeader += ` results=${pollResult}`;
}
if (pollMin && pollType !== REGULAR_POLL_TYPE) {
pollHeader += ` min=${pollMin}`;
}
if (pollMax && pollType !== REGULAR_POLL_TYPE) {
pollHeader += ` max=${pollMax}`;
}
if (pollType === NUMBER_POLL_TYPE) {
pollHeader += ` step=${step}`;
}
if (publicPoll) {
pollHeader += ` public=true`;
}
if (chartType && pollType !== NUMBER_POLL_TYPE) {
pollHeader += ` chartType=${chartType}`;
}
if (pollGroups && pollGroups.length > 0) {
pollHeader += ` groups=${pollGroups}`;
}
if (pollAutoClose) {
pollHeader += ` close=${pollAutoClose.toISOString()}`;
}
pollHeader += "]";
output += `${pollHeader}\n`;
if (pollTitle) {
output += `# ${pollTitle.trim()}\n`;
}
if (pollOptions.length > 0 && pollType !== NUMBER_POLL_TYPE) {
pollOptions.forEach((option) => {
if (option.value.length > 0) {
output += `* ${option.value.trim()}\n`;
}
});
}
output += "[/poll]\n";
return output;
},
@discourseComputed("isNumber", "pollOptionsCount")
minNumOfOptionsValidation(isNumber, pollOptionsCount) {
let options = { ok: true };
if (!isNumber) {
if (pollOptionsCount < 1) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.options_min_count"),
});
}
if (pollOptionsCount > this.siteSettings.poll_maximum_options) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.options_max_count", {
count: this.siteSettings.poll_maximum_options,
}),
});
}
}
return EmberObject.create(options);
},
@discourseComputed("pollOptions.@each.value")
showMinNumOfOptionsValidation(pollOptions) {
return pollOptions.length !== 1 || pollOptions[0].value !== "";
},
@discourseComputed(
"isMultiple",
"pollOptionsCount",
"isNumber",
"pollMin",
"pollMax",
"pollStep"
)
minMaxValueValidation(
isMultiple,
pollOptionsCount,
isNumber,
pollMin,
pollMax,
pollStep
) {
pollMin = parseInt(pollMin, 10) || 0;
pollMax = parseInt(pollMax, 10) || 0;
pollStep = parseInt(pollStep, 10) || 0;
if (pollMin < 0) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.invalid_min_value"),
});
}
if (pollMax < 0 || (isMultiple && pollMax > pollOptionsCount)) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.invalid_max_value"),
});
}
if (pollMin > pollMax) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.invalid_values"),
});
}
if (isNumber) {
if (pollStep < 1) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.min_step_value"),
});
}
const optionsCount = (pollMax - pollMin + 1) / pollStep;
if (optionsCount < 1) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.options_min_count"),
});
}
if (optionsCount > this.siteSettings.poll_maximum_options) {
return EmberObject.create({
failed: true,
reason: I18n.t("poll.ui_builder.help.options_max_count", {
count: this.siteSettings.poll_maximum_options,
}),
});
}
}
return EmberObject.create({ ok: true });
},
@discourseComputed("minMaxValueValidation", "minNumOfOptionsValidation")
disableInsert(minMaxValueValidation, minNumOfOptionsValidation) {
return !minMaxValueValidation.ok || !minNumOfOptionsValidation.ok;
},
_comboboxOptions(startIndex, endIndex) {
return [...Array(endIndex - startIndex).keys()].map((number) => ({
value: number + startIndex,
name: number + startIndex,
}));
},
@action
onOptionsTextChange(e) {
let idx = 0;
this.set(
"pollOptions",
e.target.value
.split("\n")
.map((value) => EmberObject.create({ idx: idx++, value }))
);
},
@action
insertPoll() {
this.toolbarEvent.addText(this.pollOutput);
this.send("closeModal");
},
@action
toggleAdvanced() {
this.toggleProperty("showAdvanced");
if (this.showAdvanced) {
this.set(
"pollOptionsText",
this.pollOptions.map((x) => x.value).join("\n")
);
}
},
@action
addOption(beforeOption, value, e) {
if (value !== "") {
const idx = this.pollOptions.indexOf(beforeOption) + 1;
const option = EmberObject.create({ value: "" });
this.pollOptions.insertAt(idx, option);
let lastOptionIdx = 0;
this.pollOptions.forEach((o) => o.set("idx", lastOptionIdx++));
next(() => {
const pollOptions = document.getElementsByClassName("poll-options");
if (pollOptions) {
const inputs = pollOptions[0].getElementsByTagName("input");
if (option.idx < inputs.length) {
inputs[option.idx].focus();
}
}
});
}
if (e) {
e.preventDefault();
}
},
@action
removeOption(option) {
this.pollOptions.removeObject(option);
},
});