Skip to content

Commit

Permalink
Merge pull request #12 from Ovski4/copy-to-clipboad-snackbar
Browse files Browse the repository at this point in the history
Copy to clipboad toast message
  • Loading branch information
Ovski4 authored Dec 8, 2023
2 parents 1db2eca + 69c29de commit 677e6d3
Show file tree
Hide file tree
Showing 16 changed files with 352 additions and 88 deletions.
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ jekyllTabs.init({
syncTabsWithSameLabels: false,
activateTabFromUrl: false,
addCopyToClipboardButtons: false,
copyToClipboardButtonHtml: '<button>Copy</button>',
copyToClipboardSettings: {
buttonHTML: '<button>Copy</button>',
showToastMessageOnCopy: false,
toastMessage: 'Code copied to clipboard',
toastDuration: 3000,
}
});
```

Expand Down Expand Up @@ -191,11 +196,31 @@ To get a button that will copy the code within a tab, set the `addCopyToClipboar

This will apply only if `<pre>` tags can be found inside the tabs contents.

You can override the button HTML using the `copyToClipboardButtonHtml` property. The default value is `<button>Copy</button>`.
You can override the button HTML using the `copyToClipboardSettings.buttonHTML` property. The default value is `<button>Copy</button>`.

```
jekyllTabs.init({
addCopyToClipboardButtons: true,
copyToClipboardButtonHtml: '<button class="btn">Copy me!</button>'
copyToClipboardSettings: {
buttonHTML: '<button class="btn">Copy me!</button>',
}
});
```

To give users a visual indicator that the code has been copied, you can display a toast message that will disappear after a short period of time.

Set `showToastMessageOnCopy` to `true` under the `copyToClipboardSettings` property to set it up. You can also update the message text as well as how long it will be displayed.

```
jekyllTabs.init({
addCopyToClipboardButtons: true,
copyToClipboardSettings: {
buttonHTML: '<button>Copy</button>',
showToastMessageOnCopy: true,
toastMessage: 'Code copied to clipboard',
toastDuration: 3000, // duration in milliseconds
}
});
```

Default styles for the toast message are present in the [css file](https://github.com/Ovski4/jekyll-tabs/blob/master/docs/tabs.css#L50-L70).
24 changes: 23 additions & 1 deletion docs/tabs.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,27 @@
display: none;
}
.tab-content > li.active {
display: initial;
display: block;
}

/* The 2 following blocks can be removed if the script is not configured to show the toast message */
#jekyll-tabs-copy-to-clipboard-message {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 1;
right: 50%;
bottom: 30px;
}

#jekyll-tabs-copy-to-clipboard-message.show {
visibility: visible;
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
6 changes: 4 additions & 2 deletions docs/tabs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 20 additions & 5 deletions js/domHelpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Get the element position looking from the perspective of the parent element.
*
* Considering the following html:
* Considering the following HTML:
*
* <ul>
* <li class="zero">0</li>
Expand Down Expand Up @@ -40,17 +40,32 @@ const findElementsWithTextContent = (selector, text) => {
}

/**
* Create a javascript element from html markup.
* Create a javascript element from HTML markup.
*/
const createElementFromHtml = (html) => {
const createElementFromHTML = (html) => {
const template = document.createElement('template');
template.innerHTML = html.trim();

return template.content.firstChild;
}

/**
* Add the class on the given element for the duration of the timeout.
*/
const addClass = (element, addedClass, timeout) => {
element.className = element.className
? `${element.className} ${addedClass}`
: addedClass;

// After the timeout in milliseconds, remove the class.
setTimeout(() => {
element.className = element.className.replace(addedClass, '').trim();
}, timeout);
}

module.exports = {
getChildPosition,
findElementsWithTextContent,
createElementFromHtml,
};
createElementFromHTML,
addClass,
};
62 changes: 40 additions & 22 deletions js/jekyllTabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,62 @@ const {
handleTabClicked,
addCopyToClipboardButtons,
syncTabsWithSameLabels,
appendToastMessageHTML,
} = require('./tabsHelpers');

const init = (overriddenConfiguration = {}) => {
const defaultConfiguration = {
syncTabsWithSameLabels: false,
activateTabFromUrl: false,
addCopyToClipboardButtons: false,
copyToClipboardButtonHtml: '<button>Copy</button>',
copyToClipboardSettings: {
buttonHTML: '<button>Copy</button>',
showToastMessageOnCopy: false,
toastMessage: 'Code copied to clipboard',
toastDuration: 3000,
}
};
const configuration = Object.assign(defaultConfiguration, overriddenConfiguration);

window.addEventListener('load', () => {
const tabLinks = document.querySelectorAll('ul.tab > li > a');
const configuration = {
...defaultConfiguration,
...overriddenConfiguration,
copyToClipboardSettings: {
...defaultConfiguration.copyToClipboardSettings,
...overriddenConfiguration.copyToClipboardSettings,
}
};

Array.prototype.forEach.call(tabLinks, (link) => {
link.addEventListener('click', (event) => {
event.preventDefault();
const tabLinks = document.querySelectorAll('ul.tab > li > a');

handleTabClicked(link);
Array.prototype.forEach.call(tabLinks, (link) => {
link.addEventListener('click', (event) => {
event.preventDefault();

if (configuration.activateTabFromUrl) {
updateUrlWithActiveTab(link);
}
handleTabClicked(link);

if (configuration.syncTabsWithSameLabels) {
syncTabsWithSameLabels(link);
}
}, false);
});
if (configuration.activateTabFromUrl) {
updateUrlWithActiveTab(link);
}

if (configuration.addCopyToClipboardButtons) {
addCopyToClipboardButtons(configuration.copyToClipboardButtonHtml);
}
if (configuration.syncTabsWithSameLabels) {
syncTabsWithSameLabels(link);
}
}, false);
});

if (configuration.activateTabFromUrl) {
activateTabFromUrl();
if (configuration.addCopyToClipboardButtons) {
const settings = configuration.copyToClipboardSettings;

addCopyToClipboardButtons(settings);

if (settings.showToastMessageOnCopy) {
appendToastMessageHTML(settings.toastMessage);
}
});
}

if (configuration.activateTabFromUrl) {
activateTabFromUrl();
}
};

module.exports = {
Expand Down
63 changes: 53 additions & 10 deletions js/tabsHelpers.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
const { getChildPosition, createElementFromHtml, findElementsWithTextContent } = require('../js/domHelpers');
const {
getChildPosition,
createElementFromHTML,
findElementsWithTextContent,
addClass
} = require('../js/domHelpers');

/**
* Remove all "active" classes on li elements that belong to the given ul element.
*/
const removeActiveClasses = (ulElement) => {
const liElements = ulElement.querySelectorAll('ul > li');

Array.prototype.forEach.call(liElements, function(liElement) {
Array.prototype.forEach.call(liElements, (liElement) => {
liElement.classList.remove('active');
});
}
Expand Down Expand Up @@ -45,7 +50,7 @@ const handleTabClicked = (link) => {
*
* See https://stackoverflow.com/questions/51805395/navigator-clipboard-is-undefined
*/
const copyToClipboard = (text) => {
const copyToClipboard = (text, callBack) => {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text);
} else {
Expand All @@ -68,6 +73,10 @@ const copyToClipboard = (text) => {
textArea.remove();
}
};

if (typeof callBack === 'function') {
callBack();
}
}

/**
Expand Down Expand Up @@ -119,13 +128,16 @@ const updateUrlWithActiveTab = (link) => {
history.replaceState(null, '', updatedUrl);
};

const addCopyToClipboardButtons = (buttonHTML) => {
/**
* Add the "Copy to clipboard" button on the top right hand side of tabs with embedded code (<pre> tags).
*/
const addCopyToClipboardButtons = ({ buttonHTML, showToastMessageOnCopy, toastDuration }) => {
const preElements = document.querySelectorAll('ul.tab-content > li pre');

for(let i = 0; i < preElements.length; i++) {
const preElement = preElements[i];
const preParentNode = preElement.parentNode;
const button = createElementFromHtml(buttonHTML);
const button = createElementFromHTML(buttonHTML);

preParentNode.style.position = 'relative';
button.style.position = 'absolute';
Expand All @@ -134,17 +146,47 @@ const addCopyToClipboardButtons = (buttonHTML) => {

preParentNode.appendChild(button);

button.addEventListener('click', function () {
copyToClipboard(preElement.innerText);
let copyToClipboardCallBack;

if (showToastMessageOnCopy) {
copyToClipboardCallBack = () => {
showToastMessage(toastDuration);
};
}

button.addEventListener('click', () => {
copyToClipboard(preElement.innerText, copyToClipboardCallBack);
});
}
};

const syncTabsWithSameLabels = (activeLink) => {
const linksWithSameName = findElementsWithTextContent('a', activeLink.textContent);
/**
* Insert a div that contains the toast message at the end of the <body> tag.
*/
const appendToastMessageHTML = (toastMessage) => {
const toastMessageDiv = document.createElement('div');

toastMessageDiv.id = 'jekyll-tabs-copy-to-clipboard-message';
toastMessageDiv.textContent = toastMessage;

document.getElementsByTagName('body')[0].appendChild(toastMessageDiv);
};

/**
* Set '.show' class on the div that contains the toast message for the given duration.
*/
const showToastMessage = (toastDuration) => {
addClass(document.getElementById('jekyll-tabs-copy-to-clipboard-message'), 'show', toastDuration);
}

/**
* Activate tabs that have the same label as the one related to the given link.
*/
const syncTabsWithSameLabels = (link) => {
const linksWithSameName = findElementsWithTextContent('a', link.textContent);

for(let i = 0; i < linksWithSameName.length; i++) {
if (linksWithSameName[i] !== activeLink) {
if (linksWithSameName[i] !== link) {
handleTabClicked(linksWithSameName[i]);
}
}
Expand All @@ -158,4 +200,5 @@ module.exports = {
activateTabFromUrl,
updateUrlWithActiveTab,
syncTabsWithSameLabels,
appendToastMessageHTML,
};
Loading

0 comments on commit 677e6d3

Please sign in to comment.