-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add `withMutation` feature Release-As: 0.2.0
- Loading branch information
Showing
35 changed files
with
9,353 additions
and
4,165 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
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
Empty file.
Empty file.
3,326 changes: 3,326 additions & 0 deletions
3,326
apps/demo/public/assets/data/gh-org-repos-angular.json
Large diffs are not rendered by default.
Oops, something went wrong.
3,332 changes: 3,332 additions & 0 deletions
3,332
apps/demo/public/assets/data/gh-org-repos-google.json
Large diffs are not rendered by default.
Oops, something went wrong.
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,44 +1,6 @@ | ||
@if (isFetching() || isLoading()) { | ||
<mat-progress-bar class="busy-indicator" mode="indeterminate" /> | ||
} | ||
|
||
<mat-list class="list"> | ||
<div mat-subheader>{{ organization() | titlecase }} Repositories</div> | ||
|
||
@if (isFetching() || isLoading()) { | ||
<ng-container *ngTemplateOutlet="skeleton" /> | ||
<ng-container *ngTemplateOutlet="skeleton" /> | ||
<ng-container *ngTemplateOutlet="skeleton" /> | ||
} @else { | ||
@for (repo of data(); track repo) { | ||
<mat-list-item class="list-item"> | ||
<mat-icon class="list-item__icon" matListItemIcon>folder</mat-icon> | ||
<div class="list-item__title" matListItemTitle> | ||
<a class="list-item__link" [href]="repo.html_url" target="_blank" rel="external nofollow noopener noreferrer"> | ||
{{ repo.name }} | ||
</a> | ||
<span class="list-item__pill">{{ repo.private ? 'Private' : 'Public' }}</span> | ||
</div> | ||
<div class="list-item__description" matListItemLine> | ||
{{ repo.description }} | ||
</div> | ||
</mat-list-item> | ||
} @empty { | ||
<div class="no-records">No repositories found</div> | ||
} | ||
} | ||
</mat-list> | ||
<ssq-counter /> | ||
<ssq-github-repos /> | ||
|
||
<ng-template #skeleton> | ||
<mat-list-item class="list-item skeleton"> | ||
<mat-icon class="list-item__icon" matListItemIcon>folder</mat-icon> | ||
<div class="list-item__title" matListItemTitle> | ||
<ssq-skeleton-text /> | ||
</div> | ||
<div class="list-item__description" matListItemLine> | ||
<ssq-skeleton-text inlineSize="12rem" /> | ||
</div> | ||
</mat-list-item> | ||
</ng-template> | ||
|
||
<angular-query-devtools /> | ||
@defer (when isDevMode) { | ||
<angular-query-devtools /> | ||
} |
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,40 +1,7 @@ | ||
@use '@angular/material' as mat; | ||
@use 'theme' as *; | ||
|
||
@mixin apply-theme { | ||
--ssq-chip-border-color: #{mat.get-theme-color($light-theme, on-surface)}; | ||
|
||
@media (prefers-color-scheme: dark) { | ||
--ssq-chip-border-color: #{mat.get-theme-color($dark-theme, on-surface)}; | ||
} | ||
} | ||
|
||
:host { | ||
@include apply-theme; | ||
|
||
margin-inline: auto; | ||
padding: 2rem; | ||
inline-size: clamp(20rem, 100%, 64rem); | ||
min-block-size: 100%; | ||
display: block; | ||
|
||
.busy-indicator { | ||
inset: 0 auto 0; | ||
position: fixed; | ||
} | ||
|
||
.list-item { | ||
:is(&__pill) { | ||
padding: 0.05rem 0.375rem; | ||
translate: 0.125rem -0.25rem; | ||
display: inline-grid; | ||
align-items: center; | ||
border: 0.05rem solid hsl(from var(--ssq-chip-border-color) h s l / 50%); | ||
border-radius: 2em; | ||
font-size: 0.75rem; | ||
} | ||
} | ||
|
||
.no-records { | ||
display: block; | ||
place-content: center; | ||
text-align: center; | ||
opacity: 0.5; | ||
} | ||
} |
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,54 +1,19 @@ | ||
import { NgTemplateOutlet, TitleCasePipe } from '@angular/common'; | ||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; | ||
|
||
import { MatIconModule } from '@angular/material/icon'; | ||
import { MatListModule } from '@angular/material/list'; | ||
import { MatProgressBarModule } from '@angular/material/progress-bar'; | ||
import { ChangeDetectionStrategy, Component, isDevMode } from '@angular/core'; | ||
|
||
import { AngularQueryDevtools } from '@tanstack/angular-query-devtools-experimental'; | ||
|
||
import { AppStore } from './app.store'; | ||
import { SkeletonTextComponent } from './skeleton-text.component'; | ||
import { CounterComponent } from './counter'; | ||
import { GithubReposComponent } from './gh-repos'; | ||
import { SkeletonTextComponent } from './skeleton-text'; | ||
|
||
@Component({ | ||
standalone: true, | ||
selector: 'ssq-app', | ||
templateUrl: './app.component.html', | ||
styleUrl: './app.component.scss', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
providers: [AppStore], | ||
imports: [ | ||
TitleCasePipe, | ||
MatIconModule, | ||
MatListModule, | ||
MatProgressBarModule, | ||
AngularQueryDevtools, | ||
SkeletonTextComponent, | ||
NgTemplateOutlet, | ||
], | ||
imports: [AngularQueryDevtools, CounterComponent, GithubReposComponent, SkeletonTextComponent], | ||
}) | ||
export class AppComponent { | ||
protected readonly store = inject(AppStore); | ||
|
||
protected organization = this.store.organization; | ||
|
||
protected githubQuery = this.store.githubQuery; | ||
|
||
protected isFetching = computed(() => { | ||
const githubQuery = this.githubQuery(); | ||
|
||
return githubQuery.isFetching(); | ||
}); | ||
|
||
protected isLoading = computed(() => { | ||
const githubQuery = this.githubQuery(); | ||
|
||
return githubQuery.isLoading(); | ||
}); | ||
|
||
protected data = computed(() => { | ||
const githubQuery = this.githubQuery(); | ||
|
||
return githubQuery.data() ?? []; | ||
}); | ||
protected readonly isDevMode = isDevMode(); | ||
} |
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,9 @@ | ||
<output aria-label="Current count">{{ value() }}</output> | ||
|
||
<button mat-fab type="button" aria-label="Increase" title="Increase" [disabled]="isBusy()" (click)="increase()"> | ||
<mat-icon fontIcon="exposure_plus_1" /> | ||
</button> | ||
|
||
<button mat-fab type="button" aria-label="Decrease" title="Decrease" [disabled]="isBusy()" (click)="decrease()"> | ||
<mat-icon fontIcon="exposure_neg_1" /> | ||
</button> |
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 @@ | ||
@use '@angular/material' as mat; | ||
|
||
@use 'theme' as *; | ||
|
||
:host { | ||
margin-inline: auto; | ||
min-inline-size: 6rem; | ||
inline-size: fit-content; | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto 3rem; | ||
justify-items: center; | ||
gap: 0.5rem; | ||
|
||
output { | ||
grid-column: 1 / 3; | ||
display: block; | ||
align-content: center; | ||
text-align: center; | ||
font: #{mat.get-theme-typography($light-theme, headline-large, font)}; | ||
} | ||
} |
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,36 @@ | ||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; | ||
|
||
import { MatButtonModule } from '@angular/material/button'; | ||
import { MatIconModule } from '@angular/material/icon'; | ||
|
||
import { CounterStore } from './counter.store'; | ||
|
||
@Component({ | ||
standalone: true, | ||
selector: 'ssq-counter', | ||
templateUrl: './counter.component.html', | ||
styleUrl: './counter.component.scss', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
providers: [CounterStore], | ||
imports: [MatButtonModule, MatIconModule], | ||
}) | ||
export class CounterComponent { | ||
private readonly store = inject(CounterStore); | ||
|
||
protected readonly value = this.store.count; | ||
|
||
public readonly isBusy = computed(() => { | ||
const increase = this.store.increaseMutation.isPending(); | ||
const decrease = this.store.decreaseMutation.isPending(); | ||
|
||
return increase || decrease; | ||
}); | ||
|
||
public increase(): void { | ||
this.store.increaseMutation.mutate(1); | ||
} | ||
|
||
public decrease(): void { | ||
this.store.decreaseMutation.mutate(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,76 @@ | ||
import { DestroyRef, inject } from '@angular/core'; | ||
|
||
import { MatSnackBar } from '@angular/material/snack-bar'; | ||
|
||
import { patchState, signalStore, withState } from '@ngrx/signals'; | ||
import { withMutation } from '@ngx-signal-store-query/core'; | ||
|
||
export const CounterStore = signalStore( | ||
withState({ count: 0 }), | ||
withMutation('increase', (store) => () => { | ||
const destroyRef = inject(DestroyRef); | ||
const snackBar = inject(MatSnackBar); | ||
|
||
let timer: ReturnType<typeof setTimeout> | null = null; | ||
|
||
destroyRef.onDestroy(() => timer != null && clearTimeout(timer)); | ||
|
||
return { | ||
mutationFn(amount: number): Promise<CounterResponse> { | ||
const count = store.count(); | ||
|
||
return new Promise((resolve, reject) => { | ||
if (count >= 5) { | ||
return reject(new RangeError('Count is to big')); | ||
} | ||
|
||
timer = setTimeout(() => resolve({ count: count + amount }), 250); | ||
}); | ||
}, | ||
onSuccess({ count }: CounterResponse): void { | ||
return patchState(store, { count }); | ||
}, | ||
onError(error: Error): void { | ||
snackBar.open(error.message, '', { | ||
panelClass: 'popover-error', | ||
duration: 5000, | ||
}); | ||
}, | ||
}; | ||
}), | ||
withMutation('decrease', (store) => () => { | ||
const destroyRef = inject(DestroyRef); | ||
const snackBar = inject(MatSnackBar); | ||
|
||
let timer: ReturnType<typeof setTimeout> | null = null; | ||
|
||
destroyRef.onDestroy(() => timer != null && clearTimeout(timer)); | ||
|
||
return { | ||
mutationFn: (amount: number): Promise<CounterResponse> => { | ||
const count = store.count(); | ||
|
||
return new Promise((resolve, reject) => { | ||
if (count <= 0) { | ||
return reject(new RangeError('Count is to low')); | ||
} | ||
|
||
timer = setTimeout(() => resolve({ count: count - amount }), 250); | ||
}); | ||
}, | ||
onSuccess: ({ count }: CounterResponse): void => { | ||
return patchState(store, { count }); | ||
}, | ||
onError: (error: Error): void => { | ||
snackBar.open(error.message, '', { | ||
panelClass: 'popover-error', | ||
duration: 5000, | ||
}); | ||
}, | ||
}; | ||
}), | ||
); | ||
|
||
interface CounterResponse { | ||
count: number; | ||
} |
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 './counter.component'; |
Oops, something went wrong.