@teammodlr/ui-kit 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/package.json +2 -1
  2. package/src/__storybook__/argTypes.ts +42 -0
  3. package/src/__storybook__/createStory.ts +15 -0
  4. package/src/__storybook__/index.ts +2 -0
  5. package/src/components/avatar/Avatar.css +69 -0
  6. package/src/components/avatar/Avatar.vue +102 -0
  7. package/src/components/avatar/examples/basic.vue +6 -0
  8. package/src/components/avatar/examples/custom-size.vue +7 -0
  9. package/src/components/avatar/examples/index.stories.ts +28 -0
  10. package/src/components/avatar/examples/index.ts +5 -0
  11. package/src/components/avatar/examples/sizes.vue +33 -0
  12. package/src/components/avatar/examples/slots.vue +7 -0
  13. package/src/components/avatar/examples/variants.vue +18 -0
  14. package/src/components/avatargroup/AvatarGroup.css +8 -0
  15. package/src/components/avatargroup/AvatarGroup.vue +32 -0
  16. package/src/components/avatargroup/examples/basic.vue +11 -0
  17. package/src/components/avatargroup/examples/index.stories.ts +28 -0
  18. package/src/components/avatargroup/examples/index.ts +1 -0
  19. package/src/components/breadcrumb/Breadcrumb.css +46 -0
  20. package/src/components/breadcrumb/Breadcrumb.vue +51 -0
  21. package/src/components/breadcrumb/examples/basic.vue +9 -0
  22. package/src/components/breadcrumb/examples/index.stories.ts +20 -0
  23. package/src/components/breadcrumb/examples/index.ts +1 -0
  24. package/src/components/breadcrumbitem/BreadcrumbItem.vue +66 -0
  25. package/src/components/breadcrumbitem/index.ts +1 -0
  26. package/src/components/button/Button.css +378 -0
  27. package/src/components/button/Button.vue +108 -0
  28. package/src/components/button/examples/basic.vue +3 -0
  29. package/src/components/button/examples/colors-secondary.vue +8 -0
  30. package/src/components/button/examples/colors.vue +8 -0
  31. package/src/components/button/examples/disabled.vue +3 -0
  32. package/src/components/button/examples/icon.vue +10 -0
  33. package/src/components/button/examples/index.stories.ts +33 -0
  34. package/src/components/button/examples/index.ts +7 -0
  35. package/src/components/button/examples/rounded.vue +7 -0
  36. package/src/components/button/examples/size.vue +7 -0
  37. package/src/components/buttongroup/ButtonGroup.vue +47 -0
  38. package/src/components/buttongroup/examples/basic.vue +9 -0
  39. package/src/components/buttongroup/examples/index.stories.ts +21 -0
  40. package/src/components/buttongroup/examples/index.ts +1 -0
  41. package/src/components/checkbox/Checkbox.css +185 -0
  42. package/src/components/checkbox/Checkbox.vue +140 -0
  43. package/src/components/checkbox/examples/basic.vue +9 -0
  44. package/src/components/checkbox/examples/index.stories.ts +21 -0
  45. package/src/components/checkbox/examples/index.ts +3 -0
  46. package/src/components/checkbox/examples/slot.vue +11 -0
  47. package/src/components/dropdown/Dropdown.css +68 -0
  48. package/src/components/dropdown/Dropdown.vue +237 -0
  49. package/src/components/dropdown/DropdownItem.vue +118 -0
  50. package/src/components/dropdown/DropdownList.vue +20 -0
  51. package/src/components/dropdown/DropdownWrapper.vue +17 -0
  52. package/src/components/dropdown/examples/basic.vue +73 -0
  53. package/src/components/dropdown/examples/context.vue +71 -0
  54. package/src/components/dropdown/examples/index.stories.ts +23 -0
  55. package/src/components/dropdown/examples/index.ts +2 -0
  56. package/src/components/dropdown/hooks.ts +30 -0
  57. package/src/components/form/Form.vue +141 -0
  58. package/src/components/form/examples/basic.vue +73 -0
  59. package/src/components/form/examples/index.stories.ts +23 -0
  60. package/src/components/form/examples/index.ts +1 -0
  61. package/src/components/image/Image.vue +71 -0
  62. package/src/components/image/examples/basic.vue +5 -0
  63. package/src/components/image/examples/index.stories.ts +12 -0
  64. package/src/components/image/examples/index.ts +1 -0
  65. package/src/components/index.ts +27 -0
  66. package/src/components/input/Input.css +173 -0
  67. package/src/components/input/Input.vue +176 -0
  68. package/src/components/input/examples/basic.vue +7 -0
  69. package/src/components/input/examples/index.stories.ts +24 -0
  70. package/src/components/input/examples/index.ts +4 -0
  71. package/src/components/input/examples/prefix.vue +7 -0
  72. package/src/components/input/examples/sizes.vue +9 -0
  73. package/src/components/input/examples/suffix.vue +7 -0
  74. package/src/components/link/Link.css +147 -0
  75. package/src/components/link/Link.vue +54 -0
  76. package/src/components/link/examples/basic.vue +3 -0
  77. package/src/components/link/examples/index.stories.ts +20 -0
  78. package/src/components/link/examples/index.ts +1 -0
  79. package/src/components/messageprovider/Message.vue +76 -0
  80. package/src/components/messageprovider/MessageProvider.css +36 -0
  81. package/src/components/messageprovider/MessageProvider.vue +142 -0
  82. package/src/components/messageprovider/examples/basic.vue +19 -0
  83. package/src/components/messageprovider/examples/index.stories.ts +30 -0
  84. package/src/components/messageprovider/examples/index.ts +1 -0
  85. package/src/components/messageprovider/examples/wrapper.vue +9 -0
  86. package/src/components/messageprovider/types.ts +39 -0
  87. package/src/components/messageprovider/useMessage.ts +12 -0
  88. package/src/components/select/Select.css +226 -0
  89. package/src/components/select/Select.vue +474 -0
  90. package/src/components/select/examples/basic.vue +20 -0
  91. package/src/components/select/examples/index.stories.ts +22 -0
  92. package/src/components/select/examples/index.ts +3 -0
  93. package/src/components/select/examples/multiple.vue +22 -0
  94. package/src/components/select/examples/searchable.vue +20 -0
  95. package/src/components/skeleton/Skeleton.css +73 -0
  96. package/src/components/skeleton/Skeleton.vue +47 -0
  97. package/src/components/skeleton/examples/basic.vue +3 -0
  98. package/src/components/skeleton/examples/index.stories.ts +24 -0
  99. package/src/components/skeleton/examples/index.ts +2 -0
  100. package/src/components/skeleton/examples/post.vue +8 -0
  101. package/src/components/tab/Tab.css +113 -0
  102. package/src/components/tab/Tab.vue +98 -0
  103. package/src/components/tab/TabGroup.vue +90 -0
  104. package/src/components/tab/examples/basic.vue +19 -0
  105. package/src/components/tab/examples/index.stories.ts +25 -0
  106. package/src/components/tab/examples/index.ts +2 -0
  107. package/src/components/tab/examples/vertical.vue +19 -0
  108. package/src/components/table/Table.css +45 -0
  109. package/src/components/table/Table.vue +108 -0
  110. package/src/components/table/TableBody.vue +13 -0
  111. package/src/components/table/TableCell.vue +13 -0
  112. package/src/components/table/TableHead.vue +22 -0
  113. package/src/components/table/TableHeadRow.vue +15 -0
  114. package/src/components/table/TableRow.vue +13 -0
  115. package/src/components/table/examples/basic.vue +46 -0
  116. package/src/components/table/examples/index.stories.ts +19 -0
  117. package/src/components/table/examples/index.ts +1 -0
  118. package/src/components/table/types/index.ts +5 -0
  119. package/src/components/tooltip/Tooltip.css +49 -0
  120. package/src/components/tooltip/Tooltip.vue +85 -0
  121. package/src/components/tooltip/examples/basic.vue +8 -0
  122. package/src/components/tooltip/examples/index.stories.ts +21 -0
  123. package/src/components/tooltip/examples/index.ts +1 -0
  124. package/src/components/virtualscroll/VirtualScroll.css +13 -0
  125. package/src/components/virtualscroll/VirtualScroll.vue +61 -0
  126. package/src/components/virtualscroll/examples/basic.vue +37 -0
  127. package/src/components/virtualscroll/examples/index.stories.ts +31 -0
  128. package/src/components/virtualscroll/examples/index.ts +1 -0
  129. package/src/composables/element.ts +13 -0
  130. package/src/composables/useCachedUid.ts +48 -0
  131. package/src/composables/useEventListener.ts +14 -0
  132. package/src/composables/useInput.ts +134 -0
  133. package/src/composables/useIsServer.ts +7 -0
  134. package/src/composables/useMount.ts +43 -0
  135. package/src/composables/usePopper.ts +194 -0
  136. package/src/create.ts +34 -0
  137. package/src/css/components.css +14 -0
  138. package/src/css/index.css +4 -0
  139. package/src/css/normalize.css +379 -0
  140. package/src/css/transitions.css +40 -0
  141. package/src/css/variables.css +80 -0
  142. package/src/exports/nuxt.js +33 -0
  143. package/src/exports/nuxt.plugin.js +8 -0
  144. package/src/index.ts +5 -0
  145. package/src/install.ts +8 -0
  146. package/src/shims-vue.d.ts +6 -0
  147. package/src/types/props.ts +3 -0
@@ -0,0 +1,185 @@
1
+ .m-checkbox {
2
+ --checkbox-width: 22px;
3
+ --checkbox-height: 22px;
4
+ --checkbox-border-radius: 6px;
5
+ --checkbox-border-width: 1px;
6
+ --checkbox-background-color: var(--white);
7
+ --checkbox-border-color: var(--gray-300);
8
+ --checkbox-gap: 6px;
9
+
10
+ --checkbox-tick-width: 4px;
11
+ --checkbox-tick-height: 10px;
12
+ --checkbox-tick-height-negative: -10px;
13
+
14
+ --checkbox-tick-top: 8px;
15
+ --checkbox-tick-left: 3px;
16
+ --checkbox-tick-border-width: 3px;
17
+
18
+ display: flex;
19
+ align-items: center;
20
+ position: relative;
21
+ font-weight: bold;
22
+ cursor: pointer;
23
+ gap: var(--checkbox-gap);
24
+ }
25
+
26
+ .m-checkbox.m-checkbox--xs {
27
+ --checkbox-width: 16px;
28
+ --checkbox-height: 16px;
29
+ --checkbox-border-radius: 4px;
30
+ --checkbox-border-width: 1px;
31
+ --checkbox-tick-top: 6px;
32
+ --checkbox-tick-left: 1px;
33
+
34
+ --checkbox-tick-width: 3px;
35
+ --checkbox-tick-height: 8px;
36
+ --checkbox-tick-height-negative: -8px;
37
+ }
38
+
39
+ .m-checkbox.m-checkbox--sm {
40
+ --checkbox-width: 20px;
41
+ --checkbox-height: 20px;
42
+ --checkbox-border-radius: 4px;
43
+ --checkbox-border-width: 1px;
44
+ --checkbox-tick-top: 7px;
45
+ --checkbox-tick-left: 2px;
46
+ }
47
+
48
+ .m-checkbox.m-checkbox--lg {
49
+ --checkbox-width: 26px;
50
+ --checkbox-height: 26px;
51
+ --checkbox-border-radius: 8px;
52
+ --checkbox-border-width: 1px;
53
+
54
+ --checkbox-tick-top: 10px;
55
+ --checkbox-tick-left: 3px;
56
+
57
+ --checkbox-tick-border-width: 4px;
58
+ --checkbox-tick-width: 4px;
59
+ --checkbox-tick-height: 14px;
60
+ --checkbox-tick-height-negative: -14px;
61
+ }
62
+
63
+ .m-checkbox.m-checkbox--xl {
64
+ --checkbox-width: 32px;
65
+ --checkbox-height: 32px;
66
+ --checkbox-border-radius: 8px;
67
+ --checkbox-border-width: 1px;
68
+
69
+ --checkbox-tick-top: 14px;
70
+ --checkbox-tick-left: 5px;
71
+
72
+ --checkbox-tick-border-width: 4px;
73
+ --checkbox-tick-width: 5px;
74
+ --checkbox-tick-height: 16px;
75
+ --checkbox-tick-height-negative: -16px;
76
+ }
77
+
78
+ .m-checkbox.m-checkbox--blue.m-checkbox--checked {
79
+ --checkbox-background-color: var(--blue);
80
+ --checkbox-border-color: var(--blue);
81
+ }
82
+
83
+ .m-checkbox.m-checkbox--green.m-checkbox--checked {
84
+ --checkbox-background-color: var(--green);
85
+ --checkbox-border-color: var(--green);
86
+ }
87
+
88
+ .m-checkbox.m-checkbox--red.m-checkbox--checked {
89
+ --checkbox-background-color: var(--red);
90
+ --checkbox-border-color: var(--red);
91
+ }
92
+
93
+ .m-checkbox.m-checkbox--yellow.m-checkbox--checked {
94
+ --checkbox-background-color: var(--yellow);
95
+ --checkbox-border-color: var(--yellow);
96
+ }
97
+
98
+ .m-checkbox input {
99
+ position: absolute;
100
+ opacity: 0;
101
+ cursor: pointer;
102
+ height: 0;
103
+ width: 0;
104
+ display: none;
105
+ }
106
+
107
+ .m-checkbox span {
108
+ display: flex;
109
+ height: var(--checkbox-height);
110
+ width: var(--checkbox-width);
111
+ border-radius: var(--checkbox-border-radius);
112
+ border: var(--checkbox-border-width) solid var(--checkbox-border-color);
113
+ background-color: var(--checkbox-background-color);
114
+ transition: all 125ms cubic-bezier(0.4, 0, 0.2, 1);
115
+ position: relative;
116
+ }
117
+
118
+ .m-checkbox:hover:not(.m-checkbox--checked):not(.m-checkbox--disabled) span {
119
+ --checkbox-border-color: var(--gray-400);
120
+ --checkbox-background-color: var(--gray-100);
121
+ }
122
+
123
+ .m-checkbox.m-checkbox--disabled span,
124
+ .m-checkbox.m-checkbox--disabled .m-checkbox__label {
125
+ cursor: not-allowed;
126
+ }
127
+
128
+ .m-checkbox.m-checkbox--disabled span {
129
+ opacity: 0.6;
130
+ }
131
+
132
+ .m-checkbox:not(.m-checkbox--checked).m-checkbox--disabled span {
133
+ --checkbox-border-color: var(--gray-300);
134
+ --checkbox-background-color: var(--gray-100);
135
+ }
136
+
137
+ .m-checkbox.m-checkbox--checked span {
138
+ background-color: var(--checkbox-background-color);
139
+ border-color: var(--checkbox-background-color);
140
+ }
141
+
142
+ .m-checkbox.m-checkbox--checked span::before {
143
+ content: "";
144
+ position: absolute;
145
+ transform: rotate(45deg);
146
+ border-right: var(--checkbox-tick-border-width) solid transparent;
147
+ border-bottom: var(--checkbox-tick-border-width) solid transparent;
148
+ top: var(--checkbox-tick-top);
149
+ left: var(--checkbox-tick-left);
150
+ color: #FFFFFF;
151
+ transform-origin: 0% 100%;
152
+ animation: draw-tick 125ms 170ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
153
+ }
154
+
155
+ .m-checkbox .m-checkbox__label {
156
+ display: inline-block;
157
+ vertical-align: middle;
158
+ font-size: 14px;
159
+ line-height: 24px;
160
+ color: #000000;
161
+ font-weight: 400;
162
+ user-select: none;
163
+ }
164
+
165
+ @keyframes draw-tick {
166
+ 0% {
167
+ height: 0px;
168
+ width: 0px;
169
+ border-color: #FFF;
170
+ transform: translate3d(0, 0, 0) rotate(45deg);
171
+ }
172
+
173
+ 25% {
174
+ height: 0px;
175
+ width: var(--checkbox-tick-width);
176
+ transform: translate3d(0, 0, 0) rotate(45deg);
177
+ }
178
+
179
+ 100% {
180
+ border-color: #FFF;
181
+ height: var(--checkbox-tick-height);
182
+ width: var(--checkbox-tick-width);
183
+ transform: translate3d(0, var(--checkbox-tick-height-negative), 0) rotate(45deg);
184
+ }
185
+ }
@@ -0,0 +1,140 @@
1
+ <template>
2
+ <div ref="checkboxRef" :class="wrapperComputedClasses">
3
+ <div :class="checkboxComputedClass" @click.prevent="toggle">
4
+ <input
5
+ v-model="checked"
6
+ type="checkbox"
7
+ :aria-checked="checked ? 'true' : 'false'"
8
+ :aria-disabled="(disabled || loading) ? 'true' : 'false'"
9
+ :aria-labelledby="`${id}-label`"
10
+ :disabled="disabled || loading"
11
+ :name="name"
12
+ :id="id"
13
+ />
14
+ <span></span>
15
+ <div class="m-checkbox__label" :id="`${id}-label`">
16
+ <template v-if="hasSlot">
17
+ <slot />
18
+ </template>
19
+ <template v-else>
20
+ {{ label }}
21
+ </template>
22
+ </div>
23
+ </div>
24
+ <p v-if="inputError" class="m-input__error">
25
+ {{ inputError }}
26
+ </p>
27
+ </div>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import { defineComponent, toRef, ref, type PropType, watch, computed } from 'vue';
32
+ import { useInput } from '@/composables/useInput';
33
+ import type { Size, Color } from '@/types/props';
34
+
35
+ export default defineComponent({
36
+ name: "Checkbox",
37
+ emits: useInput.emits(),
38
+ props: {
39
+ ...useInput.props(),
40
+ label: {
41
+ type: String
42
+ },
43
+ size: {
44
+ type: String as PropType<Size>,
45
+ default: 'md',
46
+ },
47
+ color: {
48
+ type: String as PropType<Color>,
49
+ default: 'blue',
50
+ },
51
+ disabled: {
52
+ type: Boolean,
53
+ default: false
54
+ },
55
+ loading: {
56
+ type: Boolean,
57
+ default: false
58
+ }
59
+ },
60
+ setup(props, { slots, emit }) {
61
+ const focus = () => { };
62
+
63
+ const {
64
+ id,
65
+ insideForm,
66
+ reset,
67
+ validate,
68
+ setError,
69
+ formError,
70
+ formDisabled,
71
+ formLoading
72
+ } = useInput(props, { focus, emit, withListeners: false });
73
+
74
+ const disabled = computed(() => formDisabled || props.disabled);
75
+ const loading = computed(() => formLoading || props.loading);
76
+ const name = toRef(props, 'name');
77
+ const checked = ref(false);
78
+ const checkboxRef = ref<HTMLElement | null>(null);
79
+ const hasSlot = computed(() => !!(slots.default));
80
+
81
+ const wrapperComputedClasses = computed(() => {
82
+ return [
83
+ 'm-checkbox__container',
84
+ {
85
+ 'm-form__input': insideForm
86
+ }
87
+ ]
88
+ });
89
+
90
+ const checkboxComputedClass = computed(() => {
91
+ return {
92
+ 'm-checkbox': true,
93
+ 'm-checkbox--checked': checked.value,
94
+ 'm-checkbox--disabled': disabled.value || loading.value,
95
+ 'm-checkbox--error': formError.value,
96
+ [`m-checkbox--${props.size}`]: true,
97
+ [`m-checkbox--${props.color}`]: true,
98
+ }
99
+ });
100
+
101
+ const inputError = computed(() => {
102
+ return formError.value || props.error;
103
+ });
104
+
105
+ watch(() => props.modelValue, (value) => {
106
+ checked.value = !!value;
107
+ }, { immediate: true });
108
+
109
+ watch(() => checked.value, (value) => {
110
+ emit('update:modelValue', value);
111
+ });
112
+
113
+ function toggle() {
114
+ if (disabled.value) return;
115
+ checked.value = !checked.value;
116
+ }
117
+
118
+
119
+ return {
120
+ id,
121
+ formError,
122
+ insideForm,
123
+ reset,
124
+ validate,
125
+ setError,
126
+ disabled,
127
+ loading,
128
+ name,
129
+ checked,
130
+ checkboxRef,
131
+ inputError,
132
+ toggle,
133
+
134
+ wrapperComputedClasses,
135
+ checkboxComputedClass,
136
+ hasSlot
137
+ }
138
+ }
139
+ })
140
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <MCheckbox v-model="checked" label="Test" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import { ref } from 'vue'
7
+
8
+ const checked = ref(false);
9
+ </script>
@@ -0,0 +1,21 @@
1
+ import MCheckbox from "../Checkbox.vue";
2
+ import {
3
+ MCheckboxBasic,
4
+ MCheckboxSlot
5
+ } from "./index";
6
+
7
+ import {
8
+ createStory, sizeArgType, colorArgType
9
+ } from "@/__storybook__";
10
+
11
+ export default {
12
+ component: MCheckbox,
13
+ title: 'Components/Checkbox',
14
+ argTypes: {
15
+ ...sizeArgType(),
16
+ ...colorArgType()
17
+ }
18
+ }
19
+
20
+ export const Basic = createStory(MCheckboxBasic);
21
+ export const Slot = createStory(MCheckboxSlot);
@@ -0,0 +1,3 @@
1
+ export { default as MCheckboxBasic } from './basic.vue';
2
+ export { default as MCheckboxSlot } from './slot.vue';
3
+
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <MCheckbox v-model="checked">
3
+ Weapon
4
+ </MCheckbox>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { ref } from 'vue'
9
+
10
+ const checked = ref(false);
11
+ </script>
@@ -0,0 +1,68 @@
1
+ .m-dropdown {
2
+ position: relative;
3
+ }
4
+
5
+ .m-dropdown-menu {
6
+ --dropdown-menu-width: 200px;
7
+ --dropdown-menu-padding: 6px;
8
+ --dropdown-menu-background-color: var(--white);
9
+ --dropdown-menu-box-shadow: 0px 4px 10px 1px rgb(154 161 171 / 20%);
10
+ --dropdown-menu-border-radius: 4px;
11
+ --dropdown-menu-border: 1px solid var(--gray-200);
12
+
13
+ --dropdown-menu-item-font-size: 15px;
14
+ --dropdown-menu-item-height: 34px;
15
+ --dropdown-menu-item-line-height: 34px;
16
+ --dropdown-menu-item-color: var(--gray-600);
17
+ --dropdown-menu-item-background-color: var(--white);
18
+ --dropdown-menu-item-border-radius: 4px;
19
+ --dropdown-menu-item-padding: 0px 8px;
20
+ --dropdown-menu-gap: 4px;
21
+
22
+ position: relative;
23
+ background-color: var(--dropdown-menu-background-color);
24
+ box-shadow: var(--dropdown-menu-box-shadow);
25
+ border-radius: var(--dropdown-menu-border-radius);
26
+ min-width: var(--dropdown-menu-width);
27
+ padding: var(--dropdown-menu-padding);
28
+ border: var(--dropdown-menu-border);
29
+ }
30
+
31
+ .m-dropdown-menu ul {
32
+ list-style: none;
33
+ margin: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ .m-dropdown-menu ul .m-dropdown-item {
38
+ position: relative;
39
+ display: flex;
40
+ align-items: center;
41
+ cursor: pointer;
42
+
43
+ font-size: var(--dropdown-menu-item-font-size);
44
+ height: var(--dropdown-menu-item-height);
45
+ line-height: var(--dropdown-menu-item-line-height);
46
+ color: var(--dropdown-menu-item-color);
47
+ background-color: var(--dropdown-menu-item-background-color);
48
+ border-radius: var(--dropdown-menu-item-border-radius);
49
+ padding: var(--dropdown-menu-item-padding);
50
+ gap: var(--dropdown-menu-gap);
51
+ }
52
+
53
+ .m-dropdown-menu ul .m-dropdown-item .m-dropdown__text {
54
+ white-space: nowrap;
55
+ flex: 1;
56
+ z-index: 1;
57
+ }
58
+
59
+ .m-dropdown-menu ul .m-dropdown-item:hover {
60
+ --dropdown-menu-item-color: var(--blue);
61
+ --dropdown-menu-item-background-color: var(--gray-100);
62
+ }
63
+
64
+ .m-dropdown__icon-right,
65
+ .m-dropdown__icon-left {
66
+ display: flex;
67
+ align-items: center;
68
+ }
@@ -0,0 +1,237 @@
1
+ <template>
2
+ <div ref="wrapper" :class="computedWrapperClasses">
3
+ <div ref="referenceRef" :class="computedReferenceClasses" @click="toggleDropdown">
4
+ <slot />
5
+ </div>
6
+
7
+ <DropdownWrapper>
8
+ <Teleport :disabled="isNested" to="body">
9
+ <transition :name="transitionName">
10
+ <div v-show="isOpen" ref="popper" :class="[
11
+ 'm-dropdown-menu',
12
+ `m-dropdown-menu--${placement}`
13
+ ]">
14
+ <slot name="menu" />
15
+ </div>
16
+ </transition>
17
+ </Teleport>
18
+ </DropdownWrapper>
19
+ </div>
20
+ </template>
21
+
22
+ <script lang="ts">
23
+ import { usePopper } from '@/composables/usePopper';
24
+ import type { Placement, VirtualElement } from '@popperjs/core';
25
+ import { defineComponent, inject, ref, type PropType, provide, watch, onMounted, nextTick, computed, Teleport, toRef, onUnmounted } from 'vue';
26
+ import { onClickOutside, useEventListener } from '@vueuse/core';
27
+ import DropdownWrapper from './DropdownWrapper.vue';
28
+ import { useLabel } from './hooks';
29
+
30
+ export default defineComponent({
31
+ name: 'Dropdown',
32
+ components: {
33
+ DropdownWrapper
34
+ },
35
+ props: {
36
+ open: {
37
+ type: Boolean,
38
+ default: false
39
+ },
40
+ placement: {
41
+ type: String as PropType<Placement>,
42
+ default: 'bottom-start'
43
+ },
44
+ transfer: {
45
+ type: Boolean,
46
+ default: false
47
+ },
48
+ trigger: {
49
+ type: String as PropType<'click' | 'hover'>,
50
+ default: 'click'
51
+ },
52
+ virtual: {
53
+ type: [Object],
54
+ default: null
55
+ },
56
+ x: {
57
+ type: Number,
58
+ default: 0
59
+ },
60
+ y: {
61
+ type: Number,
62
+ default: 0
63
+ },
64
+ label: {
65
+ default: null,
66
+ static: true
67
+ },
68
+ transitionName: {
69
+ type: String,
70
+ default: 'fade'
71
+ },
72
+ onSelect: {
73
+ type: Function as PropType<(labels: any) => void>,
74
+ default: () => { }
75
+ }
76
+ },
77
+ emits: [
78
+ 'update:open'
79
+ ],
80
+ setup(props, { emit }) {
81
+ const state = inject("dropdown", null);
82
+
83
+ const isNested = !!state;
84
+ const label = toRef(props, 'label')
85
+ const isOpen = ref(props.open);
86
+ const placement = ref(props.placement);
87
+ const transfer = toRef(props, 'transfer');
88
+ const wrapper = ref(null);
89
+ const referenceRef = ref(null);
90
+
91
+ const reference = computed(() => {
92
+ if (props.virtual !== null) {
93
+ const virtual = (props.virtual as any).$el ?? props.virtual;
94
+ if (virtual) {
95
+ if ('getBoundingClientRect' in virtual) {
96
+ return virtual as VirtualElement;
97
+ }
98
+
99
+ if ('x' in virtual && 'y' in virtual) {
100
+ return {
101
+ getBoundingClientRect: () => ({
102
+ x: virtual.x,
103
+ y: virtual.y,
104
+ left: virtual.x,
105
+ top: virtual.y,
106
+ width: 0,
107
+ height: 0
108
+ })
109
+ } as VirtualElement
110
+ }
111
+ }
112
+ }
113
+
114
+ return referenceRef.value;
115
+ });
116
+
117
+ const { popper, transferTo, updatePopper } = usePopper({
118
+ reference: reference,
119
+ placement,
120
+ transfer,
121
+ wrapper,
122
+ isDrop: true,
123
+ offset: isNested ? [-2, 2] : undefined
124
+ });
125
+
126
+ const currentLabel = useLabel(label, referenceRef)
127
+
128
+ provide("dropdown-select", null);
129
+ provide("dropdown", {
130
+ handleSelect,
131
+ isOpen: isOpen,
132
+ });
133
+
134
+ watch(() => props.open, (value) => {
135
+ isOpen.value = value;
136
+ });
137
+
138
+ watch(() => props.placement, (value) => {
139
+ setPlacement(value);
140
+ });
141
+
142
+ watch(isOpen, (value) => {
143
+ if (value) {
144
+ updatePopper();
145
+ }
146
+
147
+ emit('update:open', value);
148
+ });
149
+
150
+ onMounted(() => {
151
+ nextTick(() => {
152
+ setPlacement(props.placement)
153
+ });
154
+ });
155
+
156
+ useEventListener('resize', () => {
157
+ updatePopper();
158
+ });
159
+
160
+ onClickOutside(wrapper, (e) => {
161
+ let parent = e.target as HTMLElement;
162
+
163
+ // fail safe of 10
164
+ let i = 0;
165
+ while (parent) {
166
+ if (parent.classList.contains('m-dropdown-menu')) {
167
+ return;
168
+ }
169
+ parent = parent.parentElement as HTMLElement;
170
+
171
+ if (i > 10) {
172
+ break;
173
+ }
174
+ i++;
175
+ }
176
+
177
+ isOpen.value = false;
178
+ });
179
+
180
+ function setPlacement(value: Placement) {
181
+ const [xPlacement] = value.split('-');
182
+
183
+ if (isNested && xPlacement !== 'right' && xPlacement !== 'left') {
184
+ placement.value = 'right-start';
185
+ } else {
186
+ placement.value = value;
187
+ }
188
+ }
189
+
190
+ function toggleDropdown() {
191
+ isOpen.value = !isOpen.value;
192
+ }
193
+
194
+ function handleSelect(labels: string[]) {
195
+ isOpen.value = false;
196
+ props.onSelect(labels);
197
+ // @ts-ignore
198
+ if (typeof state?.handleSelect === 'function') {
199
+ // @ts-ignore
200
+ state.handleSelect([currentLabel.value!, ...labels]);
201
+ }
202
+ }
203
+
204
+ const computedWrapperClasses = computed(() => {
205
+ return [
206
+ 'm-dropdown',
207
+ {
208
+ 'm-dropdown--open': isOpen.value,
209
+ 'm-dropdown--nested': isNested
210
+ }
211
+ ]
212
+ });
213
+
214
+ const computedReferenceClasses = computed(() => {
215
+
216
+ });
217
+
218
+ const transitionName = computed(() => {
219
+ return `m-transition__${props.transitionName}`;
220
+ });
221
+
222
+ return {
223
+ wrapper,
224
+ referenceRef,
225
+ popper,
226
+ transferTo,
227
+ isOpen,
228
+ isNested,
229
+ placement,
230
+ computedWrapperClasses,
231
+ computedReferenceClasses,
232
+ transitionName,
233
+ toggleDropdown
234
+ }
235
+ }
236
+ })
237
+ </script>