|
Before Width: | Height: | Size: 970 B After Width: | Height: | Size: 970 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 446 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,31 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import {setupResponsiveness} from './toolbar/responsive.jsx'; |
||||
|
|
||||
|
const rootAttributes = { |
||||
|
previewFrame: document.getElementById('preview_frame'), |
||||
|
} |
||||
|
|
||||
|
setupResponsiveness(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.previewFrameUrl + '?data=' + this.value; |
||||
|
}); |
||||
|
} |
||||
@ -1,38 +0,0 @@ |
|||||
(function () { |
|
||||
|
|
||||
const previewFrame = document.getElementById('preview_frame'); |
|
||||
|
|
||||
initDataOptions(); |
|
||||
initResponsiveMode(); |
|
||||
|
|
||||
/** |
|
||||
* Functions |
|
||||
*/ |
|
||||
|
|
||||
function initDataOptions() { |
|
||||
const dataOptionsSelect = document.getElementById('data-options'); |
|
||||
if (!dataOptionsSelect) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
dataOptionsSelect.addEventListener('change', function () { |
|
||||
previewFrame.src = window.previewFrameUrl + '?data=' + this.value; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function initResponsiveMode() { |
|
||||
|
|
||||
document.querySelectorAll('input[name="responsive_mode"]').forEach((input) => { |
|
||||
input.addEventListener('change', (event) => { |
|
||||
const mode = event.target.value; |
|
||||
|
|
||||
previewFrame.classList.remove('breakpoint--desktop'); |
|
||||
previewFrame.classList.remove('breakpoint--tablet'); |
|
||||
previewFrame.classList.remove('breakpoint--mobile'); |
|
||||
|
|
||||
previewFrame.classList.add('breakpoint--' + mode); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
})(); |
|
||||
|
After Width: | Height: | Size: 970 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 446 B |
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,94 @@ |
|||||
|
import React, {useCallback, 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], |
||||
|
} |
||||
|
|
||||
|
const defaultResponsiveOptions = { |
||||
|
reset: '100%', |
||||
|
desktop: responsiveOptions.desktop[0], |
||||
|
tablet: responsiveOptions.tablet[1], |
||||
|
mobile: responsiveOptions.mobile[2], |
||||
|
} |
||||
|
|
||||
|
export function ResponsiveButton({mode, active, onSelect}) { |
||||
|
// active = true; |
||||
|
const [state, setState] = useState({open: true, breakpoint: defaultResponsiveOptions[mode]}); |
||||
|
const updateState = (update) => { |
||||
|
// update.open = true; |
||||
|
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 outsideClickAction = async (e) => await isClickOutside(e) ? updateState({open: false}) : null; |
||||
|
const handleBlur = useCallback(outsideClickAction, []); |
||||
|
|
||||
|
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.breakpoint === breakpoint ? 'active' : ''} onClick={() => select(breakpoint)}>{breakpoint}</a> |
||||
|
</li>; |
||||
|
})} |
||||
|
</ResponsiveOptionsDropdown> |
||||
|
} |
||||
|
</ResponsiveButtonStyle>; |
||||
|
|
||||
|
// |
||||
|
// Actions |
||||
|
// |
||||
|
|
||||
|
function select(breakpoint = null) { |
||||
|
// Click on option in Dropdown. |
||||
|
if (breakpoint) { |
||||
|
updateState({open: false, breakpoint}); |
||||
|
onSelect(breakpoint); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Click on device button. |
||||
|
if (!active) { |
||||
|
onSelect(state.breakpoint) |
||||
|
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)); |
||||
|
}, 100); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
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,81 @@ |
|||||
|
// 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 [state, setState] = useState(initialState); |
||||
|
const updateState = (update) => setState(Object.assign({}, state, update)); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
// Update the document title using the browser API |
||||
|
updateController(state); |
||||
|
}); |
||||
|
|
||||
|
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') { |
||||
|
updateState(initialState); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
updateState({mode, breakpoint}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function isActive(mode) { |
||||
|
return mode === state.mode; |
||||
|
} |
||||
|
|
||||
|
function updateController() { |
||||
|
const unit = typeof state.breakpoint === 'string' ? '' : 'px'; |
||||
|
previewFrame.style.setProperty('--breakpoint', state.breakpoint + unit); |
||||
|
previewFrame.classList.add('has-breakpoint'); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
export function setupResponsiveness(rootAttributes) { |
||||
|
// INIT |
||||
|
const wrapper = document.createElement('div'); |
||||
|
document.querySelector('.page_toolbar').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; |
||||
|
`;
|
||||
@ -1,38 +0,0 @@ |
|||||
|
|
||||
&__responsive_mode { |
|
||||
display: flex; |
|
||||
gap: 0.5rem; |
|
||||
|
|
||||
input[type='radio'] { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
label { |
|
||||
--size: 1.5rem; |
|
||||
cursor: pointer; |
|
||||
background-image: url("../images/icon-desktop.svg"); |
|
||||
background-repeat: no-repeat; |
|
||||
background-size: calc(var(--size) - 0.15rem); |
|
||||
background-position: center; |
|
||||
font-size: 1px; |
|
||||
color: rgba(0, 0, 0, 0); |
|
||||
line-height: 1; |
|
||||
display: block; |
|
||||
width: var(--size); |
|
||||
height: var(--size); |
|
||||
border-radius: 0.25rem; |
|
||||
|
|
||||
&[for="responsive_mode__tablet"] { |
|
||||
background-image: url("../images/icon-tablet.svg"); |
|
||||
} |
|
||||
|
|
||||
&[for="responsive_mode__mobile"] { |
|
||||
background-image: url("../images/icon-mobile.svg"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
input[type='radio']:checked + label { |
|
||||
background-color: #CBD5E0; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1 +1 @@ |
|||||
{"version":3,"sourceRoot":"","sources":["page--main.scss","_page--main--responsive-mode.scss","_page--breakpoints.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EAEA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EACA;;AAGA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;ACxDN;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;;;AClCJ;EACE;EACA;EACA;EAEA;EAEA;;AAEA;EACE;EACA;;AAGA;EACE;;AAGF;EACE","file":"page--main.css"} |
{"version":3,"sourceRoot":"","sources":["page--main.scss","_page--breakpoints.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EAEA;;;AAGF;EACE;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EACA;;AAGA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;;ACnDN;EACE;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA","file":"page--main.css"} |
||||
@ -0,0 +1,35 @@ |
|||||
|
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' |
||||
|
|
||||
|
export default { |
||||
|
input: 'layouts/scripts/index.js', |
||||
|
output: { |
||||
|
file: 'layouts/scripts/dist/index.min.js', |
||||
|
sourcemap: true |
||||
|
}, |
||||
|
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(), |
||||
|
copy({ |
||||
|
targets: [ |
||||
|
{ src: 'layouts/scripts/toolbar/images/**/*', dest: 'layouts/scripts/dist/toolbar/images' } |
||||
|
] |
||||
|
}) |
||||
|
], |
||||
|
} |
||||