@juspay/svelte-ui-components 4.34.0 → 4.35.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/dist/ChatComposer/ChatComposer.svelte +212 -118
- package/dist/ChatComposer/properties.d.ts +4 -0
- package/dist/CommandMenu/CommandMenu.svelte +41 -10
- package/dist/CommandMenu/CommandMenu.svelte.d.ts +1 -1
- package/dist/CommandMenu/properties.d.ts +6 -0
- package/dist/HITL/HITL.svelte +68 -26
- package/dist/HITL/properties.d.ts +9 -1
- package/dist/Select/Select.svelte +5 -0
- package/dist/Select/properties.d.ts +2 -0
- package/dist/_interaction/dismissal.js +1 -1
- package/dist-wc/index.js +2621 -2455
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
let {
|
|
22
22
|
value = $bindable(''),
|
|
23
|
+
layout = 'row',
|
|
23
24
|
placeholder = '',
|
|
24
25
|
disabled = false,
|
|
25
26
|
textDisabled = null,
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
attachIcon,
|
|
58
59
|
actionIcon,
|
|
59
60
|
actionLabel = 'Voice conversation',
|
|
61
|
+
actionText,
|
|
60
62
|
leading,
|
|
61
63
|
statusText,
|
|
62
64
|
statusTestId,
|
|
@@ -346,6 +348,140 @@
|
|
|
346
348
|
};
|
|
347
349
|
</script>
|
|
348
350
|
|
|
351
|
+
{#snippet leadingControls()}
|
|
352
|
+
{#if showAttach}
|
|
353
|
+
<div class="control attach">
|
|
354
|
+
<Button
|
|
355
|
+
variant="ghost"
|
|
356
|
+
onclick={() => {
|
|
357
|
+
if (typeof onattachclick === 'function') {
|
|
358
|
+
onattachclick();
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
fileInput?.click();
|
|
362
|
+
}}
|
|
363
|
+
{disabled}
|
|
364
|
+
ariaLabel={attachLabel}
|
|
365
|
+
testId={attachTestId}
|
|
366
|
+
>
|
|
367
|
+
{#if typeof attachIcon === 'function'}
|
|
368
|
+
{@render attachIcon()}
|
|
369
|
+
{:else}
|
|
370
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
371
|
+
{@html attachSvg}
|
|
372
|
+
{/if}
|
|
373
|
+
</Button>
|
|
374
|
+
<input
|
|
375
|
+
bind:this={fileInput}
|
|
376
|
+
type="file"
|
|
377
|
+
{accept}
|
|
378
|
+
{multiple}
|
|
379
|
+
class="file-input"
|
|
380
|
+
onchange={handleFiles}
|
|
381
|
+
hidden
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
{/if}
|
|
385
|
+
|
|
386
|
+
{#if typeof leading === 'function'}
|
|
387
|
+
<div class="leading">{@render leading()}</div>
|
|
388
|
+
{/if}
|
|
389
|
+
{/snippet}
|
|
390
|
+
|
|
391
|
+
{#snippet textInput()}
|
|
392
|
+
<textarea
|
|
393
|
+
class="input"
|
|
394
|
+
data-pw={inputTestId ?? null}
|
|
395
|
+
bind:value
|
|
396
|
+
{placeholder}
|
|
397
|
+
disabled={resolvedTextDisabled}
|
|
398
|
+
rows="1"
|
|
399
|
+
aria-label={inputAriaLabel ?? (placeholder.length > 0 ? placeholder : 'Message')}
|
|
400
|
+
role={slashCommands !== null ? 'combobox' : null}
|
|
401
|
+
aria-expanded={slashCommands !== null ? slashOpen : null}
|
|
402
|
+
aria-controls={slashOpen ? slashListboxId : null}
|
|
403
|
+
aria-autocomplete={slashCommands !== null ? 'list' : null}
|
|
404
|
+
aria-activedescendant={slashActiveId}
|
|
405
|
+
maxlength={maxLength > 0 ? maxLength : null}
|
|
406
|
+
oninput={handleInput}
|
|
407
|
+
onkeydown={handleKeydown}
|
|
408
|
+
onpaste={(event) => onpaste?.(event)}
|
|
409
|
+
use:autoGrow={value}
|
|
410
|
+
></textarea>
|
|
411
|
+
{/snippet}
|
|
412
|
+
|
|
413
|
+
{#snippet trailingControls()}
|
|
414
|
+
{#if showVoice}
|
|
415
|
+
<div
|
|
416
|
+
class="control voice"
|
|
417
|
+
class:recording={dictationState === 'recording'}
|
|
418
|
+
class:busy={dictationState === 'busy'}
|
|
419
|
+
>
|
|
420
|
+
<Button
|
|
421
|
+
variant="ghost"
|
|
422
|
+
onclick={() => onvoice?.()}
|
|
423
|
+
disabled={resolvedVoiceDisabled}
|
|
424
|
+
ariaLabel={voiceLabel}
|
|
425
|
+
testId={voiceTestId}
|
|
426
|
+
>
|
|
427
|
+
{#if typeof voiceIcon === 'function'}
|
|
428
|
+
{@render voiceIcon()}
|
|
429
|
+
{:else}
|
|
430
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
431
|
+
{@html micSvg}
|
|
432
|
+
{/if}
|
|
433
|
+
</Button>
|
|
434
|
+
</div>
|
|
435
|
+
{/if}
|
|
436
|
+
|
|
437
|
+
{#if streaming}
|
|
438
|
+
<div class="control send stop" data-pw={sendSlotTestId ?? null}>
|
|
439
|
+
<Button onclick={() => onstop?.()} ariaLabel={stopLabel} testId={stopTestId}>
|
|
440
|
+
{#if typeof stopIcon === 'function'}
|
|
441
|
+
{@render stopIcon()}
|
|
442
|
+
{:else}
|
|
443
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
444
|
+
{@html stopSvg}
|
|
445
|
+
{/if}
|
|
446
|
+
</Button>
|
|
447
|
+
</div>
|
|
448
|
+
{:else if typeof onaction === 'function' && !canSend && dictationState === 'idle'}
|
|
449
|
+
<div
|
|
450
|
+
class="control send action"
|
|
451
|
+
class:action-with-text={typeof actionText === 'string' && actionText.length > 0}
|
|
452
|
+
data-pw={sendSlotTestId ?? null}
|
|
453
|
+
>
|
|
454
|
+
<Button
|
|
455
|
+
onclick={() => onaction()}
|
|
456
|
+
disabled={resolvedSendDisabled}
|
|
457
|
+
ariaLabel={actionLabel}
|
|
458
|
+
testId={actionTestId}
|
|
459
|
+
>
|
|
460
|
+
{#if typeof actionIcon === 'function'}
|
|
461
|
+
{@render actionIcon()}
|
|
462
|
+
{:else}
|
|
463
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
464
|
+
{@html micSvg}
|
|
465
|
+
{/if}
|
|
466
|
+
{#if typeof actionText === 'string' && actionText.length > 0}
|
|
467
|
+
<span>{actionText}</span>
|
|
468
|
+
{/if}
|
|
469
|
+
</Button>
|
|
470
|
+
</div>
|
|
471
|
+
{:else}
|
|
472
|
+
<div class="control send" data-pw={sendSlotTestId ?? null}>
|
|
473
|
+
<Button onclick={submit} disabled={!canSend} ariaLabel={sendLabel} testId={sendTestId}>
|
|
474
|
+
{#if typeof sendIcon === 'function'}
|
|
475
|
+
{@render sendIcon()}
|
|
476
|
+
{:else}
|
|
477
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
478
|
+
{@html sendSvg}
|
|
479
|
+
{/if}
|
|
480
|
+
</Button>
|
|
481
|
+
</div>
|
|
482
|
+
{/if}
|
|
483
|
+
{/snippet}
|
|
484
|
+
|
|
349
485
|
<div
|
|
350
486
|
class="chat-composer {classes ?? ''}"
|
|
351
487
|
class:disabled
|
|
@@ -441,126 +577,18 @@
|
|
|
441
577
|
</div>
|
|
442
578
|
{/if}
|
|
443
579
|
|
|
444
|
-
<div class="input-row">
|
|
445
|
-
{#if
|
|
446
|
-
<div class="
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
onattachclick();
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
fileInput?.click();
|
|
455
|
-
}}
|
|
456
|
-
{disabled}
|
|
457
|
-
ariaLabel={attachLabel}
|
|
458
|
-
testId={attachTestId}
|
|
459
|
-
>
|
|
460
|
-
{#if typeof attachIcon === 'function'}
|
|
461
|
-
{@render attachIcon()}
|
|
462
|
-
{:else}
|
|
463
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
464
|
-
{@html attachSvg}
|
|
465
|
-
{/if}
|
|
466
|
-
</Button>
|
|
467
|
-
<input
|
|
468
|
-
bind:this={fileInput}
|
|
469
|
-
type="file"
|
|
470
|
-
{accept}
|
|
471
|
-
{multiple}
|
|
472
|
-
class="file-input"
|
|
473
|
-
onchange={handleFiles}
|
|
474
|
-
hidden
|
|
475
|
-
/>
|
|
476
|
-
</div>
|
|
477
|
-
{/if}
|
|
478
|
-
|
|
479
|
-
{#if typeof leading === 'function'}
|
|
480
|
-
<div class="leading">{@render leading()}</div>
|
|
481
|
-
{/if}
|
|
482
|
-
|
|
483
|
-
<textarea
|
|
484
|
-
class="input"
|
|
485
|
-
data-pw={inputTestId ?? null}
|
|
486
|
-
bind:value
|
|
487
|
-
{placeholder}
|
|
488
|
-
disabled={resolvedTextDisabled}
|
|
489
|
-
rows="1"
|
|
490
|
-
aria-label={inputAriaLabel ?? (placeholder.length > 0 ? placeholder : 'Message')}
|
|
491
|
-
role={slashCommands !== null ? 'combobox' : null}
|
|
492
|
-
aria-expanded={slashCommands !== null ? slashOpen : null}
|
|
493
|
-
aria-controls={slashOpen ? slashListboxId : null}
|
|
494
|
-
aria-autocomplete={slashCommands !== null ? 'list' : null}
|
|
495
|
-
aria-activedescendant={slashActiveId}
|
|
496
|
-
maxlength={maxLength > 0 ? maxLength : null}
|
|
497
|
-
oninput={handleInput}
|
|
498
|
-
onkeydown={handleKeydown}
|
|
499
|
-
onpaste={(event) => onpaste?.(event)}
|
|
500
|
-
use:autoGrow={value}
|
|
501
|
-
></textarea>
|
|
502
|
-
|
|
503
|
-
{#if showVoice}
|
|
504
|
-
<div
|
|
505
|
-
class="control voice"
|
|
506
|
-
class:recording={dictationState === 'recording'}
|
|
507
|
-
class:busy={dictationState === 'busy'}
|
|
508
|
-
>
|
|
509
|
-
<Button
|
|
510
|
-
variant="ghost"
|
|
511
|
-
onclick={() => onvoice?.()}
|
|
512
|
-
disabled={resolvedVoiceDisabled}
|
|
513
|
-
ariaLabel={voiceLabel}
|
|
514
|
-
testId={voiceTestId}
|
|
515
|
-
>
|
|
516
|
-
{#if typeof voiceIcon === 'function'}
|
|
517
|
-
{@render voiceIcon()}
|
|
518
|
-
{:else}
|
|
519
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
520
|
-
{@html micSvg}
|
|
521
|
-
{/if}
|
|
522
|
-
</Button>
|
|
523
|
-
</div>
|
|
524
|
-
{/if}
|
|
525
|
-
|
|
526
|
-
{#if streaming}
|
|
527
|
-
<div class="control send stop" data-pw={sendSlotTestId ?? null}>
|
|
528
|
-
<Button onclick={() => onstop?.()} ariaLabel={stopLabel} testId={stopTestId}>
|
|
529
|
-
{#if typeof stopIcon === 'function'}
|
|
530
|
-
{@render stopIcon()}
|
|
531
|
-
{:else}
|
|
532
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
533
|
-
{@html stopSvg}
|
|
534
|
-
{/if}
|
|
535
|
-
</Button>
|
|
536
|
-
</div>
|
|
537
|
-
{:else if typeof onaction === 'function' && !canSend && dictationState === 'idle'}
|
|
538
|
-
<div class="control send action" data-pw={sendSlotTestId ?? null}>
|
|
539
|
-
<Button
|
|
540
|
-
onclick={() => onaction()}
|
|
541
|
-
disabled={resolvedSendDisabled}
|
|
542
|
-
ariaLabel={actionLabel}
|
|
543
|
-
testId={actionTestId}
|
|
544
|
-
>
|
|
545
|
-
{#if typeof actionIcon === 'function'}
|
|
546
|
-
{@render actionIcon()}
|
|
547
|
-
{:else}
|
|
548
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
549
|
-
{@html micSvg}
|
|
550
|
-
{/if}
|
|
551
|
-
</Button>
|
|
580
|
+
<div class="input-row" class:stacked={layout === 'stacked'}>
|
|
581
|
+
{#if layout === 'stacked'}
|
|
582
|
+
<div class="text-row">{@render textInput()}</div>
|
|
583
|
+
<div class="control-row">
|
|
584
|
+
{@render leadingControls()}
|
|
585
|
+
<div class="control-spacer" aria-hidden="true"></div>
|
|
586
|
+
{@render trailingControls()}
|
|
552
587
|
</div>
|
|
553
588
|
{:else}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
{@render sendIcon()}
|
|
558
|
-
{:else}
|
|
559
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
560
|
-
{@html sendSvg}
|
|
561
|
-
{/if}
|
|
562
|
-
</Button>
|
|
563
|
-
</div>
|
|
589
|
+
{@render leadingControls()}
|
|
590
|
+
{@render textInput()}
|
|
591
|
+
{@render trailingControls()}
|
|
564
592
|
{/if}
|
|
565
593
|
</div>
|
|
566
594
|
</div>
|
|
@@ -609,6 +637,32 @@
|
|
|
609
637
|
gap: var(--chat-composer-gap, 8px);
|
|
610
638
|
}
|
|
611
639
|
|
|
640
|
+
.input-row.stacked {
|
|
641
|
+
flex-direction: column;
|
|
642
|
+
align-items: stretch;
|
|
643
|
+
gap: var(--chat-composer-stack-gap, 8px);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.text-row {
|
|
647
|
+
display: flex;
|
|
648
|
+
min-width: 0;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.control-row {
|
|
652
|
+
display: flex;
|
|
653
|
+
align-items: flex-end;
|
|
654
|
+
gap: var(--chat-composer-gap, 8px);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.control-spacer {
|
|
658
|
+
flex: 1;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.stacked .input {
|
|
662
|
+
min-width: 0;
|
|
663
|
+
width: 100%;
|
|
664
|
+
}
|
|
665
|
+
|
|
612
666
|
.leading {
|
|
613
667
|
display: flex;
|
|
614
668
|
align-items: center;
|
|
@@ -776,6 +830,46 @@
|
|
|
776
830
|
color: var(--chat-composer-slash-badge-color, #2563eb);
|
|
777
831
|
}
|
|
778
832
|
|
|
833
|
+
.send.action {
|
|
834
|
+
--button-width: var(--chat-composer-idle-action-width, var(--chat-composer-send-size, 40px));
|
|
835
|
+
--button-padding: var(
|
|
836
|
+
--chat-composer-idle-action-padding,
|
|
837
|
+
var(--chat-composer-send-padding, 8px)
|
|
838
|
+
);
|
|
839
|
+
--button-border-radius: var(
|
|
840
|
+
--chat-composer-idle-action-border-radius,
|
|
841
|
+
var(--chat-composer-send-border-radius, 50%)
|
|
842
|
+
);
|
|
843
|
+
--button-color: var(
|
|
844
|
+
--chat-composer-idle-action-background-color,
|
|
845
|
+
var(--chat-composer-send-background-color, #18181b)
|
|
846
|
+
);
|
|
847
|
+
--button-text-color: var(
|
|
848
|
+
--chat-composer-idle-action-color,
|
|
849
|
+
var(--chat-composer-send-color, #ffffff)
|
|
850
|
+
);
|
|
851
|
+
--button-hover-color: var(
|
|
852
|
+
--chat-composer-idle-action-hover-background-color,
|
|
853
|
+
var(--chat-composer-send-hover-background-color, #27272a)
|
|
854
|
+
);
|
|
855
|
+
--button-hover-text-color: var(
|
|
856
|
+
--chat-composer-idle-action-hover-color,
|
|
857
|
+
var(--button-text-color)
|
|
858
|
+
);
|
|
859
|
+
--button-content-gap: var(--chat-composer-idle-action-gap, 0px);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.send.action.action-with-text {
|
|
863
|
+
--button-width: var(--chat-composer-idle-action-width, max-content);
|
|
864
|
+
--button-padding: var(--chat-composer-idle-action-padding, 8px 12px);
|
|
865
|
+
--button-content-gap: var(--chat-composer-idle-action-gap, 6px);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.action-with-text :global(svg) {
|
|
869
|
+
width: var(--chat-composer-idle-action-icon-size, 20px);
|
|
870
|
+
height: var(--chat-composer-idle-action-icon-size, 20px);
|
|
871
|
+
}
|
|
872
|
+
|
|
779
873
|
/* Visually hidden but still read by assistive technology -- same pattern
|
|
780
874
|
Table's caption and Label's required marker use. */
|
|
781
875
|
.sr-only {
|
|
@@ -3,6 +3,8 @@ import type { AttachmentChipFile, AttachmentChipImage, AttachmentChipVideo } fro
|
|
|
3
3
|
export type ChatComposerProperties = OptionalChatComposerProperties & ChatComposerEventProperties;
|
|
4
4
|
export type OptionalChatComposerProperties = {
|
|
5
5
|
value?: string;
|
|
6
|
+
/** Text above a separate control row when stacked; row preserves the default. */
|
|
7
|
+
layout?: 'row' | 'stacked';
|
|
6
8
|
placeholder?: string;
|
|
7
9
|
disabled?: boolean;
|
|
8
10
|
/**
|
|
@@ -80,6 +82,8 @@ export type OptionalChatComposerProperties = {
|
|
|
80
82
|
/** Glyph for the idle action button (see `onaction`). */
|
|
81
83
|
actionIcon?: Snippet;
|
|
82
84
|
actionLabel?: string;
|
|
85
|
+
/** Optional visible text beside the idle action icon; omitted keeps icon-only markup. */
|
|
86
|
+
actionText?: string;
|
|
83
87
|
leading?: Snippet;
|
|
84
88
|
/**
|
|
85
89
|
* Opt-in sr-only status region (`role="status" aria-live="polite"`) for
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
let {
|
|
17
17
|
items,
|
|
18
18
|
open = $bindable(false),
|
|
19
|
+
query = $bindable(''),
|
|
20
|
+
shortcutEnabled = true,
|
|
19
21
|
placeholder = 'Search commands...',
|
|
20
22
|
emptyText = 'No results found.',
|
|
21
23
|
testId,
|
|
@@ -28,7 +30,8 @@
|
|
|
28
30
|
onclose,
|
|
29
31
|
classes,
|
|
30
32
|
ariaLabel = 'Command menu',
|
|
31
|
-
clearStateOnClose = false
|
|
33
|
+
clearStateOnClose = false,
|
|
34
|
+
onquerychange
|
|
32
35
|
}: CommandMenuProperties = $props();
|
|
33
36
|
|
|
34
37
|
// Stable per-instance ids keep ARIA references distinct across menus.
|
|
@@ -56,8 +59,6 @@
|
|
|
56
59
|
easingTokens: ['--command-menu-panel-transition-easing', '--ease-smooth-out', '--motion-easing']
|
|
57
60
|
};
|
|
58
61
|
|
|
59
|
-
let query = $state('');
|
|
60
|
-
let activeIndex = $state(0);
|
|
61
62
|
let inputElement: HTMLInputElement | null = $state(null);
|
|
62
63
|
let listElement: HTMLDivElement | null = $state(null);
|
|
63
64
|
let dialogElement: HTMLDivElement | null = $state(null);
|
|
@@ -98,6 +99,10 @@
|
|
|
98
99
|
return result;
|
|
99
100
|
});
|
|
100
101
|
|
|
102
|
+
// A changed result list (including externally changed query/items) resets
|
|
103
|
+
// navigation. Keyboard/pointer navigation can override this until it changes.
|
|
104
|
+
let activeIndex = $derived(flatItems.length > 0 ? 0 : -1);
|
|
105
|
+
|
|
101
106
|
let flatIndexLookup = $derived.by(() => {
|
|
102
107
|
const map = new SvelteMap<string, number>();
|
|
103
108
|
for (let i = 0; i < flatItems.length; i++) {
|
|
@@ -113,7 +118,7 @@
|
|
|
113
118
|
// is keyed by its flat index (see the option's own id below) — null once the
|
|
114
119
|
// list is empty, since there is then no option to describe.
|
|
115
120
|
let activeDescendantId = $derived(
|
|
116
|
-
flatItems.
|
|
121
|
+
flatItems.at(activeIndex) ? `command-menu-option-${uid}-${activeIndex}` : null
|
|
117
122
|
);
|
|
118
123
|
|
|
119
124
|
// Guarded rather than assumed single-fire: out:tokenizedFly keeps the dialog
|
|
@@ -125,7 +130,10 @@
|
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
open = false;
|
|
128
|
-
query
|
|
133
|
+
if (query !== '') {
|
|
134
|
+
query = '';
|
|
135
|
+
onquerychange?.('');
|
|
136
|
+
}
|
|
129
137
|
activeIndex = 0;
|
|
130
138
|
onclose?.();
|
|
131
139
|
}
|
|
@@ -151,7 +159,7 @@
|
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
function handleKeyDown(event: KeyboardEvent) {
|
|
154
|
-
if (!open) {
|
|
162
|
+
if (!open || event.isComposing || event.keyCode === 229) {
|
|
155
163
|
return;
|
|
156
164
|
}
|
|
157
165
|
|
|
@@ -247,10 +255,31 @@
|
|
|
247
255
|
// against other listeners are unchanged; the opt-out is read per event, which also makes
|
|
248
256
|
// toggling enableHotkey after mount take effect immediately.
|
|
249
257
|
function handleGlobalKeyDown(event: KeyboardEvent) {
|
|
250
|
-
if (
|
|
258
|
+
if (
|
|
259
|
+
!shortcutEnabled ||
|
|
260
|
+
!enableHotkey ||
|
|
261
|
+
event.defaultPrevented ||
|
|
262
|
+
event.isComposing ||
|
|
263
|
+
event.keyCode === 229 ||
|
|
264
|
+
event.repeat ||
|
|
265
|
+
event.altKey
|
|
266
|
+
) {
|
|
251
267
|
return;
|
|
252
268
|
}
|
|
253
|
-
if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
|
269
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
|
|
270
|
+
const dialog = event
|
|
271
|
+
.composedPath()
|
|
272
|
+
.find(
|
|
273
|
+
(node) =>
|
|
274
|
+
node instanceof Element &&
|
|
275
|
+
node.matches('[role="dialog"], [role="alertdialog"], [aria-modal="true"]')
|
|
276
|
+
);
|
|
277
|
+
if (
|
|
278
|
+
dialog instanceof Element &&
|
|
279
|
+
(dialogElement === null || !dialog.contains(dialogElement))
|
|
280
|
+
) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
254
283
|
event.preventDefault();
|
|
255
284
|
if (open) {
|
|
256
285
|
close();
|
|
@@ -388,9 +417,11 @@
|
|
|
388
417
|
{/if}
|
|
389
418
|
<input
|
|
390
419
|
bind:this={inputElement}
|
|
391
|
-
|
|
392
|
-
oninput={() => {
|
|
420
|
+
value={query}
|
|
421
|
+
oninput={(event) => {
|
|
422
|
+
query = event.currentTarget.value;
|
|
393
423
|
activeIndex = 0;
|
|
424
|
+
onquerychange?.(query);
|
|
394
425
|
}}
|
|
395
426
|
type="text"
|
|
396
427
|
class="command-menu-input"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { CommandMenuProperties } from './properties';
|
|
2
|
-
declare const CommandMenu: import("svelte").Component<CommandMenuProperties, {}, "open">;
|
|
2
|
+
declare const CommandMenu: import("svelte").Component<CommandMenuProperties, {}, "open" | "query">;
|
|
3
3
|
type CommandMenu = ReturnType<typeof CommandMenu>;
|
|
4
4
|
export default CommandMenu;
|
|
@@ -13,6 +13,10 @@ export type MandatoryCommandMenuProperties = {
|
|
|
13
13
|
};
|
|
14
14
|
export type OptionalCommandMenuProperties = {
|
|
15
15
|
open?: boolean;
|
|
16
|
+
/** Bindable search text. Omit to keep the built-in uncontrolled search. */
|
|
17
|
+
query?: string;
|
|
18
|
+
/** Disable the built-in global Cmd/Ctrl+K listener when the caller owns shortcuts. */
|
|
19
|
+
shortcutEnabled?: boolean;
|
|
16
20
|
placeholder?: string;
|
|
17
21
|
emptyText?: string;
|
|
18
22
|
testId?: string;
|
|
@@ -45,6 +49,8 @@ export type OptionalCommandMenuProperties = {
|
|
|
45
49
|
clearStateOnClose?: boolean;
|
|
46
50
|
};
|
|
47
51
|
export type CommandMenuEventProperties = {
|
|
52
|
+
/** Fires on user input and when closing resets a non-empty query. */
|
|
53
|
+
onquerychange?: (query: string) => void;
|
|
48
54
|
onselect?: (item: CommandItem) => void;
|
|
49
55
|
onclose?: () => void;
|
|
50
56
|
};
|