asciidoctor-tabs 1.0.0.beta.1 → 1.0.0.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +32 -0
- data/README.adoc +11 -4
- data/data/css/tabs.css +4 -0
- data/data/js/tabs.js +40 -22
- 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: 79ee9882999f6e27787ce575352f3382272a502fa99c3e33090c265110290ab8
|
4
|
+
data.tar.gz: 1c8d5d2001a1b8f277ae84fc0c4e0f0d54f65266d97bd948e78835de7d5a91ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bf6818b2492f96aee01f2d29658e09a5bac3614e3c6e6565a83a817d0b1c5cf80897da04ca7859f0fe2fa6ce587cd5f10e37b74688a78e3c4d07a94a0b8d350
|
7
|
+
data.tar.gz: 12a6df3597b1c1da6fbff9f3bf130d1ec34d0485102bf780a27d8cde79596a22ca44b8f6620ab202ca6ac03d386da94e155d58b49d186e47117a02864ea5998e
|
data/CHANGELOG.adoc
CHANGED
@@ -4,6 +4,38 @@
|
|
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.3 (2023-02-01) - @mojavelinux
|
8
|
+
|
9
|
+
=== Added
|
10
|
+
|
11
|
+
* Allow storage of sync tab selection to be configured using `tabs-sync-storage-key` and `tabs-sync-storage-scope` document attributes
|
12
|
+
* Add `is-loaded` class to tabs blocks on next tick after initialization for binding transitions (#50)
|
13
|
+
* Allow sync group ID to be specified rather than derived using the `sync-group-id` attribute on the tabs block (#52)
|
14
|
+
|
15
|
+
=== Changed
|
16
|
+
|
17
|
+
* Rename data-sync-group attribute to data-sync-group-id
|
18
|
+
* Don't lowercase sync group ID
|
19
|
+
|
20
|
+
=== Details
|
21
|
+
|
22
|
+
{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]
|
23
|
+
|
24
|
+
== 1.0.0-beta.2 (2023-01-30) - @mojavelinux
|
25
|
+
|
26
|
+
=== Changed
|
27
|
+
|
28
|
+
* Only sync congruent tabs blocks (i.e., tabs blocks that have the same tablist) (#47)
|
29
|
+
* Store sync tab selection by sync group (e.g., `preferred-tab-a|b|c`) (#47)
|
30
|
+
|
31
|
+
=== Fixed
|
32
|
+
|
33
|
+
* Turn off :focus-visible outline on tab selected by URL fragment
|
34
|
+
|
35
|
+
=== Details
|
36
|
+
|
37
|
+
{url-repo}/releases/tag/v1.0.0-beta.2[git tag] | {url-repo}/compare/v1.0.0-beta.1\...v1.0.0-beta.2[full diff]
|
38
|
+
|
7
39
|
== 1.0.0-beta.1 (2023-01-30) - @mojavelinux
|
8
40
|
|
9
41
|
=== Added
|
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.3, 2023-02-01
|
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
|
|
@@ -85,7 +86,7 @@ The term is used as the tab's label and the description is used as the tab's con
|
|
85
86
|
The contents can be defined as primary text, attached blocks, or both.
|
86
87
|
If the attached blocks are themselves enclosed in a single open block, the open block enclosure itself is discarded upon conversion.
|
87
88
|
|
88
|
-
You may choose to extend the block delimiter length from the typical 4 characters to 6 in order to avoid conflicts with any example blocks inside the
|
89
|
+
You may choose to extend the block delimiter length from the typical 4 characters to 6 in order to avoid conflicts with any example blocks inside the tabs block (or just as a matter of style).
|
89
90
|
|
90
91
|
[,asciidoc]
|
91
92
|
----
|
@@ -125,12 +126,18 @@ Tab B:: Contents of tab B in second tabset.
|
|
125
126
|
====
|
126
127
|
----
|
127
128
|
|
129
|
+
Only tabs blocks with congruent tablists are synchronized by default.
|
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`).
|
133
|
+
|
128
134
|
Alternately, you can set the `sync` option on each tabs block.
|
129
135
|
If you want to delist a tabs block from sync, set the `nosync` option on that block.
|
130
136
|
|
131
137
|
If you want to persist the sync selection, assign a value to the `data-sync-storage-key` attribute on the `<script>` tag.
|
132
138
|
By default, the sync selection will be assigned to the specified key in local storage.
|
133
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.
|
134
141
|
|
135
142
|
== Usage
|
136
143
|
|
data/data/css/tabs.css
CHANGED
data/data/js/tabs.js
CHANGED
@@ -8,14 +8,11 @@
|
|
8
8
|
|
9
9
|
function init (tabsBlocks) {
|
10
10
|
if (!tabsBlocks.length) return
|
11
|
-
var preferredSyncId = 'syncStorageKey' in config
|
12
|
-
? window[(config.syncStorageScope || 'local') + 'Storage'].getItem(config.syncStorageKey)
|
13
|
-
: undefined
|
14
11
|
forEach.call(tabsBlocks, function (tabs) {
|
15
12
|
var syncIds = tabs.classList.contains('is-sync') ? {} : undefined
|
16
13
|
var tablist = tabs.querySelector('.tablist ul')
|
17
14
|
tablist.setAttribute('role', 'tablist')
|
18
|
-
var
|
15
|
+
var initial
|
19
16
|
forEach.call(tablist.querySelectorAll('li'), function (tab, idx) {
|
20
17
|
tab.setAttribute('role', (tab.className = 'tab')) // NOTE converter may not have set class on li
|
21
18
|
var id, anchor, syncId
|
@@ -25,11 +22,10 @@
|
|
25
22
|
}
|
26
23
|
var panel = tabs.querySelector('.tabpanel[aria-labelledby~="' + id + '"]')
|
27
24
|
if (!panel) return // invalid state
|
28
|
-
if (!idx) first = { tab: tab, panel: panel }
|
29
25
|
tab.tabIndex = -1
|
30
|
-
syncIds &&
|
31
|
-
|
32
|
-
|
26
|
+
syncIds && (((syncId = tab.textContent.trim()) in syncIds) ? (syncId = undefined) : true) &&
|
27
|
+
(syncIds[(tab.dataset.syncId = syncId)] = tab)
|
28
|
+
idx || (initial = { tab: tab, panel: panel }) && syncIds ? toggleHidden(panel, true) : toggleSelected(tab, true)
|
33
29
|
tab.setAttribute('aria-controls', panel.id)
|
34
30
|
panel.setAttribute('role', 'tabpanel')
|
35
31
|
forEach.call(panel.querySelectorAll('table.tableblock'), function (table) {
|
@@ -39,12 +35,24 @@
|
|
39
35
|
var onClick = syncId === undefined ? activateTab : activateTabSync
|
40
36
|
tab.addEventListener('click', onClick.bind({ tabs: tabs, tab: tab, panel: panel }))
|
41
37
|
})
|
42
|
-
if (
|
38
|
+
if (syncIds && initial) {
|
39
|
+
var syncGroupId
|
40
|
+
for (var i = 0, lst = tabs.classList, len = lst.length, className; i !== len; i++) {
|
41
|
+
if (!(className = lst.item(i)).startsWith('data-sync-group-id=')) continue
|
42
|
+
tabs.dataset.syncGroupId = syncGroupId = lst.remove(className) || className.slice(19).replace(/\u00a0/g, ' ')
|
43
|
+
break
|
44
|
+
}
|
45
|
+
if (syncGroupId === undefined) tabs.dataset.syncGroupId = syncGroupId = Object.keys(syncIds).sort().join('|')
|
46
|
+
var preferredSyncId = 'syncStorageKey' in config &&
|
47
|
+
window[(config.syncStorageScope || 'local') + 'Storage'].getItem(config.syncStorageKey + '-' + syncGroupId)
|
48
|
+
var tab = preferredSyncId && syncIds[preferredSyncId]
|
49
|
+
tab && Object.assign(initial, { tab: tab, panel: document.getElementById(tab.getAttribute('aria-controls')) })
|
50
|
+
toggleSelected(initial.tab, true) || toggleHidden(initial.panel, false)
|
51
|
+
}
|
43
52
|
})
|
44
53
|
onHashChange()
|
45
|
-
|
46
|
-
|
47
|
-
})
|
54
|
+
toggleClassOnEach(tabsBlocks, 'is-loading', 'remove')
|
55
|
+
window.setTimeout(toggleClassOnEach.bind(null, tabsBlocks, 'is-loaded', 'add'), 0)
|
48
56
|
window.addEventListener('hashchange', onHashChange)
|
49
57
|
}
|
50
58
|
|
@@ -58,8 +66,9 @@
|
|
58
66
|
forEach.call(tabs.querySelectorAll('.tabpanel'), function (el) {
|
59
67
|
toggleHidden(el, el !== panel)
|
60
68
|
})
|
61
|
-
if (!this.isSync && 'syncStorageKey' in config &&
|
62
|
-
|
69
|
+
if (!this.isSync && 'syncStorageKey' in config && 'syncGroupId' in tabs.dataset) {
|
70
|
+
var storageKey = config.syncStorageKey + '-' + tabs.dataset.syncGroupId
|
71
|
+
window[(config.syncStorageScope || 'local') + 'Storage'].setItem(storageKey, tab.dataset.syncId)
|
63
72
|
}
|
64
73
|
if (!e) return
|
65
74
|
var loc = window.location
|
@@ -70,23 +79,32 @@
|
|
70
79
|
|
71
80
|
function activateTabSync (e) {
|
72
81
|
activateTab.call(this, e)
|
73
|
-
var
|
82
|
+
var thisTabs = this.tabs
|
74
83
|
var thisTab = this.tab
|
75
|
-
var initialY =
|
76
|
-
forEach.call(document.querySelectorAll('.tabs
|
77
|
-
if (
|
84
|
+
var initialY = thisTabs.getBoundingClientRect().y
|
85
|
+
forEach.call(document.querySelectorAll('.tabs'), function (tabs) {
|
86
|
+
if (tabs === thisTabs || tabs.dataset.syncGroupId !== thisTabs.dataset.syncGroupId) return
|
87
|
+
forEach.call(tabs.querySelectorAll('.tablist .tab'), function (tab) {
|
88
|
+
if (tab.dataset.syncId === thisTab.dataset.syncId) activateTab.call({ tabs: tabs, tab: tab, isSync: true })
|
89
|
+
})
|
78
90
|
})
|
79
|
-
var shiftedBy =
|
91
|
+
var shiftedBy = thisTabs.getBoundingClientRect().y - initialY
|
80
92
|
if (shiftedBy && (shiftedBy = Math.round(shiftedBy))) window.scrollBy({ top: shiftedBy, behavior: 'instant' })
|
81
93
|
}
|
82
94
|
|
95
|
+
function toggleClassOnEach (elements, className, method) {
|
96
|
+
forEach.call(elements, function (el) {
|
97
|
+
el.classList[method](className)
|
98
|
+
})
|
99
|
+
}
|
100
|
+
|
83
101
|
function toggleHidden (el, state) {
|
84
|
-
el.classList
|
102
|
+
el.classList[(el.hidden = state) ? 'add' : 'remove']('is-hidden')
|
85
103
|
}
|
86
104
|
|
87
105
|
function toggleSelected (el, state) {
|
88
106
|
el.setAttribute('aria-selected', '' + state)
|
89
|
-
el.classList
|
107
|
+
el.classList[state ? 'add' : 'remove']('is-selected')
|
90
108
|
el.tabIndex = state ? 0 : -1
|
91
109
|
}
|
92
110
|
|
@@ -95,6 +113,6 @@
|
|
95
113
|
if (!id) return
|
96
114
|
var tab = document.getElementById(~id.indexOf('%') ? decodeURIComponent(id) : id)
|
97
115
|
if (!(tab && tab.classList.contains('tab'))) return
|
98
|
-
tab.dataset
|
116
|
+
'syncId' in tab.dataset ? activateTabSync.call({ tab: tab }) : activateTab.call({ tab: tab })
|
99
117
|
}
|
100
118
|
})()
|
@@ -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.3
|
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-01
|
11
|
+
date: 2023-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|