mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 14:12:10 +08:00
DEV: [gjs-codemod] merge js and hbs
This commit is contained in:
@ -1,3 +1,9 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class Checkbox extends Component {}
|
||||||
|
|
||||||
<label class="wizard-container__label">
|
<label class="wizard-container__label">
|
||||||
<PluginOutlet
|
<PluginOutlet
|
||||||
@name="wizard-checkbox"
|
@name="wizard-checkbox"
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class Checkbox extends Component {}
|
|
@ -1,3 +1,37 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { action, set } from "@ember/object";
|
||||||
|
|
||||||
|
export default class Checkboxes extends Component {
|
||||||
|
init(...args) {
|
||||||
|
super.init(...args);
|
||||||
|
this.set("field.value", this.field.value || []);
|
||||||
|
|
||||||
|
for (let choice of this.field.choices) {
|
||||||
|
if (this.field.value.includes(choice.id)) {
|
||||||
|
set(choice, "checked", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
changed(checkbox) {
|
||||||
|
let newFieldValue = this.field.value;
|
||||||
|
const checkboxValue = checkbox.parentElement
|
||||||
|
.getAttribute("value")
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (checkbox.checked) {
|
||||||
|
newFieldValue.push(checkboxValue);
|
||||||
|
} else {
|
||||||
|
const index = newFieldValue.indexOf(checkboxValue);
|
||||||
|
if (index > -1) {
|
||||||
|
newFieldValue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.set("field.value", newFieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#each this.field.choices as |c|}}
|
{{#each this.field.choices as |c|}}
|
||||||
<div class="checkbox-field-choice {{this.fieldClass}}">
|
<div class="checkbox-field-choice {{this.fieldClass}}">
|
||||||
<label id={{c.id}} value={{c.label}}>
|
<label id={{c.id}} value={{c.label}}>
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { action, set } from "@ember/object";
|
|
||||||
|
|
||||||
export default class Checkboxes extends Component {
|
|
||||||
init(...args) {
|
|
||||||
super.init(...args);
|
|
||||||
this.set("field.value", this.field.value || []);
|
|
||||||
|
|
||||||
for (let choice of this.field.choices) {
|
|
||||||
if (this.field.value.includes(choice.id)) {
|
|
||||||
set(choice, "checked", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
changed(checkbox) {
|
|
||||||
let newFieldValue = this.field.value;
|
|
||||||
const checkboxValue = checkbox.parentElement
|
|
||||||
.getAttribute("value")
|
|
||||||
.toLowerCase();
|
|
||||||
|
|
||||||
if (checkbox.checked) {
|
|
||||||
newFieldValue.push(checkboxValue);
|
|
||||||
} else {
|
|
||||||
const index = newFieldValue.indexOf(checkboxValue);
|
|
||||||
if (index > -1) {
|
|
||||||
newFieldValue.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.set("field.value", newFieldValue);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1,7 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { classNameBindings } from "@ember-decorators/component";
|
||||||
|
|
||||||
|
@classNameBindings(":wizard-image-preview", "fieldClass")
|
||||||
|
export default class Generic extends Component {}
|
||||||
|
|
||||||
<img src={{this.field.value}} class={{this.fieldClass}} />
|
<img src={{this.field.value}} class={{this.fieldClass}} />
|
@ -1,5 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { classNameBindings } from "@ember-decorators/component";
|
|
||||||
|
|
||||||
@classNameBindings(":wizard-image-preview", "fieldClass")
|
|
||||||
export default class Generic extends Component {}
|
|
@ -1,3 +1,463 @@
|
|||||||
|
/*eslint no-bitwise:0 */
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { Promise } from "rsvp";
|
||||||
|
import getUrl from "discourse/lib/get-url";
|
||||||
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import { darkLightDiff, drawHeader } from "../../../lib/preview";
|
||||||
|
|
||||||
|
const scaled = {};
|
||||||
|
|
||||||
|
function canvasFor(image, w, h) {
|
||||||
|
w = Math.ceil(w);
|
||||||
|
h = Math.ceil(h);
|
||||||
|
|
||||||
|
const scale = window.devicePixelRatio;
|
||||||
|
|
||||||
|
const can = document.createElement("canvas");
|
||||||
|
can.width = w * scale;
|
||||||
|
can.height = h * scale;
|
||||||
|
|
||||||
|
const ctx = can.getContext("2d");
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
ctx.drawImage(image, 0, 0, w, h);
|
||||||
|
return can;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scale = window.devicePixelRatio;
|
||||||
|
export default class PreviewBase extends Component {
|
||||||
|
ctx = null;
|
||||||
|
loaded = false;
|
||||||
|
loadingFontVariants = false;
|
||||||
|
|
||||||
|
get elementWidth() {
|
||||||
|
return this.width * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
get elementHeight() {
|
||||||
|
return this.height * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canvasStyle() {
|
||||||
|
return htmlSafe(`width:${this.width}px;height:${this.height}px`);
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
super.didInsertElement(...arguments);
|
||||||
|
this.fontMap = PreloadStore.get("fontMap");
|
||||||
|
this.loadedFonts = new Set();
|
||||||
|
const c = this.element.querySelector("canvas");
|
||||||
|
this.ctx = c.getContext("2d");
|
||||||
|
this.ctx.scale(scale, scale);
|
||||||
|
|
||||||
|
if (this.step) {
|
||||||
|
this.step.findField("color_scheme")?.addListener(this.themeChanged);
|
||||||
|
this.step.findField("homepage_style")?.addListener(this.themeChanged);
|
||||||
|
this.step.findField("body_font")?.addListener(this.themeBodyFontChanged);
|
||||||
|
this.step.findField("site_font")?.addListener(this.themeFontChanged);
|
||||||
|
this.step
|
||||||
|
.findField("heading_font")
|
||||||
|
?.addListener(this.themeHeadingFontChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
super.willDestroyElement(...arguments);
|
||||||
|
|
||||||
|
if (this.step) {
|
||||||
|
this.step.findField("color_scheme")?.removeListener(this.themeChanged);
|
||||||
|
this.step.findField("homepage_style")?.removeListener(this.themeChanged);
|
||||||
|
this.step
|
||||||
|
.findField("body_font")
|
||||||
|
?.removeListener(this.themeBodyFontChanged);
|
||||||
|
this.step.findField("site_font")?.removeListener(this.themeFontChanged);
|
||||||
|
this.step
|
||||||
|
.findField("heading_font")
|
||||||
|
?.removeListener(this.themeHeadingFontChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
themeChanged() {
|
||||||
|
this.triggerRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
themeBodyFontChanged() {
|
||||||
|
if (!this.loadingFontVariants) {
|
||||||
|
this.loadFontVariants(this.wizard.font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
themeHeadingFontChanged() {
|
||||||
|
if (!this.loadingFontVariants) {
|
||||||
|
this.loadFontVariants(this.wizard.headingFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
themeFontChanged() {
|
||||||
|
if (!this.loadingFontVariants) {
|
||||||
|
this.loadFontVariants(this.wizard.font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFontVariants(font) {
|
||||||
|
if (!font) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontVariantData = this.fontMap[font.id];
|
||||||
|
|
||||||
|
// System font for example does not need to load from a remote source.
|
||||||
|
if (!fontVariantData) {
|
||||||
|
this.loadedFonts.add(font.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontVariantData && !this.loadedFonts.has(font.id)) {
|
||||||
|
this.loadingFontVariants = true;
|
||||||
|
const fontFaces = fontVariantData.map((fontVariant) => {
|
||||||
|
return new FontFace(font.label, `url(${fontVariant.url})`, {
|
||||||
|
style: "normal",
|
||||||
|
weight: fontVariant.weight,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
fontFaces.map((fontFace) =>
|
||||||
|
fontFace.load().then((loadedFont) => {
|
||||||
|
document.fonts.add(loadedFont);
|
||||||
|
|
||||||
|
// We use our own Set because, though document.fonts.check is available,
|
||||||
|
// it does not seem very reliable, returning false for fonts that have
|
||||||
|
// definitely been loaded.
|
||||||
|
this.loadedFonts.add(font.id);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.triggerRepaint();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadingFontVariants = false;
|
||||||
|
});
|
||||||
|
} else if (this.loadedFonts.has(font.id)) {
|
||||||
|
return Promise.resolve(this.triggerRepaint());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
images() {}
|
||||||
|
|
||||||
|
// NOTE: This works for fonts included in a style that is actually using the
|
||||||
|
// @font-faces on load, but for fonts that we aren't using yet we need to
|
||||||
|
// make sure they are loaded before rendering the canvas via loadFontVariants.
|
||||||
|
loadFonts() {
|
||||||
|
return document.fonts.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadImages() {
|
||||||
|
const images = this.images();
|
||||||
|
if (images) {
|
||||||
|
return Promise.all(
|
||||||
|
Object.keys(images).map((id) => {
|
||||||
|
return loadImage(images[id]).then((img) => (this[id] = img));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
Promise.all([this.loadFonts(), this.loadImages()]).then(() => {
|
||||||
|
// NOTE: This must be done otherwise the "bold" variant of the body font
|
||||||
|
// will not be loaded for some reason before rendering the canvas.
|
||||||
|
//
|
||||||
|
// The header font does not suffer from this issue.
|
||||||
|
this.loadFontVariants(this.wizard.font).then(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
this.triggerRepaint();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerRepaint() {
|
||||||
|
scheduleOnce("afterRender", this, "repaint");
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint() {
|
||||||
|
if (!this.loaded) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorsArray = this.wizard.currentColors;
|
||||||
|
if (!colorsArray) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let colors = {};
|
||||||
|
colorsArray.forEach(function (c) {
|
||||||
|
const name = c.name;
|
||||||
|
colors[name] = `#${c.hex}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { font, headingFont } = this.wizard;
|
||||||
|
if (!font) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ctx } = this;
|
||||||
|
|
||||||
|
ctx.fillStyle = colors.secondary;
|
||||||
|
ctx.fillRect(0, 0, this.width, this.height);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
ctx,
|
||||||
|
colors,
|
||||||
|
font,
|
||||||
|
headingFont,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
};
|
||||||
|
this.paint(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
categories() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: i18n("wizard.homepage_preview.category_names.icebreakers"),
|
||||||
|
color: "#652D90",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n("wizard.homepage_preview.category_names.news"),
|
||||||
|
color: "#3AB54A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n("wizard.homepage_preview.category_names.site_feedback"),
|
||||||
|
color: "#25AAE2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleImage(image, x, y, w, h) {
|
||||||
|
w = Math.floor(w);
|
||||||
|
h = Math.floor(h);
|
||||||
|
|
||||||
|
const { ctx } = this;
|
||||||
|
|
||||||
|
const key = `${image.src}-${w}-${h}`;
|
||||||
|
|
||||||
|
if (!scaled[key]) {
|
||||||
|
let copy = image;
|
||||||
|
let ratio = copy.width / copy.height;
|
||||||
|
let newH = copy.height * 0.5;
|
||||||
|
while (newH > h) {
|
||||||
|
copy = canvasFor(copy, ratio * newH, newH);
|
||||||
|
newH = newH * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled[key] = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawImage(scaled[key], x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
get headerHeight() {
|
||||||
|
return this.height * 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawFullHeader(colors, font, logo) {
|
||||||
|
const { ctx } = this;
|
||||||
|
|
||||||
|
drawHeader(ctx, colors, this.width, this.headerHeight);
|
||||||
|
|
||||||
|
const avatarSize = this.height * 0.1;
|
||||||
|
const headerMargin = this.headerHeight * 0.2;
|
||||||
|
|
||||||
|
if (logo) {
|
||||||
|
const logoHeight = this.headerHeight - headerMargin * 2;
|
||||||
|
|
||||||
|
const ratio = logoHeight / logo.height;
|
||||||
|
this.scaleImage(
|
||||||
|
logo,
|
||||||
|
headerMargin,
|
||||||
|
headerMargin,
|
||||||
|
logo.width * ratio,
|
||||||
|
logoHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
this.scaleImage(logo, this.width, headerMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top right menu
|
||||||
|
this.scaleImage(
|
||||||
|
this.avatar,
|
||||||
|
this.width - avatarSize - headerMargin,
|
||||||
|
headerMargin,
|
||||||
|
avatarSize,
|
||||||
|
avatarSize
|
||||||
|
);
|
||||||
|
|
||||||
|
// accounts for hard-set color variables in solarized themes
|
||||||
|
ctx.fillStyle =
|
||||||
|
colors.primary_low_mid ||
|
||||||
|
darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
||||||
|
|
||||||
|
const pathScale = this.headerHeight / 1200;
|
||||||
|
const searchIcon = new Path2D(
|
||||||
|
"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"
|
||||||
|
);
|
||||||
|
const hamburgerIcon = new Path2D(
|
||||||
|
"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"
|
||||||
|
);
|
||||||
|
const chatIcon = new Path2D(
|
||||||
|
"M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z"
|
||||||
|
);
|
||||||
|
ctx.save(); // Save the previous state for translation and scale
|
||||||
|
ctx.translate(
|
||||||
|
this.width - avatarSize * 2 - headerMargin * 0.5,
|
||||||
|
avatarSize / 2
|
||||||
|
);
|
||||||
|
// need to scale paths otherwise they're too large
|
||||||
|
ctx.scale(pathScale, pathScale);
|
||||||
|
ctx.fill(searchIcon);
|
||||||
|
ctx.restore();
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(
|
||||||
|
this.width - avatarSize * 3 - headerMargin * 0.5,
|
||||||
|
avatarSize / 2
|
||||||
|
);
|
||||||
|
ctx.scale(pathScale, pathScale);
|
||||||
|
ctx.fill(chatIcon);
|
||||||
|
ctx.restore();
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(headerMargin * 1.75, avatarSize / 2);
|
||||||
|
ctx.scale(pathScale, pathScale);
|
||||||
|
ctx.fill(hamburgerIcon);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPills(colors, font, headerHeight, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
const { ctx } = this;
|
||||||
|
|
||||||
|
const badgeHeight = headerHeight * 2 * 0.25;
|
||||||
|
const headerMargin = headerHeight * 0.2;
|
||||||
|
const fontSize = Math.round(badgeHeight * 0.5);
|
||||||
|
ctx.font = `${fontSize}px '${font.label}'`;
|
||||||
|
|
||||||
|
const allCategoriesText = i18n(
|
||||||
|
"wizard.homepage_preview.nav_buttons.all_categories"
|
||||||
|
);
|
||||||
|
const categoriesWidth = ctx.measureText(allCategoriesText).width;
|
||||||
|
const categoriesBoxWidth = categoriesWidth + headerMargin * 2;
|
||||||
|
|
||||||
|
// Box around "all categories >"
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = colors.primary;
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.rect(
|
||||||
|
headerMargin,
|
||||||
|
headerHeight + headerMargin,
|
||||||
|
categoriesBoxWidth,
|
||||||
|
badgeHeight
|
||||||
|
);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
ctx.fillText(
|
||||||
|
allCategoriesText,
|
||||||
|
headerMargin * 1.5,
|
||||||
|
headerHeight + headerMargin * 1.4 + fontSize
|
||||||
|
);
|
||||||
|
|
||||||
|
// Caret (>) at the end of "all categories" box
|
||||||
|
const pathScale = badgeHeight / 1000;
|
||||||
|
const caretIcon = new Path2D(
|
||||||
|
"M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(
|
||||||
|
categoriesBoxWidth,
|
||||||
|
headerHeight + headerMargin + badgeHeight / 4
|
||||||
|
);
|
||||||
|
ctx.scale(pathScale, pathScale);
|
||||||
|
ctx.fill(caretIcon);
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
const categoryHomepage =
|
||||||
|
opts.homepageStyle.includes("category") ||
|
||||||
|
opts.homepageStyle.includes("categories");
|
||||||
|
|
||||||
|
// First top menu item
|
||||||
|
const otherHomepageText = i18n(
|
||||||
|
`wizard.top_menu_items.${opts.homepageStyle}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstTopMenuItemText = categoryHomepage
|
||||||
|
? i18n("wizard.top_menu_items.categories")
|
||||||
|
: otherHomepageText;
|
||||||
|
|
||||||
|
const newText = i18n("wizard.top_menu_items.new");
|
||||||
|
const unreadText = i18n("wizard.top_menu_items.unread");
|
||||||
|
const topText = i18n("wizard.top_menu_items.top");
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillStyle = colors.tertiary;
|
||||||
|
ctx.rect(
|
||||||
|
categoriesBoxWidth + headerMargin * 2,
|
||||||
|
headerHeight + headerMargin,
|
||||||
|
ctx.measureText(firstTopMenuItemText).width + headerMargin * 2,
|
||||||
|
badgeHeight
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.font = `${fontSize}px '${font.label}'`;
|
||||||
|
ctx.fillStyle = colors.secondary;
|
||||||
|
const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize;
|
||||||
|
const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth;
|
||||||
|
ctx.fillText(
|
||||||
|
firstTopMenuItemText,
|
||||||
|
firstTopMenuItemX,
|
||||||
|
pillButtonTextY,
|
||||||
|
ctx.measureText(firstTopMenuItemText).width
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
|
||||||
|
const newTextX =
|
||||||
|
firstTopMenuItemX +
|
||||||
|
ctx.measureText(firstTopMenuItemText).width +
|
||||||
|
headerMargin * 2.0;
|
||||||
|
ctx.fillText(newText, newTextX, pillButtonTextY);
|
||||||
|
|
||||||
|
const unreadTextX =
|
||||||
|
newTextX + ctx.measureText(newText).width + headerMargin * 2.0;
|
||||||
|
ctx.fillText(unreadText, unreadTextX, pillButtonTextY);
|
||||||
|
|
||||||
|
const topTextX =
|
||||||
|
unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0;
|
||||||
|
ctx.fillText(topText, topTextX, pillButtonTextY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadImage(src) {
|
||||||
|
if (!src) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.src = getUrl(src);
|
||||||
|
return new Promise((resolve) => (img.onload = () => resolve(img)));
|
||||||
|
}
|
||||||
|
|
||||||
<div class="wizard-container__preview">
|
<div class="wizard-container__preview">
|
||||||
<canvas
|
<canvas
|
||||||
width={{this.elementWidth}}
|
width={{this.elementWidth}}
|
||||||
|
@ -1,459 +0,0 @@
|
|||||||
/*eslint no-bitwise:0 */
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { scheduleOnce } from "@ember/runloop";
|
|
||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import { Promise } from "rsvp";
|
|
||||||
import getUrl from "discourse/lib/get-url";
|
|
||||||
import PreloadStore from "discourse/lib/preload-store";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import { darkLightDiff, drawHeader } from "../../../lib/preview";
|
|
||||||
|
|
||||||
const scaled = {};
|
|
||||||
|
|
||||||
function canvasFor(image, w, h) {
|
|
||||||
w = Math.ceil(w);
|
|
||||||
h = Math.ceil(h);
|
|
||||||
|
|
||||||
const scale = window.devicePixelRatio;
|
|
||||||
|
|
||||||
const can = document.createElement("canvas");
|
|
||||||
can.width = w * scale;
|
|
||||||
can.height = h * scale;
|
|
||||||
|
|
||||||
const ctx = can.getContext("2d");
|
|
||||||
ctx.scale(scale, scale);
|
|
||||||
ctx.drawImage(image, 0, 0, w, h);
|
|
||||||
return can;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scale = window.devicePixelRatio;
|
|
||||||
export default class PreviewBase extends Component {
|
|
||||||
ctx = null;
|
|
||||||
loaded = false;
|
|
||||||
loadingFontVariants = false;
|
|
||||||
|
|
||||||
get elementWidth() {
|
|
||||||
return this.width * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
get elementHeight() {
|
|
||||||
return this.height * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
get canvasStyle() {
|
|
||||||
return htmlSafe(`width:${this.width}px;height:${this.height}px`);
|
|
||||||
}
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
super.didInsertElement(...arguments);
|
|
||||||
this.fontMap = PreloadStore.get("fontMap");
|
|
||||||
this.loadedFonts = new Set();
|
|
||||||
const c = this.element.querySelector("canvas");
|
|
||||||
this.ctx = c.getContext("2d");
|
|
||||||
this.ctx.scale(scale, scale);
|
|
||||||
|
|
||||||
if (this.step) {
|
|
||||||
this.step.findField("color_scheme")?.addListener(this.themeChanged);
|
|
||||||
this.step.findField("homepage_style")?.addListener(this.themeChanged);
|
|
||||||
this.step.findField("body_font")?.addListener(this.themeBodyFontChanged);
|
|
||||||
this.step.findField("site_font")?.addListener(this.themeFontChanged);
|
|
||||||
this.step
|
|
||||||
.findField("heading_font")
|
|
||||||
?.addListener(this.themeHeadingFontChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
super.willDestroyElement(...arguments);
|
|
||||||
|
|
||||||
if (this.step) {
|
|
||||||
this.step.findField("color_scheme")?.removeListener(this.themeChanged);
|
|
||||||
this.step.findField("homepage_style")?.removeListener(this.themeChanged);
|
|
||||||
this.step
|
|
||||||
.findField("body_font")
|
|
||||||
?.removeListener(this.themeBodyFontChanged);
|
|
||||||
this.step.findField("site_font")?.removeListener(this.themeFontChanged);
|
|
||||||
this.step
|
|
||||||
.findField("heading_font")
|
|
||||||
?.removeListener(this.themeHeadingFontChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
themeChanged() {
|
|
||||||
this.triggerRepaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
themeBodyFontChanged() {
|
|
||||||
if (!this.loadingFontVariants) {
|
|
||||||
this.loadFontVariants(this.wizard.font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
themeHeadingFontChanged() {
|
|
||||||
if (!this.loadingFontVariants) {
|
|
||||||
this.loadFontVariants(this.wizard.headingFont);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
themeFontChanged() {
|
|
||||||
if (!this.loadingFontVariants) {
|
|
||||||
this.loadFontVariants(this.wizard.font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFontVariants(font) {
|
|
||||||
if (!font) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const fontVariantData = this.fontMap[font.id];
|
|
||||||
|
|
||||||
// System font for example does not need to load from a remote source.
|
|
||||||
if (!fontVariantData) {
|
|
||||||
this.loadedFonts.add(font.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontVariantData && !this.loadedFonts.has(font.id)) {
|
|
||||||
this.loadingFontVariants = true;
|
|
||||||
const fontFaces = fontVariantData.map((fontVariant) => {
|
|
||||||
return new FontFace(font.label, `url(${fontVariant.url})`, {
|
|
||||||
style: "normal",
|
|
||||||
weight: fontVariant.weight,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(
|
|
||||||
fontFaces.map((fontFace) =>
|
|
||||||
fontFace.load().then((loadedFont) => {
|
|
||||||
document.fonts.add(loadedFont);
|
|
||||||
|
|
||||||
// We use our own Set because, though document.fonts.check is available,
|
|
||||||
// it does not seem very reliable, returning false for fonts that have
|
|
||||||
// definitely been loaded.
|
|
||||||
this.loadedFonts.add(font.id);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
this.triggerRepaint();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loadingFontVariants = false;
|
|
||||||
});
|
|
||||||
} else if (this.loadedFonts.has(font.id)) {
|
|
||||||
return Promise.resolve(this.triggerRepaint());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
images() {}
|
|
||||||
|
|
||||||
// NOTE: This works for fonts included in a style that is actually using the
|
|
||||||
// @font-faces on load, but for fonts that we aren't using yet we need to
|
|
||||||
// make sure they are loaded before rendering the canvas via loadFontVariants.
|
|
||||||
loadFonts() {
|
|
||||||
return document.fonts.ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadImages() {
|
|
||||||
const images = this.images();
|
|
||||||
if (images) {
|
|
||||||
return Promise.all(
|
|
||||||
Object.keys(images).map((id) => {
|
|
||||||
return loadImage(images[id]).then((img) => (this[id] = img));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
reload() {
|
|
||||||
Promise.all([this.loadFonts(), this.loadImages()]).then(() => {
|
|
||||||
// NOTE: This must be done otherwise the "bold" variant of the body font
|
|
||||||
// will not be loaded for some reason before rendering the canvas.
|
|
||||||
//
|
|
||||||
// The header font does not suffer from this issue.
|
|
||||||
this.loadFontVariants(this.wizard.font).then(() => {
|
|
||||||
this.loaded = true;
|
|
||||||
this.triggerRepaint();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerRepaint() {
|
|
||||||
scheduleOnce("afterRender", this, "repaint");
|
|
||||||
}
|
|
||||||
|
|
||||||
repaint() {
|
|
||||||
if (!this.loaded) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const colorsArray = this.wizard.currentColors;
|
|
||||||
if (!colorsArray) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let colors = {};
|
|
||||||
colorsArray.forEach(function (c) {
|
|
||||||
const name = c.name;
|
|
||||||
colors[name] = `#${c.hex}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const { font, headingFont } = this.wizard;
|
|
||||||
if (!font) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ctx } = this;
|
|
||||||
|
|
||||||
ctx.fillStyle = colors.secondary;
|
|
||||||
ctx.fillRect(0, 0, this.width, this.height);
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
ctx,
|
|
||||||
colors,
|
|
||||||
font,
|
|
||||||
headingFont,
|
|
||||||
width: this.width,
|
|
||||||
height: this.height,
|
|
||||||
};
|
|
||||||
this.paint(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
categories() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: i18n("wizard.homepage_preview.category_names.icebreakers"),
|
|
||||||
color: "#652D90",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n("wizard.homepage_preview.category_names.news"),
|
|
||||||
color: "#3AB54A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n("wizard.homepage_preview.category_names.site_feedback"),
|
|
||||||
color: "#25AAE2",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleImage(image, x, y, w, h) {
|
|
||||||
w = Math.floor(w);
|
|
||||||
h = Math.floor(h);
|
|
||||||
|
|
||||||
const { ctx } = this;
|
|
||||||
|
|
||||||
const key = `${image.src}-${w}-${h}`;
|
|
||||||
|
|
||||||
if (!scaled[key]) {
|
|
||||||
let copy = image;
|
|
||||||
let ratio = copy.width / copy.height;
|
|
||||||
let newH = copy.height * 0.5;
|
|
||||||
while (newH > h) {
|
|
||||||
copy = canvasFor(copy, ratio * newH, newH);
|
|
||||||
newH = newH * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
scaled[key] = copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.drawImage(scaled[key], x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
get headerHeight() {
|
|
||||||
return this.height * 0.15;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawFullHeader(colors, font, logo) {
|
|
||||||
const { ctx } = this;
|
|
||||||
|
|
||||||
drawHeader(ctx, colors, this.width, this.headerHeight);
|
|
||||||
|
|
||||||
const avatarSize = this.height * 0.1;
|
|
||||||
const headerMargin = this.headerHeight * 0.2;
|
|
||||||
|
|
||||||
if (logo) {
|
|
||||||
const logoHeight = this.headerHeight - headerMargin * 2;
|
|
||||||
|
|
||||||
const ratio = logoHeight / logo.height;
|
|
||||||
this.scaleImage(
|
|
||||||
logo,
|
|
||||||
headerMargin,
|
|
||||||
headerMargin,
|
|
||||||
logo.width * ratio,
|
|
||||||
logoHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
this.scaleImage(logo, this.width, headerMargin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top right menu
|
|
||||||
this.scaleImage(
|
|
||||||
this.avatar,
|
|
||||||
this.width - avatarSize - headerMargin,
|
|
||||||
headerMargin,
|
|
||||||
avatarSize,
|
|
||||||
avatarSize
|
|
||||||
);
|
|
||||||
|
|
||||||
// accounts for hard-set color variables in solarized themes
|
|
||||||
ctx.fillStyle =
|
|
||||||
colors.primary_low_mid ||
|
|
||||||
darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
|
||||||
|
|
||||||
const pathScale = this.headerHeight / 1200;
|
|
||||||
const searchIcon = new Path2D(
|
|
||||||
"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"
|
|
||||||
);
|
|
||||||
const hamburgerIcon = new Path2D(
|
|
||||||
"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"
|
|
||||||
);
|
|
||||||
const chatIcon = new Path2D(
|
|
||||||
"M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z"
|
|
||||||
);
|
|
||||||
ctx.save(); // Save the previous state for translation and scale
|
|
||||||
ctx.translate(
|
|
||||||
this.width - avatarSize * 2 - headerMargin * 0.5,
|
|
||||||
avatarSize / 2
|
|
||||||
);
|
|
||||||
// need to scale paths otherwise they're too large
|
|
||||||
ctx.scale(pathScale, pathScale);
|
|
||||||
ctx.fill(searchIcon);
|
|
||||||
ctx.restore();
|
|
||||||
ctx.save();
|
|
||||||
ctx.translate(
|
|
||||||
this.width - avatarSize * 3 - headerMargin * 0.5,
|
|
||||||
avatarSize / 2
|
|
||||||
);
|
|
||||||
ctx.scale(pathScale, pathScale);
|
|
||||||
ctx.fill(chatIcon);
|
|
||||||
ctx.restore();
|
|
||||||
ctx.save();
|
|
||||||
ctx.translate(headerMargin * 1.75, avatarSize / 2);
|
|
||||||
ctx.scale(pathScale, pathScale);
|
|
||||||
ctx.fill(hamburgerIcon);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
drawPills(colors, font, headerHeight, opts) {
|
|
||||||
opts = opts || {};
|
|
||||||
|
|
||||||
const { ctx } = this;
|
|
||||||
|
|
||||||
const badgeHeight = headerHeight * 2 * 0.25;
|
|
||||||
const headerMargin = headerHeight * 0.2;
|
|
||||||
const fontSize = Math.round(badgeHeight * 0.5);
|
|
||||||
ctx.font = `${fontSize}px '${font.label}'`;
|
|
||||||
|
|
||||||
const allCategoriesText = i18n(
|
|
||||||
"wizard.homepage_preview.nav_buttons.all_categories"
|
|
||||||
);
|
|
||||||
const categoriesWidth = ctx.measureText(allCategoriesText).width;
|
|
||||||
const categoriesBoxWidth = categoriesWidth + headerMargin * 2;
|
|
||||||
|
|
||||||
// Box around "all categories >"
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.strokeStyle = colors.primary;
|
|
||||||
ctx.lineWidth = 0.5;
|
|
||||||
ctx.rect(
|
|
||||||
headerMargin,
|
|
||||||
headerHeight + headerMargin,
|
|
||||||
categoriesBoxWidth,
|
|
||||||
badgeHeight
|
|
||||||
);
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
ctx.fillStyle = colors.primary;
|
|
||||||
ctx.fillText(
|
|
||||||
allCategoriesText,
|
|
||||||
headerMargin * 1.5,
|
|
||||||
headerHeight + headerMargin * 1.4 + fontSize
|
|
||||||
);
|
|
||||||
|
|
||||||
// Caret (>) at the end of "all categories" box
|
|
||||||
const pathScale = badgeHeight / 1000;
|
|
||||||
const caretIcon = new Path2D(
|
|
||||||
"M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.save();
|
|
||||||
ctx.translate(
|
|
||||||
categoriesBoxWidth,
|
|
||||||
headerHeight + headerMargin + badgeHeight / 4
|
|
||||||
);
|
|
||||||
ctx.scale(pathScale, pathScale);
|
|
||||||
ctx.fill(caretIcon);
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
const categoryHomepage =
|
|
||||||
opts.homepageStyle.includes("category") ||
|
|
||||||
opts.homepageStyle.includes("categories");
|
|
||||||
|
|
||||||
// First top menu item
|
|
||||||
const otherHomepageText = i18n(
|
|
||||||
`wizard.top_menu_items.${opts.homepageStyle}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const firstTopMenuItemText = categoryHomepage
|
|
||||||
? i18n("wizard.top_menu_items.categories")
|
|
||||||
: otherHomepageText;
|
|
||||||
|
|
||||||
const newText = i18n("wizard.top_menu_items.new");
|
|
||||||
const unreadText = i18n("wizard.top_menu_items.unread");
|
|
||||||
const topText = i18n("wizard.top_menu_items.top");
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.fillStyle = colors.tertiary;
|
|
||||||
ctx.rect(
|
|
||||||
categoriesBoxWidth + headerMargin * 2,
|
|
||||||
headerHeight + headerMargin,
|
|
||||||
ctx.measureText(firstTopMenuItemText).width + headerMargin * 2,
|
|
||||||
badgeHeight
|
|
||||||
);
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
ctx.font = `${fontSize}px '${font.label}'`;
|
|
||||||
ctx.fillStyle = colors.secondary;
|
|
||||||
const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize;
|
|
||||||
const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth;
|
|
||||||
ctx.fillText(
|
|
||||||
firstTopMenuItemText,
|
|
||||||
firstTopMenuItemX,
|
|
||||||
pillButtonTextY,
|
|
||||||
ctx.measureText(firstTopMenuItemText).width
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.fillStyle = colors.primary;
|
|
||||||
|
|
||||||
const newTextX =
|
|
||||||
firstTopMenuItemX +
|
|
||||||
ctx.measureText(firstTopMenuItemText).width +
|
|
||||||
headerMargin * 2.0;
|
|
||||||
ctx.fillText(newText, newTextX, pillButtonTextY);
|
|
||||||
|
|
||||||
const unreadTextX =
|
|
||||||
newTextX + ctx.measureText(newText).width + headerMargin * 2.0;
|
|
||||||
ctx.fillText(unreadText, unreadTextX, pillButtonTextY);
|
|
||||||
|
|
||||||
const topTextX =
|
|
||||||
unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0;
|
|
||||||
ctx.fillText(topText, topTextX, pillButtonTextY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadImage(src) {
|
|
||||||
if (!src) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const img = new Image();
|
|
||||||
img.src = getUrl(src);
|
|
||||||
return new Promise((resolve) => (img.onload = () => resolve(img)));
|
|
||||||
}
|
|
@ -1,3 +1,254 @@
|
|||||||
|
import { action } from "@ember/object";
|
||||||
|
import { observes } from "@ember-decorators/object";
|
||||||
|
import { bind } from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import {
|
||||||
|
chooseDarker,
|
||||||
|
darkLightDiff,
|
||||||
|
resizeTextLinesToFitRect,
|
||||||
|
} from "../../../lib/preview";
|
||||||
|
import HomepagePreview from "./-homepage-preview";
|
||||||
|
import PreviewBaseComponent from "./-preview-base";
|
||||||
|
|
||||||
|
export default class Index extends PreviewBaseComponent {
|
||||||
|
width = 630;
|
||||||
|
height = 380;
|
||||||
|
logo = null;
|
||||||
|
avatar = null;
|
||||||
|
previewTopic = true;
|
||||||
|
draggingActive = false;
|
||||||
|
startX = 0;
|
||||||
|
scrollLeft = 0;
|
||||||
|
HomepagePreview = HomepagePreview;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(...arguments);
|
||||||
|
this.step
|
||||||
|
.findField("homepage_style")
|
||||||
|
?.addListener(this.onHomepageStyleChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
super.willDestroy(...arguments);
|
||||||
|
this.step
|
||||||
|
.findField("homepage_style")
|
||||||
|
?.removeListener(this.onHomepageStyleChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
super.didInsertElement(...arguments);
|
||||||
|
this.element.addEventListener("mouseleave", this.handleMouseLeave);
|
||||||
|
this.element.addEventListener("mousemove", this.handleMouseMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
super.willDestroyElement(...arguments);
|
||||||
|
this.element.removeEventListener("mouseleave", this.handleMouseLeave);
|
||||||
|
this.element.removeEventListener("mousemove", this.handleMouseMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseDown(e) {
|
||||||
|
const slider = this.element.querySelector(".previews");
|
||||||
|
this.setProperties({
|
||||||
|
draggingActive: true,
|
||||||
|
startX: e.pageX - slider.offsetLeft,
|
||||||
|
scrollLeft: slider.scrollLeft,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
handleMouseLeave() {
|
||||||
|
this.set("draggingActive", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseUp() {
|
||||||
|
this.set("draggingActive", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
handleMouseMove(e) {
|
||||||
|
if (!this.draggingActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const slider = this.element.querySelector(".previews"),
|
||||||
|
x = e.pageX - slider.offsetLeft,
|
||||||
|
walk = (x - this.startX) * 1.5;
|
||||||
|
|
||||||
|
slider.scrollLeft = this.scrollLeft - walk;
|
||||||
|
|
||||||
|
if (slider.scrollLeft < 50) {
|
||||||
|
this.set("previewTopic", true);
|
||||||
|
}
|
||||||
|
if (slider.scrollLeft > slider.offsetWidth - 50) {
|
||||||
|
this.set("previewTopic", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
didUpdateAttrs() {
|
||||||
|
super.didUpdateAttrs(...arguments);
|
||||||
|
|
||||||
|
this.triggerRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
onHomepageStyleChange() {
|
||||||
|
this.set("previewTopic", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@observes("previewTopic")
|
||||||
|
scrollPreviewArea() {
|
||||||
|
const el = this.element.querySelector(".previews");
|
||||||
|
el.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
left: this.previewTopic ? 0 : el.scrollWidth - el.offsetWidth,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
images() {
|
||||||
|
return {
|
||||||
|
logo: this.wizard.logoUrl,
|
||||||
|
avatar: "/images/wizard/trout.png",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
paint({ ctx, colors, font, headingFont, width, height }) {
|
||||||
|
this.drawFullHeader(colors, headingFont, this.logo);
|
||||||
|
|
||||||
|
const margin = 20;
|
||||||
|
const avatarSize = height * 0.1 + 5;
|
||||||
|
const lineHeight = height / 14;
|
||||||
|
const leftHandTextGutter = margin + avatarSize + margin;
|
||||||
|
const timelineX = width * 0.86;
|
||||||
|
|
||||||
|
// Draw a fake topic
|
||||||
|
this.scaleImage(
|
||||||
|
this.avatar,
|
||||||
|
margin,
|
||||||
|
this.headerHeight + height * 0.22,
|
||||||
|
avatarSize,
|
||||||
|
avatarSize
|
||||||
|
);
|
||||||
|
|
||||||
|
const titleFontSize = this.headerHeight / 30;
|
||||||
|
|
||||||
|
// Topic title
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
ctx.font = `700 ${titleFontSize}em '${headingFont.label}'`;
|
||||||
|
ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3);
|
||||||
|
|
||||||
|
// Topic OP text
|
||||||
|
const bodyFontSize = 1;
|
||||||
|
ctx.font = `${bodyFontSize}em '${font.label}'`;
|
||||||
|
|
||||||
|
let verticalLinePos = 0;
|
||||||
|
const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books");
|
||||||
|
const topicOpLines = topicOp.split("\n");
|
||||||
|
|
||||||
|
resizeTextLinesToFitRect(
|
||||||
|
topicOpLines,
|
||||||
|
timelineX - leftHandTextGutter,
|
||||||
|
ctx,
|
||||||
|
bodyFontSize,
|
||||||
|
font,
|
||||||
|
(textLine, idx) => {
|
||||||
|
verticalLinePos = height * 0.4 + idx * lineHeight;
|
||||||
|
ctx.fillText(textLine, leftHandTextGutter, verticalLinePos);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.font = `${bodyFontSize}em '${font.label}'`;
|
||||||
|
|
||||||
|
// Share button
|
||||||
|
const shareButtonWidth =
|
||||||
|
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
|
||||||
|
margin;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(margin, verticalLinePos, shareButtonWidth, height * 0.1);
|
||||||
|
// accounts for hard-set color variables in solarized themes
|
||||||
|
ctx.fillStyle =
|
||||||
|
colors.primary_low ||
|
||||||
|
darkLightDiff(colors.primary, colors.secondary, 90, 65);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = chooseDarker(colors.primary, colors.secondary);
|
||||||
|
ctx.fillText(
|
||||||
|
i18n("wizard.previews.share_button"),
|
||||||
|
margin + 10,
|
||||||
|
verticalLinePos + lineHeight * 0.9
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reply button
|
||||||
|
const replyButtonWidth =
|
||||||
|
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
|
||||||
|
margin;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(
|
||||||
|
shareButtonWidth + margin + 10,
|
||||||
|
verticalLinePos,
|
||||||
|
replyButtonWidth,
|
||||||
|
height * 0.1
|
||||||
|
);
|
||||||
|
ctx.fillStyle = colors.tertiary;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = colors.secondary;
|
||||||
|
ctx.fillText(
|
||||||
|
i18n("wizard.previews.reply_button"),
|
||||||
|
shareButtonWidth + margin * 2,
|
||||||
|
verticalLinePos + lineHeight * 0.9
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw timeline
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = colors.tertiary;
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.moveTo(timelineX, height * 0.3);
|
||||||
|
ctx.lineTo(timelineX, height * 0.7);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Timeline handle
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = colors.tertiary;
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.moveTo(timelineX, height * 0.3 + 10);
|
||||||
|
ctx.lineTo(timelineX, height * 0.4);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Timeline post count
|
||||||
|
const postCountY = height * 0.3 + margin + 10;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.font = `700 ${bodyFontSize}em '${font.label}'`;
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
ctx.fillText("1 / 20", timelineX + margin / 2, postCountY);
|
||||||
|
|
||||||
|
// Timeline post date
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`;
|
||||||
|
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65);
|
||||||
|
ctx.fillText(
|
||||||
|
"Nov 22",
|
||||||
|
timelineX + margin / 2,
|
||||||
|
postCountY + lineHeight * 0.75
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setPreviewHomepage(event) {
|
||||||
|
event?.preventDefault();
|
||||||
|
this.set("previewTopic", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setPreviewTopic(event) {
|
||||||
|
event?.preventDefault();
|
||||||
|
this.set("previewTopic", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<div class="previews {{if this.draggingActive 'dragging'}}">
|
<div class="previews {{if this.draggingActive 'dragging'}}">
|
||||||
<div class="wizard-container__preview topic-preview">
|
<div class="wizard-container__preview topic-preview">
|
||||||
<canvas
|
<canvas
|
||||||
|
@ -1,250 +0,0 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { observes } from "@ember-decorators/object";
|
|
||||||
import { bind } from "discourse/lib/decorators";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import {
|
|
||||||
chooseDarker,
|
|
||||||
darkLightDiff,
|
|
||||||
resizeTextLinesToFitRect,
|
|
||||||
} from "../../../lib/preview";
|
|
||||||
import HomepagePreview from "./-homepage-preview";
|
|
||||||
import PreviewBaseComponent from "./-preview-base";
|
|
||||||
|
|
||||||
export default class Index extends PreviewBaseComponent {
|
|
||||||
width = 630;
|
|
||||||
height = 380;
|
|
||||||
logo = null;
|
|
||||||
avatar = null;
|
|
||||||
previewTopic = true;
|
|
||||||
draggingActive = false;
|
|
||||||
startX = 0;
|
|
||||||
scrollLeft = 0;
|
|
||||||
HomepagePreview = HomepagePreview;
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(...arguments);
|
|
||||||
this.step
|
|
||||||
.findField("homepage_style")
|
|
||||||
?.addListener(this.onHomepageStyleChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy(...arguments);
|
|
||||||
this.step
|
|
||||||
.findField("homepage_style")
|
|
||||||
?.removeListener(this.onHomepageStyleChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
super.didInsertElement(...arguments);
|
|
||||||
this.element.addEventListener("mouseleave", this.handleMouseLeave);
|
|
||||||
this.element.addEventListener("mousemove", this.handleMouseMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
super.willDestroyElement(...arguments);
|
|
||||||
this.element.removeEventListener("mouseleave", this.handleMouseLeave);
|
|
||||||
this.element.removeEventListener("mousemove", this.handleMouseMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
mouseDown(e) {
|
|
||||||
const slider = this.element.querySelector(".previews");
|
|
||||||
this.setProperties({
|
|
||||||
draggingActive: true,
|
|
||||||
startX: e.pageX - slider.offsetLeft,
|
|
||||||
scrollLeft: slider.scrollLeft,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
handleMouseLeave() {
|
|
||||||
this.set("draggingActive", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
mouseUp() {
|
|
||||||
this.set("draggingActive", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
handleMouseMove(e) {
|
|
||||||
if (!this.draggingActive) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const slider = this.element.querySelector(".previews"),
|
|
||||||
x = e.pageX - slider.offsetLeft,
|
|
||||||
walk = (x - this.startX) * 1.5;
|
|
||||||
|
|
||||||
slider.scrollLeft = this.scrollLeft - walk;
|
|
||||||
|
|
||||||
if (slider.scrollLeft < 50) {
|
|
||||||
this.set("previewTopic", true);
|
|
||||||
}
|
|
||||||
if (slider.scrollLeft > slider.offsetWidth - 50) {
|
|
||||||
this.set("previewTopic", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
didUpdateAttrs() {
|
|
||||||
super.didUpdateAttrs(...arguments);
|
|
||||||
|
|
||||||
this.triggerRepaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
onHomepageStyleChange() {
|
|
||||||
this.set("previewTopic", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@observes("previewTopic")
|
|
||||||
scrollPreviewArea() {
|
|
||||||
const el = this.element.querySelector(".previews");
|
|
||||||
el.scrollTo({
|
|
||||||
top: 0,
|
|
||||||
left: this.previewTopic ? 0 : el.scrollWidth - el.offsetWidth,
|
|
||||||
behavior: "smooth",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
images() {
|
|
||||||
return {
|
|
||||||
logo: this.wizard.logoUrl,
|
|
||||||
avatar: "/images/wizard/trout.png",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
paint({ ctx, colors, font, headingFont, width, height }) {
|
|
||||||
this.drawFullHeader(colors, headingFont, this.logo);
|
|
||||||
|
|
||||||
const margin = 20;
|
|
||||||
const avatarSize = height * 0.1 + 5;
|
|
||||||
const lineHeight = height / 14;
|
|
||||||
const leftHandTextGutter = margin + avatarSize + margin;
|
|
||||||
const timelineX = width * 0.86;
|
|
||||||
|
|
||||||
// Draw a fake topic
|
|
||||||
this.scaleImage(
|
|
||||||
this.avatar,
|
|
||||||
margin,
|
|
||||||
this.headerHeight + height * 0.22,
|
|
||||||
avatarSize,
|
|
||||||
avatarSize
|
|
||||||
);
|
|
||||||
|
|
||||||
const titleFontSize = this.headerHeight / 30;
|
|
||||||
|
|
||||||
// Topic title
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.fillStyle = colors.primary;
|
|
||||||
ctx.font = `700 ${titleFontSize}em '${headingFont.label}'`;
|
|
||||||
ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3);
|
|
||||||
|
|
||||||
// Topic OP text
|
|
||||||
const bodyFontSize = 1;
|
|
||||||
ctx.font = `${bodyFontSize}em '${font.label}'`;
|
|
||||||
|
|
||||||
let verticalLinePos = 0;
|
|
||||||
const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books");
|
|
||||||
const topicOpLines = topicOp.split("\n");
|
|
||||||
|
|
||||||
resizeTextLinesToFitRect(
|
|
||||||
topicOpLines,
|
|
||||||
timelineX - leftHandTextGutter,
|
|
||||||
ctx,
|
|
||||||
bodyFontSize,
|
|
||||||
font,
|
|
||||||
(textLine, idx) => {
|
|
||||||
verticalLinePos = height * 0.4 + idx * lineHeight;
|
|
||||||
ctx.fillText(textLine, leftHandTextGutter, verticalLinePos);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.font = `${bodyFontSize}em '${font.label}'`;
|
|
||||||
|
|
||||||
// Share button
|
|
||||||
const shareButtonWidth =
|
|
||||||
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
|
|
||||||
margin;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.rect(margin, verticalLinePos, shareButtonWidth, height * 0.1);
|
|
||||||
// accounts for hard-set color variables in solarized themes
|
|
||||||
ctx.fillStyle =
|
|
||||||
colors.primary_low ||
|
|
||||||
darkLightDiff(colors.primary, colors.secondary, 90, 65);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.fillStyle = chooseDarker(colors.primary, colors.secondary);
|
|
||||||
ctx.fillText(
|
|
||||||
i18n("wizard.previews.share_button"),
|
|
||||||
margin + 10,
|
|
||||||
verticalLinePos + lineHeight * 0.9
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reply button
|
|
||||||
const replyButtonWidth =
|
|
||||||
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
|
|
||||||
margin;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.rect(
|
|
||||||
shareButtonWidth + margin + 10,
|
|
||||||
verticalLinePos,
|
|
||||||
replyButtonWidth,
|
|
||||||
height * 0.1
|
|
||||||
);
|
|
||||||
ctx.fillStyle = colors.tertiary;
|
|
||||||
ctx.fill();
|
|
||||||
ctx.fillStyle = colors.secondary;
|
|
||||||
ctx.fillText(
|
|
||||||
i18n("wizard.previews.reply_button"),
|
|
||||||
shareButtonWidth + margin * 2,
|
|
||||||
verticalLinePos + lineHeight * 0.9
|
|
||||||
);
|
|
||||||
|
|
||||||
// Draw timeline
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.strokeStyle = colors.tertiary;
|
|
||||||
ctx.lineWidth = 0.5;
|
|
||||||
ctx.moveTo(timelineX, height * 0.3);
|
|
||||||
ctx.lineTo(timelineX, height * 0.7);
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
// Timeline handle
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.strokeStyle = colors.tertiary;
|
|
||||||
ctx.lineWidth = 3;
|
|
||||||
ctx.moveTo(timelineX, height * 0.3 + 10);
|
|
||||||
ctx.lineTo(timelineX, height * 0.4);
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
// Timeline post count
|
|
||||||
const postCountY = height * 0.3 + margin + 10;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.font = `700 ${bodyFontSize}em '${font.label}'`;
|
|
||||||
ctx.fillStyle = colors.primary;
|
|
||||||
ctx.fillText("1 / 20", timelineX + margin / 2, postCountY);
|
|
||||||
|
|
||||||
// Timeline post date
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`;
|
|
||||||
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65);
|
|
||||||
ctx.fillText(
|
|
||||||
"Nov 22",
|
|
||||||
timelineX + margin / 2,
|
|
||||||
postCountY + lineHeight * 0.75
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setPreviewHomepage(event) {
|
|
||||||
event?.preventDefault();
|
|
||||||
this.set("previewTopic", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setPreviewTopic(event) {
|
|
||||||
event?.preventDefault();
|
|
||||||
this.set("previewTopic", true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,7 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
|
||||||
|
export default class Text extends Component {}
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
id={{this.field.id}}
|
id={{this.field.id}}
|
||||||
@value={{this.field.value}}
|
@value={{this.field.value}}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default class Text extends Component {}
|
|
@ -1,3 +1,36 @@
|
|||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("color-palettes-row")
|
||||||
|
export default class ColorPalettesRow extends SelectKitRowComponent {
|
||||||
|
@computed("item.colors.[]")
|
||||||
|
get palettes() {
|
||||||
|
return htmlSafe(
|
||||||
|
(this.item.colors || [])
|
||||||
|
.filter((color) => color.name !== "secondary")
|
||||||
|
.map((color) => `#${escape(color.hex)}`)
|
||||||
|
.map(
|
||||||
|
(hex) =>
|
||||||
|
`<span class="palette" style="background-color:${hex}"></span>`
|
||||||
|
)
|
||||||
|
.join("")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item.colors.[]")
|
||||||
|
get backgroundColor() {
|
||||||
|
const secondary = (this.item.colors || []).findBy("name", "secondary");
|
||||||
|
|
||||||
|
if (secondary && secondary.hex) {
|
||||||
|
return htmlSafe(`background-color:#${escape(secondary.hex)}`);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<span class="name">
|
<span class="name">
|
||||||
{{this.label}}
|
{{this.label}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import { computed } from "@ember/object";
|
|
||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("color-palettes-row")
|
|
||||||
export default class ColorPalettesRow extends SelectKitRowComponent {
|
|
||||||
@computed("item.colors.[]")
|
|
||||||
get palettes() {
|
|
||||||
return htmlSafe(
|
|
||||||
(this.item.colors || [])
|
|
||||||
.filter((color) => color.name !== "secondary")
|
|
||||||
.map((color) => `#${escape(color.hex)}`)
|
|
||||||
.map(
|
|
||||||
(hex) =>
|
|
||||||
`<span class="palette" style="background-color:${hex}"></span>`
|
|
||||||
)
|
|
||||||
.join("")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item.colors.[]")
|
|
||||||
get backgroundColor() {
|
|
||||||
const secondary = (this.item.colors || []).findBy("name", "secondary");
|
|
||||||
|
|
||||||
if (secondary && secondary.hex) {
|
|
||||||
return htmlSafe(`background-color:#${escape(secondary.hex)}`);
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1,20 @@
|
|||||||
|
import { schedule } from "@ember/runloop";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("create-color-row")
|
||||||
|
export default class CreateColorRow extends SelectKitRowComponent {
|
||||||
|
didReceiveAttrs() {
|
||||||
|
super.didReceiveAttrs(...arguments);
|
||||||
|
|
||||||
|
schedule("afterRender", () => {
|
||||||
|
const color = escapeExpression(this.rowValue);
|
||||||
|
this.element.style.borderLeftColor = color.startsWith("#")
|
||||||
|
? color
|
||||||
|
: `#${color}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<span>{{this.label}}</span>
|
<span>{{this.label}}</span>
|
@ -1,18 +0,0 @@
|
|||||||
import { schedule } from "@ember/runloop";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("create-color-row")
|
|
||||||
export default class CreateColorRow extends SelectKitRowComponent {
|
|
||||||
didReceiveAttrs() {
|
|
||||||
super.didReceiveAttrs(...arguments);
|
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
const color = escapeExpression(this.rowValue);
|
|
||||||
this.element.style.borderLeftColor = color.startsWith("#")
|
|
||||||
? color
|
|
||||||
: `#${color}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,12 @@
|
|||||||
|
import { readOnly } from "@ember/object/computed";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("dropdown-select-box-row")
|
||||||
|
export default class DropdownSelectBoxRow extends SelectKitRowComponent {
|
||||||
|
@readOnly("item.description") description;
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.icons}}
|
{{#if this.icons}}
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<span class="selection-indicator"></span>
|
<span class="selection-indicator"></span>
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { readOnly } from "@ember/object/computed";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("dropdown-select-box-row")
|
|
||||||
export default class DropdownSelectBoxRow extends SelectKitRowComponent {
|
|
||||||
@readOnly("item.description") description;
|
|
||||||
}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("email-group-user-chooser-row")
|
||||||
|
export default class EmailGroupUserChooserRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{#if this.item.isUser}}
|
{{#if this.item.isUser}}
|
||||||
{{avatar this.item imageSize="tiny"}}
|
{{avatar this.item imageSize="tiny"}}
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("email-group-user-chooser-row")
|
|
||||||
export default class EmailGroupUserChooserRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("flair-row")
|
||||||
|
export default class FlairRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{#if this.item.url}}
|
{{#if this.item.url}}
|
||||||
<AvatarFlair
|
<AvatarFlair
|
||||||
@flairName={{this.item.name}}
|
@flairName={{this.item.name}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("flair-row")
|
|
||||||
export default class FlairRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("future-date-input-selector-row")
|
||||||
|
export default class FutureDateInputSelectorRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{#if this.item.icon}}
|
{{#if this.item.icon}}
|
||||||
<div class="future-date-input-selector-icons">
|
<div class="future-date-input-selector-icons">
|
||||||
{{d-icon this.item.icon}}
|
{{d-icon this.item.icon}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("future-date-input-selector-row")
|
|
||||||
export default class FutureDateInputSelectorRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("homepage-style-selector-row")
|
||||||
|
export default class HomepageStyleSelectorRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
<div class="texts">
|
<div class="texts">
|
||||||
<span class="name">{{html-safe this.label}}</span>
|
<span class="name">{{html-safe this.label}}</span>
|
||||||
{{#if this.item.description}}
|
{{#if this.item.description}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("homepage-style-selector-row")
|
|
||||||
export default class HomepageStyleSelectorRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,34 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { reads } from "@ember/object/computed";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class SelectedCollection extends Component {
|
||||||
|
@reads("collection.content.selectedTags.[]") selectedTags;
|
||||||
|
|
||||||
|
@computed("selectedTags.[]", "selectKit.filter")
|
||||||
|
get tags() {
|
||||||
|
if (!this.selectedTags) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags = this.selectedTags;
|
||||||
|
if (tags.length >= 20 && this.selectKit.filter) {
|
||||||
|
tags = tags.filter((t) => t.includes(this.selectKit.filter));
|
||||||
|
} else if (tags.length >= 20) {
|
||||||
|
tags = tags.slice(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags.map((selectedTag) => {
|
||||||
|
return {
|
||||||
|
value: selectedTag,
|
||||||
|
classNames: "selected-tag",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.tags}}
|
{{#if this.tags}}
|
||||||
<div class="mini-tag-chooser-selected-collection selected-tags">
|
<div class="mini-tag-chooser-selected-collection selected-tags">
|
||||||
{{#each this.tags as |tag|}}
|
{{#each this.tags as |tag|}}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
import { reads } from "@ember/object/computed";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class SelectedCollection extends Component {
|
|
||||||
@reads("collection.content.selectedTags.[]") selectedTags;
|
|
||||||
|
|
||||||
@computed("selectedTags.[]", "selectKit.filter")
|
|
||||||
get tags() {
|
|
||||||
if (!this.selectedTags) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let tags = this.selectedTags;
|
|
||||||
if (tags.length >= 20 && this.selectKit.filter) {
|
|
||||||
tags = tags.filter((t) => t.includes(this.selectKit.filter));
|
|
||||||
} else if (tags.length >= 20) {
|
|
||||||
tags = tags.slice(0, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags.map((selectedTag) => {
|
|
||||||
return {
|
|
||||||
value: selectedTag,
|
|
||||||
classNames: "selected-tag",
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,28 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
import { makeArray } from "discourse/lib/helpers";
|
||||||
|
import UtilsMixin from "select-kit/mixins/utils";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class FormatSelectedContent extends Component.extend(
|
||||||
|
UtilsMixin
|
||||||
|
) {
|
||||||
|
content = null;
|
||||||
|
selectKit = null;
|
||||||
|
|
||||||
|
@computed("content")
|
||||||
|
get formattedContent() {
|
||||||
|
if (this.content) {
|
||||||
|
return makeArray(this.content)
|
||||||
|
.map((c) => this.getName(c))
|
||||||
|
.join(", ");
|
||||||
|
} else {
|
||||||
|
return this.getName(this.selectKit.noneItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<span class="formatted-selection">
|
<span class="formatted-selection">
|
||||||
{{this.formattedContent}}
|
{{this.formattedContent}}
|
||||||
</span>
|
</span>
|
@ -1,24 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import { makeArray } from "discourse/lib/helpers";
|
|
||||||
import UtilsMixin from "select-kit/mixins/utils";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class FormatSelectedContent extends Component.extend(
|
|
||||||
UtilsMixin
|
|
||||||
) {
|
|
||||||
content = null;
|
|
||||||
selectKit = null;
|
|
||||||
|
|
||||||
@computed("content")
|
|
||||||
get formattedContent() {
|
|
||||||
if (this.content) {
|
|
||||||
return makeArray(this.content)
|
|
||||||
.map((c) => this.getName(c))
|
|
||||||
.join(", ");
|
|
||||||
} else {
|
|
||||||
return this.getName(this.selectKit.noneItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,41 @@
|
|||||||
|
import { action } from "@ember/object";
|
||||||
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import SelectKitFilterComponent from "select-kit/components/select-kit/select-kit-filter";
|
||||||
|
|
||||||
|
@classNames("multi-select-filter")
|
||||||
|
export default class MultiSelectFilter extends SelectKitFilterComponent {
|
||||||
|
@discourseComputed("placeholder", "selectKit.hasSelection")
|
||||||
|
computedPlaceholder(placeholder, hasSelection) {
|
||||||
|
if (this.hidePlaceholderWithSelection && hasSelection) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEmpty(placeholder) ? "" : placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onPaste(event) {
|
||||||
|
const data = event?.clipboardData;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = data.getData("text").split("|").filter(Boolean);
|
||||||
|
|
||||||
|
if (parts.length > 1) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.selectKit.append(parts);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#unless this.isHidden}}
|
{{#unless this.isHidden}}
|
||||||
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
||||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { isEmpty } from "@ember/utils";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import SelectKitFilterComponent from "select-kit/components/select-kit/select-kit-filter";
|
|
||||||
|
|
||||||
@classNames("multi-select-filter")
|
|
||||||
export default class MultiSelectFilter extends SelectKitFilterComponent {
|
|
||||||
@discourseComputed("placeholder", "selectKit.hasSelection")
|
|
||||||
computedPlaceholder(placeholder, hasSelection) {
|
|
||||||
if (this.hidePlaceholderWithSelection && hasSelection) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEmpty(placeholder) ? "" : placeholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onPaste(event) {
|
|
||||||
const data = event?.clipboardData;
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = data.getData("text").split("|").filter(Boolean);
|
|
||||||
|
|
||||||
if (parts.length > 1) {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.selectKit.append(parts);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,22 @@
|
|||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||||
|
import SelectedNameComponent from "select-kit/components/selected-name";
|
||||||
|
|
||||||
|
@classNames("selected-category")
|
||||||
|
export default class SelectedCategory extends SelectedNameComponent {
|
||||||
|
@computed("item")
|
||||||
|
get badge() {
|
||||||
|
return htmlSafe(
|
||||||
|
categoryBadgeHTML(this.item, {
|
||||||
|
allowUncategorized: true,
|
||||||
|
link: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
{{on "click" this.onSelectedNameClick}}
|
{{on "click" this.onSelectedNameClick}}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { computed } from "@ember/object";
|
|
||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
|
||||||
import SelectedNameComponent from "select-kit/components/selected-name";
|
|
||||||
|
|
||||||
@classNames("selected-category")
|
|
||||||
export default class SelectedCategory extends SelectedNameComponent {
|
|
||||||
@computed("item")
|
|
||||||
get badge() {
|
|
||||||
return htmlSafe(
|
|
||||||
categoryBadgeHTML(this.item, {
|
|
||||||
allowUncategorized: true,
|
|
||||||
link: false,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,23 @@
|
|||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import CategoryRowComponent from "select-kit/components/category-row";
|
||||||
|
|
||||||
|
@classNames("none category-row")
|
||||||
|
export default class NoneCategoryRow extends CategoryRowComponent {
|
||||||
|
@discourseComputed("category")
|
||||||
|
badgeForCategory(category) {
|
||||||
|
return htmlSafe(
|
||||||
|
categoryBadgeHTML(category, {
|
||||||
|
link: false,
|
||||||
|
allowUncategorized: true,
|
||||||
|
hideParent: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.category}}
|
{{#if this.category}}
|
||||||
<div class="category-status" aria-hidden="true">
|
<div class="category-status" aria-hidden="true">
|
||||||
{{#if this.hasParentCategory}}
|
{{#if this.hasParentCategory}}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import CategoryRowComponent from "select-kit/components/category-row";
|
|
||||||
|
|
||||||
@classNames("none category-row")
|
|
||||||
export default class NoneCategoryRow extends CategoryRowComponent {
|
|
||||||
@discourseComputed("category")
|
|
||||||
badgeForCategory(category) {
|
|
||||||
return htmlSafe(
|
|
||||||
categoryBadgeHTML(category, {
|
|
||||||
link: false,
|
|
||||||
allowUncategorized: true,
|
|
||||||
hideParent: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,18 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import { fmt } from "discourse/lib/computed";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
|
||||||
|
|
||||||
|
@classNames("notifications-filter-header", "btn-flat")
|
||||||
|
export default class NotificationsFilterHeader extends DropdownSelectBoxHeaderComponent {
|
||||||
|
@fmt("value", "user.user_notifications.filters.%@") label;
|
||||||
|
|
||||||
|
@discourseComputed("selectKit.isExpanded")
|
||||||
|
caretIcon(isExpanded) {
|
||||||
|
return isExpanded ? "caret-up" : "caret-down";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<div class="select-kit-header-wrapper">
|
<div class="select-kit-header-wrapper">
|
||||||
<span class="filter-text">
|
<span class="filter-text">
|
||||||
{{i18n "user.user_notifications.filters.filter_by"}}
|
{{i18n "user.user_notifications.filters.filter_by"}}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { fmt } from "discourse/lib/computed";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
|
|
||||||
|
|
||||||
@classNames("notifications-filter-header", "btn-flat")
|
|
||||||
export default class NotificationsFilterHeader extends DropdownSelectBoxHeaderComponent {
|
|
||||||
@fmt("value", "user.user_notifications.filters.%@") label;
|
|
||||||
|
|
||||||
@discourseComputed("selectKit.isExpanded")
|
|
||||||
caretIcon(isExpanded) {
|
|
||||||
return isExpanded ? "caret-up" : "caret-down";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,15 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
|
||||||
|
|
||||||
|
@classNames("period-chooser-header", "btn-flat")
|
||||||
|
export default class PeriodChooserHeader extends DropdownSelectBoxHeaderComponent {
|
||||||
|
@discourseComputed("selectKit.isExpanded")
|
||||||
|
caretIcon(isExpanded) {
|
||||||
|
return isExpanded ? "caret-up" : "caret-down";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<h2 class="selected-name" title={{this.title}}>
|
<h2 class="selected-name" title={{this.title}}>
|
||||||
{{period-title
|
{{period-title
|
||||||
this.value
|
this.value
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
|
|
||||||
|
|
||||||
@classNames("period-chooser-header", "btn-flat")
|
|
||||||
export default class PeriodChooserHeader extends DropdownSelectBoxHeaderComponent {
|
|
||||||
@discourseComputed("selectKit.isExpanded")
|
|
||||||
caretIcon(isExpanded) {
|
|
||||||
return isExpanded ? "caret-up" : "caret-down";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,16 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import DropdownSelectBoxRowComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
|
||||||
|
|
||||||
|
@classNames("period-chooser-row")
|
||||||
|
export default class PeriodChooserRow extends DropdownSelectBoxRowComponent {
|
||||||
|
@discourseComputed("rowName")
|
||||||
|
title(rowName) {
|
||||||
|
return i18n(`filters.top.${rowName || "this_week"}`).title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<span class="selection-indicator"></span>
|
<span class="selection-indicator"></span>
|
||||||
|
|
||||||
<span class="period-title">
|
<span class="period-title">
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import DropdownSelectBoxRowComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
|
|
||||||
|
|
||||||
@classNames("period-chooser-row")
|
|
||||||
export default class PeriodChooserRow extends DropdownSelectBoxRowComponent {
|
|
||||||
@discourseComputed("rowName")
|
|
||||||
title(rowName) {
|
|
||||||
return i18n(`filters.top.${rowName || "this_week"}`).title;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class ErrorsCollection extends Component {}
|
||||||
|
|
||||||
{{#if this.collection.content}}
|
{{#if this.collection.content}}
|
||||||
<ul class="select-kit-errors-collection">
|
<ul class="select-kit-errors-collection">
|
||||||
{{#each this.collection.content as |item|}}
|
{{#each this.collection.content as |item|}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class ErrorsCollection extends Component {}
|
|
@ -1,3 +1,68 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { next } from "@ember/runloop";
|
||||||
|
import { classNameBindings, classNames } from "@ember-decorators/component";
|
||||||
|
import { bind } from "discourse/lib/decorators";
|
||||||
|
|
||||||
|
@classNames("select-kit-body")
|
||||||
|
@classNameBindings("emptyBody:empty-body")
|
||||||
|
export default class SelectKitBody extends Component {
|
||||||
|
@computed("selectKit.{filter,hasNoContent}")
|
||||||
|
get emptyBody() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
super.didInsertElement(...arguments);
|
||||||
|
|
||||||
|
this.element.style.position = "relative";
|
||||||
|
document.addEventListener("click", this.handleClick, true);
|
||||||
|
this.selectKit
|
||||||
|
.mainElement()
|
||||||
|
.addEventListener("keydown", this._handleKeydown, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
super.willDestroyElement(...arguments);
|
||||||
|
document.removeEventListener("click", this.handleClick, true);
|
||||||
|
this.selectKit
|
||||||
|
.mainElement()
|
||||||
|
?.removeEventListener("keydown", this._handleKeydown, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
handleClick(event) {
|
||||||
|
if (!this.selectKit.isExpanded || !this.selectKit.mainElement()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selectKit.mainElement().contains(event.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectKit.close(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
|
_handleKeydown(event) {
|
||||||
|
if (!this.selectKit.isExpanded || event.key !== "Tab") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(() => {
|
||||||
|
if (
|
||||||
|
this.isDestroying ||
|
||||||
|
this.isDestroyed ||
|
||||||
|
this.selectKit.mainElement()?.contains(document.activeElement)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectKit.close(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.selectKit.isExpanded}}
|
{{#if this.selectKit.isExpanded}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
{{/if}}
|
{{/if}}
|
@ -1,64 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
import { next } from "@ember/runloop";
|
|
||||||
import { classNameBindings, classNames } from "@ember-decorators/component";
|
|
||||||
import { bind } from "discourse/lib/decorators";
|
|
||||||
|
|
||||||
@classNames("select-kit-body")
|
|
||||||
@classNameBindings("emptyBody:empty-body")
|
|
||||||
export default class SelectKitBody extends Component {
|
|
||||||
@computed("selectKit.{filter,hasNoContent}")
|
|
||||||
get emptyBody() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
super.didInsertElement(...arguments);
|
|
||||||
|
|
||||||
this.element.style.position = "relative";
|
|
||||||
document.addEventListener("click", this.handleClick, true);
|
|
||||||
this.selectKit
|
|
||||||
.mainElement()
|
|
||||||
.addEventListener("keydown", this._handleKeydown, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
super.willDestroyElement(...arguments);
|
|
||||||
document.removeEventListener("click", this.handleClick, true);
|
|
||||||
this.selectKit
|
|
||||||
.mainElement()
|
|
||||||
?.removeEventListener("keydown", this._handleKeydown, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
handleClick(event) {
|
|
||||||
if (!this.selectKit.isExpanded || !this.selectKit.mainElement()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selectKit.mainElement().contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectKit.close(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
_handleKeydown(event) {
|
|
||||||
if (!this.selectKit.isExpanded || event.key !== "Tab") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
next(() => {
|
|
||||||
if (
|
|
||||||
this.isDestroying ||
|
|
||||||
this.isDestroyed ||
|
|
||||||
this.selectKit.mainElement()?.contains(document.activeElement)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectKit.close(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("create")
|
||||||
|
export default class SelectKitCreateRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{#each this.icons as |icon|}}
|
{{#each this.icons as |icon|}}
|
||||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("create")
|
|
||||||
export default class SelectKitCreateRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,144 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { action, computed } from "@ember/object";
|
||||||
|
import { not } from "@ember/object/computed";
|
||||||
|
import { isPresent } from "@ember/utils";
|
||||||
|
import {
|
||||||
|
attributeBindings,
|
||||||
|
classNameBindings,
|
||||||
|
classNames,
|
||||||
|
} from "@ember-decorators/component";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import UtilsMixin from "select-kit/mixins/utils";
|
||||||
|
|
||||||
|
@classNames("select-kit-filter")
|
||||||
|
@classNameBindings("isExpanded:is-expanded")
|
||||||
|
@attributeBindings("role")
|
||||||
|
export default class SelectKitFilter extends Component.extend(UtilsMixin) {
|
||||||
|
tabIndex = -1;
|
||||||
|
|
||||||
|
@not("isHidden") isExpanded;
|
||||||
|
|
||||||
|
@computed(
|
||||||
|
"selectKit.options.{filterable,allowAny,autoFilterable}",
|
||||||
|
"content.[]"
|
||||||
|
)
|
||||||
|
get isHidden() {
|
||||||
|
return (
|
||||||
|
!this.selectKit.options.filterable &&
|
||||||
|
!this.selectKit.options.allowAny &&
|
||||||
|
!this.selectKit.options.autoFilterable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"selectKit.options.filterPlaceholder",
|
||||||
|
"selectKit.options.translatedFilterPlaceholder",
|
||||||
|
"selectKit.options.allowAny"
|
||||||
|
)
|
||||||
|
placeholder(placeholder, translatedPlaceholder) {
|
||||||
|
if (isPresent(translatedPlaceholder)) {
|
||||||
|
return translatedPlaceholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(placeholder)) {
|
||||||
|
return i18n(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18n(
|
||||||
|
this.selectKit.options.allowAny
|
||||||
|
? "select_kit.filter_placeholder_with_any"
|
||||||
|
: "select_kit.filter_placeholder"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onPaste() {}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onInput(event) {
|
||||||
|
this.selectKit.onInput(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onKeyup(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onKeydown(event) {
|
||||||
|
if (!this.selectKit.onKeydown(event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Tab" && this.selectKit.isLoading) {
|
||||||
|
this.selectKit.cancelSearch();
|
||||||
|
this.selectKit.close(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Backspace" && !this.selectKit.filter) {
|
||||||
|
this.selectKit.deselectLast();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowUp") {
|
||||||
|
this.selectKit.highlightLast();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
if (!this.selectKit.isExpanded) {
|
||||||
|
this.selectKit.open(event);
|
||||||
|
}
|
||||||
|
this.selectKit.highlightFirst();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
this.selectKit.close(event);
|
||||||
|
this.selectKit.headerElement().focus();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Enter" && this.selectKit.highlighted) {
|
||||||
|
this.selectKit.select(
|
||||||
|
this.getValue(this.selectKit.highlighted),
|
||||||
|
this.selectKit.highlighted
|
||||||
|
);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.key === "Enter" &&
|
||||||
|
(!this.selectKit.highlighted || this.selectKit.enterDisabled)
|
||||||
|
) {
|
||||||
|
this.element.querySelector("input").focus();
|
||||||
|
if (this.selectKit.enterDisabled) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectKit.set("highlighted", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#unless this.isHidden}}
|
{{#unless this.isHidden}}
|
||||||
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
||||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { action, computed } from "@ember/object";
|
|
||||||
import { not } from "@ember/object/computed";
|
|
||||||
import { isPresent } from "@ember/utils";
|
|
||||||
import {
|
|
||||||
attributeBindings,
|
|
||||||
classNameBindings,
|
|
||||||
classNames,
|
|
||||||
} from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import UtilsMixin from "select-kit/mixins/utils";
|
|
||||||
|
|
||||||
@classNames("select-kit-filter")
|
|
||||||
@classNameBindings("isExpanded:is-expanded")
|
|
||||||
@attributeBindings("role")
|
|
||||||
export default class SelectKitFilter extends Component.extend(UtilsMixin) {
|
|
||||||
tabIndex = -1;
|
|
||||||
|
|
||||||
@not("isHidden") isExpanded;
|
|
||||||
|
|
||||||
@computed(
|
|
||||||
"selectKit.options.{filterable,allowAny,autoFilterable}",
|
|
||||||
"content.[]"
|
|
||||||
)
|
|
||||||
get isHidden() {
|
|
||||||
return (
|
|
||||||
!this.selectKit.options.filterable &&
|
|
||||||
!this.selectKit.options.allowAny &&
|
|
||||||
!this.selectKit.options.autoFilterable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed(
|
|
||||||
"selectKit.options.filterPlaceholder",
|
|
||||||
"selectKit.options.translatedFilterPlaceholder",
|
|
||||||
"selectKit.options.allowAny"
|
|
||||||
)
|
|
||||||
placeholder(placeholder, translatedPlaceholder) {
|
|
||||||
if (isPresent(translatedPlaceholder)) {
|
|
||||||
return translatedPlaceholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(placeholder)) {
|
|
||||||
return i18n(placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i18n(
|
|
||||||
this.selectKit.options.allowAny
|
|
||||||
? "select_kit.filter_placeholder_with_any"
|
|
||||||
: "select_kit.filter_placeholder"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onPaste() {}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onInput(event) {
|
|
||||||
this.selectKit.onInput(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onKeyup(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onKeydown(event) {
|
|
||||||
if (!this.selectKit.onKeydown(event)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "Tab" && this.selectKit.isLoading) {
|
|
||||||
this.selectKit.cancelSearch();
|
|
||||||
this.selectKit.close(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "Backspace" && !this.selectKit.filter) {
|
|
||||||
this.selectKit.deselectLast();
|
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "ArrowUp") {
|
|
||||||
this.selectKit.highlightLast();
|
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "ArrowDown") {
|
|
||||||
if (!this.selectKit.isExpanded) {
|
|
||||||
this.selectKit.open(event);
|
|
||||||
}
|
|
||||||
this.selectKit.highlightFirst();
|
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
this.selectKit.close(event);
|
|
||||||
this.selectKit.headerElement().focus();
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "Enter" && this.selectKit.highlighted) {
|
|
||||||
this.selectKit.select(
|
|
||||||
this.getValue(this.selectKit.highlighted),
|
|
||||||
this.selectKit.highlighted
|
|
||||||
);
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
event.key === "Enter" &&
|
|
||||||
(!this.selectKit.highlighted || this.selectKit.enterDisabled)
|
|
||||||
) {
|
|
||||||
this.element.querySelector("input").focus();
|
|
||||||
if (this.selectKit.enterDisabled) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectKit.set("highlighted", null);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("none")
|
||||||
|
export default class SelectKitNoneRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{#each this.icons as |icon|}}
|
{{#each this.icons as |icon|}}
|
||||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("none")
|
|
||||||
export default class SelectKitNoneRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,210 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { action, computed } from "@ember/object";
|
||||||
|
import { reads } from "@ember/object/computed";
|
||||||
|
import { guidFor } from "@ember/object/internals";
|
||||||
|
import { dasherize } from "@ember/string";
|
||||||
|
import {
|
||||||
|
attributeBindings,
|
||||||
|
classNameBindings,
|
||||||
|
classNames,
|
||||||
|
tagName,
|
||||||
|
} from "@ember-decorators/component";
|
||||||
|
import { makeArray } from "discourse/lib/helpers";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import UtilsMixin from "select-kit/mixins/utils";
|
||||||
|
|
||||||
|
@classNames("select-kit-row")
|
||||||
|
@tagName("li")
|
||||||
|
@attributeBindings(
|
||||||
|
"tabIndex",
|
||||||
|
"title",
|
||||||
|
"rowValue:data-value",
|
||||||
|
"rowName:data-name",
|
||||||
|
"index:data-index",
|
||||||
|
"role",
|
||||||
|
"ariaChecked:aria-checked",
|
||||||
|
"guid:data-guid",
|
||||||
|
"rowLang:lang"
|
||||||
|
)
|
||||||
|
@classNameBindings(
|
||||||
|
"isHighlighted",
|
||||||
|
"isSelected",
|
||||||
|
"isNone",
|
||||||
|
"isNone:none",
|
||||||
|
"item.classNames"
|
||||||
|
)
|
||||||
|
export default class SelectKitRow extends Component.extend(UtilsMixin) {
|
||||||
|
tabIndex = 0;
|
||||||
|
index = 0;
|
||||||
|
role = "menuitemradio";
|
||||||
|
|
||||||
|
@reads("item.lang") lang;
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
super.didInsertElement(...arguments);
|
||||||
|
|
||||||
|
if (this.site.desktopView) {
|
||||||
|
this.element.addEventListener("mouseenter", this.handleMouseEnter);
|
||||||
|
this.element.addEventListener("focus", this.handleMouseEnter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
super.willDestroyElement(...arguments);
|
||||||
|
|
||||||
|
if (this.site.desktopView) {
|
||||||
|
this.element.removeEventListener("mouseenter", this.handleMouseEnter);
|
||||||
|
this.element.removeEventListener("focus", this.handleMouseEnter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("rowValue")
|
||||||
|
get isNone() {
|
||||||
|
return this.rowValue === this.getValue(this.selectKit.noneItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item")
|
||||||
|
get guid() {
|
||||||
|
return guidFor(this.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("isSelected")
|
||||||
|
get ariaChecked() {
|
||||||
|
return this.isSelected ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("rowTitle", "item.title", "rowName")
|
||||||
|
get title() {
|
||||||
|
return (
|
||||||
|
this.rowTitle || this.getProperty(this.item, "title") || this.rowName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("title")
|
||||||
|
get dasherizedTitle() {
|
||||||
|
return dasherize((this.title || "").replace(".", "-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("rowLabel", "item.label", "title", "rowName")
|
||||||
|
get label() {
|
||||||
|
const label =
|
||||||
|
this.rowLabel ||
|
||||||
|
this.getProperty(this.item, "label") ||
|
||||||
|
this.title ||
|
||||||
|
this.rowName;
|
||||||
|
if (
|
||||||
|
this.selectKit.options.allowAny &&
|
||||||
|
this.rowValue === this.selectKit.filter &&
|
||||||
|
this.getName(this.selectKit.noneItem) !== this.rowName &&
|
||||||
|
this.getName(this.selectKit.newItem) === this.rowName
|
||||||
|
) {
|
||||||
|
return i18n("select_kit.create", { content: label });
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
super.didReceiveAttrs(...arguments);
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
rowName: this.getName(this.item),
|
||||||
|
rowValue: this.getValue(this.item),
|
||||||
|
rowLabel: this.getProperty(this.item, "labelProperty"),
|
||||||
|
rowTitle: this.getProperty(this.item, "titleProperty"),
|
||||||
|
rowLang: this.getProperty(this.item, "langProperty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item.{icon,icons}")
|
||||||
|
get icons() {
|
||||||
|
const icon = makeArray(this.getProperty(this.item, "icon"));
|
||||||
|
const icons = makeArray(this.getProperty(this.item, "icons"));
|
||||||
|
return icon.concat(icons).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("selectKit.highlighted")
|
||||||
|
get highlightedValue() {
|
||||||
|
return this.getValue(this.selectKit.highlighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("rowValue", "highlightedValue")
|
||||||
|
get isHighlighted() {
|
||||||
|
return this.rowValue === this.highlightedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("rowValue", "value")
|
||||||
|
get isSelected() {
|
||||||
|
return this.rowValue === this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handleMouseEnter() {
|
||||||
|
if (!this.isDestroying || !this.isDestroyed) {
|
||||||
|
this.selectKit.onHover(this.rowValue, this.item);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
click(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.selectKit.select(this.rowValue, this.item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseDown(event) {
|
||||||
|
if (this.selectKit.options.preventHeaderFocus) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focusIn(event) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
keyDown(event) {
|
||||||
|
if (this.selectKit.isExpanded) {
|
||||||
|
if (event.key === "Backspace") {
|
||||||
|
if (this.selectKit.isFilterExpanded) {
|
||||||
|
this.selectKit.set("filter", this.selectKit.filter.slice(0, -1));
|
||||||
|
this.selectKit.triggerSearch();
|
||||||
|
this.selectKit.focusFilter();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (event.key === "ArrowUp") {
|
||||||
|
this.selectKit.highlightPrevious();
|
||||||
|
return false;
|
||||||
|
} else if (event.key === "ArrowDown") {
|
||||||
|
this.selectKit.highlightNext();
|
||||||
|
return false;
|
||||||
|
} else if (event.key === "Enter") {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
this.selectKit.select(
|
||||||
|
this.getValue(this.selectKit.highlighted),
|
||||||
|
this.selectKit.highlighted
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} else if (event.key === "Escape") {
|
||||||
|
this.selectKit.close(event);
|
||||||
|
this.selectKit.headerElement().focus();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
} else {
|
||||||
|
if (this.isValidInput(event.key)) {
|
||||||
|
this.selectKit.set("filter", event.key);
|
||||||
|
this.selectKit.triggerSearch();
|
||||||
|
this.selectKit.focusFilter();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#each this.icons as |icon|}}
|
{{#each this.icons as |icon|}}
|
||||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
@ -1,206 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { action, computed } from "@ember/object";
|
|
||||||
import { reads } from "@ember/object/computed";
|
|
||||||
import { guidFor } from "@ember/object/internals";
|
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
import {
|
|
||||||
attributeBindings,
|
|
||||||
classNameBindings,
|
|
||||||
classNames,
|
|
||||||
tagName,
|
|
||||||
} from "@ember-decorators/component";
|
|
||||||
import { makeArray } from "discourse/lib/helpers";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import UtilsMixin from "select-kit/mixins/utils";
|
|
||||||
|
|
||||||
@classNames("select-kit-row")
|
|
||||||
@tagName("li")
|
|
||||||
@attributeBindings(
|
|
||||||
"tabIndex",
|
|
||||||
"title",
|
|
||||||
"rowValue:data-value",
|
|
||||||
"rowName:data-name",
|
|
||||||
"index:data-index",
|
|
||||||
"role",
|
|
||||||
"ariaChecked:aria-checked",
|
|
||||||
"guid:data-guid",
|
|
||||||
"rowLang:lang"
|
|
||||||
)
|
|
||||||
@classNameBindings(
|
|
||||||
"isHighlighted",
|
|
||||||
"isSelected",
|
|
||||||
"isNone",
|
|
||||||
"isNone:none",
|
|
||||||
"item.classNames"
|
|
||||||
)
|
|
||||||
export default class SelectKitRow extends Component.extend(UtilsMixin) {
|
|
||||||
tabIndex = 0;
|
|
||||||
index = 0;
|
|
||||||
role = "menuitemradio";
|
|
||||||
|
|
||||||
@reads("item.lang") lang;
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
super.didInsertElement(...arguments);
|
|
||||||
|
|
||||||
if (this.site.desktopView) {
|
|
||||||
this.element.addEventListener("mouseenter", this.handleMouseEnter);
|
|
||||||
this.element.addEventListener("focus", this.handleMouseEnter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
super.willDestroyElement(...arguments);
|
|
||||||
|
|
||||||
if (this.site.desktopView) {
|
|
||||||
this.element.removeEventListener("mouseenter", this.handleMouseEnter);
|
|
||||||
this.element.removeEventListener("focus", this.handleMouseEnter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("rowValue")
|
|
||||||
get isNone() {
|
|
||||||
return this.rowValue === this.getValue(this.selectKit.noneItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item")
|
|
||||||
get guid() {
|
|
||||||
return guidFor(this.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("isSelected")
|
|
||||||
get ariaChecked() {
|
|
||||||
return this.isSelected ? "true" : "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("rowTitle", "item.title", "rowName")
|
|
||||||
get title() {
|
|
||||||
return (
|
|
||||||
this.rowTitle || this.getProperty(this.item, "title") || this.rowName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("title")
|
|
||||||
get dasherizedTitle() {
|
|
||||||
return dasherize((this.title || "").replace(".", "-"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("rowLabel", "item.label", "title", "rowName")
|
|
||||||
get label() {
|
|
||||||
const label =
|
|
||||||
this.rowLabel ||
|
|
||||||
this.getProperty(this.item, "label") ||
|
|
||||||
this.title ||
|
|
||||||
this.rowName;
|
|
||||||
if (
|
|
||||||
this.selectKit.options.allowAny &&
|
|
||||||
this.rowValue === this.selectKit.filter &&
|
|
||||||
this.getName(this.selectKit.noneItem) !== this.rowName &&
|
|
||||||
this.getName(this.selectKit.newItem) === this.rowName
|
|
||||||
) {
|
|
||||||
return i18n("select_kit.create", { content: label });
|
|
||||||
}
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
super.didReceiveAttrs(...arguments);
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
rowName: this.getName(this.item),
|
|
||||||
rowValue: this.getValue(this.item),
|
|
||||||
rowLabel: this.getProperty(this.item, "labelProperty"),
|
|
||||||
rowTitle: this.getProperty(this.item, "titleProperty"),
|
|
||||||
rowLang: this.getProperty(this.item, "langProperty"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item.{icon,icons}")
|
|
||||||
get icons() {
|
|
||||||
const icon = makeArray(this.getProperty(this.item, "icon"));
|
|
||||||
const icons = makeArray(this.getProperty(this.item, "icons"));
|
|
||||||
return icon.concat(icons).filter(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("selectKit.highlighted")
|
|
||||||
get highlightedValue() {
|
|
||||||
return this.getValue(this.selectKit.highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("rowValue", "highlightedValue")
|
|
||||||
get isHighlighted() {
|
|
||||||
return this.rowValue === this.highlightedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("rowValue", "value")
|
|
||||||
get isSelected() {
|
|
||||||
return this.rowValue === this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
handleMouseEnter() {
|
|
||||||
if (!this.isDestroying || !this.isDestroyed) {
|
|
||||||
this.selectKit.onHover(this.rowValue, this.item);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
click(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
this.selectKit.select(this.rowValue, this.item);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mouseDown(event) {
|
|
||||||
if (this.selectKit.options.preventHeaderFocus) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
focusIn(event) {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
keyDown(event) {
|
|
||||||
if (this.selectKit.isExpanded) {
|
|
||||||
if (event.key === "Backspace") {
|
|
||||||
if (this.selectKit.isFilterExpanded) {
|
|
||||||
this.selectKit.set("filter", this.selectKit.filter.slice(0, -1));
|
|
||||||
this.selectKit.triggerSearch();
|
|
||||||
this.selectKit.focusFilter();
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (event.key === "ArrowUp") {
|
|
||||||
this.selectKit.highlightPrevious();
|
|
||||||
return false;
|
|
||||||
} else if (event.key === "ArrowDown") {
|
|
||||||
this.selectKit.highlightNext();
|
|
||||||
return false;
|
|
||||||
} else if (event.key === "Enter") {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
|
|
||||||
this.selectKit.select(
|
|
||||||
this.getValue(this.selectKit.highlighted),
|
|
||||||
this.selectKit.highlighted
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
} else if (event.key === "Escape") {
|
|
||||||
this.selectKit.close(event);
|
|
||||||
this.selectKit.headerElement().focus();
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
} else {
|
|
||||||
if (this.isValidInput(event.key)) {
|
|
||||||
this.selectKit.set("filter", event.key);
|
|
||||||
this.selectKit.triggerSearch();
|
|
||||||
this.selectKit.focusFilter();
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,46 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
import { guidFor } from "@ember/object/internals";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
import UtilsMixin from "select-kit/mixins/utils";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class SelectedChoice extends Component.extend(UtilsMixin) {
|
||||||
|
item = null;
|
||||||
|
selectKit = null;
|
||||||
|
extraClass = null;
|
||||||
|
id = null;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(...arguments);
|
||||||
|
|
||||||
|
this.set("id", guidFor(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item")
|
||||||
|
get itemValue() {
|
||||||
|
return this.getValue(this.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item")
|
||||||
|
get itemName() {
|
||||||
|
return this.getName(this.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item")
|
||||||
|
get mandatoryValuesArray() {
|
||||||
|
return this.get("mandatoryValues")?.split("|") || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item")
|
||||||
|
get readOnly() {
|
||||||
|
if (typeof this.item === "string") {
|
||||||
|
return this.mandatoryValuesArray.includes(this.item);
|
||||||
|
}
|
||||||
|
return this.mandatoryValuesArray.includes(this.item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.readOnly}}
|
{{#if this.readOnly}}
|
||||||
<button
|
<button
|
||||||
class="btn btn-default disabled"
|
class="btn btn-default disabled"
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
import { guidFor } from "@ember/object/internals";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import UtilsMixin from "select-kit/mixins/utils";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class SelectedChoice extends Component.extend(UtilsMixin) {
|
|
||||||
item = null;
|
|
||||||
selectKit = null;
|
|
||||||
extraClass = null;
|
|
||||||
id = null;
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(...arguments);
|
|
||||||
|
|
||||||
this.set("id", guidFor(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item")
|
|
||||||
get itemValue() {
|
|
||||||
return this.getValue(this.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item")
|
|
||||||
get itemName() {
|
|
||||||
return this.getName(this.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item")
|
|
||||||
get mandatoryValuesArray() {
|
|
||||||
return this.get("mandatoryValues")?.split("|") || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item")
|
|
||||||
get readOnly() {
|
|
||||||
if (typeof this.item === "string") {
|
|
||||||
return this.mandatoryValuesArray.includes(this.item);
|
|
||||||
}
|
|
||||||
return this.mandatoryValuesArray.includes(this.item.id);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
import SelectedNameComponent from "select-kit/components/selected-name";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class SelectedFlair extends SelectedNameComponent {}
|
||||||
|
|
||||||
{{#if this.item.url}}
|
{{#if this.item.url}}
|
||||||
<AvatarFlair
|
<AvatarFlair
|
||||||
@flairName={{this.item.name}}
|
@flairName={{this.item.name}}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import SelectedNameComponent from "select-kit/components/selected-name";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class SelectedFlair extends SelectedNameComponent {}
|
|
@ -1,3 +1,96 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import { computed, get } from "@ember/object";
|
||||||
|
import { reads } from "@ember/object/computed";
|
||||||
|
import { guidFor } from "@ember/object/internals";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
import { makeArray } from "discourse/lib/helpers";
|
||||||
|
import UtilsMixin from "select-kit/mixins/utils";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class SelectedName extends Component.extend(UtilsMixin) {
|
||||||
|
name = null;
|
||||||
|
value = null;
|
||||||
|
headerTitle = null;
|
||||||
|
headerLang = null;
|
||||||
|
headerLabel = null;
|
||||||
|
id = null;
|
||||||
|
|
||||||
|
@reads("headerLang") lang;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(...arguments);
|
||||||
|
|
||||||
|
this.set("id", guidFor(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
super.didReceiveAttrs(...arguments);
|
||||||
|
|
||||||
|
// we can't listen on `item.nameProperty` given it's variable
|
||||||
|
this.setProperties({
|
||||||
|
headerLabel: this.getProperty(this.item, "labelProperty"),
|
||||||
|
headerTitle: this.getProperty(this.item, "titleProperty"),
|
||||||
|
headerLang: this.getProperty(this.item, "langProperty"),
|
||||||
|
name: this.getName(this.item),
|
||||||
|
renderIcon: this.canDisplayIcon,
|
||||||
|
value:
|
||||||
|
this.item === this.selectKit.noneItem ? null : this.getValue(this.item),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("selectKit.options.shouldDisplayIcon")
|
||||||
|
get canDisplayIcon() {
|
||||||
|
return this.selectKit.options.shouldDisplayIcon ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item", "sanitizedTitle")
|
||||||
|
get ariaLabel() {
|
||||||
|
return this._safeProperty("ariaLabel", this.item) || this.sanitizedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this might need a more advanced solution
|
||||||
|
// but atm it's the only case we have to handle
|
||||||
|
@computed("title")
|
||||||
|
get sanitizedTitle() {
|
||||||
|
return String(this.title).replace("…", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("headerTitle", "item")
|
||||||
|
get title() {
|
||||||
|
return (
|
||||||
|
this.headerTitle ||
|
||||||
|
this._safeProperty("title", this.item) ||
|
||||||
|
this.name ||
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("headerLabel", "title", "name")
|
||||||
|
get label() {
|
||||||
|
return (
|
||||||
|
this.headerLabel ||
|
||||||
|
this._safeProperty("label", this.item) ||
|
||||||
|
this.title ||
|
||||||
|
this.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed("item.{icon,icons}")
|
||||||
|
get icons() {
|
||||||
|
const icon = makeArray(this._safeProperty("icon", this.item));
|
||||||
|
const icons = makeArray(this._safeProperty("icons", this.item));
|
||||||
|
return icon.concat(icons).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
_safeProperty(name, content) {
|
||||||
|
if (!content) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get(content, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.selectKit.options.showFullTitle}}
|
{{#if this.selectKit.options.showFullTitle}}
|
||||||
<div
|
<div
|
||||||
lang={{this.lang}}
|
lang={{this.lang}}
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { computed, get } from "@ember/object";
|
|
||||||
import { reads } from "@ember/object/computed";
|
|
||||||
import { guidFor } from "@ember/object/internals";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import { makeArray } from "discourse/lib/helpers";
|
|
||||||
import UtilsMixin from "select-kit/mixins/utils";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class SelectedName extends Component.extend(UtilsMixin) {
|
|
||||||
name = null;
|
|
||||||
value = null;
|
|
||||||
headerTitle = null;
|
|
||||||
headerLang = null;
|
|
||||||
headerLabel = null;
|
|
||||||
id = null;
|
|
||||||
|
|
||||||
@reads("headerLang") lang;
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(...arguments);
|
|
||||||
|
|
||||||
this.set("id", guidFor(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
super.didReceiveAttrs(...arguments);
|
|
||||||
|
|
||||||
// we can't listen on `item.nameProperty` given it's variable
|
|
||||||
this.setProperties({
|
|
||||||
headerLabel: this.getProperty(this.item, "labelProperty"),
|
|
||||||
headerTitle: this.getProperty(this.item, "titleProperty"),
|
|
||||||
headerLang: this.getProperty(this.item, "langProperty"),
|
|
||||||
name: this.getName(this.item),
|
|
||||||
renderIcon: this.canDisplayIcon,
|
|
||||||
value:
|
|
||||||
this.item === this.selectKit.noneItem ? null : this.getValue(this.item),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("selectKit.options.shouldDisplayIcon")
|
|
||||||
get canDisplayIcon() {
|
|
||||||
return this.selectKit.options.shouldDisplayIcon ?? true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item", "sanitizedTitle")
|
|
||||||
get ariaLabel() {
|
|
||||||
return this._safeProperty("ariaLabel", this.item) || this.sanitizedTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this might need a more advanced solution
|
|
||||||
// but atm it's the only case we have to handle
|
|
||||||
@computed("title")
|
|
||||||
get sanitizedTitle() {
|
|
||||||
return String(this.title).replace("…", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("headerTitle", "item")
|
|
||||||
get title() {
|
|
||||||
return (
|
|
||||||
this.headerTitle ||
|
|
||||||
this._safeProperty("title", this.item) ||
|
|
||||||
this.name ||
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("headerLabel", "title", "name")
|
|
||||||
get label() {
|
|
||||||
return (
|
|
||||||
this.headerLabel ||
|
|
||||||
this._safeProperty("label", this.item) ||
|
|
||||||
this.title ||
|
|
||||||
this.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed("item.{icon,icons}")
|
|
||||||
get icons() {
|
|
||||||
const icon = makeArray(this._safeProperty("icon", this.item));
|
|
||||||
const icons = makeArray(this._safeProperty("icons", this.item));
|
|
||||||
return icon.concat(icons).filter(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
_safeProperty(name, content) {
|
|
||||||
if (!content) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return get(content, name);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1,7 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("tag-chooser-row")
|
||||||
|
export default class TagChooserRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{discourse-tag this.rowValue count=this.item.count noHref=true}}
|
{{discourse-tag this.rowValue count=this.item.count noHref=true}}
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("tag-chooser-row")
|
|
||||||
export default class TagChooserRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,15 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("tag-row")
|
||||||
|
export default class TagRow extends SelectKitRowComponent {
|
||||||
|
@discourseComputed("item")
|
||||||
|
isTag(item) {
|
||||||
|
return item.id !== "no-tags" && item.id !== "all-tags";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{{#if this.isTag}}
|
{{#if this.isTag}}
|
||||||
{{discourse-tag
|
{{discourse-tag
|
||||||
this.rowValue
|
this.rowValue
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("tag-row")
|
|
||||||
export default class TagRow extends SelectKitRowComponent {
|
|
||||||
@discourseComputed("item")
|
|
||||||
isTag(item) {
|
|
||||||
return item.id !== "no-tags" && item.id !== "all-tags";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1,5 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
|
||||||
|
export default class ToolbarPopupMenuOptionsHeading extends Component {}
|
||||||
|
|
||||||
{{this.heading}}
|
{{this.heading}}
|
@ -1,3 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
|
|
||||||
export default class ToolbarPopupMenuOptionsHeading extends Component {}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("topic-row")
|
||||||
|
export default class TopicRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
<TopicStatus @topic={{this.item}} @disableActions={{true}} />
|
<TopicStatus @topic={{this.item}} @disableActions={{true}} />
|
||||||
<div class="topic-title">{{replace-emoji this.item.title}}</div>
|
<div class="topic-title">{{replace-emoji this.item.title}}</div>
|
||||||
<div class="topic-categories">
|
<div class="topic-categories">
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("topic-row")
|
|
||||||
export default class TopicRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,9 @@
|
|||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
||||||
|
|
||||||
|
@classNames("user-row")
|
||||||
|
export default class UserRow extends SelectKitRowComponent {}
|
||||||
|
|
||||||
{{avatar this.item imageSize="tiny"}}
|
{{avatar this.item imageSize="tiny"}}
|
||||||
|
|
||||||
<span class="username">{{format-username this.item.username}}</span>
|
<span class="username">{{format-username this.item.username}}</span>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
|
|
||||||
|
|
||||||
@classNames("user-row")
|
|
||||||
export default class UserRow extends SelectKitRowComponent {}
|
|
@ -1,3 +1,35 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { cached } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatComposerMessageDetails extends Component {
|
||||||
|
@service site;
|
||||||
|
@service session;
|
||||||
|
@service keyValueStore;
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
@cached
|
||||||
|
get message() {
|
||||||
|
return new ChatFabricators(getOwner(this)).message({
|
||||||
|
user: this.currentUser,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleMode() {
|
||||||
|
if (this.message.editing) {
|
||||||
|
this.message.editing = false;
|
||||||
|
this.message.inReplyTo = new ChatFabricators(getOwner(this)).message();
|
||||||
|
} else {
|
||||||
|
this.message.editing = true;
|
||||||
|
this.message.inReplyTo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<ChatComposerMessageDetails>">
|
<StyleguideExample @title="<ChatComposerMessageDetails>">
|
||||||
<Styleguide::Component>
|
<Styleguide::Component>
|
||||||
<ChatComposerMessageDetails @message={{this.message}} />
|
<ChatComposerMessageDetails @message={{this.message}} />
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { cached } from "@glimmer/tracking";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { getOwner } from "@ember/owner";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
|
|
||||||
export default class ChatStyleguideChatComposerMessageDetails extends Component {
|
|
||||||
@service site;
|
|
||||||
@service session;
|
|
||||||
@service keyValueStore;
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
@cached
|
|
||||||
get message() {
|
|
||||||
return new ChatFabricators(getOwner(this)).message({
|
|
||||||
user: this.currentUser,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleMode() {
|
|
||||||
if (this.message.editing) {
|
|
||||||
this.message.editing = false;
|
|
||||||
this.message.inReplyTo = new ChatFabricators(getOwner(this)).message();
|
|
||||||
} else {
|
|
||||||
this.message.editing = true;
|
|
||||||
this.message.inReplyTo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,36 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatComposer extends Component {
|
||||||
|
@service chatChannelComposer;
|
||||||
|
@service chatChannelPane;
|
||||||
|
|
||||||
|
channel = new ChatFabricators(getOwner(this)).channel({ id: -999 });
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleDisabled() {
|
||||||
|
if (this.channel.status === CHANNEL_STATUSES.open) {
|
||||||
|
this.channel.status = CHANNEL_STATUSES.readOnly;
|
||||||
|
} else {
|
||||||
|
this.channel.status = CHANNEL_STATUSES.open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleSending() {
|
||||||
|
this.chatChannelPane.sending = !this.chatChannelPane.sending;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onSendMessage() {
|
||||||
|
this.chatChannelComposer.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<ChatComposer>">
|
<StyleguideExample @title="<ChatComposer>">
|
||||||
<Styleguide::Component>
|
<Styleguide::Component>
|
||||||
<Chat::Composer::Channel
|
<Chat::Composer::Channel
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { getOwner } from "@ember/owner";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
||||||
|
|
||||||
export default class ChatStyleguideChatComposer extends Component {
|
|
||||||
@service chatChannelComposer;
|
|
||||||
@service chatChannelPane;
|
|
||||||
|
|
||||||
channel = new ChatFabricators(getOwner(this)).channel({ id: -999 });
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleDisabled() {
|
|
||||||
if (this.channel.status === CHANNEL_STATUSES.open) {
|
|
||||||
this.channel.status = CHANNEL_STATUSES.readOnly;
|
|
||||||
} else {
|
|
||||||
this.channel.status = CHANNEL_STATUSES.open;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleSending() {
|
|
||||||
this.chatChannelPane.sending = !this.chatChannelPane.sending;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onSendMessage() {
|
|
||||||
this.chatChannelComposer.reset();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,55 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import {
|
||||||
|
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_NEVER,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
|
||||||
|
} from "discourse/plugins/chat/discourse/controllers/preferences-chat";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatHeaderIcon extends Component {
|
||||||
|
@tracked isActive = false;
|
||||||
|
@tracked currentUserInDnD = false;
|
||||||
|
@tracked urgentCount;
|
||||||
|
@tracked unreadCount;
|
||||||
|
@tracked indicatorPreference = HEADER_INDICATOR_PREFERENCE_ALL_NEW;
|
||||||
|
|
||||||
|
get indicatorPreferences() {
|
||||||
|
return [
|
||||||
|
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
|
||||||
|
HEADER_INDICATOR_PREFERENCE_NEVER,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleIsActive() {
|
||||||
|
this.isActive = !this.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleCurrentUserInDnD() {
|
||||||
|
this.currentUserInDnD = !this.currentUserInDnD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
updateUnreadCount(event) {
|
||||||
|
this.unreadCount = event.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
updateUrgentCount(event) {
|
||||||
|
this.urgentCount = event.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
updateIndicatorPreference(value) {
|
||||||
|
this.indicatorPreference = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<Chat::Header::Icon>">
|
<StyleguideExample @title="<Chat::Header::Icon>">
|
||||||
<Styleguide::Component>
|
<Styleguide::Component>
|
||||||
<header
|
<header
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import {
|
|
||||||
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_NEVER,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
|
|
||||||
} from "discourse/plugins/chat/discourse/controllers/preferences-chat";
|
|
||||||
|
|
||||||
export default class ChatStyleguideChatHeaderIcon extends Component {
|
|
||||||
@tracked isActive = false;
|
|
||||||
@tracked currentUserInDnD = false;
|
|
||||||
@tracked urgentCount;
|
|
||||||
@tracked unreadCount;
|
|
||||||
@tracked indicatorPreference = HEADER_INDICATOR_PREFERENCE_ALL_NEW;
|
|
||||||
|
|
||||||
get indicatorPreferences() {
|
|
||||||
return [
|
|
||||||
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
|
|
||||||
HEADER_INDICATOR_PREFERENCE_NEVER,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleIsActive() {
|
|
||||||
this.isActive = !this.isActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleCurrentUserInDnD() {
|
|
||||||
this.currentUserInDnD = !this.currentUserInDnD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updateUnreadCount(event) {
|
|
||||||
this.unreadCount = event.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updateUrgentCount(event) {
|
|
||||||
this.urgentCount = event.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updateIndicatorPreference(value) {
|
|
||||||
this.indicatorPreference = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,99 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager";
|
||||||
|
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatMessage extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
manager = new ChatMessagesManager(getOwner(this));
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.message = new ChatFabricators(getOwner(this)).message({
|
||||||
|
user: this.currentUser,
|
||||||
|
});
|
||||||
|
this.message.cook();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleDeleted() {
|
||||||
|
if (this.message.deletedAt) {
|
||||||
|
this.message.deletedAt = null;
|
||||||
|
} else {
|
||||||
|
this.message.deletedAt = moment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleBookmarked() {
|
||||||
|
if (this.message.bookmark) {
|
||||||
|
this.message.bookmark = null;
|
||||||
|
} else {
|
||||||
|
this.message.bookmark = new ChatFabricators(getOwner(this)).bookmark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleHighlighted() {
|
||||||
|
this.message.highlighted = !this.message.highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleEdited() {
|
||||||
|
this.message.edited = !this.message.edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleThread() {
|
||||||
|
if (this.message.thread) {
|
||||||
|
this.message.channel.threadingEnabled = false;
|
||||||
|
this.message.thread = null;
|
||||||
|
} else {
|
||||||
|
this.message.thread = new ChatFabricators(getOwner(this)).thread({
|
||||||
|
channel: this.message.channel,
|
||||||
|
});
|
||||||
|
this.message.thread.preview.replyCount = 1;
|
||||||
|
this.message.channel.threadingEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async updateMessage(event) {
|
||||||
|
this.message.message = event.target.value;
|
||||||
|
await this.message.cook();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleReaction() {
|
||||||
|
if (this.message.reactions?.length) {
|
||||||
|
this.message.reactions = [];
|
||||||
|
} else {
|
||||||
|
this.message.reactions = [
|
||||||
|
new ChatFabricators(getOwner(this)).reaction({ emoji: "heart" }),
|
||||||
|
new ChatFabricators(getOwner(this)).reaction({
|
||||||
|
emoji: "rocket",
|
||||||
|
reacted: true,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleUpload() {
|
||||||
|
if (this.message.uploads?.length) {
|
||||||
|
this.message.uploads = [];
|
||||||
|
} else {
|
||||||
|
this.message.uploads = [
|
||||||
|
new ChatFabricators(getOwner(this)).upload(),
|
||||||
|
new ChatFabricators(getOwner(this)).upload(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<ChatMessage>">
|
<StyleguideExample @title="<ChatMessage>">
|
||||||
<Styleguide::Component>
|
<Styleguide::Component>
|
||||||
<ChatMessage @message={{this.message}} @context="channel" />
|
<ChatMessage @message={{this.message}} @context="channel" />
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { getOwner } from "@ember/owner";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager";
|
|
||||||
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
|
|
||||||
export default class ChatStyleguideChatMessage extends Component {
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
manager = new ChatMessagesManager(getOwner(this));
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.message = new ChatFabricators(getOwner(this)).message({
|
|
||||||
user: this.currentUser,
|
|
||||||
});
|
|
||||||
this.message.cook();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleDeleted() {
|
|
||||||
if (this.message.deletedAt) {
|
|
||||||
this.message.deletedAt = null;
|
|
||||||
} else {
|
|
||||||
this.message.deletedAt = moment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleBookmarked() {
|
|
||||||
if (this.message.bookmark) {
|
|
||||||
this.message.bookmark = null;
|
|
||||||
} else {
|
|
||||||
this.message.bookmark = new ChatFabricators(getOwner(this)).bookmark();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleHighlighted() {
|
|
||||||
this.message.highlighted = !this.message.highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleEdited() {
|
|
||||||
this.message.edited = !this.message.edited;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleThread() {
|
|
||||||
if (this.message.thread) {
|
|
||||||
this.message.channel.threadingEnabled = false;
|
|
||||||
this.message.thread = null;
|
|
||||||
} else {
|
|
||||||
this.message.thread = new ChatFabricators(getOwner(this)).thread({
|
|
||||||
channel: this.message.channel,
|
|
||||||
});
|
|
||||||
this.message.thread.preview.replyCount = 1;
|
|
||||||
this.message.channel.threadingEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
async updateMessage(event) {
|
|
||||||
this.message.message = event.target.value;
|
|
||||||
await this.message.cook();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleReaction() {
|
|
||||||
if (this.message.reactions?.length) {
|
|
||||||
this.message.reactions = [];
|
|
||||||
} else {
|
|
||||||
this.message.reactions = [
|
|
||||||
new ChatFabricators(getOwner(this)).reaction({ emoji: "heart" }),
|
|
||||||
new ChatFabricators(getOwner(this)).reaction({
|
|
||||||
emoji: "rocket",
|
|
||||||
reacted: true,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleUpload() {
|
|
||||||
if (this.message.uploads?.length) {
|
|
||||||
this.message.uploads = [];
|
|
||||||
} else {
|
|
||||||
this.message.uploads = [
|
|
||||||
new ChatFabricators(getOwner(this)).upload(),
|
|
||||||
new ChatFabricators(getOwner(this)).upload(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,25 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
|
||||||
|
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatModalArchiveChannel extends Component {
|
||||||
|
@service modal;
|
||||||
|
|
||||||
|
channel = new ChatFabricators(getOwner(this)).channel();
|
||||||
|
|
||||||
|
@action
|
||||||
|
openModal() {
|
||||||
|
return this.modal.show(ChatModalArchiveChannel, {
|
||||||
|
model: {
|
||||||
|
channel: this.channel,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<Chat::Modal::ArchiveChannel>">
|
<StyleguideExample @title="<Chat::Modal::ArchiveChannel>">
|
||||||
<Styleguide::Controls::Row>
|
<Styleguide::Controls::Row>
|
||||||
<DButton @translatedLabel="Open modal" @action={{this.openModal}} />
|
<DButton @translatedLabel="Open modal" @action={{this.openModal}} />
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { getOwner } from "@ember/owner";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
|
|
||||||
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
|
|
||||||
export default class ChatStyleguideChatModalArchiveChannel extends Component {
|
|
||||||
@service modal;
|
|
||||||
|
|
||||||
channel = new ChatFabricators(getOwner(this)).channel();
|
|
||||||
|
|
||||||
@action
|
|
||||||
openModal() {
|
|
||||||
return this.modal.show(ChatModalArchiveChannel, {
|
|
||||||
model: {
|
|
||||||
channel: this.channel,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,17 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import ChatModalCreateChannel from "discourse/plugins/chat/discourse/components/chat/modal/create-channel";
|
||||||
|
|
||||||
|
export default class ChatStyleguideChatModalCreateChannel extends Component {
|
||||||
|
@service modal;
|
||||||
|
|
||||||
|
@action
|
||||||
|
openModal() {
|
||||||
|
return this.modal.show(ChatModalCreateChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<StyleguideExample @title="<Chat::Modal::CreateChannel>">
|
<StyleguideExample @title="<Chat::Modal::CreateChannel>">
|
||||||
<Styleguide::Controls::Row>
|
<Styleguide::Controls::Row>
|
||||||
<DButton @translatedLabel="Open modal" @action={{this.openModal}} />
|
<DButton @translatedLabel="Open modal" @action={{this.openModal}} />
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user