Refactored alpinejs in forms for better maintainability

This commit is contained in:
2025-02-16 11:26:57 +11:00
parent 07453b0f02
commit fa64b05415
6 changed files with 120 additions and 86 deletions

View File

@@ -15,6 +15,41 @@ templ ChangeBio(err string, bio string) {
class="w-[90%] mx-auto mt-5"
x-data={ templ.JSFuncCall("bioComponent", bio, user.Bio, err).CallInline }
>
<script>
function bioComponent(newBio, oldBio, err) {
return {
bio: newBio,
initialBio: oldBio,
err: err,
bioLenText: '',
updateTextArea() {
this.$nextTick(() => {
if (this.$refs.bio) {
this.$refs.bio.style.height = 'auto';
this.$refs.bio.style.height = `
${this.$refs.bio.scrollHeight+20}px`;
};
this.bioLenText = `${this.bio.length}/128`;
});
},
resetBio() {
this.bio = this.initialBio;
this.err = "",
this.updateTextArea();
},
init() {
this.$nextTick(() => {
// this timeout makes sure the textarea resizes on
// page render correctly. seems 20ms is the sweet
// spot between a noticable delay and not working
setTimeout(() => {
this.updateTextArea();
}, 20);
});
}
};
}
</script>
<div
class="flex flex-col"
>
@@ -78,40 +113,5 @@ templ ChangeBio(err string, bio string) {
x-show="err"
x-text="err"
></p>
<script>
function bioComponent(newBio, oldBio, err) {
return {
bio: newBio,
initialBio: oldBio,
err: err,
bioLenText: '',
updateTextArea() {
this.$nextTick(() => {
if (this.$refs.bio) {
this.$refs.bio.style.height = 'auto';
this.$refs.bio.style.height = `
${this.$refs.bio.scrollHeight+20}px`;
};
this.bioLenText = `${this.bio.length}/128`;
});
},
resetBio() {
this.bio = this.initialBio;
this.err = "",
this.updateTextArea();
},
init() {
this.$nextTick(() => {
// this timeout makes sure the textarea resizes on
// page render correctly. seems 20ms is the sweet
// spot between a noticable delay and not working
setTimeout(() => {
this.updateTextArea();
}, 20);
});
}
};
}
</script>
</form>
}

View File

@@ -13,11 +13,25 @@ templ ChangeUsername(err string, username string) {
hx-post="/change-username"
hx-swap="outerHTML"
class="w-[90%] mx-auto mt-5"
x-data={ "{ err: '" + err + "'}" }
x-data={ templ.JSFuncCall(
"usernameComponent", username, user.Username, err,
).CallInline }
>
<script>
function usernameComponent(newUsername, oldUsername, err) {
return {
username: newUsername,
initialUsername: oldUsername,
err: err,
resetUsername() {
this.username = this.initialUsername;
this.err = "";
},
};
}
</script>
<div
class="flex flex-col sm:flex-row"
x-data={ "{username: '" + username + "'}" }
>
<div
class="flex flex-col sm:flex-row sm:items-center relative"
@@ -65,7 +79,7 @@ templ ChangeUsername(err string, username string) {
class="rounded-lg bg-blue py-1 px-2 text-mantle sm:ml-2
hover:cursor-pointer hover:bg-blue/75 transition"
x-cloak
x-show={ "username !=='" + user.Username + "'" }
x-show="username !== initialUsername"
x-transition.opacity.duration.500ms
>
Update
@@ -76,9 +90,9 @@ templ ChangeUsername(err string, username string) {
type="button"
href="#"
x-cloak
x-show={ "username !=='" + user.Username + "'" }
x-show="username !== initialUsername"
x-transition.opacity.duration.500ms
@click={ "username='" + user.Username + "';err=''" }
@click="resetUsername()"
>
Cancel
</button>

View File

@@ -1,22 +1,27 @@
package form
import "fmt"
templ ConfirmPassword(err string) {
{{
xdata := fmt.Sprintf(
"{ errMsg: '%s'}",
err,
)
}}
<form
hx-post="/reauthenticate"
x-data="{ submitted: false, buttontext: 'Confirm' }"
x-data={ templ.JSFuncCall(
"confirmPassData", err,
).CallInline }
x-on:htmx:xhr:loadstart="submitted=true;buttontext='Loading...'"
>
<script>
function usernameComponent(err) {
return {
submitted: false,
buttontext: 'Confirm',
err: err,
reset() {
this.err = "";
},
};
}
</script>
<div
class="grid gap-y-4"
x-data={ xdata }
>
<div class="mt-5">
<div class="relative">
@@ -30,6 +35,7 @@ templ ConfirmPassword(err string) {
placeholder="Confirm password"
required
aria-describedby="password-error"
@input="reset()"
/>
<div
class="absolute inset-y-0 end-0
@@ -52,7 +58,8 @@ templ ConfirmPassword(err string) {
1 0 1 0 0 2 1 1 0 0 0 0-2z"
></path>
</svg>
</div>
</div> - change username
- confirm password
</div>
<p
class="text-center text-xs text-red mt-2"

View File

@@ -1,31 +1,34 @@
package form
import "fmt"
// Login Form. If loginError is not an empty string, it will display the
// contents of loginError to the user.
// If loginError is "Username or password incorrect" it will also show
// error icons on the username and password field
templ LoginForm(loginError string) {
{{
errCreds := "false"
if loginError == "Username or password incorrect" {
errCreds = "true"
}
xdata := fmt.Sprintf(
"{credentialError: %s, errorMessage: '%s'}",
errCreds,
loginError,
)
}}
{{ credErr := "Username or password incorrect" }}
<form
hx-post="/login"
x-data="{ submitted: false, buttontext: 'Login' }"
x-data={ templ.JSFuncCall(
"loginFormData", loginError, credErr,
).CallInline }
x-on:htmx:xhr:loadstart="submitted=true;buttontext='Loading...'"
>
<script>
function loginFormData(err, credError) {
return {
submitted: false,
buttontext: 'Login',
errorMessage: err,
credentialError: err === credError ? true : false,
resetErr() {
this.errorMessage = "";
this.credentialError = false;
},
};
}
</script>
<div
class="grid gap-y-4"
x-data={ xdata }
>
<!-- Form Group -->
<div>
@@ -44,6 +47,7 @@ templ LoginForm(loginError string) {
disabled:pointer-events-none"
required
aria-describedby="username-error"
@input="resetErr()"
/>
<div
class="absolute inset-y-0 end-0
@@ -93,6 +97,7 @@ templ LoginForm(loginError string) {
disabled:opacity-50 disabled:pointer-events-none"
required
aria-describedby="password-error"
@input="resetErr()"
/>
<div
class="absolute inset-y-0 end-0

View File

@@ -1,36 +1,41 @@
package form
import "fmt"
// Login Form. If loginError is not an empty string, it will display the
// contents of loginError to the user.
templ RegisterForm(registerError string) {
{{
errUsername := "false"
errPasswords := "false"
if registerError == "Username is taken" {
errUsername = "true"
} else if registerError == "Passwords do not match" ||
registerError == "Password exceeds maximum length of 72 bytes" {
errPasswords = "true"
usernameErr := "Username is taken"
passErrs := []string{
"Password exceeds maximum length of 72 bytes",
"Passwords do not match",
}
xdata := fmt.Sprintf(
"{errUsername: %s, errPasswords: %s, errorMessage: '%s'}",
errUsername,
errPasswords,
registerError,
)
}}
<form
hx-post="/register"
x-data="{ submitted: false, buttontext: 'Login' }"
x-data={ templ.JSFuncCall(
"registerFormData", registerError, usernameErr, passErrs,
).CallInline }
x-on:htmx:xhr:loadstart="submitted=true;buttontext='Loading...'"
>
<script>
function registerFormData(err, usernameErr, passErrs) {
return {
submitted: false,
buttontext: 'Register',
errorMessage: err,
errUsername: err === usernameErr ? true : false,
errPasswords: passErrs.includes(err) ? true : false,
resetErr() {
this.errorMessage = "";
this.errUsername = false;
this.errPasswords = false;
},
};
}
</script>
<div
class="grid gap-y-4"
x-data={ xdata }
>
<!-- Form Group -->
<div>
<label
for="email"
@@ -47,6 +52,7 @@ templ RegisterForm(registerError string) {
disabled:pointer-events-none"
required
aria-describedby="username-error"
@input="resetErr()"
/>
<div
class="absolute inset-y-0 end-0
@@ -96,6 +102,7 @@ templ RegisterForm(registerError string) {
disabled:opacity-50 disabled:pointer-events-none"
required
aria-describedby="password-error"
@input="resetErr()"
/>
<div
class="absolute inset-y-0 end-0
@@ -138,6 +145,7 @@ templ RegisterForm(registerError string) {
disabled:opacity-50 disabled:pointer-events-none"
required
aria-describedby="confirm-password-error"
@input="resetErr()"
/>
<div
class="absolute inset-y-0 end-0

View File

@@ -43,7 +43,7 @@ templ Global() {
// htmx.logAll();
</script>
<script>
const popups = {
const bodyData = {
showError: false,
showConfirmPasswordModal: false,
handleHtmxBeforeOnLoad(event) {
@@ -78,7 +78,7 @@ templ Global() {
</head>
<body
class="bg-base text-text ubuntu-mono-regular overflow-x-hidden"
x-data="popups"
x-data="bodyData"
x-on:htmx:error="handleHtmxError($event)"
x-on:htmx:before-on-load="handleHtmxBeforeOnLoad($event)"
>