From 152a76935b03e27c9d0827fa1fc5da5d1fd68c39 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sun, 25 Sep 2022 22:07:34 +0200 Subject: [PATCH] Generate navigation links --- .../Content/Element+LocalizedMetadata.swift | 20 ++++++++++ Sources/Generator/Content/Element.swift | 9 +++++ .../Content/GenericMetadata+Localized.swift | 28 ++++++++++++-- .../Generator/Generators/PageGenerator.swift | 38 +++++++++++++------ .../Generator/Generators/SiteGenerator.swift | 13 ++++--- 5 files changed, 87 insertions(+), 21 deletions(-) diff --git a/Sources/Generator/Content/Element+LocalizedMetadata.swift b/Sources/Generator/Content/Element+LocalizedMetadata.swift index 63fa09d..fcf6279 100644 --- a/Sources/Generator/Content/Element+LocalizedMetadata.swift +++ b/Sources/Generator/Content/Element+LocalizedMetadata.swift @@ -126,6 +126,20 @@ extension Element { This property is mandatory at root level, and is propagated to child elements. */ let relatedContentText: String + + /** + The text to display on the navigation element pointing to this element as the next page. + + This property is mandatory at root level, and is propagated to child elements. + */ + let navigationTextAsNextPage: String + + /** + The text to display on a navigation element pointing to this element as the previous page. + + This property is mandatory at root level, and is propagated to child elements. + */ + let navigationTextAsPreviousPage: String } } @@ -171,6 +185,10 @@ extension Element.LocalizedMetadata { self.externalUrl = log.unexpected(data.externalUrl, name: "externalUrl", source: source) self.relatedContentText = log .required(data.relatedContentText, name: "relatedContentText", source: source) ?? "" + self.navigationTextAsNextPage = log + .required(data.navigationTextAsNextPage, name: "navigationTextAsNextPage", source: source) ?? "" + self.navigationTextAsPreviousPage = log + .required(data.navigationTextAsPreviousPage, name: "navigationTextAsPreviousPage", source: source) ?? "" guard isComplete else { return nil @@ -207,6 +225,8 @@ extension Element.LocalizedMetadata { self.cornerText = data.cornerText self.externalUrl = data.externalUrl self.relatedContentText = data.relatedContentText ?? parent.relatedContentText + self.navigationTextAsPreviousPage = data.navigationTextAsPreviousPage ?? parent.navigationTextAsPreviousPage + self.navigationTextAsNextPage = data.navigationTextAsNextPage ?? parent.navigationTextAsNextPage guard isComplete else { return nil diff --git a/Sources/Generator/Content/Element.swift b/Sources/Generator/Content/Element.swift index 57a9b1d..2aa4b11 100644 --- a/Sources/Generator/Content/Element.swift +++ b/Sources/Generator/Content/Element.swift @@ -310,6 +310,15 @@ extension Element { elements.filter { $0.state.isShownInOverview } } + var linkedElements: [LinkedElement] { + let items = sortedItems + return items.enumerated().map { i, element in + let previous = i+1 < items.count ? items[i+1] : nil + let next = i > 0 ? items[i-1] : nil + return (previous, element, next) + } + } + /** The url of the top-level section of the element. */ diff --git a/Sources/Generator/Content/GenericMetadata+Localized.swift b/Sources/Generator/Content/GenericMetadata+Localized.swift index 7ea6aac..a50d1f0 100644 --- a/Sources/Generator/Content/GenericMetadata+Localized.swift +++ b/Sources/Generator/Content/GenericMetadata+Localized.swift @@ -119,6 +119,20 @@ extension GenericMetadata { This property is mandatory at root level, and is propagated to child elements. */ let relatedContentText: String? + + /** + The text to display on a navigation element pointing to this element as the previous page. + + This property is mandatory at root level, and is propagated to child elements. + */ + let navigationTextAsPreviousPage: String? + + /** + The text to display on the navigation element pointing to this element as the next page. + + This property is mandatory at root level, and is propagated to child elements. + */ + let navigationTextAsNextPage: String? } } @@ -142,6 +156,8 @@ extension GenericMetadata.LocalizedMetadata: Codable { .cornerText, .externalUrl, .relatedContentText, + .navigationTextAsPreviousPage, + .navigationTextAsNextPage, ] } @@ -172,7 +188,9 @@ extension GenericMetadata.LocalizedMetadata { thumbnailSuffix: nil, cornerText: nil, externalUrl: nil, - relatedContentText: nil) + relatedContentText: nil, + navigationTextAsPreviousPage: nil, + navigationTextAsNextPage: nil) } /** @@ -194,7 +212,9 @@ extension GenericMetadata.LocalizedMetadata { thumbnailSuffix: nil, cornerText: nil, externalUrl: nil, - relatedContentText: "") + relatedContentText: "", + navigationTextAsPreviousPage: "", + navigationTextAsNextPage: "") } static var full: GenericMetadata.LocalizedMetadata { @@ -213,6 +233,8 @@ extension GenericMetadata.LocalizedMetadata { thumbnailSuffix: "", cornerText: "", externalUrl: "", - relatedContentText: "") + relatedContentText: "", + navigationTextAsPreviousPage: "", + navigationTextAsNextPage: "") } } diff --git a/Sources/Generator/Generators/PageGenerator.swift b/Sources/Generator/Generators/PageGenerator.swift index d935e3c..48278f5 100644 --- a/Sources/Generator/Generators/PageGenerator.swift +++ b/Sources/Generator/Generators/PageGenerator.swift @@ -3,20 +3,13 @@ import Ink struct PageGenerator { - struct NavigationLink { - - let link: String - - let text: String - } - private let factory: LocalizedSiteTemplate init(factory: LocalizedSiteTemplate) { self.factory = factory } - func generate(page: Element, language: String, nextPage: NavigationLink?, previousPage: NavigationLink?) { + func generate(page: Element, language: String, previousPage: Element?, nextPage: Element?) { guard !page.isExternalPage else { return } @@ -35,10 +28,10 @@ struct PageGenerator { content[.header] = makeHeader(page: page, metadata: metadata, language: language) content[.content] = pageContent - content[.previousPageLinkText] = previousPage.unwrapped { factory.factory.html.makePrevText($0.text) } - content[.previousPageUrl] = previousPage?.link - content[.nextPageLinkText] = nextPage.unwrapped { factory.factory.html.makeNextText($0.text) } - content[.nextPageUrl] = nextPage?.link + content[.previousPageLinkText] = previousText(for: previousPage, language: language) + content[.previousPageUrl] = navLink(from: page, to: previousPage, language: language) + content[.nextPageLinkText] = nextText(for: nextPage, language: language) + content[.nextPageUrl] = navLink(from: page, to: nextPage, language: language) content[.footer] = page.customFooterContent() if pageIncludesCode { @@ -58,6 +51,27 @@ struct PageGenerator { files.generated(page: path) } + private func navLink(from element: Element, to destination: Element?, language: String) -> String? { + guard let fullPath = destination?.fullPageUrl(for: language) else { + return nil + } + return element.relativePathToOtherSiteElement(file: fullPath) + } + + private func previousText(for element: Element?, language: String) -> String? { + guard let text = element?.localized(for: language).navigationTextAsPreviousPage else { + return nil + } + return factory.factory.html.makePrevText(text) + } + + private func nextText(for element: Element?, language: String) -> String? { + guard let text = element?.localized(for: language).navigationTextAsNextPage else { + return nil + } + return factory.factory.html.makeNextText(text) + } + private func makeContent(page: Element, metadata: Element.LocalizedMetadata, language: String, path: String) -> (content: String, includesCode: Bool, isEmpty: Bool) { let create = configuration.createMdFilesIfMissing if let raw = files.contentOfOptionalFile(atPath: path, source: page.path, createEmptyFileIfMissing: create)? diff --git a/Sources/Generator/Generators/SiteGenerator.swift b/Sources/Generator/Generators/SiteGenerator.swift index 7caba8a..5ce2e36 100644 --- a/Sources/Generator/Generators/SiteGenerator.swift +++ b/Sources/Generator/Generators/SiteGenerator.swift @@ -1,5 +1,7 @@ import Foundation +typealias LinkedElement = (previous: Element?, element: Element, next: Element?) + struct SiteGenerator { let templates: TemplateFactory @@ -26,22 +28,21 @@ struct SiteGenerator { let overviewGenerator = OverviewPageGenerator(factory: template) let pageGenerator = PageGenerator(factory: template) - var elementsToProcess: [Element] = [site] - while let element = elementsToProcess.popLast() { + var elementsToProcess: [LinkedElement] = [(nil, site, nil)] + while let (previous, element, next) = elementsToProcess.popLast() { // Move recursively down to all pages - elementsToProcess.append(contentsOf: element.elements) + elementsToProcess.append(contentsOf: element.linkedElements) processAllFiles(for: element) if !element.elements.isEmpty { overviewGenerator.generate(section: element, language: language) } else { - #warning("Determine previous and next pages (with relative links)") pageGenerator.generate( page: element, language: language, - nextPage: nil, - previousPage: nil) + previousPage: previous, + nextPage: next) } } }