Make underlying form a slot.
This commit is contained in:
@@ -1,23 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* This is the object that ties a group of inputs to a name.
|
||||||
|
* It assumes that every input in the group is of the same type.
|
||||||
|
*/
|
||||||
|
class Field {
|
||||||
|
|
||||||
|
private _type: string;
|
||||||
|
|
||||||
|
constructor(private name: string, private _inputs: FieldElement[]) {
|
||||||
|
if (this._inputs.length === 0) {
|
||||||
|
throw new Error("[Declaform] Error: cannot construct Field with empty inputs array.")
|
||||||
|
}
|
||||||
|
this._type = this._inputs[0]!.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
switch(this._type) {
|
||||||
|
case 'checkbox':
|
||||||
|
return (this._inputs as HTMLInputElement[]).filter(x => x.checked).map(x => x.value);
|
||||||
|
case 'radio':
|
||||||
|
return (this._inputs as HTMLInputElement[]).find(x => x.checked)?.value;
|
||||||
|
default:
|
||||||
|
case 'date':
|
||||||
|
case 'datatime-local':
|
||||||
|
case 'email':
|
||||||
|
case 'number':
|
||||||
|
case 'password':
|
||||||
|
case 'select':
|
||||||
|
case 'text':
|
||||||
|
// can assert this._inputs[0]! because we throw Error in constructor if _inputs.length = 0
|
||||||
|
return (this._inputs.length > 1) ? this._inputs.map(x => x.value) : this._inputs[0]!.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(x: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Declaform extends HTMLElement {
|
export class Declaform extends HTMLElement {
|
||||||
|
|
||||||
static observedAttributes = [
|
static observedAttributes = [
|
||||||
'customInputs', // Lets user specify additional inputs to be considered "fields" by the form. Example: <object-list>
|
'customInputs', // Lets user specify additional inputs to be considered "fields" by the form. Example: <object-list>
|
||||||
|
'src', // defines api endpoint used to hydrate this object
|
||||||
|
'action', // defines api endpoint used to submit this object to
|
||||||
|
'method', // defines http method to use on "action" endpoint
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/* The character that gets used to join multiple field values */
|
||||||
|
static joining_char = ',';
|
||||||
|
|
||||||
private _fields: Field[];
|
private _fields: Field[];
|
||||||
private _form: HTMLFormElement;
|
private _form: HTMLFormElement | null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.attachInternals();
|
this.attachInternals();
|
||||||
this.attachShadow({ mode: 'open' });
|
this.attachShadow({ mode: 'open' });
|
||||||
this.shadowRoot!.innerHTML = '<form><slot></slot></form>';
|
this.shadowRoot!.innerHTML = '<slot name="form"></slot>';
|
||||||
this._fields = [];
|
this._fields = [];
|
||||||
this._form = this.shadowRoot!.querySelector('form')!;
|
this._form = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
|
const formSlot = this.shadowRoot?.querySelector('slot[name="form"]') as HTMLSlotElement;
|
||||||
|
if (!formSlot) {
|
||||||
|
throw new Error('[Declaform] Error: "form" slot is unassigned.')
|
||||||
|
}
|
||||||
|
if (formSlot.assignedElements()[0]?.tagName !== 'FORM') {
|
||||||
|
throw new Error('[Declaform] Error: "form" slot must be assigned to a <form>.')
|
||||||
|
}
|
||||||
|
this._form = formSlot.assignedElements()[0] as HTMLFormElement;
|
||||||
|
|
||||||
const customInputs = this.getAttribute('customInputs')?.split(',').map(x => x.trim()) ?? [];
|
const customInputs = this.getAttribute('customInputs')?.split(',').map(x => x.trim()) ?? [];
|
||||||
const selectors = ['input', 'select'].concat(customInputs).map(x => `${x}[name]`).join(',');
|
const selectors = ['input', 'select'].concat(customInputs).map(x => `${x}[name]`).join(',');
|
||||||
const inputs = Array.from(this.querySelectorAll(selectors));
|
const inputs = Array.from(this.querySelectorAll(selectors));
|
||||||
@@ -73,42 +130,4 @@ function isFieldElement(x: unknown): x is FieldElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the object that ties a group of inputs to a name.
|
|
||||||
* It assumes that every input in the group is of the same type.
|
|
||||||
*/
|
|
||||||
class Field {
|
|
||||||
|
|
||||||
/* The character that gets used to join multiple field values */
|
|
||||||
static joining_char = ',';
|
|
||||||
|
|
||||||
private _type: string;
|
|
||||||
|
|
||||||
constructor(private name: string, private _inputs: FieldElement[]) {
|
|
||||||
if (this._inputs.length === 0) {
|
|
||||||
throw new Error("[Declaform] Error: cannot construct Field with empty inputs array.")
|
|
||||||
}
|
|
||||||
this._type = this._inputs[0]!.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
switch(this._type) {
|
|
||||||
default:
|
|
||||||
case 'date':
|
|
||||||
case 'datatime-local':
|
|
||||||
case 'email':
|
|
||||||
case 'number':
|
|
||||||
case 'text':
|
|
||||||
return this._inputs.map(x => x.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(x: any) {
|
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this._type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user