You can create a custom component to add functionality that is not present in the UI Bakery components list.
Basics
As a developer, you can create a custom component to use in UI Bakery. This component can have its own logic and interface that is defined by you. Additionally, the custom component can communicate with other features in UI Bakery by triggering events and receiving data to display.
Custom components can be written in pure JavaScript or can import custom libraries, such as jQuery or React.
Please note that the custom component is rendered inside of an iframe, thus we recommend using it only for the fix-sized elements and avoid overlays and popups inside of it.
Additionally, you can use the Unrestricted custom component to render any HTML and JavaScript without any restrictions. This component is not rendered inside of an iframe.
Component anatomy
A Custom component is an HTML page embedded within an iframe that can contain any HTML, CSS, and JavaScript. You can provide its code in the Code setting. The Custom component can communicate with other UI Bakery components, actions, and app state. It can receive data and fire events.
A typical custom component would have the following structure:
3rd party scripts
custom styles
root element (usually a <div> tag)
custom JavaScript script tag
<!-- 3rd party scripts and styles --><scriptsrc="https://unpkg.com/react@17/umd/react.production.min.js"crossorigin></script><scriptsrc="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"crossorigin></script><scriptsrc="https://unpkg.com/babel-standalone@6/babel.min.js"></script><!-- custom styles --><style>body {padding:1rem; }p {margin-top:0; }button {margin-bottom:1rem; }.container {display:flex;flex-direction:column;align-items:flex-start; }</style><!-- root element where the component will be rendered --><divid="root"></div><!-- custom logic --><scripttype="text/babel">functionCustomComponent() {// receive data from UI Bakeryconstdata=UB.useData();return ( <divclassName="container"> <p>Data from UI Bakery: {data.title}</p> <buttononClick={() =>UB.triggerEvent("Data from custom component")}>Trigger Event</button> <inputonChange={(event) =>UB.updateValue(event.target.value)} placeholder="Set state"></input> </div> ); }constComponent=UB.connectReactComponent(CustomComponent);ReactDOM.render(<Component />,document.getElementById("root"));</script>
By default, the custom component is rendered inside an iframe, so there are no specific limitations to the code and styles specified by the developer.
Passing data inside of the component
To pass the data into your custom component use the "Data" setting. This can be done by passing a JavaScript object that contains the necessary data, such as:
{ data: [1,2,3], display:'only_new',}
Additionally, you can pass the data using JS API in your actions:
ui.customComponent.setData({ ... })
To access this data within the custom component, use the following code:
constdata=UB.useData()
You can also subscribe to updates of the data using the following code:
Receiving data and triggering actions from the component
If your custom component produces events or needs to trigger an action, use the following code:
To set the value of the component, use the following code inside the custom component:
UB.updateValue('Data from custom component');
Once executed, the new value is available as {{ui.customComponent.value}};
To trigger an action, use the following code inside the custom component:
UB.triggerEvent('Data from custom component');
Then, subscribe your action to the On Event trigger of the custom component. Once the UB.triggerEvent('data') is executed, the assigned action will be triggered.
The data supplied to the triggerEvent() function is available as {{ui.customComponent.value}} as well as {{params}} variable in the assigned action.
jQuery example
Copy and paste the whole example into the custom component Code field:
In this example, we will create a custom calendar to display the appointments.
Start with loading the data: add an action - Load Table with appointments data.
Find a Custom component and place it onto the working area.
In the component's Data field, assign the newly created action:{ events: {{ actions.loadAppointments.data }} }.
4. In the Code field, type in the below code:
<linkrel="stylesheet"href="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css"> <scriptsrc="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js"></script><divclass="container"> <divid="calendar"></div></div><style>body,html {height:100%;padding:0;margin:0; }.container {background:white;padding:2rem;height:100%;overflow:hidden;border-radius:0.25rem;border:0.0625rem solid #dde1eb;box-shadow:0 0.5rem 1rem 0 rgb(44 51 73 / 10%); }.fc-daygrid-event-harness {cursor:pointer; }</style><script>document.addEventListener('DOMContentLoaded',function() {var calendarEl =document.getElementById('calendar');var calendar =newFullCalendar.Calendar(calendarEl, { initialView:'dayGridMonth',eventClick: (info) => {// Update UI variable valueUB.updateValue({ id:info.event.id });// Event triggeringUB.triggerEvent({ id:info.event.id }); } });calendar.render();// Callback to process new data in custom componentUB.onData(data => {calendar.removeAllEvents();constevents= data &&data.events ?data.events : [];if (events && events[0]) {// In case of new data, the first event is automatically selectedUB.updateValue({ id: events[0].id });UB.triggerEvent({ id: events[0].id }); }events.forEach(event => {calendar.addEvent(event); }); }); });</script>
Your calendar is ready now!
Google Charts example
Google Charts offer a vast variety of charts, so you can connect the charts library to UI Bakery and code your own chart.
Now, let's create a project milestones Gantt chart. We will use the data about each project milestone from the data source and show its timeline and details on a Gantt chart.
For this example, we are using Google Sheets as a data source. In the table, there are the following columns:
Id
Task
Team
Responsible
Start date
ETA
Percent done.
2. Create a new Action - Code step. This action is to map the data to later use it in the chat configuration:
constrawRows=await {{actions.loadPhases.trigger()}};constmsInDay=24*60*60*1000;returnrawRows.map((row) => [// Task IDrow.id.toString(),// Task namerow.Task,// Task's start date row['Start Date'],// Task's end date can be omitted (null), if you have durationnull,// Duration in milliseconds. Use null if you have an end daterow.ETA*msInDay,// % of completion row['Percent done'] *100,// List dependencies, if necessary. Need to be listed comma-separated, e.g.'Development,NDA'null,]);
3. Find a Custom Component and drag it onto the working area.
4. In the Data field of the component, assign the newly created action: {{actions.mapData.data}}.
5. In the component's Code field, you'll need to specify the code for the Gantt. We'll take the sample from the documentation and then make adjustments according to your data:
<html> <scriptsrc="https://www.gstatic.com/charts/loader.js"></script> <head> <script>// Variables initialization, that will be used in a chartlet isLoaded =false;let rows = [];let chart;let chartData;// Loading Gantt chart librarygoogle.charts.load("current", { packages: ["gantt"] });// Chart initialization only after it's loadedgoogle.charts.setOnLoadCallback(() => {// Chatt initialization chart =newgoogle.visualization.Gantt(document.getElementById("chart_div")); chartData =newgoogle.visualization.DataTable();// Setting up chart structurechartData.addColumn("string","Task Id");chartData.addColumn("string","Task Name");chartData.addColumn("date","Start Date");chartData.addColumn("date","End Date");chartData.addColumn("number","Duration");chartData.addColumn("number","Percent Complete");chartData.addColumn("string","Dependencies"); isLoaded =true;drawChart(); });// Callback to process new data in custom componentUB.onData((data) => { rows = data ?? [];// Convert dates to Date objectsfor (let i =0; i <rows.length; i++) { rows[i][2] =newDate(rows[i][2]); // Convert Start Date }// The data can be received before the chart is initialiased.// That's why rendering of a chart with new data should be done after chart initializationif (isLoaded) {drawChart(); } });functiondrawChart() {// Clear previous data before adding new rowschartData.removeRows(0,chartData.getNumberOfRows());chartData.addRows(rows);constoptions= { height:600, gantt: { trackHeight:30, }, };chart.draw(chartData, options); } </script> </head> <body> <divid="chart_div"></div> </body></html>
Check the component now - you will see a Gantt chart populated. When you hover over a milestone, you will see additional details about it.
MUI React library template example
Here's an example of how to connect and use the MUI library to build custom components in UI Bakery:
Copy and paste the following code into the custom component Code setting: