npm package: @serpstat/pagebuilder
Description: [Dev Documentation](https://xwiki.serpstat.com/bin/view/%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F%20%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8F/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D0%B8%20Serpstat/%D0%90%D0
Latest version: 2.1.61

# Page builder

[Dev Documentation](https://xwiki.serpstat.com/bin/view/%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F%20%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8F/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D0%B8%20Serpstat/%D0%90%D0%B4%D0%BC%D0%B8%D0%BD-%D0%BF%D0%B0%D0%BD%D0%B5%D0%BB%D1%8C%2C%20%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%BA%D0%B0%20Serpstat%2C%20AdminPages/Content/Builder%20%D1%81%D1%82%D0%BE%D1%80%D1%96%D0%BD%D0%BE%D0%BA%20%D0%B2%20%D0%B0%D0%B4%D0%BC%D1%96%D0%BD%D1%86%D1%96%20Serpstat/)

[User Documntation](https://xwiki.serpstat.com/bin/view/%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F%20%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8F/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D0%B8%20Serpstat/%D0%90%D0%B4%D0%BC%D0%B8%D0%BD-%D0%BF%D0%B0%D0%BD%D0%B5%D0%BB%D1%8C%2C%20%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%BA%D0%B0%20Serpstat%2C%20AdminPages/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D0%B8%20%D0%B8%20%D0%BC%D0%B0%D0%BD%D1%83%D0%B0%D0%BB%D1%8B%20%D0%BF%D0%BE%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0%D0%BC%20%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD-%D0%BF%D0%B0%D0%BD%D0%B5%D0%BB%D0%B8/%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BD%D1%82/Builder%20%D1%81%D1%82%D0%BE%D1%80%D1%96%D0%BD%D0%BE%D0%BA%20%D0%B2%20%D0%B0%D0%B4%D0%BC%D1%96%D0%BD%D1%86%D1%96%20Serpstat%20/)

# Development

`npm install`
`npm run start`

Open:

`http://localhost:8085/`


# Add new component

1. Create new forlder in `src/components`.
2. Add index.js

```js
export default function NewComponent(editor) {
}
```

3. Add style file for component (optional). Naming by template `NewComponent.style.scss`.  

4. Add new component to file `src/components/index.js`

```js
 ...
 require('./NewComponent').default(editor);
}
```

# Usage Serpstat admin panel

For example see file `backend/views/pagebuilder_script/pagebuilder_script.php` in prodvigator repo.

```html
<link href="https://unpkg.com/grapesjs/dist/css/grapes.min.css" rel="stylesheet">
<link href="https://unpkg.com/@serpstat/pagebuilder@2.1.39/dist/css/pagebuilderStyles.css" rel="stylesheet">
<script src="https://unpkg.com/grapesjs@0.19.5/dist/grapes.min.js"></script>
<script src="https://unpkg.com/@serpstat/pagebuilder@2.1.39/dist/dist/pagebuilder.min.js"></script>

<script>
{
  const openPageBuilderBtn = document.querySelectorAll('.open-in-page-builder');

  function setReadyHtmlCss({ destinationResultHtmlId, html, css }) {
    const el = document.getElementById(destinationResultHtmlId);

    el.value = [
      `<style>${css}</style>`,
      html.replace('<body>', '').replace('</body>', ''),
    ].join('');
  }

  const builderVisibility = {
    hide(pagebilderContainerId) {
      const editorWrapp = document.getElementById(pagebilderContainerId);

      editorWrapp.style.display = 'none';
      document.querySelector('html').style.fontSize = '10px';
    },
    show(pagebilderContainerId) {
      const editorWrapp = document.getElementById(pagebilderContainerId);
      editorWrapp.style.display = 'block';
      editorWrapp.style.width = '100%';
      editorWrapp.style.position = 'fixed';
      editorWrapp.style.zIndex = '10000';
      editorWrapp.style.left = 0;
      editorWrapp.style.top = 0;
      document.querySelector('html').style.fontSize = '16px';
    }
  };

  try {
    for (const btn of openPageBuilderBtn) {
      let gEditor = null;

      function runEditorOnClick({
        storageDocId,
        destinationResultHtmlId,
        pagebilderContainerId,
        storageTextareaSelector,
      }) {
        return runEditor({
          container: `#${pagebilderContainerId}`,
          storageDocId: `${window.location.hostname}_${storageDocId}`,
          dependenciesStyles: [
            'https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&amp;subset=latin,cyrillic&amp;display=swap',
            'https://serpstat.com/css/base_global.css',
            'https://serpstat.com/css/dialog.css?518901310',
            'https://unpkg.com/@serpstat/pagebuilder@2.1.39/dist/css/pagebuilder.css'
          ],
          saveAndCloseCallback() {
            const html = gEditor.getHtml({ cleanId: true });
            const css = gEditor.getCss();

            setReadyHtmlCss({
              destinationResultHtmlId, html, css
            });

            builderVisibility.hide(pagebilderContainerId);
          },
          assetManagerConfig: {
            upload: '/site/upload_image',
            uploadName: 'uploadfile',
            multiUpload: false,
            customFetch(url, fetchOpts) {
              return fetch(url, fetchOpts).then(res =>
                ((res.status / 200) | 0) == 1 ?
                  res.json().then(res => {
                    return {
                      data: [res.filelink],
                    }
                  }) :
                  res.text().then(text => Promise.reject(text)
                  )
              );
            }
          },
          storageManager: {
            type: 'textarea-storage',
            options: {
              ['textarea-storage']: {
                selector: storageTextareaSelector,
              }
            }
          }
        });
      }

      btn.addEventListener('click', function () {
        const {
          storageDocId,
          destinationResultHtmlId,
          pagebilderContainerId,
          storageTextareaSelector,
        } = this.dataset;

        if (!gEditor) {
          if (typeof runEditor === 'undefined') {
            const initContent = btn.innerText;
            btn.setAttribute('disable', true);
            btn.innerText = 'Loading builder...';

            const internal = setInterval(() => {
              if (
                typeof runEditor === 'undefined' ||
                typeof grapesjs === 'undefined'
              ) {
                return;
              };

              btn.setAttribute('disable', false);
              btn.innerText = initContent;
              gEditor = runEditorOnClick({
                storageDocId,
                destinationResultHtmlId,
                pagebilderContainerId,
                storageTextareaSelector,
              });
              builderVisibility.show(pagebilderContainerId);

              clearInterval(internal);
            }, 1000);

          } else {
            gEditor = runEditorOnClick({
              storageDocId,
              destinationResultHtmlId,
              pagebilderContainerId,
              storageTextareaSelector,
            });
            builderVisibility.show(pagebilderContainerId);
          }

          return;
        };

        builderVisibility.show(pagebilderContainerId);
      });
    }
  } catch (e) {
    console.error(e);
  }
}
</script>
```