-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ebfbdac
commit 71add32
Showing
40 changed files
with
1,574 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './tree.component'; | ||
export * from './tree.gen'; |
233 changes: 233 additions & 0 deletions
233
angular/bootstrap/src/components/tree/tree.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
import type {SlotContent} from '@agnos-ui/angular-headless'; | ||
import {BaseWidgetDirective, callWidgetFactory, ComponentTemplate, SlotDirective, UseDirective} from '@agnos-ui/angular-headless'; | ||
import { | ||
ChangeDetectionStrategy, | ||
Component, | ||
ContentChild, | ||
Directive, | ||
EventEmitter, | ||
inject, | ||
Input, | ||
Output, | ||
TemplateRef, | ||
ViewChild, | ||
} from '@angular/core'; | ||
import type {TreeContext, TreeItem, TreeSlotItemContext, TreeWidget} from './tree.gen'; | ||
import {createTree} from './tree.gen'; | ||
|
||
@Directive({selector: 'ng-template[auTreeStructure]', standalone: true}) | ||
export class TreeStructureDirective { | ||
public templateRef = inject(TemplateRef<TreeContext>); | ||
static ngTemplateContextGuard(_dir: TreeStructureDirective, context: unknown): context is TreeContext { | ||
return true; | ||
} | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
imports: [UseDirective, TreeStructureDirective, SlotDirective], | ||
template: ` | ||
<ng-template autTreeStructure #structure let-state="state" let-directives="directives" let-api="api"> | ||
<ul role="tree" class="au-tree {{ state.className() }}" [auUse]="directives.navigationDirective"> | ||
@for (node of state.normalizedNodes(); track node) { | ||
<ng-template [auSlot]="state.root()" [auSlotProps]="{state, api, directives, item: node}"></ng-template> | ||
} | ||
</ul> | ||
</ng-template> | ||
`, | ||
}) | ||
export class TreeDefaultStructureSlotComponent { | ||
@ViewChild('structure', {static: true}) readonly structure!: TemplateRef<TreeContext>; | ||
} | ||
|
||
export const treeDefaultSlotStructure = new ComponentTemplate(TreeDefaultStructureSlotComponent, 'structure'); | ||
|
||
@Directive({selector: 'ng-template[auTreeToggle]', standalone: true}) | ||
export class TreeToggleDirective { | ||
public templateRef = inject(TemplateRef<TreeSlotItemContext>); | ||
static ngTemplateContextGuard(_dir: TreeToggleDirective, context: unknown): context is TreeSlotItemContext { | ||
return true; | ||
} | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
imports: [UseDirective, TreeToggleDirective], | ||
template: ` | ||
<ng-template auTreeToggle #toggle let-state="state" let-directives="directives" let-api="api" let-item="item"> | ||
@if (item.children!.length > 0) { | ||
<button | ||
class="au-tree-expand-button" | ||
[class.au-tree-expand-button-expanded]="state.expandedMap().get(item)" | ||
[auUse]="[directives.itemToggleDirective, {item}]" | ||
></button> | ||
} @else { | ||
<span class="au-tree-expand-button-placeholder"></span> | ||
} | ||
</ng-template> | ||
`, | ||
}) | ||
export class TreeDefaultToggleSlotComponent { | ||
@ViewChild('toggle', {static: true}) readonly toggle!: TemplateRef<TreeSlotItemContext>; | ||
} | ||
|
||
export const treeDefaultSlotToggle = new ComponentTemplate(TreeDefaultToggleSlotComponent, 'toggle'); | ||
|
||
@Directive({selector: 'ng-template[auTreeItem]', standalone: true}) | ||
export class TreeItemDirective { | ||
public templateRef = inject(TemplateRef<TreeSlotItemContext>); | ||
static ngTemplateContextGuard(_dir: TreeItemDirective, context: unknown): context is TreeSlotItemContext { | ||
return true; | ||
} | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
imports: [UseDirective, SlotDirective, TreeItemDirective], | ||
template: ` | ||
<ng-template auTreeItem #treeItem let-state="state" let-directives="directives" let-item="item" let-api="api"> | ||
<span class="au-tree-item"> | ||
<ng-template [auSlot]="state.toggle()" [auSlotProps]="{state, api, directives, item}"></ng-template> | ||
{{ item.label }} | ||
</span> | ||
</ng-template> | ||
`, | ||
}) | ||
export class TreeDefaultItemSlotComponent { | ||
@ViewChild('treeItem', {static: true}) readonly treeItem!: TemplateRef<TreeSlotItemContext>; | ||
} | ||
|
||
export const treeDefaultSlotItem = new ComponentTemplate(TreeDefaultItemSlotComponent, 'treeItem'); | ||
|
||
@Directive({selector: 'ng-template[auTreeRoot]', standalone: true}) | ||
export class TreeRootDirective { | ||
public templateRef = inject(TemplateRef<TreeSlotItemContext>); | ||
static ngTemplateContextGuard(_dir: TreeRootDirective, context: unknown): context is TreeSlotItemContext { | ||
return true; | ||
} | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
imports: [UseDirective, SlotDirective, TreeRootDirective], | ||
template: ` | ||
<ng-template auTreeRoot #treeRoot let-state="state" let-directives="directives" let-item="item" let-api="api"> | ||
<li [auUse]="[directives.itemAttributesDirective, {item}]"> | ||
<ng-template [auSlot]="state.item()" [auSlotProps]="{state, api, directives, item}"></ng-template> | ||
@if (state.expandedMap().get(item)) { | ||
<ul role="group"> | ||
@for (child of item.children; track child) { | ||
<ng-template [auSlot]="state.root()" [auSlotProps]="{state, api, directives, item: child}"></ng-template> | ||
} | ||
</ul> | ||
} | ||
</li> | ||
</ng-template> | ||
`, | ||
}) | ||
export class TreeDefaultRootSlotComponent { | ||
@ViewChild('treeRoot', {static: true}) readonly treeRoot!: TemplateRef<TreeSlotItemContext>; | ||
} | ||
|
||
export const treeDefaultSlotRoot = new ComponentTemplate(TreeDefaultRootSlotComponent, 'treeRoot'); | ||
|
||
@Component({ | ||
selector: '[auTree]', | ||
standalone: true, | ||
imports: [UseDirective, SlotDirective], | ||
template: ` <ng-template [auSlot]="state.structure()" [auSlotProps]="{state, api, directives}"></ng-template> `, | ||
}) | ||
export class TreeComponent extends BaseWidgetDirective<TreeWidget> { | ||
constructor() { | ||
super( | ||
callWidgetFactory({ | ||
factory: createTree, | ||
widgetName: 'tree', | ||
defaultConfig: { | ||
structure: treeDefaultSlotStructure, | ||
root: treeDefaultSlotRoot, | ||
item: treeDefaultSlotItem, | ||
toggle: treeDefaultSlotToggle, | ||
}, | ||
events: { | ||
onExpandToggle: (item: TreeItem) => this.expandToggle.emit(item), | ||
}, | ||
slotTemplates: () => ({ | ||
structure: this.slotStructureFromContent?.templateRef, | ||
root: this.slotRootFromContent?.templateRef, | ||
item: this.slotItemFromContent?.templateRef, | ||
toggle: this.slotToggleFromContent?.templateRef, | ||
}), | ||
}), | ||
); | ||
} | ||
/** | ||
* Optional accessiblity label for the tree if there is no explicit label | ||
* | ||
* @defaultValue `''` | ||
*/ | ||
@Input('auAriaLabel') ariaLabel: string | undefined; | ||
/** | ||
* Array of the tree nodes to display | ||
* | ||
* @defaultValue `[]` | ||
*/ | ||
@Input('auNodes') nodes: TreeItem[] | undefined; | ||
/** | ||
* CSS classes to be applied on the widget main container | ||
* | ||
* @defaultValue `''` | ||
*/ | ||
@Input('auClassName') className: string | undefined; | ||
/** | ||
* Retrieves expand items of the TreeItem | ||
* | ||
* @param node - HTML element that is representing the expand item | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* (node: HTMLElement) => node.querySelectorAll('button') | ||
* ``` | ||
*/ | ||
@Input('auNavSelector') navSelector: ((node: HTMLElement) => NodeListOf<HTMLSpanElement | HTMLInputElement>) | undefined; | ||
|
||
/** | ||
* An event emitted when the user toggles the expand of the TreeItem. | ||
* | ||
* Event payload is equal to the TreeItem clicked. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* () => {} | ||
* ``` | ||
*/ | ||
@Output('auExpandToggle') expandToggle = new EventEmitter<TreeItem>(); | ||
|
||
/** | ||
* Slot to change the default tree item | ||
*/ | ||
@Input('auItem') item: SlotContent<TreeSlotItemContext>; | ||
@ContentChild(TreeItemDirective, {static: false}) slotItemFromContent: TreeItemDirective | undefined; | ||
|
||
/** | ||
* Slot to change the default display of the tree | ||
*/ | ||
@Input('auStructure') structure: SlotContent<TreeContext>; | ||
@ContentChild(TreeStructureDirective, {static: false}) slotStructureFromContent: TreeStructureDirective | undefined; | ||
|
||
/** | ||
* Slot to change the default tree item toggle | ||
*/ | ||
@Input('auToggle') toggle: SlotContent<TreeSlotItemContext>; | ||
@ContentChild(TreeToggleDirective, {static: false}) slotToggleFromContent: TreeToggleDirective | undefined; | ||
|
||
/** | ||
* Slot to change the default tree root | ||
*/ | ||
@Input('auRoot') root: SlotContent<TreeSlotItemContext>; | ||
@ContentChild(TreeRootDirective, {static: false}) slotRootFromContent: TreeRootDirective | undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
angular/demo/bootstrap/src/app/samples/tree/basic.route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import type {TreeItem} from '@agnos-ui/angular-bootstrap'; | ||
import {AgnosUIAngularModule} from '@agnos-ui/angular-bootstrap'; | ||
import {Component} from '@angular/core'; | ||
|
||
@Component({ | ||
standalone: true, | ||
template: ` <au-component auTree [auNodes]="nodes"></au-component> `, | ||
imports: [AgnosUIAngularModule], | ||
}) | ||
export default class BasicTreeComponent { | ||
nodes: TreeItem[] = [ | ||
{ | ||
label: 'Node 1', | ||
isExpanded: true, | ||
ariaLabel: 'Node 1', | ||
children: [ | ||
{ | ||
label: 'Node 1.1', | ||
isExpanded: false, | ||
ariaLabel: 'Node 1.1', | ||
children: [ | ||
{ | ||
label: 'Node 1.1.1', | ||
ariaLabel: 'Node 1.1.1', | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'Node 1.2', | ||
ariaLabel: 'Node 1.2', | ||
children: [ | ||
{ | ||
label: 'Node 1.2.1', | ||
ariaLabel: 'Node 1.2.1', | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './tree'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type {TreeProps as CoreProps, TreeState as CoreState, TreeApi, TreeDirectives, TreeItem} from '@agnos-ui/core/components/tree'; | ||
import {createTree as createCoreTree, getTreeDefaultConfig as getCoreDefaultConfig} from '@agnos-ui/core/components/tree'; | ||
import {extendWidgetProps} from '@agnos-ui/core/services/extendWidget'; | ||
import type {SlotContent, Widget, WidgetFactory, WidgetSlotContext} from '@agnos-ui/core/types'; | ||
|
||
export * from '@agnos-ui/core/components/tree'; | ||
|
||
export type TreeContext = WidgetSlotContext<TreeWidget>; | ||
export type TreeSlotItemContext = TreeContext & {item: TreeItem}; | ||
|
||
interface TreeExtraProps { | ||
/** | ||
* Slot to change the default display of the tree | ||
*/ | ||
structure: SlotContent<TreeContext>; | ||
/** | ||
* Slot to change the default tree root | ||
*/ | ||
root: SlotContent<TreeSlotItemContext>; | ||
/** | ||
* Slot to change the default tree item | ||
*/ | ||
item: SlotContent<TreeSlotItemContext>; | ||
/** | ||
* Slot to change the default tree item toggle | ||
*/ | ||
toggle: SlotContent<TreeSlotItemContext>; | ||
} | ||
|
||
export interface TreeState extends CoreState, TreeExtraProps {} | ||
export interface TreeProps extends CoreProps, TreeExtraProps {} | ||
|
||
export type TreeWidget = Widget<TreeProps, TreeState, TreeApi, TreeDirectives>; | ||
|
||
const defaultConfigExtraProps: TreeExtraProps = { | ||
structure: undefined, | ||
root: undefined, | ||
item: undefined, | ||
toggle: undefined, | ||
}; | ||
|
||
/** | ||
* Retrieve a shallow copy of the default Tree config | ||
* @returns the default Tree config | ||
*/ | ||
export function getTreeDefaultConfig(): TreeProps { | ||
return {...getCoreDefaultConfig(), ...defaultConfigExtraProps}; | ||
} | ||
|
||
/** | ||
* Create a Tree with given config props | ||
* @param config - an optional tree config | ||
* @returns a TreeWidget | ||
*/ | ||
export const createTree: WidgetFactory<TreeWidget> = extendWidgetProps(createCoreTree, defaultConfigExtraProps, { | ||
structure: undefined, | ||
root: undefined, | ||
item: undefined, | ||
toggle: undefined, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.