diff --git a/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.js b/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.js
new file mode 100644
index 0000000000000..cec2dbef6365c
--- /dev/null
+++ b/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.js
@@ -0,0 +1,81 @@
+import { Component, onMounted, onWillStart, useSubEnv } from "@odoo/owl";
+import { _t } from "@web/core/l10n/translation";
+import { useService } from "@web/core/utils/hooks";
+import {
+ basicContainerBuilderComponentProps,
+ useVisibilityObserver,
+ useApplyVisibility,
+ useSelectableComponent,
+} from "./utils";
+import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
+import { BuilderSelect } from "./builder_select";
+import { BuilderSelectItem } from "./builder_select_item";
+
+export class BuilderFontFamilyPicker extends Component {
+ static template = "html_builder.website.BuilderFontFamilyPicker";
+ static props = {
+ ...basicContainerBuilderComponentProps,
+ id: { type: String, optional: true },
+ valueParamName: String,
+ };
+ static components = {
+ BuilderSelect,
+ BuilderSelectItem,
+ };
+
+ setup() {
+ this.dialog = useService("dialog");
+ this.orm = useService("orm");
+ useVisibilityObserver("content", useApplyVisibility("root"));
+ useSelectableComponent(this.props.id, {
+ /*
+ onItemChange(item) {
+ currentLabel = item.getLabel();
+ updateCurrentLabel();
+ },
+ */
+ });
+ onMounted(() => {});
+ useSubEnv({
+ /*
+ onSelectItem: () => {
+ this.dropdown.close();
+ },
+ */
+ });
+ this.fonts = [];
+ onWillStart(async () => {
+ const fontsData = await this.env.editor.shared.websiteFont.getFontsData();
+ this.fonts = fontsData._fonts;
+ });
+ }
+ getAllClasses() {
+ return "TODO";
+ }
+ forwardProps(fontValue) {
+ const result = Object.assign({}, this.props, {
+ [this.props.valueParamName]: fontValue.fontFamily,
+ });
+ delete result.selectMethod;
+ delete result.valueParamName;
+ return result;
+ }
+ async onAddFontClick() {
+ await this.env.editor.shared.websiteFont.addFont();
+ }
+ async onDeleteFontClick(font) {
+ const save = await new Promise((resolve) => {
+ this.env.services.dialog.add(ConfirmationDialog, {
+ body: _t(
+ "Deleting a font requires a reload of the page. This will save all your changes and reload the page, are you sure you want to proceed?"
+ ),
+ confirm: () => resolve(true),
+ cancel: () => resolve(false),
+ });
+ });
+ if (!save) {
+ return;
+ }
+ await this.env.editor.shared.websiteFont.deleteFont(font);
+ }
+}
diff --git a/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.xml b/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.xml
new file mode 100644
index 0000000000000..cd9c2201d5d6a
--- /dev/null
+++ b/addons/html_builder/static/src/core/building_blocks/builder_fontfamilypicker.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Delete this font
+
+
+
+
+
+
+
+ Add a Custom Font
+
+
+
+
+
diff --git a/addons/html_builder/static/src/core/building_blocks/builder_select.js b/addons/html_builder/static/src/core/building_blocks/builder_select.js
index 57dae671c7d95..b220d6e4a6529 100644
--- a/addons/html_builder/static/src/core/building_blocks/builder_select.js
+++ b/addons/html_builder/static/src/core/building_blocks/builder_select.js
@@ -15,6 +15,7 @@ export class BuilderSelect extends Component {
static props = {
...basicContainerBuilderComponentProps,
id: { type: String, optional: true },
+ className: { type: String, optional: true },
slots: Object,
};
static components = {
diff --git a/addons/html_builder/static/src/core/building_blocks/builder_select.xml b/addons/html_builder/static/src/core/building_blocks/builder_select.xml
index dd306b4d83d38..c39de3728490c 100644
--- a/addons/html_builder/static/src/core/building_blocks/builder_select.xml
+++ b/addons/html_builder/static/src/core/building_blocks/builder_select.xml
@@ -6,7 +6,7 @@
-
+
diff --git a/addons/html_builder/static/src/core/building_blocks/builder_select_item.js b/addons/html_builder/static/src/core/building_blocks/builder_select_item.js
index 11cee1b6b0e70..50b31239bc14f 100644
--- a/addons/html_builder/static/src/core/building_blocks/builder_select_item.js
+++ b/addons/html_builder/static/src/core/building_blocks/builder_select_item.js
@@ -9,6 +9,7 @@ export class BuilderSelectItem extends Component {
...clickableBuilderComponentProps,
id: { type: String, optional: true },
title: { type: String, optional: true },
+ className: { type: String, optional: true },
slots: { type: Object, optional: true },
};
static components = { BuilderComponent };
diff --git a/addons/html_builder/static/src/core/default_builder_components.js b/addons/html_builder/static/src/core/default_builder_components.js
index f39a24499eff5..818d309fa72a9 100644
--- a/addons/html_builder/static/src/core/default_builder_components.js
+++ b/addons/html_builder/static/src/core/default_builder_components.js
@@ -15,6 +15,8 @@ import { BuilderContext } from "./building_blocks/builder_context";
import { BasicMany2Many } from "./building_blocks/basic_many2many";
import { BuilderMany2Many } from "./building_blocks/builder_many2many";
import { BuilderUrlPicker } from "./building_blocks/builder_urlpicker";
+// TODO Font family picker is actually website-specific
+import { BuilderFontFamilyPicker } from "./building_blocks/builder_fontfamilypicker";
import { ModelMany2Many } from "./building_blocks/model_many2many";
export const defaultBuilderComponents = {
@@ -36,4 +38,5 @@ export const defaultBuilderComponents = {
ModelMany2Many,
BuilderDateTimePicker,
BuilderUrlPicker,
+ BuilderFontFamilyPicker,
};
diff --git a/addons/html_builder/static/src/sidebar/theme_tab.js b/addons/html_builder/static/src/sidebar/theme_tab.js
index 33af2ac684ce1..0fcd233ddef17 100644
--- a/addons/html_builder/static/src/sidebar/theme_tab.js
+++ b/addons/html_builder/static/src/sidebar/theme_tab.js
@@ -16,7 +16,9 @@ export class ThemeTab extends Component {
setup() {
useOptionsSubEnv(() => [this.env.editor.document.body]);
this.isActiveItem = useIsActiveItem();
- this.state = useState({});
+ this.state = useState({
+ fontsData: {},
+ });
this.optionsContainers = this.env.editor.resources["theme_options"];
}
}
diff --git a/addons/html_builder/static/src/sidebar/theme_tab.xml b/addons/html_builder/static/src/sidebar/theme_tab.xml
index 9aede5d10c247..166e56019e69c 100644
--- a/addons/html_builder/static/src/sidebar/theme_tab.xml
+++ b/addons/html_builder/static/src/sidebar/theme_tab.xml
@@ -82,11 +82,6 @@
-
-
@@ -102,10 +97,7 @@
- TODO BuilderFontFamilyPicker
-
+
@@ -139,11 +131,8 @@
-
- TODO BuilderFontFamilyPicker
-
+
+
- TODO BuilderFontFamilyPicker
-
+
- TODO BuilderFontFamilyPicker
-
+
- TODO BuilderFontFamilyPicker
-
+
{
+ el.setAttribute("id", "google_font");
+ },
+ () => [this.inputRef.el]
+ );
+ }
+
+ get dropdownOptions() {
+ return {
+ ...super.dropdownOptions,
+ position: "bottom-fit",
+ };
+ }
+
+ onInput(ev) {
+ super.onInput(ev);
+ if (this.sourcesListRef.el) {
+ this.sourcesListRef.el.scrollTop = 0;
+ }
+ }
+}
+
+export class AddFontDialog extends Component {
+ static template = "html_builder.website.dialog.addFont";
+ static components = { GoogleFontAutoComplete, Dialog };
+ static props = {
+ close: Function,
+ allFonts: Array,
+ googleFonts: Array,
+ googleLocalFonts: Array,
+ uploadedLocalFonts: Array,
+ customize: Function,
+ };
+ state = useState({
+ valid: true,
+ loading: false,
+ googleFontFamily: undefined,
+ googleServe: true,
+ uploadedFontName: undefined,
+ uploadedFonts: [],
+ uploadedFontFaces: undefined,
+ previewText: _t("The quick brown fox jumps over the lazy dog."),
+ });
+ setup() {
+ this.fileInput = useRef("fileInput");
+ this.dialog = useService("dialog");
+ this.orm = useService("orm");
+ }
+
+ async onClickSave() {
+ if (this.state.loading) {
+ return;
+ }
+ this.state.loading = true;
+ const shouldClose = await this.save(this.state);
+ if (shouldClose) {
+ this.props.close();
+ return;
+ }
+ this.state.loading = false;
+ }
+ onClickCancel() {
+ this.props.close();
+ }
+ get getGoogleFontList() {
+ return [
+ {
+ options: async (term) => {
+ if (!this.googleFontList) {
+ await rpc("/website/google_font_metadata").then((data) => {
+ this.googleFontList = data.familyMetadataList.map(
+ (font) => font.family
+ );
+ });
+ }
+ const lowerCaseTerm = term.toLowerCase();
+ const filtered = this.googleFontList.filter((value) =>
+ value.toLowerCase().includes(lowerCaseTerm)
+ );
+ return filtered.map((fontFamilyName) => ({
+ label: fontFamilyName,
+ value: fontFamilyName,
+ }));
+ },
+ },
+ ];
+ }
+ async onGoogleFontSelect(selected) {
+ this.fileInput.el.value = "";
+ this.state.uploadedFonts = [];
+ this.state.uploadedFontName = undefined;
+ this.state.uploadedFontFaces = undefined;
+ try {
+ const fontFamily = selected.value;
+ const result = await fetch(
+ `https://fonts.googleapis.com/css?family=${encodeURIComponent(
+ fontFamily
+ )}:300,300i,400,400i,700,700i`,
+ { method: "HEAD" }
+ );
+ // Google fonts server returns a 400 status code if family is not valid.
+ if (result.ok) {
+ const linkId = `previewFont${fontFamily}`;
+ if (!document.querySelector(`link[id='${linkId}']`)) {
+ const linkEl = document.createElement("link");
+ linkEl.id = linkId;
+ linkEl.setAttribute("href", result.url);
+ linkEl.setAttribute("rel", "stylesheet");
+ linkEl.dataset.fontPreview = true;
+ document.head.appendChild(linkEl);
+ }
+ this.state.googleFontFamily = fontFamily;
+ } else {
+ this.state.googleFontFamily = undefined;
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ async onUploadChange(e) {
+ this.state.googleFontFamily = undefined;
+ const file = this.fileInput.el.files[0];
+ if (!file) {
+ this.state.uploadedFonts = [];
+ this.state.uploadedFontName = undefined;
+ this.state.uploadedFontFaces = undefined;
+ return;
+ }
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const base64 = e.target.result.split(",")[1];
+ rpc("/website/theme_upload_font", {
+ name: file.name,
+ data: base64,
+ }).then((result) => {
+ this.state.uploadedFonts = result;
+ this.updateFontStyle(file.name.substr(0, file.name.lastIndexOf(".")));
+ });
+ };
+ reader.readAsDataURL(file);
+ }
+ /**
+ * Deduces the style of uploaded fonts and creates inline style
+ * elements in the backend iframe's head to make the font-faces
+ * available for preview.
+ *
+ * @param baseFontName
+ */
+ updateFontStyle(baseFontName) {
+ const targetFonts = {};
+ // Add candidate tags to fonts.
+ let shortestNamedFont;
+ for (const font of this.state.uploadedFonts) {
+ if (!shortestNamedFont || font.name.length < shortestNamedFont.name.length) {
+ shortestNamedFont = font;
+ }
+ font.isItalic = /italic/i.test(font.name);
+ font.isLight = /light|300/i.test(font.name);
+ font.isBold = /bold|700/i.test(font.name);
+ font.isRegular = /regular|400/i.test(font.name);
+ font.weight = font.isRegular ? 400 : font.isLight ? 300 : font.isBold ? 700 : undefined;
+ if (font.isItalic && !font.weight) {
+ if (!/00|thin|medium|black|condense|extrude/i.test(font.name)) {
+ font.isRegular = true;
+ font.weight = 400;
+ }
+ }
+ font.style = font.isItalic ? "italic" : "normal";
+ if (font.weight) {
+ targetFonts[`${font.weight}${font.style}`] = font;
+ }
+ }
+ if (!Object.values(targetFonts).filter((font) => font.isRegular).length) {
+ // Keep font with shortest name.
+ shortestNamedFont.weight = 400;
+ shortestNamedFont.style = "normal";
+ targetFonts["400"] = shortestNamedFont;
+ }
+ const fontFaces = [];
+ for (const font of Object.values(targetFonts)) {
+ fontFaces.push(`@font-face{
+ font-family: ${baseFontName};
+ font-style: ${font.style};
+ font-weight: ${font.weight};
+ src:url("${font.url}");
+ }`);
+ }
+ let styleEl = document.head.querySelector(
+ `style[id='WebsiteThemeFontPreview-${baseFontName}']`
+ );
+ if (!styleEl) {
+ styleEl = document.createElement("style");
+ styleEl.id = `WebsiteThemeFontPreview-${baseFontName}`;
+ styleEl.dataset.fontPreview = true;
+ document.head.appendChild(styleEl);
+ }
+ const previewFontFaces = fontFaces.join("");
+ styleEl.textContent = previewFontFaces;
+ this.state.uploadedFontName = baseFontName;
+ this.state.uploadedFontFaces = previewFontFaces;
+ }
+ async save(state) {
+ const uploadedFontName = state.uploadedFontName;
+ const uploadedFontFaces = state.uploadedFontFaces;
+ let font = undefined;
+ if (uploadedFontName && uploadedFontFaces) {
+ const fontExistsLocally = this.props.uploadedLocalFonts.some(
+ (localFont) => localFont.split(":")[0] === `'${uploadedFontName}'`
+ );
+ if (fontExistsLocally) {
+ this.dialog.add(ConfirmationDialog, {
+ title: _t("Font exists"),
+ body: _t(
+ "This uploaded font already exists.\nTo replace an existing font, remove it first."
+ ),
+ });
+ return;
+ }
+ const homonymGoogleFontExists =
+ this.props.googleFonts.some((font) => font === uploadedFontName) ||
+ this.props.googleLocalFonts.some(
+ (font) => font.split(":")[0] === `'${uploadedFontName}'`
+ );
+ if (homonymGoogleFontExists) {
+ this.dialog.add(ConfirmationDialog, {
+ title: _t("Font name already used"),
+ body: _t(
+ "A font with the same name already exists.\nTry renaming the uploaded file."
+ ),
+ });
+ return;
+ }
+ // Create attachment.
+ const [fontCssId] = await this.orm.call("ir.attachment", "create_unique", [
+ [
+ {
+ name: uploadedFontName,
+ description: `CSS font face for ${uploadedFontName}`,
+ datas: btoa(uploadedFontFaces),
+ res_model: "ir.attachment",
+ mimetype: "text/css",
+ public: true,
+ },
+ ],
+ ]);
+ this.props.uploadedLocalFonts.push(`'${uploadedFontName}': ${fontCssId}`);
+ font = uploadedFontName;
+ } else {
+ let isValidFamily = false;
+ font = state.googleFontFamily;
+
+ try {
+ const result = await fetch(
+ "https://fonts.googleapis.com/css?family=" +
+ encodeURIComponent(font) +
+ ":300,300i,400,400i,700,700i",
+ { method: "HEAD" }
+ );
+ // Google fonts server returns a 400 status code if family is not valid.
+ if (result.ok) {
+ isValidFamily = true;
+ }
+ } catch (error) {
+ console.error(error);
+ }
+
+ if (!isValidFamily) {
+ this.dialog.add(ConfirmationDialog, {
+ title: _t("Font access"),
+ body: _t("The selected font cannot be accessed."),
+ });
+ return;
+ }
+
+ const googleFontServe = state.googleServe;
+ const fontName = `'${font}'`;
+ // If the font already exists, it will only be added if
+ // the user chooses to add it locally when it is already
+ // imported from the Google Fonts server.
+ const fontExistsLocally = this.props.googleLocalFonts.some(
+ (localFont) => localFont.split(":")[0] === fontName
+ );
+ const fontExistsOnServer = this.props.allFonts.includes(fontName);
+ const preventFontAddition =
+ fontExistsLocally || (fontExistsOnServer && googleFontServe);
+ if (preventFontAddition) {
+ this.dialog.add(ConfirmationDialog, {
+ title: _t("Font exists"),
+ body: _t(
+ "This font already exists, you can only add it as a local font to replace the server version."
+ ),
+ });
+ return;
+ }
+ if (googleFontServe) {
+ this.props.googleFonts.push(font);
+ } else {
+ this.props.googleLocalFonts.push(`'${font}': ''`);
+ }
+ }
+ this.props.customize({
+ values: { [this.props.variable]: `'${font}'` },
+ googleFonts: this.props.googleFonts,
+ googleLocalFonts: this.props.googleLocalFonts,
+ uploadedLocalFonts: this.props.uploadedLocalFonts,
+ });
+ const styleEl = document.head.querySelector(`[id='WebsiteThemeFontPreview-${font}']`);
+ if (styleEl) {
+ delete styleEl.dataset.fontPreview;
+ }
+ return true;
+ }
+}
+
+export function showAddFontDialog(dialog, fontsData, customize) {
+ dialog.add(
+ AddFontDialog,
+ {
+ allFonts: fontsData.allFonts,
+ googleFonts: fontsData.googleFonts,
+ googleLocalFonts: fontsData.googleLocalFonts,
+ uploadedLocalFonts: fontsData.uploadedLocalFonts,
+ customize,
+ },
+ {
+ onClose: () => {
+ for (const el of document.head.querySelectorAll("[data-font-preview]")) {
+ el.remove();
+ }
+ },
+ }
+ );
+}
diff --git a/addons/html_builder/static/src/website_builder/plugins/font/add_font_dialog.xml b/addons/html_builder/static/src/website_builder/plugins/font/add_font_dialog.xml
new file mode 100644
index 0000000000000..7ce09d80d47aa
--- /dev/null
+++ b/addons/html_builder/static/src/website_builder/plugins/font/add_font_dialog.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Light
+ 300
+
+
+ Regular
+ 400
+
+
+ Bold
+ 700
+
+
+
+
+
+
+
diff --git a/addons/html_builder/static/src/website_builder/plugins/font/font_plugin.js b/addons/html_builder/static/src/website_builder/plugins/font/font_plugin.js
new file mode 100644
index 0000000000000..f89a6f82f75e7
--- /dev/null
+++ b/addons/html_builder/static/src/website_builder/plugins/font/font_plugin.js
@@ -0,0 +1,197 @@
+import { Plugin } from "@html_editor/plugin";
+import { registry } from "@web/core/registry";
+import { _t } from "@web/core/l10n/translation";
+import { Cache } from "@web/core/utils/cache";
+import { loadCSS } from "@web/core/assets";
+import { getCSSVariableValue } from "@html_builder/utils/utils_css";
+import { showAddFontDialog } from "./add_font_dialog";
+
+// TODO Website-specific
+class FontPlugin extends Plugin {
+ static id = "websiteFont";
+ static shared = ["addFont", "deleteFont", "getFontsData"];
+ static dependencies = ["savePlugin", "themeTab"];
+ resources = {
+ // Lists CSS variables that will be reset when a font is deleted if
+ // they refer to that font.
+ fontCssVariables: [
+ "font",
+ "headings-font",
+ "h2-font",
+ "h3-font",
+ "h4-font",
+ "h5-font",
+ "h6-font",
+ "display-1-font",
+ "display-2-font",
+ "display-3-font",
+ "display-4-font",
+ "buttons-font",
+ ],
+ };
+ setup() {
+ this.fontsCache = new Cache(this._fetchFonts.bind(this), JSON.stringify);
+ }
+ destroy() {
+ super.destroy();
+ this.fontsCache.invalidate();
+ }
+ async addFont() {
+ const fontsData = await this.getFontsData();
+ showAddFontDialog(this.services.dialog, fontsData, this.customizeFonts.bind(this));
+ }
+ async customizeFonts({ values = {}, googleFonts, googleLocalFonts, uploadedLocalFonts }) {
+ if (googleFonts.length) {
+ values["google-fonts"] = "('" + googleFonts.join("', '") + "')";
+ } else {
+ values["google-fonts"] = "null";
+ }
+ if (googleLocalFonts.length) {
+ values["google-local-fonts"] = "(" + googleLocalFonts.join(", ") + ")";
+ } else {
+ values["google-local-fonts"] = "null";
+ }
+ if (uploadedLocalFonts.length) {
+ values["uploaded-local-fonts"] = "(" + uploadedLocalFonts.join(", ") + ")";
+ } else {
+ values["uploaded-local-fonts"] = "null";
+ }
+ await this.dependencies.themeTab.makeSCSSCusto(
+ "/website/static/src/scss/options/user_values.scss",
+ values
+ );
+ this.fontsCache.invalidate();
+ // TODO reloadEditor: true
+ await this.dependencies.savePlugin.save(/* not in translation */);
+ }
+ async deleteFont(font) {
+ const { googleFonts, googleLocalFonts, uploadedLocalFonts } = await this.getFontsData();
+ const values = {};
+
+ // Remove Google font
+ const fontIndex = font.indexForType;
+ const localFont = font.type;
+ let fontName;
+ if (localFont === "uploaded") {
+ const font = uploadedLocalFonts[fontIndex].split(":");
+ // Remove double quotes
+ fontName = font[0].substring(1, font[0].length - 1);
+ values["delete-font-attachment-id"] = font[1];
+ uploadedLocalFonts.splice(fontIndex, 1);
+ } else if (localFont === "google") {
+ const googleFont = googleLocalFonts[fontIndex].split(":");
+ // Remove double quotes
+ fontName = googleFont[0].substring(1, googleFont[0].length - 1);
+ values["delete-font-attachment-id"] = googleFont[1];
+ googleLocalFonts.splice(fontIndex, 1);
+ } else {
+ fontName = googleFonts[fontIndex];
+ googleFonts.splice(fontIndex, 1);
+ }
+
+ // Adapt font variable indexes to the removal
+ const style = window.getComputedStyle(this.document.documentElement);
+ this.getResource("fontCssVariables").forEach((variable) => {
+ const value = getCSSVariableValue(variable, style);
+ if (value.substring(1, value.length - 1) === fontName) {
+ // If an element is using the google font being removed, reset
+ // it to the theme default.
+ values[variable] = "null";
+ }
+ });
+ await this.customizeFonts({
+ values: values,
+ googleFonts: googleFonts,
+ googleLocalFonts: googleLocalFonts,
+ uploadedLocalFonts: uploadedLocalFonts,
+ });
+ }
+ async getFontsData() {
+ return this.fontsCache.read({});
+ }
+ async _fetchFonts() {
+ const style = window.getComputedStyle(this.document.documentElement);
+ const nbFonts = parseInt(getCSSVariableValue("number-of-fonts", style));
+ // User fonts served by google server.
+ const googleFontsProperty = getCSSVariableValue("google-fonts", style);
+ let googleFonts = googleFontsProperty ? googleFontsProperty.split(/\s*,\s*/g) : [];
+ googleFonts = googleFonts.map((font) => font.substring(1, font.length - 1)); // Unquote
+ // Local user fonts.
+ const googleLocalFontsProperty = getCSSVariableValue("google-local-fonts", style);
+ const googleLocalFonts = googleLocalFontsProperty
+ ? googleLocalFontsProperty.slice(1, -1).split(/\s*,\s*/g)
+ : [];
+ const uploadedLocalFontsProperty = getCSSVariableValue("uploaded-local-fonts", style);
+ const uploadedLocalFonts = uploadedLocalFontsProperty
+ ? uploadedLocalFontsProperty.slice(1, -1).split(/\s*,\s*/g)
+ : [];
+ // If a same font exists both remotely and locally, we remove the remote
+ // font to prioritize the local font. The remote one will never be
+ // displayed or loaded as long as the local one exists.
+ googleFonts = googleFonts.filter((font) => {
+ const localFonts = googleLocalFonts.map((localFont) => localFont.split(":")[0]);
+ return localFonts.indexOf(`'${font}'`) === -1;
+ });
+ const allFonts = [];
+
+ const fontsToLoad = [];
+ for (const font of googleFonts) {
+ const fontURL = `https://fonts.googleapis.com/css?family=${encodeURIComponent(
+ font
+ ).replace(/%20/g, "+")}`;
+ fontsToLoad.push(fontURL);
+ }
+ for (const font of googleLocalFonts) {
+ const attachmentId = font.split(/\s*:\s*/)[1];
+ const fontURL = `/web/content/${encodeURIComponent(attachmentId)}`;
+ fontsToLoad.push(fontURL);
+ }
+ const proms = fontsToLoad.map(async (fontURL) => loadCSS(fontURL));
+
+ const _fonts = [];
+ const themeFontsNb =
+ nbFonts - (googleLocalFonts.length + googleFonts.length + uploadedLocalFonts.length);
+ const localFontsOffset = nbFonts - googleLocalFonts.length - uploadedLocalFonts.length;
+ const uploadedFontsOffset = nbFonts - uploadedLocalFonts.length;
+
+ for (let fontNb = 0; fontNb < nbFonts; fontNb++) {
+ const realFontNb = fontNb + 1;
+ const fontKey = getCSSVariableValue(`font-number-${realFontNb}`, style);
+ allFonts.push(fontKey);
+ let fontName = fontKey.slice(1, -1); // Unquote
+ let fontFamily = fontName;
+ const isSystemFonts = fontName === "SYSTEM_FONTS";
+ if (isSystemFonts) {
+ fontName = _t("System Fonts");
+ fontFamily = "var(--o-system-fonts)";
+ }
+
+ let type = "cloud";
+ let indexForType = fontNb - themeFontsNb;
+ if (fontNb >= localFontsOffset) {
+ if (fontNb < uploadedFontsOffset) {
+ type = "google";
+ indexForType = fontNb - localFontsOffset;
+ } else {
+ type = "uploaded";
+ indexForType = fontNb - uploadedFontsOffset;
+ }
+ }
+ _fonts.push({
+ type,
+ indexForType,
+ fontFamily,
+ string: fontName,
+ });
+ }
+ await Promise.all(proms);
+ return {
+ allFonts,
+ googleFonts,
+ googleLocalFonts,
+ uploadedLocalFonts,
+ _fonts,
+ };
+ }
+}
+registry.category("website-plugins").add(FontPlugin.id, FontPlugin);