design-system-next 1.0.21

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.
@@ -0,0 +1,65 @@
1
+ import { computed, ref, ComputedRef } from "vue";
2
+ import { useElementHover } from "@vueuse/core";
3
+ import classNames from "classnames";
4
+ import type { RadioPropTypes } from "./radio";
5
+
6
+ export const useRadioButton = (props: RadioPropTypes) => {
7
+ const radioRef = ref<HTMLInputElement | null>(null);
8
+ const isHovered = useElementHover(radioRef);
9
+
10
+ const radioClasses: ComputedRef<string> = computed(() => {
11
+ const baseClasses = "tw-sr-only tw-peer";
12
+
13
+ if (props.disabled) {
14
+ return classNames(baseClasses, "tw-cursor-default");
15
+ }
16
+
17
+ return baseClasses;
18
+ });
19
+
20
+ const indicatorClasses: ComputedRef<string> = computed(() => {
21
+ console.log(props.disabled);
22
+ const baseClasses =
23
+ "tw-inline-block tw-w-4 tw-h-4 tw-rounded-full tw-border-2 tw-border-solid tw-mr-2";
24
+
25
+ if (props.disabled) {
26
+ return classNames(
27
+ baseClasses,
28
+ props.modelValue === props.value
29
+ ? "tw-border-color-disabled tw-background-color-disabled tw-shadow-[inset_0px_0px_0px_2.5px_#fff] tw-cursor-default"
30
+ : "tw-border-color-disabled tw-background-color tw-cursor-default"
31
+ );
32
+ }
33
+
34
+ if (props.modelValue === props.value) {
35
+ return classNames(
36
+ baseClasses,
37
+ "tw-border-color-brand-base tw-background-color-brand-base tw-shadow-[inset_0px_0px_0px_2.5px_#fff]"
38
+ );
39
+ }
40
+
41
+ if (isHovered.value) {
42
+ return classNames(
43
+ baseClasses,
44
+ "tw-background-color-base tw-border-2 tw-border-color-supporting tw-shadow-[inset_0px_0px_0px_2.5px_#fff]"
45
+ );
46
+ }
47
+
48
+ return classNames(baseClasses, "tw-border-color-supporting tw-shadow-[inset_0px_0px_0px_2.5px_#fff]");
49
+ });
50
+
51
+ const radioLabelClasses: ComputedRef<string> = computed(() => {
52
+ if (props.disabled) {
53
+ return "tw-text-color-disabled tw-cursor-default";
54
+ }
55
+
56
+ return "tw-text-color-strong tw-cursor-pointer";
57
+ });
58
+
59
+ return {
60
+ radioRef,
61
+ radioClasses,
62
+ indicatorClasses,
63
+ radioLabelClasses,
64
+ };
65
+ };
@@ -0,0 +1,43 @@
1
+ import type { PropType, ExtractPropTypes } from 'vue';
2
+
3
+ export const sidenavPropTypes = {
4
+ /**
5
+ * @description Sidenav has quick actions
6
+ */
7
+ hasQuickActions: {
8
+ type: Boolean,
9
+ validator: (value: unknown) => typeof value === 'boolean',
10
+ default: false,
11
+ },
12
+ /**
13
+ * @description Sidenav has search
14
+ */
15
+ hasSearch: {
16
+ type: Boolean,
17
+ validator: (value: unknown) => typeof value === 'boolean',
18
+ default: false,
19
+ },
20
+ /**
21
+ * @description Sidenav active navigation
22
+ */
23
+ activeNav: {
24
+ type: Object as PropType<{ parentNav: string; menu: string; submenu: string }>,
25
+ validator: (value: unknown) => typeof value === 'object',
26
+ default: () => ({ parentNav: '', menu: '', submenu: '' }),
27
+ },
28
+ /**
29
+ * @description Sidenav navlinks
30
+ */
31
+ navLinks: {
32
+ type: Array as PropType<Array<{ parentLinks: Array<unknown> }>>,
33
+ validator: (value: unknown) => Array.isArray(value),
34
+ default: () => [],
35
+ },
36
+ };
37
+
38
+ export const sidenavEmitTypes = {
39
+ 'route-push': String,
40
+ };
41
+
42
+ export type SidenavPropTypes = ExtractPropTypes<typeof sidenavPropTypes>;
43
+ export type SidenavEmitTypes = typeof sidenavEmitTypes;
@@ -0,0 +1,235 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'tw-hidden-scrolls tw-fixed tw-bottom-0 tw-left-0 tw-top-0',
5
+ 'tw-background-color tw-w-auto tw-overflow-y-auto tw-overflow-x-hidden',
6
+ 'tw-border-color-weak tw-border-b-0 tw-border-l-0 tw-border-r tw-border-t-0 tw-border-solid',
7
+ 'tw-transition tw-duration-150 tw-ease-in-out',
8
+ ]"
9
+ >
10
+ <div class="tw-grid tw-h-full tw-grid-rows-[auto_1fr] tw-px-[12px]">
11
+ <div class="tw-hidden-scrolls tw-mt-[16px] tw-overflow-auto">
12
+ <!-- #region - Logo -->
13
+ <div
14
+ :class="[
15
+ {
16
+ 'tw tw-grid tw-justify-center': true,
17
+ '[&>img]:tw-mx-auto [&>img]:tw-h-[24px] [&>img]:tw-w-[24px]': true,
18
+ 'tw-pb-[16px]': !props.hasQuickActions && !props.hasSearch && !props.navLinks,
19
+ },
20
+ ]"
21
+ >
22
+ <slot name="logo-image" />
23
+ </div>
24
+ <!-- #endregion - Logo -->
25
+
26
+ <div class="tw-grid tw-justify-center tw-gap-[8px] tw-pb-[16px] tw-pt-[16px]">
27
+ <!-- #region - Quick Actions -->
28
+ <div
29
+ v-if="props.hasQuickActions"
30
+ :class="[
31
+ 'tw-text-color-brand-base tw-mx-auto tw-h-[32px] tw-cursor-pointer tw-text-[28px] tw-transition tw-duration-150 tw-ease-in-out',
32
+ 'hover:tw-text-color-success-hover',
33
+ 'active:tw-text-color-success-pressed active:tw-scale-90',
34
+ ]"
35
+ >
36
+ <IconPlusCircleFill />
37
+ </div>
38
+ <!-- #endregion - Quick Actions -->
39
+
40
+ <!-- #region - Search -->
41
+ <div
42
+ v-if="props.hasSearch"
43
+ :class="[
44
+ 'justify-center tw-flex tw-cursor-pointer tw-items-center tw-rounded-[8px] tw-p-[8px] tw-transition tw-duration-150 tw-ease-in-out',
45
+ 'hover:tw-background-color-hover',
46
+ 'active:tw-background-color-single-active active:tw-scale-90',
47
+ ]"
48
+ >
49
+ <IconMagnifyingGlass />
50
+ </div>
51
+ <!-- #endregion - Search -->
52
+
53
+ <!-- #region - Grouped Nav Links -->
54
+ <template v-for="(navLink, navLinkIndex) in props.navLinks" :key="navLinkIndex">
55
+ <template v-for="(parentLink, parentLinkIndex) in navLink.parentLinks" :key="parentLinkIndex">
56
+ <!-- #region - Parent link with menu links -->
57
+ <template v-if="parentLink.menuLinks && parentLink.menuLinks.length > 0">
58
+ <Menu
59
+ aria-id="sidenav-menu-wrapper"
60
+ distance="18"
61
+ placement="right"
62
+ :triggers="['click', 'hover']"
63
+ instant-move
64
+ >
65
+ <div
66
+ :class="[
67
+ {
68
+ 'justify-center tw-flex tw-cursor-pointer tw-items-center tw-rounded-[8px] tw-p-[8px] tw-transition tw-duration-150 tw-ease-in-out': true,
69
+ 'tw-background-color-single-active tw-border-color-brand-base tw-border-[1.5px] tw-border-solid active:tw-scale-90':
70
+ props.activeNav.parentNav === parentLink.title,
71
+ 'hover:tw-background-color-hover': props.activeNav.parentNav != parentLink.title,
72
+ 'active:tw-background-color-single-active active:tw-scale-90': true,
73
+ },
74
+ ]"
75
+ >
76
+ <component :is="parentLink.icon" v-if="parentLink.icon" class="tw-h-[1.25em] tw-w-[1.25em]" />
77
+ <IconGlobe v-else />
78
+ </div>
79
+
80
+ <template #popper>
81
+ <div
82
+ class="tw-border-color-weak tw-border-x-0 tw-border-b tw-border-t-0 tw-border-solid tw-p-[8px]"
83
+ >
84
+ <h3 class="tw-body-sm-regular-medium tw-m-0">
85
+ {{ parentLink.title }}
86
+ </h3>
87
+ </div>
88
+
89
+ <template v-for="(menuLink, menuLinkIndex) in parentLink.menuLinks" :key="menuLinkIndex">
90
+ <!-- #region - Menu link with submenu links -->
91
+ <template v-if="menuLink.submenuLinks && menuLink.submenuLinks.length > 0">
92
+ <Menu
93
+ aria-id="sidenav-submenu-wrapper"
94
+ distance="4"
95
+ placement="right-start"
96
+ :triggers="['click', 'hover']"
97
+ instant-move
98
+ >
99
+ <div
100
+ :class="[
101
+ {
102
+ 'tw-body-sm-regular tw-relative tw-m-0 tw-flex tw-cursor-pointer tw-justify-between tw-px-[8px] tw-py-[6px] tw-align-middle tw-duration-150 tw-ease-in-out': true,
103
+ 'tw-background-color-single-active': props.activeNav.menu === menuLink.title,
104
+ 'hover:tw-background-color-hover': props.activeNav.menu !== menuLink.title,
105
+ 'active:tw-background-color-pressed': true,
106
+ },
107
+ ]"
108
+ >
109
+ <div
110
+ v-if="props.activeNav.menu === menuLink.title"
111
+ class="tw-background-color-brand-base tw-absolute tw-left-0 tw-top-0 tw-h-full tw-w-[2px]"
112
+ ></div>
113
+ <span>{{ menuLink.title }}</span>
114
+ <IconCaretRight
115
+ :class="[
116
+ 'tw-h-[16px] tw-w-[16px] tw-transform tw-font-normal tw-transition-transform tw-duration-300',
117
+ props.activeNav.menu === menuLink.title ? '-tw-rotate-90' : 'hover:-tw-rotate-90',
118
+ ]"
119
+ />
120
+ </div>
121
+
122
+ <template #popper>
123
+ <template
124
+ v-for="(submenuLink, submenuLinkIndex) in menuLink.submenuLinks"
125
+ :key="submenuLinkIndex"
126
+ >
127
+ <Menu aria-id="sidenav-sub-submenu-wrapper" :triggers="['click', 'hover']" instant-move>
128
+ <div
129
+ :class="[
130
+ {
131
+ 'tw-body-sm-regular tw-relative tw-m-0 tw-flex tw-cursor-pointer tw-justify-between tw-px-[8px] tw-py-[6px] tw-align-middle tw-duration-150 tw-ease-in-out': true,
132
+ 'tw-background-color-single-active':
133
+ props.activeNav.submenu === submenuLink.title,
134
+ 'hover:tw-background-color-hover': props.activeNav.submenu !== submenuLink.title,
135
+ 'active:tw-background-color-pressed': true,
136
+ },
137
+ ]"
138
+ @click="handleRedirect($event, submenuLink.redirect)"
139
+ >
140
+ <div
141
+ v-show="props.activeNav.submenu === submenuLink.title"
142
+ class="tw-background-color-brand-base tw-absolute tw-left-0 tw-top-0 tw-h-full tw-w-[2px]"
143
+ ></div>
144
+ <span>{{ submenuLink.title }}</span>
145
+ </div>
146
+ </Menu>
147
+ </template>
148
+ </template>
149
+ </Menu>
150
+ </template>
151
+ <!-- #endregion - Menu link with submenu links -->
152
+
153
+ <!-- #region - Menu link only -->
154
+ <template v-else>
155
+ <div
156
+ :class="[
157
+ 'tw-body-sm-regular tw-m-0 tw-flex tw-cursor-pointer tw-justify-between tw-px-[8px] tw-py-[6px] tw-align-middle tw-duration-300 tw-ease-in-out',
158
+ 'hover:tw-background-color-hover',
159
+ 'active:tw-background-color-pressed',
160
+ 'last:tw-rounded-b-[12px]',
161
+ ]"
162
+ @click="handleRedirect($event, menuLink.redirect)"
163
+ >
164
+ <span>{{ menuLink.title }}</span>
165
+ </div>
166
+ </template>
167
+ <!-- #endregion - Menu link only -->
168
+ </template>
169
+ </template>
170
+ </Menu>
171
+ </template>
172
+ <!-- #endregion - Parent link with menu links -->
173
+
174
+ <!-- #region - Parent link only -->
175
+ <template v-else>
176
+ <Tooltip aria-id="default-tooltip" placement="right" distance="18" :triggers="['click']">
177
+ <template #popper>
178
+ <span class="tw-label-xs-medium">{{ parentLink.title }}</span>
179
+ </template>
180
+ <div
181
+ :class="[
182
+ 'justify-center tw-flex tw-cursor-pointer tw-items-center tw-rounded-[8px] tw-p-[8px] tw-transition tw-duration-150 tw-ease-in-out',
183
+ 'hover:tw-background-color-hover',
184
+ 'active:tw-background-color-single-active active:tw-scale-90',
185
+ ]"
186
+ @click="handleRedirect($event, parentLink.redirect)"
187
+ >
188
+ <component :is="parentLink.icon" v-if="parentLink.icon" class="tw-h-[1.25em] tw-w-[1.25em]" />
189
+ <IconGlobe v-else />
190
+ </div>
191
+ </Tooltip>
192
+ </template>
193
+ <!-- #endregion - Parent link only -->
194
+ </template>
195
+ <div
196
+ v-if="navLinks.length > 0 && navLinkIndex < navLinks.length - 1"
197
+ class="tw-background-color-hover tw-h-[2px] tw-w-full"
198
+ ></div>
199
+ </template>
200
+ <!-- #endregion - Grouped Nav Links -->
201
+ </div>
202
+ </div>
203
+
204
+ <!-- #region - Avatar -->
205
+ <div
206
+ :class="[
207
+ 'tw tw-grid tw-items-end tw-justify-center tw-pb-[16px]',
208
+ '[&>img]:tw-mx-auto [&>img]:tw-h-[24px] [&>img]:tw-w-[24px] [&>img]:tw-rounded-full',
209
+ ]"
210
+ >
211
+ <slot name="avatar-image" />
212
+ </div>
213
+ <!-- #endregion - Avatar -->
214
+ </div>
215
+ </div>
216
+ </template>
217
+
218
+ <script lang="ts" setup>
219
+ import { sidenavPropTypes, sidenavEmitTypes } from './sidenav';
220
+ import { useSidenav } from './use-sidenav';
221
+
222
+ import { Menu, Tooltip } from 'floating-vue';
223
+
224
+ import IconPlusCircleFill from '~icons/ph/plus-circle-fill';
225
+ import IconMagnifyingGlass from '~icons/ph/magnifying-glass';
226
+ import IconGlobe from '~icons/ph/globe';
227
+ import IconCaretRight from '~icons/ph/caret-right';
228
+
229
+ import 'floating-vue/dist/style.css';
230
+
231
+ const props = defineProps(sidenavPropTypes);
232
+ const emit = defineEmits(sidenavEmitTypes);
233
+
234
+ const { handleRedirect } = useSidenav(props, emit);
235
+ </script>
@@ -0,0 +1,31 @@
1
+ import type { SetupContext } from 'vue';
2
+
3
+ import type { SidenavPropTypes, SidenavEmitTypes } from './sidenav';
4
+
5
+ interface MouseEventWithCtrl extends MouseEvent {
6
+ ctrlKey: boolean;
7
+ }
8
+
9
+ interface Redirect {
10
+ openInNewTab: boolean;
11
+ isAbsoluteURL: boolean;
12
+ link: string;
13
+ }
14
+
15
+ export const useSidenav = (props: SidenavPropTypes, emit: SetupContext<SidenavEmitTypes>['emit']) => {
16
+ const handleRedirect = (e: MouseEventWithCtrl, redirect: Redirect) => {
17
+ if (redirect) {
18
+ if (redirect.openInNewTab) {
19
+ window.open(redirect.link, '_blank');
20
+ } else if (redirect.isAbsoluteURL) {
21
+ location.href = redirect.link;
22
+ } else {
23
+ emit('route-push', redirect.link);
24
+ }
25
+ }
26
+ };
27
+
28
+ return {
29
+ handleRedirect,
30
+ };
31
+ };
@@ -0,0 +1,35 @@
1
+ import type { PropType, ExtractPropTypes } from 'vue';
2
+
3
+ export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
4
+ const SWITCH_STATES = ['default', 'hover', 'pressed', 'disabled'] as const;
5
+
6
+ export const switchPropTypes = {
7
+ /**
8
+ * @description Switch UI state when hovered, pressed, disabled
9
+ */
10
+ state: {
11
+ type: String as PropType<(typeof SWITCH_STATES)[number]>,
12
+ validator: (value: (typeof SWITCH_STATES)[number]) => SWITCH_STATES.includes(value),
13
+ default: 'default',
14
+ },
15
+ /**
16
+ * @description Switch input state when disabled
17
+ */
18
+ disabled: {
19
+ type: Boolean,
20
+ default: false,
21
+ },
22
+ /**
23
+ * @description Required prop value for v-model
24
+ */
25
+ modelValue: {
26
+ type: Boolean,
27
+ required: true,
28
+ default: false,
29
+ },
30
+ };
31
+
32
+ export const switchEmitTypes = ['update:modelValue'];
33
+
34
+ export type SwitchPropTypes = ExtractPropTypes<typeof switchPropTypes>;
35
+ export type SwitchEmitTypes = typeof switchEmitTypes;
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <div v-bind="switchProps" :class="['switch', switchTextClass]">
3
+ <label class="switch_text switch_left-text">
4
+ <slot name="leftText">
5
+ <slot></slot>
6
+ </slot>
7
+ </label>
8
+ <div ref="switchWrapperRef" class="switch_wrapper" >
9
+ <input
10
+ ref="switchRef"
11
+ v-model="proxyValue"
12
+ type="checkbox"
13
+ name="checkbox"
14
+ :class="['switch_input', switchInputClass]"
15
+ :disabled="props.disabled"
16
+ >
17
+ <span :class="['switch_mark', switchMarkClass]"></span>
18
+ </div>
19
+ <label class="switch_text switch_right-text">
20
+ <slot name="rightText"></slot>
21
+ </label>
22
+ </div>
23
+ </template>
24
+
25
+ <script lang="ts" setup>
26
+ import { switchEmitTypes, switchPropTypes } from './switch';
27
+ import { useSwitch } from './use-switch';
28
+ import { useVModel } from '@vueuse/core';
29
+
30
+ const props = defineProps(switchPropTypes);
31
+ const emit = defineEmits(switchEmitTypes);
32
+
33
+ const proxyValue = useVModel(props, "modelValue", emit);
34
+
35
+ const {
36
+ switchWrapperRef,
37
+ switchRef,
38
+ switchProps,
39
+ switchMarkClass,
40
+ switchTextClass,
41
+ switchInputClass,
42
+ } = useSwitch(props);
43
+ </script>
44
+
45
+ <style lang="scss" scoped>
46
+ .switch {
47
+ display: flex;
48
+ align-items: center;
49
+ width: fit-content;
50
+
51
+ &_left-text {
52
+ margin-right: 8px;
53
+ }
54
+
55
+ &_right-text {
56
+ margin-left: 8px;
57
+ }
58
+
59
+ &_wrapper{
60
+ position: relative;
61
+ display: inline;
62
+ height: 24px;
63
+ }
64
+
65
+ &_input{
66
+ position: absolute;
67
+ top: 0;
68
+ left: 0;
69
+ opacity: 0;
70
+ z-index: 1;
71
+ width: 48px;
72
+ height: 24px;
73
+ margin: 0;
74
+
75
+ &:checked ~ .switch_mark:before{
76
+ left: 28px;
77
+ }
78
+ }
79
+
80
+ &_mark{
81
+ display: inline-block;
82
+ position: relative;
83
+ box-sizing: border-box;
84
+ border-radius: 40px;
85
+ width: 48px;
86
+ height: 24px;
87
+ padding: 4px;
88
+
89
+ &:before,
90
+ &:after{
91
+ content: "";
92
+ position: absolute;
93
+ }
94
+
95
+ &:before{
96
+ @apply tw-bg-white-50;
97
+
98
+ top: 4px;
99
+ left: 4px;
100
+ width: 16px;
101
+ height: 16px;
102
+ border-radius: 50%;
103
+ }
104
+ }
105
+ }
106
+ </style>
@@ -0,0 +1,106 @@
1
+ import { computed, ref, ComputedRef } from 'vue';
2
+ import { useElementHover, useMousePressed } from '@vueuse/core';
3
+ import classNames from 'classnames';
4
+ import type { SwitchPropTypes } from './switch';
5
+
6
+ export const useSwitch = (props: SwitchPropTypes) => {
7
+ const switchWrapperRef = ref<HTMLDivElement | null>(null);
8
+ const switchRef = ref<HTMLInputElement | null>(null);
9
+ const isHovered = useElementHover(switchWrapperRef);
10
+ const { pressed } = useMousePressed({ target: switchRef });
11
+ const { disabled, state, modelValue } = props;
12
+
13
+ const switchProps: ComputedRef<Record<string, unknown>> = computed(() => {
14
+ return {
15
+ ...(disabled && { ariaDisabled: true }),
16
+ disabled: disabled,
17
+ autofocus: state === 'hover',
18
+ modelValue: modelValue,
19
+ };
20
+ });
21
+
22
+ // #region - Background CSS Class
23
+ const switchBackgroundCssClass: ComputedRef<string> = computed(() => {
24
+ if (props.disabled) {
25
+ return getDisabledBackground()
26
+ }
27
+ if (pressed.value) {
28
+ return getPressedBackground();
29
+ }
30
+ if (isHovered.value) {
31
+ return getHoveredBackground();
32
+ }
33
+
34
+ return getDefaultBackground();
35
+ });
36
+
37
+ function getDefaultBackground(): string {
38
+ return props.modelValue ?
39
+ 'tw-background-color-success-base' :
40
+ 'tw-switch-background-default';
41
+ }
42
+
43
+ function getHoveredBackground(): string {
44
+ return props.modelValue ?
45
+ 'tw-background-color-success-hover' :
46
+ 'tw-switch-background-hover';
47
+ }
48
+
49
+ function getPressedBackground(): string {
50
+ return props.modelValue ?
51
+ 'tw-background-color-success-pressed' :
52
+ 'tw-switch-background-pressed';
53
+ }
54
+
55
+ function getDisabledBackground(): string {
56
+ return classNames(
57
+ {
58
+ 'tw-background-color-success-base' : props.modelValue,
59
+ 'tw-switch-background-default' : !props.modelValue
60
+ },
61
+ 'tw-opacity-60',
62
+ );
63
+ }
64
+ // #endregion - Background CSS Class
65
+
66
+ const switchTextClass: ComputedRef<string> = computed(() => {
67
+ if (props.disabled) {
68
+ return 'tw-text-color-disabled';
69
+ }
70
+
71
+ return 'tw-text-color-strong';
72
+ });
73
+
74
+ const switchAnimationCssClass: ComputedRef<string> = computed(() => {
75
+ return classNames(
76
+ 'tw-transition-colors',
77
+ 'before:tw-transition-all',
78
+ 'before:tw-duration-150',
79
+ 'after:tw-transition-all',
80
+ 'after:tw-duration-150',
81
+ );
82
+ });
83
+
84
+ const switchMarkClass: ComputedRef<string> = computed(() => {
85
+ return classNames(
86
+ switchBackgroundCssClass.value,
87
+ switchAnimationCssClass.value
88
+ );
89
+ });
90
+
91
+ const switchInputClass: ComputedRef<string> = computed(() => {
92
+ return classNames({
93
+ 'tw-cursor-not-allowed': props.disabled,
94
+ 'tw-cursor-pointer': !props.disabled,
95
+ });
96
+ });
97
+
98
+ return {
99
+ switchWrapperRef,
100
+ switchRef,
101
+ switchProps,
102
+ switchMarkClass,
103
+ switchTextClass,
104
+ switchInputClass
105
+ };
106
+ };
package/src/main.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { createApp } from 'vue';
2
+
3
+ import App from '@/App.vue';
4
+
5
+ import '@/assets/styles/tailwind.css';
6
+
7
+ import FloatingVue from 'floating-vue';
8
+
9
+ const app = createApp(App);
10
+
11
+ app.use(FloatingVue);
12
+
13
+ app.mount('#app');