@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,219 @@
1
+ <script lang="ts">
2
+ import type { ButtonHTMLAttributes } from 'vue'
3
+ import type { AppConfig } from '@nuxt/schema'
4
+ import _appConfig from '#build/app.config'
5
+ import type { RouterLinkProps, RouteLocationRaw } from 'vue-router'
6
+ import theme from '#build/b24ui/link'
7
+ import { tv } from '../utils/tv'
8
+
9
+ interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
10
+ /**
11
+ * Route Location the link should navigate to when clicked on.
12
+ */
13
+ to?: RouteLocationRaw // need to manually type to avoid breaking typedPages
14
+ /**
15
+ * An alias for `to`. If used with `to`, `href` will be ignored
16
+ */
17
+ href?: NuxtLinkProps['to']
18
+ /**
19
+ * Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases
20
+ */
21
+ external?: boolean
22
+ /**
23
+ * Where to display the linked URL as the name for a browsing context.
24
+ */
25
+ target?: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null
26
+ /**
27
+ * A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links.
28
+ */
29
+ rel?: 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | (string & {}) | null
30
+ /**
31
+ * If set to true, no rel attribute will be added to the link
32
+ */
33
+ noRel?: boolean
34
+ /**
35
+ * A class to apply to links that have been prefetched.
36
+ */
37
+ prefetchedClass?: string
38
+ /**
39
+ * When enabled will prefetch middleware, layouts and payloads of links in the viewport.
40
+ */
41
+ prefetch?: boolean
42
+ /**
43
+ * Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
44
+ */
45
+ prefetchOn?: 'visibility' | 'interaction' | Partial<{
46
+ visibility: boolean
47
+ interaction: boolean
48
+ }>
49
+ /**
50
+ * Escape hatch to disable `prefetch` attribute.
51
+ */
52
+ noPrefetch?: boolean
53
+ }
54
+
55
+ const appConfigLink = _appConfig as AppConfig & { b24ui: { link: Partial<typeof theme> } }
56
+
57
+ const link = tv({ extend: tv(theme), ...(appConfigLink.b24ui?.link || {}) })
58
+
59
+ export interface LinkProps extends NuxtLinkProps {
60
+ /**
61
+ * The element or component this component should render as when not a link.
62
+ * @defaultValue 'button'
63
+ */
64
+ as?: any
65
+ /**
66
+ * The type of the button when not a link.
67
+ * @defaultValue 'button'
68
+ */
69
+ type?: ButtonHTMLAttributes['type']
70
+ disabled?: boolean
71
+ /** Force the link to be active independent of the current route. */
72
+ active?: boolean
73
+ /** Will only be active if the current route is an exact match. */
74
+ exact?: boolean
75
+ /** Allows controlling how the current route query sets the link as active. */
76
+ exactQuery?: boolean | 'partial'
77
+ /** Will only be active if the current route hash is an exact match. */
78
+ exactHash?: boolean
79
+ /** The class to apply when the link is inactive. */
80
+ inactiveClass?: string
81
+ custom?: boolean
82
+ /** When `true`, uses special underlined styling. */
83
+ isAction?: boolean
84
+ /** When `true`, only styles from `class`, `activeClass`, and `inactiveClass` will be applied. */
85
+ raw?: boolean
86
+ class?: any
87
+ }
88
+
89
+ export interface LinkSlots {
90
+ default(props: { active: boolean }): any
91
+ }
92
+ </script>
93
+
94
+ <script setup lang="ts">
95
+ import { computed } from 'vue'
96
+ import { isEqual, diff } from 'ohash'
97
+ import { useForwardProps } from 'reka-ui'
98
+ import { reactiveOmit } from '@vueuse/core'
99
+ import { useRoute } from '#imports'
100
+ import B24LinkBase from './LinkBase.vue'
101
+
102
+ defineOptions({ inheritAttrs: false })
103
+
104
+ const props = withDefaults(defineProps<LinkProps>(), {
105
+ as: 'button',
106
+ type: 'button',
107
+ active: undefined,
108
+ isAction: false,
109
+ activeClass: '',
110
+ inactiveClass: ''
111
+ })
112
+ defineSlots<LinkSlots>()
113
+
114
+ const route = useRoute()
115
+ const nuxtLinkProps = useForwardProps(reactiveOmit(props, 'as', 'type', 'disabled', 'active', 'exact', 'exactQuery', 'exactHash', 'activeClass', 'inactiveClass', 'raw', 'class'))
116
+
117
+ const b24ui = computed(() => tv({
118
+ extend: link,
119
+ variants: {
120
+ active: {
121
+ true: props.activeClass,
122
+ false: props.inactiveClass
123
+ }
124
+ }
125
+ }))
126
+
127
+ function isPartiallyEqual(item1: any, item2: any) {
128
+ const diffedKeys = diff(item1, item2).reduce((filtered, q) => {
129
+ if (q.type === 'added') {
130
+ filtered.push(q.key)
131
+ }
132
+ return filtered
133
+ }, [] as string[])
134
+ return isEqual(item1, item2, { excludeKeys: key => diffedKeys.includes(key) })
135
+ }
136
+
137
+ function isLinkActive({ route: linkRoute, isActive, isExactActive }: any) {
138
+ if (props.active !== undefined) {
139
+ return props.active
140
+ }
141
+
142
+ if (props.exactQuery === 'partial') {
143
+ if (!isPartiallyEqual(linkRoute.query, route.query)) return false
144
+ } else if (props.exactQuery === true) {
145
+ if (!isEqual(linkRoute.query, route.query)) return false
146
+ }
147
+
148
+ if (props.exactHash && linkRoute.hash !== route.hash) {
149
+ return false
150
+ }
151
+
152
+ if (props.exact && isExactActive) {
153
+ return true
154
+ }
155
+
156
+ if (!props.exact && isActive) {
157
+ return true
158
+ }
159
+
160
+ return false
161
+ }
162
+
163
+ function resolveLinkClass({ route, isActive, isExactActive }: any) {
164
+ const active = isLinkActive({ route, isActive, isExactActive })
165
+
166
+ if (props.raw) {
167
+ return [props.class, active ? props.activeClass : props.inactiveClass]
168
+ }
169
+
170
+ return b24ui.value({
171
+ class: props.class,
172
+ active,
173
+ disabled: props.disabled,
174
+ isAction: Boolean(props.isAction)
175
+ })
176
+ }
177
+ </script>
178
+
179
+ <template>
180
+ <NuxtLink
181
+ v-slot="{ href, navigate, route: linkRoute, rel, target, isExternal, isActive, isExactActive }"
182
+ v-bind="nuxtLinkProps"
183
+ custom
184
+ >
185
+ <template v-if="custom">
186
+ <slot
187
+ v-bind="{
188
+ ...$attrs,
189
+ as,
190
+ type,
191
+ disabled,
192
+ href,
193
+ navigate,
194
+ rel,
195
+ target,
196
+ isExternal,
197
+ active: isLinkActive({ route: linkRoute, isActive, isExactActive })
198
+ }"
199
+ />
200
+ </template>
201
+ <B24LinkBase
202
+ v-else
203
+ v-bind="{
204
+ ...$attrs,
205
+ as,
206
+ type,
207
+ disabled,
208
+ href,
209
+ navigate,
210
+ rel,
211
+ target,
212
+ isExternal
213
+ }"
214
+ :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive })"
215
+ >
216
+ <slot :active="isLinkActive({ route: linkRoute, isActive, isExactActive })" />
217
+ </B24LinkBase>
218
+ </NuxtLink>
219
+ </template>
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ export interface LinkBaseProps {
3
+ as?: string
4
+ type?: string
5
+ disabled?: boolean
6
+ onClick?: ((e: MouseEvent) => void | Promise<void>) | Array<((e: MouseEvent) => void | Promise<void>)>
7
+ href?: string
8
+ navigate?: (e: MouseEvent) => void
9
+ rel?: string
10
+ target?: string
11
+ isExternal?: boolean
12
+ }
13
+ </script>
14
+
15
+ <script setup lang="ts">
16
+ import { Primitive } from 'reka-ui'
17
+
18
+ const props = withDefaults(defineProps<LinkBaseProps>(), {
19
+ as: 'button',
20
+ type: 'button'
21
+ })
22
+
23
+ function onClickWrapper(e: MouseEvent) {
24
+ if (props.disabled) {
25
+ e.stopPropagation()
26
+ e.preventDefault()
27
+ return
28
+ }
29
+
30
+ if (props.onClick) {
31
+ for (const onClick of Array.isArray(props.onClick) ? props.onClick : [props.onClick]) {
32
+ onClick(e)
33
+ }
34
+ }
35
+
36
+ if (props.href && props.navigate && !props.isExternal) {
37
+ props.navigate(e)
38
+ }
39
+ }
40
+ </script>
41
+
42
+ <template>
43
+ <Primitive
44
+ v-bind="href ? {
45
+ 'as': 'a',
46
+ 'href': disabled ? undefined : href,
47
+ 'aria-disabled': disabled ? 'true' : undefined,
48
+ 'role': disabled ? 'link' : undefined,
49
+ 'tabindex': disabled ? -1 : undefined
50
+ } : as === 'button' ? {
51
+ as,
52
+ type,
53
+ disabled
54
+ } : {
55
+ as
56
+ }"
57
+ :rel="rel"
58
+ :target="target"
59
+ @click="onClickWrapper"
60
+ >
61
+ <slot />
62
+ </Primitive>
63
+ </template>
@@ -0,0 +1,182 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
2
+ <script lang="ts">
3
+ import type { VariantProps } from 'tailwind-variants'
4
+ import type { ProgressRootProps, ProgressRootEmits } from 'reka-ui'
5
+ import type { AppConfig } from '@nuxt/schema'
6
+ import _appConfig from '#build/app.config'
7
+ import theme from '#build/b24ui/progress'
8
+ import { tv } from '../utils/tv'
9
+
10
+ const appConfigProgress = _appConfig as AppConfig & { b24ui: { progress: Partial<typeof theme> } }
11
+
12
+ const progress = tv({ extend: tv(theme), ...(appConfigProgress.b24ui?.progress || {}) })
13
+
14
+ type ProgressVariants = VariantProps<typeof progress>
15
+
16
+ export interface ProgressProps extends Pick<ProgressRootProps, 'getValueLabel' | 'modelValue'> {
17
+ /**
18
+ * The element or component this component should render as.
19
+ * @defaultValue 'div'
20
+ */
21
+ as?: any
22
+ /** The maximum progress value. */
23
+ max?: number | Array<any>
24
+ /** Display the current progress value. */
25
+ status?: boolean
26
+ /** Whether the progress is visually inverted. */
27
+ inverted?: boolean
28
+ size?: ProgressVariants['size']
29
+ color?: ProgressVariants['color']
30
+ /**
31
+ * The orientation of the progress bar.
32
+ * @defaultValue 'horizontal'
33
+ */
34
+ orientation?: ProgressVariants['orientation']
35
+ animation?: ProgressVariants['animation']
36
+ class?: any
37
+ b24ui?: Partial<typeof progress.slots>
38
+ }
39
+
40
+ export interface ProgressEmits extends ProgressRootEmits {}
41
+
42
+ export type ProgressSlots = {
43
+ status(props: { percent?: number }): any
44
+ } & {
45
+ [key: string]: (props: { step: number }) => any
46
+ }
47
+
48
+ </script>
49
+
50
+ <script setup lang="ts">
51
+ import { computed } from 'vue'
52
+ import { Primitive, ProgressRoot, ProgressIndicator, useForwardPropsEmits } from 'reka-ui'
53
+ import { reactivePick } from '@vueuse/core'
54
+ import { useLocale } from '../composables/useLocale'
55
+
56
+ const props = withDefaults(defineProps<ProgressProps>(), {
57
+ inverted: false,
58
+ modelValue: null,
59
+ orientation: 'horizontal'
60
+ })
61
+ const emits = defineEmits<ProgressEmits>()
62
+ const slots = defineSlots<ProgressSlots>()
63
+
64
+ const { dir } = useLocale()
65
+
66
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'getValueLabel', 'modelValue'), emits)
67
+
68
+ const isIndeterminate = computed(() => rootProps.value.modelValue === null)
69
+ const hasSteps = computed(() => Array.isArray(props.max))
70
+
71
+ const realMax = computed(() => {
72
+ if (isIndeterminate.value || !props.max) {
73
+ return undefined
74
+ }
75
+
76
+ if (Array.isArray(props.max)) {
77
+ return props.max.length - 1
78
+ }
79
+
80
+ return Number(props.max)
81
+ })
82
+
83
+ const percent = computed(() => {
84
+ if (isIndeterminate.value) {
85
+ return undefined
86
+ }
87
+
88
+ switch (true) {
89
+ case rootProps.value.modelValue! < 0: return 0
90
+ case rootProps.value.modelValue! > (realMax.value ?? 100): return 100
91
+ default: return Math.round((rootProps.value.modelValue! / (realMax.value ?? 100)) * 100)
92
+ }
93
+ })
94
+
95
+ const indicatorStyle = computed(() => {
96
+ if (percent.value === undefined) {
97
+ return
98
+ }
99
+
100
+ if (props.orientation === 'vertical') {
101
+ return {
102
+ transform: `translateY(${props.inverted ? '' : '-'}${100 - percent.value}%)`
103
+ }
104
+ } else {
105
+ if (dir.value === 'rtl') {
106
+ return {
107
+ transform: `translateX(${props.inverted ? '-' : ''}${100 - percent.value}%)`
108
+ }
109
+ } else {
110
+ return {
111
+ transform: `translateX(${props.inverted ? '' : '-'}${100 - percent.value}%)`
112
+ }
113
+ }
114
+ }
115
+ })
116
+
117
+ const statusStyle = computed(() => {
118
+ return {
119
+ [props.orientation === 'vertical' ? 'height' : 'width']: percent.value ? `${percent.value}%` : 'fit-content'
120
+ }
121
+ })
122
+
123
+ function isActive(index: number) {
124
+ return index === Number(props.modelValue)
125
+ }
126
+
127
+ function isFirst(index: number) {
128
+ return index === 0
129
+ }
130
+
131
+ function isLast(index: number) {
132
+ return index === realMax.value
133
+ }
134
+
135
+ function stepVariant(index: number | string) {
136
+ index = Number(index)
137
+
138
+ if (isActive(index) && !isFirst(index)) {
139
+ return 'active'
140
+ }
141
+
142
+ if (isFirst(index) && isActive(index)) {
143
+ return 'first'
144
+ }
145
+
146
+ if (isLast(index) && isActive(index)) {
147
+ return 'last'
148
+ }
149
+
150
+ return 'other'
151
+ }
152
+
153
+ const b24ui = computed(() => progress({
154
+ animation: props.animation,
155
+ size: props.size,
156
+ color: props.color,
157
+ orientation: props.orientation,
158
+ inverted: props.inverted
159
+ }))
160
+ </script>
161
+
162
+ <template>
163
+ <Primitive :as="as" :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
164
+ <div v-if="!isIndeterminate && (status || !!slots.status)" :class="b24ui.status({ class: props.b24ui?.status })" :style="statusStyle">
165
+ <slot name="status" :percent="percent">
166
+ {{ percent }}%
167
+ </slot>
168
+ </div>
169
+
170
+ <ProgressRoot v-bind="rootProps" :max="realMax" :class="b24ui.base({ class: props.b24ui?.base })" style="transform: translateZ(0)">
171
+ <ProgressIndicator :class="b24ui.indicator({ class: props.b24ui?.indicator })" :style="indicatorStyle" />
172
+ </ProgressRoot>
173
+
174
+ <div v-if="hasSteps" :class="b24ui.steps({ class: props.b24ui?.steps })">
175
+ <div v-for="(step, index) in max" :key="index" :class="b24ui.step({ class: props.b24ui?.step, step: stepVariant(index) })">
176
+ <slot :name="`step-${index}`" :step="step">
177
+ {{ step }}
178
+ </slot>
179
+ </div>
180
+ </div>
181
+ </Primitive>
182
+ </template>
@@ -0,0 +1,178 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { RadioGroupRootProps, RadioGroupRootEmits, AcceptableValue } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/radio-group'
7
+ import { tv } from '../utils/tv'
8
+
9
+ const appConfigRadioGroup = _appConfig as AppConfig & { b24ui: { radioGroup: Partial<typeof theme> } }
10
+
11
+ const radioGroup = tv({ extend: tv(theme), ...(appConfigRadioGroup.b24ui?.radioGroup || {}) })
12
+
13
+ type RadioGroupVariants = VariantProps<typeof radioGroup>
14
+
15
+ export interface RadioGroupItem {
16
+ label?: string
17
+ description?: string
18
+ disabled?: boolean
19
+ value?: string
20
+ }
21
+
22
+ export interface RadioGroupProps<T> extends Pick<RadioGroupRootProps, 'defaultValue' | 'disabled' | 'loop' | 'modelValue' | 'name' | 'required'> {
23
+ /**
24
+ * The element or component this component should render as.
25
+ * @defaultValue 'div'
26
+ */
27
+ as?: any
28
+ legend?: string
29
+ /**
30
+ * When `items` is an array of objects, select the field to use as the value.
31
+ * @defaultValue 'value'
32
+ */
33
+ valueKey?: string
34
+ /**
35
+ * When `items` is an array of objects, select the field to use as the label.
36
+ * @defaultValue 'label'
37
+ */
38
+ labelKey?: string
39
+ /**
40
+ * When `items` is an array of objects, select the field to use as the description.
41
+ * @defaultValue 'description'
42
+ */
43
+ descriptionKey?: string
44
+ items?: T[]
45
+ size?: RadioGroupVariants['size']
46
+ color?: RadioGroupVariants['color']
47
+ /**
48
+ * The orientation the radio buttons are laid out.
49
+ * @defaultValue 'vertical'
50
+ */
51
+ orientation?: RadioGroupRootProps['orientation']
52
+ class?: any
53
+ b24ui?: Partial<typeof radioGroup.slots>
54
+ }
55
+
56
+ export type RadioGroupEmits = RadioGroupRootEmits & {
57
+ change: [payload: Event]
58
+ }
59
+
60
+ type SlotProps<T> = (props: { item: T, modelValue?: AcceptableValue }) => any
61
+
62
+ export interface RadioGroupSlots<T> {
63
+ legend(props?: {}): any
64
+ label: SlotProps<T>
65
+ description: SlotProps<T>
66
+ }
67
+ </script>
68
+
69
+ <script setup lang="ts" generic="T extends RadioGroupItem | AcceptableValue">
70
+ import { computed, useId } from 'vue'
71
+ import { RadioGroupRoot, RadioGroupItem, RadioGroupIndicator, Label, useForwardPropsEmits } from 'reka-ui'
72
+ import { reactivePick } from '@vueuse/core'
73
+ import { useFormField } from '../composables/useFormField'
74
+ import { get } from '../utils'
75
+
76
+ const props = withDefaults(defineProps<RadioGroupProps<T>>(), {
77
+ valueKey: 'value',
78
+ labelKey: 'label',
79
+ descriptionKey: 'description',
80
+ orientation: 'vertical'
81
+ })
82
+ const emits = defineEmits<RadioGroupEmits>()
83
+ const slots = defineSlots<RadioGroupSlots<T>>()
84
+
85
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits)
86
+
87
+ const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<RadioGroupProps<T>>(props, { bind: false })
88
+ const id = _id.value ?? useId()
89
+
90
+ const b24ui = computed(() => radioGroup({
91
+ size: size.value,
92
+ color: color.value,
93
+ disabled: disabled.value,
94
+ required: props.required,
95
+ orientation: props.orientation
96
+ }))
97
+
98
+ function normalizeItem(item: any) {
99
+ if (['string', 'number', 'boolean'].includes(typeof item)) {
100
+ return {
101
+ id: `${id}:${item}`,
102
+ value: item,
103
+ label: item
104
+ }
105
+ }
106
+
107
+ const value = get(item, props.valueKey as string)
108
+ const label = get(item, props.labelKey as string)
109
+ const description = get(item, props.descriptionKey as string)
110
+
111
+ return {
112
+ ...item,
113
+ value,
114
+ label,
115
+ description,
116
+ id: `${id}:${value}`
117
+ }
118
+ }
119
+
120
+ const normalizedItems = computed(() => {
121
+ if (!props.items) {
122
+ return []
123
+ }
124
+
125
+ return props.items.map(normalizeItem)
126
+ })
127
+
128
+ function onUpdate(value: any) {
129
+ // @ts-expect-error - 'target' does not exist in type 'EventInit'
130
+ const event = new Event('change', { target: { value } })
131
+ emits('change', event)
132
+ emitFormChange()
133
+ emitFormInput()
134
+ }
135
+ </script>
136
+
137
+ <template>
138
+ <RadioGroupRoot
139
+ :id="id"
140
+ v-slot="{ modelValue }"
141
+ v-bind="rootProps"
142
+ :name="name"
143
+ :disabled="disabled"
144
+ :class="b24ui.root({ class: [props.class, props.b24ui?.root] })"
145
+ @update:model-value="onUpdate"
146
+ >
147
+ <fieldset :class="b24ui.fieldset({ class: props.b24ui?.fieldset })" v-bind="ariaAttrs">
148
+ <legend v-if="legend || !!slots.legend" :class="b24ui.legend({ class: props.b24ui?.legend })">
149
+ <slot name="legend">
150
+ {{ legend }}
151
+ </slot>
152
+ </legend>
153
+ <div v-for="item in normalizedItems" :key="item.value" :class="b24ui.item({ class: props.b24ui?.item })">
154
+ <div :class="b24ui.container({ class: props.b24ui?.container })">
155
+ <RadioGroupItem
156
+ :id="item.id"
157
+ :value="item.value"
158
+ :disabled="disabled"
159
+ :class="b24ui.base({ class: props.b24ui?.base })"
160
+ >
161
+ <RadioGroupIndicator :class="b24ui.indicator({ class: props.b24ui?.indicator })" />
162
+ </RadioGroupItem>
163
+ </div>
164
+
165
+ <div :class="b24ui.wrapper({ class: props.b24ui?.wrapper })">
166
+ <Label :class="b24ui.label({ class: props.b24ui?.label })" :for="item.id">
167
+ <slot name="label" :item="item" :model-value="modelValue">{{ item.label }}</slot>
168
+ </Label>
169
+ <p v-if="item.description || !!slots.description" :class="b24ui.description({ class: props.b24ui?.description })">
170
+ <slot name="description" :item="item" :model-value="modelValue">
171
+ {{ item.description }}
172
+ </slot>
173
+ </p>
174
+ </div>
175
+ </div>
176
+ </fieldset>
177
+ </RadioGroupRoot>
178
+ </template>