mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Separate base and heading font site_settings (#10807)
Allows site administrators to pick different fonts for headings in the wizard and in their site settings. Also correctly displays the header logos in wizard previews.
This commit is contained in:
parent
bdfb370f19
commit
a4356b99af
@ -1,30 +1,26 @@
|
|||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
import { observes } from "discourse-common/utils/decorators";
|
||||||
import {
|
import {
|
||||||
createPreviewComponent,
|
createPreviewComponent,
|
||||||
darkLightDiff,
|
darkLightDiff,
|
||||||
chooseDarker,
|
chooseDarker,
|
||||||
LOREM,
|
|
||||||
} from "wizard/lib/preview";
|
} from "wizard/lib/preview";
|
||||||
|
|
||||||
export default createPreviewComponent(305, 165, {
|
const LOREM = `
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing.
|
||||||
|
Nullam eget sem non elit tincidunt rhoncus. Fusce
|
||||||
|
velit nisl, porttitor sed nisl ac, consectetur interdum
|
||||||
|
metus. Fusce in consequat augue, vel facilisis felis.`;
|
||||||
|
|
||||||
|
export default createPreviewComponent(659, 320, {
|
||||||
logo: null,
|
logo: null,
|
||||||
avatar: null,
|
avatar: null,
|
||||||
|
|
||||||
classNameBindings: ["isSelected"],
|
@observes(
|
||||||
|
"step.fieldsById.body_font.value",
|
||||||
@discourseComputed("selectedId", "fontId")
|
"step.fieldsById.heading_font.value"
|
||||||
isSelected(selectedId, fontId) {
|
)
|
||||||
return selectedId === fontId;
|
fontChanged() {
|
||||||
},
|
|
||||||
|
|
||||||
click() {
|
|
||||||
this.onChange(this.fontId);
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("step.fieldsById.base_scheme_id.value")
|
|
||||||
themeChanged() {
|
|
||||||
this.triggerRepaint();
|
this.triggerRepaint();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -35,47 +31,44 @@ export default createPreviewComponent(305, 165, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint({ ctx, colors, font, headingFont, width, height }) {
|
||||||
const headerHeight = height * 0.3;
|
const headerHeight = height * 0.3;
|
||||||
this.drawFullHeader(colors, font);
|
|
||||||
|
this.drawFullHeader(colors, headingFont, this.logo);
|
||||||
|
|
||||||
const margin = width * 0.04;
|
const margin = width * 0.04;
|
||||||
const avatarSize = height * 0.2;
|
const avatarSize = height * 0.2;
|
||||||
const lineHeight = height / 9.5;
|
const lineHeight = height / 11;
|
||||||
|
|
||||||
// Draw a fake topic
|
// Draw a fake topic
|
||||||
this.scaleImage(
|
this.scaleImage(
|
||||||
this.avatar,
|
this.avatar,
|
||||||
margin,
|
margin,
|
||||||
headerHeight + height * 0.085,
|
headerHeight + height * 0.11,
|
||||||
avatarSize,
|
avatarSize,
|
||||||
avatarSize
|
avatarSize
|
||||||
);
|
);
|
||||||
|
|
||||||
const titleFontSize = headerHeight / 44;
|
const titleFontSize = headerHeight / 55;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = colors.primary;
|
ctx.fillStyle = colors.primary;
|
||||||
ctx.font = `bold ${titleFontSize}em '${font}'`;
|
ctx.font = `bold ${titleFontSize}em '${headingFont}'`;
|
||||||
ctx.fillText(
|
ctx.fillText(I18n.t("wizard.previews.topic_title"), margin, height * 0.3);
|
||||||
I18n.t("wizard.previews.font_title", { font }),
|
|
||||||
margin,
|
|
||||||
height * 0.3
|
|
||||||
);
|
|
||||||
|
|
||||||
const bodyFontSize = height / 220.0;
|
const bodyFontSize = height / 330.0;
|
||||||
ctx.font = `${bodyFontSize}em '${font}'`;
|
ctx.font = `${bodyFontSize}em '${font}'`;
|
||||||
|
|
||||||
let line = 0;
|
let line = 0;
|
||||||
const lines = LOREM.split("\n");
|
const lines = LOREM.split("\n");
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
line = height * 0.35 + i * lineHeight;
|
line = height * 0.35 + i * lineHeight;
|
||||||
ctx.fillText(lines[i], margin + avatarSize + margin, line);
|
ctx.fillText(lines[i], margin + avatarSize + margin, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share Button
|
// Share Button
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.rect(margin, line + lineHeight, width * 0.14, height * 0.14);
|
ctx.rect(margin, line + lineHeight, width * 0.1, height * 0.12);
|
||||||
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 90, 65);
|
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 90, 65);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = chooseDarker(colors.primary, colors.secondary);
|
ctx.fillStyle = chooseDarker(colors.primary, colors.secondary);
|
||||||
@ -89,10 +82,10 @@ export default createPreviewComponent(305, 165, {
|
|||||||
// Reply Button
|
// Reply Button
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.rect(
|
ctx.rect(
|
||||||
margin * 2 + width * 0.14,
|
margin + width * 0.12,
|
||||||
line + lineHeight,
|
line + lineHeight,
|
||||||
width * 0.14,
|
width * 0.1,
|
||||||
height * 0.14
|
height * 0.12
|
||||||
);
|
);
|
||||||
ctx.fillStyle = colors.tertiary;
|
ctx.fillStyle = colors.tertiary;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
@ -100,12 +93,12 @@ export default createPreviewComponent(305, 165, {
|
|||||||
ctx.font = `${bodyFontSize}em '${font}'`;
|
ctx.font = `${bodyFontSize}em '${font}'`;
|
||||||
ctx.fillText(
|
ctx.fillText(
|
||||||
I18n.t("wizard.previews.reply_button"),
|
I18n.t("wizard.previews.reply_button"),
|
||||||
margin * 2 + width * 0.14 + width / 55,
|
margin + width * 0.12 + width / 55,
|
||||||
line + lineHeight * 1.85
|
line + lineHeight * 1.85
|
||||||
);
|
);
|
||||||
|
|
||||||
// Draw Timeline
|
// Draw Timeline
|
||||||
const timelineX = width * 0.8;
|
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;
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
export default Component.extend({
|
|
||||||
actions: {
|
|
||||||
changed(value) {
|
|
||||||
this.set("field.value", value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
@ -21,8 +21,8 @@ export default createPreviewComponent(659, 320, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint({ ctx, colors, font, width, height }) {
|
||||||
this.drawFullHeader(colors, font);
|
this.drawFullHeader(colors, font, this.logo);
|
||||||
|
|
||||||
if (this.get("step.fieldsById.homepage_style.value") === "latest") {
|
if (this.get("step.fieldsById.homepage_style.value") === "latest") {
|
||||||
this.drawPills(colors, font, height * 0.15);
|
this.drawPills(colors, font, height * 0.15);
|
||||||
|
@ -14,7 +14,8 @@ export default createPreviewComponent(371, 124, {
|
|||||||
return { tab: "/images/wizard/tab.png", image: this.get("field.value") };
|
return { tab: "/images/wizard/tab.png", image: this.get("field.value") };
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint(options) {
|
||||||
|
const { ctx, width, height } = options;
|
||||||
this.scaleImage(this.tab, 0, 0, width, height);
|
this.scaleImage(this.tab, 0, 0, width, height);
|
||||||
this.scaleImage(this.image, 40, 25, 30, 30);
|
this.scaleImage(this.image, 40, 25, 30, 30);
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ export default createPreviewComponent(325, 125, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint(options) {
|
||||||
|
const { width, height } = options;
|
||||||
this.scaleImage(this.image, 10, 8, 87, 87);
|
this.scaleImage(this.image, 10, 8, 87, 87);
|
||||||
this.scaleImage(this.ios, 0, 0, width, height);
|
this.scaleImage(this.ios, 0, 0, width, height);
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,8 @@ export default createPreviewComponent(375, 100, {
|
|||||||
return { image: this.get("field.value") };
|
return { image: this.get("field.value") };
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint(options) {
|
||||||
|
const { ctx, colors, font, headingFont, width, height } = options;
|
||||||
const headerHeight = height / 2;
|
const headerHeight = height / 2;
|
||||||
|
|
||||||
drawHeader(ctx, colors, width, headerHeight);
|
drawHeader(ctx, colors, width, headerHeight);
|
||||||
@ -39,7 +40,8 @@ export default createPreviewComponent(375, 100, {
|
|||||||
|
|
||||||
const afterLogo = headerMargin * 1.7 + imageWidth;
|
const afterLogo = headerMargin * 1.7 + imageWidth;
|
||||||
const fontSize = Math.round(headerHeight * 0.4);
|
const fontSize = Math.round(headerHeight * 0.4);
|
||||||
ctx.font = `Bold ${fontSize}px '${font}'`;
|
|
||||||
|
ctx.font = `Bold ${fontSize}px '${headingFont}'`;
|
||||||
ctx.fillStyle = colors.primary;
|
ctx.fillStyle = colors.primary;
|
||||||
const title = LOREM.substring(0, 27);
|
const title = LOREM.substring(0, 27);
|
||||||
ctx.fillText(
|
ctx.fillText(
|
||||||
|
@ -13,12 +13,13 @@ export default createPreviewComponent(400, 100, {
|
|||||||
return { image: this.get("field.value") };
|
return { image: this.get("field.value") };
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint({ ctx, colors, font, width, height }) {
|
||||||
const headerHeight = height / 2;
|
const headerHeight = height / 2;
|
||||||
|
|
||||||
drawHeader(ctx, colors, width, headerHeight);
|
drawHeader(ctx, colors, width, headerHeight);
|
||||||
|
|
||||||
const image = this.image;
|
const image = this.image;
|
||||||
|
|
||||||
const headerMargin = headerHeight * 0.2;
|
const headerMargin = headerHeight * 0.2;
|
||||||
|
|
||||||
const imageHeight = headerHeight - headerMargin * 2;
|
const imageHeight = headerHeight - headerMargin * 2;
|
||||||
|
@ -35,10 +35,10 @@ export default createPreviewComponent(305, 165, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
paint(ctx, colors, font, width, height) {
|
paint({ ctx, colors, font, headingFont, width, height }) {
|
||||||
const headerHeight = height * 0.3;
|
const headerHeight = height * 0.3;
|
||||||
|
|
||||||
this.drawFullHeader(colors, font);
|
this.drawFullHeader(colors, headingFont, this.logo);
|
||||||
|
|
||||||
const margin = width * 0.04;
|
const margin = width * 0.04;
|
||||||
const avatarSize = height * 0.2;
|
const avatarSize = height * 0.2;
|
||||||
@ -57,7 +57,7 @@ export default createPreviewComponent(305, 165, {
|
|||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = colors.primary;
|
ctx.fillStyle = colors.primary;
|
||||||
ctx.font = `bold ${titleFontSize}em '${font}'`;
|
ctx.font = `bold ${titleFontSize}em '${headingFont}'`;
|
||||||
ctx.fillText(I18n.t("wizard.previews.topic_title"), margin, height * 0.3);
|
ctx.fillText(I18n.t("wizard.previews.topic_title"), margin, height * 0.3);
|
||||||
|
|
||||||
const bodyFontSize = height / 220.0;
|
const bodyFontSize = height / 220.0;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
keyPress(e) {
|
keyPress(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
|
import { dasherize } from "@ember/string";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
@ -16,7 +17,9 @@ export default Controller.extend({
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const fontField = fontsStep.get("fieldsById.font_previews");
|
const fontField = fontsStep.get("fieldsById.body_font");
|
||||||
return fontField.choices.map((choice) => `font-${choice.data.class}`);
|
return fontField.choices.map(
|
||||||
|
(choice) => `body-font-${dasherize(choice.id)}`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -93,6 +93,10 @@ export function createPreviewComponent(width, height, obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const font = this.wizard.getCurrentFont(this.fontId);
|
const font = this.wizard.getCurrentFont(this.fontId);
|
||||||
|
const headingFont = this.wizard.getCurrentFont(
|
||||||
|
this.fontId,
|
||||||
|
"heading_font"
|
||||||
|
);
|
||||||
if (!font) {
|
if (!font) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -102,7 +106,15 @@ export function createPreviewComponent(width, height, obj) {
|
|||||||
ctx.fillStyle = colors.secondary;
|
ctx.fillStyle = colors.secondary;
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
this.paint(ctx, colors, font, this.width, this.height);
|
const options = {
|
||||||
|
ctx,
|
||||||
|
colors,
|
||||||
|
font,
|
||||||
|
headingFont,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
};
|
||||||
|
this.paint(options);
|
||||||
|
|
||||||
// draw border
|
// draw border
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
@ -142,7 +154,7 @@ export function createPreviewComponent(width, height, obj) {
|
|||||||
ctx.drawImage(scaled[key], x, y, w, h);
|
ctx.drawImage(scaled[key], x, y, w, h);
|
||||||
},
|
},
|
||||||
|
|
||||||
drawFullHeader(colors, font) {
|
drawFullHeader(colors, font, logo) {
|
||||||
const { ctx } = this;
|
const { ctx } = this;
|
||||||
|
|
||||||
const headerHeight = height * 0.15;
|
const headerHeight = height * 0.15;
|
||||||
@ -154,10 +166,16 @@ export function createPreviewComponent(width, height, obj) {
|
|||||||
const headerMargin = headerHeight * 0.2;
|
const headerMargin = headerHeight * 0.2;
|
||||||
const logoHeight = headerHeight - headerMargin * 2;
|
const logoHeight = headerHeight - headerMargin * 2;
|
||||||
|
|
||||||
ctx.beginPath();
|
const ratio = logoHeight / logo.height;
|
||||||
ctx.fillStyle = colors.header_primary;
|
this.scaleImage(
|
||||||
ctx.font = `bold ${logoHeight}px '${font}'`;
|
logo,
|
||||||
ctx.fillText("Discourse", headerMargin, headerHeight - headerMargin);
|
headerMargin,
|
||||||
|
headerMargin,
|
||||||
|
logo.width * ratio,
|
||||||
|
logoHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
this.scaleImage(logo, width, headerMargin);
|
||||||
|
|
||||||
// Top right menu
|
// Top right menu
|
||||||
this.scaleImage(
|
this.scaleImage(
|
||||||
|
@ -21,7 +21,7 @@ const Wizard = EmberObject.extend({
|
|||||||
if (!logoStep) {
|
if (!logoStep) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return logoStep.get("fieldsById.logo_url.value");
|
return logoStep.get("fieldsById.logo.value");
|
||||||
},
|
},
|
||||||
|
|
||||||
// A bit clunky, but get the current colors from the appropriate step
|
// A bit clunky, but get the current colors from the appropriate step
|
||||||
@ -54,13 +54,13 @@ const Wizard = EmberObject.extend({
|
|||||||
return option.data.colors;
|
return option.data.colors;
|
||||||
},
|
},
|
||||||
|
|
||||||
getCurrentFont(fontId) {
|
getCurrentFont(fontId, type = "body_font") {
|
||||||
const fontsStep = this.steps.findBy("id", "fonts");
|
const fontsStep = this.steps.findBy("id", "fonts");
|
||||||
if (!fontsStep) {
|
if (!fontsStep) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fontChoice = fontsStep.get("fieldsById.font_previews");
|
const fontChoice = fontsStep.get(`fieldsById.${type}`);
|
||||||
if (!fontChoice) {
|
if (!fontChoice) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ const Wizard = EmberObject.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return option.data.name;
|
return option.label;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<ul class="grid">
|
|
||||||
{{#each field.choices as |choice|}}
|
|
||||||
<li>
|
|
||||||
{{font-preview wizard=wizard
|
|
||||||
fontId=choice.id
|
|
||||||
selectedId=field.value
|
|
||||||
onChange=(action "changed")}}
|
|
||||||
{{radio-button radioValue=choice.id
|
|
||||||
label=choice.label
|
|
||||||
value=field.value
|
|
||||||
onChange=(action "changed")}}
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
@ -90,6 +90,7 @@ h3,
|
|||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
|
font-family: $heading-font-family;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
@ -566,6 +567,8 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.control-label {
|
.control-label {
|
||||||
|
font-family: $heading-font-family;
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: $font-up-2;
|
font-size: $font-up-2;
|
||||||
line-height: $line-height-large;
|
line-height: $line-height-large;
|
||||||
|
@ -48,6 +48,7 @@ $base-font-size: 0.938em !default; // eq. to 15px
|
|||||||
$base-font-size-larger: 1.063em !default; // eq. to 17px
|
$base-font-size-larger: 1.063em !default; // eq. to 17px
|
||||||
$base-font-size-largest: 1.118em !default; // eq. to 19px
|
$base-font-size-largest: 1.118em !default; // eq. to 19px
|
||||||
$base-font-family: var(--font-family) !default;
|
$base-font-family: var(--font-family) !default;
|
||||||
|
$heading-font-family: var(--heading-font-family) !default;
|
||||||
|
|
||||||
// Font-size defintions, multiplier ^ (step / interval)
|
// Font-size defintions, multiplier ^ (step / interval)
|
||||||
$font-up-6: 2.296em;
|
$font-up-6: 2.296em;
|
||||||
|
@ -92,6 +92,10 @@ body.wizard {
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-canvas {
|
.wizard-canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -157,8 +161,7 @@ body.wizard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-step-colors,
|
.wizard-step-colors {
|
||||||
.wizard-step-fonts {
|
|
||||||
max-height: 465px;
|
max-height: 465px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
.grid {
|
.grid {
|
||||||
@ -203,6 +206,16 @@ body.wizard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wizard-step-fonts {
|
||||||
|
.dropdown-field {
|
||||||
|
float: left;
|
||||||
|
margin-right: 1.5em;
|
||||||
|
}
|
||||||
|
.component-field {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-column {
|
.wizard-column {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
|
@ -27,7 +27,7 @@ DiscourseEvent.on(:site_setting_changed) do |name, old_value, new_value|
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Stylesheet::Manager.clear_core_cache!(["desktop", "mobile"]) if name == :base_font
|
Stylesheet::Manager.clear_core_cache!(["desktop", "mobile"]) if [:base_font, :heading_font].include?(name)
|
||||||
|
|
||||||
Report.clear_cache(:storage_stats) if [:backup_location, :s3_backup_bucket].include?(name)
|
Report.clear_cache(:storage_stats) if [:backup_location, :s3_backup_bucket].include?(name)
|
||||||
|
|
||||||
|
@ -2233,7 +2233,8 @@ en:
|
|||||||
push_notifications_prompt: "Display user consent prompt."
|
push_notifications_prompt: "Display user consent prompt."
|
||||||
push_notifications_icon: "The badge icon that appears in the notification corner. A 96×96 monochromatic PNG with transparency is recommended."
|
push_notifications_icon: "The badge icon that appears in the notification corner. A 96×96 monochromatic PNG with transparency is recommended."
|
||||||
|
|
||||||
base_font: "Font to use in most places on the site. Themes can override."
|
base_font: "Base font to use for most text on the site. Themes can override via the `--font-family` CSS custom property."
|
||||||
|
heading_font: "Font to use for headings on the site. Themes can override via the `--heading-font-family` CSS custom property."
|
||||||
|
|
||||||
short_title: "The short title will be used on the user's home screen, launcher, or other places where space may be limited. It should be limited to 12 characters."
|
short_title: "The short title will be used on the user's home screen, launcher, or other places where space may be limited. It should be limited to 12 characters."
|
||||||
|
|
||||||
@ -4708,6 +4709,13 @@ en:
|
|||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
title: "Fonts"
|
title: "Fonts"
|
||||||
|
fields:
|
||||||
|
body_font:
|
||||||
|
label: "Body font"
|
||||||
|
heading_font:
|
||||||
|
label: "Heading font"
|
||||||
|
font_preview:
|
||||||
|
label: "Preview"
|
||||||
|
|
||||||
logos:
|
logos:
|
||||||
title: "Logos"
|
title: "Logos"
|
||||||
|
@ -329,6 +329,10 @@ basic:
|
|||||||
default: "helvetica"
|
default: "helvetica"
|
||||||
enum: "BaseFontSetting"
|
enum: "BaseFontSetting"
|
||||||
refresh: true
|
refresh: true
|
||||||
|
heading_font:
|
||||||
|
default: "helvetica"
|
||||||
|
enum: "BaseFontSetting"
|
||||||
|
refresh: true
|
||||||
|
|
||||||
login:
|
login:
|
||||||
invite_only:
|
invite_only:
|
||||||
|
@ -41,18 +41,28 @@ module Stylesheet
|
|||||||
end
|
end
|
||||||
|
|
||||||
register_import "font" do
|
register_import "font" do
|
||||||
font = DiscourseFonts.fonts.find { |f| f[:key] == SiteSetting.base_font }
|
body_font = DiscourseFonts.fonts.find { |f| f[:key] == SiteSetting.base_font }
|
||||||
|
heading_font = DiscourseFonts.fonts.find { |f| f[:key] == SiteSetting.heading_font }
|
||||||
|
contents = +""
|
||||||
|
|
||||||
contents = if font.present?
|
if body_font.present?
|
||||||
<<~EOF
|
contents << <<~EOF
|
||||||
#{font_css(font)}
|
#{font_css(body_font)}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-family: #{font[:stack]};
|
--font-family: #{body_font[:stack]};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
if heading_font.present?
|
||||||
|
contents << <<~EOF
|
||||||
|
#{font_css(heading_font)}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--heading-font-family: #{heading_font[:stack]};
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
else
|
|
||||||
""
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Import.new("font.scss", source: contents)
|
Import.new("font.scss", source: contents)
|
||||||
@ -73,7 +83,10 @@ module Stylesheet
|
|||||||
|
|
||||||
contents << font_css(font)
|
contents << font_css(font)
|
||||||
contents << <<~EOF
|
contents << <<~EOF
|
||||||
.font-#{font[:key].tr("_", "-")} {
|
.body-font-#{font[:key].tr("_", "-")} {
|
||||||
|
font-family: #{font[:stack]};
|
||||||
|
}
|
||||||
|
.heading-font-#{font[:key].tr("_", "-")} h2 {
|
||||||
font-family: #{font[:stack]};
|
font-family: #{font[:stack]};
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
@ -406,12 +406,13 @@ class Stylesheet::Manager
|
|||||||
cs = @color_scheme || theme&.color_scheme
|
cs = @color_scheme || theme&.color_scheme
|
||||||
|
|
||||||
category_updated = Category.where("uploaded_background_id IS NOT NULL").pluck(:updated_at).map(&:to_i).sum
|
category_updated = Category.where("uploaded_background_id IS NOT NULL").pluck(:updated_at).map(&:to_i).sum
|
||||||
|
fonts = "#{SiteSetting.base_font}-#{SiteSetting.heading_font}"
|
||||||
|
|
||||||
if cs || category_updated > 0
|
if cs || category_updated > 0
|
||||||
theme_color_defs = theme&.resolve_baked_field(:common, :color_definitions)
|
theme_color_defs = theme&.resolve_baked_field(:common, :color_definitions)
|
||||||
Digest::SHA1.hexdigest "#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.last_file_updated}-#{category_updated}-#{SiteSetting.base_font}"
|
Digest::SHA1.hexdigest "#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.last_file_updated}-#{category_updated}-#{fonts}"
|
||||||
else
|
else
|
||||||
digest_string = "defaults-#{Stylesheet::Manager.last_file_updated}-#{SiteSetting.base_font}"
|
digest_string = "defaults-#{Stylesheet::Manager.last_file_updated}-#{fonts}"
|
||||||
|
|
||||||
if cdn_url = GlobalSetting.cdn_url
|
if cdn_url = GlobalSetting.cdn_url
|
||||||
digest_string = "#{digest_string}-#{cdn_url}"
|
digest_string = "#{digest_string}-#{cdn_url}"
|
||||||
|
@ -210,18 +210,22 @@ class Wizard
|
|||||||
end
|
end
|
||||||
|
|
||||||
@wizard.append_step('fonts') do |step|
|
@wizard.append_step('fonts') do |step|
|
||||||
field = step.add_field(
|
body_font = step.add_field(id: 'body_font', type: 'dropdown', value: SiteSetting.base_font)
|
||||||
id: 'font_previews',
|
heading_font = step.add_field(id: 'heading_font', type: 'dropdown', value: SiteSetting.heading_font)
|
||||||
type: 'component',
|
|
||||||
value: SiteSetting.base_font
|
|
||||||
)
|
|
||||||
|
|
||||||
DiscourseFonts.fonts.each do |font|
|
DiscourseFonts.fonts.each do |font|
|
||||||
field.add_choice(font[:key], data: { class: font[:key].tr("_", "-"), name: font[:name] })
|
body_font.add_choice(font[:key], label: font[:name])
|
||||||
|
heading_font.add_choice(font[:key], label: font[:name])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
step.add_field(
|
||||||
|
id: 'font_preview',
|
||||||
|
type: 'component'
|
||||||
|
)
|
||||||
|
|
||||||
step.on_update do |updater|
|
step.on_update do |updater|
|
||||||
updater.update_setting(:base_font, updater.fields[:font_previews])
|
updater.update_setting(:base_font, updater.fields[:body_font])
|
||||||
|
updater.update_setting(:heading_font, updater.fields[:heading_font])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,8 +40,23 @@ describe Stylesheet::Importer do
|
|||||||
.to include(":root{--font-family: Helvetica, Arial, sans-serif}")
|
.to include(":root{--font-family: Helvetica, Arial, sans-serif}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "includes separate body and heading font declarations" do
|
||||||
|
base_font = DiscourseFonts.fonts[2]
|
||||||
|
heading_font = DiscourseFonts.fonts[3]
|
||||||
|
|
||||||
|
SiteSetting.base_font = base_font[:key]
|
||||||
|
SiteSetting.heading_font = heading_font[:key]
|
||||||
|
|
||||||
|
expect(compile_css("desktop"))
|
||||||
|
.to include(":root{--font-family: #{base_font[:stack]}}")
|
||||||
|
.and include(":root{--heading-font-family: #{heading_font[:stack]}}")
|
||||||
|
end
|
||||||
|
|
||||||
it "includes all fonts in wizard" do
|
it "includes all fonts in wizard" do
|
||||||
expect(compile_css("wizard").scan(/\.font-/).count)
|
expect(compile_css("wizard").scan(/\.body-font-/).count)
|
||||||
|
.to eq(DiscourseFonts.fonts.count)
|
||||||
|
|
||||||
|
expect(compile_css("wizard").scan(/\.heading-font-/).count)
|
||||||
.to eq(DiscourseFonts.fonts.count)
|
.to eq(DiscourseFonts.fonts.count)
|
||||||
|
|
||||||
expect(compile_css("wizard").scan(/@font-face/).count)
|
expect(compile_css("wizard").scan(/@font-face/).count)
|
||||||
|
@ -189,13 +189,18 @@ describe Stylesheet::Manager do
|
|||||||
expect(digest1).to_not eq(digest2)
|
expect(digest1).to_not eq(digest2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates digest when setting base font" do
|
it "updates digest when setting fonts" do
|
||||||
manager = Stylesheet::Manager.new(:desktop_theme, theme.id)
|
manager = Stylesheet::Manager.new(:desktop_theme, theme.id)
|
||||||
digest1 = manager.color_scheme_digest
|
digest1 = manager.color_scheme_digest
|
||||||
SiteSetting.base_font = "nunito"
|
SiteSetting.base_font = DiscourseFonts.fonts[2][:key]
|
||||||
digest2 = manager.color_scheme_digest
|
digest2 = manager.color_scheme_digest
|
||||||
|
|
||||||
expect(digest1).to_not eq(digest2)
|
expect(digest1).to_not eq(digest2)
|
||||||
|
|
||||||
|
SiteSetting.heading_font = DiscourseFonts.fonts[4][:key]
|
||||||
|
digest3 = manager.color_scheme_digest
|
||||||
|
|
||||||
|
expect(digest3).to_not eq(digest2)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -168,12 +168,13 @@ describe Wizard::StepUpdater do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context "fonts step" do
|
context "fonts step" do
|
||||||
it "updates the font" do
|
it "updates fonts" do
|
||||||
updater = wizard.create_updater('fonts', font_previews: 'open_sans')
|
updater = wizard.create_updater('fonts', body_font: 'open_sans', heading_font: 'oswald')
|
||||||
updater.update
|
updater.update
|
||||||
expect(updater.success?).to eq(true)
|
expect(updater.success?).to eq(true)
|
||||||
expect(wizard.completed_steps?('fonts')).to eq(true)
|
expect(wizard.completed_steps?('fonts')).to eq(true)
|
||||||
expect(SiteSetting.base_font).to eq('open_sans')
|
expect(SiteSetting.base_font).to eq('open_sans')
|
||||||
|
expect(SiteSetting.heading_font).to eq('oswald')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user