-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Wiebe Nieuwenhuis
committed
Jan 10, 2023
0 parents
commit ccca955
Showing
8 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "wiebenieuwenhuis/filament-char-counter", | ||
"description": "Count the amount of characters in a field", | ||
"autoload": { | ||
"psr-4": { | ||
"Wiebenieuwenhuis\\FilamentCharCounter\\": "src/" | ||
} | ||
}, | ||
"authors": [ | ||
{ | ||
"name": "Wiebe Nieuwenhuis", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"require": { | ||
"php": "^8.0", | ||
"filament/forms": "^2.9", | ||
"spatie/laravel-package-tools": "^1.10" | ||
}, | ||
"minimum-stability": "dev", | ||
"config": { | ||
"sort-packages": true | ||
}, | ||
"extra": { | ||
"laravel": { | ||
"providers": [ | ||
"Wiebenieuwenhuis\\FilamentCharCounter\\FilamentCharCounterProvider" | ||
] | ||
} | ||
}, | ||
"prefer-stable": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Filament Char Counter | ||
|
||
Show a character counter in a text input or textarea. | ||
|
||
TextInput with a 55 max counter, you can exceed the 55 character limit, the counter will turn red. | ||
```php | ||
TextInput::make('title')->characterLimit(55), | ||
``` | ||
|
||
Textarea with a max length of 55 characters. You can't exceed the 55 character limit. | ||
```php | ||
Textarea::make('content')->maxLength(55), | ||
``` | ||
|
||
![screenshot](screenshot.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
@php | ||
$datalistOptions = $getDatalistOptions(); | ||
$affixLabelClasses = [ | ||
'whitespace-nowrap group-focus-within:text-primary-500', | ||
'text-gray-400' => ! $errors->has($getStatePath()), | ||
'text-danger-400' => $errors->has($getStatePath()), | ||
]; | ||
@endphp | ||
<x-dynamic-component | ||
:component="$getFieldWrapperView()" | ||
:id="$getId()" | ||
:label="$getLabel()" | ||
:label-sr-only="$isLabelHidden()" | ||
:helper-text="$getHelperText()" | ||
:hint="$getHint()" | ||
:hint-action="$getHintAction()" | ||
:hint-color="$getHintColor()" | ||
:hint-icon="$getHintIcon()" | ||
:required="$isRequired()" | ||
:state-path="$getStatePath()" | ||
> | ||
<div x-data="{characterLimit: {{ $getCharacterLimit() }}, characterCount: {{ strlen($getState()) }}}" {{ $attributes->merge($getExtraAttributes())->class(['filament-forms-text-input-component flex items-center space-x-2 rtl:space-x-reverse group']) }}> | ||
@if (($prefixAction = $getPrefixAction()) && (! $prefixAction->isHidden())) | ||
{{ $prefixAction }} | ||
@endif | ||
@if ($icon = $getPrefixIcon()) | ||
<x-dynamic-component :component="$icon" class="w-5 h-5" /> | ||
@endif | ||
|
||
@if ($label = $getPrefixLabel()) | ||
<span @class($affixLabelClasses)> | ||
{{ $label }} | ||
</span> | ||
@endif | ||
<div class="flex-1 relative"> | ||
<div class="bg-white dark:bg-gray-700 absolute right-1 px-2 top-1 bottom-1 flex items-center text-sm" @if($getCharacterLimit()) :class="{'text-danger-500': characterCount > {{ $getCharacterLimit() }}}" @endif> | ||
<span x-text="characterCount"></span>@if($getCharacterLimit())/{{ $getCharacterLimit() }}@endif | ||
</div> | ||
<input | ||
@keyup="characterCount = $event.target.value.length" | ||
@unless ($hasMask()) | ||
x-data="{}" | ||
{{ $applyStateBindingModifiers('wire:model') }}="{{ $getStatePath() }}" | ||
type="{{ $getType() }}" | ||
@else | ||
x-data="textInputFormComponent({ | ||
{{ $hasMask() ? "getMaskOptionsUsing: (IMask) => ({$getJsonMaskConfiguration()})," : null }} | ||
state: $wire.{{ $applyStateBindingModifiers('entangle(\'' . $getStatePath() . '\')', lazilyEntangledModifiers: ['defer']) }}, | ||
})" | ||
type="text" | ||
wire:ignore | ||
{!! $isLazy() ? "x-on:blur=\"\$wire.\$refresh\"" : null !!} | ||
{!! $isDebounced() ? "x-on:input.debounce.{$getDebounce()}=\"\$wire.\$refresh\"" : null !!} | ||
{{ $getExtraAlpineAttributeBag() }} | ||
@endunless | ||
dusk="filament.forms.{{ $getStatePath() }}" | ||
{!! ($autocapitalize = $getAutocapitalize()) ? "autocapitalize=\"{$autocapitalize}\"" : null !!} | ||
{!! ($autocomplete = $getAutocomplete()) ? "autocomplete=\"{$autocomplete}\"" : null !!} | ||
{!! $isAutofocused() ? 'autofocus' : null !!} | ||
{!! $isDisabled() ? 'disabled' : null !!} | ||
id="{{ $getId() }}" | ||
{!! ($inputMode = $getInputMode()) ? "inputmode=\"{$inputMode}\"" : null !!} | ||
{!! $datalistOptions ? "list=\"{$getId()}-list\"" : null !!} | ||
{!! ($placeholder = $getPlaceholder()) ? "placeholder=\"{$placeholder}\"" : null !!} | ||
{!! ($interval = $getStep()) ? "step=\"{$interval}\"" : null !!} | ||
@if (! $isConcealed()) | ||
{!! filled($length = $getMaxLength()) ? "maxlength=\"{$length}\"" : null !!} | ||
{!! filled($value = $getMaxValue()) ? "max=\"{$value}\"" : null !!} | ||
{!! filled($length = $getMinLength()) ? "minlength=\"{$length}\"" : null !!} | ||
{!! filled($value = $getMinValue()) ? "min=\"{$value}\"" : null !!} | ||
{!! $isRequired() ? 'required' : null !!} | ||
@endif | ||
{{ $getExtraInputAttributeBag()->class([ | ||
'block w-full transition duration-75 rounded-lg shadow-sm focus:border-primary-500 focus:ring-1 focus:ring-inset focus:ring-primary-500 disabled:opacity-70', | ||
'dark:bg-gray-700 dark:text-white dark:focus:border-primary-500' => config('forms.dark_mode'), | ||
]) }} | ||
x-bind:class="{ | ||
'border-gray-300': ! (@js($getStatePath()) in $wire.__instance.serverMemo.errors), | ||
'dark:border-gray-600': ! (@js($getStatePath()) in $wire.__instance.serverMemo.errors) && @js(config('forms.dark_mode')), | ||
'border-danger-600 ring-danger-600': (@js($getStatePath()) in $wire.__instance.serverMemo.errors), | ||
'dark:border-danger-400 dark:ring-danger-400': (@js($getStatePath()) in $wire.__instance.serverMemo.errors) && @js(config('forms.dark_mode')), | ||
}" | ||
/> | ||
</div> | ||
|
||
@if ($label = $getSuffixLabel()) | ||
<span @class($affixLabelClasses)> | ||
{{ $label }} | ||
</span> | ||
@endif | ||
|
||
@if ($icon = $getSuffixIcon()) | ||
<x-dynamic-component :component="$icon" class="w-5 h-5" /> | ||
@endif | ||
|
||
@if (($suffixAction = $getSuffixAction()) && (! $suffixAction->isHidden())) | ||
{{ $suffixAction }} | ||
@endif | ||
</div> | ||
|
||
@if ($datalistOptions) | ||
<datalist id="{{ $getId() }}-list"> | ||
@foreach ($datalistOptions as $option) | ||
<option value="{{ $option }}" /> | ||
@endforeach | ||
</datalist> | ||
@endif | ||
</x-dynamic-component> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<x-dynamic-component | ||
:component="$getFieldWrapperView()" | ||
:id="$getId()" | ||
:label="$getLabel()" | ||
:label-sr-only="$isLabelHidden()" | ||
:helper-text="$getHelperText()" | ||
:hint="$getHint()" | ||
:hint-action="$getHintAction()" | ||
:hint-color="$getHintColor()" | ||
:hint-icon="$getHintIcon()" | ||
:required="$isRequired()" | ||
:state-path="$getStatePath()" | ||
> | ||
<div class="relative overflow-hidden" | ||
x-data="{characterLimit: {{ $getCharacterLimit() }}, characterCount: {{ strlen($getState()) }}}"> | ||
<div class="bg-white dark:bg-gray-700 absolute right-1 px-2 bottom-1 pb-1 text-sm" @if($getCharacterLimit()) :class="{'text-danger-500': characterCount > {{ $getCharacterLimit() }}}" @endif> | ||
<span x-text="characterCount"></span>@if($getCharacterLimit())/{{ $getCharacterLimit() }}@endif | ||
</div> | ||
|
||
<textarea | ||
@keyup="characterCount = $event.target.value.length" | ||
{!! ($autocapitalize = $getAutocapitalize()) ? "autocapitalize=\"{$autocapitalize}\"" : null !!} | ||
{!! ($autocomplete = $getAutocomplete()) ? "autocomplete=\"{$autocomplete}\"" : null !!} | ||
{!! $isAutofocused() ? 'autofocus' : null !!} | ||
{!! ($cols = $getCols()) ? "cols=\"{$cols}\"" : null !!} | ||
{!! $isDisabled() ? 'disabled' : null !!} | ||
id="{{ $getId() }}" | ||
dusk="filament.forms.{{ $getStatePath() }}" | ||
{!! ($placeholder = $getPlaceholder()) ? "placeholder=\"{$placeholder}\"" : null !!} | ||
{!! ($rows = $getRows()) ? "rows=\"{$rows}\"" : null !!} | ||
{{ $applyStateBindingModifiers('wire:model') }}="{{ $getStatePath() }}" | ||
@if (! $isConcealed()) | ||
{!! filled($length = $getMaxLength()) ? "maxlength=\"{$length}\"" : null !!} | ||
{!! filled($length = $getMinLength()) ? "minlength=\"{$length}\"" : null !!} | ||
{!! $isRequired() ? 'required' : null !!} | ||
@endif | ||
{{ | ||
$attributes | ||
->merge($getExtraAttributes()) | ||
->merge($getExtraInputAttributeBag()->getAttributes()) | ||
->class([ | ||
'filament-forms-textarea-component block w-full transition duration-75 rounded-lg shadow-sm focus:border-primary-500 focus:ring-1 focus:ring-inset focus:ring-primary-500 disabled:opacity-70', | ||
'dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:focus:border-primary-500' => config('forms.dark_mode'), | ||
'border-gray-300' => ! $errors->has($getStatePath()), | ||
'border-danger-600 ring-danger-600' => $errors->has($getStatePath()), | ||
'dark:border-danger-400 dark:ring-danger-400' => $errors->has($getStatePath()) && config('forms.dark_mode') | ||
]) | ||
}} | ||
@if ($shouldAutosize()) | ||
x-data="textareaFormComponent()" | ||
x-on:input="render()" | ||
style="height: 150px" | ||
{{ $getExtraAlpineAttributeBag() }} | ||
@endif | ||
></textarea> | ||
</div> | ||
</x-dynamic-component> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
namespace Wiebenieuwenhuis\FilamentCharCounter; | ||
|
||
use Filament\PluginServiceProvider; | ||
use Spatie\LaravelPackageTools\Package; | ||
|
||
class FilamentCharCounterProvider extends PluginServiceProvider | ||
{ | ||
public static string $name = 'filament-char-counter'; | ||
|
||
public function configurePackage(Package $package): void | ||
{ | ||
$package->name(static::$name)->hasViews(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Wiebenieuwenhuis\FilamentCharCounter; | ||
|
||
use Filament\Forms\Components\TextInput as FilamentTextInput; | ||
|
||
class TextInput extends FilamentTextInput | ||
{ | ||
protected string $view = 'filament-char-counter::text-input'; | ||
|
||
protected $characterLimit = 0; | ||
|
||
public function characterLimit(int $value): self | ||
{ | ||
$this->characterLimit = $value; | ||
return $this; | ||
} | ||
|
||
public function getCharacterLimit(): int | ||
{ | ||
if($this->maxLength){ | ||
return $this->maxLength; | ||
} | ||
return $this->characterLimit; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Wiebenieuwenhuis\FilamentCharCounter; | ||
|
||
use Filament\Forms\Components\Textarea as FilamentTextarea; | ||
|
||
class Textarea extends FilamentTextarea | ||
{ | ||
protected string $view = 'filament-char-counter::textarea'; | ||
|
||
protected $characterLimit = 0; | ||
|
||
public function characterLimit(int $value): self | ||
{ | ||
$this->characterLimit = $value; | ||
return $this; | ||
} | ||
|
||
public function getCharacterLimit(): int | ||
{ | ||
if($this->maxLength){ | ||
return $this->maxLength; | ||
} | ||
return $this->characterLimit; | ||
} | ||
} |