UX: Add Styling step to wizard (#14132)

Refactors three wizard steps (colors, fonts, homepage style) into one new step called Styling.
This commit is contained in:
Penar Musaraj
2021-08-25 17:10:12 -04:00
committed by GitHub
parent cfbf69848a
commit 85b8fea262
28 changed files with 548 additions and 371 deletions

View File

@@ -3,14 +3,14 @@ import {
createPreviewComponent,
darkLightDiff,
} from "wizard/lib/preview";
import { observes } from "discourse-common/utils/decorators";
export default createPreviewComponent(659, 320, {
logo: null,
avatar: null,
@observes("step.fieldsById.homepage_style.value")
styleChanged() {
didUpdateAttrs() {
this._super(...arguments);
this.triggerRepaint();
},
@@ -22,7 +22,9 @@ export default createPreviewComponent(659, 320, {
},
paint({ ctx, colors, font, width, height }) {
this.drawFullHeader(colors, font, this.logo);
if (this.logo) {
this.drawFullHeader(colors, font, this.logo);
}
if (this.get("step.fieldsById.homepage_style.value") === "latest") {
this.drawPills(colors, font, height * 0.15);

View File

@@ -15,13 +15,66 @@ metus. Fusce in consequat augue, vel facilisis felis.`;
export default createPreviewComponent(659, 320, {
logo: null,
avatar: null,
previewTopic: true,
draggingActive: false,
startX: 0,
scrollLeft: 0,
mouseDown(e) {
const slider = this.element.querySelector(".previews");
this.setProperties({
draggingActive: true,
startX: e.pageX - slider.offsetLeft,
scrollLeft: slider.scrollLeft,
});
},
mouseLeave() {
this.set("draggingActive", false);
},
mouseUp() {
this.set("draggingActive", false);
},
mouseMove(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) {
this.set("previewTopic", false);
}
},
didUpdateAttrs() {
this._super(...arguments);
@observes(
"step.fieldsById.body_font.value",
"step.fieldsById.heading_font.value"
)
fontChanged() {
this.triggerRepaint();
if (this.stylingDropdown?.id === "homepage_style") {
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() {
@@ -39,14 +92,14 @@ export default createPreviewComponent(659, 320, {
}
const margin = 20;
const avatarSize = height * 0.2;
const lineHeight = height / 11;
const avatarSize = height * 0.15;
const lineHeight = height / 14;
// Draw a fake topic
this.scaleImage(
this.avatar,
margin,
headerHeight + height * 0.11,
headerHeight + height * 0.09,
avatarSize,
avatarSize
);
@@ -80,7 +133,7 @@ export default createPreviewComponent(659, 320, {
ctx.fillText(
I18n.t("wizard.previews.share_button"),
margin + 10,
line + lineHeight * 1.7
line + lineHeight * 1.9
);
// Reply Button
@@ -100,7 +153,7 @@ export default createPreviewComponent(659, 320, {
ctx.fillText(
I18n.t("wizard.previews.reply_button"),
shareButtonWidth + margin + 20,
line + lineHeight * 1.7
line + lineHeight * 1.9
);
// Draw Timeline
@@ -124,4 +177,14 @@ export default createPreviewComponent(659, 320, {
ctx.fillStyle = colors.primary;
ctx.fillText("1 / 20", timelineX + margin, height * 0.3 + margin * 1.5);
},
actions: {
setPreviewHomepage() {
this.set("previewTopic", false);
},
setPreviewTopic() {
this.set("previewTopic", true);
},
},
});

View File

@@ -1,8 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
actions: {
changed(value) {
this.set("field.value", value);
},
},
});

View File

@@ -1,7 +1,36 @@
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
import { set } from "@ember/object";
export default Component.extend({
init(...args) {
this._super(...args);
if (this.field.id === "color_scheme") {
for (let choice of this.field.choices) {
if (choice?.data?.colors) {
set(choice, "colors", choice.data.colors);
}
}
}
},
@discourseComputed("field.id")
componentName(id) {
if (id === "color_scheme") {
return "color-palettes";
}
return "combo-box";
},
keyPress(e) {
e.stopPropagation();
},
actions: {
onChangeValue(value) {
this.set("field.value", value);
this.stylingDropdownChanged(this.field.id, value);
},
},
});

View File

@@ -3,10 +3,11 @@ import { dasherize } from "@ember/string";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [":wizard-field", "typeClass", "field.invalid"],
classNameBindings: [":wizard-field", "typeClasses", "field.invalid"],
@discourseComputed("field.type")
typeClass: (type) => `${dasherize(type)}-field`,
@discourseComputed("field.type", "field.id")
typeClasses: (type, id) =>
`${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`,
@discourseComputed("field.id")
fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`,

View File

@@ -27,6 +27,11 @@ export default Component.extend({
classNames: ["wizard-step"],
saving: null,
init() {
this._super(...arguments);
this.set("stylingDropdown", {});
},
didInsertElement() {
this._super(...arguments);
this.autoFocus();
@@ -96,6 +101,11 @@ export default Component.extend({
return htmlSafe(`width: ${ratio * 200}px`);
},
@discourseComputed("step.fields")
includeSidebar(fields) {
return !!fields.findBy("show_in_sidebar");
},
autoFocus() {
schedule("afterRender", () => {
const $invalid = $(
@@ -130,6 +140,10 @@ export default Component.extend({
document.location = getUrl("/");
},
stylingDropdownChanged(id, value) {
this.set("stylingDropdown", { id, value });
},
exitEarly() {
const step = this.step;
step.validate();

View File

@@ -12,7 +12,7 @@ export default Controller.extend({
@discourseComputed("model")
fontClasses(model) {
const fontsStep = model.steps.findBy("id", "fonts");
const fontsStep = model.steps.findBy("id", "styling");
if (!fontsStep) {
return [];
}

View File

@@ -2,6 +2,7 @@ import Component from "@ember/component";
import { Promise } from "rsvp";
/*eslint no-bitwise:0 */
import getUrl from "discourse-common/lib/get-url";
import { htmlSafe } from "@ember/template";
import { scheduleOnce } from "@ember/runloop";
export const LOREM = `
@@ -41,7 +42,7 @@ export function createPreviewComponent(width, height, obj) {
height,
elementWidth: width * scale,
elementHeight: height * scale,
canvasStyle: `width:${width}px;height:${height}px`,
canvasStyle: htmlSafe(`width:${width}px;height:${height}px`),
ctx: null,
loaded: false,
@@ -87,11 +88,17 @@ export function createPreviewComponent(width, height, obj) {
return false;
}
const colors = this.wizard.getCurrentColors(this.colorsId);
if (!colors) {
const colorsArray = this.wizard.getCurrentColors(this.colorsId);
if (!colorsArray) {
return;
}
let colors = {};
colorsArray.forEach(function (c) {
const name = c.name;
colors[name] = `#${c.hex}`;
});
const font = this.wizard.getCurrentFont(this.fontId);
const headingFont = this.wizard.getCurrentFont(
this.fontId,
@@ -115,12 +122,6 @@ export function createPreviewComponent(width, height, obj) {
height: this.height,
};
this.paint(options);
// draw border
ctx.beginPath();
ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
ctx.rect(0, 0, width, height);
ctx.stroke();
},
categories() {

View File

@@ -26,12 +26,12 @@ const Wizard = EmberObject.extend({
// A bit clunky, but get the current colors from the appropriate step
getCurrentColors(schemeId) {
const colorStep = this.steps.findBy("id", "colors");
const colorStep = this.steps.findBy("id", "styling");
if (!colorStep) {
return this.current_color_scheme;
}
const themeChoice = colorStep.get("fieldsById.theme_previews");
const themeChoice = colorStep.get("fieldsById.color_scheme");
if (!themeChoice) {
return;
}
@@ -55,7 +55,7 @@ const Wizard = EmberObject.extend({
},
getCurrentFont(fontId, type = "body_font") {
const fontsStep = this.steps.findBy("id", "fonts");
const fontsStep = this.steps.findBy("id", "styling");
if (!fontsStep) {
return;
}

View File

@@ -1,8 +0,0 @@
<div class="preview-area">
<canvas
width={{elementWidth}}
height={{elementHeight}}
style={{canvasStyle}}
>
</canvas>
</div>

View File

@@ -0,0 +1,22 @@
<div class="previews {{if draggingActive "dragging"}}">
<div class="preview-area topic-preview">
<canvas width={{elementWidth}} height={{elementHeight}} style={{canvasStyle}}>
</canvas>
</div>
<div class="preview-area homepage-preview">
{{homepage-preview
wizard=wizard
step=step
stylingDropdown=stylingDropdown
}}
</div>
</div>
<div class="preview-nav">
<a href class="preview-nav-button {{if previewTopic "active"}}" {{action "setPreviewTopic"}}>
{{i18n "wizard.previews.topic_preview"}}
</a>
<a href class="preview-nav-button {{unless previewTopic "active"}}" {{action "setPreviewHomepage"}}>
{{i18n "wizard.previews.homepage_preview"}}
</a>
</div>

View File

@@ -1,14 +0,0 @@
<ul class="grid">
{{#each field.choices as |choice|}}
<li>
{{theme-preview colorsId=choice.id
wizard=wizard
selectedId=field.value
onChange=(action "changed")}}
{{radio-button radioValue=choice.id
label=choice.id
value=field.value
onChange=(action "changed")}}
</li>
{{/each}}
</ul>

View File

@@ -1,9 +1,12 @@
{{combo-box
id=field.id
{{component
componentName
class=fieldClass
value=field.value
content=field.choices
nameProperty="label"
tabindex="9"
onChange=(action (mut field.value))
onChange=(action "onChangeValue")
options=(hash
translatedNone=false
)
}}

View File

@@ -13,7 +13,14 @@
</label>
<div class="input-area">
{{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard}}
{{component
inputComponentName
field=field step=step
fieldClass=fieldClass
wizard=wizard
stylingDropdownChanged=stylingDropdownChanged
stylingDropdown=stylingDropdown
}}
</div>
{{#if field.errorDescription}}

View File

@@ -14,9 +14,32 @@
</div>
{{#wizard-step-form step=step}}
{{#each step.fields as |field|}}
{{wizard-field field=field step=step wizard=wizard}}
{{/each}}
{{#if includeSidebar}}
<div class="wizard-fields-sidebar">
{{#each step.fields as |field|}}
{{#if field.show_in_sidebar}}
{{wizard-field
field=field
step=step
wizard=wizard
stylingDropdownChanged=(action "stylingDropdownChanged")
}}
{{/if}}
{{/each}}
</div>
{{/if}}
<div class="wizard-fields-main">
{{#each step.fields as |field|}}
{{#unless field.show_in_sidebar}}
{{wizard-field
field=field
step=step
wizard=wizard
stylingDropdown=stylingDropdown
}}
{{/unless}}
{{/each}}
</div>
{{/wizard-step-form}}
</div>