Added DataOptions sidebar.

This commit is contained in:
roman committed 2022-10-11 09:25:45 +03:00
1 parent 56839fee2e
commit 0ec37c4c5b
12 files changed
+464 -68

No files matched your search

+1 -11
View File
@@ -1,18 +1,8 @@
<header class="page_toolbar"> <header class="page_toolbar">
<div class="page_toolbar__left">#</div> <div class="page_toolbar__left"></div>
<div class="page_toolbar__middle"> <div class="page_toolbar__middle">
<div class="page_toolbar__data_options">
<label for="data-options">Data Options: </label>
<select name="data" id="data-options">
{{#each config.dataFiles }}
<option value="{{ name }}" {{#if active }}selected="selected"{{/if}}>{{ name }}</option>
{{/each}}
</select>
</div>
<div> <div>
Sizes: <b>1rem = {{ config.remToPx }}px</b> Sizes: <b>1rem = {{ config.remToPx }}px</b>
</div> </div>
+200 -21
View File
@@ -11378,29 +11378,208 @@ function setupPublish(rootAttributes) {
root.render(html); root.render(html);
} }
const SidebarStyle = qe.div`
--sidebarWidth: 300px;
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;
box-sizing: border-box;
overflow: hidden;
transition: left .2s ease-in-out, visibility .2s ease-in-out;
visibility: hidden;
color: #333;
&.active {
left: 0;
visibility: visible;
}
pre {
overflow-x: auto;
padding: 0.5rem;
background-color: #EDF2F7;
border-radius: 4px;
color: #333;
border: 1px solid #cbd5e0;
}
`;
const SidebarHeaderStyle = qe.header`
min-height: 34px;
padding: 0.5rem 0;
display: flex;
align-items: center;
justify-content: flex-end;
`;
const SidebarButtonToggleStyle = qe.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;
`;
const SidebarCloseButtonStyle = qe.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;
`;
const SidebarDataOptionsStyle = qe.div`
margin-top: 0.5rem;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
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;
}
`;
function DataOptions(props = {}) {
props.rootAttributes = props.rootAttributes ?? {};
const initialState = {
dataName: 'default',
data: {},
dataOptions: []
};
const [state, setState] = react.exports.useState(initialState);
const updateState = update => setState(Object.assign({}, state, update));
const [sidebarOpen, setSidebarOpen] = react.exports.useState(false);
react.exports.useEffect(async () => {
const data = await fetchDataOptions(state.dataName);
updateState(data);
}, []);
const handleCloseSidebarEscEvent = react.exports.useCallback(e => {
if (isEscHit(e)) {
(() => {
closeSidebar();
})();
}
}, []);
react.exports.useEffect(async () => {
document.addEventListener("keydown", handleCloseSidebarEscEvent); // Unsubscribe from ESC listener.
return () => {
document.removeEventListener("keydown", handleCloseSidebarEscEvent);
};
}, [handleCloseSidebarEscEvent]);
const handleBlur = async e => (await isClickOutside(e)) ? closeSidebar() : null;
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SidebarButtonToggleStyle, {
onClick: () => openSidebar(),
title: "Open a Sidebar with Data Options"
}, "#"), /*#__PURE__*/React.createElement(SidebarStyle, {
className: sidebarOpen ? 'active sidebar-active' : '',
tabIndex: "0",
onBlur: handleBlur
}, /*#__PURE__*/React.createElement(SidebarHeaderStyle, null, /*#__PURE__*/React.createElement(SidebarCloseButtonStyle, {
onClick: () => closeSidebar()
})), state.dataOptions && !!state.dataOptions.length && /*#__PURE__*/React.createElement(SidebarDataOptionsStyle, null, /*#__PURE__*/React.createElement("label", {
htmlFor: "data-options"
}, "Data Options"), /*#__PURE__*/React.createElement("select", {
name: "data",
id: "data-options",
onChange: e => changeDataOption(e),
value: state.dataName
}, state.dataOptions.map(item => {
const isSelected = state.dataName === item;
return /*#__PURE__*/React.createElement("option", {
value: item,
selected: isSelected
}, item);
}))), state.data && /*#__PURE__*/React.createElement("pre", null, JSON.stringify(state.data, null, 2))));
function openSidebar() {
setSidebarOpen(true);
setTimeout(() => document.querySelector('.sidebar-active').focus());
}
function closeSidebar() {
setSidebarOpen(false);
}
async function changeDataOption(e) {
const optionName = e.target.value;
props.rootAttributes.previewFrame.src = window.devTool.previewFrameUrl + '?data=' + optionName;
const dataOption = await fetchDataOptions(optionName);
updateState({
data: dataOption.data,
dataName: optionName
});
}
async function fetchDataOptions(name = 'default') {
const queryParameters = new URLSearchParams({
name
});
const response = await fetch(`/data?${queryParameters}`);
return await response.json();
}
}
function setupDataOptions(rootAttributes) {
// INIT
const wrapper = document.createElement('div');
document.querySelector('.page_toolbar__left').prepend(wrapper);
const root = createRoot(wrapper);
const html = /*#__PURE__*/React.createElement(DataOptions, {
rootAttributes: rootAttributes
});
root.render(html);
}
const rootAttributes = { const rootAttributes = {
previewFrame: document.getElementById('preview_frame') previewFrame: document.getElementById('preview_frame')
}; };
setupResponsiveness(rootAttributes); setupResponsiveness(rootAttributes);
setupPublish(rootAttributes); // const responsiveness = connectResponsiveness(rootAttributes); setupDataOptions(rootAttributes);
// setTimeout(() => responsiveness.selectMode('tablet'), 5000) setupPublish(rootAttributes);
// setTimeout(() => responsiveness.selectMode('mobile'), 10000)
const previewFrame = rootAttributes.previewFrame;
initDataOptions();
/**
* Functions
*/
function initDataOptions() {
const dataOptionsSelect = document.getElementById('data-options');
if (!dataOptionsSelect) {
return;
}
dataOptionsSelect.addEventListener('change', function () {
previewFrame.src = window.devTool.previewFrameUrl + '?data=' + this.value;
});
}
//# sourceMappingURL=index.min.js.map //# sourceMappingURL=index.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"index.min.js","sources":["../../../node_modules/react/cjs/react.production.min.js","../../../node_modules/react/index.js","../../../node_modules/scheduler/cjs/scheduler.production.min.js","../../../node_modules/scheduler/index.js","../../../node_modules/react-dom/cjs/react-dom.production.min.js","../../../node_modules/react-dom/index.js","../../../node_modules/react-dom/client.js","../../../node_modules/react-is/cjs/react-is.production.min.js","../../../node_modules/react-is/index.js","../../../node_modules/@emotion/stylis/dist/stylis.esm.js","../../../node_modules/@emotion/unitless/dist/unitless.esm.js","../../../node_modules/@emotion/memoize/dist/emotion-memoize.esm.js","../../../node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js","../../../node_modules/hoist-non-react-statics/node_modules/react-is/cjs/react-is.production.min.js","../../../node_modules/hoist-non-react-statics/node_modules/react-is/index.js","../../../node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js","../toolbar/responsive.style.js","../toolbar/responsive-button/responsive-button.style.js","../toolbar/responsive-button/ResponsiveButton.jsx","../toolbar/responsive.jsx","../toolbar/publish.jsx","../index.js"],"sourcesContent":["/**\n * @license React\n * react.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var l=Symbol.for(\"react.element\"),n=Symbol.for(\"react.portal\"),p=Symbol.for(\"react.fragment\"),q=Symbol.for(\"react.strict_mode\"),r=Symbol.for(\"react.profiler\"),t=Symbol.for(\"react.provider\"),u=Symbol.for(\"react.context\"),v=Symbol.for(\"react.forward_ref\"),w=Symbol.for(\"react.suspense\"),x=Symbol.for(\"react.memo\"),y=Symbol.for(\"react.lazy\"),z=Symbol.iterator;function A(a){if(null===a||\"object\"!==typeof a)return null;a=z&&a[z]||a[\"@@iterator\"];return\"function\"===typeof a?a:null}\nvar B={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}E.prototype.isReactComponent={};\nE.prototype.setState=function(a,b){if(\"object\"!==typeof a&&\"function\"!==typeof a&&null!=a)throw Error(\"setState(...): takes an object of state variables to update or a function which returns an object of state variables.\");this.updater.enqueueSetState(this,a,b,\"setState\")};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,\"forceUpdate\")};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}var H=G.prototype=new F;\nH.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0};\nfunction M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=\"\"+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1<g){for(var f=Array(g),m=0;m<g;m++)f[m]=arguments[m+2];c.children=f}if(a&&a.defaultProps)for(d in g=a.defaultProps,g)void 0===c[d]&&(c[d]=g[d]);return{$$typeof:l,type:a,key:k,ref:h,props:c,_owner:K.current}}\nfunction N(a,b){return{$$typeof:l,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function O(a){return\"object\"===typeof a&&null!==a&&a.$$typeof===l}function escape(a){var b={\"=\":\"=0\",\":\":\"=2\"};return\"$\"+a.replace(/[=:]/g,function(a){return b[a]})}var P=/\\/+/g;function Q(a,b){return\"object\"===typeof a&&null!==a&&null!=a.key?escape(\"\"+a.key):b.toString(36)}\nfunction R(a,b,e,d,c){var k=typeof a;if(\"undefined\"===k||\"boolean\"===k)a=null;var h=!1;if(null===a)h=!0;else switch(k){case \"string\":case \"number\":h=!0;break;case \"object\":switch(a.$$typeof){case l:case n:h=!0}}if(h)return h=a,c=c(h),a=\"\"===d?\".\"+Q(h,0):d,I(c)?(e=\"\",null!=a&&(e=a.replace(P,\"$&/\")+\"/\"),R(c,b,e,\"\",function(a){return a})):null!=c&&(O(c)&&(c=N(c,e+(!c.key||h&&h.key===c.key?\"\":(\"\"+c.key).replace(P,\"$&/\")+\"/\")+a)),b.push(c)),1;h=0;d=\"\"===d?\".\":d+\":\";if(I(a))for(var g=0;g<a.length;g++){k=\na[g];var f=d+Q(k,g);h+=R(k,b,e,f,c)}else if(f=A(a),\"function\"===typeof f)for(a=f.call(a),g=0;!(k=a.next()).done;)k=k.value,f=d+Q(k,g++),h+=R(k,b,e,f,c);else if(\"object\"===k)throw b=String(a),Error(\"Objects are not valid as a React child (found: \"+(\"[object Object]\"===b?\"object with keys {\"+Object.keys(a).join(\", \")+\"}\":b)+\"). If you meant to render a collection of children, use an array instead.\");return h}\nfunction S(a,b,e){if(null==a)return a;var d=[],c=0;R(a,d,\"\",\"\",function(a){return b.call(e,a,c++)});return d}function T(a){if(-1===a._status){var b=a._result;b=b();b.then(function(b){if(0===a._status||-1===a._sLine truncated {"version":3,"file":"index.min.js","sources":["../../../node_modules/react/cjs/react.production.min.js","../../../node_modules/react/index.js","../../../node_modules/scheduler/cjs/scheduler.production.min.js","../../../node_modules/scheduler/index.js","../../../node_modules/react-dom/cjs/react-dom.production.min.js","../../../node_modules/react-dom/index.js","../../../node_modules/react-dom/client.js","../../../node_modules/react-is/cjs/react-is.production.min.js","../../../node_modules/react-is/index.js","../../../node_modules/@emotion/stylis/dist/stylis.esm.js","../../../node_modules/@emotion/unitless/dist/unitless.esm.js","../../../node_modules/@emotion/memoize/dist/emotion-memoize.esm.js","../../../node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js","../../../node_modules/hoist-non-react-statics/node_modules/react-is/cjs/react-is.production.min.js","../../../node_modules/hoist-non-react-statics/node_modules/react-is/index.js","../../../node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js","../toolbar/responsive.style.js","../toolbar/responsive-button/responsive-button.style.js","../toolbar/responsive-button/ResponsiveButton.jsx","../toolbar/responsive.jsx","../toolbar/publish.jsx","../toolbar/data-options/data-options.style.js","../toolbar/data-options/DataOptions.jsx","../index.js"],"sourcesContent":["/**\n * @license React\n * react.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var l=Symbol.for(\"react.element\"),n=Symbol.for(\"react.portal\"),p=Symbol.for(\"react.fragment\"),q=Symbol.for(\"react.strict_mode\"),r=Symbol.for(\"react.profiler\"),t=Symbol.for(\"react.provider\"),u=Symbol.for(\"react.context\"),v=Symbol.for(\"react.forward_ref\"),w=Symbol.for(\"react.suspense\"),x=Symbol.for(\"react.memo\"),y=Symbol.for(\"react.lazy\"),z=Symbol.iterator;function A(a){if(null===a||\"object\"!==typeof a)return null;a=z&&a[z]||a[\"@@iterator\"];return\"function\"===typeof a?a:null}\nvar B={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}E.prototype.isReactComponent={};\nE.prototype.setState=function(a,b){if(\"object\"!==typeof a&&\"function\"!==typeof a&&null!=a)throw Error(\"setState(...): takes an object of state variables to update or a function which returns an object of state variables.\");this.updater.enqueueSetState(this,a,b,\"setState\")};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,\"forceUpdate\")};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}var H=G.prototype=new F;\nH.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0};\nfunction M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=\"\"+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1<g){for(var f=Array(g),m=0;m<g;m++)f[m]=arguments[m+2];c.children=f}if(a&&a.defaultProps)for(d in g=a.defaultProps,g)void 0===c[d]&&(c[d]=g[d]);return{$$typeof:l,type:a,key:k,ref:h,props:c,_owner:K.current}}\nfunction N(a,b){return{$$typeof:l,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function O(a){return\"object\"===typeof a&&null!==a&&a.$$typeof===l}function escape(a){var b={\"=\":\"=0\",\":\":\"=2\"};return\"$\"+a.replace(/[=:]/g,function(a){return b[a]})}var P=/\\/+/g;function Q(a,b){return\"object\"===typeof a&&null!==a&&null!=a.key?escape(\"\"+a.key):b.toString(36)}\nfunction R(a,b,e,d,c){var k=typeof a;if(\"undefined\"===k||\"boolean\"===k)a=null;var h=!1;if(null===a)h=!0;else switch(k){case \"string\":case \"number\":h=!0;break;case \"object\":switch(a.$$typeof){case l:case n:h=!0}}if(h)return h=a,c=c(h),a=\"\"===d?\".\"+Q(h,0):d,I(c)?(e=\"\",null!=a&&(e=a.replace(P,\"$&/\")+\"/\"),R(c,b,e,\"\",function(a){return a})):null!=c&&(O(c)&&(c=N(c,e+(!c.key||h&&h.key===c.key?\"\":(\"\"+c.key).replace(P,\"$&/\")+\"/\")+a)),b.push(c)),1;h=0;d=\"\"===d?\".\":d+\":\";if(I(a))for(var g=0;g<a.length;g++){k=\na[g];var f=d+Q(k,g);h+=R(k,b,e,f,c)}else if(f=A(a),\"function\"===typeof f)for(a=f.call(a),g=0;!(k=a.next()).done;)k=k.value,f=d+Q(k,g++),h+=R(k,b,e,f,c);else if(\"object\"===k)throw b=String(a),Error(\"Objects are not valid as a React child (found: \"+(\"[object Object]\"===b?\"object with keys {\"+Object.keys(a).join(\", \")+\"}\":b)+\"). If you meant to render a collection of children, use an array instead.\");return h}\nfunction S(a,b,e){if(null==a)return a;var d=[],c=0;R(a,d,\"\",\"\",function(a){return b.call(e,a,c++)});return d}function T(Line truncated
+4
View File
@@ -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

+4
View File
@@ -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

+2 -22
View File
@@ -2,32 +2,12 @@
import {setupResponsiveness} from './toolbar/responsive.jsx'; import {setupResponsiveness} from './toolbar/responsive.jsx';
import {setupPublish} from "./toolbar/publish.jsx"; import {setupPublish} from "./toolbar/publish.jsx";
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
const rootAttributes = { const rootAttributes = {
previewFrame: document.getElementById('preview_frame'), previewFrame: document.getElementById('preview_frame'),
} }
setupResponsiveness(rootAttributes); setupResponsiveness(rootAttributes);
setupDataOptions(rootAttributes);
setupPublish(rootAttributes) setupPublish(rootAttributes)
// const responsiveness = connectResponsiveness(rootAttributes);
// setTimeout(() => responsiveness.selectMode('tablet'), 5000)
// setTimeout(() => responsiveness.selectMode('mobile'), 10000)
const previewFrame = rootAttributes.previewFrame;
initDataOptions();
/**
* Functions
*/
function initDataOptions() {
const dataOptionsSelect = document.getElementById('data-options');
if (!dataOptionsSelect) {
return;
}
dataOptionsSelect.addEventListener('change', function () {
previewFrame.src = window.devTool.previewFrameUrl + '?data=' + this.value;
});
}
@@ -0,0 +1,103 @@
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";
function DataOptions(props = {}) {
props.rootAttributes = props.rootAttributes ?? {};
const initialState = {dataName: 'default', data: {}, dataOptions: []};
const [state, setState] = useState(initialState);
const updateState = (update) => setState(Object.assign({}, state, update));
const [sidebarOpen, setSidebarOpen] = useState(false);
useEffect(async () => {
const data = await fetchDataOptions(state.dataName);
updateState(data);
}, []);
const handleCloseSidebarEscEvent = useCallback((e) => {
if (isEscHit(e)) {
(() => {
closeSidebar()
})();
}
}, []);
useEffect(async () => {
document.addEventListener("keydown", handleCloseSidebarEscEvent);
// Unsubscribe from ESC listener.
return () => {
document.removeEventListener("keydown", handleCloseSidebarEscEvent);
}
}, [handleCloseSidebarEscEvent]);
const handleBlur = async (e) => await isClickOutside(e) ? closeSidebar() : null;
return <>
<SidebarButtonToggleStyle onClick={() => openSidebar()} title="Open a Sidebar with Data Options">#</SidebarButtonToggleStyle>
<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}>
{state.dataOptions.map((item) => {
const isSelected = state.dataName === item;
return <option value={item} selected={isSelected}>{item}</option>
})}
</select>
</SidebarDataOptionsStyle>
}
{state.data &&
<pre>{JSON.stringify(state.data, null, 2)}</pre>
}
</SidebarStyle>
</>;
function openSidebar() {
setSidebarOpen(true);
setTimeout(() => document.querySelector('.sidebar-active').focus());
}
function closeSidebar() {
setSidebarOpen(false);
}
async function changeDataOption(e) {
const optionName = e.target.value;
props.rootAttributes.previewFrame.src = window.devTool.previewFrameUrl + '?data=' + optionName;
const dataOption = await fetchDataOptions(optionName);
updateState({data: dataOption.data, dataName: optionName})
}
async function fetchDataOptions(name = 'default') {
const queryParameters = new URLSearchParams({name});
const response = await fetch(`/data?${queryParameters}`);
return await response.json();
}
}
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,107 @@
import styled from "styled-components";
export const SidebarStyle = styled.div`
--sidebarWidth: 300px;
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;
box-sizing: border-box;
overflow: hidden;
transition: left .2s ease-in-out, visibility .2s ease-in-out;
visibility: hidden;
color: #333;
&.active {
left: 0;
visibility: visible;
}
pre {
overflow-x: auto;
padding: 0.5rem;
background-color: #EDF2F7;
border-radius: 4px;
color: #333;
border: 1px solid #cbd5e0;
}
`;
export const SidebarHeaderStyle = styled.header`
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;
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,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,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

+27 -13
View File
@@ -64,7 +64,7 @@ const dataFiles = prepareListOfDataFiles(await fs.readdir('./data'));
app.get('/', async (req, res) => { app.get('/', async (req, res) => {
let jsonFileName = req.query.data ? req.query.data : 'default'; let jsonFileName = req.query.data ? req.query.data : 'default';
const data = await getBlockConfigs(jsonFileName); const data = await getBlockConfigs(jsonFileName, {includeConfigs: true});
if (data.error && data.errorMessage) { if (data.error && data.errorMessage) {
return res.send(data.errorMessage); return res.send(data.errorMessage);
} }
@@ -80,7 +80,7 @@ app.get('/', async (req, res) => {
app.get('/view/:baseView', async (req, res) => { app.get('/view/:baseView', async (req, res) => {
let jsonFileName = req.query.data ? req.query.data : 'default'; let jsonFileName = req.query.data ? req.query.data : 'default';
const data = await getBlockConfigs(jsonFileName); const data = await getBlockConfigs(jsonFileName, {includeConfigs: true});
if (data.error && data.errorMessage) { if (data.error && data.errorMessage) {
return res.send(data.errorMessage); return res.send(data.errorMessage);
} }
@@ -140,6 +140,18 @@ app.get('/publish', async (req, res) => {
await fs.unlink('./dist.zip'); await fs.unlink('./dist.zip');
}); });
app.get('/data', async (req, res) => {
let jsonDataFileName = req.query.name ? req.query.name : 'default';
const data = await getBlockConfigs(jsonDataFileName);
const dataFiles = prepareListOfDataFiles(await fs.readdir('./data'));
return res.json({
dataOptions: dataFiles,
data,
});
});
app.use(express.static('src')); app.use(express.static('src'));
app.use(express.static(projectDir + 'layouts')); app.use(express.static(projectDir + 'layouts'));
@@ -240,22 +252,24 @@ async function readJSONFile(jsonFile) {
return data; return data;
} }
async function getBlockConfigs(jsonFileName = 'default') { async function getBlockConfigs(jsonFileName = 'default', {includeConfigs} = {}) {
let data = await readJSONFile(`./data/${jsonFileName}.json`); let data = await readJSONFile(`./data/${jsonFileName}.json`);
if (data.error) { if (data.error) {
return data; return data;
} }
Object.assign(data, { if (includeConfigs) {
config: Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object. Object.assign(data, {
{ config: Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
projectDir, activeDataFile: jsonFileName, dataFiles: dataFiles.map((name) => { {
return { projectDir, activeDataFile: jsonFileName, dataFiles: dataFiles.map((name) => {
name, active: jsonFileName === name, return {
}; name, active: jsonFileName === name,
}), remToPx: config.has('remToPx') ? config.get('remToPx') : 16, };
}) }), remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
}); })
});
}
return data; return data;
} }