flowbite-svelte 1.20.1 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/kanban/KanbanCard.svelte +1 -1
- package/dist/kanban/KanbanCard.svelte.d.ts +1 -1
- package/dist/mega-menu/MegaMenu.svelte +1 -1
- package/dist/mega-menu/theme.js +2 -2
- package/dist/split-pane/Divider.svelte +47 -0
- package/dist/split-pane/Divider.svelte.d.ts +17 -0
- package/dist/split-pane/Pane.svelte +47 -0
- package/dist/split-pane/Pane.svelte.d.ts +13 -0
- package/dist/split-pane/SplitPane.svelte +403 -0
- package/dist/split-pane/SplitPane.svelte.d.ts +33 -0
- package/dist/split-pane/index.d.ts +4 -0
- package/dist/split-pane/index.js +4 -0
- package/dist/split-pane/theme.d.ts +65 -0
- package/dist/split-pane/theme.js +45 -0
- package/dist/theme/themes.d.ts +1 -0
- package/dist/theme/themes.js +1 -0
- package/dist/tooltip/theme.d.ts +3 -0
- package/dist/tooltip/theme.js +8 -1
- package/dist/types.d.ts +30 -5
- package/dist/typography/a/A.svelte +1 -1
- package/dist/typography/a/A.svelte.d.ts +1 -1
- package/dist/typography/blockquote/Blockquote.svelte +1 -1
- package/dist/typography/blockquote/Blockquote.svelte.d.ts +1 -1
- package/dist/typography/descriptionlist/DescriptionList.svelte +1 -1
- package/dist/typography/descriptionlist/DescriptionList.svelte.d.ts +1 -1
- package/dist/typography/heading/Heading.svelte +1 -1
- package/dist/typography/heading/Heading.svelte.d.ts +1 -1
- package/dist/typography/img/EnhancedImg.svelte +1 -1
- package/dist/typography/img/EnhancedImg.svelte.d.ts +1 -1
- package/dist/typography/img/Img.svelte +1 -1
- package/dist/typography/img/Img.svelte.d.ts +1 -1
- package/dist/typography/layout/Layout.svelte +1 -1
- package/dist/typography/layout/Layout.svelte.d.ts +1 -1
- package/dist/typography/list/Li.svelte +1 -1
- package/dist/typography/list/Li.svelte.d.ts +1 -1
- package/dist/typography/list/List.svelte +1 -1
- package/dist/typography/list/List.svelte.d.ts +1 -1
- package/dist/typography/mark/Mark.svelte +1 -1
- package/dist/typography/mark/Mark.svelte.d.ts +1 -1
- package/dist/typography/paragraph/P.svelte +1 -1
- package/dist/typography/paragraph/P.svelte.d.ts +1 -1
- package/dist/typography/secondary/Secondary.svelte +1 -1
- package/dist/typography/secondary/Secondary.svelte.d.ts +1 -1
- package/dist/typography/span/Span.svelte +1 -1
- package/dist/typography/span/Span.svelte.d.ts +1 -1
- package/dist/utils/Arrow.svelte +1 -1
- package/dist/utils/Arrow.svelte.d.ts +1 -1
- package/dist/utils/Popper.svelte +75 -65
- package/dist/utils/Popper.svelte.d.ts +7 -6
- package/dist/utils/debounce.d.ts +17 -0
- package/dist/utils/debounce.js +41 -0
- package/dist/video/Video.svelte +1 -1
- package/dist/video/Video.svelte.d.ts +1 -1
- package/package.json +13 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
@component
|
|
48
48
|
[Go to docs](https://flowbite-svelte.com/)
|
|
49
49
|
## Type
|
|
50
|
-
[KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
50
|
+
[KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2116)
|
|
51
51
|
## Props
|
|
52
52
|
@prop card
|
|
53
53
|
@prop isDragging = false
|
|
@@ -2,7 +2,7 @@ import type { KanbanCardProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2116)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop card
|
|
8
8
|
* @prop isDragging = false
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
const { base, div, ul, extra: extraCls } = $derived(megamenu({ full, hasExtra: !!extra }));
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
|
-
<Popper
|
|
17
|
+
<Popper arrow={false} bind:isOpen trigger="click" placement="bottom" yOnly={full} {...restProps} class={base({ class: clsx(theme?.base, className) })}>
|
|
18
18
|
<div class={div({ class: clsx(theme?.div, classes?.div) })}>
|
|
19
19
|
<ul class={ul({ class: clsx(theme?.ul, styling.ul) })}>
|
|
20
20
|
{#each items as item, index}
|
package/dist/mega-menu/theme.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { tv } from "tailwind-variants";
|
|
2
2
|
export const megamenu = tv({
|
|
3
3
|
slots: {
|
|
4
|
-
base: "w-fit bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-lg border border-gray-100 dark:border-gray-600 divide-gray-100 dark:divide-gray-600",
|
|
4
|
+
base: "w-fit bg-white shadow-md dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-lg border border-gray-100 dark:border-gray-600 divide-gray-100 dark:divide-gray-600",
|
|
5
5
|
div: "flex flex-col md:flex-row p-4 max-w-(--breakpoint-md) justify-center mx-auto mt-2",
|
|
6
6
|
ul: "grid grid-flow-row gap-y-4 md:gap-x-0 auto-col-max auto-row-max grid-cols-2 md:grid-cols-3 text-sm font-medium",
|
|
7
7
|
extra: "md:w-1/3 mt-4 md:mt-0"
|
|
8
8
|
},
|
|
9
9
|
variants: {
|
|
10
10
|
full: {
|
|
11
|
-
true: { base: "border-y w-full ml-0 rounded-none" }
|
|
11
|
+
true: { base: "border-y shadow-xs w-full ml-0 rounded-none" }
|
|
12
12
|
},
|
|
13
13
|
hasExtra: {
|
|
14
14
|
true: {}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DividerProps } from "../types";
|
|
3
|
+
import { divider, dividerHitArea } from "./theme";
|
|
4
|
+
import { getTheme } from "../theme/themeUtils";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
|
|
7
|
+
let { direction, index, onMouseDown, onKeyDown, isDragging, currentSize, class: className = "" }: DividerProps = $props();
|
|
8
|
+
|
|
9
|
+
const themePane = getTheme("divider");
|
|
10
|
+
const themeDividerHitArea = getTheme("dividerHitArea");
|
|
11
|
+
|
|
12
|
+
const isHorizontal = $derived(direction === "horizontal");
|
|
13
|
+
const roundedSize = $derived(Math.round(currentSize));
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
17
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
18
|
+
<div
|
|
19
|
+
role="separator"
|
|
20
|
+
tabindex="0"
|
|
21
|
+
aria-orientation={isHorizontal ? "vertical" : "horizontal"}
|
|
22
|
+
aria-label={`Resize ${isHorizontal ? "horizontal" : "vertical"} panes`}
|
|
23
|
+
aria-valuenow={roundedSize}
|
|
24
|
+
aria-valuemin="0"
|
|
25
|
+
aria-valuemax="100"
|
|
26
|
+
aria-valuetext={`${roundedSize} percent`}
|
|
27
|
+
class={divider({ direction, isDragging, class: clsx(themePane, className) })}
|
|
28
|
+
onmousedown={(e) => onMouseDown(e, index)}
|
|
29
|
+
onkeydown={(e) => onKeyDown(e, index)}
|
|
30
|
+
>
|
|
31
|
+
<div class={dividerHitArea({ direction, class: clsx(themeDividerHitArea, className) })}></div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!--
|
|
35
|
+
@component
|
|
36
|
+
[Go to docs](https://flowbite-svelte.com/)
|
|
37
|
+
## Type
|
|
38
|
+
[DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2145)
|
|
39
|
+
## Props
|
|
40
|
+
@prop direction
|
|
41
|
+
@prop index
|
|
42
|
+
@prop onMouseDown
|
|
43
|
+
@prop onKeyDown
|
|
44
|
+
@prop isDragging
|
|
45
|
+
@prop currentSize
|
|
46
|
+
@prop class: className = ""
|
|
47
|
+
-->
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DividerProps } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
|
+
* ## Type
|
|
5
|
+
* [DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2145)
|
|
6
|
+
* ## Props
|
|
7
|
+
* @prop direction
|
|
8
|
+
* @prop index
|
|
9
|
+
* @prop onMouseDown
|
|
10
|
+
* @prop onKeyDown
|
|
11
|
+
* @prop isDragging
|
|
12
|
+
* @prop currentSize
|
|
13
|
+
* @prop class: className = ""
|
|
14
|
+
*/
|
|
15
|
+
declare const Divider: import("svelte").Component<DividerProps, {}, "">;
|
|
16
|
+
type Divider = ReturnType<typeof Divider>;
|
|
17
|
+
export default Divider;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PaneProps } from "../types";
|
|
3
|
+
import { getSplitPaneContext } from "./SplitPane.svelte";
|
|
4
|
+
import Divider from "./Divider.svelte";
|
|
5
|
+
import { pane } from "./theme";
|
|
6
|
+
import { getTheme } from "../theme/themeUtils";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
|
|
9
|
+
let { children, class: className = "", style = "" }: PaneProps = $props();
|
|
10
|
+
|
|
11
|
+
const theme = getTheme("pane");
|
|
12
|
+
|
|
13
|
+
const context = getSplitPaneContext();
|
|
14
|
+
const paneIndex = context ? context.registerPane() : 0;
|
|
15
|
+
|
|
16
|
+
const paneStyle = $derived.by(() => {
|
|
17
|
+
const styles = [style];
|
|
18
|
+
if (context) {
|
|
19
|
+
const contextStyle = context.getPaneStyle(paneIndex);
|
|
20
|
+
styles.push(contextStyle);
|
|
21
|
+
}
|
|
22
|
+
return styles.filter(Boolean).join("; ");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const showDivider = $derived(context?.shouldRenderDivider(paneIndex) ?? false);
|
|
26
|
+
const direction = $derived(context?.getDirection() ?? "horizontal");
|
|
27
|
+
const isDragging = $derived(context?.getIsDragging() ?? false);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div class={pane({ class: clsx(theme, className) })} style={paneStyle}>
|
|
31
|
+
{@render children?.()}
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{#if showDivider && context}
|
|
35
|
+
<Divider {direction} index={paneIndex} {isDragging} currentSize={context.getPaneSize(paneIndex)} onMouseDown={context.onMouseDown} onKeyDown={context.onKeyDown} />
|
|
36
|
+
{/if}
|
|
37
|
+
|
|
38
|
+
<!--
|
|
39
|
+
@component
|
|
40
|
+
[Go to docs](https://flowbite-svelte.com/)
|
|
41
|
+
## Type
|
|
42
|
+
[PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2139)
|
|
43
|
+
## Props
|
|
44
|
+
@prop children
|
|
45
|
+
@prop class: className = ""
|
|
46
|
+
@prop style = ""
|
|
47
|
+
-->
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PaneProps } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
|
+
* ## Type
|
|
5
|
+
* [PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2139)
|
|
6
|
+
* ## Props
|
|
7
|
+
* @prop children
|
|
8
|
+
* @prop class: className = ""
|
|
9
|
+
* @prop style = ""
|
|
10
|
+
*/
|
|
11
|
+
declare const Pane: import("svelte").Component<PaneProps, {}, "">;
|
|
12
|
+
type Pane = ReturnType<typeof Pane>;
|
|
13
|
+
export default Pane;
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { setContext, getContext } from "svelte";
|
|
3
|
+
|
|
4
|
+
const SPLIT_PANE_KEY = Symbol("SPLIT_PANE");
|
|
5
|
+
|
|
6
|
+
interface SplitPaneContext {
|
|
7
|
+
registerPane: () => number;
|
|
8
|
+
getPaneStyle: (index: number) => string;
|
|
9
|
+
getPaneSize: (index: number) => number;
|
|
10
|
+
shouldRenderDivider: (index: number) => boolean;
|
|
11
|
+
getDirection: () => "horizontal" | "vertical";
|
|
12
|
+
getIsDragging: () => boolean;
|
|
13
|
+
onMouseDown: (e: MouseEvent, index: number) => void;
|
|
14
|
+
onKeyDown: (e: KeyboardEvent, index: number) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function setSplitPaneContext(ctx: SplitPaneContext) {
|
|
18
|
+
setContext(SPLIT_PANE_KEY, ctx);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getSplitPaneContext(): SplitPaneContext | undefined {
|
|
22
|
+
return getContext(SPLIT_PANE_KEY);
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import type { SplitPaneProps } from "../types";
|
|
28
|
+
import { splitpane } from "./theme";
|
|
29
|
+
import { getTheme } from "../theme/themeUtils";
|
|
30
|
+
import clsx from "clsx";
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
direction = "horizontal",
|
|
34
|
+
minSize = 100,
|
|
35
|
+
responsive = true,
|
|
36
|
+
breakpoint = 768,
|
|
37
|
+
transition: transitionProp = true,
|
|
38
|
+
transitionDuration = 150,
|
|
39
|
+
keyboardStep = 2,
|
|
40
|
+
initialSizes,
|
|
41
|
+
onResize,
|
|
42
|
+
children,
|
|
43
|
+
class: className = ""
|
|
44
|
+
}: SplitPaneProps = $props();
|
|
45
|
+
|
|
46
|
+
const TOLERANCE = 0.5; // For pixel comparisons (minSize - TOLERANCE)
|
|
47
|
+
const MIN_CHANGE_THRESHOLD = 0.01; // For percentage changes (size deltas)
|
|
48
|
+
const MIN_DELTA = 1; // For mouse movement (Math.abs(delta) < MIN_DELTA)
|
|
49
|
+
|
|
50
|
+
// Validate numeric props
|
|
51
|
+
if (minSize <= 0) {
|
|
52
|
+
console.warn(`minSize must be positive, got ${minSize}. Using default 100.`);
|
|
53
|
+
minSize = 100;
|
|
54
|
+
}
|
|
55
|
+
if (keyboardStep <= 0) {
|
|
56
|
+
console.warn(`keyboardStep must be positive, got ${keyboardStep}. Using default 2.`);
|
|
57
|
+
keyboardStep = 2;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let transition = $state(transitionProp);
|
|
61
|
+
$effect(() => {
|
|
62
|
+
// syncing local transition state with prop changes
|
|
63
|
+
if (!isDragging) {
|
|
64
|
+
transition = transitionProp;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const theme = getTheme("splitpane");
|
|
68
|
+
|
|
69
|
+
let isDragging = $state(false);
|
|
70
|
+
let startPos = $state(0);
|
|
71
|
+
let sizes = $state<number[]>([]);
|
|
72
|
+
let container: HTMLDivElement;
|
|
73
|
+
let currentDirection = $state(direction);
|
|
74
|
+
let registeredPanes = $state(0);
|
|
75
|
+
|
|
76
|
+
// Register panes as they mount
|
|
77
|
+
function registerPane(): number {
|
|
78
|
+
const index = registeredPanes;
|
|
79
|
+
registeredPanes++;
|
|
80
|
+
return index;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getPaneStyle(index: number): string {
|
|
84
|
+
if (sizes[index] === undefined) return "";
|
|
85
|
+
|
|
86
|
+
const size = `${sizes[index]}%`;
|
|
87
|
+
const transitionStyle = transition ? `${currentDirection === "horizontal" ? "width" : "height"} ${transitionDuration}ms ease` : "none";
|
|
88
|
+
|
|
89
|
+
if (currentDirection === "horizontal") {
|
|
90
|
+
return `width: ${size}; height: 100%; transition: ${transitionStyle};`;
|
|
91
|
+
} else {
|
|
92
|
+
return `height: ${size}; width: 100%; transition: ${transitionStyle};`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shouldRenderDivider(paneIndex: number): boolean {
|
|
97
|
+
return paneIndex < registeredPanes - 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Set context for child Pane components
|
|
101
|
+
setSplitPaneContext({
|
|
102
|
+
registerPane,
|
|
103
|
+
getPaneStyle,
|
|
104
|
+
getPaneSize: (index: number) => sizes[index] ?? (registeredPanes > 0 ? 100 / registeredPanes : 0),
|
|
105
|
+
shouldRenderDivider,
|
|
106
|
+
getDirection: () => currentDirection,
|
|
107
|
+
getIsDragging: () => isDragging,
|
|
108
|
+
onMouseDown: startResize,
|
|
109
|
+
onKeyDown: handleKeyResize
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
let containerSize = $state(0);
|
|
113
|
+
|
|
114
|
+
// Track container size changes
|
|
115
|
+
$effect(() => {
|
|
116
|
+
if (!container) return;
|
|
117
|
+
|
|
118
|
+
const updateSize = () => {
|
|
119
|
+
containerSize = currentDirection === "horizontal" ? container.offsetWidth : container.offsetHeight;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
updateSize();
|
|
123
|
+
|
|
124
|
+
const resizeObserver = new ResizeObserver(updateSize);
|
|
125
|
+
resizeObserver.observe(container);
|
|
126
|
+
|
|
127
|
+
return () => resizeObserver.disconnect();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Initialize and maintain sizes
|
|
131
|
+
$effect(() => {
|
|
132
|
+
if (registeredPanes === 0) {
|
|
133
|
+
sizes = [];
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// If container not ready yet, use equal distribution
|
|
138
|
+
if (containerSize < 1) {
|
|
139
|
+
if (sizes.length !== registeredPanes) {
|
|
140
|
+
const equal = 100 / registeredPanes;
|
|
141
|
+
sizes = Array.from({ length: registeredPanes }, () => equal);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const minPercent = (minSize / containerSize) * 100;
|
|
147
|
+
|
|
148
|
+
// Check if minSize is achievable for all panes
|
|
149
|
+
const totalMinRequired = minPercent * registeredPanes;
|
|
150
|
+
if (totalMinRequired > 100) {
|
|
151
|
+
console.error(
|
|
152
|
+
`Cannot satisfy minSize=${minSize}px for ${registeredPanes} panes in ${containerSize}px container. ` + `Required: ${(minSize * registeredPanes).toFixed(0)}px. Using equal distribution.`
|
|
153
|
+
);
|
|
154
|
+
const equal = 100 / registeredPanes;
|
|
155
|
+
sizes = Array.from({ length: registeredPanes }, () => equal);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if current sizes violate minSize constraints
|
|
160
|
+
const currentPixelSizes = sizes.map((s) => (s / 100) * containerSize);
|
|
161
|
+
const violatesMinSize = currentPixelSizes.some((pixelSize) => pixelSize < minSize - TOLERANCE); // small tolerance
|
|
162
|
+
|
|
163
|
+
if (violatesMinSize) {
|
|
164
|
+
// Recalculate sizes to respect minSize
|
|
165
|
+
let newSizes = sizes.map((s) => Math.max((s / 100) * containerSize, minSize));
|
|
166
|
+
const totalPixels = newSizes.reduce((a, b) => a + b, 0);
|
|
167
|
+
|
|
168
|
+
// Convert back to percentages
|
|
169
|
+
if (totalPixels > containerSize) {
|
|
170
|
+
// If we can't fit all panes at minSize, distribute proportionally
|
|
171
|
+
newSizes = newSizes.map((s) => (s / totalPixels) * containerSize);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
sizes = newSizes.map((pixelSize) => (pixelSize / containerSize) * 100);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Handle initialSizes (only on first initialization)
|
|
178
|
+
if (initialSizes && initialSizes.length === registeredPanes && sizes.length !== registeredPanes) {
|
|
179
|
+
const hasInvalidValues = initialSizes.some((s) => s < 0 || !isFinite(s));
|
|
180
|
+
|
|
181
|
+
if (hasInvalidValues) {
|
|
182
|
+
console.warn("initialSizes contains invalid values. Using equal distribution.");
|
|
183
|
+
const equal = Math.max(100 / registeredPanes, minPercent);
|
|
184
|
+
sizes = Array.from({ length: registeredPanes }, () => equal);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const sum = initialSizes.reduce((a, b) => a + b, 0);
|
|
189
|
+
|
|
190
|
+
if (sum <= 0 || sum < 0.01) {
|
|
191
|
+
console.warn("initialSizes sum to zero. Using equal distribution.");
|
|
192
|
+
const equal = Math.max(100 / registeredPanes, minPercent);
|
|
193
|
+
sizes = Array.from({ length: registeredPanes }, () => equal);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let normalizedSizes = initialSizes.map((s) => (s / sum) * 100);
|
|
198
|
+
|
|
199
|
+
const violatesConstraints = normalizedSizes.some((size) => size < minPercent);
|
|
200
|
+
|
|
201
|
+
if (violatesConstraints) {
|
|
202
|
+
console.warn(
|
|
203
|
+
`initialSizes [${normalizedSizes.map((s) => s.toFixed(1)).join("%, ")}%] ` +
|
|
204
|
+
`violate minSize constraint (${minSize}px = ${minPercent.toFixed(1)}%). ` +
|
|
205
|
+
`Adjusting to respect minimum constraints.`
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
normalizedSizes = normalizedSizes.map((size) => Math.max(size, minPercent));
|
|
209
|
+
const newSum = normalizedSizes.reduce((a, b) => a + b, 0);
|
|
210
|
+
normalizedSizes = normalizedSizes.map((size) => (size / newSum) * 100);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
sizes = normalizedSizes;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Default: equal distribution respecting minSize
|
|
218
|
+
if (sizes.length !== registeredPanes) {
|
|
219
|
+
const equal = Math.max(100 / registeredPanes, minPercent);
|
|
220
|
+
sizes = Array.from({ length: registeredPanes }, () => equal);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Responsive direction handling
|
|
225
|
+
$effect(() => {
|
|
226
|
+
if (!responsive) {
|
|
227
|
+
currentDirection = direction;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (typeof window === "undefined") {
|
|
232
|
+
currentDirection = direction;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
237
|
+
|
|
238
|
+
const handleResize = () => {
|
|
239
|
+
// deferring the direction switch until the drag completes
|
|
240
|
+
if (!isDragging) {
|
|
241
|
+
currentDirection = mq.matches ? "vertical" : "horizontal";
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
handleResize();
|
|
246
|
+
mq.addEventListener("change", handleResize);
|
|
247
|
+
|
|
248
|
+
return () => mq.removeEventListener("change", handleResize);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Notify parent of size changes
|
|
252
|
+
$effect(() => {
|
|
253
|
+
if (sizes.length > 0 && onResize) {
|
|
254
|
+
onResize(sizes);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
function startResize(e: MouseEvent, index: number) {
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
isDragging = true;
|
|
261
|
+
transition = false;
|
|
262
|
+
startPos = currentDirection === "horizontal" ? e.clientX : e.clientY;
|
|
263
|
+
|
|
264
|
+
const moveHandler = (ev: MouseEvent) => resize(ev, index);
|
|
265
|
+
const upHandler = () => stopResize(moveHandler, upHandler);
|
|
266
|
+
|
|
267
|
+
window.addEventListener("mousemove", moveHandler);
|
|
268
|
+
window.addEventListener("mouseup", upHandler);
|
|
269
|
+
|
|
270
|
+
document.body.style.cursor = currentDirection === "horizontal" ? "col-resize" : "row-resize";
|
|
271
|
+
document.body.style.userSelect = "none";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function stopResize(moveHandler: (e: MouseEvent) => void, upHandler: () => void) {
|
|
275
|
+
isDragging = false;
|
|
276
|
+
transition = transitionProp;
|
|
277
|
+
window.removeEventListener("mousemove", moveHandler);
|
|
278
|
+
window.removeEventListener("mouseup", upHandler);
|
|
279
|
+
|
|
280
|
+
document.body.style.cursor = "";
|
|
281
|
+
document.body.style.userSelect = "";
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function resize(e: MouseEvent, index: number) {
|
|
285
|
+
if (!isDragging || !container) return;
|
|
286
|
+
if (index < 0 || index + 1 >= sizes.length) return;
|
|
287
|
+
|
|
288
|
+
const currentPos = currentDirection === "horizontal" ? e.clientX : e.clientY;
|
|
289
|
+
const delta = currentPos - startPos;
|
|
290
|
+
|
|
291
|
+
if (Math.abs(delta) < MIN_DELTA) return; // Ignore very small movements
|
|
292
|
+
|
|
293
|
+
const containerSize = currentDirection === "horizontal" ? container.offsetWidth : container.offsetHeight;
|
|
294
|
+
|
|
295
|
+
// Bail out if container has zero or near-zero dimensions
|
|
296
|
+
if (containerSize < 1) return;
|
|
297
|
+
|
|
298
|
+
const deltaPercent = (delta / containerSize) * 100;
|
|
299
|
+
|
|
300
|
+
// Calculate min as percentage based on current container size
|
|
301
|
+
const minPercent = (minSize / containerSize) * 100;
|
|
302
|
+
|
|
303
|
+
// Store original sizes
|
|
304
|
+
const oldSize1 = sizes[index];
|
|
305
|
+
const oldSize2 = sizes[index + 1];
|
|
306
|
+
const totalSize = oldSize1 + oldSize2;
|
|
307
|
+
|
|
308
|
+
// Calculate desired new sizes
|
|
309
|
+
let newSize1 = oldSize1 + deltaPercent;
|
|
310
|
+
let newSize2 = oldSize2 - deltaPercent;
|
|
311
|
+
|
|
312
|
+
// Apply minimum constraints - clamp to valid range
|
|
313
|
+
newSize1 = Math.max(minPercent, newSize1);
|
|
314
|
+
newSize2 = totalSize - newSize1;
|
|
315
|
+
|
|
316
|
+
// Check if second pane violates minimum constraint after first pane clamping
|
|
317
|
+
if (newSize2 < minPercent) {
|
|
318
|
+
newSize2 = minPercent;
|
|
319
|
+
newSize1 = totalSize - newSize2;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Only update if changed significantly
|
|
323
|
+
if (Math.abs(newSize1 - oldSize1) > MIN_CHANGE_THRESHOLD) {
|
|
324
|
+
sizes[index] = newSize1;
|
|
325
|
+
sizes[index + 1] = newSize2;
|
|
326
|
+
startPos = currentPos;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function handleKeyResize(e: KeyboardEvent, index: number) {
|
|
331
|
+
if (!container) return;
|
|
332
|
+
if (index < 0 || index + 1 >= sizes.length) return;
|
|
333
|
+
|
|
334
|
+
const step = keyboardStep;
|
|
335
|
+
let handled = false;
|
|
336
|
+
|
|
337
|
+
const isHorizontal = currentDirection === "horizontal";
|
|
338
|
+
const increaseKeys = isHorizontal ? ["ArrowRight"] : ["ArrowDown"];
|
|
339
|
+
const decreaseKeys = isHorizontal ? ["ArrowLeft"] : ["ArrowUp"];
|
|
340
|
+
|
|
341
|
+
const containerSize = isHorizontal ? container.offsetWidth : container.offsetHeight;
|
|
342
|
+
// Bail out if container has zero or near-zero dimensions
|
|
343
|
+
if (containerSize < 1) return;
|
|
344
|
+
|
|
345
|
+
const minPercent = (minSize / containerSize) * 100;
|
|
346
|
+
|
|
347
|
+
const total = sizes[index] + sizes[index + 1];
|
|
348
|
+
|
|
349
|
+
const applyClamp = (target: number) => {
|
|
350
|
+
let newSize1 = Math.min(total - minPercent, Math.max(minPercent, target));
|
|
351
|
+
let newSize2 = total - newSize1;
|
|
352
|
+
|
|
353
|
+
if (newSize2 < minPercent) {
|
|
354
|
+
newSize2 = minPercent;
|
|
355
|
+
newSize1 = total - newSize2;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (Math.abs(newSize1 - sizes[index]) > MIN_CHANGE_THRESHOLD) {
|
|
359
|
+
sizes[index] = newSize1;
|
|
360
|
+
sizes[index + 1] = newSize2;
|
|
361
|
+
handled = true;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
if (increaseKeys.includes(e.key)) {
|
|
366
|
+
applyClamp(sizes[index] + step);
|
|
367
|
+
} else if (decreaseKeys.includes(e.key)) {
|
|
368
|
+
applyClamp(sizes[index] - step);
|
|
369
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
370
|
+
// Reset to equal sizes
|
|
371
|
+
const equal = 100 / registeredPanes;
|
|
372
|
+
sizes = sizes.map(() => equal);
|
|
373
|
+
handled = true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (handled) {
|
|
377
|
+
e.preventDefault();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
</script>
|
|
381
|
+
|
|
382
|
+
<div bind:this={container} class={splitpane({ direction: currentDirection, class: clsx(theme, className) })}>
|
|
383
|
+
{@render children()}
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<!--
|
|
387
|
+
@component
|
|
388
|
+
[Go to docs](https://flowbite-svelte.com/)
|
|
389
|
+
## Type
|
|
390
|
+
[SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2125)
|
|
391
|
+
## Props
|
|
392
|
+
@prop direction = "horizontal"
|
|
393
|
+
@prop minSize = 100
|
|
394
|
+
@prop responsive = true
|
|
395
|
+
@prop breakpoint = 768
|
|
396
|
+
@prop transition: transitionProp = true
|
|
397
|
+
@prop transitionDuration = 150
|
|
398
|
+
@prop keyboardStep = 2
|
|
399
|
+
@prop initialSizes
|
|
400
|
+
@prop onResize
|
|
401
|
+
@prop children
|
|
402
|
+
@prop class: className = ""
|
|
403
|
+
-->
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface SplitPaneContext {
|
|
2
|
+
registerPane: () => number;
|
|
3
|
+
getPaneStyle: (index: number) => string;
|
|
4
|
+
getPaneSize: (index: number) => number;
|
|
5
|
+
shouldRenderDivider: (index: number) => boolean;
|
|
6
|
+
getDirection: () => "horizontal" | "vertical";
|
|
7
|
+
getIsDragging: () => boolean;
|
|
8
|
+
onMouseDown: (e: MouseEvent, index: number) => void;
|
|
9
|
+
onKeyDown: (e: KeyboardEvent, index: number) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function setSplitPaneContext(ctx: SplitPaneContext): void;
|
|
12
|
+
export declare function getSplitPaneContext(): SplitPaneContext | undefined;
|
|
13
|
+
import type { SplitPaneProps } from "../types";
|
|
14
|
+
/**
|
|
15
|
+
* [Go to docs](https://flowbite-svelte.com/)
|
|
16
|
+
* ## Type
|
|
17
|
+
* [SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2125)
|
|
18
|
+
* ## Props
|
|
19
|
+
* @prop direction = "horizontal"
|
|
20
|
+
* @prop minSize = 100
|
|
21
|
+
* @prop responsive = true
|
|
22
|
+
* @prop breakpoint = 768
|
|
23
|
+
* @prop transition: transitionProp = true
|
|
24
|
+
* @prop transitionDuration = 150
|
|
25
|
+
* @prop keyboardStep = 2
|
|
26
|
+
* @prop initialSizes
|
|
27
|
+
* @prop onResize
|
|
28
|
+
* @prop children
|
|
29
|
+
* @prop class: className = ""
|
|
30
|
+
*/
|
|
31
|
+
declare const SplitPane: import("svelte").Component<SplitPaneProps, {}, "">;
|
|
32
|
+
type SplitPane = ReturnType<typeof SplitPane>;
|
|
33
|
+
export default SplitPane;
|