@redseed/redseed-ui-vue3 1.9.2 → 2.0.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/index.js +20 -111
- package/package.json +5 -5
- package/src/components/Badge/index.js +7 -0
- package/src/components/Button/ButtonDanger.vue +6 -12
- package/src/components/Button/ButtonDangerFull.vue +1 -7
- package/src/components/Button/ButtonDangerFullRounded.vue +1 -7
- package/src/components/Button/ButtonDangerRounded.vue +1 -7
- package/src/components/Button/ButtonPrimary.vue +6 -12
- package/src/components/Button/ButtonPrimaryFull.vue +1 -8
- package/src/components/Button/ButtonPrimaryFullRounded.vue +1 -8
- package/src/components/Button/ButtonPrimaryRounded.vue +1 -8
- package/src/components/Button/ButtonSecondary.vue +6 -12
- package/src/components/Button/ButtonSecondaryFull.vue +1 -8
- package/src/components/Button/ButtonSecondaryFullRounded.vue +1 -8
- package/src/components/Button/ButtonSecondaryRounded.vue +1 -8
- package/src/components/Button/ButtonSlot.vue +39 -98
- package/src/components/Button/ButtonTertiary.vue +6 -12
- package/src/components/Button/ButtonTertiaryFull.vue +1 -7
- package/src/components/Button/ButtonTertiaryFullRounded.vue +1 -7
- package/src/components/Button/ButtonTertiaryRounded.vue +1 -7
- package/src/components/Button/index.js +37 -0
- package/src/components/ButtonGroup/ButtonGroup.vue +2 -11
- package/src/components/ButtonGroup/ButtonGroupItem.vue +17 -25
- package/src/components/ButtonGroup/index.js +7 -0
- package/src/components/Card/Card.vue +21 -8
- package/src/components/Card/CardResource.vue +26 -0
- package/src/components/Card/index.js +7 -0
- package/src/components/DropdownMenu/DropdownMenu.vue +1 -6
- package/src/components/DropdownMenu/DropdownOption.vue +1 -1
- package/src/components/DropdownMenu/index.js +7 -0
- package/src/components/Form/index.js +13 -0
- package/src/components/FormField/FormFieldCheckbox.vue +1 -1
- package/src/components/FormField/FormFieldSearch.vue +1 -1
- package/src/components/FormField/FormFieldSelect.vue +209 -0
- package/src/components/FormField/FormFieldSlot.vue +8 -2
- package/src/components/FormField/index.js +23 -0
- package/src/components/Image/Image16By9.vue +7 -1
- package/src/components/Image/Image3By1.vue +7 -1
- package/src/components/Image/Image9By16.vue +7 -1
- package/src/components/Image/ImageCircle.vue +7 -1
- package/src/components/Image/index.js +13 -0
- package/src/components/Link/LinkPrimary.vue +19 -0
- package/src/components/Link/LinkSlot.vue +87 -0
- package/src/components/Link/index.js +7 -0
- package/src/components/List/List.vue +70 -0
- package/src/components/List/ListControl.vue +217 -0
- package/src/components/List/ListItem.vue +192 -0
- package/src/components/List/index.js +9 -0
- package/src/components/Logo/index.js +11 -0
- package/src/components/MessageBox/index.js +5 -0
- package/src/components/MetaInfo/MetaInfo.vue +2 -2
- package/src/components/MetaInfo/index.js +5 -0
- package/src/components/Modal/index.js +5 -0
- package/src/components/PageHeading/index.js +5 -0
- package/src/components/Pagination/Pagination.vue +275 -0
- package/src/components/Pagination/PaginationItem.vue +60 -0
- package/src/components/Pagination/PaginationItemCollapsed.vue +18 -0
- package/src/components/Pagination/PaginationItemNext.vue +17 -0
- package/src/components/Pagination/PaginationItemPrevious.vue +17 -0
- package/src/components/Pagination/index.js +13 -0
- package/src/components/Progress/index.js +5 -0
- package/src/components/Social/index.js +7 -0
- package/src/components/Sorting/Sorting.vue +79 -0
- package/src/components/Sorting/index.js +5 -0
- package/src/components/TwoColumnLayout/index.js +5 -0
|
@@ -7,7 +7,13 @@ defineOptions({
|
|
|
7
7
|
</script>
|
|
8
8
|
<template>
|
|
9
9
|
<div class="rsui-image-circle">
|
|
10
|
-
<Image v-bind="$attrs"
|
|
10
|
+
<Image v-bind="$attrs">
|
|
11
|
+
<template v-for="(_, name) in $slots"
|
|
12
|
+
v-slot:[name]="slotProps"
|
|
13
|
+
>
|
|
14
|
+
<slot :name="name" v-bind="slotProps"></slot>
|
|
15
|
+
</template>
|
|
16
|
+
</Image>
|
|
11
17
|
</div>
|
|
12
18
|
</template>
|
|
13
19
|
<style lang="scss" scoped>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Image from './Image.vue'
|
|
2
|
+
import Image3By1 from './Image3By1.vue'
|
|
3
|
+
import Image9By16 from './Image9By16.vue'
|
|
4
|
+
import Image16By9 from './Image16By9.vue'
|
|
5
|
+
import ImageCircle from './ImageCircle.vue'
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
Image,
|
|
9
|
+
Image3By1,
|
|
10
|
+
Image9By16,
|
|
11
|
+
Image16By9,
|
|
12
|
+
ImageCircle,
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import LinkSlot from './LinkSlot.vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
})
|
|
7
|
+
</script>
|
|
8
|
+
<template>
|
|
9
|
+
<LinkSlot class="rsui-link-primary"
|
|
10
|
+
v-bind="$attrs"
|
|
11
|
+
>
|
|
12
|
+
<slot></slot>
|
|
13
|
+
</LinkSlot>
|
|
14
|
+
</template>
|
|
15
|
+
<style lang="scss" scoped>
|
|
16
|
+
.rsui-link-primary {
|
|
17
|
+
@apply text-primary;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
md: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false,
|
|
12
|
+
},
|
|
13
|
+
lg: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: false,
|
|
16
|
+
},
|
|
17
|
+
disabled: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: false,
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
defineEmits(['click'])
|
|
24
|
+
|
|
25
|
+
const defaultSize = computed(() => !props.md && !props.lg)
|
|
26
|
+
|
|
27
|
+
const linkSlotClass = computed(() => [
|
|
28
|
+
'rsui-link-slot',
|
|
29
|
+
{
|
|
30
|
+
// link sizes
|
|
31
|
+
'rsui-link-slot--md': props.md,
|
|
32
|
+
'rsui-link-slot--lg': props.lg || defaultSize.value,
|
|
33
|
+
// link disabled state
|
|
34
|
+
'rsui-link-slot--disabled': props.disabled,
|
|
35
|
+
},
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
const linkLabelClass = computed(() => [
|
|
39
|
+
'rsui-link-slot__label',
|
|
40
|
+
{
|
|
41
|
+
// link label disabled state
|
|
42
|
+
'rsui-link-slot__label--disabled': props.disabled,
|
|
43
|
+
},
|
|
44
|
+
])
|
|
45
|
+
</script>
|
|
46
|
+
<template>
|
|
47
|
+
<a
|
|
48
|
+
:class="linkSlotClass"
|
|
49
|
+
v-bind="$attrs"
|
|
50
|
+
@click="$emit('click', $event)"
|
|
51
|
+
>
|
|
52
|
+
<span :class="linkLabelClass">
|
|
53
|
+
<slot></slot>
|
|
54
|
+
</span>
|
|
55
|
+
</a>
|
|
56
|
+
</template>
|
|
57
|
+
<style lang="scss" scoped>
|
|
58
|
+
.rsui-link-slot {
|
|
59
|
+
// default shape and control
|
|
60
|
+
@apply relative w-fit h-fit inline-flex items-center justify-center;
|
|
61
|
+
@apply select-none outline-none cursor-pointer transition;
|
|
62
|
+
// default text styles
|
|
63
|
+
@apply font-medium text-black;
|
|
64
|
+
// disabled state
|
|
65
|
+
&--disabled {
|
|
66
|
+
@apply opacity-30 cursor-default pointer-events-none;
|
|
67
|
+
}
|
|
68
|
+
// modifier md size
|
|
69
|
+
&--md {
|
|
70
|
+
@apply text-base leading-6;
|
|
71
|
+
}
|
|
72
|
+
// modifier lg size
|
|
73
|
+
&--lg {
|
|
74
|
+
@apply text-lg leading-7;
|
|
75
|
+
}
|
|
76
|
+
// label
|
|
77
|
+
&__label {
|
|
78
|
+
@apply relative;
|
|
79
|
+
@apply after:content-[''] after:absolute after:inset-x-0 after:bottom-0;
|
|
80
|
+
@apply after:w-full after:h-px after:transition;
|
|
81
|
+
@apply after:bg-current after:opacity-30 hover:after:opacity-100;
|
|
82
|
+
&--disabled {
|
|
83
|
+
@apply after:opacity-0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import ListControl from './ListControl.vue'
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
control: {
|
|
11
|
+
type: Boolean,
|
|
12
|
+
default: true
|
|
13
|
+
},
|
|
14
|
+
totalItems: {
|
|
15
|
+
type: Number,
|
|
16
|
+
default: 0
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits(['change'])
|
|
21
|
+
|
|
22
|
+
const controlApplied = ref(false)
|
|
23
|
+
|
|
24
|
+
const showControl = computed(() => props.control && (controlApplied.value || props.totalItems > 0))
|
|
25
|
+
|
|
26
|
+
const showEmptyMessage = computed(() => !props.totalItems)
|
|
27
|
+
|
|
28
|
+
function controlChanged(event) {
|
|
29
|
+
controlApplied.value = true
|
|
30
|
+
|
|
31
|
+
emit('change', event)
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
<template>
|
|
35
|
+
<div class="rsui-list">
|
|
36
|
+
<ListControl
|
|
37
|
+
v-if="showControl"
|
|
38
|
+
v-bind="$attrs"
|
|
39
|
+
:totalItems="totalItems"
|
|
40
|
+
@change="controlChanged"
|
|
41
|
+
>
|
|
42
|
+
<template #sort-asc-label v-if="$slots['sort-asc-label']">
|
|
43
|
+
<slot name="sort-asc-label"></slot>
|
|
44
|
+
</template>
|
|
45
|
+
<template #sort-desc-label v-if="$slots['sort-desc-label']">
|
|
46
|
+
<slot name="sort-desc-label"></slot>
|
|
47
|
+
</template>
|
|
48
|
+
</ListControl>
|
|
49
|
+
<div v-if="!showEmptyMessage"
|
|
50
|
+
class="rsui-list__items"
|
|
51
|
+
>
|
|
52
|
+
<slot></slot>
|
|
53
|
+
</div>
|
|
54
|
+
<div v-if="showEmptyMessage"
|
|
55
|
+
class="rsui-list__empty"
|
|
56
|
+
>
|
|
57
|
+
<slot name="empty">No items found.</slot>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
<style lang="scss" scoped>
|
|
62
|
+
.rsui-list {
|
|
63
|
+
&__items {
|
|
64
|
+
@apply mt-6;
|
|
65
|
+
}
|
|
66
|
+
&__empty {
|
|
67
|
+
@apply mt-6;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
3
|
+
import FormFieldSearch from '../FormField/FormFieldSearch.vue'
|
|
4
|
+
import FormFieldSelect from '../FormField/FormFieldSelect.vue'
|
|
5
|
+
import Sorting from '../Sorting/Sorting.vue'
|
|
6
|
+
import Pagination from '../Pagination/Pagination.vue'
|
|
7
|
+
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
searchable: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: true
|
|
12
|
+
},
|
|
13
|
+
sortable: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: true
|
|
16
|
+
},
|
|
17
|
+
totalItems: {
|
|
18
|
+
type: Number,
|
|
19
|
+
default: 0
|
|
20
|
+
},
|
|
21
|
+
perPage: {
|
|
22
|
+
type: Number,
|
|
23
|
+
default: 10
|
|
24
|
+
},
|
|
25
|
+
currentPage: {
|
|
26
|
+
type: Number,
|
|
27
|
+
default: 1
|
|
28
|
+
},
|
|
29
|
+
filters: {
|
|
30
|
+
type: Array,
|
|
31
|
+
default: () => []
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const paginationClass = computed(() => [
|
|
36
|
+
'rsui-list-control__pagination',
|
|
37
|
+
{
|
|
38
|
+
'rsui-list-control__pagination--inline': !props.filters.length && !props.sortable
|
|
39
|
+
}
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits(['change'])
|
|
43
|
+
|
|
44
|
+
const listControlElement = ref(null)
|
|
45
|
+
|
|
46
|
+
const filterLimit = ref(4)
|
|
47
|
+
|
|
48
|
+
const searchText = ref('')
|
|
49
|
+
const appliedFilters = ref([])
|
|
50
|
+
const sortDesc = ref(false)
|
|
51
|
+
const pagination = ref(null)
|
|
52
|
+
|
|
53
|
+
watch(searchText, () => { fireChangeEvent() }, { flush: 'post' })
|
|
54
|
+
watch(appliedFilters, () => { fireChangeEvent() }, { deep: true, flush: 'post' })
|
|
55
|
+
watch(sortDesc, () => { fireChangeEvent() }, { flush: 'post' })
|
|
56
|
+
watch(pagination, () => { fireChangeEvent() }, { deep: true, flush: 'post' })
|
|
57
|
+
|
|
58
|
+
function fireChangeEvent() {
|
|
59
|
+
emit('change', {
|
|
60
|
+
searchText: searchText.value,
|
|
61
|
+
appliedFilters: appliedFilters.value,
|
|
62
|
+
sortDesc: sortDesc.value,
|
|
63
|
+
pagination: pagination.value
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function calculateWidth() {
|
|
68
|
+
if (!listControlElement.value) return
|
|
69
|
+
|
|
70
|
+
const sizeClasses = [
|
|
71
|
+
'rsui-list-control--sm',
|
|
72
|
+
'rsui-list-control--md',
|
|
73
|
+
'rsui-list-control--lg',
|
|
74
|
+
'rsui-list-control--xl',
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
listControlElement.value.classList.remove(...sizeClasses)
|
|
78
|
+
|
|
79
|
+
const width = listControlElement.value.offsetWidth
|
|
80
|
+
|
|
81
|
+
if (width >= 1280) {
|
|
82
|
+
listControlElement.value.classList.add(sizeClasses[3])
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (width >= 1024) {
|
|
87
|
+
listControlElement.value.classList.add(sizeClasses[2])
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (width >= 640) {
|
|
92
|
+
listControlElement.value.classList.add(sizeClasses[1])
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
listControlElement.value.classList.add(sizeClasses[0])
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onMounted(() => {
|
|
101
|
+
calculateWidth()
|
|
102
|
+
window.addEventListener('resize', calculateWidth)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
onUnmounted(() => window.addEventListener('resize', calculateWidth))
|
|
106
|
+
</script>
|
|
107
|
+
<template>
|
|
108
|
+
<div ref="listControlElement"
|
|
109
|
+
class="rsui-list-control"
|
|
110
|
+
>
|
|
111
|
+
<div v-if="searchable"
|
|
112
|
+
class="rsui-list-control__search"
|
|
113
|
+
>
|
|
114
|
+
<FormFieldSearch
|
|
115
|
+
v-model="searchText"
|
|
116
|
+
:placeholder="'Search'"
|
|
117
|
+
></FormFieldSearch>
|
|
118
|
+
</div>
|
|
119
|
+
<div v-if="filters.length"
|
|
120
|
+
class="rsui-list-control__filters"
|
|
121
|
+
>
|
|
122
|
+
<div v-for="(filter, index) in filters.slice(0, filterLimit)"
|
|
123
|
+
:key="filter"
|
|
124
|
+
class="rsui-list-control__filter"
|
|
125
|
+
>
|
|
126
|
+
<FormFieldSelect
|
|
127
|
+
v-model="appliedFilters[index]"
|
|
128
|
+
:options="filter.options"
|
|
129
|
+
>
|
|
130
|
+
<template #default-option>
|
|
131
|
+
{{ filter.label }}
|
|
132
|
+
</template>
|
|
133
|
+
</FormFieldSelect>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<div v-if="sortable"
|
|
137
|
+
class="rsui-list-control__sort"
|
|
138
|
+
>
|
|
139
|
+
<Sorting primary
|
|
140
|
+
@asc="sortDesc = false"
|
|
141
|
+
@desc="sortDesc = true"
|
|
142
|
+
>
|
|
143
|
+
<template #asc-label>
|
|
144
|
+
<slot name="sort-asc-label">
|
|
145
|
+
Name A to Z
|
|
146
|
+
</slot>
|
|
147
|
+
</template>
|
|
148
|
+
<template #desc-label>
|
|
149
|
+
<slot name="sort-desc-label">
|
|
150
|
+
Name Z to A
|
|
151
|
+
</slot>
|
|
152
|
+
</template>
|
|
153
|
+
</Sorting>
|
|
154
|
+
</div>
|
|
155
|
+
<div v-if="totalItems"
|
|
156
|
+
:class="paginationClass"
|
|
157
|
+
>
|
|
158
|
+
<Pagination
|
|
159
|
+
:totalItems="totalItems"
|
|
160
|
+
:perPage="perPage"
|
|
161
|
+
:currentPage="currentPage"
|
|
162
|
+
@change="pagination = $event"
|
|
163
|
+
></Pagination>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</template>
|
|
167
|
+
<style lang="scss" scoped>
|
|
168
|
+
.rsui-list-control {
|
|
169
|
+
@apply flex flex-wrap justify-between gap-x-2 gap-y-3;
|
|
170
|
+
&--sm {
|
|
171
|
+
.rsui-list-control__search {
|
|
172
|
+
@apply w-full;
|
|
173
|
+
}
|
|
174
|
+
.rsui-list-control__filters {
|
|
175
|
+
@apply w-full grid-cols-2;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
&--md {
|
|
179
|
+
.rsui-list-control__filters {
|
|
180
|
+
@apply flex;
|
|
181
|
+
}
|
|
182
|
+
.rsui-list-control__filter {
|
|
183
|
+
@apply max-w-40 w-40;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
&--lg {
|
|
187
|
+
.rsui-list-control__filters {
|
|
188
|
+
@apply flex-1 grid-cols-4;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
&--xl {
|
|
192
|
+
.rsui-list-control__filters {
|
|
193
|
+
@apply grid-cols-4;
|
|
194
|
+
}
|
|
195
|
+
.rsui-list-control__filter {
|
|
196
|
+
@apply max-w-52;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&__search {
|
|
201
|
+
@apply w-auto;
|
|
202
|
+
}
|
|
203
|
+
&__filters {
|
|
204
|
+
@apply grid gap-2;
|
|
205
|
+
}
|
|
206
|
+
&__filter {}
|
|
207
|
+
&__sort {
|
|
208
|
+
@apply max-h-10 flex items-center;
|
|
209
|
+
}
|
|
210
|
+
&__pagination {
|
|
211
|
+
@apply w-full flex items-center justify-center;
|
|
212
|
+
&--inline {
|
|
213
|
+
@apply w-1/2 mx-auto;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed, useSlots, onMounted, onUnmounted, watch } from 'vue'
|
|
3
|
+
import { UserIcon } from '@heroicons/vue/24/solid'
|
|
4
|
+
import ButtonTertiary from '../Button/ButtonTertiary.vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
avatar: {
|
|
8
|
+
type: Boolean,
|
|
9
|
+
default: false,
|
|
10
|
+
},
|
|
11
|
+
action: {
|
|
12
|
+
type: Boolean,
|
|
13
|
+
default: false,
|
|
14
|
+
},
|
|
15
|
+
clickable: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const listItemElement = ref(null)
|
|
22
|
+
|
|
23
|
+
const isWide = ref(false)
|
|
24
|
+
|
|
25
|
+
function calculateWidth() {
|
|
26
|
+
if (!listItemElement.value) return
|
|
27
|
+
|
|
28
|
+
const width = listItemElement.value.offsetWidth
|
|
29
|
+
|
|
30
|
+
isWide.value = width >= 480
|
|
31
|
+
if (isWide.value) {
|
|
32
|
+
listItemElement.value.classList.add('rsui-list-item--wide')
|
|
33
|
+
} else {
|
|
34
|
+
listItemElement.value.classList.remove('rsui-list-item--wide')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
calculateWidth()
|
|
40
|
+
window.addEventListener('resize', calculateWidth)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
onUnmounted(() => window.addEventListener('resize', calculateWidth))
|
|
44
|
+
|
|
45
|
+
const slots = useSlots()
|
|
46
|
+
|
|
47
|
+
const shouldShowBody = computed(() => {
|
|
48
|
+
return isWide.value && (slots.meta || slots.description)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const listItemClass = computed(() => [
|
|
52
|
+
'rsui-list-item',
|
|
53
|
+
{
|
|
54
|
+
'rsui-list-item--clickable': props.clickable,
|
|
55
|
+
'rsui-list-item--avatar': props.avatar,
|
|
56
|
+
}
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
const listItemBackgroundClass = computed(() => [
|
|
60
|
+
'rsui-list-item__bg',
|
|
61
|
+
{
|
|
62
|
+
'rsui-list-item__bg--clickable': props.clickable,
|
|
63
|
+
}
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
const emit = defineEmits(['click', 'clickAction'])
|
|
67
|
+
|
|
68
|
+
function clickAction(event) {
|
|
69
|
+
event.stopPropagation()
|
|
70
|
+
|
|
71
|
+
emit('clickAction', event)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clickItem(event) {
|
|
75
|
+
if (!props.clickable) return
|
|
76
|
+
|
|
77
|
+
emit('click', event)
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
<template>
|
|
81
|
+
<div ref="listItemElement"
|
|
82
|
+
:class="listItemClass"
|
|
83
|
+
>
|
|
84
|
+
<!--
|
|
85
|
+
This header div is used to show avatar, title, status and action slots.
|
|
86
|
+
It is always visible.
|
|
87
|
+
The peer class is used to show hover effect on the whole item with the bg div.
|
|
88
|
+
-->
|
|
89
|
+
<div class="rsui-list-item__header peer">
|
|
90
|
+
<div v-if="avatar" class="rsui-list-item__avatar">
|
|
91
|
+
<slot name="avatar">
|
|
92
|
+
<div class="rsui-list-item__avatar-placeholder">
|
|
93
|
+
<UserIcon></UserIcon>
|
|
94
|
+
</div>
|
|
95
|
+
</slot>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="rsui-list-item__title">
|
|
98
|
+
<span>
|
|
99
|
+
<slot name="title"></slot>
|
|
100
|
+
</span>
|
|
101
|
+
</div>
|
|
102
|
+
<div v-if="$slots.status" class="rsui-list-item__status">
|
|
103
|
+
<slot name="status"></slot>
|
|
104
|
+
</div>
|
|
105
|
+
<div v-if="action" class="rsui-list-item__action">
|
|
106
|
+
<slot name="action" :clickAction="clickAction">
|
|
107
|
+
<ButtonTertiary xs @click="clickAction">
|
|
108
|
+
<slot name="action-label">Action</slot>
|
|
109
|
+
</ButtonTertiary>
|
|
110
|
+
</slot>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<!--
|
|
114
|
+
This background needs to be sibling to header div above.
|
|
115
|
+
It is used to make the whole item clickable and show hover effect.
|
|
116
|
+
the hover effect is also applied when the header action button is hovered,
|
|
117
|
+
but not the active effect when the header action button is clicked.
|
|
118
|
+
-->
|
|
119
|
+
<div :class="listItemBackgroundClass" @click="clickItem"></div>
|
|
120
|
+
<!--
|
|
121
|
+
This body div is used to show meta and description slots in larger screens.
|
|
122
|
+
It is hidden in smaller screens.
|
|
123
|
+
-->
|
|
124
|
+
<div v-if="shouldShowBody" class="rsui-list-item__body">
|
|
125
|
+
<div v-if="$slots.meta" class="rsui-list-item__meta">
|
|
126
|
+
<slot name="meta"></slot>
|
|
127
|
+
</div>
|
|
128
|
+
<div v-if="$slots.description" class="rsui-list-item__description">
|
|
129
|
+
<slot name="description"></slot>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
<style lang="scss" scoped>
|
|
135
|
+
.rsui-list-item {
|
|
136
|
+
@apply relative flex flex-col rounded-md p-3 overflow-hidden;
|
|
137
|
+
&--clickable {
|
|
138
|
+
@apply cursor-pointer;
|
|
139
|
+
}
|
|
140
|
+
&--avatar {
|
|
141
|
+
@apply pl-12 lg:pl-16 *:pl-2 *:lg:pl-1;
|
|
142
|
+
}
|
|
143
|
+
&--wide {
|
|
144
|
+
.rsui-list-item__status {
|
|
145
|
+
@apply w-auto order-none;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
&__header {
|
|
149
|
+
@apply relative z-1 flex flex-wrap items-center gap-2 pointer-events-none;
|
|
150
|
+
}
|
|
151
|
+
&__bg {
|
|
152
|
+
@apply absolute inset-0 bg-white transition;
|
|
153
|
+
@apply hover:bg-gray-100 peer-hover:bg-gray-100;
|
|
154
|
+
&--clickable {
|
|
155
|
+
@apply active:bg-gray-200;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
&__body {
|
|
159
|
+
@apply relative z-1 pointer-events-none;
|
|
160
|
+
}
|
|
161
|
+
&__avatar {
|
|
162
|
+
@apply absolute top-0 ml-1 -left-10 lg:-left-14;
|
|
163
|
+
@apply aspect-square overflow-hidden size-9 lg:size-12;
|
|
164
|
+
&-placeholder {
|
|
165
|
+
@apply size-full rounded-full overflow-hidden bg-neutral-200;
|
|
166
|
+
@apply flex items-center justify-center;
|
|
167
|
+
svg {
|
|
168
|
+
@apply size-5 lg:size-6 fill-neutral-400;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
&__title {
|
|
173
|
+
@apply flex-1 inline-flex items-center text-base font-semibold;
|
|
174
|
+
@apply h-9 leading-5 lg:h-12 lg:leading-6;
|
|
175
|
+
span {
|
|
176
|
+
@apply line-clamp-2;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
&__status {
|
|
180
|
+
@apply inline-flex items-center w-full order-last;
|
|
181
|
+
}
|
|
182
|
+
&__action {
|
|
183
|
+
@apply pointer-events-auto;
|
|
184
|
+
}
|
|
185
|
+
&__meta {
|
|
186
|
+
@apply mt-2 flex flex-wrap gap-x-4 gap-y-2;
|
|
187
|
+
}
|
|
188
|
+
&__description {
|
|
189
|
+
@apply line-clamp-2 mt-2 text-base;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import LogoRedSeedBuild from './LogoRedSeedBuild.vue'
|
|
2
|
+
import LogoRedSeedCoach from './LogoRedSeedCoach.vue'
|
|
3
|
+
import LogoRedSeedCourses from './LogoRedSeedCourses.vue'
|
|
4
|
+
import LogoRedSeedLMS from './LogoRedSeedLMS.vue'
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
LogoRedSeedBuild,
|
|
8
|
+
LogoRedSeedCoach,
|
|
9
|
+
LogoRedSeedCourses,
|
|
10
|
+
LogoRedSeedLMS,
|
|
11
|
+
}
|
|
@@ -37,7 +37,7 @@ const emit = defineEmits(['click'])
|
|
|
37
37
|
@apply flex space-x-1 items-center;
|
|
38
38
|
}
|
|
39
39
|
&__label {
|
|
40
|
-
@apply font-semibold text-sm text-default
|
|
40
|
+
@apply font-semibold text-sm text-rsui-default leading-4;
|
|
41
41
|
}
|
|
42
42
|
&__help {
|
|
43
43
|
@apply text-gray-400 size-4 cursor-pointer;
|
|
@@ -46,7 +46,7 @@ const emit = defineEmits(['click'])
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
&__value {
|
|
49
|
-
@apply text-xs text-medium leading-5;
|
|
49
|
+
@apply text-xs text-rsui-medium leading-5;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
</style>
|