@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 TwoColumnLayout from './src/components/TwoColumnLayout.vue'
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.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
- <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>
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 {