PageSourceSearch

https://drivetimedevotions.com/scripts/Resources/SeriesCarousel.js?v=067bac55dc3729ccedc88375e13966800226cff8

js drivetimedevotions.com collected 2026-09-26 03:07:08 UTC 4,743 bytes, 123 lines download raw bytes

1import LoadingSpinner from '../VueComponents/LoadingSpinner.js';
2import ResourceCarousel from './ResourceCarousel.js';
3import weekendMessagesEndpoints from '../utils/weekendMessagesEndpoints.js';
4
5export default {
6    name: 'SeriesCarousel',
7    components: {
8        LoadingSpinner,
9        ResourceCarousel,
10    },
11    props: {
12        healthyChurchDomain: {
13            type: String,
14            required: true,
15        },
16        useLegacy: {
17            type: Boolean,
18            required: false,
19            default: false,
20        },
21        // Controlled by the parent (single source of truth). null = nothing selected.
22        // Holds the Sardius seriesAssetId string (matches series.seriesAssetId).
23        selectedSeriesId: {
24            type: String,
25            required: false,
26            default: null,
27        },
28    },
29    emits: ['series-selected'],
30    data() {
31        return {
32            isLoading: true,
33            seriesList: [],
34        };
35    },
36    watch: {
37        // Fires whatever moved the selection — a card click, or the series dropdown on
38        // either filter surface (desktop FilterCard / FilterPanelMobile). Selections made
39        // from outside the strip are the ones that matter: the matching card is usually
40        // scrolled out of view.
41        selectedSeriesId() {
42            this.$nextTick(this.scrollSelectedIntoView);
43        },
44    },
45    mounted() {
46        this.fetchSeries();
47    },
48    methods: {
49        fetchSeries() {
50            this.isLoading = true;
51            const url = weekendMessagesEndpoints.getSeriesList(this.healthyChurchDomain, this.useLegacy);
52            window.sbDebug && console.log('[SeriesCarousel] getSeriesList:', url);
53            axios
54                .get(url)
55                .then((response) => {
56                    this.seriesList = response.data?.results ?? [];
57                })
58                .catch((error) => {
59                    console.error(error);
60                })
61                .finally(() => {
62                    this.isLoading = false;
63                    // A selection can predate the list (deep link / filter set while loading).
64                    this.$nextTick(this.scrollSelectedIntoView);
65                });
66        },
67        scrollSelectedIntoView() {
68            if (this.selectedSeriesId === null) {
69                return;
70            }
71            const index = this.seriesList.findIndex((series) => series.seriesAssetId === this.selectedSeriesId);
72            if (index === -1) {
73                return;
74            }
75            this.$refs.carousel?.scrollItemIntoView(index);
76        },
77        selectSeries(series) {
78            // Toggle: re-clicking the selected series clears the selection. The parent
79            // owns the state, so we only emit intent — the prop flows back down.
80            const isDeselecting = this.selectedSeriesId === series.seriesAssetId;
81            this.$emit('series-selected', isDeselecting ? null : series);
82        },
83        isDimmed(series) {
84            // Dim every card except the selected one, only while something is selected.
85            return this.selectedSeriesId !== null && series.seriesAssetId !== this.selectedSeriesId;
86        },
87        isSelected(series) {
88            return this.selectedSeriesId !== null && series.seriesAssetId === this.selectedSeriesId;
89        },
90    },
91    template: `
92        <section class="series-carousel">
93            <h3 class="series-carousel__heading">Series</h3>
94            <LoadingSpinner v-show="isLoading" />
95            <ResourceCarousel
96                ref="carousel"
97                v-show="!isLoading"
98                :items="seriesList"
99                itemKey="id"
100                prevLabel="Previous series"
101                nextLabel="Next series"
102            >
103                <template #item="{ item }">
104                    <button
105                        type="button"
106                        class="series-card"
107                        :class="{ 'is-dimmed': isDimmed(item), 'is-selected': isSelected(item) }"
108                        :aria-label="'Filter messages by ' + item.name"
109                        :aria-pressed="isSelected(item)"
110                        @click="selectSeries(item)"
111                    >
112                        <img
113                            class="series-card__image"
114                            :src="item.heroImage"
115                            :alt="item.name"
116                            loading="lazy"
117                        />
118                    </button>
119                </template>
120            </ResourceCarousel>
121        </section>
122    `,
123};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.