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);
|
||||
}
|
||||
|
||||
get headerHeight() {
|
||||
return this.height * 0.15;
|
||||
}
|
||||
|
||||
drawFullHeader(colors, font, logo) {
|
||||
const { ctx } = this;
|
||||
|
||||
const headerHeight = this.height * 0.15;
|
||||
drawHeader(ctx, colors, this.width, headerHeight);
|
||||
drawHeader(ctx, colors, this.width, this.headerHeight);
|
||||
|
||||
const avatarSize = this.height * 0.1;
|
||||
const headerMargin = headerHeight * 0.2;
|
||||
const headerMargin = this.headerHeight * 0.2;
|
||||
|
||||
if (logo) {
|
||||
const logoHeight = headerHeight - headerMargin * 2;
|
||||
const logoHeight = this.headerHeight - headerMargin * 2;
|
||||
|
||||
const ratio = logoHeight / logo.height;
|
||||
this.scaleImage(
|
||||
@ -285,7 +288,7 @@ export default class PreviewBase extends Component {
|
||||
colors.primary_low_mid ||
|
||||
darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
||||
|
||||
const pathScale = headerHeight / 1200;
|
||||
const pathScale = this.headerHeight / 1200;
|
||||
// search icon SVG path
|
||||
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"
|
||||
@ -390,6 +393,29 @@ export default class PreviewBase extends Component {
|
||||
x += categoriesSize * 0.6;
|
||||
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) {
|
||||
|
@ -7,8 +7,8 @@ import HomepagePreview from "./-homepage-preview";
|
||||
import PreviewBaseComponent from "./-preview-base";
|
||||
|
||||
export default class Index extends PreviewBaseComponent {
|
||||
width = 628;
|
||||
height = 322;
|
||||
width = 630;
|
||||
height = 380;
|
||||
logo = null;
|
||||
avatar = null;
|
||||
previewTopic = true;
|
||||
@ -111,51 +111,60 @@ export default class Index extends PreviewBaseComponent {
|
||||
}
|
||||
|
||||
paint({ ctx, colors, font, headingFont, width, height }) {
|
||||
const headerHeight = height * 0.3;
|
||||
|
||||
this.drawFullHeader(colors, headingFont, this.logo);
|
||||
|
||||
const margin = 20;
|
||||
const avatarSize = height * 0.15;
|
||||
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,
|
||||
headerHeight + height * 0.09,
|
||||
this.headerHeight + height * 0.22,
|
||||
avatarSize,
|
||||
avatarSize
|
||||
);
|
||||
|
||||
const titleFontSize = headerHeight / 55;
|
||||
const titleFontSize = this.headerHeight / 30;
|
||||
|
||||
// Topic title
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = colors.primary;
|
||||
ctx.font = `bold ${titleFontSize}em '${headingFont}'`;
|
||||
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}'`;
|
||||
|
||||
let line = 0;
|
||||
const lines = i18n("wizard.homepage_preview.topic_ops.what_books").split(
|
||||
"\n"
|
||||
let verticalLinePos = 0;
|
||||
const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books");
|
||||
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}'`;
|
||||
|
||||
// Share Button
|
||||
// Share button
|
||||
const shareButtonWidth =
|
||||
Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) +
|
||||
20;
|
||||
margin;
|
||||
|
||||
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
|
||||
ctx.fillStyle =
|
||||
colors.primary_low ||
|
||||
@ -165,18 +174,18 @@ export default class Index extends PreviewBaseComponent {
|
||||
ctx.fillText(
|
||||
i18n("wizard.previews.share_button"),
|
||||
margin + 10,
|
||||
line + lineHeight * 1.9
|
||||
verticalLinePos + lineHeight * 0.9
|
||||
);
|
||||
|
||||
// Reply Button
|
||||
// Reply button
|
||||
const replyButtonWidth =
|
||||
Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) +
|
||||
20;
|
||||
margin;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(
|
||||
shareButtonWidth + margin + 10,
|
||||
line + lineHeight,
|
||||
verticalLinePos,
|
||||
replyButtonWidth,
|
||||
height * 0.1
|
||||
);
|
||||
@ -185,12 +194,11 @@ export default class Index extends PreviewBaseComponent {
|
||||
ctx.fillStyle = colors.secondary;
|
||||
ctx.fillText(
|
||||
i18n("wizard.previews.reply_button"),
|
||||
shareButtonWidth + margin + 20,
|
||||
line + lineHeight * 1.9
|
||||
shareButtonWidth + margin * 2,
|
||||
verticalLinePos + lineHeight * 0.9
|
||||
);
|
||||
|
||||
// Draw Timeline
|
||||
const timelineX = width * 0.86;
|
||||
// Draw timeline
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = colors.tertiary;
|
||||
ctx.lineWidth = 0.5;
|
||||
@ -198,17 +206,29 @@ export default class Index extends PreviewBaseComponent {
|
||||
ctx.lineTo(timelineX, height * 0.7);
|
||||
ctx.stroke();
|
||||
|
||||
// Timeline
|
||||
// Timeline handle
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = colors.tertiary;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.moveTo(timelineX, height * 0.3);
|
||||
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.font = `Bold ${bodyFontSize}em ${font}`;
|
||||
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
|
||||
|
@ -151,6 +151,12 @@ body.wizard {
|
||||
}
|
||||
}
|
||||
|
||||
&__step.styling .wizard-container__field.styling-preview-field {
|
||||
label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__field {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user