Docusaurus: add custom button to code block? - docusaurus

I’m testing to use Docusaurus for a tutorial and documentation site. I want to customize the code block in markup (md or mdx) to for example add an edit button inside the code block that will open the code in plnkr.
My question is, how can I add a button (or several buttons) in the code block and add click events to the buttons?
I have tested Eleventy https://www.11ty.dev/ recently and in that I used markdown-it-attrs to add attributes in markdown.
const markdownIt = require("markdown-it");
const markdownItAttrs = require("markdown-it-attrs");
and then in the markdown I could add attributes to a code block indicating if that should have an edit button or not.
```js {edit=yes}
let a = 2;
console.log(a);
```
And then in a script find this code block and add a button and an event listener.
.hasAttribute("edit")
.insertAdjacentHTML("afterbegin", `<button style="float: right;" class="code_edit">Edit</button>`);
.addEventListener("click", () => {
I have tried doing something similar in Docusaurus by adding a script in docusausrus.config.js
scripts: [{ defer: true, src: "/mycustom.js" }],
I can see that this script is added in the head but the script can’t find any html elements. I don’t know where to start with adding attributes to the markup.

Related

liquid schema works, but removes the section when modified from the editor

I am new to the entire Shopify and liquid environment. However I was able to modify a section that used the {%schema%} tag to display a control to set a background and the maximum width of a text box.
So I ventured to create a section for myself to add a small FAQ block on one of the pages.
I have read everything I can to make sure that I am not forgetting anything, I have also checked the code on existing sections that work correctly, and I cant find the reason for this issue.
when I open the page with the section in it in the Theme editor, I loads correctly and displays the default color. I also see the modifier block on the left pane, however as soon as I change the value in the editor, it simply makes the entire section disappear.
can somebody help me to point out what am I doing wrong?
thank you very much
this is the entire code in the section:
<style>
.faq{
max-width:900px;
width:80%;
}
.faq-container{
background-color:{{section.settings.container_background_color}};
display: flex;
justify-content:center;
}
</style>
<div class="faq-container">
<div class="faq" id="ndnappseasyfaqs-wrapper"></div>
</div>
{%schema%}
{
"name": "FAQ section",
"settings": [
{
"type": "color",
"id": "container_background_color",
"label": "Background color",
"default": "#a0cf67"
}
]
}
{% endschema %}
this is the result before I try to modify it:
before modification
this is the result as soon as I modify the color:
After modification
This might be an issue linked to preview update in JS.
Did you try to save your changes and see if it works?
According to Integration with the theme editor,
When merchants customize sections, the HTML of those sections is dynamically added, removed, or re-rendered directly onto the existing DOM without reloading the entire page.
JavaScript that runs when the page loads will not run again when a section is re-rendered or added to the page. This poses a problem for any custom scripts that would need to be re-run.
If you're running some js on page load, it won't be run again after the user makes a change in section. So I guess you have to run the js manually. Just bind the event with your event listener
document.addEventListener('shopify:section:load', function(event){
[your code...]
});

CKEditor 5 copy selected content from one editor to another

I have two editors on the screen, one read-only. What I want to do is allow the user to select content from the read-only editor and paste it into the current position of the other by clicking a button. (the logic may manipulate the text which is one reason I don't want to use the system's clipboard.)
So far I have the function that is able to paste the text like as follows. (I am using the Angular wrapper which explains the presence of the CKEditorComponent reference.
doPaste(pasteEvent: PasteEvent, editorComponent: CKEditorComponent) {
const editor = editorComponent.editorInstance;
editor.model.change(writer => {
writer.insertText(pasteEvent.text, editor.model.document.selection.getFirstPosition() );
});
}
What I can't find from the documentation is how to extract the selected text. What I have so far is:
clickPasteSelectedPlain(editorComponent: CKEditorComponent) {
const editor = editorComponent.editorInstance;
const selection = editor.model.document.selection;
console.log('clickPasteAll selection', selection);
console.log('clickPasteAll selectedcontent', editor.model.document.getSelectedContent);
}
The selection appears to change depending on what is selected in the editor's view. The getSelectedContent function is undefined. How do I get the content?
With a bit of poking around I figured out how to do this. I'll document it here on the chance that it will help someone down the road avoid the process of discovery that I went through.
On the source document I have a ckeditor element like this:
<div *ngIf="document">
<ckeditor #ckEditor
[editor]="Editor" [config]="ckconfig" [disabled]="true"
[(ngModel)]="document.text"></ckeditor>
<button mat-flat-button (click)="clickPasteSelectedPlain(ckEditor)">Paste Selected Text Plain</button>
</div>
In the component the function called on the click event is like this:
#Output() paste = new EventEmitter<PasteEvent>();
...
clickPasteSelectedPlain(editorComponent: CKEditorComponent) {
const editor = editorComponent.editorInstance;
this.paste.emit({
content: editor.model.getSelectedContent(editor.model.document.selection),
obj: this.document,
quote: false
});
}
The PasteEvent is defined as an exported interface which I will omit here to save space. The content key will refer to a DocumentFragment.
Note that I am passing the CKEditorComponent as a parameter. You could also access it via an Angular #ViewChild declaration but note that my ckeditor is inside an *ngIf structure. I think that works well in Angular 6 but in the past I have had difficulty with #ViewChild references when the target was conditionally in the DOM. This method always works but use whatever method you want.
The event fired by the emit is processed with a method that looks like this:
doPaste(pasteEvent: PasteEvent, editorComponent: CKEditorComponent) {
const editor = editorComponent.editorInstance;
editor.model.insertContent(pasteEvent.content);
}
Because the content is a DocumentFragment the paste operation will include all formatting and text attributes contained in the selected source. But that's all there is to it.

how to attach an event to dojox.mobile.heading 'back' button

In addition to the 'back' button functioning as expected, I need to asynchronously invoke a function to update some db tables and refresh the UI.
Prior to making this post, I did some research and tried the following on this...
<h1 data-dojo-type="dojox.mobile.Heading" id="hdgSettings" data-dojo-props="label:'Settings',back:'Done',moveTo:'svStart',fixed:'top'"></h1>
dojo.connect(dijit.registry.byId("hdgSettings"), "onclick",
function() {
if (gblLoggerOn) WL.Logger.debug(">> hdgSettings(onclick) fired...");
loadTopLvlStats();
});
Since my heading doesn't have any other widgets than the 'back' button, I thought that attaching this event to it would solve my problem... it did nothing. So I changed it to this...
dojo.connect(dijit.registry.byId("hdgSettings")._body, "onclick",
function() {
if (gblLoggerOn) WL.Logger.debug(">> hdgSettings(onclick) fired...");
loadTopLvlStats();
});
As it turns out, the '._body' attribute must be shared by the Accordion widget that I just happen to use as my app's main UI component, and any attempt to interact w the Accordion rendered my entire app useless.
As a last resort, I guess I could simply forgo using the built-in 'back' button, and simply place my own tabBarButton on the heading to control my app's transition and event processing.
If the community suggests that I use my own tabBarButton, then so be it, however there has to be a way to cleanly attach an event to the built-in 'back' button support.
Thoughts?
The following should do the trick:
var backButton = dijit.registry.byId("hdgSettings").backButton;
if (backButton) {
dojo.connect(backButton, "onClick", function() { ... });
}
Remarks:
The code above should be executed via a dojo/ready call, to avoid using dijit's widget registry before it gets filled. See http://dojotoolkit.org/reference-guide/1.9/dojo/ready.html.
Note the capitalization of the event name: "onClick" (not "onclick").
Not knowing what Dojo version you use (please always include the Dojo version information when asking questions), I kept your pre-AMD syntax, which is not recommended with recent Dojo versions (1.8, 1.9). See http://dojotoolkit.org/documentation/tutorials/1.9/modern_dojo/ for details.

Dynamic menu button items in TinyMCE

I have a custom menubutton in my tinyMCE editor that uses specific HTML elements elsewhere on the page as the menu items. I use a jQuery selector to get the list of elements and then add one each as a menu item:
c.onRenderMenu.add(function(c,m) {
m.add({ title: 'Pick One:', 'class': 'mceMenuItemTitle' }).setDisabled(1);
$('span[data-menuitem]').each(function() {
var val = $(this).html();
m.add({
title: $(this).attr("data-menuitem"),
onclick: function () { tinyMCE.activeEditor.execCommand('mceInsertContent', false, val) }
});
});
});
My problem is that this only happens once when the button is first clicked and the menu is first rendered. The HTML elements on the current page will change occasionally based on user clicks and some AJAX, so I need this selector code to run each time the menu is rendered to make sure the menu is fully up-to-date. Is that possible?
Failing that, is it possible to dynamically update the control from the end of my AJAX call elsewhere in the page? I'm not sure how to access the menu item and to update it. Something using tinyMCE.activeEditor.controlManager...?
Thanks!
I found a solution to this problem, though I'm not sure it's the best path.
It doesn't look like I can make tinyMCE re-render the menu, so instead I've added some code at the end of my AJAX call: after it has updated the DOM then it manually updates the tinymce drop menu.
The menu object is accessible using:
tinyMCE.activeEditor.controlManager.get('editor_mybutton_menu')
where mybutton is the name of my custom control. My quick-and-dirty solution is to call removeAll() on this menu object (to remove all the current menu items) and then to re-execute my selector code to find the matching elements in the (new) DOM and to add the menu items back based on the new state.
It seems to work just fine, though tweaks & ideas are always welcome!

dojo - how to allow a module to popup a dialog

i have a module. I need it to be able to display a modal dialog.
Seems like I need to inject a div into the main DOM and then parse it. Is this the right approach. Are there samples (or even a util- seems like this would not be that uncommon)
There are samples for almost everything in DojoCampus and in the tests directory:
var pane = dojo.byId('thirdDialog');
thirdDlg = new dijit.Dialog({
id: "dialog3",
refocus:false,
title: "Programatic Dialog Creation"
},pane);
Note that this particular widget doesn't need to be inserted to the DOM manually - it appends itself to the end of the page. Here the second parameter to the Dialog constructor - pane - is a reference to the node whose content should be displayed inside the Dialog.
UPDATE: Based on the new information you should try this:
dojo.require("dijit.Dialog");
dojo.addOnLoad(function() {
secondDlg = new dijit.Dialog({
title: "Programatic Dialog Creation",
style: "width: 300px",
content: "Insert text here"
});
secondDlg.show()
});
As shown here, you can pass Dialog content in the content attribute. (This sample is executable in FireBug on any page that includes Dojo.)
UPDATE: So, you want to have a form inside the Dialog? Nothing special here. Hey, you can even have a dijit form over there! Be sure to check out that DojoCampus article on Dialogs to learn how Dialog can communicate with a dijit form.
dojo.require("dijit.Dialog");
dojo.require("dijit.form.TextBox");
dojo.addOnLoad(function() {
secondDlg = new dijit.Dialog({
title: "Programatic Dialog Creation",
style: "width: 300px",
content: "<h2>Sample Form</h2>" +
"name: <input dojoType='dijit.form.TextBox' type='text' name='name' id='name'>"
});
secondDlg.show()
});
(Again this sample is executable in FireBug on any page that includes Dojo.)