@gameap/ui 1.0.0 → 1.1.0
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/components/GDeletableList.vue +4 -2
- package/components/GIcon.vue +66 -0
- package/components/GMenu.vue +67 -0
- package/components/GMenuButton.vue +28 -0
- package/components/GMenuItem.vue +36 -0
- package/components/GMenuItems.vue +35 -0
- package/components/Loading.vue +9 -13
- package/index.js +24 -0
- package/package.json +2 -6
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
class="ml-[2px] text-xs h-full inline-flex items-center px-2 py-1 bg-red-500 hover:bg-red-600 text-white rounded-e cursor-pointer"
|
|
12
12
|
@click="onClickDelete(item.id)"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
14
|
+
<GIcon name="delete" />
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
</template>
|
|
20
20
|
|
|
21
21
|
<script setup>
|
|
22
|
+
import GIcon from './GIcon.vue'
|
|
23
|
+
|
|
22
24
|
const props = defineProps({
|
|
23
|
-
items:
|
|
25
|
+
items: Array,
|
|
24
26
|
clickCallback: Function,
|
|
25
27
|
deleteCallback: Function,
|
|
26
28
|
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
v-if="isComponent"
|
|
4
|
+
:is="resolvedIcon"
|
|
5
|
+
:class="props.class"
|
|
6
|
+
:style="sizeStyle"
|
|
7
|
+
/>
|
|
8
|
+
<i
|
|
9
|
+
v-else
|
|
10
|
+
:class="[resolvedIcon, sizeClass, props.class]"
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import { computed } from 'vue'
|
|
16
|
+
import { getIcon, hasIcon } from '../icons/registry.js'
|
|
17
|
+
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
name: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'md',
|
|
26
|
+
validator: (value) => ['sm', 'md', 'lg', 'xl'].includes(value)
|
|
27
|
+
},
|
|
28
|
+
class: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: ''
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const resolvedIcon = computed(() => {
|
|
35
|
+
if (!hasIcon(props.name)) {
|
|
36
|
+
console.warn(`GIcon: Unknown icon "${props.name}"`)
|
|
37
|
+
return 'fa-solid fa-circle-question'
|
|
38
|
+
}
|
|
39
|
+
return getIcon(props.name)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const isComponent = computed(() => {
|
|
43
|
+
return typeof resolvedIcon.value === 'object' || typeof resolvedIcon.value === 'function'
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const fontAwesomeSizeMap = {
|
|
47
|
+
sm: 'fa-sm',
|
|
48
|
+
md: '',
|
|
49
|
+
lg: 'fa-lg',
|
|
50
|
+
xl: 'fa-2x'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const componentSizeMap = {
|
|
54
|
+
sm: '0.875em',
|
|
55
|
+
md: '1em',
|
|
56
|
+
lg: '1.25em',
|
|
57
|
+
xl: '2em'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sizeClass = computed(() => fontAwesomeSizeMap[props.size] || '')
|
|
61
|
+
|
|
62
|
+
const sizeStyle = computed(() => ({
|
|
63
|
+
width: componentSizeMap[props.size],
|
|
64
|
+
height: componentSizeMap[props.size]
|
|
65
|
+
}))
|
|
66
|
+
</script>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, provide, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
as: { type: String, default: 'div' }
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const isOpen = ref(false)
|
|
9
|
+
const menuRef = ref(null)
|
|
10
|
+
const activeIndex = ref(-1)
|
|
11
|
+
const items = ref([])
|
|
12
|
+
|
|
13
|
+
const toggle = () => {
|
|
14
|
+
isOpen.value = !isOpen.value
|
|
15
|
+
if (!isOpen.value) activeIndex.value = -1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const close = () => {
|
|
19
|
+
isOpen.value = false
|
|
20
|
+
activeIndex.value = -1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const registerItem = (id) => {
|
|
24
|
+
items.value.push(id)
|
|
25
|
+
return items.value.length - 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const unregisterItem = (id) => {
|
|
29
|
+
const idx = items.value.indexOf(id)
|
|
30
|
+
if (idx > -1) items.value.splice(idx, 1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const setActiveIndex = (index) => {
|
|
34
|
+
activeIndex.value = index
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleClickOutside = (event) => {
|
|
38
|
+
if (menuRef.value && !menuRef.value.contains(event.target)) {
|
|
39
|
+
close()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onMounted(() => {
|
|
44
|
+
document.addEventListener('click', handleClickOutside, true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
onUnmounted(() => {
|
|
48
|
+
document.removeEventListener('click', handleClickOutside, true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
provide('menu', {
|
|
52
|
+
isOpen,
|
|
53
|
+
toggle,
|
|
54
|
+
close,
|
|
55
|
+
activeIndex,
|
|
56
|
+
items,
|
|
57
|
+
registerItem,
|
|
58
|
+
unregisterItem,
|
|
59
|
+
setActiveIndex
|
|
60
|
+
})
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<component :is="as" ref="menuRef">
|
|
65
|
+
<slot />
|
|
66
|
+
</component>
|
|
67
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
|
|
4
|
+
const { isOpen, toggle } = inject('menu')
|
|
5
|
+
|
|
6
|
+
const handleClick = () => {
|
|
7
|
+
toggle()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const handleKeydown = (event) => {
|
|
11
|
+
if (['Enter', ' ', 'ArrowDown'].includes(event.key)) {
|
|
12
|
+
event.preventDefault()
|
|
13
|
+
toggle()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
:aria-expanded="isOpen"
|
|
22
|
+
aria-haspopup="true"
|
|
23
|
+
@click="handleClick"
|
|
24
|
+
@keydown="handleKeydown"
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</button>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { inject, ref, computed, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
|
|
4
|
+
const { close, activeIndex, registerItem, unregisterItem, setActiveIndex } = inject('menu')
|
|
5
|
+
|
|
6
|
+
const itemId = Symbol()
|
|
7
|
+
const index = ref(-1)
|
|
8
|
+
|
|
9
|
+
onMounted(() => {
|
|
10
|
+
index.value = registerItem(itemId)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
onUnmounted(() => {
|
|
14
|
+
unregisterItem(itemId)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const active = computed(() => activeIndex.value === index.value)
|
|
18
|
+
|
|
19
|
+
const handleMouseEnter = () => {
|
|
20
|
+
setActiveIndex(index.value)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleMouseLeave = () => {
|
|
24
|
+
setActiveIndex(-1)
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div
|
|
30
|
+
role="menuitem"
|
|
31
|
+
@mouseenter="handleMouseEnter"
|
|
32
|
+
@mouseleave="handleMouseLeave"
|
|
33
|
+
>
|
|
34
|
+
<slot :active="active" :close="close" />
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
unmount: { type: Boolean, default: true }
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const { isOpen, close } = inject('menu')
|
|
9
|
+
|
|
10
|
+
const handleKeydown = (event) => {
|
|
11
|
+
if (event.key === 'Escape') {
|
|
12
|
+
event.preventDefault()
|
|
13
|
+
close()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const shouldRender = computed(() => {
|
|
18
|
+
if (props.unmount) return isOpen.value
|
|
19
|
+
return true
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const shouldShow = computed(() => isOpen.value)
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div
|
|
27
|
+
v-if="shouldRender"
|
|
28
|
+
v-show="shouldShow"
|
|
29
|
+
role="menu"
|
|
30
|
+
tabindex="-1"
|
|
31
|
+
@keydown="handleKeydown"
|
|
32
|
+
>
|
|
33
|
+
<slot />
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
package/components/Loading.vue
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex justify-center">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
enter="
|
|
6
|
-
enter-
|
|
7
|
-
|
|
8
|
-
leave="
|
|
9
|
-
leave-
|
|
10
|
-
leave-to="opacity-0"
|
|
3
|
+
<Transition
|
|
4
|
+
enter-active-class="transition-opacity duration-[2000ms]"
|
|
5
|
+
enter-from-class="opacity-0"
|
|
6
|
+
enter-to-class="opacity-100"
|
|
7
|
+
leave-active-class="transition-opacity duration-150"
|
|
8
|
+
leave-from-class="opacity-100"
|
|
9
|
+
leave-to-class="opacity-0"
|
|
11
10
|
>
|
|
12
|
-
<div class="fa-3x">
|
|
11
|
+
<div v-if="showTransition" class="fa-3x">
|
|
13
12
|
<i class="fa-solid fa-gear fa-spin"></i>
|
|
14
13
|
</div>
|
|
15
|
-
</
|
|
14
|
+
</Transition>
|
|
16
15
|
</div>
|
|
17
16
|
</template>
|
|
18
17
|
|
|
19
18
|
<script setup>
|
|
20
19
|
import { onMounted, ref } from "vue"
|
|
21
|
-
import { TransitionRoot } from '@headlessui/vue'
|
|
22
20
|
|
|
23
21
|
const showTransition = ref(false)
|
|
24
22
|
|
|
@@ -27,6 +25,4 @@ onMounted(() => {
|
|
|
27
25
|
showTransition.value = true
|
|
28
26
|
}, 10)
|
|
29
27
|
})
|
|
30
|
-
|
|
31
|
-
|
|
32
28
|
</script>
|
package/index.js
CHANGED
|
@@ -3,6 +3,25 @@ export { default as GDeletableList } from './components/GDeletableList.vue'
|
|
|
3
3
|
export { default as GStatusBadge } from './components/GStatusBadge.vue'
|
|
4
4
|
export { default as Loading } from './components/Loading.vue'
|
|
5
5
|
export { default as Progressbar } from './components/Progressbar.vue'
|
|
6
|
+
export { default as GMenu } from './components/GMenu.vue'
|
|
7
|
+
export { default as GMenuButton } from './components/GMenuButton.vue'
|
|
8
|
+
export { default as GMenuItems } from './components/GMenuItems.vue'
|
|
9
|
+
export { default as GMenuItem } from './components/GMenuItem.vue'
|
|
10
|
+
export { default as GIcon } from './components/GIcon.vue'
|
|
11
|
+
|
|
12
|
+
export { registerIcons, iconRegistry, getIcon, hasIcon } from './icons/registry.js'
|
|
13
|
+
export { defaultIconMap } from './icons/iconMap.js'
|
|
14
|
+
|
|
15
|
+
import GBreadcrumbs from './components/GBreadcrumbs.vue'
|
|
16
|
+
import GDeletableList from './components/GDeletableList.vue'
|
|
17
|
+
import GStatusBadge from './components/GStatusBadge.vue'
|
|
18
|
+
import Loading from './components/Loading.vue'
|
|
19
|
+
import Progressbar from './components/Progressbar.vue'
|
|
20
|
+
import GMenu from './components/GMenu.vue'
|
|
21
|
+
import GMenuButton from './components/GMenuButton.vue'
|
|
22
|
+
import GMenuItems from './components/GMenuItems.vue'
|
|
23
|
+
import GMenuItem from './components/GMenuItem.vue'
|
|
24
|
+
import GIcon from './components/GIcon.vue'
|
|
6
25
|
|
|
7
26
|
export function install(app) {
|
|
8
27
|
app.component('GBreadcrumbs', GBreadcrumbs)
|
|
@@ -10,6 +29,11 @@ export function install(app) {
|
|
|
10
29
|
app.component('GStatusBadge', GStatusBadge)
|
|
11
30
|
app.component('Loading', Loading)
|
|
12
31
|
app.component('Progressbar', Progressbar)
|
|
32
|
+
app.component('GMenu', GMenu)
|
|
33
|
+
app.component('GMenuButton', GMenuButton)
|
|
34
|
+
app.component('GMenuItems', GMenuItems)
|
|
35
|
+
app.component('GMenuItem', GMenuItem)
|
|
36
|
+
app.component('GIcon', GIcon)
|
|
13
37
|
}
|
|
14
38
|
|
|
15
39
|
export default { install }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gameap/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -17,15 +17,11 @@
|
|
|
17
17
|
],
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"vue": "^3.5.0",
|
|
20
|
-
"vue-router": "^4.0.0"
|
|
21
|
-
"@headlessui/vue": "^1.7.0"
|
|
20
|
+
"vue-router": "^4.0.0"
|
|
22
21
|
},
|
|
23
22
|
"peerDependenciesMeta": {
|
|
24
23
|
"vue-router": {
|
|
25
24
|
"optional": true
|
|
26
|
-
},
|
|
27
|
-
"@headlessui/vue": {
|
|
28
|
-
"optional": true
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
27
|
}
|