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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 461e1d2d860df917423ed9dea49891739dd1129bda4aabb4bb9a9fc9bfbd09d8
4
- data.tar.gz: 85189a9d92e881b4570f395357673b42d3bdd272fed80d2b1b718a3d67ff13a7
3
+ metadata.gz: 79ee9882999f6e27787ce575352f3382272a502fa99c3e33090c265110290ab8
4
+ data.tar.gz: 1c8d5d2001a1b8f277ae84fc0c4e0f0d54f65266d97bd948e78835de7d5a91ae
5
5
  SHA512:
6
- metadata.gz: 1d3cf0e88c3852b1660f133bff1253d674b0ffa2ed38d0e1e700cbb1e7d41449582c007df86d8f1af20539717d5bdcbff85f8f732b47255b0198eddaa87378bd
7
- data.tar.gz: fe30e9640c17b3cdbd64f78ead25dc37735eaf5275c1a75f918b00d185caceb3499673aad700938ed117be83c7ffa6e30b4e5e84ae08f46b55df47d2571d72f1
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.1, 2023-01-30
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
- The tabbed interface that this block produces can help to organize information by code language, operating system, or product variant.
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 tab block (or just as a matter of style).
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
@@ -22,6 +22,10 @@
22
22
  position: relative;
23
23
  }
24
24
 
25
+ .tablist > ul li:focus-visible {
26
+ outline: none;
27
+ }
28
+
25
29
  .tablist.ulist,
26
30
  .tablist.ulist > ul li {
27
31
  margin: 0;
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 first, selectedTab
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 && !((syncId = tab.textContent.trim()) in syncIds) && (syncIds[(tab.dataset.syncId = syncId)] = true)
31
- ? syncId === preferredSyncId ? toggleSelected((selectedTab = tab), true) : toggleHidden(panel, true)
32
- : (syncId = toggleHidden(panel, true))
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 (!selectedTab && first) toggleSelected(first.tab, true) || toggleHidden(first.panel, false)
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
- forEach.call(tabsBlocks, function (tabs) {
46
- tabs.classList.remove('is-loading')
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 && tab.dataset.syncId !== undefined) {
62
- window[(config.syncStorageScope || 'local') + 'Storage'].setItem(config.syncStorageKey, tab.dataset.syncId)
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 tabs = this.tabs
82
+ var thisTabs = this.tabs
74
83
  var thisTab = this.tab
75
- var initialY = tabs.getBoundingClientRect().y
76
- forEach.call(document.querySelectorAll('.tabs .tablist .tab'), function (tab) {
77
- if (tab !== thisTab && tab.dataset.syncId === thisTab.dataset.syncId) activateTab.call({ tab: tab, isSync: true })
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 = tabs.getBoundingClientRect().y - initialY
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.toggle('is-hidden', (el.hidden = state))
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.toggle('is-selected', state)
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.syncId === undefined ? activateTab.call({ tab: tab }) : activateTabSync.call({ tab: tab })
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
- tabs_sync = !(block.option? 'nosync') && ((block.option? 'sync') || (doc.option? 'tabs-sync')) ? ' is-sync' : ''
22
- tabs_role = (tabs_role = attrs['role']) ? %( #{tabs_role}) : ''
23
- tabs = create_open_block parent, nil, { 'id' => tabs_id, 'role' => %(tabs#{tabs_sync}#{tabs_role} is-loading) }
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Tabs
5
- VERSION = '1.0.0.beta.1'
5
+ VERSION = '1.0.0.beta.3'
6
6
  end
7
7
  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.1
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-30 00:00:00.000000000 Z
11
+ date: 2023-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor