Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scoping class is not being applied in Svelte v5 #11565

Open
webJose opened this issue May 12, 2024 · 1 comment
Open

Scoping class is not being applied in Svelte v5 #11565

webJose opened this issue May 12, 2024 · 1 comment

Comments

@webJose
Copy link

webJose commented May 12, 2024

Describe the bug

Ok, this is going to be difficult for me because I'm not front-end dev. Maybe the bug is not a bug but an error on my side. The summary: In SveteKit + svelte@5.0.0-next.127, I use a CSS class named grid-cell that doesn't have "explicit" setting in the script's style tag, but it is used in a very specific selector: div.grid.row.row-highlight:hover div.grid-cell-bg.sticky-data > div.grid-cell. There it is. Apparently, this usage is not enough for Svelte to add the scoping class to those DIV's. The structure is: div.grid-container > div.grid > div.grid-body > div.grid-row-bg > div.grid-row > div.grid-cell-bg > div.grid-cell. That's the markup the component specifies. That last DIV, when inspected in the browser, doesn't have the scoping class.

Just in case it isn't clear: The compiled CSS adds the scoping class to the big selector above. If I inspect the div.grid-cell element and manually add the scoping class, then the style is applied.

Reproduction

I currently cannot sit down to make a small repro, so I figured I should post the entire component. It is a bit over 200 lines (not much, right?). It is a table component made out of DIV's.

<script context="module" lang="ts">
    export type ColAlignment = 'start' | 'center' | 'end';

    export type WjGridRow<TRow extends Record<string, any> = Record<string, any>> = TRow & {
        id: string | number;
        selected?: boolean;
        expanded?: boolean;
    }

    export type WjGridColumn<TCol extends Record<string, any> = Record<string, any>, TRow extends Record<string, any> = Record<string, any>> = TCol & {
        key: string;
        text: string;
        width?: number;
        pinnable?: boolean;
        pinned?: boolean;
        alignment?: ColAlignment;
        get?: (row: TRow) => any;
    }
</script>

<script lang="ts" generics="TCol extends Record<string, any> = Record<string, any>, TRow extends Record<string, any> = Record<string, any>">
    import { combineClasses } from "./utils.js";

    let {
        columns,
        data,
        get = (r, k) => r[k],
        defaultWidth = 10,
        rowHighlight = true,
        striped = true,
        class: cssClass, ...restProps
    }: {
        columns: WjGridColumn<TCol, TRow>[];
        data: WjGridRow<TRow>[];
        get?: (row: TRow, key: string) => any;
        defaultWidth?: number;
        rowHighlight?: boolean;
        striped?: boolean;
        class?: string;
    } = $props();

    type ColumnInfo = {
        column: WjGridColumn<TCol, TRow>;
        left?: number;
    }

    const segregatedColumns = $derived(columns.reduce<{
        accPinnedWidth: number;
        accUnpinnedWidth: number;
        pinned: ColumnInfo[];
        unpinned: ColumnInfo[];
    }>((p, c) => {
        if (c.pinned) {
            p.pinned.push({
                column: c,
                left: p.accPinnedWidth
            });
            p.accPinnedWidth += columnWidth(c);
        }
        else {
            p.unpinned.push({
                column: c,
                left: p.accUnpinnedWidth
            });
            p.accUnpinnedWidth += columnWidth(c);
        }
        return p;
    }, { accPinnedWidth: 0, accUnpinnedWidth: 0, pinned: [], unpinned: [] }));

    $effect(() => {
        console.log('Column data: %o', segregatedColumns);
    });

    function columnWidth(col: WjGridColumn<TCol, TRow>) {
        return col.width ?? defaultWidth;
    }
</script>

{#snippet colHeaders(cols: ColumnInfo[])}
    {#each cols as ci (ci.column.key)}
    <div
        class={combineClasses('col-header', { 'sticky-header': !!ci.column.pinned })}
        role="columnheader"
        style:width={`${columnWidth(ci.column)}em`}
        style:left={ci.left !== undefined ? `${ci.left}em` : undefined}
    >
        {ci.column.text}
    </div>
    {/each}
{/snippet}

{#snippet colData(row: WjGridRow<TRow>, cols: ColumnInfo[])}
    {#each cols as ci (ci.column.key)}
    {@const getFn = ci.column.get ?? (r => get(r, ci.column.key))}
    <div
        class={combineClasses('grid-cell-bg', { 'sticky-data': !!ci.column.pinned })}
        role="gridcell"
        style:width={`${ci.column.width ?? defaultWidth}em`}
        style:left={ci.left !== undefined ? `${ci.left}em` : undefined}
    >
        <div class="grid-cell"> <!-- THIS GUY!  WHEN RENDERED, THE SCOPING CLASS IS NOT THERE.-->
            {getFn(row)}
        </div>
    </div>
    {/each}
{/snippet}

<div class={combineClasses('grid-container', cssClass)}>
    <div class="grid" role="table" {...restProps}>
        <div class="header-group" role="rowheader">
            {#if segregatedColumns.pinned.length}
                {@render colHeaders(segregatedColumns.pinned)}
            {/if}
            {@render colHeaders(segregatedColumns.unpinned)}
            <div class="col-header extra-header">&nbsp;</div>
        </div>
        <div class={combineClasses('grid-body', { striped })}>
            {#each data as row (row.id)}
            <div class="grid-row-bg">
                <div class={combineClasses('grid-row', { 'row-highlight': rowHighlight })}>
                    {#if segregatedColumns.pinned.length}
                        {@render colData(row, segregatedColumns.pinned)}
                    {/if}
                    {@render colData(row, segregatedColumns.unpinned)}
                    <div class="grid-cell"></div>
                </div>
            </div>
            {/each}
        </div>
    </div>
</div>

<style lang="scss">
    $tableGap: 0rem;
    div.grid-container {
        height: 100%;
        width: 100%;
        overflow: auto;
        position: relative;
        --wjg-bg-color-rgb: 255, 255, 255;
        --wjg-bg-color-opacity: 1;
        --wjg-color: inherit;
        --wjg-striped-even-bg-color-rgb: 240, 240, 240;
        --wjg-striped-odd-bg-color-rgb: 255, 255, 255;
        --wjg-rowhighlight-bg-color-rgb: 0, 0, 0;
        --wjg-rowhightlight-bg-opacity: 0.12;
    }

    div.grid {
        display: table;
        flex-direction: column;
        flex-wrap: nowrap;
        color: var(--wjg-color);
        min-width: 100%;
        background-color: rgba(var(--wjg-bg-color-rgb), var(--wjg-bg-color-opacity));
    }

    div.header-group {
        display: flex;
        flex-direction: row;
        gap: $tableGap;
    }

    div.col-header {
        position: sticky;
        z-index: 1;
        top: 0;
        font-weight: bold;
        box-shadow: inset 0 -0.4em 1em rgba(0, 0, 0, 0.15);
        &.sticky-header {
            z-index: 2;
        }
    }

    div.extra-header {
        flex: 1 1 0;
    }

    div.grid-body {
        display: flex;
        flex-direction: column;

        &.striped {
            > div.grid-row-bg {
                background-color: rgba(var(--wjg-striped-even-bg-color-rgb), var(--wjg-bg-color-opacity));
                & div.grid-cell-bg.sticky-data {
                    background-color: rgba(var(--wjg-striped-even-bg-color-rgb), var(--wjg-bg-color-opacity));
                }
            }
            > div.grid-row-bg:nth-of-type(2n+1) {
                background-color: rgba(var(--wjg-striped-odd-bg-color-rgb), var(--wjg-bg-color-opacity));
                & div.grid-cell-bg.sticky-data {
                    background-color: rgba(var(--wjg-striped-odd-bg-color-rgb), var(--wjg-bg-color-opacity));
                }
            }
        }
    }

    div.grid-row {
        display: flex;
        flex-direction: row;
        gap: $tableGap;
        padding: 0.2em 0em;

        &.row-highlight {
            &:hover, &.hover {
                background-color: rgba(0, 0, 0, var(--wjg-rowhightlight-bg-opacity));
                div.grid-cell-bg.sticky-data > div.grid-cell {
                    background-color: rgba(0, 0, 0, var(--wjg-rowhightlight-bg-opacity));
                }
            }
        }
    }

    div.grid-cell-bg {
        overflow: hidden;
        text-wrap: nowrap;
        text-overflow: ellipsis;
        &.sticky-data {
            position: sticky;
            z-index: 1;
        }
    }
</style>

Logs

No response

System Info

System:
    OS: Windows 11 10.0.22631
    CPU: (16) x64 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
    Memory: 4.39 GB / 15.77 GB
  Binaries:
    Node: 20.6.1 - C:\Program Files\nodejs\node.EXE
    npm: 9.8.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (), Chromium (123.0.2420.97), ChromiumDev ()
    Internet Explorer: 11.0.22621.1
  npmPackages:
    svelte: ^5.0.0-next.1 => 5.0.0-next.127

Severity

blocking an upgrade

@webJose
Copy link
Author

webJose commented May 12, 2024

By the way, the workaround is easy, I suppose:

    div.grid-row {
        display: flex;
        flex-direction: row;
        gap: $tableGap;
        padding: 0.2em 0em;

        &.row-highlight {
            &:hover, &.hover {
                background-color: rgba(0, 0, 0, var(--wjg-rowhightlight-bg-opacity));
                div.grid-cell-bg.sticky-data > :global(div.grid-cell) { // WORKAROUND:  USING :global()
                    background-color: rgba(0, 0, 0, var(--wjg-rowhightlight-bg-opacity));
                }
            }
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant