1
1
<template >
2
2
<div
3
+ ref =" observerTarget"
3
4
class =" oc-tile-card oc-card oc-card-default oc-rounded"
4
5
:data-item-id =" resource.id"
5
6
:class =" {
9
10
}"
10
11
@contextmenu =" $emit('contextmenu', $event)"
11
12
>
12
- <resource-link
13
- class =" oc-card-media-top oc-flex oc-flex-center oc-flex-middle oc-m-rm"
14
- :resource =" resource"
15
- :link =" resourceRoute"
16
- :is-resource-clickable =" isResourceClickable"
17
- tabindex =" -1"
18
- @click =" $emit('click')"
19
- >
20
- <div class =" oc-tile-card-selection" >
21
- <slot name =" selection" :item =" resource" />
22
- </div >
23
- <oc-tag
24
- v-if =" isResourceDisabled && isProjectSpaceResource(resource)"
25
- class =" resource-disabled-indicator oc-position-absolute"
26
- type =" span"
27
- >
28
- <span v-text =" $gettext('Disabled')" />
29
- </oc-tag >
30
- <div
31
- v-oc-tooltip =" tooltipLabelIcon"
32
- class =" oc-tile-card-preview oc-flex oc-flex-middle oc-flex-center"
33
- :aria-label =" tooltipLabelIcon"
13
+ <div v-if =" isHidden" class =" oc-tile-card-lazy-shimmer" ></div >
14
+ <template v-else >
15
+ <resource-link
16
+ class =" oc-card-media-top oc-flex oc-flex-center oc-flex-middle oc-m-rm"
17
+ :resource =" resource"
18
+ :link =" resourceRoute"
19
+ :is-resource-clickable =" isResourceClickable"
20
+ tabindex =" -1"
21
+ @click =" $emit('click')"
34
22
>
35
- <div class =" oc-tile-card-hover" ></div >
36
- <slot name =" imageField" :item =" resource" >
37
- <oc-img
38
- v-if =" shouldDisplayThumbnails(resource)"
39
- class =" tile-preview"
40
- :src =" resource.thumbnail"
41
- />
42
- <resource-icon
43
- v-else
44
- :resource =" resource"
45
- :size =" resourceIconSize"
46
- class =" tile-default-image oc-pt-xs"
47
- >
48
- <template v-if =" showStatusIcon " #status >
49
- <oc-icon v-bind =" statusIconAttrs" size =" xsmall" />
50
- </template >
51
- </resource-icon >
52
- </slot >
53
- </div >
54
- </resource-link >
55
- <div class =" oc-card-body oc-p-s" >
56
- <div class =" oc-flex oc-flex-between oc-flex-middle" >
57
- <div class =" oc-flex oc-flex-middle oc-text-truncate resource-name-wrapper" >
58
- <resource-list-item
59
- :resource =" resource"
60
- :is-icon-displayed =" false"
61
- :is-extension-displayed =" isExtensionDisplayed"
62
- :is-resource-clickable =" isResourceClickable"
63
- :link =" resourceRoute"
64
- @click =" $emit('click')"
65
- />
23
+ <div class =" oc-tile-card-selection" >
24
+ <slot name =" selection" :item =" resource" />
25
+ </div >
26
+ <oc-tag
27
+ v-if =" isResourceDisabled && isProjectSpaceResource(resource)"
28
+ class =" resource-disabled-indicator oc-position-absolute"
29
+ type =" span"
30
+ >
31
+ <span v-text =" $gettext('Disabled')" />
32
+ </oc-tag >
33
+ <div
34
+ v-oc-tooltip =" tooltipLabelIcon"
35
+ class =" oc-tile-card-preview oc-flex oc-flex-middle oc-flex-center"
36
+ :aria-label =" tooltipLabelIcon"
37
+ >
38
+ <div class =" oc-tile-card-hover" ></div >
39
+ <slot name =" imageField" :item =" resource" >
40
+ <oc-img
41
+ v-if =" shouldDisplayThumbnails(resource)"
42
+ class =" tile-preview"
43
+ :src =" resource.thumbnail"
44
+ />
45
+ <resource-icon
46
+ v-else
47
+ :resource =" resource"
48
+ :size =" resourceIconSize"
49
+ class =" tile-default-image oc-pt-xs"
50
+ >
51
+ <template v-if =" showStatusIcon " #status >
52
+ <oc-icon v-bind =" statusIconAttrs" size =" xsmall" />
53
+ </template >
54
+ </resource-icon >
55
+ </slot >
66
56
</div >
67
- <div class =" oc-flex oc-flex-middle" >
68
- <!-- Slot for indicators !-->
69
- <slot name =" indicators" :item =" resource" class =" resource-indicators" />
70
- <!-- Slot for individual actions -->
71
- <slot name =" actions" :item =" resource" />
72
- <!-- Slot for contextmenu -->
73
- <slot name =" contextMenu" :item =" resource" />
57
+ </resource-link >
58
+ <div class =" oc-card-body oc-p-s" >
59
+ <div class =" oc-flex oc-flex-between oc-flex-middle" >
60
+ <div class =" oc-flex oc-flex-middle oc-text-truncate resource-name-wrapper" >
61
+ <resource-list-item
62
+ :resource =" resource"
63
+ :is-icon-displayed =" false"
64
+ :is-extension-displayed =" isExtensionDisplayed"
65
+ :is-resource-clickable =" isResourceClickable"
66
+ :link =" resourceRoute"
67
+ @click =" $emit('click')"
68
+ />
69
+ </div >
70
+ <div class =" oc-flex oc-flex-middle" >
71
+ <!-- Slot for indicators !-->
72
+ <slot name =" indicators" :item =" resource" class =" resource-indicators" />
73
+ <!-- Slot for individual actions -->
74
+ <slot name =" actions" :item =" resource" />
75
+ <!-- Slot for contextmenu -->
76
+ <slot name =" contextMenu" :item =" resource" />
77
+ </div >
74
78
</div >
79
+ <p v-if =" resourceDescription" class =" oc-text-left oc-my-rm oc-text-truncate" >
80
+ <small v-text =" resourceDescription" />
81
+ </p >
75
82
</div >
76
- <p v-if =" resourceDescription" class =" oc-text-left oc-my-rm oc-text-truncate" >
77
- <small v-text =" resourceDescription" />
78
- </p >
79
- </div >
83
+ </template >
80
84
</div >
81
85
</template >
82
86
@@ -90,6 +94,8 @@ import { useGettext } from 'vue3-gettext'
90
94
import { isSpaceResource } from ' @ownclouders/web-client'
91
95
import { isResourceTxtFileAlmostEmpty } from ' ../../helpers'
92
96
import { RouteLocationRaw } from ' vue-router'
97
+ import { useIsVisible } from ' @ownclouders/design-system/src/composables'
98
+ import { customRef , ref , unref } from ' vue'
93
99
94
100
export default defineComponent ({
95
101
name: ' ResourceTile' ,
@@ -132,11 +138,30 @@ export default defineComponent({
132
138
validator : (value : string ) => {
133
139
return [' large' , ' xlarge' , ' xxlarge' , ' xxxlarge' ].includes (value )
134
140
}
141
+ },
142
+ lazy: {
143
+ type: Boolean ,
144
+ default: false
135
145
}
136
146
},
137
147
emits: [' click' , ' contextmenu' ],
138
148
setup(props ) {
139
149
const { $gettext } = useGettext ()
150
+
151
+ const observerTarget = customRef ((track , trigger ) => {
152
+ let $el: HTMLElement
153
+ return {
154
+ get() {
155
+ track ()
156
+ return $el
157
+ },
158
+ set(value ) {
159
+ $el = value
160
+ trigger ()
161
+ }
162
+ }
163
+ })
164
+
140
165
const showStatusIcon = computed (() => {
141
166
return props .resource .locked || props .resource .processing
142
167
})
@@ -176,13 +201,23 @@ export default defineComponent({
176
201
return resource .thumbnail && ! isResourceTxtFileAlmostEmpty (resource )
177
202
}
178
203
204
+ const { isVisible } = props .lazy
205
+ ? useIsVisible ({
206
+ target: observerTarget
207
+ })
208
+ : { isVisible: ref (true ) }
209
+
210
+ const isHidden = computed (() => ! unref (isVisible ))
211
+
179
212
return {
180
213
statusIconAttrs ,
181
214
showStatusIcon ,
182
215
tooltipLabelIcon ,
183
216
resourceDescription ,
184
217
shouldDisplayThumbnails ,
185
- isProjectSpaceResource
218
+ isProjectSpaceResource ,
219
+ isHidden ,
220
+ observerTarget
186
221
}
187
222
}
188
223
})
@@ -306,5 +341,36 @@ export default defineComponent({
306
341
max-width : 70% ;
307
342
overflow : hidden ;
308
343
}
344
+
345
+ & -lazy-shimmer {
346
+ height : 120px ;
347
+ opacity : 0.2 ;
348
+ position : relative ;
349
+ overflow : hidden ;
350
+ }
351
+
352
+ & -lazy-shimmer ::after {
353
+ animation : shimmer 2s infinite ;
354
+ background-image : linear-gradient (
355
+ 90deg ,
356
+ rgba (#4c5f79 , 0 ) 0 ,
357
+ rgba (#4c5f79 , 0.2 ) 20% ,
358
+ rgba (#4c5f79 , 0.5 ) 60% ,
359
+ rgba (#4c5f79 , 0 )
360
+ );
361
+ bottom : 0 ;
362
+ content : ' ' ;
363
+ left : 0 ;
364
+ position : absolute ;
365
+ right : 0 ;
366
+ top : 0 ;
367
+ transform : translateX (-100% );
368
+ }
369
+
370
+ @keyframes shimmer {
371
+ 100% {
372
+ transform : translateX (100% );
373
+ }
374
+ }
309
375
}
310
376
</style >
0 commit comments