Skip to content

Commit 79c18b9

Browse files
committed
Make editor panes resizable
Fix #311
1 parent f8a03b4 commit 79c18b9

2 files changed

Lines changed: 128 additions & 83 deletions

File tree

src/lib/components/contents/details/content-details-overlay.svelte

Lines changed: 126 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<script>
2-
import { Alert, Button, EmptyState, Group, Toast } from '@sveltia/ui';
2+
import {
3+
Alert,
4+
Button,
5+
EmptyState,
6+
Group,
7+
ResizableHandle,
8+
ResizablePane,
9+
ResizablePaneGroup,
10+
Toast,
11+
} from '@sveltia/ui';
312
import { onMount, tick, untrack } from 'svelte';
413
import { _ } from 'svelte-i18n';
514
@@ -65,6 +74,28 @@
6574
const limit = $derived(entryCollection?.limit ?? Infinity);
6675
const createDisabled = $derived(!canCreateEntry(collection));
6776
77+
const [firstPaneSize, secondPaneSize, minPaneSize] = $derived.by(() => {
78+
if (
79+
typeof $editorFirstPane?.width === 'number' &&
80+
typeof $editorSecondPane?.width === 'number' &&
81+
$editorFirstPane.width >= 30 &&
82+
$editorSecondPane.width >= 30 &&
83+
$editorFirstPane.width + $editorSecondPane.width === 100
84+
) {
85+
return [$editorFirstPane.width, $editorSecondPane.width, 30];
86+
}
87+
88+
if (!$editorFirstPane && !$editorSecondPane) {
89+
return [0, 0, 0];
90+
}
91+
92+
if (!$editorFirstPane || !$editorSecondPane) {
93+
return [$editorFirstPane ? 100 : 0, $editorSecondPane ? 100 : 0, 0];
94+
}
95+
96+
return [50, 50, 30];
97+
});
98+
6899
/**
69100
* Restore the pane state from IndexedDB.
70101
* @returns {Promise<boolean>} Whether the panes are restored.
@@ -97,14 +128,10 @@
97128
restoring = true;
98129
await tick();
99130
$editorFirstPane = _editorFirstPane;
100-
$editorSecondPane = _editorSecondPane;
131+
$editorSecondPane = $isSmallScreen || $isMediumScreen ? null : _editorSecondPane;
101132
await tick();
102133
restoring = false;
103134
104-
if ($isSmallScreen || $isMediumScreen) {
105-
$editorSecondPane = null;
106-
}
107-
108135
return true;
109136
};
110137
@@ -212,6 +239,57 @@
212239
});
213240
</script>
214241
242+
{#snippet firstPane()}
243+
{#if $editorFirstPane}
244+
{@const { locale, mode } = $editorFirstPane}
245+
<div class="pane-wrapper">
246+
<Group
247+
class="pane"
248+
aria-label={$_(mode === 'edit' ? 'edit_x_locale' : 'preview_x_locale', {
249+
values: { locale: getLocaleLabel(locale) ?? locale },
250+
})}
251+
data-locale={locale}
252+
data-mode={mode}
253+
>
254+
<PaneHeader id="first-pane-header" thisPane={editorFirstPane} thatPane={editorSecondPane} />
255+
<PaneBody
256+
id="first-pane-body"
257+
thisPane={editorFirstPane}
258+
bind:thisPaneContentArea={firstPaneContentArea}
259+
bind:thatPaneContentArea={secondPaneContentArea}
260+
/>
261+
</Group>
262+
</div>
263+
{/if}
264+
{/snippet}
265+
266+
{#snippet secondPane()}
267+
{#if $editorSecondPane}
268+
{@const { locale, mode } = $editorSecondPane}
269+
<div class="pane-wrapper">
270+
<Group
271+
aria-label={$_(mode === 'edit' ? 'edit_x_locale' : 'preview_x_locale', {
272+
values: { locale: getLocaleLabel(locale) ?? locale },
273+
})}
274+
data-locale={locale}
275+
data-mode={mode}
276+
>
277+
<PaneHeader
278+
id="second-pane-header"
279+
thisPane={editorSecondPane}
280+
thatPane={editorFirstPane}
281+
/>
282+
<PaneBody
283+
id="second-pane-body"
284+
thisPane={editorSecondPane}
285+
bind:thisPaneContentArea={secondPaneContentArea}
286+
bind:thatPaneContentArea={firstPaneContentArea}
287+
/>
288+
</Group>
289+
</div>
290+
{/if}
291+
{/snippet}
292+
215293
<div
216294
role="group"
217295
class="wrapper content-editor"
@@ -248,57 +326,29 @@
248326
</div>
249327
</EmptyState>
250328
{:else}
251-
<div role="none" class="cols">
252-
{#if collection}
253-
{#if $editorFirstPane}
254-
<!-- Somehow we need a fallback object or we’ll get a property destructuring error -->
255-
{@const { locale, mode } = $editorFirstPane ?? {}}
256-
<Group
257-
class="pane"
258-
aria-label={$_(mode === 'edit' ? 'edit_x_locale' : 'preview_x_locale', {
259-
values: { locale: getLocaleLabel(locale) ?? locale },
260-
})}
261-
data-locale={locale}
262-
data-mode={mode}
263-
>
264-
<PaneHeader
265-
id="first-pane-header"
266-
thisPane={editorFirstPane}
267-
thatPane={editorSecondPane}
268-
/>
269-
<PaneBody
270-
id="first-pane-body"
271-
thisPane={editorFirstPane}
272-
bind:thisPaneContentArea={firstPaneContentArea}
273-
bind:thatPaneContentArea={secondPaneContentArea}
274-
/>
275-
</Group>
276-
{/if}
277-
{#if $editorSecondPane}
278-
<!-- Ditto -->
279-
{@const { locale, mode } = $editorSecondPane ?? {}}
280-
<Group
281-
aria-label={$_(mode === 'edit' ? 'edit_x_locale' : 'preview_x_locale', {
282-
values: { locale: getLocaleLabel(locale) ?? locale },
283-
})}
284-
data-locale={locale}
285-
data-mode={mode}
286-
>
287-
<PaneHeader
288-
id="second-pane-header"
289-
thisPane={editorSecondPane}
290-
thatPane={editorFirstPane}
291-
/>
292-
<PaneBody
293-
id="second-pane-body"
294-
thisPane={editorSecondPane}
295-
bind:thisPaneContentArea={secondPaneContentArea}
296-
bind:thatPaneContentArea={firstPaneContentArea}
297-
/>
298-
</Group>
299-
{/if}
329+
{#key collection}
330+
{#if $editorFirstPane && $editorSecondPane && firstPaneSize && secondPaneSize}
331+
<ResizablePaneGroup
332+
onResize={({ sizes }) => {
333+
if ($editorFirstPane && $editorSecondPane) {
334+
[$editorFirstPane.width, $editorSecondPane.width] = sizes;
335+
}
336+
}}
337+
>
338+
<ResizablePane defaultSize={firstPaneSize} minSize={minPaneSize}>
339+
{@render firstPane()}
340+
</ResizablePane>
341+
<ResizableHandle />
342+
<ResizablePane defaultSize={secondPaneSize} minSize={minPaneSize}>
343+
{@render secondPane()}
344+
</ResizablePane>
345+
</ResizablePaneGroup>
346+
{:else if $editorFirstPane}
347+
{@render firstPane()}
348+
{:else if $editorSecondPane}
349+
{@render secondPane()}
300350
{/if}
301-
</div>
351+
{/key}
302352
{/if}
303353
{/key}
304354
</div>
@@ -320,33 +370,26 @@
320370
flex-direction: column;
321371
background-color: var(--sui-secondary-background-color);
322372
323-
.cols {
324-
flex: auto;
325-
overflow: hidden;
326-
display: flex;
327-
gap: 4px;
328-
background-color: var(--sui-secondary-background-color); // same as toolbar
329-
330-
:global {
331-
& > div {
332-
display: flex;
333-
flex-direction: column;
334-
min-width: 480px;
335-
background-color: var(--sui-primary-background-color);
336-
transition: all 500ms;
337-
338-
&[data-mode='edit'] {
339-
flex: 1 1;
340-
}
341-
342-
&[data-mode='preview'] {
343-
flex: 2 1;
344-
}
345-
346-
@media (width < 768px) {
347-
min-width: auto;
348-
}
349-
}
373+
:global {
374+
.sui.resizable-pane-group {
375+
background-color: var(--sui-secondary-background-color); // same as toolbar
376+
}
377+
378+
.sui.resizable-pane {
379+
background-color: var(--sui-primary-background-color);
380+
}
381+
}
382+
}
383+
384+
.pane-wrapper {
385+
display: contents;
386+
387+
:global {
388+
& > .group {
389+
height: 100%;
390+
overflow: hidden;
391+
display: flex;
392+
flex-direction: column;
350393
}
351394
}
352395
}

src/lib/types/private.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,8 @@
882882
* @typedef {object} EntryEditorPane
883883
* @property {'edit' | 'preview'} mode Mode.
884884
* @property {InternalLocaleCode} locale Locale.
885+
* @property {number} [width] Percentage width of the pane in the editor layout. The sum of the
886+
* widths of all panes should be 100.
885887
*/
886888

887889
/**

0 commit comments

Comments
 (0)