Skip to content

Commit

Permalink
Merge branch 'release/0.3.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
jm-1997 committed Apr 4, 2022
2 parents 8a01521 + 5ffc013 commit 346f03a
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 20 deletions.
7 changes: 6 additions & 1 deletion examples/angular/src/app/forms/forms.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { MyFormModel, MyQuestionType } from './models/mock.model';
template: `
<div #formContainer></div>
<div class="actions-container">
<button class="btn" (click)="handleSaveClick()">Save</button>
<button class="btn" style="margin-right: 1rem;" (click)="handleResetClick()">Reset</button>
<button class="btn btn-primary" (click)="handleSaveClick()">Save</button>
</div>
`,
styleUrls: ['./forms.component.css'],
Expand Down Expand Up @@ -60,6 +61,10 @@ export class FormsComponent implements OnInit, OnDestroy, AfterViewInit {
}
}

handleResetClick() {
this._form.getControl('semester')?.setValue(null);
}

/**
* Setup dynamic form initialization
*/
Expand Down
12 changes: 8 additions & 4 deletions examples/angular/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
:root {
/* colors */
--color-primary: #1c70ff;
--color-secondary: #f9fafb;
--color-secondary: #e6e6e6;

--text-color: #374151;

Expand Down Expand Up @@ -117,15 +117,14 @@ body {
align-items: center;
justify-content: flex-end;
padding: 1rem;
background-color: var(--color-secondary);
}

.btn {
background-color: var(--color-primary);
background-color: var(--color-secondary);
border-radius: var(--rounded);
border: solid 1px transparent;
box-shadow: var(--shadow);
color: #fff;
color: var(--text-color);
cursor: pointer;
display: inline-flex;
font-size: var(--text-sm);
Expand All @@ -143,3 +142,8 @@ body {
outline: var(--ring);
outline-offset: 2px;
}

.btn-primary {
background-color: var(--color-primary);
color: #fff;
}
15 changes: 10 additions & 5 deletions packages/forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [DatePicker](#datepicker)
- [TimePicker](#timepicker)
- [API options](#api-options)
- [Get control](#get-control)
- [Render](#render)
- [Reset](#reset)
- [Validate](#validate)
Expand Down Expand Up @@ -201,6 +202,10 @@ form.render();

## **API options**

### Get control

Get control by key identifier

### Render

Will render the generated form inside some specified target
Expand Down Expand Up @@ -327,7 +332,7 @@ Check the official **[ValidationsPlugin](https://github.com/Ekisa-Team/cdk/blob/

## **Plugins**

Plugins are building blocks that extend the form functionality. @ekisa/forms come with built-in plugins that solve different purposes and make the integration process seamlessly. There are two types of plugins: internal and external. Internal plugins will run inside the codebase and cannot be modified, although they could be rewritten as custom plugins but would probably need more logic on the implementation side because external plugins cannot listen for events that occur inside the form class. All internal plugins must be passed to the class constructor at the instantiation moment. External plugins whatsoever can be fully replaced by custom plugins and are easy to replicate or modify. All custom plugins are external.
Plugins are building blocks that extend the form functionality. @ekisa/forms come with built-in plugins that are useful for different purposes and make the integration process seamlessly. There are two types of plugins: internal and external. Internal plugins will run inside the codebase and cannot be modified, although they could be rewritten as custom plugins but would probably need more logic on the implementation side because external plugins cannot listen for events that occur inside the form class. All internal plugins must be passed to the class constructor at the instantiation moment. External plugins whatsoever can be fully replaced by custom plugins and are easy to replicate or modify. All custom plugins are external.

### **AutoMapper (external)**

Expand Down Expand Up @@ -459,10 +464,10 @@ const mappingProfile: AutoMapperPluginConfig<MyQuestionType> = {
};
```

**3. Create an instance of the AutoMapperPlugin and pass in your questions and the mapping profile**
**3. Create an instance of the AutoMapperPlugin, pass in your questions, and the mapping profile. Lastly, run the plugin to get the generated data source**

```ts
const mapper = new AutoMapperPlugin<MyQuestionType>(questions, this._formsService.mappingProfile);
const mapper = new AutoMapperPlugin<MyQuestionType>(questions, mappingProfile);
const dataSource = mapper.run();
```

Expand All @@ -486,7 +491,7 @@ A flexible plugin that allows you to easily attach events to a form control
const eventsPlugin = new EventsPlugin();
```
**2. Run your custom logic**
**2. Attach the event to any control by key**
```ts
eventsPlugin.run({
Expand All @@ -510,7 +515,7 @@ eventsPlugin.run({
### **Validations (internal)**
This plugin will run inside the form and will be smart enough to detect when the form is invalid and change the `data-status` attribute depending on the form status. It will also append the validations messages automatically on each control wrapper.
This plugin will run inside the form to detect when it is invalid and change the `data-status` attribute depending on the validity status. It will also append the validation messages automatically in each control wrapper.
#### Usage
Expand Down
2 changes: 1 addition & 1 deletion packages/forms/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ekisa-cdk/forms",
"version": "0.1.0",
"version": "0.1.1",
"description": "🛠️ Easily build & integrate dynamic forms",
"license": "MIT",
"files": [
Expand Down
2 changes: 2 additions & 0 deletions packages/forms/src/abstract-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type ValidationOutput = Array<{
}> | null;

export abstract class AbstractForm {
abstract getControl(key: string): AbstractControl | undefined;

abstract render(parent: HTMLBodyElement | HTMLDivElement): void;

abstract reset(): void;
Expand Down
12 changes: 11 additions & 1 deletion packages/forms/src/controls/abstract-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,18 @@ export abstract class AbstractControl {
}

getValue(): any | null {
const element = document.querySelector(`#${this.key}`);
const element = this.getElement();

if (!element) return null;

return (element as any)?.value.trim() || null;
}

setValue(value: any): void {
const element = this.getElement() as any;

if (!element) return;

element.value = value;
}
}
8 changes: 7 additions & 1 deletion packages/forms/src/controls/check-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export class CheckBox extends AbstractControl {
}

override getValue(): boolean {
const node = document.querySelector(`#${this.key}`) as HTMLInputElement | null;
const node = this.getElement() as HTMLInputElement | null;
return node?.checked || false;
}

override setValue(value: boolean): void {
const node = this.getElement() as HTMLInputElement | null;
if (!node) return;
node.checked = value;
}
}
8 changes: 7 additions & 1 deletion packages/forms/src/controls/date-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export class DatePicker extends AbstractControl {
}

override getValue(): Date | null {
const node = document.querySelector(`#${this.key}`) as HTMLInputElement | null;
const node = this.getElement() as HTMLInputElement | null;
return node?.valueAsDate || null;
}

override setValue(value: Date): void {
const node = this.getElement() as HTMLInputElement | null;
if (!node) return;
node.valueAsDate = value;
}
}
8 changes: 7 additions & 1 deletion packages/forms/src/controls/number-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ export class NumberBox extends AbstractControl {
}

override getValue(): number | null {
const node = document.querySelector(`#${this.key}`) as HTMLInputElement | null;
const node = this.getElement() as HTMLInputElement | null;
return node?.valueAsNumber || null;
}

override setValue(value: number): void {
const node = this.getElement() as HTMLInputElement | null;
if (!node) return;
node.valueAsNumber = value;
}
}
16 changes: 16 additions & 0 deletions packages/forms/src/controls/radio-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,20 @@ export class RadioGroup extends AbstractControl {

return node?.value || null;
}

override setValue(value: string): void {
const node = Array.from<HTMLInputElement>(
document.querySelectorAll(`[name=${this.key}]`),
)?.find((n) => n.value === value) as HTMLInputElement | null;

if (!node) return;

node.checked = true;
}

reset() {
Array.from<HTMLInputElement>(document.querySelectorAll(`[name=${this.key}]`))?.forEach(
(n) => (n.checked = false),
);
}
}
8 changes: 7 additions & 1 deletion packages/forms/src/controls/time-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export class TimePicker extends AbstractControl {
}

override getValue(): Date | null {
const node = document.querySelector(`#${this.key}`) as HTMLInputElement | null;
const node = this.getElement() as HTMLInputElement | null;
return node?.valueAsDate || null;
}

override setValue(value: Date): void {
const node = this.getElement() as HTMLInputElement | null;
if (!node) return;
node.valueAsDate = value;
}
}
42 changes: 38 additions & 4 deletions packages/forms/src/form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AbstractForm, ValidationOutput } from './abstract-form';
import renderUtils from './builder';
import { FieldSet } from './controls';
import { FieldSet, RadioGroup } from './controls';
import { AbstractControl } from './controls/abstract-control';
import { findPlugin, PluginsCollection } from './plugins';
import { ValidationsPlugin } from './plugins/validations.plugin';
Expand All @@ -17,33 +17,67 @@ export class Form extends AbstractForm {
*/
plugins: PluginsCollection;

private _controls: Array<AbstractControl> = [];

//#region Accesors
get controls(): Array<AbstractControl> {
return this._flattenControls(this.dataSource) as Array<AbstractControl>;
return this._controls;
}
//#endregion

constructor(args: { dataSource: FormControls; plugins?: PluginsCollection }) {
super();

this.dataSource = args.dataSource;
this.plugins = args.plugins || [];
}

/**
* Get form control
* @param key control key identifier
*/
getControl(key: string): AbstractControl | undefined {
return this._controls.find((control) => control.key === key);
}

/**
* Render dynamic form inside parent element
* @param parent HTML element where the form will be rendered
*/
render(parent: HTMLBodyElement | HTMLDivElement): void {
const form = renderUtils.buildForm(this.dataSource);
parent.append(form);
this._controls = this._flattenControls(this.dataSource) as Array<AbstractControl>;
}

/**
* Reset form elements to defaults
*/
reset(): void {
throw new Error('Method not implemented.');
this.controls.forEach((control) => {
// Reset controls value based on type
switch (control.type) {
case 'CheckBox':
control.setValue(false);
break;
case 'DatePicker':
case 'TimePicker':
case 'NumberBox':
control.setValue(null);
break;
case 'RadioGroup':
(control as RadioGroup).reset();
break;
default:
control.setValue('');
}

// Remove control status
const parent = control.getParentElement()!;
delete parent.dataset.status;

// Remove validations container
control.getValidationsElement()?.remove();
});
}

/**
Expand Down

0 comments on commit 346f03a

Please sign in to comment.