Commit Diff


commit - 696d8188d3a1f41988285e7d3f1df6505c59c017
commit + e774592a186abac652c858f5fff5c6228bc1ffd8
blob - e61803b676a8709506ac851a1cac86c1a8d911c8
blob + d15dff7b5ff689dc2644879736a52743c47b151b
--- README.md
+++ README.md
@@ -264,6 +264,57 @@ SVG дополнительно минифицируеÑ
 ) -}}
 ```
 
+### slideshow
+
+Отображает слайдшоу, создавая простую HTML-структуру:
+
+```html
+<div class="slideshow">
+  <img class="slideshow-slide">
+  <img class="slideshow-slide">
+</div>
+```
+
+Каждое изображение отображается через partial `image`, поэтому изображения, которые Hugo умеет обрабатывать, получают адаптивные варианты.
+
+`images` может быть одиночной строкой — слайдшоу из одного изображения — или списком. Элемент списка может быть:
+
+* строкой — путём к ресурсу страницы, глобальному ресурсу, путём от корня сайта или абсолютным URL;
+* Hugo image resource;
+* `dict` с ключами `image` (ресурс или путь), `src`, `alt`, `title` и `attributes` (атрибуты `<img>`).
+
+Относительный путь сначала ищется в ресурсах страницы, затем в глобальных ресурсах. Если разрешить его не удалось, сборка завершается ошибкой. Пути от корня сайта и абсолютные URL передаются в `src` без изменений.
+
+Атрибуты контейнера передаются через `attributes`; переданный `class` дополняет, а не заменяет класс `slideshow`. Для изображений дополнительно устанавливается класс `slideshow-slide`.
+
+Всем слайдам устанавливается `decoding="async"`, слайдам после первого — `loading="lazy"`: скрытые слайды не загружаются заранее. Значения переопределяются через `attributes` изображения.
+
+Контейнеру устанавливается CSS-переменная `--slide-count`, каждому слайду — `--slide-index` (с нуля). Они позволяют сайту строить анимацию перелистывания без знания разметки, например:
+
+```css
+.slideshow-slide {
+  animation-delay: calc(var(--slide-index) * 4s);
+  animation-duration: calc(var(--slide-count) * 4s);
+}
+```
+
+Переданный в `attributes` `style` сохраняется; переменные дописываются к нему.
+
+Параметры `widths`, `sizes` и `mode` передаются каждому изображению; без них используются настройки `radium.images`.
+
+Например:
+
+```go-html-template
+{{- partial "slideshow.html" (dict
+  "page" .
+  "images" (slice
+    "photo-1.jpg"
+    (dict "src" "photo-2.jpg" "alt" "Описание")
+  )
+  "attributes" (dict "class" "hero")
+) -}}
+```
+
 ### publish/security-txt
 
 Генерирует ресурс `.well-known/security.txt` по RFC 9116.
@@ -506,6 +557,40 @@ Person.jsonld
 
 Поддерживает обычную и Markdown-нотацию shortcode.
 
+### slideshow
+
+Создаёт слайдшоу через одноимённый partial.
+
+Изображения передаются внутренним содержимым — по одному пути или URL в строке:
+
+```md
+{{< slideshow >}}
+photo-1.jpg
+photo-2.jpg
+{{< /slideshow >}}
+```
+
+или параметром `images` через запятую:
+
+```md
+{{< slideshow images="photo-1.jpg, photo-2.jpg" />}}
+```
+
+Относительные пути разрешаются в ресурсах страницы и глобальных ресурсах; неразрешимый относительный путь завершает сборку ошибкой. Пути от корня сайта и абсолютные URL используются как есть.
+
+Поддерживаются атрибуты контейнера:
+
+* `class`;
+* `id`;
+* `role`;
+* `tabindex`;
+* `aria-label`;
+* `aria-hidden`.
+
+Также можно передать параметры `widths`, `sizes` и `mode`, управляющие генерацией адаптивных вариантов (см. partial `image`).
+
+Для `alt`, `title` и других атрибутов отдельных изображений используйте partial `slideshow` напрямую.
+
 ### video
 
 Создаёт элемент `<video>`.
blob - /dev/null
blob + 1600c96b4db6dbbcddf90550dc247d0bc913e9df (mode 644)
--- /dev/null
+++ layouts/_partials/slideshow.html
@@ -0,0 +1,205 @@
+{{- $page := .page -}}
+{{- $images := .images | default slice -}}
+{{- $attributes := .attributes | default dict -}}
+
+
+{{/* Container class */}}
+
+{{- $class := "slideshow" -}}
+
+{{- with $attributes.class -}}
+  {{- $class = printf "slideshow %s" . -}}
+{{- end -}}
+
+{{- $attributes = merge $attributes (dict "class" $class) -}}
+
+
+{{/*
+  Expose slide count as a CSS custom property so that site CSS
+  can derive animation timing without knowing the markup.
+
+  Caller style is preserved.
+*/}}
+
+{{- $style := printf "--slide-count: %d" (len $images) -}}
+
+{{- with $attributes.style -}}
+  {{- $style = printf "%s; %s" . $style -}}
+{{- end -}}
+
+{{- $attributes = merge $attributes (dict "style" $style) -}}
+
+
+{{/* A single string is accepted as a one-item slideshow */}}
+
+{{- if eq (printf "%T" $images) "string" -}}
+  {{- $images = slice $images -}}
+{{- end -}}
+
+{{- if not (reflect.IsSlice $images) -}}
+  {{- errorf "slideshow.html: images must be a slice or a string" -}}
+{{- end -}}
+
+{{- if not $images -}}
+  {{- errorf "slideshow.html: images is required" -}}
+{{- end -}}
+
+
+<div{{ partial "attrs.html" $attributes | safeHTMLAttr }}>
+  {{- range $index, $item := $images -}}
+
+
+  {{/*
+    Normalize the item.
+
+    An item can be:
+      - a string: path or URL;
+      - an image resource;
+      - a dict: image, src, alt, title, attributes.
+  */}}
+
+  {{- $image := false -}}
+  {{- $src := "" -}}
+  {{- $imageAttributes := dict -}}
+
+  {{- if reflect.IsMap $item -}}
+
+    {{- $image = $item.image | default false -}}
+
+    {{- if eq (printf "%T" $image) "string" -}}
+      {{- $src = $image -}}
+      {{- $image = false -}}
+    {{- end -}}
+
+    {{- if not $src -}}
+      {{- $src = $item.src | default "" -}}
+    {{- end -}}
+
+    {{- $imageAttributes = $item.attributes | default dict -}}
+
+    {{- if isset $item "alt" -}}
+      {{- $imageAttributes = merge
+        $imageAttributes
+        (dict "alt" $item.alt)
+      -}}
+    {{- end -}}
+
+    {{- if isset $item "title" -}}
+      {{- $imageAttributes = merge
+        $imageAttributes
+        (dict "title" $item.title)
+      -}}
+    {{- end -}}
+
+  {{- else if reflect.IsImageResource $item -}}
+
+    {{- $image = $item -}}
+
+  {{- else if eq (printf "%T" $item) "string" -}}
+
+    {{- $src = $item -}}
+
+  {{- else -}}
+
+    {{- errorf "slideshow.html: unsupported image item %v" $item -}}
+
+  {{- end -}}
+
+
+  {{/*
+    Resolve a relative path against page resources, then global
+    resources. Root-relative paths and absolute URLs are passed
+    through as a plain src.
+  */}}
+
+  {{- if $src -}}
+
+    {{- $url := urls.Parse $src -}}
+
+    {{- if and
+      (not $url.IsAbs)
+      (not $url.Host)
+      (not (strings.HasPrefix $url.Path "/"))
+    -}}
+      {{- $path := strings.TrimPrefix "./" $url.Path -}}
+      {{- $resource := false -}}
+
+      {{- with $page -}}
+        {{- $resource = .Resources.Get $path -}}
+      {{- end -}}
+
+      {{- if not $resource -}}
+        {{- $resource = resources.Get $path -}}
+      {{- end -}}
+
+      {{- if $resource -}}
+        {{- $image = $resource -}}
+        {{- $src = "" -}}
+      {{- else -}}
+        {{- errorf
+          "slideshow.html: image %q not found in page or global resources"
+          $src
+        -}}
+      {{- end -}}
+
+    {{- end -}}
+
+  {{- end -}}
+
+
+  {{/*
+    Hidden slides are lazy-loaded; the first slide stays eager.
+    Caller attributes take precedence.
+  */}}
+
+  {{- $slideDefaults := dict "decoding" "async" -}}
+
+  {{- if gt $index 0 -}}
+    {{- $slideDefaults = merge $slideDefaults (dict "loading" "lazy") -}}
+  {{- end -}}
+
+  {{- $imageAttributes = merge $slideDefaults $imageAttributes -}}
+
+
+  {{/* Slide class */}}
+
+  {{- $class := "slideshow-slide" -}}
+
+  {{- with $imageAttributes.class -}}
+    {{- $class = printf "slideshow-slide %s" . -}}
+  {{- end -}}
+
+  {{- $imageAttributes = merge
+    $imageAttributes
+    (dict "class" $class)
+  -}}
+
+
+{{/*
+  Expose slide position as a CSS custom property; see
+  --slide-count above. Caller style is preserved.
+*/}}
+
+{{- $style := printf "--slide-index: %d" $index -}}
+
+{{- with $imageAttributes.style -}}
+  {{- $style = printf "%s; %s" . $style -}}
+  {{- end -}}
+
+{{- $imageAttributes = merge
+  $imageAttributes
+  (dict "style" $style)
+  -}}
+
+
+  {{- partial "image.html" (dict
+    "image" $image
+    "src" $src
+    "page" $page
+    "attributes" $imageAttributes
+    "widths" $.widths
+    "sizes" $.sizes
+    "mode" $.mode
+  ) -}}
+  {{- end -}}
+</div>
blob - /dev/null
blob + be62dee89fa41946a058bfcd37c26840215dc14c (mode 644)
--- /dev/null
+++ layouts/_shortcodes/slideshow.html
@@ -0,0 +1,66 @@
+{{- $allowed := slice
+  "class"
+  "id"
+  "role"
+  "tabindex"
+  "aria-label"
+  "aria-hidden"
+-}}
+
+{{- $attributes := partial "pick.html" (dict
+  "dict" .Params
+  "allowed" $allowed
+) -}}
+
+
+{{/*
+  Collect images from the images parameter (comma-separated)
+  and/or inner content (one path or URL per line).
+*/}}
+
+{{- $images := slice -}}
+
+{{- with .Get "images" -}}
+  {{- range (split . ",") -}}
+    {{- with . | strings.TrimSpace -}}
+      {{- $images = $images | append . -}}
+    {{- end -}}
+  {{- end -}}
+{{- end -}}
+
+{{- with .Inner -}}
+  {{- range (split . "\n") -}}
+    {{- with . | strings.TrimSpace -}}
+      {{- $images = $images | append . -}}
+    {{- end -}}
+  {{- end -}}
+{{- end -}}
+
+
+{{/* Width ladder: comma-separated string -> slice of ints */}}
+
+{{- $widths := .Get "widths" -}}
+
+{{- with $widths -}}
+  {{- $parsed := slice -}}
+  {{- range (split . ",") -}}
+    {{- with . | strings.TrimSpace -}}
+      {{- $parsed = $parsed | append (int .) -}}
+    {{- end -}}
+  {{- end -}}
+  {{- $widths = $parsed -}}
+{{- end -}}
+
+
+{{- if not $images -}}
+  {{- errorf "The %q shortcode requires at least one image, via the images parameter or inner content. See %s" .Name .Position -}}
+{{- end -}}
+
+{{- partial "slideshow.html" (dict
+  "page" .Page
+  "images" $images
+  "attributes" $attributes
+  "widths" $widths
+  "sizes" (.Get "sizes")
+  "mode" (.Get "mode")
+) -}}