@una-ui/nuxt 0.1.0-beta.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -0
  3. package/dist/module.cjs +5 -0
  4. package/dist/module.d.ts +31 -0
  5. package/dist/module.json +8 -0
  6. package/dist/module.mjs +93 -0
  7. package/dist/runtime/components/elements/Accordion.vue +201 -0
  8. package/dist/runtime/components/elements/Alert.vue +138 -0
  9. package/dist/runtime/components/elements/Avatar.vue +80 -0
  10. package/dist/runtime/components/elements/AvatarGroup.vue +27 -0
  11. package/dist/runtime/components/elements/Badge.vue +50 -0
  12. package/dist/runtime/components/elements/Button.vue +94 -0
  13. package/dist/runtime/components/elements/Icon.vue +9 -0
  14. package/dist/runtime/components/elements/Indicator.vue +60 -0
  15. package/dist/runtime/components/forms/FormGroup.vue +141 -0
  16. package/dist/runtime/components/forms/Input.vue +151 -0
  17. package/dist/runtime/components/forms/Switch.vue +117 -0
  18. package/dist/runtime/components/misc/ThemeSwitcher.vue +111 -0
  19. package/dist/runtime/components/slots/AvatarGroupDefault.d.ts +22 -0
  20. package/dist/runtime/components/slots/AvatarGroupDefault.mjs +44 -0
  21. package/dist/runtime/components/slots/FormGroupDefault.d.ts +25 -0
  22. package/dist/runtime/components/slots/FormGroupDefault.mjs +23 -0
  23. package/dist/runtime/composables/themes.d.ts +7 -0
  24. package/dist/runtime/composables/themes.mjs +119 -0
  25. package/dist/runtime/plugins/theme.client.d.ts +5 -0
  26. package/dist/runtime/plugins/theme.client.mjs +28 -0
  27. package/dist/runtime/plugins/theme.server.d.ts +2 -0
  28. package/dist/runtime/plugins/theme.server.mjs +24 -0
  29. package/dist/runtime/types/accordion.d.ts +112 -0
  30. package/dist/runtime/types/accordion.mjs +0 -0
  31. package/dist/runtime/types/alert.d.ts +55 -0
  32. package/dist/runtime/types/alert.mjs +0 -0
  33. package/dist/runtime/types/avatar-group.d.ts +26 -0
  34. package/dist/runtime/types/avatar-group.mjs +0 -0
  35. package/dist/runtime/types/avatar.d.ts +71 -0
  36. package/dist/runtime/types/avatar.mjs +0 -0
  37. package/dist/runtime/types/badge.d.ts +41 -0
  38. package/dist/runtime/types/badge.mjs +0 -0
  39. package/dist/runtime/types/button.d.ts +95 -0
  40. package/dist/runtime/types/button.mjs +0 -0
  41. package/dist/runtime/types/form-group.d.ts +83 -0
  42. package/dist/runtime/types/form-group.mjs +0 -0
  43. package/dist/runtime/types/icon.d.ts +9 -0
  44. package/dist/runtime/types/icon.mjs +0 -0
  45. package/dist/runtime/types/index.d.ts +11 -0
  46. package/dist/runtime/types/index.mjs +11 -0
  47. package/dist/runtime/types/indicator.d.ts +45 -0
  48. package/dist/runtime/types/indicator.mjs +0 -0
  49. package/dist/runtime/types/input.d.ts +91 -0
  50. package/dist/runtime/types/input.mjs +0 -0
  51. package/dist/runtime/types/switch.d.ts +69 -0
  52. package/dist/runtime/types/switch.mjs +0 -0
  53. package/dist/runtime/utils/index.d.ts +19 -0
  54. package/dist/runtime/utils/index.mjs +36 -0
  55. package/dist/types.d.ts +15 -0
  56. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 <https://github.com/phojie>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # 🌼 Una-UI
2
+
3
+ > **Warning**: This project is heavily working in progress.
4
+
5
+ ## 📌 Todo
6
+
7
+ - [ ] Playground
8
+ - [ ] Docs
9
+ - [ ] Style Presets
10
+ - [ ] UI-Components
11
+ - [ ] Nuxt 3 modules
12
+
13
+ <!-- [![NPM version](https://img.shields.io/npm/v/jieui?color=a1b858&label=)](https://www.npmjs.com/package/@una-ui) -->
14
+
15
+ <!-- ## Sponsors -->
16
+
17
+ <!-- <p align="center">
18
+ </p> -->
19
+
20
+ ## 🏛️ License
21
+
22
+ [MIT](./LICENSE) License © 2023 [Phojie](https://github.com/phojie)
@@ -0,0 +1,5 @@
1
+ module.exports = function(...args) {
2
+ return import('./module.mjs').then(m => m.default.call(this, ...args))
3
+ }
4
+ const _meta = module.exports.meta = require('./module.json')
5
+ module.exports.getMeta = () => Promise.resolve(_meta)
@@ -0,0 +1,31 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ /**
5
+ * @default 'N'
6
+ */
7
+ prefix?: string;
8
+ /**
9
+ * @default true
10
+ * @description Enable themeable ui
11
+ *
12
+ */
13
+ themeable?: boolean;
14
+ /**
15
+ * @default true
16
+ * @description Register components globally
17
+ */
18
+ global?: boolean;
19
+ /**
20
+ * @default '@una-ui/preset'
21
+ * @description Path to preset
22
+ */
23
+ preset?: string;
24
+ /**
25
+ * @default false
26
+ */
27
+ dev: boolean;
28
+ }
29
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
30
+
31
+ export { ModuleOptions, _default as default };
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@una-ui/nuxt",
3
+ "configKey": "una",
4
+ "version": "0.1.0-beta.1",
5
+ "compatibility": {
6
+ "nuxt": "^3.0.0-rc.8"
7
+ }
8
+ }
@@ -0,0 +1,93 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { defineNuxtModule, createResolver, addComponentsDir, addPlugin, installModule } from '@nuxt/kit';
3
+ import { presetUno, presetAttributify, presetIcons, transformerDirectives, transformerVariantGroup } from 'unocss';
4
+ import presetUna from '@una-ui/preset';
5
+
6
+ const name = "@una-ui/nuxt";
7
+ const version = "0.1.0-beta.1";
8
+
9
+ function extendUnocssOptions(user = {}) {
10
+ return {
11
+ ...user,
12
+ preflight: false,
13
+ presets: [
14
+ presetUno({
15
+ // attributifyPseudo: true,
16
+ }),
17
+ presetAttributify(),
18
+ presetIcons({
19
+ scale: 1.2,
20
+ extraProperties: {
21
+ "display": "inline-block",
22
+ "vertical-align": "middle"
23
+ }
24
+ }),
25
+ presetUna(),
26
+ ...user.presets || []
27
+ ],
28
+ transformers: [
29
+ transformerDirectives(),
30
+ transformerVariantGroup()
31
+ ]
32
+ };
33
+ }
34
+
35
+ function rPath(p) {
36
+ return fileURLToPath(new URL(p, import.meta.url).toString());
37
+ }
38
+ const module = defineNuxtModule({
39
+ meta: {
40
+ name,
41
+ configKey: "una",
42
+ version,
43
+ compatibility: {
44
+ nuxt: "^3.0.0-rc.8"
45
+ }
46
+ },
47
+ defaults: {
48
+ prefix: "N",
49
+ themeable: true,
50
+ preset: rPath("./preset"),
51
+ global: true,
52
+ dev: false
53
+ },
54
+ async setup(options, nuxt) {
55
+ const { resolve } = createResolver(import.meta.url);
56
+ nuxt.options.css.unshift("@unocss/reset/tailwind-compat.css");
57
+ nuxt.options.css.unshift("@una-ui/preset/una.css");
58
+ const runtimeDir = resolve("./runtime");
59
+ nuxt.options.build.transpile.push(runtimeDir);
60
+ nuxt.options.build.transpile.push("@headlessui/vue");
61
+ addComponentsDir({
62
+ path: resolve(runtimeDir, "components", "elements"),
63
+ prefix: options.prefix,
64
+ global: options.global,
65
+ watch: nuxt.options.dev
66
+ });
67
+ addComponentsDir({
68
+ path: resolve(runtimeDir, "components", "forms"),
69
+ prefix: options.prefix,
70
+ global: options.global,
71
+ watch: nuxt.options.dev
72
+ });
73
+ addComponentsDir({
74
+ path: resolve(runtimeDir, "components", "misc"),
75
+ prefix: options.prefix,
76
+ global: options.global,
77
+ watch: nuxt.options.dev
78
+ });
79
+ if (options.themeable) {
80
+ addPlugin(resolve(runtimeDir, "plugins", "theme.client"));
81
+ addPlugin(resolve(runtimeDir, "plugins", "theme.server"));
82
+ }
83
+ if (!options.dev)
84
+ nuxt.options.unocss = extendUnocssOptions(nuxt.options.unocss);
85
+ await installModule("@unocss/nuxt");
86
+ await installModule("@nuxtjs/color-mode", {
87
+ classSuffix: ""
88
+ });
89
+ await installModule("@vueuse/nuxt");
90
+ }
91
+ });
92
+
93
+ export { module as default };
@@ -0,0 +1,201 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Disclosure,
4
+ DisclosureButton,
5
+ DisclosurePanel,
6
+ } from '@headlessui/vue'
7
+ import { createReusableTemplate } from '@vueuse/core'
8
+ import { computed, ref } from 'vue'
9
+
10
+ import type { NAccordionItemProps, NAccordionProps } from '../../types'
11
+ import { pickProps } from '../../utils'
12
+ import NIcon from './Icon.vue'
13
+ import NButton from './Button.vue'
14
+
15
+ const props = withDefaults(defineProps<NAccordionProps>(), {
16
+ trailingOpen: 'accordion-trailing-icon',
17
+ loadingPlacement: 'trailing',
18
+ })
19
+
20
+ const buttonRefs = ref<(() => void)[]>([])
21
+
22
+ function closeOthers(index: number) {
23
+ if (props.multiple && !props.items[index].closeOthers)
24
+ return
25
+
26
+ buttonRefs.value
27
+ .filter((_, i) => i !== index)
28
+ .forEach(close => close())
29
+ }
30
+
31
+ function onEnter(element: Element, done: () => void) {
32
+ const el = element as HTMLElement
33
+ el.style.height = '0'
34
+ el.style.height = `${element.scrollHeight}px`
35
+ el.addEventListener('transitionend', done, { once: true })
36
+ }
37
+
38
+ function onAfterEnter(element: Element) {
39
+ const el = element as HTMLElement
40
+ el.style.height = 'auto'
41
+ }
42
+
43
+ function onBeforeLeave(element: Element) {
44
+ const el = element as HTMLElement
45
+ el.style.height = `${el.scrollHeight}px`
46
+ }
47
+
48
+ function onLeave(element: Element, done: () => void) {
49
+ const el = element as HTMLElement
50
+ el.style.height = '0'
51
+
52
+ el.addEventListener('transitionend', done, { once: true })
53
+ }
54
+
55
+ const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
56
+ item: NAccordionItemProps
57
+ index: number
58
+ open: boolean
59
+ close: () => void
60
+ }>()
61
+
62
+ // TODO: optimized this one
63
+ function mergedProps(itemProps: NAccordionItemProps) {
64
+ return Object.assign(pickProps(props, ['reverse', 'icon', 'btn', 'label', 'leading', 'loading', 'loadingPlacement', 'una', 'trailing', 'leading', 'to', 'type', 'disabled']), itemProps)
65
+ }
66
+
67
+ // TODO: refactor this to sync with NButton variants
68
+ const btnVariants = ['solid', 'outline', 'soft', 'ghost', 'link', 'text'] as const
69
+ const hasVariant = computed(() => btnVariants.some(btnVariants => props.btn?.includes(btnVariants)))
70
+ const isBaseVariant = computed(() => props.btn?.includes('~'))
71
+ </script>
72
+
73
+ <template>
74
+ <div
75
+ :accordion="accordion"
76
+ class="accordion"
77
+ :class="[
78
+ unstyle ? 'space-y-3' : 'accordion-(border divider)',
79
+ una?.accordion,
80
+ ]"
81
+ >
82
+ <DefineTemplate v-slot="{ item, close, index, open }">
83
+ <slot
84
+ :name="item.content ? 'content' : index"
85
+ :item="item" :index="index" :open="open" :close="close"
86
+ >
87
+ <div
88
+ accordion="panel"
89
+ :class="[
90
+ una?.accordionPanel,
91
+ { 'border-t-0': unstyle },
92
+ ]"
93
+ >
94
+ {{ item.content }}
95
+ </div>
96
+ </slot>
97
+ </DefineTemplate>
98
+
99
+ <Disclosure
100
+ v-for="(item, i) in items"
101
+ :key="i"
102
+ v-slot="{ open, close }"
103
+ as="div"
104
+ accordion="item"
105
+ :default-open="item.defaultOpen ?? defaultOpen"
106
+ :class="una?.accordionItem"
107
+ >
108
+ <DisclosureButton
109
+ :ref="() => (buttonRefs[i] = close)"
110
+ as="template"
111
+ :disabled="item.disabled ?? disabled"
112
+ @click="closeOthers(i)"
113
+ >
114
+ <slot name="label" :item="item" :index="i" :open="open" :close="close">
115
+ <NButton
116
+ v-bind="mergedProps(item)"
117
+ :btn="`~ block ${btn ?? ''}`"
118
+ :class="[
119
+ { 'accordion-button-default-variant': !hasVariant && !isBaseVariant },
120
+ { 'accordion-button-padding': !unstyle },
121
+ una?.accordionButton,
122
+ ]"
123
+ accordion="button"
124
+ :una="{
125
+ btnLabel: 'accordion-label',
126
+ }"
127
+ >
128
+ <template #leading>
129
+ <!-- TODO: fix conditional statement -->
130
+ <NIcon
131
+ v-if="leading || item.leading"
132
+ accordion="leading"
133
+ :class="una?.accordionLeading"
134
+ :name="item.leading ?? leading ?? ''"
135
+ aria-hidden="true"
136
+ />
137
+ </template>
138
+
139
+ <template #trailing>
140
+ <span
141
+ v-if="trailingOpen || trailingClose"
142
+ accordion="trailing"
143
+ :class="[
144
+ trailingClose || (!trailingClose && open)
145
+ ? una?.accordionTrailingClose ?? 'accordion-trailing-close'
146
+ : una?.accordionTrailingOpen ?? 'accordion-trailing-open',
147
+ una?.accordionTrailing,
148
+ ]"
149
+ >
150
+ <NIcon
151
+ v-if="(open || !trailingClose) && trailingOpen"
152
+ :name="trailingOpen"
153
+ aria-hidden="true"
154
+ />
155
+ <NIcon
156
+ v-else-if="!open && trailingClose"
157
+ :name="trailingClose"
158
+ aria-hidden="true"
159
+ />
160
+ </span>
161
+ </template>
162
+ </NButton>
163
+ </slot>
164
+ </DisclosureButton>
165
+
166
+ <Transition
167
+ :enter-active-class="una?.accordionLeaveActive ?? 'accordion-leave-active'"
168
+ :leave-active-class="una?.accordionEnterActive ?? 'accordion-enter-active'"
169
+ @enter="onEnter"
170
+ @after-enter="onAfterEnter"
171
+ @before-leave="onBeforeLeave"
172
+ @leave="onLeave"
173
+ >
174
+ <DisclosurePanel v-if="!item.mounted ?? !mounted">
175
+ <ReuseTemplate
176
+ v-bind="{
177
+ item,
178
+ index: i,
179
+ open,
180
+ close,
181
+ }"
182
+ />
183
+ </DisclosurePanel>
184
+ <DisclosurePanel
185
+ v-else
186
+ v-show="open"
187
+ static
188
+ >
189
+ <ReuseTemplate
190
+ v-bind="{
191
+ item,
192
+ index: i,
193
+ open,
194
+ close,
195
+ }"
196
+ />
197
+ </DisclosurePanel>
198
+ </Transition>
199
+ </Disclosure>
200
+ </div>
201
+ </template>
@@ -0,0 +1,138 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { NAlertProps } from '../../types'
4
+ import NIcon from '../elements/Icon.vue'
5
+
6
+ defineOptions({
7
+ inheritAttrs: false,
8
+ })
9
+
10
+ const props = withDefaults(defineProps<NAlertProps>(), {
11
+ icon: false,
12
+ })
13
+
14
+ const emit = defineEmits<{ (...args: any): void }>()
15
+
16
+ const slots = defineSlots<{
17
+ default?: any
18
+ title?: any
19
+ description?: any
20
+ icon?: any
21
+ closeIcon?: any
22
+ }>()
23
+
24
+ const alertVariants = ['soft', 'outline', 'border'] as const
25
+ const hasVariant = computed(() => alertVariants.some(alertVariants => props.alert?.includes(alertVariants)))
26
+ const isBaseVariant = computed(() => props.alert?.includes('~'))
27
+
28
+ const alertClassVariants = computed(() => {
29
+ const icon = {
30
+ info: 'alert-info-icon',
31
+ success: 'alert-success-icon',
32
+ warning: 'alert-warning-icon',
33
+ error: 'alert-error-icon',
34
+ default: '',
35
+ }
36
+
37
+ // TODO: simplify and optimize this
38
+ const alertType = props.alert ? (props.alert.includes('info') ? 'info' : (props.alert.includes('success') ? 'success' : (props.alert.includes('warning') ? 'warning' : (props.alert.includes('error') ? 'error' : 'default')))) : 'default'
39
+
40
+ return {
41
+ icon: icon[alertType],
42
+ }
43
+ })
44
+
45
+ const icon = computed(() => {
46
+ if (props.icon === '' || props.icon === undefined || props.icon === true)
47
+ return alertClassVariants.value.icon
48
+
49
+ return props.icon.toString()
50
+ })
51
+ </script>
52
+
53
+ <template>
54
+ <div
55
+ v-bind="$attrs"
56
+ :alert="alert"
57
+ class="alert"
58
+ :class="[
59
+ { 'alert-default-variant': !hasVariant && !isBaseVariant },
60
+ una?.alert,
61
+ ]"
62
+ >
63
+ <div
64
+ alert="inner-wrapper"
65
+ :class="una?.alertInnerWrapper"
66
+ >
67
+ <div
68
+ v-if="props.icon !== false"
69
+ alert="icon-wrapper"
70
+ :class="una?.alertIconWrapper"
71
+ >
72
+ <slot name="icon">
73
+ <NIcon
74
+ alert="icon-base"
75
+ :name="icon"
76
+ aria-hidden="true"
77
+ />
78
+ </slot>
79
+ </div>
80
+
81
+ <slot>
82
+ <div
83
+ alert="content-wrapper"
84
+ :class="una?.alertContentWrapper"
85
+ >
86
+ <div
87
+ v-if="slots.title || title"
88
+ alert="title"
89
+ :class="una?.alertTitle"
90
+ >
91
+ <slot name="title">
92
+ <h3>
93
+ {{ title }}
94
+ </h3>
95
+ </slot>
96
+ </div>
97
+ <div
98
+ v-if="slots.description || description"
99
+ alert="description"
100
+ :class="una?.alertDescription"
101
+ >
102
+ <slot name="description">
103
+ <p>
104
+ {{ description }}
105
+ </p>
106
+ </slot>
107
+ </div>
108
+ </div>
109
+ </slot>
110
+
111
+ <div
112
+ v-if="slots.closeIcon || closable"
113
+ alert="close-wrapper"
114
+ :class="una?.alertCloseWrapper"
115
+ >
116
+ <div
117
+ alert="close-inner-wrapper"
118
+ :class="una?.alertCloseInnerWrapper"
119
+ >
120
+ <NButton
121
+ alert="close"
122
+ :class="una?.alertClose"
123
+ btn="~ square"
124
+ @click="emit('close')"
125
+ >
126
+ <slot name="closeIcon">
127
+ <NIcon
128
+ :class="`${una?.alertCloseIconBase} alert-close-icon-base`"
129
+ :name="una?.alertCloseIcon ?? 'alert-close-icon'"
130
+ aria-hidden="true"
131
+ />
132
+ </slot>
133
+ </NButton>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </template>
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ import { useImage } from '@vueuse/core'
3
+ import { computed } from 'vue'
4
+ import type { NAvatarProps } from '../../types'
5
+
6
+ const props = withDefaults(defineProps<NAvatarProps>(), {
7
+ delay: 0,
8
+ })
9
+
10
+ const { isLoading, error, isReady } = useImage({ src: props?.src ?? '' }, { delay: props.delay })
11
+
12
+ const avatarVariants = ['solid', 'soft', 'outline'] as const
13
+ const hasVariant = computed(() => avatarVariants.some(avatarVariants => props.avatar?.includes(avatarVariants)))
14
+ const isBaseVariant = computed(() => props.avatar?.includes('~'))
15
+
16
+ const placeholder = computed(() => {
17
+ if (props.label)
18
+ return props.label
19
+
20
+ return props.alt?.split(' ').map(word => word[0]).join('').slice(0, 2)
21
+ })
22
+ </script>
23
+
24
+ <template>
25
+ <span
26
+ :avatar="avatar"
27
+ class="avatar"
28
+ :class="[
29
+ { 'avatar-default-variant': !hasVariant && !isBaseVariant },
30
+ { 'animate-pulse': isLoading && skeleton && src },
31
+ una?.avatar,
32
+ ]"
33
+ >
34
+ <template v-if="!icon">
35
+ <slot
36
+ v-if="!skeleton"
37
+ v-bind="{ isLoading, error, isReady, ...props }"
38
+ >
39
+ <img
40
+ v-if="isReady && !error"
41
+ avatar="src"
42
+ :src="src"
43
+ :alt="alt"
44
+ :class="una?.avatarSrc"
45
+ >
46
+
47
+ <img
48
+ v-else-if="fallback"
49
+ avatar="src"
50
+ :src="fallback"
51
+ :alt="alt ?? 'avatar'"
52
+ :class="una?.avatarFallback"
53
+ >
54
+
55
+ <span
56
+ v-else-if="placeholder"
57
+ avatar="label"
58
+ :class="una?.avatarLabel"
59
+ >
60
+ {{ placeholder }}
61
+ </span>
62
+
63
+ <NIcon
64
+ v-else
65
+ avatar="fallback-icon-base"
66
+ :name="una?.avatarFallbackIcon ?? 'avatar-fallback-icon'"
67
+ :class="una?.avatarFallbackIcon"
68
+ />
69
+ </slot>
70
+ </template>
71
+
72
+ <template v-else>
73
+ <NIcon
74
+ avatar="icon-base"
75
+ :class="una?.avatarIconBase"
76
+ :name="icon"
77
+ />
78
+ </template>
79
+ </span>
80
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import type { NAvatarGroupProps } from '../../types'
3
+ import NAvatarGroupDefaultSlot from '../slots/AvatarGroupDefault'
4
+
5
+ defineOptions({
6
+ inheritAttrs: false,
7
+ })
8
+
9
+ const props = withDefaults(defineProps<NAvatarGroupProps>(), {
10
+ max: 3,
11
+ })
12
+ </script>
13
+
14
+ <template>
15
+ <div
16
+ avatar-group
17
+ v-bind="$attrs"
18
+ :class="una?.avatarGroup"
19
+ >
20
+ <NAvatarGroupDefaultSlot
21
+ :max="max"
22
+ :avatar="props"
23
+ >
24
+ <slot />
25
+ </NAvatarGroupDefaultSlot>
26
+ </div>
27
+ </template>
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { NBadgeProps } from '../../types'
4
+ import NIcon from './Icon.vue'
5
+
6
+ const props = defineProps<NBadgeProps>()
7
+
8
+ const emit = defineEmits<{ (...args: any): void }>()
9
+
10
+ const badgeVariants = ['solid', 'soft', 'outline'] as const
11
+ const hasVariant = computed(() => badgeVariants.some(badgeVariants => props.badge?.includes(badgeVariants)))
12
+ const isBaseVariant = computed(() => props.badge?.includes('~'))
13
+ </script>
14
+
15
+ <template>
16
+ <span
17
+ :badge="badge"
18
+ class="badge"
19
+ :class="[
20
+ { 'badge-default-variant': !hasVariant && !isBaseVariant },
21
+ una?.badge,
22
+ ]"
23
+ >
24
+ <NIcon
25
+ v-if="icon"
26
+ badge="icon-base"
27
+ :class="una?.badgeIconBase"
28
+ :name="icon"
29
+ />
30
+
31
+ <slot>
32
+ {{ label }}
33
+ </slot>
34
+
35
+ <button
36
+ v-if="closable"
37
+ badge="close"
38
+ :class="una?.badgeClose"
39
+ group
40
+ @click="emit('close')"
41
+ >
42
+ <NIcon
43
+ :name="una?.badgeCloseIcon ?? 'badge-close-icon'"
44
+ :class="una?.badgeCloseIconBase"
45
+ badge="close-icon-base"
46
+ />
47
+ <span class="absolute -inset-0.25em" />
48
+ </button>
49
+ </span>
50
+ </template>