@indielayer/ui 1.0.0-alpha.0 → 1.0.0-alpha.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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -72
  3. package/lib/components/avatar/Avatar.vue.d.ts +2 -2
  4. package/lib/components/badge/Badge.vue.d.ts +2 -2
  5. package/lib/components/button/Button.vue.d.ts +2 -2
  6. package/lib/components/button/ButtonGroup.vue.d.ts +2 -2
  7. package/lib/components/checkbox/Checkbox.vue.d.ts +4 -3
  8. package/lib/components/drawer/Drawer.vue.d.ts +2 -2
  9. package/lib/components/icon/Icon.vue.d.ts +7 -3
  10. package/lib/components/index.d.ts +2 -2
  11. package/lib/components/input/Input.vue.d.ts +3 -2
  12. package/lib/components/menu/Menu.vue.d.ts +2 -2
  13. package/lib/components/menu/MenuItem.vue.d.ts +3 -3
  14. package/lib/components/notifications/Notifications.vue.d.ts +2 -2
  15. package/lib/components/pagination/Pagination.vue.d.ts +3 -2
  16. package/lib/components/pagination/PaginationItem.vue.d.ts +2 -2
  17. package/lib/components/radio/Radio.vue.d.ts +2 -2
  18. package/lib/components/select/Select.vue.d.ts +3 -2
  19. package/lib/components/slider/Slider.vue.d.ts +2 -2
  20. package/lib/components/spacer/Spacer.vue.d.ts +1 -1
  21. package/lib/components/spinner/Spinner.vue.d.ts +2 -2
  22. package/lib/components/{tabs → tab}/Tab.vue.d.ts +2 -2
  23. package/lib/components/{tabs/Tabs.vue.d.ts → tab/TabGroup.vue.d.ts} +0 -0
  24. package/lib/components/table/TableBody.vue.d.ts +1 -1
  25. package/lib/components/table/TableHead.vue.d.ts +1 -1
  26. package/lib/components/tag/Tag.vue.d.ts +2 -2
  27. package/lib/components/textarea/Textarea.vue.d.ts +3 -11
  28. package/lib/components/toggle/Toggle.vue.d.ts +2 -2
  29. package/lib/composables/keys.d.ts +1 -0
  30. package/lib/create.d.ts +12 -0
  31. package/lib/index.cjs.js +2 -2
  32. package/lib/index.d.ts +2 -0
  33. package/lib/index.es.js +271 -130
  34. package/lib/install.d.ts +4 -6
  35. package/lib/nuxt.js +15 -16
  36. package/lib/nuxt.plugin.js +8 -0
  37. package/lib/style.css +1 -1
  38. package/lib/version.d.ts +1 -1
  39. package/package.json +21 -15
  40. package/src/components/alert/Alert.vue +164 -0
  41. package/src/components/avatar/Avatar.vue +137 -0
  42. package/src/components/badge/Badge.vue +107 -0
  43. package/src/components/breadcrumbs/Breadcrumbs.vue +60 -0
  44. package/src/components/button/Button.vue +433 -0
  45. package/src/components/button/ButtonGroup.vue +73 -0
  46. package/src/components/card/Card.vue +25 -0
  47. package/src/components/checkbox/Checkbox.vue +205 -0
  48. package/src/components/collapse/Collapse.vue +181 -0
  49. package/src/components/container/Container.vue +23 -0
  50. package/src/components/divider/Divider.vue +52 -0
  51. package/src/components/drawer/Drawer.vue +244 -0
  52. package/src/components/form/Form.vue +111 -0
  53. package/src/components/icon/Icon.vue +123 -0
  54. package/src/components/image/Image.vue +36 -0
  55. package/src/components/index.ts +45 -0
  56. package/src/components/input/Input.vue +199 -0
  57. package/src/components/link/Link.vue +110 -0
  58. package/src/components/menu/Menu.vue +118 -0
  59. package/src/components/menu/MenuItem.vue +277 -0
  60. package/src/components/modal/Modal.vue +175 -0
  61. package/src/components/notifications/Notifications.vue +318 -0
  62. package/src/components/pagination/Pagination.vue +181 -0
  63. package/src/components/pagination/PaginationItem.vue +58 -0
  64. package/src/components/popover/Popover.vue +194 -0
  65. package/src/components/popover/PopoverContainer.vue +23 -0
  66. package/src/components/progress/Progress.vue +86 -0
  67. package/src/components/radio/Radio.vue +220 -0
  68. package/src/components/scroll/Scroll.vue +143 -0
  69. package/src/components/select/Select.vue +408 -0
  70. package/src/components/skeleton/Skeleton.vue +23 -0
  71. package/src/components/slider/Slider.vue +240 -0
  72. package/src/components/spacer/Spacer.vue +11 -0
  73. package/src/components/spinner/Spinner.vue +45 -0
  74. package/src/components/tab/Tab.vue +100 -0
  75. package/src/components/tab/TabGroup.vue +151 -0
  76. package/src/components/table/Table.vue +172 -0
  77. package/src/components/table/TableBody.vue +13 -0
  78. package/src/components/table/TableCell.vue +78 -0
  79. package/src/components/table/TableHead.vue +15 -0
  80. package/src/components/table/TableHeader.vue +94 -0
  81. package/src/components/table/TableRow.vue +43 -0
  82. package/src/components/tag/Tag.vue +98 -0
  83. package/src/components/textarea/Textarea.vue +156 -0
  84. package/src/components/toggle/Toggle.vue +144 -0
  85. package/src/components/tooltip/Tooltip.vue +26 -0
  86. package/src/composables/colors-utils.ts +378 -0
  87. package/src/composables/colors.ts +82 -0
  88. package/src/composables/common.ts +20 -0
  89. package/src/composables/css.ts +45 -0
  90. package/src/composables/index.ts +7 -0
  91. package/src/composables/inputtable.ts +128 -0
  92. package/src/composables/interactive.ts +16 -0
  93. package/src/composables/keys.ts +8 -0
  94. package/src/composables/notification.ts +10 -0
  95. package/src/create.ts +38 -0
  96. package/src/exports/nuxt.js +32 -0
  97. package/src/exports/nuxt.plugin.js +8 -0
  98. package/src/exports/tailwind.preset.js +55 -0
  99. package/src/index.ts +8 -0
  100. package/src/install.ts +8 -0
  101. package/src/shims-vue.d.ts +6 -0
  102. package/src/version.ts +1 -0
  103. package/volar.d.ts +1 -1
@@ -0,0 +1,205 @@
1
+ <script lang="ts">
2
+ import { defineComponent, ref, watch, computed, type StyleValue } from 'vue'
3
+ import { useCSS } from '../../composables/css'
4
+ import { useCommon } from '../../composables/common'
5
+ import { useColors } from '../../composables/colors'
6
+ import { useInteractive } from '../../composables/interactive'
7
+ import { useInputtable } from '../../composables/inputtable'
8
+
9
+ import XSpinner from '../../components/spinner/Spinner.vue'
10
+
11
+ export default defineComponent({
12
+ name: 'XCheckbox',
13
+
14
+ components: {
15
+ XSpinner,
16
+ },
17
+
18
+ validators: {
19
+ ...useCommon.validators(),
20
+ },
21
+
22
+ props: {
23
+ ...useCommon.props(),
24
+ ...useColors.props('primary'),
25
+ ...useInteractive.props(),
26
+ ...useInputtable.props(),
27
+ label: String,
28
+ glow: Boolean,
29
+ },
30
+
31
+ emits: useInputtable.emits(false),
32
+
33
+ expose: ['toggle'],
34
+
35
+ setup(props, { attrs, emit }) {
36
+ const elRef = ref<HTMLElement>()
37
+ const checked = ref(false)
38
+
39
+ watch(() => props.modelValue, (value) => {
40
+ checked.value = !!value
41
+ })
42
+
43
+ watch(() => checked.value, (value) => {
44
+ emit('update:modelValue', value)
45
+ })
46
+
47
+ const sizeClasses = computed(() => {
48
+ if (props.size === 'xs' || props.size === 'sm') return 'h-4 w-4'
49
+ else if (props.size === 'xl') return 'h-6 w-6'
50
+
51
+ return 'h-5 w-5'
52
+ })
53
+
54
+ const iconSizeClasses = computed(() => {
55
+ if (props.size === 'xs' || props.size === 'sm') return 'h-2 w-2'
56
+ else if (props.size === 'xl') return 'h-4 w-4'
57
+
58
+ return 'h-3 w-3'
59
+ })
60
+
61
+ const css = useCSS()
62
+ const colors = useColors()
63
+ const gray = colors.getPalette('gray')
64
+
65
+ const cssVariables = computed(() => {
66
+ const color = colors.getPalette(props.color)
67
+ const vars: (object | string)[] = []
68
+
69
+ if (props.loading) {
70
+ return css.variables({
71
+ bg: 'transparent',
72
+ border: 'transparent',
73
+ dark: {
74
+ bg: 'transparent',
75
+ border: 'transparent',
76
+ },
77
+ })
78
+ }
79
+
80
+ if (props.disabled) {
81
+ vars.push(css.variables({
82
+ bg: gray[100],
83
+ border: gray[200],
84
+ dark: {
85
+ bg: gray[800],
86
+ border: gray[700],
87
+ },
88
+ }))
89
+ } else {
90
+ if (checked.value) {
91
+ vars.push(css.variables({
92
+ bg: color[500],
93
+ border: color[500],
94
+ dark: {
95
+ bg: color[500],
96
+ border: color[500],
97
+ },
98
+ }))
99
+ } else {
100
+ vars.push(css.variables({
101
+ bg: '#fff',
102
+ border: props.glow ? color[300] : gray[300],
103
+ dark: {
104
+ bg: gray[900],
105
+ border: props.glow ? color[300] : gray[400],
106
+ },
107
+ }))
108
+ }
109
+
110
+ if (props.glow) {
111
+ vars.push(css.get('glow', colors.getColorOpacity(color[500], 0.5)))
112
+ }
113
+ }
114
+
115
+ return vars as StyleValue
116
+ })
117
+
118
+ function toggle() {
119
+ checked.value = !checked.value
120
+ }
121
+
122
+ const interactive = useInteractive(elRef)
123
+
124
+ return {
125
+ ...interactive,
126
+ ...useInputtable(props, { focus: interactive.focus, emit, withListeners: false }),
127
+ elRef,
128
+ checked,
129
+ sizeClasses,
130
+ iconSizeClasses,
131
+ cssVariables,
132
+ toggle,
133
+ }
134
+ },
135
+ })
136
+ </script>
137
+
138
+ <template>
139
+ <label class="inline-block relative cursor-pointer align-middle mb-1 pb-2">
140
+ <div
141
+ ref="elRef"
142
+ class="flex items-center"
143
+ :class="{ 'cursor-not-allowed': disabled }"
144
+ tabindex="0"
145
+ @keypress.prevent.stop.space="toggle"
146
+ >
147
+ <input
148
+ v-model="checked"
149
+ :aria-checked="checked ? 'true' : 'false'"
150
+ :aria-disabled="disabled ? 'true' : undefined"
151
+ type="checkbox"
152
+ class="invisible absolute"
153
+ :disabled="disabled || loading"
154
+ :name="name"
155
+ :required="required"
156
+ />
157
+ <div
158
+ class="rounded flex justify-center items-center flex-shrink-0 border-2
159
+ border-[color:var(--x-border)]
160
+ bg-[color:var(--x-bg)]
161
+ dark:border-[color:var(--x-dark-border)]
162
+ dark:bg-[color:var(--x-dark-bg)]
163
+ "
164
+ :style="cssVariables"
165
+ :class="[
166
+ [(glow && !disabled && !loading) ? $style['checkbox--glow'] : ''],
167
+ sizeClasses,
168
+ ]"
169
+ >
170
+ <x-spinner v-if="loading" :size="size" class="absolute" />
171
+ <svg
172
+ v-else
173
+ class="fill-current text-gray-100 dark:text-gray-900"
174
+ :class="[iconSizeClasses, {
175
+ 'opacity-0': !checked,
176
+ }]"
177
+ viewBox="0 0 20 20"
178
+ >
179
+ <path d="M0 11l2-2 5 5L18 3l2 2L7 18z" />
180
+ </svg>
181
+ </div>
182
+ <div
183
+ class="inline-block font-medium text-gray-800 dark:text-gray-200 pl-2"
184
+ :class="{
185
+ 'text-xs': size === 'xs',
186
+ 'text-sm': size === 'sm',
187
+ 'text-lg': size === 'lg',
188
+ 'text-xl': size === 'xl',
189
+ }"
190
+ >
191
+ <span v-if="label" v-text="label"></span>
192
+ <slot v-else></slot>
193
+ </div>
194
+ </div>
195
+ <p v-if="errorInternal" class="text-sm text-red-500 mt-1" v-text="errorInternal"></p>
196
+ </label>
197
+ </template>
198
+
199
+ <style lang="postcss" module scoped>
200
+ .checkbox {
201
+ &--glow {
202
+ box-shadow: 0 0 #000, 0 0 #000, 0 10px 15px -3px var(--x-glow),0 4px 6px -4px var(--x-glow);
203
+ }
204
+ }
205
+ </style>
@@ -0,0 +1,181 @@
1
+ <script lang="ts">
2
+ import { defineComponent, ref, watch } from 'vue'
3
+
4
+ import XIcon from '../../components/icon/Icon.vue'
5
+
6
+ export default defineComponent({
7
+ name: 'XCollapse',
8
+
9
+ components: {
10
+ XIcon,
11
+ },
12
+
13
+ props: {
14
+ tag: {
15
+ type: String,
16
+ default: 'div',
17
+ },
18
+ disabled: Boolean,
19
+ expanded: Boolean,
20
+ showIcon: {
21
+ type: Boolean,
22
+ default: true,
23
+ },
24
+ icon: String,
25
+ color: String,
26
+ },
27
+
28
+ emits: ['expand'],
29
+
30
+ expose: ['toggle', 'open', 'close'],
31
+
32
+ setup(props, { emit }) {
33
+ const collapsed = ref(!props.expanded)
34
+ const animated = ref(true)
35
+
36
+ watch(() => props.expanded, () => {
37
+ collapsed.value = !props.expanded
38
+ })
39
+
40
+ function onBeforeEnter(el: HTMLElement) {
41
+ if (animated.value) el.style.height = '0px'
42
+ }
43
+
44
+ function onEnter(el: HTMLElement, done: ()=> void) {
45
+ if (!animated.value) done()
46
+ else {
47
+ el.addEventListener('transitionend', done)
48
+ setTimeout(() => {
49
+ el.style.height = `${el.scrollHeight}px`
50
+ }, 1)
51
+ }
52
+ }
53
+
54
+ function onAfterEnter(el: HTMLElement) {
55
+ if (!animated.value) {
56
+ animated.value = true
57
+ } else {
58
+ el.style.removeProperty('height')
59
+ }
60
+ }
61
+
62
+ function onBeforeLeave(el: HTMLElement) {
63
+ if (!animated.value) return
64
+ el.style.height = `${el.scrollHeight}px`
65
+ }
66
+
67
+ function onLeave(el: HTMLElement ,done: ()=> void) {
68
+ if (!animated.value) done()
69
+ else {
70
+ el.addEventListener('transitionend', done)
71
+ setTimeout(() => {
72
+ el.style.height = '0px'
73
+ }, 1)
74
+ }
75
+ }
76
+
77
+ function onAfterLeave(el: HTMLElement) {
78
+ if (!animated.value) {
79
+ animated.value = true
80
+ } else {
81
+ el.style.removeProperty('height')
82
+ }
83
+ }
84
+
85
+ function open(anim = true) {
86
+ animated.value = anim
87
+ collapsed.value = false
88
+ }
89
+
90
+ function close(anim = true) {
91
+ animated.value = anim
92
+ collapsed.value = true
93
+ }
94
+
95
+ function toggle() {
96
+ if (!props.disabled) collapsed.value = !collapsed.value
97
+ }
98
+
99
+ function onExpand(anim = true) {
100
+ open(anim)
101
+ emit('expand')
102
+ }
103
+
104
+ return {
105
+ collapsed,
106
+ onBeforeEnter,
107
+ onEnter,
108
+ onAfterEnter,
109
+ onBeforeLeave,
110
+ onLeave,
111
+ onAfterLeave,
112
+ onExpand,
113
+ toggle,
114
+ close,
115
+ open,
116
+ }
117
+ },
118
+ })
119
+ </script>
120
+
121
+ <template>
122
+ <component
123
+ :is="tag"
124
+ :aria-disabled="disabled"
125
+ :aria-expanded="collapsed ? 'false' : 'true'"
126
+ >
127
+ <div
128
+ class="flex items-center relative"
129
+ :class="{
130
+ 'cursor-pointer' : !disabled,
131
+ 'flex-row-reverse': icon,
132
+ }"
133
+ @click="toggle"
134
+ >
135
+ <div class="flex-1 overflow-hidden">
136
+ <slot :collapsed="collapsed"></slot>
137
+ </div>
138
+
139
+ <div v-if="showIcon" class="absolute top-1/2 transform -translate-y-1/2 right-3">
140
+ <span
141
+ class="flex transform transition-transform duration-150"
142
+ :class="[
143
+ {
144
+ 'rotate-180': !collapsed,
145
+ 'text-gray-300': disabled
146
+ }
147
+ ]"
148
+ >
149
+ <x-icon v-if="icon" :icon="icon" />
150
+ <svg
151
+ v-else
152
+ viewBox="0 0 24 24"
153
+ stroke="currentColor"
154
+ fill="none"
155
+ role="presentation"
156
+ class="stroke-2 w-5 h-5"
157
+ >
158
+ <path d="M19 9l-7 7-7-7" />
159
+ </svg>
160
+ </span>
161
+ </div>
162
+ </div>
163
+
164
+ <template v-if="$slots.summary">
165
+ <slot name="summary"></slot>
166
+ </template>
167
+
168
+ <Transition
169
+ @before-enter="onBeforeEnter"
170
+ @enter="onEnter"
171
+ @after-enter="onAfterEnter"
172
+ @before-leave="onBeforeLeave"
173
+ @leave="onLeave"
174
+ @after-leave="onAfterLeave"
175
+ >
176
+ <div v-show="!collapsed" class="transition-[height] duration-150 overflow-y-hidden">
177
+ <slot name="content" :expand="onExpand" :collapsed="collapsed"></slot>
178
+ </div>
179
+ </Transition>
180
+ </component>
181
+ </template>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { defineComponent } from 'vue'
3
+
4
+ export default defineComponent({
5
+ name: 'XContainer',
6
+
7
+ props: {
8
+ tag: {
9
+ type: String,
10
+ default: 'div',
11
+ },
12
+ },
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <component
18
+ :is="tag"
19
+ class="max-w-screen-2xl mx-auto px-4"
20
+ >
21
+ <slot></slot>
22
+ </component>
23
+ </template>
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { defineComponent } from 'vue'
3
+
4
+ export default defineComponent({
5
+ name: 'XDivider',
6
+
7
+ props: {
8
+ label: String,
9
+ vertical: Boolean,
10
+ },
11
+ })
12
+ </script>
13
+
14
+ <template>
15
+ <div
16
+ :class="[
17
+ {
18
+ 'h-full flex-col': vertical,
19
+ 'w-full': !vertical
20
+ },
21
+ ]"
22
+ class="flex justify-center items-center"
23
+ >
24
+ <div
25
+ class="bg-gray-200 dark:bg-slate-700 flex-grow"
26
+ :style="[
27
+ {
28
+ width: vertical ? '1px' : 'auto',
29
+ height: !vertical ? '1px' : 'auto'
30
+ }
31
+ ]"
32
+ ></div>
33
+ <div
34
+ v-if="label"
35
+ class="font-medium text-sm text-gray-600 dark:text-gray-300"
36
+ :class="[{
37
+ 'my-2': vertical,
38
+ 'mx-4': !vertical
39
+ }]"
40
+ v-text="label"
41
+ ></div>
42
+ <div
43
+ class="bg-gray-200 dark:bg-slate-700 flex-grow"
44
+ :style="[
45
+ {
46
+ width: vertical ? '1px' : 'auto',
47
+ height: !vertical ? '1px' : 'auto'
48
+ }
49
+ ]"
50
+ ></div>
51
+ </div>
52
+ </template>
@@ -0,0 +1,244 @@
1
+ <script lang="ts">
2
+ import { computed, defineComponent, onMounted, ref, watch, watchEffect, type PropType } from 'vue'
3
+ import { breakpointsTailwind, SwipeDirection, useBreakpoints, useEventListener, useSwipe, type Breakpoints } from '@vueuse/core'
4
+
5
+ import XScroll from '../../components/scroll/Scroll.vue'
6
+
7
+ export default defineComponent({
8
+ name: 'XDrawer',
9
+
10
+ components: {
11
+ XScroll,
12
+ },
13
+
14
+ inheritAttrs: false,
15
+
16
+ props: {
17
+ modelValue: Boolean,
18
+ position: {
19
+ type: String as PropType<'left' | 'right' | 'top' | 'bottom'>,
20
+ default: 'left',
21
+ },
22
+ to: {
23
+ type: [String, Object] as PropType<string | HTMLElement>,
24
+ default: 'body',
25
+ },
26
+ width: {
27
+ type: [String, Number],
28
+ default: 320,
29
+ },
30
+ height: {
31
+ type: [String, Number],
32
+ default: 320,
33
+ },
34
+ breakpoint: [String, Number],
35
+ backdrop: {
36
+ type: Boolean,
37
+ default: true,
38
+ },
39
+ },
40
+
41
+ emits: ['update:modelValue'],
42
+
43
+ expose: ['open', 'close'],
44
+
45
+ setup(props, { emit }) {
46
+ const detached = ref<boolean>(true)
47
+ const visible = ref<boolean>(true)
48
+ const value = ref<boolean>(props.modelValue)
49
+ const backdropRef = ref<HTMLElement | null>(null)
50
+ const drawerRef = ref<HTMLElement | null>(null)
51
+
52
+ const isTailwindBreakpoint = typeof props.breakpoint === 'string'
53
+ const breakpoints = useBreakpoints(isTailwindBreakpoint ? breakpointsTailwind : { md: props.breakpoint || 768 } as Breakpoints)
54
+ const point = breakpoints.smaller(isTailwindBreakpoint ? props.breakpoint : 'md')
55
+
56
+ watchEffect(() => {
57
+ if (props.breakpoint) {
58
+ close()
59
+ detached.value = point.value
60
+ }
61
+ })
62
+
63
+ useEventListener(backdropRef, 'pointerdown', close)
64
+
65
+ watch(() => props.modelValue, (val) => {
66
+ value.value = val
67
+ })
68
+
69
+ useEventListener(document, 'keydown', onKeyDown)
70
+
71
+ function onKeyDown(event: KeyboardEvent) {
72
+ if (event.key === 'Escape' && value.value) close()
73
+ }
74
+
75
+ const { lengthX, lengthY } = useSwipe(drawerRef, {
76
+ // passive: false,
77
+ // onSwipe(e: TouchEvent) {
78
+ // if (lengthX.value < 0) {
79
+ // const length = Math.abs(lengthX.value)
80
+ // left.value = `${length}px`
81
+ // } else {
82
+ // left.value = '0'
83
+ // }
84
+ // },
85
+ // onSwipeEnd(e: TouchEvent, direction: SwipeDirection) {
86
+ // if (lengthX.value < 0 && props.width && (Math.abs(lengthX.value) / props.width) >= 0.5) {
87
+ // left.value = '100%'
88
+ // } else {
89
+ // left.value = '0'
90
+ // }
91
+ // },
92
+ onSwipeEnd(e: TouchEvent, direction: SwipeDirection) {
93
+ if (detached.value) {
94
+ if (
95
+ (props.position === 'left' && direction === 'LEFT') ||
96
+ (props.position === 'right' && direction === 'RIGHT') ||
97
+ (props.position === 'top' && direction === 'UP') ||
98
+ (props.position === 'bottom' && direction === 'DOWN')
99
+ ) close()
100
+ }
101
+ },
102
+ })
103
+
104
+ function close(e?: PointerEvent) {
105
+ if (e && e.target !== backdropRef.value) return
106
+ value.value = false
107
+ emit('update:modelValue', false)
108
+ }
109
+
110
+ function open() {
111
+ value.value = true
112
+ emit('update:modelValue', true)
113
+ }
114
+
115
+ const styles = computed(() => {
116
+ const styles: Record<string, string> = {}
117
+
118
+ if (props.position === 'left' || props.position === 'right') styles['width'] = props.width + 'px'
119
+ else if (props.position === 'top' || props.position === 'bottom') styles['height'] = props.height + 'px'
120
+
121
+ return styles
122
+ })
123
+
124
+ const classes = computed(() => {
125
+ const classes = []
126
+
127
+ if (detached.value) {
128
+ classes.push('absolute shadow-lg')
129
+
130
+ if (props.position === 'top') classes.push('top-0 inset-x-0')
131
+ else if (props.position === 'bottom') classes.push('bottom-0 inset-x-0')
132
+ else if (props.position === 'left') classes.push('left-0 inset-y-0')
133
+ else if (props.position === 'right') classes.push('right-0 inset-y-0')
134
+ }
135
+
136
+ return classes
137
+ })
138
+
139
+ function onBeforeEnter(el: HTMLElement) {
140
+ if (props.position === 'top') el.style.top = `-${props.height}px`
141
+ else if (props.position === 'bottom') el.style.bottom = `-${props.height}px`
142
+ else if (props.position === 'left') el.style.left = `-${props.width}px`
143
+ else if (props.position === 'right') el.style.right = `-${props.width}px`
144
+ }
145
+
146
+ function onEnter(el: HTMLElement, done: ()=> void) {
147
+ if (!detached.value) {
148
+ done()
149
+
150
+ return
151
+ }
152
+ el.addEventListener('transitionend', done)
153
+ setTimeout(() => {
154
+ if (props.backdrop) el.classList.add('bg-gray-500/30')
155
+ if (props.position === 'top') el.style.top = '0'
156
+ else if (props.position === 'bottom') el.style.bottom = '0'
157
+ else if (props.position === 'left') el.style.left = '0'
158
+ else if (props.position === 'right') el.style.right = '0'
159
+ }, 1)
160
+ }
161
+
162
+ function onBeforeLeave(el: HTMLElement) {}
163
+
164
+ function onLeave(el: HTMLElement, done: ()=> void) {
165
+ el.addEventListener('transitionend', done)
166
+ setTimeout(() => {
167
+ if (props.backdrop) el.classList.remove('bg-gray-500/30')
168
+ if (props.position === 'top') el.style.top = `-${props.height}px`
169
+ else if (props.position === 'bottom') el.style.bottom = `-${props.height}px`
170
+ else if (props.position === 'left') el.style.left = `-${props.width}px`
171
+ else if (props.position === 'right') el.style.right = `-${props.width}px`
172
+ }, 1)
173
+ }
174
+
175
+ const deferShow = ref<boolean>(!!(props.to && (props.to instanceof HTMLElement || document.querySelector(props.to))))
176
+
177
+ onMounted(() => {
178
+ deferShow.value = true
179
+ })
180
+
181
+ return {
182
+ deferShow,
183
+ backdropRef,
184
+ drawerRef,
185
+ visible,
186
+ classes,
187
+ styles,
188
+ detached,
189
+ value,
190
+ open,
191
+ close,
192
+ onBeforeEnter,
193
+ onEnter,
194
+ onBeforeLeave,
195
+ onLeave,
196
+ }
197
+ },
198
+ })
199
+ </script>
200
+
201
+ <template>
202
+ <Teleport v-if="deferShow" :to="to" :disabled="!detached">
203
+ <Transition
204
+ :css="false"
205
+ @before-enter="onBeforeEnter"
206
+ @enter="onEnter"
207
+ @before-leave="onBeforeLeave"
208
+ @leave="onLeave"
209
+ >
210
+ <div
211
+ v-if="!detached || (detached && value)"
212
+ ref="backdropRef"
213
+ :class="[
214
+ $attrs.class,
215
+ {
216
+ 'absolute z-40 inset-0 duration-150 ease-in-out': detached,
217
+ },
218
+ ]"
219
+ >
220
+ <div
221
+ v-if="detached && value"
222
+ ref="swipeRef"
223
+ class="flex flex-col max-h-full"
224
+ ></div>
225
+ <div
226
+ ref="drawerRef"
227
+ class="flex flex-col max-h-full bg-white dark:bg-gray-800"
228
+ :class="classes"
229
+ :style="styles"
230
+ >
231
+ <slot name="header"></slot>
232
+ <x-scroll
233
+ :scrollbar="false"
234
+ vertical
235
+ class="flex-1"
236
+ >
237
+ <slot></slot>
238
+ </x-scroll>
239
+ <slot name="footer"></slot>
240
+ </div>
241
+ </div>
242
+ </Transition>
243
+ </Teleport>
244
+ </template>