Skip to content

Commit 2de1351

Browse files
committed
feat(components): add SearchableSelect
1 parent 937dec4 commit 2de1351

File tree

5 files changed

+132
-2
lines changed

5 files changed

+132
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script>
2+
import { generateRandomID } from '../../../random'
3+
import { createEventDispatcher } from 'svelte'
4+
5+
export let options = {}
6+
export let choice = ''
7+
export let placeholder = ''
8+
export let padding = '12px'
9+
export let width = '232px'
10+
export let maxlength = 255
11+
export let disabled = false
12+
13+
let randomId = generateRandomID('dataList-')
14+
15+
$: internalChoice = choice
16+
17+
const dispatch = createEventDispatcher()
18+
19+
const onChange = () => options[internalChoice] && dispatch('chosen', options[internalChoice])
20+
</script>
21+
22+
<style>
23+
*,
24+
*::before,
25+
*::after {
26+
box-sizing: border-box;
27+
}
28+
29+
.custom-field {
30+
font-size: 14px;
31+
position: relative;
32+
border-top: 20px solid transparent;
33+
}
34+
35+
.custom-field input {
36+
border-radius: 8px;
37+
border: 1px solid gray;
38+
padding: var(--field-padding) 0 var(--field-padding) var(--field-padding);
39+
}
40+
41+
.custom-field .placeholder {
42+
position: absolute;
43+
bottom: -52px;
44+
top: 23px;
45+
transform: translateY(-50%);
46+
color: #aaa;
47+
overflow: hidden;
48+
white-space: nowrap;
49+
text-overflow: ellipsis;
50+
left: var(--field-padding);
51+
width: calc(100% - (var(--field-padding) * 2));
52+
transition: top 0.3s ease, color 0.3s ease, font-size 0.3s ease;
53+
}
54+
55+
.custom-field input:not(:placeholder-shown) + .placeholder,
56+
.custom-field input:focus + .placeholder {
57+
top: 3px;
58+
font-size: 10px;
59+
color: #222;
60+
}
61+
</style>
62+
63+
<label class="custom-field" style="--field-padding: {padding}; {$$props.class}">
64+
<input
65+
{disabled}
66+
{maxlength}
67+
class="fs-14 {$$props.class}"
68+
style="width: {width}"
69+
list={randomId}
70+
placeholder="&nbsp;"
71+
bind:value={internalChoice}
72+
on:change={onChange}
73+
on:blur={() => {
74+
dispatch('check', internalChoice)
75+
internalChoice = choice
76+
}}
77+
on:focus={() => (internalChoice = '')}
78+
/>
79+
<span class="placeholder">{placeholder}</span>
80+
</label>
81+
82+
<datalist id={randomId}>
83+
{#each Object.keys(options) as option}
84+
<option value={option} />
85+
{/each}
86+
</datalist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SearchableSelect from './SearchableSelect.svelte'
2+
3+
export default SearchableSelect

components/custom/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Badge from './Badge'
22
import Form from './Form'
33
import StaticChip from './StaticChip'
4+
import SearchableSelect from './SearchableSelect'
45

5-
export { Badge, Form, StaticChip }
6+
export { Badge, Form, SearchableSelect, StaticChip }

index.mjs

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
setNotice,
2828
} from './components/mdc/index.js'
2929

30-
import { Badge, Form, StaticChip } from './components/custom/index.js'
30+
import { Badge, Form, SearchableSelect, StaticChip } from './components/custom/index.js'
3131

3232
export {
3333
actions,
@@ -47,6 +47,7 @@ export {
4747
MoneyInput,
4848
Progress,
4949
Select,
50+
SearchableSelect,
5051
Snackbar,
5152
TabBar,
5253
TextArea,
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script>
2+
import { Meta, Template, Story } from '@storybook/addon-svelte-csf'
3+
import { SearchableSelect } from '../components/custom'
4+
import { copyAndModifyArgs } from './helpers.js'
5+
6+
const args = {
7+
options: {
8+
'choice 1': '0',
9+
'choice 2': '1',
10+
'choice 3': '2',
11+
'choice 4': '3',
12+
'choice 5': '4',
13+
'choice 6': '5',
14+
},
15+
placeholder: 'choice 1',
16+
padding: '12px',
17+
choice: '',
18+
width: '232px',
19+
maxlength: 255,
20+
'on:chosen': (e) => alert(e.detail + ' chosen'),
21+
'on:check': (e) => {
22+
!args.options[e.detail] && alert(e.detail + ' not available')
23+
},
24+
class: '', //only works for global classes
25+
}
26+
</script>
27+
28+
<Meta title="Atoms/SearchableSelect" component={SearchableSelect} />
29+
30+
<Template let:args>
31+
<SearchableSelect {...args} on:chosen={args['on:chosen']} on:check={args['on:check']} />
32+
</Template>
33+
34+
<Story name="Default" {args} />
35+
<Story name="placeholder" args={copyAndModifyArgs(args, { label: 'Label' })} />
36+
<Story name="Width" args={copyAndModifyArgs(args, { width: '560px' })} />
37+
<Story name="Disabled" args={copyAndModifyArgs(args, { disabled: true })} />
38+
<Story name="Maxlength" args={copyAndModifyArgs(args, { maxlength: 25 })} />
39+
<Story name="padding" args={copyAndModifyArgs(args, { padding: '20px' })} />

0 commit comments

Comments
 (0)