Skip to content

Commit d81f990

Browse files
authored
Merge pull request #6732 from nextcloud-libraries/feat/user-select
feat!: split `NcSelect` super component into `NcSelect` and `NcSelectUsers`
2 parents ddb30c3 + d8ca52c commit d81f990

File tree

6 files changed

+396
-252
lines changed

6 files changed

+396
-252
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ Especially the following are now provided as composables:
140140
- The `range` property was removed in favor of `type="datetime-range"` (datetime ranges), `type="date-range"` (date only ranges), and `type="time-range"` (time only ranges).
141141
- The `lang` property was replaced with the `locale` property.
142142
- The `formatter` property was removed.
143-
143+
- `NcSelect`
144+
- `userSelect` property was removed, instead just use the `NcSelectUsers` component
145+
- `closeOnSelect` property was removed in favor of `keepOpen`.
146+
144147
### 🚀 Enhancements
145148
* Allow writing components using Typescript and provide type definitions for `NcButton` [\#4525](https://github.com/nextcloud-libraries/nextcloud-vue/pull/4525) \([susnux](https://github.com/susnux)\)
146149

src/components/NcSelect/NcSelect.vue

Lines changed: 21 additions & 246 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ const selectArray = [
6868
{
6969
props: {
7070
inputLabel: 'Multiple (objects, pre-selected, stay open on select)',
71+
keepOpen: true,
7172
multiple: true,
72-
closeOnSelect: false,
7373
options: [
7474
{
7575
id: 'foo',
@@ -234,7 +234,7 @@ const data1 = {
234234
props: {
235235
inputLabel: 'Wrapped (Default)',
236236
multiple: true,
237-
closeOnSelect: false,
237+
keepOpen: true,
238238
options: [
239239
'foo',
240240
'bar',
@@ -265,8 +265,8 @@ const data1 = {
265265
const data2 = {
266266
props: {
267267
inputLabel: 'Not wrapped',
268+
keepOpen: true,
268269
multiple: true,
269-
closeOnSelect: false,
270270
options: [
271271
'foo',
272272
'bar',
@@ -319,185 +319,12 @@ export default {
319319
}
320320
</style>
321321
```
322-
323-
### User select examples
324-
325-
```vue
326-
<template>
327-
<div class="grid">
328-
<div v-for="{ props } in selectArray"
329-
class="container">
330-
<NcSelect v-bind="props"
331-
v-model="props.value" />
332-
</div>
333-
</div>
334-
</template>
335-
336-
<script>
337-
import AccountGroup from '@mdi/svg/svg/account-group.svg?raw'
338-
import Email from '@mdi/svg/svg/email.svg?raw'
339-
340-
const selectArray = [
341-
{
342-
props: {
343-
inputLabel: 'User select',
344-
userSelect: true,
345-
options: [
346-
{
347-
id: '0-john',
348-
displayName: 'John',
349-
isNoUser: false,
350-
subname: 'john@example.org',
351-
icon: '',
352-
// Example of how to show the user status within the option
353-
user: '0-john',
354-
preloadedUserStatus: {
355-
icon: '',
356-
status: 'online',
357-
message: 'I am online',
358-
},
359-
},
360-
{
361-
id: '0-emma',
362-
displayName: 'Emma',
363-
isNoUser: false,
364-
subname: 'emma@example.org',
365-
icon: '',
366-
},
367-
{
368-
id: '0-olivia',
369-
displayName: 'Olivia',
370-
isNoUser: false,
371-
subname: 'olivia@example.org',
372-
icon: '',
373-
},
374-
{
375-
id: '0-noah',
376-
displayName: 'Noah',
377-
isNoUser: false,
378-
subname: 'noah@example.org',
379-
icon: '',
380-
},
381-
{
382-
id: '0-oliver',
383-
displayName: 'Oliver',
384-
isNoUser: false,
385-
subname: 'oliver@example.org',
386-
icon: '',
387-
},
388-
{
389-
id: '1-admin',
390-
displayName: 'Admin',
391-
isNoUser: true,
392-
subname: null,
393-
iconSvg: AccountGroup,
394-
iconName: 'Group icon',
395-
},
396-
{
397-
id: '2-org@example.org',
398-
displayName: 'Organization',
399-
isNoUser: true,
400-
subname: 'org@example.org',
401-
iconSvg: Email,
402-
iconName: 'Email icon',
403-
},
404-
],
405-
},
406-
},
407-
408-
{
409-
props: {
410-
inputLabel: 'Multiple user select (stay open on select)',
411-
userSelect: true,
412-
multiple: true,
413-
closeOnSelect: false,
414-
options: [
415-
{
416-
id: '0-john',
417-
displayName: 'John',
418-
isNoUser: false,
419-
subname: 'john@example.org',
420-
icon: '',
421-
},
422-
{
423-
id: '0-emma',
424-
displayName: 'Emma',
425-
isNoUser: false,
426-
subname: 'emma@example.org',
427-
icon: '',
428-
},
429-
{
430-
id: '0-olivia',
431-
displayName: 'Olivia',
432-
isNoUser: false,
433-
subname: 'olivia@example.org',
434-
icon: '',
435-
},
436-
{
437-
id: '0-noah',
438-
displayName: 'Noah',
439-
isNoUser: false,
440-
subname: 'noah@example.org',
441-
icon: '',
442-
},
443-
{
444-
id: '0-oliver',
445-
displayName: 'Oliver',
446-
isNoUser: false,
447-
subname: 'oliver@example.org',
448-
icon: '',
449-
},
450-
{
451-
id: '1-admin',
452-
displayName: 'Admin',
453-
isNoUser: true,
454-
subname: null,
455-
iconSvg: AccountGroup,
456-
iconName: 'Group icon',
457-
},
458-
{
459-
id: '2-org@example.org',
460-
displayName: 'Organization',
461-
isNoUser: true,
462-
subname: 'org@example.org',
463-
iconSvg: Email,
464-
iconName: 'Email icon',
465-
},
466-
],
467-
},
468-
},
469-
]
470-
471-
export default {
472-
data() {
473-
return {
474-
selectArray,
475-
}
476-
},
477-
}
478-
</script>
479-
480-
<style>
481-
.grid {
482-
display: grid;
483-
grid-template-columns: repeat(1, 500px);
484-
gap: 10px;
485-
}
486-
487-
.container {
488-
display: flex;
489-
flex-direction: column;
490-
gap: 2px 0;
491-
}
492-
</style>
493-
```
494322
</docs>
495323

496324
<template>
497325
<VueSelect class="select"
498326
:class="{
499327
'select--no-wrap': noWrap,
500-
'user-select': userSelect,
501328
}"
502329
v-bind="propsToForward"
503330
@search="search = $event"
@@ -525,25 +352,18 @@ export default {
525352
<!-- Set size to 26 to make up for the increased padding of this icon -->
526353
</template>
527354
<template #option="option">
528-
<NcListItemIcon v-if="userSelect"
529-
v-bind="option"
530-
:avatar-size="32"
531-
:name="option[localLabel]"
532-
:search="search" />
533-
<NcEllipsisedOption v-else
534-
:name="String(option[localLabel])"
535-
:search="search" />
355+
<!-- @slot Customize how a option is rendered. -->
356+
<slot name="option" v-bind="option">
357+
<NcEllipsisedOption :name="String(option[localLabel])"
358+
:search="search" />
359+
</slot>
536360
</template>
537361
<template #selected-option="selectedOption">
538-
<NcListItemIcon v-if="userSelect"
539-
v-bind="selectedOption"
540-
:avatar-size="avatarSize"
541-
:name="selectedOption[localLabel]"
542-
no-margin
543-
:search="search" />
544-
<NcEllipsisedOption v-else
545-
:name="String(selectedOption[localLabel])"
546-
:search="search" />
362+
<!-- @slot Customize how a selected option is rendered -->
363+
<slot name="selected-option" :v-bind="selectedOption">
364+
<NcEllipsisedOption :name="String(selectedOption[localLabel])"
365+
:search="search" />
366+
</slot>
547367
</template>
548368
<template #spinner="spinner">
549369
<NcLoadingIcon v-if="spinner.loading" />
@@ -581,7 +401,6 @@ import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
581401
import Close from 'vue-material-design-icons/Close.vue'
582402
583403
import NcEllipsisedOption from '../NcEllipsisedOption/index.js'
584-
import NcListItemIcon from '../NcListItemIcon/index.js'
585404
import NcLoadingIcon from '../NcLoadingIcon/index.js'
586405
587406
export default {
@@ -590,7 +409,6 @@ export default {
590409
components: {
591410
ChevronDown,
592411
NcEllipsisedOption,
593-
NcListItemIcon,
594412
NcLoadingIcon,
595413
VueSelect,
596414
},
@@ -663,13 +481,14 @@ export default {
663481
},
664482
665483
/**
666-
* Close the dropdown when selecting an option
484+
* Keep the dropdown open after selecting an option.
667485
*
668-
* @see https://vue-select.org/api/props.html#closeonselect
486+
* @default false
487+
* @since 8.25.0
669488
*/
670-
closeOnSelect: {
489+
keepOpen: {
671490
type: Boolean,
672-
default: true,
491+
default: false,
673492
},
674493
675494
/**
@@ -731,10 +550,6 @@ export default {
731550
* Defaults to the internal vue-select function documented at the link
732551
* below
733552
*
734-
* Enabling `userSelect` will automatically set this to filter by the
735-
* `displayName` and `subname` properties of the user option object
736-
* unless this prop is set explicitly
737-
*
738553
* @see https://vue-select.org/api/props.html#filterby
739554
*/
740555
filterBy: {
@@ -793,9 +608,6 @@ export default {
793608
* Defaults to the internal vue-select string documented at the link
794609
* below
795610
*
796-
* Enabling `userSelect` will automatically set this to `'displayName'`
797-
* unless this prop is set explicitly
798-
*
799611
* @see https://vue-select.org/api/props.html#label
800612
*/
801613
label: {
@@ -915,18 +727,6 @@ export default {
915727
default: true,
916728
},
917729
918-
/**
919-
* Enable the user selector with avatars
920-
*
921-
* Objects must contain the data expected by the
922-
* [NcListItemIcon](#/Components/NcListItemIcon) and
923-
* [NcAvatar](#/Components/NcAvatar) components
924-
*/
925-
userSelect: {
926-
type: Boolean,
927-
default: false,
928-
},
929-
930730
/**
931731
* Currently selected value
932732
*
@@ -1055,32 +855,11 @@ export default {
1055855
},
1056856
1057857
localFilterBy() {
1058-
// Match the email notation like "Jane <j.doe@example.com>" with the email address as matching group
1059-
const EMAIL_NOTATION = /[^<]*<([^>]+)/
1060-
1061-
if (this.filterBy !== null) {
1062-
return this.filterBy
1063-
}
1064-
if (this.userSelect) {
1065-
return (option, label, search) => {
1066-
const match = search.match(EMAIL_NOTATION)
1067-
return (match && option.subname?.toLocaleLowerCase?.()?.indexOf(match[1].toLocaleLowerCase()) > -1)
1068-
|| (`${label} ${option.subname}`
1069-
.toLocaleLowerCase()
1070-
.indexOf(search.toLocaleLowerCase()) > -1)
1071-
}
1072-
}
1073-
return VueSelect.props.filterBy.default
858+
return this.filterBy ?? VueSelect.props.filterBy.default
1074859
},
1075860
1076861
localLabel() {
1077-
if (this.label !== null) {
1078-
return this.label
1079-
}
1080-
if (this.userSelect) {
1081-
return 'displayName'
1082-
}
1083-
return VueSelect.props.label.default
862+
return this.label ?? VueSelect.props.label.default
1084863
},
1085864
1086865
propsToForward() {
@@ -1097,6 +876,7 @@ export default {
1097876
...initialPropsToForward,
1098877
// Custom overrides of vue-select props
1099878
calculatePosition: this.localCalculatePosition,
879+
closeOnSelect: !this.keepOpen,
1100880
filterBy: this.localFilterBy,
1101881
label: this.localLabel,
1102882
}
@@ -1361,9 +1141,4 @@ body {
13611141
color: var(--color-text-lighter) !important;
13621142
}
13631143
}
1364-
1365-
// Selected users require slightly different padding
1366-
.user-select .vs__selected {
1367-
padding-inline: 0 5px !important;
1368-
}
13691144
</style>

0 commit comments

Comments
 (0)