@bitrix24/b24ui-nuxt 0.1.1

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 (159) hide show
  1. package/.nuxt/b24ui/advice.ts +52 -0
  2. package/.nuxt/b24ui/alert.ts +118 -0
  3. package/.nuxt/b24ui/avatar-group.ts +52 -0
  4. package/.nuxt/b24ui/avatar.ts +63 -0
  5. package/.nuxt/b24ui/badge.ts +256 -0
  6. package/.nuxt/b24ui/button-group.ts +27 -0
  7. package/.nuxt/b24ui/button.ts +342 -0
  8. package/.nuxt/b24ui/checkbox.ts +128 -0
  9. package/.nuxt/b24ui/chip.ts +205 -0
  10. package/.nuxt/b24ui/container.ts +3 -0
  11. package/.nuxt/b24ui/content/description-list.ts +62 -0
  12. package/.nuxt/b24ui/countdown.ts +94 -0
  13. package/.nuxt/b24ui/form-field.ts +57 -0
  14. package/.nuxt/b24ui/form.ts +3 -0
  15. package/.nuxt/b24ui/index.ts +28 -0
  16. package/.nuxt/b24ui/input.ts +472 -0
  17. package/.nuxt/b24ui/kbd.ts +31 -0
  18. package/.nuxt/b24ui/link.ts +20 -0
  19. package/.nuxt/b24ui/progress.ts +303 -0
  20. package/.nuxt/b24ui/radio-group.ts +135 -0
  21. package/.nuxt/b24ui/range.ts +172 -0
  22. package/.nuxt/b24ui/select.ts +550 -0
  23. package/.nuxt/b24ui/separator.ts +176 -0
  24. package/.nuxt/b24ui/skeleton.ts +3 -0
  25. package/.nuxt/b24ui/switch.ts +134 -0
  26. package/.nuxt/b24ui/tabs.ts +341 -0
  27. package/.nuxt/b24ui/textarea.ts +332 -0
  28. package/.nuxt/b24ui/toast.ts +89 -0
  29. package/.nuxt/b24ui/toaster.ts +91 -0
  30. package/.nuxt/b24ui/tooltip.ts +16 -0
  31. package/LICENSE +21 -0
  32. package/README.md +101 -0
  33. package/cli/commands/make/component.mjs +95 -0
  34. package/cli/commands/make/index.mjs +14 -0
  35. package/cli/commands/make/locale.mjs +64 -0
  36. package/cli/index.mjs +15 -0
  37. package/cli/package.json +13 -0
  38. package/cli/templates.mjs +240 -0
  39. package/cli/utils.mjs +31 -0
  40. package/dist/meta.cjs +23610 -0
  41. package/dist/meta.d.cts +23608 -0
  42. package/dist/meta.d.mts +23608 -0
  43. package/dist/meta.d.ts +23608 -0
  44. package/dist/meta.mjs +23608 -0
  45. package/dist/module.cjs +54 -0
  46. package/dist/module.d.cts +14 -0
  47. package/dist/module.d.mts +14 -0
  48. package/dist/module.d.ts +14 -0
  49. package/dist/module.json +13 -0
  50. package/dist/module.mjs +51 -0
  51. package/dist/runtime/components/Advice.vue +115 -0
  52. package/dist/runtime/components/Alert.vue +136 -0
  53. package/dist/runtime/components/App.vue +52 -0
  54. package/dist/runtime/components/Avatar.vue +118 -0
  55. package/dist/runtime/components/AvatarGroup.vue +99 -0
  56. package/dist/runtime/components/Badge.vue +114 -0
  57. package/dist/runtime/components/Button.vue +177 -0
  58. package/dist/runtime/components/ButtonGroup.vue +58 -0
  59. package/dist/runtime/components/Checkbox.vue +110 -0
  60. package/dist/runtime/components/Chip.vue +81 -0
  61. package/dist/runtime/components/Container.vue +36 -0
  62. package/dist/runtime/components/Countdown.vue +498 -0
  63. package/dist/runtime/components/Form.vue +271 -0
  64. package/dist/runtime/components/FormField.vue +128 -0
  65. package/dist/runtime/components/Input.vue +224 -0
  66. package/dist/runtime/components/Kbd.vue +50 -0
  67. package/dist/runtime/components/Link.vue +219 -0
  68. package/dist/runtime/components/LinkBase.vue +63 -0
  69. package/dist/runtime/components/Progress.vue +182 -0
  70. package/dist/runtime/components/RadioGroup.vue +178 -0
  71. package/dist/runtime/components/Range.vue +114 -0
  72. package/dist/runtime/components/Select.vue +328 -0
  73. package/dist/runtime/components/Separator.vue +82 -0
  74. package/dist/runtime/components/Skeleton.vue +31 -0
  75. package/dist/runtime/components/Switch.vue +133 -0
  76. package/dist/runtime/components/Tabs.vue +127 -0
  77. package/dist/runtime/components/Textarea.vue +216 -0
  78. package/dist/runtime/components/Toast.vue +168 -0
  79. package/dist/runtime/components/Toaster.vue +143 -0
  80. package/dist/runtime/components/Tooltip.vue +94 -0
  81. package/dist/runtime/components/content/DescriptionList.vue +220 -0
  82. package/dist/runtime/composables/defineLocale.d.ts +9 -0
  83. package/dist/runtime/composables/defineLocale.js +4 -0
  84. package/dist/runtime/composables/defineShortcuts.d.ts +15 -0
  85. package/dist/runtime/composables/defineShortcuts.js +135 -0
  86. package/dist/runtime/composables/useAvatarGroup.d.ts +10 -0
  87. package/dist/runtime/composables/useAvatarGroup.js +10 -0
  88. package/dist/runtime/composables/useButtonGroup.d.ts +17 -0
  89. package/dist/runtime/composables/useButtonGroup.js +10 -0
  90. package/dist/runtime/composables/useComponentIcons.d.ts +20 -0
  91. package/dist/runtime/composables/useComponentIcons.js +25 -0
  92. package/dist/runtime/composables/useFormField.d.ts +42 -0
  93. package/dist/runtime/composables/useFormField.js +65 -0
  94. package/dist/runtime/composables/useKbd.d.ts +35 -0
  95. package/dist/runtime/composables/useKbd.js +52 -0
  96. package/dist/runtime/composables/useLocale.d.ts +4 -0
  97. package/dist/runtime/composables/useLocale.js +10 -0
  98. package/dist/runtime/composables/useToast.d.ts +12 -0
  99. package/dist/runtime/composables/useToast.js +62 -0
  100. package/dist/runtime/dictionary/icons.d.ts +20 -0
  101. package/dist/runtime/dictionary/icons.js +35 -0
  102. package/dist/runtime/index.css +1 -0
  103. package/dist/runtime/keyframes.css +1 -0
  104. package/dist/runtime/locale/en.d.ts +2 -0
  105. package/dist/runtime/locale/en.js +48 -0
  106. package/dist/runtime/locale/es.d.ts +2 -0
  107. package/dist/runtime/locale/es.js +48 -0
  108. package/dist/runtime/locale/index.d.ts +3 -0
  109. package/dist/runtime/locale/index.js +3 -0
  110. package/dist/runtime/locale/ru.d.ts +2 -0
  111. package/dist/runtime/locale/ru.js +48 -0
  112. package/dist/runtime/plugins/colors.d.ts +2 -0
  113. package/dist/runtime/plugins/colors.js +40 -0
  114. package/dist/runtime/types/app.config.d.ts +6 -0
  115. package/dist/runtime/types/form.d.ts +84 -0
  116. package/dist/runtime/types/form.js +12 -0
  117. package/dist/runtime/types/icons.d.ts +3 -0
  118. package/dist/runtime/types/icons.js +0 -0
  119. package/dist/runtime/types/index.d.ts +33 -0
  120. package/dist/runtime/types/index.js +33 -0
  121. package/dist/runtime/types/locale.d.ts +50 -0
  122. package/dist/runtime/types/locale.js +0 -0
  123. package/dist/runtime/types/utils.d.ts +22 -0
  124. package/dist/runtime/types/utils.js +0 -0
  125. package/dist/runtime/utils/form.d.ts +17 -0
  126. package/dist/runtime/utils/form.js +153 -0
  127. package/dist/runtime/utils/fuse.d.ts +4 -0
  128. package/dist/runtime/utils/fuse.js +63 -0
  129. package/dist/runtime/utils/index.d.ts +6 -0
  130. package/dist/runtime/utils/index.js +63 -0
  131. package/dist/runtime/utils/link.d.ts +29 -0
  132. package/dist/runtime/utils/link.js +4 -0
  133. package/dist/runtime/utils/locale.d.ts +15 -0
  134. package/dist/runtime/utils/locale.js +25 -0
  135. package/dist/runtime/utils/tv.d.ts +1 -0
  136. package/dist/runtime/utils/tv.js +4 -0
  137. package/dist/runtime/vue/components/Link.vue +203 -0
  138. package/dist/runtime/vue/plugins/color-mode.d.ts +4 -0
  139. package/dist/runtime/vue/plugins/color-mode.js +6 -0
  140. package/dist/runtime/vue/plugins/head.d.ts +4 -0
  141. package/dist/runtime/vue/plugins/head.js +6 -0
  142. package/dist/runtime/vue/stubs.d.ts +15 -0
  143. package/dist/runtime/vue/stubs.js +27 -0
  144. package/dist/shared/b24ui-nuxt.CNGvMe2S.mjs +4074 -0
  145. package/dist/shared/b24ui-nuxt.D22QQtm8.cjs +4079 -0
  146. package/dist/types.d.mts +1 -0
  147. package/dist/types.d.ts +1 -0
  148. package/dist/unplugin.cjs +213 -0
  149. package/dist/unplugin.d.cts +22 -0
  150. package/dist/unplugin.d.mts +22 -0
  151. package/dist/unplugin.d.ts +22 -0
  152. package/dist/unplugin.mjs +202 -0
  153. package/dist/vite.cjs +21 -0
  154. package/dist/vite.d.cts +12 -0
  155. package/dist/vite.d.mts +12 -0
  156. package/dist/vite.d.ts +12 -0
  157. package/dist/vite.mjs +19 -0
  158. package/package.json +166 -0
  159. package/vue-plugin.d.ts +5 -0
@@ -0,0 +1,133 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { SwitchRootProps } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/switch'
7
+ import { tv } from '../utils/tv'
8
+ import type { IconComponent } from '../types'
9
+ import type { PartialString } from '../types/utils'
10
+
11
+ const appConfigSwitch = _appConfig as AppConfig & { b24ui: { switch: Partial<typeof theme> } }
12
+
13
+ const switchTv = tv({ extend: tv(theme), ...(appConfigSwitch.b24ui?.switch || {}) })
14
+
15
+ type SwitchVariants = VariantProps<typeof switchTv>
16
+
17
+ export interface SwitchProps extends Pick<SwitchRootProps, 'disabled' | 'id' | 'name' | 'required' | 'value' | 'defaultValue'> {
18
+ /**
19
+ * The element or component this component should render as.
20
+ * @defaultValue 'div'
21
+ */
22
+ as?: any
23
+ color?: SwitchVariants['color']
24
+ size?: SwitchVariants['size']
25
+ /** When `true`, the loading icon will be displayed. */
26
+ loading?: boolean
27
+ /**
28
+ * The icon when the `loading` prop is `true`.
29
+ * @defaultValue icons.refresh
30
+ */
31
+ loadingIcon?: IconComponent
32
+ /** Display an icon when the switch is checked. */
33
+ checkedIcon?: IconComponent
34
+ /** Display an icon when the switch is unchecked. */
35
+ uncheckedIcon?: IconComponent
36
+ label?: string
37
+ description?: string
38
+ class?: any
39
+ b24ui?: PartialString<typeof switchTv.slots>
40
+ }
41
+
42
+ export type SwitchEmits = {
43
+ change: [payload: Event]
44
+ }
45
+
46
+ export interface SwitchSlots {
47
+ label(props: { label?: string }): any
48
+ description(props: { description?: string }): any
49
+ }
50
+ </script>
51
+
52
+ <script setup lang="ts">
53
+ import { computed, useId } from 'vue'
54
+ import { Primitive, SwitchRoot, SwitchThumb, useForwardProps, Label } from 'reka-ui'
55
+ import { reactivePick } from '@vueuse/core'
56
+ import { useFormField } from '../composables/useFormField'
57
+ import icons from '../dictionary/icons'
58
+
59
+ const props = defineProps<SwitchProps>()
60
+ const slots = defineSlots<SwitchSlots>()
61
+ const emits = defineEmits<SwitchEmits>()
62
+
63
+ const modelValue = defineModel<boolean>({ default: undefined })
64
+
65
+ const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
66
+
67
+ const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled, ariaAttrs } = useFormField<SwitchProps>(props)
68
+ const id = _id.value ?? useId()
69
+
70
+ const b24ui = computed(() => switchTv({
71
+ size: size.value,
72
+ color: color.value,
73
+ required: props.required,
74
+ loading: props.loading,
75
+ disabled: disabled.value || props.loading
76
+ }))
77
+
78
+ function onUpdate(value: any) {
79
+ // @ts-expect-error - 'target' does not exist in type 'EventInit'
80
+ const event = new Event('change', { target: { value } })
81
+ emits('change', event)
82
+ emitFormChange()
83
+ emitFormInput()
84
+ }
85
+ </script>
86
+
87
+ <template>
88
+ <Primitive :as="as" :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
89
+ <div :class="b24ui.container({ class: props.b24ui?.container })">
90
+ <SwitchRoot
91
+ :id="id"
92
+ v-bind="{ ...rootProps, ...ariaAttrs }"
93
+ v-model="modelValue"
94
+ :name="name"
95
+ :disabled="disabled || loading"
96
+ :class="b24ui.base({ class: props.b24ui?.base })"
97
+ @update:model-value="onUpdate"
98
+ >
99
+ <SwitchThumb :class="b24ui.thumb({ class: props.b24ui?.thumb })">
100
+ <Component
101
+ :is="loadingIcon || icons.refresh"
102
+ v-if="loading"
103
+ :class="b24ui.icon({ class: props.b24ui?.icon, checked: true, unchecked: true })"
104
+ />
105
+ <template v-else>
106
+ <Component
107
+ :is="checkedIcon"
108
+ v-if="checkedIcon"
109
+ :class="b24ui.icon({ class: props.b24ui?.icon, checked: true })"
110
+ />
111
+ <Component
112
+ :is="uncheckedIcon"
113
+ v-if="uncheckedIcon"
114
+ :class="b24ui.icon({ class: props.b24ui?.icon, unchecked: true })"
115
+ />
116
+ </template>
117
+ </SwitchThumb>
118
+ </SwitchRoot>
119
+ </div>
120
+ <div v-if="(label || !!slots.label) || (description || !!slots.description)" :class="b24ui.wrapper({ class: props.b24ui?.wrapper })">
121
+ <Label v-if="label || !!slots.label" :for="id" :class="b24ui.label({ class: props.b24ui?.label })">
122
+ <slot name="label" :label="label">
123
+ {{ label }}
124
+ </slot>
125
+ </Label>
126
+ <p v-if="description || !!slots.description" :class="b24ui.description({ class: props.b24ui?.description })">
127
+ <slot name="description" :description="description">
128
+ {{ description }}
129
+ </slot>
130
+ </p>
131
+ </div>
132
+ </Primitive>
133
+ </template>
@@ -0,0 +1,127 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { TabsRootProps, TabsRootEmits } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/tabs'
7
+ import { tv } from '../utils/tv'
8
+ import type { AvatarProps, IconComponent } from '../types'
9
+ import type { DynamicSlots, PartialString } from '../types/utils'
10
+
11
+ const appConfigTabs = _appConfig as AppConfig & { b24ui: { tabs: Partial<typeof theme> } }
12
+
13
+ const tabs = tv({ extend: tv(theme), ...(appConfigTabs.b24ui?.tabs || {}) })
14
+
15
+ export interface TabsItem {
16
+ label?: string
17
+ icon?: IconComponent
18
+ avatar?: AvatarProps
19
+ slot?: string
20
+ content?: string
21
+ /** A unique value for the tab item. Defaults to the index. */
22
+ value?: string | number
23
+ disabled?: boolean
24
+ }
25
+
26
+ type TabsVariants = VariantProps<typeof tabs>
27
+
28
+ export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
29
+ /**
30
+ * The element or component this component should render as.
31
+ * @defaultValue 'div'
32
+ */
33
+ as?: any
34
+ items?: T[]
35
+ color?: TabsVariants['color']
36
+ variant?: TabsVariants['variant']
37
+ size?: TabsVariants['size']
38
+ /**
39
+ * The orientation of the tabs.
40
+ * @defaultValue 'horizontal'
41
+ */
42
+ orientation?: TabsRootProps['orientation']
43
+ /**
44
+ * The content of the tabs, can be disabled to prevent rendering the content.
45
+ * @defaultValue true
46
+ */
47
+ content?: boolean
48
+ /**
49
+ * The key used to get the label from the item.
50
+ * @defaultValue 'label'
51
+ */
52
+ labelKey?: string
53
+ class?: any
54
+ b24ui?: PartialString<typeof tabs.slots>
55
+ }
56
+
57
+ export interface TabsEmits extends TabsRootEmits<string | number> {}
58
+
59
+ type SlotProps<T> = (props: { item: T, index: number }) => any
60
+
61
+ export type TabsSlots<T extends { slot?: string }> = {
62
+ leading: SlotProps<T>
63
+ default: SlotProps<T>
64
+ trailing: SlotProps<T>
65
+ content: SlotProps<T>
66
+ } & DynamicSlots<T, SlotProps<T>>
67
+ </script>
68
+
69
+ <script setup lang="ts" generic="T extends TabsItem">
70
+ import { computed } from 'vue'
71
+ import { TabsRoot, TabsList, TabsIndicator, TabsTrigger, TabsContent, useForwardPropsEmits } from 'reka-ui'
72
+ import { reactivePick } from '@vueuse/core'
73
+ import { get } from '../utils'
74
+ import B24Avatar from './Avatar.vue'
75
+
76
+ const props = withDefaults(defineProps<TabsProps<T>>(), {
77
+ content: true,
78
+ defaultValue: '0',
79
+ orientation: 'horizontal',
80
+ unmountOnHide: true,
81
+ labelKey: 'label'
82
+ })
83
+ const emits = defineEmits<TabsEmits>()
84
+ const slots = defineSlots<TabsSlots<T>>()
85
+
86
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'activationMode', 'unmountOnHide'), emits)
87
+
88
+ const b24ui = computed(() => tabs({
89
+ color: props.color,
90
+ variant: props.variant,
91
+ size: props.size,
92
+ orientation: props.orientation
93
+ }))
94
+ </script>
95
+
96
+ <template>
97
+ <TabsRoot v-bind="rootProps" :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
98
+ <TabsList :class="b24ui.list({ class: props.b24ui?.list })">
99
+ <TabsIndicator :class="b24ui.indicator({ class: props.b24ui?.indicator })" />
100
+
101
+ <TabsTrigger v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :disabled="item.disabled" :class="b24ui.trigger({ class: props.b24ui?.trigger })">
102
+ <slot name="leading" :item="item" :index="index">
103
+ <Component
104
+ :is="item.icon"
105
+ v-if="item.icon"
106
+ :class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })"
107
+ />
108
+ <B24Avatar v-else-if="item.avatar" :size="((props.b24ui?.leadingAvatarSize || b24ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="b24ui.leadingAvatar({ class: props.b24ui?.leadingAvatar })" />
109
+ </slot>
110
+
111
+ <span v-if="get(item, props.labelKey as string) || !!slots.default" :class="b24ui.label({ class: props.b24ui?.label })">
112
+ <slot :item="item" :index="index">{{ get(item, props.labelKey as string) }}</slot>
113
+ </span>
114
+
115
+ <slot name="trailing" :item="item" :index="index" />
116
+ </TabsTrigger>
117
+ </TabsList>
118
+
119
+ <template v-if="!!content">
120
+ <TabsContent v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :class="b24ui.content({ class: props.b24ui?.content })">
121
+ <slot :name="item.slot || 'content'" :item="item" :index="index">
122
+ {{ item.content }}
123
+ </slot>
124
+ </TabsContent>
125
+ </template>
126
+ </TabsRoot>
127
+ </template>
@@ -0,0 +1,216 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { AppConfig } from '@nuxt/schema'
4
+ import _appConfig from '#build/app.config'
5
+ import theme from '#build/b24ui/textarea'
6
+ import { tv } from '../utils/tv'
7
+
8
+ const appConfigTextarea = _appConfig as AppConfig & { b24ui: { textarea: Partial<typeof theme> } }
9
+
10
+ const textarea = tv({ extend: tv(theme), ...(appConfigTextarea.b24ui?.textarea || {}) })
11
+
12
+ type TextareaVariants = VariantProps<typeof textarea>
13
+
14
+ export interface TextareaProps {
15
+ /**
16
+ * The element or component this component should render as.
17
+ * @defaultValue 'div'
18
+ */
19
+ as?: any
20
+ id?: string
21
+ name?: string
22
+ /** The placeholder text when the textarea is empty. */
23
+ placeholder?: string
24
+ color?: TextareaVariants['color']
25
+ /** Removes padding from input. */
26
+ noPadding?: boolean
27
+ /** removes all borders (rings). */
28
+ noBorder?: boolean
29
+ /** removes all borders (rings) except the bottom one. */
30
+ underline?: boolean
31
+ /** Rounds the corners of the button. */
32
+ rounded?: boolean
33
+ required?: boolean
34
+ autofocus?: boolean
35
+ autofocusDelay?: number
36
+ disabled?: boolean
37
+ rows?: number
38
+ maxrows?: number
39
+ autoresize?: boolean
40
+ tag?: string
41
+ tagColor?: TextareaVariants['tagColor']
42
+ /** Highlight the ring color like a focus state. */
43
+ highlight?: boolean
44
+ class?: any
45
+ b24ui?: Partial<typeof textarea.slots>
46
+ }
47
+
48
+ export interface TextareaEmits {
49
+ (e: 'update:modelValue', payload: string | number): void
50
+ (e: 'blur', event: FocusEvent): void
51
+ (e: 'change', event: Event): void
52
+ }
53
+
54
+ export interface TextareaSlots {
55
+ default(props?: {}): any
56
+ }
57
+ </script>
58
+
59
+ <script setup lang="ts">
60
+ import { ref, computed, onMounted, nextTick, watch } from 'vue'
61
+ import { Primitive } from 'reka-ui'
62
+ import { useFormField } from '../composables/useFormField'
63
+ import { looseToNumber } from '../utils'
64
+
65
+ defineOptions({ inheritAttrs: false })
66
+
67
+ const props = withDefaults(defineProps<TextareaProps>(), {
68
+ rows: 3,
69
+ maxrows: 5,
70
+ autofocusDelay: 0
71
+ })
72
+ defineSlots<TextareaSlots>()
73
+ const emits = defineEmits<TextareaEmits>()
74
+
75
+ const [modelValue, modelModifiers] = defineModel<string | number>()
76
+
77
+ const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps>(props, { deferInputValidation: true })
78
+
79
+ const isTag = computed(() => {
80
+ return props.tag
81
+ })
82
+
83
+ const b24ui = computed(() => textarea({
84
+ color: color.value,
85
+ highlight: highlight.value,
86
+ tagColor: props.tagColor,
87
+ rounded: Boolean(props.rounded),
88
+ noPadding: Boolean(props.noPadding),
89
+ noBorder: Boolean(props.noBorder),
90
+ underline: Boolean(props.underline)
91
+ }))
92
+
93
+ const textareaRef = ref<HTMLTextAreaElement | null>(null)
94
+
95
+ function autoFocus() {
96
+ if (props.autofocus) {
97
+ textareaRef.value?.focus()
98
+ }
99
+ }
100
+
101
+ // Custom function to handle the v-model properties
102
+ function updateInput(value: string) {
103
+ if (modelModifiers.trim) {
104
+ value = value.trim()
105
+ }
106
+
107
+ if (modelModifiers.number) {
108
+ value = looseToNumber(value)
109
+ }
110
+
111
+ modelValue.value = value
112
+ emitFormInput()
113
+ }
114
+
115
+ function onInput(event: Event) {
116
+ autoResize()
117
+
118
+ if (!modelModifiers.lazy) {
119
+ updateInput((event.target as HTMLInputElement).value)
120
+ }
121
+ }
122
+
123
+ function onChange(event: Event) {
124
+ const value = (event.target as HTMLInputElement).value
125
+
126
+ if (modelModifiers.lazy) {
127
+ updateInput(value)
128
+ }
129
+
130
+ // Update trimmed textarea so that it has same behavior as native textarea https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
131
+ if (modelModifiers.trim) {
132
+ (event.target as HTMLInputElement).value = value.trim()
133
+ }
134
+
135
+ emitFormChange()
136
+ emits('change', event)
137
+ }
138
+
139
+ function onBlur(event: FocusEvent) {
140
+ emitFormBlur()
141
+ emits('blur', event)
142
+ }
143
+
144
+ onMounted(() => {
145
+ setTimeout(() => {
146
+ autoFocus()
147
+ }, props.autofocusDelay)
148
+ })
149
+
150
+ function autoResize() {
151
+ if (props.autoresize) {
152
+ if (!textareaRef.value) {
153
+ return
154
+ }
155
+
156
+ textareaRef.value.rows = props.rows
157
+ const overflow = textareaRef.value.style.overflow
158
+ textareaRef.value.style.overflow = 'hidden'
159
+
160
+ const styles = window.getComputedStyle(textareaRef.value)
161
+ const paddingTop = Number.parseInt(styles.paddingTop)
162
+ const paddingBottom = Number.parseInt(styles.paddingBottom)
163
+ const padding = paddingTop + paddingBottom
164
+ const lineHeight = Number.parseInt(styles.lineHeight)
165
+ const { scrollHeight } = textareaRef.value
166
+ const newRows = (scrollHeight - padding) / lineHeight
167
+
168
+ if (newRows > props.rows) {
169
+ textareaRef.value.rows = props.maxrows ? Math.min(newRows, props.maxrows) : newRows
170
+ }
171
+
172
+ textareaRef.value.style.overflow = overflow
173
+ }
174
+ }
175
+
176
+ watch(modelValue, () => {
177
+ nextTick(autoResize)
178
+ })
179
+
180
+ defineExpose({
181
+ textareaRef
182
+ })
183
+
184
+ onMounted(() => {
185
+ setTimeout(() => {
186
+ autoResize()
187
+ }, 100)
188
+ })
189
+ </script>
190
+
191
+ <template>
192
+ <Primitive :as="as" :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
193
+ <div v-if="isTag" :class="b24ui.tag({ class: props.b24ui?.tag })">
194
+ {{ props.tag }}
195
+ </div>
196
+
197
+ <textarea
198
+ :id="id"
199
+ ref="textareaRef"
200
+ :value="modelValue"
201
+ :name="name"
202
+ :rows="rows"
203
+ :placeholder="placeholder"
204
+ :class="b24ui.base({ class: props.b24ui?.base })"
205
+ :disabled="disabled"
206
+ :required="required"
207
+ v-bind="{ ...$attrs, ...ariaAttrs }"
208
+ @input="onInput"
209
+ @blur="onBlur"
210
+ @change="onChange"
211
+ @focus="emitFormFocus"
212
+ />
213
+
214
+ <slot />
215
+ </Primitive>
216
+ </template>
@@ -0,0 +1,168 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { ToastRootProps, ToastRootEmits } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/toast'
7
+ import { tv } from '../utils/tv'
8
+ import type { AvatarProps, ButtonProps, IconComponent } from '../types'
9
+
10
+ const appConfigToast = _appConfig as AppConfig & { b24ui: { toast: Partial<typeof theme> } }
11
+
12
+ const toast = tv({ extend: tv(theme), ...(appConfigToast.b24ui?.toast || {}) })
13
+
14
+ type ToastVariants = VariantProps<typeof toast>
15
+
16
+ export interface ToastProps extends Pick<ToastRootProps, 'defaultOpen' | 'open' | 'type' | 'duration'> {
17
+ /**
18
+ * The element or component this component should render as.
19
+ * @defaultValue 'li'
20
+ */
21
+ as?: any
22
+ title?: string
23
+ description?: string
24
+ icon?: IconComponent
25
+ avatar?: AvatarProps
26
+ color?: ToastVariants['color']
27
+ /**
28
+ * Display a list of actions:
29
+ * - under the title and description if multiline
30
+ * - next to the close button if not multiline
31
+ * `{ size: 'xs' }`{lang="ts-type"}
32
+ */
33
+ actions?: ButtonProps[]
34
+ /**
35
+ * Display a close button to dismiss the toast.
36
+ * `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
37
+ * @defaultValue true
38
+ */
39
+ close?: ButtonProps | boolean
40
+ /**
41
+ * The icon displayed in the close button.
42
+ * @defaultValue icons.close
43
+ */
44
+ closeIcon?: IconComponent
45
+ class?: any
46
+ b24ui?: Partial<typeof toast.slots>
47
+ }
48
+
49
+ export interface ToastEmits extends ToastRootEmits {}
50
+
51
+ export interface ToastSlots {
52
+ leading(props?: {}): any
53
+ title(props?: {}): any
54
+ description(props?: {}): any
55
+ actions(props?: {}): any
56
+ close(props: { b24ui: any }): any
57
+ }
58
+ </script>
59
+
60
+ <script setup lang="ts">
61
+ import { ref, computed, onMounted } from 'vue'
62
+ import { ToastRoot, ToastTitle, ToastDescription, ToastAction, ToastClose, useForwardPropsEmits } from 'reka-ui'
63
+ import { reactivePick } from '@vueuse/core'
64
+ import { useLocale } from '../composables/useLocale'
65
+ import icons from '../dictionary/icons'
66
+ import B24Avatar from './Avatar.vue'
67
+ import B24Button from './Button.vue'
68
+
69
+ const props = withDefaults(defineProps<ToastProps>(), {
70
+ close: true
71
+ })
72
+ const emits = defineEmits<ToastEmits>()
73
+ const slots = defineSlots<ToastSlots>()
74
+
75
+ const { t } = useLocale()
76
+
77
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'duration', 'type'), emits)
78
+
79
+ const multiline = computed(() => !!props.title && !!props.description)
80
+
81
+ const b24ui = computed(() => toast({
82
+ color: props.color
83
+ }))
84
+
85
+ const el = ref()
86
+ const height = ref(0)
87
+
88
+ onMounted(() => {
89
+ if (!el.value) {
90
+ return
91
+ }
92
+
93
+ setTimeout(() => {
94
+ height.value = el.value.$el.getBoundingClientRect()?.height
95
+ }, 0)
96
+ })
97
+
98
+ defineExpose({
99
+ height
100
+ })
101
+ </script>
102
+
103
+ <template>
104
+ <ToastRoot
105
+ ref="el"
106
+ v-slot="{ remaining, duration }"
107
+ v-bind="rootProps"
108
+ :class="b24ui.root({ class: [props.class, props.b24ui?.root], multiline })"
109
+ :style="{ '--height': height }"
110
+ >
111
+ <slot name="leading">
112
+ <B24Avatar v-if="avatar" :size="((props.b24ui?.avatarSize || b24ui.avatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="b24ui.avatar({ class: props.b24ui?.avatar })" />
113
+ <Component
114
+ :is="icon"
115
+ v-if="icon"
116
+ :class="b24ui.icon({ class: props.b24ui?.icon })"
117
+ />
118
+ </slot>
119
+
120
+ <div :class="b24ui.wrapper({ class: props.b24ui?.wrapper })">
121
+ <ToastTitle v-if="title || !!slots.title" :class="b24ui.title({ class: props.b24ui?.title })">
122
+ <slot name="title">
123
+ {{ title }}
124
+ </slot>
125
+ </ToastTitle>
126
+ <ToastDescription v-if="description || !!slots.description" :class="b24ui.description({ class: props.b24ui?.description })">
127
+ <slot name="description">
128
+ {{ description }}
129
+ </slot>
130
+ </ToastDescription>
131
+
132
+ <div v-if="multiline && actions?.length" :class="b24ui.actions({ class: props.b24ui?.actions, multiline: true })">
133
+ <slot name="actions">
134
+ <ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
135
+ <B24Button size="xs" :color="color" v-bind="action" />
136
+ </ToastAction>
137
+ </slot>
138
+ </div>
139
+ </div>
140
+
141
+ <div v-if="(!multiline && actions?.length) || close !== null" :class="b24ui.actions({ class: props.b24ui?.actions, multiline: false })">
142
+ <template v-if="!multiline">
143
+ <slot name="actions">
144
+ <ToastAction v-for="(action, index) in actions" :key="index" :alt-text="action.label || 'Action'" as-child @click.stop>
145
+ <B24Button size="xs" :color="color" v-bind="action" />
146
+ </ToastAction>
147
+ </slot>
148
+ </template>
149
+
150
+ <ToastClose as-child>
151
+ <slot name="close" :b24ui="b24ui">
152
+ <B24Button
153
+ v-if="close"
154
+ :icon="closeIcon || icons.close"
155
+ size="xs"
156
+ color="link"
157
+ :aria-label="t('toast.close')"
158
+ v-bind="typeof close === 'object' ? close : undefined"
159
+ :class="b24ui.close({ class: props.b24ui?.close })"
160
+ @click.stop
161
+ />
162
+ </slot>
163
+ </ToastClose>
164
+ </div>
165
+
166
+ <div v-if="remaining > 0 && duration" :class="b24ui.progress({ class: props.b24ui?.progress })" :style="{ width: `${remaining / duration * 100}%` }" />
167
+ </ToastRoot>
168
+ </template>