Skip to content

Commit 0eff57f

Browse files
committed
refacted basic events
1 parent bd21b4b commit 0eff57f

13 files changed

+669
-290
lines changed

.github/stale.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Number of days of inactivity before an issue becomes stale
2-
daysUntilStale: 15
2+
daysUntilStale: 180
33
# Number of days of inactivity before a stale issue is closed
44
daysUntilClose: 3
55
# Issues with these labels will never be considered stale

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353

5454
* Vue 2 & 3 support
5555
* No dependencies
56-
* Lightweight (<6 kB gzipped)
5756
* 100% coverage
5857
* TypeScript support
5958
* ESM support

postcss.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {}

src/Multiselect.vue

+58-168
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,60 @@
11
<template>
22
<div
3-
class="multiselect"
4-
:class="[`is-${mode}`, {
5-
'is-open': isOpen,
6-
'is-searchable': searchable,
7-
'is-disabled': disabled,
8-
'no-caret': !caret,
9-
'open-top': openDirection === 'top',
10-
}]"
11-
:id="id"
12-
@keydown.prevent.enter
133
ref="multiselect"
4+
:tabindex="tabindex"
5+
:class="classList.container"
6+
:id="id"
7+
@focusin="open"
8+
@focusout="close"
9+
@keydown.self="searchable ? false : handleKeydown($event)"
1410
>
15-
<div
16-
class="multiselect-input"
17-
:tabindex="tabindex"
18-
@mousedown="handleInputMousedown"
19-
@focus="openDropdown"
20-
@blur="closeDropdown"
21-
@keyup.esc="handleEsc"
22-
@keyup.enter="selectPointer"
23-
@keydown.prevent.delete="handleBackspace"
24-
@keydown.prevent.up="openDirection === 'top' ? forwardPointer() : backwardPointer()"
25-
@keydown.prevent.down="openDirection === 'top' ? backwardPointer() : forwardPointer()"
26-
>
27-
<!-- Single label -->
28-
<template v-if="mode == 'single' && hasSelected && !search && iv">
29-
<slot name="singlelabel" :value="iv">
30-
<div class="multiselect-single-label">
31-
{{ iv[label] }}
32-
</div>
33-
</slot>
34-
</template>
35-
36-
<!-- Multiple label -->
37-
<template v-if="mode == 'multiple' && hasSelected && !search">
38-
<slot name="multiplelabel" :values="iv">
39-
<div class="multiselect-multiple-label">
40-
{{ multipleLabelText }}
41-
</div>
42-
</slot>
43-
</template>
44-
45-
<!-- Search -->
46-
<template v-if="mode !== 'tags' && searchable && !disabled">
47-
<div class="multiselect-search">
48-
<input
49-
:modelValue="search"
50-
:value="search"
51-
@focus.stop="openDropdown"
52-
@blur.stop="closeDropdown"
53-
@keyup.stop.esc="handleEsc"
54-
@keyup.stop.enter="selectPointer"
55-
@keydown.delete="handleSearchBackspace"
56-
@keydown.stop.up="openDirection === 'top' ? forwardPointer() : backwardPointer()"
57-
@keydown.stop.down="openDirection === 'top' ? backwardPointer() : forwardPointer()"
58-
@input="handleSearchInput"
59-
ref="input"
60-
/>
61-
</div>
62-
</template>
63-
64-
<!-- Tags (with search) -->
65-
<template v-if="mode == 'tags'">
66-
<div class="multiselect-tags">
67-
68-
<span v-for="(option, i, key) in iv" :key="key">
69-
<slot name="tag" :option="option" :handleTagRemove="handleTagRemove" :disabled="disabled">
70-
<div class="multiselect-tag">
71-
{{ option[label] }}
72-
<i
73-
v-if="!disabled"
74-
@click.prevent
75-
@mousedown.prevent.stop="handleTagRemove(option, $event)"
76-
></i>
77-
</div>
78-
</slot>
79-
</span>
80-
81-
<div
82-
v-if="searchable && !disabled"
83-
class="multiselect-search"
84-
:style="{ width: tagsSearchWidth }"
85-
>
86-
<input
87-
:modelValue="search"
88-
:value="search"
89-
@focus.stop="openDropdown"
90-
@blur.stop="closeDropdown"
91-
@keyup.stop.esc="handleEsc"
92-
@keyup.stop.enter="handleAddTag"
93-
@keyup.stop.space="handleAddTag"
94-
@keydown.delete="handleSearchBackspace"
95-
@keydown.stop.up="openDirection === 'top' ? forwardPointer() : backwardPointer()"
96-
@keydown.stop.down="openDirection === 'top' ? backwardPointer() : forwardPointer()"
97-
@input="handleSearchInput"
98-
:style="{ width: tagsSearchWidth }"
99-
ref="input"
100-
/>
101-
</div>
11+
<!-- Single label -->
12+
<template v-if="mode == 'single' && hasSelected && !search && iv">
13+
<slot name="singlelabel" :value="iv">
14+
<div class="multiselect-single-label">
15+
{{ iv[label] }}
10216
</div>
103-
</template>
104-
105-
<!-- Placeholder -->
106-
<template v-if="placeholder && !hasSelected && !search">
107-
<slot name="placeholder">
108-
<div class="multiselect-placeholder">
109-
{{ placeholder }}
110-
</div>
111-
</slot>
112-
</template>
113-
114-
<slot v-if="!hasSelected && caret && !busy" name="caret">
115-
<span class="multiselect-caret"></span>
116-
</slot>
117-
118-
<slot v-if="hasSelected && !disabled && !busy && canDeselect" name="clear" :clear="clear">
119-
<a class="multiselect-clear" @click.prevent="clear"></a>
12017
</slot>
121-
122-
<transition name="multiselect-loading">
123-
<span v-if="busy">
124-
<slot name="spinner">
125-
<span class="multiselect-spinner"></span>
126-
</slot>
127-
</span>
128-
</transition>
129-
</div>
18+
</template>
19+
20+
<!-- Search -->
21+
<template v-if="mode !== 'tags' && searchable && !disabled">
22+
<div class="multiselect-search">
23+
<input
24+
:modelValue="search"
25+
:value="search"
26+
@keydown.self="handleKeydown"
27+
@input="handleSearchInput"
28+
ref="input"
29+
/>
30+
</div>
31+
</template>
13032

13133
<!-- Options -->
13234
<transition v-if="!resolving || !clearOnSearch" name="multiselect" @after-leave="clearSearch">
35+
<!-- @mousedown.prevent: do not close when before/afterlist is clicked -->
13336
<div
13437
v-show="isOpen && showOptions"
13538
class="multiselect-options"
13639
:style="{ maxHeight: contentMaxHeight }"
13740
>
13841
<slot name="beforelist"></slot>
139-
140-
<span
141-
v-for="(option, i, key) in fo"
142-
:tabindex="-1"
143-
class="multiselect-option"
144-
:class="{
145-
'is-pointed': isPointed(option),
146-
'is-selected': isSelected(option),
147-
'is-disabled': isDisabled(option),
148-
}"
149-
:key="key"
150-
@mousedown.prevent
151-
@mouseenter="setPointer(option)"
152-
@click.stop.prevent="handleOptionClick(option)"
153-
>
154-
<slot name="option" :option="option" :search="search">
155-
<span>{{ option[label] }}</span>
156-
</slot>
157-
</span>
158-
159-
<span v-show="noOptions">
160-
<slot name="nooptions">
161-
<div class="multiselect-no-options">{{ noOptionsText }}</div>
162-
</slot>
163-
</span>
164-
165-
<span v-show="noResults">
166-
<slot name="noresults">
167-
<div class="multiselect-no-results">{{ noResultsText }}</div>
168-
</slot>
169-
</span>
170-
171-
<slot name="afterlist"></slot>
42+
<ul>
43+
<li
44+
v-for="(option, i, key) in fo"
45+
:class="classList.option(option)"
46+
:key="key"
47+
@mouseenter="setPointer(option)"
48+
@click="handleOptionClick(option)"
49+
>
50+
<slot name="option" :option="option" :search="search">
51+
<span>{{ option[label] }}</span>
52+
</slot>
53+
</li>
54+
</ul>
17255
</div>
17356
</transition>
17457

175-
<!-- Hacky input element to show HTML5 required warning -->
176-
<input v-if="required" class="multiselect-fake-input" tabindex="-1" :value="textValue" required/>
177-
178-
<template v-if="nativeSupport">
179-
<input v-if="mode == 'single'" type="hidden" :name="name" :value="plainValue !== undefined ? plainValue : ''" />
180-
<template v-else>
181-
<input v-for="(v, i) in plainValue" type="hidden" :name="`${name}[]`" :value="v" :key="i" />
182-
</template>
183-
</template>
18458
</div>
18559
</template>
18660

@@ -194,6 +68,7 @@
19468
import useDropdown from './composables/useDropdown'
19569
import useMultiselect from './composables/useMultiselect'
19670
import useKeyboard from './composables/useKeyboard'
71+
import useClasses from './composables/useClasses'
19772
19873
export default {
19974
name: 'Multiselect',
@@ -217,7 +92,6 @@
21792
id: {
21893
type: [String, Number],
21994
required: false,
220-
default: 'multiselect',
22195
},
22296
name: {
22397
type: [String, Number],
@@ -382,7 +256,6 @@
382256
setup(props, context)
383257
{
384258
const value = useValue(props, context)
385-
const multiselect = useMultiselect(props, context)
386259
const pointer = usePointer(props, context)
387260
388261
const data = useData(props, context, {
@@ -393,6 +266,10 @@
393266
iv: value.iv,
394267
})
395268
269+
const multiselect = useMultiselect(props, context, {
270+
input: search.input,
271+
})
272+
396273
const dropdown = useDropdown(props, context, {
397274
multiselect: multiselect.multiselect,
398275
blurInput: multiselect.blurInput,
@@ -405,18 +282,20 @@
405282
ev: value.ev,
406283
iv: value.iv,
407284
search: search.search,
408-
blurSearch: search.blurSearch,
285+
blur: multiselect.blur,
409286
clearSearch: search.clearSearch,
410287
update: data.update,
411288
blurInput: multiselect.blurInput,
412289
pointer: pointer.pointer,
290+
close: dropdown.close,
413291
})
414292
415293
const pointerAction = usePointerAction(props, context, {
416294
fo: options.fo,
417295
handleOptionClick: options.handleOptionClick,
418296
search: search.search,
419297
pointer: pointer.pointer,
298+
multiselect: multiselect.multiselect,
420299
})
421300
422301
const keyboard = useKeyboard(props, context, {
@@ -426,6 +305,16 @@
426305
clearPointer: pointerAction.clearPointer,
427306
search: search.search,
428307
selectPointer: pointerAction.selectPointer,
308+
forwardPointer: pointerAction.forwardPointer,
309+
backwardPointer: pointerAction.backwardPointer,
310+
blur: multiselect.blur,
311+
})
312+
313+
const classes = useClasses(props, context, {
314+
isOpen: dropdown.isOpen,
315+
isPointed: pointerAction.isPointed,
316+
isSelected: options.isSelected,
317+
isDisabled: options.isDisabled,
429318
})
430319
431320
return {
@@ -438,6 +327,7 @@
438327
...options,
439328
...pointerAction,
440329
...keyboard,
330+
...classes,
441331
}
442332
}
443333
}

0 commit comments

Comments
 (0)