DEV: [gjs-codemod] merge js and hbs

This commit is contained in:
David Taylor
2025-04-14 15:27:52 +01:00
150 changed files with 3433 additions and 3372 deletions

View File

@ -1,3 +1,9 @@
import Component from "@ember/component";
import { tagName } from "@ember-decorators/component";
@tagName("")
export default class Checkbox extends Component {}
<label class="wizard-container__label"> <label class="wizard-container__label">
<PluginOutlet <PluginOutlet
@name="wizard-checkbox" @name="wizard-checkbox"

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
import { tagName } from "@ember-decorators/component";
@tagName("")
export default class Checkbox extends Component {}

View File

@ -1,3 +1,37 @@
import Component from "@ember/component";
import { action, set } from "@ember/object";
export default class Checkboxes extends Component {
init(...args) {
super.init(...args);
this.set("field.value", this.field.value || []);
for (let choice of this.field.choices) {
if (this.field.value.includes(choice.id)) {
set(choice, "checked", true);
}
}
}
@action
changed(checkbox) {
let newFieldValue = this.field.value;
const checkboxValue = checkbox.parentElement
.getAttribute("value")
.toLowerCase();
if (checkbox.checked) {
newFieldValue.push(checkboxValue);
} else {
const index = newFieldValue.indexOf(checkboxValue);
if (index > -1) {
newFieldValue.splice(index, 1);
}
}
this.set("field.value", newFieldValue);
}
}
{{#each this.field.choices as |c|}} {{#each this.field.choices as |c|}}
<div class="checkbox-field-choice {{this.fieldClass}}"> <div class="checkbox-field-choice {{this.fieldClass}}">
<label id={{c.id}} value={{c.label}}> <label id={{c.id}} value={{c.label}}>

View File

@ -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);
}
}

View File

@ -1 +1,7 @@
import Component from "@ember/component";
import { classNameBindings } from "@ember-decorators/component";
@classNameBindings(":wizard-image-preview", "fieldClass")
export default class Generic extends Component {}
<img src={{this.field.value}} class={{this.fieldClass}} /> <img src={{this.field.value}} class={{this.fieldClass}} />

View File

@ -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 {}

View File

@ -1,3 +1,463 @@
/*eslint no-bitwise:0 */
import Component from "@ember/component";
import { action } from "@ember/object";
import { scheduleOnce } from "@ember/runloop";
import { htmlSafe } from "@ember/template";
import { Promise } from "rsvp";
import getUrl from "discourse/lib/get-url";
import PreloadStore from "discourse/lib/preload-store";
import { i18n } from "discourse-i18n";
import { darkLightDiff, drawHeader } from "../../../lib/preview";
const scaled = {};
function canvasFor(image, w, h) {
w = Math.ceil(w);
h = Math.ceil(h);
const scale = window.devicePixelRatio;
const can = document.createElement("canvas");
can.width = w * scale;
can.height = h * scale;
const ctx = can.getContext("2d");
ctx.scale(scale, scale);
ctx.drawImage(image, 0, 0, w, h);
return can;
}
const scale = window.devicePixelRatio;
export default class PreviewBase extends Component {
ctx = null;
loaded = false;
loadingFontVariants = false;
get elementWidth() {
return this.width * scale;
}
get elementHeight() {
return this.height * scale;
}
get canvasStyle() {
return htmlSafe(`width:${this.width}px;height:${this.height}px`);
}
didInsertElement() {
super.didInsertElement(...arguments);
this.fontMap = PreloadStore.get("fontMap");
this.loadedFonts = new Set();
const c = this.element.querySelector("canvas");
this.ctx = c.getContext("2d");
this.ctx.scale(scale, scale);
if (this.step) {
this.step.findField("color_scheme")?.addListener(this.themeChanged);
this.step.findField("homepage_style")?.addListener(this.themeChanged);
this.step.findField("body_font")?.addListener(this.themeBodyFontChanged);
this.step.findField("site_font")?.addListener(this.themeFontChanged);
this.step
.findField("heading_font")
?.addListener(this.themeHeadingFontChanged);
}
this.reload();
}
willDestroyElement() {
super.willDestroyElement(...arguments);
if (this.step) {
this.step.findField("color_scheme")?.removeListener(this.themeChanged);
this.step.findField("homepage_style")?.removeListener(this.themeChanged);
this.step
.findField("body_font")
?.removeListener(this.themeBodyFontChanged);
this.step.findField("site_font")?.removeListener(this.themeFontChanged);
this.step
.findField("heading_font")
?.removeListener(this.themeHeadingFontChanged);
}
}
@action
themeChanged() {
this.triggerRepaint();
}
@action
themeBodyFontChanged() {
if (!this.loadingFontVariants) {
this.loadFontVariants(this.wizard.font);
}
}
@action
themeHeadingFontChanged() {
if (!this.loadingFontVariants) {
this.loadFontVariants(this.wizard.headingFont);
}
}
@action
themeFontChanged() {
if (!this.loadingFontVariants) {
this.loadFontVariants(this.wizard.font);
}
}
loadFontVariants(font) {
if (!font) {
return Promise.resolve();
}
const fontVariantData = this.fontMap[font.id];
// System font for example does not need to load from a remote source.
if (!fontVariantData) {
this.loadedFonts.add(font.id);
}
if (fontVariantData && !this.loadedFonts.has(font.id)) {
this.loadingFontVariants = true;
const fontFaces = fontVariantData.map((fontVariant) => {
return new FontFace(font.label, `url(${fontVariant.url})`, {
style: "normal",
weight: fontVariant.weight,
});
});
return Promise.all(
fontFaces.map((fontFace) =>
fontFace.load().then((loadedFont) => {
document.fonts.add(loadedFont);
// We use our own Set because, though document.fonts.check is available,
// it does not seem very reliable, returning false for fonts that have
// definitely been loaded.
this.loadedFonts.add(font.id);
})
)
)
.then(() => {
this.triggerRepaint();
})
.finally(() => {
this.loadingFontVariants = false;
});
} else if (this.loadedFonts.has(font.id)) {
return Promise.resolve(this.triggerRepaint());
}
}
images() {}
// NOTE: This works for fonts included in a style that is actually using the
// @font-faces on load, but for fonts that we aren't using yet we need to
// make sure they are loaded before rendering the canvas via loadFontVariants.
loadFonts() {
return document.fonts.ready;
}
loadImages() {
const images = this.images();
if (images) {
return Promise.all(
Object.keys(images).map((id) => {
return loadImage(images[id]).then((img) => (this[id] = img));
})
);
}
return Promise.resolve();
}
reload() {
Promise.all([this.loadFonts(), this.loadImages()]).then(() => {
// NOTE: This must be done otherwise the "bold" variant of the body font
// will not be loaded for some reason before rendering the canvas.
//
// The header font does not suffer from this issue.
this.loadFontVariants(this.wizard.font).then(() => {
this.loaded = true;
this.triggerRepaint();
});
});
}
triggerRepaint() {
scheduleOnce("afterRender", this, "repaint");
}
repaint() {
if (!this.loaded) {
return false;
}
const colorsArray = this.wizard.currentColors;
if (!colorsArray) {
return;
}
let colors = {};
colorsArray.forEach(function (c) {
const name = c.name;
colors[name] = `#${c.hex}`;
});
const { font, headingFont } = this.wizard;
if (!font) {
return;
}
const { ctx } = this;
ctx.fillStyle = colors.secondary;
ctx.fillRect(0, 0, this.width, this.height);
const options = {
ctx,
colors,
font,
headingFont,
width: this.width,
height: this.height,
};
this.paint(options);
}
categories() {
return [
{
name: i18n("wizard.homepage_preview.category_names.icebreakers"),
color: "#652D90",
},
{
name: i18n("wizard.homepage_preview.category_names.news"),
color: "#3AB54A",
},
{
name: i18n("wizard.homepage_preview.category_names.site_feedback"),
color: "#25AAE2",
},
];
}
scaleImage(image, x, y, w, h) {
w = Math.floor(w);
h = Math.floor(h);
const { ctx } = this;
const key = `${image.src}-${w}-${h}`;
if (!scaled[key]) {
let copy = image;
let ratio = copy.width / copy.height;
let newH = copy.height * 0.5;
while (newH > h) {
copy = canvasFor(copy, ratio * newH, newH);
newH = newH * 0.5;
}
scaled[key] = copy;
}
ctx.drawImage(scaled[key], x, y, w, h);
}
get headerHeight() {
return this.height * 0.15;
}
drawFullHeader(colors, font, logo) {
const { ctx } = this;
drawHeader(ctx, colors, this.width, this.headerHeight);
const avatarSize = this.height * 0.1;
const headerMargin = this.headerHeight * 0.2;
if (logo) {
const logoHeight = this.headerHeight - headerMargin * 2;
const ratio = logoHeight / logo.height;
this.scaleImage(
logo,
headerMargin,
headerMargin,
logo.width * ratio,
logoHeight
);
this.scaleImage(logo, this.width, headerMargin);
}
// Top right menu
this.scaleImage(
this.avatar,
this.width - avatarSize - headerMargin,
headerMargin,
avatarSize,
avatarSize
);
// accounts for hard-set color variables in solarized themes
ctx.fillStyle =
colors.primary_low_mid ||
darkLightDiff(colors.primary, colors.secondary, 45, 55);
const pathScale = this.headerHeight / 1200;
const searchIcon = new Path2D(
"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"
);
const hamburgerIcon = new Path2D(
"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"
);
const chatIcon = new Path2D(
"M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z"
);
ctx.save(); // Save the previous state for translation and scale
ctx.translate(
this.width - avatarSize * 2 - headerMargin * 0.5,
avatarSize / 2
);
// need to scale paths otherwise they're too large
ctx.scale(pathScale, pathScale);
ctx.fill(searchIcon);
ctx.restore();
ctx.save();
ctx.translate(
this.width - avatarSize * 3 - headerMargin * 0.5,
avatarSize / 2
);
ctx.scale(pathScale, pathScale);
ctx.fill(chatIcon);
ctx.restore();
ctx.save();
ctx.translate(headerMargin * 1.75, avatarSize / 2);
ctx.scale(pathScale, pathScale);
ctx.fill(hamburgerIcon);
ctx.restore();
}
drawPills(colors, font, headerHeight, opts) {
opts = opts || {};
const { ctx } = this;
const badgeHeight = headerHeight * 2 * 0.25;
const headerMargin = headerHeight * 0.2;
const fontSize = Math.round(badgeHeight * 0.5);
ctx.font = `${fontSize}px '${font.label}'`;
const allCategoriesText = i18n(
"wizard.homepage_preview.nav_buttons.all_categories"
);
const categoriesWidth = ctx.measureText(allCategoriesText).width;
const categoriesBoxWidth = categoriesWidth + headerMargin * 2;
// Box around "all categories >"
ctx.beginPath();
ctx.strokeStyle = colors.primary;
ctx.lineWidth = 0.5;
ctx.rect(
headerMargin,
headerHeight + headerMargin,
categoriesBoxWidth,
badgeHeight
);
ctx.stroke();
ctx.fillStyle = colors.primary;
ctx.fillText(
allCategoriesText,
headerMargin * 1.5,
headerHeight + headerMargin * 1.4 + fontSize
);
// Caret (>) at the end of "all categories" box
const pathScale = badgeHeight / 1000;
const caretIcon = new Path2D(
"M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
);
ctx.save();
ctx.translate(
categoriesBoxWidth,
headerHeight + headerMargin + badgeHeight / 4
);
ctx.scale(pathScale, pathScale);
ctx.fill(caretIcon);
ctx.restore();
const categoryHomepage =
opts.homepageStyle.includes("category") ||
opts.homepageStyle.includes("categories");
// First top menu item
const otherHomepageText = i18n(
`wizard.top_menu_items.${opts.homepageStyle}`
);
const firstTopMenuItemText = categoryHomepage
? i18n("wizard.top_menu_items.categories")
: otherHomepageText;
const newText = i18n("wizard.top_menu_items.new");
const unreadText = i18n("wizard.top_menu_items.unread");
const topText = i18n("wizard.top_menu_items.top");
ctx.beginPath();
ctx.fillStyle = colors.tertiary;
ctx.rect(
categoriesBoxWidth + headerMargin * 2,
headerHeight + headerMargin,
ctx.measureText(firstTopMenuItemText).width + headerMargin * 2,
badgeHeight
);
ctx.fill();
ctx.font = `${fontSize}px '${font.label}'`;
ctx.fillStyle = colors.secondary;
const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize;
const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth;
ctx.fillText(
firstTopMenuItemText,
firstTopMenuItemX,
pillButtonTextY,
ctx.measureText(firstTopMenuItemText).width
);
ctx.fillStyle = colors.primary;
const newTextX =
firstTopMenuItemX +
ctx.measureText(firstTopMenuItemText).width +
headerMargin * 2.0;
ctx.fillText(newText, newTextX, pillButtonTextY);
const unreadTextX =
newTextX + ctx.measureText(newText).width + headerMargin * 2.0;
ctx.fillText(unreadText, unreadTextX, pillButtonTextY);
const topTextX =
unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0;
ctx.fillText(topText, topTextX, pillButtonTextY);
}
}
function loadImage(src) {
if (!src) {
return Promise.resolve();
}
const img = new Image();
img.src = getUrl(src);
return new Promise((resolve) => (img.onload = () => resolve(img)));
}
<div class="wizard-container__preview"> <div class="wizard-container__preview">
<canvas <canvas
width={{this.elementWidth}} width={{this.elementWidth}}

View File

@ -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)));
}

View File

@ -1,3 +1,254 @@
import { action } from "@ember/object";
import { observes } from "@ember-decorators/object";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import {
chooseDarker,
darkLightDiff,
resizeTextLinesToFitRect,
} from "../../../lib/preview";
import HomepagePreview from "./-homepage-preview";
import PreviewBaseComponent from "./-preview-base";
export default class Index extends PreviewBaseComponent {
width = 630;
height = 380;
logo = null;
avatar = null;
previewTopic = true;
draggingActive = false;
startX = 0;
scrollLeft = 0;
HomepagePreview = HomepagePreview;
init() {
super.init(...arguments);
this.step
.findField("homepage_style")
?.addListener(this.onHomepageStyleChange);
}
willDestroy() {
super.willDestroy(...arguments);
this.step
.findField("homepage_style")
?.removeListener(this.onHomepageStyleChange);
}
didInsertElement() {
super.didInsertElement(...arguments);
this.element.addEventListener("mouseleave", this.handleMouseLeave);
this.element.addEventListener("mousemove", this.handleMouseMove);
}
willDestroyElement() {
super.willDestroyElement(...arguments);
this.element.removeEventListener("mouseleave", this.handleMouseLeave);
this.element.removeEventListener("mousemove", this.handleMouseMove);
}
mouseDown(e) {
const slider = this.element.querySelector(".previews");
this.setProperties({
draggingActive: true,
startX: e.pageX - slider.offsetLeft,
scrollLeft: slider.scrollLeft,
});
}
@bind
handleMouseLeave() {
this.set("draggingActive", false);
}
mouseUp() {
this.set("draggingActive", false);
}
@bind
handleMouseMove(e) {
if (!this.draggingActive) {
return;
}
e.preventDefault();
const slider = this.element.querySelector(".previews"),
x = e.pageX - slider.offsetLeft,
walk = (x - this.startX) * 1.5;
slider.scrollLeft = this.scrollLeft - walk;
if (slider.scrollLeft < 50) {
this.set("previewTopic", true);
}
if (slider.scrollLeft > slider.offsetWidth - 50) {
this.set("previewTopic", false);
}
}
didUpdateAttrs() {
super.didUpdateAttrs(...arguments);
this.triggerRepaint();
}
@bind
onHomepageStyleChange() {
this.set("previewTopic", false);
}
@observes("previewTopic")
scrollPreviewArea() {
const el = this.element.querySelector(".previews");
el.scrollTo({
top: 0,
left: this.previewTopic ? 0 : el.scrollWidth - el.offsetWidth,
behavior: "smooth",
});
}
images() {
return {
logo: this.wizard.logoUrl,
avatar: "/images/wizard/trout.png",
};
}
paint({ ctx, colors, font, headingFont, width, height }) {
this.drawFullHeader(colors, headingFont, this.logo);
const margin = 20;
const avatarSize = height * 0.1 + 5;
const lineHeight = height / 14;
const leftHandTextGutter = margin + avatarSize + margin;
const timelineX = width * 0.86;
// Draw a fake topic
this.scaleImage(
this.avatar,
margin,
this.headerHeight + height * 0.22,
avatarSize,
avatarSize
);
const titleFontSize = this.headerHeight / 30;
// Topic title
ctx.beginPath();
ctx.fillStyle = colors.primary;
ctx.font = `700 ${titleFontSize}em '${headingFont.label}'`;
ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3);
// Topic OP text
const bodyFontSize = 1;
ctx.font = `${bodyFontSize}em '${font.label}'`;
let verticalLinePos = 0;
const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books");
const topicOpLines = topicOp.split("\n");
resizeTextLinesToFitRect(
topicOpLines,
timelineX - leftHandTextGutter,
ctx,
bodyFontSize,
font,
(textLine, idx) => {
verticalLinePos = height * 0.4 + idx * lineHeight;
ctx.fillText(textLine, leftHandTextGutter, verticalLinePos);
}
);
ctx.font = `${bodyFontSize}em '${font.label}'`;
// Share button
const shareButtonWidth =
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
margin;
ctx.beginPath();
ctx.rect(margin, verticalLinePos, shareButtonWidth, height * 0.1);
// accounts for hard-set color variables in solarized themes
ctx.fillStyle =
colors.primary_low ||
darkLightDiff(colors.primary, colors.secondary, 90, 65);
ctx.fill();
ctx.fillStyle = chooseDarker(colors.primary, colors.secondary);
ctx.fillText(
i18n("wizard.previews.share_button"),
margin + 10,
verticalLinePos + lineHeight * 0.9
);
// Reply button
const replyButtonWidth =
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
margin;
ctx.beginPath();
ctx.rect(
shareButtonWidth + margin + 10,
verticalLinePos,
replyButtonWidth,
height * 0.1
);
ctx.fillStyle = colors.tertiary;
ctx.fill();
ctx.fillStyle = colors.secondary;
ctx.fillText(
i18n("wizard.previews.reply_button"),
shareButtonWidth + margin * 2,
verticalLinePos + lineHeight * 0.9
);
// Draw timeline
ctx.beginPath();
ctx.strokeStyle = colors.tertiary;
ctx.lineWidth = 0.5;
ctx.moveTo(timelineX, height * 0.3);
ctx.lineTo(timelineX, height * 0.7);
ctx.stroke();
// Timeline handle
ctx.beginPath();
ctx.strokeStyle = colors.tertiary;
ctx.lineWidth = 3;
ctx.moveTo(timelineX, height * 0.3 + 10);
ctx.lineTo(timelineX, height * 0.4);
ctx.stroke();
// Timeline post count
const postCountY = height * 0.3 + margin + 10;
ctx.beginPath();
ctx.font = `700 ${bodyFontSize}em '${font.label}'`;
ctx.fillStyle = colors.primary;
ctx.fillText("1 / 20", timelineX + margin / 2, postCountY);
// Timeline post date
ctx.beginPath();
ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`;
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65);
ctx.fillText(
"Nov 22",
timelineX + margin / 2,
postCountY + lineHeight * 0.75
);
}
@action
setPreviewHomepage(event) {
event?.preventDefault();
this.set("previewTopic", false);
}
@action
setPreviewTopic(event) {
event?.preventDefault();
this.set("previewTopic", true);
}
}
<div class="previews {{if this.draggingActive 'dragging'}}"> <div class="previews {{if this.draggingActive 'dragging'}}">
<div class="wizard-container__preview topic-preview"> <div class="wizard-container__preview topic-preview">
<canvas <canvas

View File

@ -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);
}
}

View File

@ -1,3 +1,7 @@
import Component from "@ember/component";
export default class Text extends Component {}
<Input <Input
id={{this.field.id}} id={{this.field.id}}
@value={{this.field.value}} @value={{this.field.value}}

View File

@ -1,3 +0,0 @@
import Component from "@ember/component";
export default class Text extends Component {}

View File

@ -1,3 +1,36 @@
import { computed } from "@ember/object";
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("color-palettes-row")
export default class ColorPalettesRow extends SelectKitRowComponent {
@computed("item.colors.[]")
get palettes() {
return htmlSafe(
(this.item.colors || [])
.filter((color) => color.name !== "secondary")
.map((color) => `#${escape(color.hex)}`)
.map(
(hex) =>
`<span class="palette" style="background-color:${hex}"></span>`
)
.join("")
);
}
@computed("item.colors.[]")
get backgroundColor() {
const secondary = (this.item.colors || []).findBy("name", "secondary");
if (secondary && secondary.hex) {
return htmlSafe(`background-color:#${escape(secondary.hex)}`);
} else {
return "";
}
}
}
<span class="name"> <span class="name">
{{this.label}} {{this.label}}
</span> </span>

View File

@ -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 "";
}
}
}

View File

@ -1 +1,20 @@
import { schedule } from "@ember/runloop";
import { classNames } from "@ember-decorators/component";
import { escapeExpression } from "discourse/lib/utilities";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("create-color-row")
export default class CreateColorRow extends SelectKitRowComponent {
didReceiveAttrs() {
super.didReceiveAttrs(...arguments);
schedule("afterRender", () => {
const color = escapeExpression(this.rowValue);
this.element.style.borderLeftColor = color.startsWith("#")
? color
: `#${color}`;
});
}
}
<span>{{this.label}}</span> <span>{{this.label}}</span>

View File

@ -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}`;
});
}
}

View File

@ -1,3 +1,12 @@
import { readOnly } from "@ember/object/computed";
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("dropdown-select-box-row")
export default class DropdownSelectBoxRow extends SelectKitRowComponent {
@readOnly("item.description") description;
}
{{#if this.icons}} {{#if this.icons}}
<div class="icons"> <div class="icons">
<span class="selection-indicator"></span> <span class="selection-indicator"></span>

View File

@ -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;
}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("email-group-user-chooser-row")
export default class EmailGroupUserChooserRow extends SelectKitRowComponent {}
{{#if this.item.isUser}} {{#if this.item.isUser}}
{{avatar this.item imageSize="tiny"}} {{avatar this.item imageSize="tiny"}}
<div> <div>

View File

@ -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 {}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("flair-row")
export default class FlairRow extends SelectKitRowComponent {}
{{#if this.item.url}} {{#if this.item.url}}
<AvatarFlair <AvatarFlair
@flairName={{this.item.name}} @flairName={{this.item.name}}

View File

@ -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 {}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("future-date-input-selector-row")
export default class FutureDateInputSelectorRow extends SelectKitRowComponent {}
{{#if this.item.icon}} {{#if this.item.icon}}
<div class="future-date-input-selector-icons"> <div class="future-date-input-selector-icons">
{{d-icon this.item.icon}} {{d-icon this.item.icon}}

View File

@ -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 {}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("homepage-style-selector-row")
export default class HomepageStyleSelectorRow extends SelectKitRowComponent {}
<div class="texts"> <div class="texts">
<span class="name">{{html-safe this.label}}</span> <span class="name">{{html-safe this.label}}</span>
{{#if this.item.description}} {{#if this.item.description}}

View File

@ -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 {}

View File

@ -1,3 +1,34 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import { reads } from "@ember/object/computed";
import { tagName } from "@ember-decorators/component";
@tagName("")
export default class SelectedCollection extends Component {
@reads("collection.content.selectedTags.[]") selectedTags;
@computed("selectedTags.[]", "selectKit.filter")
get tags() {
if (!this.selectedTags) {
return [];
}
let tags = this.selectedTags;
if (tags.length >= 20 && this.selectKit.filter) {
tags = tags.filter((t) => t.includes(this.selectKit.filter));
} else if (tags.length >= 20) {
tags = tags.slice(0, 20);
}
return tags.map((selectedTag) => {
return {
value: selectedTag,
classNames: "selected-tag",
};
});
}
}
{{#if this.tags}} {{#if this.tags}}
<div class="mini-tag-chooser-selected-collection selected-tags"> <div class="mini-tag-chooser-selected-collection selected-tags">
{{#each this.tags as |tag|}} {{#each this.tags as |tag|}}

View File

@ -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",
};
});
}
}

View File

@ -1,3 +1,28 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import { tagName } from "@ember-decorators/component";
import { makeArray } from "discourse/lib/helpers";
import UtilsMixin from "select-kit/mixins/utils";
@tagName("")
export default class FormatSelectedContent extends Component.extend(
UtilsMixin
) {
content = null;
selectKit = null;
@computed("content")
get formattedContent() {
if (this.content) {
return makeArray(this.content)
.map((c) => this.getName(c))
.join(", ");
} else {
return this.getName(this.selectKit.noneItem);
}
}
}
<span class="formatted-selection"> <span class="formatted-selection">
{{this.formattedContent}} {{this.formattedContent}}
</span> </span>

View File

@ -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);
}
}
}

View File

@ -1,3 +1,41 @@
import { action } from "@ember/object";
import { isEmpty } from "@ember/utils";
import { classNames } from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import SelectKitFilterComponent from "select-kit/components/select-kit/select-kit-filter";
@classNames("multi-select-filter")
export default class MultiSelectFilter extends SelectKitFilterComponent {
@discourseComputed("placeholder", "selectKit.hasSelection")
computedPlaceholder(placeholder, hasSelection) {
if (this.hidePlaceholderWithSelection && hasSelection) {
return "";
}
return isEmpty(placeholder) ? "" : placeholder;
}
@action
onPaste(event) {
const data = event?.clipboardData;
if (!data) {
return;
}
const parts = data.getData("text").split("|").filter(Boolean);
if (parts.length > 1) {
event.stopPropagation();
event.preventDefault();
this.selectKit.append(parts);
return false;
}
}
}
{{#unless this.isHidden}} {{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }} {{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }} {{! template-lint-disable no-pointer-down-event-binding }}

View File

@ -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;
}
}
}

View File

@ -1,3 +1,22 @@
import { computed } from "@ember/object";
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import SelectedNameComponent from "select-kit/components/selected-name";
@classNames("selected-category")
export default class SelectedCategory extends SelectedNameComponent {
@computed("item")
get badge() {
return htmlSafe(
categoryBadgeHTML(this.item, {
allowUncategorized: true,
link: false,
})
);
}
}
<div <div
{{on "click" this.onSelectedNameClick}} {{on "click" this.onSelectedNameClick}}
tabindex="0" tabindex="0"

View File

@ -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,
})
);
}
}

View File

@ -1,3 +1,23 @@
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import discourseComputed from "discourse/lib/decorators";
import CategoryRowComponent from "select-kit/components/category-row";
@classNames("none category-row")
export default class NoneCategoryRow extends CategoryRowComponent {
@discourseComputed("category")
badgeForCategory(category) {
return htmlSafe(
categoryBadgeHTML(category, {
link: false,
allowUncategorized: true,
hideParent: true,
})
);
}
}
{{#if this.category}} {{#if this.category}}
<div class="category-status" aria-hidden="true"> <div class="category-status" aria-hidden="true">
{{#if this.hasParentCategory}} {{#if this.hasParentCategory}}

View File

@ -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,
})
);
}
}

View File

@ -1,3 +1,18 @@
import { classNames } from "@ember-decorators/component";
import { fmt } from "discourse/lib/computed";
import discourseComputed from "discourse/lib/decorators";
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
@classNames("notifications-filter-header", "btn-flat")
export default class NotificationsFilterHeader extends DropdownSelectBoxHeaderComponent {
@fmt("value", "user.user_notifications.filters.%@") label;
@discourseComputed("selectKit.isExpanded")
caretIcon(isExpanded) {
return isExpanded ? "caret-up" : "caret-down";
}
}
<div class="select-kit-header-wrapper"> <div class="select-kit-header-wrapper">
<span class="filter-text"> <span class="filter-text">
{{i18n "user.user_notifications.filters.filter_by"}} {{i18n "user.user_notifications.filters.filter_by"}}

View File

@ -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";
}
}

View File

@ -1,3 +1,15 @@
import { classNames } from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
@classNames("period-chooser-header", "btn-flat")
export default class PeriodChooserHeader extends DropdownSelectBoxHeaderComponent {
@discourseComputed("selectKit.isExpanded")
caretIcon(isExpanded) {
return isExpanded ? "caret-up" : "caret-down";
}
}
<h2 class="selected-name" title={{this.title}}> <h2 class="selected-name" title={{this.title}}>
{{period-title {{period-title
this.value this.value

View File

@ -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";
}
}

View File

@ -1,3 +1,16 @@
import { classNames } from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import DropdownSelectBoxRowComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
@classNames("period-chooser-row")
export default class PeriodChooserRow extends DropdownSelectBoxRowComponent {
@discourseComputed("rowName")
title(rowName) {
return i18n(`filters.top.${rowName || "this_week"}`).title;
}
}
<span class="selection-indicator"></span> <span class="selection-indicator"></span>
<span class="period-title"> <span class="period-title">

View File

@ -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;
}
}

View File

@ -1,3 +1,9 @@
import Component from "@ember/component";
import { tagName } from "@ember-decorators/component";
@tagName("")
export default class ErrorsCollection extends Component {}
{{#if this.collection.content}} {{#if this.collection.content}}
<ul class="select-kit-errors-collection"> <ul class="select-kit-errors-collection">
{{#each this.collection.content as |item|}} {{#each this.collection.content as |item|}}

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
import { tagName } from "@ember-decorators/component";
@tagName("")
export default class ErrorsCollection extends Component {}

View File

@ -1,3 +1,68 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import { next } from "@ember/runloop";
import { classNameBindings, classNames } from "@ember-decorators/component";
import { bind } from "discourse/lib/decorators";
@classNames("select-kit-body")
@classNameBindings("emptyBody:empty-body")
export default class SelectKitBody extends Component {
@computed("selectKit.{filter,hasNoContent}")
get emptyBody() {
return false;
}
didInsertElement() {
super.didInsertElement(...arguments);
this.element.style.position = "relative";
document.addEventListener("click", this.handleClick, true);
this.selectKit
.mainElement()
.addEventListener("keydown", this._handleKeydown, true);
}
willDestroyElement() {
super.willDestroyElement(...arguments);
document.removeEventListener("click", this.handleClick, true);
this.selectKit
.mainElement()
?.removeEventListener("keydown", this._handleKeydown, true);
}
@bind
handleClick(event) {
if (!this.selectKit.isExpanded || !this.selectKit.mainElement()) {
return;
}
if (this.selectKit.mainElement().contains(event.target)) {
return;
}
this.selectKit.close(event);
}
@bind
_handleKeydown(event) {
if (!this.selectKit.isExpanded || event.key !== "Tab") {
return;
}
next(() => {
if (
this.isDestroying ||
this.isDestroyed ||
this.selectKit.mainElement()?.contains(document.activeElement)
) {
return;
}
this.selectKit.close(event);
});
}
}
{{#if this.selectKit.isExpanded}} {{#if this.selectKit.isExpanded}}
{{yield}} {{yield}}
{{/if}} {{/if}}

View File

@ -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);
});
}
}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("create")
export default class SelectKitCreateRow extends SelectKitRowComponent {}
{{#each this.icons as |icon|}} {{#each this.icons as |icon|}}
{{d-icon icon translatedTitle=this.dasherizedTitle}} {{d-icon icon translatedTitle=this.dasherizedTitle}}
{{/each}} {{/each}}

View File

@ -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 {}

View File

@ -1,3 +1,144 @@
import Component from "@ember/component";
import { action, computed } from "@ember/object";
import { not } from "@ember/object/computed";
import { isPresent } from "@ember/utils";
import {
attributeBindings,
classNameBindings,
classNames,
} from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import UtilsMixin from "select-kit/mixins/utils";
@classNames("select-kit-filter")
@classNameBindings("isExpanded:is-expanded")
@attributeBindings("role")
export default class SelectKitFilter extends Component.extend(UtilsMixin) {
tabIndex = -1;
@not("isHidden") isExpanded;
@computed(
"selectKit.options.{filterable,allowAny,autoFilterable}",
"content.[]"
)
get isHidden() {
return (
!this.selectKit.options.filterable &&
!this.selectKit.options.allowAny &&
!this.selectKit.options.autoFilterable
);
}
@discourseComputed(
"selectKit.options.filterPlaceholder",
"selectKit.options.translatedFilterPlaceholder",
"selectKit.options.allowAny"
)
placeholder(placeholder, translatedPlaceholder) {
if (isPresent(translatedPlaceholder)) {
return translatedPlaceholder;
}
if (isPresent(placeholder)) {
return i18n(placeholder);
}
return i18n(
this.selectKit.options.allowAny
? "select_kit.filter_placeholder_with_any"
: "select_kit.filter_placeholder"
);
}
@action
onPaste() {}
@action
onInput(event) {
this.selectKit.onInput(event);
return true;
}
@action
onKeyup(event) {
event.preventDefault();
event.stopImmediatePropagation();
return true;
}
@action
onKeydown(event) {
if (!this.selectKit.onKeydown(event)) {
return false;
}
if (event.key === "Tab" && this.selectKit.isLoading) {
this.selectKit.cancelSearch();
this.selectKit.close(event);
return true;
}
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
return true;
}
if (event.key === "Backspace" && !this.selectKit.filter) {
this.selectKit.deselectLast();
event.preventDefault();
return false;
}
if (event.key === "ArrowUp") {
this.selectKit.highlightLast();
event.preventDefault();
return false;
}
if (event.key === "ArrowDown") {
if (!this.selectKit.isExpanded) {
this.selectKit.open(event);
}
this.selectKit.highlightFirst();
event.preventDefault();
return false;
}
if (event.key === "Escape") {
this.selectKit.close(event);
this.selectKit.headerElement().focus();
event.preventDefault();
event.stopPropagation();
return false;
}
if (event.key === "Enter" && this.selectKit.highlighted) {
this.selectKit.select(
this.getValue(this.selectKit.highlighted),
this.selectKit.highlighted
);
event.preventDefault();
event.stopImmediatePropagation();
return false;
}
if (
event.key === "Enter" &&
(!this.selectKit.highlighted || this.selectKit.enterDisabled)
) {
this.element.querySelector("input").focus();
if (this.selectKit.enterDisabled) {
event.preventDefault();
event.stopImmediatePropagation();
}
return false;
}
this.selectKit.set("highlighted", null);
}
}
{{#unless this.isHidden}} {{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }} {{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }} {{! template-lint-disable no-pointer-down-event-binding }}

View File

@ -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);
}
}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("none")
export default class SelectKitNoneRow extends SelectKitRowComponent {}
{{#each this.icons as |icon|}} {{#each this.icons as |icon|}}
{{d-icon icon translatedTitle=this.dasherizedTitle}} {{d-icon icon translatedTitle=this.dasherizedTitle}}
{{/each}} {{/each}}

View File

@ -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 {}

View File

@ -1,3 +1,210 @@
import Component from "@ember/component";
import { action, computed } from "@ember/object";
import { reads } from "@ember/object/computed";
import { guidFor } from "@ember/object/internals";
import { dasherize } from "@ember/string";
import {
attributeBindings,
classNameBindings,
classNames,
tagName,
} from "@ember-decorators/component";
import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n";
import UtilsMixin from "select-kit/mixins/utils";
@classNames("select-kit-row")
@tagName("li")
@attributeBindings(
"tabIndex",
"title",
"rowValue:data-value",
"rowName:data-name",
"index:data-index",
"role",
"ariaChecked:aria-checked",
"guid:data-guid",
"rowLang:lang"
)
@classNameBindings(
"isHighlighted",
"isSelected",
"isNone",
"isNone:none",
"item.classNames"
)
export default class SelectKitRow extends Component.extend(UtilsMixin) {
tabIndex = 0;
index = 0;
role = "menuitemradio";
@reads("item.lang") lang;
didInsertElement() {
super.didInsertElement(...arguments);
if (this.site.desktopView) {
this.element.addEventListener("mouseenter", this.handleMouseEnter);
this.element.addEventListener("focus", this.handleMouseEnter);
}
}
willDestroyElement() {
super.willDestroyElement(...arguments);
if (this.site.desktopView) {
this.element.removeEventListener("mouseenter", this.handleMouseEnter);
this.element.removeEventListener("focus", this.handleMouseEnter);
}
}
@computed("rowValue")
get isNone() {
return this.rowValue === this.getValue(this.selectKit.noneItem);
}
@computed("item")
get guid() {
return guidFor(this.item);
}
@computed("isSelected")
get ariaChecked() {
return this.isSelected ? "true" : "false";
}
@computed("rowTitle", "item.title", "rowName")
get title() {
return (
this.rowTitle || this.getProperty(this.item, "title") || this.rowName
);
}
@computed("title")
get dasherizedTitle() {
return dasherize((this.title || "").replace(".", "-"));
}
@computed("rowLabel", "item.label", "title", "rowName")
get label() {
const label =
this.rowLabel ||
this.getProperty(this.item, "label") ||
this.title ||
this.rowName;
if (
this.selectKit.options.allowAny &&
this.rowValue === this.selectKit.filter &&
this.getName(this.selectKit.noneItem) !== this.rowName &&
this.getName(this.selectKit.newItem) === this.rowName
) {
return i18n("select_kit.create", { content: label });
}
return label;
}
didReceiveAttrs() {
super.didReceiveAttrs(...arguments);
this.setProperties({
rowName: this.getName(this.item),
rowValue: this.getValue(this.item),
rowLabel: this.getProperty(this.item, "labelProperty"),
rowTitle: this.getProperty(this.item, "titleProperty"),
rowLang: this.getProperty(this.item, "langProperty"),
});
}
@computed("item.{icon,icons}")
get icons() {
const icon = makeArray(this.getProperty(this.item, "icon"));
const icons = makeArray(this.getProperty(this.item, "icons"));
return icon.concat(icons).filter(Boolean);
}
@computed("selectKit.highlighted")
get highlightedValue() {
return this.getValue(this.selectKit.highlighted);
}
@computed("rowValue", "highlightedValue")
get isHighlighted() {
return this.rowValue === this.highlightedValue;
}
@computed("rowValue", "value")
get isSelected() {
return this.rowValue === this.value;
}
@action
handleMouseEnter() {
if (!this.isDestroying || !this.isDestroyed) {
this.selectKit.onHover(this.rowValue, this.item);
}
return false;
}
click(event) {
event.preventDefault();
event.stopPropagation();
this.selectKit.select(this.rowValue, this.item);
return false;
}
mouseDown(event) {
if (this.selectKit.options.preventHeaderFocus) {
event.preventDefault();
}
}
focusIn(event) {
event.stopImmediatePropagation();
}
keyDown(event) {
if (this.selectKit.isExpanded) {
if (event.key === "Backspace") {
if (this.selectKit.isFilterExpanded) {
this.selectKit.set("filter", this.selectKit.filter.slice(0, -1));
this.selectKit.triggerSearch();
this.selectKit.focusFilter();
event.preventDefault();
event.stopPropagation();
return false;
}
} else if (event.key === "ArrowUp") {
this.selectKit.highlightPrevious();
return false;
} else if (event.key === "ArrowDown") {
this.selectKit.highlightNext();
return false;
} else if (event.key === "Enter") {
event.stopImmediatePropagation();
this.selectKit.select(
this.getValue(this.selectKit.highlighted),
this.selectKit.highlighted
);
return false;
} else if (event.key === "Escape") {
this.selectKit.close(event);
this.selectKit.headerElement().focus();
event.preventDefault();
event.stopPropagation();
} else {
if (this.isValidInput(event.key)) {
this.selectKit.set("filter", event.key);
this.selectKit.triggerSearch();
this.selectKit.focusFilter();
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
{{#each this.icons as |icon|}} {{#each this.icons as |icon|}}
{{d-icon icon translatedTitle=this.dasherizedTitle}} {{d-icon icon translatedTitle=this.dasherizedTitle}}
{{/each}} {{/each}}

View File

@ -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();
}
}
}
}
}

View File

@ -1,3 +1,46 @@
import Component from "@ember/component";
import { computed } from "@ember/object";
import { guidFor } from "@ember/object/internals";
import { tagName } from "@ember-decorators/component";
import UtilsMixin from "select-kit/mixins/utils";
@tagName("")
export default class SelectedChoice extends Component.extend(UtilsMixin) {
item = null;
selectKit = null;
extraClass = null;
id = null;
init() {
super.init(...arguments);
this.set("id", guidFor(this));
}
@computed("item")
get itemValue() {
return this.getValue(this.item);
}
@computed("item")
get itemName() {
return this.getName(this.item);
}
@computed("item")
get mandatoryValuesArray() {
return this.get("mandatoryValues")?.split("|") || [];
}
@computed("item")
get readOnly() {
if (typeof this.item === "string") {
return this.mandatoryValuesArray.includes(this.item);
}
return this.mandatoryValuesArray.includes(this.item.id);
}
}
{{#if this.readOnly}} {{#if this.readOnly}}
<button <button
class="btn btn-default disabled" class="btn btn-default disabled"

View File

@ -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);
}
}

View File

@ -1,3 +1,9 @@
import { tagName } from "@ember-decorators/component";
import SelectedNameComponent from "select-kit/components/selected-name";
@tagName("")
export default class SelectedFlair extends SelectedNameComponent {}
{{#if this.item.url}} {{#if this.item.url}}
<AvatarFlair <AvatarFlair
@flairName={{this.item.name}} @flairName={{this.item.name}}

View File

@ -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 {}

View File

@ -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("&hellip;", "");
}
@computed("headerTitle", "item")
get title() {
return (
this.headerTitle ||
this._safeProperty("title", this.item) ||
this.name ||
""
);
}
@computed("headerLabel", "title", "name")
get label() {
return (
this.headerLabel ||
this._safeProperty("label", this.item) ||
this.title ||
this.name
);
}
@computed("item.{icon,icons}")
get icons() {
const icon = makeArray(this._safeProperty("icon", this.item));
const icons = makeArray(this._safeProperty("icons", this.item));
return icon.concat(icons).filter(Boolean);
}
_safeProperty(name, content) {
if (!content) {
return null;
}
return get(content, name);
}
}
{{#if this.selectKit.options.showFullTitle}} {{#if this.selectKit.options.showFullTitle}}
<div <div
lang={{this.lang}} lang={{this.lang}}

View File

@ -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("&hellip;", "");
}
@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);
}
}

View File

@ -1 +1,7 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("tag-chooser-row")
export default class TagChooserRow extends SelectKitRowComponent {}
{{discourse-tag this.rowValue count=this.item.count noHref=true}} {{discourse-tag this.rowValue count=this.item.count noHref=true}}

View File

@ -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 {}

View File

@ -1,3 +1,15 @@
import { classNames } from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("tag-row")
export default class TagRow extends SelectKitRowComponent {
@discourseComputed("item")
isTag(item) {
return item.id !== "no-tags" && item.id !== "all-tags";
}
}
{{#if this.isTag}} {{#if this.isTag}}
{{discourse-tag {{discourse-tag
this.rowValue this.rowValue

View File

@ -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";
}
}

View File

@ -1 +1,5 @@
import Component from "@ember/component";
export default class ToolbarPopupMenuOptionsHeading extends Component {}
{{this.heading}} {{this.heading}}

View File

@ -1,3 +0,0 @@
import Component from "@ember/component";
export default class ToolbarPopupMenuOptionsHeading extends Component {}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("topic-row")
export default class TopicRow extends SelectKitRowComponent {}
<TopicStatus @topic={{this.item}} @disableActions={{true}} /> <TopicStatus @topic={{this.item}} @disableActions={{true}} />
<div class="topic-title">{{replace-emoji this.item.title}}</div> <div class="topic-title">{{replace-emoji this.item.title}}</div>
<div class="topic-categories"> <div class="topic-categories">

View File

@ -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 {}

View File

@ -1,3 +1,9 @@
import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("user-row")
export default class UserRow extends SelectKitRowComponent {}
{{avatar this.item imageSize="tiny"}} {{avatar this.item imageSize="tiny"}}
<span class="username">{{format-username this.item.username}}</span> <span class="username">{{format-username this.item.username}}</span>

View File

@ -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 {}

View File

@ -1,3 +1,35 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { action } from "@ember/object";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
export default class ChatStyleguideChatComposerMessageDetails extends Component {
@service site;
@service session;
@service keyValueStore;
@service currentUser;
@cached
get message() {
return new ChatFabricators(getOwner(this)).message({
user: this.currentUser,
});
}
@action
toggleMode() {
if (this.message.editing) {
this.message.editing = false;
this.message.inReplyTo = new ChatFabricators(getOwner(this)).message();
} else {
this.message.editing = true;
this.message.inReplyTo = null;
}
}
}
<StyleguideExample @title="<ChatComposerMessageDetails>"> <StyleguideExample @title="<ChatComposerMessageDetails>">
<Styleguide::Component> <Styleguide::Component>
<ChatComposerMessageDetails @message={{this.message}} /> <ChatComposerMessageDetails @message={{this.message}} />

View File

@ -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;
}
}
}

View File

@ -1,3 +1,36 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
export default class ChatStyleguideChatComposer extends Component {
@service chatChannelComposer;
@service chatChannelPane;
channel = new ChatFabricators(getOwner(this)).channel({ id: -999 });
@action
toggleDisabled() {
if (this.channel.status === CHANNEL_STATUSES.open) {
this.channel.status = CHANNEL_STATUSES.readOnly;
} else {
this.channel.status = CHANNEL_STATUSES.open;
}
}
@action
toggleSending() {
this.chatChannelPane.sending = !this.chatChannelPane.sending;
}
@action
onSendMessage() {
this.chatChannelComposer.reset();
}
}
<StyleguideExample @title="<ChatComposer>"> <StyleguideExample @title="<ChatComposer>">
<Styleguide::Component> <Styleguide::Component>
<Chat::Composer::Channel <Chat::Composer::Channel

View File

@ -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();
}
}

View File

@ -1,3 +1,55 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
} from "discourse/plugins/chat/discourse/controllers/preferences-chat";
export default class ChatStyleguideChatHeaderIcon extends Component {
@tracked isActive = false;
@tracked currentUserInDnD = false;
@tracked urgentCount;
@tracked unreadCount;
@tracked indicatorPreference = HEADER_INDICATOR_PREFERENCE_ALL_NEW;
get indicatorPreferences() {
return [
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER,
];
}
@action
toggleIsActive() {
this.isActive = !this.isActive;
}
@action
toggleCurrentUserInDnD() {
this.currentUserInDnD = !this.currentUserInDnD;
}
@action
updateUnreadCount(event) {
this.unreadCount = event.target.value;
}
@action
updateUrgentCount(event) {
this.urgentCount = event.target.value;
}
@action
updateIndicatorPreference(value) {
this.indicatorPreference = value;
}
}
<StyleguideExample @title="<Chat::Header::Icon>"> <StyleguideExample @title="<Chat::Header::Icon>">
<Styleguide::Component> <Styleguide::Component>
<header <header

View File

@ -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;
}
}

View File

@ -1,3 +1,99 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
export default class ChatStyleguideChatMessage extends Component {
@service currentUser;
manager = new ChatMessagesManager(getOwner(this));
constructor() {
super(...arguments);
this.message = new ChatFabricators(getOwner(this)).message({
user: this.currentUser,
});
this.message.cook();
}
@action
toggleDeleted() {
if (this.message.deletedAt) {
this.message.deletedAt = null;
} else {
this.message.deletedAt = moment();
}
}
@action
toggleBookmarked() {
if (this.message.bookmark) {
this.message.bookmark = null;
} else {
this.message.bookmark = new ChatFabricators(getOwner(this)).bookmark();
}
}
@action
toggleHighlighted() {
this.message.highlighted = !this.message.highlighted;
}
@action
toggleEdited() {
this.message.edited = !this.message.edited;
}
@action
toggleThread() {
if (this.message.thread) {
this.message.channel.threadingEnabled = false;
this.message.thread = null;
} else {
this.message.thread = new ChatFabricators(getOwner(this)).thread({
channel: this.message.channel,
});
this.message.thread.preview.replyCount = 1;
this.message.channel.threadingEnabled = true;
}
}
@action
async updateMessage(event) {
this.message.message = event.target.value;
await this.message.cook();
}
@action
toggleReaction() {
if (this.message.reactions?.length) {
this.message.reactions = [];
} else {
this.message.reactions = [
new ChatFabricators(getOwner(this)).reaction({ emoji: "heart" }),
new ChatFabricators(getOwner(this)).reaction({
emoji: "rocket",
reacted: true,
}),
];
}
}
@action
toggleUpload() {
if (this.message.uploads?.length) {
this.message.uploads = [];
} else {
this.message.uploads = [
new ChatFabricators(getOwner(this)).upload(),
new ChatFabricators(getOwner(this)).upload(),
];
}
}
}
<StyleguideExample @title="<ChatMessage>"> <StyleguideExample @title="<ChatMessage>">
<Styleguide::Component> <Styleguide::Component>
<ChatMessage @message={{this.message}} @context="channel" /> <ChatMessage @message={{this.message}} @context="channel" />

View File

@ -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(),
];
}
}
}

View File

@ -1,3 +1,25 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
export default class ChatStyleguideChatModalArchiveChannel extends Component {
@service modal;
channel = new ChatFabricators(getOwner(this)).channel();
@action
openModal() {
return this.modal.show(ChatModalArchiveChannel, {
model: {
channel: this.channel,
},
});
}
}
<StyleguideExample @title="<Chat::Modal::ArchiveChannel>"> <StyleguideExample @title="<Chat::Modal::ArchiveChannel>">
<Styleguide::Controls::Row> <Styleguide::Controls::Row>
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />

View File

@ -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,
},
});
}
}

View File

@ -1,3 +1,17 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import ChatModalCreateChannel from "discourse/plugins/chat/discourse/components/chat/modal/create-channel";
export default class ChatStyleguideChatModalCreateChannel extends Component {
@service modal;
@action
openModal() {
return this.modal.show(ChatModalCreateChannel);
}
}
<StyleguideExample @title="<Chat::Modal::CreateChannel>"> <StyleguideExample @title="<Chat::Modal::CreateChannel>">
<Styleguide::Controls::Row> <Styleguide::Controls::Row>
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />

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