RSS Git Download  Clone
Raw Blame History 5kB 98 lines
HTML rendered
<cdk-virtual-scroll-viewport
    *ngIf="dataSource.length > 0 && isEnabled"
    [itemSize]="28"
    class="p3xr-database-tree-viewport">

    <div *cdkVirtualFor="let node of dataSource; trackBy: trackByKey"
         class="p3xr-database-tree-row"
         [style.padding-left.px]="node.level * 20 + 4">

        <!-- Folder icon — at row level with its own click handler -->
        @if (node.expandable) {
            <span class="p3xr-tree-branch-head"
                  [class.tree-expanded]="isExpanded(node)"
                  [class.tree-collapsed]="!isExpanded(node)"
                  (click)="toggleExpand(node); $event.stopPropagation()">
            </span>
        } @else if (node.keysInfo) {
            <!-- Type icon — clickable to select node (matches React/Vue) -->
            <span (click)="selectNode(node)" style="cursor:pointer">
                @switch (node.keysInfo.type) {
                    @case ('hash') { <i class="fas fa-hashtag p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('list') { <i class="fas fa-list-ol p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('set') { <i class="fas fa-list p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('string') { <i class="fas fa-ellipsis-h p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('zset') { <i class="fas fa-chart-line p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('stream') { <i class="fas fa-stream p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('json') { <i class="fas fa-code p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('timeseries') { <i class="fas fa-chart-area p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('bloom') { <i class="fas fa-filter p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('cuckoo') { <i class="fas fa-filter p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('topk') { <i class="fas fa-trophy p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('cms') { <i class="fas fa-chart-simple p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('tdigest') { <i class="fas fa-chart-bar p3xr-tree-type-icon" aria-hidden="true"></i> }
                    @case ('vectorset') { <i class="fas fa-brain p3xr-tree-type-icon" aria-hidden="true"></i> }
                }
            </span>
        }

        <!-- Node content -->
        <span [attr.data-p3xr-tree-key]="node.type === 'folder' ? '' : node.key"
              style="display:inline-flex;align-items:center">
            <label class="p3xr-database-tree-node"
                   (click)="node.expandable ? toggleExpand(node) : selectNode(node)"
                   [matTooltip]="extractNodeTooltip(node)"
                   matTooltipPosition="right"
                   matTooltipClass="p3xr-tree-node-tooltip">

                <span class="p3xr-database-tree-node-label">{{ node.label }}</span>

                <!-- Folder child count -->
                @if (node.type === 'folder') {
                    <span class="p3xr-database-tree-node-count">{{ divider }}* ({{ node.childCount }})</span>
                }

                <!-- Element length (non-string) -->
                @if (node.type !== 'folder' && node.keysInfo?.type !== 'string' && node.keysInfo?.type !== 'json' && node.keysInfo) {
                    <span class="p3xr-database-tree-node-count">({{ node.keysInfo.length }})</span>
                }


            </label>

            <!-- TTL indicator (outside label to avoid tooltip conflict) -->
            @if (node.type !== 'folder' && getRemainingTtl(node) > 0) {
                <span class="p3xr-tree-ttl-badge" [class]="getTtlClass(node)"
                      [matTooltip]="'TTL: ' + formatTtl(node)" matTooltipPosition="right"
                      matTooltipClass="p3xr-tree-node-tooltip">
                    <span class="material-icons p3xr-tree-ttl-icon">schedule</span>
                </span>
            }
        </span>

        <!-- Action buttons (shown on row hover via CSS, hidden in readonly) -->
        @if (!isReadonly) {
            <span class="p3xr-database-tree-actions">
                <!-- Delete tree (folder) / Delete key (element) -->
                @if (node.type === 'folder') {
                    <span [matTooltip]="deleteTreeTooltip(node)" matTooltipPosition="after" matTooltipClass="p3xr-tree-node-tooltip">
                        <span class="material-icons p3xr-database-treecontrol-delete-icon"
                              (click)="deleteTree($event, node)">delete</span>
                    </span>
                } @else {
                    <span [matTooltip]="strings()?.intention?.delete" matTooltipPosition="after" matTooltipClass="p3xr-tree-node-tooltip">
                        <span class="material-icons p3xr-database-treecontrol-delete-icon"
                              (click)="deleteKey($event, node.key)">delete</span>
                    </span>
                }

                <!-- Add key -->
                <span [matTooltip]="strings()?.intention?.addKey" matTooltipPosition="after" matTooltipClass="p3xr-tree-node-tooltip">
                    <span class="material-icons p3xr-database-treecontrol-folder-icon"
                          (click)="addKey($event, node)">add</span>
                </span>
            </span>
        }
    </div>
</cdk-virtual-scroll-viewport>