Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4192d16b7a | |||
| 12abd35cfa | |||
| 073ed52932 | |||
| 8f2605be9d | |||
| 9e7860542b | |||
| f48b2a3274 | |||
| b76f83d4fa | |||
| 977ec0003c | |||
| 0e91c42e89 | |||
| 0a4fd3608f | |||
| 11152dda87 | |||
| 246c9ddd35 | |||
| 164ef7433a | |||
| a6decb9cfb | |||
| 9c85a89a8f | |||
| 58ed3de9dc | |||
| 3de4a4abb6 | |||
| 256450226b | |||
| 51db3da072 | |||
| 05c5697bc2 | |||
| 01e4160040 | |||
| 81f5166043 | |||
| a1929fe33c | |||
| 840c002834 | |||
| f4474fb7e4 | |||
| 8ca6cb0365 | |||
| e2553358be | |||
| 34dbff5701 | |||
| c408f2f5b3 | |||
| 15dbe26220 | |||
| 9818067013 | |||
| 996d222436 | |||
| e964892e65 | |||
| 6d1ab34b2b | |||
| 18d89a9a19 | |||
| 5c7ea9df3c | |||
| 010154797a | |||
| 98d0720bc0 | |||
| 378038b244 | |||
| 090978e8f8 | |||
| aae474901b | |||
| a4193d63e1 | |||
| 9f5c157e54 | |||
| b399d2fc89 | |||
| c899f8c229 | |||
| 80464a61d7 | |||
| 3a78aee088 | |||
| 0f60a0ac30 | |||
| 7dcaa80d71 | |||
| 0a8cdf1504 | |||
| 1718157b4e | |||
| df7866b8b5 | |||
| 50d743140b | |||
| f86afc8394 | |||
| 269fbbc401 | |||
| f236674c1e | |||
| e9827fdd88 | |||
| b441ac613d | |||
| 394d5a42d9 | |||
| 6885db162e | |||
| 1a19d63192 | |||
| 3c163b8e76 | |||
| c627c873f9 | |||
| ec61e01950 | |||
| 682299445c | |||
| ddbc713603 | |||
| 4abdd29709 | |||
| a0def8467b | |||
| bd5cafa549 | |||
| dd860ec473 | |||
| c1340f2035 | |||
| b43a4d6b4f | |||
| 1a98a60fac | |||
| de0b6740d1 | |||
| f9c0852ede | |||
| cdbb4d3064 | |||
| 8d7ce53e46 | |||
| e42643f182 | |||
| 05b5ccb2b5 | |||
| 54523ed903 | |||
| f611185a47 | |||
| 7024c2bfcb | |||
| b882c9a1c3 | |||
| 1dc8d192f7 | |||
| d8c3d4c54b | |||
| 6626ecff2a | |||
| 25862c4b1c | |||
| a61510f136 | |||
| 0d3ae8f03e | |||
| b58ef27f1e | |||
| 161e34e8ee | |||
| 8eb7bea2e8 | |||
| d9543bb22c | |||
| 02abf4652e | |||
| 1ed038ff02 | |||
| 0bb705ccdc | |||
| 9d25bdcb88 | |||
| 4d1d2337bf | |||
| 00866a8115 | |||
| 1d733fb952 | |||
| 295c6f82af | |||
| f0af682a43 | |||
| ad35540111 | |||
| 5cb6343184 | |||
| 502326fd8d | |||
| 38a2d60f2c | |||
| 15c5286905 | |||
| f3e5b92318 | |||
| c42c9fecba | |||
| 6ebd37190a | |||
| 37ecce6290 | |||
| e0ae4d3d69 | |||
| 70d4630971 | |||
| a2f8c7ebc1 | |||
| 1ae643b306 | |||
| 52109f5010 | |||
| e0fb681d3b | |||
| 840f73d739 | |||
| 702cd6e226 | |||
| eabe411b7a | |||
| 7bf571e0fb | |||
| 73a10d9d55 | |||
| 527416c63d | |||
| ac70e7eb9a | |||
| 05660eb4e4 | |||
| 499c5b8163 | |||
| 16f65254da | |||
| 812ea6d2c5 | |||
| 28ad7f1571 | |||
| 3c39f49a84 | |||
| 344ce55e67 | |||
| 73be6c050b | |||
| f23c745a15 | |||
| 6ddc9b9910 | |||
| b5d23d612f | |||
| 7be17a28fa | |||
| c5502e2be2 | |||
| a9597684de | |||
| d1bbffce2e | |||
| 12ef5e0570 | |||
| c48420249b | |||
| 82d4f1b755 | |||
| f8a46b8148 | |||
| 8c75133759 | |||
| 1d214ca60f | |||
| ff33974b78 | |||
| 212e0a414b | |||
| e407dcd0ca | |||
| 7d168b27ee | |||
| c570cd9c75 | |||
| 86f64e8b4b | |||
| 9ac3a19b2f | |||
| 89e24c9285 | |||
| f9d30546ca | |||
| 7201d360ee | |||
| 7eeafdbd31 | |||
| 1d3aec4dff | |||
| 1132790fd8 | |||
| 5672912f04 | |||
| fe95eb0ef3 | |||
| 86602b0ab8 | |||
| 49cc9f685c | |||
| 0ace3de83e | |||
| 38629e7fbb | |||
| 44dc99c641 | |||
| dfcd9681bc | |||
| 29a75a6e26 | |||
| 0ec37c4c5b | |||
| 56839fee2e | |||
| 20c5a68d81 | |||
| 35afd615f8 | |||
| 70d89f567a | |||
| ab0fe727b3 | |||
| 40c67a0bd8 | |||
| e735f2d141 | |||
| 804c504c06 | |||
| 50501b05c6 | |||
| 5fcdc36b09 | |||
| 2a5daa2445 | |||
| e184c3483a | |||
| ee197d24c4 | |||
| 30a3e964bd | |||
| 93c39981f9 | |||
| 019e51ab1c | |||
| 760825be2d | |||
| 6b3ed38e04 | |||
| 190800cc80 | |||
| 33dbe48a2c | |||
| c7e492ae8a | |||
| ae68a88fd1 | |||
| 7b7a8c101f | |||
| 3aa5370c2b | |||
| 92e4ca76f4 | |||
| 2917cb7332 | |||
| 0d4baf6b11 | |||
| 50d7117609 | |||
| 28ecfa281c | |||
| 9173efe9c9 | |||
| a7e51803cf | |||
| de8d78ea31 | |||
| 29734a6b94 | |||
| d791edda3e | |||
| 0ea8f8b443 | |||
| 718e6b32a9 |
@@ -18,6 +18,10 @@ indent_size = 2
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.php]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ deploy-*.sh
|
|||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
blocks
|
blocks
|
||||||
|
exports
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
FROM node:19.4.0-slim as node
|
||||||
|
|
||||||
|
WORKDIR /app/
|
||||||
|
|
||||||
|
COPY ./package.json .
|
||||||
|
COPY ./package-lock.json .
|
||||||
|
COPY ./inc ./inc
|
||||||
|
COPY ./layouts ./layouts
|
||||||
|
COPY ./platforms ./platforms
|
||||||
|
COPY ./routes ./routes
|
||||||
|
COPY ./env.js .
|
||||||
|
COPY ./helpers.js .
|
||||||
|
COPY ./rollup.config.js .
|
||||||
|
COPY ./server.js .
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
EXPOSE 3010
|
||||||
|
CMD npm run view-mode
|
||||||
@@ -20,3 +20,11 @@ environment.
|
|||||||
|
|
||||||
Generated blocks are including this repository.
|
Generated blocks are including this repository.
|
||||||
This project is running a nodejs script with `browsersync` and `gulp` which improves the development process.
|
This project is running a nodejs script with `browsersync` and `gulp` which improves the development process.
|
||||||
|
|
||||||
|
## Development / Testing the tool
|
||||||
|
|
||||||
|
Run `npm run generate-block` command and give the `development` name to the block, it will generate the `/blocks` folder.
|
||||||
|
|
||||||
|
Copy the `/development/data` and `/development/src` folders to root folder of project.
|
||||||
|
Now you're ready to run development process: `npm run dev`.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// For development purposes - run `npm run build-platform`.
|
||||||
|
|
||||||
|
import config from 'config';
|
||||||
|
import prompts from "prompts";
|
||||||
|
import {buildExportFiles, getConfigs} from "./helpers.js";
|
||||||
|
|
||||||
|
const {isDev, developmentBlockName} = getConfigs();
|
||||||
|
const blockName = !isDev && config.has('blockName') ? config.get('blockName') : developmentBlockName;
|
||||||
|
|
||||||
|
export const PLATFORM_OPTIONS = [{
|
||||||
|
name: 'wordpress-acf-block',
|
||||||
|
title: 'WordPress AFC Block'
|
||||||
|
}, {
|
||||||
|
name: 'wordpress',
|
||||||
|
title: 'WordPress'
|
||||||
|
}, {
|
||||||
|
name: 'wordpress-component-manager',
|
||||||
|
title: 'WordPress (Component Manager)'
|
||||||
|
}, {
|
||||||
|
name: 'wordpress-elementor',
|
||||||
|
title: 'WordPress Elementor'
|
||||||
|
}, {
|
||||||
|
name: 'hubspot',
|
||||||
|
title: 'Hubspot'
|
||||||
|
}, {
|
||||||
|
name: 'hubspot-email',
|
||||||
|
title: 'Hubspot Email'
|
||||||
|
}, {
|
||||||
|
name: 'javascript',
|
||||||
|
title: 'JavaScript'
|
||||||
|
}, {
|
||||||
|
name: 'php',
|
||||||
|
title: 'PHP'
|
||||||
|
}];
|
||||||
|
|
||||||
|
const data = await getExportData();
|
||||||
|
const selectedPlatform = PLATFORM_OPTIONS[data['platform']];
|
||||||
|
await buildExportFiles(blockName, selectedPlatform);
|
||||||
|
|
||||||
|
console.log('--------------------\nDone!');
|
||||||
|
|
||||||
|
//
|
||||||
|
// Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
function getExportData() {
|
||||||
|
return prompts([
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "platform",
|
||||||
|
message: "Choose Platform",
|
||||||
|
choices: PLATFORM_OPTIONS.map(item => item.title),
|
||||||
|
default: 'WordPress'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import {getConfigs} from "./helpers.js";
|
||||||
|
|
||||||
|
const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs();
|
||||||
|
|
||||||
|
console.log('----------------------------------\n')
|
||||||
|
console.log('Local Details', process.versions, '\n');
|
||||||
|
console.log('Configs', {
|
||||||
|
isDev,
|
||||||
|
modulesPath,
|
||||||
|
projectPath,
|
||||||
|
developmentBlockName
|
||||||
|
});
|
||||||
|
console.log('\n----------------------------------\n')
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since this file overwrites environment variables for `config` lib,
|
||||||
|
* it's important to "import" this file before all scripts in entry point file,
|
||||||
|
* especially before `config` module import.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export constant variables
|
||||||
|
*/
|
||||||
|
export const PRODUCTION_REGISTRY_URL = 'https://blocks-registery.axe-web.com';
|
||||||
|
export const IS_DEV = process.env.NODE_ENV === 'development';
|
||||||
|
export const BLOCK_NAME = process.env.BLOCK_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite env variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
process.env.NODE_CONFIG_DIR = path.join(getProjectPath(), 'config');
|
||||||
|
|
||||||
|
|
||||||
|
export function getModulePath() {
|
||||||
|
let modulePath = 'node_modules/block-dev-tool';
|
||||||
|
|
||||||
|
if (typeof process.env.MODULE_PATH !== 'undefined') {
|
||||||
|
modulePath = process.env.MODULE_PATH;
|
||||||
|
} else if (process.env.BLOCK_NAME) {
|
||||||
|
modulePath = 'node_modules/@axe-web/block-dev-tool';
|
||||||
|
}
|
||||||
|
|
||||||
|
return modulePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectPath() {
|
||||||
|
let projectPath = '';
|
||||||
|
|
||||||
|
if (typeof process.env.PROJECT_PATH !== 'undefined') {
|
||||||
|
projectPath = path.join(process.env.PROJECT_PATH ?? '', process.env.BLOCK_NAME ?? '')
|
||||||
|
} else if (process.env.BLOCK_NAME) {
|
||||||
|
projectPath = path.join('blocks', process.env.BLOCK_NAME ?? '')
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectPath;
|
||||||
|
}
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const Generator = require('yeoman-generator');
|
|
||||||
// const exec = require('child_process').exec;
|
|
||||||
|
|
||||||
const baseDir = path.join(__dirname, '../../');
|
|
||||||
|
|
||||||
module.exports = class extends Generator {
|
|
||||||
async prompting() {
|
|
||||||
this.data = await this.prompt([
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
name: "name",
|
|
||||||
message: "Block Name",
|
|
||||||
validate: (str) => {
|
|
||||||
const matches = str.match(/\d+/g);
|
|
||||||
|
|
||||||
if (matches != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!str;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// type: "input",
|
|
||||||
// name: "name",
|
|
||||||
// message: "Project ID"
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
type: "list",
|
|
||||||
name: "baseView",
|
|
||||||
message: "View Template",
|
|
||||||
default: 'container',
|
|
||||||
choices: ['container', 'alignfull'],
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// type: "confirm",
|
|
||||||
// name: "include_script",
|
|
||||||
// default: false,
|
|
||||||
// message: "Include script.js File?"
|
|
||||||
// },
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
writing() {
|
|
||||||
const title = capitalize(this.data.name);
|
|
||||||
const data = Object.assign(this.data, {
|
|
||||||
title,
|
|
||||||
blockFilename: title.toLowerCase().replace(/ /ig, '-'),
|
|
||||||
blockClassName: title.toLowerCase().replace(/ /ig, '_'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const pathDist = path.join(baseDir, 'blocks', data.blockFilename);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('src/template.template.hbs'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'src', data.blockFilename + '.template.hbs')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('src/styles/template.scss'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'src', 'styles', data.blockFilename + '.scss')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('src/scripts/template.mjs'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'src', 'scripts', data.blockFilename + '.mjs')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('src/images/demo.jpeg'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'src', 'images', 'demo.jpeg')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('config/default.cjs'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'config', 'default.cjs')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('package.json'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'package.json')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('data/default.json'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'data', 'default.json')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('data/advanced.json'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'data', 'advanced.json')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('README.md'),
|
|
||||||
this.destinationPath(path.join(pathDist, 'README.md')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('.editorconfig'),
|
|
||||||
this.destinationPath(path.join(pathDist, '.editorconfig')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('.gitignore'),
|
|
||||||
this.destinationPath(path.join(pathDist, '.gitignore')),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
// Run BUILD script
|
|
||||||
// var cmd = exec("npm run build", function (err, stdout, stderr) {
|
|
||||||
// if (err) {
|
|
||||||
// console.log('Issue with running - "npm run build"\n\n', err);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// console.log(`\n\nDon't forget to connect the Component in your functions.php file ;)\n\n`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function capitalize(str) {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
.toLowerCase()
|
|
||||||
.split(/[ -_]/g)
|
|
||||||
.filter((word) => !!word)
|
|
||||||
.map((word) => {
|
|
||||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
||||||
})
|
|
||||||
.join(' ');
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# This file is for unifying the coding style for different editors and IDEs
|
|
||||||
# editorconfig.org
|
|
||||||
|
|
||||||
# WordPress Coding Standards
|
|
||||||
# https://make.wordpress.org/core/handbook/coding-standards/
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.json]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Basic
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
vendor
|
|
||||||
|
|
||||||
# Custom
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
# Development Setup
|
|
||||||
|
|
||||||
Install required modules:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
Run development environment:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
## How it works ⚙️
|
|
||||||
|
|
||||||
The project runs `gulp` and `browsersync` beyond the scenes.
|
|
||||||
Each change in template, styling or script files will reload the page. The idea is to have a comfortable development
|
|
||||||
environment.
|
|
||||||
|
|
||||||
# Block Structure
|
|
||||||
|
|
||||||
You will find all the block files in `/src` folder.
|
|
||||||
|
|
||||||
```
|
|
||||||
/src/images/
|
|
||||||
*.png, *jpg, *.svg
|
|
||||||
|
|
||||||
/src/scritps/
|
|
||||||
*.mjs, *js
|
|
||||||
|
|
||||||
/src/styles/
|
|
||||||
*.scss
|
|
||||||
|
|
||||||
/src/*.template.hbs
|
|
||||||
```
|
|
||||||
|
|
||||||
# Block rules
|
|
||||||
|
|
||||||
We have a “blocks system”, means you will have to follow strict rules and use listed technologies:
|
|
||||||
|
|
||||||
## Template Layout & Data
|
|
||||||
|
|
||||||
### Data
|
|
||||||
|
|
||||||
You will find multiple `*.json` files in `/data` folder after the first run of `npm start`.
|
|
||||||
|
|
||||||
These files are used as data sources in layout. By reviewing these files you can understand what parameters you have and
|
|
||||||
how the data is stored.
|
|
||||||
|
|
||||||
There are multiple data sources since the block can be reused in different situations with different data. We have to
|
|
||||||
make sure that the block is always rendered properly.
|
|
||||||
|
|
||||||
> Don't change or edit these files - use the data as it is!
|
|
||||||
|
|
||||||
### Handlebars
|
|
||||||
|
|
||||||
[Handlebars](https://handlebarsjs.com/guide/ "What is Handlebars") used as a template engine in the project.
|
|
||||||
|
|
||||||
All the template layout must be in a single `*.hbs` file that located in `/src` folder.
|
|
||||||
|
|
||||||
> Use the data that is available in `/data` folder.
|
|
||||||
> Avoid using any kind of "freestyle" input/content. Your template file must contain only HTML tags and Handlebars parameters.
|
|
||||||
|
|
||||||
### Static Media Files 🖼️
|
|
||||||
|
|
||||||
Use the `/src/images` folder in case you need to upload images or video files to the template.
|
|
||||||
These files will be available by the next URL: http://localhost:3000/images/${filename}
|
|
||||||
|
|
||||||
### Class Naming Convention
|
|
||||||
|
|
||||||
Use BEM naming system for class names: http://getbem.com/naming/
|
|
||||||
|
|
||||||
## Styling rules 🎨
|
|
||||||
|
|
||||||
### Mobile First 📱
|
|
||||||
|
|
||||||
Use Mobile First approach. Start with mobile device styling and then focus on other screen sizes by using @media (
|
|
||||||
min-width: ...px).
|
|
||||||
|
|
||||||
### CSS Units
|
|
||||||
|
|
||||||
Use `rem` units instead of pixels.
|
|
||||||
For example, `1rem = 16pixels` depends on project.
|
|
||||||
|
|
||||||
No need to use rem in situations of `1px`, `2px` or `3px`. (usually used for borders width).
|
|
||||||
It's important to mention that the block is "elastic". By using `rem` units we are able to scale the layout and keep the
|
|
||||||
aspect ratio.
|
|
||||||
|
|
||||||
### Global Styling
|
|
||||||
|
|
||||||
The development environment includes CSS rules of the original project. This is including fonts, CSS variables,
|
|
||||||
container logic etc...
|
|
||||||
|
|
||||||
The CSS file is already embedded in served HTML.
|
|
||||||
|
|
||||||
# JavaScript
|
|
||||||
|
|
||||||
## Sliders
|
|
||||||
|
|
||||||
For any kind of slides animations - use [SwiperJS](https://swiperjs.com/get-started).
|
|
||||||
|
|
||||||
The lib is included in the project and ready for use. Test with `window.Swiper` in browser console.
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
|
|
||||||
## Short Video Review
|
|
||||||
Check the [Video Guide](https://www.loom.com/share/1c707a4ea14e48b7a35a49d7b0a6f1e0)
|
|
||||||
> PRO TIP
|
|
||||||
> Use x2 speed in the player 😜
|
|
||||||
|
|
||||||
|
|
||||||
## Testing ✅
|
|
||||||
|
|
||||||
### Development Toolbar ✔️
|
|
||||||
|
|
||||||
Use development toolbar to switch between data sources.
|
|
||||||
|
|
||||||
### Hover/Focus States ✔️
|
|
||||||
|
|
||||||
Make sure all "clickable" elements have `&:hover` & `&:focus` states in CSS rules.
|
|
||||||
This might be related to links, buttons or any other interactive elements.
|
|
||||||
|
|
||||||
### Responsiveness ✔️
|
|
||||||
|
|
||||||
Make sure the layout is rendered correctly on all standard breakpoint size:
|
|
||||||
|
|
||||||
1. 1920px in width...
|
|
||||||
2. 1680px
|
|
||||||
3. 1440px
|
|
||||||
4. 1360px
|
|
||||||
5. 1280px
|
|
||||||
6. 1024px
|
|
||||||
7. 980px
|
|
||||||
8. 768px
|
|
||||||
9. 600px
|
|
||||||
10. 414px
|
|
||||||
11. 375px
|
|
||||||
|
|
||||||
If you follow the rules of REM unit - your layout must be "elastic" and most of the breakpoints should be rendered
|
|
||||||
properly.
|
|
||||||
|
|
||||||
### Pixel Perfect Test ✔️
|
|
||||||
|
|
||||||
Compare the final result with provided screenshots.
|
|
||||||
|
|
||||||
You can use
|
|
||||||
available [Browser Extensions](https://chrome.google.com/webstore/detail/perfectpixel-by-welldonec/dkaagdgjmgdmbnecmcefdhjekcoceebi?hl=en)
|
|
||||||
or any other tool/approach that you're familiar with.
|
|
||||||
The idea is to have "pretty close" result to requested design.
|
|
||||||
|
|
||||||
## Delivery
|
|
||||||
Once you finished with the task, before letting me know - please review the next [Google Form link](https://forms.gle/w4sJjAyRDSSBRuGLA](https://forms.gle/w4sJjAyRDSSBRuGLA)
|
|
||||||
|
|
||||||
If everything is OK - send a Pull Request to the git project or share a ZIP file with me directly.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
cssUrl: "https://",
|
|
||||||
blockName: "<%= blockFilename %>",
|
|
||||||
baseView: "<%= baseView %>",
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Option Two",
|
|
||||||
"content": "<p><b>Delivering top results to industry leaders:</b></p>",
|
|
||||||
"cta_text": "Our Success Stories",
|
|
||||||
"url": "https://google.com"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tips for good data.json example:
|
|
||||||
* - Provide Short and Long version of text (title/content).
|
|
||||||
* - Put <a> and <strong> elements in content.
|
|
||||||
* - Upload different sizes and ratios of images.
|
|
||||||
* - Provide options with and without CTA to test conditional logic.
|
|
||||||
*/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "As a Global Salesforce Partner,\n we deliver Service Cloud solutions\n and complex Field Service Management\n in diverse industries worldwide",
|
|
||||||
"content": "<p><b>Delivering top results to industry leaders:</b></p>",
|
|
||||||
"cta_text": "Our Success Stories",
|
|
||||||
"url": "https://google.com"
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "<%= blockFilename %>",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "component-dev"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"create-block-dev-tool": "git+https://roman-axe-web@bitbucket.org/axeweb/create-block-dev-tool.git#master"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 9.3 KiB |
@@ -1,7 +0,0 @@
|
|||||||
(function ($) {
|
|
||||||
|
|
||||||
console.log('Ready!');
|
|
||||||
console.log('jQuery =', $);
|
|
||||||
console.log('Swiper =', window.Swiper);
|
|
||||||
|
|
||||||
})(window.jQuery);
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Use "rem" instead of pixels. 1rem = 16pixels
|
|
||||||
* No need to use rem in situations of "1px". (usually used for borders width).
|
|
||||||
*
|
|
||||||
* Use BEM naming system for class names: http://getbem.com/naming/
|
|
||||||
*
|
|
||||||
* Use Mobile First approach.
|
|
||||||
* Start with mobile device styling and then focus on other screen sizes by using @media (min-width: ...px).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
.<%= blockClassName %> {
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
// Header Mobile.
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
// Header Desktop.
|
|
||||||
}
|
|
||||||
|
|
||||||
&-heading {
|
|
||||||
// Child Element.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__visual {
|
|
||||||
&-image {
|
|
||||||
// Example of BEM usage.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<section class="<%= blockClassName %>">
|
|
||||||
|
|
||||||
{{!
|
|
||||||
Remove Everything Below:
|
|
||||||
}}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
code {
|
|
||||||
background-color: #eee;
|
|
||||||
color: #333;
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
border: 2px solid #d8d8d8;
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<header class="<%= blockClassName %>__header">
|
|
||||||
<h1 class="<%= blockClassName %>__header-heading">Ready!</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="<%= blockClassName %>__content">
|
|
||||||
<p>
|
|
||||||
Review the `/data` folder with JSON data files.<br>
|
|
||||||
Don't change data JSON files - use the data as it is.
|
|
||||||
</p>
|
|
||||||
<p>Build the layout based on provided data structure.</p>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>Image Example</h2>
|
|
||||||
<div>
|
|
||||||
<img src="images/demo.jpeg" alt="">
|
|
||||||
<br>
|
|
||||||
<code>
|
|
||||||
<img src="images/demo.jpeg" alt="">
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>Available Buttons Styling</h2>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<a href="#" class="btn btn--primary">Primary</a>
|
|
||||||
<br>
|
|
||||||
<code>
|
|
||||||
<a href="#" class="btn btn--primary">Primary</a>
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<a href="#" class="btn btn--secondary">Secondary</a>
|
|
||||||
<br>
|
|
||||||
<code>
|
|
||||||
<a href="#" class="btn btn--secondary">Secondary</a>
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!
|
|
||||||
Remove END
|
|
||||||
}}
|
|
||||||
|
|
||||||
</section>
|
|
||||||
@@ -0,0 +1,392 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import {BLOCK_NAME, getModulePath, getProjectPath, IS_DEV} from "./env.js";
|
||||||
|
import config from 'config';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
import memFs from 'mem-fs';
|
||||||
|
import editor from 'mem-fs-editor';
|
||||||
|
import fsExtra from "fs-extra";
|
||||||
|
import archiver from "archiver";
|
||||||
|
import {buildWordPress} from "./platforms/wordpress/wordpress-adapter.js";
|
||||||
|
import {buildHubspotEmail} from "./platforms/hubspot/hubspot-email-adapter.js";
|
||||||
|
import {buildHubspotPage} from "./platforms/hubspot/hubspot-page-adapter.js";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import {constants} from "fs";
|
||||||
|
import fetch from "node-fetch";
|
||||||
|
import mime from "mime-types";
|
||||||
|
import {exec} from "child_process";
|
||||||
|
import {REGISTRY_URL} from "@axe-web/create-block/env.js";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
export function getConfigs() {
|
||||||
|
return {
|
||||||
|
isDev: IS_DEV,
|
||||||
|
developmentBlockName: BLOCK_NAME,
|
||||||
|
modulesPath: getModulePath(),
|
||||||
|
projectPath: getProjectPath(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readJSONFile(jsonFile) {
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(jsonFile, constants.F_OK | constants.R_OK);
|
||||||
|
data = await fsExtra.readJson(jsonFile);
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
error: true, errorMessage: getErrorHtml("JSON Syntax error. Please make sure the dataFile is valid.", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getErrorHtml(message = '', errorMessage = '') {
|
||||||
|
return `<div style="padding: 10px 15px; font-family: Arial, sans-serif">
|
||||||
|
<p>${message}</p>
|
||||||
|
<pre style="padding: 10px 15px; background-color: #ffd0d0; border: 1px solid red;">${errorMessage}</pre>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBlockData(jsonFileName = 'default', {projectPath} = {jsonFileName: 'default'}) {
|
||||||
|
const filePath = path.join(projectPath, 'data', `${jsonFileName}.json`);
|
||||||
|
const data = await readJSONFile(filePath);
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
console.log(filePath, data.errorMessage.replace(/<[^>]*>?/gm, ''));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBlockConfigs(args) {
|
||||||
|
const config = await readJSONFile(path.join('blocks', '@' + args.project, args.name, 'config', 'default.json'));
|
||||||
|
|
||||||
|
const updatedConfig = Object.assign({}, config, // The entire config object.
|
||||||
|
{
|
||||||
|
projectDir: '', dataFiles: args.dataFiles.map((name) => {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
}), remToPx: 16,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Avoid cache conflict on Global Project css/js files.
|
||||||
|
if (updatedConfig.project) {
|
||||||
|
updatedConfig.project.css = updatedConfig.project.css ? updatedConfig.project.css + '?cache=' + Date.now() : undefined;
|
||||||
|
updatedConfig.project.js = updatedConfig.project.js ? updatedConfig.project.js + '?cache=' + Date.now() : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlockName(name = '') {
|
||||||
|
// Remove all @ from the beginning of the string.
|
||||||
|
name = name.replace(/^@/, '');
|
||||||
|
|
||||||
|
const arr = name.split('/');
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockName: `@${arr[0]}/${arr[1]}`,
|
||||||
|
project: arr[0],
|
||||||
|
name: arr[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createFiles(data, files = [], {pathDist, generatorsPath}) {
|
||||||
|
generatorsPath = generatorsPath ?? path.join(__dirname, 'generators/block/templates');
|
||||||
|
|
||||||
|
const store = memFs.create();
|
||||||
|
const filesystem = editor.create(store);
|
||||||
|
for (let file of files) {
|
||||||
|
const from = typeof file !== 'string' ? `${generatorsPath}/${file.from}` : `${generatorsPath}/${file}`;
|
||||||
|
const to = typeof file !== 'string' ? `${pathDist}/${file.to}` : `${pathDist}/${file}`;
|
||||||
|
|
||||||
|
await filesystem.copyTplAsync(from, to, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesystem.commit(); // Promise
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalize(str) {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/[-_ ]/g)
|
||||||
|
.filter((word) => !!word)
|
||||||
|
.map((word) => {
|
||||||
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
|
})
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function zipProject(srcDir, outputFileName = 'dist.zip') {
|
||||||
|
// create a file to stream archive data to.
|
||||||
|
const output = await fsExtra.createWriteStream(outputFileName);
|
||||||
|
const archive = archiver('zip', {});
|
||||||
|
|
||||||
|
// listen for all archive data to be written
|
||||||
|
// 'close' event is fired only when a file descriptor is involved
|
||||||
|
output.on('close', function () {
|
||||||
|
console.log(archive.pointer() + ' total bytes');
|
||||||
|
console.log('archiver has been finalized and the output file descriptor has closed.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// This event is fired when the data source is drained no matter what was the data source.
|
||||||
|
// It is not part of this library but rather from the NodeJS Stream API.
|
||||||
|
// @see: https://nodejs.org/api/stream.html#stream_event_end
|
||||||
|
output.on('end', function () {
|
||||||
|
console.log('Data has been drained');
|
||||||
|
});
|
||||||
|
|
||||||
|
// good practice to catch warnings (ie stat failures and other non-blocking errors)
|
||||||
|
archive.on('warning', function (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
// log warning
|
||||||
|
} else {
|
||||||
|
// throw error
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// good practice to catch this error explicitly
|
||||||
|
archive.on('error', function (err) {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
// pipe archive data to the file
|
||||||
|
archive.pipe(output);
|
||||||
|
|
||||||
|
// append files from a subdirectory, putting its contents at the root of archive
|
||||||
|
archive.directory(srcDir, false);
|
||||||
|
|
||||||
|
// finalize the archive (ie we are done appending files but streams have to finish yet)
|
||||||
|
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
|
||||||
|
await archive.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildExportFiles(blockName, platform) {
|
||||||
|
if (platform.name.startsWith('wordpress')) {
|
||||||
|
await buildWordPress(blockName, {platform: platform.name});
|
||||||
|
} else if (platform.name === 'hubspot-email') {
|
||||||
|
await buildHubspotEmail(blockName)
|
||||||
|
} else if (platform.name === 'hubspot') {
|
||||||
|
await buildHubspotPage(blockName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeCommentsFromCss(content) {
|
||||||
|
return content.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeCommentsFromJs(content) {
|
||||||
|
return content.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadFile(filePath, uploadUrl, validator) {
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
const contentType = mime.lookup(filePath);
|
||||||
|
if (['text/css', 'application/javascript'].includes(contentType)) {
|
||||||
|
options.encoding = 'utf8';
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = await fs.readFile(filePath, options);
|
||||||
|
|
||||||
|
if (validator) {
|
||||||
|
body = validator(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(uploadUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: body,
|
||||||
|
headers: {'Content-Type': contentType}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.status !== 200;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
const fileName = filePath.split('/').pop();
|
||||||
|
throw new Error(`Can't upload "${fileName}" file. Server permission error.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getImagesList(rootFolder, subFolder = '') {
|
||||||
|
const imagesPath = path.join(rootFolder, subFolder);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(imagesPath);
|
||||||
|
} catch (err) {
|
||||||
|
// Folder doesn't exist.
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = await fs.readdir(imagesPath);
|
||||||
|
const imagesList = [];
|
||||||
|
|
||||||
|
for (const image of images) {
|
||||||
|
const stats = await fs.stat(path.join(imagesPath, image));
|
||||||
|
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
const subImages = await getImagesList(rootFolder, image);
|
||||||
|
imagesList.push(...subImages);
|
||||||
|
} else {
|
||||||
|
imagesList.push(path.join(subFolder, image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isFileEmpty(filePath, ignoreComments = false) {
|
||||||
|
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||||
|
const lines = fileContent.split('\n');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (ignoreComments && line.trim().startsWith('//')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.trim().length > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceNames(content, images, uploadedImages) {
|
||||||
|
images.forEach((image, index) => {
|
||||||
|
content = content.replace(image, uploadedImages[index].fileName);
|
||||||
|
});
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBlockFromCloud(blockName, blocksRegistry) {
|
||||||
|
const queryString = new URLSearchParams();
|
||||||
|
queryString.append('blockName', blockName);
|
||||||
|
queryString.append('includeDevConfig', 'true');
|
||||||
|
|
||||||
|
const response = await fetch(`${blocksRegistry}?${queryString.toString()}`);
|
||||||
|
const responseData = await response.json();
|
||||||
|
if (!responseData || !responseData.name) {
|
||||||
|
const message = `⚠️ Block not found, please contact administrator. [${blockName}]`
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseData.statusCode && responseData.statusCode !== 200) {
|
||||||
|
const message = ["⚠️ [ERROR]", responseData.message || "Server side error."].join(' ');
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyVersion(blockName, projectPath, blocksRegistry) {
|
||||||
|
const block = await getBlockFromCloud(blockName, blocksRegistry);
|
||||||
|
if (!block) {
|
||||||
|
throw new Error(`Block not found. [${blockName}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockJson = await readJSONFile(path.join(projectPath, `block.json`));
|
||||||
|
if (blockJson.error) {
|
||||||
|
return false; // Block doesn't exist locally.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This block is managed on cloud.
|
||||||
|
* Let's detect the latest version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return block.version === blockJson.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncFilesWithCloud(blockName, bs, sourceFiles = false) {
|
||||||
|
const block = await getBlockFromCloud(blockName, REGISTRY_URL);
|
||||||
|
|
||||||
|
let fileExists = false;
|
||||||
|
try {
|
||||||
|
await readJSONFile(path.join('blocks', '@' + block.project, block.name, 'block.json'));
|
||||||
|
fileExists = true;
|
||||||
|
} catch (e) {
|
||||||
|
// Dir doesn't exist.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Pause watcher of the block?
|
||||||
|
blockName = `@${block.project}/${block.name}`;
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const createBlockModulePath = `./node_modules/@axe-web/create-block`;
|
||||||
|
const action = sourceFiles ? fileExists ? 'sync --source' : 'pull' : 'sync';
|
||||||
|
|
||||||
|
exec(`BLOCK_NAME=${blockName} node ${createBlockModulePath}/create-block.js ${action}`, (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) {
|
||||||
|
const message = err || stderr;
|
||||||
|
console.error('Error:', message);
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(stdout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Release watcher of the block?
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBlockVariations(projectPath) {
|
||||||
|
const dataFiles = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(path.join(projectPath, 'data'));
|
||||||
|
|
||||||
|
const files = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data')));
|
||||||
|
dataFiles.push(...files);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Warning: data folder not found.', projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareListOfDataFiles(dataFiles) {
|
||||||
|
return dataFiles
|
||||||
|
.filter((fileName) => fileName.split('.').pop() === 'json')
|
||||||
|
.map((fileName) => {
|
||||||
|
const splitName = fileName.split('.');
|
||||||
|
splitName.pop();
|
||||||
|
return splitName.join('');
|
||||||
|
})
|
||||||
|
.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handlebarLayoutsPath() {
|
||||||
|
return path.join(...arguments)
|
||||||
|
.replace(/\\/g, '/'); // Windows path issue. Fix all "\" for Handlebars.
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getListOfDesignPreviewFiles(jsonDataFileName, previewFiles, block) {
|
||||||
|
return previewFiles
|
||||||
|
.filter(fileName => {
|
||||||
|
return fileName.startsWith(jsonDataFileName + '.');
|
||||||
|
})
|
||||||
|
.map(fileName => {
|
||||||
|
const fileData = fileName.split('.');
|
||||||
|
const fileFormat = fileData.pop();
|
||||||
|
const previewSize = fileData.pop();
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataSource: jsonDataFileName,
|
||||||
|
widthDimension: Number.parseInt(previewSize, 10),
|
||||||
|
url: `/design/${block.project}/${block.name}/preview/${fileName}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import gulp from "gulp";
|
||||||
|
import path from "path";
|
||||||
|
import sourcemaps from "gulp-sourcemaps";
|
||||||
|
import babel from "gulp-babel";
|
||||||
|
import PluginError from "plugin-error";
|
||||||
|
import rename from "gulp-rename";
|
||||||
|
import uglify from "gulp-uglify";
|
||||||
|
import dartSass from 'sass';
|
||||||
|
import gulpSass from "gulp-sass";
|
||||||
|
import {getBlockName} from "../helpers.js";
|
||||||
|
|
||||||
|
const sass = gulpSass(dartSass);
|
||||||
|
|
||||||
|
export function setupWatcher(viewSync) {
|
||||||
|
|
||||||
|
const watchSCSS = gulp.watch(['blocks/**/*.scss'], {delay: 400});
|
||||||
|
watchSCSS.on('change', async function (filepath, stats) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
buildStyleFiles(path.join('blocks', '@' + block.project, block.name), () => {
|
||||||
|
viewSync.syncTemplate(block.blockName, 'styleUpdate');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchJS = gulp.watch(['blocks/**/*.js', '!blocks/**/*.min.js'], {delay: 400});
|
||||||
|
watchJS.on('change', async function (filepath) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
buildScriptFiles(path.join('blocks', '@' + block.project, block.name), () => {
|
||||||
|
viewSync.syncTemplate(block.blockName, 'scriptUpdate');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchHBS = gulp.watch(['blocks/**/*.hbs'], {delay: 400});
|
||||||
|
watchHBS.on('change', async function (filepath) {
|
||||||
|
const pathArray = filepath.split('/', 3);
|
||||||
|
pathArray.shift();
|
||||||
|
const block = getBlockName(pathArray.join('/'));
|
||||||
|
|
||||||
|
await viewSync.syncTemplate(block.blockName);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAssetFiles(projectPath) {
|
||||||
|
// Register tasks.
|
||||||
|
gulp.task('build-script-files', (done) => buildScriptFiles(projectPath, done));
|
||||||
|
gulp.task('build-styling-files', (done) => buildStyleFiles(projectPath, done));
|
||||||
|
|
||||||
|
// Run first build.
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
gulp.series('build-script-files', 'build-styling-files', function () {
|
||||||
|
resolve();
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(errorMessage) {
|
||||||
|
console.log(errorMessage);
|
||||||
|
|
||||||
|
// TODO: Send this message to browser.
|
||||||
|
// So the developer can understand there is an error.
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildScriptFiles(projectPath = '', done) {
|
||||||
|
const files = getJSBundleFiles(projectPath);
|
||||||
|
const stream = gulp.src(files, {base: path.posix.join(projectPath, 'src')})
|
||||||
|
.pipe(sourcemaps.init({}))
|
||||||
|
.pipe(babel()).on('error', function (error) {
|
||||||
|
showError(new PluginError('JavaScript', error).toString());
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.pipe(gulp.src(path.join(projectPath, 'vendor/*.js')))
|
||||||
|
// .pipe(gulp.dest('src/'))
|
||||||
|
.pipe(rename({extname: '.min.js'}))
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src')));
|
||||||
|
|
||||||
|
stream.on('end', done);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSBundleFiles(projectPath = '') {
|
||||||
|
return [path.posix.join(projectPath, "src/**/*.js"), path.posix.join(projectPath, "src/**/*.mjs"), "!" + path.posix.join(projectPath, "src/**/*.min.js")];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStyleFiles(projectPath = '', done) {
|
||||||
|
const stream = gulp.src(path.join(projectPath, 'src/**/*.scss'), {base: path.posix.join(projectPath, 'src')})
|
||||||
|
.pipe(sourcemaps.init({}))
|
||||||
|
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', function (error) {
|
||||||
|
showError(new PluginError('SCSS', error.messageFormatted).toString());
|
||||||
|
// sass.logError(error);
|
||||||
|
done();
|
||||||
|
}))
|
||||||
|
// .pipe(gulp.dest('src/'))
|
||||||
|
.pipe(rename({extname: '.min.css'}))
|
||||||
|
.pipe(sourcemaps.write('.', {}))
|
||||||
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src')));
|
||||||
|
|
||||||
|
stream.on('end', done);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export function setHeaders(req, res, next) {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
next();
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import express from "express";
|
||||||
|
import bodyParser from "body-parser";
|
||||||
|
import {createServer} from "http";
|
||||||
|
import {create} from "express-handlebars";
|
||||||
|
import {escape} from "lodash-es";
|
||||||
|
import {sanitizeUrl} from "@braintree/sanitize-url";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// parse application/x-www-form-urlencoded
|
||||||
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
|
|
||||||
|
// parse application/json
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
const hbs = getHandlebarsViewEngine();
|
||||||
|
|
||||||
|
app.engine('.hbs', hbs.engine);
|
||||||
|
app.set('view engine', '.hbs');
|
||||||
|
|
||||||
|
const modulesPath = '.';
|
||||||
|
app.set('views', path.join(modulesPath, 'layouts'));
|
||||||
|
|
||||||
|
// Static Files of blockDevTool.
|
||||||
|
app.use(express.static(path.join(modulesPath, 'layouts')));
|
||||||
|
|
||||||
|
return {
|
||||||
|
expressApp: app,
|
||||||
|
httpServer: createServer(app),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHandlebarsViewEngine() {
|
||||||
|
return create({
|
||||||
|
extname: '.hbs', defaultLayout: false, partialsDir: ['.'], helpers: handlebarsHelpers()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlebarsHelpers() {
|
||||||
|
return {
|
||||||
|
esc_attr(attr) {
|
||||||
|
return escape(attr);
|
||||||
|
},
|
||||||
|
esc_url(url) {
|
||||||
|
return sanitizeUrl(url);
|
||||||
|
},
|
||||||
|
esc_html(html) {
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {Server} from "socket.io";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import {getBlockName, handlebarLayoutsPath} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export class ViewSync {
|
||||||
|
constructor(httpServer) {
|
||||||
|
this.sessions = {};
|
||||||
|
this.init(httpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSession(session, blockName) {
|
||||||
|
if (!this.sessions[blockName]) {
|
||||||
|
this.sessions[blockName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessions[blockName].push(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(httpServer) {
|
||||||
|
const io = new Server(httpServer);
|
||||||
|
io.on('connection', async (socket) => {
|
||||||
|
const blockName = socket.handshake.query.block;
|
||||||
|
|
||||||
|
if (!blockName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addSession(socket, blockName);
|
||||||
|
await this.syncTemplate(blockName);
|
||||||
|
});
|
||||||
|
|
||||||
|
// return httpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncTemplate(blockName, updateType = 'templateUpdate') {
|
||||||
|
const block = getBlockName(blockName);
|
||||||
|
|
||||||
|
const projectPath = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const hbsTemplate = await fs.readFile(handlebarLayoutsPath(projectPath, 'src', `${block.name}.template.hbs`), 'utf8');
|
||||||
|
|
||||||
|
if (!this.sessions[blockName]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessions[blockName].forEach(session => {
|
||||||
|
session.emit(updateType, {block: blockName, template: hbsTemplate});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/head") }}
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
<body>
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/toolbar") }}
|
<div id="hbs-container"></div>
|
||||||
|
|
||||||
<main>
|
|
||||||
{{> (include_block_template) }}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/scripts") }}
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/head") }}
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
<body>
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/toolbar") }}
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{> (include_block_template) }}
|
<div id="hbs-container"></div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
|
||||||
|
|
||||||
{{> (include_partial "layouts/partials/scripts") }}
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container" style="max-width: 1280px; margin: 1rem auto;">
|
||||||
|
<h1>Syntax Error:</h1>
|
||||||
|
<pre style="padding: 10px 15px; background-color: #ffe6e6; border: 1px solid red; border-radius: 0.25rem; overflow: auto">{{err}}</pre>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="/styles/page--main.css">
|
||||||
|
<title>Block Development Tool</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="{{#if viewMode }}view-mode{{/if}}">
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/toolbar") }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.devTool = {
|
||||||
|
blockName: '{{ blockName }}',
|
||||||
|
previewFrameUrl: '{{ previewFrameUrl }}',
|
||||||
|
{{#if publicUrl}}
|
||||||
|
publicUrl: true,
|
||||||
|
{{/if}}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="preview">
|
||||||
|
<iframe id="preview_frame" src="{{ previewFrameUrl }}?iframe=true&block={{blockName}}" class="breakpoint"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/scripts/dist/index.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -2,8 +2,13 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ config.cssUrl }}">
|
<link rel="stylesheet" href="https://unpkg.com/swiper@8.4.5/swiper-bundle.min.css"/>
|
||||||
<link rel="stylesheet" href="styles/page--main.css">
|
{{#if config.project.css }} <link rel="stylesheet" href="{{ config.project.css }}">
|
||||||
<link rel="stylesheet" href="styles/{{ config.blockName }}.min.css">
|
{{/if}}
|
||||||
<link rel="stylesheet" href="https://unpkg.com/swiper@8/swiper-bundle.min.css"/>
|
{{#if config.cssUrl }}
|
||||||
|
{{#each config.cssUrl }}
|
||||||
|
<link rel="stylesheet" href="{{ this }}">
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}<link rel="stylesheet" href="/styles/page--view.css">{{# if config.blockName}}
|
||||||
|
<link rel="stylesheet" id="block-stylesheet" href="{{staticFilesPath}}/styles/{{ config.blockName }}.min.css?v=1">{{/if}}
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
|
||||||
|
<script src="/scripts/dist/frame-index.min.js"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
|
||||||
<script src="https://unpkg.com/swiper@8/swiper-bundle.min.js"></script>
|
<script src="https://unpkg.com/swiper@8.4.5/swiper-bundle.min.js"></script>
|
||||||
<script src="scripts/page--toolbar.js"></script>
|
{{#if config.project.js }}<script src="{{ config.project.js }}"></script>
|
||||||
<script src="scripts/{{ config.blockName }}.min.js"></script>
|
{{/if}}
|
||||||
|
{{#if config.jsUrl }}
|
||||||
|
{{#each config.jsUrl }}<script src="{{ this }}"></script>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if config.blockName }}
|
||||||
|
<script id="block-script" src="{{staticFilesPath}}/scripts/{{ config.blockName }}.min.js?v=1"></script>
|
||||||
|
{{/if}}
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
<header class="page_toolbar">
|
<header class="page_toolbar">
|
||||||
<div class="page_toolbar__data_options">
|
|
||||||
<label for="data-options">Data Options: </label>
|
|
||||||
|
|
||||||
<select name="data" id="data-options">
|
<div class="page_toolbar__left"></div>
|
||||||
{{#each config.dataFiles }}
|
|
||||||
<option value="{{ name }}" {{#if active }}selected="selected"{{/if}}>{{ name }}</option>
|
<div class="page_toolbar__middle">
|
||||||
{{/each}}
|
<div style="display: none">
|
||||||
</select>
|
Version: {{ config.version }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="page_toolbar__right">
|
||||||
Sizes: <b>1rem = {{ config.remToPx }}px</b>
|
{{#if shareUrl}}
|
||||||
|
<a href="{{ shareUrl }}" target="_blank" class="share" title="Share URL"></a>
|
||||||
|
{{/if}}
|
||||||
|
{{#if styleGuideUrl}}
|
||||||
|
<a href="{{ styleGuideUrl }}" target="_blank" class="palette" title="Open Style Guide"></a>
|
||||||
|
{{/if}}
|
||||||
|
<a href="{{ previewFrameUrl }}?block={{blockName}}" target="_blank" class="open_in_new_tab" title="Open in New Window"></a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
let e;window.initBlock=function(e="",n="",o){t=function(){document.querySelectorAll(n).forEach((e=>o(e)))},t()};let t,n={};!function(){const o=r().block;function r(){const e=new URLSearchParams(window.location.search),t={};for(const[n,o]of e)t[n]=o;return t}function a(e){const t=document.getElementById(e),n=t.parentNode;let o,r;return"SCRIPT"===t.tagName?(o=document.createElement("script"),o.type="text/javascript",r="src"):"LINK"===t.tagName&&(o=document.createElement("link"),o.rel="stylesheet",r="href"),o.id=e,o[r]=t[r].replace(/(\?v=)[^&]+/,`$1${Date.now()}`),t.remove(),n.append(o),o}function c(r={}){r.template&&(e=r.template),r.data&&(n=r.data),e&&function(e,n,r){const a=Handlebars.compile(e);let c;Handlebars.registerHelper("esc_attr",(function(e){return e})),Handlebars.registerHelper("esc_url",(function(e){return e})),Handlebars.registerHelper("esc_html",(function(e){return e})),Handlebars.registerHelper("base_url",(function(){const e=["block"];return o&&e.push(o.substr(1)),["",...e,""].join("/")}));try{c=a(n)}catch(e){c=`<div style="max-width: 1280px; margin: 1rem auto;">\n <h1 style="all: unset; font-size: 1.5rem; font-weight: bold; display: block;">Syntax Error:</h1>\n <pre style="all: unset; padding: 10px 15px; background-color: #ffe6e6; border: 1px solid red; border-radius: 0.25rem; overflow: auto; display: block; white-space: pre;">${e.toString()}</pre>\n </div>`}r.innerHTML=c,t&&t()}(e,n||{},document.getElementById("hbs-container"))}!function(){const e=new URLSearchParams({block:o,name:r().data||"default"});fetch(`/data?${e}`).then((e=>e.json())).then((e=>{n=e.data,c({data:n})}))}(),window.addEventListener("message",(function(e){const t=e.data,o="dataUpdate:";if("string"==typeof t&&t.startsWith(o))try{n=JSON.parse(t.substring(o.length)),c({data:n})}catch(e){console.log("Error parsing incoming data.",e)}})),function(){const e=window.io.connect("",{query:`block=${o}`});e.on("error",(function(e){console.log(e)})),e.on("templateUpdate",(function(e){c({template:e.template})})),e.on("scriptUpdate",(function(e){a("block-script").onload=()=>{c({template:e.template})}})),e.on("styleUpdate",(function(e){a("block-stylesheet")}))}()}();
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M12.45 38.7 9.3 35.55 20.85 24 9.3 12.5l3.15-3.2L24 20.8 35.55 9.3l3.15 3.2L27.2 24l11.5 11.55-3.15 3.15L24 27.2Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 218 B |
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M15 20H9" stroke="url(#paint0_linear_17102_17357)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path
|
||||||
|
d="M19 5H5C4.44772 5 4 5.44772 4 6V16C4 16.5523 4.44772 17 5 17H19C19.5523 17 20 16.5523 20 16V6C20 5.44772 19.5523 5 19 5Z"
|
||||||
|
stroke="url(#paint1_linear_17102_17357)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17357" x1="9" y1="21" x2="11.3077" y2="17.5385"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17357" x1="4" y1="17" x2="22" y2="11" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 970 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
|
||||||
|
<path id="XMLID_225_" d="M325.607,79.393c-5.857-5.857-15.355-5.858-21.213,0.001l-139.39,139.393L25.607,79.393
|
||||||
|
c-5.857-5.857-15.355-5.858-21.213,0.001c-5.858,5.858-5.858,15.355,0,21.213l150.004,150c2.813,2.813,6.628,4.393,10.606,4.393
|
||||||
|
s7.794-1.581,10.606-4.394l149.996-150C331.465,94.749,331.465,85.251,325.607,79.393z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M28.6 40.75V36.2h4.75q1.2 0 2.05-.825.85-.825.85-2.075v-3.85q0-1.8 1.075-3.225T40.15 24.2v-.4q-1.75-.55-2.825-2-1.075-1.45-1.075-3.3v-3.85q0-1.2-.85-2.05-.85-.85-2.05-.85H28.6V7.2h5.95q2.6 0 4.425 1.825Q40.8 10.85 40.8 13.5v3.85q0 1.2.825 2.05.825.85 2.075.85h1.1v7.5h-1.1q-1.25 0-2.075.85-.825.85-.825 2.05v3.85q0 2.65-1.825 4.45-1.825 1.8-4.425 1.8Zm-15.1 0q-2.7 0-4.475-1.8-1.775-1.8-1.775-4.45v-3.85q0-1.2-.85-2.05-.85-.85-2.05-.85h-1.1v-7.5h1.1q1.2 0 2.05-.85.85-.85.85-2.05V13.5q0-2.65 1.8-4.475Q10.85 7.2 13.5 7.2h5.95v4.55H14.7q-1.25 0-2.075.85-.825.85-.825 2.05v3.85q0 1.85-1.1 3.3-1.1 1.45-2.85 2v.4q1.75.6 2.85 2.025 1.1 1.425 1.1 3.225v3.85q0 1.25.825 2.075.825.825 2.075.825h4.75v4.55Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 803 B |
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M16 3H8C7.44772 3 7 3.44772 7 4V20C7 20.5523 7.44772 21 8 21H16C16.5523 21 17 20.5523 17 20V4C17 3.44772 16.5523 3 16 3Z"
|
||||||
|
stroke="url(#paint0_linear_17102_17362)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 18H12.002V18.002H12V18Z" stroke="url(#paint1_linear_17102_17362)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17362" x1="7" y1="21" x2="19.2634" y2="19.2967"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17362" x1="12" y1="18.002" x2="12.0024" y2="18.0014"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M9.5 43.05q-1.85 0-3.2-1.35t-1.35-3.2v-29q0-1.9 1.35-3.25T9.5 4.9h13.35v4.6H9.5v29h29V25.15h4.6V38.5q0 1.85-1.35 3.2t-3.25 1.35ZM20.3 30.9l-3.15-3.2 18.2-18.2h-9.5V4.9H43.1v17.25h-4.6V12.7Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 294 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M24 45.25q-4.35 0-8.225-1.675T9 39q-2.9-2.9-4.575-6.775Q2.75 28.35 2.75 24q0-4.5 1.675-8.375t4.625-6.75Q12 6 15.95 4.35q3.95-1.65 8.4-1.65 4.2 0 8 1.425t6.675 3.95Q41.9 10.6 43.6 14.05q1.7 3.45 1.7 7.5 0 5.7-3.2 9.35-3.2 3.65-8.9 3.65h-2.9q-.8 0-1.4.65-.6.65-.6 1.45 0 1.25.5 1.75t.5 1.55q0 2.2-1.5 3.75-1.5 1.55-3.8 1.55ZM24 24Zm-11.2 1.3q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1 0-1.75.75t-.75 1.75q0 1 .75 1.75t1.75.75Zm6.1-8.15q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm10.25 0q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm6.2 8.15q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1.05 0-1.775.75-.725.75-.725 1.75t.725 1.75q.725.75 1.775.75ZM23.8 40.55q.4 0 .6-.175.2-.175.2-.625 0-.7-.75-1.175-.75-.475-.75-2.375 0-2.45 1.65-4.4 1.65-1.95 4.1-1.95h4.35q3.7 0 5.525-2.2 1.825-2.2 1.825-5.8 0-6.6-4.925-10.5Q30.7 7.45 24.4 7.45q-7.1 0-12.025 4.825Q7.45 17.1 7.45 24q0 6.9 4.8 11.725 4.8 4.825 11.55 4.825Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="m31.7 26.4-3.85-3.8q1.05-2.2-.625-3.45t-3.125-.3l-3.5-3.55q.75-.4 1.65-.6.9-.2 1.75-.2 3.55 0 6.025 2.475Q32.5 19.45 32.5 23q0 .8-.2 1.8t-.6 1.6Zm7.4 7.5-2.75-2.8q2.2-1.75 3.825-3.85T42.8 23q-2.6-5.5-7.525-8.75Q30.35 11 24.5 11q-2.1 0-4.1.35-2 .35-2.95.8l-3.3-3.3q1.75-.8 4.625-1.4 2.875-.6 5.475-.6 7.35 0 13.6 4.25t9.3 11.9q-1.25 3.3-3.375 6.075Q41.65 31.85 39.1 33.9Zm1.25 11.35-7.7-7.6q-1.75.7-4 1.1-2.25.4-4.65.4-7.55 0-13.825-4.275Q3.9 30.6.85 23q.9-2.55 2.75-5.15 1.85-2.6 4.3-5L1.75 6.8l2.5-2.6L42.7 42.65ZM10.8 15.7Q9 17.15 7.5 19.125 6 21.1 5.2 23q2.6 5.55 7.65 8.775Q17.9 35 24.4 35q1.35 0 2.775-.15 1.425-.15 2.225-.55l-3.2-3.2q-.4.2-1.025.3-.625.1-1.175.1-3.5 0-6-2.45T15.5 23q0-.55.075-1.15.075-.6.225-1.05Zm16.15 6.4Zm-6.85 3.45Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 825 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M24 31.5q3.55 0 6.025-2.475Q32.5 26.55 32.5 23q0-3.55-2.475-6.025Q27.55 14.5 24 14.5q-3.55 0-6.025 2.475Q15.5 19.45 15.5 23q0 3.55 2.475 6.025Q20.45 31.5 24 31.5Zm0-3.7q-2 0-3.4-1.4T19.2 23q0-2 1.4-3.4t3.4-1.4q2 0 3.4 1.4t1.4 3.4q0 2-1.4 3.4T24 27.8Zm0 11.35q-7.7 0-13.9-4.5T.85 23q3.05-7.15 9.25-11.65T24 6.85q7.7 0 13.9 4.5T47.15 23q-3.05 7.15-9.25 11.65T24 39.15ZM24 23Zm0 12q6 0 11.05-3.275Q40.1 28.45 42.75 23q-2.65-5.45-7.675-8.725Q30.05 11 24 11q-6 0-11.05 3.275Q7.9 17.55 5.2 23q2.7 5.45 7.725 8.725Q17.95 35 24 35Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 604 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
|
||||||
|
<path
|
||||||
|
d="M22.55 42.65q-6.45-.55-10.85-5.3Q7.3 32.6 7.3 26.1q0-3.95 1.775-7.4t4.975-5.75l2.8 2.85q-2.6 1.6-4.1 4.375-1.5 2.775-1.5 5.925 0 4.9 3.225 8.45 3.225 3.55 8.075 4.1Zm3 0v-4q4.9-.6 8.075-4.125Q36.8 31 36.8 26.1q0-5.2-3.55-8.875T24.5 13.35h-1.1l3.15 3.15-2.4 2.4-7.65-7.65 7.65-7.7 2.4 2.4-3.45 3.4h1.1q6.95 0 11.775 4.9T40.8 26.1q0 6.5-4.4 11.25t-10.85 5.3Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 446 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="478" height="506" viewBox="0 0 478 506" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M379.8 315.4C352.01 315.4 327.062 327.817 310.146 347.315L186.499 278.249C188.838 270.119 190.1 261.521 190.1 252.7C190.1 243.782 188.834 235.223 186.427 227.037L309.923 158.096C326.774 177.698 351.821 190.2 379.7 190.2C430.409 190.2 471.8 148.918 471.8 98.1C471.8 47.2863 430.514 6 379.7 6C328.886 6 287.6 47.2863 287.6 98.1C287.6 106.995 288.859 115.615 291.265 123.769L167.886 192.697C151.025 173.006 125.968 160.6 98.1 160.6C47.3908 160.6 6 201.882 6 252.7C6 303.523 47.3954 344.8 98.2 344.8C126.093 344.8 151.144 332.287 168.074 312.598L291.571 381.633C289.143 389.853 287.8 398.557 287.8 407.5C287.8 458.209 329.082 499.6 379.9 499.6C430.714 499.6 472 458.314 472 407.5C472 356.677 430.605 315.4 379.8 315.4ZM379.8 45.1C409.086 45.1 432.9 68.9137 432.9 98.2C432.9 127.486 409.086 151.3 379.8 151.3C350.514 151.3 326.7 127.486 326.7 98.2C326.7 68.9203 350.607 45.1 379.8 45.1ZM98.2 305.8C68.9137 305.8 45.1 281.986 45.1 252.7C45.1 223.414 68.9137 199.6 98.2 199.6C127.486 199.6 151.3 223.414 151.3 252.7C151.3 281.98 127.393 305.8 98.2 305.8ZM379.8 460.5C350.514 460.5 326.7 436.686 326.7 407.4C326.7 378.114 350.514 354.3 379.8 354.3C409.086 354.3 432.9 378.114 432.9 407.4C432.9 436.686 409.086 460.5 379.8 460.5Z" fill="#21252D" stroke="#21252D" stroke-width="12"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M20 3H4C3.44772 3 3 3.44772 3 4V20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20V4C21 3.44772 20.5523 3 20 3Z"
|
||||||
|
stroke="url(#paint0_linear_17102_17360)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 18H12.002V18.002H12V18Z" stroke="url(#paint1_linear_17102_17360)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17360" x1="3" y1="21" x2="24.1765" y2="15.7059"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17360" x1="12" y1="18.002" x2="12.0024" y2="18.0014"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,184 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.initBlock = initBlock;
|
||||||
|
|
||||||
|
let template;
|
||||||
|
let data = {};
|
||||||
|
let reload;
|
||||||
|
|
||||||
|
// Blocks Initialization.
|
||||||
|
function initBlock(blockName = '', selector = '', cb) {
|
||||||
|
reload = function () {
|
||||||
|
document.querySelectorAll(selector).forEach((el) => cb(el));
|
||||||
|
}
|
||||||
|
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data Updates Listeners.
|
||||||
|
(function () {
|
||||||
|
const block = getQueryParams().block;
|
||||||
|
|
||||||
|
loadDataOptions();
|
||||||
|
listenToDataOptionsUpdates();
|
||||||
|
|
||||||
|
function listenToDataOptionsUpdates() {
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
const message = event.data;
|
||||||
|
const prefix = 'dataUpdate:';
|
||||||
|
|
||||||
|
if (typeof message !== "string" || !message.startsWith(prefix)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(message.substring(prefix.length));
|
||||||
|
updateBlock({data});
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error parsing incoming data.', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryParams() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
for (const [key, value] of urlParams) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDataOptions() {
|
||||||
|
const queryParameters = new URLSearchParams({
|
||||||
|
block,
|
||||||
|
name: getQueryParams().data || 'default'
|
||||||
|
});
|
||||||
|
fetch(`/data?${queryParameters}`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((response) => {
|
||||||
|
data = response.data; // Update state.
|
||||||
|
updateBlock({data});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to Template updates.
|
||||||
|
initSocket();
|
||||||
|
|
||||||
|
function initSocket() {
|
||||||
|
const socket = window.io.connect('', {query: `block=${block}`});
|
||||||
|
|
||||||
|
socket.on('error', function (err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// socket.on('connect', function () {
|
||||||
|
// console.log('user connected', socket.id)
|
||||||
|
// });
|
||||||
|
|
||||||
|
socket.on('templateUpdate', function (args) {
|
||||||
|
updateBlock({template: args.template});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('scriptUpdate', function (args) {
|
||||||
|
const tag = updateTag('block-script');
|
||||||
|
tag.onload = () => {
|
||||||
|
updateBlock({template: args.template});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('styleUpdate', function (args) {
|
||||||
|
updateTag('block-stylesheet');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTag(id) {
|
||||||
|
const tag = document.getElementById(id);
|
||||||
|
const wrapper = tag.parentNode;
|
||||||
|
|
||||||
|
let clone;
|
||||||
|
let attr;
|
||||||
|
if (tag.tagName === 'SCRIPT') {
|
||||||
|
clone = document.createElement('script');
|
||||||
|
clone.type = 'text/javascript';
|
||||||
|
attr = 'src';
|
||||||
|
} else if (tag.tagName === 'LINK') {
|
||||||
|
clone = document.createElement('link');
|
||||||
|
clone.rel = 'stylesheet';
|
||||||
|
attr = 'href';
|
||||||
|
}
|
||||||
|
|
||||||
|
clone.id = id;
|
||||||
|
|
||||||
|
// Add version to the stylesheet URL, make sure we override the cache and already existing version there.
|
||||||
|
clone[attr] = tag[attr].replace(/(\?v=)[^&]+/, `$1${Date.now()}`);
|
||||||
|
|
||||||
|
tag.remove();
|
||||||
|
wrapper.append(clone);
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBlock(args = {}) {
|
||||||
|
if (args.template) {
|
||||||
|
template = args.template; // Update state.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.data) {
|
||||||
|
data = args.data; // Update state.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!template) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBlock(template, data || {}, document.getElementById("hbs-container"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBlock(templateHbs, jsonData, target) {
|
||||||
|
const template = Handlebars.compile(templateHbs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handlebars Helpers
|
||||||
|
*/
|
||||||
|
Handlebars.registerHelper('esc_attr', function (attr) {
|
||||||
|
return attr;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('esc_url', function (attr) {
|
||||||
|
return attr;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('esc_html', function (attr) {
|
||||||
|
return attr;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('base_url', function () {
|
||||||
|
const list = ['block'];
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
list.push(block.substr(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['', ...list, ''].join('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
let html;
|
||||||
|
|
||||||
|
try {
|
||||||
|
html = template(jsonData);
|
||||||
|
} catch (e) {
|
||||||
|
html = `<div style="max-width: 1280px; margin: 1rem auto;">
|
||||||
|
<h1 style="all: unset; font-size: 1.5rem; font-weight: bold; display: block;">Syntax Error:</h1>
|
||||||
|
<pre style="all: unset; padding: 10px 15px; background-color: #ffe6e6; border: 1px solid red; border-radius: 0.25rem; overflow: auto; display: block; white-space: pre;">${e.toString()}</pre>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.innerHTML = html;
|
||||||
|
if (reload) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {setupResponsiveness} from './toolbar/responsive.jsx';
|
||||||
|
import {setupPublish} from "./toolbar/publish.jsx";
|
||||||
|
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
|
||||||
|
|
||||||
|
const rootAttributes = {
|
||||||
|
previewFrame: getPreviewFrame(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// setupFrameResizeListener();
|
||||||
|
setupResponsiveness(rootAttributes);
|
||||||
|
setupDataOptions(rootAttributes);
|
||||||
|
setupPublish(rootAttributes);
|
||||||
|
|
||||||
|
function getPreviewFrame() {
|
||||||
|
return document.getElementById('preview_frame');
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
(function () {
|
|
||||||
|
|
||||||
const dataOptionsSelect = document.getElementById('data-options');
|
|
||||||
if (!dataOptionsSelect) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataOptionsSelect.addEventListener('change', function () {
|
|
||||||
console.log(this.value)
|
|
||||||
window.location = '?data=' + this.value;
|
|
||||||
})
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React, {useState} from "react";
|
||||||
|
import * as ReactDOM from "react-dom/client";
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const wrapper = document.querySelector('#screen');
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(wrapper);
|
||||||
|
const html = (<SyncScreen/>);
|
||||||
|
|
||||||
|
root.render(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SyncScreen() {
|
||||||
|
const [loading, setLoading] = useState(null);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
return <>
|
||||||
|
|
||||||
|
<section className="container py-5">
|
||||||
|
<h1 style={{marginBottom: '2rem'}}>Oops... Block not in sync.</h1>
|
||||||
|
{error && <p className="alert alert-danger">{error}</p>}
|
||||||
|
|
||||||
|
{loading ?
|
||||||
|
<>
|
||||||
|
{loading === 'syncing' &&
|
||||||
|
<p>Version upgrade in progress...</p>
|
||||||
|
}
|
||||||
|
<p>Please wait...</p>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<p>Your version of the block is not in sync with the cloud (not latest version).<br/>
|
||||||
|
Would you like to update it?</p>
|
||||||
|
<div className="options">
|
||||||
|
<button className="btn btn-primary" style={{marginRight: '0.5rem'}} onClick={runSyncLogic}>Yes, Update to
|
||||||
|
Latest
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-secondary" onClick={ignoreVersionSync}>Ignore</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
|
async function ignoreVersionSync() {
|
||||||
|
// Add "ignore" query parameter to current URl and redirect.
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set('ignoreVersionSync', 'true');
|
||||||
|
window.location = url.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSyncLogic() {
|
||||||
|
setLoading('syncing');
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
try {
|
||||||
|
const response = await fetch('/sync', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({block: getQueryParams().block}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
data = await response.json();
|
||||||
|
} catch (err) {
|
||||||
|
setError('Error: ' + err.message);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status !== 200) {
|
||||||
|
setError(data.message);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => window.location.reload(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This function is used in multiple places (repeated code).
|
||||||
|
// Move to a common place.
|
||||||
|
function getQueryParams() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
for (const [key, value] of urlParams) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
import {
|
||||||
|
SidebarButtonToggleStyle,
|
||||||
|
SidebarCloseButtonStyle, SidebarDataOptionsStyle,
|
||||||
|
SidebarHeaderStyle,
|
||||||
|
SidebarStyle,
|
||||||
|
} from "./data-options.style.js";
|
||||||
|
import {isClickOutside, isEscHit} from "../responsive-button/ResponsiveButton.jsx";
|
||||||
|
import {DesignPreview} from "./DesignPreview.jsx";
|
||||||
|
|
||||||
|
export const PRODUCTION_REGISTRY_URL = 'https://blocks-registery.axe-web.com';
|
||||||
|
|
||||||
|
function DataOptions(props = {}) {
|
||||||
|
props.rootAttributes = props.rootAttributes ?? {};
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
dataName: 'default',
|
||||||
|
data: {},
|
||||||
|
dataText: '{}',
|
||||||
|
dataOptions: [],
|
||||||
|
designPreview: [],
|
||||||
|
errorMessage: null,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const [state, setState] = useState(initialState);
|
||||||
|
const [previewOption, setPreviewOption] = useState(getDesignPreviewImage(state.dataName, state.designPreview));
|
||||||
|
const updateState = (update) => setState(Object.assign({}, state, update));
|
||||||
|
|
||||||
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
await syncDataOptions(state.dataName);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCloseSidebarEscEvent = useCallback((e) => {
|
||||||
|
if (isEscHit(e)) {
|
||||||
|
(() => {
|
||||||
|
closeSidebar()
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener("keydown", handleCloseSidebarEscEvent);
|
||||||
|
|
||||||
|
// Unsubscribe from ESC listener.
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", handleCloseSidebarEscEvent);
|
||||||
|
}
|
||||||
|
}, [handleCloseSidebarEscEvent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('message', responsiveModeChangesHandler);
|
||||||
|
return () => window.removeEventListener('message', responsiveModeChangesHandler);
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPreviewOption(getDesignPreviewImage(state.dataName, state.designPreview));
|
||||||
|
}, [state.designPreview]);
|
||||||
|
|
||||||
|
const handleBlur = async (e) => await isClickOutside(e) ? closeSidebar() : null;
|
||||||
|
|
||||||
|
return <div style={{display: 'flex', gap: '1rem', alignItems: 'center'}}>
|
||||||
|
<SidebarButtonToggleStyle onClick={() => openSidebar()} title="Open a Sidebar with Data Options"/>
|
||||||
|
|
||||||
|
<DesignPreview previewOption={previewOption}/>
|
||||||
|
|
||||||
|
<SidebarStyle className={sidebarOpen ? 'active sidebar-active' : ''} tabIndex='0' onBlur={handleBlur}>
|
||||||
|
<SidebarHeaderStyle>
|
||||||
|
<SidebarCloseButtonStyle onClick={() => closeSidebar()}></SidebarCloseButtonStyle>
|
||||||
|
</SidebarHeaderStyle>
|
||||||
|
|
||||||
|
{state.dataOptions && !!state.dataOptions.length &&
|
||||||
|
<SidebarDataOptionsStyle>
|
||||||
|
<label htmlFor="data-options">Data Options</label>
|
||||||
|
|
||||||
|
<select name="data" id="data-options" onChange={(e) => changeDataOption(e)} value={state.dataName} disabled={state.loading}>
|
||||||
|
{state.dataOptions.map((item) => {
|
||||||
|
const isSelected = state.dataName === item;
|
||||||
|
return <option value={item} selected={isSelected}>{item}</option>
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</SidebarDataOptionsStyle>
|
||||||
|
}
|
||||||
|
|
||||||
|
{state.data &&
|
||||||
|
<textarea value={state.dataText} onChange={dataOptionUpdate} disabled={state.loading}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{state.errorMessage &&
|
||||||
|
<p className={'alert alert--error'}>{state.errorMessage}</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
{state.loading &&
|
||||||
|
<p className={'alert'}>Loading, please wait...</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={'actions'}>
|
||||||
|
<button className='btn btn--secondary' disabled={state.loading} onClick={(e) => copyToClipboard(e, state.data)}>Copy to Clipboard</button>
|
||||||
|
<button className='btn btn--secondary' disabled={state.loading} onClick={(e) => generateVariation(e, state.data)}>Generate Test</button>
|
||||||
|
</div>
|
||||||
|
</SidebarStyle>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
function generateVariation() {
|
||||||
|
const url = PRODUCTION_REGISTRY_URL + '/content-generator/';
|
||||||
|
|
||||||
|
const myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: JSON.stringify({json: state.data}),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateState({loading: true, errorMessage: null});
|
||||||
|
|
||||||
|
return fetch(url, requestOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
console.log(result)
|
||||||
|
if (result.statusCode !== 200) {
|
||||||
|
throw new Error(result.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = result.variation;
|
||||||
|
updateState({dataText: JSON.stringify(data, null, 2), data, errorMessage: null, loading: false});
|
||||||
|
updateIframe(data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
updateState({loading: false, errorMessage: 'Something went wrong, please try again.'})
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataOptionUpdate(e) {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(e.target.value);
|
||||||
|
} catch (err) {
|
||||||
|
updateState({dataText: e.target.value, errorMessage: 'Invalid JSON, please review and try again.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState({dataText: e.target.value, data, errorMessage: null});
|
||||||
|
updateIframe(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSidebar() {
|
||||||
|
setSidebarOpen(true);
|
||||||
|
setTimeout(() => document.querySelector('.sidebar-active').focus());
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSidebar() {
|
||||||
|
setSidebarOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeDataOption(e) {
|
||||||
|
const dataName = e.target.value;
|
||||||
|
await syncDataOptions(dataName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchDataOptions(name = 'default') {
|
||||||
|
const queryParameters = new URLSearchParams({name, block: window.devTool.blockName});
|
||||||
|
const response = await fetch(`/data?${queryParameters}`);
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDesignPreviewImage(currentDataName, designPreviewOptions = []) {
|
||||||
|
const dataOptions = designPreviewOptions.filter((item) => {
|
||||||
|
return item.dataSource === currentDataName;
|
||||||
|
});
|
||||||
|
|
||||||
|
dataOptions.sort((a, b) => b.widthDimension - a.widthDimension);
|
||||||
|
let size = window.responsiveState.breakpoint;
|
||||||
|
if (size === '100%') {
|
||||||
|
size = window.innerWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataOptions.find((item) => {
|
||||||
|
return size >= item.widthDimension;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function responsiveModeChangesHandler(e) {
|
||||||
|
if (e.data !== 'responsiveUpdate') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreviewOption(getDesignPreviewImage(state.dataName, state.designPreview));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncDataOptions(dataName) {
|
||||||
|
const dataOptions = await fetchDataOptions(dataName);
|
||||||
|
updateIframe(dataOptions.data);
|
||||||
|
|
||||||
|
const newState = Object.assign({errorMessage: null},
|
||||||
|
dataOptions,
|
||||||
|
{dataName},
|
||||||
|
{dataText: JSON.stringify(dataOptions.data, null, 2)},
|
||||||
|
);
|
||||||
|
updateState(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateIframe(data) {
|
||||||
|
const previewIframe = props.rootAttributes.previewFrame;
|
||||||
|
previewIframe.contentWindow.postMessage('dataUpdate:' + JSON.stringify(data || {}), '*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(e, context) {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (typeof context !== 'string') {
|
||||||
|
context = JSON.stringify(context, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
|
// This case will not work on non-https pages.
|
||||||
|
return navigator.clipboard.writeText(context);
|
||||||
|
} else {
|
||||||
|
let textArea = document.createElement("textarea");
|
||||||
|
textArea.value = context;
|
||||||
|
textArea.style.position = "fixed";
|
||||||
|
textArea.style.left = "-999999px";
|
||||||
|
textArea.style.top = "-999999px";
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
// textArea.focus();
|
||||||
|
textArea.select();
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
document.execCommand('copy') ? res() : rej();
|
||||||
|
textArea.remove();
|
||||||
|
e.target.focus()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupDataOptions(rootAttributes) {
|
||||||
|
// INIT
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
document.querySelector('.page_toolbar__left').prepend(wrapper)
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(wrapper);
|
||||||
|
const html = (<DataOptions rootAttributes={rootAttributes}/>);
|
||||||
|
root.render(html);
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import React, {createRef, useCallback, useEffect, useState} from 'react';
|
||||||
|
import {PreviewButtonStyle, PreviewStyle} from "./design-preview.style.js";
|
||||||
|
|
||||||
|
let startDragPosition = {x: 0, y: 0};
|
||||||
|
|
||||||
|
export function DesignPreview({previewOption = {widthDimension: 0}}) {
|
||||||
|
const reference = createRef();
|
||||||
|
const [active, setActive] = useState(false);
|
||||||
|
const [position, setPosition] = useState({x: getInitialXPosition(previewOption), y: 0});
|
||||||
|
const [opacity, setOpacity] = useState(85);
|
||||||
|
|
||||||
|
const keyDownHandler = useCallback(
|
||||||
|
function (e) {
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const step = e.shiftKey ? 10 : 1;
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
updatePosition({y: step * -1}, true)
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
updatePosition({y: step}, true)
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
updatePosition({x: step * -1}, true)
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
updatePosition({x: step}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [position, active]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('keydown', keyDownHandler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', keyDownHandler)
|
||||||
|
}
|
||||||
|
}, [keyDownHandler]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updatePosition({x: getInitialXPosition(previewOption), y: 0});
|
||||||
|
|
||||||
|
window.addEventListener('message', responsiveModeChangesHandler);
|
||||||
|
return () => window.removeEventListener('message', responsiveModeChangesHandler);
|
||||||
|
}, [previewOption.widthDimension]);
|
||||||
|
|
||||||
|
if (!previewOption) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabled = !previewOption.url;
|
||||||
|
|
||||||
|
const classNames = [];
|
||||||
|
if (!disabled && active) {
|
||||||
|
classNames.push('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
classNames.push('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpacityChange = (e) => {
|
||||||
|
setOpacity(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<PreviewButtonStyle onClick={() => toggle()} className={classNames.join(' ')} disabled={disabled}/>
|
||||||
|
|
||||||
|
{active && !disabled &&
|
||||||
|
<>
|
||||||
|
<input type="range" value={opacity} min="0" max="100" onChange={handleOpacityChange}/>
|
||||||
|
<PreviewStyle onMouseUp={e => stop(e)} style={{opacity: opacity / 100}}>
|
||||||
|
<img src={previewOption.url} alt="" ref={reference}
|
||||||
|
onMouseDown={e => start(e)}
|
||||||
|
draggable={false}
|
||||||
|
style={{top: position.y + 'px', left: position.x + 'px'}}
|
||||||
|
/>
|
||||||
|
</PreviewStyle>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
function start(e) {
|
||||||
|
startDragPosition.x = e.clientX;
|
||||||
|
startDragPosition.y = e.clientY;
|
||||||
|
|
||||||
|
document.body.addEventListener('mousemove', handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(e) {
|
||||||
|
const target = reference.current;
|
||||||
|
updatePosition({x: target.offsetLeft, y: target.offsetTop});
|
||||||
|
|
||||||
|
document.body.removeEventListener('mousemove', handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handler(e) {
|
||||||
|
const target = reference.current;
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
const updatedPosition = {
|
||||||
|
x: target.offsetLeft - (startDragPosition.x - e.clientX),
|
||||||
|
y: target.offsetTop - (startDragPosition.y - e.clientY),
|
||||||
|
};
|
||||||
|
|
||||||
|
target.style.left = updatedPosition.x + 'px';
|
||||||
|
target.style.top = updatedPosition.y + 'px';
|
||||||
|
|
||||||
|
startDragPosition.x = e.clientX;
|
||||||
|
startDragPosition.y = e.clientY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
setActive(!active);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePosition({x, y}, relative = false) {
|
||||||
|
x = typeof x === 'undefined' ? position.x : relative ? position.x + x : x;
|
||||||
|
y = typeof y === 'undefined' ? position.y : relative ? position.y + y : y;
|
||||||
|
|
||||||
|
setPosition({x, y})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInitialXPosition(previewOption) {
|
||||||
|
if (!previewOption) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollbarOffset = 7;
|
||||||
|
return window.innerWidth / 2 - previewOption.widthDimension / 2 - scrollbarOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function responsiveModeChangesHandler(e) {
|
||||||
|
if (e.data !== 'responsiveUpdate') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.responsiveState.breakpoint === '100%') {
|
||||||
|
updatePosition({x: getInitialXPosition(previewOption), y: 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
export const SidebarStyle = styled.div`
|
||||||
|
--sidebarWidth: 360px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: calc(var(--sidebarWidth) * -1);
|
||||||
|
width: var(--sidebarWidth);
|
||||||
|
background-color: #E2E8F0;
|
||||||
|
border-right: 1px solid #CBD5E0;
|
||||||
|
padding: 0 0.75rem 0.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: left .2s ease-in-out, visibility .2s ease-in-out;
|
||||||
|
visibility: hidden;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
left: 0;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, textarea {
|
||||||
|
height: 100%;
|
||||||
|
//min-height: 480px;
|
||||||
|
resize: vertical;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: #EDF2F7;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #333;
|
||||||
|
border: 1px solid #cbd5e0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
display: block;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.alert--error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SidebarHeaderStyle = styled.header`
|
||||||
|
width: 100%;
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SidebarButtonToggleStyle = styled.button`
|
||||||
|
--size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-json.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center center;
|
||||||
|
background-color: initial;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SidebarCloseButtonStyle = styled.button`
|
||||||
|
--size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-close.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center center;
|
||||||
|
background-color: initial;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SidebarDataOptionsStyle = styled.div`
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
select {
|
||||||
|
flex: 1 1;
|
||||||
|
display: block;
|
||||||
|
appearance: none;
|
||||||
|
border: 1px solid #cbd5e0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
background-color: #edf2f7;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-dropdown-arrow.svg");
|
||||||
|
background-position: right 0.75rem center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
export const PreviewButtonStyle = styled.button`
|
||||||
|
--size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-preview.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center center;
|
||||||
|
background-color: initial;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-preview-disabled.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.25;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PreviewStyle = styled.div`
|
||||||
|
position: fixed;
|
||||||
|
top: calc(var(--top_panel_height) - 2px);
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 14px; // Scrollbars
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
cursor: move;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PreviewImageStyle = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: top;
|
||||||
|
cursor: move;
|
||||||
|
`;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M12.45 38.7 9.3 35.55 20.85 24 9.3 12.5l3.15-3.2L24 20.8 35.55 9.3l3.15 3.2L27.2 24l11.5 11.55-3.15 3.15L24 27.2Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 218 B |
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M15 20H9" stroke="url(#paint0_linear_17102_17357)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path
|
||||||
|
d="M19 5H5C4.44772 5 4 5.44772 4 6V16C4 16.5523 4.44772 17 5 17H19C19.5523 17 20 16.5523 20 16V6C20 5.44772 19.5523 5 19 5Z"
|
||||||
|
stroke="url(#paint1_linear_17102_17357)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17357" x1="9" y1="21" x2="11.3077" y2="17.5385"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17357" x1="4" y1="17" x2="22" y2="11" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 970 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
|
||||||
|
<path id="XMLID_225_" d="M325.607,79.393c-5.857-5.857-15.355-5.858-21.213,0.001l-139.39,139.393L25.607,79.393
|
||||||
|
c-5.857-5.857-15.355-5.858-21.213,0.001c-5.858,5.858-5.858,15.355,0,21.213l150.004,150c2.813,2.813,6.628,4.393,10.606,4.393
|
||||||
|
s7.794-1.581,10.606-4.394l149.996-150C331.465,94.749,331.465,85.251,325.607,79.393z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M28.6 40.75V36.2h4.75q1.2 0 2.05-.825.85-.825.85-2.075v-3.85q0-1.8 1.075-3.225T40.15 24.2v-.4q-1.75-.55-2.825-2-1.075-1.45-1.075-3.3v-3.85q0-1.2-.85-2.05-.85-.85-2.05-.85H28.6V7.2h5.95q2.6 0 4.425 1.825Q40.8 10.85 40.8 13.5v3.85q0 1.2.825 2.05.825.85 2.075.85h1.1v7.5h-1.1q-1.25 0-2.075.85-.825.85-.825 2.05v3.85q0 2.65-1.825 4.45-1.825 1.8-4.425 1.8Zm-15.1 0q-2.7 0-4.475-1.8-1.775-1.8-1.775-4.45v-3.85q0-1.2-.85-2.05-.85-.85-2.05-.85h-1.1v-7.5h1.1q1.2 0 2.05-.85.85-.85.85-2.05V13.5q0-2.65 1.8-4.475Q10.85 7.2 13.5 7.2h5.95v4.55H14.7q-1.25 0-2.075.85-.825.85-.825 2.05v3.85q0 1.85-1.1 3.3-1.1 1.45-2.85 2v.4q1.75.6 2.85 2.025 1.1 1.425 1.1 3.225v3.85q0 1.25.825 2.075.825.825 2.075.825h4.75v4.55Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 803 B |
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M16 3H8C7.44772 3 7 3.44772 7 4V20C7 20.5523 7.44772 21 8 21H16C16.5523 21 17 20.5523 17 20V4C17 3.44772 16.5523 3 16 3Z"
|
||||||
|
stroke="url(#paint0_linear_17102_17362)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 18H12.002V18.002H12V18Z" stroke="url(#paint1_linear_17102_17362)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17362" x1="7" y1="21" x2="19.2634" y2="19.2967"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17362" x1="12" y1="18.002" x2="12.0024" y2="18.0014"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M9.5 43.05q-1.85 0-3.2-1.35t-1.35-3.2v-29q0-1.9 1.35-3.25T9.5 4.9h13.35v4.6H9.5v29h29V25.15h4.6V38.5q0 1.85-1.35 3.2t-3.25 1.35ZM20.3 30.9l-3.15-3.2 18.2-18.2h-9.5V4.9H43.1v17.25h-4.6V12.7Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 294 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#21252d">
|
||||||
|
<path
|
||||||
|
d="M24 45.25q-4.35 0-8.225-1.675T9 39q-2.9-2.9-4.575-6.775Q2.75 28.35 2.75 24q0-4.5 1.675-8.375t4.625-6.75Q12 6 15.95 4.35q3.95-1.65 8.4-1.65 4.2 0 8 1.425t6.675 3.95Q41.9 10.6 43.6 14.05q1.7 3.45 1.7 7.5 0 5.7-3.2 9.35-3.2 3.65-8.9 3.65h-2.9q-.8 0-1.4.65-.6.65-.6 1.45 0 1.25.5 1.75t.5 1.55q0 2.2-1.5 3.75-1.5 1.55-3.8 1.55ZM24 24Zm-11.2 1.3q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1 0-1.75.75t-.75 1.75q0 1 .75 1.75t1.75.75Zm6.1-8.15q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm10.25 0q1 0 1.75-.75t.75-1.75q0-1.05-.75-1.775-.75-.725-1.75-.725-1.05 0-1.775.725-.725.725-.725 1.725 0 1.05.725 1.8t1.775.75Zm6.2 8.15q1 0 1.75-.75t.75-1.75q0-1-.75-1.75t-1.75-.75q-1.05 0-1.775.75-.725.75-.725 1.75t.725 1.75q.725.75 1.775.75ZM23.8 40.55q.4 0 .6-.175.2-.175.2-.625 0-.7-.75-1.175-.75-.475-.75-2.375 0-2.45 1.65-4.4 1.65-1.95 4.1-1.95h4.35q3.7 0 5.525-2.2 1.825-2.2 1.825-5.8 0-6.6-4.925-10.5Q30.7 7.45 24.4 7.45q-7.1 0-12.025 4.825Q7.45 17.1 7.45 24q0 6.9 4.8 11.725 4.8 4.825 11.55 4.825Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="m31.7 26.4-3.85-3.8q1.05-2.2-.625-3.45t-3.125-.3l-3.5-3.55q.75-.4 1.65-.6.9-.2 1.75-.2 3.55 0 6.025 2.475Q32.5 19.45 32.5 23q0 .8-.2 1.8t-.6 1.6Zm7.4 7.5-2.75-2.8q2.2-1.75 3.825-3.85T42.8 23q-2.6-5.5-7.525-8.75Q30.35 11 24.5 11q-2.1 0-4.1.35-2 .35-2.95.8l-3.3-3.3q1.75-.8 4.625-1.4 2.875-.6 5.475-.6 7.35 0 13.6 4.25t9.3 11.9q-1.25 3.3-3.375 6.075Q41.65 31.85 39.1 33.9Zm1.25 11.35-7.7-7.6q-1.75.7-4 1.1-2.25.4-4.65.4-7.55 0-13.825-4.275Q3.9 30.6.85 23q.9-2.55 2.75-5.15 1.85-2.6 4.3-5L1.75 6.8l2.5-2.6L42.7 42.65ZM10.8 15.7Q9 17.15 7.5 19.125 6 21.1 5.2 23q2.6 5.55 7.65 8.775Q17.9 35 24.4 35q1.35 0 2.775-.15 1.425-.15 2.225-.55l-3.2-3.2q-.4.2-1.025.3-.625.1-1.175.1-3.5 0-6-2.45T15.5 23q0-.55.075-1.15.075-.6.225-1.05Zm16.15 6.4Zm-6.85 3.45Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 825 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M24 31.5q3.55 0 6.025-2.475Q32.5 26.55 32.5 23q0-3.55-2.475-6.025Q27.55 14.5 24 14.5q-3.55 0-6.025 2.475Q15.5 19.45 15.5 23q0 3.55 2.475 6.025Q20.45 31.5 24 31.5Zm0-3.7q-2 0-3.4-1.4T19.2 23q0-2 1.4-3.4t3.4-1.4q2 0 3.4 1.4t1.4 3.4q0 2-1.4 3.4T24 27.8Zm0 11.35q-7.7 0-13.9-4.5T.85 23q3.05-7.15 9.25-11.65T24 6.85q7.7 0 13.9 4.5T47.15 23q-3.05 7.15-9.25 11.65T24 39.15ZM24 23Zm0 12q6 0 11.05-3.275Q40.1 28.45 42.75 23q-2.65-5.45-7.675-8.725Q30.05 11 24 11q-6 0-11.05 3.275Q7.9 17.55 5.2 23q2.7 5.45 7.725 8.725Q17.95 35 24 35Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 604 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
|
||||||
|
<path
|
||||||
|
d="M22.55 42.65q-6.45-.55-10.85-5.3Q7.3 32.6 7.3 26.1q0-3.95 1.775-7.4t4.975-5.75l2.8 2.85q-2.6 1.6-4.1 4.375-1.5 2.775-1.5 5.925 0 4.9 3.225 8.45 3.225 3.55 8.075 4.1Zm3 0v-4q4.9-.6 8.075-4.125Q36.8 31 36.8 26.1q0-5.2-3.55-8.875T24.5 13.35h-1.1l3.15 3.15-2.4 2.4-7.65-7.65 7.65-7.7 2.4 2.4-3.45 3.4h1.1q6.95 0 11.775 4.9T40.8 26.1q0 6.5-4.4 11.25t-10.85 5.3Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 446 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="478" height="506" viewBox="0 0 478 506" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M379.8 315.4C352.01 315.4 327.062 327.817 310.146 347.315L186.499 278.249C188.838 270.119 190.1 261.521 190.1 252.7C190.1 243.782 188.834 235.223 186.427 227.037L309.923 158.096C326.774 177.698 351.821 190.2 379.7 190.2C430.409 190.2 471.8 148.918 471.8 98.1C471.8 47.2863 430.514 6 379.7 6C328.886 6 287.6 47.2863 287.6 98.1C287.6 106.995 288.859 115.615 291.265 123.769L167.886 192.697C151.025 173.006 125.968 160.6 98.1 160.6C47.3908 160.6 6 201.882 6 252.7C6 303.523 47.3954 344.8 98.2 344.8C126.093 344.8 151.144 332.287 168.074 312.598L291.571 381.633C289.143 389.853 287.8 398.557 287.8 407.5C287.8 458.209 329.082 499.6 379.9 499.6C430.714 499.6 472 458.314 472 407.5C472 356.677 430.605 315.4 379.8 315.4ZM379.8 45.1C409.086 45.1 432.9 68.9137 432.9 98.2C432.9 127.486 409.086 151.3 379.8 151.3C350.514 151.3 326.7 127.486 326.7 98.2C326.7 68.9203 350.607 45.1 379.8 45.1ZM98.2 305.8C68.9137 305.8 45.1 281.986 45.1 252.7C45.1 223.414 68.9137 199.6 98.2 199.6C127.486 199.6 151.3 223.414 151.3 252.7C151.3 281.98 127.393 305.8 98.2 305.8ZM379.8 460.5C350.514 460.5 326.7 436.686 326.7 407.4C326.7 378.114 350.514 354.3 379.8 354.3C409.086 354.3 432.9 378.114 432.9 407.4C432.9 436.686 409.086 460.5 379.8 460.5Z" fill="#21252D" stroke="#21252D" stroke-width="12"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M20 3H4C3.44772 3 3 3.44772 3 4V20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20V4C21 3.44772 20.5523 3 20 3Z"
|
||||||
|
stroke="url(#paint0_linear_17102_17360)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 18H12.002V18.002H12V18Z" stroke="url(#paint1_linear_17102_17360)" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17102_17360" x1="3" y1="21" x2="24.1765" y2="15.7059"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17102_17360" x1="12" y1="18.002" x2="12.0024" y2="18.0014"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#14181F"/>
|
||||||
|
<stop offset="1" stop-color="#272A31"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,52 @@
|
|||||||
|
import React, {useState} from 'react';
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
|
function Publish(props = {}) {
|
||||||
|
const [state, setState] = useState({loading: false});
|
||||||
|
const updateState = (update) => setState(Object.assign({}, state, update));
|
||||||
|
|
||||||
|
if (window.devTool.publicUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
{state.loading &&
|
||||||
|
<div className="overlay overlay--loading">Loading, Please wait...</div>
|
||||||
|
}
|
||||||
|
<button onClick={submit} disabled={state.loading} className="btn btn--primary">Publish</button>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
const ready = confirm('Are you ready to submit the code?');
|
||||||
|
if (!ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState({loading: true});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/publish`);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
alert('Your code is successfully sent to project manager! Thank you!');
|
||||||
|
} else {
|
||||||
|
alert('Can\'t send your code, please try again or contact project manager.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Something went wrong, please try again or contact project manager.');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState({loading: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupPublish(rootAttributes) {
|
||||||
|
// INIT
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.setAttribute('id', 'publish-btn');
|
||||||
|
document.querySelector('.page_toolbar__right').append(wrapper)
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(wrapper);
|
||||||
|
const html = (<Publish rootAttributes={rootAttributes}/>);
|
||||||
|
root.render(html);
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import {ButtonStyling, ResponsiveButtonStyle, ResponsiveOptionsDropdown} from "./responsive-button.style.js";
|
||||||
|
|
||||||
|
const responsiveOptions = {
|
||||||
|
desktop: [1920, 1800, 1680, 1440, 1360, 1280, 1024],
|
||||||
|
tablet: [992, 768, 600],
|
||||||
|
mobile: [480, 414, 375, 360, 320],
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultResponsiveOptions = {
|
||||||
|
reset: '100%',
|
||||||
|
desktop: responsiveOptions.desktop[0],
|
||||||
|
tablet: responsiveOptions.tablet[1],
|
||||||
|
mobile: responsiveOptions.mobile[2],
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResponsiveButton({mode, active, onSelect}) {
|
||||||
|
const [state, setState] = useState({open: true, activeBreakpoint: defaultResponsiveOptions[mode]});
|
||||||
|
const updateState = (update) => setState(Object.assign({}, state, update));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const closeDropdown = (e) => isEscHit(e) ? updateState({open: false}) : null;
|
||||||
|
|
||||||
|
if (state.open) {
|
||||||
|
document.addEventListener("keydown", closeDropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe from ESC listener.
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", closeDropdown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Blur event / Outside click
|
||||||
|
const handleBlur = async (e) => await isClickOutside(e) ? updateState({open: false}) : null;
|
||||||
|
|
||||||
|
return <ResponsiveButtonStyle tabIndex='0' onBlur={handleBlur}>
|
||||||
|
<ButtonStyling data-mode={mode} data-active={active} onClick={() => select()}>{mode}</ButtonStyling>
|
||||||
|
{active && state.open && responsiveOptions[mode] &&
|
||||||
|
<ResponsiveOptionsDropdown>
|
||||||
|
{responsiveOptions[mode].map((breakpoint) => {
|
||||||
|
return <li>
|
||||||
|
<a className={state.activeBreakpoint === breakpoint ? 'active' : ''} onClick={() => select(breakpoint)}>{breakpoint}</a>
|
||||||
|
</li>;
|
||||||
|
})}
|
||||||
|
</ResponsiveOptionsDropdown>
|
||||||
|
}
|
||||||
|
</ResponsiveButtonStyle>;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Actions
|
||||||
|
//
|
||||||
|
|
||||||
|
function select(activeBreakpoint = null) {
|
||||||
|
// Click on option in Dropdown.
|
||||||
|
if (activeBreakpoint) {
|
||||||
|
updateState({open: false, activeBreakpoint});
|
||||||
|
onSelect(activeBreakpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click on device button.
|
||||||
|
if (!active) {
|
||||||
|
onSelect(state.activeBreakpoint)
|
||||||
|
updateState({open: false});
|
||||||
|
} else {
|
||||||
|
updateState({open: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(state, update) {
|
||||||
|
return Object.assign({}, state, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isClickOutside(e) {
|
||||||
|
const currentTarget = e.currentTarget;
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => resolve(!currentTarget.contains(document.activeElement)));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEscHit(event) {
|
||||||
|
return event.key === 'Escape'
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
export const ButtonStyling = styled.button`
|
||||||
|
--size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-desktop.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center;
|
||||||
|
background-color: initial;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&[data-mode='tablet'] {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-tablet.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-mode='mobile'] {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-mobile.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-mode='reset'] {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-reset.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-active='true'] {
|
||||||
|
background-color: #CBD5E0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ResponsiveOptionsDropdown = styled.ul`
|
||||||
|
list-style: none;
|
||||||
|
padding: 4px;
|
||||||
|
position: absolute;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||||
|
box-shadow: 2px 2px 4px 0 #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
left: 50%;
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #14181F;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: #EDF2F7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #cbd5e0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ResponsiveButtonStyle = styled.div`
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// export function connectResponsiveness(rootAttributes) {
|
||||||
|
// // API
|
||||||
|
// return {
|
||||||
|
// selectMode: (mode) => selectMode(mode),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
import {WrapperStyling} from "./responsive.style.js";
|
||||||
|
import {ResponsiveButton} from "./responsive-button/ResponsiveButton.jsx";
|
||||||
|
|
||||||
|
const modes = [
|
||||||
|
'reset',
|
||||||
|
'desktop',
|
||||||
|
'tablet',
|
||||||
|
'mobile'
|
||||||
|
];
|
||||||
|
|
||||||
|
function Responsive(props = {}) {
|
||||||
|
props.rootAttributes = props.rootAttributes ?? {};
|
||||||
|
|
||||||
|
const initialState = {mode: 'default', breakpoint: '100%'}
|
||||||
|
const [mode, setMode] = useState(initialState.mode);
|
||||||
|
const [breakpoint, setBreakpoint] = useState(initialState.breakpoint);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateController();
|
||||||
|
}, [mode, breakpoint]);
|
||||||
|
|
||||||
|
const previewFrame = props.rootAttributes.previewFrame;
|
||||||
|
return render();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
return <WrapperStyling>
|
||||||
|
{modes.map((mode) => {
|
||||||
|
return <ResponsiveButton mode={mode}
|
||||||
|
active={isActive(mode)}
|
||||||
|
onSelect={(breakpoint) => selectMode(mode, breakpoint)}/>
|
||||||
|
})}
|
||||||
|
</WrapperStyling>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectMode(mode, breakpoint) {
|
||||||
|
if (mode === 'reset') {
|
||||||
|
setMode(initialState.mode);
|
||||||
|
setBreakpoint(initialState.breakpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMode(mode);
|
||||||
|
setBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isActive(requestedMode) {
|
||||||
|
return mode === requestedMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateController() {
|
||||||
|
let frameBreakpoint = breakpoint;
|
||||||
|
if (typeof frameBreakpoint !== 'string') {
|
||||||
|
const scrollbarWidth = 15;
|
||||||
|
frameBreakpoint = (scrollbarWidth + frameBreakpoint) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
previewFrame.style.setProperty('--breakpoint', frameBreakpoint);
|
||||||
|
previewFrame.classList.add('has-breakpoint');
|
||||||
|
window.postMessage('responsiveUpdate');
|
||||||
|
window.responsiveState = {mode, breakpoint};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupResponsiveness(rootAttributes) {
|
||||||
|
// INIT
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
document.querySelector('.page_toolbar__middle').prepend(wrapper)
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(wrapper);
|
||||||
|
const html = (<Responsive rootAttributes={rootAttributes}/>);
|
||||||
|
root.render(html);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
export const WrapperStyling = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
`;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/head") }}
|
||||||
|
|
||||||
|
<body class="{{#if iframeMode}}body--iframe{{/if}}">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<section class="fullscreen_layout"></section>
|
||||||
|
<div id="hbs-container"></div>
|
||||||
|
<section class="fullscreen_layout"></section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> (include_partial "layouts/partials/scripts") }}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
.btn {
|
||||||
|
--btn-color: #333;
|
||||||
|
--btn-background-color: white;
|
||||||
|
display: inline-block;
|
||||||
|
padding: calc(0.375rem - 2px) 0.75rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--btn-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
border: 1px solid var(--btn-background-color);
|
||||||
|
background-color: var(--btn-background-color);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.75;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
--btn-color: white;
|
||||||
|
--btn-background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--secondary {
|
||||||
|
border-color: #cbd5e0;
|
||||||
|
--btn-color: #333;
|
||||||
|
--btn-background-color: #edf2f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
.preview {
|
||||||
|
height: calc(100% - var(--top_panel_height));
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview_frame {
|
||||||
|
display: block;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
min-height: 100%;
|
||||||
|
//height: 100%;
|
||||||
|
|
||||||
|
--top_spacing: 0px;
|
||||||
|
--breakpoint_top_spacing: 30px;
|
||||||
|
|
||||||
|
margin-top: var(--top_spacing);
|
||||||
|
background-color: white;
|
||||||
|
border: 0;
|
||||||
|
transition: max-width .3s ease-in-out, width .3s ease-in-out, margin-top .3s ease-in-out;
|
||||||
|
|
||||||
|
&.has-breakpoint {
|
||||||
|
--breakpoint: 100%;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,28 +1,90 @@
|
|||||||
|
:root {
|
||||||
|
--top_panel_vertical_height: 0.5rem;
|
||||||
|
--top_panel_height: calc(36px + var(--top_panel_vertical_height) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
background-color: #F7FAFC;
|
||||||
|
}
|
||||||
|
body.view-mode #publish-btn {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
header.page_toolbar {
|
.btn {
|
||||||
|
--btn-color: #333;
|
||||||
|
--btn-background-color: white;
|
||||||
|
display: inline-block;
|
||||||
|
padding: calc(0.375rem - 2px) 0.75rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--btn-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
border: 1px solid var(--btn-background-color);
|
||||||
|
background-color: var(--btn-background-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.btn:disabled {
|
||||||
|
opacity: 0.75;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.btn--primary {
|
||||||
|
--btn-color: white;
|
||||||
|
--btn-background-color: #333;
|
||||||
|
}
|
||||||
|
.btn--secondary {
|
||||||
|
border-color: #cbd5e0;
|
||||||
|
--btn-color: #333;
|
||||||
|
--btn-background-color: #edf2f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page_toolbar {
|
||||||
--gap: 2.5rem;
|
--gap: 2.5rem;
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
background-color: #ccc;
|
background-color: #EDF2F7;
|
||||||
padding: 1rem;
|
padding: var(--top_panel_vertical_height) 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.page_toolbar__middle {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: var(--gap);
|
gap: var(--gap);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
}
|
||||||
|
.page_toolbar__middle > div {
|
||||||
header.page_toolbar > div {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@media (max-width: 1024px) {
|
||||||
header.page_toolbar > div:not(:first-child):after {
|
.page_toolbar__middle > div {
|
||||||
content: '|';
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page_toolbar__middle > div:not(:first-child):after {
|
||||||
|
content: "|";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: calc(var(--gap) / 2 * -1);
|
left: calc(var(--gap) / 2 * -1);
|
||||||
top: 40%;
|
top: 40%;
|
||||||
@@ -30,18 +92,79 @@ header.page_toolbar > div:not(:first-child):after {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
.page_toolbar__middle > div select {
|
||||||
header.page_toolbar select {
|
font-size: 0.85em;
|
||||||
font-size: 0.85rem;
|
margin: 0;
|
||||||
padding: 0.25rem;
|
display: inline-block;
|
||||||
|
border-radius: initial;
|
||||||
|
min-width: initial;
|
||||||
|
width: initial !important;
|
||||||
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
header.page_toolbar > div {
|
.page_toolbar__middle__data_options {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header.page_toolbar .page_toolbar__data_options {
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page_toolbar__left, .page_toolbar__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
min-width: 480px;
|
||||||
|
}
|
||||||
|
.page_toolbar__right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
height: calc(100% - var(--top_panel_height));
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview_frame {
|
||||||
|
display: block;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
min-height: 100%;
|
||||||
|
--top_spacing: 0px;
|
||||||
|
--breakpoint_top_spacing: 30px;
|
||||||
|
margin-top: var(--top_spacing);
|
||||||
|
background-color: white;
|
||||||
|
border: 0;
|
||||||
|
transition: max-width 0.3s ease-in-out, width 0.3s ease-in-out, margin-top 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
#preview_frame.has-breakpoint {
|
||||||
|
--breakpoint: 100%;
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.open_in_new_tab, .share, .palette {
|
||||||
|
--size: 1.5rem;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-open-new-tab.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center center;
|
||||||
|
background-color: initial;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.palette {
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-palette.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.share {
|
||||||
|
background-size: calc(var(--size) - 0.35rem);
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-share.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=page--main.css.map */
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["page--main.scss","_buttons.scss","_overlay.scss","_page--preview.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EAEA;;AAGE;EACE;;;ACbN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;;AC5BJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AFUF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;AAKN;EAEE;EACA;EACA;EACA;;AAGF;EACE;;;AGxFJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EACA;EACA;;AAEA;EACE;EAEA;EACA;;;AHuEJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;;;AAGF;EAEE;EACA","file":"page--main.css"}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
:root {
|
||||||
|
--top_panel_vertical_height: 0.5rem;
|
||||||
|
--top_panel_height: calc(36px + var(--top_panel_vertical_height) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
//overflow: none;
|
||||||
|
background-color: #F7FAFC;
|
||||||
|
|
||||||
|
&.view-mode {
|
||||||
|
#publish-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "buttons";
|
||||||
|
@import "overlay";
|
||||||
|
|
||||||
|
.page_toolbar {
|
||||||
|
--gap: 2.5rem;
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #EDF2F7;
|
||||||
|
padding: var(--top_panel_vertical_height) 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&__middle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--gap);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:first-child):after {
|
||||||
|
content: '|';
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--gap) / 2 * -1);
|
||||||
|
top: 40%;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
font-size: 0.85em;
|
||||||
|
margin: 0;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: initial;
|
||||||
|
min-width: initial;
|
||||||
|
width: initial !important;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__data_options {
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left,
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
min-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "page--preview";
|
||||||
|
|
||||||
|
.open_in_new_tab {
|
||||||
|
--size: 1.5rem;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-open-new-tab.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(var(--size) - 0.15rem);
|
||||||
|
background-position: center center;
|
||||||
|
background-color: initial;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
font-size: 1px;
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
line-height: 1;
|
||||||
|
display: block;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.palette {
|
||||||
|
@extend .open_in_new_tab;
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-palette.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.share {
|
||||||
|
@extend .open_in_new_tab;
|
||||||
|
background-size: calc(var(--size) - 0.35rem);
|
||||||
|
background-image: url("/scripts/dist/toolbar/images/icon-share.svg");
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--iframe {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen_layout {
|
||||||
|
background-color: #9cc3ff;
|
||||||
|
min-height: 100%;
|
||||||
|
background-image: url("https://i.ibb.co/pjwL8D1/shapelined-JBKdviwe-XI-unsplash.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=page--view.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["page--view.scss"],"names":[],"mappings":"AAAA;EACE;;;AAIF;EACE;;;AAGF;EACE;EACA;EACA;EACA","file":"page--view.css"}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iFrame mode
|
||||||
|
.body--iframe {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen_layout {
|
||||||
|
background-color: #9cc3ff;
|
||||||
|
min-height: 100%;
|
||||||
|
background-image: url('https://i.ibb.co/pjwL8D1/shapelined-JBKdviwe-XI-unsplash.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Block Development Tool</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="screen">Loading...</div>
|
||||||
|
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script src="/scripts/dist/sync.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,17 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "create-block-dev-tool",
|
"name": "@axe-web/block-dev-tool",
|
||||||
"version": "1.0.2",
|
"version": "1.0.31",
|
||||||
|
"author": {
|
||||||
|
"name": "AXE-WEB",
|
||||||
|
"email": "office@axe-web.com",
|
||||||
|
"url": "https://axe-web.com/"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "component-dev",
|
"info": "node debug.js",
|
||||||
"dev": "NODE_ENV=development node server.js",
|
"dev": "node server.js",
|
||||||
"generate-block": "yo ./generators/block/index.cjs"
|
"view-mode": "VIEW_MODE=true node server.js",
|
||||||
|
"build-platform": "BLOCK_NAME=swiper-test MODULE_PATH= node ./build.js",
|
||||||
|
"dev-dev-tool": "rollup --config rollup.config.js --watch",
|
||||||
|
"build-dev-tool": "rollup --config rollup.config.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17.3"
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"main": "server.js",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@axe-web/create-block": "^1.1.30",
|
||||||
|
"@braintree/sanitize-url": "^6.0.0",
|
||||||
|
"archiver": "^5.3.1",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
"browser-sync": "^2.27.9",
|
"browser-sync": "^2.27.9",
|
||||||
"config": "^3.3.7",
|
"config": "^3.3.7",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"exec-php": "^0.0.6",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-handlebars": "^6.0.4",
|
"express-handlebars": "^6.0.4",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
@@ -21,11 +37,50 @@
|
|||||||
"gulp-sass": "^5.1.0",
|
"gulp-sass": "^5.1.0",
|
||||||
"gulp-sourcemaps": "^3.0.0",
|
"gulp-sourcemaps": "^3.0.0",
|
||||||
"gulp-uglify": "^3.0.2",
|
"gulp-uglify": "^3.0.2",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mem-fs": "^2.2.1",
|
||||||
|
"mem-fs-editor": "^9.5.0",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
|
"ngrok": "^5.0.0-beta.2",
|
||||||
|
"node-fetch": "^3.2.10",
|
||||||
|
"open": "^8.4.0",
|
||||||
|
"plugin-error": "^2.0.0",
|
||||||
|
"prompts": "^2.4.2",
|
||||||
|
"sanitize-html": "^2.7.1",
|
||||||
"sass": "^1.50.1",
|
"sass": "^1.50.1",
|
||||||
"yeoman-generator": "^5.6.1",
|
"ws": "^8.13.0"
|
||||||
"yo": "^4.3.0"
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/preset-react": "^7.18.6",
|
||||||
|
"@modular-css/rollup": "^28.2.2",
|
||||||
|
"@rollup/plugin-babel": "^5.3.1",
|
||||||
|
"@rollup/plugin-commonjs": "^22.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"rollup": "^2.77.2",
|
||||||
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
|
"rollup-plugin-jsx": "^1.0.3",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"socket.io": "^4.6.2",
|
||||||
|
"styled-components": "^5.3.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"component-dev": "./server.js"
|
"component-dev": "./server.js",
|
||||||
}
|
"component-build": "./build.js",
|
||||||
|
"component-info": "./debug.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"env.js",
|
||||||
|
"helpers.js",
|
||||||
|
"debug.js",
|
||||||
|
"layouts/**/*.hbs",
|
||||||
|
"layouts/styles/*.css*",
|
||||||
|
"layouts/scripts/dist/*.js*",
|
||||||
|
"layouts/scripts/dist/**/*.jpg",
|
||||||
|
"layouts/scripts/dist/**/*.png",
|
||||||
|
"layouts/scripts/dist/**/*.svg",
|
||||||
|
"platforms/**/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,274 @@
|
|||||||
|
import path from "path";
|
||||||
|
import {readFile, writeFile, mkdir, copyFile} from "fs/promises";
|
||||||
|
import {capitalize, getConfigs} from "../../helpers.js";
|
||||||
|
|
||||||
|
export async function buildHubspotEmail(blockName) {
|
||||||
|
const distPath = await createDistFolder(blockName);
|
||||||
|
|
||||||
|
// Template
|
||||||
|
let handlebars = await readFile(path.join(srcPath, `${blockName}.template.hbs`), "utf8");
|
||||||
|
await writeFile(path.join(distPath, 'module.html'), handlebarsToHubl(handlebars));
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
await buildHubspotJSONFiles(distPath, {
|
||||||
|
global: false,
|
||||||
|
host_template_types: ["EMAIL"],
|
||||||
|
label: capitalize(blockName),
|
||||||
|
is_available_for_new_content: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createDistFolder(blockName, projectPath = '') {
|
||||||
|
const distPath = path.join(projectPath, 'exports', 'hubspot', `${blockName}.module`);
|
||||||
|
await mkdir(distPath, {recursive: true})
|
||||||
|
|
||||||
|
return distPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlockFields(block = {}, type = 'content') {
|
||||||
|
if (!block['field_groups']) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields_group = block['field_groups'].find((group) => group.name === type);
|
||||||
|
const fields = [];
|
||||||
|
|
||||||
|
if (!fields_group) {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(fields_group['fields']).forEach((key) => {
|
||||||
|
const field = fields_group['fields'][key];
|
||||||
|
field['name'] = key;
|
||||||
|
|
||||||
|
fields.push(field);
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields.map((field) => {
|
||||||
|
return convertToHubspotField(field);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertToHubspotField(field = {}) {
|
||||||
|
const data = {
|
||||||
|
id: field.name,
|
||||||
|
name: field.name,
|
||||||
|
label: field.label,
|
||||||
|
display_width: null,
|
||||||
|
validation_regex: "",
|
||||||
|
required: false,
|
||||||
|
locked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (field.default) {
|
||||||
|
data.default = field.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sub_fields = [];
|
||||||
|
|
||||||
|
switch (field.type) {
|
||||||
|
case 'text':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "text",
|
||||||
|
allow_new_line: true,
|
||||||
|
show_emoji_picker: false,
|
||||||
|
});
|
||||||
|
case 'wysiwyg':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "richtext"
|
||||||
|
});
|
||||||
|
case 'number':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "number",
|
||||||
|
display: "text",
|
||||||
|
step: 1,
|
||||||
|
});
|
||||||
|
case 'range':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "number",
|
||||||
|
display: "slider",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 3,
|
||||||
|
});
|
||||||
|
case 'boolean':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "boolean",
|
||||||
|
display: "toggle",
|
||||||
|
});
|
||||||
|
case 'checkbox':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "boolean",
|
||||||
|
display: "checkbox",
|
||||||
|
});
|
||||||
|
case 'select':
|
||||||
|
const options = [];
|
||||||
|
if (field.options) {
|
||||||
|
Object.keys(field.options).forEach(value => options.push([value, field.options[value]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "choice",
|
||||||
|
choices: options
|
||||||
|
});
|
||||||
|
case 'link':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "url",
|
||||||
|
supported_types: [
|
||||||
|
"EXTERNAL"
|
||||||
|
],
|
||||||
|
default: {
|
||||||
|
content_id: null,
|
||||||
|
href: "https://www.twitter.com/...",
|
||||||
|
type: "EXTERNAL"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case 'image':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "image",
|
||||||
|
responsive: true,
|
||||||
|
resizable: false,
|
||||||
|
show_loading: false,
|
||||||
|
default: {
|
||||||
|
src: "",
|
||||||
|
alt: null,
|
||||||
|
loading: "lazy"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case 'file':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "file",
|
||||||
|
picker: "file",
|
||||||
|
});
|
||||||
|
case 'stringList':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "text",
|
||||||
|
occurrence: {
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
sorting_label_field: null,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
allow_new_line: false,
|
||||||
|
show_emoji_picker: false,
|
||||||
|
});
|
||||||
|
case 'gallery':
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "image",
|
||||||
|
occurrence: {
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
sorting_label_field: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
resizable: false,
|
||||||
|
show_loading: false,
|
||||||
|
default: {
|
||||||
|
src: "",
|
||||||
|
alt: null,
|
||||||
|
loading: "lazy"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case 'group':
|
||||||
|
field.sub_fields = field.sub_fields || {};
|
||||||
|
sub_fields = Object.keys(field.sub_fields).map(name => {
|
||||||
|
// const sub_field = Object.assign({}, field.sub_fields[name], {name: `${field.name}_${name}`});
|
||||||
|
const sub_field = Object.assign({}, field.sub_fields[name], {name});
|
||||||
|
return convertToHubspotField(sub_field);
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "group",
|
||||||
|
children: sub_fields,
|
||||||
|
tab: "CONTENT",
|
||||||
|
expanded: false,
|
||||||
|
default: {}
|
||||||
|
});
|
||||||
|
case 'repeater':
|
||||||
|
field.sub_fields = field.sub_fields || {};
|
||||||
|
sub_fields = Object.keys(field.sub_fields).map(name => {
|
||||||
|
const sub_field = Object.assign({}, field.sub_fields[name], {name});
|
||||||
|
return convertToHubspotField(sub_field);
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "group",
|
||||||
|
children: sub_fields,
|
||||||
|
occurrence: {
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
sorting_label_field: null,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
tab: "CONTENT",
|
||||||
|
expanded: false,
|
||||||
|
default: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// case 'YOUR_FIELD':
|
||||||
|
// return Object.assign({}, data, {});
|
||||||
|
default:
|
||||||
|
// type === 'string'
|
||||||
|
return Object.assign({}, data, {
|
||||||
|
type: "text",
|
||||||
|
allow_new_line: false,
|
||||||
|
show_emoji_picker: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildHubspotJSONFiles(distPath, metaData) {
|
||||||
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
|
await writeFile(path.join(distPath, 'meta.json'), JSON.stringify(metaData, null, 4));
|
||||||
|
|
||||||
|
const blockJSON = await readFile(path.join(projectPath, 'block.json'), "utf8");
|
||||||
|
const block = JSON.parse(blockJSON);
|
||||||
|
|
||||||
|
const fields = getBlockFields(block, 'content');
|
||||||
|
|
||||||
|
// Styling TAB.
|
||||||
|
const stylingFields = getBlockFields(block, 'styling');
|
||||||
|
|
||||||
|
if (stylingFields.length) {
|
||||||
|
const stylingFieldsByName = {};
|
||||||
|
stylingFields.forEach(field => stylingFieldsByName[field.name] = field);
|
||||||
|
|
||||||
|
const stylingGroup = convertToHubspotField({
|
||||||
|
type: 'group',
|
||||||
|
name: 'style',
|
||||||
|
label: "Style",
|
||||||
|
});
|
||||||
|
|
||||||
|
stylingGroup.children = Object.values(stylingFieldsByName);
|
||||||
|
stylingGroup.tab = "STYLE";
|
||||||
|
|
||||||
|
fields.push(stylingGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export JSON file.
|
||||||
|
await writeFile(path.join(distPath, 'fields.json'), JSON.stringify(fields, null, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function handlebarsToHubl(handlebars) {
|
||||||
|
handlebars = handlebars.replace(/{{ else }}/g, '{% else %}');
|
||||||
|
handlebars = handlebars.replace(/{{else}}/g, '{% else %}');
|
||||||
|
handlebars = handlebars.replace(/{{#if /g, '{% if module.');
|
||||||
|
handlebars = handlebars.replace(/{{\/if}}/g, '{% endif %}');
|
||||||
|
handlebars = handlebars.replace(/{{#each /g, '{% for item in module.');
|
||||||
|
handlebars = handlebars.replace(/{{\/each}}/g, '{% endfor %}');
|
||||||
|
handlebars = handlebars.replace(/{{base_url}}/g, '');
|
||||||
|
handlebars = handlebars.replace(/{esc_attr /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{esc_url /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{esc_html /g, '{');
|
||||||
|
handlebars = handlebars.replace(/{{{ /g, '{{');
|
||||||
|
handlebars = handlebars.replace(/ }}}/g, '}}');
|
||||||
|
handlebars = handlebars.replace(/{{{/g, '{{');
|
||||||
|
handlebars = handlebars.replace(/}}}/g, '}}');
|
||||||
|
handlebars = handlebars.replace(/{{/g, '{{module.');
|
||||||
|
handlebars = handlebars.replace(/{{module. /g, '{{ module.');
|
||||||
|
handlebars = handlebars.replace(/.url/g, '.src');
|
||||||
|
|
||||||
|
return handlebars;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import path from "path";
|
||||||
|
import {copyFile, readFile, writeFile} from "fs/promises";
|
||||||
|
import {capitalize, getConfigs} from "../../helpers.js";
|
||||||
|
import {buildHubspotJSONFiles, createDistFolder, handlebarsToHubl,} from "./hubspot-email-adapter.js";
|
||||||
|
|
||||||
|
export async function buildHubspotPage(blockName) {
|
||||||
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
|
const distPath = await createDistFolder(blockName, projectPath);
|
||||||
|
|
||||||
|
const srcPath = path.join(projectPath, 'src');
|
||||||
|
|
||||||
|
// Template
|
||||||
|
let handlebars = await readFile(path.join(srcPath, `${blockName}.template.hbs`), "utf8");
|
||||||
|
await writeFile(path.join(distPath, 'module.html'), handlebarsToHubl(handlebars));
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
await copyFile(path.join(srcPath, 'styles', `${blockName}.min.css`), path.join(distPath, 'module.css'));
|
||||||
|
await copyFile(path.join(srcPath, 'scripts', `${blockName}.min.js`), path.join(distPath, 'module.js'));
|
||||||
|
// await copy(path.join(projectPath, 'src', 'images'), path.join(distPath, 'images'));
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
await buildHubspotJSONFiles(distPath, {
|
||||||
|
global: false,
|
||||||
|
host_template_types: ["PAGE"],
|
||||||
|
label: capitalize(blockName),
|
||||||
|
is_available_for_new_content: true
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Composer - Autoloader
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Brick\VarExporter\ExportException;
|
||||||
|
use LightnCandy\Flags;
|
||||||
|
use LightnCandy\LightnCandy;
|
||||||
|
use Brick\VarExporter\VarExporter;
|
||||||
|
|
||||||
|
trait Custom_Handlebars {
|
||||||
|
public array $custom_handlebars = [];
|
||||||
|
|
||||||
|
public function register_default_handlebar_helpers(): void {
|
||||||
|
$this->add_handlebar( 'esc_url', function ( $context ) {
|
||||||
|
if ( function_exists( 'esc_url' ) ) {
|
||||||
|
return esc_url( $context );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
} );
|
||||||
|
|
||||||
|
$this->add_handlebar( 'esc_attr', function ( $context ) {
|
||||||
|
if ( function_exists( 'esc_attr' ) ) {
|
||||||
|
return esc_attr( $context );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
} );
|
||||||
|
|
||||||
|
$this->add_handlebar( 'esc_html', function ( $context ) {
|
||||||
|
if ( function_exists( 'esc_html' ) ) {
|
||||||
|
return esc_html( $context );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
} );
|
||||||
|
|
||||||
|
$this->add_handlebar( 'safe_html', function ( $context ) {
|
||||||
|
if ( function_exists( 'wp_kses_post' ) ) {
|
||||||
|
return wp_kses_post( $context );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_handlebar( $key, $func ): void {
|
||||||
|
$this->custom_handlebars[ $key ] = $func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Component_Builder {
|
||||||
|
use Custom_Handlebars;
|
||||||
|
|
||||||
|
public string $component_name = '';
|
||||||
|
public string $module_path = '';
|
||||||
|
public string $project_path = '';
|
||||||
|
private string $dist_path = '';
|
||||||
|
|
||||||
|
function __construct( $args = [] ) {
|
||||||
|
if ( ! isset( $args['backPath'] ) || ! isset( $args['projectPath'] ) || ! isset( $args['blockName'] ) || ! isset( $args['platform'] ) ) {
|
||||||
|
throw new \Exception( 'Error: Missing arguments. Make sure all parameter passed.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->module_path = $args['backPath'];
|
||||||
|
$this->project_path = $args['projectPath'];
|
||||||
|
$this->component_name = $args['blockName'];
|
||||||
|
|
||||||
|
$this->dist_path = __DIR__ . '/' . $this->module_path . '/../../' . $this->project_path . '/exports' . '/' . $args['platform'];
|
||||||
|
$this->register_default_handlebar_helpers();
|
||||||
|
|
||||||
|
$this->add_handlebar( 'base_url', function ( $context ) {
|
||||||
|
$path = join( '/', [ $this->block_project, $this->block_name, 'templates' ] );
|
||||||
|
return join( '/', [ get_site_url(),'wp-content', 'axe-web-blocks', $path, '' ] );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
function build(): void {
|
||||||
|
$root_path = __DIR__ . '/' . $this->module_path . '/../../' . $this->project_path;
|
||||||
|
|
||||||
|
$this->buildTemplatePhpFile( $root_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildTemplatePhpFile( $root_path ): void {
|
||||||
|
$file_name = $this->get_handlebars_template( "$root_path/src/$this->component_name.template.hbs" );
|
||||||
|
|
||||||
|
$output_folder = $this->dist_path . '/templates';
|
||||||
|
rename( $file_name, "$output_folder/$this->component_name.template.php" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_handlebars_template( $path = '' ): string {
|
||||||
|
$template = file_get_contents( $path );
|
||||||
|
$phpStr = LightnCandy::compile( $template,
|
||||||
|
[
|
||||||
|
'flags' => Flags::FLAG_NOESCAPE | Flags::FLAG_PARENT | Flags::FLAG_SPVARS | Flags::FLAG_ELSE | Flags::FLAG_JSLENGTH | Flags::FLAG_JSTRUE | Flags::FLAG_THIS,
|
||||||
|
'helpers' => $this->custom_handlebars ?? [],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE:
|
||||||
|
* PHP 8.0.0 has problems with the LightCandy lib.
|
||||||
|
* If you're running the exact php8.0.0 version, try to downgrade to LightCandy@1.2.5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return self::create_cache_file( $path, $phpStr );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function create_cache_file( string $file_path, string $content ): string {
|
||||||
|
$file_path_parts = explode( ".", $file_path );
|
||||||
|
array_pop( $file_path_parts ); // remove ".hbs" format.
|
||||||
|
$file_path_parts[] = 'php';
|
||||||
|
$file_path = join( '.', $file_path_parts );
|
||||||
|
|
||||||
|
$comment = "
|
||||||
|
/**
|
||||||
|
* FILE INFO:
|
||||||
|
* This file was generated by LightCandy::compile function.
|
||||||
|
* The original source is the .HBS(handlebars) file that is located next to this generated PHP file.
|
||||||
|
*
|
||||||
|
* Compiled at " . date( 'Y-m-d h:i:s' ) . "
|
||||||
|
*/
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
|
$t = file_put_contents( $file_path, '<?php ' . $comment . $content . ' ?>' );
|
||||||
|
if ( $t === false ) {
|
||||||
|
throw new \Exception( "Error: Can't generate HBS template to PHP file. Cache folder is not accessible." );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions below will be triggered by JavaScript (index.js).
|
||||||
|
*/
|
||||||
|
|
||||||
|
function build( $args = [] ): void {
|
||||||
|
( new Component_Builder( $args ) )->build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ExportException
|
||||||
|
*/
|
||||||
|
function jsonToPhp( $args = [] ): ?string {
|
||||||
|
$json = $args['json'] ?? [];
|
||||||
|
|
||||||
|
return VarExporter::export( $json );
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0",
|
||||||
|
"zordius/lightncandy": "^1.2.6",
|
||||||
|
"brick/varexporter": "^0.3.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "6731e0a19d488ee457b6682e62ef9018",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "brick/varexporter",
|
||||||
|
"version": "0.3.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/brick/varexporter.git",
|
||||||
|
"reference": "3e263cd718d242594c52963760fee2059fd5833c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/brick/varexporter/zipball/3e263cd718d242594c52963760fee2059fd5833c",
|
||||||
|
"reference": "3e263cd718d242594c52963760fee2059fd5833c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"nikic/php-parser": "^4.0",
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.2",
|
||||||
|
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||||
|
"vimeo/psalm": "4.23.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Brick\\VarExporter\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()",
|
||||||
|
"keywords": [
|
||||||
|
"var_export"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/brick/varexporter/issues",
|
||||||
|
"source": "https://github.com/brick/varexporter/tree/0.3.7"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/BenMorel",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-06-29T23:37:57+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nikic/php-parser",
|
||||||
|
"version": "v4.15.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-tokenizer": "*",
|
||||||
|
"php": ">=7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ircmaxell/php-yacc": "^0.0.7",
|
||||||
|
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/php-parse"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpParser\\": "lib/PhpParser"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nikita Popov"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A PHP parser written in PHP",
|
||||||
|
"keywords": [
|
||||||
|
"parser",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
|
||||||
|
},
|
||||||
|
"time": "2022-11-12T15:38:23+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zordius/lightncandy",
|
||||||
|
"version": "v1.2.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/zordius/lightncandy.git",
|
||||||
|
"reference": "b451f73e8b5c73e62e365997ba3c993a0376b72a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/zordius/lightncandy/zipball/b451f73e8b5c73e62e365997ba3c993a0376b72a",
|
||||||
|
"reference": "b451f73e8b5c73e62e365997ba3c993a0376b72a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": ">=7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.2.5-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"LightnCandy\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Zordius Chen",
|
||||||
|
"email": "zordius@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ).",
|
||||||
|
"homepage": "https://github.com/zordius/lightncandy",
|
||||||
|
"keywords": [
|
||||||
|
"handlebars",
|
||||||
|
"logicless",
|
||||||
|
"mustache",
|
||||||
|
"php",
|
||||||
|
"template"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/zordius/lightncandy/issues",
|
||||||
|
"source": "https://github.com/zordius/lightncandy/tree/v1.2.6"
|
||||||
|
},
|
||||||
|
"time": "2021-07-11T04:52:41+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"platform-overrides": {
|
||||||
|
"php": "8.0.0"
|
||||||
|
},
|
||||||
|
"plugin-api-version": "2.1.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_Component <% if (isComponentManager || isElementor) { %>extends \Core\Component <% } %>{
|
||||||
|
const VERSION = '<%= version %>';
|
||||||
|
public $block_project = '<%= ownerFilename %>';
|
||||||
|
public $block_name = '<%= blockFilename %>';
|
||||||
|
public $hook_prefix = 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>';
|
||||||
|
|
||||||
|
<% if (!isComponentManager && !isElementor) { %>public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
// add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] );
|
||||||
|
add_action( 'after_setup_theme', [ $this, 'register_assets' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
<% } %>function register_assets(): void {
|
||||||
|
$base_path = plugin_dir_url( __FILE__ ); // In Plugins
|
||||||
|
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/'; // In Theme
|
||||||
|
|
||||||
|
$style_deps = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::style_deps', [ 'assets-style' ] );
|
||||||
|
wp_register_style( 'block-<%= blockFilename %>', $base_path . 'templates/styles/<%= blockFilename %>.min.css', $style_deps, self::VERSION );
|
||||||
|
|
||||||
|
$script_deps = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::script_deps', [ 'assets-script' ] );
|
||||||
|
wp_register_script( 'block-<%= blockFilename %>', $base_path . 'templates/scripts/<%= blockFilename %>.min.js', $script_deps, self::VERSION, true );<% if (!isElementor) { %>
|
||||||
|
|
||||||
|
if ( ! is_admin() ) {
|
||||||
|
wp_enqueue_style( 'block-<%= blockFilename %>' );
|
||||||
|
wp_enqueue_script( 'block-<%= blockFilename %>' );
|
||||||
|
}<% } %>
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_content( $args = [] ): string {
|
||||||
|
$default_args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::default_args', [] ); // Not really practical.
|
||||||
|
|
||||||
|
$args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::prepare_args', array_merge( $default_args, $args ) );
|
||||||
|
|
||||||
|
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( $args, self::class );
|
||||||
|
|
||||||
|
return apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::content', $output );
|
||||||
|
}<% if (isElementor) { %>
|
||||||
|
|
||||||
|
function register_custom_logic(): void {
|
||||||
|
add_action( 'elementor/widgets/widgets_registered', [ $this, 'register_elementor_widget' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
function register_elementor_widget( $widgets_manager ): void {
|
||||||
|
require_once "helpers/<%= blockClassModel %>_Elementor_Widget.php";
|
||||||
|
$widgets_manager->register_widget_type( new Helpers\<%= blockClassModel %>_Elementor_Widget() );
|
||||||
|
}<% } %>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<% if (isComponentManager) { %><%= blockClassModel %>_Component::get_instance();<% } else {
|
||||||
|
%>new <%= blockClassModel %>_Component();<% } %>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>;
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_Component extends \Core\Component {
|
||||||
|
const VERSION = '<%= version %>';
|
||||||
|
public $block_project = '<%= ownerFilename %>';
|
||||||
|
public $block_name = '<%= blockFilename %>';
|
||||||
|
public $hook_prefix = 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>';
|
||||||
|
|
||||||
|
public function get_content( $args = [] ): string {
|
||||||
|
$default_args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::default_args', [] ); // Not really practical.
|
||||||
|
|
||||||
|
$args = apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::prepare_args', array_merge( $default_args, $args ) );
|
||||||
|
|
||||||
|
$output = ( include( __DIR__ . '/templates/<%= blockFilename %>.template.php' ) )( $args, self::class );
|
||||||
|
|
||||||
|
return apply_filters( 'axeweb_blocks/<%= ownerFilename %>/<%= blockFilename %>::content', $output );
|
||||||
|
}
|
||||||
|
<% if (!include_acf_block && !include_native_gutenberg_block) { %>function register_assets(): void {
|
||||||
|
$version = get_plugin_data( __DIR__ . "/../../scytale-custom-blocks.php" )['Version']; // In Plugins
|
||||||
|
// $version = \Core\Global_Functions::get_current_version_number(); // In Theme
|
||||||
|
|
||||||
|
// $base_path = get_template_directory_uri() . '/components/partials/<%= blockFilename %>/';
|
||||||
|
wp_enqueue_style( 'block-<%= blockFilename %>', $this->get_assets_path_url( 'templates/styles/<%= blockFilename %>.min.css' ), ['assets-style'], self::VERSION );<% if (include_script) { %>
|
||||||
|
wp_enqueue_script( 'block-<%= blockFilename %>', $this->get_assets_path_url( 'templates/scripts/<%= blockFilename %>.min.js' ), ['assets-script'], self::VERSION, true );<% } %>
|
||||||
|
|
||||||
|
wp_enqueue_script( 'script-block-<%= blockFilename %>' );
|
||||||
|
}<% } %>
|
||||||
|
<% if (include_acf_block) { %> function register_acf_block() {
|
||||||
|
$this->register_block( __DIR__ . "/<%= blockFilename %>.block.json", [
|
||||||
|
'style_assets' => [
|
||||||
|
[
|
||||||
|
'name' => '<%= blockFilename %>',
|
||||||
|
'url' => $this->get_assets_path_url( 'templates/styles/<%= blockFilename %>.min.css' ),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'script_assets' => [
|
||||||
|
[
|
||||||
|
'name' => '<%= blockFilename %>',
|
||||||
|
'url' => $this->get_assets_path_url( 'templates/scripts/<%= blockFilename %>.min.js' ),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
<% } %><% if (include_native_gutenberg_block) { %> function register_native_gutenberg_block() {
|
||||||
|
register_block_type( __DIR__ . '/templates/gutenberg-block/block.json' );<% if (include_script) { %>
|
||||||
|
|
||||||
|
add_action( 'wp_enqueue_scripts', function () {
|
||||||
|
$asset_file_front = include( plugin_dir_path( __FILE__ ) . '/templates/gutenberg-block/build/front.asset.php' );
|
||||||
|
wp_enqueue_script(
|
||||||
|
'gutenberg-<%= blockFilename %>-scripts-front',
|
||||||
|
$this->get_assets_path_url( 'templates/gutenberg-block/build/front.js' ),
|
||||||
|
$asset_file_front['dependencies'],
|
||||||
|
$asset_file_front['version'],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} );<% } %>
|
||||||
|
}<% } %>
|
||||||
|
}
|
||||||
|
|
||||||
|
<%= blockClassModel %>_Component::get_instance();
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_API {
|
||||||
|
|
||||||
|
static function prepare_args( $args = [] ) {
|
||||||
|
|
||||||
|
// $args = array_merge( [], $args );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component's default args.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_Defaults {
|
||||||
|
public static function default_args(): array {
|
||||||
|
$args = <%- defaultData %>;
|
||||||
|
|
||||||
|
// $args['base_url'] = \Core\Global_Functions::get_file_url( __DIR__ . '/../templates/' );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\Helpers;
|
||||||
|
|
||||||
|
class <%= blockClassModel %>_Elementor_Widget extends \Elementor\Widget_Base {
|
||||||
|
const PLUGIN_NAME = '<%= blockFilename %>';
|
||||||
|
|
||||||
|
public function get_name() {
|
||||||
|
return self::PLUGIN_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_title() {
|
||||||
|
return '<%= blockClassModel %>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_icon() {
|
||||||
|
return 'eicon-plus-square-o';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _register_controls() {
|
||||||
|
$block_data = \Core\Block::get_block_data( __DIR__ . '/../<%= blockFilename %>.block.json' );
|
||||||
|
\Core\Elementor_Block::register_groups( $block_data, $this );
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_style_depends() {
|
||||||
|
return [ 'block-<%= blockFilename %>' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_script_depends() {
|
||||||
|
return [ 'block-<%= blockFilename %>' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render() {
|
||||||
|
$settings = $this->get_settings_for_display();
|
||||||
|
|
||||||
|
$component = new \AXEWEB_Blocks\Blocks\<%= ownerClass %>\<%= blockClassModel %>\<%= blockClassModel %>_Component();
|
||||||
|
|
||||||
|
$args = self::prepare( $settings );
|
||||||
|
$component->render( $args );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function prepare( $args ): array {
|
||||||
|
// Prepare $args for render function.
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import path from "path";
|
||||||
|
import fs, {mkdir, copyFile} from "fs/promises";
|
||||||
|
import {capitalize, createFiles, getBlockName, getConfigs, readJSONFile} from "../../helpers.js";
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
import {copy} from "fs-extra";
|
||||||
|
import {exec} from 'child_process';
|
||||||
|
import execPhp from "exec-php";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
export async function buildWordPress(blockName, args = {}) {
|
||||||
|
const isBlock = args.platform === 'wordpress-acf-block';
|
||||||
|
const isElementor = args.platform === 'wordpress-elementor';
|
||||||
|
const isComponentManager = args.platform === 'wordpress-component-manager'
|
||||||
|
|
||||||
|
const {modulesPath, projectPath} = getConfigs();
|
||||||
|
|
||||||
|
const distPath = path.join(projectPath, 'exports', args.platform);
|
||||||
|
// await mkdir(distPath, {recursive: true})
|
||||||
|
await mkdir(path.join(distPath, 'templates'), {recursive: true})
|
||||||
|
|
||||||
|
const blockFilePath = path.join(projectPath, 'block.json');
|
||||||
|
|
||||||
|
let data = await readJSONFile(blockFilePath);
|
||||||
|
Object.assign(data, getBlockName(data.name));
|
||||||
|
|
||||||
|
const title = capitalize(data.name);
|
||||||
|
const owner = capitalize(data.project);
|
||||||
|
|
||||||
|
data = Object.assign(data, {
|
||||||
|
title,
|
||||||
|
blockClassModel: title.replace(/ /ig, '_'),
|
||||||
|
blockFilename: title.toLowerCase().replace(/ /ig, '-'),
|
||||||
|
blockClassName: title.toLowerCase().replace(/ /ig, '_'),
|
||||||
|
owner,
|
||||||
|
ownerClass: owner.replace(/ /ig, '_'),
|
||||||
|
ownerFilename: owner.toLowerCase().replace(/ /ig, '-'),
|
||||||
|
templateFormat: 'php',
|
||||||
|
include_acf_block: isBlock,
|
||||||
|
include_native_gutenberg_block: false,
|
||||||
|
include_script: true,
|
||||||
|
include_elementor_widget: isElementor,
|
||||||
|
isElementor,
|
||||||
|
isComponentManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
await copyFile(blockFilePath, path.join(distPath, data.blockFilename + '.block.json'));
|
||||||
|
|
||||||
|
const backPath = modulesPath ? modulesPath.split('/').map(() => '..').join('/') : '';
|
||||||
|
|
||||||
|
const phpGeneratorPath = path.join(modulesPath, 'platforms', 'php');
|
||||||
|
await execCommand(`cd ${phpGeneratorPath} && composer install`);
|
||||||
|
await execPHPFile(path.join(phpGeneratorPath, 'build.php'), 'build', {
|
||||||
|
blockName,
|
||||||
|
backPath,
|
||||||
|
projectPath,
|
||||||
|
platform: args.platform
|
||||||
|
});
|
||||||
|
|
||||||
|
await copyStaticFile(
|
||||||
|
path.join(projectPath, 'src', 'styles', `${data.blockFilename}.min.css`),
|
||||||
|
path.join(distPath, 'templates', 'styles', `${data.blockFilename}.min.css`),
|
||||||
|
);
|
||||||
|
|
||||||
|
await copyStaticFile(
|
||||||
|
path.join(projectPath, 'src', 'scripts', `${data.blockFilename}.min.js`),
|
||||||
|
path.join(distPath, 'templates', 'scripts', `${data.blockFilename}.min.js`),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const imagesPath = path.join(projectPath, 'src', 'images');
|
||||||
|
await fs.access(imagesPath);
|
||||||
|
|
||||||
|
await copy(
|
||||||
|
path.join(imagesPath),
|
||||||
|
path.join(distPath, 'templates', 'images'),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
// Folder doesn't exist.
|
||||||
|
}
|
||||||
|
|
||||||
|
const phpDataObject = await execPHPFile(path.join(phpGeneratorPath, 'build.php'), 'jsonToPhp', {
|
||||||
|
json: await readJSONFile(path.join(projectPath, 'data', 'default.json'), "utf8"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// await createFiles(Object.assign({}, data, {defaultData: phpDataObject}), [{
|
||||||
|
// from: `templates/helpers/Template_Defaults.php`,
|
||||||
|
// to: `helpers/${data.blockClassModel}_Defaults.php`,
|
||||||
|
// }], {
|
||||||
|
// pathDist: distPath,
|
||||||
|
// generatorsPath: path.join(__dirname),
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (isElementor) {
|
||||||
|
await createFiles(data, [{
|
||||||
|
from: `templates/helpers/Template_Elementor_Widget.php`,
|
||||||
|
to: `helpers/${data.blockClassModel}_Elementor_Widget.php`,
|
||||||
|
}], {
|
||||||
|
pathDist: distPath,
|
||||||
|
generatorsPath: path.join(__dirname)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlock) {
|
||||||
|
await createFiles(data, [{
|
||||||
|
from: `templates/Template_Component.php`,
|
||||||
|
to: `${data.blockClassModel}_Component.php`,
|
||||||
|
}], {
|
||||||
|
pathDist: distPath,
|
||||||
|
generatorsPath: path.join(__dirname)
|
||||||
|
});
|
||||||
|
|
||||||
|
// await createFiles(data, [{
|
||||||
|
// from: `templates/helpers/Template_API.php`,
|
||||||
|
// to: `helpers/${data.blockClassModel}_API.php`,
|
||||||
|
// }], {
|
||||||
|
// pathDist: distPath,
|
||||||
|
// generatorsPath: path.join(__dirname)
|
||||||
|
// });
|
||||||
|
} else {
|
||||||
|
await createFiles(data, [{
|
||||||
|
from: `templates/Template_Basic_Component.php`,
|
||||||
|
to: `${data.blockClassModel}_Component.php`,
|
||||||
|
}], {
|
||||||
|
pathDist: distPath,
|
||||||
|
generatorsPath: path.join(__dirname)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function execCommand(cmd = '') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
exec(cmd, function (error) {
|
||||||
|
if (error) {
|
||||||
|
console.log('Error:', error)
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(stdout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function execPHPFile(file = '', functionName = '', args = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execPhp(file, 'php', (err, php, out) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(out);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
php[functionName.toLowerCase()](args, (err, res, out) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(out);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(res);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyStaticFile(from = '', to = '') {
|
||||||
|
await mkdir(path.dirname(to), {recursive: true})
|
||||||
|
await copyFile(from, to);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import babel from '@rollup/plugin-babel';
|
||||||
|
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import replace from '@rollup/plugin-replace';
|
||||||
|
import css from "@modular-css/rollup";
|
||||||
|
import copy from 'rollup-plugin-copy';
|
||||||
|
import {terser} from "rollup-plugin-terser";
|
||||||
|
|
||||||
|
const devMode = (process.env.NODE_ENV === 'development');
|
||||||
|
console.log('Build Mode', devMode ? 'Development' : 'Production');
|
||||||
|
|
||||||
|
export default [{
|
||||||
|
input: 'layouts/scripts/index.js',
|
||||||
|
output: {
|
||||||
|
file: 'layouts/scripts/dist/index.min.js',
|
||||||
|
sourcemap: devMode,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
nodeResolve({
|
||||||
|
extensions: [".js"],
|
||||||
|
}),
|
||||||
|
replace({
|
||||||
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||||
|
preventAssignment: true,
|
||||||
|
}),
|
||||||
|
css(),
|
||||||
|
babel({
|
||||||
|
compact: false,
|
||||||
|
babelHelpers: 'bundled',
|
||||||
|
presets: ["@babel/preset-react"],
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
!devMode && terser(),
|
||||||
|
copy({
|
||||||
|
targets: [
|
||||||
|
{src: 'layouts/scripts/toolbar/images/**/*', dest: 'layouts/scripts/dist/toolbar/images'}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
input: 'layouts/scripts/frame/frame.js',
|
||||||
|
output: {
|
||||||
|
file: 'layouts/scripts/dist/frame-index.min.js',
|
||||||
|
sourcemap: devMode
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
commonjs(),
|
||||||
|
!devMode && terser()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
input: 'layouts/scripts/sync.jsx',
|
||||||
|
output: {
|
||||||
|
file: 'layouts/scripts/dist/sync.min.js',
|
||||||
|
sourcemap: devMode
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
nodeResolve({
|
||||||
|
extensions: [".js"],
|
||||||
|
}),
|
||||||
|
replace({
|
||||||
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||||
|
preventAssignment: true,
|
||||||
|
}),
|
||||||
|
babel({
|
||||||
|
compact: false,
|
||||||
|
babelHelpers: 'bundled',
|
||||||
|
presets: ["@babel/preset-react"],
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
!devMode && terser()
|
||||||
|
]
|
||||||
|
}];
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {getBlockConfigs, getBlockName, getBlockVariations, handlebarLayoutsPath} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
import {buildAssetFiles} from "../inc/changes-watcher.js";
|
||||||
|
|
||||||
|
export default async function (req, res) {
|
||||||
|
const blockName = req.query.block;
|
||||||
|
const block = getBlockName(blockName);
|
||||||
|
|
||||||
|
const blockDir = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const dataFiles = await getBlockVariations(blockDir);
|
||||||
|
|
||||||
|
const data = await getDataOfFrame(!!req.query.iframe, Object.assign({}, block, {dataFiles}));
|
||||||
|
|
||||||
|
if (data.error && data.errorMessage) {
|
||||||
|
return res.send(data.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseView = req.params.baseView ?? 'alignfull';
|
||||||
|
|
||||||
|
await buildAssetFiles(blockDir);
|
||||||
|
|
||||||
|
res.render(baseView, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDataOfFrame(isIframe = false, block) {
|
||||||
|
const data = {config: await getBlockConfigs(block)};
|
||||||
|
if (data.error && data.errorMessage) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.helpers = {
|
||||||
|
include_partial: (filesPath) => handlebarLayoutsPath(filesPath),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIframe) {
|
||||||
|
data.iframeMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.staticFilesPath = `/block/${block.project}/${block.name}`;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import {
|
||||||
|
getBlockData,
|
||||||
|
getBlockName,
|
||||||
|
getBlockVariations,
|
||||||
|
getListOfDesignPreviewFiles,
|
||||||
|
} from "../helpers.js";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
|
||||||
|
export default async function (req, res, next) {
|
||||||
|
let variationName = req.query.name ? req.query.name : 'default';
|
||||||
|
const block = getBlockName(req.query.block);
|
||||||
|
|
||||||
|
const blockDir = path.join('blocks', '@' + block.project, block.name);
|
||||||
|
const dataFiles = await getBlockVariations(blockDir);
|
||||||
|
const data = await getBlockData(variationName, {projectPath: blockDir});
|
||||||
|
|
||||||
|
let designPreviewFiles = [];
|
||||||
|
try {
|
||||||
|
designPreviewFiles = getListOfDesignPreviewFiles(variationName, await fs.readdir(path.join(blockDir, 'design', 'preview')), block);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Preview Design doesn\'t exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
dataOptions: dataFiles,
|
||||||
|
designPreview: designPreviewFiles,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||