@dolanske/vui 0.3.4 → 0.5.0

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 (114) hide show
  1. package/LICENSE +673 -673
  2. package/README.md +41 -40
  3. package/dist/components/Dropdown/DropdownItem.vue.d.ts +1 -0
  4. package/dist/components/Flex/Flex.vue.d.ts +3 -1
  5. package/dist/components/Grid/Grid.vue.d.ts +3 -1
  6. package/dist/components/Tabs/Tabs.vue.d.ts +4 -0
  7. package/dist/style.css +1 -1
  8. package/dist/vui.js +1547 -1534
  9. package/package.json +68 -68
  10. package/src/App.vue +176 -175
  11. package/src/components/Accordion/Accordion.vue +91 -91
  12. package/src/components/Accordion/AccordionGroup.vue +43 -43
  13. package/src/components/Accordion/accordion.scss +81 -80
  14. package/src/components/Alert/Alert.vue +53 -53
  15. package/src/components/Alert/alert.scss +80 -80
  16. package/src/components/Avatar/Avatar.vue +50 -50
  17. package/src/components/Avatar/avatar.scss +52 -52
  18. package/src/components/Badge/Badge.vue +21 -21
  19. package/src/components/Badge/badge.scss +89 -89
  20. package/src/components/Breadcrumbs/BreadcrumbItem.vue +26 -26
  21. package/src/components/Breadcrumbs/Breadcrumbs.vue +33 -33
  22. package/src/components/Breadcrumbs/breadcrumbs.scss +30 -30
  23. package/src/components/Button/Button.vue +90 -90
  24. package/src/components/Button/button.scss +178 -176
  25. package/src/components/ButtonGroup/ButtonGroup.vue +25 -25
  26. package/src/components/ButtonGroup/button-group.scss +51 -51
  27. package/src/components/Calendar/Calendar.vue +63 -60
  28. package/src/components/Calendar/calendar.scss +60 -56
  29. package/src/components/Card/Card.vue +48 -48
  30. package/src/components/Card/card.scss +53 -53
  31. package/src/components/Checkbox/Checkbox.vue +51 -52
  32. package/src/components/Checkbox/checkbox.scss +75 -66
  33. package/src/components/CopyClipboard/CopyClipboard.vue +82 -82
  34. package/src/components/CopyClipboard/copy-clipboard.scss +17 -17
  35. package/src/components/Divider/Divider.vue +44 -44
  36. package/src/components/Divider/divider.scss +35 -35
  37. package/src/components/Drawer/Drawer.vue +97 -97
  38. package/src/components/Drawer/drawer.scss +36 -36
  39. package/src/components/Dropdown/Dropdown.vue +111 -111
  40. package/src/components/Dropdown/DropdownItem.vue +33 -29
  41. package/src/components/Dropdown/DropdownTitle.vue +8 -8
  42. package/src/components/Dropdown/dropdown-item.scss +77 -0
  43. package/src/components/Dropdown/dropdown.scss +39 -117
  44. package/src/components/Flex/Flex.vue +113 -106
  45. package/src/components/Grid/Grid.vue +60 -54
  46. package/src/components/Input/Counter.vue +70 -70
  47. package/src/components/Input/Dropzone.vue +65 -65
  48. package/src/components/Input/File.vue +15 -15
  49. package/src/components/Input/Input.vue +121 -121
  50. package/src/components/Input/Password.vue +47 -47
  51. package/src/components/Input/Textarea.vue +76 -76
  52. package/src/components/Input/input.scss +208 -208
  53. package/src/components/Kbd/Kbd.vue +48 -48
  54. package/src/components/Kbd/KbdGroup.vue +31 -31
  55. package/src/components/Kbd/kbd.scss +18 -18
  56. package/src/components/Modal/Confirm.vue +56 -56
  57. package/src/components/Modal/Modal.vue +91 -91
  58. package/src/components/Modal/modal.scss +49 -49
  59. package/src/components/OTP/OTP.vue +133 -133
  60. package/src/components/OTP/OTPItem.vue +37 -37
  61. package/src/components/OTP/otp.scss +83 -83
  62. package/src/components/Pagination/Pagination.vue +74 -74
  63. package/src/components/Pagination/pagination.ts +78 -78
  64. package/src/components/Popout/Popout.vue +42 -42
  65. package/src/components/Popout/popout.scss +8 -8
  66. package/src/components/Progress/Progress.vue +90 -90
  67. package/src/components/Progress/progress.scss +41 -41
  68. package/src/components/Radio/Radio.vue +36 -36
  69. package/src/components/Radio/RadioGroup.vue +40 -40
  70. package/src/components/Radio/radio.scss +68 -59
  71. package/src/components/Select/Select.vue +180 -180
  72. package/src/components/Select/select.scss +44 -44
  73. package/src/components/Sheet/Sheet.vue +92 -92
  74. package/src/components/Sheet/sheet.scss +60 -60
  75. package/src/components/Sidebar/Sidebar.vue +102 -0
  76. package/src/components/Sidebar/sidebar.scss +123 -0
  77. package/src/components/Skeleton/Skeleton.vue +43 -43
  78. package/src/components/Skeleton/skeleton.scss +14 -14
  79. package/src/components/Spinner/Spinner.vue +42 -42
  80. package/src/components/Spinner/spinner.scss +46 -46
  81. package/src/components/Switch/Switch.vue +30 -30
  82. package/src/components/Switch/switch.scss +60 -52
  83. package/src/components/Table/Cell.vue +23 -23
  84. package/src/components/Table/Header.vue +59 -59
  85. package/src/components/Table/Row.vue +9 -9
  86. package/src/components/Table/SelectAll.vue +23 -23
  87. package/src/components/Table/SelectRow.vue +29 -29
  88. package/src/components/Table/Table.vue +66 -66
  89. package/src/components/Table/table.scss +134 -134
  90. package/src/components/Table/table.ts +244 -244
  91. package/src/components/Tabs/Tab.vue +27 -27
  92. package/src/components/Tabs/Tabs.vue +89 -82
  93. package/src/components/Tabs/tabs.scss +80 -79
  94. package/src/components/Toast/Toasts.vue +47 -47
  95. package/src/components/Toast/toast.scss +41 -41
  96. package/src/components/Toast/toast.ts +68 -68
  97. package/src/components/Tooltip/Tooltip.vue +86 -86
  98. package/src/components/Tooltip/tooltip.scss +4 -4
  99. package/src/index.scss +1 -1
  100. package/src/index.ts +119 -119
  101. package/src/internal/Backdrop/Backdrop.vue +22 -22
  102. package/src/internal/Backdrop/backdrop.scss +28 -28
  103. package/src/main.ts +5 -5
  104. package/src/shared/helpers.ts +74 -74
  105. package/src/shared/types.ts +29 -29
  106. package/src/style/animation.scss +21 -21
  107. package/src/style/core.scss +150 -148
  108. package/src/style/layout.scss +168 -136
  109. package/src/style/media-query.scss +29 -29
  110. package/src/style/reset.scss +135 -135
  111. package/src/style/{fonts.scss → text.scss} +74 -53
  112. package/src/style/tooltip.scss +128 -128
  113. package/src/style/typography.scss +338 -338
  114. package/src/style/utils.scss +36 -36
@@ -1,180 +1,180 @@
1
- <!-- eslint-disable ts/consistent-type-definitions -->
2
- <script setup lang='ts' generic="T">
3
- import { Icon } from '@iconify/vue'
4
- import { computed, onMounted, ref, useTemplateRef } from 'vue'
5
- import { searchString } from '../../shared/helpers'
6
- import Button from '../Button/Button.vue'
7
- import Dropdown from '../Dropdown/Dropdown.vue'
8
- import DropdownItem from '../Dropdown/DropdownItem.vue'
9
- import DropdownTitle from '../Dropdown/DropdownTitle.vue'
10
- import Input from '../Input/Input.vue'
11
- import '../Input/input.scss'
12
- import './select.scss'
13
-
14
- export type SelectOption = {
15
- value: any
16
- label: string
17
- }
18
-
19
- type Props = {
20
- single?: boolean
21
- readonly?: boolean
22
- options: SelectOption[]
23
- label?: string
24
- placeholder?: string
25
- required?: boolean
26
- expand?: boolean
27
- hint?: string
28
- search?: boolean
29
- maxActiveOptions?: number
30
- showClear?: boolean
31
- }
32
-
33
- const {
34
- expand,
35
- readonly,
36
- required,
37
- hint,
38
- placeholder,
39
- options,
40
- single = true,
41
- search,
42
- maxActiveOptions = 3,
43
- showClear,
44
- } = defineProps<Props>()
45
-
46
- const selected = defineModel<SelectOption[] | undefined>()
47
-
48
- //
49
- function setValue(option: SelectOption) {
50
- if (single) {
51
- // Single
52
- if (selected.value?.some(o => o.value === option.value) && !required) {
53
- selected.value = undefined
54
- }
55
- else {
56
- selected.value = [option]
57
- }
58
- }
59
- else {
60
- if (selected.value && selected.value?.some(o => o.value === option.value)) {
61
- const values = selected.value.filter(o => o.value !== option.value)
62
- // Cant remove the last value if it's required
63
- if ((required && values.length > 0) || !required) {
64
- selected.value = values.length > 0 ? values : undefined
65
- }
66
- }
67
- else {
68
- if (!selected.value) {
69
- selected.value = [option]
70
- }
71
- else {
72
- selected.value?.push(option)
73
- }
74
- }
75
- }
76
- }
77
-
78
- // Search
79
- const searchStr = ref()
80
- const filteredOptions = computed(() => {
81
- if (!searchStr.value || searchStr.value.length === 0)
82
- return options
83
-
84
- return options.filter((option) => {
85
- return searchString(option.label, searchStr.value)
86
- })
87
- })
88
-
89
- // Render text inside the button
90
- const renderPlaceholder = computed(() => {
91
- // Render placeholder or a default select option
92
- if (!selected.value || selected.value.length === 0)
93
- return placeholder ?? 'Select an option'
94
-
95
- // Selected values
96
- if (single)
97
- return selected.value[0].label
98
-
99
- // If amount of selected exceeds the active capacity
100
- if (selected.value.length > maxActiveOptions) {
101
- return `${selected.value.length} selected`
102
- }
103
-
104
- // Just list ALL selected optionsx
105
- return selected.value.map(o => o.label).join(', ')
106
- })
107
-
108
- onMounted(() => {
109
- if (readonly && showClear) {
110
- console.warn('[Select] readonly and showClear are mutually exclusive props. The clear button will not show up if required is present.')
111
- }
112
- })
113
-
114
- const dropdownRef = useTemplateRef('dropdown')
115
-
116
- function clearValue() {
117
- selected.value = undefined
118
- dropdownRef.value?.close()
119
- }
120
- </script>
121
-
122
- <template>
123
- <div class="vui-input-container vui-select" :class="{ expand, required, readonly }">
124
- <Dropdown ref="dropdown" :expand>
125
- <template #trigger="{ toggle, isOpen }">
126
- <div class="vui-input vui-select-trigger-content">
127
- <label v-if="label" for="id">{{ label }}</label>
128
- <p v-if="hint" class="vui-input-hint">
129
- {{ hint }}
130
- </p>
131
-
132
- <button class="vui-input-style vui-select-trigger-container" @click="toggle">
133
- <span>
134
- {{ renderPlaceholder }}
135
- </span>
136
- <Button
137
- v-if="showClear && !required && selected"
138
- plain
139
- icon="ph:x"
140
- square
141
- size="s"
142
- @click.stop="clearValue"
143
- />
144
- <Icon :icon="isOpen ? 'ph:caret-up' : 'ph:caret-down'" />
145
- </button>
146
- </div>
147
- </template>
148
-
149
- <template #default="{ close, isOpen }">
150
- <DropdownTitle v-if="search">
151
- <Input
152
- v-model="searchStr"
153
- placeholder="Search..."
154
- :focus="isOpen"
155
- />
156
- </DropdownTitle>
157
-
158
- <p v-if="filteredOptions.length === 0" class="vue-select-no-results">
159
- No results...
160
- </p>
161
-
162
- <DropdownItem
163
- v-for="option in filteredOptions"
164
- :key="option.value"
165
- :class="{ selected: selected?.find(v => v.value === option.value) }"
166
- :icon="selected?.find(v => v.value === option.value) ? 'ph:check-bold' : ''"
167
- @click="() => {
168
- setValue(option)
169
- // In single mode, close modal after clicking
170
- if (single && !(required && selected && selected[0].value === option.value)) {
171
- close()
172
- }
173
- }"
174
- >
175
- {{ option.label }}
176
- </DropdownItem>
177
- </template>
178
- </Dropdown>
179
- </div>
180
- </template>
1
+ <!-- eslint-disable ts/consistent-type-definitions -->
2
+ <script setup lang='ts' generic="T">
3
+ import { Icon } from '@iconify/vue'
4
+ import { computed, onMounted, ref, useTemplateRef } from 'vue'
5
+ import { searchString } from '../../shared/helpers'
6
+ import Button from '../Button/Button.vue'
7
+ import Dropdown from '../Dropdown/Dropdown.vue'
8
+ import DropdownItem from '../Dropdown/DropdownItem.vue'
9
+ import DropdownTitle from '../Dropdown/DropdownTitle.vue'
10
+ import Input from '../Input/Input.vue'
11
+ import '../Input/input.scss'
12
+ import './select.scss'
13
+
14
+ export type SelectOption = {
15
+ value: any
16
+ label: string
17
+ }
18
+
19
+ type Props = {
20
+ single?: boolean
21
+ readonly?: boolean
22
+ options: SelectOption[]
23
+ label?: string
24
+ placeholder?: string
25
+ required?: boolean
26
+ expand?: boolean
27
+ hint?: string
28
+ search?: boolean
29
+ maxActiveOptions?: number
30
+ showClear?: boolean
31
+ }
32
+
33
+ const {
34
+ expand,
35
+ readonly,
36
+ required,
37
+ hint,
38
+ placeholder,
39
+ options,
40
+ single = true,
41
+ search,
42
+ maxActiveOptions = 3,
43
+ showClear,
44
+ } = defineProps<Props>()
45
+
46
+ const selected = defineModel<SelectOption[] | undefined>()
47
+
48
+ //
49
+ function setValue(option: SelectOption) {
50
+ if (single) {
51
+ // Single
52
+ if (selected.value?.some(o => o.value === option.value) && !required) {
53
+ selected.value = undefined
54
+ }
55
+ else {
56
+ selected.value = [option]
57
+ }
58
+ }
59
+ else {
60
+ if (selected.value && selected.value?.some(o => o.value === option.value)) {
61
+ const values = selected.value.filter(o => o.value !== option.value)
62
+ // Cant remove the last value if it's required
63
+ if ((required && values.length > 0) || !required) {
64
+ selected.value = values.length > 0 ? values : undefined
65
+ }
66
+ }
67
+ else {
68
+ if (!selected.value) {
69
+ selected.value = [option]
70
+ }
71
+ else {
72
+ selected.value?.push(option)
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ // Search
79
+ const searchStr = ref()
80
+ const filteredOptions = computed(() => {
81
+ if (!searchStr.value || searchStr.value.length === 0)
82
+ return options
83
+
84
+ return options.filter((option) => {
85
+ return searchString(option.label, searchStr.value)
86
+ })
87
+ })
88
+
89
+ // Render text inside the button
90
+ const renderPlaceholder = computed(() => {
91
+ // Render placeholder or a default select option
92
+ if (!selected.value || selected.value.length === 0)
93
+ return placeholder ?? 'Select an option'
94
+
95
+ // Selected values
96
+ if (single)
97
+ return selected.value[0].label
98
+
99
+ // If amount of selected exceeds the active capacity
100
+ if (selected.value.length > maxActiveOptions) {
101
+ return `${selected.value.length} selected`
102
+ }
103
+
104
+ // Just list ALL selected optionsx
105
+ return selected.value.map(o => o.label).join(', ')
106
+ })
107
+
108
+ onMounted(() => {
109
+ if (readonly && showClear) {
110
+ console.warn('[Select] readonly and showClear are mutually exclusive props. The clear button will not show up if required is present.')
111
+ }
112
+ })
113
+
114
+ const dropdownRef = useTemplateRef('dropdown')
115
+
116
+ function clearValue() {
117
+ selected.value = undefined
118
+ dropdownRef.value?.close()
119
+ }
120
+ </script>
121
+
122
+ <template>
123
+ <div class="vui-input-container vui-select" :class="{ expand, required, readonly }">
124
+ <Dropdown ref="dropdown" :expand>
125
+ <template #trigger="{ toggle, isOpen }">
126
+ <div class="vui-input vui-select-trigger-content">
127
+ <label v-if="label" for="id">{{ label }}</label>
128
+ <p v-if="hint" class="vui-input-hint">
129
+ {{ hint }}
130
+ </p>
131
+
132
+ <button class="vui-input-style vui-select-trigger-container" @click="toggle">
133
+ <span>
134
+ {{ renderPlaceholder }}
135
+ </span>
136
+ <Button
137
+ v-if="showClear && !required && selected"
138
+ plain
139
+ icon="ph:x"
140
+ square
141
+ size="s"
142
+ @click.stop="clearValue"
143
+ />
144
+ <Icon :icon="isOpen ? 'ph:caret-up' : 'ph:caret-down'" />
145
+ </button>
146
+ </div>
147
+ </template>
148
+
149
+ <template #default="{ close, isOpen }">
150
+ <DropdownTitle v-if="search">
151
+ <Input
152
+ v-model="searchStr"
153
+ placeholder="Search..."
154
+ :focus="isOpen"
155
+ />
156
+ </DropdownTitle>
157
+
158
+ <p v-if="filteredOptions.length === 0" class="vue-select-no-results">
159
+ No results...
160
+ </p>
161
+
162
+ <DropdownItem
163
+ v-for="option in filteredOptions"
164
+ :key="option.value"
165
+ :class="{ selected: selected?.find(v => v.value === option.value) }"
166
+ :icon="selected?.find(v => v.value === option.value) ? 'ph:check-bold' : ''"
167
+ @click="() => {
168
+ setValue(option)
169
+ // In single mode, close modal after clicking
170
+ if (single && !(required && selected && selected[0].value === option.value)) {
171
+ close()
172
+ }
173
+ }"
174
+ >
175
+ {{ option.label }}
176
+ </DropdownItem>
177
+ </template>
178
+ </Dropdown>
179
+ </div>
180
+ </template>
@@ -1,44 +1,44 @@
1
- .vui-input-container {
2
- &.vui-select {
3
- &.expand {
4
- .vui-dropdown-trigger-wrap,
5
- .vui-dropdown-trigger-content {
6
- width: 100%;
7
- }
8
- }
9
-
10
- .vui-select-trigger-container {
11
- span {
12
- white-space: nowrap;
13
- text-overflow: ellipsis;
14
- overflow-x: hidden;
15
- }
16
- }
17
-
18
- .vue-select-no-results {
19
- text-align: center;
20
- padding-block: var(--space-xs);
21
- }
22
-
23
- .vui-input-style {
24
- display: flex;
25
- align-items: center;
26
- justify-content: space-between;
27
- gap: var(--space-s);
28
-
29
- & > button {
30
- margin-right: -6px;
31
-
32
- .vui-button-slot svg {
33
- width: 14px;
34
- height: 14px;
35
- color: var(--color-text-light);
36
- }
37
- }
38
-
39
- &:hover {
40
- border-color: var(--color-border-strong);
41
- }
42
- }
43
- }
44
- }
1
+ .vui-input-container {
2
+ &.vui-select {
3
+ &.expand {
4
+ .vui-dropdown-trigger-wrap,
5
+ .vui-dropdown-trigger-content {
6
+ width: 100%;
7
+ }
8
+ }
9
+
10
+ .vui-select-trigger-container {
11
+ span {
12
+ white-space: nowrap;
13
+ text-overflow: ellipsis;
14
+ overflow-x: hidden;
15
+ }
16
+ }
17
+
18
+ .vue-select-no-results {
19
+ text-align: center;
20
+ padding-block: var(--space-xs);
21
+ }
22
+
23
+ .vui-input-style {
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ gap: var(--space-s);
28
+
29
+ & > button {
30
+ margin-right: -6px;
31
+
32
+ .vui-button-slot svg {
33
+ width: 14px;
34
+ height: 14px;
35
+ color: var(--color-text-light);
36
+ }
37
+ }
38
+
39
+ &:hover {
40
+ border-color: var(--color-border-strong);
41
+ }
42
+ }
43
+ }
44
+ }
@@ -1,92 +1,92 @@
1
- <script setup lang='ts'>
2
- import { computed } from 'vue'
3
- import Backdrop from '../../internal/Backdrop/Backdrop.vue'
4
- import { formatUnitValue } from '../../shared/helpers'
5
- import Button from '../Button/Button.vue'
6
- import Divider from '../Divider/Divider.vue'
7
- import './sheet.scss'
8
-
9
- interface Props {
10
- position?: 'left' | 'right' | 'top' | 'bottom'
11
- size?: number | string
12
- separator?: boolean
13
- }
14
-
15
- const {
16
- position = 'right',
17
- size = 398,
18
- separator,
19
- } = defineProps<Props>()
20
-
21
- const TRANSITION_OFFSET = 16
22
-
23
- const open = defineModel<boolean>()
24
-
25
- function close() {
26
- open.value = false
27
- }
28
-
29
- const style = computed(() => {
30
- if (position === 'left' || position === 'right') {
31
- return { width: formatUnitValue(size) }
32
- }
33
-
34
- return undefined
35
- })
36
-
37
- // Used to compute base location so that sheet appears to animate form the edge of the screen
38
- const baseTransform = computed(() => {
39
- switch (position) {
40
- case 'left': return `translate(-${TRANSITION_OFFSET}px, 0)`
41
- case 'top': return `translate(0, -${TRANSITION_OFFSET}px)`
42
- case 'bottom': return `translate(0, ${TRANSITION_OFFSET}px)`
43
- case 'right':
44
- default: return `translate(${TRANSITION_OFFSET}px, 0)`
45
- }
46
- })
47
- </script>
48
-
49
- <template>
50
- <Teleport to="body">
51
- <Transition appear name="sheet">
52
- <Backdrop v-if="open" @close="open = false">
53
- <div v-if="open" class="vui-sheet" :class="[`vui-sheet-position-${position}`]" :style>
54
- <div class="vui-sheet-header">
55
- <div class="flex-1">
56
- <slot name="header" :close />
57
- </div>
58
- <Button square icon="ph:x" @click="open = false" />
59
- </div>
60
-
61
- <Divider v-if="separator && $slots.header" :space="1" />
62
-
63
- <div v-if="$slots.default" class="vui-sheet-content">
64
- <slot :close />
65
- </div>
66
- </div>
67
- </Backdrop>
68
- </Transition>
69
- </Teleport>
70
- </template>
71
-
72
- <style scoped lang="scss">
73
- .vui-backdrop {
74
- padding: 0;
75
- }
76
-
77
- .sheet-enter-active,
78
- .sheet-leave-active {
79
- transition: var(--transition);
80
- }
81
-
82
- .sheet-enter-to,
83
- .sheet-leave-from {
84
- transform: translate(0, 0);
85
- }
86
-
87
- .sheet-enter-from,
88
- .sheet-leave-to {
89
- opacity: 0;
90
- transform: v-bind(baseTransform);
91
- }
92
- </style>
1
+ <script setup lang='ts'>
2
+ import { computed } from 'vue'
3
+ import Backdrop from '../../internal/Backdrop/Backdrop.vue'
4
+ import { formatUnitValue } from '../../shared/helpers'
5
+ import Button from '../Button/Button.vue'
6
+ import Divider from '../Divider/Divider.vue'
7
+ import './sheet.scss'
8
+
9
+ interface Props {
10
+ position?: 'left' | 'right' | 'top' | 'bottom'
11
+ size?: number | string
12
+ separator?: boolean
13
+ }
14
+
15
+ const {
16
+ position = 'right',
17
+ size = 398,
18
+ separator,
19
+ } = defineProps<Props>()
20
+
21
+ const TRANSITION_OFFSET = 16
22
+
23
+ const open = defineModel<boolean>()
24
+
25
+ function close() {
26
+ open.value = false
27
+ }
28
+
29
+ const style = computed(() => {
30
+ if (position === 'left' || position === 'right') {
31
+ return { width: formatUnitValue(size) }
32
+ }
33
+
34
+ return undefined
35
+ })
36
+
37
+ // Used to compute base location so that sheet appears to animate form the edge of the screen
38
+ const baseTransform = computed(() => {
39
+ switch (position) {
40
+ case 'left': return `translate(-${TRANSITION_OFFSET}px, 0)`
41
+ case 'top': return `translate(0, -${TRANSITION_OFFSET}px)`
42
+ case 'bottom': return `translate(0, ${TRANSITION_OFFSET}px)`
43
+ case 'right':
44
+ default: return `translate(${TRANSITION_OFFSET}px, 0)`
45
+ }
46
+ })
47
+ </script>
48
+
49
+ <template>
50
+ <Teleport to="body">
51
+ <Transition appear name="sheet">
52
+ <Backdrop v-if="open" @close="open = false">
53
+ <div v-if="open" class="vui-sheet" :class="[`vui-sheet-position-${position}`]" :style>
54
+ <div class="vui-sheet-header">
55
+ <div class="flex-1">
56
+ <slot name="header" :close />
57
+ </div>
58
+ <Button square icon="ph:x" @click="open = false" />
59
+ </div>
60
+
61
+ <Divider v-if="separator && $slots.header" :space="1" />
62
+
63
+ <div v-if="$slots.default" class="vui-sheet-content">
64
+ <slot :close />
65
+ </div>
66
+ </div>
67
+ </Backdrop>
68
+ </Transition>
69
+ </Teleport>
70
+ </template>
71
+
72
+ <style scoped lang="scss">
73
+ .vui-backdrop {
74
+ padding: 0;
75
+ }
76
+
77
+ .sheet-enter-active,
78
+ .sheet-leave-active {
79
+ transition: var(--transition);
80
+ }
81
+
82
+ .sheet-enter-to,
83
+ .sheet-leave-from {
84
+ transform: translate(0, 0);
85
+ }
86
+
87
+ .sheet-enter-from,
88
+ .sheet-leave-to {
89
+ opacity: 0;
90
+ transform: v-bind(baseTransform);
91
+ }
92
+ </style>