mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-17 05:21:07 +00:00
feat: init school select component
This commit is contained in:
parent
8fbefbf9ce
commit
60323b9be5
4 changed files with 177 additions and 36 deletions
|
|
@ -1,36 +0,0 @@
|
|||
<script lang="ts">
|
||||
import LL from '$i18n/i18n-svelte';
|
||||
|
||||
import type { School } from '$lib/stores/candidate';
|
||||
// TODO
|
||||
// import AutoComplete from 'simple-svelte-autocomplete';
|
||||
import { onMount } from 'svelte';
|
||||
// import schoollistString from '$lib/assets/schoollist.txt';
|
||||
|
||||
let schools: string[] = [];
|
||||
|
||||
onMount(async () => {
|
||||
schools = await fetch('/schoollist.txt')
|
||||
.then((response) => response.text())
|
||||
.then((text) => text.split(';'));
|
||||
});
|
||||
|
||||
export let selectedSchool: School;
|
||||
export let schoolName: string = selectedSchool.name;
|
||||
$: selectedSchool.name = schoolName;
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row">
|
||||
<div>
|
||||
<span>
|
||||
{$LL.input.selectedSchool()}: {selectedSchool.name}
|
||||
</span>
|
||||
<!-- TODO -->
|
||||
<!-- <AutoComplete items={schools} bind:selectedItem={schoolName} /> -->
|
||||
<input type="text" bind:value={schoolName} />
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>{$LL.input.fieldOfStudy()}: </span>
|
||||
<input type="text" bind:value={selectedSchool.field} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
export let itemLabel: string;
|
||||
export let highlighted: boolean;
|
||||
</script>
|
||||
|
||||
<li class="autocomplete-items" class:autocomplete-active={highlighted} on:click on:keydown={null}>
|
||||
{@html itemLabel}
|
||||
</li>
|
||||
|
||||
<style>
|
||||
li.autocomplete-items {
|
||||
list-style: none;
|
||||
border-bottom: 1px solid #d4d4d4;
|
||||
z-index: 99;
|
||||
/*position the autocomplete items to be the same width as the container:*/
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
li.autocomplete-items:hover {
|
||||
/*when hovering an item:*/
|
||||
background-color: #81921f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
li.autocomplete-items:active {
|
||||
/*when navigating through the items using the arrow keys:*/
|
||||
background-color: DodgerBlue !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.autocomplete-active {
|
||||
/*when navigating through the items using the arrow keys:*/
|
||||
background-color: DodgerBlue !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
<script lang="ts">
|
||||
import schoollistString from '$lib/assets/schoollist.txt?raw';
|
||||
import School from './School.svelte';
|
||||
import type { School as SchoolType } from '$lib/stores/candidate';
|
||||
|
||||
const schoolList: Array<string> = schoollistString.split(';');
|
||||
|
||||
let filteredSchools: Array<string> = [];
|
||||
|
||||
const filterSchools = () => {
|
||||
let storageArr: Array<string> = [];
|
||||
if (schoolNameInputValue) {
|
||||
schoolList.forEach((school) => {
|
||||
if (school.toLowerCase().startsWith(schoolNameInputValue.toLowerCase())) {
|
||||
storageArr = [...storageArr, makeMatchBold(school)];
|
||||
}
|
||||
});
|
||||
}
|
||||
filteredSchools = storageArr;
|
||||
};
|
||||
|
||||
let searchInput: HTMLInputElement;
|
||||
let optionsList: HTMLUListElement;
|
||||
|
||||
let schoolNameInputValue = '';
|
||||
let schoolFieldInputValue = '';
|
||||
|
||||
$: if (!schoolNameInputValue) {
|
||||
filteredSchools = [];
|
||||
hiLiteIndex = -1;
|
||||
}
|
||||
|
||||
const setInputVal = (schoolName: string) => {
|
||||
schoolNameInputValue = removeBold(schoolName);
|
||||
filteredSchools = [];
|
||||
hiLiteIndex = -1;
|
||||
searchInput.focus();
|
||||
};
|
||||
|
||||
const makeMatchBold = (str: string) => {
|
||||
let matched = str.substring(0, schoolNameInputValue.length);
|
||||
let makeBold = `<strong>${matched}</strong>`;
|
||||
let boldedMatch = str.replace(matched, makeBold);
|
||||
return boldedMatch;
|
||||
};
|
||||
|
||||
const removeBold = (str: string) => {
|
||||
return str.replace(/<(.)*?>/g, '');
|
||||
};
|
||||
|
||||
let hiLiteIndex: number = 0;
|
||||
|
||||
const navigateList = (e: KeyboardEvent) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
if (hiLiteIndex < filteredSchools.length - 1) {
|
||||
hiLiteIndex++;
|
||||
// scroll optionsList
|
||||
let option = optionsList.children[hiLiteIndex];
|
||||
if (option) {
|
||||
option.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
if (hiLiteIndex > 0) {
|
||||
hiLiteIndex--;
|
||||
}
|
||||
} else if (e.key === 'Enter') {
|
||||
if (hiLiteIndex > -1) {
|
||||
setInputVal(filteredSchools[hiLiteIndex]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export let selectedSchool: SchoolType;
|
||||
|
||||
$: selectedSchool.name = schoolNameInputValue;
|
||||
$: selectedSchool.field = schoolFieldInputValue;
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={navigateList} />
|
||||
|
||||
<div class="autocomplete">
|
||||
<div class="flex">
|
||||
<input
|
||||
class="flex-1"
|
||||
type="text"
|
||||
bind:this={searchInput}
|
||||
bind:value={schoolNameInputValue}
|
||||
on:input={filterSchools}
|
||||
/>
|
||||
<input class="ml-2 w-2/5" type="text" bind:value={schoolFieldInputValue} />
|
||||
</div>
|
||||
{#if filteredSchools.length > 0}
|
||||
<ul bind:this={optionsList} class="schoolAutocompleteList">
|
||||
{#each filteredSchools as country, i}
|
||||
<School
|
||||
itemLabel={country}
|
||||
highlighted={i === hiLiteIndex}
|
||||
on:click={() => setInputVal(country)}
|
||||
/>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
div,
|
||||
input {
|
||||
@apply w-full;
|
||||
}
|
||||
div {
|
||||
@apply relative flex items-center justify-center;
|
||||
}
|
||||
input {
|
||||
@apply hover:border-sspsBlue w-full rounded-lg border border-2 bg-[#f8fafb] p-3 text-xl shadow-lg outline-none transition-colors duration-300;
|
||||
}
|
||||
div span {
|
||||
@apply absolute right-0 top-0 bottom-0 my-auto flex bg-transparent p-3;
|
||||
}
|
||||
.withIcon {
|
||||
@apply pr-14;
|
||||
}
|
||||
.error {
|
||||
@apply border-red-700;
|
||||
}
|
||||
|
||||
.autocomplete {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.schoolAutocompleteList {
|
||||
@apply absolute top-20 z-50;
|
||||
@apply w-full;
|
||||
@apply max-h-72 overflow-scroll;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue