summaryrefslogtreecommitdiff
path: root/src/components/ShelfItemLayout.svelte
blob: ef1d07cbd7edc7ccf96278f6ed07056a9bfe74ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<!--
@component
Renders a set of `Shelf` items in either a horizontal shelf
or a grid, depending on the `shelf` configuration

Note: when configuring the `gridType` property, a single value will be used
for both the shelf-based or grid-based item layouts. If two different grid types
are needed instead, `gridTypeForShelf` and `gridTypeForGrid` are needed instead;
these properties cannot be used alongside the general-purpose `gridType`.
-->
<script lang="ts" generics="Item">
    import type { Shelf } from '@jet-app/app-store/api/models';

    import type { GridType } from '@amp/web-app-components/src/components/Shelf/types';

    import type { XOR } from '~/utils/types';
    import HorizontalShelf from '~/components/jet/shelf/HorizontalShelf.svelte';
    import Grid from '~/components/Grid.svelte';

    /**
     * The sub-set of {@linkcode Shelf} that is necesary to render this component
     */
    interface RequiredShelf
        extends Pick<Shelf, 'rowsPerColumn' | 'isHorizontal'> {
        items: Item[];
    }

    interface $$Slots {
        default: {
            item: Item;
        };
    }

    /**
     * Represents the `gridType` properties of this component
     *
     * Either a `gridType` that will be used for both the shelf or grid
     * layouts can be provided, OR specific properties for the grid type
     * for the shelf and grid respectively; this `XOR` here prevents
     * these approachs from being mixed-and-matched.
     */
    type GeneralOrIndividualGridType = XOR<
        {
            gridType: GridType;
        },
        {
            gridTypeForGrid: GridType;
            gridTypeForShelf: GridType;
        }
    >;

    type $$Props = GeneralOrIndividualGridType & {
        shelf: RequiredShelf;
        rowsPerColumnOverride?: number | null;
    };

    /**
     * The shelf to render items for
     */
    export let shelf: RequiredShelf;

    /**
     * An optional override of the shelfs `rowsPerColumn` property
     */
    export let rowsPerColumnOverride: number | null = null;

    /**
     * Determine the grid type configuration for the shelf or grid layouts
     * based on the mutually-exclusive properties of {@linkcode GeneralOrIndividualGridType}
     */
    function extractGridTypes(props: $$Props) {
        if (typeof props.gridType === 'string') {
            return {
                gridTypeForShelf: props.gridType,
                gridTypeForGrid: props.gridType,
            };
        } else {
            return props;
        }
    }

    $: ({ gridTypeForShelf, gridTypeForGrid } = extractGridTypes(
        $$props as $$Props,
    ));

    $: isHorizontal = shelf.isHorizontal;
    $: gridRows = rowsPerColumnOverride ?? shelf.rowsPerColumn ?? undefined;
</script>

{#if isHorizontal}
    <HorizontalShelf
        items={shelf.items}
        {gridRows}
        gridType={gridTypeForShelf}
        let:item
    >
        <slot {item} />
    </HorizontalShelf>
{:else}
    <Grid items={shelf.items} gridType={gridTypeForGrid} let:item>
        <slot {item} />
    </Grid>
{/if}