kopia lustrzana https://github.com/wagtail/wagtail
Make it possible to resize the page editor’s side panels (#9276)
Co-authored-by: Thibaud Colas <thibaudcolas@gmail.com>pull/9659/head
rodzic
119f288a3c
commit
dd0d2c4b88
|
@ -9,6 +9,7 @@ Changelog
|
|||
* Ensure that the `rebuild_references_index` command can run without console output if called with `--verbosity 0` (Omerzahid Ali, Aman Pandey)
|
||||
* Add full support for secondary buttons with icons in the Wagtail design system - `button bicolor button--icon button-secondary` including the `button-small` variant (Seremba Patrick)
|
||||
* Add `purge_embeds` management command to delete all the cached embed objects in the database (Aman Pandey)
|
||||
* Make it possible to resize the page editor’s side panels (Sage Abdullah)
|
||||
* Fix: Make sure workflow timeline icons are visible in high-contrast mode (Loveth Omokaro)
|
||||
* Fix: Ensure authentication forms (login, password reset) have a visible border in Windows high-contrast mode (Loveth Omokaro)
|
||||
* Fix: Ensure visual consistency between buttons and links as buttons in Windows high-contrast mode (Albina Starykova)
|
||||
|
|
|
@ -6,6 +6,14 @@
|
|||
@apply w-overflow-y-hidden sm:w-overflow-y-auto;
|
||||
}
|
||||
|
||||
.side-panel-resizing {
|
||||
@apply w-select-none w-cursor-ew-resize;
|
||||
|
||||
.form-side {
|
||||
@apply w-transition;
|
||||
}
|
||||
}
|
||||
|
||||
.form-side {
|
||||
@apply w-absolute
|
||||
w-right-0
|
||||
|
@ -15,18 +23,16 @@
|
|||
md:w-w-1/3
|
||||
w-transform
|
||||
w-translate-x-full
|
||||
w-px-5
|
||||
xl:w-px-10
|
||||
w-py-4
|
||||
w-bg-white
|
||||
w-box-border
|
||||
w-transition
|
||||
w-transition-all
|
||||
motion-reduce:w-transition-none
|
||||
w-duration-300
|
||||
w-border-l
|
||||
w-border-grey-100
|
||||
w-overflow-y-auto
|
||||
w-scrollbar-thin
|
||||
w-min-w-full
|
||||
md:w-min-w-[22.875rem]
|
||||
w-max-w-full
|
||||
sm:w-max-w-[22.5rem]
|
||||
md:w-max-w-[35.937rem]
|
||||
lg:w-max-w-[31.25rem]
|
||||
|
@ -41,6 +47,10 @@
|
|||
@apply w-transition-none;
|
||||
}
|
||||
|
||||
&--preview {
|
||||
@apply sm:w-max-w-[70vw];
|
||||
}
|
||||
|
||||
&__close-button {
|
||||
@apply w-text-primary w-absolute w-left-3 w-top-3 hover:w-text-primary-200 w-bg-white w-p-3 w-hidden w-transition;
|
||||
|
||||
|
@ -49,12 +59,45 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__resize-grip-container {
|
||||
@apply w-absolute w-place-items-center w-hidden md:w-flex w-z-10 w-left-[-21.5px];
|
||||
|
||||
height: calc(100% - theme('spacing.8'));
|
||||
}
|
||||
|
||||
&__resize-grip {
|
||||
@apply w-text-primary hover:w-text-primary-200 w-border w-border-transparent w-rounded w-bg-white w-p-3 w-hidden w-touch-pinch-zoom w-cursor-ew-resize;
|
||||
|
||||
.form-side--open & {
|
||||
@apply w-flex;
|
||||
}
|
||||
|
||||
&:focus-within:has(:focus-visible) {
|
||||
@include focus-outline;
|
||||
}
|
||||
|
||||
@supports not selector(:focus-visible) {
|
||||
&:focus-within {
|
||||
/* Fallback for browsers without :focus-visible support */
|
||||
@include focus-outline;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
@apply w-w-4 w-h-4;
|
||||
}
|
||||
}
|
||||
|
||||
&__width-input {
|
||||
@apply w-w-0 w-h-0 w-opacity-0 w-absolute w-pointer-events-none;
|
||||
}
|
||||
|
||||
&--open .form-side__close-button,
|
||||
&--open .form-side__panel {
|
||||
@apply w-block;
|
||||
}
|
||||
|
||||
&__panel {
|
||||
@apply w-hidden w-h-full;
|
||||
@apply w-px-5 xl:w-px-10 w-py-4 w-w-full w-h-full w-overflow-y-auto w-scrollbar-thin w-hidden;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
}
|
||||
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
width: calc(var(--preview-iframe-width) * var(--preview-width-ratio));
|
||||
height: 100%;
|
||||
margin-inline-start: auto;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ngettext } from '../utils/gettext';
|
||||
|
||||
export default function initSidePanel() {
|
||||
const sidePanelWrapper = document.querySelector('[data-form-side]');
|
||||
|
||||
|
@ -7,6 +9,20 @@ export default function initSidePanel() {
|
|||
// For now, we do not want to persist the side panel state in the explorer
|
||||
const inExplorer = 'formSideExplorer' in sidePanelWrapper.dataset;
|
||||
|
||||
const resizeGrip = document.querySelector('[data-form-side-resize-grip]');
|
||||
const widthInput = document.querySelector('[data-form-side-width-input]');
|
||||
|
||||
const getSidePanelWidthStyles = () => {
|
||||
const sidePanelStyles = getComputedStyle(sidePanelWrapper);
|
||||
const minWidth = parseFloat(sidePanelStyles.minWidth);
|
||||
const maxWidth = parseFloat(sidePanelStyles.maxWidth);
|
||||
const width = parseFloat(sidePanelStyles.width);
|
||||
const range = maxWidth - minWidth;
|
||||
const percentage = ((width - minWidth) / range) * 100;
|
||||
|
||||
return { minWidth, maxWidth, width, range, percentage };
|
||||
};
|
||||
|
||||
const setPanel = (panelName) => {
|
||||
const body = document.querySelector('body');
|
||||
const selectedPanel = document.querySelector(
|
||||
|
@ -36,17 +52,20 @@ export default function initSidePanel() {
|
|||
}
|
||||
|
||||
document.querySelectorAll('[data-side-panel]').forEach((panel) => {
|
||||
if (panel.dataset.sidePanel === panelName) {
|
||||
const name = panel.dataset.sidePanel;
|
||||
if (name === panelName) {
|
||||
if (panel.hidden) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
panel.hidden = false;
|
||||
panel.dispatchEvent(new CustomEvent('show'));
|
||||
sidePanelWrapper.classList.add(`form-side--${name}`);
|
||||
body.classList.add('side-panel-open');
|
||||
}
|
||||
} else if (!panel.hidden) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
panel.hidden = true;
|
||||
panel.dispatchEvent(new CustomEvent('hide'));
|
||||
sidePanelWrapper.classList.remove(`form-side--${name}`);
|
||||
|
||||
if (panelName === '') {
|
||||
body.classList.remove('side-panel-open');
|
||||
|
@ -69,6 +88,15 @@ export default function initSidePanel() {
|
|||
} catch (e) {
|
||||
// Proceed without saving the last-open panel.
|
||||
}
|
||||
|
||||
// Update width input percentage as each panel may have its own maxWidth
|
||||
// (e.g. the preview panel), use timeout to wait until the resize
|
||||
// transition has finished
|
||||
setTimeout(() => {
|
||||
const { percentage } = getSidePanelWidthStyles();
|
||||
// Invert the percentage to make the slider work in the opposite direction
|
||||
widthInput.value = 100 - percentage;
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -99,6 +127,65 @@ export default function initSidePanel() {
|
|||
});
|
||||
}
|
||||
|
||||
const setSidePanelWidth = (targetWidth) => {
|
||||
const { minWidth, maxWidth, range, width } = getSidePanelWidthStyles();
|
||||
const newWidth =
|
||||
parseInt(Math.max(minWidth, Math.min(targetWidth, maxWidth)), 10) ||
|
||||
width;
|
||||
|
||||
const valueText = ngettext('{num} pixel', '{num} pixels', newWidth).replace(
|
||||
'{num}',
|
||||
newWidth,
|
||||
);
|
||||
|
||||
sidePanelWrapper.style.width = `${newWidth}px`;
|
||||
widthInput.value = 100 - ((newWidth - minWidth) / range) * 100;
|
||||
widthInput.setAttribute('aria-valuetext', valueText);
|
||||
|
||||
// Save the new width to localStorage unless we're in the explorer
|
||||
if (inExplorer) return;
|
||||
try {
|
||||
localStorage.setItem('wagtail:side-panel-width', newWidth);
|
||||
} catch (e) {
|
||||
// Proceed without saving the side panel width.
|
||||
}
|
||||
};
|
||||
|
||||
let startPos;
|
||||
let startWidth;
|
||||
|
||||
const onPointerMove = (e) => {
|
||||
if (!e.screenX || !startPos || !startWidth) return;
|
||||
const delta = startPos - e.screenX;
|
||||
setSidePanelWidth(startWidth + delta);
|
||||
};
|
||||
|
||||
resizeGrip.addEventListener('pointerdown', (e) => {
|
||||
// Remember the starting position and width of the side panel, so we can
|
||||
// calculate the new width based on the position change during the drag and
|
||||
// not resize the panel when it has gone past the minimum/maximum width.
|
||||
startPos = e.screenX;
|
||||
startWidth = getSidePanelWidthStyles().width;
|
||||
|
||||
document.body.classList.add('side-panel-resizing');
|
||||
resizeGrip.setPointerCapture(e.pointerId);
|
||||
resizeGrip.addEventListener('pointermove', onPointerMove);
|
||||
});
|
||||
|
||||
resizeGrip.addEventListener('pointerup', (e) => {
|
||||
resizeGrip.removeEventListener('pointermove', onPointerMove);
|
||||
resizeGrip.releasePointerCapture(e.pointerId);
|
||||
document.body.classList.remove('side-panel-resizing');
|
||||
});
|
||||
|
||||
// Handle resizing with keyboard using a hidden range input.
|
||||
widthInput.addEventListener('change', (event) => {
|
||||
const { minWidth, range } = getSidePanelWidthStyles();
|
||||
const inputPercentage = 100 - parseInt(event.target.value, 10);
|
||||
const newWidth = minWidth + (range * inputPercentage) / 100;
|
||||
setSidePanelWidth(newWidth);
|
||||
});
|
||||
|
||||
// Open the last opened panel if not in explorer,
|
||||
// use timeout to allow comments to load first
|
||||
setTimeout(() => {
|
||||
|
@ -107,8 +194,9 @@ export default function initSidePanel() {
|
|||
if (!inExplorer && sidePanelOpen) {
|
||||
setPanel(sidePanelOpen);
|
||||
}
|
||||
setSidePanelWidth(localStorage.getItem('wagtail:side-panel-width'));
|
||||
} catch (e) {
|
||||
// Proceed without remembering the last-open panel.
|
||||
// Proceed without remembering the last-open panel and the panel width.
|
||||
}
|
||||
|
||||
// Skip the animation on initial load only,
|
||||
|
|
|
@ -19,6 +19,7 @@ depth: 1
|
|||
* Ensure that the `rebuild_references_index` command can run without console output if called with `--verbosity 0` (Omerzahid Ali, Aman Pandey)
|
||||
* Add full support for secondary buttons with icons in the Wagtail design system - `button bicolor button--icon button-secondary` including the `button-small` variant (Seremba Patrick)
|
||||
* Add [`purge_embeds`](purge_embeds) management command to delete all the cached embed objects in the database (Aman Pandey)
|
||||
* Make it possible to resize the page editor’s side panels (Sage Abdullah)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
|
|
@ -5,6 +5,14 @@
|
|||
{% icon name="expand-right" %}
|
||||
</button>
|
||||
|
||||
<div class="form-side__resize-grip-container">
|
||||
<label class="form-side__resize-grip" data-form-side-resize-grip>
|
||||
<input type="range" step="10" class="form-side__width-input" name="wagtail-side-panel-width" data-form-side-width-input />
|
||||
<span class="w-sr-only">{% trans 'Side panel width' %}</span>
|
||||
{% icon name="grip" %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{% for panel in side_panels %}
|
||||
<div class="form-side__panel" data-side-panel="{{ panel.name }}" hidden>
|
||||
<h2 id="side-panel-{{ panel.name }}-title" class="w-sr-only">{{ panel.title }}</h2>
|
||||
|
|
Ładowanie…
Reference in New Issue