How do I customize the a generated-index page in Docusaurus? - docusaurus

I have a generated-index page and I can see how to customize the on page title and description in the category.json for the directory, but is there a way to customize the items that are generated by the files that are in the same directory? for example:
tutorials
_category_.json
01-first-tutorial.md
02-second-tutorial.md
I want to be able to have an icon for each of the files and different text than what is pulled from those files first paragraph like seems to be the default. What I want perhaps looks something like this page, but the icons and text need to be links to the tutorial pages.
I have tried using a DocCardList, adding in descriptions, adding in items (failed), and changing each of my tutorial files, but so far no love.

EDIT:
They've come up with a new component called DocCardList which you can use in version 2.3.0.
Create an index.mdx file in your category folder.
Add the following:
import DocCardList from '#theme/DocCardList';
<DocCardList />
Swizzle or otherwise override this component in your src/theme folder to add custom styling, etc.
ORIGINAL ANSWER:
Maybe you could try swapping the generated index component using the docCategoryGeneratedIndexComponent prop (link to reference). That would replace all auto-generated index pages which might be what you want.
In docusaurus.config.js, in the presets section, add
presets: [
[
"classic",
/** #type {import('#docusaurus/preset-classic').Options} */
({
docs: {
sidebarPath: require.resolve("./sidebars.js"),
docCategoryGeneratedIndexComponent:
"#site/src/components/CategoryIndexPage",
},
// etc.
}),
],
],
And then try adding the following custom component under src/components/CategoryIndexPage.tsx:
import React from "react";
export default function CategoryIndexPage(props) {
return (
<pre>
<code>{JSON.stringify(props, null, 2)}</code>
</pre>
);
}
This will just show you what the prop structure is in the component.
When I looked in the theme component which generates this page, it uses
const category = useCurrentSidebarCategory();
But when I try that to get the list of items, I get the following error:
Hook useDocsSidebar is called outside the .
Maybe you can figure out the next steps, I was not able to. 😅
Alternatively, you can create an index.mdx file in your category folder and import a custom React component into that. That gives me the same context violation error, though.
# My custom category page
Some Markdown content here.
import CategoryIndex from "#site/src/components/CategoryIndex.tsx";
<CategoryIndex />

Related

Add DIV Element to Layout of the Header

Having a problem, that I need to add a Element to the NavigationBar in the Header for styling purposes
I would need to have
<header>
<cx-pagelayout>
<div>
<cx-pageslot>
The only option I see is to overwrite the storefront component but i would like to not overwrite a main component. Is there any other option that I not see?
I can see two ways of going about it.
Like you mentioned you could create your own storefront component and create a header with the DOM you want there.
You can use the outlets mechanism to replace the header with your custom component. This would look something like this:
<ng-template cxOutletRef="cx-header">
<custom-header></custom-header>
</ng-template>
or use the provideOutlet provider in your custom header's module like so:
providers: [
provideOutlet({
id: 'cx-header',
position: OutletPosition.REPLACE,
component: CustomHeaderComponent,
}),
],
In the above example, custom-header (CustomHeaderComponent) is your header component. Also note, you need to import the OutletRefModule in your module.
Note, if you use the HTML method: to override the header outlet, you need to put your OutletRef in the app.component.html

Prevent DOM reuse within lit-html/lit-element

I am looking for a way to NOT reuse DOM elements within lit-html/lit-element (yes, I know, I'm turning off one of the prime features). The particular scenario is moving an existing system to lit-element/lit-html that at certain points embeds the trumbowyg WYSIWYG editor. This editor attaches itself to a <div> tag made within lit-element and modifies its own internal DOM, but of course lit-html does not know that this has happened, so it will often reuse the same <div> tag instead of creating a new one. I am looking for something similar to the vue.js key attribute (e.g., preventing Vue from aggresively reusing dom-elements)
I feel like the live() directive in lit-html should be useful for this, but that guards against reuse based on a given attribute, and I want to prevent reuse even if all attributes are identical. Thanks!
I have had similar issues with rich text editors and contenteditable - due to how templates update the DOM you don't want that to be part of a template.
You do this by adding a new element with the non-Lit DOM and then adding that to the DOM that Lit does manage:
class TrumbowygEditor
extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const div = document.createElement('div');
shadow.appendChild(div);
const style = document.createElement('style');
// Add CSS required
shadow.appendChild(style);
$(div).trumbowyg(); //init
}
}
customElements.define('trumbowyg-editor', TrumbowygEditor);
As this is running in a custom element's shadow DOM Lit won't touch it, you can do:
html`
<div>Lit managed DOM</div>
<trumbowyg-editor></trumbowyg-editor>`;
However, you will have to implement properties and events on TrumbowygEditor to add everything you want to pass to or get from the nested jQuery component.
You can add the scripts with import if you can get module versions of jQuery/Trumbowyg (or your build tools support it) or you can add <script> tags to your component, add fallback loading DOM content in the constructor, and then on the load event of the <script> call the $(div).trumbowyg() to init the component.
While messier and more work I'd recommend the latter as both components are large and (thanks to jQuery being built on assumptions that are now 15 years old) need to load synchronously (<script async or <script defer don't work). Especially on slower connections Lit will be ready long before jQuery/Trumbowyg have loaded in, so you want <trumbowyg-editor> to look good (show spinner, layout in the right amount of space etc) while that's happening.
You write that you attach the external library directly to an element managed by lit-html. It sounds like you're doing essentially this:
render(html`<section><div id=target></div></section>`, document.body)
external_lib.render_to(document.querySelector("#target"))
If this is what you do instead try to create your own div, let the external lib render to that div, and finally attach that div to lit-html:
let target_div = document.createElement('div')
render(html`<section>${div}</section>`, document.body)
external_lib.render_to(target_div)
The most up-to-date answer to this problem is to use Lit's built-in keyed directive. This scenario is exactly what it's for:
https://lit.dev/docs/templates/directives/#keyed
Associates a renderable value with a unique key. When the key changes, the previous DOM is removed and disposed before rendering the next value, even if the value—such as a template—is the same.
#customElement('my-element')
class MyElement extends LitElement {
#property()
userId: string = '';
render() {
return html`
<div>
${keyed(this.userId, html`<user-card .userId=${this.userId}></user-card>`)}
</div>`;
}
}

How to import multiple vue files as one

In other to avoid multiple imports into my vuejs app I created an index.js file and imported all the files in it like so:
import AddMember from "./AddMember.vue";
import EditMember from "./EditMember.vue";
export {
AddMember,
EditMember,
};
Then in my component compenent I imported them like so:
import * as Members from "../members/index.js";
export default {
name: "members-table",
components: {
AddMember: Members.AddMember
EditMember: Members.EditMember,
},
}
The EditMember Component is a dialog that opens up per the member clicked. But Anytime I click on a member on a the table I get and error that looks like this: even though the name prop was defined in all the components.
Unknown custom element: <edit-member> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
I resolved the problem my importing the EditMember.vue file itselfimport EditMember from './EditMember';. My question however, is there a way I can achieve this. Or better still what I'm I missing or did wrong.
well if it`s reusable components your trying to do so wouldnt it be better to create base components? and then you dont need to import them each time?
import { AddMember, EditMember } from "../members/index.js"; this should work like #Asimple said
Maybe you can try to import them separately?
Like this:
import { AddMember, EditMember } from "../members";
Update:
Changed import source, please, try it.
Working example here
Try this, you may need to create alias as:
components: {
'AddMember': Members.AddMember, // use single quotes
'EditMember': Members.EditMember,
},

Vuetify and require.js: How do I show a dynamic component?

I am creating a tab component that loads its v-tab-item components dynamically, given an array of configuration objects that consist of tabName, id, and tabContent which is a resource location for the component. I have it successfully loading the components. However, they don't actually initialize (or run their created() methods) until I switch tabs. I just get empty tabs with the correct labels. Using the DOM inspector initially shows just <componentId></componentId>, and then when I switch tabs, those tags are replaced with all of the component's content.
How do I get the dynamic components to initialize as soon as they are loaded?
EDIT: I created a CodePen here:
https://codepen.io/sgarfio/project/editor/DKgQON
But as this is my first CodePen, I haven't yet figured out how to reference other files in the project (i.e. what to set tabContent to so that require.js can load them up). I'm seeing "Access is denied" in the console, which makes it sound like it found the files but isn't allowed to access them, which is weird because all the files belong to the same project. So my CodePen doesn't even work as well as my actual project. But maybe it will help someone understand what I'm trying to do.
Also, after poking around a bit more, I found this:
http://michaelnthiessen.com/force-re-render/
that says I should change the key on the component and that will force the component to re-render. I also found this:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Which has a pretty good example of what I'm trying to do, but it doesn't force the async component to initialize in the first place. That's what I need the async components to do - they don't initialize until I switch tabs. In fact they don't even show up in the network calls. Vue is simply generating a placeholder for them.
I got it working! What I ended up doing was to emit an event from the code that loads the async components to indicate that that component was loaded. The listener for that event keeps a count of how many components have been loaded (it already knows how many there should be), and as soon as it receives the right number of these events, it changes the value of this.active (v-model value for the v-tabs component, which indicates which tab is currently active) to "0". I tried this because as I noted before, the async components were loading/rendering whenever I switched tabs. I also have prev/next buttons to set this.active, and today I noticed that if I used the "next" button instead of clicking on a tab, it would load the async components but not advance the tab. I had already figured out how to emit an event from the loading code, so all I had to do at that point was capture the number of loaded components and then manipulate this.active.
I might try to update my CodePen to reflect this, and if I do I'll come back and comment accordingly. For now, here's a sample of what I ended up with. I'm still adding things to make it more robust (e.g. in case the configuration object contains a non-existent component URL), but this is the basic gist of it.
created: function() {
this.$on("componentLoaded", () => {
this.numTabsInitialized++;
if(this.numTabsInitialized == this.numTabs) {
// All tabs loaded; update active to force them to load
this.active = "0";
}
})
},
methods: {
loadComponent: function(config) {
var id = config.id;
var compPath = config.tabContent;
var self = this;
require([compPath], function(comp) {
Vue.component(id, comp);
self.$emit("componentLoaded");
});
}
}

Vue multiples components

I'm doing a project with ElementUI Tabs (just HTML and JS files, no .vue files) and I want to open a new Tab, and add html inside, like I've always used to do in Jquery and SemanticUI, for example, the user clicks the menu called "Person" and the Person View (a Vue component) opens in the tab (id = "tab1") to add a new person register, and if the user clicks again the "Person" menu, another tab opens (id = "tab2") with the Person View.
First Question: Because the Vue Component has no "el:" selector, how can I tell to component (Person View) to open inside the "tab1", and another click to open inside the "tab2" ? There is any selector like "el" in Vue.component()?
Second Question: Using Vue instance ( new Vue ({options}) ), it works, because is possible to use the selector "el", but I've read before in some blogs, that is not good practice, because the app must have only one instance of Vue. Is correct add more than one Vue instance ( new Vue () ) as used to be done adding many Vue.component ({}) in the project?
Third Question: I've read before that Vue.component() is a Vue instance, and so would be correct to say that Vue.component() and Vue() is the same thing, but with different sintax ?
Question 1:
Actually, a component does have an el. Your template determines what el is.
For example, I created an inline template for my select2 that look like this:
<select2>
<select></select>
</select2>
Vue.componet("select2", {blah blah blah});
in this case el is the select box directly.
If I did:
<select2>
<div>
<select></select>
</div>
</select2>
the component el would be the div.
Question 2: what you heard from those blogs is nonsense, at least as far as Vue 2 is concerned (never worked with ver 1)
You, as a coder, determine what el is in your code so it is safe to use as a selector. I do it all of the time.
Vues cannot overlap but you can have as many on a page as makes sense. On one set of my tabs, each tab is completely different from each other and independent of each other so each has its own Vue instance. On another, each tab is the same so a made a single component and generated it inside each tab as part of the parent Vue instance.
question 3:
Think of Components as parts and the Vue instance as the whole containing the parts. I personally use components to reduce and compartmentalize code. For example, I have a DataTables component, a select2 component and a tab component, in all cases I have a number of each on each page. Then all I need to do is include them in my Vue instance definition.
After almost two weeks trying, I got it !
First i created an object that has a component structure in a JS file
(personview.js) that i load with requireJS, and pass as a parameter to a
method of Vue Instance called appVue:
appVue.addComponent(componentName,{name:"personview",template:"<div>html tags...</div>",methods:...});
In appVue i added the method:
var appVue=new Vue({
el:'#app',
data() {
return {
components: {},
instances: {}
}
},
methods: {
addComponent(componentName,componentBody){
this.$data.components[componentName]=Vue.component(componentName,Vue.extend(componentBody));
}
}
}
When the user clicks on menu, the method openViewByClickOnMenu is called
and executes:
methods: {
openViewByClickOnMenu(){
//id to identify the components and scripts to load
var componentName="personView"; //for this example i forced the name
//call a method that adds the new tab, and inside the tab adds
//<div id="divX"></div> and return the counter ever increased.
//X in id attribute is the number genereate by the counter
var ctTab=body.addTab({label:menuItem.label});
// will be used to identify an instance of compoment
var componentId=componentName+ctTab; //will be personView1, personView2, etc..
// will be used to identify the div where i want to show component
var divTabId="div"+ctTab;
//load the personview.js with component body
requirejs([componentName],function(){
//creates a new instance of component
app.$data.instances[componentId]=new app.$data.componentes[componentName];
//mounts the component in the div that i want
app.$data.instances[componentId].$mount("#"+divTabId);
});
}
I think the Vue team could add a method in Vue instance to add
components dinamically more easily, sometimes there's no need to
load all html and js files because the user has no acess/permissions
to see some views. And i miss a way to load html native, like
Angular does, because sometimes we need generate html from template engine
inside a SpringBoot for example.