asciidoctor-carousels 0.1.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.adoc +10 -0
- data/LICENSE +21 -0
- data/README.adoc +304 -0
- data/asciidoctor-carousels.gemspec +38 -0
- data/data/css/carousels.css +162 -0
- data/data/js/carousels.js +124 -0
- data/design.adoc +353 -0
- data/lib/asciidoctor/carousels/block.rb +189 -0
- data/lib/asciidoctor/carousels/docinfo.rb +51 -0
- data/lib/asciidoctor/carousels/extensions.rb +50 -0
- data/lib/asciidoctor/carousels/version.rb +7 -0
- data/lib/asciidoctor/carousels.rb +5 -0
- data/lib/asciidoctor-carousels.rb +3 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f6624de15321b8a6228b276a27716201e2226260dd73d326bf80e0394be80a76
|
|
4
|
+
data.tar.gz: 9f4958a66c8cc54a9f80406e1c51f2c59a4ec4a665992f28e8f46c934a0cc191
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9d5b695dab6c1c90c21cfa43c54b6cda60a6164cb47d66d4eb1c00849d9168a9279fe4262ce91dd22fae8cbcf135f58cea7bd14bbae671f444019fdc78f925ce
|
|
7
|
+
data.tar.gz: 52fc451ad1803c4987a5ac126f463c9696425bdf5a373d9a5c687cb8594035919d4a791288ec79596e9d4be07295dbd47192dfc368cee074278be97c08979795
|
data/CHANGELOG.adoc
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
= Changelog
|
|
2
|
+
|
|
3
|
+
This document provides a summary of notable changes to the Asciidoctor Carousels project by release.
|
|
4
|
+
|
|
5
|
+
== 0.1.0.alpha.1 (unreleased)
|
|
6
|
+
|
|
7
|
+
* Initial implementation of the `carousel` block extension.
|
|
8
|
+
* Add support for image blocks, inline image paragraphs, and Asciidoctor Diagram blocks, block macros, and inline macros.
|
|
9
|
+
* Add responsive stylesheet and dependency-free script with autoplay, loop, controls, indicators, keyboard, touch, and fade support.
|
|
10
|
+
* Add Ruby gem packaging and RSpec test suite.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Asciidoctor Carousels contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.adoc
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
= Asciidoctor Carousels
|
|
2
|
+
|
|
3
|
+
An Asciidoctor extension that adds a carousel block to the AsciiDoc syntax.
|
|
4
|
+
It turns a sequence of images into a responsive, accessible image carousel (or slideshow).
|
|
5
|
+
|
|
6
|
+
NOTE: This extension is intended for HTML backends (e.g., `html5`).
|
|
7
|
+
For all other backends, the custom block enclosure is discarded and its visual content is converted normally.
|
|
8
|
+
|
|
9
|
+
== Overview
|
|
10
|
+
|
|
11
|
+
Each carousel is constructed from visual blocks enclosed in an example block annotated with the `carousel` style.
|
|
12
|
+
Every child block that resolves to a single image becomes one slide.
|
|
13
|
+
The following child blocks are supported:
|
|
14
|
+
|
|
15
|
+
* block image macro (`image::`)
|
|
16
|
+
* Asciidoctor Diagram block (e.g., `[plantuml]`)
|
|
17
|
+
* Asciidoctor Diagram block macro (e.g., `plantuml::file.puml[]`)
|
|
18
|
+
* Asciidoctor Diagram inline macro on its own paragraph (e.g., `plantuml:file.puml[]`)
|
|
19
|
+
* any other paragraph that contains a single inline image (e.g., `+image:logo.svg[]+`)
|
|
20
|
+
|
|
21
|
+
The extension does not redefine image or diagram processing.
|
|
22
|
+
It delegates each slide to the normal AsciiDoc converters, so existing image and diagram attributes continue to work.
|
|
23
|
+
|
|
24
|
+
== Install
|
|
25
|
+
|
|
26
|
+
=== Using gem command
|
|
27
|
+
|
|
28
|
+
$ gem install asciidoctor-carousels
|
|
29
|
+
|
|
30
|
+
=== Using Bundler
|
|
31
|
+
|
|
32
|
+
Create a [.path]_Gemfile_ in your project:
|
|
33
|
+
|
|
34
|
+
.Gemfile
|
|
35
|
+
[,ruby]
|
|
36
|
+
----
|
|
37
|
+
source 'https://rubygems.org'
|
|
38
|
+
|
|
39
|
+
gem 'asciidoctor-carousels'
|
|
40
|
+
----
|
|
41
|
+
|
|
42
|
+
Then install the dependencies:
|
|
43
|
+
|
|
44
|
+
$ bundle
|
|
45
|
+
|
|
46
|
+
== Syntax
|
|
47
|
+
|
|
48
|
+
The simplest carousel contains two or more block image macros.
|
|
49
|
+
|
|
50
|
+
.basic.adoc
|
|
51
|
+
[,asciidoc]
|
|
52
|
+
----
|
|
53
|
+
[carousel]
|
|
54
|
+
====
|
|
55
|
+
.Sunset over the bay
|
|
56
|
+
image::images/sunset.jpg[Sunset,800,600]
|
|
57
|
+
|
|
58
|
+
.Beach at noon
|
|
59
|
+
image::images/beach.jpg[Beach,800,600]
|
|
60
|
+
|
|
61
|
+
.City at dusk
|
|
62
|
+
image::images/city.jpg[City,800,600]
|
|
63
|
+
====
|
|
64
|
+
----
|
|
65
|
+
|
|
66
|
+
The optional block title above each image becomes that slide's caption.
|
|
67
|
+
Within a carousel, those titles are not numbered as figures.
|
|
68
|
+
|
|
69
|
+
=== Images included from external files
|
|
70
|
+
|
|
71
|
+
You can use an include directive to load a sequence of slides.
|
|
72
|
+
|
|
73
|
+
.carousel-with-include.adoc
|
|
74
|
+
[,asciidoc]
|
|
75
|
+
----
|
|
76
|
+
[carousel]
|
|
77
|
+
====
|
|
78
|
+
\include::gallery/slides.adoc[]
|
|
79
|
+
====
|
|
80
|
+
----
|
|
81
|
+
|
|
82
|
+
The included file contains the image macros:
|
|
83
|
+
|
|
84
|
+
.gallery/slides.adoc
|
|
85
|
+
[,asciidoc]
|
|
86
|
+
----
|
|
87
|
+
.Sunset over the bay
|
|
88
|
+
image::sunset.jpg[Sunset,800,600]
|
|
89
|
+
|
|
90
|
+
.Beach at noon
|
|
91
|
+
image::beach.jpg[Beach,800,600]
|
|
92
|
+
----
|
|
93
|
+
|
|
94
|
+
=== Using Asciidoctor Diagram
|
|
95
|
+
|
|
96
|
+
Load Asciidoctor Diagram alongside this extension and use any of its block forms as a slide.
|
|
97
|
+
|
|
98
|
+
.diagrams.adoc
|
|
99
|
+
[,asciidoc]
|
|
100
|
+
----
|
|
101
|
+
[carousel]
|
|
102
|
+
====
|
|
103
|
+
.Flow diagram
|
|
104
|
+
[plantuml,target=flow,format=svg]
|
|
105
|
+
....
|
|
106
|
+
@startuml
|
|
107
|
+
Alice -> Bob: hello
|
|
108
|
+
@enduml
|
|
109
|
+
....
|
|
110
|
+
|
|
111
|
+
.Sequence diagram
|
|
112
|
+
plantuml::diagrams/sequence.puml[format=svg]
|
|
113
|
+
|
|
114
|
+
.Overview diagram
|
|
115
|
+
plantuml:diagrams/overview.puml[format=svg]
|
|
116
|
+
====
|
|
117
|
+
----
|
|
118
|
+
|
|
119
|
+
Diagram attributes such as `target`, `format`, `server-url`, and `svg-type` are passed through to Asciidoctor Diagram unchanged.
|
|
120
|
+
If a diagram outputs a text format (e.g., `txt`), the carousel falls back to a normal example block.
|
|
121
|
+
|
|
122
|
+
== Options
|
|
123
|
+
|
|
124
|
+
Options and named attributes are assigned on the carousel block using standard AsciiDoc block attribute syntax.
|
|
125
|
+
|
|
126
|
+
.carousel-options.adoc
|
|
127
|
+
[,asciidoc]
|
|
128
|
+
----
|
|
129
|
+
[carousel#tour.hero%autoplay%loop,interval=3000,active-slide=2,height=400px]
|
|
130
|
+
====
|
|
131
|
+
image::images/a.jpg[First]
|
|
132
|
+
image::images/b.jpg[Second]
|
|
133
|
+
image::images/c.jpg[Third]
|
|
134
|
+
====
|
|
135
|
+
----
|
|
136
|
+
|
|
137
|
+
=== Named attributes
|
|
138
|
+
|
|
139
|
+
[cols="1,3"]
|
|
140
|
+
|===
|
|
141
|
+
|Attribute |Description
|
|
142
|
+
|
|
143
|
+
|`interval`
|
|
144
|
+
|Autoplay interval in milliseconds. Defaults to `5000`.
|
|
145
|
+
|
|
146
|
+
|`active-slide`
|
|
147
|
+
|Initial slide number, starting at `1`. Defaults to `1`.
|
|
148
|
+
|
|
149
|
+
|`height`
|
|
150
|
+
|Optional stage height as a CSS length (e.g., `400px` or `24rem`).
|
|
151
|
+
|
|
152
|
+
|`width`
|
|
153
|
+
|Optional stage width as a CSS length.
|
|
154
|
+
|
|
155
|
+
|`aspect-ratio`
|
|
156
|
+
|Optional stage aspect-ratio hint (e.g., `16/9`).
|
|
157
|
+
|===
|
|
158
|
+
|
|
159
|
+
=== Behavior options
|
|
160
|
+
|
|
161
|
+
[cols="1,3"]
|
|
162
|
+
|===
|
|
163
|
+
|Option |Description
|
|
164
|
+
|
|
165
|
+
|`autoplay`
|
|
166
|
+
|Advance slides automatically using `interval`.
|
|
167
|
+
|
|
168
|
+
|`loop`
|
|
169
|
+
|Wrap from the last slide to the first. Enabled by default.
|
|
170
|
+
|
|
171
|
+
|`noloop`
|
|
172
|
+
|Disable looping.
|
|
173
|
+
|
|
174
|
+
|`fade`
|
|
175
|
+
|Use a crossfade transition instead of a horizontal slide transition.
|
|
176
|
+
|
|
177
|
+
|`pause-on-hover`
|
|
178
|
+
|Pause autoplay while the pointer is over the carousel.
|
|
179
|
+
|
|
180
|
+
|`keyboard`
|
|
181
|
+
|Enable left and right arrow keys. Enabled by default.
|
|
182
|
+
|
|
183
|
+
|`nokeyboard`
|
|
184
|
+
|Disable keyboard navigation.
|
|
185
|
+
|
|
186
|
+
|`touch`
|
|
187
|
+
|Enable swipe navigation on touch devices. Enabled by default.
|
|
188
|
+
|
|
189
|
+
|`notouch`
|
|
190
|
+
|Disable touch navigation.
|
|
191
|
+
|
|
192
|
+
|`controls`
|
|
193
|
+
|Show previous and next controls. Enabled by default.
|
|
194
|
+
|
|
195
|
+
|`nocontrols`
|
|
196
|
+
|Hide the previous and next controls.
|
|
197
|
+
|
|
198
|
+
|`indicators`
|
|
199
|
+
|Show slide indicators. Enabled by default.
|
|
200
|
+
|
|
201
|
+
|`noindicators`
|
|
202
|
+
|Hide the slide indicators.
|
|
203
|
+
|===
|
|
204
|
+
|
|
205
|
+
== Usage
|
|
206
|
+
|
|
207
|
+
=== CLI
|
|
208
|
+
|
|
209
|
+
$ asciidoctor -r asciidoctor-carousels basic.adoc
|
|
210
|
+
|
|
211
|
+
To use diagrams in a carousel, load Asciidoctor Diagram as well:
|
|
212
|
+
|
|
213
|
+
$ asciidoctor -r asciidoctor-carousels -r asciidoctor-diagram diagrams.adoc
|
|
214
|
+
|
|
215
|
+
You can specify an alternate stylesheet using the `carousel-stylesheet` document attribute.
|
|
216
|
+
|
|
217
|
+
$ asciidoctor -r asciidoctor-carousels -a carousel-stylesheet=my-carousel.css basic.adoc
|
|
218
|
+
|
|
219
|
+
The value of `carousel-stylesheet` is handled in the same way as the built-in `stylesheet` document attribute.
|
|
220
|
+
|
|
221
|
+
=== API
|
|
222
|
+
|
|
223
|
+
Require the extension with either of these two forms:
|
|
224
|
+
|
|
225
|
+
[,ruby]
|
|
226
|
+
----
|
|
227
|
+
require 'asciidoctor'
|
|
228
|
+
require 'asciidoctor-carousels'
|
|
229
|
+
----
|
|
230
|
+
|
|
231
|
+
[,ruby]
|
|
232
|
+
----
|
|
233
|
+
require 'asciidoctor'
|
|
234
|
+
require 'asciidoctor/carousels'
|
|
235
|
+
----
|
|
236
|
+
|
|
237
|
+
You can also register the extension with a scoped registry:
|
|
238
|
+
|
|
239
|
+
[,ruby]
|
|
240
|
+
----
|
|
241
|
+
require 'asciidoctor'
|
|
242
|
+
require 'asciidoctor/carousels/extensions'
|
|
243
|
+
|
|
244
|
+
registry = Asciidoctor::Extensions.create
|
|
245
|
+
Asciidoctor::Carousels::Extensions.register registry
|
|
246
|
+
|
|
247
|
+
Asciidoctor.convert_file 'basic.adoc', extension_registry: registry, safe: :safe
|
|
248
|
+
----
|
|
249
|
+
|
|
250
|
+
== How it Works
|
|
251
|
+
|
|
252
|
+
The extension transforms the visual blocks inside the example block into a single carousel block that is a specialization of an open block.
|
|
253
|
+
The example block enclosure is discarded.
|
|
254
|
+
|
|
255
|
+
The carousel interface consists of:
|
|
256
|
+
|
|
257
|
+
* a stage element that clips the visible slide,
|
|
258
|
+
* a track element that contains the slides,
|
|
259
|
+
* one slide element per visual block,
|
|
260
|
+
* optional previous and next controls,
|
|
261
|
+
* optional slide indicators.
|
|
262
|
+
|
|
263
|
+
The supporting stylesheet and script are added only when producing a standalone HTML document.
|
|
264
|
+
The stylesheet is added to the end of the `<head>` element and the script is added to the end of the `<body>` element.
|
|
265
|
+
By default, both assets are embedded into the HTML.
|
|
266
|
+
If the `linkcss` attribute is set, the HTML links to these assets instead.
|
|
267
|
+
*In that case, copy the assets from the [.path]_data_ folder of the gem to the linked location.*
|
|
268
|
+
|
|
269
|
+
The first slide is selected by default, or the slide specified by `active-slide`.
|
|
270
|
+
|
|
271
|
+
== Managing Assets
|
|
272
|
+
|
|
273
|
+
The carousel is activated at runtime by a stylesheet and a script.
|
|
274
|
+
Both assets are embedded by default, so there is normally nothing else to do.
|
|
275
|
+
|
|
276
|
+
If `linkcss` is set, the linked assets must be copied to the expected location.
|
|
277
|
+
The files are [.path]_data/css/carousels.css_ and [.path]_data/js/carousels.js_ in the gem or project repository.
|
|
278
|
+
Copy them as [.path]_asciidoctor-carousels.css_ and [.path]_asciidoctor-carousels.js_ in the directory of the HTML document, or adjust `stylesdir` and `scriptsdir` accordingly.
|
|
279
|
+
|
|
280
|
+
== Development
|
|
281
|
+
|
|
282
|
+
=== Run the tests
|
|
283
|
+
|
|
284
|
+
The test suite is located in the [.path]_spec_ directory and uses RSpec.
|
|
285
|
+
|
|
286
|
+
$ bundle exec rspec
|
|
287
|
+
|
|
288
|
+
=== Lint the code
|
|
289
|
+
|
|
290
|
+
$ bundle exec rake lint
|
|
291
|
+
|
|
292
|
+
=== Build the gem
|
|
293
|
+
|
|
294
|
+
$ bundle exec rake build
|
|
295
|
+
|
|
296
|
+
== Copyright and License
|
|
297
|
+
|
|
298
|
+
Copyright (C) 2026-present Asciidoctor Carousels contributors.
|
|
299
|
+
Use of this software is granted under the terms of the MIT License.
|
|
300
|
+
See the link:LICENSE[LICENSE] file for the full license text.
|
|
301
|
+
|
|
302
|
+
== Trademarks
|
|
303
|
+
|
|
304
|
+
AsciiDoc(R) is a trademark of the Eclipse Foundation, Inc.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require_relative 'lib/asciidoctor/carousels/version'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
require 'asciidoctor/carousels/version'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = 'asciidoctor-carousels'
|
|
9
|
+
s.version = Asciidoctor::Carousels::VERSION
|
|
10
|
+
s.summary = 'An Asciidoctor extension that adds a carousel block to the AsciiDoc syntax.'
|
|
11
|
+
s.description = 'An Asciidoctor extension that adds a carousel block to the AsciiDoc syntax. The carousel is constructed from visual blocks, such as image macros and Asciidoctor Diagram blocks, enclosed in an example block marked with the carousel style.'
|
|
12
|
+
s.authors = ['Asciidoctor Carousels contributors']
|
|
13
|
+
s.email = 'noreply@asciidoctor.org'
|
|
14
|
+
s.homepage = 'https://asciidoctor.org'
|
|
15
|
+
s.license = 'MIT'
|
|
16
|
+
# NOTE required ruby version is informational only; it's not enforced since it can't be overridden and can cause builds to break
|
|
17
|
+
#s.required_ruby_version = '>= 2.3.0'
|
|
18
|
+
s.metadata = {
|
|
19
|
+
'bug_tracker_uri' => 'https://github.com/asciidoctor/asciidoctor-carousels/issues',
|
|
20
|
+
'changelog_uri' => 'https://github.com/asciidoctor/asciidoctor-carousels/blob/main/CHANGELOG.adoc',
|
|
21
|
+
'mailing_list_uri' => 'https://chat.asciidoctor.org',
|
|
22
|
+
'source_code_uri' => 'https://github.com/asciidoctor/asciidoctor-carousels'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# NOTE the logic to build the list of files is designed to produce a usable package even when the git command is not available.
|
|
26
|
+
# The directory listing is merged in so the gem can also be built from an uncommitted working tree.
|
|
27
|
+
begin
|
|
28
|
+
files = `git ls-files -z`.split ?\0
|
|
29
|
+
files = files.empty? ? Dir['**/*'] : (files + Dir['**/*']).uniq
|
|
30
|
+
rescue
|
|
31
|
+
files = Dir['**/*']
|
|
32
|
+
end
|
|
33
|
+
s.files = files.grep %r/^(?:(?:data|lib)\/.+|LICENSE|(?:CHANGELOG|README|design)\.adoc|#{s.name}\.gemspec)$/
|
|
34
|
+
s.executables = (files.grep %r/^bin\//).map {|f| File.basename f }
|
|
35
|
+
s.require_paths = ['lib']
|
|
36
|
+
|
|
37
|
+
s.add_runtime_dependency 'asciidoctor', ['>= 2.0.0', '< 3.0.0']
|
|
38
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*! Asciidoctor Carousels | Copyright (c) 2026-present Asciidoctor Carousels contributors | MIT License */
|
|
2
|
+
.carousel {
|
|
3
|
+
margin-bottom: 1.25em;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.carousel-stage {
|
|
7
|
+
position: relative;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
background-color: #fff;
|
|
10
|
+
border: 1px solid #dcdcdc;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.carousel-track {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: stretch;
|
|
16
|
+
transition: transform 300ms ease-in-out;
|
|
17
|
+
will-change: transform;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.carousel-slide {
|
|
21
|
+
flex: 0 0 100%;
|
|
22
|
+
min-width: 100%;
|
|
23
|
+
position: relative;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.carousel-slide.is-hidden {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.carousel-slide .imageblock,
|
|
31
|
+
.carousel-slide .paragraph {
|
|
32
|
+
margin: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.carousel-slide .imageblock .content,
|
|
36
|
+
.carousel-slide .paragraph p {
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.carousel-slide img {
|
|
41
|
+
display: block;
|
|
42
|
+
width: 100%;
|
|
43
|
+
max-width: 100%;
|
|
44
|
+
height: auto;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.carousel-slide-caption {
|
|
49
|
+
position: absolute;
|
|
50
|
+
top: 0;
|
|
51
|
+
left: 0;
|
|
52
|
+
z-index: 1;
|
|
53
|
+
max-width: calc(100% - 2rem);
|
|
54
|
+
padding: 0.4rem 0.75rem;
|
|
55
|
+
background-color: rgba(0, 0, 0, 0.55);
|
|
56
|
+
color: #fff;
|
|
57
|
+
font-size: 0.875em;
|
|
58
|
+
line-height: 1.4;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.carousel-control {
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 50%;
|
|
65
|
+
z-index: 2;
|
|
66
|
+
width: 2.5em;
|
|
67
|
+
height: 2.5em;
|
|
68
|
+
padding: 0;
|
|
69
|
+
border: 1px solid #dcdcdc;
|
|
70
|
+
border-radius: 50%;
|
|
71
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
72
|
+
color: rgba(0, 0, 0, 0.7);
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
transform: translateY(-50%);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.carousel-control:hover,
|
|
78
|
+
.carousel-control:focus-visible {
|
|
79
|
+
background-color: #fff;
|
|
80
|
+
color: #2156a5;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.carousel-control-prev {
|
|
84
|
+
left: 0.5rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.carousel-control-next {
|
|
88
|
+
right: 0.5rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.carousel-indicators {
|
|
92
|
+
position: absolute;
|
|
93
|
+
right: 0;
|
|
94
|
+
bottom: 0;
|
|
95
|
+
left: 0;
|
|
96
|
+
z-index: 2;
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-wrap: wrap;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
gap: 0.25rem;
|
|
101
|
+
padding: 0.5rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.carousel-indicator {
|
|
105
|
+
width: 0.75rem;
|
|
106
|
+
height: 0.75rem;
|
|
107
|
+
padding: 0;
|
|
108
|
+
border: 1px solid #dcdcdc;
|
|
109
|
+
border-radius: 50%;
|
|
110
|
+
background-color: rgba(255, 255, 255, 0.7);
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.carousel-indicator:hover,
|
|
115
|
+
.carousel-indicator:focus-visible,
|
|
116
|
+
.carousel-indicator.is-active {
|
|
117
|
+
background-color: #2156a5;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.carousel-indicator span {
|
|
121
|
+
display: block;
|
|
122
|
+
font-size: 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.carousel.is-fade .carousel-track {
|
|
126
|
+
display: block;
|
|
127
|
+
transition: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.carousel.is-fade .carousel-slide {
|
|
131
|
+
position: absolute;
|
|
132
|
+
top: 0;
|
|
133
|
+
right: 0;
|
|
134
|
+
bottom: 0;
|
|
135
|
+
left: 0;
|
|
136
|
+
display: block;
|
|
137
|
+
opacity: 0;
|
|
138
|
+
pointer-events: none;
|
|
139
|
+
transition: opacity 300ms ease-in-out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.carousel.is-fade .carousel-slide.is-active {
|
|
143
|
+
position: relative;
|
|
144
|
+
opacity: 1;
|
|
145
|
+
pointer-events: auto;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.carousel.is-fade .carousel-stage {
|
|
149
|
+
height: var(--carousel-height, auto);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.carousel-stage[style*='--carousel-height'] .carousel-slide img {
|
|
153
|
+
height: var(--carousel-height, auto);
|
|
154
|
+
object-fit: contain;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@media (max-width: 767px) {
|
|
158
|
+
.carousel-control {
|
|
159
|
+
width: 2.25em;
|
|
160
|
+
height: 2.25em;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
;(function () { /*! Asciidoctor Carousels | Copyright (c) 2026-present Asciidoctor Carousels contributors | MIT License */
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
var forEach = Array.prototype.forEach
|
|
5
|
+
var slice = Array.prototype.slice
|
|
6
|
+
|
|
7
|
+
init(document.querySelectorAll('.carousel'))
|
|
8
|
+
|
|
9
|
+
function init (carousels) {
|
|
10
|
+
forEach.call(carousels, function (carousel) {
|
|
11
|
+
var stage = carousel.querySelector('.carousel-stage')
|
|
12
|
+
var track = carousel.querySelector('.carousel-track')
|
|
13
|
+
var slides = slice.call(carousel.querySelectorAll('.carousel-slide'))
|
|
14
|
+
if (!stage || !track || !slides.length) return
|
|
15
|
+
|
|
16
|
+
var config = stage.dataset || {}
|
|
17
|
+
var activeIndex = clamp(parseInt(config.activeSlide || '1', 10) - 1, 0, slides.length - 1)
|
|
18
|
+
var interval = parsePositiveInt(config.interval || '5000', 5000)
|
|
19
|
+
var autoplay = config.autoplay === 'true'
|
|
20
|
+
var loop = config.loop !== 'false'
|
|
21
|
+
var keyboard = config.keyboard !== 'false'
|
|
22
|
+
var touch = config.touch !== 'false'
|
|
23
|
+
var pauseOnHover = config.pauseOnHover === 'true'
|
|
24
|
+
var isFade = carousel.classList.contains('is-fade')
|
|
25
|
+
var timer
|
|
26
|
+
|
|
27
|
+
slides.forEach(function (slide) { slide.classList.remove('is-hidden') })
|
|
28
|
+
if (keyboard) stage.setAttribute('tabindex', '0')
|
|
29
|
+
|
|
30
|
+
function setActive (index, skipFocus) {
|
|
31
|
+
activeIndex = clamp(index, 0, slides.length - 1)
|
|
32
|
+
slides.forEach(function (slide, idx) {
|
|
33
|
+
var active = idx === activeIndex
|
|
34
|
+
slide.classList[active ? 'add' : 'remove']('is-active')
|
|
35
|
+
slide.setAttribute('aria-hidden', active ? 'false' : 'true')
|
|
36
|
+
})
|
|
37
|
+
if (!isFade) track.style.transform = 'translateX(-' + activeIndex * 100 + '%)'
|
|
38
|
+
forEach.call(carousel.querySelectorAll('.carousel-indicator'), function (indicator, idx) {
|
|
39
|
+
indicator.classList[idx === activeIndex ? 'add' : 'remove']('is-active')
|
|
40
|
+
})
|
|
41
|
+
if (!skipFocus && keyboard) stage.focus()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function next () {
|
|
45
|
+
var target = activeIndex + 1
|
|
46
|
+
if (target >= slides.length) target = loop ? 0 : slides.length - 1
|
|
47
|
+
setActive(target)
|
|
48
|
+
if (!loop && target === slides.length - 1 && timer) stop()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function prev () {
|
|
52
|
+
var target = activeIndex - 1
|
|
53
|
+
if (target < 0) target = loop ? slides.length - 1 : 0
|
|
54
|
+
setActive(target)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function go (index) {
|
|
58
|
+
setActive(index)
|
|
59
|
+
stop()
|
|
60
|
+
if (autoplay) start()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function start () {
|
|
64
|
+
if (timer) stop()
|
|
65
|
+
timer = window.setInterval(next, interval)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function stop () {
|
|
69
|
+
if (!timer) return
|
|
70
|
+
window.clearInterval(timer)
|
|
71
|
+
timer = undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
forEach.call(carousel.querySelectorAll('.carousel-control'), function (control) {
|
|
75
|
+
control.addEventListener('click', function () {
|
|
76
|
+
control.dataset.carouselAction === 'prev' ? prev() : next()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
forEach.call(carousel.querySelectorAll('.carousel-indicator'), function (indicator) {
|
|
81
|
+
indicator.addEventListener('click', function () {
|
|
82
|
+
go(parseInt(indicator.dataset.carouselIndex, 10))
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
if (keyboard) {
|
|
87
|
+
stage.addEventListener('keydown', function (e) {
|
|
88
|
+
if (e.key === 'ArrowLeft') prev()
|
|
89
|
+
else if (e.key === 'ArrowRight') next()
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (touch) {
|
|
94
|
+
var startX
|
|
95
|
+
stage.addEventListener('pointerdown', function (e) { startX = e.clientX })
|
|
96
|
+
stage.addEventListener('pointerup', function (e) {
|
|
97
|
+
if (startX === undefined) return
|
|
98
|
+
var delta = e.clientX - startX
|
|
99
|
+
if (Math.abs(delta) > 30) delta < 0 ? next() : prev()
|
|
100
|
+
startX = undefined
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (autoplay) start()
|
|
105
|
+
if (pauseOnHover) {
|
|
106
|
+
carousel.addEventListener('mouseenter', stop)
|
|
107
|
+
carousel.addEventListener('mouseleave', function () { if (autoplay) start() })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setActive(activeIndex, true)
|
|
111
|
+
carousel.classList.remove('is-loading')
|
|
112
|
+
carousel.classList.add('is-loaded')
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function clamp (value, min, max) {
|
|
117
|
+
return Math.min(max, Math.max(min, value))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parsePositiveInt (value, fallback) {
|
|
121
|
+
var parsed = parseInt(value, 10)
|
|
122
|
+
return parsed > 0 ? parsed : fallback
|
|
123
|
+
}
|
|
124
|
+
})()
|