Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pixel sizing support #58

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,25 @@ All notable changes to this project will be documented in this file. See [standa

## [0.7.12](https://github.com/orefalo/svelte-splitpanes/compare/v0.7.11...v0.7.12) (2023-01-25)


### Bug Fixes

* avoid equalizing on the edge case that there is not even a single pane ([0adb387](https://github.com/orefalo/svelte-splitpanes/commit/0adb38772490d61e84af4eb893af33c1483c4319))
* cleaner dragging calculation, and omit the buggy `margin-left: -1px;` from the splitter ([b453d74](https://github.com/orefalo/svelte-splitpanes/commit/b453d74ee7aff3c4812a3de2f2bbd2372ad597f3))
- avoid equalizing on the edge case that there is not even a single pane ([0adb387](https://github.com/orefalo/svelte-splitpanes/commit/0adb38772490d61e84af4eb893af33c1483c4319))
- cleaner dragging calculation, and omit the buggy `margin-left: -1px;` from the splitter ([b453d74](https://github.com/orefalo/svelte-splitpanes/commit/b453d74ee7aff3c4812a3de2f2bbd2372ad597f3))

## [0.7.11](https://github.com/orefalo/svelte-splitpanes/compare/v0.7.10...v0.7.11) (2023-01-09)


### Features

* add the \`strictEvents\` attribute to components, so the user will know that no other events happen ([db15de3](https://github.com/orefalo/svelte-splitpanes/commit/db15de3f79994690e5a259ca78e7b603c0012e2c))

- add the \`strictEvents\` attribute to components, so the user will know that no other events happen ([db15de3](https://github.com/orefalo/svelte-splitpanes/commit/db15de3f79994690e5a259ca78e7b603c0012e2c))

### Bug Fixes

* require svelte to be installed as a peer dependency ([ec13655](https://github.com/orefalo/svelte-splitpanes/commit/ec136552979b37f28f25edb16c62fa19f2928a9c))
* ssr issue on partially defined sizes ([5fb6661](https://github.com/orefalo/svelte-splitpanes/commit/5fb6661b7bd5d1b388a9e4ecd390a528eccce281))

- require svelte to be installed as a peer dependency ([ec13655](https://github.com/orefalo/svelte-splitpanes/commit/ec136552979b37f28f25edb16c62fa19f2928a9c))
- ssr issue on partially defined sizes ([5fb6661](https://github.com/orefalo/svelte-splitpanes/commit/5fb6661b7bd5d1b388a9e4ecd390a528eccce281))

### Miscellaneous Chores

* release 0.7.11 ([1df8854](https://github.com/orefalo/svelte-splitpanes/commit/1df8854bcf66365c39cd04ece32d6a5ce5a92287))
- release 0.7.11 ([1df8854](https://github.com/orefalo/svelte-splitpanes/commit/1df8854bcf66365c39cd04ece32d6a5ce5a92287))

## [0.7.10](https://github.com/orefalo/svelte-splitpanes/compare/v0.7.9...v0.7.10) (2023-01-02)

Expand Down
38 changes: 34 additions & 4 deletions src/lib/Pane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@
import type { ClientCallbacks, IPane, PaneInitFunction, SplitContext } from './index.js';
import { browser } from './internal/env.js';
import { gatheringKey } from './internal/GatheringRound.svelte';
import { getDimensionName } from './internal/utils/sizing.js';
import { getDimensionName, type SizeUnit } from './internal/utils/sizing.js';
import { carefullCallbackSource } from './internal/utils/functions';

const {
ssrRegisterPaneSize,
onPaneInit,
clientOnly: clientOnlyContext,
isHorizontal,
splitterDefaultSize,
splitterSumSize,
showFirstSplitter,
veryFirstPaneKey
} = getContext<SplitContext>(KEY);

// PROPS

export let size: number | null = null;
export let sizeUnit: SizeUnit = '%';
export let minSize = 0;
export let minSizeUnit: SizeUnit = '%';
export let maxSize = 100;
export let maxSizeUnit: SizeUnit = '%';
export let snapSize = 0;
export let snapSizeUnit: SizeUnit = '%';
/** The size of the splitter in pixels. */
export let splitterSize: number | null = null;
// css class
let clazz = '';
export { clazz as class };
Expand All @@ -35,7 +43,7 @@
const { undefinedPaneInitSize } = (!gathering ? onPaneInit(key) : {}) as ReturnType<PaneInitFunction>;

let element: HTMLElement;
let sz: number = size ?? undefinedPaneInitSize;
let sz: number = size ?? (sizeUnit === '%' ? undefinedPaneInitSize : 0);
let isSplitterActive = false;

// CALLBACKS
Expand All @@ -60,23 +68,44 @@
}
};
$: {
// TODO: When the user min/max size gets changed, need to calc the size again
if (browser && size != null) {
reportGivenSizeChangeSafe(size);
}
}

$: {
if (browser) {
carefullClientCallbacks('reportSplitterSizeChange')(splitterSize);
}
}

$: dimension = getDimensionName($isHorizontal);

$: style = `${dimension}: ${sz}%;`;
const renderSize = (sz: number, szPx: number) => {
if (szPx === 0) {
return `${sz}%`;
} else if (sz === 0) {
return `${szPx}px`;
} else {
const signPx = szPx < 0 ? '-' : '+';
return `calc(${sz}% ${signPx} ${Math.abs(szPx)}px)`;
}
};
/** Removes the relative total splitter size (only on % unit) and render it */
const displayedSize = (sz: number, sizeUnit: SizeUnit) =>
sizeUnit === '%' ? renderSize(sz, (-sz / 100) * $splitterSumSize) : renderSize(0, sz);
$: style = `${dimension}: ${displayedSize(sz, sizeUnit)};`;

if (gathering) {
ssrRegisterPaneSize(size);
ssrRegisterPaneSize(size, splitterSize, sizeUnit);
} else if (browser) {
onMount(() => {
const inst: IPane = {
key,
element: element,
givenSize: size,
givenSplitterSize: splitterSize,
sz: () => sz,
setSz: (v) => {
sz = v;
Expand Down Expand Up @@ -111,6 +140,7 @@
{#if $veryFirstPaneKey !== key || $showFirstSplitter}
<div
class="splitpanes__splitter {isSplitterActive ? 'splitpanes__splitter__active' : ''}"
style="{dimension}: {splitterSize ?? $splitterDefaultSize}px;"
on:mousedown={carefullClientCallbacks('onSplitterDown')}
on:touchstart={carefullClientCallbacks('onSplitterDown')}
on:click={carefullClientCallbacks('onSplitterClick')}
Expand Down
103 changes: 62 additions & 41 deletions src/lib/Splitpanes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

<script lang="ts" strictEvents>
import { onMount, onDestroy, setContext, createEventDispatcher, tick, afterUpdate } from 'svelte';
import { writable } from 'svelte/store';
import { derived, writable } from 'svelte/store';
import type { IPane, IPaneSizingEvent, SplitContext, PaneInitFunction, ClientCallbacks } from './index.js';
import GatheringRound from './internal/GatheringRound.svelte';
import { browser } from './internal/env.js';
import { getDimensionName } from './internal/utils/sizing.js';
import { getDimensionName, type SizeUnit } from './internal/utils/sizing.js';
import {
type Position,
elementRectWithoutBorder,
getGlobalMousePosition,
positionDiff,
getElementRect
} from './internal/utils/position.js';
import { forEachPartial, sumPartial } from './internal/utils/array.js';
import { forEachPartial, sumPartial, sum3Way } from './internal/utils/array.js';
import { calcComputedStyle } from './internal/utils/styling.js';

// TYPE DECLARATIONS ----------------
Expand All @@ -40,6 +40,10 @@
export let dblClickSplitter = true;
// true if RTL
export let rtl: boolean | 'auto' = 'auto';
/** The size of the splitter in pixels.
*
* Can be override in the pane props by the user. */
export let splitterSize = 7;
// true to display the first splitter
export let firstSplitter = false;
// css style
Expand Down Expand Up @@ -85,25 +89,35 @@
let panes = new Array<IPane>();
// passed to the children via the context - writable to ensure proper reactivity
let isHorizontal = writable<boolean>(horizontal);
const splitterDefaultSize = writable<number>(splitterSize);
const splitterSumSize = writable<number>(0);
const showFirstSplitter = writable<boolean>(firstSplitter);
// tells the key of the very first pane, or undefined if not recieved yet
const veryFirstPaneKey = writable<any>(undefined);
let activeSplitterElement: HTMLElement | null = null;
let activeSplitterDrag: number | null = null;
let ssrRegisterPaneSizeCalled = false;
let ssrPaneDefinedSizeSum = 0;
let ssrPaneUndefinedSizeCount = 0;

// REACTIVE ----------------

$: $isHorizontal = horizontal;
$: $splitterDefaultSize = splitterSize;
$: $showFirstSplitter = firstSplitter;

function ssrRegisterPaneSize(size: number | null) {
const calcPaneSplitterSize = (afterFirst: boolean, paneSplitterSize: number | null) =>
afterFirst || firstSplitter ? paneSplitterSize ?? splitterSize : 0;

function ssrRegisterPaneSize(size: number | null, paneSplitterSize: number | null, unit: SizeUnit) {
if (size == null) {
++ssrPaneUndefinedSizeCount;
} else {
} else if (unit === '%') {
ssrPaneDefinedSizeSum += size;
}

splitterSumSize.update((prevSum) => prevSum + calcPaneSplitterSize(ssrRegisterPaneSizeCalled, paneSplitterSize));

ssrRegisterPaneSizeCalled = true;
}

const onPaneInit: PaneInitFunction = (key: any) => {
Expand All @@ -120,6 +134,8 @@
showFirstSplitter,
veryFirstPaneKey,
isHorizontal,
splitterDefaultSize,
splitterSumSize,
ssrRegisterPaneSize: browser ? undefined : ssrRegisterPaneSize,
onPaneInit,
clientOnly: browser
Expand All @@ -146,12 +162,14 @@
//inserts pane at proper array index
panes.splice(index, 0, pane);

// reindex panes
// reindex panes and update splitter sum
for (let i = 0; i < panes.length; i++) {
panes[i].index = i;
}

if (isReady) {
recalcSplitterSizeSum();

// 2. tick and resize the panes.
tickAndResetPaneSizes().then(() => {
// 3. Set the pane as ready
Expand All @@ -178,7 +196,8 @@
onSplitterClick: paneForward(onSplitterClick, false),
onSplitterDblClick: paneForward(onSplitterDblClick),
onPaneClick: paneForward(onPaneClick),
reportGivenSizeChange: paneForward(reportGivenSizeChange)
reportGivenSizeChange: paneForward(reportGivenSizeChange),
reportSplitterSizeChange: paneForward(reportSplitterSizeChange)
};
}

Expand All @@ -200,6 +219,8 @@
}

if (isReady) {
recalcSplitterSizeSum();

// 3. tick and resize the panes.
await tickAndResetPaneSizes();

Expand All @@ -223,10 +244,31 @@
tickAndResetPaneSizes();
}

function reportSplitterSizeChange(newSplitterSize: number | null, pane: IPane | undefined) {
let newSum = 0;

for (let i = 0; i < panes.length; i++) {
const currentPane = panes[i];
if (currentPane === pane) {
currentPane.givenSplitterSize = newSplitterSize;
}

newSum += calcPaneSplitterSize(i > 0, currentPane.givenSplitterSize);
}

splitterSumSize.set(newSum);
}

const recalcSplitterSizeSum = () => reportSplitterSizeChange(null, undefined);

onMount(() => {
verifyAndUpdatePanesOrder();
resetPaneSizes();

// Trigger update when the splitpanes properties `splitterDefaultSize` and `showFirstSplitter` are changed,
// and also on the first time before the change.
const unsubscriber = derived([splitterDefaultSize, showFirstSplitter], () => 0).subscribe(recalcSplitterSizeSum);

for (let i = 0; i < panes.length; i++) {
panes[i].isReady = true;
}
Expand All @@ -236,6 +278,9 @@
setTimeout(() => {
isAfterInitialTimeoutZero = true;
}, 0);

// This will tell Svelte to unsubscribe when the component is being unmounted.
return unsubscriber;
});

if (browser) {
Expand Down Expand Up @@ -326,10 +371,8 @@
return; // Don't bind move event on error
}

activeSplitterElement = activeSplitterNode as HTMLElement;

const globalMousePosition = getGlobalMousePosition(event);
const splitterRect = getElementRect(activeSplitterElement);
const splitterRect = getElementRect(activeSplitterNode as HTMLElement);
activeSplitterDrag = getOrientedDiff(
positionDiff(globalMousePosition, splitterRect),
splitterRect[getCurrentDimensionName()],
Expand Down Expand Up @@ -477,35 +520,13 @@
snap: pane.snap()
}));

/**
* Returns the drag percentage of the splitter relative to the 2 parts it's inbetween, meaning the ratio between
* the size that all the panes before the splitter consumes (ignoring other splitters size) and the total size of the container.
*/
// Calculate the ratio by taking into account that the splitters also takes up space
function getCurrentDragPercentage(tdrag: number, containerSizeWithoutBorder: number) {
// Here we want the splitter size **including the borders**.
// We need to use `Element.getBoundingClientRect()` and not `Element.clientWidth` and `Element.clientHeight`,
// bacause the latter round the number of pixels to integer, and additionally, they don't include the borders.
const splitterSize = (node: Node) => getElementRect(node as HTMLElement)[getCurrentDimensionName()];

const activeSplitterSize = splitterSize(activeSplitterElement);

let splittersTotalSizeBefore = 0;
let currentBeforeNode = activeSplitterElement.previousSibling;
while (currentBeforeNode != null) {
if (isSplitterElement(currentBeforeNode)) {
splittersTotalSizeBefore += splitterSize(currentBeforeNode);
}
currentBeforeNode = currentBeforeNode.previousSibling;
}

let splittersTotalSizeAfter = 0;
let currentAfterNode = activeSplitterElement.nextSibling;
while (currentAfterNode != null) {
if (isSplitterElement(currentAfterNode)) {
splittersTotalSizeAfter += splitterSize(currentAfterNode);
}
currentAfterNode = currentAfterNode.nextSibling;
}
const {
start: splittersTotalSizeBefore,
middle: activeSplitterSize,
end: splittersTotalSizeAfter
} = sum3Way(panes, activeSplitter, (pane, i) => calcPaneSplitterSize(i > 0, pane.givenSplitterSize));

const totalSplitterBefore = splittersTotalSizeBefore + activeSplitterDrag;
const totalSplitter = splittersTotalSizeBefore + activeSplitterSize + splittersTotalSizeAfter;
Expand Down Expand Up @@ -895,6 +916,8 @@

panes = newPanes;
$veryFirstPaneKey = panes.length > 0 ? panes[0].key : undefined;

recalcSplitterSizeSum();
}
}
</script>
Expand Down Expand Up @@ -1011,7 +1034,6 @@
}
&.splitpanes--vertical > .splitpanes__splitter,
.splitpanes--vertical > .splitpanes__splitter {
width: 7px;
border-left: 1px solid #eee;
cursor: col-resize;
&:before,
Expand All @@ -1029,7 +1051,6 @@
}
&.splitpanes--horizontal > .splitpanes__splitter,
.splitpanes--horizontal > .splitpanes__splitter {
height: 7px;
border-top: 1px solid #eee;
cursor: row-resize;
&:before,
Expand Down
Loading