Last active
June 5, 2021 20:56
-
-
Save Parables/e17a96ba4b217178f9f918d3dbbea30c to your computer and use it in GitHub Desktop.
This is the test version of the source code used to make Dynamic Form Building using Classes
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
<script> | |
import FormBuilder from './FormBuilder.svelte' | |
import { Form } from './Form' | |
let form = new Form({id: "Test Form", sections: []}) | |
let title ="New Section" | |
function addSection(){ | |
form= form.addSection({title: title, rows:[]}); | |
form.print() | |
} | |
function removeSection(e){ | |
let {section, index} = e.detail | |
form = form.removeSection(section, index) | |
} | |
</script> | |
<div> | |
<label for="newSection"></label> | |
<input id="newSection" name="newSection" type="text" bind:value={title} /> | |
<button on:click={addSection} > | |
Add Section | |
</button> | |
</div> | |
<FormBuilder bind:form on:sectionremoved={removeSection} /> |
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
<script> | |
import {createEventDispatcher } from 'svelte' | |
let dispatch = createEventDispatcher() | |
import { Form } from './Form' | |
export let form = new Form({id: "Test Form", sections: []}) | |
</script> | |
<h1>{form.id}</h1> | |
{#each form.sections as s, i (i)} | |
<div> | |
<span style="margin-right: 20px;">{s.title}</span> | |
<button on:click={()=>{dispatch('sectionremoved',{section: s, index: i})}}>Remove Section </button> | |
</div> | |
{/each} |
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
/** | |
* @param `arr` {Array<T>} The array of `el` elements | |
* @param {el} | |
* @returns {boolean} | |
* | |
* @description returns true if arr includes el otherwise false */ | |
export function exists<T>(arr: T[], el: T): boolean { | |
return arr.includes(el); | |
} | |
/** returns an immuatable array of T[] with the el added or removed based on the mutation */ | |
export function mutateList<T>(mutation: "ADD" | "REMOVE", arr: T[], el: T, index: number = arr.length): T[] { | |
if (mutation === "ADD") | |
return exists(arr, el) ? arr.slice() : [...arr.slice(0, index), el, ...arr.slice(index)]; | |
else return [...arr.slice(0, index), ...arr.slice(index + 1)]; | |
} | |
/** returns a new clone of an item... | |
* uses props to add different properties | |
* please add these props: id, name, value when cloning Fields | |
* and id, title when cloning rows | |
*/ | |
export function clone<T, IT>(target: T, props?: Partial<IT>): T { | |
return props ? Object.assign(target, props) : target | |
} | |
export interface IForm { | |
id: string; | |
sections: ISection[]; | |
classNames?: string; | |
store?: any; | |
validator?: any; | |
} | |
export default class Form implements IForm { | |
id: string = ""; | |
sections: Section[] = []; | |
classNames?: string = ""; | |
store?: any; | |
validator?: any; | |
constructor(props: IForm) { | |
Object.assign(this, props); | |
} | |
static from(props: IForm) { | |
return new Form(props); | |
} | |
addSection(section: ISection, insertAt?: number) { | |
this.sections = mutateList("ADD", this.sections, new Section(section), insertAt); | |
this.print() | |
return this; | |
} | |
removeSection(section: ISection, removeAt?: number) { | |
this.sections = mutateList("REMOVE", this.sections, new Section(section), removeAt); | |
this.print() | |
return this; | |
} | |
cloneSection(sectionAt: number, newProps?: Partial<ISection>, insertAt?: number) { | |
let newSection = clone(this.sections[sectionAt], newProps) | |
this.sections = mutateList("ADD", this.sections, newSection, insertAt); | |
this.print() | |
return this; | |
} | |
addRow(row: IRow, sectionAt: number, insertAt?: number) { | |
this.sections[sectionAt].addRow(row, insertAt) | |
this.print() | |
return this | |
} | |
removeRow(row: IRow, sectionAt: number, removeAt: number) { | |
this.sections[sectionAt].removeRow(row, removeAt) | |
this.print() | |
return this | |
} | |
cloneRow(sectionAt: number, rowAt: number, newProps: Partial<IRow>, insertAt?: number) { | |
let newRow = clone(this.sections[sectionAt].rows[rowAt], newProps) | |
this.addRow(newRow, sectionAt, insertAt) | |
this.print() | |
return this | |
} | |
addField(field: IField, sectionAt: number, rowAt: number, insertAt?: number) { | |
this.sections[sectionAt].rows[rowAt].addField(field, insertAt) | |
this.print() | |
return this | |
} | |
removeField(field: IField, sectionAt: number, rowAt: number, removeAt: number) { | |
this.sections[sectionAt].rows[rowAt].removeField(field, removeAt) | |
this.print() | |
return this | |
} | |
cloneField(sectionAt: number, rowAt: number, fieldAt: number, newProps: Partial<IField>, insertAt?: number) { | |
let newField = clone(this.sections[sectionAt].rows[rowAt].fields[fieldAt], newProps) | |
this.addField(newField, sectionAt, rowAt, insertAt) | |
this.print() | |
return this | |
} | |
print() { | |
let form = { ...this }; | |
console.log(form); | |
return form; | |
} | |
} | |
export interface ISection { | |
title?: string; | |
classNames?: string; | |
rows: IRow[]; | |
} | |
export class Section implements ISection { | |
title?: string = ""; | |
classNames?: string = ""; | |
rows: Row[] = []; | |
constructor(props: ISection) { | |
Object.assign(this, props); | |
} | |
addRow(row: IRow, insertAt?: number) { | |
this.rows = mutateList("ADD", this.rows, new Row(row), insertAt); | |
return this.rows; | |
} | |
removeRow(row: IRow, removeAt?: number) { | |
this.rows = mutateList("REMOVE", this.rows, new Row(row), removeAt); | |
return this.rows; | |
} | |
cloneRow(rowAt: number, newProps?: Partial<IRow>, insertAt?: number) { | |
let newRow = clone(this.rows[rowAt], newProps) | |
this.rows = mutateList("ADD", this.rows, newRow, insertAt); | |
return this.rows; | |
} | |
} | |
export interface IRow { | |
classNames?: string; | |
fields: IField[]; | |
rowStyle?: "block" | "inline"; | |
rowHeader?: string; | |
} | |
class Row { | |
classNames?: string = ""; | |
fields: Field[] = []; | |
rowStyle?: "block" | "inline" = "inline"; | |
rowHeader?: string = ""; | |
constructor(props: IRow) { | |
Object.assign(this, props); | |
} | |
addField(field: IField, insertAt?: number) { | |
this.fields = mutateList("ADD", this.fields, new Field(field), insertAt); | |
return this.fields; | |
} | |
removeField(field: IField, removeAt?: number) { | |
this.fields = mutateList("REMOVE", this.fields, new Field(field), removeAt); | |
return this.fields; | |
} | |
cloneField(fieldAt: number, newProps?: Partial<IField>, insertAt?: number) { | |
let newField = clone(this.fields[fieldAt], newProps) | |
this.fields = mutateList("ADD", this.fields, newField, insertAt); | |
return this.fields; | |
} | |
} | |
export type FieldType = | |
| "text" | |
| "email" | |
| "password" | |
| "tel" | |
| "date" | |
| "time" | |
| "number" | |
| "typeahead" | |
| "radio" | |
| "checkbox" | |
| "checklist" | |
| "chip" | |
| "chipinput" | |
| "select" | |
| "range-time" | |
| "range-date" | |
| "range-number" | |
| "repeatField"; | |
export interface IField { | |
id: string; | |
name: string; | |
value?: string; | |
label?: string; | |
type?: FieldType; | |
placeholder?: string; | |
colors?: any; | |
width?: string; | |
height?: string; | |
margin?: string; | |
min?: number; | |
max?: number; | |
step?: number; | |
items?: Item[] | string[]; | |
passwordChar?: string; | |
multiSelect?: boolean; | |
readonly?: boolean; | |
variant?: "outlined" | "material" | "default"; | |
validate?: string[]; | |
startIcon?: boolean; | |
endIcon?: boolean; | |
validators?: any[]; | |
} | |
export interface Item { | |
id: string; | |
name: string; | |
value: string; | |
label?: string; | |
} | |
export class Field implements IField { | |
id: string = ""; | |
name: string = ""; | |
value?: string = ""; | |
label?: string; | |
type?: FieldType; | |
placeholder?: string; | |
colors?: any; | |
width?: string; | |
height?: string; | |
margin?: string; | |
min?: number; | |
max?: number; | |
step?: number; | |
items?: Item[] | string[]; | |
passwordChar?: string; | |
multiSelect?: boolean; | |
readonly?: boolean; | |
variant?: "outlined" | "material" | "default"; | |
validate?: string[]; | |
startIcon?: boolean; | |
endIcon?: boolean; | |
validators?: any[]; | |
constructor(props: Partial<IField>) { | |
Object.assign(this, 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
function isEqual(obj1, obj2) { | |
let props1 = Object.getOwnPropertyNames(obj1); | |
let props2 = Object.getOwnPropertyNames(obj2); | |
if (props1.length != props2.length) { | |
return false; | |
} | |
for (let i = 0; i < props1.length; i++) { | |
let prop = props1[i]; | |
let bothAreObjects = typeof(obj1[prop]) === ‘object’ && typeof(obj2[prop]) === ‘object’; | |
if ((!bothAreObjects && (obj1[prop] !== obj2[prop])) | |
|| (bothAreObjects && !isEqual(obj1[prop], obj2[prop]))) { | |
return false; | |
} | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment