Adding a Custom Top Bar for ContentBox.js

This article continues from the previous one, 'Using ContentBox.js in a React Project'. Now, we will add a custom top bar for our ContentBox.js. Within the top bar, you can include your logo, links, or additional buttons to meet your specific needs.

Implementation

Open your ContentBox.js implementation in src/App.jsx and replace the existing code with the following:

import { useEffect, useState } from 'react';
import ContentBox from '@innovastudio/contentbox';
import '@innovastudio/contentbox/public/contentbox/contentbox.css';
import '@innovastudio/contentbuilder/public/contentbuilder/contentbuilder.css';

let builder;

function App() {

  const [topbarOpacity, setTopbarOpacity] = useState(0);

  useEffect(() => {
    
    builder = new ContentBox({

      // More Settings
      canvas: true, // Enable the new canvas mode
      previewURL: 'preview.html',
      controlPanel: true, // Enable the new control panel
      iframeSrc: 'blank.html', // Enable resizable content area
      topSpace: true, // Needed when using custom toolbar
      iframeCentered: true,
      onStart: ()=>{
          setTopbarOpacity(1); // Shows custom topbar (by setting opacity to 1)
      },
      // Hide uneeded buttons (we'll add these buttons on the topbar)
      toggleDeviceButton: false, 
      deviceButtons: false,

      // Default file/image picker
      imageSelect: '/assets.html',
      videoSelect: '/assets.html',
      audioSelect: '/assets.html',
      fileSelect: '/assets.html',
      mediaSelect: '/assets.html', // for image & video
      // You can replace the default file/image picker with your own asset/file manager application
      // or use Files.js Asset Manager: https://innovastudio.com/asset-manager

      // Template Sets
      templates: [
          {   
              url: 'assets/templates-simple/templates.js',
              path: 'assets/templates-simple/', 
              pathReplace: [],
              numbering: true,
              showNumberOnHover: true,
          },
          {   
              url: 'assets/templates-quick/templates.js',
              path: 'assets/templates-quick/', 
              pathReplace: [],
              numbering: true,
              showNumberOnHover: true,
          },
          {   
              url: 'assets/templates-animated/templates.js',
              path: 'assets/templates-animated/', 
              pathReplace: [],
              numbering: true,
              showNumberOnHover: true,
          },
      ],

      slider: 'glide', // Enable slider
      navbar: true, // Enable navbar
    });

    // Adding more ContentBox features on the left sidebar (Animation & Timeline editor buttons)
    builder.addButton({ 
      'pos': 2,
      'title': 'Animation',
      'html': '<svg class="is-icon-flex" style="fill:rgba(0, 0, 0, 0.7);width:14px;height:14px;"><use xlink:href="#icon-wand"></use></svg>', 
      'onClick': ()=>{
          builder.openAnimationPanel();
      }
    });
    builder.addButton({ 
      'pos': 3,
      'title': 'Timeline Editor',
      'html': '<svg><use xlink:href="#icon-anim-timeline"></use></svg>', 
      'onClick': ()=>{
          builder.openAnimationTimeline();
      }
    });

    builder.addButton({ 
      'pos': 5,
      'title': 'Clear Content',
      'html': '<svg class="is-icon-flex"><use xlink:href="#icon-eraser"></use></svg>', 
      'onClick': (e)=>{
          builder.clear();
      }
    });

    // Load sample content
    let html = `<div class="is-section is-section-100 is-box type-system-ui">
      <div class="is-overlay"></div>
      <div class="is-container v2 size-18 leading-14 is-content-1300">
        <div class="row">
          <div class="column">
              <h1 class="leading-11 size-88">We design. We develop. We get it done — nicely.</h1>
          </div>
        </div>
      </div>
    </div>`;
    let mainCss = '';
    let sectionCss = '';

    builder.loadHtml(html); // Load html
    builder.loadStyles(mainCss, sectionCss); // Load styles

    return () => {
      // cleanup
      builder.destroy();
      builder = null;
    };
  
  }, []);
  
  // Topbar Functions
  function back() {
    //
  }

  function save(e) {
    //
  }

  function publish(e) {
    //
  }

  function undo(e) {
    if(!builder) return;
    builder.undo();
  }

  function redo(e) {
    if(!builder) return;
    builder.redo();
  }

  function setScreenMode(e) {
    if(!builder) return;

    document.querySelectorAll('.custom-topbar [data-device]').forEach(btn=>btn.classList.remove('on'));

    const btn = e.target.closest('button');
    const screenMode = btn.getAttribute('data-device');

    builder.setScreenMode(screenMode);
    btn.classList.add('on');
  }

  function download() {
    if(!builder) return;
    builder.download();
  }

  function viewHtml() {
    if(!builder) return;
    builder.viewHtml();
  }

  function preview() {
    if(!builder) return;
    
    let html = builder.html();
    localStorage.setItem('preview-html', html); 
    let mainCss = builder.mainCss(); 
    localStorage.setItem('preview-maincss', mainCss); 
    let sectionCss = builder.sectionCss();
    localStorage.setItem('preview-sectioncss', sectionCss);

    window.open('/preview.html', '_blank').focus();
  }

  function togglePanel() {
    if(!builder) return;
    builder.toggleEditPanel();
  }

  return (
    <>
      <div className="builder-ui keep-selection custom-topbar" style={{opacity:topbarOpacity}} data-tooltip>
        <div>
            Your Name
        </div>
        <div>
          <button className="btn-back" onClick={back} title="Back">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-back"></use></svg>
              ` }} />
              <span>Back</span>
          </button>

          <div className="separator"></div>

          <button className="btn-undo" onClick={undo} title="Undo">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-undo"></use></svg>
              ` }} />
          </button>

          <button className="btn-redo" onClick={redo} title="Redo">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-redo"></use></svg>
              ` }} />
          </button>

          <button onClick={save} title="Save">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-save"></use></svg>
              ` }} />
              <span>Save</span>
          </button>
          
          <button onClick={publish} title="Publish">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-publish"></use></svg>
              ` }} />
              <span>Publish</span>
          </button>
          
        </div>
        <div>
          <button className="btn-device-desktop-large" data-device="desktop-lg" onClick={setScreenMode} title="Desktop - Large Screen">
              <div dangerouslySetInnerHTML={{ __html: `
                  <svg style="width:18px;height:18px;"><use xlink:href="#icon-device-desktop"></use></svg>
              ` }} />
          </button>
          <button className="btn-device-desktop" data-device="desktop" onClick={setScreenMode} title="Desktop / Laptop">
              <div dangerouslySetInnerHTML={{ __html: `
                  <svg style="width:18px;height:18px;"><use xlink:href="#icon-device-laptop"></use></svg>
              ` }} />
          </button>
          <button className="btn-device-tablet-landscape" data-device="tablet-landscape" onClick={setScreenMode} title="Tablet - Landscape">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg style="width:18px;height:18px;transform:rotate(-90deg)"><use xlink:href="#icon-device-tablet"></use></svg>
              ` }} />
          </button>
          <button className="btn-device-tablet" data-device="tablet" onClick={setScreenMode} title="Tablet - Portrait">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg  style="width:18px;height:18px;"><use xlink:href="#icon-device-tablet"></use></svg>
              ` }} />
          </button>
          <button className="btn-device-mobile" data-device="mobile" onClick={setScreenMode} title="Mobile">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg  style="width:18px;height:18px;"><use xlink:href="#icon-device-mobile"></use></svg>
              ` }} />
          </button>
          <button className="btn-fullview" data-device="fullview" onClick={setScreenMode} title="Full View">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg  style="width:18px;height:18px;"><use xlink:href="#icon-fullview"></use></svg>
              ` }} />
          </button>

          <div className="separator"></div>

          <button className="btn-download" onClick={download} title="Download">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-download"></use></svg>
              ` }} />
          </button>

          <button className="btn-html" onClick={viewHtml} title="HTML">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-code"></use></svg>
              ` }} />
          </button>

          <button className="btn-preview" onClick={preview} title="Preview">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-eye"></use></svg>
              ` }} />
          </button>
          
          <div className="separator"></div>

          <button className="btn-togglepanel" data-button="togglepanel" onClick={togglePanel} title="Toggle Edit Panel">
              <div dangerouslySetInnerHTML={{ __html: `
              <svg><use xlink:href="#icon-pencil"></use></svg>
              ` }} />
          </button>
        </div>
    </div>
    </>
  )
}

export default App

Let's examine one of the top bar buttons:

<button onClick={save} title="Save">
       <div dangerouslySetInnerHTML={{ __html: `
       <svg><use xlink:href="#icon-save"></use></svg>
       ` }} />
       <span>Save</span>
</button>

This 'Save' button can be used to invoke your content saving function, for example, to post the content to a server (e.g., for saving into a database). This button is provided merely as an example; you can add your own buttons according to your requirements. The buttons on the top bar use SVG icons, allowing you to easily replace them with your own icons.

The screenshot below displays the top bar and its buttons as they appear on the page.

In this implementation, we have also expanded the features of ContentBox.js by adding additional buttons to the left sidebar—specifically, buttons to open the animation panel and the animation timeline editor.

Now, you're ready to use ContentBox.js in your React project to create your own drag & drop online web builder!

ContentBox.js documentation

Should you require further information or assistance, please refer to the ContentBox.js documentation:

https://demo.innovastudio.com/docs/ContentBox.pdf


© 2024 InnovaStudio

This site is built with Site Builder Kit