@redseed/redseed-ui-vue3 1.0.0 → 1.0.2
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/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Card from './src/components/Card/Card.vue'
|
|
2
|
+
import Image from './src/components/Image/Image.vue'
|
|
3
|
+
import TwoColumnLayout from './src/components/TwoColumnLayout/TwoColumnLayout.vue'
|
|
2
4
|
|
|
3
5
|
export {
|
|
6
|
+
Card,
|
|
7
|
+
Image,
|
|
4
8
|
TwoColumnLayout,
|
|
5
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redseed/redseed-ui-vue3",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "RedSeed UI Vue 3 components",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"author": "",
|
|
11
11
|
"license": "ISC",
|
|
12
12
|
"devDependencies": {
|
|
13
|
+
"@heroicons/vue": "^2.1.3",
|
|
14
|
+
"@vueuse/components": "^10.9.0",
|
|
15
|
+
"@vueuse/core": "^10.9.0",
|
|
13
16
|
"vue": "^3.4.21"
|
|
14
17
|
}
|
|
15
18
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
clickable: {
|
|
6
|
+
type: Boolean,
|
|
7
|
+
default: true,
|
|
8
|
+
},
|
|
9
|
+
hoverable: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: true,
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const emit = defineEmits(['click'])
|
|
16
|
+
|
|
17
|
+
const cardClass = computed(() => [
|
|
18
|
+
'card',
|
|
19
|
+
{
|
|
20
|
+
'card--hoverable': props.hoverable,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
'card--clickable': props.clickable,
|
|
24
|
+
},
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
function clicked() {
|
|
28
|
+
if (props.clickable) emit('click')
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
<template>
|
|
32
|
+
<div :class="cardClass"
|
|
33
|
+
@click.prevent="clicked"
|
|
34
|
+
>
|
|
35
|
+
<div v-if="$slots.image"
|
|
36
|
+
class="card__image"
|
|
37
|
+
>
|
|
38
|
+
<slot name="image"></slot>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="card__content">
|
|
41
|
+
<div v-if="$slots.title || $slots.status"
|
|
42
|
+
class="card__content__top"
|
|
43
|
+
>
|
|
44
|
+
<div v-if="$slots.title"
|
|
45
|
+
class="card__content__top__title"
|
|
46
|
+
>
|
|
47
|
+
<slot name="title"></slot>
|
|
48
|
+
</div>
|
|
49
|
+
<div v-if="$slots.status"
|
|
50
|
+
class="card__content__top__status"
|
|
51
|
+
>
|
|
52
|
+
<slot name="status"></slot>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<slot></slot>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
<style lang="scss" scoped>
|
|
60
|
+
.card {
|
|
61
|
+
@apply border border-gray-100 rounded-lg bg-white select-none overflow-hidden shadow-full-light transition duration-200;
|
|
62
|
+
&--hoverable {
|
|
63
|
+
@apply hover:shadow-md;
|
|
64
|
+
}
|
|
65
|
+
&--clickable {
|
|
66
|
+
@apply cursor-pointer;
|
|
67
|
+
}
|
|
68
|
+
&__image {
|
|
69
|
+
@apply aspect-video overflow-hidden;
|
|
70
|
+
}
|
|
71
|
+
&__content {
|
|
72
|
+
@apply min-h-14 p-3;
|
|
73
|
+
&__top {
|
|
74
|
+
@apply flex justify-between space-x-2 pb-2;
|
|
75
|
+
&__title {
|
|
76
|
+
@apply h-12 text-lg font-semibold leading-6 line-clamp-2;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import { useImage } from '@vueuse/core'
|
|
4
|
+
import { PhotoIcon } from '@heroicons/vue/24/outline'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
originalUrl: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: '',
|
|
10
|
+
},
|
|
11
|
+
largeUrl: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: '',
|
|
14
|
+
},
|
|
15
|
+
mediumUrl: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: '',
|
|
18
|
+
},
|
|
19
|
+
smallUrl: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: '',
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const large = computed(() => props.largeUrl ?? props.originalUrl)
|
|
26
|
+
const medium = computed(() => props.mediumUrl ?? props.originalUrl)
|
|
27
|
+
const small = computed(() => props.smallUrl ?? props.originalUrl)
|
|
28
|
+
|
|
29
|
+
const originalOptions = computed(() => ({
|
|
30
|
+
src: props.originalUrl,
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
const largeOptions = computed(() => ({
|
|
34
|
+
src: large.value,
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
const mediumOptions = computed(() => ({
|
|
38
|
+
src: medium.value,
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
const smallOptions = computed(() => ({
|
|
42
|
+
src: small.value,
|
|
43
|
+
}))
|
|
44
|
+
|
|
45
|
+
const originalImage = ref(useImage(originalOptions.value))
|
|
46
|
+
const largeImage = ref(useImage(largeOptions.value))
|
|
47
|
+
const mediumImage = ref(useImage(mediumOptions.value))
|
|
48
|
+
const smallImage = ref(useImage(smallOptions.value))
|
|
49
|
+
|
|
50
|
+
const isLoading = computed(() => {
|
|
51
|
+
return originalImage.value.isLoading
|
|
52
|
+
|| largeImage.value.isLoading
|
|
53
|
+
|| mediumImage.value.isLoading
|
|
54
|
+
|| smallImage.value.isLoading
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const error = computed(() => {
|
|
58
|
+
return originalImage.value.error
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const sources = computed(() => {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
media: '(min-width:1024px)',
|
|
65
|
+
srcset: large.value
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
media: '(min-width:640px)',
|
|
69
|
+
srcset: medium.value
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
media: '(min-width:320px)',
|
|
73
|
+
srcset: small.value
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
})
|
|
77
|
+
</script>
|
|
78
|
+
<template>
|
|
79
|
+
<div class="image">
|
|
80
|
+
<div v-if="!originalUrl"
|
|
81
|
+
class="image__empty"
|
|
82
|
+
>
|
|
83
|
+
<slot name="empty-icon">
|
|
84
|
+
<PhotoIcon class="icon"></PhotoIcon>
|
|
85
|
+
</slot>
|
|
86
|
+
</div>
|
|
87
|
+
<div v-else-if="isLoading"
|
|
88
|
+
class="image__message"
|
|
89
|
+
>
|
|
90
|
+
<slot name="loading">
|
|
91
|
+
Loading...
|
|
92
|
+
</slot>
|
|
93
|
+
</div>
|
|
94
|
+
<div v-else-if="error"
|
|
95
|
+
class="image__message"
|
|
96
|
+
>
|
|
97
|
+
<slot name="error">
|
|
98
|
+
Could not load image
|
|
99
|
+
</slot>
|
|
100
|
+
</div>
|
|
101
|
+
<picture v-else>
|
|
102
|
+
<source v-for="{ media, srcset } in sources"
|
|
103
|
+
:key="media"
|
|
104
|
+
:media="media"
|
|
105
|
+
:srcset="srcset"
|
|
106
|
+
>
|
|
107
|
+
<img :src="originalUrl">
|
|
108
|
+
</picture>
|
|
109
|
+
</div>
|
|
110
|
+
</template>
|
|
111
|
+
<style lang="scss" scoped>
|
|
112
|
+
.image {
|
|
113
|
+
@apply w-full h-full flex items-center justify-center bg-gray-100 overflow-hidden;
|
|
114
|
+
&__empty {
|
|
115
|
+
@apply flex items-center justify-center text-gray-300;
|
|
116
|
+
.icon {
|
|
117
|
+
@apply w-1/4;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
&__message {
|
|
121
|
+
@apply text-gray-400 text-sm;
|
|
122
|
+
}
|
|
123
|
+
picture {
|
|
124
|
+
@apply w-full h-full;
|
|
125
|
+
img {
|
|
126
|
+
@apply w-full h-full object-cover;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
</style>
|
|
@@ -7,17 +7,17 @@ const props = defineProps({
|
|
|
7
7
|
})
|
|
8
8
|
</script>
|
|
9
9
|
<template>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</div>
|
|
17
|
-
<div class="two-column-layout__aside">
|
|
18
|
-
<slot name="aside"></slot>
|
|
19
|
-
</div>
|
|
10
|
+
<div :class="[
|
|
11
|
+
'two-column-layout',
|
|
12
|
+
leftAside ? 'two-column-layout--left-aside' : '',
|
|
13
|
+
]">
|
|
14
|
+
<div class="two-column-layout__main">
|
|
15
|
+
<slot name="main"></slot>
|
|
20
16
|
</div>
|
|
17
|
+
<div class="two-column-layout__aside">
|
|
18
|
+
<slot name="aside"></slot>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
21
|
</template>
|
|
22
22
|
<style lang="scss">
|
|
23
23
|
.two-column-layout {
|