Browse Source

Added DataOptions sidebar.

pull/1/head
Roman Axelrod 3 years ago
parent
commit
0ec37c4c5b
  1. 12
      layouts/partials/toolbar.hbs
  2. 215
      layouts/scripts/dist/index.min.js
  3. 2
      layouts/scripts/dist/index.min.js.map
  4. 4
      layouts/scripts/dist/toolbar/images/icon-close.svg
  5. 4
      layouts/scripts/dist/toolbar/images/icon-json.svg
  6. 24
      layouts/scripts/index.js
  7. 103
      layouts/scripts/toolbar/data-options/DataOptions.jsx
  8. 107
      layouts/scripts/toolbar/data-options/data-options.style.js
  9. 4
      layouts/scripts/toolbar/images/icon-close.svg
  10. 7
      layouts/scripts/toolbar/images/icon-dropdown-arrow.svg
  11. 4
      layouts/scripts/toolbar/images/icon-json.svg
  12. 40
      server.js

12
layouts/partials/toolbar.hbs

@ -1,18 +1,8 @@
<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__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>
Sizes: <b>1rem = {{ config.remToPx }}px</b>
</div>

215
layouts/scripts/dist/index.min.js

@ -11378,29 +11378,208 @@ function setupPublish(rootAttributes) {
root.render(html);
}
const rootAttributes = {
previewFrame: document.getElementById('preview_frame')
};
setupResponsiveness(rootAttributes);
setupPublish(rootAttributes); // const responsiveness = connectResponsiveness(rootAttributes);
// setTimeout(() => responsiveness.selectMode('tablet'), 5000)
// setTimeout(() => responsiveness.selectMode('mobile'), 10000)
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;
const previewFrame = rootAttributes.previewFrame;
initDataOptions();
/**
* Functions
*/
select {
flex: 1 1;
display: block;
appearance: none;
border: 1px solid #cbd5e0;
padding: 0.5rem;
color: #333;
border-radius: 4px;
function initDataOptions() {
const dataOptionsSelect = document.getElementById('data-options');
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;
}
if (!dataOptionsSelect) {
return;
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.
dataOptionsSelect.addEventListener('change', function () {
previewFrame.src = window.devTool.previewFrameUrl + '?data=' + this.value;
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 = {
previewFrame: document.getElementById('preview_frame')
};
setupResponsiveness(rootAttributes);
setupDataOptions(rootAttributes);
setupPublish(rootAttributes);
//# sourceMappingURL=index.min.js.map

2
layouts/scripts/dist/index.min.js.map

File diff suppressed because one or more lines are too long

4
layouts/scripts/dist/toolbar/images/icon-close.svg

@ -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
layouts/scripts/dist/toolbar/images/icon-json.svg

@ -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

24
layouts/scripts/index.js

@ -2,32 +2,12 @@
import {setupResponsiveness} from './toolbar/responsive.jsx';
import {setupPublish} from "./toolbar/publish.jsx";
import {setupDataOptions} from "./toolbar/data-options/DataOptions.jsx";
const rootAttributes = {
previewFrame: document.getElementById('preview_frame'),
}
setupResponsiveness(rootAttributes);
setupDataOptions(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;
});
}

103
layouts/scripts/toolbar/data-options/DataOptions.jsx

@ -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);
}

107
layouts/scripts/toolbar/data-options/data-options.style.js

@ -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;
}
`;

4
layouts/scripts/toolbar/images/icon-close.svg

@ -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

7
layouts/scripts/toolbar/images/icon-dropdown-arrow.svg

@ -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

4
layouts/scripts/toolbar/images/icon-json.svg

@ -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

40
server.js

@ -64,7 +64,7 @@ const dataFiles = prepareListOfDataFiles(await fs.readdir('./data'));
app.get('/', async (req, res) => {
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) {
return res.send(data.errorMessage);
}
@ -80,7 +80,7 @@ app.get('/', async (req, res) => {
app.get('/view/:baseView', async (req, res) => {
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) {
return res.send(data.errorMessage);
}
@ -140,6 +140,18 @@ app.get('/publish', async (req, res) => {
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(projectDir + 'layouts'));
@ -240,22 +252,24 @@ async function readJSONFile(jsonFile) {
return data;
}
async function getBlockConfigs(jsonFileName = 'default') {
async function getBlockConfigs(jsonFileName = 'default', {includeConfigs} = {}) {
let data = await readJSONFile(`./data/${jsonFileName}.json`);
if (data.error) {
return data;
}
Object.assign(data, {
config: Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
{
projectDir, activeDataFile: jsonFileName, dataFiles: dataFiles.map((name) => {
return {
name, active: jsonFileName === name,
};
}), remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
})
});
if (includeConfigs) {
Object.assign(data, {
config: Object.assign(JSON.parse(JSON.stringify(config)), // The entire config object.
{
projectDir, activeDataFile: jsonFileName, dataFiles: dataFiles.map((name) => {
return {
name, active: jsonFileName === name,
};
}), remToPx: config.has('remToPx') ? config.get('remToPx') : 16,
})
});
}
return data;
}

Loading…
Cancel
Save