@@ -61,6 +61,21 @@ const selectArray = [
61
61
},
62
62
},
63
63
64
+ {
65
+ title: 'Simple (top placement)',
66
+ props: {
67
+ inputId: getRandomId(),
68
+ placement: 'top',
69
+ options: [
70
+ 'foo',
71
+ 'bar',
72
+ 'baz',
73
+ 'qux',
74
+ 'quux',
75
+ ],
76
+ },
77
+ },
78
+
64
79
{
65
80
title: 'Multiple (with placeholder)',
66
81
props: {
@@ -508,6 +523,14 @@ export default {
508
523
<script >
509
524
import VueSelect from ' vue-select'
510
525
import ' vue-select/dist/vue-select.css'
526
+ import {
527
+ autoUpdate ,
528
+ computePosition ,
529
+ flip ,
530
+ limitShift ,
531
+ offset ,
532
+ shift ,
533
+ } from ' @floating-ui/dom'
511
534
512
535
import ChevronDown from ' vue-material-design-icons/ChevronDown.vue'
513
536
import Close from ' vue-material-design-icons/Close.vue'
@@ -537,6 +560,32 @@ export default {
537
560
// Add VueSelect props to $props
538
561
... VueSelect .props ,
539
562
563
+ /**
564
+ * Append the dropdown element to the end of the body
565
+ * and size/position it dynamically.
566
+ *
567
+ * @see https://vue-select.org/api/props.html#appendtobody
568
+ */
569
+ appendToBody: {
570
+ type: Boolean ,
571
+ default: true ,
572
+ },
573
+
574
+ /**
575
+ * When `appendToBody` is true, this function is responsible for
576
+ * positioning the drop down list.
577
+ *
578
+ * If a function is returned from `calculatePosition`, it will
579
+ * be called when the drop down list is removed from the DOM.
580
+ * This allows for any garbage collection you may need to do.
581
+ *
582
+ * @see https://vue-select.org/api/props.html#calculateposition
583
+ */
584
+ calculatePosition: {
585
+ type: Function ,
586
+ default: null ,
587
+ },
588
+
540
589
/**
541
590
* Close the dropdown when selecting an option
542
591
*
@@ -673,6 +722,16 @@ export default {
673
722
default: ' ' ,
674
723
},
675
724
725
+ /**
726
+ * When `appendToBody` is true, this sets the placement of the dropdown
727
+ *
728
+ * @type {'bottom' | 'top'}
729
+ */
730
+ placement: {
731
+ type: String ,
732
+ default: ' bottom' ,
733
+ },
734
+
676
735
/**
677
736
* Enable the user selector with avatars
678
737
*
@@ -723,6 +782,66 @@ export default {
723
782
},
724
783
725
784
computed: {
785
+ localCalculatePosition () {
786
+ if (this .calculatePosition !== null ) {
787
+ return this .calculatePosition
788
+ }
789
+
790
+ return (dropdownMenu , component , { width }) => {
791
+ dropdownMenu .style .width = width
792
+
793
+ const addClass = {
794
+ name: ' addClass' ,
795
+ fn (_middlewareArgs ) {
796
+ dropdownMenu .classList .add (' vs__dropdown-menu--floating' )
797
+ return {}
798
+ },
799
+ }
800
+
801
+ const togglePlacementClass = {
802
+ name: ' togglePlacementClass' ,
803
+ fn ({ placement }) {
804
+ component .$el .classList .toggle (
805
+ ' select--drop-up' ,
806
+ placement === ' top' ,
807
+ )
808
+ dropdownMenu .classList .toggle (
809
+ ' vs__dropdown-menu--floating-placement-top' ,
810
+ placement === ' top' ,
811
+ )
812
+ return {}
813
+ },
814
+ }
815
+
816
+ const updatePosition = () => {
817
+ computePosition (component .$refs .toggle , dropdownMenu, {
818
+ placement: this .placement ,
819
+ middleware: [
820
+ offset (- 1 ),
821
+ addClass,
822
+ togglePlacementClass,
823
+ // Match popperjs default collision prevention behavior by appending the following middleware in order
824
+ flip (),
825
+ shift ({ limiter: limitShift () }),
826
+ ],
827
+ }).then (({ x, y }) => {
828
+ Object .assign (dropdownMenu .style , {
829
+ left: ` ${ x} px` ,
830
+ top: ` ${ y} px` ,
831
+ })
832
+ })
833
+ }
834
+
835
+ const cleanup = autoUpdate (
836
+ component .$refs .toggle ,
837
+ dropdownMenu,
838
+ updatePosition,
839
+ )
840
+
841
+ return cleanup
842
+ }
843
+ },
844
+
726
845
localFilterBy () {
727
846
if (this .filterBy !== null ) {
728
847
return this .filterBy
@@ -752,17 +871,20 @@ export default {
752
871
propsToForward () {
753
872
const {
754
873
// Custom overrides of vue-select props
874
+ calculatePosition,
755
875
filterBy,
756
876
label,
757
877
// Props handled by the component itself
758
878
noWrap,
879
+ placement,
759
880
userSelect,
760
881
// Props to forward
761
882
... initialPropsToForward
762
883
} = this .$props
763
884
764
885
const propsToForward = {
765
886
... initialPropsToForward,
887
+ calculatePosition: this .localCalculatePosition ,
766
888
label: this .localLabel ,
767
889
}
768
890
@@ -776,8 +898,8 @@ export default {
776
898
}
777
899
</script >
778
900
779
- <style lang="scss" scoped >
780
- .select {
901
+ <style lang="scss">
902
+ :root {
781
903
/* Set custom vue-select CSS variables */
782
904
783
905
/* Search Input */
@@ -826,26 +948,54 @@ export default {
826
948
827
949
/* Transitions */
828
950
--vs-transition-duration : 0ms ;
951
+ }
829
952
953
+ .v-select.select {
830
954
/* Override default vue-select styles */
831
955
min-height : $clickable-area ;
832
956
min-width : 260px ;
833
957
margin : 0 ;
834
958
959
+ .vs__selected {
960
+ min-height : 36px ;
961
+ padding : 0 0.5em ;
962
+ }
963
+
964
+ .vs__clear {
965
+ margin-right : 2px ;
966
+ }
967
+
835
968
& --no-wrap {
836
- & : deep ( .vs__selected-options ) {
969
+ .vs__selected-options {
837
970
flex-wrap : nowrap ;
838
971
overflow : auto ;
839
972
}
840
973
}
841
974
842
- & :deep(.vs__selected ) {
843
- min-height : 36px ;
844
- padding : 0 0.5em ;
975
+ & --drop-up {
976
+ & .vs--open {
977
+ .vs__dropdown-toggle {
978
+ border-radius : 0 0 var (--vs-border-radius ) var (--vs-border-radius );
979
+ border-top-color : transparent ;
980
+ border-bottom-color : var (--vs-border-color );
981
+ }
982
+ }
845
983
}
984
+ }
846
985
847
- & :deep(.vs__clear ) {
848
- margin-right : 2px ;
986
+ .vs__dropdown-menu {
987
+ & --floating {
988
+ width : max-content ;
989
+ position : absolute ;
990
+ top : 0 ;
991
+ left : 0 ;
992
+
993
+ & -placement-top {
994
+ border-radius : var (--vs-border-radius ) var (--vs-border-radius ) 0 0 ;
995
+ border-top-style : var (--vs-border-style );
996
+ border-bottom-style : none ;
997
+ box-shadow : 0px -1px 1px 0px var (--color-box-shadow );
998
+ }
849
999
}
850
1000
}
851
1001
</style >
0 commit comments