jekyll-tabs 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c266623f115ba3a1622127faa6198a519ec07f088acf63eda6f3699ddcedc9d
4
- data.tar.gz: 41a9a2a5a7900e78b74b26bf251ff78a997fd62bb4992e9b2adf37d7a57421aa
3
+ metadata.gz: 49d3f07cb95500acd0b2866c35e78b2c425616708ba8460446b0337642a9ed9c
4
+ data.tar.gz: 8e237cea3292d2ad4875640b3cdd5ba6cd7704b8cdf653f8dc5d2eb4917c94da
5
5
  SHA512:
6
- metadata.gz: a528f0a8014d0fa8c623fc364cd8d9a617d37a6fdd319c8fcbfd53580fb1e87e6e226e995bc2ad4c5a4c26acd7021ceb47fcbe04e8eb7a5412c80c200c116d6b
7
- data.tar.gz: 712766d195a12f7db59a4fb78d086cd6199b9d50a392afc396f4c8cd4d372aec83a714a5dac6b70efc0ca0eaaa0193a4a8746a130b079e05edd7ea5d120e88de
6
+ metadata.gz: a49f0c568b7a74376be5b22cea12e98a042f89deadb9e73ff3da5b22aa00972be5c017ae2f13d07acb60cfec3b6ee0cbdc3f4070cc5303db5dc240122c7dfcd4
7
+ data.tar.gz: 201fa4899553cc756998ae997c160653e795eaa1b9e266308db26b2f199852bd6c90e65d6839c5f6aeb2a8d5de3bf14da79cfe0202bfca3154881ccc5fc0d921
data/README.md CHANGED
@@ -10,60 +10,63 @@ Installation
10
10
 
11
11
  ### Install the plugin
12
12
 
13
- Add this line to your Gemfile:
13
+ Add this line to your `Gemfile`:
14
14
 
15
15
  ```ruby
16
16
  group :jekyll_plugins do
17
-
17
+ # ... other gems
18
18
  gem "jekyll-tabs"
19
19
  end
20
20
  ```
21
21
 
22
- Add in you _config.yml:
22
+ Install the gem by running:
23
23
 
24
- ```yaml
25
- plugins:
26
- - jekyll-tabs
24
+ ```bash
25
+ bundle install
27
26
  ```
28
27
 
29
- And then execute:
30
-
31
- $ bundle
32
-
33
- and put this in your ``_config.yml``
28
+ Then add the gem to the plugin list in your `_config.yml` file:
34
29
 
35
30
  ```yaml
36
31
  plugins:
37
32
  - jekyll-tabs
38
33
  ```
39
34
 
40
- ### Add the javascripts
35
+ ### Include the javascript
41
36
 
42
- Copy the content of [this file](docs/tabs.js) in your public folder, let's say **public/js/tabs.js**.
43
- Include the script in your layout, for instance in **_layouts_/default.html**
37
+ Copy the content of [this file](https://raw.githubusercontent.com/Ovski4/jekyll-tabs/master/docs/tabs.js) in your public folder (for example **assets/js/tabs.js**), and include the script in your layout (such as **_layouts/default.html**).
44
38
 
45
39
  ```html
46
40
  <!DOCTYPE html>
47
41
  <html lang="en-us">
48
- {% include head.html %}
49
- <body>
50
- {{ content }}
51
- <script src="/public/js/tabs.js"></script>
52
- </body>
42
+ <head>
43
+ ...
44
+ </head>
45
+ <body>
46
+ {{ content }}
47
+ <script src="/assets/js/tabs.js"></script>
48
+ </body>
53
49
  </html>
54
50
  ```
55
51
 
56
- ### Add some style to the tabs
52
+ ### Style the tabs
57
53
 
58
- Feel free to style it the way you want. Here is an example.
54
+ Feel free to style it the way you want. You can use [this existing css file](https://raw.githubusercontent.com/Ovski4/jekyll-tabs/master/docs/tabs.css) to get started.
59
55
 
60
- Create a file called **custom.css** in **public/css** with [this content](docs/tabs.css). Include it in **_include/head.html**
56
+ Paste the content in a file (for example **assets/css/custom.css**), and include it in the html <head> tag of your jekyll website.
61
57
 
62
58
  ```html
63
- <link rel="stylesheet" href="/public/css/custom.css">
59
+ <!DOCTYPE html>
60
+ <html lang="en-us">
61
+ <head>
62
+ ...
63
+ <link rel="stylesheet" href="/assets/css/custom.css">
64
+ </head>
65
+ <body>
66
+ ....
64
67
  ```
65
68
 
66
- That's it
69
+ You are all set! Let's see the markup.
67
70
 
68
71
  Usage
69
72
  -----
@@ -121,6 +124,41 @@ hello:
121
124
 
122
125
  Here is the result:
123
126
 
124
- ![Image of Yaktocat](docs/tabs-example.png)
127
+ ![Example to demo how tabs will render](docs/tabs-example.png)
128
+
129
+ In the following markup:
130
+
131
+ ```
132
+ {% tab data-struct Some label here %}
133
+ ```
134
+
135
+ * The first word after the `tab` keyword (`data-struct` here) is used to group tabs.
136
+ * All words after will be displayed as the tab label.
137
+
138
+ Which is why in the above example, we have 2 groups of tabs: `data-struct` and `log`.
139
+
140
+ Additional configuration
141
+ ------------------------
142
+
143
+ ### Sync tabs with similar labels
144
+
145
+ To get all tabs with the same label synced, set the `syncTabsWithSameLabels` value to **true** in the `jekyllTabsConfiguration` object ([link to related line of code](https://github.com/Ovski4/jekyll-tabs/blob/master/docs/tabs.js#L5)).
146
+
147
+ ### Open a specific tab on page load
148
+
149
+ To link and open a specific tab on page load, set the `activateTabFromUrl` value to **true** in the `jekyllTabsConfiguration` object ([link to related line of code](https://github.com/Ovski4/jekyll-tabs/blob/master/docs/tabs.js#L6)).
150
+
151
+ You will need to append a combination of url anchor (#) and query param (?active_tab) to the page URL.
152
+
153
+ * The anchor is used to target the tab group
154
+ * The query parameter `active_tab` is used to target the tab that will open
155
+
156
+ Clicking on a tab will automatically set the anchor and query parameter in the url.
157
+
158
+ ### Add a copy to clipboard button
159
+
160
+ To get a button to copy the code within a tab, set the `addCopyToClipboardButton` value to **true** in the `jekyllTabsConfiguration` object ([link to related line of code](https://github.com/Ovski4/jekyll-tabs/blob/master/docs/tabs.js#L7)).
161
+
162
+ This will apply only if `<pre>` tags can be found inside the tabs contents.
125
163
 
126
- Characters before the first space must be the same between tabs of the same scope (log and data-struct in this example). Every characters following will be displayed as the tab label.
164
+ You can override the button HTML using the `copyToClipboardButtonHtml` property.
data/docs/tabs.css CHANGED
@@ -31,18 +31,18 @@
31
31
  border-color: #1e87f0;
32
32
  }
33
33
 
34
- .tab li a {
34
+ .tab > li > a {
35
35
  text-decoration: none;
36
- cursor: pointer;
36
+ cursor: pointer;
37
37
  }
38
38
 
39
- .tab-content{
39
+ .tab-content {
40
40
  padding: 0;
41
41
  }
42
42
 
43
- .tab-content li {
43
+ .tab-content > li {
44
44
  display: none;
45
45
  }
46
- .tab-content li.active {
46
+ .tab-content > li.active {
47
47
  display: initial;
48
48
  }
data/docs/tabs.js CHANGED
@@ -1,14 +1,43 @@
1
- const removeActiveClasses = function (ulElement) {
2
- const lis = ulElement.querySelectorAll('li');
3
- Array.prototype.forEach.call(lis, function(li) {
4
- li.classList.remove('active');
5
- });
6
- }
1
+ /**
2
+ * Configure the tabs behavior.
3
+ */
4
+ const jekyllTabsConfiguration = {
5
+ syncTabsWithSameLabels: false,
6
+ activateTabFromUrl: false,
7
+ addCopyToClipboardButton: false,
8
+ copyToClipboardButtonHtml: '<button>Copy</button>',
9
+ };
10
+
11
+ const jekyllTabsModule = (function() {
12
+
13
+ /**
14
+ * Remove all "active" classes on li elements that belong to the given ul element.
15
+ */
16
+ const removeActiveClasses = function (ulElement) {
17
+ const liElements = ulElement.querySelectorAll('ul > li');
18
+
19
+ Array.prototype.forEach.call(liElements, function(liElement) {
20
+ liElement.classList.remove('active');
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Get the element position looking from the perspective of the parent element.
26
+ *
27
+ * Considering the following html:
28
+ *
29
+ * <ul>
30
+ * <li class="zero">0</li>
31
+ * <li class="one">1</li>
32
+ * <li class="two">2</li>
33
+ * </ul>
34
+ *
35
+ * Then getChildPosition(document.querySelector('.one')) would return 1.
36
+ */
37
+ const getChildPosition = function (element) {
38
+ const parent = element.parentNode;
7
39
 
8
- const getChildPosition = function (element) {
9
- var parent = element.parentNode;
10
- var i = 0;
11
- for (var i = 0; i < parent.children.length; i++) {
40
+ for (let i = 0; i < parent.children.length; i++) {
12
41
  if (parent.children[i] === element) {
13
42
  return i;
14
43
  }
@@ -17,27 +46,195 @@ const removeActiveClasses = function (ulElement) {
17
46
  throw new Error('No parent found');
18
47
  }
19
48
 
20
- window.addEventListener('load', function () {
21
- const tabLinks = document.querySelectorAll('ul.tab li a');
49
+ /**
50
+ * Returns a list of elements of the given tag that contains the given text.
51
+ */
52
+ const findElementsContaining = function(elementTag, text) {
53
+ const elements = document.querySelectorAll(elementTag);
54
+ const elementsThatContainText = [];
22
55
 
23
- Array.prototype.forEach.call(tabLinks, function(link) {
24
- link.addEventListener('click', function (event) {
25
- event.preventDefault();
56
+ for (let i = 0; i < elements.length; i++) {
57
+ const element = elements[i];
58
+
59
+ if (element.textContent.includes(text)) {
60
+ elementsThatContainText.push(element);
61
+ }
62
+ }
63
+
64
+ return elementsThatContainText;
65
+ }
66
+
67
+ /**
68
+ * Handle adding or removing active classes on tab list items.
69
+ */
70
+ const handleTabClicked = function(link) {
71
+ const liTab = link.parentNode;
72
+ const ulTab = liTab.parentNode;
73
+ const liPositionInUl = getChildPosition(liTab);
26
74
 
27
- liTab = link.parentNode;
28
- ulTab = liTab.parentNode;
29
- position = getChildPosition(liTab);
30
75
  if (liTab.className.includes('active')) {
31
- return;
76
+ return;
32
77
  }
33
78
 
79
+ const tabContentId = ulTab.getAttribute('data-tab');
80
+ const tabContentElement = document.getElementById(tabContentId);
81
+
82
+ // Remove all "active" classes first.
34
83
  removeActiveClasses(ulTab);
35
- tabContentId = ulTab.getAttribute('data-tab');
36
- tabContentElement = document.getElementById(tabContentId);
37
84
  removeActiveClasses(tabContentElement);
38
85
 
39
- tabContentElement.querySelectorAll('li')[position].classList.add('active');
86
+ // Then add back active classes depending on the tab (ul element) that was clicked on.
87
+ tabContentElement.querySelectorAll('ul.tab-content > li')[liPositionInUl].classList.add('active');
40
88
  liTab.classList.add('active');
41
- }, false);
89
+ }
90
+
91
+ /**
92
+ * Create a javascript element from html markup.
93
+ */
94
+ const createElementFromHtml = function(html) {
95
+ const template = document.createElement('template');
96
+ template.innerHTML = html.trim();
97
+
98
+ return template.content.firstChild;
99
+ }
100
+
101
+ /**
102
+ * Copy the given text in the clipboard.
103
+ *
104
+ * See https://stackoverflow.com/questions/51805395/navigator-clipboard-is-undefined
105
+ */
106
+ const copyToClipboard = function(text) {
107
+ if (navigator.clipboard && window.isSecureContext) {
108
+ navigator.clipboard.writeText(text);
109
+ } else {
110
+ // Use the 'out of viewport hidden text area' trick
111
+ const textArea = document.createElement("textarea");
112
+ textArea.value = text;
113
+
114
+ // Move textarea out of the viewport so it's not visible
115
+ textArea.style.position = "absolute";
116
+ textArea.style.left = "-999999px";
117
+
118
+ document.body.prepend(textArea);
119
+ textArea.select();
120
+
121
+ try {
122
+ document.execCommand('copy');
123
+ } catch (error) {
124
+ console.error(error);
125
+ } finally {
126
+ textArea.remove();
127
+ }
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Open the tab specified by a combination of url anchor (#) and query param (?active_tab).
133
+ *
134
+ * For example, considering url http://your-jekyll-website.com/some-page/?active_tab=tab-2#my_tabs
135
+ * Then the tabs with name 'my_tabs' would see tab with label 'tab 2' automatically open.
136
+ */
137
+ const activateTabFromUrl = function() {
138
+ const tabsAnchor = window.location.hash.substring(1);
139
+
140
+ if (!tabsAnchor) {
141
+ return;
142
+ }
143
+
144
+ const targetedTabs = document.getElementById(tabsAnchor);
145
+
146
+ if (!targetedTabs) {
147
+ return;
148
+ }
149
+
150
+ const urlParams = new URLSearchParams(window.location.search);
151
+ const tabIdToActivate = urlParams.get('active_tab');
152
+
153
+ if (!tabIdToActivate) {
154
+ return;
155
+ }
156
+
157
+ const tabLink = targetedTabs.querySelector('li#' + tabIdToActivate + ' > a');
158
+
159
+ if (!tabLink) {
160
+ return;
161
+ }
162
+
163
+ jekyllTabsModule.handleTabClicked(tabLink);
164
+ }
165
+
166
+ /**
167
+ * Update the url when clicking on a tab. See method activateTabFromUrl above.
168
+ */
169
+ const updateUrlWithActiveTab = function(link) {
170
+ const liTab = link.parentNode;
171
+ const ulTab = liTab.parentNode;
172
+
173
+ const searchParams = new URLSearchParams(window.location.search);
174
+ searchParams.set('active_tab', liTab.id);
175
+
176
+ const updatedUrl = window.location.pathname + '?' + searchParams.toString() + '#' + ulTab.id;
177
+ history.replaceState(null, '', updatedUrl);
178
+ };
179
+
180
+ return {
181
+ findElementsContaining,
182
+ handleTabClicked,
183
+ createElementFromHtml,
184
+ copyToClipboard,
185
+ activateTabFromUrl,
186
+ updateUrlWithActiveTab,
187
+ };
188
+
189
+ })();
190
+
191
+ window.addEventListener('load', function () {
192
+ const tabLinks = document.querySelectorAll('ul.tab > li > a');
193
+
194
+ Array.prototype.forEach.call(tabLinks, function(link) {
195
+ link.addEventListener('click', function (event) {
196
+ event.preventDefault();
197
+
198
+ jekyllTabsModule.handleTabClicked(link);
199
+
200
+ if (jekyllTabsConfiguration.activateTabFromUrl) {
201
+ jekyllTabsModule.updateUrlWithActiveTab(link);
202
+ }
203
+
204
+ if (jekyllTabsConfiguration.syncTabsWithSameLabels) {
205
+ const linksWithSameName = jekyllTabsModule.findElementsContaining('a', link.textContent);
206
+
207
+ for(let i = 0; i < linksWithSameName.length; i++) {
208
+ if (linksWithSameName[i] !== link) {
209
+ jekyllTabsModule.handleTabClicked(linksWithSameName[i]);
210
+ }
211
+ }
212
+ }
213
+ }, false);
42
214
  });
215
+
216
+ if (jekyllTabsConfiguration.addCopyToClipboardButton) {
217
+ const preElements = document.querySelectorAll('ul.tab-content > li pre');
218
+
219
+ for(let i = 0; i < preElements.length; i++) {
220
+ const preElement = preElements[i];
221
+ const preParentNode = preElement.parentNode;
222
+ const button = jekyllTabsModule.createElementFromHtml(jekyllTabsConfiguration.copyToClipboardButtonHtml);
223
+
224
+ preParentNode.style.position = 'relative';
225
+ button.style.position = 'absolute';
226
+ button.style.top = '0px';
227
+ button.style.right = '0px';
228
+
229
+ preParentNode.appendChild(button);
230
+
231
+ button.addEventListener('click', function () {
232
+ jekyllTabsModule.copyToClipboard(preElement.innerText);
233
+ });
234
+ }
235
+ }
236
+
237
+ if (jekyllTabsConfiguration.activateTabFromUrl) {
238
+ jekyllTabsModule.activateTabFromUrl();
239
+ }
43
240
  });
@@ -1,5 +1,5 @@
1
1
  module Jekyll
2
2
  module Tabs
3
- VERSION = "1.1.1"
3
+ VERSION = "1.2.1"
4
4
  end
5
5
  end
data/lib/jekyll-tabs.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  require 'securerandom'
2
2
  require 'erb'
3
3
 
4
+ def sanitizeName(name)
5
+ return name
6
+ .strip # remove leading and trailing whitespace
7
+ .downcase # lowercase
8
+ .gsub(/[^0-9a-z]/, '-') # replace all non alphabjetical or non numerical characetrs by a dash
9
+ end
10
+
4
11
  module Jekyll
5
12
  module Tabs
6
13
  class TabsBlock < Liquid::Block
@@ -9,7 +16,8 @@ module Jekyll
9
16
  if markup == ''
10
17
  raise SyntaxError.new("Block #{block_name} requires 1 attribute")
11
18
  end
12
- @name = markup.strip
19
+ @name = sanitizeName(markup)
20
+
13
21
  end
14
22
 
15
23
  def render(context)
@@ -33,7 +41,7 @@ module Jekyll
33
41
  if markups.length != 2
34
42
  raise SyntaxError.new("Block #{block_name} requires 2 attributes")
35
43
  end
36
- @name = markups[0]
44
+ @name = sanitizeName(markups[0])
37
45
  @tab = markups[1]
38
46
  end
39
47
 
data/lib/template.erb CHANGED
@@ -1,6 +1,6 @@
1
- <ul class="tab" data-tab="<%= uuid %>" data-name="<%= @name %>">
1
+ <ul id="<%= @name %>" class="tab" data-tab="<%= uuid %>" data-name="<%= @name %>">
2
2
  <% environment["tabs-#{@name}"].each_with_index do |(key, value), index| %>
3
- <li<%= index == 0 ? ' class="active"' : ''%>>
3
+ <li<%= index == 0 ? ' class="active"' : ''%> id="<%= @name + '-' + key.dup.strip.downcase.gsub(/[^0-9a-z]/, '-') %>">
4
4
  <a href="#"><%= key %></a>
5
5
  </li>
6
6
  <% end %>
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-tabs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nejc Zdovc
8
8
  - Baptiste Bouchereau
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-02-25 00:00:00.000000000 Z
12
+ date: 2023-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jekyll
@@ -52,7 +52,7 @@ homepage: https://github.com/ovski4/jekyll-tabs
52
52
  licenses:
53
53
  - MIT
54
54
  metadata: {}
55
- post_install_message:
55
+ post_install_message:
56
56
  rdoc_options: []
57
57
  require_paths:
58
58
  - lib
@@ -67,8 +67,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  requirements: []
70
- rubygems_version: 3.1.2
71
- signing_key:
70
+ rubygems_version: 3.3.15
71
+ signing_key:
72
72
  specification_version: 4
73
73
  summary: A Jekyll plugin to add tabs
74
74
  test_files: []