Skip to content

Commit

Permalink
Add a continue button to the OTP input
Browse files Browse the repository at this point in the history
ref DEV-1479
  • Loading branch information
louischan-oursky committed Jul 10, 2024
2 parents 8065416 + c614f5e commit ac1d9bf
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 58 deletions.
25 changes: 10 additions & 15 deletions authui/src/authflowv2/components/otp-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
--otp-input-error-message__font-weight: var(
--typography-body-medium__font-weight
);

--otp-input-resend-button-margin-top: 1rem;
}

:root.dark {
Expand All @@ -53,17 +51,18 @@
@apply relative;
@apply w-full;

height: var(--otp-input__digit-height);

input {
@apply w-full;
@apply h-full;
@apply outline-none;
@apply selection:bg-white/0;
@apply caret-white/0;
/* iOS treats element with `opacity: 0` as not interactable, set text to transparent instead */
@apply text-transparent;
background-color: var(--otp-input__bg-color);

&.with-js {
height: var(--otp-input__digit-height);
@apply outline-none;
@apply selection:bg-white/0;
@apply caret-white/0;
/* iOS treats element with `opacity: 0` as not interactable, set text to transparent instead */
@apply text-transparent;
background-color: var(--otp-input__bg-color);
}
}
}

Expand Down Expand Up @@ -123,10 +122,6 @@
font-weight: var(--otp-input-error-message__font-weight);
}

.otp-input__resend-btn {
margin-top: var(--otp-input-resend-button-margin-top);
}

.otp-input {
&.otp-input--error {
.otp-input__digit {
Expand Down
1 change: 1 addition & 0 deletions authui/src/authflowv2/components/widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
max-width: var(--widget__max-width);
}

/* In __html_head.html, there is a <noscript> element to revert this visibility. */
:root:not([alignment-content]) .widget {
/* This rule is to prevent ui shifting before the attribute added */
@apply invisible;
Expand Down
17 changes: 12 additions & 5 deletions authui/src/authflowv2/otpInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ export class OtpInputController extends Controller {
};

connect(): void {
this.inputTarget.classList.remove("input");
this.inputTarget.classList.add("with-js");
this.inputTarget.addEventListener("input", this.handleInput);
this.inputTarget.addEventListener("paste", this.handlePaste);
this.inputTarget.addEventListener("focus", this.handleFocus);
this.inputTarget.addEventListener("blur", this.handleBlur);
this.submitTarget.disabled = true;
// element.selectionchange is NOT the same as document.selectionchange
// element.selectionchange is an experimental technology.
window.document.addEventListener(
Expand All @@ -41,6 +44,8 @@ export class OtpInputController extends Controller {
}

disconnect(): void {
this.inputTarget.classList.add("input");
this.inputTarget.classList.remove("with-js");
this.inputTarget.removeEventListener("input", this.handleInput);
this.inputTarget.removeEventListener("paste", this.handlePaste);
this.inputTarget.removeEventListener("focus", this.handleFocus);
Expand All @@ -50,6 +55,7 @@ export class OtpInputController extends Controller {
this.handleSelectionChange
);
document.removeEventListener("turbo:before-cache", this.beforeCache);
this.submitTarget.disabled = false;
}

_setValue = (value: string): void => {
Expand All @@ -59,6 +65,7 @@ export class OtpInputController extends Controller {

const reachedMaxDigits = this.value.length === this.maxLength;
if (reachedMaxDigits) {
this.submitTarget.disabled = false;
this.submitTarget.click();
}

Expand Down Expand Up @@ -111,15 +118,15 @@ export class OtpInputController extends Controller {

for (let i = 0; i < this.maxLength; i++) {
let textContent = this.value.slice(i, i + 1) || "";
let className = this.isSpanSelected(i)
? "otp-input__digit otp-input__digit--focus"
: "otp-input__digit";
const classes = this.isSpanSelected(i)
? ["otp-input__digit", "otp-input__digit--focus"]
: ["otp-input__digit"];

const isLastDigit = i < this.value.length - 1;
const isBlurred = this.inputTarget !== document.activeElement;
if (textContent && (isLastDigit || isBlurred)) {
textContent = " ";
className += " otp-input__digit--masked";
classes.push("otp-input__digit--masked");
}

this.inputTarget.style.letterSpacing = `calc(${this.inputTarget.offsetWidth}px / ${this.maxLength})`;
Expand All @@ -133,7 +140,7 @@ export class OtpInputController extends Controller {
}

span.textContent = textContent;
span.className = className;
span.className = classes.join(" ");
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@
<link rel="stylesheet" href="{{ call $.StaticAssetURL "authgear-authflowv2-dark-theme.css" }}">
{{ end }}
{{ end }}
{{/* This element reverts the visibility in widget.css */}}
<noscript>
<style>
:root:not([alignment-content]) .widget {
visibility: visible;
}
</style>
</noscript>

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preload" href='{{ call $.GeneratedStaticAssetURL "material-symbols-outlined-subset.woff2" }}' as="font" crossorigin="anonymous">
Expand Down
80 changes: 42 additions & 38 deletions resources/authgear/templates/en/web/authflowv2/__otp_input.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,50 @@

{{ define "authflowv2/__otp_input.html" }}
<div
class="flex flex-col gap-4"
data-controller="otp-input text-field"
data-text-field-input-container-error-class-value="otp-input--error"
>
<div
class="otp-input{{ if $.ErrorMessage }} otp-input--error{{ end }}"
data-text-field-target="inputContainer"
>
<input
form="{{ $.FormName }}"
type="text"
name="{{ or $.InputName "x_code" }}"
inputmode="numeric"
autocomplete="one-time-code"
pattern="\d{{ $.CodeLength }}"
maxlength="{{ $.CodeLength }}"
autofocus=""
required
data-otp-input-target="input"
data-text-field-target="input"
{{ if $.Disabled }}disabled{{ end }}
/>
<div data-otp-input-target="digitsContainer" class="otp-input__digits-container">
<div class="otp-input__digit"></div>
</div>
</div>

{{ if $.ErrorMessage }}
<div>
<div
class="otp-input__error-message"
data-text-field-target="errorMessage"
class="otp-input{{ if $.ErrorMessage }} otp-input--error{{ end }}"
data-text-field-target="inputContainer"
>
{{ $.ErrorMessage }}
<input
class="input"
form="{{ $.FormName }}"
type="text"
name="{{ or $.InputName "x_code" }}"
inputmode="numeric"
autocomplete="one-time-code"
pattern="\d{{ $.CodeLength }}"
maxlength="{{ $.CodeLength }}"
autofocus=""
required
data-otp-input-target="input"
data-text-field-target="input"
{{ if $.Disabled }}disabled{{ end }}
/>
<div data-otp-input-target="digitsContainer" class="otp-input__digits-container"></div>
</div>
{{ end }}

<button
form="{{ $.FormName }}"
class="hidden"
type="submit"
name="{{ or $.SubmitButtonName "x_action" }}"
value="{{ or $.SubmitButtonValue "submit" }}"
data-otp-input-target="submit"
data-authgear-event="{{ $.SubmitEvent }}"
></button>
{{ if $.ErrorMessage }}
<div
class="otp-input__error-message"
data-text-field-target="errorMessage"
>
{{ $.ErrorMessage }}
</div>
{{ end }}

</div>

{{ if not $.ResendButtonHidden }}
<form method="post" novalidate>
{{ $.CSRFField }}
<button
id="resend-button"
class="otp-input__resend-btn link-btn"
class="link-btn"
type="submit"
name="{{ or $.ResendButtonName "x_action" }}"
value="{{ or $.ResendButtonValue "resend" }}"
Expand All @@ -88,5 +81,16 @@
</button>
</form>
{{ end }}

<button
form="{{ $.FormName }}"
class="primary-btn w-full"
type="submit"
name="{{ or $.SubmitButtonName "x_action" }}"
value="{{ or $.SubmitButtonValue "submit" }}"
data-otp-input-target="submit"
data-authgear-event="{{ $.SubmitEvent }}"
> {{ template "v2-button-label-continue" }} </button>

</div>
{{ end }}

0 comments on commit ac1d9bf

Please sign in to comment.