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

Add a horizontal scrollbar to ngx-graph with a large amount of data #554

Open
Harvi29 opened this issue Oct 8, 2024 · 0 comments
Open

Comments

@Harvi29
Copy link

Harvi29 commented Oct 8, 2024

Hello,
I'm working with a huge dataset. My client needs to see the whole dataset that (obviously) doesn't fit a single page width. If I set a fixed width to the parent container then the scroll does not work when zooming is enabled in the ngx-graph.

Is there a way to add a horizontal scroll bar to the ngx-graph when dealing with large data?

HTML code:

<div class="space-top-16 vc-form-container" [vcProgressSpinner]="loading">
    @if (loaded) {
    <ngx-graph
        class="mss-graph-container"
        [layout]="dagreLayout"
        [curve]="curve"
        [links]="links"
        [nodes]="nodes"
        [autoZoom]="true"
        [autoCenter]="true">
        <ng-template #nodeTemplate let-node>
            <svg
                height="80"
                [attr.width]="node.data.children && node.data.children.length > 0 ? '112' : '72'"
                [attr.max-width]="112">
                <foreignObject
                    height="80"
                    [attr.width]="node.data.children && node.data.children.length > 0 ? '112' : '72'"
                    [attr.max-width]="112">
                    <div class="vc-mss-device-node-wrapper">
                        <div class="vc-mss-device-node" (click)="$event.preventDefault(); showWidget(node)">
                            <span
                                class="vc-mss-device-icon material-icons"
                                [style.border-color]="getBorderColor()"
                                [style.color]="node.data.icon.color"
                                >{{ node.data.icon.label }}</span
                            >
                            <div
                                xmlns="http://www.w3.org/1999/xhtml"
                                class="vc-mss-device-node-text body-s-1"
                                [matTooltip]="node.label"
                                [matTooltipPosition]="'above'">
                                {{ node.label }} - {{ node.data.type }}
                            </div>
                        </div>

                        @if (node.data.children && node.data.children.length > 0) {
                        <div class="vc-mss-device-node">
                            <button
                                mat-icon-button
                                class="vc-mss-device-expand-icon"
                                (click)="openOrCloseNode(node); selectedNode = null">
                                <mat-icon>{{ node.data.expanded ? 'expand_more' : 'expand_less' }}</mat-icon>
                            </button>

                            <span>({{ node.data.children.length }})</span>
                        </div>
                        }
                    </div>
                </foreignObject>
            </svg>
        </ng-template>
        <ng-template #linkTemplate let-link>
            <svg:g class="edge" xmlns:svg="http://www.w3.org/2000/svg">
                <path class="edge line mss-device-overview-line" [attr.d]="link.line"></path>
            </svg:g>
        </ng-template>
    </ngx-graph>
    } @if (selectedNode) {
    <div class="vc-mss-device-node-widget vc-form-container">
        <div class="vc-mss-device-title">
            <h3
                class="headline-s vc-mss-device-overview-high-emphasis-color"
                i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.NODE_OVERVIEW">
                Node overview
            </h3>
            <vc-button
                class="vc-mss-device-close-button"
                mode="icon"
                iconName="close"
                iconColor="var(--text-low-emphasis)"
                (trigger)="selectedNode = null"></vc-button>
        </div>

        <div class="vc-mss-device-item-container">
            <div class="vc-mss-device-node-item vc-mss-device-overview-medium-emphasis-color">
                <span class="body-m-1" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.FULL_NAME">Full name: </span>
                <span class="body-m-2">{{ selectedNode.label }}</span>
            </div>
            <div class="vc-mss-device-node-item">
                <span
                    class="body-m-1 vc-mss-device-overview-medium-emphasis-color"
                    i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.DISCOVERED_BY"
                    >Discovered by:
                </span>
                <span class="body-m-2 vc-mss-device-overview-high-emphasis-color">{{ selectedNode.data.type }}</span>
            </div>
        </div>

        <div class="vc-mss-device-node-action-wrapper vc-form-container">
            <div class="vc-mss-device-node-action-description">
                <h4 class="body-m-2" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.PING_THE_DEVICE">Ping the device</h4>
                <span class="body-s" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.PING_DESCRIPTION"
                    >Packet Loss and Response Time Analyzer</span
                >
            </div>
            <vc-button
                label="Ping"
                i18n-label="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.PING"
                mode="basic"
                (trigger)="pingDevice()"></vc-button>
        </div>

        <div class="vc-mss-device-node-action-wrapper vc-form-container">
            <div class="vc-mss-device-node-action-description">
                <h4 class="body-m-2" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.EVENTS">Events</h4>
                <span class="body-s" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.EVENTS_DESCRIPTION"
                    >View related events</span
                >
            </div>
            <vc-button
                label="View"
                i18n-label="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.VIEW"
                mode="basic"
                (trigger)="navigateToEventsSearch()"></vc-button>
        </div>

        <div class="vc-mss-device-node-action-wrapper vc-form-container">
            <div class="vc-mss-device-node-action-description">
                <h4 class="body-m-2" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.FINDINGS">Findings</h4>
                <span class="body-s" i18n="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.FINDINGS_DESCRIPTION"
                    >View related Findings</span
                >
            </div>
            <vc-button
                label="View"
                i18n-label="@@MSS.SITE_DETAIL.TABS.DEVICE_OVERVIEW.VIEW"
                mode="basic"
                (trigger)="navigateToFindingsSearch()"></vc-button>
        </div>
    </div>
    }

    <div class="vc-form-container vc-mss-device-legend">
        @for (type of assetTypes; track type) {
        <div class="vc-mss-device-legend-item body-s-1">
            <vc-icon [name]="getNodeIcon(type).label" [color]="getNodeIcon(type).color"></vc-icon>
            {{ getAssetTypeLabel(type) }}
        </div>
        }
    </div>
</div>

SCSS:

:host {
    display: block;
    height: 100%;
    width: 100%;

    .vc-mss-device-node-wrapper {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 8px;

        .vc-mss-device-node {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 8px;

            .vc-mss-device-icon {
                display: flex;
                justify-content: center;
                align-items: center;
                height: 36px;
                width: 36px;
                border: 1px solid;
                border-radius: 50%;
            }

            .vc-mss-device-node-text {
                width: 56px;
                text-align: center;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
        }

        .vc-mss-device-node {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 4px;
            cursor: pointer;

            button.vc-mss-device-expand-icon {
                height: 24px !important;
                width: 24px !important;
                box-shadow: none !important;

                .mat-icon {
                    height: 24px;
                    width: 24px;
                    line-height: 24px;
                    font-size: 24px;
                }

                &.mat-mdc-icon-button:not([disabled]) {
                    &:hover,
                    &:focus-visible {
                        border-radius: 4px !important;
                        background-color: var(--primary-50);
                    }

                    &:focus-visible {
                        outline: 1px solid var(--primary-600);
                        outline-offset: 1px;
                    }
                }
            }
        }
    }

    .mss-graph-container {
        display: block;
        height: calc(100vh - 345px);
        width: 100%;
    }

    .mss-device-overview-item {
        cursor: pointer;
    }

    .mss-device-overview-line {
        stroke: var(--outline);
        stroke-width: 1;
    }

    .vc-mss-device-node-widget {
        display: flex;
        flex-direction: column;
        gap: 16px;
        position: absolute;
        top: 32px;
        right: 16px;
        width: 430px;
        height: calc(100% - 50px);
        overflow: auto;

        .vc-mss-device-title {
            display: inline-flex;
            justify-content: space-between;
            align-items: flex-start;

            h3 {
                margin: 0;
            }

            .vc-mss-device-close-button {
                position: relative;
                top: -8px;
                right: -8px;
            }
        }

        .vc-mss-device-item-container {
            display: flex;
            flex-direction: column;
            gap: 8px;

            .vc-mss-device-node-item {
                display: inline-flex;
                gap: 6px;
            }
        }

        .vc-mss-device-node-action-wrapper {
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: center;

            .vc-mss-device-node-action-description {
                display: flex;
                flex-direction: column;
                gap: 8px;

                h4 {
                    margin: 0;
                }
            }
        }
    }

    .vc-mss-device-legend {
        display: flex;
        flex-direction: column;
        gap: 8px;
        position: absolute;
        top: 32px;
        left: 16px;
        width: 150px;
        background-color: var(--ghost-white);

        .vc-mss-device-legend-item {
            display: inline-flex;
            align-items: center;
            gap: 8px;
        }
    }

    .vc-mss-device-overview-high-emphasis-color {
        color: var(--text-high-emphasis);
    }

    .vc-mss-device-overview-medium-emphasis-color {
        color: var(--text-medium-emphasis);
    }
}

Thanks,

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

No branches or pull requests

1 participant