refac
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
id: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
is_active?: boolean;
|
||||
};
|
||||
|
||||
export let skills: Skill[] = [];
|
||||
@@ -15,11 +16,13 @@
|
||||
|
||||
const i18n = getContext('i18n') as any;
|
||||
|
||||
$: selectedSkills = skills.filter((skill) => selectedSkillIds.includes(skill.id));
|
||||
$: availableSkills = skills.filter((skill) => !selectedSkillIds.includes(skill.id));
|
||||
$: activeSkills = skills.filter((skill) => skill.is_active !== false);
|
||||
$: selectedSkills = activeSkills.filter((skill) => selectedSkillIds.includes(skill.id));
|
||||
|
||||
const selectSkill = (skill: Skill) => {
|
||||
selectedSkillIds = [...selectedSkillIds, skill.id];
|
||||
const toggleSkill = (skill: Skill) => {
|
||||
selectedSkillIds = selectedSkillIds.includes(skill.id)
|
||||
? selectedSkillIds.filter((id) => id !== skill.id)
|
||||
: [...selectedSkillIds, skill.id];
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -29,14 +32,20 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mb-1">
|
||||
{#if skills.length > 0}
|
||||
{#if activeSkills.length > 0}
|
||||
<TypeaheadSelector
|
||||
id="model-skills-selector"
|
||||
items={availableSkills}
|
||||
items={activeSkills}
|
||||
selectedIds={selectedSkillIds}
|
||||
className="w-48 max-w-full"
|
||||
placeholder={$i18n.t('Search skills')}
|
||||
on:select={(e) => {
|
||||
selectSkill(e.detail);
|
||||
toggleSkill(e.detail);
|
||||
}}
|
||||
on:enableall={(e) => {
|
||||
selectedSkillIds = [
|
||||
...new Set([...selectedSkillIds, ...e.detail.map((skill) => skill.id)])
|
||||
];
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -61,6 +70,18 @@
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if selectedSkills.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="py-0.5 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
on:click={() => {
|
||||
selectedSkillIds = [];
|
||||
}}
|
||||
>
|
||||
Disable all
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
export let voices: Voice[] = [];
|
||||
export let placeholder = '';
|
||||
export let className = 'w-full';
|
||||
export let selectedIds: string[] | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: Voice }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: Voice;
|
||||
enableall: Voice[];
|
||||
}>();
|
||||
const listboxId = `${id}-options`;
|
||||
|
||||
let inputElement: HTMLInputElement | null = null;
|
||||
@@ -24,18 +28,17 @@
|
||||
let suggestionsOpen = false;
|
||||
|
||||
$: query = value.trim().toLowerCase();
|
||||
$: filteredVoices = (voices ?? [])
|
||||
.filter((voice) => {
|
||||
const id = voice.id.toLowerCase();
|
||||
const name = (voice.name ?? '').toLowerCase();
|
||||
const description = (voice.description ?? voice.meta?.description ?? '').toLowerCase();
|
||||
$: matchedVoices = (voices ?? []).filter((voice) => {
|
||||
const id = voice.id.toLowerCase();
|
||||
const name = (voice.name ?? '').toLowerCase();
|
||||
const description = (voice.description ?? voice.meta?.description ?? '').toLowerCase();
|
||||
|
||||
return (
|
||||
query === '' || id.includes(query) || name.includes(query) || description.includes(query)
|
||||
);
|
||||
})
|
||||
.slice(0, 8);
|
||||
$: if (suggestionsOpen && filteredVoices.length > 0) {
|
||||
return (
|
||||
query === '' || id.includes(query) || name.includes(query) || description.includes(query)
|
||||
);
|
||||
});
|
||||
$: optionVoices = selectedIds === null ? matchedVoices.slice(0, 8) : matchedVoices;
|
||||
$: if (suggestionsOpen && optionVoices.length > 0) {
|
||||
tick().then(positionPopup);
|
||||
}
|
||||
|
||||
@@ -60,11 +63,18 @@
|
||||
};
|
||||
|
||||
const selectVoice = (voice: Voice) => {
|
||||
value = voice.id;
|
||||
if (selectedIds === null) {
|
||||
value = voice.id;
|
||||
}
|
||||
suggestionsOpen = false;
|
||||
dispatch('select', voice);
|
||||
};
|
||||
|
||||
const enableVoices = () => {
|
||||
suggestionsOpen = false;
|
||||
dispatch('enableall', matchedVoices);
|
||||
};
|
||||
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
if (!suggestionsOpen) return;
|
||||
|
||||
@@ -93,7 +103,7 @@
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-controls={listboxId}
|
||||
aria-expanded={suggestionsOpen && filteredVoices.length > 0}
|
||||
aria-expanded={suggestionsOpen && optionVoices.length > 0}
|
||||
autocomplete="off"
|
||||
on:focus={() => {
|
||||
suggestionsOpen = true;
|
||||
@@ -117,7 +127,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if suggestionsOpen && filteredVoices.length > 0}
|
||||
{#if suggestionsOpen && optionVoices.length > 0}
|
||||
<div
|
||||
use:portal
|
||||
bind:this={popupElement}
|
||||
@@ -126,12 +136,29 @@
|
||||
role="listbox"
|
||||
style="z-index: 9999; top: 0; left: 0;"
|
||||
>
|
||||
{#each filteredVoices as voice (voice.id)}
|
||||
{#if selectedIds !== null}
|
||||
{#if matchedVoices.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center justify-between gap-3 rounded-xl px-2 py-[5px] text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800"
|
||||
role="option"
|
||||
on:mousedown={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
on:click={() => {
|
||||
enableVoices();
|
||||
}}
|
||||
>
|
||||
<span class="truncate">Enable all ({matchedVoices.length})</span>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{#each optionVoices as voice (voice.id)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center justify-between gap-3 rounded-xl px-2 py-[5px] text-left text-xs text-gray-700 transition-colors hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-800"
|
||||
role="option"
|
||||
aria-selected={value === voice.id}
|
||||
aria-selected={selectedIds?.includes(voice.id) ?? value === voice.id}
|
||||
on:mousedown={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
const i18n = getContext('i18n') as any;
|
||||
|
||||
$: selectedTools = tools.filter((tool) => selectedToolIds.includes(tool.id));
|
||||
$: availableTools = tools.filter((tool) => !selectedToolIds.includes(tool.id));
|
||||
|
||||
const selectTool = (tool: Tool) => {
|
||||
selectedToolIds = [...selectedToolIds, tool.id];
|
||||
const toggleTool = (tool: Tool) => {
|
||||
selectedToolIds = selectedToolIds.includes(tool.id)
|
||||
? selectedToolIds.filter((id) => id !== tool.id)
|
||||
: [...selectedToolIds, tool.id];
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -34,11 +35,15 @@
|
||||
{#if tools.length > 0}
|
||||
<TypeaheadSelector
|
||||
id="model-tools-selector"
|
||||
items={availableTools}
|
||||
items={tools}
|
||||
selectedIds={selectedToolIds}
|
||||
className="w-48 max-w-full"
|
||||
placeholder={$i18n.t('Search tools')}
|
||||
on:select={(e) => {
|
||||
selectTool(e.detail);
|
||||
toggleTool(e.detail);
|
||||
}}
|
||||
on:enableall={(e) => {
|
||||
selectedToolIds = [...new Set([...selectedToolIds, ...e.detail.map((tool) => tool.id)])];
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -63,11 +68,25 @@
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if selectedTools.length > 0}
|
||||
<button
|
||||
type="button"
|
||||
class="py-0.5 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
on:click={() => {
|
||||
selectedToolIds = [];
|
||||
}}
|
||||
>
|
||||
Disable all
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class=" text-xs dark:text-gray-700">
|
||||
{$i18n.t('To select toolkits here, add them to the "Tools" workspace first.')}
|
||||
{$i18n.t(
|
||||
'To select toolkits here, add them to the "Tools" workspace or enable a tool server first.'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
export let placeholder = '';
|
||||
export let id = 'typeahead-selector';
|
||||
export let className = 'w-full';
|
||||
export let selectedIds: string[] | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: Item }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: Item;
|
||||
enableall: Item[];
|
||||
}>();
|
||||
let value = '';
|
||||
</script>
|
||||
|
||||
@@ -26,10 +30,12 @@
|
||||
voices={items}
|
||||
{placeholder}
|
||||
{className}
|
||||
{selectedIds}
|
||||
bind:value
|
||||
on:select={(e) => {
|
||||
dispatch('select', e.detail);
|
||||
value = '';
|
||||
if (selectedIds === null) value = '';
|
||||
}}
|
||||
on:enableall={(e) => dispatch('enableall', e.detail)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user