Merge pull request 'Added form preloading, and autofocus on the first input element' (#26) from BordedDev/snek:main into main
Reviewed-on: https://molodetz.nl/retoor/snek/pulls/26 Reviewed-by: retoor <retoor@noreply@molodetz.nl>
This commit is contained in:
		
						commit
						d9ac1813ba
					
				| @ -165,7 +165,13 @@ class GenericField extends HTMLElement { | |||||||
|     this[name] = value; |     this[name] = value; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   focus(options) { | ||||||
|  |     this.inputElement?.focus(options); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   updateAttributes() { |   updateAttributes() { | ||||||
|  |     const inputUpdate = this.inputElement != null; | ||||||
|  | 
 | ||||||
|     if (this.inputElement == null && this.field) { |     if (this.inputElement == null && this.field) { | ||||||
|       this.inputElement = document.createElement(this.field.tag); |       this.inputElement = document.createElement(this.field.tag); | ||||||
|       if (this.field.tag === 'button' && this.field.value === "submit") { |       if (this.field.tag === 'button' && this.field.value === "submit") { | ||||||
| @ -218,7 +224,9 @@ class GenericField extends HTMLElement { | |||||||
|     } |     } | ||||||
|     this.inputElement.setAttribute("tabindex", this.field.index); |     this.inputElement.setAttribute("tabindex", this.field.index); | ||||||
|     this.inputElement.classList.add(this.field.name); |     this.inputElement.classList.add(this.field.name); | ||||||
|  |     if (this.field.value != null || !inputUpdate) { | ||||||
|       this.value = this.field.value; |       this.value = this.field.value; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     let place_holder = this.field.place_holder ?? null; |     let place_holder = this.field.place_holder ?? null; | ||||||
|     if (this.field.required && place_holder) { |     if (this.field.required && place_holder) { | ||||||
| @ -281,6 +289,15 @@ class GenericForm extends HTMLElement { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   connectedCallback() { |   connectedCallback() { | ||||||
|  |     const preloadedForm = this.getAttribute('preloaded-structure'); | ||||||
|  |     if (preloadedForm) { | ||||||
|  |       try { | ||||||
|  |         const form = JSON.parse(preloadedForm); | ||||||
|  |         this.constructForm(form) | ||||||
|  |       } catch (error) { | ||||||
|  |         console.error(error, preloadedForm); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|     const url = this.getAttribute('url'); |     const url = this.getAttribute('url'); | ||||||
|     if (url) { |     if (url) { | ||||||
|       const fullUrl = url.startsWith("/") ? window.location.origin + url : new URL(window.location.origin + "/http-get"); |       const fullUrl = url.startsWith("/") ? window.location.origin + url : new URL(window.location.origin + "/http-get"); | ||||||
| @ -293,25 +310,35 @@ class GenericForm extends HTMLElement { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async loadForm(url) { |   async constructForm(formPayload) { | ||||||
|     try { |     try { | ||||||
|       const response = await fetch(url); |       this.form = formPayload; | ||||||
|       if (!response.ok) { |  | ||||||
|         throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); |  | ||||||
|       } |  | ||||||
|       this.form = await response.json(); |  | ||||||
| 
 | 
 | ||||||
|       let fields = Object.values(this.form.fields); |       let fields = Object.values(this.form.fields); | ||||||
| 
 | 
 | ||||||
|  |       let hasAutoFocus = Object.keys(this.fields).length !== 0; | ||||||
|  | 
 | ||||||
|       fields.sort((a, b) => a.index - b.index); |       fields.sort((a, b) => a.index - b.index); | ||||||
|       fields.forEach(field => { |       fields.forEach(field => { | ||||||
|         const fieldElement = document.createElement('generic-field'); |         const updatingField = field.name in this.fields | ||||||
|         this.fields[field.name] = fieldElement; | 
 | ||||||
|  |         this.fields[field.name] ??= document.createElement('generic-field'); | ||||||
|  | 
 | ||||||
|  |         const fieldElement = this.fields[field.name]; | ||||||
|  | 
 | ||||||
|         fieldElement.setAttribute("form", this); |         fieldElement.setAttribute("form", this); | ||||||
|         fieldElement.setAttribute("field", field); |         fieldElement.setAttribute("field", field); | ||||||
|         this.container.appendChild(fieldElement); | 
 | ||||||
|         fieldElement.updateAttributes(); |         fieldElement.updateAttributes(); | ||||||
| 
 | 
 | ||||||
|  |         if (!updatingField) { | ||||||
|  |           this.container.appendChild(fieldElement); | ||||||
|  | 
 | ||||||
|  |           if (!hasAutoFocus && field.tag === "input") { | ||||||
|  |             fieldElement.focus(); | ||||||
|  |             hasAutoFocus = true; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|           fieldElement.addEventListener("change", (e) => { |           fieldElement.addEventListener("change", (e) => { | ||||||
|             this.form.fields[e.detail.name].value = e.detail.value; |             this.form.fields[e.detail.name].value = e.detail.value; | ||||||
|           }); |           }); | ||||||
| @ -337,8 +364,21 @@ class GenericForm extends HTMLElement { | |||||||
|               } |               } | ||||||
|             } |             } | ||||||
|           }) |           }) | ||||||
|  |         } | ||||||
|       }); |       }); | ||||||
|  |     } catch (error) { | ||||||
|  |       this.container.textContent = `Error: ${error.message}`; | ||||||
|  |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|  |   async loadForm(url) { | ||||||
|  |     try { | ||||||
|  |       const response = await fetch(url); | ||||||
|  |       if (!response.ok) { | ||||||
|  |         throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       await this.constructForm(await response.json()); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       this.container.textContent = `Error: ${error.message}`; |       this.container.textContent = `Error: ${error.message}`; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -11,6 +11,6 @@ | |||||||
| {% block main %} | {% block main %} | ||||||
|     <div class="back-form"> |     <div class="back-form"> | ||||||
|         <fancy-button url="/back" text="Back" size="auto"></fancy-button> |         <fancy-button url="/back" text="Back" size="auto"></fancy-button> | ||||||
|         <generic-form class="center" url="/login.json"></generic-form> |         <generic-form class="center" url="/login.json" preloaded-structure='{{ form|tojson|safe }}'></generic-form> | ||||||
|     </div> |     </div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  | |||||||
| @ -12,6 +12,6 @@ | |||||||
|     <div class="back-form"> |     <div class="back-form"> | ||||||
|         <fancy-button url="/back" text="Back" size="auto"></fancy-button> |         <fancy-button url="/back" text="Back" size="auto"></fancy-button> | ||||||
| 
 | 
 | ||||||
|         <generic-form class="center" url="/register.json"></generic-form> |         <generic-form class="center" url="/register.json" preloaded-structure='{{ form|tojson|safe }}'></generic-form> | ||||||
|     </div> |     </div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
| @ -18,7 +18,7 @@ class LoginView(BaseFormView): | |||||||
|             return web.HTTPFound("/web.html") |             return web.HTTPFound("/web.html") | ||||||
|         if self.request.path.endswith(".json"): |         if self.request.path.endswith(".json"): | ||||||
|             return await super().get() |             return await super().get() | ||||||
|         return await self.render_template("login.html") |         return await self.render_template("login.html", {"form": await self.form(app=self.app).to_json()}) | ||||||
| 
 | 
 | ||||||
|     async def submit(self, form): |     async def submit(self, form): | ||||||
|         if await form.is_valid: |         if await form.is_valid: | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ class RegisterView(BaseFormView): | |||||||
|             return web.HTTPFound("/web.html") |             return web.HTTPFound("/web.html") | ||||||
|         if self.request.path.endswith(".json"): |         if self.request.path.endswith(".json"): | ||||||
|             return await super().get() |             return await super().get() | ||||||
|         return await self.render_template("register.html") |         return await self.render_template("register.html", {"form": await self.form(app=self.app).to_json()}) | ||||||
| 
 | 
 | ||||||
|     async def submit(self, form): |     async def submit(self, form): | ||||||
|         result = await self.app.services.user.register( |         result = await self.app.services.user.register( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user