• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / iznik-nuxt3 / 99e3f760-c611-4bfd-88c0-b43b43faaba6

05 Dec 2025 11:02PM UTC coverage: 42.545% (+0.4%) from 42.1%
99e3f760-c611-4bfd-88c0-b43b43faaba6

push

circleci

actions-user
Auto-merge production to app-ci-fd (daily scheduled)

Automated merge from production branch after successful tests.

🤖 Automated by GitHub Actions

1890 of 4798 branches covered (39.39%)

Branch coverage included in aggregate %.

1 of 3 new or added lines in 1 file covered. (33.33%)

305 existing lines in 20 files now uncovered.

2376 of 5229 relevant lines covered (45.44%)

31.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.77
/components/InfiniteLoading.vue
1
<template>
2
  <client-only>
3
    <div
4
      :key="bump"
5
      v-observe-visibility="{
6
        callback: visibilityChanged,
7
        options: {
8
          rootMargin: '0px 0px ' + distance + 'px 0px',
9
        },
10
      }"
11
      class="infinite-loader pb-4"
12
    >
13
      <slot v-if="state == 'loading'" name="spinner"></slot>
22✔
14
      <slot v-if="state == 'complete'" name="complete"></slot>
22✔
15
      <slot v-if="state == 'error'" name="error"></slot>
22!
16
    </div>
17
  </client-only>
18
</template>
19
<script setup>
20
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
21

22
// Derived from https://github.com/oumoussa98/vue3-infinite-loading.  Reworked radically to allow an async event
23
// handler, and to make consistent with the rest of this codebase.
24
const props = defineProps({
11✔
25
  top: { type: Boolean, required: false },
26
  target: { type: [String, Boolean], required: false, default: null },
27
  distance: { type: Number, required: false, default: 0 },
28
  identifier: { type: [String, Number], required: false, default: null },
29
  firstload: { type: Boolean, required: false, default: true },
30
  slots: { type: Object, required: false, default: null },
31
})
32

33
const emit = defineEmits(['infinite'])
11✔
34

35
const state = ref('ready')
11✔
36
const bump = ref(0)
11✔
37
const visible = ref(false)
11✔
38
let timer = null
11✔
39

40
// Methods
41
function visibilityChanged(isVisible) {
22✔
42
  visible.value = isVisible
22✔
43
}
44

45
function loading() {
11✔
46
  state.value = 'loading'
11✔
47
}
48

49
function loaded() {
×
50
  state.value = 'loaded'
×
51
}
52

53
function complete() {
22✔
54
  state.value = 'complete'
22✔
55
}
56

57
function error() {
×
58
  state.value = 'error'
×
59
}
60

61
function stopObserver() {
11✔
62
  complete()
11✔
63
}
64

65
async function emitInfinite() {
11✔
66
  loading()
11✔
67

68
  // Wait for the next tick otherwise if the event handlers return synchronously we may not end up triggering
69
  // the watch.
70
  await nextTick()
11✔
71
  emit('infinite', {
11✔
72
    loading,
73
    loaded,
74
    complete,
75
    error,
76
    stopObserver,
77
  })
78
}
79

80
function fallback() {
168✔
81
  timer = null
168✔
82

83
  if (visible.value && state.value === 'loaded') {
168!
84
    // We have loaded and not completed, and yet it's still visible.  We need to do some more.
85
    emitInfinite()
×
86
  }
87

88
  timer = setTimeout(fallback, 100)
168✔
89
}
90

91
// Watch state changes
92
watch(state, (newVal) => {
11✔
93
  // console.log('state changed', newVal)
94
  if (newVal === 'loading') {
22!
95
    // This is an internal change - nothing to do.
96
  } else if (newVal === 'complete') {
97
    // console.log('Complete, stop observer')
98
    stopObserver()
11✔
99
  } else {
100
    const parentEl = props.target || document.documentElement
×
101
    const prevHeight = parentEl.scrollHeight
×
102

103
    if (newVal === 'loaded' && props.top) {
×
104
      // console.log('Adjust scrollTop')
105
      parentEl.scrollTop = parentEl.scrollHeight - prevHeight
×
106
    }
107
  }
108
})
109

110
// Watch identifier changes
111
watch(
11✔
112
  () => props.identifier,
113
  () => {
114
    // We've been asked to kick the component to reset it.
115
    bump.value++
×
116
    emitInfinite()
×
117
  }
118
)
119

120
// Lifecycle hooks
121
onMounted(async () => {
11✔
122
  if (props.firstload) {
11!
123
    await emitInfinite()
11✔
124
  }
125

126
  // It would be nice if we didn't need a timer and could be purely event-driven.  But there is no guarantee
127
  // that what happens in response to our emit will result in all the components being rendered, and although
128
  // Vue3 has Suspense I can't see an easy way of waiting for all renders to finish.
129
  timer = setTimeout(fallback, 100)
11✔
130
})
131

132
onBeforeUnmount(() => {
11✔
UNCOV
133
  if (timer) {
×
UNCOV
134
    clearTimeout(timer)
×
135
  }
136
})
137

138
// Expose methods to parent components
139
defineExpose({
11✔
140
  loading,
141
  loaded,
142
  complete,
143
  error,
144
  stopObserver,
145
})
146
</script>
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc