Cool parent revealing in detail dropdown

This commit is contained in:
shamoon
2025-09-10 10:14:58 -07:00
parent 89c09283e5
commit 0f6b266106
4 changed files with 72 additions and 2 deletions

View File

@@ -26,9 +26,20 @@
</button>
</ng-template>
<ng-template ng-option-tmp let-item="item" let-index="index" let-search="searchTerm">
<div class="tag-wrap">
<div class="tag-option-row d-flex align-items-center">
@if (item.id && tags) {
<pngx-tag class="me-2" [tag]="getTag(item.id)" showParents="true"></pngx-tag>
@if (getTag(item.id)?.parent) {
<i-bs name="list-nested" class="me-1"></i-bs>
<span class="hierarchy-reveal d-flex align-items-center">
<span class="parents d-flex align-items-center">
@for (p of getParentChain(item.id); track p.id) {
<span class="badge me-1" [style.background]="p.color" [style.color]="p.text_color">{{p.name}}</span>
<i-bs name="chevron-right" width=".8em" height=".8em" class="me-1"></i-bs>
}
</span>
</span>
}
<pngx-tag class="current-tag d-flex" [tag]="getTag(item.id)"></pngx-tag>
}
</div>
</ng-template>

View File

@@ -20,3 +20,40 @@
}
}
}
// Dropdown hierarchy reveal for ng-select options
::ng-deep .ng-dropdown-panel .ng-option {
.tag-option-row {
font-size: 1rem;
}
.hierarchy-reveal {
overflow: hidden;
max-width: 0;
transition: max-width 200ms ease;
}
// .hierarchy-indicator {
// display: inline-block;
// width: .35rem;
// height: 1rem;
// border-radius: .25rem;
// background: var(--pngx-border-color, rgba(0,0,0,.2));
// }
.parents .badge {
white-space: nowrap;
}
}
// Expand reveal area when hovering or when option is keyboard-marked
::ng-deep .ng-dropdown-panel .ng-option:hover .hierarchy-reveal,
::ng-deep .ng-dropdown-panel .ng-option.ng-option-marked .hierarchy-reveal {
max-width: 1000px; // effectively "auto" for transition
}
// Fade indicator when expanded
::ng-deep .ng-dropdown-panel .ng-option:hover .hierarchy-indicator,
::ng-deep .ng-dropdown-panel .ng-option.ng-option-marked .hierarchy-indicator {
background: transparent;
}

View File

@@ -211,4 +211,22 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
this.tags.filter((t) => this.value.includes(t.id))
)
}
/**
* Returns ancestors from root → immediate parent for a tag id
*/
getParentChain(id: number): Tag[] {
const chain: Tag[] = []
let current = this.getTag(id)
const guard = new Set<number>()
while (current?.parent) {
if (guard.has(current.parent)) break
guard.add(current.parent)
const parent = this.getTag(current.parent)
if (!parent) break
chain.unshift(parent)
current = parent
}
return chain
}
}

View File

@@ -55,6 +55,7 @@ import {
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
chevronRight,
clipboard,
clipboardCheck,
clipboardCheckFill,
@@ -94,6 +95,7 @@ import {
infoCircle,
journals,
link,
listNested,
listTask,
listUl,
microsoft,
@@ -264,6 +266,7 @@ const icons = {
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
chevronRight,
clipboard,
clipboardCheck,
clipboardCheckFill,
@@ -303,6 +306,7 @@ const icons = {
infoCircle,
journals,
link,
listNested,
listTask,
listUl,
microsoft,