mirror of
https://github.com/discourse/discourse.git
synced 2025-05-25 19:29:34 +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">
|
||||
<PluginOutlet
|
||||
@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|}}
|
||||
<div class="checkbox-field-choice {{this.fieldClass}}">
|
||||
<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}} />
|
@ -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">
|
||||
<canvas
|
||||
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="wizard-container__preview topic-preview">
|
||||
<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
|
||||
id={{this.field.id}}
|
||||
@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">
|
||||
{{this.label}}
|
||||
</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>
|
@ -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}}
|
||||
<div class="icons">
|
||||
<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}}
|
||||
{{avatar this.item imageSize="tiny"}}
|
||||
<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}}
|
||||
<AvatarFlair
|
||||
@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}}
|
||||
<div class="future-date-input-selector-icons">
|
||||
{{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">
|
||||
<span class="name">{{html-safe this.label}}</span>
|
||||
{{#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}}
|
||||
<div class="mini-tag-chooser-selected-collection selected-tags">
|
||||
{{#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">
|
||||
{{this.formattedContent}}
|
||||
</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}}
|
||||
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
||||
{{! 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
|
||||
{{on "click" this.onSelectedNameClick}}
|
||||
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}}
|
||||
<div class="category-status" aria-hidden="true">
|
||||
{{#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">
|
||||
<span class="filter-text">
|
||||
{{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}}>
|
||||
{{period-title
|
||||
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="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}}
|
||||
<ul class="select-kit-errors-collection">
|
||||
{{#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}}
|
||||
{{yield}}
|
||||
{{/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|}}
|
||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||
{{/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}}
|
||||
{{! filter-input-search prevents 1password from attempting autocomplete }}
|
||||
{{! 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|}}
|
||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||
{{/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|}}
|
||||
{{d-icon icon translatedTitle=this.dasherizedTitle}}
|
||||
{{/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}}
|
||||
<button
|
||||
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}}
|
||||
<AvatarFlair
|
||||
@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}}
|
||||
<div
|
||||
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}}
|
@ -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}}
|
||||
{{discourse-tag
|
||||
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}}
|
@ -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}} />
|
||||
<div class="topic-title">{{replace-emoji this.item.title}}</div>
|
||||
<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"}}
|
||||
|
||||
<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>">
|
||||
<Styleguide::Component>
|
||||
<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>">
|
||||
<Styleguide::Component>
|
||||
<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>">
|
||||
<Styleguide::Component>
|
||||
<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>">
|
||||
<Styleguide::Component>
|
||||
<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>">
|
||||
<Styleguide::Controls::Row>
|
||||
<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>">
|
||||
<Styleguide::Controls::Row>
|
||||
<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