svelte-select-5 6.0.0
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/.claude/settings.local.json +20 -0
- package/.prettierignore +2 -0
- package/.prettierrc +17 -0
- package/CHANGELOG.md +571 -0
- package/LICENSE +9 -0
- package/MIGRATION_GUIDE.md +98 -0
- package/README.md +316 -0
- package/docs/generate_theming_variables_md.cjs +53 -0
- package/docs/theming_variables.md +113 -0
- package/jsconfig.json +3 -0
- package/package.json +77 -0
- package/rollup.config.js +33 -0
- package/src/app.html +29 -0
- package/src/global.d.ts +1 -0
- package/src/lib/ChevronIcon.svelte +22 -0
- package/src/lib/ClearIcon.svelte +22 -0
- package/src/lib/LoadingIcon.svelte +33 -0
- package/src/lib/Select.svelte +1345 -0
- package/src/lib/filter.js +38 -0
- package/src/lib/get-items.js +25 -0
- package/src/lib/index.js +1 -0
- package/src/lib/tailwind.css +130 -0
- package/src/post-prepare.cjs +6 -0
- package/src/remove-styles.cjs +22 -0
- package/src/routes/+layout.js +1 -0
- package/src/routes/+layout.svelte +263 -0
- package/src/routes/+page.js +5 -0
- package/src/routes/+page.svelte +0 -0
- package/src/routes/examples/+page.svelte +84 -0
- package/src/routes/examples/advanced/create-item/+page.svelte +36 -0
- package/src/routes/examples/advanced/create-item-multiple/+page.svelte +38 -0
- package/src/routes/examples/advanced/floating-ui/+page.svelte +22 -0
- package/src/routes/examples/advanced/form-action/+page.server.js +10 -0
- package/src/routes/examples/advanced/form-action/+page.svelte +20 -0
- package/src/routes/examples/advanced/limit-multi-value/+page.svelte +31 -0
- package/src/routes/examples/advanced/long-item/+page.svelte +38 -0
- package/src/routes/examples/advanced/multi-item-checkboxes/+page.svelte +49 -0
- package/src/routes/examples/advanced/style-props/+page.svelte +14 -0
- package/src/routes/examples/advanced/virtual-list/+page.svelte +95 -0
- package/src/routes/examples/events/blur/+page.svelte +19 -0
- package/src/routes/examples/events/change/+page.svelte +16 -0
- package/src/routes/examples/events/clear/+page.svelte +18 -0
- package/src/routes/examples/events/error/+page.svelte +17 -0
- package/src/routes/examples/events/filter/+page.svelte +16 -0
- package/src/routes/examples/events/focus/+page.svelte +16 -0
- package/src/routes/examples/events/hoverItem/+page.svelte +16 -0
- package/src/routes/examples/events/input/+page.svelte +16 -0
- package/src/routes/examples/events/loaded/+page.svelte +23 -0
- package/src/routes/examples/props/class/+page.svelte +17 -0
- package/src/routes/examples/props/clearFilterTextOnBlur/+page.svelte +13 -0
- package/src/routes/examples/props/clearable/+page.svelte +13 -0
- package/src/routes/examples/props/closeListOnChange/+page.svelte +12 -0
- package/src/routes/examples/props/container-styles/+page.svelte +11 -0
- package/src/routes/examples/props/debounce-wait/+page.svelte +19 -0
- package/src/routes/examples/props/disabled/+page.svelte +15 -0
- package/src/routes/examples/props/filter-text/+page.svelte +14 -0
- package/src/routes/examples/props/floating-config/+page.svelte +42 -0
- package/src/routes/examples/props/focused/+page.svelte +16 -0
- package/src/routes/examples/props/group-header-selectable/+page.svelte +18 -0
- package/src/routes/examples/props/hide-empty-state/+page.svelte +8 -0
- package/src/routes/examples/props/id/+page.svelte +15 -0
- package/src/routes/examples/props/input-attributes/+page.svelte +11 -0
- package/src/routes/examples/props/item-id/+page.svelte +15 -0
- package/src/routes/examples/props/items/+page.svelte +15 -0
- package/src/routes/examples/props/just-value/+page.svelte +16 -0
- package/src/routes/examples/props/label/+page.svelte +15 -0
- package/src/routes/examples/props/list-auto-width/+page.svelte +21 -0
- package/src/routes/examples/props/list-offset/+page.svelte +21 -0
- package/src/routes/examples/props/list-open/+page.svelte +31 -0
- package/src/routes/examples/props/loadOptions/+page.svelte +16 -0
- package/src/routes/examples/props/loading/+page.svelte +15 -0
- package/src/routes/examples/props/multiFullItemClearable/+page.svelte +12 -0
- package/src/routes/examples/props/multiple/+page.svelte +12 -0
- package/src/routes/examples/props/name/+page.svelte +13 -0
- package/src/routes/examples/props/placeholder/+page.svelte +14 -0
- package/src/routes/examples/props/placeholder-always-show/+page.svelte +11 -0
- package/src/routes/examples/props/required/+page.svelte +14 -0
- package/src/routes/examples/props/searchable/+page.svelte +15 -0
- package/src/routes/examples/props/show-chevron/+page.svelte +15 -0
- package/src/routes/examples/props/value/+page.svelte +19 -0
- package/src/routes/examples/slots/chevron-icon/+page.svelte +16 -0
- package/src/routes/examples/slots/clear-icon/+page.svelte +21 -0
- package/src/routes/examples/slots/empty/+page.svelte +18 -0
- package/src/routes/examples/slots/input-hidden/+page.server.js +10 -0
- package/src/routes/examples/slots/input-hidden/+page.svelte +22 -0
- package/src/routes/examples/slots/item/+page.svelte +15 -0
- package/src/routes/examples/slots/list/+page.svelte +49 -0
- package/src/routes/examples/slots/list-append/+page.svelte +16 -0
- package/src/routes/examples/slots/list-prepend/+page.svelte +16 -0
- package/src/routes/examples/slots/loading-icon/+page.svelte +29 -0
- package/src/routes/examples/slots/multi-clear-icon/+page.svelte +16 -0
- package/src/routes/examples/slots/prepend/+page.svelte +22 -0
- package/src/routes/examples/slots/required/+page.svelte +31 -0
- package/src/routes/examples/slots/selection/+page.svelte +25 -0
- package/static/nav-icon.svg +2 -0
- package/static/svelte-select.png +0 -0
- package/svelte-select.png +0 -0
- package/svelte.config.js +10 -0
- package/tailwind.config.cjs +4 -0
- package/test/public/favicon.ico +0 -0
- package/test/public/index.html +15 -0
- package/test/src/ChevronSlotTest.svelte +19 -0
- package/test/src/ClearIconSlotTest.svelte +12 -0
- package/test/src/CustomItem.svelte +78 -0
- package/test/src/GroupHeaderNotSelectable.svelte +17 -0
- package/test/src/HoverItemIndexTest.svelte +21 -0
- package/test/src/InputHiddenSlotTest.svelte +12 -0
- package/test/src/ItemHeightTest.svelte +7 -0
- package/test/src/ItemSlotTest.svelte +11 -0
- package/test/src/ListSlotTest.svelte +14 -0
- package/test/src/LoadOptionsGroup.svelte +21 -0
- package/test/src/MultiItemColor.svelte +7 -0
- package/test/src/OuterListTest.svelte +16 -0
- package/test/src/PrependSlotTest.svelte +12 -0
- package/test/src/Select/ParentContainer.svelte +11 -0
- package/test/src/SelectionSlotMultipleTest.svelte +12 -0
- package/test/src/SelectionSlotTest.svelte +12 -0
- package/test/src/TestClearIcon.svelte +1 -0
- package/test/src/TestIcon.svelte +15 -0
- package/test/src/env.js +1 -0
- package/test/src/test-utils.js +124 -0
- package/test/src/tests.js +3745 -0
- package/vite.config.js +9 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>svelte-select tests</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="testTemplate"></div>
|
|
11
|
+
<div id="extra"></div>
|
|
12
|
+
<script type="module" src="/tests.js"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
15
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state(undefined);
|
|
6
|
+
let listOpen = $state(true);
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<Select bind:items bind:value bind:listOpen showChevron>
|
|
10
|
+
{#snippet chevronIcon({ listOpen })}
|
|
11
|
+
<div>
|
|
12
|
+
{#if listOpen}
|
|
13
|
+
⬆️
|
|
14
|
+
{:else}
|
|
15
|
+
⬇️
|
|
16
|
+
{/if}
|
|
17
|
+
</div>
|
|
18
|
+
{/snippet}
|
|
19
|
+
</Select>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let { active = false, first = false, hover = false, item = undefined } = $props();
|
|
3
|
+
|
|
4
|
+
let itemClasses = $derived.by(() => {
|
|
5
|
+
const classes = [];
|
|
6
|
+
if (active) {
|
|
7
|
+
classes.push('active');
|
|
8
|
+
}
|
|
9
|
+
if (first) {
|
|
10
|
+
classes.push('first');
|
|
11
|
+
}
|
|
12
|
+
if (hover) {
|
|
13
|
+
classes.push('hover');
|
|
14
|
+
}
|
|
15
|
+
return classes.join(' ');
|
|
16
|
+
});
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div class="customItem {itemClasses}">
|
|
20
|
+
<img src={item.image_url} alt={item.name} />
|
|
21
|
+
<div class="customItem_title">
|
|
22
|
+
<div class="customItem_name">{item.name}</div>
|
|
23
|
+
<div class="customItem_tagline">{item.tagline}</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.customItem {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
cursor: default;
|
|
32
|
+
height: 42px;
|
|
33
|
+
line-height: 42px;
|
|
34
|
+
padding: 0 16px;
|
|
35
|
+
text-overflow: ellipsis;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.customItem:active {
|
|
41
|
+
background: #b9daff;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.customItem.active {
|
|
45
|
+
background: #007aff;
|
|
46
|
+
color: #fff;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.customItem.first {
|
|
50
|
+
border-radius: 4px 4px 0 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.customItem.hover:not(.active) {
|
|
54
|
+
background: #e7f2ff;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
img {
|
|
58
|
+
width: 5px;
|
|
59
|
+
padding: 5px 0;
|
|
60
|
+
margin: 0 10px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.customItem_title {
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.customItem_name {
|
|
70
|
+
display: inline-block;
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
margin-right: 10px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.customItem_tagline {
|
|
76
|
+
display: inline-block;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let { filterText = $bindable('') } = $props();
|
|
5
|
+
|
|
6
|
+
let items = [
|
|
7
|
+
{ value: 'chocolate', label: 'Chocolate', group: 'Sweet' },
|
|
8
|
+
{ value: 'pizza', label: 'Pizza', group: 'Savory' },
|
|
9
|
+
{ value: 'cake', label: 'Cake', group: 'Sweet', selectable: false },
|
|
10
|
+
{ value: 'chips', label: 'Chips', group: 'Savory' },
|
|
11
|
+
{ value: 'ice-cream', label: 'Ice Cream', group: 'Sweet' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
let value = $state(undefined);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<Select {items} groupBy={(item) => item.group} bind:value bind:filterText />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = [];
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < 100; i++) {
|
|
7
|
+
items.push({ label: i, value: i, group: 'a' });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let value = $state(null);
|
|
11
|
+
|
|
12
|
+
let { hoverItemIndex = $bindable() } = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<Select {items} bind:value groupBy={(i) => i.group} bind:hoverItemIndex />
|
|
16
|
+
|
|
17
|
+
{#if value}
|
|
18
|
+
<p>
|
|
19
|
+
Selected value: {value.label}
|
|
20
|
+
</p>
|
|
21
|
+
{/if}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state('one');
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items name="test" bind:value>
|
|
9
|
+
{#snippet inputHidden({ value })}
|
|
10
|
+
<input type="hidden" name="test" value={value.value} />
|
|
11
|
+
{/snippet}
|
|
12
|
+
</Select>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state('one');
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items bind:value listOpen>
|
|
9
|
+
{#snippet list({ filteredItems })}
|
|
10
|
+
{#each filteredItems as item}
|
|
11
|
+
{item.label}
|
|
12
|
+
{/each}
|
|
13
|
+
{/snippet}
|
|
14
|
+
</Select>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
const items = [
|
|
5
|
+
{ value: 'chocolate', label: 'Chocolate', group: 'Sweet' },
|
|
6
|
+
{ value: 'pizza', label: 'Pizza', group: 'Savory' },
|
|
7
|
+
{ value: 'cake', label: 'Cake', group: 'Sweet', selectable: false },
|
|
8
|
+
{ value: 'chips', label: 'Chips', group: 'Savory' },
|
|
9
|
+
{ value: 'ice-cream', label: 'Ice Cream', group: 'Sweet' },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
let { filterText = $bindable(), value = $bindable(undefined) } = $props();
|
|
13
|
+
|
|
14
|
+
async function loadOptions() {
|
|
15
|
+
return items.filter((i) => i.label.toLowerCase().includes(filterText.toLowerCase()));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const groupBy = (i) => i.group;
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<Select {loadOptions} bind:filterText {groupBy} {value} />
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state('one');
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items bind:value listOpen>
|
|
9
|
+
{#snippet listPrepend()}
|
|
10
|
+
prepend
|
|
11
|
+
{/snippet}
|
|
12
|
+
|
|
13
|
+
{#snippet listAppend()}
|
|
14
|
+
append
|
|
15
|
+
{/snippet}
|
|
16
|
+
</Select>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state(undefined);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items bind:value>
|
|
9
|
+
{#snippet prepend()}
|
|
10
|
+
<div class="before">Before it all</div>
|
|
11
|
+
{/snippet}
|
|
12
|
+
</Select>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state(['one', 'two']);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items bind:value multiple>
|
|
9
|
+
{#snippet selection({ selection, index })}
|
|
10
|
+
Index: {index} Slot: {selection.label}
|
|
11
|
+
{/snippet}
|
|
12
|
+
</Select>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Select from '../../src/lib/Select.svelte';
|
|
3
|
+
|
|
4
|
+
let items = $state(['one', 'two']);
|
|
5
|
+
let value = $state('one');
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Select bind:items bind:value>
|
|
9
|
+
{#snippet selection({ selection })}
|
|
10
|
+
Slot: {selection.label}
|
|
11
|
+
{/snippet}
|
|
12
|
+
</Select>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="testClearIcon" width="100%" height="100%" viewBox="0 0 512 512"><path d="m437 75c-100-100-262-100-362 0-100 100-100 262 0 362 100 100 262 100 362 0 100-100 100-262 0-362z m-86 274c-1 1-1 1 0 0-5 4-10 6-15 7-6 0-11-2-15-6l-65-65-65 65c-4 4-9 6-15 6-5 0-10-2-14-6-8-8-8-21-1-29 0 0 0 0 1 0l65-65-65-65c-8-8-8-21 0-29 8-8 21-8 29 0l65 65 65-65c8-8 21-8 29 0 8 8 8 21 0 29l-65 65 65 65c8 8 8 21 1 28z" fill="currentColor"></path></svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
id="testIcon"
|
|
3
|
+
width="16"
|
|
4
|
+
height="16"
|
|
5
|
+
viewBox="0 0 16 16"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
7
|
+
<path
|
|
8
|
+
d="M11.0526 0.294881L9.28328 2.06416L13.9577 6.71672L15.7051 4.94744C16.0983
|
|
9
|
+
4.55427 16.0983 4.00819 15.7051 3.61502L12.4068 0.294881C11.9918 -0.0982935
|
|
10
|
+
11.4457 -0.0982935 11.0526 0.294881ZM2.11877 11.2164L1.66007 13.4881L2.51195
|
|
11
|
+
14.3618L4.78362 13.8812L2.11877 11.2164ZM0 15.5631L1.11399 10.2116L8.51877
|
|
12
|
+
2.80683L13.1932 7.45939L5.76655 14.8642L0.371331 15.9563C0.349488 15.9782
|
|
13
|
+
0.327645 15.9782 0.305802 15.9782C0.174744 15.9782 0 15.8471 0 15.5631Z"
|
|
14
|
+
fill="currentColor" />
|
|
15
|
+
</svg>
|
package/test/src/env.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const browser = true;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test utilities for Svelte 5 component testing
|
|
3
|
+
*
|
|
4
|
+
* This module provides backward-compatible helpers for testing Svelte 5 components
|
|
5
|
+
* using an API similar to Svelte 3/4.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { mount, unmount, flushSync } from 'svelte';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a test instance of a Svelte component with a backward-compatible API.
|
|
12
|
+
*
|
|
13
|
+
* In Svelte 5, components no longer have $set or direct property access.
|
|
14
|
+
* This wrapper provides those capabilities for testing purposes.
|
|
15
|
+
*
|
|
16
|
+
* @param {Component} Component - The Svelte component to instantiate
|
|
17
|
+
* @param {Object} options - Options for the component
|
|
18
|
+
* @param {HTMLElement} options.target - The DOM element to mount the component in
|
|
19
|
+
* @param {Object} options.props - Initial props for the component
|
|
20
|
+
* @returns {Object} A wrapper object with $set, $destroy, and property access
|
|
21
|
+
*/
|
|
22
|
+
export function createTestComponent(Component, { target, props = {} } = {}) {
|
|
23
|
+
let currentProps = { ...props };
|
|
24
|
+
let instance = null;
|
|
25
|
+
let eventHandlers = {};
|
|
26
|
+
|
|
27
|
+
// Create a proxy to intercept property access
|
|
28
|
+
const handler = {
|
|
29
|
+
get(obj, prop) {
|
|
30
|
+
if (prop === '$set') {
|
|
31
|
+
return async function(newProps) {
|
|
32
|
+
// Merge new props
|
|
33
|
+
currentProps = { ...currentProps, ...newProps };
|
|
34
|
+
|
|
35
|
+
// Unmount and remount with new props
|
|
36
|
+
if (instance) {
|
|
37
|
+
unmount(instance);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Re-apply event handlers as callback props
|
|
41
|
+
const propsWithEvents = { ...currentProps };
|
|
42
|
+
for (const [event, handler] of Object.entries(eventHandlers)) {
|
|
43
|
+
propsWithEvents[event] = handler;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
instance = mount(Component, { target, props: propsWithEvents });
|
|
47
|
+
flushSync();
|
|
48
|
+
|
|
49
|
+
return new Promise(f => setTimeout(f, 0));
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (prop === '$destroy') {
|
|
54
|
+
return function() {
|
|
55
|
+
if (instance) {
|
|
56
|
+
unmount(instance);
|
|
57
|
+
instance = null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (prop === '$on') {
|
|
63
|
+
return function(eventName, handler) {
|
|
64
|
+
// Store event handlers as callback props
|
|
65
|
+
const callbackName = `on${eventName}`;
|
|
66
|
+
eventHandlers[callbackName] = handler;
|
|
67
|
+
|
|
68
|
+
// Re-mount with new event handler
|
|
69
|
+
currentProps[callbackName] = (detail) => handler({ detail });
|
|
70
|
+
if (instance) {
|
|
71
|
+
unmount(instance);
|
|
72
|
+
}
|
|
73
|
+
instance = mount(Component, { target, props: currentProps });
|
|
74
|
+
flushSync();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Return current prop value
|
|
79
|
+
if (prop in currentProps) {
|
|
80
|
+
return currentProps[prop];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return undefined;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
set(obj, prop, value) {
|
|
87
|
+
// Update prop and re-mount
|
|
88
|
+
currentProps[prop] = value;
|
|
89
|
+
|
|
90
|
+
if (instance) {
|
|
91
|
+
unmount(instance);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const propsWithEvents = { ...currentProps };
|
|
95
|
+
for (const [event, handler] of Object.entries(eventHandlers)) {
|
|
96
|
+
propsWithEvents[event] = handler;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
instance = mount(Component, { target, props: propsWithEvents });
|
|
100
|
+
flushSync();
|
|
101
|
+
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Initial mount
|
|
107
|
+
const propsWithEvents = { ...currentProps };
|
|
108
|
+
instance = mount(Component, { target, props: propsWithEvents });
|
|
109
|
+
flushSync();
|
|
110
|
+
|
|
111
|
+
return new Proxy({}, handler);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Helper to wait for next tick
|
|
116
|
+
*/
|
|
117
|
+
export function tick() {
|
|
118
|
+
return new Promise(f => setTimeout(f, 0));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Helper to flush pending updates synchronously
|
|
123
|
+
*/
|
|
124
|
+
export { flushSync };
|