|
|
|
class GenericField extends HTMLElement {
|
|
form = null
|
|
field = null
|
|
inputElement = null
|
|
footerElement = null
|
|
action = null
|
|
container = null
|
|
styleElement = null
|
|
name = null
|
|
get value() {
|
|
return this.inputElement.value
|
|
}
|
|
get type() {
|
|
|
|
return this.field.tag
|
|
}
|
|
set value(val) {
|
|
val = val == null ? '' : val
|
|
this.inputElement.value = val
|
|
this.inputElement.setAttribute("value", val)
|
|
}
|
|
setInvalid(){
|
|
this.inputElement.classList.add("error")
|
|
this.inputElement.classList.remove("valid")
|
|
}
|
|
setErrors(errors){
|
|
if(errors.length)
|
|
this.inputElement.setAttribute("title", errors[0])
|
|
else
|
|
this.inputElement.setAttribute("title","")
|
|
}
|
|
setValid(){
|
|
this.inputElement.classList.remove("error")
|
|
this.inputElement.classList.add("valid")
|
|
}
|
|
constructor() {
|
|
super()
|
|
this.attachShadow({mode:'open'})
|
|
this.container = document.createElement('div')
|
|
this.styleElement = document.createElement('style')
|
|
this.styleElement.innerHTML = `
|
|
|
|
h1 {
|
|
font-size: 2em;
|
|
color: #f05a28;
|
|
margin-bottom: 20px;
|
|
margin-top: 0px;
|
|
}
|
|
|
|
input {
|
|
width: 90%;
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border: 1px solid #333;
|
|
border-radius: 5px;
|
|
background-color: #1a1a1a;
|
|
color: #e6e6e6;
|
|
font-size: 1em;
|
|
}
|
|
|
|
button {
|
|
width: 50%;
|
|
padding: 10px;
|
|
background-color: #f05a28;
|
|
border: none;
|
|
float: right;
|
|
margin-top: 10px;
|
|
margin-left: 10px;
|
|
margin-right: 10px;
|
|
border-radius: 5px;
|
|
color: white;
|
|
font-size: 1em;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
clear: both;
|
|
}
|
|
|
|
button:hover {
|
|
background-color: #e04924;
|
|
}
|
|
|
|
a {
|
|
color: #f05a28;
|
|
text-decoration: none;
|
|
display: block;
|
|
margin-top: 15px;
|
|
font-size: 0.9em;
|
|
transition: color 0.3s;
|
|
}
|
|
|
|
a:hover {
|
|
color: #e04924;
|
|
}
|
|
.valid {
|
|
border: 1px solid green;
|
|
color:green;
|
|
font-size: 0.9em;
|
|
margin-top: 5px;
|
|
}
|
|
.error {
|
|
border: 3px solid red;
|
|
color: #d8000c;
|
|
font-size: 0.9em;
|
|
margin-top: 5px;
|
|
}
|
|
@media (max-width: 500px) {
|
|
input {
|
|
width: 90%;
|
|
}
|
|
}
|
|
|
|
`
|
|
this.container.appendChild(this.styleElement)
|
|
|
|
this.shadowRoot.appendChild(this.container)
|
|
}
|
|
connectedCallback(){
|
|
|
|
this.updateAttributes()
|
|
|
|
}
|
|
setAttribute(name,value){
|
|
this[name] = value
|
|
}
|
|
updateAttributes(){
|
|
if(this.inputElement == null && this.field){
|
|
this.inputElement = document.createElement(this.field.tag)
|
|
if(this.field.tag == 'button'){
|
|
if(this.field.value == "submit"){
|
|
|
|
|
|
}
|
|
this.action = this.field.value
|
|
}
|
|
this.inputElement.name = this.field.name
|
|
this.name = this.inputElement.name
|
|
const me = this
|
|
this.inputElement.addEventListener("keyup",(e)=>{
|
|
if(e.key == 'Enter'){
|
|
me.dispatchEvent(new Event("submit"))
|
|
}else if(me.field.value != e.target.value)
|
|
{
|
|
const event = new CustomEvent("change", {detail:me,bubbles:true})
|
|
me.dispatchEvent(event)
|
|
}
|
|
})
|
|
this.inputElement.addEventListener("click",(e)=>{
|
|
const event = new CustomEvent("click",{detail:me,bubbles:true})
|
|
me.dispatchEvent(event)
|
|
})
|
|
this.container.appendChild(this.inputElement)
|
|
|
|
}
|
|
if(!this.field){
|
|
return
|
|
}
|
|
this.inputElement.setAttribute("type",this.field.type == null ? 'input' : this.field.type)
|
|
this.inputElement.setAttribute("name",this.field.name == null ? '' : this.field.name)
|
|
|
|
if(this.field.text != null){
|
|
this.inputElement.innerText = this.field.text
|
|
}
|
|
if(this.field.html != null){
|
|
this.inputElement.innerHTML = this.field.html
|
|
}
|
|
if(this.field.class_name){
|
|
this.inputElement.classList.add(this.field.class_name)
|
|
}
|
|
this.inputElement.setAttribute("tabindex", this.field.index)
|
|
this.inputElement.classList.add(this.field.name)
|
|
this.value = this.field.value
|
|
let place_holder = null
|
|
if(this.field.place_holder)
|
|
place_holder = this.field.place_holder
|
|
if(this.field.required && place_holder){
|
|
place_holder = place_holder
|
|
}
|
|
if(place_holder)
|
|
this.field.place_holder = "* " + place_holder
|
|
this.inputElement.setAttribute("placeholder",place_holder)
|
|
if(this.field.required)
|
|
this.inputElement.setAttribute("required","required")
|
|
else
|
|
this.inputElement.removeAttribute("required")
|
|
if(!this.footerElement){
|
|
this.footerElement = document.createElement('div')
|
|
this.footerElement.style.clear = 'both'
|
|
this.container.appendChild(this.footerElement)
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define('generic-field', GenericField);
|
|
|
|
class GenericForm extends HTMLElement {
|
|
fields = {}
|
|
form = {}
|
|
constructor() {
|
|
|
|
|
|
super();
|
|
this.attachShadow({ mode: 'open' });
|
|
this.styleElement = document.createElement("style")
|
|
this.styleElement.innerHTML = `
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
width:90%
|
|
|
|
}
|
|
|
|
div {
|
|
|
|
background-color: #0f0f0f;
|
|
border-radius: 10px;
|
|
padding: 30px;
|
|
width: 400px;
|
|
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
|
|
text-align: center;
|
|
|
|
}
|
|
@media (max-width: 500px) {
|
|
width:100%;
|
|
height:100%;
|
|
form {
|
|
height:100%;
|
|
width: 100%;
|
|
width: 80%;
|
|
}
|
|
}`
|
|
|
|
this.container = document.createElement('div');
|
|
this.container.appendChild(this.styleElement)
|
|
this.container.classList.add("generic-form-container")
|
|
this.shadowRoot.appendChild(this.container);
|
|
}
|
|
|
|
connectedCallback() {
|
|
const url = this.getAttribute('url');
|
|
if (url) {
|
|
const fullUrl = url.startsWith("/") ? window.location.origin + url : new URL(window.location.origin + "/http-get")
|
|
if(!url.startsWith("/"))
|
|
fullUrl.searchParams.set('url', url)
|
|
this.loadForm(fullUrl.toString());
|
|
} else {
|
|
this.container.textContent = "No URL provided!";
|
|
}
|
|
}
|
|
|
|
async loadForm(url) {
|
|
const me = this
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
|
|
}
|
|
me.form = await response.json();
|
|
|
|
let fields = Object.values(me.form.fields)
|
|
|
|
fields = fields.sort((a,b)=>{
|
|
console.info(a.index,b.index)
|
|
return a.index - b.index
|
|
})
|
|
fields.forEach(field=>{
|
|
const fieldElement = document.createElement('generic-field')
|
|
me.fields[field.name] = fieldElement
|
|
fieldElement.setAttribute("form", me)
|
|
fieldElement.setAttribute("field", field)
|
|
me.container.appendChild(fieldElement)
|
|
fieldElement.updateAttributes()
|
|
fieldElement.addEventListener("change",(e)=>{
|
|
me.form.fields[e.detail.name].value = e.detail.value
|
|
})
|
|
fieldElement.addEventListener("click",async (e)=>{
|
|
if(e.detail.type == "button"){
|
|
if(e.detail.value == "submit")
|
|
{
|
|
const isValid = await me.validate()
|
|
if(isValid){
|
|
const isProcessed = await me.submit()
|
|
console.info({processed:isProcessed})
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
} catch (error) {
|
|
this.container.textContent = `Error: ${error.message}`;
|
|
}
|
|
}
|
|
async validate(){
|
|
const url = this.getAttribute("url")
|
|
const me = this
|
|
let response = await fetch(url,{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({"action":"validate", "form":me.form})
|
|
});
|
|
|
|
|
|
const form = await response.json()
|
|
Object.values(form.fields).forEach(field=>{
|
|
if(!me.form.fields[field.name])
|
|
return
|
|
me.form.fields[field.name].is_valid = field.is_valid
|
|
if(!field.is_valid){
|
|
me.fields[field.name].setInvalid()
|
|
me.fields[field.name].setErrors(field.errors)
|
|
console.info(field.name,"is invalid")
|
|
}else{
|
|
me.fields[field.name].setValid()
|
|
}
|
|
me.fields[field.name].setAttribute("field",field)
|
|
me.fields[field.name].updateAttributes()
|
|
})
|
|
Object.values(form.fields).forEach(field=>{
|
|
console.info(field.errors)
|
|
me.fields[field.name].setErrors(field.errors)
|
|
})
|
|
console.info({XX:form})
|
|
return form['is_valid']
|
|
}
|
|
async submit(){
|
|
const me = this
|
|
const url = me.getAttribute("url")
|
|
const response = await fetch(url,{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({"action":"submit", "form":me.form})
|
|
});
|
|
return await response.json()
|
|
|
|
}
|
|
|
|
}
|
|
customElements.define('generic-form', GenericForm); |