diff --git a/README.md b/README.md index 314df5af..82c171b6 100644 --- a/README.md +++ b/README.md @@ -254,15 +254,16 @@ and limitations for this exporter. The look and feel of the generated site can be customized with several additional view properties in the C4 architecture model: -| Property name | Description | Default | Example | -|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------------------| -| `generatr.style.colors.primary` | Primary site color, used for header bar background and active menu background. | `#333333` | `#485fc7` | -| `generatr.style.colors.secondary` | Secondary site color, used for font color in header bar and for active menu. | `#cccccc` | `#ffffff` | -| `generatr.style.faviconPath` | Site logo location relative to the configured `assets` folder. When configured, the logo image will be place on the left side in the header bar. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/favicon.ico` | -| `generatr.style.logoPath` | Site favicon location relative to the configured `assets` folder. When configured, the favicon will be set for all generated pages. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/logo.png` | -| `generatr.search.language` | Indexing/stemming language for the search index. See [Lunr language support](https://github.com/olivernn/lunr-languages) | `en` | `nl` | -| `generatr.markdown.flexmark.extensions` | Additional extensions to the markdown generator to add new markdown capabilities. [More Details](https://avisi-cloud.github.io/structurizr-site-generatr/main/extended-markdown-features/) | Tables | `Tables,Admonition` | -| `generatr.svglink.target` | Specifies the link target for element links in the exported svg | `_top` | `_self` | +| Property name | Description | Default | Example | +|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------------------------------------------| +| `generatr.style.colors.primary` | Primary site color, used for header bar background and active menu background. | `#333333` | `#485fc7` | +| `generatr.style.colors.secondary` | Secondary site color, used for font color in header bar and for active menu. | `#cccccc` | `#ffffff` | +| `generatr.style.faviconPath` | Site logo location relative to the configured `assets` folder. When configured, the logo image will be place on the left side in the header bar. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/favicon.ico` | +| `generatr.style.logoPath` | Site favicon location relative to the configured `assets` folder. When configured, the favicon will be set for all generated pages. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/logo.png` | +| `generatr.style.customStylesheet` | URL to hosted custom stylesheet or path to custom stylesheet file (location relative to the configured `assets` folder). When configured this css file will be loaded for all pages. When using a path to a file the `--assets-dir` switch must be used when generating the site and the corresponding file is available in the `assets` folder. | | `site/custom.css` or 'https://uri.example/custom.css | +| `generatr.search.language` | Indexing/stemming language for the search index. See [Lunr language support](https://github.com/olivernn/lunr-languages) | `en` | `nl` | +| `generatr.markdown.flexmark.extensions` | Additional extensions to the markdown generator to add new markdown capabilities. [More Details](https://avisi-cloud.github.io/structurizr-site-generatr/main/extended-markdown-features/) | Tables | `Tables,Admonition` | +| `generatr.svglink.target` | Specifies the link target for element links in the exported svg | `_top` | `_self` | See the included example for usage of some those properties in the diff --git a/docs/example/assets/site/custom.css b/docs/example/assets/site/custom.css new file mode 100644 index 00000000..a42e8e1c --- /dev/null +++ b/docs/example/assets/site/custom.css @@ -0,0 +1,12 @@ +svg g g[id^="link"]:hover path { + stroke:#FF0000 !important; + stroke-width: 2.0; +} + +svg g g[id^="link"]:hover polygon { + stroke:#FF0000 !important; +} + +svg g g[id^="link"]:hover text { + fill:#FF0000 !important; +} diff --git a/docs/example/workspace.dsl b/docs/example/workspace.dsl index 7b3260c9..8082bb3a 100644 --- a/docs/example/workspace.dsl +++ b/docs/example/workspace.dsl @@ -161,6 +161,8 @@ workspace "Big Bank plc" "This is an example workspace to illustrate the key fea "generatr.style.colors.secondary" "#ffffff" "generatr.style.faviconPath" "site/favicon.ico" "generatr.style.logoPath" "site/logo.png" + "generatr.style.customStylesheet" "site/custom.css" + //"generatr.style.customStylesheet" "https://example.com/custom.css" "generatr.svglink.target" "_self" // full list of available "generatr.markdown.flexmark.extensions" diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModel.kt new file mode 100644 index 00000000..3cc9906a --- /dev/null +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModel.kt @@ -0,0 +1,23 @@ +package nl.avisi.structurizr.site.generatr.site.model + +import nl.avisi.structurizr.site.generatr.site.GeneratorContext + +class CustomStylesheetViewModel(generatorContext: GeneratorContext) { + val resourceURI = getResourceURI(generatorContext) + val includeCustomStylesheet = resourceURI != null + + private fun getResourceURI(generatorContext: GeneratorContext): String? { + val stylesheet = generatorContext.workspace.views.configuration.properties + .getOrDefault( + "generatr.style.customStylesheet", + null + ) + + if (stylesheet != null) { + return if (stylesheet.lowercase().startsWith("http")) stylesheet else "./$stylesheet" + } + + return null + } + +} diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PageViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PageViewModel.kt index bbbdb27f..9ea1c83d 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PageViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PageViewModel.kt @@ -12,16 +12,13 @@ abstract class PageViewModel(protected val generatorContext: GeneratorContext) { pageSubTitle } val favicon by lazy { FaviconViewModel(generatorContext, this) } + val customStylesheet by lazy { CustomStylesheetViewModel(generatorContext) } val headerBar by lazy { HeaderBarViewModel(this, generatorContext) } val menu by lazy { MenuViewModel(generatorContext, this) } val includeAutoReloading = generatorContext.serving - - val flexmarkConfig by lazy { - buildFlexmarkConfig(generatorContext) - } + val flexmarkConfig by lazy { buildFlexmarkConfig(generatorContext) } val includeAdmonition = flexmarkConfig.selectedExtensionMap.containsKey("Admonition") val includeKatex = flexmarkConfig.selectedExtensionMap.containsKey("GitLab") - val configuration = generatorContext.workspace.views.configuration.properties abstract val url: String diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Page.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Page.kt index e013ab8b..a84f25fc 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Page.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Page.kt @@ -18,16 +18,10 @@ private fun HTML.headFragment(viewModel: PageViewModel) { meta(name = "viewport", content = "width=device-width, initial-scale=1") title { +viewModel.pageTitle } link(rel = "stylesheet", href = "https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css") - link( - rel = "stylesheet", - href = "../" + "/style.css".asUrlToFile(viewModel.url) - ) - link( - rel = "stylesheet", - href = "./" + "/style-branding.css".asUrlToFile(viewModel.url) - ) + link(rel = "stylesheet", href = "../" + "/style.css".asUrlToFile(viewModel.url)) + link(rel = "stylesheet", href = "./" + "/style-branding.css".asUrlToFile(viewModel.url)) - if (viewModel.includeAdmonition) + if (viewModel.includeAdmonition) markdownAdmonitionStylesheet(viewModel) if (viewModel.includeKatex) @@ -42,6 +36,9 @@ private fun HTML.headFragment(viewModel: PageViewModel) { if (viewModel.includeAutoReloading) autoReloadScript(viewModel) + + if (viewModel.customStylesheet.includeCustomStylesheet) + link(rel = "stylesheet", href = viewModel.customStylesheet.resourceURI) } } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModelTest.kt new file mode 100644 index 00000000..55578472 --- /dev/null +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/CustomStylesheetViewModelTest.kt @@ -0,0 +1,29 @@ +package nl.avisi.structurizr.site.generatr.site.model + +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.jupiter.api.Test + +class CustomStylesheetViewModelTest : ViewModelTest() { + private val generatorContext = generatorContext() + + @Test + fun `Configuration Property Missing`() { + val customStylesheetViewModel = CustomStylesheetViewModel(generatorContext) + assertThat(customStylesheetViewModel.resourceURI).isEqualTo(null) + } + + @Test + fun `Configuration Property Set To URL`() { + generatorContext.workspace.views.configuration.addProperty("generatr.style.customStylesheet","http://example.uri/custom.css") + val customStylesheetViewModel = CustomStylesheetViewModel(generatorContext) + assertThat(customStylesheetViewModel.resourceURI).isEqualTo("http://example.uri/custom.css") + } + + @Test + fun `Configuration Property Set To Local File`() { + generatorContext.workspace.views.configuration.addProperty("generatr.style.customStylesheet","custom.css") + val customStylesheetViewModel = CustomStylesheetViewModel(generatorContext) + assertThat(customStylesheetViewModel.resourceURI).isEqualTo("./custom.css") + } +}