React Material UI Visual Editor - reactjs

I'm starting an open source project, a visual editor for React Material UI.
This is the link to the project.
Users will be able to drag and drop material components on the left drawer to the dropzone (middle/user layout), and use the toolbox on the right drawer to edit the CSS of individual components in the dropzone. Finally with a click of the button, the platform will generate react/ react-material-ui code and also have the capability convert the xml structure to a json for various device purposes.
This project is at the very beginning phase where I only have 1 button component.
Before diving deeper I would like to understand if my current implementations are valid or if there are better ways to implement this.
Particularly:
When I handle drag start:
ev.dataTransfer.setData("text/plain", ev.target.id);
ev.dataTransfer.effectAllowed = 'copy';
ev.dataTransfer.setData('text/html', ev.currentTarget.innerHTML);
and when I handle drop:
ev.preventDefault();
ev.stopPropagation();
let html = ev.dataTransfer.getData("text/html");
ev.currentTarget.style.border = "none";
let text = ev.dataTransfer.getData("text/plain");
let element = document.getElementById(text)
let element_prime = element.cloneNode(true)
ev.currentTarget.append(element_prime)
The reason why I feel uncomfortable is because I'm actually using the document queries, which is not exactly the "react way" of doing things.
I'm thinking of only using createRef() when selecting a component in the dropzone when working on the CSS in the toolbox area.
link to createRef()
I generate the ids of the components with:
import { nanoid } from '#reduxjs/toolkit'

######## UPDATE - 26 Apr 2022 ########
Based on some work done and relooking into the comments by #tunaayberk I found a way to gracefully handle it the react way. So react-dnd does make use of the redux flow and it has its own provider. An even better way to form the hierarchical json tree is to actually store it in the global state and just rendering the components in the dropzone from the json tree. That way we do away with handling the MUI classnames, DOM nested targets etc. Graceful way to handle this project using the react way.
checkout my medium post and give it some claps: https://medium.com/#kenneth.gzhao/react-material-ui-visual-editor-d1949262bce4

Related

How to know deep inside component hierarchy we have a special component, efficiently?

I'm creating a form builder using React.
The point is, everything is dynamic.
A form can have fields, or field groups. Each field group can have many fields. Each field can have it's own controls, hints, valdiations, etc.
This means that a form is a component hierarchy. A tree of components.
Now I want to know if in this tree, any Upload field exist at all.
Because if there is no Upload field, then I submit the form using application/json content type. Otherwise I would submit the form using application/form-data content type.
I know I can traverse the entire JSON of component props. But that seems very inefficient.
Is there a way to traverse React's component tree in an efficient way?
You can do so by using contextApi or with its hook for React Version > 16.8 the useContext.
Read more in React official docs
My suggestion is to use formik npm package to build your form correctly and easily, formik using context for managing the state behind the scene. I recommend it for regular use of forms, not heavy form with many fields (~25-30 max) like in enterprises.
You can read about formik more here:
https://formik.org/docs/api/formik
There is another alternative for formik which is also popular in react using forms:
react form hook: https://react-hook-form.com/
I didn't use it yet but as I know it has some pros/cons over formik.
Easiest way is vanilla JS.
const FORM_SELECTOR = '#some-form-id';
const UPLOAD_SELECTOR = '.some-upload-class';
const form = document.querySelector(FORM_SELECTOR)
if(form.querySelectorAll(UPLOAD_SELECTOR).length) {
// upload exists
} else {
// upload does not exist
}

declarative composition vs imperative in react js

While designing the application in react js to increase the reusability I have used the Tabs and then passed the tabs and headers something like this
const tabs ={
"tabHeader1": TabContent1,
"tabHeader2": TabContent2
}
<SwipableTabs tabs={tabs} />
Now my confusion arose when I had to render them permission based , In order to avoid if else juggling I have designed a component like below :
<ProtectedAction>
{children}
</ProtectedAction>
my ProtectedAction component will check for permission and will render the children based on that. Which is exactly what react suggest(be declarative).
Now when I see the above example like tabs which is data driven I am forced to use if else again which I wanted to get rid of.
Please suggest if any better approach is possible .

Office Fabric UI custom headers

I am using Office Fabric UI React Detaillist component. I want to add a heading on top of column headers like below:
Please help how to achieve this using offfice Fabric UI React. Reference offuce Fabric UI react detail list below:
https://developer.microsoft.com/en-us/fabric#/components/detailslist
Docs on fabric ui are really outdated. While package is updated regularly (which is great).
Consider looking through source code / examples on github for better understanding of this package.
Looking at IColumn type at https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/DetailsList/DetailsList.types.ts#L299
it has
/**
* If provided uses this method to render custom cell content, rather than the default text rendering.
*/
onRender?: (item?: any, index?: number, column?: IColumn) => any;
which you can utilize to render header cell any way you like.
Here are some examples
https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/DetailsList/examples/DetailsList.CustomColumns.Example.tsx
https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/DetailsList/examples/DetailsList.CustomGroupHeaders.Example.tsx

Office UI Fabric - How to apply css styles to existing components

I'm using the provided components and every time I need to change a component style I wonder what's the proper way to do it.
Lets say I need to change the IconButton background color when it's disabled.
https://codepen.io/elsl/pen/KrQQdV
If I provide a theme, how am I supposed to know which palette/semanticColor is used by that component?
const iconsTheme = Fabric.createTheme({
semanticColors: {
disabledBackground: "#ff9933"
}
});
<Fabric.IconButton
iconProps={{iconName:'ChevronRight'}}
disabled
theme={iconsTheme}
/>
If I provide an IButtonStyles, how am I supposed to know that the property name is "rootDisabled.backgroundColor"?
const iconButtonStyles: IButtonStyles = {
rootDisabled: {
backgroundColor: "#ff0000",
}
};
<Fabric.IconButton
iconProps={{iconName:'CalculatorEqualTo'}}
disabled
styles={iconButtonStyles}
/>
For both these options, I had to dig into the component's source code on github to find out.
Is this the expected/correct way?
If so, between creating a Theme or an IStyle which would be the ideal/best practice?
Theme vs IStyles
I would say, use a Theme if you want all Fabric components to have the same customization.
Use the styles property if you just want to customize that specific component (or that one specific instance of the component).
How to discover the styling hooks if using IStyles
There are four ways that comes to mind.
Look at the documentation (e.g. https://developer.microsoft.com/en-us/fabric#/components/dropdown, look at the IDropdownStyles interface)
(screenshot)
Utilize IntelliSense if you're using an editor like Visual Studio Code, which automatically enumerates the IComponentStyles and provides documentation if any.
Inspecting the DOM often provides hints (the hook areas usually look like {area}-{number} so root-33 for instance where the "area" name is root.
Read the source code.
Unfortunately for option 1 and option 2, Fabric React isn't super consistent with the IComponentStyles documentation so not all components have equally descriptive comments and in those cases, you may need to fallback to option 3 and option 4.

I don't know how to organize a web app that uses component based web framework

I am trying to learn component based frameworks for frontend apps. Currently, I am using RiotJS but it applies to any framework that uses the same concepts (React, Angular 2.0 etc).
In a basic MVC frontend frameworks (e.g AngularJS), the controllers and router were very connected to each other. But with a component based framework, the line between router and controllers is much wider. And this is what confuses me the most.
Here is one example of an app that I am trying to build:
I have three main UI elements: Navigation Bar, Content Area, and Signin Form. So, I created three components: my-navbar, my-content, my-signin. I was able to create multiple routes per component. So for example, if there is a route changes, the navbar updates the active "module." Making this was easy because all I am doing is changing class of a list item.
Now, I want to load other tags inside <my-content></my-content>. In AngularJS, I was always changing the view completely (using ui-router). How can I achieve that in a component based framework. Let's say that I have 2 more components called my-content-users-list-view, my-content-users-detail-view. How can I add them to the component my-content based on the route? Do I just add it like document.innerHTML += '<my-content-users-list-view></my-content-users-list-view>?
I know most of my syntax is RiotJS but I will understand it if you write it in another framework's syntax.
Thank you!
Essentially, yes, you could just append your tag as a DOM node and then call Riot to mount it:
riot.route('/awesome-route', () => {
const tag = 'your-awesome-tag';
const options = { ... };
const elem = document.createElement(tag);
// TODO empty your content container using pure DOM or jQuery to get rid of the previous route's view...
document.querySelector('#content').appendChild(elem);
riot.mount(elem, tag, options);
});

Resources