-
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.
feat(progressbar): first draft of progressbar
- Loading branch information
1 parent
c40a8b7
commit ddb0241
Showing
50 changed files
with
1,437 additions
and
68 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import {ProgressbarComponent, ProgressbarContentDirective} from '@agnos-ui/angular'; | ||
import {NgIf} from '@angular/common'; | ||
import type {OnDestroy} from '@angular/core'; | ||
import {Component} from '@angular/core'; | ||
import type {Subscription} from 'rxjs'; | ||
import {interval, map, startWith, takeWhile} from 'rxjs'; | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [ProgressbarComponent, ProgressbarContentDirective, NgIf], | ||
template: ` | ||
<div class="d-flex align-items-center flex-wrap"> | ||
<div style="width: 350px"> | ||
<div au-progressbar #progressbar [value]="value"> | ||
<ng-template auProgressbarContent let-state="state"> | ||
<div class="position-relative" style="height: 300px"> | ||
<div class="cup"> | ||
<div class="cup-fill-parent"> | ||
<div class="cup-fill" [style.height.px]="value * 1.7"> | ||
<div class="bubble bubble-1" *ngIf="value >= 50"></div> | ||
<div class="bubble bubble-2" *ngIf="value >= 50"></div> | ||
<div class="bubble bubble-3" *ngIf="value >= 50"></div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</ng-template> | ||
</div> | ||
</div> | ||
<div class="d-flex flex-column justify-content-evenly h-100 ms-5"> | ||
<div class="btn-group" role="group"> | ||
<button class="btn btn-outline-primary" (click)="start()" [disabled]="progressbar.state().started">Start</button> | ||
<button | ||
class="btn btn-outline-primary" | ||
[disabled]="!progressbar.state().started || progressbar.state().finished" | ||
(click)="toggleProgress()" | ||
> | ||
{{ subscription ? 'Pause' : 'Resume' }} | ||
</button> | ||
<button class="btn btn-outline-primary" [disabled]="!progressbar.state().started" (click)="stop(true)">Reset</button> | ||
</div> | ||
<p class="mt-3"> | ||
<span>{{ value === 0 ? 'Need to wake up.' : value < 100 ? 'Retrieving coffee... ' + value + '%' : 'Ready to work !' }}</span> | ||
</p> | ||
</div> | ||
</div> | ||
`, | ||
}) | ||
export default class DefaultProgressBarComponent implements OnDestroy { | ||
value = 0; | ||
subscription: Subscription | undefined; | ||
|
||
start() { | ||
if (!this.subscription) { | ||
this.subscription = interval(500) | ||
.pipe( | ||
startWith(-1), | ||
map((val) => 10 * (val + 2)), | ||
takeWhile((val) => val <= 100) | ||
) | ||
.subscribe((val) => { | ||
this.value = val; | ||
}); | ||
} | ||
} | ||
stop(reset = false) { | ||
this.subscription?.unsubscribe(); | ||
this.subscription = undefined; | ||
if (reset) { | ||
this.value = 0; | ||
} | ||
} | ||
toggleProgress() { | ||
if (this.subscription) { | ||
this.stop(); | ||
} else { | ||
this.start(); | ||
} | ||
} | ||
ngOnDestroy() { | ||
this.stop(); | ||
} | ||
} |
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,23 @@ | ||
import {ProgressbarComponent, provideWidgetsConfig} from '@agnos-ui/angular'; | ||
import {Component} from '@angular/core'; | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [ProgressbarComponent], | ||
providers: [ | ||
provideWidgetsConfig((config) => { | ||
config.progressbar = {...config.progressbar, showPercentage: true}; | ||
return config; | ||
}), | ||
], | ||
template: ` | ||
<div class="d-flex flex-column gap-2"> | ||
<div au-progressbar [value]="20"></div> | ||
<div au-progressbar [value]="40" className="text-bg-success"></div> | ||
<div au-progressbar [value]="60" className="text-bg-info"></div> | ||
<div au-progressbar [value]="80" className="text-bg-warning"></div> | ||
<div au-progressbar [value]="100" className="text-bg-danger"></div> | ||
</div> | ||
`, | ||
}) | ||
export default class DefaultProgressBarComponent {} |
23 changes: 23 additions & 0 deletions
23
angular/demo/src/app/samples/progressbar/playground.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,23 @@ | ||
import type {RatingComponent} from '@agnos-ui/angular'; | ||
import {ProgressbarComponent} from '@agnos-ui/angular'; | ||
import {getProgressbarDefaultConfig} from '@agnos-ui/core'; | ||
import {Component, ViewChild} from '@angular/core'; | ||
import {getUndefinedValues, hashChangeHook, provideHashConfig} from '../../utils'; | ||
|
||
const undefinedConfig = getUndefinedValues(getProgressbarDefaultConfig()); | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [ProgressbarComponent], | ||
providers: provideHashConfig('progressbar'), | ||
template: `<div au-progressbar #widget></div>`, | ||
}) | ||
export default class PlaygroundComponent { | ||
@ViewChild('widget') widget: RatingComponent; | ||
|
||
constructor() { | ||
hashChangeHook((props) => { | ||
this.widget?._widget.patch({...undefinedConfig, ...props}); | ||
}); | ||
} | ||
} |
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,22 @@ | ||
import {ProgressbarComponent} from '@agnos-ui/angular'; | ||
import {Component} from '@angular/core'; | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [ProgressbarComponent], | ||
template: ` | ||
<div class="pb-2"> | ||
A progressbar using custom values for minimum and maximum: | ||
<div au-progressbar [minimum]="1" [maximum]="5" [value]="4" ariaLabel="Step 4 out of 5">Step 4 out of 5</div> | ||
</div> | ||
<div class="pb-2"> | ||
A striped animated progress bar: | ||
<div au-progressbar className="text-bg-info" [value]="63" [striped]="true" [animated]="true"></div> | ||
</div> | ||
<div class="pb-2"> | ||
Changing the height: | ||
<div au-progressbar [height]="'1.5rem'" [value]="47"></div> | ||
</div> | ||
`, | ||
}) | ||
export default class StripedProgressBarComponent {} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 |
---|---|---|
@@ -1,3 +1,7 @@ | ||
/* You can add global styles to this file, and also import other style files */ | ||
|
||
@import 'bootstrap/dist/css/bootstrap.css'; | ||
|
||
.cup-fill { | ||
background: url('assets/coffee-img-1.svg'), url('assets/coffee-img-2.svg'); | ||
} |
141 changes: 141 additions & 0 deletions
141
angular/lib/src/lib/progressbar/progressbar.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,141 @@ | ||
import type {AdaptSlotContentProps, AdaptWidgetSlots, SlotContent} from '../slot.directive'; | ||
import {ComponentTemplate, SlotDirective, callWidgetFactory} from '../slot.directive'; | ||
import type {AfterContentChecked, OnChanges, Signal, SimpleChanges} from '@angular/core'; | ||
import {NgClass, NgIf} from '@angular/common'; | ||
import {ChangeDetectionStrategy, Component, ContentChild, Directive, Input, TemplateRef, ViewChild, inject} from '@angular/core'; | ||
import type {ProgressbarContext as ProgressbarCoreContext, WidgetProps, WidgetState} from '@agnos-ui/core'; | ||
import {createProgressbar, toSlotContextWidget} from '@agnos-ui/core'; | ||
import {writable} from '@amadeus-it-group/tansu'; | ||
import {toSignal} from '@angular/core/rxjs-interop'; | ||
import {patchSimpleChanges} from '../utils'; | ||
import {SlotDefaultDirective} from '../slotDefault.directive'; | ||
|
||
export type ProgressbarWidget = AdaptWidgetSlots<ReturnType<typeof createProgressbar>>; | ||
export type ProgressbarState = WidgetState<ProgressbarWidget>; | ||
export type ProgressbarProps = WidgetProps<ProgressbarWidget>; | ||
|
||
export type ProgressbarContext = AdaptSlotContentProps<ProgressbarCoreContext>; | ||
|
||
@Directive({selector: 'ng-template[auProgressbarContent]', standalone: true}) | ||
export class ProgressbarContentDirective { | ||
public templateRef = inject(TemplateRef<ProgressbarContext>); | ||
static ngTemplateContextGuard(_dir: ProgressbarContentDirective, context: unknown): context is ProgressbarCoreContext { | ||
return true; | ||
} | ||
} | ||
|
||
@Component({ | ||
standalone: true, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
imports: [NgClass, NgIf, SlotDirective, ProgressbarContentDirective], | ||
template: ` | ||
<ng-template auProgressbarContent #content let-state="state" let-widget="widget"> | ||
<div class="progress" [style.height]="state.height"> | ||
<div | ||
class="progress-bar" | ||
[class.progress-bar-striped]="state.striped" | ||
[class.progress-bar-animated]="state.animated" | ||
[ngClass]="state.className" | ||
[style.width.%]="state.percentage" | ||
> | ||
<ng-template [auSlot]="state.slotDefault" [auSlotProps]="{state, widget}"></ng-template> | ||
<ng-container *ngIf="state.showPercentage">{{ state.percentage }}%</ng-container> | ||
</div> | ||
</div> | ||
</ng-template> | ||
`, | ||
}) | ||
export class ProgressbarDefaultSlotsComponent { | ||
@ViewChild('content', {static: true}) content: TemplateRef<ProgressbarCoreContext>; | ||
} | ||
|
||
export const progressbarDefaultSlotContent = new ComponentTemplate(ProgressbarDefaultSlotsComponent, 'content'); | ||
|
||
const defaultConfig: Partial<ProgressbarProps> = { | ||
slotContent: progressbarDefaultSlotContent, | ||
}; | ||
|
||
@Component({ | ||
// eslint-disable-next-line @angular-eslint/component-selector | ||
selector: 'div[au-progressbar]', | ||
standalone: true, | ||
imports: [SlotDirective, SlotDefaultDirective], | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
host: { | ||
role: 'progressbar', | ||
'[attr.aria-label]': 'state().ariaLabel || undefined', | ||
'[attr.aria-valuenow]': 'state().value', | ||
'[attr.aria-valuemin]': 'state().minimum', | ||
'[attr.aria-valuemax]': 'state().maximum', | ||
}, | ||
template: ` | ||
<ng-template [auSlotDefault]="defaultSlots"><ng-content></ng-content></ng-template> | ||
<ng-template [auSlot]="state().slotContent" [auSlotProps]="{state: state(), widget}"></ng-template> | ||
`, | ||
}) | ||
export class ProgressbarComponent implements AfterContentChecked, OnChanges { | ||
readonly defaultSlots = writable(defaultConfig); | ||
/** | ||
* the aria label | ||
*/ | ||
@Input() ariaLabel: string | undefined; | ||
|
||
/** | ||
* the minimum value | ||
*/ | ||
@Input() minimum: number | undefined; | ||
|
||
/** | ||
* the maximum value | ||
*/ | ||
@Input() maximum: number | undefined; | ||
|
||
/** | ||
* the current value | ||
*/ | ||
@Input() value: number | undefined; | ||
|
||
/** | ||
* class to add to the content | ||
*/ | ||
@Input() className: string | undefined; | ||
|
||
@Input() slotDefault: SlotContent<AdaptSlotContentProps<ProgressbarCoreContext>>; | ||
@Input() slotContent: SlotContent<AdaptSlotContentProps<ProgressbarCoreContext>>; | ||
@ContentChild(ProgressbarContentDirective, {static: false}) slotContentFromContent: ProgressbarContentDirective | undefined; | ||
|
||
/** | ||
* if `true`, display the current percentage in the `xx%` format | ||
*/ | ||
@Input() showPercentage: boolean | undefined; | ||
|
||
/** | ||
* height of the progressbar, can be any valid css height value | ||
*/ | ||
@Input() height: string | undefined; | ||
|
||
/** | ||
* if `true`, animates a striped progressbar | ||
*/ | ||
@Input() animated: boolean | undefined; | ||
|
||
/** | ||
* if `true`, shows a striped progressbar | ||
*/ | ||
@Input() striped: boolean | undefined; | ||
|
||
readonly _widget = callWidgetFactory(createProgressbar, 'progressbar', this.defaultSlots); | ||
readonly widget = toSlotContextWidget(this._widget); | ||
readonly api = this._widget.api; | ||
readonly state: Signal<ProgressbarState> = toSignal(this._widget.state$, {requireSync: true}); | ||
|
||
ngAfterContentChecked(): void { | ||
this._widget.patch({ | ||
slotContent: this.slotContentFromContent?.templateRef, | ||
}); | ||
} | ||
|
||
ngOnChanges(changes: SimpleChanges): void { | ||
patchSimpleChanges(this._widget.patch, changes); | ||
} | ||
} |
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.