Skip to content

Commit

Permalink
feat: support different icon sources (#8)
Browse files Browse the repository at this point in the history
Bigger refactoring:

Now generates a treeview.css instead of creating image tags
Uses roles on an tag to define the icons.
Supports antora, embedded, jsdelivr and custom url

Closes #4
  • Loading branch information
lask79 committed Feb 5, 2024
1 parent 06b589d commit 5fb58e2
Show file tree
Hide file tree
Showing 17 changed files with 677 additions and 238 deletions.
76 changes: 71 additions & 5 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ toc::[]
* Supports dark and light mode
* Uses different icons for dark and light mode
* Supports callouts / conums
* Supports different ways of retrieving the icons:
** jsdelivr (default) => downloads from CDN
** embedded => embeds the icons into the css as data uri
** antora => copies icons into UI catalog
** <custom_path> => configure your own path or url
* Dark and light mode
+
[%header,cols="^1a,1a""]
Expand Down Expand Up @@ -270,7 +275,9 @@ module.exports = require('asciidoctor-treeview')

== Configuration

=== Theme
=== Asciidoc Attributes

==== treeview-theme
`Default: dark`

* Use `treeview-theme` attribute on document
Expand All @@ -295,12 +302,59 @@ module.exports = require('asciidoctor-treeview')
----
--------

==== treeview-icon-source
`Default: jsdelivr`

* Use `treeview-icon-source` attribute on document
* Supported values:
** `jsdelivr` (default) => downloads from CDN
** `embedded` => embeds the icons into the css as data-uri
** `antora` => copies icons into UI catalog
** `<custom_path>` => configure your own or url to the folder that contains the icons.

*Examples:*

.Embed icons as data-uri in CSS
[source,plaintext]
----
= Document Title
:treeview-icon-source: embedded
----

.Use custom url
[source,plaintext]
----
= Document Title
:treeview-icon-source: https://example.com/cdn/icons
----

The icon name like file.svg will be added as suffix to the given url.

=== Antora

[source, yaml]
----
antora:
extensions:
- require: "asciidoctor-treeview"
icon_source: antora # or embedded or jsdelivr
----

`Default: antora`

* Use `icon-source` attribute on document
* Supported values:
** `antora` (default) => copies icons into UI catalog
** `jsdelivr` => downloads from CDN
** `embedded` => embeds the icons into the css as data-uri

WARNING: The asciidoctor attribute `treeview-icon-source` will be ignored when antora is used.

== Symbol Based Parser

* Symbols * and # are already autodetected.
* If you want to use a custom symbol like '-' then you need to configure it on the treeview block.


.Autodetected symbol #
[source,plaintext]
--------
Expand Down Expand Up @@ -344,10 +398,22 @@ Then that line will be marked as a comment.

== Changelog

.v1.0.0-alpha.6
* Features
=== v1.0.0-alpha.7
* *Features*
** add support for different icon sources https://github.com/lask79/asciidoctor-treeview/pull/8[(#8)]
*** `jsdelivr` (default) => downloads from CDN
*** `embedded` => embeds the icons into the css as data uri
*** `antora` => copies icons into UI catalog
*** `<custom_path>` => configure your own path or url

* *Refactoring*
** Now generates a treeview.css that uses urls for the different icons instead of creating image tags inside of the document.
** Uses roles on an <i> tag to define the icons.

=== v1.0.0-alpha.6
* *Features*
** allow comments on lines https://github.com/lask79/asciidoctor-treeview/pull/6[(#6)]
** mark lines as folders (see HowTos)

* Fixes
* *Fixes*
** do not render empty lines as files without name allow comments on lines https://github.com/lask79/asciidoctor-treeview/pull/5[(#5)]
20 changes: 20 additions & 0 deletions data/css/treeview.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,23 @@
.tv-line-element > .tv-item-name {
margin-left: var(--treeview-item-name-offset, 5px);
}

.tv-icon {
height: var(--treeview-icon-height, 18px);
width: var(--treeview-icon-width, 18px);
}

.tv-icon::before {
content: "";
width: var(--treeview-icon-width, 18px);
height: var(--treeview-icon-height, 18px);
/* margin-top: 0px; */
position: relative;
display: inline-flex;
/* background-size: var(--treeview-icon-height, 18px); */
background-repeat: no-repeat;
}

.tv-icon.asciidoc::before {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2NC43NCAyMy40MjVhMTIuMiAxMi4yIDAgMCAwLTQuNzYuOTM2IDEyLjE1NSAxMi4xNTUgMCAwIDAtNS42MjEgNC43MDZjLS41MzguNzUxLTEgMS41NzQtMS4zNyAyLjQ2NmwtNDUuOTY2IDExMS4wOUg2Mi42OThjLTYuNzU4IDAtMTIuMiA1LjQ0Mi0xMi4yIDEyLjJhMTIuMTczIDEyLjE3MyAwIDAgMCAxMi4yIDEyLjJoMzQuMjNsLS4wMDYuMDEzaDI2LjQwNmwuMDA2LS4wMTRoMjYuNDI0YzYuNzU4IDAgMTIuMTk5LTUuNDQgMTIuMTk5LTEyLjE5OSAwLTYuNzU4LTUuNDQtMTIuMi0xMi4yLTEyLjJoLTE2LjMzbDMxLjE0NC03NS4yNjMgODAuNjU1IDE5NC45MmExMi4xNzMgMTIuMTczIDAgMCAwIDE1LjkzNyA2LjYwOCAxMi4xNzMgMTIuMTczIDAgMCAwIDYuNjA3LTE1LjkzN0wxNzUuOTE3IDMwLjk3YTEyLjE3NCAxMi4xNzQgMCAwIDAtMTEuMTc2LTcuNTQ0ek00My42OCAxODguNjM1YTEyLjE3MyAxMi4xNzMgMCAwIDAtMTIuMTk5IDEyLjJjMCA2Ljc1OSA1LjQ0IDEyLjIgMTIuMTk5IDEyLjJoMzQuMjFsLTE2Ljc1MiA0MC40ODdhMTIuMTcgMTIuMTcgMCAwIDAgNi42MDkgMTUuOTM0IDEyLjE2OCAxMi4xNjggMCAwIDAgMTUuOTM0LTYuNjA2bDIwLjYxNC00OS44MTZoMjYuNDQ1YTEyLjE3MyAxMi4xNzMgMCAwIDAgMTIuMTk5LTEyLjJjMC02Ljc1OS01LjQ0LTEyLjItMTIuMTk5LTEyLjJ6IiBzdHlsZT0iZmlsbDojZjQ0MzM2O3BhaW50LW9yZGVyOmZpbGwgbWFya2VycyBzdHJva2U7c3Ryb2tlLW1pdGVybGltaXQ6MS41O3N0cm9rZS13aWR0aDowIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguMTM3IC4zODkpIHNjYWxlKC4wNzkwNCkiLz48L3N2Zz4=");
}
59 changes: 59 additions & 0 deletions data/templates/treeview.css.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.listingblock.treeview.dark {
--treeview-background-color: #222;
--treeview-font-color: #d8dee9;
--treeview-line-height: 1;
}

.listingblock.treeview.light {
--treeview-background-color: #f7f7f8;
--treeview-font-color: #222;
--treeview-line-height: 1;
}

.listingblock.treeview > .content > pre {
background-color: var(--treeview-background-color, #222);
color: var(--treeview-font-color, #d8dee9);
line-height: var(--treeview-line-height, 1);
padding: 0.5em;
border-radius: 4px;
}

.tv-line {
display: inline-flex;
align-items: center;
}

.tv-line-element {
display: contents;
}

.tv-line-element > img {
height: var(--treeview-icon-height, 18px);
width: var(--treeview-icon-width, 18px);
}

.tv-line-element > .tv-item-name {
margin-left: var(--treeview-item-name-offset, 5px);
}

.tv-icon {
height: var(--treeview-icon-height, 18px);
width: var(--treeview-icon-width, 18px);
}

.tv-icon::before {
content: "";
width: var(--treeview-icon-width, 18px);
height: var(--treeview-icon-height, 18px);
/* margin-top: 0px; */
position: relative;
display: inline-flex;
/* background-size: var(--treeview-icon-height, 18px); */
background-repeat: no-repeat;
}

{{#each iconRules}}
.tv-icon.{{this.baseName}}::before {
background-image: url('{{{this.url}}}');
}
{{/each}}
39 changes: 33 additions & 6 deletions lib/antora/extension.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
'use strict'

const ospath = require('path')
const fs = require('fs')
const { name: packageName } = require('../../package.json')
const { assetFile, partialFile } = require('./utils/asset-utils')
const { assetCacheFile, partialFile, copyAssetFiles } = require('./utils/asset-utils')
const { createFolder } = require('./utils/folder-utils')
const validateConfig = require('./utils/validate-config')

module.exports = function (context, { playbook, config }) {
module.exports = function (context, { playbook, config }, iconRegistry, cssGenerator) {
const logger = context.getLogger(packageName)
const validatedConfig = validateConfig(config, packageName, logger)

logger.info('Start extension')
logger.info(' > Register asciidoctor-treeview as asciidoc extension')
playbook.asciidoc.extensions.push('asciidoctor-treeview')

context.on('uiLoaded', async ({ playbook, uiCatalog }) => {
context.on('documentsConverted', async ({ playbook, uiCatalog }) => {
logger.info('Handle UICatalog files ...')

const extensionContext = {
logger,
config: validatedConfig,
}

const { uiOutputDir, cacheDir = './.cache/antora' } = getDirectories(playbook)
Expand All @@ -24,6 +29,8 @@ module.exports = function (context, { playbook, config }) {
extensionContext.uiOutputDir = uiOutputDir
extensionContext.cacheDir = cacheDir
extensionContext.extensionCacheDir = ospath.join(cacheDir, '..', packageName)
extensionContext.iconRegistry = iconRegistry
extensionContext.cssGenerator = cssGenerator

await processAssets(extensionContext)
})
Expand All @@ -37,15 +44,35 @@ module.exports = function (context, { playbook, config }) {

async function processAssets (extensionContext) {
copyTreeViewStyleCss(extensionContext)
copyTreeViewIcons(extensionContext)
copyTreeViewStyleHbs(extensionContext)
}

function copyTreeViewStyleCss (extensionContext) {
const { uiCatalog, uiOutputDir, logger } = extensionContext

const { uiCatalog, uiOutputDir, cacheDir, logger, iconRegistry, cssGenerator, config } = extensionContext
const basename = 'treeview.css'
const cssDir = 'css'
assetFile(packageName, uiCatalog, logger, uiOutputDir, cssDir, basename)

createFolder(ospath.join(cacheDir, cssDir), logger)

logger.info(' > Generating treeview.css ...')
const cssContent = cssGenerator.generateCss(iconRegistry, config.iconSource)
const cssPath = ospath.resolve(cacheDir, cssDir, basename)
fs.writeFileSync(cssPath, cssContent)

assetCacheFile(packageName, uiCatalog, logger, uiOutputDir, cssDir, basename, cacheDir)
}

function copyTreeViewIcons (extensionContext) {
const { uiCatalog, uiOutputDir, cacheDir, logger, iconRegistry, config } = extensionContext

if (config.iconSource !== 'antora') return

const assetDir = 'img/treeview'
const materialIconsModule = require.resolve('material-icon-theme/icons/file.svg')
const sourceDir = ospath.dirname(materialIconsModule)

copyAssetFiles(packageName, uiCatalog, logger, uiOutputDir, assetDir, sourceDir, iconRegistry.getIconValues())
}

function copyTreeViewStyleHbs (extensionContext) {
Expand Down
32 changes: 31 additions & 1 deletion lib/antora/utils/asset-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ function assetFile (packageName, uiCatalog, logger, uiOutputDir, assetDir, basen
)
}

function assetCacheFile (packageName, uiCatalog, logger, uiOutputDir, assetDir, basename, cacheDir, overwrite = false) {
handleFile(
ASSET_TYPE,
packageName,
uiCatalog,
logger,
uiOutputDir,
assetDir,
basename,
(assetPath) => new LazyReadable(() => fs.createReadStream(path.join(cacheDir, assetPath))),
overwrite
)
}

function partialFile (packageName, uiCatalog, logger, uiOutputDir, assetDir, basename, overwrite = false) {
handleFile(
PARTIAL_TYPE,
Expand All @@ -79,6 +93,22 @@ function partialFile (packageName, uiCatalog, logger, uiOutputDir, assetDir, bas
)
}

function copyAssetFiles (packageName, uiCatalog, logger, uiOutputDir, assetDir, sourceDir, fileNames) {
for (const fileName of fileNames) {
handleFile(
ASSET_TYPE,
packageName,
uiCatalog,
logger,
uiOutputDir,
assetDir,
fileName,
(assetPath) => Buffer.from(fs.readFileSync(path.join(sourceDir, fileName))),
false
)
}
}

function getPartialFileContent (packageName, uiCatalog, logger, uiOutputDir, assetDir, basename) {
const assetPath = path.join(assetDir, basename)
const existingFiles = uiCatalog.findByType(PARTIAL_TYPE)
Expand All @@ -87,4 +117,4 @@ function getPartialFileContent (packageName, uiCatalog, logger, uiOutputDir, ass
return existingFile.contents.toString()
}

module.exports = { assetFile, partialFile, getPartialFileContent }
module.exports = { assetFile, assetCacheFile, partialFile, getPartialFileContent, copyAssetFiles }
22 changes: 22 additions & 0 deletions lib/antora/utils/validate-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const defaultIconSource = 'antora' // jsdelivr, embedded

function validateConfig (config, packageName, logger) {
const { iconSource = defaultIconSource, ...unknownOptions } = config

if (Object.keys(unknownOptions).length) {
throw new Error(`Unrecognized options specified for ${packageName}: ${Object.keys(unknownOptions).join(', ')}`)
}

if (![defaultIconSource, 'jsdelivr', 'embedded'].includes(iconSource)) {
throw new Error(`Invalid icon path specified for ${packageName}: ${iconSource}`)
}

const validatedConfig = { iconSource }

logger.info(`Registering ${packageName} with config`)
logger.info(` > Icon Path: ${validatedConfig.iconPath}`)

return validatedConfig
}

module.exports = validateConfig
Loading

0 comments on commit 5fb58e2

Please sign in to comment.