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.
Files changed (56) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/kanban/KanbanCard.svelte +1 -1
  4. package/dist/kanban/KanbanCard.svelte.d.ts +1 -1
  5. package/dist/mega-menu/MegaMenu.svelte +1 -1
  6. package/dist/mega-menu/theme.js +2 -2
  7. package/dist/split-pane/Divider.svelte +47 -0
  8. package/dist/split-pane/Divider.svelte.d.ts +17 -0
  9. package/dist/split-pane/Pane.svelte +47 -0
  10. package/dist/split-pane/Pane.svelte.d.ts +13 -0
  11. package/dist/split-pane/SplitPane.svelte +403 -0
  12. package/dist/split-pane/SplitPane.svelte.d.ts +33 -0
  13. package/dist/split-pane/index.d.ts +4 -0
  14. package/dist/split-pane/index.js +4 -0
  15. package/dist/split-pane/theme.d.ts +65 -0
  16. package/dist/split-pane/theme.js +45 -0
  17. package/dist/theme/themes.d.ts +1 -0
  18. package/dist/theme/themes.js +1 -0
  19. package/dist/tooltip/theme.d.ts +3 -0
  20. package/dist/tooltip/theme.js +8 -1
  21. package/dist/types.d.ts +30 -5
  22. package/dist/typography/a/A.svelte +1 -1
  23. package/dist/typography/a/A.svelte.d.ts +1 -1
  24. package/dist/typography/blockquote/Blockquote.svelte +1 -1
  25. package/dist/typography/blockquote/Blockquote.svelte.d.ts +1 -1
  26. package/dist/typography/descriptionlist/DescriptionList.svelte +1 -1
  27. package/dist/typography/descriptionlist/DescriptionList.svelte.d.ts +1 -1
  28. package/dist/typography/heading/Heading.svelte +1 -1
  29. package/dist/typography/heading/Heading.svelte.d.ts +1 -1
  30. package/dist/typography/img/EnhancedImg.svelte +1 -1
  31. package/dist/typography/img/EnhancedImg.svelte.d.ts +1 -1
  32. package/dist/typography/img/Img.svelte +1 -1
  33. package/dist/typography/img/Img.svelte.d.ts +1 -1
  34. package/dist/typography/layout/Layout.svelte +1 -1
  35. package/dist/typography/layout/Layout.svelte.d.ts +1 -1
  36. package/dist/typography/list/Li.svelte +1 -1
  37. package/dist/typography/list/Li.svelte.d.ts +1 -1
  38. package/dist/typography/list/List.svelte +1 -1
  39. package/dist/typography/list/List.svelte.d.ts +1 -1
  40. package/dist/typography/mark/Mark.svelte +1 -1
  41. package/dist/typography/mark/Mark.svelte.d.ts +1 -1
  42. package/dist/typography/paragraph/P.svelte +1 -1
  43. package/dist/typography/paragraph/P.svelte.d.ts +1 -1
  44. package/dist/typography/secondary/Secondary.svelte +1 -1
  45. package/dist/typography/secondary/Secondary.svelte.d.ts +1 -1
  46. package/dist/typography/span/Span.svelte +1 -1
  47. package/dist/typography/span/Span.svelte.d.ts +1 -1
  48. package/dist/utils/Arrow.svelte +1 -1
  49. package/dist/utils/Arrow.svelte.d.ts +1 -1
  50. package/dist/utils/Popper.svelte +75 -65
  51. package/dist/utils/Popper.svelte.d.ts +7 -6
  52. package/dist/utils/debounce.d.ts +17 -0
  53. package/dist/utils/debounce.js +41 -0
  54. package/dist/video/Video.svelte +1 -1
  55. package/dist/video/Video.svelte.d.ts +1 -1
  56. package/package.json +13 -1
package/dist/index.d.ts CHANGED
@@ -80,3 +80,4 @@ export * from "./utils";
80
80
  export * from "./types";
81
81
  export * from "./virtuallist";
82
82
  export * from "./kanban";
83
+ export * from "./split-pane";
package/dist/index.js CHANGED
@@ -86,3 +86,4 @@ export * from "./types";
86
86
  // extend
87
87
  export * from "./virtuallist";
88
88
  export * from "./kanban";
89
+ export * from "./split-pane";
@@ -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#L2119)
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#L2119)
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 color={full ? "default" : "dropdown"} arrow={false} bind:isOpen trigger="click" placement="bottom" yOnly={full} {...restProps} class={base({ class: clsx(theme?.base, className) })}>
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}
@@ -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;
@@ -0,0 +1,4 @@
1
+ export { default as SplitPane } from "./SplitPane.svelte";
2
+ export { default as Pane } from "./Pane.svelte";
3
+ export { default as Divider } from "./Divider.svelte";
4
+ export { splitpane, pane, divider, dividerHitArea } from "./theme";
@@ -0,0 +1,4 @@
1
+ export { default as SplitPane } from "./SplitPane.svelte";
2
+ export { default as Pane } from "./Pane.svelte";
3
+ export { default as Divider } from "./Divider.svelte";
4
+ export { splitpane, pane, divider, dividerHitArea } from "./theme";