-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plugins: Toolbar & Copy to Clipboard #891
Plugins: Toolbar & Copy to Clipboard #891
Conversation
This plugin exposes a `registerButton` method, which other plugins can use to add buttons to the toolbar. Comes with styles.
Registers a "Hello World!" tag with the toolbar.
@LeaVerou @Golmote @zeitgeist87 I'm on vacation this coming week, so I should have some time to tweak this and extract the other plugin to your preferences, if you have guys have a chance to take a look and let me know what you think. Thanks! |
Hi @mAAdhaTTah, I've tested your plugin and it works fine. I have a few comments on the plugin itself, but I can't really help you with your question about third-party dependencies.
Thanks for contributing to Prism! Sorry for the late response. |
It was mostly to expose the function in case a plugin/developer wanted to unhook and possibly run manually. Not sure if it's necessary, but figured the extra flexibility would be helpful down the line.
This is actually a good idea. What about making the function registerButton(opts) {
var callback;
if (typeof opts === 'function') {
callback = opts;
} else {
callback = function(env) {
// create element from opts.text & optional opts.onClick
};
}
callbacks.push(callback);
} I would probably set this up to ensure the
Can do. And if we like the polymorphic idea, we can use the helloworld button to demonstrate how to do it that way.
Interesting. Might be more useful as an example than the helloworld button. The Clipboard.js implementation uses "select-all" as a fallback when the actual copying fails (primarily in Safari, maybe older browsers).
I mostly copied it from the original implementation with some tweaks (I preferred having each button appear visually distinct), but I am not really a designer, so if anyone has any improvements, I'm all ears.
Not a problem! Figured it was worth following up, since you guys are pretty active. I've added checkboxes for the open questions remaining on this PR. If the polymorphic idea is acceptable, I should be able to make those changes tonight. |
I like that idea. It also allows later additions to the interface. Maybe someone would like to implement buttons with icons or something else... |
Regarding third party scripts, I think the plugin page should simply mention explicitly that script is required. And that's it. ^^ I think Prism should not distribute directly the third party script. |
@Golmote Any issues/considerations for Node/Browserify users? I don't know if that matters on the Node side, but Browserify users, I believe, will have to explicitly assign the Clipboard var as a global. I'll have to take a closer look myself, but would a |
I really don't feel like including it as a dependency of Prism globally. |
Yeah, I would be opposed to adding global dependencies to support a plugin. However, I'm not sure about just mentioning that Clipboard.js is needed. Part of why Prism is so easy to use is that you download a copy and it just works. I wonder if we can use gulp to pull in the latest version from clipboard.js’ repo and bundle it with the plugin. |
There is an |
This allows developers to provide either a callback or an object with a `text` string and an optional `onClick` function to create a new button.
6af7086
to
44d0c1d
Compare
So Alternatively:
This should be possible; maybe we could do something similar to the way the autoloader's language dependencies map is built. It would mean that the Clipboard.js library would be inlined into the Copy to Clipboard plugin.
The only issue with this is we can't be sure the CDN asset will be fully loaded before the highlighting starts, unless The only other idea I had was to modify the downloads page and ensure Clipboard.js is loaded first there when selecting the Copy to Clipboard plugin, but I'm not sure how to accomplish that at first glance of the I've pushed a first pass at Copy to Clipboard plugin. The demo page has Clipboard.js loaded from the CDN. Definitely interested in your feedback. |
if (typeof opts.onClick === 'function') { | ||
element = document.createElement('a'); | ||
element.addEventListener('click', function () { | ||
opts.onClick(env); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also pass the this
pointer like this:
opts.onClick.call(this, env, opts);
That way the callback function gets access to the element that was clicked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how important it is, but I didn't want to change the this
value of the passed in callback in case the developer is relying on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well without it, the this
pointer will point to the opts
object and the developer has no access to the created DOM element. I think it is standard practice to set the this
pointer to something sensible, like the source of the click event. In what specific use case would the developer want this
to be the opts
object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it was more about not messing with developer expectations, but I think you're right. Will update.
The design on the https://clipboardjs.com/ homepage looks nice. They have nice copy buttons for their source code samples. Nice fade-in and fade-out animations, nice icon, colored box-shadows and small transparent speech bubbles with status messages. We could use that design as an inspiration. |
This ensures additional HTML can't be passed to the toolbar via the `text` property, ensuring a consistent display for the buttons.
This provides access to the clicked element, which is what `this` is usually bound to on event listeners.
This will install Clipboard.js when installing from `npm`, but won't fail the build if the installation of Clipboard.js fails.
Added a hover animation and a drop shadown. Does that make it pop a little bit more? Also, I added |
* gh-pages: (22 commits) Remove direction-property from themes Add tests for new greedy-pattern feature and fix bug in Kotlin Fix double HTML-encoding bug in Groovy language Add comments to better document the greedy-pattern feature Partial solution for the "Comment-like substrings"-problem Add property 'aliasTitles' to components.js [unescaped markup] Fix small issues @zeitgeist87 pointed out [unescaped markup] Fixed bug with escaped </script> Added unescaped markup plugin (hidden) Implemented @zeitgeist87’s suggestion in PrismJS#890 re: env.elements Changed weight in the header, as we’re now 2KB minified & gzipped. Also way overdue :) Changed the text in the header. Way overdue, as Prism’s popularity has way surpassed that of Dabblet Add before-highlightall hook Fix catastrophic backtracking regex issues Add missing prism.js to the documentation of normalize-whitespace Cleanup normalize-whitespace and improve keep-markup integration Preserve Markup in Normalize-Whitespace plugin Update CHANGELOG and run gulp Removed firstWhiteSpaces code Add support for automatic line breaks ... # Conflicts: # components.js
Bump. Thoughts on handling the dependency? |
What about checking if Clipboard.js is loaded (e.g. via some global it defines) and if not, loading it dynamically from the CDN? That way we don't have the issues of bundling a possibly outdated version or the hassle of requiring users to load it themselves. |
var element; | ||
|
||
if (typeof opts.onClick === 'function') { | ||
element = document.createElement('a'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably use a <button type="button">
element here, instead of an <a>
.
Or we could allow for both depending on whether there is an onClick
property (button) or an href
property (link).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. I used the a
b/c there would be fewer style overrides, as browsers tend to add a number of default styles to buttons, but it would be more semantically correct.
I think there should be a way to reorder the labels/actions in the toolbar, especially since some of them may be added automatically by plugins, and so depend on the order in which the plugins executed. There could be a way to add buttons with the HTML API by providing a There should also be a way to add multiple labels, maybe with numbered attributes, like data-label-1, data-label-2, etc. <script>
function version_info() { alert('Some info about the version'); };
</script>
<pre data-label-1="v2.8" data-label-1-onclick="version_info" data-label-2="Download lib" data-label-2-href="http://example.com/lib.js"> I'm still very bad at naming things, so there are really just suggestions... |
That sounds interesting, but how do we coordinate between the HTML API & the JS API? Copy to Clipboard isn't using the HTML API; should it just go second, all of the HTML-based labels/buttons have been added? Regarding the |
You're right the data-label-onclick is quite useless. Regarding the coordination, I think there should be a way to position the buttons that were added through plugins. Maybe this could be achieved with other data attributes |
Also adds a `url` property which creats an anchor tag and sets the href. Adds some styles to override the button defaults.
This allows the HTML API to create links in the Toolbar.
I fixed the The HTML API provides a simple API for adding a single label. If you need multiple labels, or onClick handlers, or anything else more complex, I think we should push users to use the JS API. |
I'm fine with it. If there is a real need for multiple labels through HTML API, it might be added later anyway. What about the order, when using plugins? Can you think of a way to handle this using HTML? |
We could ask plugins to register by name, then have users declare on a the
We can fall back to the order they were registered if the data attribute doesn't exist. |
It seems good. Though I don't think there's a need for the brackets and single quotes. A comma separated list of aliases should be enough. Would it be possible to control the position of the label added through the data-label attribute this way? Maybe by giving it an arbitrary alias? |
In the above example, |
Oh no, sorry, "label" is fine! I missed it somehow. The plugins should probably register their exact name for consistency, i.e. "show-language". |
Idea: What about enabling |
Uses a data-attribute on the `body` tag to update the order, should the user choose to do so.
Just updated to control the order via data-attribute. @LeaVerou I'll take a look at that later today. Would be useful if someone wanted to include an icon or something of that nature. How would someone declare their event handlers in a <template id="my-custom-button">
<button onclick="alert('Hi')">Hello!</button>
</template> |
If we wanted to go crazy w/ that idea, we could even implement a mini-DSL and allow people to slot in the code snippet's language and such into the label: <template id="my-custom-button">
<button onclick="alert('Hi')">Hello {{language}}!</button>
</template> |
Either inline or through event delegation. |
This provides one of several options a user can implement in order to get a custom button. Also fixes some bugs in the documentation.
e471398
to
87e2aeb
Compare
@LeaVerou PR updated to include that suggestion. I think that addresses all of the questions I had when I opened the PR originally. I know this is a big one, and I just want to say thank you for everyone's patience and feedback in working through this. |
Will have some time this weekend to make edits as needed. Bumping this PR; maybe get a merge? |
* gh-pages: Remove important token in ini definition (PrismJS#1047) Add missing `from` keyword to typescript & set `ts` as alias. (PrismJS#1042) Fix greedy-flag bug Add yarn.lock (PrismJS#1035) update patterns (PrismJS#1032) Test suite: fixed missing diff in error message
The autoloader will rehighlight the element after the language arrives. This means the complete hook can run multiple times. Without a check, multiple toolbars can get added to an element.
Sorry for the late answer. I'm merging now. |
* PrismJS/gh-pages: Plugins: Toolbar & Copy to Clipboard (PrismJS#891) Ini: Fix test after PrismJS#1047 Add support for the Jolie language (PrismJS#1014) Fix order of decoding entities in groovy (PrismJS#1049) (PrismJS#1050) Ruby: Make strings greedy. Fixes PrismJS#1048 Ini: Remove newline at end of minified file. Ruby: Fix test after PrismJS#1023 Remove important token in ini definition (PrismJS#1047) Add missing `from` keyword to typescript & set `ts` as alias. (PrismJS#1042) Fix greedy-flag bug Add yarn.lock (PrismJS#1035)
No worries! Happy to contribute. |
I'm using Prism as the syntax highlighter for my WordPress plugin, WP-Gistpen. I've built a toolbar plugin that I'd like to contribute to Prism. I've got a first pass at extracting the plugin from its CommonJS origins,, but I need some feedback on how to change it to fit the Prism way of doing things, e.g. the HTML API. I also see some possible synergy with the Parse Settings plugin.
The Toolbar exposes two methods: a
hook
method, which is registered with Prism'scomplete
hook, and aregisterButton
method, which takes a callback that creates the buttons. The callback will be passed theenv
and should return ana
orspan
element, which other plugins can use to register themselves with the Toolbar to get their text or link to display.I've also created a Clipboard plugin is an example of this callback, which creates an anchor tag that, when clicked, copies the code to the clipboard. I plan to extract that into a Prism plugin as well, but I'm not sure how Prism wants to handle dependencies like Clipboard.js, or how the download page will work with internal dependencies like this. In the interest of user friendliness, what's the best approach to take here?
Prior art:
Open Questions:
registerButton
method be polymorphic?