From 7a2c04cba7a06d524bc6af5fad3317041fa2193c Mon Sep 17 00:00:00 2001 From: Cell <1024@lruihao.cn> Date: Mon, 31 Aug 2026 17:11:39 +0800 Subject: [PATCH] feat(assets): add table auto-numbering and sorting support - Add CSS counter-based table auto-numbering with i18n support - Add client-side table sorting with numeric/string/date detection - Support per-table opt-out via Markdown attributes (number=false, sort=false) - Add table caption support via {caption="..."} attribute - Extract table styles from _content.scss to dedicated _table.scss - Add TableConfig TypeScript interface - Register table-sort script via .Store.hasTableSort for lazy loading --- apps/test/content/posts/color-preview-test.md | 2 + assets/js/lib/table-sort.ts | 154 ++++++++++++++++++ assets/js/types/config.ts | 6 + assets/scss/content/_content.scss | 55 +------ assets/scss/content/_index.scss | 1 + assets/scss/content/_table.scss | 114 +++++++++++++ hugo.toml | 9 +- i18n/ar.toml | 1 + i18n/de.toml | 1 + i18n/en.toml | 1 + i18n/es.toml | 1 + i18n/fa.toml | 1 + i18n/fr.toml | 1 + i18n/hi.toml | 1 + i18n/it.toml | 1 + i18n/ja.toml | 1 + i18n/ko.toml | 1 + i18n/pl.toml | 1 + i18n/pt-BR.toml | 1 + i18n/ro.toml | 1 + i18n/ru.toml | 1 + i18n/sr.toml | 1 + i18n/ur.toml | 1 + i18n/vi.toml | 1 + i18n/zh-CN.toml | 1 + i18n/zh-TW.toml | 1 + layouts/_markup/render-table.html | 41 ++++- layouts/_partials/base/assets.html | 5 + 28 files changed, 349 insertions(+), 57 deletions(-) create mode 100644 assets/js/lib/table-sort.ts create mode 100644 assets/scss/content/_table.scss diff --git a/apps/test/content/posts/color-preview-test.md b/apps/test/content/posts/color-preview-test.md index c1734d1e..e70550e8 100644 --- a/apps/test/content/posts/color-preview-test.md +++ b/apps/test/content/posts/color-preview-test.md @@ -12,6 +12,7 @@ tags: This post is a test post to preview the color syntax in Markdown. + ## 📝 Syntax @@ -20,6 +21,7 @@ This post is a test post to preview the color syntax in Markdown. | HEX | `` `#RRGGBB` `` | `` `#0969DA` `` | `#0969DA` | | RGB | `` `rgb(R,G,B)` `` | `` `rgb(9, 105, 218)` `` | `rgb(9, 105, 218)` | | HSL | `` `hsl(H,S,L)` `` | `` `hsl(212, 92%, 45%)` `` | `hsl(212, 92%, 45%)` | +{caption="Color syntax in Markdown"} e.g. The background color is `#ffffff` for light mode and `#000000` for dark mode. diff --git a/assets/js/lib/table-sort.ts b/assets/js/lib/table-sort.ts new file mode 100644 index 00000000..12664efd --- /dev/null +++ b/assets/js/lib/table-sort.ts @@ -0,0 +1,154 @@ +/** + * Table sort module for FixIt content blocks. + * + * Responsibilities: + * - Initialize sortable tables (marked with `data-sortable` attribute). + * - Add click handlers to `` elements for ascending/descending/no-sort toggle. + * - Support numeric, date, and string sorting. + * - Re-initialize after content decryption events. + */ +import { eventBus } from '../core/event-bus' + +type SortDirection = 'asc' | 'desc' | null + +function detectValueType(value: string): 'number' | 'date' | 'string' { + const stripped = value.replace(/[\s,%$€£¥]/g, '') + if (stripped !== '' && !Number.isNaN(Number(stripped))) + return 'number' + if (/\d{4}[-/]\d{1,2}[-/]\d{1,2}/.test(value) && !Number.isNaN(Date.parse(value))) + return 'date' + return 'string' +} + +function compareValues(a: string, b: string, direction: SortDirection): number { + if (!direction) + return 0 + const type = detectValueType(a) + let result = 0 + + switch (type) { + case 'number': + result = Number(a.replace(/[\s,%$€£¥]/g, '')) - Number(b.replace(/[\s,%$€£¥]/g, '')) + break + case 'date': + result = new Date(a).getTime() - new Date(b).getTime() + break + default: + result = a.localeCompare(b) + } + + return direction === 'asc' ? result : -result +} + +function nextDirection(current: SortDirection): SortDirection { + if (current === null) + return 'asc' + if (current === 'asc') + return 'desc' + return null +} + +function sortTable(table: HTMLTableElement, colIndex: number, direction: SortDirection) { + const tbody = table.querySelector('tbody') + if (!tbody) + return + + const rows = Array.from(tbody.querySelectorAll('tr')) + + if (!direction) { + rows.sort((a, b) => { + const orderA = parseInt(a.dataset.order || '0', 10) + const orderB = parseInt(b.dataset.order || '0', 10) + return orderA - orderB + }) + } + else { + rows.sort((a, b) => { + const cellA = a.children[colIndex]?.textContent?.trim() || '' + const cellB = b.children[colIndex]?.textContent?.trim() || '' + return compareValues(cellA, cellB, direction) + }) + } + + rows.forEach(row => tbody.appendChild(row)) +} + +function updateSortIndicators(table: HTMLTableElement, colIndex: number, direction: SortDirection) { + table.querySelectorAll('th').forEach((th, i) => { + th.classList.remove('sort-asc', 'sort-desc') + if (i === colIndex && direction) { + th.classList.add(direction === 'asc' ? 'sort-asc' : 'sort-desc') + } + }) +} + +function initSortableTable(table: HTMLTableElement) { + if (table.dataset.sortInit) + return + table.dataset.sortInit = 'true' + + const tbody = table.querySelector('tbody') + if (tbody) { + Array.from(tbody.querySelectorAll('tr')).forEach((row, i) => { + row.dataset.order = String(i) + }) + } + + table.classList.add('is-sortable') + + const headers = table.querySelectorAll('thead th') + headers.forEach((th, index) => { + th.classList.add('sort-header') + th.setAttribute('role', 'button') + th.setAttribute('tabindex', '0') + th.setAttribute('aria-sort', 'none') + + const handleClick = () => { + const currentDir = (table.dataset.sortDir === 'asc' && table.dataset.sortCol === String(index)) + ? 'asc' + : (table.dataset.sortDir === 'desc' && table.dataset.sortCol === String(index)) + ? 'desc' + : null + + const newDir = nextDirection(currentDir) + + if (newDir) { + table.dataset.sortCol = String(index) + table.dataset.sortDir = newDir + th.setAttribute('aria-sort', newDir === 'asc' ? 'ascending' : 'descending') + } + else { + delete table.dataset.sortCol + delete table.dataset.sortDir + th.setAttribute('aria-sort', 'none') + } + + sortTable(table, index, newDir) + updateSortIndicators(table, index, newDir) + } + + th.addEventListener('click', handleClick, false) + th.addEventListener('keydown', (e) => { + if ((e as KeyboardEvent).key === 'Enter' || (e as KeyboardEvent).key === ' ') { + e.preventDefault() + handleClick() + } + }, false) + }) +} + +function initTableSort(target: Element | Document = document) { + target.querySelectorAll('table[data-sortable]:not([data-sort-init])') + .forEach(initSortableTable) +} + +function bindEvents() { + eventBus.on('fixit:content-decrypted', ({ detail }) => { + initTableSort(detail.target) + }) +} + +document.addEventListener('DOMContentLoaded', () => { + initTableSort() + bindEvents() +}, false) diff --git a/assets/js/types/config.ts b/assets/js/types/config.ts index 059a3ed9..ae7e1813 100644 --- a/assets/js/types/config.ts +++ b/assets/js/types/config.ts @@ -32,6 +32,7 @@ export interface FixItConfig { encryption?: EncryptionConfig print?: PrintConfig postChat?: boolean + table?: TableConfig } export interface EchartsConfig { @@ -121,3 +122,8 @@ export interface PrintConfig { expandDetails?: boolean expandFileTree?: boolean } + +export interface TableConfig { + number?: boolean + sort?: boolean +} diff --git a/assets/scss/content/_content.scss b/assets/scss/content/_content.scss index 10c397df..d1e00e34 100644 --- a/assets/scss/content/_content.scss +++ b/assets/scss/content/_content.scss @@ -141,7 +141,7 @@ .content { margin-block: 1rem; - counter-reset: h2 h3 h4 h5 h6; + counter-reset: h2 h3 h4 h5 h6 table-counter; > h2.heading-element { counter-increment: h2; @@ -167,6 +167,10 @@ counter-increment: h6; } + > .table-wrapper table[data-table-numbered] { + counter-increment: table-counter; + } + > h2.heading-element .heading-numbered::before { content: counter(h2) ". "; } @@ -358,53 +362,8 @@ } } - .table-wrapper { - position: relative; - overflow-x: auto; - overflow-y: hidden; - margin: 0.5rem 0; - background: fi-var(table-background-color); - border: 1px solid fi-var(table-border-color); - @include border-radius; - @include set-fi-var(scrollbar-thumb-color, fi-var(table-background-color)); - - > table { - width: 100%; - max-width: 100%; - margin: 0; - border-spacing: 0; - background: fi-var(table-background-color); - border-collapse: separate; - - thead { - background: fi-var(table-thead-color); - } - - &:not([class]) tbody { - & tr:nth-child(odd) { - background: light-dark(color.adjust($table-background-color, $lightness: -2.25%), color.adjust($table-background-color-dark, $lightness: 2.75%)); - } - - & tr:hover { - background: light-dark(color.adjust($table-background-color, $lightness: -4.5%), color.adjust($table-background-color-dark, $lightness: 5.5%)); - } - } - - th, - td { - padding: 0.3rem 1rem; - border-inline-end: 1px solid fi-var(table-border-color); - border-bottom: 1px solid fi-var(table-border-color); - } - - tr > :last-child { - border-inline-end: none; - } - - > :last-child > tr:last-child > * { - border-bottom: none; - } - } + > .table-wrapper table[data-table-numbered] { + counter-increment: table-counter; } img { diff --git a/assets/scss/content/_index.scss b/assets/scss/content/_index.scss index 24bd91b3..57d27d3c 100644 --- a/assets/scss/content/_index.scss +++ b/assets/scss/content/_index.scss @@ -2,5 +2,6 @@ @forward "shortcodes"; @forward "alert"; @forward "code"; +@forward "table"; @forward "fixit-decryptor"; @forward "patch"; diff --git a/assets/scss/content/_table.scss b/assets/scss/content/_table.scss new file mode 100644 index 00000000..8c54bff4 --- /dev/null +++ b/assets/scss/content/_table.scss @@ -0,0 +1,114 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "variables" as *; + +.table-wrapper { + position: relative; + overflow-x: auto; + overflow-y: hidden; + margin: 0.5rem 0; + background: fi-var(table-background-color); + border: 1px solid fi-var(table-border-color); + @include border-radius; + @include set-fi-var(scrollbar-thumb-color, fi-var(table-background-color)); + + > table { + width: 100%; + max-width: 100%; + margin: 0; + border-spacing: 0; + background: fi-var(table-background-color); + border-collapse: separate; + + thead { + background: fi-var(table-thead-color); + } + + tbody { + tr:nth-child(odd) { + background: light-dark(color.adjust($table-background-color, $lightness: -2.25%), color.adjust($table-background-color-dark, $lightness: 2.75%)); + } + tr:hover { + background: light-dark(color.adjust($table-background-color, $lightness: -4.5%), color.adjust($table-background-color-dark, $lightness: 5.5%)); + } + } + + th, + td { + padding: 0.3rem 1rem; + border-inline-end: 1px solid fi-var(table-border-color); + border-bottom: 1px solid fi-var(table-border-color); + } + + tr > :last-child { + border-inline-end: none; + } + + > :last-child > tr:last-child > * { + border-bottom: none; + } + + > caption { + caption-side: top; + padding: 0.5rem 1rem; + text-align: center; + font-weight: 600; + font-size: 0.875em; + border-bottom: 1px solid fi-var(table-border-color); + border-radius: fi-var(global-border-radius) fi-var(global-border-radius) 0 0; + + .table-number { + &::before { + content: attr(data-i18n-table) " " counter(table-counter); + } + + & + .table-caption { + font-weight: normal; + + &::before { + content: "-"; + margin-inline: 0.25em; + } + } + } + } + + // Sortable table styles + &.is-sortable thead th.sort-header { + cursor: pointer; + user-select: none; + position: relative; + padding-inline-end: 1.5rem; + + &::after { + content: ""; + position: absolute; + inset-inline-end: 0.5rem; + top: 50%; + translate: 0 -50%; + width: 0; + height: 0; + border-inline: 4px solid transparent; + border-block: 4px solid transparent; + opacity: 0.3; + transition: all 0.2s, translate 0.2s; + } + + &:hover::after { + opacity: 0.6; + } + + &.sort-asc::after, + &.sort-desc::after { + opacity: 1; + border-block-end: 5px solid fi-var(global-font-color); + border-block-start: 0; + } + + &.sort-desc::after { + rotate: 180deg; + } + } + } +} diff --git a/hugo.toml b/hugo.toml index 4d8d00a8..c4882639 100644 --- a/hugo.toml +++ b/hugo.toml @@ -735,7 +735,7 @@ enable = false # Disclaimer type. # available values: ["ai", "repost", "original", "custom"] type = "ai" -# Custom disclaimer content (optional, uses i18n default if empty). +# Custom disclaimer content (optional, uses I18n default if empty). content = "" # Display a message at the beginning of an article to warn the reader that its content might be expired. @@ -760,6 +760,13 @@ enable = false # Only enable in main section pages (default is posts). only_main_section = true +# {{< version 1.0.0 >}} Table configuration. +[params.table] +# Whether to enable auto table numbering. +number = false +# Whether to enable table sorting. +sort = true + # Mathematical formulas configuration. # See: [Formula](https://fixit.lruihao.cn/docs/content-management/markdown-syntax/extended/#formula) [params.math] diff --git a/i18n/ar.toml b/i18n/ar.toml index 555ea025..3e710043 100644 --- a/i18n/ar.toml +++ b/i18n/ar.toml @@ -89,6 +89,7 @@ valinePlaceholder = "تعليقك ..." # === Assets === [assets] +table = "جدول" search = "بحث" searchPlaceholder = "ابحث في العناوين أو المحتوى ..." searchIn = "ابحث في {{ . }}" diff --git a/i18n/de.toml b/i18n/de.toml index 42774802..6812dec0 100644 --- a/i18n/de.toml +++ b/i18n/de.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Ihr Kommentar ..." # === Assets === [assets] +table = "Tabelle" search = "Suche" searchPlaceholder = "Suche nach Titel und Inhalt..." searchIn = "Suche in {{ . }}" diff --git a/i18n/en.toml b/i18n/en.toml index d4d3a260..fe460da7 100644 --- a/i18n/en.toml +++ b/i18n/en.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Your comment ..." # === Assets === [assets] +table = "Table" search = "Search" searchPlaceholder = "Search titles or contents ..." searchIn = "Search in {{ . }}" diff --git a/i18n/es.toml b/i18n/es.toml index 1c99fcf8..35599760 100644 --- a/i18n/es.toml +++ b/i18n/es.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Tu comentario ..." # === Assets === [assets] +table = "Tabla" search = "Buscar" searchPlaceholder = "Busca títulos o contenido..." searchIn = "Buscar en {{ . }}" diff --git a/i18n/fa.toml b/i18n/fa.toml index 10668117..1375349f 100644 --- a/i18n/fa.toml +++ b/i18n/fa.toml @@ -89,6 +89,7 @@ valinePlaceholder = "نظر شما ..." # === Assets === [assets] +table = "جدول" search = "جستجو" searchPlaceholder = "جستجوی عناوین یا محتوا ..." searchIn = "جستجو در {{ . }}" diff --git a/i18n/fr.toml b/i18n/fr.toml index 4f874300..a0c14317 100644 --- a/i18n/fr.toml +++ b/i18n/fr.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Votre commentaire ..." # === Assets === [assets] +table = "Tableau" search = "Chercher" searchPlaceholder = "Rechercher des titres, des contenus..." searchIn = "Rechercher dans {{ . }}" diff --git a/i18n/hi.toml b/i18n/hi.toml index f06b3df5..d78d40bc 100644 --- a/i18n/hi.toml +++ b/i18n/hi.toml @@ -89,6 +89,7 @@ valinePlaceholder = "आपकी टिप्पणी ..." # === Assets === [assets] +table = "तालिका" search = "खोज" searchPlaceholder = "शीर्षक या सामग्री खोजें ..." searchIn = "{{ . }} में खोजें" diff --git a/i18n/it.toml b/i18n/it.toml index 2c5eb275..cabaa963 100644 --- a/i18n/it.toml +++ b/i18n/it.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Il tuo commento ..." # === Assets === [assets] +table = "Tabella" search = "Cerca" searchPlaceholder = "Cerca il titolo o il contenuto dell'articolo ..." searchIn = "Cerca in" diff --git a/i18n/ja.toml b/i18n/ja.toml index 685f5e15..72492c2d 100644 --- a/i18n/ja.toml +++ b/i18n/ja.toml @@ -86,6 +86,7 @@ valinePlaceholder = "あなたのコメント……" # === Assets === [assets] +table = "テーブル" search = "検索" searchPlaceholder = "タイトルまたは内容を検索..." searchIn = "{{ . }} で検索" diff --git a/i18n/ko.toml b/i18n/ko.toml index f56fff90..30ddb4d3 100644 --- a/i18n/ko.toml +++ b/i18n/ko.toml @@ -86,6 +86,7 @@ valinePlaceholder = "당신의 댓글……" # === Assets === [assets] +table = "표" search = "검색" searchPlaceholder = "제목 또는 내용을 검색하세요..." searchIn = "{{ . }}에서 검색" diff --git a/i18n/pl.toml b/i18n/pl.toml index 76a8c627..6ff4b067 100644 --- a/i18n/pl.toml +++ b/i18n/pl.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Twój komentarz ..." # === Assets === [assets] +table = "Tabela" search = "Szukaj" searchPlaceholder = "Wyszukaj tytuł lub treść artykułu ..." searchIn = "Szukaj w {{ . }}" diff --git a/i18n/pt-BR.toml b/i18n/pt-BR.toml index d9bdab55..7db543a6 100644 --- a/i18n/pt-BR.toml +++ b/i18n/pt-BR.toml @@ -89,6 +89,7 @@ valinePlaceholder = "O seu comentário..." # === Assets === [assets] +table = "Tabela" search = "Pesquisa" searchPlaceholder = "Pesquisar títulos ou conteúdos..." searchIn = "Pesquisar em {{ . }}" diff --git a/i18n/ro.toml b/i18n/ro.toml index 356dd13a..05c528f3 100644 --- a/i18n/ro.toml +++ b/i18n/ro.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Comentariul dvs ..." # === Assets === [assets] +table = "Tabelă" search = "Căutare" searchPlaceholder = "Căutarea titlului sau conținutului articolului ..." searchIn = "Căutare în {{ . }}" diff --git a/i18n/ru.toml b/i18n/ru.toml index 1c5ec8c2..21102e52 100644 --- a/i18n/ru.toml +++ b/i18n/ru.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Ваш комментарий ..." # === Assets === [assets] +table = "Таблица" search = "Поиск" searchPlaceholder = "Поиск заголовков или содержимого ..." searchIn = "Поиск в {{ . }}" diff --git a/i18n/sr.toml b/i18n/sr.toml index dd26c300..8b265804 100644 --- a/i18n/sr.toml +++ b/i18n/sr.toml @@ -88,6 +88,7 @@ valinePlaceholder = "Ваш коментар ..." # === Assets === [assets] +table = "Табела" search = "Претрага" searchPlaceholder = "Претражи наслове или садржај..." searchIn = "Претражи у {{ . }}" diff --git a/i18n/ur.toml b/i18n/ur.toml index 92e21313..695db332 100644 --- a/i18n/ur.toml +++ b/i18n/ur.toml @@ -89,6 +89,7 @@ valinePlaceholder = "آپ کا تبصرہ ..." # === Assets === [assets] +table = "جدول" search = "تلاش" searchPlaceholder = "عنوان یا مواد میں تلاش کریں ..." searchIn = "{{ . }} میں تلاش کریں" diff --git a/i18n/vi.toml b/i18n/vi.toml index c7800d1c..0d157c2f 100644 --- a/i18n/vi.toml +++ b/i18n/vi.toml @@ -87,6 +87,7 @@ valinePlaceholder = "Bình luận của bạn ..." # === Assets === [assets] +table = "Bảng" search = "Tìm kiếm" searchPlaceholder = "Tìm tiêu đề hoặc nội dung..." searchIn = "Tìm kiếm trong {{ . }}" diff --git a/i18n/zh-CN.toml b/i18n/zh-CN.toml index 3bf00c53..c9b52c97 100644 --- a/i18n/zh-CN.toml +++ b/i18n/zh-CN.toml @@ -86,6 +86,7 @@ valinePlaceholder = "你的评论……" # === Assets === [assets] +table = "表" search = "搜索" searchPlaceholder = "搜索文章标题或内容……" searchIn = "在 {{ . }} 中搜索" diff --git a/i18n/zh-TW.toml b/i18n/zh-TW.toml index cd08eeba..ba01ef9c 100644 --- a/i18n/zh-TW.toml +++ b/i18n/zh-TW.toml @@ -86,6 +86,7 @@ valinePlaceholder = "你的評論……" # === Assets === [assets] +table = "表" search = "搜尋" searchPlaceholder = "搜尋文章標題或內容……" searchIn = "在 {{ . }} 中搜尋" diff --git a/layouts/_markup/render-table.html b/layouts/_markup/render-table.html index ca1d4c00..5f25da4b 100644 --- a/layouts/_markup/render-table.html +++ b/layouts/_markup/render-table.html @@ -1,15 +1,40 @@ {{- /* - Add a table wrapper to better style overflow tables - The reset of the template is the same as the default render-table.html + Custom table render hook for FixIt theme. + - Wraps tables in a scrollable container + - Supports auto-numbering via CSS counters (config: params.table.number) + - Supports client-side sorting (config: params.table.sort) + - Supports table captions via Markdown attribute {caption="..."} + - Per-table override via Markdown attributes (e.g., {number=false, sort=false}) + See https://gohugo.io/render-hooks/tables/ */ -}} +{{- $pageConfig := dict "Page" .Page "Key" "table" | partial "function/param.html" | merge .Attributes -}} +{{- $config := .Attributes | merge $pageConfig -}} +{{- $caption := .Attributes.caption -}} + +{{- $attrs := "" -}} +{{- if $config.number }} + {{- $attrs = printf "%s data-table-numbered" $attrs }} +{{- end -}} +{{- if $config.sort }} + {{- $attrs = printf "%s data-sortable" $attrs }} + {{- .Page.Store.Set "hasTableSort" true }} +{{- end -}} +{{- range $k, $v := .Attributes }} + {{- if and $v (ne $k "number") (ne $k "caption") (ne $k "sort") }} + {{- $attrs = printf "%s %s=%q" $attrs $k $v }} + {{- end }} +{{- end -}} +{{- $attrs = partial "function/trim.html" $attrs -}} +
- +
+ {{- if or $config.number $caption }} + + {{- end -}} {{- range .THead }} diff --git a/layouts/_partials/base/assets.html b/layouts/_partials/base/assets.html index fa1e5f8b..8e24de2d 100644 --- a/layouts/_partials/base/assets.html +++ b/layouts/_partials/base/assets.html @@ -267,6 +267,11 @@ {{- dict "Source" "js/lib/watermark.ts" "Build" true "Fingerprint" $fingerprint "Defer" true | dict "Page" . "Data" | partial "store/script.html" -}} {{- end -}} +{{- /* Table Sort */ -}} +{{- if .Store.Get "hasTableSort" -}} + {{- dict "Source" "js/lib/table-sort.ts" "Build" true "Fingerprint" $fingerprint "Defer" true | dict "Page" . "Data" | partial "store/script.html" -}} +{{- end -}} + {{- /* Content Decryption */ -}} {{- $encryptPartial := .Store.Get "hasEncryptor" -}} {{- if .Params.password | or $encryptPartial -}}
+ {{- if $config.number }}{{ end -}} + {{- with $caption }}{{ . }}{{ end -}} +