mirror of
https://github.com/discourse/discourse.git
synced 2025-02-05 19:11:13 +00:00
UX: Wizard style preview improvements
* Render date on timeline * Render timeline handle thicker and with a small gap at top * Make sure body text does not overflow over timeline with bounding box calculation and dymanic font resizing * Other minor improvements to spacing/sizing
This commit is contained in:
parent
2ac2c42b06
commit
af5eed5005
@ -248,17 +248,20 @@ export default class PreviewBase extends Component {
|
|||||||
ctx.drawImage(scaled[key], x, y, w, h);
|
ctx.drawImage(scaled[key], x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get headerHeight() {
|
||||||
|
return this.height * 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
drawFullHeader(colors, font, logo) {
|
drawFullHeader(colors, font, logo) {
|
||||||
const { ctx } = this;
|
const { ctx } = this;
|
||||||
|
|
||||||
const headerHeight = this.height * 0.15;
|
drawHeader(ctx, colors, this.width, this.headerHeight);
|
||||||
drawHeader(ctx, colors, this.width, headerHeight);
|
|
||||||
|
|
||||||
const avatarSize = this.height * 0.1;
|
const avatarSize = this.height * 0.1;
|
||||||
const headerMargin = headerHeight * 0.2;
|
const headerMargin = this.headerHeight * 0.2;
|
||||||
|
|
||||||
if (logo) {
|
if (logo) {
|
||||||
const logoHeight = headerHeight - headerMargin * 2;
|
const logoHeight = this.headerHeight - headerMargin * 2;
|
||||||
|
|
||||||
const ratio = logoHeight / logo.height;
|
const ratio = logoHeight / logo.height;
|
||||||
this.scaleImage(
|
this.scaleImage(
|
||||||
@ -285,7 +288,7 @@ export default class PreviewBase extends Component {
|
|||||||
colors.primary_low_mid ||
|
colors.primary_low_mid ||
|
||||||
darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
||||||
|
|
||||||
const pathScale = headerHeight / 1200;
|
const pathScale = this.headerHeight / 1200;
|
||||||
// search icon SVG path
|
// search icon SVG path
|
||||||
const searchIcon = new Path2D(
|
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"
|
"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"
|
||||||
@ -390,6 +393,29 @@ export default class PreviewBase extends Component {
|
|||||||
x += categoriesSize * 0.6;
|
x += categoriesSize * 0.6;
|
||||||
ctx.fillText("Top", x, headerHeight + headerMargin * 1.5 + fontSize);
|
ctx.fillText("Top", x, headerHeight + headerMargin * 1.5 + fontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resizeTextLinesToFitRect(
|
||||||
|
textLines,
|
||||||
|
rectWidth,
|
||||||
|
ctx,
|
||||||
|
fontSize,
|
||||||
|
font,
|
||||||
|
renderCallback
|
||||||
|
) {
|
||||||
|
const maxLengthLine = textLines.reduce((a, b) =>
|
||||||
|
a.length > b.length ? a : b
|
||||||
|
);
|
||||||
|
|
||||||
|
let fontSizeDecreaseMultiplier = 1;
|
||||||
|
while (ctx.measureText(maxLengthLine).width > rectWidth) {
|
||||||
|
fontSizeDecreaseMultiplier -= 0.1;
|
||||||
|
ctx.font = `${fontSize * fontSizeDecreaseMultiplier}em '${font}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < textLines.length; i++) {
|
||||||
|
renderCallback(textLines[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadImage(src) {
|
function loadImage(src) {
|
||||||
|
@ -7,8 +7,8 @@ import HomepagePreview from "./-homepage-preview";
|
|||||||
import PreviewBaseComponent from "./-preview-base";
|
import PreviewBaseComponent from "./-preview-base";
|
||||||
|
|
||||||
export default class Index extends PreviewBaseComponent {
|
export default class Index extends PreviewBaseComponent {
|
||||||
width = 628;
|
width = 630;
|
||||||
height = 322;
|
height = 380;
|
||||||
logo = null;
|
logo = null;
|
||||||
avatar = null;
|
avatar = null;
|
||||||
previewTopic = true;
|
previewTopic = true;
|
||||||
@ -111,51 +111,60 @@ export default class Index extends PreviewBaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paint({ ctx, colors, font, headingFont, width, height }) {
|
paint({ ctx, colors, font, headingFont, width, height }) {
|
||||||
const headerHeight = height * 0.3;
|
|
||||||
|
|
||||||
this.drawFullHeader(colors, headingFont, this.logo);
|
this.drawFullHeader(colors, headingFont, this.logo);
|
||||||
|
|
||||||
const margin = 20;
|
const margin = 20;
|
||||||
const avatarSize = height * 0.15;
|
const avatarSize = height * 0.1 + 5;
|
||||||
const lineHeight = height / 14;
|
const lineHeight = height / 14;
|
||||||
|
const leftHandTextGutter = margin + avatarSize + margin;
|
||||||
|
const timelineX = width * 0.86;
|
||||||
|
|
||||||
// Draw a fake topic
|
// Draw a fake topic
|
||||||
this.scaleImage(
|
this.scaleImage(
|
||||||
this.avatar,
|
this.avatar,
|
||||||
margin,
|
margin,
|
||||||
headerHeight + height * 0.09,
|
this.headerHeight + height * 0.22,
|
||||||
avatarSize,
|
avatarSize,
|
||||||
avatarSize
|
avatarSize
|
||||||
);
|
);
|
||||||
|
|
||||||
const titleFontSize = headerHeight / 55;
|
const titleFontSize = this.headerHeight / 30;
|
||||||
|
|
||||||
|
// Topic title
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = colors.primary;
|
ctx.fillStyle = colors.primary;
|
||||||
ctx.font = `bold ${titleFontSize}em '${headingFont}'`;
|
ctx.font = `bold ${titleFontSize}em '${headingFont}'`;
|
||||||
ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3);
|
ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3);
|
||||||
|
|
||||||
const bodyFontSize = height / 330.0;
|
// Topic OP text
|
||||||
|
const bodyFontSize = 1;
|
||||||
ctx.font = `${bodyFontSize}em '${font}'`;
|
ctx.font = `${bodyFontSize}em '${font}'`;
|
||||||
|
|
||||||
let line = 0;
|
let verticalLinePos = 0;
|
||||||
const lines = i18n("wizard.homepage_preview.topic_ops.what_books").split(
|
const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books");
|
||||||
"\n"
|
const topicOpLines = topicOp.split("\n");
|
||||||
|
|
||||||
|
this.resizeTextLinesToFitRect(
|
||||||
|
topicOpLines,
|
||||||
|
timelineX - leftHandTextGutter,
|
||||||
|
ctx,
|
||||||
|
bodyFontSize,
|
||||||
|
font,
|
||||||
|
(textLine, idx) => {
|
||||||
|
verticalLinePos = height * 0.4 + idx * lineHeight;
|
||||||
|
ctx.fillText(textLine, leftHandTextGutter, verticalLinePos);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
line = height * 0.4 + i * lineHeight;
|
|
||||||
ctx.fillText(lines[i], margin + avatarSize + margin, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.font = `${bodyFontSize}em '${font}'`;
|
ctx.font = `${bodyFontSize}em '${font}'`;
|
||||||
|
|
||||||
// Share Button
|
// Share button
|
||||||
const shareButtonWidth =
|
const shareButtonWidth =
|
||||||
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
|
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
|
||||||
20;
|
margin;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.rect(margin, line + lineHeight, shareButtonWidth, height * 0.1);
|
ctx.rect(margin, verticalLinePos, shareButtonWidth, height * 0.1);
|
||||||
// accounts for hard-set color variables in solarized themes
|
// accounts for hard-set color variables in solarized themes
|
||||||
ctx.fillStyle =
|
ctx.fillStyle =
|
||||||
colors.primary_low ||
|
colors.primary_low ||
|
||||||
@ -165,18 +174,18 @@ export default class Index extends PreviewBaseComponent {
|
|||||||
ctx.fillText(
|
ctx.fillText(
|
||||||
i18n("wizard.previews.share_button"),
|
i18n("wizard.previews.share_button"),
|
||||||
margin + 10,
|
margin + 10,
|
||||||
line + lineHeight * 1.9
|
verticalLinePos + lineHeight * 0.9
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reply Button
|
// Reply button
|
||||||
const replyButtonWidth =
|
const replyButtonWidth =
|
||||||
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
|
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
|
||||||
20;
|
margin;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.rect(
|
ctx.rect(
|
||||||
shareButtonWidth + margin + 10,
|
shareButtonWidth + margin + 10,
|
||||||
line + lineHeight,
|
verticalLinePos,
|
||||||
replyButtonWidth,
|
replyButtonWidth,
|
||||||
height * 0.1
|
height * 0.1
|
||||||
);
|
);
|
||||||
@ -185,12 +194,11 @@ export default class Index extends PreviewBaseComponent {
|
|||||||
ctx.fillStyle = colors.secondary;
|
ctx.fillStyle = colors.secondary;
|
||||||
ctx.fillText(
|
ctx.fillText(
|
||||||
i18n("wizard.previews.reply_button"),
|
i18n("wizard.previews.reply_button"),
|
||||||
shareButtonWidth + margin + 20,
|
shareButtonWidth + margin * 2,
|
||||||
line + lineHeight * 1.9
|
verticalLinePos + lineHeight * 0.9
|
||||||
);
|
);
|
||||||
|
|
||||||
// Draw Timeline
|
// Draw timeline
|
||||||
const timelineX = width * 0.86;
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = colors.tertiary;
|
ctx.strokeStyle = colors.tertiary;
|
||||||
ctx.lineWidth = 0.5;
|
ctx.lineWidth = 0.5;
|
||||||
@ -198,17 +206,29 @@ export default class Index extends PreviewBaseComponent {
|
|||||||
ctx.lineTo(timelineX, height * 0.7);
|
ctx.lineTo(timelineX, height * 0.7);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
// Timeline
|
// Timeline handle
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = colors.tertiary;
|
ctx.strokeStyle = colors.tertiary;
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 3;
|
||||||
ctx.moveTo(timelineX, height * 0.3);
|
ctx.moveTo(timelineX, height * 0.3 + 10);
|
||||||
ctx.lineTo(timelineX, height * 0.4);
|
ctx.lineTo(timelineX, height * 0.4);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Timeline post count
|
||||||
|
const postCountY = height * 0.3 + margin + 10;
|
||||||
ctx.font = `Bold ${bodyFontSize}em ${font}`;
|
ctx.font = `Bold ${bodyFontSize}em ${font}`;
|
||||||
ctx.fillStyle = colors.primary;
|
ctx.fillStyle = colors.primary;
|
||||||
ctx.fillText("1 / 20", timelineX + margin, height * 0.3 + margin * 1.5);
|
ctx.fillText("1 / 20", timelineX + margin / 2, postCountY);
|
||||||
|
|
||||||
|
// Timeline post date
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.font = `${bodyFontSize * 0.9}em ${font}`;
|
||||||
|
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65);
|
||||||
|
ctx.fillText(
|
||||||
|
"Nov 22",
|
||||||
|
timelineX + margin / 2,
|
||||||
|
postCountY + lineHeight * 0.75
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -151,6 +151,12 @@ body.wizard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__step.styling .wizard-container__field.styling-preview-field {
|
||||||
|
label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__field {
|
&__field {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user