Merge pull request 'Added form preloading, and autofocus on the first input element' (#26) from BordedDev/snek:main into main
Reviewed-on: #26 Reviewed-by: retoor <retoor@noreply@molodetz.nl>
This commit is contained in:
commit
d9ac1813ba
@ -98,58 +98,58 @@ class GenericField extends HTMLElement {
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
background-color: #e04924;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #f05a28;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-top: 15px;
|
||||
font-size: 0.9em;
|
||||
transition: color 0.3s;
|
||||
color: #f05a28;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-top: 15px;
|
||||
font-size: 0.9em;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #e04924;
|
||||
color: #e04924;
|
||||
}
|
||||
|
||||
.valid {
|
||||
border: 1px solid green;
|
||||
color: green;
|
||||
font-size: 0.9em;
|
||||
margin-top: 5px;
|
||||
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;
|
||||
color: #d8000c;
|
||||
font-size: 0.9em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
input {
|
||||
width: 90%;
|
||||
}
|
||||
input {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
this.container.appendChild(this.styleElement);
|
||||
@ -165,7 +165,13 @@ class GenericField extends HTMLElement {
|
||||
this[name] = value;
|
||||
}
|
||||
|
||||
focus(options) {
|
||||
this.inputElement?.focus(options);
|
||||
}
|
||||
|
||||
updateAttributes() {
|
||||
const inputUpdate = this.inputElement != null;
|
||||
|
||||
if (this.inputElement == null && this.field) {
|
||||
this.inputElement = document.createElement(this.field.tag);
|
||||
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.classList.add(this.field.name);
|
||||
this.value = this.field.value;
|
||||
if (this.field.value != null || !inputUpdate) {
|
||||
this.value = this.field.value;
|
||||
}
|
||||
|
||||
let place_holder = this.field.place_holder ?? null;
|
||||
if (this.field.required && place_holder) {
|
||||
@ -281,6 +289,15 @@ class GenericForm extends HTMLElement {
|
||||
}
|
||||
|
||||
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');
|
||||
if (url) {
|
||||
const fullUrl = url.startsWith("/") ? window.location.origin + url : new URL(window.location.origin + "/http-get");
|
||||
@ -293,31 +310,52 @@ class GenericForm extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
async loadForm(url) {
|
||||
async constructForm(formPayload) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
this.form = await response.json();
|
||||
this.form = formPayload;
|
||||
|
||||
let fields = Object.values(this.form.fields);
|
||||
|
||||
let hasAutoFocus = Object.keys(this.fields).length !== 0;
|
||||
|
||||
fields.sort((a, b) => a.index - b.index);
|
||||
fields.forEach(field => {
|
||||
const fieldElement = document.createElement('generic-field');
|
||||
this.fields[field.name] = fieldElement;
|
||||
const updatingField = field.name in this.fields
|
||||
|
||||
this.fields[field.name] ??= document.createElement('generic-field');
|
||||
|
||||
const fieldElement = this.fields[field.name];
|
||||
|
||||
fieldElement.setAttribute("form", this);
|
||||
fieldElement.setAttribute("field", field);
|
||||
this.container.appendChild(fieldElement);
|
||||
|
||||
fieldElement.updateAttributes();
|
||||
|
||||
fieldElement.addEventListener("change", (e) => {
|
||||
this.form.fields[e.detail.name].value = e.detail.value;
|
||||
});
|
||||
if (!updatingField) {
|
||||
this.container.appendChild(fieldElement);
|
||||
|
||||
fieldElement.addEventListener("click", async (e) => {
|
||||
if (e.detail.type === "button" && e.detail.value === "submit") {
|
||||
if (!hasAutoFocus && field.tag === "input") {
|
||||
fieldElement.focus();
|
||||
hasAutoFocus = true;
|
||||
}
|
||||
|
||||
fieldElement.addEventListener("change", (e) => {
|
||||
this.form.fields[e.detail.name].value = e.detail.value;
|
||||
});
|
||||
|
||||
fieldElement.addEventListener("click", async (e) => {
|
||||
if (e.detail.type === "button" && e.detail.value === "submit") {
|
||||
const isValid = await this.validate();
|
||||
if (isValid) {
|
||||
const saveResult = await this.submit();
|
||||
if (saveResult.redirect_url) {
|
||||
window.location.pathname = saveResult.redirect_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fieldElement.addEventListener("submit", async (e) => {
|
||||
const isValid = await this.validate();
|
||||
if (isValid) {
|
||||
const saveResult = await this.submit();
|
||||
@ -325,20 +363,22 @@ class GenericForm extends HTMLElement {
|
||||
window.location.pathname = saveResult.redirect_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fieldElement.addEventListener("submit", async (e) => {
|
||||
const isValid = await this.validate();
|
||||
if (isValid) {
|
||||
const saveResult = await this.submit();
|
||||
if (saveResult.redirect_url) {
|
||||
window.location.pathname = saveResult.redirect_url;
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
} 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) {
|
||||
this.container.textContent = `Error: ${error.message}`;
|
||||
}
|
||||
|
@ -11,6 +11,6 @@
|
||||
{% block main %}
|
||||
<div class="back-form">
|
||||
<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>
|
||||
{% endblock %}
|
||||
|
@ -12,6 +12,6 @@
|
||||
<div class="back-form">
|
||||
<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>
|
||||
{% endblock %}
|
@ -18,7 +18,7 @@ class LoginView(BaseFormView):
|
||||
return web.HTTPFound("/web.html")
|
||||
if self.request.path.endswith(".json"):
|
||||
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):
|
||||
if await form.is_valid:
|
||||
|
@ -18,7 +18,7 @@ class RegisterView(BaseFormView):
|
||||
return web.HTTPFound("/web.html")
|
||||
if self.request.path.endswith(".json"):
|
||||
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):
|
||||
result = await self.app.services.user.register(
|
||||
|
Loading…
Reference in New Issue
Block a user