12 changed files with 461 additions and 65 deletions
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 803 B |
@ -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; |
||||
|
} |
||||
|
`;
|
||||
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 553 B |
|
After Width: | Height: | Size: 803 B |
Loading…
Reference in new issue