@saasmakers/ui 0.1.58 → 0.1.60
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.
- package/app/app.config.ts +1 -0
- package/app/components/bases/BaseEmoji.vue +66 -0
- package/app/components/bases/BasePower.vue +53 -0
- package/app/components/bases/BaseTag.vue +179 -0
- package/app/components/bases/BaseTags.vue +238 -0
- package/app/composables/useIcons.ts +4 -0
- package/app/types/bases.d.ts +92 -0
- package/app/types/global.d.ts +1 -0
- package/package.json +2 -1
- package/public/images/bases/BasePower/power-401.svg +271 -0
- package/public/images/bases/BasePower/power-404.svg +227 -0
- package/public/images/bases/BasePower/power-500.svg +227 -0
- package/public/images/bases/BasePower/power-angry.svg +66 -0
- package/public/images/bases/BasePower/power-bag.svg +98 -0
- package/public/images/bases/BasePower/power-bank.svg +508 -0
- package/public/images/bases/BasePower/power-book.svg +162 -0
- package/public/images/bases/BasePower/power-browser.svg +141 -0
- package/public/images/bases/BasePower/power-chair.svg +184 -0
- package/public/images/bases/BasePower/power-christmas.svg +678 -0
- package/public/images/bases/BasePower/power-city-bis.svg +1076 -0
- package/public/images/bases/BasePower/power-city.svg +1052 -0
- package/public/images/bases/BasePower/power-coffee.svg +123 -0
- package/public/images/bases/BasePower/power-computer-book.svg +264 -0
- package/public/images/bases/BasePower/power-computer-coffee.svg +263 -0
- package/public/images/bases/BasePower/power-dead.svg +68 -0
- package/public/images/bases/BasePower/power-easter.svg +473 -0
- package/public/images/bases/BasePower/power-hourglass.svg +362 -0
- package/public/images/bases/BasePower/power-invite.svg +226 -0
- package/public/images/bases/BasePower/power-ko.svg +66 -0
- package/public/images/bases/BasePower/power-love.svg +66 -0
- package/public/images/bases/BasePower/power-magic.svg +340 -0
- package/public/images/bases/BasePower/power-popcorn.svg +235 -0
- package/public/images/bases/BasePower/power-radar.svg +1 -0
- package/public/images/bases/BasePower/power-rocket.svg +615 -0
- package/public/images/bases/BasePower/power-smiling.svg +66 -0
- package/public/images/bases/BasePower/power-social-networks.svg +1 -0
- package/public/images/bases/BasePower/power-starwars.svg +607 -0
- package/public/images/bases/BasePower/power-teach.svg +148 -0
- package/public/images/bases/BasePower/power-typewriter.svg +358 -0
- package/public/images/bases/BasePower/power-website.svg +114 -0
- package/public/images/bases/BasePower/power-welcome.svg +525 -0
- package/public/images/bases/BasePower/power-writer.svg +199 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default defineAppConfig({ urls: { storage: 'https://resilienceclub.s3.fr-par.scw.cloud' } })
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BaseEmoji } from '../../types/bases'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<BaseEmoji>(), {
|
|
5
|
+
clickable: true,
|
|
6
|
+
emoji: undefined,
|
|
7
|
+
hasBox: false,
|
|
8
|
+
size: 'base',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const emit = defineEmits<{
|
|
12
|
+
click: [event: MouseEvent, emoji?: string]
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
const { urls } = useAppConfig()
|
|
16
|
+
|
|
17
|
+
function onClick(event: MouseEvent) {
|
|
18
|
+
emit('click', event, props.emoji)
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<span
|
|
24
|
+
class="flex items-center justify-center transition"
|
|
25
|
+
:class="{
|
|
26
|
+
'border border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-900 shadow-inner': hasBox,
|
|
27
|
+
'cursor-pointer hover:border-gray-400 dark:hover:border-gray-600': clickable,
|
|
28
|
+
|
|
29
|
+
'rounded': ['xs'].includes(size) && hasBox,
|
|
30
|
+
'rounded-md': ['sm', 'base'].includes(size) && hasBox,
|
|
31
|
+
'rounded-lg': ['lg', 'xl'].includes(size) && hasBox,
|
|
32
|
+
|
|
33
|
+
'h-2 w-2': size === '3xs' && hasBox,
|
|
34
|
+
'h-4 w-4': size === '2xs' && hasBox,
|
|
35
|
+
'h-5 w-5': size === 'xs' && hasBox,
|
|
36
|
+
'h-6 w-6': size === 'sm' && hasBox,
|
|
37
|
+
'h-8.5 w-8.5': size === 'base' && hasBox,
|
|
38
|
+
'h-10 w-10': size === 'lg' && hasBox,
|
|
39
|
+
'h-12 w-12': size === 'xl' && hasBox,
|
|
40
|
+
'h-14 w-14': size === '2xl' && hasBox,
|
|
41
|
+
'h-16 w-16': size === '3xl' && hasBox,
|
|
42
|
+
'h-18 w-18': size === '4xl' && hasBox,
|
|
43
|
+
}"
|
|
44
|
+
@click="onClick"
|
|
45
|
+
>
|
|
46
|
+
<img
|
|
47
|
+
v-if="emoji"
|
|
48
|
+
class="flex"
|
|
49
|
+
:class="{
|
|
50
|
+
'h-2.5 w-2.5': size === '3xs',
|
|
51
|
+
'h-3 w-3': size === '2xs',
|
|
52
|
+
'h-3.5 w-3.5': size === 'xs',
|
|
53
|
+
'h-4 w-4': size === 'sm',
|
|
54
|
+
'h-5 w-5': size === 'base',
|
|
55
|
+
'h-5.5 w-5.5': size === 'lg',
|
|
56
|
+
'h-6 w-6': size === 'xl',
|
|
57
|
+
'h-8 w-8': size === '2xl',
|
|
58
|
+
'h-10 w-10': size === '3xl',
|
|
59
|
+
'h-12 w-12': size === '4xl',
|
|
60
|
+
}"
|
|
61
|
+
loading="lazy"
|
|
62
|
+
:src="`${urls.storage}/images/emojis/${emoji}.svg`"
|
|
63
|
+
@click.stop="onClick"
|
|
64
|
+
>
|
|
65
|
+
</span>
|
|
66
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BasePower } from '../../types/bases'
|
|
3
|
+
// import lottie from 'lottie-web'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<BasePower>(), {
|
|
6
|
+
animated: false,
|
|
7
|
+
power: 'welcome',
|
|
8
|
+
size: 'base',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const animationLoaded = ref(false)
|
|
12
|
+
const animationPlaceholder = ref<HTMLElement>()
|
|
13
|
+
|
|
14
|
+
onMounted(() => {
|
|
15
|
+
if (props.animated && ['radar'].includes(props.power) && animationPlaceholder.value) {
|
|
16
|
+
// lottie.loadAnimation({
|
|
17
|
+
// autoplay: true,
|
|
18
|
+
// container: animationPlaceholder.value,
|
|
19
|
+
// loop: true,
|
|
20
|
+
// path: `/animations/bases/BasePower/power-${props.power}.json`,
|
|
21
|
+
// renderer: 'svg',
|
|
22
|
+
// })
|
|
23
|
+
|
|
24
|
+
// setTimeout(() => (animationLoaded.value = true), 1 * 1000)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div
|
|
31
|
+
class="pointer-events-none select-none"
|
|
32
|
+
:class="{
|
|
33
|
+
'h-12 sm:h-16': size === 'xs',
|
|
34
|
+
'h-16 sm:h-20': size === 'sm',
|
|
35
|
+
'h-20 sm:h-24': size === 'base',
|
|
36
|
+
'h-24 sm:h-32': size === 'lg',
|
|
37
|
+
}"
|
|
38
|
+
>
|
|
39
|
+
<img
|
|
40
|
+
v-show="!animationLoaded"
|
|
41
|
+
:alt="power"
|
|
42
|
+
class="h-full w-full"
|
|
43
|
+
loading="lazy"
|
|
44
|
+
:src="`/images/bases/BasePower/power-${power}.svg`"
|
|
45
|
+
>
|
|
46
|
+
|
|
47
|
+
<div
|
|
48
|
+
v-show="animationLoaded"
|
|
49
|
+
ref="animationPlaceholder"
|
|
50
|
+
class="h-full w-full"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BaseTag } from '../../types/bases'
|
|
3
|
+
import { NuxtLinkLocale } from '#components'
|
|
4
|
+
import { getIcon } from '../../composables/useIcons'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<BaseTag>(), {
|
|
7
|
+
active: false,
|
|
8
|
+
avatar: '',
|
|
9
|
+
circle: '',
|
|
10
|
+
clickable: true,
|
|
11
|
+
color: 'black',
|
|
12
|
+
draggable: false,
|
|
13
|
+
editable: false,
|
|
14
|
+
icon: undefined,
|
|
15
|
+
iconSize: undefined,
|
|
16
|
+
id: undefined,
|
|
17
|
+
isCreation: false,
|
|
18
|
+
light: true,
|
|
19
|
+
removable: false,
|
|
20
|
+
rounded: false,
|
|
21
|
+
size: 'base',
|
|
22
|
+
text: '',
|
|
23
|
+
to: undefined,
|
|
24
|
+
truncate: false,
|
|
25
|
+
uppercase: true,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits<{
|
|
29
|
+
click: [event: MouseEvent, id?: number | string]
|
|
30
|
+
close: [event: MouseEvent, id?: number | string]
|
|
31
|
+
inputBlur: [event: FocusEvent, name: string, id?: number | string]
|
|
32
|
+
inputSubmit: [event: KeyboardEvent, name: string, id?: number | string]
|
|
33
|
+
remove: [event: MouseEvent, id?: number | string]
|
|
34
|
+
}>()
|
|
35
|
+
|
|
36
|
+
const hovered = ref(false)
|
|
37
|
+
|
|
38
|
+
const form = reactive({ name: '' })
|
|
39
|
+
|
|
40
|
+
onBeforeMount(() => {
|
|
41
|
+
if (!props.isCreation) {
|
|
42
|
+
initializeFormForExistingTag()
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
function initializeFormForExistingTag() {
|
|
47
|
+
form.name = String(props.text)?.substring(1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function onClick(event: MouseEvent) {
|
|
51
|
+
if (!props.removable) {
|
|
52
|
+
emit('click', event, props.id)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function onInputBlur(event: FocusEvent) {
|
|
57
|
+
emit('inputBlur', event, form.name.trim(), props.id)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function onInputSubmit(event: KeyboardEvent) {
|
|
61
|
+
emit('inputSubmit', event, form.name.trim(), props.id)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onMouseEnter() {
|
|
65
|
+
hovered.value = true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function onMouseLeave() {
|
|
69
|
+
hovered.value = false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function onRemove(event: MouseEvent) {
|
|
73
|
+
emit('remove', event, props.id)
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<span
|
|
79
|
+
class="flex select-none"
|
|
80
|
+
@click.stop="onClick"
|
|
81
|
+
@mouseenter="onMouseEnter"
|
|
82
|
+
@mouseleave="onMouseLeave"
|
|
83
|
+
>
|
|
84
|
+
<component
|
|
85
|
+
:is="to ? NuxtLinkLocale : 'span'"
|
|
86
|
+
class="inline-flex items-center border font-medium"
|
|
87
|
+
:class="{
|
|
88
|
+
'cursor-pointer': clickable,
|
|
89
|
+
'rounded-md': !rounded,
|
|
90
|
+
'rounded-full': rounded,
|
|
91
|
+
'uppercase': uppercase,
|
|
92
|
+
|
|
93
|
+
'border-gray-200 dark:border-gray-800 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200': (color === 'black' && light && !active) || removable,
|
|
94
|
+
'border-gray-900 dark:border-gray-100 bg-gray-900 dark:bg-gray-100 text-white dark:text-black': color === 'black' && (!light || active),
|
|
95
|
+
'border-gray-700 dark:border-gray-300 bg-gray-100 dark:bg-gray-900 text-gray-700 dark:text-gray-300': color === 'gray' && light && !active,
|
|
96
|
+
'border-gray-800 dark:border-gray-200 bg-gray-800 dark:bg-gray-200 text-white dark:text-black': color === 'gray' && (!light || active),
|
|
97
|
+
'border-green-700 dark:border-green-300 bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300': color === 'green' && light && !active,
|
|
98
|
+
'border-green-800 dark:border-green-200 bg-green-800 dark:bg-green-200 text-white dark:text-black': color === 'green' && (!light || active),
|
|
99
|
+
'border-indigo-700 dark:border-indigo-300 bg-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-300': color === 'indigo' && light && !active,
|
|
100
|
+
'border-indigo-800 dark:border-indigo-200 bg-indigo-800 dark:bg-indigo-200 text-white dark:text-black': color === 'indigo' && (!light || active),
|
|
101
|
+
'border-orange-700 dark:border-orange-300 bg-white dark:bg-gray-900 text-orange-700 dark:text-orange-300': color === 'orange' && light && !active,
|
|
102
|
+
'border-orange-800 dark:border-orange-200 bg-orange-800 dark:bg-orange-200 text-white dark:text-black': color === 'orange' && (!light || active),
|
|
103
|
+
'border-red-700 dark:border-red-300 bg-white dark:bg-gray-900 text-red-700 dark:text-red-300': color === 'red' && light && !active,
|
|
104
|
+
'border-red-800 dark:border-red-200 bg-red-800 dark:bg-red-200 text-white dark:text-black': color === 'red' && (!light || active),
|
|
105
|
+
'border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200': color === 'white' && light && !active,
|
|
106
|
+
'border-white dark:border-gray-900 text-white dark:text-black': color === 'white' && (!light || active),
|
|
107
|
+
'border-dashed border-gray-400 dark:border-gray-600 bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200': color === 'white' && light && !active,
|
|
108
|
+
|
|
109
|
+
'py-1.5 px-2.5 text-2xs': size === 'sm',
|
|
110
|
+
'py-2 px-3 text-xs': size === 'base',
|
|
111
|
+
}"
|
|
112
|
+
:to="to"
|
|
113
|
+
>
|
|
114
|
+
<BaseIcon
|
|
115
|
+
v-if="draggable"
|
|
116
|
+
class="js-drag-handle mr-2 cursor-move"
|
|
117
|
+
clickable
|
|
118
|
+
color="gray"
|
|
119
|
+
:icon="getIcon('drag')"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<span
|
|
123
|
+
v-if="avatar || circle || icon"
|
|
124
|
+
class="mr-1 flex items-center text-center"
|
|
125
|
+
:class="{
|
|
126
|
+
'h-2.5 w-2.5': size === 'sm' || iconSize === 2.5,
|
|
127
|
+
'h-3 w-3': size === 'base' || iconSize === 3,
|
|
128
|
+
'h-3.5 w-3.5': iconSize === 3.5,
|
|
129
|
+
'h-4 w-4': iconSize === 4,
|
|
130
|
+
}"
|
|
131
|
+
>
|
|
132
|
+
<BaseAvatar
|
|
133
|
+
v-if="avatar"
|
|
134
|
+
class="h-full w-full"
|
|
135
|
+
rounded="sm"
|
|
136
|
+
:shadow="false"
|
|
137
|
+
:src="avatar"
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<BaseIcon
|
|
141
|
+
v-else-if="icon"
|
|
142
|
+
class="h-full w-full"
|
|
143
|
+
:icon="icon"
|
|
144
|
+
/>
|
|
145
|
+
</span>
|
|
146
|
+
|
|
147
|
+
<span
|
|
148
|
+
v-if="$slots.left"
|
|
149
|
+
class="mr-1"
|
|
150
|
+
>
|
|
151
|
+
<slot name="left" />
|
|
152
|
+
</span>
|
|
153
|
+
|
|
154
|
+
<BaseText
|
|
155
|
+
v-if="!editable"
|
|
156
|
+
class="whitespace-nowrap outline-none"
|
|
157
|
+
:class="{ truncate }"
|
|
158
|
+
:text="text"
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
<FieldInput
|
|
162
|
+
v-else
|
|
163
|
+
v-model="form.name"
|
|
164
|
+
border="none"
|
|
165
|
+
class="w-16"
|
|
166
|
+
size="sm"
|
|
167
|
+
@blur="onInputBlur"
|
|
168
|
+
@submit="onInputSubmit"
|
|
169
|
+
/>
|
|
170
|
+
|
|
171
|
+
<BaseIcon
|
|
172
|
+
v-if="removable"
|
|
173
|
+
class="ml-1.5 text-red-700 dark:text-red-300 hover:text-black dark:hover:text-white"
|
|
174
|
+
:icon="getIcon('closeCircle')"
|
|
175
|
+
@click.prevent.stop="onRemove"
|
|
176
|
+
/>
|
|
177
|
+
</component>
|
|
178
|
+
</span>
|
|
179
|
+
</template>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BaseTags } from '../../types/bases'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<BaseTags>(), {
|
|
5
|
+
active: false,
|
|
6
|
+
clickable: true,
|
|
7
|
+
draggable: false,
|
|
8
|
+
editable: false,
|
|
9
|
+
hasBack: false,
|
|
10
|
+
hasCreation: false,
|
|
11
|
+
maxTags: Number.POSITIVE_INFINITY,
|
|
12
|
+
removable: false,
|
|
13
|
+
selectable: false,
|
|
14
|
+
selectableUnique: false,
|
|
15
|
+
size: 'base',
|
|
16
|
+
tags: () => [],
|
|
17
|
+
value: () => [],
|
|
18
|
+
wrap: false,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits<{
|
|
22
|
+
'attach': [event: MouseEvent, tagId?: number | string]
|
|
23
|
+
'back': [event: MouseEvent]
|
|
24
|
+
'click': [event: MouseEvent]
|
|
25
|
+
'create': [event: MouseEvent, name: string]
|
|
26
|
+
'detach': [event: MouseEvent, tagId?: number | string]
|
|
27
|
+
'remove': [event: MouseEvent, tagId?: number | string]
|
|
28
|
+
'update': [event: MouseEvent, name: string, tagId?: number | string]
|
|
29
|
+
'update:modelValue': [value: (number | string)[]]
|
|
30
|
+
}>()
|
|
31
|
+
|
|
32
|
+
const keyForTagCreation = ref(Date.now())
|
|
33
|
+
const keyForTagUpdate = ref(Date.now())
|
|
34
|
+
const root = ref<HTMLDivElement>()
|
|
35
|
+
const showingAllTags = ref(false)
|
|
36
|
+
const showingTagCreationField = ref(false)
|
|
37
|
+
|
|
38
|
+
const { t } = useI18n()
|
|
39
|
+
|
|
40
|
+
const sortedTags = computed({
|
|
41
|
+
get() {
|
|
42
|
+
return props.tags
|
|
43
|
+
.slice()
|
|
44
|
+
.filter((_tag, tagIndex) => tagIndex < props.maxTags || showingAllTags.value)
|
|
45
|
+
.sort((tagA, tagB) => (tagA.order! < tagB.order! ? -1 : 0))
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
set(tags) {
|
|
49
|
+
const suiteForTags = tags
|
|
50
|
+
.map((tag, order) => ({
|
|
51
|
+
id: tag.id,
|
|
52
|
+
order,
|
|
53
|
+
}))
|
|
54
|
+
.map(tag => ({
|
|
55
|
+
id: tag.id,
|
|
56
|
+
order: tag.order,
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
return suiteForTags
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
function onCreateTag(event: MouseEvent, name: string) {
|
|
64
|
+
showingTagCreationField.value = false
|
|
65
|
+
keyForTagCreation.value = Date.now()
|
|
66
|
+
|
|
67
|
+
emit('create', event, name)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function onGoBack(event: MouseEvent) {
|
|
71
|
+
emit('back', event)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function onShowTagField() {
|
|
75
|
+
if (!showingTagCreationField.value) {
|
|
76
|
+
showingTagCreationField.value = true
|
|
77
|
+
|
|
78
|
+
// Focus contenteditable
|
|
79
|
+
nextTick(() => {
|
|
80
|
+
const element = root.value?.querySelector<HTMLInputElement>('input[type="text"]')
|
|
81
|
+
|
|
82
|
+
element?.focus()
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onShowAllTags() {
|
|
88
|
+
showingAllTags.value = true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function onTagClick(event: MouseEvent, tagId?: number | string) {
|
|
92
|
+
if (props.selectable && tagId) {
|
|
93
|
+
let value = [...props.value]
|
|
94
|
+
|
|
95
|
+
if (value.includes(tagId)) {
|
|
96
|
+
value.splice(value.indexOf(tagId), 1)
|
|
97
|
+
emit('detach', event, tagId)
|
|
98
|
+
}
|
|
99
|
+
else if (props.selectableUnique) {
|
|
100
|
+
value = [tagId]
|
|
101
|
+
emit('attach', event, tagId)
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
value.push(tagId)
|
|
105
|
+
emit('attach', event, tagId)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
emit('update:modelValue', value)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function onUpdateTag(event: MouseEvent, name: string, tagId?: number | string) {
|
|
113
|
+
if (name) {
|
|
114
|
+
emit('update', event, name, tagId)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
else {
|
|
118
|
+
keyForTagUpdate.value = Date.now()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function onRemoveTag(event: MouseEvent, tagId?: number | string) {
|
|
123
|
+
emit('remove', event, tagId)
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<div
|
|
129
|
+
ref="root"
|
|
130
|
+
class="flex items-center"
|
|
131
|
+
:class="{
|
|
132
|
+
'flex-wrap': wrap,
|
|
133
|
+
'w-full overflow-x-auto': !wrap,
|
|
134
|
+
|
|
135
|
+
'-mt-0.75 -ml-0.75': size === 'sm',
|
|
136
|
+
'-mt-1 -ml-1': size === 'base',
|
|
137
|
+
}"
|
|
138
|
+
>
|
|
139
|
+
<BaseTag
|
|
140
|
+
v-if="hasBack"
|
|
141
|
+
key="BaseTagToGoBack"
|
|
142
|
+
:class="{
|
|
143
|
+
'm-0.75': size === 'sm',
|
|
144
|
+
'm-1': size === 'base',
|
|
145
|
+
}"
|
|
146
|
+
color="indigo"
|
|
147
|
+
:icon="getIcon('back')"
|
|
148
|
+
:size="size"
|
|
149
|
+
:text="t('globals.cancel')"
|
|
150
|
+
@click="onGoBack"
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
<BaseTag
|
|
154
|
+
v-if="hasCreation"
|
|
155
|
+
:key="keyForTagCreation"
|
|
156
|
+
:class="{
|
|
157
|
+
'm-0.75': size === 'sm',
|
|
158
|
+
'm-1': size === 'base',
|
|
159
|
+
}"
|
|
160
|
+
color="indigo"
|
|
161
|
+
:editable="showingTagCreationField"
|
|
162
|
+
:icon="showingTagCreationField ? getIcon('tags') : getIcon('plus')"
|
|
163
|
+
is-creation
|
|
164
|
+
:light="false"
|
|
165
|
+
:size="size"
|
|
166
|
+
:text="showingTagCreationField ? '' : t('newTag')"
|
|
167
|
+
@click="onShowTagField"
|
|
168
|
+
@input:blur="onCreateTag"
|
|
169
|
+
@input:submit="onCreateTag"
|
|
170
|
+
/>
|
|
171
|
+
|
|
172
|
+
<div class="flex items-center">
|
|
173
|
+
<span
|
|
174
|
+
v-for="(tag, tagIndex) in sortedTags"
|
|
175
|
+
:key="tag.id ? `${tag.id}_${tag.order}_${keyForTagUpdate}` : tagIndex"
|
|
176
|
+
class="flex flex-wrap items-center"
|
|
177
|
+
:class="{
|
|
178
|
+
'm-0.75': size === 'sm',
|
|
179
|
+
'm-1': size === 'base',
|
|
180
|
+
}"
|
|
181
|
+
>
|
|
182
|
+
<template v-if="tag">
|
|
183
|
+
<BaseTag
|
|
184
|
+
:id="tag.id || tagIndex"
|
|
185
|
+
:key="`${tag.id}_${tagIndex}`"
|
|
186
|
+
:active="tag.id && value.includes(tag.id) || tag.active"
|
|
187
|
+
:avatar="tag.avatar"
|
|
188
|
+
:circle="tag.circle"
|
|
189
|
+
:clickable="clickable"
|
|
190
|
+
:color="tag.color"
|
|
191
|
+
:draggable="draggable"
|
|
192
|
+
:editable="editable"
|
|
193
|
+
:icon="tag.icon"
|
|
194
|
+
:icon-size="tag.iconSize"
|
|
195
|
+
:light="tag.light"
|
|
196
|
+
:removable="removable"
|
|
197
|
+
:size="size"
|
|
198
|
+
:text="tag.text"
|
|
199
|
+
:to="tag.to"
|
|
200
|
+
@click="onTagClick"
|
|
201
|
+
@input:blur="onUpdateTag"
|
|
202
|
+
@input:submit="onUpdateTag"
|
|
203
|
+
@remove="onRemoveTag"
|
|
204
|
+
/>
|
|
205
|
+
</template>
|
|
206
|
+
</span>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<BaseTag
|
|
210
|
+
v-if="tags.length > maxTags && !showingAllTags"
|
|
211
|
+
key="showMore"
|
|
212
|
+
:class="{
|
|
213
|
+
'm-0.75': size === 'sm',
|
|
214
|
+
'm-1': size === 'base',
|
|
215
|
+
}"
|
|
216
|
+
:size="size"
|
|
217
|
+
:text="t('showMore', { value: tags.length - maxTags })"
|
|
218
|
+
@click="onShowAllTags"
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<i18n lang="json">
|
|
224
|
+
{
|
|
225
|
+
"en": {
|
|
226
|
+
"newTag": "New tag",
|
|
227
|
+
"showMore": "& {value} other | & {value} others"
|
|
228
|
+
},
|
|
229
|
+
"fr": {
|
|
230
|
+
"newTag": "Nouveau tag",
|
|
231
|
+
"showMore": "& {value} autre | & {value} autres"
|
|
232
|
+
},
|
|
233
|
+
"ja": {
|
|
234
|
+
"newTag": "新しいタグ",
|
|
235
|
+
"showMore": "& {value} その他 | & {value} その他"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
</i18n>
|
|
@@ -12,12 +12,16 @@ const icons = {
|
|
|
12
12
|
arrowDown: 'solar:alt-arrow-down-bold',
|
|
13
13
|
arrowRight: 'solar:alt-arrow-right-bold',
|
|
14
14
|
arrowUp: 'solar:alt-arrow-up-bold',
|
|
15
|
+
back: 'hugeicons:arrow-turn-backward',
|
|
15
16
|
checkCircle: 'hugeicons:checkmark-circle-02',
|
|
16
17
|
close: 'hugeicons:cancel-01',
|
|
17
18
|
closeCircle: 'hugeicons:cancel-circle',
|
|
18
19
|
default: 'hugeicons:help-circle',
|
|
20
|
+
drag: 'mdi:drag-horizontal-variant',
|
|
19
21
|
exclamationCircle: 'hugeicons:alert-circle',
|
|
20
22
|
infoCircle: 'hugeicons:information-circle',
|
|
23
|
+
plus: 'hugeicons:add-01',
|
|
24
|
+
tags: 'hugeicons:tags',
|
|
21
25
|
} as const
|
|
22
26
|
|
|
23
27
|
export function getIcon(attribute: keyof typeof icons) {
|
package/app/types/bases.d.ts
CHANGED
|
@@ -111,6 +111,13 @@ export type BaseDividerBorderStyle = 'dashed' | 'dotted' | 'solid'
|
|
|
111
111
|
|
|
112
112
|
export type BaseDividerSize = 'base' | 'sm'
|
|
113
113
|
|
|
114
|
+
export interface BaseEmoji {
|
|
115
|
+
clickable?: boolean
|
|
116
|
+
emoji?: string
|
|
117
|
+
hasBox?: boolean
|
|
118
|
+
size?: BaseSize
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
export interface BaseHeading {
|
|
115
122
|
alignment?: BaseHeadingAlignment
|
|
116
123
|
size?: BaseHeadingSize
|
|
@@ -184,6 +191,49 @@ export type BaseParagraphAlignment = 'center' | 'left' | 'right'
|
|
|
184
191
|
|
|
185
192
|
export type BaseParagraphSize = 'base' | 'lg' | 'sm'
|
|
186
193
|
|
|
194
|
+
export interface BasePower {
|
|
195
|
+
animated?: boolean
|
|
196
|
+
power?: BasePowerPower
|
|
197
|
+
size?: BasePowerSize
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export type BasePowerPower
|
|
201
|
+
= | '401'
|
|
202
|
+
| '404'
|
|
203
|
+
| '500'
|
|
204
|
+
| 'angry'
|
|
205
|
+
| 'bag'
|
|
206
|
+
| 'bank'
|
|
207
|
+
| 'book'
|
|
208
|
+
| 'browser'
|
|
209
|
+
| 'chair'
|
|
210
|
+
| 'christmas'
|
|
211
|
+
| 'city'
|
|
212
|
+
| 'city-bis'
|
|
213
|
+
| 'coffee'
|
|
214
|
+
| 'computer-book'
|
|
215
|
+
| 'computer-coffee'
|
|
216
|
+
| 'dead'
|
|
217
|
+
| 'easter'
|
|
218
|
+
| 'hourglass'
|
|
219
|
+
| 'invite'
|
|
220
|
+
| 'ko'
|
|
221
|
+
| 'love'
|
|
222
|
+
| 'magic'
|
|
223
|
+
| 'popcorn'
|
|
224
|
+
| 'radar'
|
|
225
|
+
| 'rocket'
|
|
226
|
+
| 'smiling'
|
|
227
|
+
| 'social-networks'
|
|
228
|
+
| 'starwars'
|
|
229
|
+
| 'teach'
|
|
230
|
+
| 'typewriter'
|
|
231
|
+
| 'website'
|
|
232
|
+
| 'welcome'
|
|
233
|
+
| 'writer'
|
|
234
|
+
|
|
235
|
+
export type BasePowerSize = 'base' | 'lg' | 'sm' | 'xs'
|
|
236
|
+
|
|
187
237
|
export interface BaseQuote {
|
|
188
238
|
background?: BaseQuoteBackground
|
|
189
239
|
character?: BaseCharacterCharacter
|
|
@@ -212,6 +262,48 @@ export interface BaseSpinner {
|
|
|
212
262
|
uppercase?: boolean
|
|
213
263
|
}
|
|
214
264
|
|
|
265
|
+
export interface BaseTag {
|
|
266
|
+
active?: boolean
|
|
267
|
+
avatar?: string
|
|
268
|
+
circle?: string
|
|
269
|
+
clickable?: boolean
|
|
270
|
+
color?: BaseColor
|
|
271
|
+
draggable?: boolean
|
|
272
|
+
editable?: boolean
|
|
273
|
+
icon?: BaseIconValue
|
|
274
|
+
iconSize?: number
|
|
275
|
+
id?: number
|
|
276
|
+
isCreation?: boolean
|
|
277
|
+
light?: boolean
|
|
278
|
+
order?: number
|
|
279
|
+
removable?: boolean
|
|
280
|
+
rounded?: boolean
|
|
281
|
+
size?: BaseTagSize
|
|
282
|
+
text: BaseTextText
|
|
283
|
+
to?: RouteLocationNamedI18n
|
|
284
|
+
truncate?: boolean
|
|
285
|
+
uppercase?: boolean
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export type BaseTagSize = 'base' | 'sm'
|
|
289
|
+
|
|
290
|
+
export interface BaseTags {
|
|
291
|
+
active?: boolean
|
|
292
|
+
clickable?: boolean
|
|
293
|
+
draggable?: boolean
|
|
294
|
+
editable?: boolean
|
|
295
|
+
hasBack?: boolean
|
|
296
|
+
hasCreation?: boolean
|
|
297
|
+
maxTags?: number
|
|
298
|
+
removable?: boolean
|
|
299
|
+
selectable?: boolean
|
|
300
|
+
selectableUnique?: boolean
|
|
301
|
+
size?: BaseTagSize
|
|
302
|
+
tags: BaseTag[]
|
|
303
|
+
value?: (number | string)[]
|
|
304
|
+
wrap?: boolean
|
|
305
|
+
}
|
|
306
|
+
|
|
215
307
|
export interface BaseText {
|
|
216
308
|
background?: BaseTextBackground
|
|
217
309
|
bold?: boolean
|
package/app/types/global.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ declare global {
|
|
|
27
27
|
type BaseDivider = Bases.BaseDivider
|
|
28
28
|
type BaseDividerBorderStyle = Bases.BaseDividerBorderStyle
|
|
29
29
|
type BaseDividerSize = Bases.BaseDividerSize
|
|
30
|
+
type BaseEmoji = Bases.BaseEmoji
|
|
30
31
|
type BaseHeading = Bases.BaseHeading
|
|
31
32
|
type BaseHeadingAlignment = Bases.BaseHeadingAlignment
|
|
32
33
|
type BaseHeadingSize = Bases.BaseHeadingSize
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasmakers/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.60",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Reusable Nuxt UI components for SaaS Makers projects",
|
|
7
7
|
"license": "MIT",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@unocss/reset": "66.5.10",
|
|
44
44
|
"chartist": "1.5.0",
|
|
45
45
|
"floating-vue": "5.2.2",
|
|
46
|
+
"lottie-web": "5.13.0",
|
|
46
47
|
"motion-v": "1.7.4",
|
|
47
48
|
"numbro": "2.5.0",
|
|
48
49
|
"snarkdown": "2.0.0",
|