@dolanske/vui 0.1.0 → 0.1.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 (162) hide show
  1. package/README.md +7 -0
  2. package/dist/components/Accordion/Accordion.vue.d.ts +45 -0
  3. package/dist/components/Accordion/AccordionGroup.vue.d.ts +32 -0
  4. package/dist/components/Alert/Alert.vue.d.ts +29 -0
  5. package/dist/components/Avatar/Avatar.vue.d.ts +9 -0
  6. package/dist/components/Badge/Badge.vue.d.ts +21 -0
  7. package/dist/components/Breadcrumbs/BreadcrumbItem.vue.d.ts +21 -0
  8. package/dist/components/Breadcrumbs/Breadcrumbs.vue.d.ts +27 -0
  9. package/dist/components/Button/Button.vue.d.ts +41 -0
  10. package/dist/components/ButtonGroup/ButtonGroup.vue.d.ts +19 -0
  11. package/dist/components/Calendar/Calendar.vue.d.ts +27 -0
  12. package/dist/components/Card/Card.vue.d.ts +25 -0
  13. package/dist/components/Checkbox/Checkbox.vue.d.ts +31 -0
  14. package/dist/components/CopyClipboard/CopyClipboard.vue.d.ts +40 -0
  15. package/dist/components/Divider/Divider.vue.d.ts +24 -0
  16. package/dist/components/Drawer/Drawer.vue.d.ts +52 -0
  17. package/dist/components/Dropdown/DropdownItem.vue.d.ts +21 -0
  18. package/dist/components/Dropdown/DropdownTitle.vue.d.ts +17 -0
  19. package/dist/components/Flex/Flex.vue.d.ts +38 -0
  20. package/dist/components/Grid/Grid.vue.d.ts +27 -0
  21. package/dist/components/Input/Counter.vue.d.ts +19 -0
  22. package/dist/components/Input/Dropzone.vue.d.ts +107 -0
  23. package/dist/components/Input/File.vue.d.ts +7 -0
  24. package/dist/components/Input/Input.vue.d.ts +54 -0
  25. package/dist/components/Input/Password.vue.d.ts +6 -0
  26. package/dist/components/Input/Textarea.vue.d.ts +30 -0
  27. package/dist/components/Kbd/Kbd.vue.d.ts +23 -0
  28. package/dist/components/Kbd/KbdGroup.vue.d.ts +31 -0
  29. package/dist/components/Modal/Confirm.vue.d.ts +45 -0
  30. package/dist/components/Modal/Modal.vue.d.ts +55 -0
  31. package/dist/components/Pagination/Pagination.vue.d.ts +42 -0
  32. package/dist/components/Pagination/pagination.d.ts +12 -0
  33. package/dist/components/Popout/Popout.vue.d.ts +34 -0
  34. package/dist/components/Progress/Progress.vue.d.ts +31 -0
  35. package/dist/components/Radio/Radio.vue.d.ts +27 -0
  36. package/dist/components/Radio/RadioGroup.vue.d.ts +40 -0
  37. package/dist/components/Select/Select.vue.d.ts +37 -0
  38. package/dist/components/Sheet/Sheet.vue.d.ts +35 -0
  39. package/dist/components/Skeleton/Skeleton.vue.d.ts +8 -0
  40. package/dist/components/Spinner/Spinner.vue.d.ts +6 -0
  41. package/dist/components/Switch/Switch.vue.d.ts +26 -0
  42. package/dist/components/Table/Cell.vue.d.ts +19 -0
  43. package/dist/components/Table/Header.vue.d.ts +29 -0
  44. package/dist/components/Table/Row.vue.d.ts +16 -0
  45. package/dist/components/Table/SelectAll.vue.d.ts +2 -0
  46. package/dist/components/Table/SelectRow.vue.d.ts +6 -0
  47. package/dist/components/Table/Table.vue.d.ts +40 -0
  48. package/dist/components/Table/table.d.ts +68 -0
  49. package/dist/components/Tabs/Tab.vue.d.ts +8 -0
  50. package/dist/components/Tabs/Tabs.vue.d.ts +43 -0
  51. package/dist/components/Toast/Toasts.vue.d.ts +2 -0
  52. package/dist/components/Toast/toast.d.ts +42 -0
  53. package/dist/components/Tooltip/Tooltip.vue.d.ts +32 -0
  54. package/dist/index.d.ts +54 -1
  55. package/dist/internal/Backdrop/Backdrop.vue.d.ts +20 -0
  56. package/dist/shared/composables.d.ts +3 -0
  57. package/dist/shared/helpers.d.ts +16 -0
  58. package/dist/shared/types.d.ts +10 -0
  59. package/dist/style.css +1 -1
  60. package/dist/vui.js +199 -214
  61. package/package.json +11 -9
  62. package/src/App.vue +162 -0
  63. package/src/components/Accordion/Accordion.vue +75 -0
  64. package/src/components/Accordion/AccordionGroup.vue +43 -0
  65. package/src/components/Accordion/accordion.scss +44 -0
  66. package/src/components/Alert/Alert.vue +53 -0
  67. package/src/components/Alert/alert.scss +80 -0
  68. package/src/components/Avatar/Avatar.vue +36 -0
  69. package/src/components/Avatar/avatar.scss +46 -0
  70. package/src/components/Badge/Badge.vue +21 -0
  71. package/src/components/Badge/badge.scss +89 -0
  72. package/src/components/Breadcrumbs/BreadcrumbItem.vue +26 -0
  73. package/src/components/Breadcrumbs/Breadcrumbs.vue +33 -0
  74. package/src/components/Breadcrumbs/breadcrumbs.scss +30 -0
  75. package/src/components/Button/Button.vue +90 -0
  76. package/src/components/Button/button.scss +176 -0
  77. package/src/components/ButtonGroup/ButtonGroup.vue +25 -0
  78. package/src/components/ButtonGroup/button-group.scss +51 -0
  79. package/src/components/Calendar/Calendar.vue +58 -0
  80. package/src/components/Calendar/calendar.scss +56 -0
  81. package/src/components/Card/Card.vue +48 -0
  82. package/src/components/Card/card.scss +53 -0
  83. package/src/components/Checkbox/Checkbox.vue +49 -0
  84. package/src/components/Checkbox/checkbox.scss +60 -0
  85. package/src/components/CopyClipboard/CopyClipboard.vue +82 -0
  86. package/src/components/CopyClipboard/copy-clipboard.scss +17 -0
  87. package/src/components/Divider/Divider.vue +34 -0
  88. package/src/components/Divider/divider.scss +35 -0
  89. package/src/components/Drawer/Drawer.vue +93 -0
  90. package/src/components/Drawer/drawer.scss +49 -0
  91. package/src/components/Dropdown/Dropdown.vue +100 -0
  92. package/src/components/Dropdown/DropdownItem.vue +29 -0
  93. package/src/components/Dropdown/DropdownTitle.vue +8 -0
  94. package/src/components/Dropdown/dropdown.scss +112 -0
  95. package/src/components/Flex/Flex.vue +109 -0
  96. package/src/components/Grid/Grid.vue +59 -0
  97. package/src/components/Input/Counter.vue +70 -0
  98. package/src/components/Input/Dropzone.vue +63 -0
  99. package/src/components/Input/File.vue +15 -0
  100. package/src/components/Input/Input.vue +118 -0
  101. package/src/components/Input/Password.vue +47 -0
  102. package/src/components/Input/Textarea.vue +73 -0
  103. package/src/components/Input/input.scss +199 -0
  104. package/src/components/Kbd/Kbd.vue +48 -0
  105. package/src/components/Kbd/KbdGroup.vue +31 -0
  106. package/src/components/Kbd/kbd.scss +18 -0
  107. package/src/components/Modal/Confirm.vue +56 -0
  108. package/src/components/Modal/Modal.vue +91 -0
  109. package/src/components/Modal/modal.scss +49 -0
  110. package/src/components/Pagination/Pagination.vue +74 -0
  111. package/src/components/Pagination/pagination.ts +78 -0
  112. package/src/components/Popout/Popout.vue +39 -0
  113. package/src/components/Popout/popout.scss +7 -0
  114. package/src/components/Progress/Progress.vue +84 -0
  115. package/src/components/Progress/progress.scss +41 -0
  116. package/src/components/Radio/Radio.vue +36 -0
  117. package/src/components/Radio/RadioGroup.vue +35 -0
  118. package/src/components/Radio/radio.scss +59 -0
  119. package/src/components/Select/Select.vue +180 -0
  120. package/src/components/Select/select.scss +43 -0
  121. package/src/components/Sheet/Sheet.vue +91 -0
  122. package/src/components/Sheet/sheet.scss +56 -0
  123. package/src/components/Skeleton/Skeleton.vue +46 -0
  124. package/src/components/Skeleton/skeleton.scss +14 -0
  125. package/src/components/Spinner/Spinner.vue +44 -0
  126. package/src/components/Spinner/spinner.scss +46 -0
  127. package/src/components/Switch/Switch.vue +30 -0
  128. package/src/components/Switch/switch.scss +52 -0
  129. package/src/components/Table/Cell.vue +23 -0
  130. package/src/components/Table/Header.vue +59 -0
  131. package/src/components/Table/Row.vue +9 -0
  132. package/src/components/Table/SelectAll.vue +23 -0
  133. package/src/components/Table/SelectRow.vue +29 -0
  134. package/src/components/Table/Table.vue +66 -0
  135. package/src/components/Table/table.scss +134 -0
  136. package/src/components/Table/table.ts +243 -0
  137. package/src/components/Tabs/Tab.vue +21 -0
  138. package/src/components/Tabs/Tabs.vue +76 -0
  139. package/src/components/Tabs/tabs.scss +78 -0
  140. package/src/components/Toast/Toasts.vue +47 -0
  141. package/src/components/Toast/toast.scss +41 -0
  142. package/src/components/Toast/toast.ts +92 -0
  143. package/src/components/Tooltip/Tooltip.vue +80 -0
  144. package/src/components/Tooltip/tooltip.scss +4 -0
  145. package/src/index.scss +1 -0
  146. package/src/index.ts +111 -0
  147. package/src/internal/Backdrop/Backdrop.vue +22 -0
  148. package/src/internal/Backdrop/backdrop.scss +28 -0
  149. package/src/main.ts +5 -0
  150. package/src/shared/composables.ts +18 -0
  151. package/src/shared/helpers.ts +53 -0
  152. package/src/shared/types.ts +11 -0
  153. package/src/style/animation.scss +21 -0
  154. package/src/style/core.scss +128 -0
  155. package/src/style/fonts.scss +0 -0
  156. package/src/style/layout.scss +9 -0
  157. package/src/style/media-query.scss +29 -0
  158. package/src/style/reset.scss +135 -0
  159. package/src/style/tooltip.scss +128 -0
  160. package/src/style/typography.scss +339 -0
  161. package/src/style/utils.scss +22 -0
  162. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,84 @@
1
+ <script setup lang='ts'>
2
+ import { onMounted, useTemplateRef, watchEffect } from 'vue'
3
+ import { delay, isNil, randomMinMax } from '../../shared/helpers'
4
+ import './progress.scss'
5
+
6
+ interface Props {
7
+ /**
8
+ * Will randomly increment but never actually reach the end
9
+ */
10
+ fake?: boolean
11
+ /**
12
+ * Indicator color. Use CSS color values or variables
13
+ */
14
+ color?: string
15
+ /**
16
+ * Displays loader at the top of the page. It is only displayed when the
17
+ * progress is between 0 and 100 (exclusive).
18
+ */
19
+ fixed?: boolean
20
+ /**
21
+ * Height
22
+ */
23
+ height?: number | string
24
+ }
25
+
26
+ const {
27
+ fake,
28
+ color = 'var(--color-accent)',
29
+ fixed,
30
+ height,
31
+ } = defineProps<Props>()
32
+
33
+ const progressAmount = defineModel<number>({
34
+ default: 0,
35
+ set(value) {
36
+ return Math.min(value, 100)
37
+ },
38
+ })
39
+
40
+ // Set height programatically
41
+ const progressRef = useTemplateRef('progress')
42
+
43
+ watchEffect(() => {
44
+ if (progressRef.value && !isNil(height)) {
45
+ progressRef.value.style.setProperty('--vui-progress-height', `${height}px`)
46
+ }
47
+ })
48
+
49
+ // Automatically / randomly increment but never reach 100% until
50
+ async function fakeIncrement() {
51
+ if (fake && progressAmount.value < 100) {
52
+ if (progressAmount.value > 95) {
53
+ // Only in crement by the fraction of the remaining amount
54
+ progressAmount.value += (100 - progressAmount.value) * 0.075
55
+ await delay(randomMinMax(500, 3000))
56
+ }
57
+ else {
58
+ progressAmount.value += randomMinMax(1, 10)
59
+ await delay(randomMinMax(200, 1000))
60
+ }
61
+ fakeIncrement()
62
+ }
63
+ }
64
+
65
+ onMounted(fakeIncrement)
66
+ </script>
67
+
68
+ <template>
69
+ <div
70
+ ref="progress"
71
+ class="vui-progress"
72
+ :class="{
73
+ fixed,
74
+ 'fixed-active': progressAmount > 0 && progressAmount < 100,
75
+ }"
76
+ >
77
+ <div
78
+ class="vui-progress-indicator" :style="{
79
+ width: `${progressAmount}%`,
80
+ backgroundColor: color,
81
+ }"
82
+ />
83
+ </div>
84
+ </template>
@@ -0,0 +1,41 @@
1
+ .vui-progress {
2
+ --vui-progress-height: 3px;
3
+ display: block;
4
+ width: 100%;
5
+ position: relative;
6
+ border-radius: 999px;
7
+ background-color: var(--color-bg-lowered);
8
+ overflow: hidden;
9
+ height: var(--vui-progress-height);
10
+
11
+ &.fixed {
12
+ transition: var(--transition-slow);
13
+ transition-property: height;
14
+ position: fixed;
15
+ inset: 0;
16
+ bottom: unset;
17
+ height: 0;
18
+ border-radius: none !important;
19
+
20
+ &.fixed-active {
21
+ // Some arbitrary value which should never happen to people with
22
+ height: var(--vui-progress-height);
23
+ }
24
+
25
+ .vui-progress-indicator {
26
+ border-top-left-radius: unset !important;
27
+ border-bottom-left-radius: unset !important;
28
+ }
29
+ }
30
+
31
+ .vui-progress-indicator {
32
+ position: absolute;
33
+ z-index: 4;
34
+ inset: 0;
35
+ right: unset;
36
+ width: 0;
37
+ border-radius: 999px;
38
+ background-color: var(--color-text-lighter);
39
+ transition: var(--transition-slow);
40
+ }
41
+ }
@@ -0,0 +1,36 @@
1
+ <script setup lang='ts'>
2
+ import { Icon } from '@iconify/vue'
3
+ import { computed, useId } from 'vue'
4
+ import './radio.scss'
5
+
6
+ export interface RadioProps {
7
+ label?: string
8
+ disabled?: boolean
9
+ value: string
10
+ }
11
+
12
+ const {
13
+ label,
14
+ disabled,
15
+ value,
16
+ } = defineProps<RadioProps>()
17
+ const slots = defineSlots()
18
+ const checked = defineModel()
19
+ const id = useId()
20
+ const isChecked = computed(() => value === checked.value)
21
+ </script>
22
+
23
+ <template>
24
+ <div class="vui-radio" :class="{ disabled: !!disabled, checked: isChecked }">
25
+ <input :id v-model="checked" type="radio" :value :checked="isChecked">
26
+ <label :for="id">
27
+ <span class="vui-radio-icon">
28
+ <Icon :icon="isChecked ? 'ph:radio-button-fill' : 'ph:circle'" />
29
+ </span>
30
+ <p v-if="!slots.default" class="vui-radio-content">{{ label || value }}</p>
31
+ <div v-else class="vui-radio-content">
32
+ <slot />
33
+ </div>
34
+ </label>
35
+ </div>
36
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang='ts'>
2
+ import type { VNode } from 'vue'
3
+ import type { FlexProps } from '../Flex/Flex.vue'
4
+ import type { RadioProps } from './Radio.vue'
5
+ import Flex from '../Flex/Flex.vue'
6
+
7
+ interface Props extends FlexProps {
8
+ disabled?: boolean
9
+ }
10
+
11
+ const {
12
+ disabled,
13
+ ...flexProps
14
+ } = defineProps<Props>()
15
+
16
+ const slots = defineSlots<{
17
+ default: () => { children: Array<VNode & { props: RadioProps }> }[]
18
+ }>()
19
+
20
+ const checked = defineModel()
21
+ </script>
22
+
23
+ <template>
24
+ <Flex v-bind="flexProps">
25
+ <Component
26
+ :is="vnode"
27
+ v-for="vnode of slots.default()[0].children"
28
+ :key="vnode.props.value"
29
+ v-bind="vnode.props"
30
+ v-model="checked"
31
+ :class="{ disabled: disabled || vnode.props.disabled }"
32
+ />
33
+ </Flex>
34
+ <!-- <div class="vui-radio-group" /> -->
35
+ </template>
@@ -0,0 +1,59 @@
1
+ .vui-radio {
2
+ --radio-size: 24px;
3
+
4
+ &.checked {
5
+ .vui-radio-icon svg {
6
+ color: var(--color-text);
7
+ }
8
+ }
9
+
10
+ &.disabled {
11
+ cursor: not-allowed;
12
+ pointer-events: none;
13
+
14
+ .vui-radio-icon svg path {
15
+ color: var(--color-text-lighter) !important;
16
+ }
17
+
18
+ input + label p {
19
+ color: var(--color-text-lighter);
20
+ }
21
+ }
22
+
23
+ .vui-radio-icon {
24
+ cursor: pointer;
25
+ width: var(--radio-size);
26
+ height: var(--radio-size);
27
+
28
+ svg {
29
+ width: 100%;
30
+ height: 100%;
31
+ color: var(--color-text);
32
+ }
33
+ }
34
+
35
+ input {
36
+ display: none;
37
+
38
+ & + label {
39
+ display: grid;
40
+ grid-template-columns: 20px 1fr;
41
+ gap: 10px;
42
+ font-size: var(--font-size-s);
43
+ color: var(--color-text);
44
+ user-select: none;
45
+
46
+ .vui-radio-content {
47
+ display: block;
48
+
49
+ &:is(p) {
50
+ display: flex;
51
+ align-items: center;
52
+ min-height: var(--radio-size);
53
+ font-size: var(--font-size-ms);
54
+ // line-height: var(--radio-size);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +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>
@@ -0,0 +1,43 @@
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
+ }
36
+ }
37
+
38
+ &:hover {
39
+ border-color: var(--color-border-strong);
40
+ }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,91 @@
1
+ <script setup lang='ts'>
2
+ import { computed } from 'vue'
3
+ import Backdrop from '../../internal/Backdrop/Backdrop.vue'
4
+ import Button from '../Button/Button.vue'
5
+ import Divider from '../Divider/Divider.vue'
6
+ import './sheet.scss'
7
+
8
+ interface Props {
9
+ position?: 'left' | 'right' | 'top' | 'bottom'
10
+ size?: number | string
11
+ separator?: boolean
12
+ }
13
+
14
+ const {
15
+ position = 'right',
16
+ size = 398,
17
+ separator,
18
+ } = defineProps<Props>()
19
+
20
+ const open = defineModel<boolean>()
21
+
22
+ function close() {
23
+ open.value = false
24
+ }
25
+
26
+ const style = computed(() => {
27
+ const formattedSizeValue = typeof size === 'number' ? `${size}px` : size
28
+ let style
29
+
30
+ if (position === 'left' || position === 'right') {
31
+ style = { width: formattedSizeValue }
32
+ }
33
+ else {
34
+ style = { minHeight: formattedSizeValue }
35
+ }
36
+
37
+ return style
38
+ })
39
+
40
+ // Used to compute base location so that sheet appears to animate form the edge of the screen
41
+ const baseTransform = computed(() => {
42
+ switch (position) {
43
+ case 'left': return `translate(-16px, 0)`
44
+ case 'top': return `translate(0, -16px)`
45
+ case 'bottom': return `translate(0, 16px)`
46
+ case 'right':
47
+ default: return `translate(16px, 0)`
48
+ }
49
+ })
50
+ </script>
51
+
52
+ <template>
53
+ <Teleport to="body">
54
+ <Transition appear name="sheet">
55
+ <Backdrop v-if="open" :style="{ padding: 0 }" @close="open = false">
56
+ <div v-if="open" class="vui-sheet" :class="[`vui-sheet-position-${position}`]" :style>
57
+ <div class="vui-sheet-header">
58
+ <div :style="{ flex: 1 }">
59
+ <slot name="header" :close />
60
+ </div>
61
+ <Button square icon="ph:x" @click="open = false" />
62
+ </div>
63
+
64
+ <Divider v-if="separator && $slots.header" :size="1" />
65
+
66
+ <div v-if="$slots.default" class="vui-sheet-content">
67
+ <slot :close />
68
+ </div>
69
+ </div>
70
+ </Backdrop>
71
+ </Transition>
72
+ </Teleport>
73
+ </template>
74
+
75
+ <style scoped lang="scss">
76
+ .sheet-enter-active,
77
+ .sheet-leave-active {
78
+ transition: var(--transition);
79
+ }
80
+
81
+ .sheet-enter-to,
82
+ .sheet-leave-from {
83
+ transform: translate(0, 0);
84
+ }
85
+
86
+ .sheet-enter-from,
87
+ .sheet-leave-to {
88
+ opacity: 0;
89
+ transform: v-bind(baseTransform);
90
+ }
91
+ </style>
@@ -0,0 +1,56 @@
1
+ .vui-sheet {
2
+ display: flex;
3
+ flex-direction: column;
4
+ position: absolute;
5
+ background-color: var(--color-bg);
6
+ border-color: var(--color-border);
7
+ height: 100dvh;
8
+
9
+ &.vui-sheet-position-top {
10
+ top: 0;
11
+ border-bottom: 1px solid var(--color-border);
12
+ }
13
+
14
+ &.vui-sheet-position-bottom {
15
+ bottom: 0;
16
+ border-top: 1px solid var(--color-border);
17
+ }
18
+
19
+ &.vui-sheet-position-right {
20
+ right: 0;
21
+ border-left: 1px solid var(--color-border);
22
+ }
23
+
24
+ &.vui-sheet-position-left {
25
+ left: 0;
26
+ border-right: 1px solid var(--color-border);
27
+ }
28
+
29
+ &.vui-sheet-position-top,
30
+ &.vui-sheet-position-bottom {
31
+ max-height: 35dvh;
32
+ width: 100%;
33
+ }
34
+
35
+ .vui-sheet-header,
36
+ .vui-sheet-content {
37
+ width: 100%;
38
+ padding: var(--space-m);
39
+ }
40
+
41
+ .vui-sheet-content {
42
+ flex: 1 1 0%;
43
+ overflow-y: auto;
44
+ height: 100%;
45
+ }
46
+
47
+ .vui-sheet-header {
48
+ display: flex;
49
+ gap: var(--space-m);
50
+ align-self: flex-start;
51
+
52
+ & + .vui-sheet-content {
53
+ padding-top: var(--space-s);
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,46 @@
1
+ <!-- eslint-disable ts/no-use-before-define -->
2
+ <script setup lang='ts'>
3
+ import './skeleton.scss'
4
+
5
+ interface Props {
6
+ radius?: string | number
7
+ width?: string | number
8
+ height?: string | number
9
+ circle?: boolean
10
+ }
11
+
12
+ const {
13
+ // @ts-expect-error I can't get ESLint to STOP moving DEFAULT_RADIUS below
14
+ // this props declaration wtf
15
+ radius = DEFAULT_RADIUS,
16
+ width = '100%',
17
+ height = '32px',
18
+ circle,
19
+ } = defineProps<Props>()
20
+
21
+ const DEFAULT_RADIUS = 'var(--border-radius-s)'
22
+
23
+ function valueToPixels(value: string | number) {
24
+ return typeof value === 'number'
25
+ ? `${value}px`
26
+ // If last value of string is NOT a number, do not add "px" at the end
27
+ // eslint-disable-next-line unicorn/prefer-number-properties
28
+ : isNaN(Number(value[value.length - 1]))
29
+ ? value
30
+ : `${value}px`
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <div
36
+ class="vui-skeleton" :style="{
37
+ // Give priority to radius, if it is NOT set to default
38
+ borderRadius: valueToPixels(circle && radius === DEFAULT_RADIUS ? 9999 : radius),
39
+ // When using `circle` prop, we want to use the same
40
+ // value for both height and width, but we can't
41
+ // know which one will be defined
42
+ width: valueToPixels(circle ? (width || height) : width),
43
+ height: valueToPixels(circle ? (width || height) : height),
44
+ }"
45
+ />
46
+ </template>
@@ -0,0 +1,14 @@
1
+ .vui-skeleton {
2
+ animation: pulse 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
3
+ background-color: var(--color-border);
4
+
5
+ @keyframes pulse {
6
+ 0%,
7
+ 100% {
8
+ opacity: 1;
9
+ }
10
+ 50% {
11
+ opacity: 0.5;
12
+ }
13
+ }
14
+ }