@teammodlr/ui-kit 1.0.2

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 (129) hide show
  1. package/env.d.ts +1 -0
  2. package/lib/__storybook__/argTypes.d.ts +31 -0
  3. package/lib/__storybook__/createStory.d.ts +8 -0
  4. package/lib/__storybook__/index.d.ts +2 -0
  5. package/lib/components/avatar/Avatar.vue.d.ts +70 -0
  6. package/lib/components/avatargroup/AvatarGroup.vue.d.ts +34 -0
  7. package/lib/components/breadcrumb/Breadcrumb.vue.d.ts +25 -0
  8. package/lib/components/breadcrumbitem/BreadcrumbItem.vue.d.ts +18 -0
  9. package/lib/components/breadcrumbitem/index.d.ts +1 -0
  10. package/lib/components/button/Button.vue.d.ts +121 -0
  11. package/lib/components/buttongroup/ButtonGroup.vue.d.ts +29 -0
  12. package/lib/components/checkbox/Checkbox.vue.d.ts +97 -0
  13. package/lib/components/dropdown/Dropdown.vue.d.ts +114 -0
  14. package/lib/components/dropdown/DropdownItem.vue.d.ts +61 -0
  15. package/lib/components/dropdown/DropdownList.vue.d.ts +2 -0
  16. package/lib/components/dropdown/DropdownWrapper.vue.d.ts +2 -0
  17. package/lib/components/dropdown/hooks.d.ts +2 -0
  18. package/lib/components/form/Form.vue.d.ts +67 -0
  19. package/lib/components/image/Image.vue.d.ts +50 -0
  20. package/lib/components/index.d.ts +26 -0
  21. package/lib/components/input/Input.vue.d.ts +126 -0
  22. package/lib/components/link/Link.vue.d.ts +51 -0
  23. package/lib/components/messageprovider/Message.vue.d.ts +61 -0
  24. package/lib/components/messageprovider/MessageProvider.vue.d.ts +51 -0
  25. package/lib/components/messageprovider/types.d.ts +28 -0
  26. package/lib/components/messageprovider/useMessage.d.ts +2 -0
  27. package/lib/components/select/Select.vue.d.ts +180 -0
  28. package/lib/components/skeleton/Skeleton.vue.d.ts +39 -0
  29. package/lib/components/tab/Tab.vue.d.ts +41 -0
  30. package/lib/components/tab/TabGroup.vue.d.ts +43 -0
  31. package/lib/components/table/Table.vue.d.ts +63 -0
  32. package/lib/components/table/TableBody.vue.d.ts +2 -0
  33. package/lib/components/table/TableCell.vue.d.ts +2 -0
  34. package/lib/components/table/TableHead.vue.d.ts +20 -0
  35. package/lib/components/table/TableHeadRow.vue.d.ts +2 -0
  36. package/lib/components/table/TableRow.vue.d.ts +2 -0
  37. package/lib/components/table/types/index.d.ts +5 -0
  38. package/lib/components/tooltip/Tooltip.vue.d.ts +54 -0
  39. package/lib/components/virtualscroll/VirtualScroll.vue.d.ts +43 -0
  40. package/lib/composables/element.d.ts +2 -0
  41. package/lib/composables/useCachedUid.d.ts +13 -0
  42. package/lib/composables/useEventListener.d.ts +1 -0
  43. package/lib/composables/useInput.d.ts +36 -0
  44. package/lib/composables/useIsServer.d.ts +1 -0
  45. package/lib/composables/useMount.d.ts +13 -0
  46. package/lib/composables/usePopper.d.ts +31 -0
  47. package/lib/create.d.ts +9 -0
  48. package/lib/index.cjs.js +5 -0
  49. package/lib/index.d.ts +4 -0
  50. package/lib/index.es.js +3164 -0
  51. package/lib/install.d.ts +4 -0
  52. package/lib/nuxt.js +33 -0
  53. package/lib/nuxt.plugin.js +8 -0
  54. package/lib/style.css +1 -0
  55. package/lib/types/props.d.ts +2 -0
  56. package/package.json +75 -0
  57. package/src/__storybook__/argTypes.ts +42 -0
  58. package/src/__storybook__/createStory.ts +15 -0
  59. package/src/__storybook__/index.ts +2 -0
  60. package/src/components/avatar/Avatar.css +69 -0
  61. package/src/components/avatar/Avatar.vue +102 -0
  62. package/src/components/avatargroup/AvatarGroup.css +8 -0
  63. package/src/components/avatargroup/AvatarGroup.vue +32 -0
  64. package/src/components/breadcrumb/Breadcrumb.css +46 -0
  65. package/src/components/breadcrumb/Breadcrumb.vue +51 -0
  66. package/src/components/breadcrumbitem/BreadcrumbItem.vue +66 -0
  67. package/src/components/breadcrumbitem/index.ts +1 -0
  68. package/src/components/button/Button.css +378 -0
  69. package/src/components/button/Button.vue +108 -0
  70. package/src/components/buttongroup/ButtonGroup.vue +47 -0
  71. package/src/components/checkbox/Checkbox.css +185 -0
  72. package/src/components/checkbox/Checkbox.vue +140 -0
  73. package/src/components/dropdown/Dropdown.css +68 -0
  74. package/src/components/dropdown/Dropdown.vue +237 -0
  75. package/src/components/dropdown/DropdownItem.vue +118 -0
  76. package/src/components/dropdown/DropdownList.vue +20 -0
  77. package/src/components/dropdown/DropdownWrapper.vue +17 -0
  78. package/src/components/dropdown/hooks.ts +30 -0
  79. package/src/components/form/Form.vue +141 -0
  80. package/src/components/image/Image.vue +71 -0
  81. package/src/components/index.ts +27 -0
  82. package/src/components/input/Input.css +173 -0
  83. package/src/components/input/Input.vue +176 -0
  84. package/src/components/link/Link.css +147 -0
  85. package/src/components/link/Link.vue +54 -0
  86. package/src/components/messageprovider/Message.vue +76 -0
  87. package/src/components/messageprovider/MessageProvider.css +36 -0
  88. package/src/components/messageprovider/MessageProvider.vue +142 -0
  89. package/src/components/messageprovider/types.ts +39 -0
  90. package/src/components/messageprovider/useMessage.ts +12 -0
  91. package/src/components/select/Select.css +226 -0
  92. package/src/components/select/Select.vue +474 -0
  93. package/src/components/skeleton/Skeleton.css +73 -0
  94. package/src/components/skeleton/Skeleton.vue +47 -0
  95. package/src/components/tab/Tab.css +113 -0
  96. package/src/components/tab/Tab.vue +98 -0
  97. package/src/components/tab/TabGroup.vue +90 -0
  98. package/src/components/table/Table.css +45 -0
  99. package/src/components/table/Table.vue +108 -0
  100. package/src/components/table/TableBody.vue +13 -0
  101. package/src/components/table/TableCell.vue +13 -0
  102. package/src/components/table/TableHead.vue +22 -0
  103. package/src/components/table/TableHeadRow.vue +15 -0
  104. package/src/components/table/TableRow.vue +13 -0
  105. package/src/components/table/types/index.ts +5 -0
  106. package/src/components/tooltip/Tooltip.css +49 -0
  107. package/src/components/tooltip/Tooltip.vue +85 -0
  108. package/src/components/virtualscroll/VirtualScroll.css +13 -0
  109. package/src/components/virtualscroll/VirtualScroll.vue +61 -0
  110. package/src/composables/element.ts +13 -0
  111. package/src/composables/useCachedUid.ts +48 -0
  112. package/src/composables/useEventListener.ts +14 -0
  113. package/src/composables/useInput.ts +134 -0
  114. package/src/composables/useIsServer.ts +7 -0
  115. package/src/composables/useMount.ts +43 -0
  116. package/src/composables/usePopper.ts +194 -0
  117. package/src/create.ts +34 -0
  118. package/src/css/components.css +14 -0
  119. package/src/css/index.css +4 -0
  120. package/src/css/normalize.css +379 -0
  121. package/src/css/transitions.css +40 -0
  122. package/src/css/variables.css +80 -0
  123. package/src/exports/nuxt.js +33 -0
  124. package/src/exports/nuxt.plugin.js +8 -0
  125. package/src/index.ts +5 -0
  126. package/src/install.ts +8 -0
  127. package/src/shims-vue.d.ts +6 -0
  128. package/src/types/props.ts +3 -0
  129. package/volar.d.ts +16 -0
@@ -0,0 +1,49 @@
1
+ .m-tooltip--wrapper {
2
+ position: relative;
3
+ display: inline-block;
4
+ cursor: pointer;
5
+ }
6
+
7
+ .m-tooltip {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ .m-tooltip.m-tooltip--reverse .m-tooltip--content {
12
+ background-color: rgba(255, 255, 255, 0.9);
13
+ color: #000;
14
+ }
15
+
16
+ .m-tooltip .m-tooltip--content {
17
+ transition: opacity 0.3s ease;
18
+ font-size: 12px;
19
+ font-weight: normal;
20
+ color: #fff;
21
+ background-color: rgba(0, 0, 0, 0.75);
22
+ border-radius: 4px;
23
+ padding: 5px 10px;
24
+ pointer-events: none;
25
+ width: max-content;
26
+ max-width: 300px;
27
+ }
28
+
29
+ .m-tooltip .tooltip--arrow {
30
+ position: absolute;
31
+ color: rgba(0, 0, 0, 0.75);
32
+ width: 10px;
33
+ }
34
+
35
+ .m-tooltip[data-popper-placement^=top] {
36
+ padding: 5px 0 14px;
37
+ }
38
+
39
+ .m-tooltip[data-popper-placement^=bottom] {
40
+ padding: 14px 0 5px;
41
+ }
42
+
43
+ .m-tooltip[data-popper-placement^=left] {
44
+ padding: 0 8px 0 5px;
45
+ }
46
+
47
+ .m-tooltip[data-popper-placement^=right] {
48
+ padding: 0 5px 0 8px;
49
+ }
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <span @mouseenter="isOpen = true" @mouseleave="isOpen = false" ref="reference">
3
+ <slot name="trigger" />
4
+ <Teleport :to="'body'">
5
+ <Transition :name="transitionName">
6
+ <div ref="popper" v-show="isOpen" :class="computedClasses">
7
+ <span class="m-tooltip--content">
8
+ <slot />
9
+ </span>
10
+ </div>
11
+ </Transition>
12
+ </Teleport>
13
+ </span>
14
+ </template>
15
+
16
+ <script lang="ts">
17
+ import { defineComponent, ref, toRef, type PropType, computed, watch } from 'vue';
18
+ import { type Placement, placementWhileList, usePopper } from "../../composables/usePopper";
19
+
20
+ export default defineComponent({
21
+ name: "MTooltip",
22
+ props: {
23
+ placement: {
24
+ type: String as PropType<Placement>,
25
+ default: 'top',
26
+ validator: (value: Placement) => placementWhileList.includes(value)
27
+ },
28
+ transitionName: {
29
+ type: String,
30
+ default: 'scale'
31
+ },
32
+ reverse: {
33
+ type: Boolean,
34
+ default: false
35
+ },
36
+ transfer: {
37
+ type: Boolean,
38
+ default: false
39
+ }
40
+ },
41
+ setup(props, { emit }) {
42
+ const placement = ref(props.placement);
43
+ // @ts-ignore
44
+ const transfer = toRef(props, 'transfer');
45
+ const wrapper = ref<HTMLElement | null>(null);
46
+ const reference = ref<HTMLElement | null>(null);
47
+ const isOpen = ref(false);
48
+
49
+ const { popper, transferTo, updatePopper } = usePopper({
50
+ placement,
51
+ transfer,
52
+ reference,
53
+ wrapper
54
+ });
55
+
56
+ const transitionName = computed(() => {
57
+ return `m-transition__${props.transitionName}`;
58
+ });
59
+
60
+ const computedClasses = computed(() => {
61
+ return [
62
+ 'm-tooltip',
63
+ {
64
+ 'm-tooltip--reverse': props.reverse
65
+ }
66
+ ]
67
+ });
68
+
69
+ watch(() => isOpen.value, (value) => {
70
+ if (value) {
71
+ updatePopper();
72
+ }
73
+ });
74
+
75
+ return {
76
+ reference,
77
+ popper,
78
+ transferTo,
79
+ transitionName,
80
+ isOpen,
81
+ computedClasses
82
+ }
83
+ }
84
+ })
85
+ </script>
@@ -0,0 +1,13 @@
1
+ .virtual-scroller {
2
+ overflow: auto;
3
+ will-change: transform;
4
+ }
5
+
6
+ .virtual-scroller>div {
7
+ overflow: hidden;
8
+ will-change: transform;
9
+ }
10
+
11
+ .virtual-scroller>div>div {
12
+ will-change: transform;
13
+ }
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <div ref="scroller" class="m-virtual-scroller" @scroll="onScroll">
3
+ <div :style="{ height: `${totalHeight}px` }">
4
+ <div :style="{ transform: `translateY(${offsetY}px)` }">
5
+ <template v-for="item in renderedItems">
6
+ <slot :item="item" :height="itemHeight"></slot>
7
+ </template>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script lang="ts">
14
+ export default {
15
+ name: "MVirtualScroller"
16
+ }
17
+ </script>
18
+
19
+ <script lang="ts" setup>
20
+ import { computed, onMounted, ref, toRefs } from 'vue';
21
+ const props = defineProps({
22
+ items: {
23
+ required: true,
24
+ type: Array,
25
+ },
26
+ itemHeight: {
27
+ required: true,
28
+ type: Number,
29
+ default: 35,
30
+ },
31
+ buffer: {
32
+ default: 10,
33
+ type: Number,
34
+ }
35
+ });
36
+ const { items, itemHeight, buffer } = toRefs(props);
37
+ const scroller = ref();
38
+ const scrollerHeight = ref(0);
39
+ const scrollTop = ref(0);
40
+ const totalHeight = computed(() => items.value.length * itemHeight.value);
41
+ const startPosition = computed(() =>
42
+ Math.max(0, Math.floor(scrollTop.value / itemHeight.value) - buffer.value)
43
+ );
44
+ const offsetY = computed(() => startPosition.value * itemHeight.value);
45
+ const renderedItems = computed(() => {
46
+ let count =
47
+ Math.ceil(scrollerHeight.value / itemHeight.value) + 2 * buffer.value;
48
+ count = Math.min(items.value.length - startPosition.value, count);
49
+ return items.value.slice(startPosition.value, startPosition.value + count);
50
+ });
51
+ const onScroll = (e: Event) => {
52
+ requestAnimationFrame(() => (scrollTop.value = (e.target as HTMLElement).scrollTop));
53
+ };
54
+ const observer = new ResizeObserver((entries) =>
55
+ entries.forEach((el) => (scrollerHeight.value = el.contentRect.height))
56
+ );
57
+ onMounted(() => {
58
+ observer.observe(scroller.value);
59
+ scrollerHeight.value = scroller.value.offsetHeight;
60
+ });
61
+ </script>
@@ -0,0 +1,13 @@
1
+ import type { Ref } from "vue";
2
+
3
+ export function getElement(ref: Ref) {
4
+ if(ref === null) {
5
+ return null;
6
+ }
7
+
8
+ if(ref.value === null) {
9
+ return null;
10
+ }
11
+
12
+ return "$el" in ref.value ? ref.value.$el : ref.value;
13
+ }
@@ -0,0 +1,48 @@
1
+ // https://github.com/liip/chusho/blob/main/packages/chusho/lib/composables/useCachedUid.ts#L20-L41 - Legend :D
2
+ import { onMounted, readonly, ref } from 'vue';
3
+ import { getElement } from './element';
4
+ import type { ComponentPublicInstance, Ref } from 'vue';
5
+
6
+ const UID_CACHE_ATTR = 'data-modlr-ssr-uid';
7
+
8
+ // @ts-nocheck
9
+ type CacheElement = HTMLElement | SVGElement | ComponentPublicInstance | null;
10
+
11
+ export function uid(prefix: string = ''): string {
12
+ const id = Date.now().toString(36) + Math.random().toString(36).substring(2);
13
+ return prefix ? `${prefix}-${id}` : id;
14
+ }
15
+
16
+ export interface IUseCacheUid {
17
+ id: Readonly<Ref<string>>;
18
+ cacheAttrs: {
19
+ ref: Ref<CacheElement>;
20
+ [UID_CACHE_ATTR]?: string;
21
+ };
22
+ }
23
+
24
+ export function useCachedUid(prefix?: string): IUseCacheUid {
25
+ let id = ref(uid(prefix));
26
+ const cacheElement = ref<CacheElement>(null);
27
+
28
+ onMounted(() => {
29
+ if(cacheElement.value) {
30
+ const element = getElement(cacheElement);
31
+ const serverId = element?.getAttribute(UID_CACHE_ATTR);
32
+
33
+ if(serverId) {
34
+ id.value = serverId;
35
+ }
36
+
37
+ element?.removeAttribute(UID_CACHE_ATTR);
38
+ }
39
+ });
40
+
41
+ return {
42
+ id: readonly(id),
43
+ cacheAttrs: {
44
+ ref: cacheElement,
45
+ [UID_CACHE_ATTR]: typeof window === undefined ? id.value : undefined
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,14 @@
1
+ import { watchEffect } from "vue";
2
+
3
+ export function useEventListener<T extends EventTarget | HTMLElement, E extends EventListener>(element: T, type: string, listener: E, options: boolean) {
4
+ if(typeof window === undefined) {
5
+ return;
6
+ }
7
+
8
+ watchEffect((onInvalidate) => {
9
+ element = element ?? window;
10
+
11
+ element.addEventListener(type, listener, options);
12
+ onInvalidate(() => element.removeEventListener(type, listener, options));
13
+ })
14
+ }
@@ -0,0 +1,134 @@
1
+ // inspired by indielayer ui
2
+ import type { PropType } from 'vue';
3
+ import { ref, watch, inject, computed, onMounted, onUnmounted } from 'vue';
4
+ import { useCachedUid } from './useCachedUid';
5
+
6
+ export interface FormInput {
7
+ focus: () => void;
8
+ validate: (value: any) => void;
9
+ setError: (value: string) => void;
10
+ }
11
+
12
+ export const useInput = (props: any, { focus, emit, withListeners = true }: {
13
+ focus: () => void,
14
+ emit: any,
15
+ withListeners?: boolean
16
+ }) => {
17
+ const isFirstValidation = ref(true);
18
+ const formError = ref(props.error);
19
+
20
+ const { id } = useCachedUid('input');
21
+ const name = props.name ? props.name : id.value;
22
+ const _name = ref(name);
23
+
24
+ watch(() => props.error, (value) => formError.value = value);
25
+ watch(() => props.name, (value) => name.value = value);
26
+
27
+ const form = inject('m-form', {
28
+ add: (inputName: string, methods: FormInput) => {},
29
+ remove: (inputName: string) => {},
30
+ insideForm: false,
31
+ disabled: false,
32
+ loading: false
33
+ });
34
+
35
+ const reset = () => {
36
+ formError.value = '';
37
+ isFirstValidation.value = true;
38
+ emit('update:modelValue', '');
39
+ };
40
+
41
+ const setError = (value: string) => {
42
+ formError.value = value;
43
+ };
44
+
45
+ const validate = (value: any): boolean => {
46
+ value = value || props.modelValue;
47
+
48
+ isFirstValidation.value = false;
49
+
50
+ for (let i = 0; i < props.rules.length; i++) {
51
+ const item = props.rules[i];
52
+
53
+ let valid = true;
54
+
55
+ if (typeof item === 'function') {
56
+ valid = item(value);
57
+ } else if(Array.isArray(item) && item.length === 2) {
58
+ const { 0: rule, 1: options } = item;
59
+ valid = rule(value, options);
60
+ } else {
61
+ const rule = item.fn;
62
+ const { options } = item;
63
+
64
+ valid = rule(value, options);
65
+ }
66
+
67
+ if (valid !== true) {
68
+ formError.value = valid;
69
+ return false;
70
+ }
71
+ }
72
+
73
+ formError.value = '';
74
+ return true;
75
+ };
76
+
77
+ const listeners = withListeners ? computed(() => {
78
+ return {
79
+ blur: (event: Event) => emit('blur', event),
80
+ focus: (event: Event) => emit('focus', event),
81
+ input: (event: Event) => {
82
+ emit('update:modelValue', (event.target as HTMLInputElement).value);
83
+ },
84
+ change: (event: Event) => emit('change', event)
85
+ }
86
+ }) : {};
87
+
88
+ onMounted(() => {
89
+ form.add(_name.value,
90
+ focus,
91
+ // @ts-ignore
92
+ validate,
93
+ // @ts-ignore
94
+ setError
95
+ );
96
+ })
97
+
98
+ onUnmounted(() => {
99
+ form.remove(_name.value);
100
+ });
101
+
102
+ return {
103
+ id,
104
+ isFirstValidation,
105
+ formError,
106
+ insideForm: form.insideForm,
107
+ listeners,
108
+ reset,
109
+ validate,
110
+ setError,
111
+ //@ts-ignore
112
+ formDisabled: form.disabled.value,
113
+ //@ts-ignore
114
+ formLoading: form.loading.value
115
+ }
116
+ };
117
+
118
+ useInput.emits = (withListeners: boolean = true): string[] => {
119
+ return withListeners ?
120
+ ['update:modelValue', 'blur', 'focus', 'input', 'change'] :
121
+ ['update:modelValue'];
122
+ }
123
+
124
+ useInput.props = () => ({
125
+ modelValue: [String, Number, Boolean, Object, Array] as PropType<string | number | boolean | object | any[] | undefined>,
126
+ name: String,
127
+ readonly: Boolean,
128
+ required: Boolean,
129
+ error: String,
130
+ rules: {
131
+ type: Array,
132
+ default: () => [],
133
+ }
134
+ } as const);
@@ -0,0 +1,7 @@
1
+ import { ref } from 'vue';
2
+
3
+ export function useIsServer() {
4
+ const isServer = ref(typeof window === 'undefined');
5
+
6
+ return isServer;
7
+ }
@@ -0,0 +1,43 @@
1
+ import { createVNode, render } from "vue";
2
+ import type { App, Component } from "vue";
3
+
4
+ export function mount(
5
+ component: Component,
6
+ {
7
+ props,
8
+ children,
9
+ element,
10
+ app
11
+ }: {
12
+ props?: Record<string, unknown>,
13
+ children?: unknown,
14
+ app?: App,
15
+ element?: HTMLElement
16
+ } = {}
17
+ ) {
18
+ let el = element;
19
+
20
+ const vNode = createVNode(component, props, children);
21
+
22
+ if (app && app._context) {
23
+ vNode.appContext = app._context;
24
+ }
25
+
26
+ if (el) {
27
+ render(vNode, el);
28
+ } else if(typeof document !== 'undefined') {
29
+ render(vNode, (el = document.createElement('div')))
30
+ }
31
+
32
+ const destroy = () => {
33
+ if (el) {
34
+ render(null, el);
35
+ }
36
+ }
37
+
38
+ return {
39
+ el,
40
+ vNode,
41
+ destroy,
42
+ }
43
+ }
@@ -0,0 +1,194 @@
1
+ import { ref, watch, nextTick, onMounted, onBeforeUnmount, watchEffect } from "vue";
2
+ import { createPopper as createInternalPopper } from '@popperjs/core';
3
+
4
+ import type { Ref, WatchStopHandle } from "vue";
5
+ import type { Placement, Modifier, Instance, Rect, VirtualElement } from '@popperjs/core';
6
+
7
+ type OffsetsFunction = (options: {
8
+ popper: Rect,
9
+ reference: Rect,
10
+ placement: Placement
11
+ }) => [number?, number?];
12
+
13
+ interface UsePopperOptions {
14
+ placement: Ref<Placement>,
15
+ transfer: Ref<boolean | string>,
16
+ wrapper: Ref<HTMLElement | null | undefined>,
17
+ isDrop?: boolean,
18
+ reference?: Ref<HTMLElement | VirtualElement | null | undefined>,
19
+ popper?: Ref<HTMLElement | null | undefined>,
20
+ offset?: OffsetsFunction | [number?, number?]
21
+ }
22
+
23
+ export type { Placement, VirtualElement };
24
+
25
+ export const placementWhileList = Object.freeze([
26
+ 'auto',
27
+ 'auto-start',
28
+ 'auto-end',
29
+ 'top',
30
+ 'top-start',
31
+ 'top-end',
32
+ 'bottom',
33
+ 'bottom-start',
34
+ 'bottom-end',
35
+ 'left',
36
+ 'left-start',
37
+ 'left-end',
38
+ 'right',
39
+ 'right-start',
40
+ 'right-end'
41
+ ] as Placement[])
42
+
43
+ export interface TransferNode extends Node {
44
+ __transferNode?: Node;
45
+ __transferElement?: Node;
46
+ }
47
+
48
+ export function usePopper(initialOptions: UsePopperOptions) {
49
+ const { placement, transfer, wrapper, isDrop = false } = initialOptions;
50
+
51
+ const reference: Ref<HTMLElement | null | undefined> = (initialOptions.reference as any) ?? ref(null);
52
+ const popper: Ref<HTMLElement | null | undefined> = initialOptions.popper ?? ref(null);
53
+ const transferTo: Ref<String> = ref('');
54
+
55
+ const options: {
56
+ placement: Placement,
57
+ modifiers: Partial<Modifier<any, any>>[]
58
+ } = {
59
+ placement: placement.value,
60
+ modifiers: [
61
+ {
62
+ name: 'computeStyles',
63
+ options: {
64
+ gpuAcceleration: false
65
+ }
66
+ },
67
+ {
68
+ name: 'preventOverflow',
69
+ options: {
70
+ rootBoundary: 'window'
71
+ }
72
+ }
73
+ ]
74
+ };
75
+
76
+ if (isDrop) {
77
+ options.modifiers.push({
78
+ name: 'setTransformOrigin',
79
+ enabled: true,
80
+ phase: 'afterWrite',
81
+ fn({ state }) {
82
+ const origin = setPopperDropOrigin(state.placement);
83
+
84
+ if (origin) {
85
+ state.elements.popper.style.transformOrigin = origin;
86
+ }
87
+ }
88
+ })
89
+ }
90
+
91
+ if (initialOptions.offset) {
92
+ options.modifiers.push({
93
+ name: 'offset',
94
+ options: {
95
+ offset: initialOptions.offset
96
+ }
97
+ })
98
+ }
99
+
100
+ let popperInstance: Instance | null = null;
101
+
102
+ watch(placement, value => {
103
+ options.placement = value;
104
+ popperInstance && popperInstance?.setOptions({ placement: value });
105
+ });
106
+
107
+ let parentWidth = ref(0);
108
+
109
+ watchEffect(() => {
110
+ if (wrapper.value && popper.value) {
111
+ (wrapper.value as TransferNode).__transferElement = popper.value;
112
+ }
113
+ });
114
+
115
+ let stopWatchPopper: WatchStopHandle | null = null;
116
+
117
+ if (transfer) {
118
+ watch(transfer, value => {
119
+ setTransformTo(value);
120
+ updatePopper();
121
+ })
122
+ }
123
+
124
+ onMounted(() => {
125
+ nextTick(createPopper);
126
+ });
127
+
128
+ onBeforeUnmount(destroyPopper);
129
+
130
+ function updatePopper() {
131
+ nextTick(() => {
132
+ popperInstance && popperInstance.forceUpdate()
133
+ if(wrapper.value) {
134
+ parentWidth.value = wrapper.value.clientWidth;
135
+ }
136
+ })
137
+ }
138
+
139
+ function createPopper() {
140
+ destroyPopper();
141
+ popperInstance && popperInstance.destroy();
142
+ createPopperInstance();
143
+
144
+ const cancelWatchReference = watch(reference, createPopperInstance);
145
+ const cancelWatchPopper = watch(popper, createPopperInstance);
146
+
147
+ stopWatchPopper = () => {
148
+ cancelWatchReference()
149
+ cancelWatchPopper()
150
+ }
151
+ }
152
+
153
+ function setTransformTo(value: boolean | string) {
154
+ transferTo.value = typeof value === 'boolean' ? (value ? 'body' : '') : value;
155
+ }
156
+
157
+ function createPopperInstance() {
158
+ if (reference.value && popper.value) {
159
+ popperInstance && popperInstance.destroy()
160
+ popperInstance = createInternalPopper(reference.value, popper.value as HTMLElement, options);
161
+ updatePopper();
162
+ }
163
+ }
164
+
165
+ function destroyPopper() {
166
+ popperInstance && popperInstance.destroy();
167
+ popperInstance = null;
168
+
169
+ if (typeof stopWatchPopper === 'function') {
170
+ stopWatchPopper();
171
+ stopWatchPopper = null;
172
+ }
173
+ }
174
+
175
+ return {
176
+ reference,
177
+ popper,
178
+ transferTo,
179
+ updatePopper,
180
+ createPopper,
181
+ destroyPopper,
182
+ width: parentWidth
183
+ };
184
+ }
185
+
186
+ function setPopperDropOrigin(placement: Placement) {
187
+ if (placement !== 'left' && placement !== 'right') {
188
+ const [placementStart, placementEnd] = placement.split('-')
189
+
190
+ return placementStart === 'bottom' || (placementStart !== 'top' && placementEnd === 'start')
191
+ ? 'center top'
192
+ : 'center bottom'
193
+ }
194
+ }
package/src/create.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { App } from 'vue';
2
+
3
+ export type ModlrUIOptions = {
4
+ prefix?: string;
5
+ components?: any;
6
+ }
7
+
8
+ const defaultOptions: ModlrUIOptions = {
9
+ prefix: 'M'
10
+ };
11
+
12
+ const create = (createOptions: ModlrUIOptions = {}) => {
13
+ const install = (app: App, installOptions: ModlrUIOptions = {}) => {
14
+ const options = {
15
+ ...defaultOptions,
16
+ ...createOptions,
17
+ ...installOptions
18
+ };
19
+
20
+ if (options.components) {
21
+ options.components.forEach((component: any) => {
22
+ const name = component.name.startsWith('M') ? component.name.slice(1) : component.name
23
+
24
+ app.component(`${options.prefix}${name}`, component)
25
+ });
26
+ }
27
+ };
28
+
29
+ return {
30
+ install
31
+ }
32
+ }
33
+
34
+ export default create;