Skip to content

Commit

Permalink
feat(shared-ui-global): criado entry-point forms
Browse files Browse the repository at this point in the history
  • Loading branch information
guiseek committed Oct 9, 2024
1 parent b2bdbfa commit 5007f8d
Show file tree
Hide file tree
Showing 68 changed files with 881 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Thumbs.db
.nx/cache
.nx/workspace-data
.env
.env.prod

.angular
assets/server/**/*
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
/diagrams
/assets/seeds
pnpm-lock.yaml

packages/shared/ui-global/forms/src/lib/components
6 changes: 6 additions & 0 deletions packages/shared/ui-global/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
"prefix": "devmx",
"style": "kebab-case"
}
],
"@angular-eslint/component-class-suffix": [
"error",
{
"suffixes": ["Component", "Dialog"]
}
]
}
},
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/ui-global/forms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @devmx/shared-ui-global/forms

Secondary entry point of `@devmx/shared-ui-global`. It can be used by importing from `@devmx/shared-ui-global/forms`.
5 changes: 5 additions & 0 deletions packages/shared/ui-global/forms/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "src/index.ts"
}
}
6 changes: 6 additions & 0 deletions packages/shared/ui-global/forms/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './lib/components';
export * from './lib/dialogs';
export * from './lib/common';
export * from './lib/fields';
export * from './lib/types';
export * from './lib/utils';
29 changes: 29 additions & 0 deletions packages/shared/ui-global/forms/src/lib/common/account-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { AccountRef } from '@devmx/shared-api-interfaces';
import { TypedForm } from '../types';
import { NameForm } from './name';

export class AccountRefForm extends FormGroup<TypedForm<AccountRef>> {
get name() {
return this.controls.name.value;
}

constructor(account?: AccountRef) {
super({
id: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
name: new NameForm(),
username: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
photo: new FormControl('', {
nonNullable: true,
}),
});

if (account) this.patchValue(account);
}
}
3 changes: 3 additions & 0 deletions packages/shared/ui-global/forms/src/lib/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './account-ref';
export * from './name';
export * from './presentation-ref';
20 changes: 20 additions & 0 deletions packages/shared/ui-global/forms/src/lib/common/name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Name } from '@devmx/shared-api-interfaces';
import { TypedForm } from '../types';

export class NameForm extends FormGroup<TypedForm<Name>> {
constructor(name?: Name) {
super({
first: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
last: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
});

if (name) this.patchValue(name);
}
}
26 changes: 26 additions & 0 deletions packages/shared/ui-global/forms/src/lib/common/presentation-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { PresentationRef } from '@devmx/shared-api-interfaces';
import { TypedForm } from '../types';

export class PresentationRefForm extends FormGroup<TypedForm<PresentationRef>> {
constructor(presentation?: PresentationRef) {
super({
id: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
title: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
description: new FormControl('', {
nonNullable: true,
}),
cover: new FormControl('', {
nonNullable: true,
}),
});

if (presentation) this.patchValue(presentation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@if (field(); as opts) {

<mat-checkbox
[formControl]="control()"
[checked]="opts.checked"
[value]="opts.value + ''"
>
{{ opts.label }}
</mat-checkbox>

}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { Checkbox } from '../../fields';

@Component({
selector: 'devmx-checkbox',
templateUrl: './checkbox.component.html',
styleUrl: './checkbox.component.scss',
imports: [ReactiveFormsModule, MatCheckboxModule],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
})
export class CheckboxComponent<T> {
field = input.required<Checkbox<T>>();

control = input.required<FormControl<T>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@if (field(); as opts) {

<mat-form-field>
<mat-label>{{ opts.label }}</mat-label>
<input matInput [matDatepicker]="picker" [min]="opts.min" [max]="opts.max">
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error>
@for (error of opts.errors | keyvalue; track error) {
@if (control().hasError(error.key)) {
{{ error.value }}
}
}
</mat-error>
</mat-form-field>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: inline-flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { input, Component, ChangeDetectionStrategy } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { provideNativeDateAdapter } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatInputModule } from '@angular/material/input';
import { KeyValuePipe } from '@angular/common';
import { Datepicker } from '../../fields';

@Component({
selector: 'devmx-datepicker',
templateUrl: './datepicker.component.html',
styleUrl: './datepicker.component.scss',
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatDatepickerModule,
MatInputModule,
KeyValuePipe,
],
providers: [provideNativeDateAdapter()],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
})
export class DatepickerComponent<T> {
field = input.required<Datepicker<T>>();

control = input.required<FormControl<T>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@if (field(); as opts) {

<mat-form-field>
<mat-label>{{ opts.label }}</mat-label>
<mat-select [formControl]="control()">
@for (opt of opts.options; track opt) {
<mat-option [value]="opt.value">{{ opt.text }}</mat-option>
}
</mat-select>
<mat-error>
@for (error of opts.errors | keyvalue; track error) {
@if (control().hasError(error.key)) {
{{ error.value }}
}
}
</mat-error>
</mat-form-field>

}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { input, Component, ChangeDetectionStrategy } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { KeyValuePipe } from '@angular/common';
import { Dropdown } from '../../fields';

@Component({
selector: 'devmx-dropdown',
templateUrl: './dropdown.component.html',
styleUrl: './dropdown.component.scss',
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatSelectModule,
KeyValuePipe,
],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
})
export class DropdownComponent<T> {
field = input.required<Dropdown<T>>();

control = input.required<FormControl<T>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@for (field of fields() | keyvalue | orderBy: 'value.order'; track field) {
@if (isFormField(field.value)) {
@switch (field.value.kind) {
@case ('textbox') {
@if (field.value.type !== 'hidden') {
<devmx-textbox [field]="field.value" [name]="field.key" [control]="getControl(field.key)" />
}
}
@case ('dropdown') {
<devmx-dropdown [field]="field.value" [control]="getControl(field.key)" />
}
@case ('datepicker') {
<devmx-datepicker [field]="field.value" [control]="getControl(field.key)" />
}
@case ('checkbox') {
<devmx-checkbox [field]="field.value" [control]="getControl(field.key)" />
}
@case ('radio') {
<devmx-radio [field]="field.value" [control]="getControl(field.key)" />
}
}
} @else {
<ng-container [formGroup]="getGroup(field.key)">
<devmx-form-group [fields]="$any(field.value)" [formGroup]="getGroup(field.key)" />
</ng-container>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:host {
display: flex;
flex-direction: column;
gap: 0.6em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { DatepickerComponent } from '../datepicker/datepicker.component';
import { DropdownComponent } from '../dropdown/dropdown.component';
import { CheckboxComponent } from '../checkbox/checkbox.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TextboxComponent } from '../textbox/textbox.component';
import { FormField, TypedFields, TypedForm } from '../../types';
import { RadioComponent } from '../radio/radio.component';
import { KeyValuePipe } from '@angular/common';
import { OrderByPipe } from '../../pipes';
import { debounceTime } from 'rxjs';
import {
input,
output,
OnInit,
inject,
Component,
DestroyRef,
ChangeDetectionStrategy,
} from '@angular/core';

@Component({
selector: 'devmx-form-group',
templateUrl: './form-group.component.html',
styleUrl: './form-group.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ReactiveFormsModule,
TextboxComponent,
DropdownComponent,
CheckboxComponent,
RadioComponent,
DatepickerComponent,
KeyValuePipe,
OrderByPipe
],
standalone: true,
})
export class FormGroupComponent<T = object> implements OnInit {
#destroyRef = inject(DestroyRef);

fields = input.required<TypedFields<T>>();

formGroup = input.required<FormGroup<TypedForm<T>>>();

valueChanges = output<T>();

getControl<K extends keyof T>(name: string) {
return this.formGroup().get(name) as FormControl<T[K]>;
}

getGroup(name: string) {
return this.formGroup().get(name) as FormGroup;
}

isFormField(field: object): field is FormField {
return 'kind' in field;
}

ngOnInit() {
this.formGroup()
.valueChanges.pipe(
debounceTime(300),
takeUntilDestroyed(this.#destroyRef)
)
.subscribe(() => {
const value = this.formGroup().getRawValue();
this.valueChanges.emit(value as T);
});
}
}
6 changes: 6 additions & 0 deletions packages/shared/ui-global/forms/src/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './checkbox/checkbox.component';
export * from './dropdown/dropdown.component';
export * from './radio/radio.component';
export * from './textbox/textbox.component';
export * from './datepicker/datepicker.component';
export * from './form-group/form-group.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@if (field(); as opts) {

<mat-radio-group [ariaLabel]="opts.label" [formControl]="control()">
@for (opt of opts.options; track opt) {
<mat-radio-button [value]="opt.value">{{ opt.text }}</mat-radio-button>
}
</mat-radio-group>

}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatRadioModule } from '@angular/material/radio';
import { Radio } from '../../fields';

@Component({
selector: 'devmx-radio',
templateUrl: './radio.component.html',
styleUrl: './radio.component.scss',
imports: [ReactiveFormsModule, MatRadioModule],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
})
export class RadioComponent<T> {
field = input.required<Radio<T>>();

control = input.required<FormControl<T>>();
}
Loading

0 comments on commit 5007f8d

Please sign in to comment.