Skip to content

Commit 2880dfe

Browse files
dark logo: dashboards
normalizeLogo + findLogo = resolveLogo we were not able to handle the case where document overrides only dark or light logo under the previous structure, because it needs to look at size order also fold in brand and document.logo undefined cases
1 parent 30de122 commit 2880dfe

File tree

7 files changed

+107
-126
lines changed

7 files changed

+107
-126
lines changed

src/core/brand/brand.ts

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -282,21 +282,40 @@ export const getFavicon = (brand: Brand): string | undefined => {
282282
return logoInfo.path;
283283
};
284284

285-
export async function normalizeLogoSpec(
285+
export function resolveLogo(
286286
brand: LightDarkBrand | undefined,
287-
spec: LogoLightDarkSpecifier,
288-
): Promise<NormalizedLogoLightDarkSpecifier> {
289-
const resolveLogo = (mode: "light" | "dark", name: string) => {
287+
spec: LogoLightDarkSpecifier | undefined,
288+
order: BrandNamedLogo[],
289+
): NormalizedLogoLightDarkSpecifier | undefined {
290+
const resolveBrandLogo = (
291+
mode: "light" | "dark",
292+
name: string,
293+
): LogoOptions => {
290294
const logo = brand?.[mode]?.processedData?.logo;
291295
return logo &&
292-
((Zod.BrandNamedLogo.options.includes(name as BrandNamedLogo) &&
293-
logo[name as BrandNamedLogo]) || logo.images[name]);
296+
((Zod.BrandNamedLogo.options.includes(name as BrandNamedLogo) &&
297+
logo[name as BrandNamedLogo]) || logo.images[name]) ||
298+
{ path: name };
294299
};
300+
function findLogo(
301+
mode: "light" | "dark",
302+
order: BrandNamedLogo[],
303+
): LogoOptions | undefined {
304+
if (brand?.[mode]) {
305+
for (const size of order) {
306+
const logo = brand[mode].processedData.logo[size];
307+
if (logo) {
308+
return logo;
309+
}
310+
}
311+
}
312+
return undefined;
313+
}
295314
const resolveLogoOptions = (
296315
mode: "light" | "dark",
297316
logo: LogoOptions,
298317
): LogoOptions => {
299-
const logo2 = resolveLogo(mode, logo.path);
318+
const logo2 = resolveBrandLogo(mode, logo.path);
300319
if (logo2) {
301320
const { path: _, ...rest } = logo;
302321
return {
@@ -306,10 +325,16 @@ export async function normalizeLogoSpec(
306325
}
307326
return logo;
308327
};
328+
if (!spec) {
329+
return {
330+
light: findLogo("light", order),
331+
dark: findLogo("dark", order),
332+
};
333+
}
309334
if (typeof spec === "string") {
310335
return {
311-
light: resolveLogo("light", spec) || { path: spec },
312-
dark: resolveLogo("light", spec) || { path: spec },
336+
light: resolveBrandLogo("light", spec),
337+
dark: resolveBrandLogo("light", spec),
313338
};
314339
}
315340
if ("path" in spec) {
@@ -319,19 +344,19 @@ export async function normalizeLogoSpec(
319344
};
320345
}
321346
let light, dark;
322-
if (spec.light) {
323-
if (typeof spec.light === "string") {
324-
light = resolveLogo("light", spec.light) || { path: spec.light };
325-
} else {
326-
light = resolveLogoOptions("light", spec.light);
327-
}
347+
if (!spec.light) {
348+
light = findLogo("light", order);
349+
} else if (typeof spec.light === "string") {
350+
light = resolveBrandLogo("light", spec.light);
351+
} else {
352+
light = resolveLogoOptions("light", spec.light);
328353
}
329-
if (spec.dark) {
330-
if (typeof spec.dark === "string") {
331-
dark = resolveLogo("dark", spec.dark) || { path: spec.dark };
332-
} else {
333-
dark = resolveLogoOptions("dark", spec.dark);
334-
}
354+
if (!spec.dark) {
355+
dark = findLogo("dark", order);
356+
} else if (typeof spec.dark === "string") {
357+
dark = resolveBrandLogo("dark", spec.dark);
358+
} else {
359+
dark = resolveLogoOptions("dark", spec.dark);
335360
}
336361
return {
337362
light,

src/format/dashboard/format-dashboard.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
kFilterParams,
1414
kIncludeAfterBody,
1515
kIpynbShellInteractivity,
16+
kLogo,
1617
kPlotlyConnected,
1718
kTemplate,
1819
kTheme,
@@ -27,12 +28,13 @@ import {
2728
kSassBundles,
2829
Metadata,
2930
} from "../../config/types.ts";
31+
import { LogoLightDarkSpecifier } from "../../resources/types/zod/schema-types.ts";
3032
import { PandocFlags } from "../../config/types.ts";
3133
import { mergeConfigs } from "../../core/config.ts";
3234
import { Document, Element } from "../../core/deno-dom.ts";
3335
import { InternalError } from "../../core/lib/error.ts";
3436
import { formatResourcePath } from "../../core/resources.ts";
35-
import { ProjectContext } from "../../project/types.ts";
37+
import { kLogoAlt, ProjectContext } from "../../project/types.ts";
3638
import { registerWriterFormatHandler } from "../format-handlers.ts";
3739
import { kPageLayout, kPageLayoutCustom } from "../html/format-html-shared.ts";
3840
import { htmlFormat } from "../html/format-html.ts";
@@ -64,6 +66,7 @@ import { processToolbars } from "./format-dashboard-toolbar.ts";
6466
import { processDatatables } from "./format-dashboard-tables.ts";
6567
import { assert } from "testing/asserts";
6668
import { brandBootstrapSassBundles } from "../../core/sass/brand.ts";
69+
import { resolveLogo } from "../../core/brand/brand.ts";
6770

6871
const kDashboardClz = "quarto-dashboard";
6972

@@ -119,6 +122,21 @@ export function dashboardFormat() {
119122
}
120123
}
121124

125+
const brand = await project.resolveBrand(input);
126+
let logoSpec = format.metadata[kLogo] as LogoLightDarkSpecifier;
127+
if (typeof logoSpec === "string" && format.metadata[kLogoAlt]) {
128+
logoSpec = {
129+
path: logoSpec,
130+
alt: format.metadata[kLogoAlt] as string,
131+
};
132+
}
133+
format.metadata[kLogo] = resolveLogo(brand, logoSpec, [
134+
"small",
135+
"medium",
136+
"large",
137+
]);
138+
console.log("dash logo", format.metadata[kLogo]);
139+
122140
const extras: FormatExtras = await baseHtmlFormat.formatExtras(
123141
input,
124142
markdown,

src/project/types/website/website-navigation.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ async function navbarEjsData(
12491249
? searchOpts.type
12501250
: false,
12511251
background: navbar.background || "primary",
1252-
logo: navbar.logo,
1252+
logo: ld.cloneDeep(navbar.logo),
12531253
[kLogoAlt]: navbar[kLogoAlt],
12541254
[kLogoHref]: navbar[kLogoHref],
12551255
collapse,
@@ -1259,9 +1259,8 @@ async function navbarEjsData(
12591259
pinned: navbar.pinned !== undefined ? !!navbar.pinned : false,
12601260
};
12611261
if (data.logo) {
1262-
console.log("navbar logo", navbar.logo);
12631262
// navbar logo has been normalized
1264-
const navbarLogo = navbar.logo as NormalizedLogoLightDarkSpecifier;
1263+
const navbarLogo = data.logo as NormalizedLogoLightDarkSpecifier;
12651264
if (navbarLogo.light) {
12661265
navbarLogo.light.path = resolveLogo(navbarLogo.light.path)!;
12671266
}

src/project/types/website/website-shared.ts

Lines changed: 31 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ import { Format, FormatExtras } from "../../../config/types.ts";
4747
import { kPageTitle, kTitle, kTitlePrefix } from "../../../config/constants.ts";
4848
import { md5HashAsync } from "../../../core/hash.ts";
4949
export { type NavigationFooter } from "../../types.ts";
50-
import { projectResolveBrand } from "../../project-shared.ts";
51-
import { normalizeLogoSpec } from "../../../core/brand/brand.ts";
50+
import { resolveLogo } from "../../../core/brand/brand.ts";
5251

5352
export interface Navigation {
5453
navbar?: Navbar;
@@ -128,17 +127,21 @@ export async function websiteNavigationConfig(project: ProjectContext) {
128127
} else if (typeof navbar !== "object") {
129128
navbar = undefined;
130129
}
131-
if (navbar && navbar.logo) {
132-
let logo = navbar.logo;
130+
131+
// note no document-level customization of brand logo #11309
132+
const projectBrand = await project.resolveBrand();
133+
if (navbar) {
134+
let navLogo = navbar.logo;
133135
if (navbar[kLogoAlt]) {
134-
if (typeof logo === "string") {
135-
logo = { path: logo, alt: navbar[kLogoAlt] };
136+
if (typeof navLogo === "string") {
137+
navLogo = { path: navLogo, alt: navbar[kLogoAlt] };
136138
}
137139
}
138-
139-
// note no document-level customization of brand logo #11309
140-
const brand = await projectResolveBrand(project);
141-
navbar.logo = await normalizeLogoSpec(brand, logo);
140+
navbar.logo = resolveLogo(projectBrand, navLogo, [
141+
"small",
142+
"medium",
143+
"large",
144+
]);
142145
}
143146
// read sidebar
144147
const sidebar = websiteConfig(kSiteSidebar, project.config);
@@ -163,37 +166,39 @@ export async function websiteNavigationConfig(project: ProjectContext) {
163166
sidebars[0].tools = [];
164167
}
165168

166-
if (sidebars[0].logo) {
167-
let logo = sidebars[0].logo;
169+
let sideLogo = sidebars[0].logo;
170+
if (sideLogo) {
168171
if (sidebars[0][kLogoAlt]) {
169172
const alt = sidebars[0][kLogoAlt];
170-
if (typeof logo === "string") {
171-
logo = { path: logo, alt };
173+
if (typeof sideLogo === "string") {
174+
sideLogo = { path: sideLogo, alt };
172175
}
173176
// possible but absurd
174-
// else if ("path" in logo) {
175-
// logo = { ...logo, alt };
177+
// else if ("path" in sideLogo) {
178+
// sideLogo = { ...sideLogo, alt };
176179
// } else {
177-
// logo = {
178-
// light: !logo.light ? undefined : typeof logo.light === "string"
180+
// sideLogo = {
181+
// light: !sideLogo.light ? undefined : typeof sideLogo.light === "string"
179182
// ? {
180-
// path: logo.light,
183+
// path: sideLogo.light,
181184
// alt,
182185
// }
183-
// : { ...logo.light, alt },
184-
// dark: !logo.dark ? undefined : typeof logo.dark === "string"
186+
// : { ...sideLogo.light, alt },
187+
// dark: !sideLogo.dark ? undefined : typeof sideLogo.dark === "string"
185188
// ? {
186-
// path: logo.dark,
189+
// path: sideLogo.dark,
187190
// alt,
188191
// }
189-
// : { ...logo.dark, alt },
192+
// : { ...sideLogo.dark, alt },
190193
// };
191194
// }
192195
}
193-
// note no document-level customization of brand logo #11309
194-
const brand = await projectResolveBrand(project);
195-
sidebars[0].logo = await normalizeLogoSpec(brand, logo);
196196
}
197+
sidebars[0].logo = resolveLogo(projectBrand, sideLogo, [
198+
"medium",
199+
"small",
200+
"large",
201+
]);
197202

198203
// convert contents: auto into items
199204
for (const sb of sidebars) {
@@ -211,47 +216,6 @@ export async function websiteNavigationConfig(project: ProjectContext) {
211216
}
212217
}
213218

214-
const projectBrand = await project.resolveBrand();
215-
if (
216-
projectBrand?.light?.processedData.logo && sidebars?.[0]
217-
) {
218-
if (sidebars[0].logo === undefined) {
219-
const light = projectBrand.light.processedData.logo.medium ??
220-
projectBrand.light.processedData.logo.small ??
221-
projectBrand.light.processedData.logo.large;
222-
const dark = projectBrand.dark && (
223-
projectBrand.dark.processedData.logo.medium ??
224-
projectBrand.dark.processedData.logo.small ??
225-
projectBrand.dark.processedData.logo.large
226-
);
227-
if (light || dark) {
228-
sidebars[0].logo = {
229-
light,
230-
dark,
231-
};
232-
}
233-
}
234-
}
235-
236-
if (
237-
projectBrand?.light?.processedData.logo && navbar
238-
) {
239-
if (navbar.logo === undefined) {
240-
const light = projectBrand.light.processedData.logo.small ??
241-
projectBrand.light.processedData.logo.medium ??
242-
projectBrand.light.processedData.logo.large;
243-
const dark = projectBrand.dark?.processedData.logo.small ??
244-
projectBrand.dark?.processedData.logo.medium ??
245-
projectBrand.dark?.processedData.logo.large;
246-
if (light || dark) {
247-
navbar.logo = {
248-
light,
249-
dark,
250-
};
251-
}
252-
}
253-
}
254-
255219
// if there is more than one sidebar then propagate options from the
256220
// first sidebar to the others
257221
if (sidebars && sidebars.length > 1) {

src/resources/filters/quarto-post/dashboard.lua

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -738,37 +738,10 @@ function render_dashboard()
738738
end
739739
end
740740
}, {
741-
-- todo: dark mode
742741
Meta = function(meta)
743-
local logo = meta.logo
744-
local resolved
745-
if logo then
746-
if pandoc.utils.type(logo) == 'Inlines' then
747-
resolved = _quarto.modules.brand.get_logo('light', logo[1].text)
748-
elseif type(logo) == 'table' then
749-
local brandLogo = _quarto.modules.brand.get_logo('light', logo.path[1].text)
750-
if brandLogo then
751-
resolved = {
752-
path = brandLogo.path,
753-
alt = logo.alt or brandLogo.alt
754-
}
755-
else
756-
resolved = {
757-
path = logo.path,
758-
alt = logo.alt
759-
}
760-
end
761-
end
762-
else
763-
resolved = _quarto.modules.brand.get_logo('light', 'small')
764-
or _quarto.modules.brand.get_logo('light', 'medium')
765-
or _quarto.modules.brand.get_logo('light', 'large')
766-
end
767-
if resolved then
768-
meta.logo = resolved.path
769-
meta['logo-alt'] = resolved.alt
770-
end
771-
742+
-- forward the fully-resolved {light,dark} logo
743+
-- calculated in format-dashboard.ts
744+
meta.logo = param('logo')
772745
return meta
773746
end
774747
}

src/resources/formats/dashboard/_nav-container.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

2-
<div class="navbar-brand-container">
3-
$if(logo)$ <a href="#"><img src="$logo$" alt="$logo-alt$" class="navbar-logo d-inline-block"></a>$endif$
2+
<div class="navbar-brand-container">
3+
$if(logo.light)$ <a href="#"><img src="$logo.light.path$" alt="$logo.light.alt$" class="navbar-logo light-content d-inline-block"></a>$endif$
4+
$if(logo.dark)$ <a href="#"><img src="$logo.dark.path$" alt="$logo.dark.alt$" class="navbar-logo dark-content d-inline-block"></a>$endif$
45
<div class="navbar-title">
56
$if(title)$<div class="navbar-title-text"><a href="#">$title$</a></div>$endif$
67
$if(subtitle)$<div class="navbar-subtitle">$subtitle$</div>$endif$

src/resources/schema/document-dashboard.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
- name: logo
22
tags:
33
formats: [dashboard]
4-
schema: path
5-
description: "Logo image (placed on the left side of the navigation bar)"
4+
schema:
5+
ref: logo-light-dark-specifier
6+
description: "Logo image(s) (placed on the left side of the navigation bar)"
67

78
- name: orientation
89
tags:

0 commit comments

Comments
 (0)