-
-
Save KevinBatdorf/8bd5f808fff6a59e100dfa08a7431822 to your computer and use it in GitHub Desktop.
// ==UserScript== | |
// @name Add AlpineJs to Tailwind UI | |
// @namespace http://tampermonkey.net/ | |
// @version 3.0 | |
// @description Add Alpine JS code to Tailwind Ui copy/paste | |
// @author https://gist.github.com/KevinBatdorf/8bd5f808fff6a59e100dfa08a7431822 | |
// @match https://tailwindui.com/components/* | |
// @grant none | |
// ==/UserScript== | |
// Requires Tampermonkey | |
// Chrome - https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en | |
// FF - https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/ | |
// Find me on Twitter: https://twitter.com/kevinbatdorf | |
// Get the version that downloads the code into a file here: | |
// https://gist.github.com/KevinBatdorf/322c68f532af6a4cc6773f906da0684d | |
// If it seems to not be working, check the classes here are matching | |
const code = Array.from(document.querySelectorAll('button')).filter( | |
b => b.classList.value === 'group relative ml-2 hidden h-9 w-9 items-center justify-center sm:flex' | |
); | |
code.forEach(node => { | |
node.addEventListener('click', function(event) { | |
event.preventDefault(); | |
// This is the iFrame the component is in | |
const iFrame = event.target.closest('[id^=component-]').querySelector('[name][id^=frame]'); | |
const contentArea = iFrame.contentWindow.document.querySelector('body > div'); | |
const markup = contentArea.innerHTML; | |
// This will attempt to extract the function used in the x-data, like x-data="Component.popover" | |
const scripts = Array.from(contentArea.querySelectorAll('[x-data]')) | |
.filter((n) => /\(.*\)/.test(n.getAttribute('x-data').toString())) | |
.map((n) => { | |
const fnNameSplit = n | |
.getAttribute('x-data') | |
.replace('window.', '') | |
.replace(/\(.*\)/, '') | |
.split('.'); | |
// We need to get the string representation of the method | |
const method = new Function(`return ${iFrame.contentWindow[fnNameSplit[0]][fnNameSplit[1]]};`); | |
// This adds checks to create the global object then register it. | |
return ` | |
window.${fnNameSplit[0]} = window.${fnNameSplit[0]} ?? {} | |
window.${fnNameSplit[0]}['${fnNameSplit[1]}'] = ${method}() | |
` | |
}) | |
.join(';') | |
.toString(); | |
const textarea = document.createElement('textarea'); | |
textarea.textContent = scripts ? `<script>${scripts}</script>` + markup : markup; | |
document.body.appendChild(textarea); | |
setTimeout(() => { | |
textarea.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(textarea); | |
}, 50); | |
}) | |
}) |
Hey @dyanakiev - it looks like those have just too much text in them so it fails to copy. I'm not really sure what we can do for that. Any ideas?
@KevinBatdorf this is amazing mate!
Did you try it with combo boxes? I don't know what but it's not working I think:
https://tailwindui.com/components/application-ui/forms/comboboxes
Hey @magarrent It doesn't appear that the demos there are using Alpine, unfortunately.
Sadly doesn't seem to work on the later ecommerce templates
@ovp87 which one specifically? I know that some are too long and the clipboard doesn't seem to handle them. I could try a different approach but can you link to one not working?
@ovp87 Not sure I can get it to work. It seems to be hitting a limit. Looking at this screenshot below, if I manually cmd+c without the </a>
it copies, but with it it doesn't. I'm assuming this is a length issue but I'm really not sure.
The problem is it also doesn't trigger an error, and worse, the navigator.clipboard
actually has the content in it (if I use the Clipboard API), so I can't compare whether the copy was a success.
How about a new script that always downloads a file with the when you press copy? Something like component-being-copied.html
. Would that be useful you think? I don't want to write it if no one wants it :)
@ovp87 Not sure I can get it to work. It seems to be hitting a limit. Looking at this screenshot below, if I manually cmd+c without the
</a>
it copies, but with it it doesn't. I'm assuming this is a length issue but I'm really not sure.The problem is it also doesn't trigger an error, and worse, the
navigator.clipboard
actually has the content in it (if I use the Clipboard API), so I can't compare whether the copy was a success.How about a new script that always downloads a file with the when you press copy? Something like
component-being-copied.html
. Would that be useful you think? I don't want to write it if no one wants it :)
Thats weird 🤔 your idea of a download could be a nice workaround, still better than doing the job manually 👍
Shame Adam decided to remove alpine in the examples.
Ok I created version that will put the content into a file like with-image-grid.html
and download it.
Give it a try: https://gist.github.com/KevinBatdorf/322c68f532af6a4cc6773f906da0684d
I only lightly tested it, but it works with the component you shared. Let me know if any bugs.
I think it stopped working after the Templates update of TailwindUI. Anyone else experiencing issues?
Namaste to all.
@calebporzio is working on headless alpine UI and later he is planning to support Tailwind UI.
Please join him and contribute.
@bureauvk Link to the component and I'll check it out. If the html is too much the copy will fail. Try the version above where it saves to a file.
@kirantpatil I don't see anything about where to "join him and contribute" - Do you have more information on that?
@kirantpatil I dont think you can contribute to that, they will be selling it.
@KevinBatdorf every component fails on my end. Same with the script that downloads the code.
The script is running, but there are no nodes found.
We are using includes('clipboard')
in the script, I think something changed on their end with the clipboard.
I added the EventListener some other way, but I cant get the contentArea.
Also the iFrame doesn't have the x-ref value anymore.
Looks like they stopped using Alpine on their site. They also have duplicated id
attributes on the iFrame and it's wrapper.
Anyway, I updated it now (this gist and the file variety) to find the iFrame a bit differently. it's more fickle now since there isn't a clean way to identify the clipboard buttons anymore, but it works now by just matching the class list.
Let me know if it breaks again.
I just tried it out on the first nav item and it seems to work still. Make sure it's active on the site. You should see a red 1 in a square
Thanks @KevinBatdorf. It is working now. You are correct. Tampermonkey was not active. My bad.
This doesn't seem to be working anymore. Trying to use it on command palettes. I tried in both safari and chrome. Chrome only copies a nearly empty container. Safari copies exactly what is in the code preview.
Does it work on other components? It won't copy if the content is larger than the clipboard can hold. You could try the gist I mention in the comment on line 18
https://gist.github.com/KevinBatdorf/322c68f532af6a4cc6773f906da0684d
@KevinBatdorf no it doesn't work on other components. Just grabs the empty div with id of app
. Tried the gist you linked to also and does the same thing.
Just got it to work on a modal component. It seems spotty in what it can extract.
Just got it to work on a modal component. It seems spotty in what it can extract.
Saw this after I commented. I can try to see why some aren't working. It could be they changed something.
@KevinBatdorf Kudos for still maintaining this 👍
It no longer seems to work?
@aitoehigie I just tried it on a dropdown and it worked. Which componen isn't working?
I can confirm, it worked for me on the full Application UI Sidebar layout
Any idea how to get iFrame defined there? We could update this line if the former is undefined.