@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,109 @@
1
+ <script setup lang="ts">
2
+ import type { Sizes } from '../../shared/types'
3
+ import { computed } from 'vue'
4
+ import { useActualGap } from '../../shared/composables'
5
+
6
+ export interface FlexProps {
7
+ inline?: boolean
8
+ wrap?: boolean
9
+ wrapReverse?: boolean
10
+
11
+ row?: boolean
12
+ column?: boolean
13
+ rowReverse?: boolean
14
+ columnReverse?: boolean
15
+
16
+ gap?: Sizes | number
17
+
18
+ // NOTE: Add more if needed
19
+ justifyStart?: boolean
20
+ justifyEnd?: boolean
21
+ justifyCenter?: boolean
22
+ spaceBetween?: boolean
23
+ spaceAround?: boolean
24
+ spaceEvenly?: boolean
25
+
26
+ // NOTE: Add more if needed
27
+ alignCenter?: boolean
28
+ alignStart?: boolean
29
+ alignEnd?: boolean
30
+ alignBaseline?: boolean
31
+
32
+ expand?: boolean
33
+ }
34
+
35
+ const props = defineProps<FlexProps>()
36
+
37
+ // Flex gap
38
+ const ag = useActualGap(props.gap)
39
+
40
+ // Flex direction
41
+ const ad = computed(() => {
42
+ if (props.row)
43
+ return 'row'
44
+ else if (props.column)
45
+ return 'column'
46
+ else if (props.rowReverse)
47
+ return 'row-reverse'
48
+ else if (props.columnReverse)
49
+ return 'column-reverse'
50
+ else return 'row'
51
+ })
52
+
53
+ // Justify content
54
+ const aj = computed(() => {
55
+ if (props.justifyStart)
56
+ return 'flex-start'
57
+ else if (props.justifyEnd)
58
+ return 'flex-end'
59
+ else if (props.justifyCenter)
60
+ return 'center'
61
+ else if (props.spaceBetween)
62
+ return 'space-between'
63
+ else if (props.spaceEvenly)
64
+ return 'space-evenly'
65
+ else if (props.spaceAround)
66
+ return 'space-around'
67
+ else return 'flex-start'
68
+ })
69
+
70
+ // Align items
71
+ const aA = computed(() => {
72
+ if (props.alignStart)
73
+ return 'flex-start'
74
+ else if (props.alignEnd)
75
+ return 'flex-end'
76
+ else if (props.alignCenter)
77
+ return 'center'
78
+ else if (props.alignBaseline)
79
+ return 'baseline'
80
+ return 'flex-start'
81
+ })
82
+
83
+ const aY = computed(() => props.inline ? 'inline-flex' : 'flex')
84
+ const aW = computed(() => props.wrap
85
+ ? 'wrap'
86
+ : props.wrapReverse
87
+ ? 'wrap-reverse'
88
+ : 'nowrap')
89
+
90
+ const aH = computed(() => props.expand ? '100%' : 'auto')
91
+ </script>
92
+
93
+ <template>
94
+ <div class="vui-flex">
95
+ <slot />
96
+ </div>
97
+ </template>
98
+
99
+ <style scoped lang="scss">
100
+ .vui-flex {
101
+ display: v-bind(aY);
102
+ flex-wrap: v-bind(aW);
103
+ flex-direction: v-bind(ad);
104
+ justify-content: v-bind(aj);
105
+ gap: v-bind(ag);
106
+ align-items: v-bind(aA);
107
+ width: v-bind(aH);
108
+ }
109
+ </style>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import type { Sizes } from '../../shared/types'
3
+ import { computed } from 'vue'
4
+ import { useActualGap } from '../../shared/composables'
5
+ import { createArray } from '../../shared/helpers'
6
+
7
+ /**
8
+ * This component is not meant for complex grids
9
+ */
10
+
11
+ interface Props {
12
+ inline?: boolean
13
+ gap?: Sizes | number
14
+ rows?: number | string
15
+ columns?: number | string
16
+ areas?: string[]
17
+ }
18
+
19
+ const props = defineProps<Props>()
20
+
21
+ const aG = useActualGap(props.gap)
22
+
23
+ const aTC = computed(() => {
24
+ if (typeof props.columns === 'number') {
25
+ return createArray(props.columns)
26
+ .map(() => '1fr')
27
+ .join(' ')
28
+ }
29
+ return props.columns ?? 'none'
30
+ })
31
+
32
+ const aTR = computed(() => {
33
+ if (typeof props.rows === 'number') {
34
+ return createArray(props.rows || 1)
35
+ .map(() => '')
36
+ .join('1fr')
37
+ }
38
+ return props.rows ?? 'none'
39
+ })
40
+
41
+ const aD = computed(() => props.inline ? 'inline-grid' : 'grid')
42
+ </script>
43
+
44
+ <template>
45
+ <div
46
+ class="vui-grid"
47
+ >
48
+ <slot />
49
+ </div>
50
+ </template>
51
+
52
+ <style scoped lang="scss">
53
+ .vui-grid {
54
+ display: v-bind(aD);
55
+ gap: v-bind(aG);
56
+ grid-template-columns: v-bind(aTC);
57
+ grid-template-rows: v-bind(aTR);
58
+ }
59
+ </style>
@@ -0,0 +1,70 @@
1
+ <script setup lang='ts'>
2
+ import type { InputProps } from './Input.vue'
3
+ import { Icon } from '@iconify/vue'
4
+ import Button from '../Button/Button.vue'
5
+ import Input from './Input.vue'
6
+
7
+ type Props = Omit<InputProps, 'type'> & {
8
+ incrementBy?: number
9
+ incrementEnabled?: boolean
10
+ hideIncrement?: boolean
11
+ decrementBy?: number
12
+ decrementEnabled?: boolean
13
+ hideDecrement?: boolean
14
+ }
15
+
16
+ const {
17
+ incrementBy = 1,
18
+ incrementEnabled = true,
19
+ hideIncrement = false,
20
+ decrementBy = 1,
21
+ decrementEnabled = true,
22
+ hideDecrement = false,
23
+ ...inputProps
24
+ } = defineProps<Props>()
25
+
26
+ const count = defineModel<number>({
27
+ default: 0,
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <Input v-bind="inputProps" v-model.number="count" type="number">
33
+ <template v-if="!hideDecrement" #start>
34
+ <Button key="decrease" :disabled="!decrementEnabled" size="s" @click="count -= decrementBy">
35
+ <Icon icon="ph:minus" /> {{ decrementBy > 1 ? decrementBy : '' }}
36
+ </Button>
37
+ </template>
38
+
39
+ <template v-if="!hideIncrement" #end>
40
+ <Button key="increase" :disabled="!incrementEnabled" size="s" @click="count += incrementBy">
41
+ <Icon icon="ph:plus" /> {{ incrementBy > 1 ? incrementBy : '' }}
42
+ </Button>
43
+ </template>
44
+ </Input>
45
+ </template>
46
+
47
+ <style scoped lang="scss">
48
+ :deep(.vui-input-style) {
49
+ padding-inline: 4px !important;
50
+ gap: 8px !important;
51
+ }
52
+
53
+ :deep(.vui-button) {
54
+ padding-inline: 2px !important;
55
+ }
56
+
57
+ :deep(input) {
58
+ text-align: center;
59
+
60
+ ::-webkit-outer-spin-button,
61
+ ::-webkit-inner-spin-button {
62
+ -webkit-appearance: none;
63
+ margin: 0;
64
+ }
65
+
66
+ /* Firefox */
67
+ -moz-appearance: textfield;
68
+ appearance: textfield;
69
+ }
70
+ </style>
@@ -0,0 +1,63 @@
1
+ <script setup lang='ts'>
2
+ import type { InputProps } from './Input.vue'
3
+ import { Icon } from '@iconify/vue'
4
+ import { useEventListener } from '@vueuse/core'
5
+ import { ref, useTemplateRef } from 'vue'
6
+ import Flex from '../Flex/Flex.vue'
7
+ import Input from './Input.vue'
8
+
9
+ type Props = Omit<InputProps, 'type' | 'modelValue' | 'focus' | 'limit' | 'placeholder'>
10
+
11
+ const props = defineProps<Props>()
12
+ const emits = defineEmits<{ files: [FileList] }>()
13
+
14
+ const dragging = ref(false)
15
+
16
+ function onSubmitHandler(e: any, isFromField?: boolean) {
17
+ e.preventDefault()
18
+ e.stopPropagation()
19
+
20
+ const files = isFromField ? e.target.files : e.dataTransfer.files
21
+
22
+ if (files.length > 0)
23
+ emits('files', files)
24
+ }
25
+
26
+ const dropzoneRef = useTemplateRef<HTMLDivElement>('dropzone')
27
+
28
+ useEventListener(dropzoneRef, 'dragenter', onSubmitHandler, false)
29
+ useEventListener(dropzoneRef, 'dragleave', onSubmitHandler, false)
30
+ useEventListener(dropzoneRef, 'dragover', onSubmitHandler, false)
31
+ useEventListener(dropzoneRef, 'drop', onSubmitHandler, false)
32
+ useEventListener(dropzoneRef, 'input', e => onSubmitHandler(e, true), false)
33
+ </script>
34
+
35
+ <template>
36
+ <Input
37
+ v-bind="props"
38
+ ref="dropzone"
39
+ type="file"
40
+ class="vui-dropzone"
41
+ :class="{ dragging }"
42
+ @dragenter="dragging = true"
43
+ @mouseleave="dragging = false"
44
+ >
45
+ <template #__internal_replace_input="{ inputId }">
46
+ <input :id="inputId" type="file">
47
+ <label :for="inputId">
48
+ <slot :dragging>
49
+ <Flex justify-center gap="s" align-baseline>
50
+ <template v-if="dragging">
51
+ <Icon icon="ph:target" />
52
+ Drop it
53
+ </template>
54
+ <template v-else>
55
+ <Icon icon="ph:file" />
56
+ Click or drag files over here
57
+ </template>
58
+ </Flex>
59
+ </slot>
60
+ </label>
61
+ </template>
62
+ </Input>
63
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang='ts'>
2
+ import type { InputProps } from './Input.vue'
3
+ import Input from './Input.vue'
4
+
5
+ const props = defineProps<Omit<InputProps, 'type'>>()
6
+ const emits = defineEmits<{ files: [FileList] }>()
7
+ </script>
8
+
9
+ <template>
10
+ <Input
11
+ type="file"
12
+ v-bind="{ ...props }"
13
+ @input="emits('files', $event.target.files)"
14
+ />
15
+ </template>
@@ -0,0 +1,118 @@
1
+ <script setup lang="ts">
2
+ import { computed, type InputTypeHTMLAttribute, useId, useTemplateRef, watchEffect } from 'vue'
3
+ import { getMaybeRefLength } from '../../shared/helpers'
4
+ import Flex from '../Flex/Flex.vue'
5
+ import '../../style/core.scss'
6
+ import './input.scss'
7
+
8
+ // FIXME: sometimes spamming inputs when limit is set, it will _just_ let me write one more letter
9
+
10
+ export interface InputProps {
11
+ type?: InputTypeHTMLAttribute
12
+ label?: string
13
+ hint?: string
14
+ limit?: number | string
15
+ expand?: boolean
16
+ placeholder?: string
17
+ required?: boolean
18
+ modelValue?: string | number
19
+ readonly?: boolean
20
+ focus?: boolean
21
+ errors?: string[]
22
+ accept?: string
23
+ multiple?: boolean
24
+ min?: number
25
+ max?: number
26
+ }
27
+
28
+ const {
29
+ type = 'text',
30
+ limit,
31
+ label,
32
+ expand = false,
33
+ hint,
34
+ placeholder,
35
+ required,
36
+ modelValue = '',
37
+ readonly,
38
+ focus,
39
+ accept,
40
+ min,
41
+ max,
42
+ errors = [] as string[],
43
+ } = defineProps<InputProps>()
44
+
45
+ const model = defineModel<string | number>({
46
+ default: '',
47
+ set(newValue) {
48
+ // Completely ignore inputs
49
+ if (readonly)
50
+ return modelValue
51
+
52
+ if (getMaybeRefLength(newValue) > Number(limit)) {
53
+ return modelValue
54
+ }
55
+ return newValue
56
+ },
57
+ })
58
+ const id = useId()
59
+
60
+ const inputRef = useTemplateRef('input')
61
+
62
+ watchEffect(() => {
63
+ if (focus)
64
+ inputRef.value?.focus()
65
+ })
66
+
67
+ defineExpose({
68
+ focus: () => {
69
+ inputRef.value?.focus()
70
+ },
71
+ })
72
+
73
+ const renderLimit = computed(() => {
74
+ return `${getMaybeRefLength(model.value)}/${limit}`
75
+ })
76
+ </script>
77
+
78
+ <template>
79
+ <div class="vui-input-container" :class="{ expand, required, readonly, 'has-errors': errors.length > 0 }">
80
+ <slot name="before" />
81
+ <div class="vui-input">
82
+ <label v-if="label" for="id">{{ label }}</label>
83
+ <p v-if="hint" class="vui-input-hint">
84
+ {{ hint }}
85
+ </p>
86
+ <Flex class="vui-input-style" :gap="3" align-center>
87
+ <slot name="start" />
88
+ <slot name="__internal_replace_input" :input-id="id" />
89
+ <input
90
+ v-if="!$slots.__internal_replace_input"
91
+ :id
92
+ ref="input"
93
+ v-model="model"
94
+ :readonly
95
+ :type
96
+ name="id"
97
+ :placeholder
98
+ :required
99
+ :max="max ?? limit"
100
+ :accept
101
+ :multiple
102
+ :min
103
+ >
104
+ <slot name="end" />
105
+ </Flex>
106
+ </div>
107
+ <p v-if="limit" class="vui-input-limit">
108
+ {{ renderLimit }}
109
+ </p>
110
+ <ul v-if="errors.length > 0" class="vui-input-errors">
111
+ <li v-for="err in errors" :key="err">
112
+ {{ err }}
113
+ </li>
114
+ </ul>
115
+
116
+ <slot name="after" />
117
+ </div>
118
+ </template>
@@ -0,0 +1,47 @@
1
+ <script setup lang='ts'>
2
+ import type { InputProps } from './Input.vue'
3
+ import { Icon } from '@iconify/vue'
4
+ import { ref } from 'vue'
5
+ import Button from '../Button/Button.vue'
6
+ import Input from './Input.vue'
7
+
8
+ type Props = Omit<InputProps, 'type'> & {
9
+ showPassword?: boolean
10
+ }
11
+
12
+ const {
13
+ showPassword = false,
14
+ // @ts-expect-error Don't want to actually provide type as we handle that in
15
+ // this component manually
16
+ type,
17
+ ...inputProps
18
+ } = defineProps<Props>()
19
+
20
+ const show = ref(showPassword)
21
+ </script>
22
+
23
+ <template>
24
+ <Input v-bind="inputProps" :type="show ? 'text' : 'password'" autocomplete="off">
25
+ <template #end>
26
+ <Button
27
+ square
28
+ size="s"
29
+ :data-title-top="show ? 'Hide password' : 'Show password'"
30
+ @click="show = !show"
31
+ >
32
+ <Icon :width="18" :height="18" :icon="show ? 'ph:eye-slash' : 'ph:eye'" />
33
+ </Button>
34
+ </template>
35
+ </Input>
36
+ </template>
37
+
38
+ <style scoped lang="scss">
39
+ :deep(.vui-input-style) {
40
+ padding-right: 4px !important;
41
+ gap: 8px !important;
42
+ }
43
+
44
+ [data-title-top]:before {
45
+ min-width: 104px;
46
+ }
47
+ </style>
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ import type { InputProps } from './Input.vue'
3
+ import { useId } from 'vue'
4
+ import '../Input/input.scss'
5
+
6
+ type Props = Omit<InputProps, 'type'> & {
7
+ resize?: boolean | 'vertical' | 'horizontal'
8
+ autoResize?: boolean
9
+ }
10
+
11
+ const {
12
+ limit,
13
+ label,
14
+ expand = false,
15
+ hint,
16
+ placeholder,
17
+ required,
18
+ modelValue = '',
19
+ readonly,
20
+ resize = 'vertical',
21
+ autoResize,
22
+ errors = [] as string[],
23
+ } = defineProps<Props>()
24
+
25
+ const model = defineModel<string>({
26
+ default: '',
27
+ set(newValue) {
28
+ if (readonly)
29
+ return modelValue
30
+
31
+ if (newValue.length > Number(limit)) {
32
+ return modelValue
33
+ }
34
+ return newValue
35
+ },
36
+ })
37
+ const id = useId()
38
+ </script>
39
+
40
+ <template>
41
+ <div class="vui-input-container" :class="{ expand, required, readonly, 'has-errors': errors.length > 0 }">
42
+ <slot name="before" />
43
+ <div class="vui-input">
44
+ <label v-if="label" for="id">{{ label }}</label>
45
+ <p v-if="hint" class="vui-input-hint">
46
+ {{ hint }}
47
+ </p>
48
+ <textarea
49
+ :id
50
+ v-model="model"
51
+ :readonly
52
+ name="id"
53
+ :placeholder
54
+ :required
55
+ :max="limit"
56
+ :style="{
57
+ 'resize': resize === true ? 'both' : (resize || 'initial'),
58
+ // @ts-expect-error This is only supported in chrome for now
59
+ 'field-sizing': autoResize ? 'content' : 'auto',
60
+ }"
61
+ />
62
+ </div>
63
+ <p v-if="limit" class="vui-input-limit">
64
+ {{ `${model.length}/${limit}` }}
65
+ </p>
66
+ <ul v-if="errors.length > 0" class="vui-input-errors">
67
+ <li v-for="err in errors" :key="err">
68
+ {{ err }}
69
+ </li>
70
+ </ul>
71
+ <slot name="after" />
72
+ </div>
73
+ </template>