asciidoctor-tabs 1.0.0.beta.2 → 1.0.0.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +34 -0
- data/README.adoc +8 -4
- data/data/css/tabs.css +18 -8
- data/data/js/tabs.js +52 -33
- data/lib/asciidoctor/tabs/block.rb +4 -4
- data/lib/asciidoctor/tabs/docinfo.rb +10 -2
- data/lib/asciidoctor/tabs/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e73997158c47ea046d9c2016a25cac989e0f0e12e013bdc17f0c3cde8128e55f
|
4
|
+
data.tar.gz: 2dd6e9907c7c2498c1ff2226f25d00d654b872644063b02230882a2090f35e45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d530e0bb402fd7aa9346c5dc747efe5224436ef8d2e1fd4139a64fb84ed3f3c044069ba0fb807f12ecfe78104d6665395d979aeb1aac77465cef6bb831b1e0ad
|
7
|
+
data.tar.gz: c1c756f94280e8d70394c13abdfeb541f226b5eb39cfe86b514211781e5ab80bf1208e7faad6162db45571b26801ada288ae656143d074a7184ba40674afac32
|
data/CHANGELOG.adoc
CHANGED
@@ -4,6 +4,40 @@
|
|
4
4
|
This document provides a curated view of the changes to Asciidoctor Tabs per release.
|
5
5
|
For a detailed view of what has changed, refer to the {url-repo}/commits/main[commit history] on GitHub.
|
6
6
|
|
7
|
+
== 1.0.0-beta.4 (2023-05-22) - @mojavelinux
|
8
|
+
|
9
|
+
=== Changed
|
10
|
+
|
11
|
+
* Rework styles for tab to make them compatible with a transition effect; stub in effect in built-in stylesheet
|
12
|
+
* Add tab class to tab element (if missing) rather than overwriting className property to preserve existing class names
|
13
|
+
|
14
|
+
=== Fixed
|
15
|
+
|
16
|
+
* Don't alter state of nested tabs when tab is selected (#55)
|
17
|
+
* Don't wrap tables inside nested tabs with tablecontainer div multiple times (#55)
|
18
|
+
* Fix fallback logic in behavior script when tab is missing ID or does not match a panel
|
19
|
+
|
20
|
+
=== Details
|
21
|
+
|
22
|
+
{url-repo}/releases/tag/v1.0.0-beta.4[git tag] | {url-repo}/compare/v1.0.0-beta.3\...v1.0.0-beta.4[full diff]
|
23
|
+
|
24
|
+
== 1.0.0-beta.3 (2023-02-01) - @mojavelinux
|
25
|
+
|
26
|
+
=== Added
|
27
|
+
|
28
|
+
* Allow storage of sync tab selection to be configured using `tabs-sync-storage-key` and `tabs-sync-storage-scope` document attributes
|
29
|
+
* Add `is-loaded` class to tabs blocks on next tick after initialization for binding transitions (#50)
|
30
|
+
* Allow sync group ID to be specified rather than derived using the `sync-group-id` attribute on the tabs block (#52)
|
31
|
+
|
32
|
+
=== Changed
|
33
|
+
|
34
|
+
* Rename data-sync-group attribute to data-sync-group-id
|
35
|
+
* Don't lowercase sync group ID
|
36
|
+
|
37
|
+
=== Details
|
38
|
+
|
39
|
+
{url-repo}/releases/tag/v1.0.0-beta.3[git tag] | {url-repo}/compare/v1.0.0-beta.2\...v1.0.0-beta.3[full diff]
|
40
|
+
|
7
41
|
== 1.0.0-beta.2 (2023-01-30) - @mojavelinux
|
8
42
|
|
9
43
|
=== Changed
|
data/README.adoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Asciidoctor Tabs
|
2
2
|
Dan Allen <https://github.com/mojavelinux[@mojavelinux]>
|
3
|
-
v1.0.0-beta.
|
3
|
+
v1.0.0-beta.4, 2023-05-22
|
4
4
|
:idprefix:
|
5
5
|
:idseparator: -
|
6
6
|
ifndef::env-github[:icons: font]
|
@@ -19,9 +19,10 @@ See the xref:js/README.adoc[README in the js folder] for details.
|
|
19
19
|
|
20
20
|
== Overview
|
21
21
|
|
22
|
-
Each set of tabs (a "`tabset`") is constructed from a description list (dlist) enclosed in an example block marked with the tabs style (i.e., `[tab]`).
|
23
|
-
|
22
|
+
Each set of tabs (aka a "`tabset`" or tabs block) is constructed from a description list (dlist) enclosed in an example block marked with the tabs style (i.e., `[tab]`).
|
23
|
+
That nested combination of blocks gets translated by this extension into a single tabs block that is a specialization of an open block.
|
24
24
|
|
25
|
+
The tabbed interface produced from this block can help organize information by code language, operating system, or product variant.
|
25
26
|
The benefit of organizing information in this way is that it condenses the use of vertical space by only showing what's relevant to the reader (and thus hiding information that's irrelevant or redundant).
|
26
27
|
The result is that readers enjoy a better user experience when reading your documentation.
|
27
28
|
|
@@ -125,8 +126,10 @@ Tab B:: Contents of tab B in second tabset.
|
|
125
126
|
====
|
126
127
|
----
|
127
128
|
|
128
|
-
|
129
|
+
Only tabs blocks with congruent tablists are synchronized by default.
|
129
130
|
Each unique combination of tabs implicitly creates a new sync group.
|
131
|
+
You can override the sync group ID using the `sync-group-id` attribute on a tabs block to force it to participate in a sync group.
|
132
|
+
By default, the sync group ID is derived from the text of each tab, sorted, then joined on `|` (e.g., `A|B`).
|
130
133
|
|
131
134
|
Alternately, you can set the `sync` option on each tabs block.
|
132
135
|
If you want to delist a tabs block from sync, set the `nosync` option on that block.
|
@@ -134,6 +137,7 @@ If you want to delist a tabs block from sync, set the `nosync` option on that bl
|
|
134
137
|
If you want to persist the sync selection, assign a value to the `data-sync-storage-key` attribute on the `<script>` tag.
|
135
138
|
By default, the sync selection will be assigned to the specified key in local storage.
|
136
139
|
You can set the `data-sync-storage-scope` attribute on the `<script>` tag to `session` to use session storage instead.
|
140
|
+
When using the extension on a standalone document, you can configure these options using the `tabs-sync-storage-key` and `tabs-sync-storage-scope` document attributes, respectively.
|
137
141
|
|
138
142
|
== Usage
|
139
143
|
|
data/data/css/tabs.css
CHANGED
@@ -35,6 +35,16 @@
|
|
35
35
|
margin-left: 0.25em;
|
36
36
|
}
|
37
37
|
|
38
|
+
.tabs .tablist li::after {
|
39
|
+
content: "";
|
40
|
+
display: block;
|
41
|
+
height: 1px;
|
42
|
+
position: absolute;
|
43
|
+
bottom: -1px;
|
44
|
+
left: 0;
|
45
|
+
right: 0;
|
46
|
+
}
|
47
|
+
|
38
48
|
.tabs.is-loading .tablist li:not(:first-child),
|
39
49
|
.tabs:not(.is-loading) .tablist li:not(.is-selected) {
|
40
50
|
background-color: #f5f5f5;
|
@@ -42,15 +52,15 @@
|
|
42
52
|
|
43
53
|
.tabs.is-loading .tablist li:first-child::after,
|
44
54
|
.tabs:not(.is-loading) .tablist li.is-selected::after {
|
45
|
-
background-color:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
right: 0;
|
55
|
+
background-color: #fff;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
.tabs:not(.is-loading) .tablist li,
|
60
|
+
.tabs:not(.is-loading) .tablist li::after {
|
61
|
+
transition: background-color 200ms ease-in-out;
|
53
62
|
}
|
63
|
+
*/
|
54
64
|
|
55
65
|
.tablist > ul p {
|
56
66
|
line-height: inherit;
|
data/data/js/tabs.js
CHANGED
@@ -12,42 +12,48 @@
|
|
12
12
|
var syncIds = tabs.classList.contains('is-sync') ? {} : undefined
|
13
13
|
var tablist = tabs.querySelector('.tablist ul')
|
14
14
|
tablist.setAttribute('role', 'tablist')
|
15
|
-
var
|
15
|
+
var start
|
16
16
|
forEach.call(tablist.querySelectorAll('li'), function (tab, idx) {
|
17
|
-
tab.
|
17
|
+
tab.tabIndex = -1
|
18
|
+
tab.setAttribute('role', tab.classList.add('tab') || 'tab')
|
18
19
|
var id, anchor, syncId
|
19
|
-
if (!(id = tab.id)) {
|
20
|
-
|
21
|
-
tab.id = id = anchor.parentNode.removeChild(anchor).id
|
20
|
+
if (!(id = tab.id) && (anchor = tab.querySelector('a[id]'))) {
|
21
|
+
id = tab.id = anchor.parentNode.removeChild(anchor).id
|
22
22
|
}
|
23
|
-
var panel = tabs.querySelector('.tabpanel[aria-labelledby~="' + id + '"]')
|
24
|
-
if (!panel) return // invalid state
|
25
|
-
tab.
|
26
|
-
syncIds && (((syncId = tab.textContent.toLowerCase().trim()) in syncIds) ? (syncId = undefined) : true) &&
|
23
|
+
var panel = id && tabs.querySelector('.tabpanel[aria-labelledby~="' + id + '"]')
|
24
|
+
if (!panel) return idx ? undefined : toggleSelected(tab, true) // invalid state
|
25
|
+
syncIds && (((syncId = tab.textContent.trim()) in syncIds) ? (syncId = undefined) : true) &&
|
27
26
|
(syncIds[(tab.dataset.syncId = syncId)] = tab)
|
28
|
-
idx || (
|
27
|
+
idx || (syncIds && (start = { tab: tab, panel: panel })) ? toggleHidden(panel, true) : toggleSelected(tab, true)
|
29
28
|
tab.setAttribute('aria-controls', panel.id)
|
30
29
|
panel.setAttribute('role', 'tabpanel')
|
31
|
-
forEach.call(panel.querySelectorAll('table.tableblock'), function (table) {
|
32
|
-
var container = Object.assign(document.createElement('div'), { className: 'tablecontainer' })
|
33
|
-
table.parentNode.insertBefore(container, table).appendChild(table)
|
34
|
-
})
|
35
30
|
var onClick = syncId === undefined ? activateTab : activateTabSync
|
36
31
|
tab.addEventListener('click', onClick.bind({ tabs: tabs, tab: tab, panel: panel }))
|
37
32
|
})
|
38
|
-
if (
|
39
|
-
|
33
|
+
if (!tabs.closest('.tabpanel')) {
|
34
|
+
forEach.call(tabs.querySelectorAll('.tabpanel table.tableblock'), function (table) {
|
35
|
+
var container = Object.assign(document.createElement('div'), { className: 'tablecontainer' })
|
36
|
+
table.parentNode.insertBefore(container, table).appendChild(table)
|
37
|
+
})
|
38
|
+
}
|
39
|
+
if (start) {
|
40
|
+
var syncGroupId
|
41
|
+
for (var i = 0, lst = tabs.classList, len = lst.length, className; i !== len; i++) {
|
42
|
+
if (!(className = lst.item(i)).startsWith('data-sync-group-id=')) continue
|
43
|
+
tabs.dataset.syncGroupId = syncGroupId = lst.remove(className) || className.slice(19).replace(/\u00a0/g, ' ')
|
44
|
+
break
|
45
|
+
}
|
46
|
+
if (syncGroupId === undefined) tabs.dataset.syncGroupId = syncGroupId = Object.keys(syncIds).sort().join('|')
|
40
47
|
var preferredSyncId = 'syncStorageKey' in config &&
|
41
|
-
window[(config.syncStorageScope || 'local') + 'Storage'].getItem(config.syncStorageKey + '-' +
|
48
|
+
window[(config.syncStorageScope || 'local') + 'Storage'].getItem(config.syncStorageKey + '-' + syncGroupId)
|
42
49
|
var tab = preferredSyncId && syncIds[preferredSyncId]
|
43
|
-
tab && Object.assign(
|
44
|
-
toggleSelected(
|
50
|
+
tab && Object.assign(start, { tab: tab, panel: document.getElementById(tab.getAttribute('aria-controls')) })
|
51
|
+
toggleSelected(start.tab, true) || toggleHidden(start.panel, false)
|
45
52
|
}
|
46
53
|
})
|
47
54
|
onHashChange()
|
48
|
-
|
49
|
-
|
50
|
-
})
|
55
|
+
toggleClassOnEach(tabsBlocks, 'is-loading', 'remove')
|
56
|
+
window.setTimeout(toggleClassOnEach.bind(null, tabsBlocks, 'is-loaded', 'add'), 0)
|
51
57
|
window.addEventListener('hashchange', onHashChange)
|
52
58
|
}
|
53
59
|
|
@@ -55,14 +61,14 @@
|
|
55
61
|
var tab = this.tab
|
56
62
|
var tabs = this.tabs || (this.tabs = tab.closest('.tabs'))
|
57
63
|
var panel = this.panel || (this.panel = document.getElementById(tab.getAttribute('aria-controls')))
|
58
|
-
|
64
|
+
querySelectorWithSiblings(tabs, '.tablist .tab', 'tab').forEach(function (el) {
|
59
65
|
toggleSelected(el, el === tab)
|
60
66
|
})
|
61
|
-
|
67
|
+
querySelectorWithSiblings(tabs, '.tabpanel', 'tabpanel').forEach(function (el) {
|
62
68
|
toggleHidden(el, el !== panel)
|
63
69
|
})
|
64
|
-
if (!this.isSync && 'syncStorageKey' in config && '
|
65
|
-
var storageKey = config.syncStorageKey + '-' + tabs.dataset.
|
70
|
+
if (!this.isSync && 'syncStorageKey' in config && 'syncGroupId' in tabs.dataset) {
|
71
|
+
var storageKey = config.syncStorageKey + '-' + tabs.dataset.syncGroupId
|
66
72
|
window[(config.syncStorageScope || 'local') + 'Storage'].setItem(storageKey, tab.dataset.syncId)
|
67
73
|
}
|
68
74
|
if (!e) return
|
@@ -78,23 +84,36 @@
|
|
78
84
|
var thisTab = this.tab
|
79
85
|
var initialY = thisTabs.getBoundingClientRect().y
|
80
86
|
forEach.call(document.querySelectorAll('.tabs'), function (tabs) {
|
81
|
-
if (tabs
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
}
|
87
|
+
if (tabs === thisTabs || tabs.dataset.syncGroupId !== thisTabs.dataset.syncGroupId) return
|
88
|
+
querySelectorWithSiblings(tabs, '.tablist .tab', 'tab').forEach(function (tab) {
|
89
|
+
if (tab.dataset.syncId === thisTab.dataset.syncId) activateTab.call({ tabs: tabs, tab: tab, isSync: true })
|
90
|
+
})
|
86
91
|
})
|
87
92
|
var shiftedBy = thisTabs.getBoundingClientRect().y - initialY
|
88
93
|
if (shiftedBy && (shiftedBy = Math.round(shiftedBy))) window.scrollBy({ top: shiftedBy, behavior: 'instant' })
|
89
94
|
}
|
90
95
|
|
96
|
+
function querySelectorWithSiblings (scope, selector, siblingClass) {
|
97
|
+
var el = scope.querySelector(selector)
|
98
|
+
if (!el) return []
|
99
|
+
var result = [el]
|
100
|
+
while ((el = el.nextElementSibling) && el.classList.contains(siblingClass)) result.push(el)
|
101
|
+
return result
|
102
|
+
}
|
103
|
+
|
104
|
+
function toggleClassOnEach (elements, className, method) {
|
105
|
+
forEach.call(elements, function (el) {
|
106
|
+
el.classList[method](className)
|
107
|
+
})
|
108
|
+
}
|
109
|
+
|
91
110
|
function toggleHidden (el, state) {
|
92
|
-
el.classList
|
111
|
+
el.classList[(el.hidden = state) ? 'add' : 'remove']('is-hidden')
|
93
112
|
}
|
94
113
|
|
95
114
|
function toggleSelected (el, state) {
|
96
115
|
el.setAttribute('aria-selected', '' + state)
|
97
|
-
el.classList
|
116
|
+
el.classList[state ? 'add' : 'remove']('is-selected')
|
98
117
|
el.tabIndex = state ? 0 : -1
|
99
118
|
}
|
100
119
|
|
@@ -18,10 +18,10 @@ module Asciidoctor
|
|
18
18
|
end
|
19
19
|
tabs_number = doc.counter 'tabs-number'
|
20
20
|
tabs_id = attrs['id'] || (generate_id %(tabs #{tabs_number}), doc)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
tabs.title = attrs['title']
|
21
|
+
tabs_role = 'tabs' + (!(block.option? 'nosync') && ((block.option? 'sync') || (doc.option? 'tabs-sync')) ?
|
22
|
+
((gid = attrs['sync-group-id']) ? %( is-sync data-sync-group-id=#{gid.gsub ' ', ?\u00a0}) : ' is-sync') : '')
|
23
|
+
tabs_role += (tabs_user_role = attrs['role']) ? %( #{tabs_user_role} is-loading) : ' is-loading'
|
24
|
+
(tabs = create_open_block parent, nil, { 'id' => tabs_id, 'role' => tabs_role }).title = attrs['title']
|
25
25
|
tablist = create_list parent, :ulist, { 'role' => 'tablist' }
|
26
26
|
panes = {}
|
27
27
|
set_id_on_tab = (doc.backend == 'html5') || (list_item_supports_id? doc)
|
@@ -35,11 +35,19 @@ module Asciidoctor
|
|
35
35
|
JAVASCRIPT_FILE = ::File.join DATA_DIR, 'js/tabs.js'
|
36
36
|
|
37
37
|
def process doc
|
38
|
+
if doc.attr? 'tabs-sync-storage-key'
|
39
|
+
config_attrs = %( data-sync-storage-key="#{doc.attr 'tabs-sync-storage-key'}")
|
40
|
+
if doc.attr? 'tabs-sync-storage-scope'
|
41
|
+
config_attrs += %( data-sync-storage-scope="#{doc.attr 'tabs-sync-storage-scope'}")
|
42
|
+
end
|
43
|
+
else
|
44
|
+
config_attrs = ''
|
45
|
+
end
|
38
46
|
if doc.attr? 'linkcss'
|
39
47
|
src = doc.normalize_web_path 'asciidoctor-tabs.js', (doc.attr 'scriptsdir')
|
40
|
-
%(<script src="#{src}"></script>)
|
48
|
+
%(<script src="#{src}"#{config_attrs}></script>)
|
41
49
|
elsif (script = doc.read_asset JAVASCRIPT_FILE)
|
42
|
-
%(<script>\n#{script.chomp}\n</script>)
|
50
|
+
%(<script#{config_attrs}>\n#{script.chomp}\n</script>)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asciidoctor-tabs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.beta.
|
4
|
+
version: 1.0.0.beta.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Allen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|