diff --git a/.changeset/dialog-drawer-event-propagation.md b/.changeset/dialog-drawer-event-propagation.md
new file mode 100644
index 00000000..bbaf0e61
--- /dev/null
+++ b/.changeset/dialog-drawer-event-propagation.md
@@ -0,0 +1,5 @@
+---
+'svelte-ux': patch
+---
+
+fix(Dialog/Drawer): event propagation preventing outside click detection
diff --git a/.changeset/multiselect-demo-enhancements.md b/.changeset/multiselect-demo-enhancements.md
new file mode 100644
index 00000000..1cc5dea6
--- /dev/null
+++ b/.changeset/multiselect-demo-enhancements.md
@@ -0,0 +1,5 @@
+---
+'svelte-ux': patch
+---
+
+docs(MultiSelect/MultiSelectField/MultiSelectMenu): Enhanced demo examples with functional item creation dialogs
diff --git a/.changeset/numberstepper-documentation.md b/.changeset/numberstepper-documentation.md
new file mode 100644
index 00000000..4ca09b68
--- /dev/null
+++ b/.changeset/numberstepper-documentation.md
@@ -0,0 +1,5 @@
+---
+'svelte-ux': patch
+---
+
+docs(NumberStepper): demo example with prefix/suffix slot
diff --git a/.changeset/selectfield-demo-improvements.md b/.changeset/selectfield-demo-improvements.md
new file mode 100644
index 00000000..f0595c53
--- /dev/null
+++ b/.changeset/selectfield-demo-improvements.md
@@ -0,0 +1,5 @@
+---
+'svelte-ux': patch
+---
+
+docs(SelectField): demo filtering logic and form handling
diff --git a/.changeset/selectfield-focus-management.md b/.changeset/selectfield-focus-management.md
new file mode 100644
index 00000000..5883a3bd
--- /dev/null
+++ b/.changeset/selectfield-focus-management.md
@@ -0,0 +1,5 @@
+---
+'svelte-ux': patch
+---
+
+fix(SelectField): focus management when used within dialogs
diff --git a/packages/svelte-ux/src/lib/components/Dialog.svelte b/packages/svelte-ux/src/lib/components/Dialog.svelte
index 426e26a4..07d7157f 100644
--- a/packages/svelte-ux/src/lib/components/Dialog.svelte
+++ b/packages/svelte-ux/src/lib/components/Dialog.svelte
@@ -103,6 +103,9 @@
classes.root
)}
on:click={onClick}
+ on:mouseup={(e) => {
+ e.stopPropagation(); // Prevent mouseup from bubbling to outside click handlers (e.g., Popover/Menu clickOutside)
+ }}
on:keydown={(e) => {
if (e.key === 'Escape') {
// Do not allow event to reach Popover's on:keydown
diff --git a/packages/svelte-ux/src/lib/components/Drawer.svelte b/packages/svelte-ux/src/lib/components/Drawer.svelte
index f26a8d47..04b76447 100644
--- a/packages/svelte-ux/src/lib/components/Drawer.svelte
+++ b/packages/svelte-ux/src/lib/components/Drawer.svelte
@@ -90,6 +90,9 @@
$$props.class
)}
style={$$props.style}
+ on:mouseup={(e) => {
+ e.stopPropagation(); // Prevent mouseup from bubbling to outside click handlers (e.g., Popover/Menu clickOutside)
+ }}
in:fly|global={{
x: placement === 'left' ? '-100%' : placement === 'right' ? '100%' : 0,
y: placement === 'top' ? '-100%' : placement === 'bottom' ? '100%' : 0,
diff --git a/packages/svelte-ux/src/lib/components/SelectField.svelte b/packages/svelte-ux/src/lib/components/SelectField.svelte
index b1291c93..e4c7a4cb 100644
--- a/packages/svelte-ux/src/lib/components/SelectField.svelte
+++ b/packages/svelte-ux/src/lib/components/SelectField.svelte
@@ -255,6 +255,7 @@
// Hide if focus not moved to menu (option clicked)
if (
fe.relatedTarget instanceof HTMLElement &&
+ !fe.relatedTarget.closest('[role="dialog"]') &&
!menuOptionsEl?.contains(fe.relatedTarget) && // TODO: Oddly Safari does not set `relatedTarget` to the clicked on menu option (like Chrome and Firefox) but instead appears to take `tabindex` into consideration. Currently resolves to `.options` after setting `tabindex="-1"
fe.relatedTarget !== menuOptionsEl?.offsetParent && // click on scroll bar
// Allow focus to move into auxiliary slot areas (beforeOptions, afterOptions, actions)
diff --git a/packages/svelte-ux/src/routes/docs/components/MultiSelect/+page.svelte b/packages/svelte-ux/src/routes/docs/components/MultiSelect/+page.svelte
index 40bafcbd..cab46049 100644
--- a/packages/svelte-ux/src/routes/docs/components/MultiSelect/+page.svelte
+++ b/packages/svelte-ux/src/routes/docs/components/MultiSelect/+page.svelte
@@ -3,24 +3,32 @@
import {
Button,
+ Dialog,
Drawer,
Form,
Icon,
MultiSelect,
MultiSelectOption,
+ TextField,
+ Toggle,
ToggleButton,
ToggleGroup,
ToggleOption,
+ type MenuOption,
} from 'svelte-ux';
import Preview from '$lib/components/Preview.svelte';
- const options = [
+ let options: MenuOption[] = [
{ label: 'One', value: 1 },
{ label: 'Two', value: 2 },
{ label: 'Three', value: 3 },
{ label: 'Four', value: 4 },
];
+ const newOption: () => MenuOption = () => {
+ return { label: '', value: null };
+ };
+
const manyOptions = Array.from({ length: 100 }).map((_, i) => ({
label: `${i + 1}`,
value: i + 1,
@@ -168,34 +176,6 @@
-
actions slot
-
-
- {value.length} selected
-
-
(value = e.detail.value)} search>
-
-
-
-
-
-
-
-actions slot with max warning
-
-
- {value.length} selected
-
-
(value = e.detail.value)} search max={2}>
-
- {#if selection.isMaxSelected()}
-
Maximum selection reached
- {/if}
-
-
-
-
-
beforeOptions slot
@@ -254,6 +234,98 @@
+actions slot
+
+
+ {value.length} selected
+
+
(value = e.detail.value)} search>
+
+
+
+
+
+actions slot with max warning
+
+
+ {value.length} selected
+
+
(value = e.detail.value)} search max={2}>
+
+ {#if selection.isMaxSelected()}
+
Maximum selection reached
+ {/if}
+
+
+
+
+
option slot with MultiSelectOption custom actions
diff --git a/packages/svelte-ux/src/routes/docs/components/MultiSelectField/+page.svelte b/packages/svelte-ux/src/routes/docs/components/MultiSelectField/+page.svelte
index 658e9131..6173559f 100644
--- a/packages/svelte-ux/src/routes/docs/components/MultiSelectField/+page.svelte
+++ b/packages/svelte-ux/src/routes/docs/components/MultiSelectField/+page.svelte
@@ -4,22 +4,31 @@
import {
Button,
+ Dialog,
Drawer,
+ Form,
MultiSelectField,
MultiSelectOption,
+ TextField,
+ Toggle,
ToggleButton,
ToggleGroup,
ToggleOption,
+ type MenuOption,
} from 'svelte-ux';
import Preview from '$lib/components/Preview.svelte';
- const options = [
+ let options: MenuOption[] = [
{ label: 'One', value: 1 },
{ label: 'Two', value: 2 },
{ label: 'Three', value: 3 },
{ label: 'Four', value: 4 },
];
+ const newOption: () => MenuOption = () => {
+ return { label: '', value: null };
+ };
+
const manyOptions = Array.from({ length: 100 }).map((_, i) => ({
label: `${i + 1}`,
value: i + 1,
@@ -169,28 +178,6 @@
/>
-actions slot
-
-
- (value = e.detail.value)}>
-
-
-
-
-
-
-actions slot with max warning
-
-
- (value = e.detail.value)} max={2}>
-
- {#if selection.isMaxSelected()}
-
Maximum selection reached
- {/if}
-
-
-
-
beforeOptions slot
@@ -241,6 +228,91 @@
+actions slot
+
+
+ (value = e.detail.value)}>
+
+
+
+
+actions slot with max warning
+
+
+ (value = e.detail.value)} max={2}>
+
+ {#if selection.isMaxSelected()}
+
Maximum selection reached
+ {/if}
+
+
+
within Drawer
diff --git a/packages/svelte-ux/src/routes/docs/components/MultiSelectMenu/+page.svelte b/packages/svelte-ux/src/routes/docs/components/MultiSelectMenu/+page.svelte
index 8e263ebb..5de8babf 100644
--- a/packages/svelte-ux/src/routes/docs/components/MultiSelectMenu/+page.svelte
+++ b/packages/svelte-ux/src/routes/docs/components/MultiSelectMenu/+page.svelte
@@ -3,21 +3,30 @@
import {
Button,
+ Dialog,
+ Form,
MultiSelectMenu,
MultiSelectOption,
+ TextField,
+ Toggle,
ToggleButton,
ToggleGroup,
ToggleOption,
+ type MenuOption,
} from 'svelte-ux';
import Preview from '$lib/components/Preview.svelte';
- const options = [
+ let options: MenuOption[] = [
{ label: 'One', value: 1 },
{ label: 'Two', value: 2 },
{ label: 'Three', value: 3 },
{ label: 'Four', value: 4 },
];
+ const newOption: () => MenuOption = () => {
+ return { label: '', value: null };
+ };
+
const manyOptions = Array.from({ length: 100 }).map((_, i) => ({
label: `${i + 1}`,
value: i + 1,
@@ -286,14 +295,14 @@
-actions slot
+beforeOptions slot
{value.length} selected
{
// @ts-expect-error
@@ -304,15 +313,26 @@
classes={{ menu: 'w-[360px]' }}
search
>
-
-
-
+
+
+
+ Any
+ Evens
+ Odds
+
+
+
-beforeOptions slot
+afterOptions slot
@@ -330,8 +350,8 @@
classes={{ menu: 'w-[360px]' }}
search
>
-
-
+
+
-afterOptions slot
+actions slot
{value.length} selected
{
// @ts-expect-error
@@ -367,20 +387,73 @@
classes={{ menu: 'w-[360px]' }}
search
>
-
-
-
+
+
+
-
-
+
+
+
+
diff --git a/packages/svelte-ux/src/routes/docs/components/NumberStepper/+page.svelte b/packages/svelte-ux/src/routes/docs/components/NumberStepper/+page.svelte
index f4757f2b..2504fd58 100644
--- a/packages/svelte-ux/src/routes/docs/components/NumberStepper/+page.svelte
+++ b/packages/svelte-ux/src/routes/docs/components/NumberStepper/+page.svelte
@@ -7,7 +7,7 @@
Examples
-basic
+Basic
@@ -26,20 +26,36 @@
console.log(e.detail.value)} />
-dense
+Dense
-min / max
+Min / Max
-step
+Step
+
+Prefix
+
+
+
+ $
+
+
+
+Suffix
+
+
+
+ kg
+
+
diff --git a/packages/svelte-ux/src/routes/docs/components/SelectField/+page.svelte b/packages/svelte-ux/src/routes/docs/components/SelectField/+page.svelte
index 6893e178..94e53b6d 100644
--- a/packages/svelte-ux/src/routes/docs/components/SelectField/+page.svelte
+++ b/packages/svelte-ux/src/routes/docs/components/SelectField/+page.svelte
@@ -75,12 +75,19 @@
let selectedStr: 'any' | 'even' | 'odds' = 'any';
// Filter options based on toggle selection
- $: optionsFiltered =
- selectedStr === 'even'
- ? options.filter((o) => typeof o.value === 'number' && o.value % 2 === 0)
- : selectedStr === 'odds'
- ? options.filter((o) => typeof o.value === 'number' && o.value % 2 !== 0)
- : options;
+ $: optionsFiltered = options.map((o) => {
+ const matches =
+ selectedStr === 'even'
+ ? typeof o.value === 'number' && o.value % 2 === 0
+ : selectedStr === 'odds'
+ ? typeof o.value === 'number' && o.value % 2 !== 0
+ : true;
+
+ return {
+ ...o,
+ disabled: (o.disabled ?? false) || !matches,
+ };
+ });
Examples
@@ -415,7 +422,16 @@
+
+`beforeOptions` slot
+
+
+
+
+
+ Any
+ Evens
+ Odds
+
+
+
+
+
+`afterOptions` slot
+
+
+
+
+
+ Any
+ Evens
+ Odds
+
+
+
+
+
`actions` slot (menu)
@@ -458,7 +513,18 @@
-
-
+
+
@@ -501,43 +578,6 @@
-`beforeOptions` slot (menu)
-
-
-
-
-
- Any
- Evens
- Odds
-
-
-
-
-`afterOptions` slot (menu)
-
-
-
-
-
- Any
- Evens
- Odds
-
-
-
-
-
Icon