svelte-multiselect 11.2.3 → 11.2.4
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/CmdPalette.svelte +15 -11
- package/dist/CmdPalette.svelte.d.ts +5 -3
- package/dist/CopyButton.svelte +25 -3
- package/dist/CopyButton.svelte.d.ts +2 -2
- package/dist/Icon.svelte.d.ts +2 -2
- package/dist/MultiSelect.svelte +82 -60
- package/dist/MultiSelect.svelte.d.ts +2 -2
- package/dist/PrevNext.svelte +9 -8
- package/dist/PrevNext.svelte.d.ts +32 -51
- package/dist/Toggle.svelte +2 -1
- package/dist/Toggle.svelte.d.ts +2 -2
- package/dist/attachments.d.ts +9 -6
- package/dist/attachments.js +124 -35
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +4 -20
- package/dist/utils.d.ts +3 -8
- package/dist/utils.js +24 -48
- package/package.json +17 -17
- package/readme.md +4 -4
- package/dist/RadioButtons.svelte +0 -67
- package/dist/RadioButtons.svelte.d.ts +0 -51
package/dist/CmdPalette.svelte
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
2
|
-
import
|
|
1
|
+
<script lang="ts">import { fade } from 'svelte/transition';
|
|
2
|
+
import MultiSelect from './MultiSelect.svelte';
|
|
3
3
|
let { actions, triggers = [`k`], close_keys = [`Escape`], fade_duration = 200, dialog_style = ``, open = $bindable(false), dialog = $bindable(null), input = $bindable(null), placeholder = `Filter actions...`, ...rest } = $props();
|
|
4
4
|
$effect(() => {
|
|
5
|
-
if (open && input && document.activeElement !== input)
|
|
5
|
+
if (open && input && document.activeElement !== input)
|
|
6
6
|
input.focus();
|
|
7
|
-
}
|
|
8
7
|
});
|
|
9
8
|
async function toggle(event) {
|
|
10
|
-
|
|
9
|
+
const is_trigger = triggers.includes(event.key) &&
|
|
10
|
+
(event.metaKey || event.ctrlKey);
|
|
11
|
+
if (is_trigger && !open)
|
|
11
12
|
open = true;
|
|
12
|
-
|
|
13
|
-
else if (close_keys.includes(event.key) && open) {
|
|
13
|
+
else if (close_keys.includes(event.key) && open)
|
|
14
14
|
open = false;
|
|
15
|
-
}
|
|
16
15
|
}
|
|
17
16
|
function close_if_outside(event) {
|
|
18
|
-
|
|
17
|
+
const target = event.target;
|
|
18
|
+
if (!target || !(target instanceof HTMLElement))
|
|
19
|
+
return;
|
|
20
|
+
if (open && !dialog?.contains(target) && !target.closest(`ul.options`)) {
|
|
19
21
|
open = false;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
|
-
function trigger_action_and_close(
|
|
23
|
-
const { action, label } =
|
|
24
|
+
function trigger_action_and_close({ option }) {
|
|
25
|
+
const { action, label } = (option ?? {});
|
|
26
|
+
if (!action)
|
|
27
|
+
return;
|
|
24
28
|
action(label);
|
|
25
29
|
open = false;
|
|
26
30
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import MultiSelect from './MultiSelect.svelte';
|
|
3
|
+
import type { ObjectOption } from './types';
|
|
2
4
|
interface Action extends ObjectOption {
|
|
3
5
|
label: string;
|
|
4
6
|
action: (label: string) => void;
|
|
5
7
|
}
|
|
6
|
-
interface Props extends Omit<
|
|
8
|
+
interface Props extends Omit<ComponentProps<typeof MultiSelect<Action>>, `options`> {
|
|
7
9
|
actions: Action[];
|
|
8
10
|
triggers?: string[];
|
|
9
11
|
close_keys?: string[];
|
|
@@ -14,6 +16,6 @@ interface Props extends Omit<MultiSelectProps<Action>, `options`> {
|
|
|
14
16
|
input?: HTMLInputElement | null;
|
|
15
17
|
placeholder?: string;
|
|
16
18
|
}
|
|
17
|
-
declare const CmdPalette: import("svelte").Component<Props, {}, "
|
|
19
|
+
declare const CmdPalette: import("svelte").Component<Props, {}, "dialog" | "input" | "open">;
|
|
18
20
|
type CmdPalette = ReturnType<typeof CmdPalette>;
|
|
19
21
|
export default CmdPalette;
|
package/dist/CopyButton.svelte
CHANGED
|
@@ -10,13 +10,22 @@ $effect(() => {
|
|
|
10
10
|
return;
|
|
11
11
|
const apply_copy_buttons = () => {
|
|
12
12
|
const btn_style = `position: absolute; top: 9pt; right: 9pt; ${rest.style ?? ``}`;
|
|
13
|
+
const skip_sel = skip_selector ?? as;
|
|
13
14
|
for (const code of document.querySelectorAll(global_selector ?? `pre > code`)) {
|
|
14
15
|
const pre = code.parentElement;
|
|
15
16
|
const content = code.textContent ?? ``;
|
|
16
|
-
if (pre && !
|
|
17
|
+
if (pre && !pre.querySelector(`[data-sms-copy]`) &&
|
|
18
|
+
!(skip_sel && pre.querySelector(skip_sel))) {
|
|
17
19
|
mount(CopyButton, {
|
|
18
20
|
target: pre,
|
|
19
|
-
props: {
|
|
21
|
+
props: {
|
|
22
|
+
content,
|
|
23
|
+
as,
|
|
24
|
+
labels,
|
|
25
|
+
...rest,
|
|
26
|
+
style: btn_style,
|
|
27
|
+
'data-sms-copy': ``,
|
|
28
|
+
},
|
|
20
29
|
});
|
|
21
30
|
}
|
|
22
31
|
}
|
|
@@ -41,7 +50,20 @@ async function copy() {
|
|
|
41
50
|
|
|
42
51
|
{#if !(global || global_selector)}
|
|
43
52
|
{@const { text, icon } = labels[state]}
|
|
44
|
-
<svelte:element
|
|
53
|
+
<svelte:element
|
|
54
|
+
this={as}
|
|
55
|
+
onclick={copy}
|
|
56
|
+
onkeydown={(event) => {
|
|
57
|
+
if (event.key === `Enter` || event.key === ` `) {
|
|
58
|
+
event.preventDefault()
|
|
59
|
+
copy()
|
|
60
|
+
}
|
|
61
|
+
}}
|
|
62
|
+
role="button"
|
|
63
|
+
tabindex={0}
|
|
64
|
+
data-sms-copy=""
|
|
65
|
+
{...rest}
|
|
66
|
+
>
|
|
45
67
|
{#if children}
|
|
46
68
|
{@render children({ state, icon, text })}
|
|
47
69
|
{:else}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CopyButton } from './';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import type { IconName } from './icons';
|
|
4
5
|
type State = `ready` | `success` | `error`;
|
|
5
|
-
interface Props {
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<HTMLButtonElement>, `children`> {
|
|
6
7
|
content?: string;
|
|
7
8
|
state?: State;
|
|
8
9
|
global_selector?: string | null;
|
|
@@ -18,7 +19,6 @@ interface Props {
|
|
|
18
19
|
icon: IconName;
|
|
19
20
|
text: string;
|
|
20
21
|
}]>;
|
|
21
|
-
[key: string]: unknown;
|
|
22
22
|
}
|
|
23
23
|
declare const CopyButton: import("svelte").Component<Props, {}, "state">;
|
|
24
24
|
type CopyButton = ReturnType<typeof CopyButton>;
|
package/dist/Icon.svelte.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
1
2
|
import { type IconName } from './icons';
|
|
2
|
-
interface Props {
|
|
3
|
+
interface Props extends HTMLAttributes<SVGSVGElement> {
|
|
3
4
|
icon: IconName;
|
|
4
|
-
[key: string]: unknown;
|
|
5
5
|
}
|
|
6
6
|
declare const Icon: import("svelte").Component<Props, {}, "">;
|
|
7
7
|
type Icon = ReturnType<typeof Icon>;
|
package/dist/MultiSelect.svelte
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
2
|
-
import { tick } from 'svelte';
|
|
1
|
+
<script lang="ts">import { tick } from 'svelte';
|
|
3
2
|
import { flip } from 'svelte/animate';
|
|
4
|
-
import {
|
|
3
|
+
import { highlight_matches } from './attachments';
|
|
4
|
+
import CircleSpinner from './CircleSpinner.svelte';
|
|
5
|
+
import Icon from './Icon.svelte';
|
|
6
|
+
import { fuzzy_match, get_label, get_style } from './utils';
|
|
7
|
+
import Wiggle from './Wiggle.svelte';
|
|
5
8
|
let { activeIndex = $bindable(null), activeOption = $bindable(null), createOptionMsg = `Create this option...`, allowUserOptions = false, allowEmpty = false, autocomplete = `off`, autoScroll = true, breakpoint = 800, defaultDisabledTitle = `This option is disabled`, disabled = false, disabledInputTitle = `This input is disabled`, duplicateOptionMsg = `This option is already selected`, duplicates = false, keepSelectedInDropdown = false, key = (opt) => `${get_label(opt)}`.toLowerCase(), filterFunc = (opt, searchText) => {
|
|
6
9
|
if (!searchText)
|
|
7
10
|
return true;
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
const label = `${get_label(opt)}`;
|
|
12
|
+
return fuzzy
|
|
13
|
+
? fuzzy_match(searchText, label)
|
|
14
|
+
: label.toLowerCase().includes(searchText.toLowerCase());
|
|
15
|
+
}, fuzzy = true, closeDropdownOnSelect = `if-mobile`, form_input = $bindable(null), highlightMatches = true, id = null, input = $bindable(null), inputClass = ``, inputStyle = null, inputmode = null, invalid = $bindable(false), liActiveOptionClass = ``, liActiveUserMsgClass = ``, liOptionClass = ``, liOptionStyle = null, liSelectedClass = ``, liSelectedStyle = null, liUserMsgClass = ``, loading = false, matchingOptions = $bindable([]), maxOptions = undefined, maxSelect = null, maxSelectMsg = (current, max) => (max > 1 ? `${current}/${max}` : ``), maxSelectMsgClass = ``, name = null, noMatchingOptionsMsg = `No matching options`, open = $bindable(false), options = $bindable(), outerDiv = $bindable(null), outerDivClass = ``, parseLabelsAsHtml = false, pattern = null, placeholder = null, removeAllTitle = `Remove all`, removeBtnTitle = `Remove`, minSelect = null, required = false, resetFilterOnAdd = true, searchText = $bindable(``), selected = $bindable(options
|
|
10
16
|
?.filter((opt) => opt instanceof Object && opt?.preselected)
|
|
11
17
|
.slice(0, maxSelect ?? undefined) ?? []), sortSelected = false, selectedOptionsDraggable = !sortSelected, style = null, ulOptionsClass = ``, ulSelectedClass = ``, ulSelectedStyle = null, ulOptionsStyle = null, value = $bindable(null), expandIcon, selectedItem, children, removeIcon, afterInput, spinner, disabledIcon, option, userMsg, onblur, onclick, onfocus, onkeydown, onkeyup, onmousedown, onmouseenter, onmouseleave, ontouchcancel, ontouchend, ontouchmove, ontouchstart, onadd, oncreate, onremove, onremoveAll, onchange, onopen, onclose, portal: portal_params = {}, ...rest } = $props();
|
|
12
18
|
$effect.pre(() => {
|
|
@@ -257,11 +263,25 @@ async function handle_keydown(event) {
|
|
|
257
263
|
// if none of the above special cases apply, we make next/prev option
|
|
258
264
|
// active with wrap around at both ends
|
|
259
265
|
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
260
|
-
|
|
266
|
+
// Include user message in total count if it exists
|
|
267
|
+
const has_user_msg = searchText && ((allowUserOptions && createOptionMsg) ||
|
|
268
|
+
(!duplicates && selected.map(get_label).includes(searchText)) ||
|
|
269
|
+
(matchingOptions.length === 0 && noMatchingOptionsMsg));
|
|
270
|
+
const total_items = matchingOptions.length + (has_user_msg ? 1 : 0);
|
|
271
|
+
activeIndex = (activeIndex + increment) % total_items;
|
|
261
272
|
// in JS % behaves like remainder operator, not real modulo, so negative numbers stay negative
|
|
262
273
|
// need to do manual wrap around at 0
|
|
263
274
|
if (activeIndex < 0)
|
|
264
|
-
activeIndex =
|
|
275
|
+
activeIndex = total_items - 1;
|
|
276
|
+
// Handle user message activation
|
|
277
|
+
if (has_user_msg && activeIndex === matchingOptions.length) {
|
|
278
|
+
option_msg_is_active = true;
|
|
279
|
+
activeOption = null;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
option_msg_is_active = false;
|
|
283
|
+
activeOption = matchingOptions[activeIndex] ?? null;
|
|
284
|
+
}
|
|
265
285
|
if (autoScroll) {
|
|
266
286
|
await tick();
|
|
267
287
|
const li = document.querySelector(`ul.options > li.active`);
|
|
@@ -273,7 +293,9 @@ async function handle_keydown(event) {
|
|
|
273
293
|
event.stopPropagation();
|
|
274
294
|
// Only remove option if it wouldn't violate minSelect
|
|
275
295
|
if (minSelect === null || selected.length > minSelect) {
|
|
276
|
-
|
|
296
|
+
const last_option = selected.at(-1);
|
|
297
|
+
if (last_option)
|
|
298
|
+
remove(last_option, event);
|
|
277
299
|
}
|
|
278
300
|
// Don't prevent default, allow normal backspace behavior if not removing
|
|
279
301
|
} // make first matching option active on any keypress (if none of the above special cases match)
|
|
@@ -310,9 +332,17 @@ const if_enter_or_space = (handler) => (event) => {
|
|
|
310
332
|
}
|
|
311
333
|
};
|
|
312
334
|
function on_click_outside(event) {
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
335
|
+
if (!outerDiv)
|
|
336
|
+
return;
|
|
337
|
+
const target = event.target;
|
|
338
|
+
// Check if click is inside the main component
|
|
339
|
+
if (outerDiv.contains(target))
|
|
340
|
+
return;
|
|
341
|
+
// If portal is active, also check if click is inside the portalled options dropdown
|
|
342
|
+
if (portal_params?.active && ul_options && ul_options.contains(target))
|
|
343
|
+
return;
|
|
344
|
+
// Click is outside both the main component and any portalled dropdown
|
|
345
|
+
close_dropdown(event);
|
|
316
346
|
}
|
|
317
347
|
let drag_idx = $state(null);
|
|
318
348
|
// event handlers enable dragging to reorder selected options
|
|
@@ -342,27 +372,6 @@ const dragstart = (idx) => (event) => {
|
|
|
342
372
|
event.dataTransfer.setData(`text/plain`, `${idx}`);
|
|
343
373
|
};
|
|
344
374
|
let ul_options = $state();
|
|
345
|
-
// Update highlights whenever search text changes (after ul_options is available)
|
|
346
|
-
$effect(() => {
|
|
347
|
-
if (ul_options && highlightMatches) {
|
|
348
|
-
if (searchText) {
|
|
349
|
-
highlight_matching_nodes(ul_options, searchText, noMatchingOptionsMsg);
|
|
350
|
-
}
|
|
351
|
-
else if (typeof CSS !== `undefined` && CSS.highlights) {
|
|
352
|
-
CSS.highlights.delete?.(`sms-search-matches`); // Clear highlights when search text is empty
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
// highlight text matching user-entered search text in available options
|
|
357
|
-
function highlight_matching_options(event) {
|
|
358
|
-
if (!highlightMatches || !ul_options)
|
|
359
|
-
return;
|
|
360
|
-
// get input's search query
|
|
361
|
-
const query = event?.target?.value.trim().toLowerCase();
|
|
362
|
-
if (!query)
|
|
363
|
-
return;
|
|
364
|
-
highlight_matching_nodes(ul_options, query, noMatchingOptionsMsg);
|
|
365
|
-
}
|
|
366
375
|
const handle_input_keydown = (event) => {
|
|
367
376
|
handle_keydown(event); // Restore internal logic
|
|
368
377
|
// Call original forwarded handler
|
|
@@ -392,6 +401,7 @@ $effect(() => {
|
|
|
392
401
|
const handle_input_blur = (event) => {
|
|
393
402
|
// For portalled dropdowns, don't close on blur since clicks on portalled elements
|
|
394
403
|
// will cause blur but we want to allow the click to register first
|
|
404
|
+
// (otherwise mobile touch event is unable to select options https://github.com/janosh/svelte-multiselect/issues/335)
|
|
395
405
|
if (portal_params?.active) {
|
|
396
406
|
onblur?.(event); // Let the click handler manage closing for portalled dropdowns
|
|
397
407
|
return;
|
|
@@ -429,7 +439,7 @@ function portal(node, params) {
|
|
|
429
439
|
tick().then(update_position);
|
|
430
440
|
window.addEventListener(`scroll`, update_position, true);
|
|
431
441
|
window.addEventListener(`resize`, update_position);
|
|
432
|
-
$effect
|
|
442
|
+
$effect(() => {
|
|
433
443
|
if (open && target_node)
|
|
434
444
|
update_position();
|
|
435
445
|
else
|
|
@@ -505,8 +515,7 @@ function portal(node, params) {
|
|
|
505
515
|
{:else}
|
|
506
516
|
<Icon
|
|
507
517
|
icon="ChevronExpand"
|
|
508
|
-
|
|
509
|
-
style="min-width: 1em; padding: 0 1pt; cursor: pointer"
|
|
518
|
+
style="width: 15px; min-width: 1em; padding: 0 1pt; cursor: pointer"
|
|
510
519
|
/>
|
|
511
520
|
{/if}
|
|
512
521
|
<ul
|
|
@@ -561,7 +570,7 @@ function portal(node, params) {
|
|
|
561
570
|
{#if removeIcon}
|
|
562
571
|
{@render removeIcon()}
|
|
563
572
|
{:else}
|
|
564
|
-
<Icon icon="Cross"
|
|
573
|
+
<Icon icon="Cross" style="width: 15px" />
|
|
565
574
|
{/if}
|
|
566
575
|
</button>
|
|
567
576
|
{/if}
|
|
@@ -577,13 +586,12 @@ function portal(node, params) {
|
|
|
577
586
|
{autocomplete}
|
|
578
587
|
{inputmode}
|
|
579
588
|
{pattern}
|
|
580
|
-
placeholder={selected.length
|
|
589
|
+
placeholder={selected.length === 0 ? placeholder : null}
|
|
581
590
|
aria-invalid={invalid ? `true` : null}
|
|
582
591
|
ondrop={() => false}
|
|
583
592
|
onmouseup={open_dropdown}
|
|
584
593
|
onkeydown={handle_input_keydown}
|
|
585
594
|
onfocus={handle_input_focus}
|
|
586
|
-
oninput={highlight_matching_options}
|
|
587
595
|
onblur={handle_input_blur}
|
|
588
596
|
{onclick}
|
|
589
597
|
{onkeyup}
|
|
@@ -619,8 +627,7 @@ function portal(node, params) {
|
|
|
619
627
|
{:else}
|
|
620
628
|
<Icon
|
|
621
629
|
icon="Disabled"
|
|
622
|
-
|
|
623
|
-
style="margin: 0 2pt"
|
|
630
|
+
style="width: 14pt; margin: 0 2pt"
|
|
624
631
|
data-name="disabled-icon"
|
|
625
632
|
aria-disabled="true"
|
|
626
633
|
/>
|
|
@@ -644,7 +651,7 @@ function portal(node, params) {
|
|
|
644
651
|
{#if removeIcon}
|
|
645
652
|
{@render removeIcon()}
|
|
646
653
|
{:else}
|
|
647
|
-
<Icon icon="Cross"
|
|
654
|
+
<Icon icon="Cross" style="width: 15px" />
|
|
648
655
|
{/if}
|
|
649
656
|
</button>
|
|
650
657
|
{/if}
|
|
@@ -654,6 +661,17 @@ function portal(node, params) {
|
|
|
654
661
|
{#if (searchText && noMatchingOptionsMsg) || options?.length > 0}
|
|
655
662
|
<ul
|
|
656
663
|
use:portal={{ target_node: outerDiv, ...portal_params }}
|
|
664
|
+
{@attach highlight_matches({
|
|
665
|
+
query: searchText,
|
|
666
|
+
disabled: !highlightMatches,
|
|
667
|
+
fuzzy,
|
|
668
|
+
css_class: `sms-search-matches`,
|
|
669
|
+
// don't highlight text in the "Create this option..." message
|
|
670
|
+
node_filter: (node) =>
|
|
671
|
+
node?.parentElement?.closest(`li.user-msg`)
|
|
672
|
+
? NodeFilter.FILTER_REJECT
|
|
673
|
+
: NodeFilter.FILTER_ACCEPT,
|
|
674
|
+
})}
|
|
657
675
|
class:hidden={!open}
|
|
658
676
|
class="options {ulOptionsClass}"
|
|
659
677
|
role="listbox"
|
|
@@ -663,10 +681,13 @@ function portal(node, params) {
|
|
|
663
681
|
bind:this={ul_options}
|
|
664
682
|
style={ulOptionsStyle}
|
|
665
683
|
>
|
|
666
|
-
{#each matchingOptions.slice(
|
|
667
|
-
|
|
684
|
+
{#each matchingOptions.slice(
|
|
685
|
+
0,
|
|
686
|
+
maxOptions == null ? Infinity : Math.max(0, maxOptions),
|
|
687
|
+
) as
|
|
688
|
+
option_item,
|
|
668
689
|
idx
|
|
669
|
-
(duplicates ? [key(
|
|
690
|
+
(duplicates ? [key(option_item), idx] : key(option_item))
|
|
670
691
|
}
|
|
671
692
|
{@const {
|
|
672
693
|
label,
|
|
@@ -674,21 +695,22 @@ function portal(node, params) {
|
|
|
674
695
|
title = null,
|
|
675
696
|
selectedTitle = null,
|
|
676
697
|
disabledTitle = defaultDisabledTitle,
|
|
677
|
-
} =
|
|
698
|
+
} = option_item instanceof Object ? option_item : { label: option_item }}
|
|
678
699
|
{@const active = activeIndex === idx}
|
|
700
|
+
{@const selected = is_selected(label)}
|
|
679
701
|
{@const optionStyle =
|
|
680
|
-
[get_style(
|
|
702
|
+
[get_style(option_item, `option`), liOptionStyle].filter(Boolean).join(
|
|
681
703
|
` `,
|
|
682
704
|
) ||
|
|
683
705
|
null}
|
|
684
706
|
<li
|
|
685
707
|
onclick={(event) => {
|
|
686
708
|
if (disabled) return
|
|
687
|
-
if (keepSelectedInDropdown) toggle_option(
|
|
688
|
-
else add(
|
|
709
|
+
if (keepSelectedInDropdown) toggle_option(option_item, event)
|
|
710
|
+
else add(option_item, event)
|
|
689
711
|
}}
|
|
690
|
-
title={disabled ? disabledTitle : (
|
|
691
|
-
class:selected
|
|
712
|
+
title={disabled ? disabledTitle : (selected && selectedTitle) || title}
|
|
713
|
+
class:selected
|
|
692
714
|
class:active
|
|
693
715
|
class:disabled
|
|
694
716
|
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
@@ -699,13 +721,13 @@ function portal(node, params) {
|
|
|
699
721
|
if (!disabled) activeIndex = idx
|
|
700
722
|
}}
|
|
701
723
|
role="option"
|
|
702
|
-
aria-selected=
|
|
724
|
+
aria-selected={selected ? `true` : `false`}
|
|
703
725
|
style={optionStyle}
|
|
704
726
|
onkeydown={(event) => {
|
|
705
727
|
if (!disabled && (event.key === `Enter` || event.code === `Space`)) {
|
|
706
728
|
event.preventDefault()
|
|
707
|
-
if (keepSelectedInDropdown) toggle_option(
|
|
708
|
-
else add(
|
|
729
|
+
if (keepSelectedInDropdown) toggle_option(option_item, event)
|
|
730
|
+
else add(option_item, event)
|
|
709
731
|
}
|
|
710
732
|
}}
|
|
711
733
|
>
|
|
@@ -713,25 +735,25 @@ function portal(node, params) {
|
|
|
713
735
|
<input
|
|
714
736
|
type="checkbox"
|
|
715
737
|
class="option-checkbox"
|
|
716
|
-
checked={
|
|
717
|
-
aria-label="Toggle {get_label(
|
|
738
|
+
checked={selected}
|
|
739
|
+
aria-label="Toggle {get_label(option_item)}"
|
|
718
740
|
tabindex="-1"
|
|
719
741
|
/>
|
|
720
742
|
{/if}
|
|
721
743
|
{#if option}
|
|
722
744
|
{@render option({
|
|
723
|
-
option:
|
|
745
|
+
option: option_item,
|
|
724
746
|
idx,
|
|
725
747
|
})}
|
|
726
748
|
{:else if children}
|
|
727
749
|
{@render children({
|
|
728
|
-
option:
|
|
750
|
+
option: option_item,
|
|
729
751
|
idx,
|
|
730
752
|
})}
|
|
731
753
|
{:else if parseLabelsAsHtml}
|
|
732
|
-
{@html get_label(
|
|
754
|
+
{@html get_label(option_item)}
|
|
733
755
|
{:else}
|
|
734
|
-
{get_label(
|
|
756
|
+
{get_label(option_item)}
|
|
735
757
|
{/if}
|
|
736
758
|
</li>
|
|
737
759
|
{/each}
|
|
@@ -739,7 +761,7 @@ function portal(node, params) {
|
|
|
739
761
|
{@const text_input_is_duplicate = selected.map(get_label).includes(searchText)}
|
|
740
762
|
{@const is_dupe = !duplicates && text_input_is_duplicate && `dupe`}
|
|
741
763
|
{@const can_create = Boolean(allowUserOptions && createOptionMsg) && `create`}
|
|
742
|
-
{@const no_match = Boolean(matchingOptions?.length
|
|
764
|
+
{@const no_match = Boolean(matchingOptions?.length === 0 && noMatchingOptionsMsg) &&
|
|
743
765
|
`no-match`}
|
|
744
766
|
{@const msgType = is_dupe || can_create || no_match}
|
|
745
767
|
{#if msgType}
|
|
@@ -2,7 +2,7 @@ import type { MultiSelectProps, Option as T } from './types';
|
|
|
2
2
|
declare function $$render<Option extends T>(): {
|
|
3
3
|
props: MultiSelectProps;
|
|
4
4
|
exports: {};
|
|
5
|
-
bindings: "
|
|
5
|
+
bindings: "input" | "invalid" | "value" | "selected" | "open" | "activeIndex" | "activeOption" | "form_input" | "matchingOptions" | "options" | "outerDiv" | "searchText";
|
|
6
6
|
slots: {};
|
|
7
7
|
events: {};
|
|
8
8
|
};
|
|
@@ -10,7 +10,7 @@ declare class __sveltets_Render<Option extends T> {
|
|
|
10
10
|
props(): ReturnType<typeof $$render<Option>>['props'];
|
|
11
11
|
events(): ReturnType<typeof $$render<Option>>['events'];
|
|
12
12
|
slots(): ReturnType<typeof $$render<Option>>['slots'];
|
|
13
|
-
bindings(): "
|
|
13
|
+
bindings(): "input" | "invalid" | "value" | "selected" | "open" | "activeIndex" | "activeOption" | "form_input" | "matchingOptions" | "options" | "outerDiv" | "searchText";
|
|
14
14
|
exports(): {};
|
|
15
15
|
}
|
|
16
16
|
interface $$IsomorphicComponent {
|
package/dist/PrevNext.svelte
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
<script lang="ts">let { items = [], node = `nav`, current = ``, log = `errors`, nav_options = { replace_state: true, no_scroll: true }, titles = { prev: `← Previous`, next: `Next →` }, onkeyup = ({ prev, next }) => ({
|
|
2
|
-
ArrowLeft: prev[0],
|
|
3
|
-
ArrowRight: next[0],
|
|
4
|
-
}), prev_snippet, children, between, next_snippet, ...rest } = $props();
|
|
1
|
+
<script lang="ts">let { items = [], node = `nav`, current = ``, log = `errors`, nav_options = { replace_state: true, no_scroll: true }, titles = { prev: `← Previous`, next: `Next →` }, onkeyup = ({ prev, next }) => ({ ArrowLeft: prev[0], ArrowRight: next[0] }), prev_snippet, children, between, next_snippet, min_items = 3, ...rest } = $props();
|
|
5
2
|
// Convert items to consistent [key, value] format
|
|
6
3
|
let items_arr = $derived((items ?? []).map((itm) => (typeof itm === `string` ? [itm, itm] : itm)));
|
|
7
4
|
// Calculate prev/next items with wraparound
|
|
@@ -11,8 +8,8 @@ let next = $derived(items_arr[idx + 1] ?? items_arr[0]);
|
|
|
11
8
|
// Validation and logging
|
|
12
9
|
$effect.pre(() => {
|
|
13
10
|
if (log !== `silent`) {
|
|
14
|
-
if (items_arr.length <
|
|
15
|
-
console.warn(`PrevNext received ${items_arr.length} items - minimum of
|
|
11
|
+
if (items_arr.length < min_items && log === `verbose`) {
|
|
12
|
+
console.warn(`PrevNext received ${items_arr.length} items - minimum of ${min_items} expected`);
|
|
16
13
|
}
|
|
17
14
|
if (idx < 0 && log === `errors`) {
|
|
18
15
|
const valid = items_arr.map(([key]) => key);
|
|
@@ -23,9 +20,13 @@ $effect.pre(() => {
|
|
|
23
20
|
function handle_keyup(event) {
|
|
24
21
|
if (!onkeyup)
|
|
25
22
|
return;
|
|
23
|
+
if ((items_arr?.length ?? 0) < min_items)
|
|
24
|
+
return;
|
|
25
|
+
if (!prev || !next)
|
|
26
|
+
return;
|
|
26
27
|
const key_map = onkeyup({ prev, next });
|
|
27
28
|
const to = key_map[event.key];
|
|
28
|
-
if (to) {
|
|
29
|
+
if (to !== undefined) {
|
|
29
30
|
const { replace_state, no_scroll } = nav_options;
|
|
30
31
|
const [scroll_x, scroll_y] = no_scroll
|
|
31
32
|
? [window.scrollX, window.scrollY]
|
|
@@ -41,7 +42,7 @@ export {};
|
|
|
41
42
|
|
|
42
43
|
<svelte:window onkeyup={handle_keyup} />
|
|
43
44
|
|
|
44
|
-
{#if items_arr.length
|
|
45
|
+
{#if items_arr.length >= min_items}
|
|
45
46
|
<svelte:element this={node} class="prev-next" {...rest}>
|
|
46
47
|
<!-- ensures `prev` is a defined [key, value] tuple.
|
|
47
48
|
Due to prior normalization of the `items` prop, any defined `prev` item
|
|
@@ -1,55 +1,36 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
no_scroll: boolean;
|
|
13
|
-
};
|
|
14
|
-
titles?: {
|
|
15
|
-
prev: string;
|
|
16
|
-
next: string;
|
|
17
|
-
};
|
|
18
|
-
onkeyup?: ((obj: {
|
|
19
|
-
prev: Item;
|
|
20
|
-
next: Item;
|
|
21
|
-
}) => Record<string, string>) | null;
|
|
22
|
-
prev_snippet?: Snippet<[{
|
|
23
|
-
item: Item;
|
|
24
|
-
}]>;
|
|
25
|
-
children?: Snippet<[{
|
|
26
|
-
kind: `prev` | `next`;
|
|
27
|
-
item: Item;
|
|
28
|
-
}]>;
|
|
29
|
-
between?: Snippet<[]>;
|
|
30
|
-
next_snippet?: Snippet<[{
|
|
31
|
-
item: Item;
|
|
32
|
-
}]>;
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export type Item = [string, unknown];
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLElement>, `children` | `onkeyup`> {
|
|
5
|
+
items?: (string | Item)[];
|
|
6
|
+
node?: string;
|
|
7
|
+
current?: string;
|
|
8
|
+
log?: `verbose` | `errors` | `silent`;
|
|
9
|
+
nav_options?: {
|
|
10
|
+
replace_state: boolean;
|
|
11
|
+
no_scroll: boolean;
|
|
33
12
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
13
|
+
titles?: {
|
|
14
|
+
prev: string;
|
|
15
|
+
next: string;
|
|
16
|
+
};
|
|
17
|
+
onkeyup?: ((obj: {
|
|
18
|
+
prev: Item;
|
|
19
|
+
next: Item;
|
|
20
|
+
}) => Record<string, string | undefined>) | null;
|
|
21
|
+
prev_snippet?: Snippet<[{
|
|
22
|
+
item: Item;
|
|
23
|
+
}]>;
|
|
24
|
+
children?: Snippet<[{
|
|
25
|
+
kind: `prev` | `next`;
|
|
26
|
+
item: Item;
|
|
27
|
+
}]>;
|
|
28
|
+
between?: Snippet<[]>;
|
|
29
|
+
next_snippet?: Snippet<[{
|
|
30
|
+
item: Item;
|
|
31
|
+
}]>;
|
|
32
|
+
min_items?: number;
|
|
52
33
|
}
|
|
53
|
-
declare const PrevNext:
|
|
54
|
-
type PrevNext
|
|
34
|
+
declare const PrevNext: import("svelte").Component<Props, {}, "">;
|
|
35
|
+
type PrevNext = ReturnType<typeof PrevNext>;
|
|
55
36
|
export default PrevNext;
|
package/dist/Toggle.svelte
CHANGED
package/dist/Toggle.svelte.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
interface Props extends HTMLAttributes<HTMLLabelElement> {
|
|
3
4
|
checked?: boolean;
|
|
4
5
|
required?: boolean;
|
|
5
6
|
input_style?: string;
|
|
@@ -9,7 +10,6 @@ interface Props {
|
|
|
9
10
|
onblur?: (event: FocusEvent) => void;
|
|
10
11
|
onkeydown?: (event: KeyboardEvent) => void;
|
|
11
12
|
children?: Snippet<[]>;
|
|
12
|
-
[key: string]: unknown;
|
|
13
13
|
}
|
|
14
14
|
declare const Toggle: import("svelte").Component<Props, {}, "checked">;
|
|
15
15
|
type Toggle = ReturnType<typeof Toggle>;
|
package/dist/attachments.d.ts
CHANGED
|
@@ -25,22 +25,25 @@ export declare const sortable: ({ header_selector, asc_class, desc_class, sorted
|
|
|
25
25
|
backgroundColor: string;
|
|
26
26
|
} | undefined;
|
|
27
27
|
}) => (node: HTMLElement) => () => void;
|
|
28
|
-
type HighlightOptions = {
|
|
28
|
+
export type HighlightOptions = {
|
|
29
29
|
query?: string;
|
|
30
30
|
disabled?: boolean;
|
|
31
|
+
fuzzy?: boolean;
|
|
31
32
|
node_filter?: (node: Node) => number;
|
|
32
33
|
css_class?: string;
|
|
33
34
|
};
|
|
34
|
-
export declare const highlight_matches: (ops: HighlightOptions) => (node: HTMLElement) => (() =>
|
|
35
|
-
export
|
|
35
|
+
export declare const highlight_matches: (ops: HighlightOptions) => (node: HTMLElement) => (() => boolean) | undefined;
|
|
36
|
+
export interface TooltipOptions {
|
|
36
37
|
content?: string;
|
|
37
38
|
placement?: `top` | `bottom` | `left` | `right`;
|
|
38
39
|
delay?: number;
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
style?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const tooltip: (options?: TooltipOptions) => Attachment;
|
|
44
|
+
export type ClickOutsideConfig<T extends HTMLElement> = {
|
|
41
45
|
enabled?: boolean;
|
|
42
46
|
exclude?: string[];
|
|
43
47
|
callback?: (node: T, config: ClickOutsideConfig<T>) => void;
|
|
44
48
|
};
|
|
45
49
|
export declare const click_outside: <T extends HTMLElement>(config?: ClickOutsideConfig<T>) => (node: T) => () => void;
|
|
46
|
-
export {};
|
package/dist/attachments.js
CHANGED
|
@@ -158,11 +158,15 @@ export const sortable = ({ header_selector = `thead th`, asc_class = `table-sort
|
|
|
158
158
|
};
|
|
159
159
|
};
|
|
160
160
|
export const highlight_matches = (ops) => (node) => {
|
|
161
|
-
const { query = ``, disabled = false, node_filter = () => NodeFilter.FILTER_ACCEPT, css_class = `highlight-match`, } = ops;
|
|
162
|
-
//
|
|
163
|
-
CSS.highlights
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
const { query = ``, disabled = false, fuzzy = false, node_filter = () => NodeFilter.FILTER_ACCEPT, css_class = `highlight-match`, } = ops;
|
|
162
|
+
// abort if CSS highlight API not supported
|
|
163
|
+
if (typeof CSS === `undefined` || !CSS.highlights)
|
|
164
|
+
return;
|
|
165
|
+
// always clear our own highlight first
|
|
166
|
+
CSS.highlights.delete(css_class);
|
|
167
|
+
// if disabled or empty query, stop after cleanup
|
|
168
|
+
if (!query || disabled)
|
|
169
|
+
return;
|
|
166
170
|
const tree_walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, {
|
|
167
171
|
acceptNode: node_filter,
|
|
168
172
|
});
|
|
@@ -175,28 +179,57 @@ export const highlight_matches = (ops) => (node) => {
|
|
|
175
179
|
// iterate over all text nodes and find matches
|
|
176
180
|
const ranges = text_nodes.map((el) => {
|
|
177
181
|
const text = el.textContent?.toLowerCase();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
if (!text)
|
|
183
|
+
return [];
|
|
184
|
+
const search = query.toLowerCase();
|
|
185
|
+
if (fuzzy) {
|
|
186
|
+
// Fuzzy highlighting: highlight individual characters that match in order
|
|
187
|
+
const matching_indices = [];
|
|
188
|
+
let search_idx = 0;
|
|
189
|
+
let target_idx = 0;
|
|
190
|
+
// Find matching character indices
|
|
191
|
+
while (search_idx < search.length && target_idx < text.length) {
|
|
192
|
+
if (search[search_idx] === text[target_idx]) {
|
|
193
|
+
matching_indices.push(target_idx);
|
|
194
|
+
search_idx++;
|
|
195
|
+
}
|
|
196
|
+
target_idx++;
|
|
197
|
+
}
|
|
198
|
+
// Only create ranges if we found all characters in order
|
|
199
|
+
if (search_idx === search.length) {
|
|
200
|
+
return matching_indices.map((index) => {
|
|
201
|
+
const range = new Range();
|
|
202
|
+
range.setStart(el, index);
|
|
203
|
+
range.setEnd(el, index + 1); // highlight single character
|
|
204
|
+
return range;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// Substring highlighting: highlight consecutive substrings
|
|
211
|
+
const indices = [];
|
|
212
|
+
let start_pos = 0;
|
|
213
|
+
while (start_pos < text.length) {
|
|
214
|
+
const index = text.indexOf(search, start_pos);
|
|
215
|
+
if (index === -1)
|
|
216
|
+
break;
|
|
217
|
+
indices.push(index);
|
|
218
|
+
start_pos = index + search.length;
|
|
219
|
+
}
|
|
220
|
+
// create range object for each substring found in the text node
|
|
221
|
+
return indices.map((index) => {
|
|
222
|
+
const range = new Range();
|
|
223
|
+
range.setStart(el, index);
|
|
224
|
+
range.setEnd(el, index + search.length);
|
|
225
|
+
return range;
|
|
226
|
+
});
|
|
186
227
|
}
|
|
187
|
-
// create range object for each str found in the text node
|
|
188
|
-
return indices.map((index) => {
|
|
189
|
-
const range = new Range();
|
|
190
|
-
range.setStart(el, index);
|
|
191
|
-
range.setEnd(el, index + query?.length);
|
|
192
|
-
return range;
|
|
193
|
-
});
|
|
194
228
|
});
|
|
195
229
|
// create Highlight object from ranges and add to registry
|
|
196
230
|
CSS.highlights.set(css_class, new Highlight(...ranges.flat()));
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
231
|
+
// Return cleanup function
|
|
232
|
+
return () => CSS.highlights.delete(css_class);
|
|
200
233
|
};
|
|
201
234
|
// Global tooltip state to ensure only one tooltip is shown at a time
|
|
202
235
|
let current_tooltip = null;
|
|
@@ -220,7 +253,7 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
220
253
|
const safe_options = options || {};
|
|
221
254
|
const cleanup_functions = [];
|
|
222
255
|
function setup_tooltip(element) {
|
|
223
|
-
if (!element)
|
|
256
|
+
if (!element || safe_options.disabled)
|
|
224
257
|
return;
|
|
225
258
|
const content = safe_options.content || element.title ||
|
|
226
259
|
element.getAttribute(`aria-label`) || element.getAttribute(`data-title`);
|
|
@@ -239,6 +272,7 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
239
272
|
tooltip.className = `custom-tooltip`;
|
|
240
273
|
const placement = safe_options.placement || `bottom`;
|
|
241
274
|
tooltip.setAttribute(`data-placement`, placement);
|
|
275
|
+
// Apply base styles
|
|
242
276
|
tooltip.style.cssText = `
|
|
243
277
|
position: absolute; z-index: 9999; opacity: 0;
|
|
244
278
|
background: var(--tooltip-bg, #333); color: var(--text-color, white); border: var(--tooltip-border, none);
|
|
@@ -246,6 +280,16 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
246
280
|
max-width: var(--tooltip-max-width, 280px); word-wrap: break-word; pointer-events: none;
|
|
247
281
|
filter: var(--tooltip-shadow, drop-shadow(0 2px 8px rgba(0,0,0,0.25))); transition: opacity 0.15s ease-out;
|
|
248
282
|
`;
|
|
283
|
+
// Apply custom styles if provided (these will override base styles due to CSS specificity)
|
|
284
|
+
if (safe_options.style) {
|
|
285
|
+
// Parse and apply custom styles as individual properties for better control
|
|
286
|
+
const custom_styles = safe_options.style.split(`;`).filter((style) => style.trim());
|
|
287
|
+
custom_styles.forEach((style) => {
|
|
288
|
+
const [property, value] = style.split(`:`).map((s) => s.trim());
|
|
289
|
+
if (property && value)
|
|
290
|
+
tooltip.style.setProperty(property, value);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
249
293
|
tooltip.innerHTML = content?.replace(/\r/g, `<br/>`) ?? ``;
|
|
250
294
|
// Mirror CSS custom properties from the trigger node onto the tooltip element
|
|
251
295
|
const trigger_styles = getComputedStyle(element);
|
|
@@ -265,7 +309,10 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
265
309
|
if (value)
|
|
266
310
|
tooltip.style.setProperty(name, value);
|
|
267
311
|
});
|
|
268
|
-
//
|
|
312
|
+
// Append early so we can read computed border styles for arrow border
|
|
313
|
+
document.body.appendChild(tooltip);
|
|
314
|
+
// Arrow elements: optional border triangle behind fill triangle
|
|
315
|
+
const tooltip_styles = getComputedStyle(tooltip);
|
|
269
316
|
const arrow = document.createElement(`div`);
|
|
270
317
|
arrow.className = `custom-tooltip-arrow`;
|
|
271
318
|
arrow.style.cssText =
|
|
@@ -274,6 +321,49 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
274
321
|
.trim();
|
|
275
322
|
const arrow_size_num = Number.parseInt(arrow_size_raw || ``, 10);
|
|
276
323
|
const arrow_px = Number.isFinite(arrow_size_num) ? arrow_size_num : 6;
|
|
324
|
+
const border_color = tooltip_styles.borderTopColor;
|
|
325
|
+
const border_width_num = Number.parseFloat(tooltip_styles.borderTopWidth || `0`);
|
|
326
|
+
const has_border = !!border_color && border_color !== `rgba(0, 0, 0, 0)` &&
|
|
327
|
+
border_width_num > 0;
|
|
328
|
+
const maybe_append_border_arrow = () => {
|
|
329
|
+
if (!has_border)
|
|
330
|
+
return;
|
|
331
|
+
const border_arrow = document.createElement(`div`);
|
|
332
|
+
border_arrow.className = `custom-tooltip-arrow-border`;
|
|
333
|
+
border_arrow.style.cssText =
|
|
334
|
+
`position: absolute; width: 0; height: 0; pointer-events: none;`;
|
|
335
|
+
const border_size = arrow_px + (border_width_num * 1.4);
|
|
336
|
+
if (placement === `top`) {
|
|
337
|
+
border_arrow.style.left = `calc(50% - ${border_size}px)`;
|
|
338
|
+
border_arrow.style.bottom = `-${border_size}px`;
|
|
339
|
+
border_arrow.style.borderLeft = `${border_size}px solid transparent`;
|
|
340
|
+
border_arrow.style.borderRight = `${border_size}px solid transparent`;
|
|
341
|
+
border_arrow.style.borderTop = `${border_size}px solid ${border_color}`;
|
|
342
|
+
}
|
|
343
|
+
else if (placement === `left`) {
|
|
344
|
+
border_arrow.style.top = `calc(50% - ${border_size}px)`;
|
|
345
|
+
border_arrow.style.right = `-${border_size}px`;
|
|
346
|
+
border_arrow.style.borderTop = `${border_size}px solid transparent`;
|
|
347
|
+
border_arrow.style.borderBottom = `${border_size}px solid transparent`;
|
|
348
|
+
border_arrow.style.borderLeft = `${border_size}px solid ${border_color}`;
|
|
349
|
+
}
|
|
350
|
+
else if (placement === `right`) {
|
|
351
|
+
border_arrow.style.top = `calc(50% - ${border_size}px)`;
|
|
352
|
+
border_arrow.style.left = `-${border_size}px`;
|
|
353
|
+
border_arrow.style.borderTop = `${border_size}px solid transparent`;
|
|
354
|
+
border_arrow.style.borderBottom = `${border_size}px solid transparent`;
|
|
355
|
+
border_arrow.style.borderRight = `${border_size}px solid ${border_color}`;
|
|
356
|
+
}
|
|
357
|
+
else { // bottom
|
|
358
|
+
border_arrow.style.left = `calc(50% - ${border_size}px)`;
|
|
359
|
+
border_arrow.style.top = `-${border_size}px`;
|
|
360
|
+
border_arrow.style.borderLeft = `${border_size}px solid transparent`;
|
|
361
|
+
border_arrow.style.borderRight = `${border_size}px solid transparent`;
|
|
362
|
+
border_arrow.style.borderBottom = `${border_size}px solid ${border_color}`;
|
|
363
|
+
}
|
|
364
|
+
tooltip.appendChild(border_arrow);
|
|
365
|
+
};
|
|
366
|
+
// Create the fill arrow on top
|
|
277
367
|
if (placement === `top`) {
|
|
278
368
|
arrow.style.left = `calc(50% - ${arrow_px}px)`;
|
|
279
369
|
arrow.style.bottom = `-${arrow_px}px`;
|
|
@@ -302,8 +392,8 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
302
392
|
arrow.style.borderRight = `${arrow_px}px solid transparent`;
|
|
303
393
|
arrow.style.borderBottom = `${arrow_px}px solid var(--tooltip-bg, #333)`;
|
|
304
394
|
}
|
|
395
|
+
maybe_append_border_arrow();
|
|
305
396
|
tooltip.appendChild(arrow);
|
|
306
|
-
document.body.appendChild(tooltip);
|
|
307
397
|
// Position tooltip
|
|
308
398
|
const rect = element.getBoundingClientRect();
|
|
309
399
|
const tooltip_rect = tooltip.getBoundingClientRect();
|
|
@@ -337,23 +427,22 @@ export const tooltip = (options = {}) => (node) => {
|
|
|
337
427
|
}
|
|
338
428
|
function hide_tooltip() {
|
|
339
429
|
clear_tooltip();
|
|
340
|
-
|
|
430
|
+
if (current_tooltip) {
|
|
431
|
+
current_tooltip.style.opacity = `0`;
|
|
341
432
|
if (current_tooltip) {
|
|
342
|
-
current_tooltip.
|
|
343
|
-
|
|
344
|
-
if (current_tooltip) {
|
|
345
|
-
current_tooltip.remove();
|
|
346
|
-
current_tooltip = null;
|
|
347
|
-
}
|
|
348
|
-
}, 150);
|
|
433
|
+
current_tooltip.remove();
|
|
434
|
+
current_tooltip = null;
|
|
349
435
|
}
|
|
350
|
-
}
|
|
436
|
+
}
|
|
351
437
|
}
|
|
352
438
|
const events = [`mouseenter`, `mouseleave`, `focus`, `blur`];
|
|
353
439
|
const handlers = [show_tooltip, hide_tooltip, show_tooltip, hide_tooltip];
|
|
354
440
|
events.forEach((event, idx) => element.addEventListener(event, handlers[idx]));
|
|
441
|
+
// Hide tooltip when user scrolls
|
|
442
|
+
globalThis.addEventListener(`scroll`, hide_tooltip, true);
|
|
355
443
|
return () => {
|
|
356
444
|
events.forEach((event, idx) => element.removeEventListener(event, handlers[idx]));
|
|
445
|
+
globalThis.removeEventListener(`scroll`, hide_tooltip, true);
|
|
357
446
|
const original_title = element.getAttribute(`data-original-title`);
|
|
358
447
|
if (original_title) {
|
|
359
448
|
element.setAttribute(`title`, original_title);
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ export { default as GitHubCorner } from './GitHubCorner.svelte';
|
|
|
8
8
|
export { default as Icon } from './Icon.svelte';
|
|
9
9
|
export { default, default as MultiSelect } from './MultiSelect.svelte';
|
|
10
10
|
export { default as PrevNext } from './PrevNext.svelte';
|
|
11
|
-
export { default as RadioButtons } from './RadioButtons.svelte';
|
|
12
11
|
export { default as Toggle } from './Toggle.svelte';
|
|
13
12
|
export * from './types';
|
|
13
|
+
export * from './utils';
|
|
14
14
|
export { default as Wiggle } from './Wiggle.svelte';
|
|
15
15
|
export declare function scroll_into_view_if_needed_polyfill(element: Element, centerIfNeeded?: boolean): IntersectionObserver;
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,9 @@ export { default as GitHubCorner } from './GitHubCorner.svelte';
|
|
|
8
8
|
export { default as Icon } from './Icon.svelte';
|
|
9
9
|
export { default, default as MultiSelect } from './MultiSelect.svelte';
|
|
10
10
|
export { default as PrevNext } from './PrevNext.svelte';
|
|
11
|
-
export { default as RadioButtons } from './RadioButtons.svelte';
|
|
12
11
|
export { default as Toggle } from './Toggle.svelte';
|
|
13
12
|
export * from './types';
|
|
13
|
+
export * from './utils';
|
|
14
14
|
export { default as Wiggle } from './Wiggle.svelte';
|
|
15
15
|
// Firefox lacks support for scrollIntoViewIfNeeded (https://caniuse.com/scrollintoviewifneeded).
|
|
16
16
|
// See https://github.com/janosh/svelte-multiselect/issues/87
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
import type {
|
|
2
|
+
import type { HTMLAttributes, HTMLInputAttributes } from 'svelte/elements';
|
|
3
3
|
export type Option = string | number | ObjectOption;
|
|
4
4
|
export type OptionStyle = string | {
|
|
5
5
|
option: string;
|
|
@@ -16,20 +16,6 @@ export type ObjectOption = {
|
|
|
16
16
|
style?: OptionStyle;
|
|
17
17
|
[key: string]: unknown;
|
|
18
18
|
};
|
|
19
|
-
export interface MultiSelectNativeEvents {
|
|
20
|
-
onblur?: FocusEventHandler<HTMLInputElement>;
|
|
21
|
-
onclick?: MouseEventHandler<HTMLInputElement>;
|
|
22
|
-
onfocus?: FocusEventHandler<HTMLInputElement>;
|
|
23
|
-
onkeydown?: KeyboardEventHandler<HTMLInputElement>;
|
|
24
|
-
onkeyup?: KeyboardEventHandler<HTMLInputElement>;
|
|
25
|
-
onmousedown?: MouseEventHandler<HTMLInputElement>;
|
|
26
|
-
onmouseenter?: MouseEventHandler<HTMLInputElement>;
|
|
27
|
-
onmouseleave?: MouseEventHandler<HTMLInputElement>;
|
|
28
|
-
ontouchcancel?: TouchEventHandler<HTMLInputElement>;
|
|
29
|
-
ontouchend?: TouchEventHandler<HTMLInputElement>;
|
|
30
|
-
ontouchmove?: TouchEventHandler<HTMLInputElement>;
|
|
31
|
-
ontouchstart?: TouchEventHandler<HTMLInputElement>;
|
|
32
|
-
}
|
|
33
19
|
export interface MultiSelectEvents<T extends Option = Option> {
|
|
34
20
|
onadd?: (data: {
|
|
35
21
|
option: T;
|
|
@@ -55,7 +41,7 @@ export interface MultiSelectEvents<T extends Option = Option> {
|
|
|
55
41
|
event: Event;
|
|
56
42
|
}) => unknown;
|
|
57
43
|
}
|
|
58
|
-
type AfterInputProps = Pick<
|
|
44
|
+
type AfterInputProps = Pick<MultiSelectProps, `selected` | `disabled` | `invalid` | `id` | `placeholder` | `open` | `required`>;
|
|
59
45
|
type UserMsgProps = {
|
|
60
46
|
searchText: string;
|
|
61
47
|
msgType: false | `dupe` | `create` | `no-match`;
|
|
@@ -87,7 +73,7 @@ export interface PortalParams {
|
|
|
87
73
|
target_node?: HTMLElement | null;
|
|
88
74
|
active?: boolean;
|
|
89
75
|
}
|
|
90
|
-
export interface
|
|
76
|
+
export interface MultiSelectProps<T extends Option = Option> extends MultiSelectEvents<T>, MultiSelectSnippets<T>, Omit<HTMLAttributes<HTMLDivElement>, `children` | `onchange` | `onclose`> {
|
|
91
77
|
activeIndex?: number | null;
|
|
92
78
|
activeOption?: T | null;
|
|
93
79
|
createOptionMsg?: string | null;
|
|
@@ -104,6 +90,7 @@ export interface MultiSelectParameters<T extends Option = Option> {
|
|
|
104
90
|
keepSelectedInDropdown?: false | `plain` | `checkboxes`;
|
|
105
91
|
key?: (opt: T) => unknown;
|
|
106
92
|
filterFunc?: (opt: T, searchText: string) => boolean;
|
|
93
|
+
fuzzy?: boolean;
|
|
107
94
|
closeDropdownOnSelect?: boolean | `if-mobile` | `retain-focus`;
|
|
108
95
|
form_input?: HTMLInputElement | null;
|
|
109
96
|
highlightMatches?: boolean;
|
|
@@ -151,8 +138,5 @@ export interface MultiSelectParameters<T extends Option = Option> {
|
|
|
151
138
|
ulOptionsStyle?: string | null;
|
|
152
139
|
value?: T | T[] | null;
|
|
153
140
|
portal?: PortalParams;
|
|
154
|
-
[key: string]: unknown;
|
|
155
|
-
}
|
|
156
|
-
export interface MultiSelectProps<T extends Option = Option> extends MultiSelectNativeEvents, MultiSelectEvents<T>, MultiSelectSnippets<T>, MultiSelectParameters<T> {
|
|
157
141
|
}
|
|
158
142
|
export {};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import type { Option
|
|
1
|
+
import type { Option } from './types';
|
|
2
2
|
export declare const get_label: (opt: Option) => string | number;
|
|
3
|
-
export declare function get_style(option:
|
|
4
|
-
|
|
5
|
-
[key: string]: unknown;
|
|
6
|
-
} | string | number, key?: `selected` | `option` | null): string;
|
|
7
|
-
export declare function highlight_matching_nodes(element: HTMLElement, // parent element
|
|
8
|
-
query?: string, // search query
|
|
9
|
-
noMatchingOptionsMsg?: string): void;
|
|
3
|
+
export declare function get_style(option: Option, key?: `selected` | `option` | null | undefined): string;
|
|
4
|
+
export declare function fuzzy_match(search_text: string, target_text: string): boolean;
|
package/dist/utils.js
CHANGED
|
@@ -14,14 +14,15 @@ export const get_label = (opt) => {
|
|
|
14
14
|
// object to be used in the style attribute of the option.
|
|
15
15
|
// If the style is a string, it will be returned as is
|
|
16
16
|
export function get_style(option, key = null) {
|
|
17
|
+
if (key === undefined)
|
|
18
|
+
key = null;
|
|
17
19
|
let css_str = ``;
|
|
18
|
-
|
|
20
|
+
const valid_key = key === null || key === `selected` || key === `option`;
|
|
21
|
+
if (!valid_key)
|
|
19
22
|
console.error(`MultiSelect: Invalid key=${key} for get_style`);
|
|
20
|
-
}
|
|
21
23
|
if (typeof option === `object` && option.style) {
|
|
22
|
-
if (typeof option.style === `string`)
|
|
24
|
+
if (typeof option.style === `string`)
|
|
23
25
|
css_str = option.style;
|
|
24
|
-
}
|
|
25
26
|
if (typeof option.style === `object`) {
|
|
26
27
|
if (key && key in option.style)
|
|
27
28
|
return option.style[key] ?? ``;
|
|
@@ -35,49 +36,24 @@ export function get_style(option, key = null) {
|
|
|
35
36
|
css_str += `;`;
|
|
36
37
|
return css_str;
|
|
37
38
|
}
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let current_node = tree_walker.nextNode();
|
|
57
|
-
while (current_node) {
|
|
58
|
-
text_nodes.push(current_node);
|
|
59
|
-
current_node = tree_walker.nextNode();
|
|
39
|
+
// Fuzzy string matching function
|
|
40
|
+
// Returns true if the search string can be found as a subsequence in the target string
|
|
41
|
+
// e.g., "tageoo" matches "tasks/geo-opt" because t-a-g-e-o-o appears in order
|
|
42
|
+
export function fuzzy_match(search_text, target_text) {
|
|
43
|
+
// Handle null/undefined inputs first
|
|
44
|
+
if (search_text === null || search_text === undefined || target_text === null ||
|
|
45
|
+
target_text === undefined)
|
|
46
|
+
return false;
|
|
47
|
+
if (!search_text)
|
|
48
|
+
return true;
|
|
49
|
+
if (!target_text)
|
|
50
|
+
return false;
|
|
51
|
+
const [search, target] = [search_text.toLowerCase(), target_text.toLowerCase()];
|
|
52
|
+
let [search_idx, target_idx] = [0, 0];
|
|
53
|
+
while (search_idx < search.length && target_idx < target.length) {
|
|
54
|
+
if (search[search_idx] === target[target_idx])
|
|
55
|
+
search_idx++;
|
|
56
|
+
target_idx++;
|
|
60
57
|
}
|
|
61
|
-
|
|
62
|
-
const ranges = text_nodes.map((el) => {
|
|
63
|
-
const text = el.textContent?.toLowerCase();
|
|
64
|
-
const indices = [];
|
|
65
|
-
let start_pos = 0;
|
|
66
|
-
while (text && start_pos < text.length) {
|
|
67
|
-
const index = text.indexOf(query, start_pos);
|
|
68
|
-
if (index === -1)
|
|
69
|
-
break;
|
|
70
|
-
indices.push(index);
|
|
71
|
-
start_pos = index + query.length;
|
|
72
|
-
}
|
|
73
|
-
// create range object for each str found in the text node
|
|
74
|
-
return indices.map((index) => {
|
|
75
|
-
const range = new Range();
|
|
76
|
-
range.setStart(el, index);
|
|
77
|
-
range.setEnd(el, index + query.length);
|
|
78
|
-
return range;
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
// create Highlight object from ranges and add to registry
|
|
82
|
-
CSS.highlights.set(`sms-search-matches`, new Highlight(...ranges.flat()));
|
|
58
|
+
return search_idx === search.length;
|
|
83
59
|
}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"homepage": "https://janosh.github.io/svelte-multiselect",
|
|
6
6
|
"repository": "https://github.com/janosh/svelte-multiselect",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "11.2.
|
|
8
|
+
"version": "11.2.4",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "./dist/index.js",
|
|
11
11
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
@@ -13,30 +13,30 @@
|
|
|
13
13
|
"svelte": "^5.35.6"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@playwright/test": "^1.
|
|
17
|
-
"@stylistic/eslint-plugin": "^5.
|
|
18
|
-
"@sveltejs/adapter-static": "^3.0.
|
|
19
|
-
"@sveltejs/kit": "^2.
|
|
20
|
-
"@sveltejs/package": "2.4
|
|
21
|
-
"@sveltejs/vite-plugin-svelte": "^6.
|
|
22
|
-
"@types/node": "^24.2
|
|
16
|
+
"@playwright/test": "^1.55.1",
|
|
17
|
+
"@stylistic/eslint-plugin": "^5.4.0",
|
|
18
|
+
"@sveltejs/adapter-static": "^3.0.10",
|
|
19
|
+
"@sveltejs/kit": "^2.43.8",
|
|
20
|
+
"@sveltejs/package": "2.5.4",
|
|
21
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
22
|
+
"@types/node": "^24.6.2",
|
|
23
23
|
"@vitest/coverage-v8": "^3.2.4",
|
|
24
|
-
"eslint": "^9.
|
|
25
|
-
"eslint-plugin-svelte": "^3.
|
|
26
|
-
"happy-dom": "^
|
|
24
|
+
"eslint": "^9.37.0",
|
|
25
|
+
"eslint-plugin-svelte": "^3.12.4",
|
|
26
|
+
"happy-dom": "^19.0.2",
|
|
27
27
|
"hastscript": "^9.0.1",
|
|
28
28
|
"mdsvex": "^0.12.6",
|
|
29
29
|
"mdsvexamples": "^0.5.0",
|
|
30
30
|
"rehype-autolink-headings": "^7.1.0",
|
|
31
31
|
"rehype-slug": "^6.0.0",
|
|
32
|
-
"svelte": "^5.
|
|
33
|
-
"svelte-check": "^4.3.
|
|
32
|
+
"svelte": "^5.39.8",
|
|
33
|
+
"svelte-check": "^4.3.2",
|
|
34
34
|
"svelte-preprocess": "^6.0.3",
|
|
35
35
|
"svelte-toc": "^0.6.2",
|
|
36
|
-
"svelte2tsx": "^0.7.
|
|
37
|
-
"typescript": "5.9.
|
|
38
|
-
"typescript-eslint": "^8.
|
|
39
|
-
"vite": "^7.1.
|
|
36
|
+
"svelte2tsx": "^0.7.44",
|
|
37
|
+
"typescript": "5.9.3",
|
|
38
|
+
"typescript-eslint": "^8.45.0",
|
|
39
|
+
"vite": "^7.1.9",
|
|
40
40
|
"vitest": "^3.2.4"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
package/readme.md
CHANGED
|
@@ -643,10 +643,10 @@ For example, here's how you might annoy your users with an alert every time one
|
|
|
643
643
|
|
|
644
644
|
```svelte
|
|
645
645
|
<MultiSelect
|
|
646
|
-
onchange={(
|
|
647
|
-
if (
|
|
648
|
-
if (
|
|
649
|
-
if (
|
|
646
|
+
onchange={(event) => {
|
|
647
|
+
if (event.detail.type === 'add') alert(`You added ${event.detail.option}`)
|
|
648
|
+
if (event.detail.type === 'remove') alert(`You removed ${event.detail.option}`)
|
|
649
|
+
if (event.detail.type === 'removeAll') alert(`You removed ${event.detail.options}`)
|
|
650
650
|
}}
|
|
651
651
|
/>
|
|
652
652
|
```
|
package/dist/RadioButtons.svelte
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
<script lang="ts">// get the label key from an option object or the option itself if it's a string or number
|
|
2
|
-
const get_label = (op) => {
|
|
3
|
-
if (op instanceof Object) {
|
|
4
|
-
if (op.label === undefined) {
|
|
5
|
-
console.error(`RadioButton option ${JSON.stringify(op)} is an object but has no label key`);
|
|
6
|
-
}
|
|
7
|
-
return op.label;
|
|
8
|
-
}
|
|
9
|
-
return op;
|
|
10
|
-
};
|
|
11
|
-
let { options, selected = $bindable(), id = null, name = null, disabled = false, required = false, aria_label = null, onclick, onchange, oninput, option_snippet, children, ...rest } = $props();
|
|
12
|
-
export {};
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<div {id} {...rest}>
|
|
16
|
-
{#each options as option (JSON.stringify(option))}
|
|
17
|
-
{@const label = get_label(option)}
|
|
18
|
-
{@const active = selected && get_label(option) === get_label(selected)}
|
|
19
|
-
<label class:active aria-label={aria_label}>
|
|
20
|
-
<input
|
|
21
|
-
type="radio"
|
|
22
|
-
value={option}
|
|
23
|
-
{name}
|
|
24
|
-
{disabled}
|
|
25
|
-
{required}
|
|
26
|
-
bind:group={selected}
|
|
27
|
-
{onchange}
|
|
28
|
-
{oninput}
|
|
29
|
-
{onclick}
|
|
30
|
-
/>
|
|
31
|
-
{#if option_snippet}
|
|
32
|
-
{@render option_snippet({ option, selected, active })}
|
|
33
|
-
{:else if children}
|
|
34
|
-
{@render children({ option, selected, active })}
|
|
35
|
-
{:else}<span>{label}</span>{/if}
|
|
36
|
-
</label>
|
|
37
|
-
{/each}
|
|
38
|
-
</div>
|
|
39
|
-
|
|
40
|
-
<style>
|
|
41
|
-
div {
|
|
42
|
-
max-width: max-content;
|
|
43
|
-
overflow: hidden;
|
|
44
|
-
height: fit-content;
|
|
45
|
-
display: var(--radio-btn-display, inline-flex);
|
|
46
|
-
border-radius: var(--radio-btn-border-radius, 0.5em);
|
|
47
|
-
}
|
|
48
|
-
input {
|
|
49
|
-
display: none;
|
|
50
|
-
}
|
|
51
|
-
span {
|
|
52
|
-
cursor: pointer;
|
|
53
|
-
display: inline-block;
|
|
54
|
-
color: var(--radio-btn-color, white);
|
|
55
|
-
padding: var(--radio-btn-padding, 2pt 5pt);
|
|
56
|
-
background: var(--radio-btn-bg, black);
|
|
57
|
-
transition: var(--radio-btn-transition, background 0.3s, transform 0.3s);
|
|
58
|
-
}
|
|
59
|
-
label:not(.active) span:hover {
|
|
60
|
-
background: var(--radio-btn-hover-bg, cornflowerblue);
|
|
61
|
-
color: var(--radio-btn-hover-color, white);
|
|
62
|
-
}
|
|
63
|
-
label.active span {
|
|
64
|
-
box-shadow: var(--radio-btn-checked-shadow, inset 0 0 1em -3pt black);
|
|
65
|
-
background: var(--radio-btn-checked-bg, darkcyan);
|
|
66
|
-
}
|
|
67
|
-
</style>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte';
|
|
2
|
-
type GenericOption = string | number | {
|
|
3
|
-
value: unknown;
|
|
4
|
-
label: string | number;
|
|
5
|
-
};
|
|
6
|
-
declare function $$render<Option extends GenericOption>(): {
|
|
7
|
-
props: {
|
|
8
|
-
[key: string]: unknown;
|
|
9
|
-
options: Option[];
|
|
10
|
-
selected?: string | number | null;
|
|
11
|
-
id?: string | null;
|
|
12
|
-
name?: string | null;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
required?: boolean;
|
|
15
|
-
aria_label?: string | null;
|
|
16
|
-
onclick?: (event: MouseEvent) => void;
|
|
17
|
-
onchange?: (event: Event) => void;
|
|
18
|
-
oninput?: (event: Event) => void;
|
|
19
|
-
option_snippet?: Snippet<[{
|
|
20
|
-
option: Option;
|
|
21
|
-
selected: boolean;
|
|
22
|
-
active: boolean;
|
|
23
|
-
}]>;
|
|
24
|
-
children?: Snippet<[{
|
|
25
|
-
option: Option;
|
|
26
|
-
selected: boolean;
|
|
27
|
-
active: boolean;
|
|
28
|
-
}]>;
|
|
29
|
-
};
|
|
30
|
-
exports: {};
|
|
31
|
-
bindings: "selected";
|
|
32
|
-
slots: {};
|
|
33
|
-
events: {};
|
|
34
|
-
};
|
|
35
|
-
declare class __sveltets_Render<Option extends GenericOption> {
|
|
36
|
-
props(): ReturnType<typeof $$render<Option>>['props'];
|
|
37
|
-
events(): ReturnType<typeof $$render<Option>>['events'];
|
|
38
|
-
slots(): ReturnType<typeof $$render<Option>>['slots'];
|
|
39
|
-
bindings(): "selected";
|
|
40
|
-
exports(): {};
|
|
41
|
-
}
|
|
42
|
-
interface $$IsomorphicComponent {
|
|
43
|
-
new <Option extends GenericOption>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Option>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Option>['props']>, ReturnType<__sveltets_Render<Option>['events']>, ReturnType<__sveltets_Render<Option>['slots']>> & {
|
|
44
|
-
$$bindings?: ReturnType<__sveltets_Render<Option>['bindings']>;
|
|
45
|
-
} & ReturnType<__sveltets_Render<Option>['exports']>;
|
|
46
|
-
<Option extends GenericOption>(internal: unknown, props: ReturnType<__sveltets_Render<Option>['props']> & {}): ReturnType<__sveltets_Render<Option>['exports']>;
|
|
47
|
-
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
48
|
-
}
|
|
49
|
-
declare const RadioButtons: $$IsomorphicComponent;
|
|
50
|
-
type RadioButtons<Option extends GenericOption> = InstanceType<typeof RadioButtons<Option>>;
|
|
51
|
-
export default RadioButtons;
|