ComboBox
This commit is contained in:
parent
e7125bc07a
commit
327d3f77b8
|
@ -34,6 +34,7 @@
|
|||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@svelte-put/clickoutside": "^3.0.0",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"brotli-compress": "^1.3.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
|
|
@ -5,6 +5,9 @@ settings:
|
|||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@svelte-put/clickoutside':
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3(@sveltejs/kit@1.20.4)
|
||||
|
@ -400,6 +403,10 @@ packages:
|
|||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
dev: false
|
||||
|
||||
/@svelte-put/clickoutside@3.0.0:
|
||||
resolution: {integrity: sha512-a2aA0+uMo2LGK4bwQE+etra57cJuqQsXRkI0IM+cPzjIowqUg5CMSSeQ6YsXBJUdtzm/s6WQKcmoswK/875wtA==}
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-auto@2.0.0(@sveltejs/kit@1.20.4):
|
||||
resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==}
|
||||
peerDependencies:
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
npm/bootstrap@4.6.1/dist/css/bootstrap-grid.min.css,
|
||||
npm/codemirror@5.65.5/lib/codemirror.min.css,
|
||||
npm/codemirror@5.65.5/addon/scroll/simplescrollbars.css,
|
||||
npm/codemirror@5.65.5/theme/dracula.min.css,
|
||||
npm/microtip@0.2.2/microtip.min.css"
|
||||
npm/codemirror@5.65.5/theme/dracula.min.css"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
|
@ -22,8 +21,6 @@ npm/microtip@0.2.2/microtip.min.css"
|
|||
|
||||
<script src="https://cdn.jsdelivr.net/combine/
|
||||
npm/lzma@2.3.2/src/lzma.min.js,
|
||||
npm/clipboard@2.0.11/dist/clipboard.min.js,
|
||||
npm/micromodal@0.4.10/dist/micromodal.min.js,
|
||||
npm/codemirror@5.65.5,
|
||||
npm/codemirror@5.65.5/addon/mode/loadmode.min.js,
|
||||
npm/codemirror@5.65.5/addon/mode/overlay.min.js,
|
||||
|
|
90
src/components/ComboBox.svelte
Normal file
90
src/components/ComboBox.svelte
Normal file
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts">
|
||||
import { clickoutside } from '@svelte-put/clickoutside'
|
||||
import { onMount, tick } from 'svelte'
|
||||
|
||||
type Item = { text: string }
|
||||
export let value: Item | null = null
|
||||
export let items: Item[]
|
||||
let filteredItems: Item[] = []
|
||||
|
||||
let inputFilter: HTMLInputElement | null = null
|
||||
let filter = ''
|
||||
|
||||
let divContainer: HTMLDivElement | null = null
|
||||
|
||||
let areOptionsVisible = false
|
||||
|
||||
$: {
|
||||
if (filter) {
|
||||
filteredItems = items.filter((item) => item.text.toLowerCase().includes(filter.toLowerCase()))
|
||||
} else {
|
||||
filteredItems = [...items]
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
// Watch filter value
|
||||
if (filter) {
|
||||
tick().then(() => {
|
||||
if (divContainer) {
|
||||
if (divContainer.scrollHeight > divContainer.clientHeight) {
|
||||
divContainer.classList.add('h-96')
|
||||
} else {
|
||||
divContainer.classList.remove('h-96')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showOptions() {
|
||||
// Add a delay so that the click outside event doesn't trigger too soon
|
||||
setTimeout(() => {
|
||||
areOptionsVisible = true
|
||||
tick().then(() => inputFilter?.select())
|
||||
}, 0)
|
||||
setTimeout(() => {}, 100)
|
||||
}
|
||||
|
||||
function hideOptions() {
|
||||
areOptionsVisible = false
|
||||
}
|
||||
|
||||
function selectItem(item: Item) {
|
||||
value = item
|
||||
hideOptions()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative" use:clickoutside on:clickoutside={hideOptions}>
|
||||
{#if areOptionsVisible}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={filter}
|
||||
bind:this={inputFilter}
|
||||
class={$$restProps.class ?? ''}
|
||||
/>
|
||||
<div
|
||||
class="absolute top-full left-0 w-full bg-gray-700 max-h-[90vh] overflow-y-auto"
|
||||
bind:this={divContainer}
|
||||
>
|
||||
{#each filteredItems as item}
|
||||
<div class="p-1 hover:bg-gray-600" on:click={() => selectItem(item)}>{item.text}</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={value?.text ?? ''}
|
||||
class={$$restProps.class ?? ''}
|
||||
on:click={showOptions}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
select {
|
||||
appearance: none;
|
||||
}
|
||||
</style>
|
|
@ -1,20 +0,0 @@
|
|||
<script lang="ts">
|
||||
type Item = { text: string }
|
||||
export let value: Item | null = null
|
||||
export let items: Item[]
|
||||
|
||||
</script>
|
||||
|
||||
<select bind:value={value} class={ ($$restProps.class ?? '') }>
|
||||
{#each items as item}
|
||||
<option value={item}>
|
||||
{item.text}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
<style lang="scss">
|
||||
select {
|
||||
appearance: none;
|
||||
}
|
||||
</style>
|
|
@ -1,9 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount, tick } from 'svelte'
|
||||
import { shorten } from '$lib/utils'
|
||||
import Select from './Select.svelte'
|
||||
import type { Editor } from 'codemirror'
|
||||
import { shareUrl, selectedLang } from '../store'
|
||||
import ComboBox from './ComboBox.svelte'
|
||||
|
||||
type Language = {
|
||||
text: string
|
||||
|
@ -64,11 +64,11 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-between p-2 text-sm relative z-10 shadow-md">
|
||||
<div>NoPaste</div>
|
||||
<div class="flex justify-between items-center px-3 py-1 text-sm relative z-10 shadow-md">
|
||||
<h1 class="text-xl">NoPaste</h1>
|
||||
<div class="flex justify-end gap-2">
|
||||
<div>
|
||||
<Select
|
||||
<ComboBox
|
||||
items={languages}
|
||||
bind:value={selectedLanguage}
|
||||
class="bg-gray-700 border border-gray-300 p-1"
|
||||
|
|
Loading…
Reference in New Issue
Block a user