Skip to content

Commit

Permalink
feat: add track example
Browse files Browse the repository at this point in the history
  • Loading branch information
d-koppenhagen committed Jan 9, 2024
1 parent c8b9b45 commit a80cfbf
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 69 deletions.
69 changes: 0 additions & 69 deletions src/app/alert-form/alert-form.component.scss
Original file line number Diff line number Diff line change
@@ -1,72 +1,3 @@
.form-field-group {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.form-field {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
flex-grow: 1;
}

input,
textarea,
select,
ul.list-box {
background: color-mix(in srgb, var(--pill-accent) 10%, transparent);
font-size: 1rem;
padding: 0.75rem 0.5rem;
border: 2px solid color-mix(in srgb, var(--electric-violet) 30%, transparent);
border-radius: 0.5rem;
font-family: var(--font-family);

&::placeholder {
color: var(--grayr-400);
}
}

input,
textarea,
select,
button, ul.list-box li {
&:focus-within {
outline-width: 3px;
outline-style: solid;
outline-color: lightblue;
outline-offset: 2px;
}
}

label:has(+ input, + textarea, + select, +ul.list-box) {
color: var(--electric-violet);
font-weight: bold;
font-size: 0.875rem;
margin-bottom: 0.2rem;
margin-left: 0.5rem;
}

label:has(+ input[required])::after,
label:has(+ textarea[required])::after,
label:has(+ select[required])::after {
content: " *";
}
label:has(+ input[required][aria-invalid="true"])::after,
label:has(+ textarea[required][aria-invalid="true"])::after,
label:has(+ select[required][aria-invalid="true"])::after {
color: var(--hot-red);
}

p.description {
color: var(--gray-900);
font-size: 0.75rem;
margin-top: 0.2rem;
margin-left: 0.5rem;
}
input[aria-invalid="true"] + p.description {
color: #ac0000;
}

ul.list-box {
display: flex;
padding: 0;
Expand Down
5 changes: 5 additions & 0 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export const routes: Routes = [
loadComponent: () => import('./deferred-loading-view/deferred-loading-view.component').then(m => m.DeferredLoadingViewComponent),
title: 'Deferred Loading'
},
{
path: 'track',
loadComponent: () => import('./track-view/track-view.component').then(m => m.TrackViewComponent),
title: 'Track List Items'
},
{
path: 'alerts',
loadComponent: () => import('./alerts-view/alerts-view.component').then(m => m.AlertsViewComponent),
Expand Down
8 changes: 8 additions & 0 deletions src/app/nav/nav.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
>
Deferred Loading
</a>
<a
class="button"
routerLink="/track"
routerLinkActive="active"
ariaCurrentWhenActive="page"
>
Track List Items
</a>
<a
class="button"
routerLink="/alerts"
Expand Down
3 changes: 3 additions & 0 deletions src/app/nav/nav.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ nav .button:nth-child(3) {
nav .button:nth-child(4) {
--pill-accent: var(--electric-violet);
}
nav .button:nth-child(5) {
--pill-accent: var(--orange-red);
}

nav svg {
margin-inline-start: 0.25rem;
Expand Down
58 changes: 58 additions & 0 deletions src/app/track-view/track-view.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<h1>Track Demo</h1>

<p>
The example below shows the difference of tracking list options with
<code>$index</code> and an actual unique attribute. By tracking a unique
attribute, the object wont be completely new initialized. In fact, when the
array changes, the focus of elements will be restored/tracked correctly.
</p>
<p>
Please enter a text in one of the input fields on the left side. After a few
seconds, items will be added to the array of the list. Keep an eye on where
you entered your text. While entering and concurrently adding new fields, your
input moves into another field as only the <code>$index</code> is tracked. The
list on the right tracks a unique attribute (<code>item.id</code>). This
ensures, the focus does not accidentally move to another input event when the
list updates concurrently.
</p>
<p>
Every 5 seconds a new entry is inserted.
</p>

<form>
<fieldset>
<legend>Track: <code>$index</code></legend>
@for(item of items; track $index) {
<div class="row">
<label class="col-sm-6 col-form-label" [for]="'col-0-' + $index">
{{ item.id }} | {{ item.name }}
</label>
<input [id]="'col-0-' + $index" class="form-control" />
</div>
}
</fieldset>

<fieldset>
<legend>Track: <code>item.id</code></legend>
@for(item of items; track item.id) {
<div class="row">
<label class="col-sm-6 col-form-label" [for]="'col-1-' + $index">
{{ item.id }} | {{ item.name }}
</label>
<input [id]="'col-1-' + $index" class="form-control" />
</div>
}
</fieldset>

<fieldset>
<legend>Track: <code>item</code></legend>
@for(item of items; track item) {
<div class="row">
<label class="col-sm-6 col-form-label" [for]="'col-2-' + $index">
{{ item.id }} | {{ item.name }}
</label>
<input [id]="'col-2-' + $index" class="form-control" />
</div>
}
</fieldset>
</form>
25 changes: 25 additions & 0 deletions src/app/track-view/track-view.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
form {
margin-top: 1rem;
display: flex;
justify-content: space-between;
gap: 1rem;
}

fieldset {
display: flex;
justify-content: space-between;
gap: .5rem;
flex-direction: column;

legend {
font-weight: bold;
}
}

p {
margin-bottom: .5rem;
}

code {
color: var(--bright-blue);
}
23 changes: 23 additions & 0 deletions src/app/track-view/track-view.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { TrackViewComponent } from './track-view.component';

describe('TrackViewComponent', () => {
let component: TrackViewComponent;
let fixture: ComponentFixture<TrackViewComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TrackViewComponent]
})
.compileComponents();

fixture = TestBed.createComponent(TrackViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
51 changes: 51 additions & 0 deletions src/app/track-view/track-view.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

interface Item {
id: number;
name: string;
}

const fullNameItems: Item[] = [
{ id: 3, name: 'Item #3' },
{ id: 2, name: 'Item #2' },
{ id: 1, name: 'Item #1' },
];

@Component({
selector: 'app-track-view',
standalone: true,
imports: [FormsModule],
templateUrl: './track-view.component.html',
styleUrl: './track-view.component.scss'
})
export class TrackViewComponent {
values = ['', '', '']; // Werte der Eingabefelder (initial)
items: Item[]; // Array mit Werten für *ngFor
toggle: boolean;

constructor() {
this.items = fullNameItems;
this.toggle = false;
setInterval(() => {
if (this.items.length >= 50) {
this.items = [];
}
this.addItem();
}, 5000);
}

addItem() {
const num = this.items.length + 1;
this.toggle = !this.toggle;
this.items = [
{ id: num, name: `Item #${num}` },

// with the following line will make the third column work as referecnes of the objects are kept
// ...this.items,

// with the follwoing line will make the third column not working as the references are not the same anymore (item === item => false)
...structuredClone(this.items),
];
}
}
69 changes: 69 additions & 0 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,75 @@ main {
}
}

.form-field-group {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.form-field {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
flex-grow: 1;
}

input,
textarea,
select,
ul.list-box {
background: color-mix(in srgb, var(--pill-accent) 10%, transparent);
font-size: 1rem;
padding: 0.75rem 0.5rem;
border: 2px solid color-mix(in srgb, var(--electric-violet) 30%, transparent);
border-radius: 0.5rem;
font-family: var(--font-family);

&::placeholder {
color: var(--grayr-400);
}
}

input,
textarea,
select,
button, ul.list-box li {
&:focus-within {
outline-width: 3px;
outline-style: solid;
outline-color: lightblue;
outline-offset: 2px;
}
}

label:has(+ input, + textarea, + select, +ul.list-box) {
color: var(--electric-violet);
font-weight: bold;
font-size: 0.875rem;
margin-bottom: 0.2rem;
margin-left: 0.5rem;
}

label:has(+ input[required])::after,
label:has(+ textarea[required])::after,
label:has(+ select[required])::after {
content: " *";
}
label:has(+ input[required][aria-invalid="true"])::after,
label:has(+ textarea[required][aria-invalid="true"])::after,
label:has(+ select[required][aria-invalid="true"])::after {
color: var(--hot-red);
}

p.description {
color: var(--gray-900);
font-size: 0.75rem;
margin-top: 0.2rem;
margin-left: 0.5rem;
}
input[aria-invalid="true"] + p.description {
color: #ac0000;
}


.alerts-overlay {
right: 0;
Expand Down

0 comments on commit a80cfbf

Please sign in to comment.