ComboBox
This commit is contained in:
parent
e7125bc07a
commit
327d3f77b8
|
@ -34,6 +34,7 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@svelte-put/clickoutside": "^3.0.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"brotli-compress": "^1.3.3",
|
"brotli-compress": "^1.3.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
|
|
@ -5,6 +5,9 @@ settings:
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@svelte-put/clickoutside':
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
'@sveltejs/adapter-static':
|
'@sveltejs/adapter-static':
|
||||||
specifier: ^2.0.3
|
specifier: ^2.0.3
|
||||||
version: 2.0.3(@sveltejs/kit@1.20.4)
|
version: 2.0.3(@sveltejs/kit@1.20.4)
|
||||||
|
@ -400,6 +403,10 @@ packages:
|
||||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
dev: false
|
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):
|
/@sveltejs/adapter-auto@2.0.0(@sveltejs/kit@1.20.4):
|
||||||
resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==}
|
resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
npm/bootstrap@4.6.1/dist/css/bootstrap-grid.min.css,
|
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/lib/codemirror.min.css,
|
||||||
npm/codemirror@5.65.5/addon/scroll/simplescrollbars.css,
|
npm/codemirror@5.65.5/addon/scroll/simplescrollbars.css,
|
||||||
npm/codemirror@5.65.5/theme/dracula.min.css,
|
npm/codemirror@5.65.5/theme/dracula.min.css"
|
||||||
npm/microtip@0.2.2/microtip.min.css"
|
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
|
@ -22,8 +21,6 @@ npm/microtip@0.2.2/microtip.min.css"
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/combine/
|
<script src="https://cdn.jsdelivr.net/combine/
|
||||||
npm/lzma@2.3.2/src/lzma.min.js,
|
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,
|
||||||
npm/codemirror@5.65.5/addon/mode/loadmode.min.js,
|
npm/codemirror@5.65.5/addon/mode/loadmode.min.js,
|
||||||
npm/codemirror@5.65.5/addon/mode/overlay.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">
|
<script lang="ts">
|
||||||
import { onMount, tick } from 'svelte'
|
import { onMount, tick } from 'svelte'
|
||||||
import { shorten } from '$lib/utils'
|
import { shorten } from '$lib/utils'
|
||||||
import Select from './Select.svelte'
|
|
||||||
import type { Editor } from 'codemirror'
|
import type { Editor } from 'codemirror'
|
||||||
import { shareUrl, selectedLang } from '../store'
|
import { shareUrl, selectedLang } from '../store'
|
||||||
|
import ComboBox from './ComboBox.svelte'
|
||||||
|
|
||||||
type Language = {
|
type Language = {
|
||||||
text: string
|
text: string
|
||||||
|
@ -64,11 +64,11 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex justify-between p-2 text-sm relative z-10 shadow-md">
|
<div class="flex justify-between items-center px-3 py-1 text-sm relative z-10 shadow-md">
|
||||||
<div>NoPaste</div>
|
<h1 class="text-xl">NoPaste</h1>
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<ComboBox
|
||||||
items={languages}
|
items={languages}
|
||||||
bind:value={selectedLanguage}
|
bind:value={selectedLanguage}
|
||||||
class="bg-gray-700 border border-gray-300 p-1"
|
class="bg-gray-700 border border-gray-300 p-1"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user