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);