Skip to content

Commit

Permalink
Added OtpInput Component
Browse files Browse the repository at this point in the history
  • Loading branch information
afsakar committed Apr 7, 2024
1 parent 855d6ed commit 7e4e5b1
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .phpunit.cache/test-results
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"pest_2.34.5","defects":[],"times":{"P\\Tests\\ArchTest::__pest_evaluable_it_will_not_use_debugging_functions":0.154,"P\\Tests\\ExampleTest::__pest_evaluable_it_can_test":0.008}}
{"version":"pest_2.34.5","defects":[],"times":{"P\\Tests\\ArchTest::__pest_evaluable_it_will_not_use_debugging_functions":0.161,"P\\Tests\\ExampleTest::__pest_evaluable_it_can_test":0.001}}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"phpstan": "vendor/bin/phpstan analyse --error-format=github --memory-limit=1G",
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage",
"format": "vendor/bin/pint -v",
"lint": "vendor/bin/pint -v",
"checks": [
"@lint",
"@phpstan",
Expand Down
2 changes: 1 addition & 1 deletion resources/lang/tr/translations.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@

'validation' => [
'invalid_code' => 'Girdiğiniz şifre geçersiz.',
'expired_code' => 'Girdiğiniz şifre süresi dolmuş.',
'expired_code' => 'Girdiğiniz şifrenin süresi dolmuş. Lütfen yeni bir şifre isteyin.',
],
];
104 changes: 104 additions & 0 deletions resources/views/forms/otp-input.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@php
$extraAlpineAttributes = $getExtraAlpineAttributes();
$id = $getId();
$isConcealed = $isConcealed();
$isDisabled = $isDisabled();
$statePath = $getStatePath();
$hintActions = $getHintActions();
$numberLength = $getNumberLength();
$isAutofocused = $isAutofocused();
@endphp

<x-dynamic-component
:component="$getFieldWrapperView()"
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div x-data="{
state: '{{ $getStatePath() }}',
length: {{$numberLength}},
autoFocus: '{{$isAutofocused}}',
type: 'text',
init: function(){
if (this.autoFocus){
this.$refs[1].focus();
}
},
handleInput(e, i) {
const input = e.target;
if(input.value.length > 1){
input.value = input.value.substring(0, 1);
}
this.state = Array.from(Array(this.length), (element, i) => {
const el = this.$refs[(i + 1)];
return el.value ? el.value : '';
}).join('');
if (i < this.length) {
this.$refs[i+1].focus();
this.$refs[i+1].select();
}
if(i == this.length){
@this.set('{{ $getStatePath() }}', this.state)
}
},
handlePaste(e) {
const paste = e.clipboardData.getData('text');
this.value = paste;
const inputs = Array.from(Array(this.length));
inputs.forEach((element, i) => {
this.$refs[(i+1)].focus();
this.$refs[(i+1)].value = paste[i] || '';
});
},
handleBackspace(e) {
const ref = e.target.getAttribute('x-ref');
e.target.value = '';
const previous = ref - 1;
this.$refs[previous] && this.$refs[previous].focus();
this.$refs[previous] && this.$refs[previous].select();
e.preventDefault();
},
}">
<div class="flex justify-between gap-6 pt-3 pb-2 h-16">

@foreach(range(1, $numberLength) as $column)
<x-filament::input.wrapper
:disabled="$isDisabled"
:valid="! $errors->has($statePath)"
:attributes="
\Filament\Support\prepare_inherited_attributes($getExtraAttributeBag())
->class(['fi-fo-text-input overflow-hidden'])
"
>
<input
{{$isDisabled ? 'disabled' : ''}}
type="text"
maxlength="1"
x-ref="{{$column}}"
required
class="fi-input block w-full border-none text-base text-gray-950 transition duration-75 placeholder:text-gray-400 focus:ring-0 dark:text-white dark:placeholder:text-gray-500 sm:leading-6 bg-white/0 text-center"
x-on:input="handleInput($event, {{$column}})"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="handleBackspace($event)"
/>

</x-filament::input.wrapper>
@endforeach

</div>
</div>
</x-dynamic-component>
27 changes: 27 additions & 0 deletions src/Filament/Forms/OtpInput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Afsakar\FilamentOtpLogin\Filament\Forms;

use Filament\Forms;

class OtpInput extends Forms\Components\TextInput
{
protected string $view = 'filament-otp-login::forms.otp-input';

protected int $numberLength = 6;

public static function make(string $name): static
{
$static = app(static::class, ['name' => $name]);
$static->configure();

$static->numberLength = config('filament-otp-login.otp_code.length');

return $static;
}

public function getNumberLength(): int
{
return $this->evaluate($this->numberLength);
}
}
20 changes: 11 additions & 9 deletions src/Filament/Pages/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Afsakar\FilamentOtpLogin\Filament\Pages;

use Afsakar\FilamentOtpLogin\Filament\Forms\OtpInput;
use Afsakar\FilamentOtpLogin\Models\OtpCode;
use Afsakar\FilamentOtpLogin\Notifications\SendOtpCode;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
Expand All @@ -11,14 +12,14 @@
use Filament\Facades\Filament;
use Filament\Forms\Components\Actions\Action as ActionComponent;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Http\Responses\Auth\Contracts\LoginResponse;
use Filament\Models\Contracts\FilamentUser;
use Filament\Notifications\Notification;
use Filament\Pages\Auth\Login as BaseLogin;
use Filament\Pages\Concerns\InteractsWithFormActions;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\HtmlString;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\On;

Expand Down Expand Up @@ -216,14 +217,15 @@ protected function getForms(): array

protected function getOtpCodeFormComponent(): Component
{
return TextInput::make('otp')
return OtpInput::make('otp')
->label(__('filament-otp-login::translations.otp_code'))
->numeric()
->suffixIcon('heroicon-o-finger-print')
->hintAction(fn () => $this->goBackAction())
->maxLength(config('filament-otp-login.otp_code.length'))
->required()
->extraInputAttributes(['tabindex' => 3]);
->hint(new HtmlString('<button type="button" wire:click="goBack()" class="focus:outline-none font-bold focus:underline hover:text-primary-400 text-primary-600 text-sm">' . __('filament-otp-login::translations.view.go_back') . '</button>'))
->required();
}

public function goBack(): void
{
$this->step = 1;
}

/**
Expand Down Expand Up @@ -257,7 +259,7 @@ protected function goBackAction(): ActionComponent
{
return ActionComponent::make('go-back')
->label(__('filament-otp-login::translations.view.go_back'))
->action(fn () => $this->step = 1);
->action(fn () => $this->goBack());
}

protected function getAuthenticateFormAction(): Action
Expand Down

0 comments on commit 7e4e5b1

Please sign in to comment.