You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

173 lines
5.4 KiB

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";
function DataOptions(props = {}) {
props.rootAttributes = props.rootAttributes ?? {};
const initialState = {dataName: 'default', data: {}, dataOptions: [], designPreview: []};
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 () => {
const data = await fetchDataOptions(state.dataName);
updateState(data);
}, []);
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}>
{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>
}
<button className='btn btn--secondary' onClick={(e) => copyToClipboard(e, state.data)}>Copy to Clipboard</button>
</SidebarStyle>
</div>;
//
// Functions
//
function openSidebar() {
setSidebarOpen(true);
setTimeout(() => document.querySelector('.sidebar-active').focus());
}
function closeSidebar() {
setSidebarOpen(false);
}
async function changeDataOption(e) {
const dataName = e.target.value;
props.rootAttributes.previewFrame.src = window.devTool.previewFrameUrl + '?data=' + dataName;
const dataOption = await fetchDataOptions(dataName);
updateState(Object.assign({}, dataOption, {dataName}));
}
async function fetchDataOptions(name = 'default') {
const queryParameters = new URLSearchParams({name});
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));
}
}
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);
}