first commit

This commit is contained in:
2025-03-07 00:29:07 -06:00
commit b21a11c2ee
564 changed files with 94041 additions and 0 deletions

View File

@ -0,0 +1,148 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelos para Livewire
'checkboxModel' => '',
'textModel' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start',
'size' => '', // Tamaño del input (sm, lg)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Elementos opcionales antes/después del input
'prefix' => null,
'suffix' => null,
// Íconos dentro del input
'icon' => null, // Ícono fijo
'clickableIcon' => null, // Ícono con botón
// Configuración del checkbox
'checked' => false,
'disabled' => false,
// Configuración del input de texto
'disableOnOffCheckbox' => true, // Deshabilita input hasta que el checkbox esté activo
'focusOnCheck' => true, // Hace foco en el input al activar el checkbox
// Texto de ayuda
'helperText' => '',
// Atributos adicionales para el input de texto
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Generación de Name, ID y Model**
$livewireCheckbox = $checkboxModel ? "wire:model=$checkboxModel" : '';
$livewireText = $textModel ? "wire:model=$textModel" : '';
$nameCheckbox = $checkboxModel;
$nameText = $attributes->get('name', $textModel);
$checkboxId = $attributes->get('id', 'chk_' . $uid);
$textInputId = 'txt_' . $uid;
// **Placeholder del input de texto**
$placeholder = $attributes->get('placeholder', 'Ingrese información');
// **Clases dinámicas**
$sizeClass = match ($size) {
'sm' => 'form-control-sm',
'lg' => 'form-control-lg',
default => '',
};
$fullClass = trim("form-control $sizeClass");
// **Fusionar atributos del input de texto**
$inputAttributes = $attributes->merge([
'id' => $textInputId,
'name' => $nameText,
'placeholder' => $placeholder,
])->class($fullClass);
@endphp
{{-- ============================ CHECKBOX CON INPUT GROUP ============================ --}}
<div class="mb-4 {{ $parentClass }}">
@if ($label)
<label for="{{ $checkboxId }}" class="{{ $labelClass }}">{{ $label }}</label>
@endif
<div class="input-group">
{{-- Checkbox dentro del input-group-text --}}
<div class="input-group-text">
<input
id="{{ $checkboxId }}"
name="{{ $nameCheckbox }}"
type="checkbox"
{{ $livewireCheckbox }}
{{ $disabled ? 'disabled' : '' }}
class="form-check-input mt-0"
onchange="toggleInputState('{{ $checkboxId }}', '{{ $textInputId }}', {{ $focusOnCheck ? 'true' : 'false' }}, {{ $disableOnOffCheckbox ? 'true' : 'false' }})"
>
</div>
{{-- Prefijo opcional --}}
@isset($prefix)
<span class="input-group-text">{{ $prefix }}</span>
@endisset
{{-- Ícono fijo opcional --}}
@isset($icon)
<span class="input-group-text"><i class="{{ $icon }}"></i></span>
@endisset
{{-- Input de texto dentro del mismo grupo --}}
<input
{{ $livewireText }}
{!! $inputAttributes !!}
@if ($disableOnOffCheckbox) disabled @endif
aria-label="Text input with checkbox"
>
{{-- Sufijo opcional --}}
@isset($suffix)
<span class="input-group-text">{{ $suffix }}</span>
@endisset
{{-- Ícono clickeable opcional --}}
@isset($clickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $clickableIcon }}"></i>
</button>
@endisset
</div>
{{-- Texto de ayuda opcional --}}
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
</div>
{{-- ============================ JAVASCRIPT PURO ============================ --}}
<script>
function toggleInputState(checkboxId, inputId, focusOnCheck, disableOnOffCheckbox) {
let checkbox = document.getElementById(checkboxId);
let textInput = document.getElementById(inputId);
if (!checkbox || !textInput) return;
// Deshabilitar o habilitar el input de texto
if (disableOnOffCheckbox) {
textInput.disabled = !checkbox.checked;
}
// Enfocar automáticamente el input si está habilitado
if (focusOnCheck && checkbox.checked) {
textInput.focus();
}
}
</script>

View File

@ -0,0 +1,133 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelo de Livewire (si aplica)
'model' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start',
'size' => 'md', // Tamaño del checkbox/switch: sm, md, lg
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Estilos del checkbox
'switch' => false, // Cambiar a modo switch
'switchType' => 'default', // 'default' o 'square'
'color' => 'primary', // Bootstrap color: primary, secondary, success, danger, etc.
'withIcon' => false, // Si el switch usa íconos en On/Off
// Diseño de disposición
'inline' => false, // Modo inline en lugar de bloque
// Texto de ayuda
'helperText' => '',
// Atributos adicionales
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Configuración de valores base**
$livewireModel = $attributes->get('wire:model', $model);
$name = $attributes->get('name', $livewireModel);
$inputId = $attributes->get('id', $name . '_' . $uid);
$checked = $attributes->get('checked', false);
$disabled = $attributes->get('disabled', false);
// **Manejo de errores**
$errorKey = $livewireModel ?: $name;
$hasError = $errors->has($errorKey);
$errorClass = $hasError ? 'is-invalid' : '';
// **Clases dinámicas**
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => '',
};
$sizeClass = match ($size) {
'sm' => 'switch-sm',
'lg' => 'switch-lg',
default => '',
};
$switchTypeClass = $switchType === 'square' ? 'switch-square' : '';
$switchColorClass = "switch-{$color}";
$checkColorClass = "form-check-{$color}";
// **Fusionar atributos con clases dinámicas**
$checkboxAttributes = $attributes->merge([
'id' => $inputId,
'name' => $name,
'type' => 'checkbox',
]);
// **Detectar si se usa input-group**
$requiresInputGroup = $attributes->get('inline');
@endphp
@if ($switch)
{{-- ============================ MODO SWITCH ============================ --}}
<div class="{{ $alignClass }} {{ $inline ? 'd-inline-block' : '' }} {{ $parentClass }} {{ $mb0 ? '' : 'mb-2' }}">
<label class="switch {{ $switchTypeClass }} {{ $switchColorClass }} {{ $sizeClass }} {{ $labelClass }}">
<input
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $checked ? 'checked' : '' }}
{!! $checkboxAttributes->class("switch-input $errorClass")->toHtml() !!}
/>
{{-- El Switch Slider --}}
<span class="switch-toggle-slider">
@if ($withIcon)
<span class="switch-on">
<i class="ti ti-check"></i>
</span>
<span class="switch-off">
<i class="ti ti-x"></i>
</span>
@endif
</span>
{{-- Etiqueta textual a la derecha del switch --}}
<span class="switch-label">
{{ $label }}
</span>
</label>
{{-- Texto de ayuda y error --}}
@isset($helperText)
<div class="form-text">{{ $helperText }}</div>
@endisset
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
@else
{{-- ============================ MODO CHECKBOX ============================ --}}
<div class="form-check {{ $checkColorClass }} {{ $alignClass }} {{ $sizeClass }} {{ $inline ? 'form-check-inline' : '' }} {{ $parentClass }} {{ $mb0 ? '' : 'mb-4' }}">
<input
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $checked ? 'checked' : '' }}
{!! $checkboxAttributes->class("form-check-input $errorClass")->toHtml() !!}
>
<label for="{{ $inputId }}" class="form-check-label {{ $labelClass }}">
{{ $label }}
</label>
{{-- Texto de ayuda y error --}}
@isset($helperText)
<div class="form-text">{{ $helperText }}</div>
@endisset
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
@endif

View File

@ -0,0 +1,54 @@
@props([
'uid' => uniqid(),
'id' => '',
'model' => '',
'name' => '',
'type' => 'checkbox', // checkbox o radio
'title' => '',
'description' => '',
'icon' => null, // Clases de iconos (ej: ti ti-rocket)
'image' => null, // URL de imagen
'checked' => false,
'disabled' => false,
'helperText' => '', // Texto de ayuda opcional
])
@php
$livewireModel = $attributes->get('wire:model', $model);
$name = $name ?: $livewireModel;
$inputId = $id ?: ($uid ? $name . '_' . $uid : $name);
$errorClass = $errors->has($model) ? 'is-invalid' : '';
$checkedAttribute = $checked ? 'checked' : '';
$visualContent = $icon
? "<i class='{$icon}'></i>"
: ($image ? "<img src='{$image}' alt='{$title}' class='img-fluid rounded'>" : '');
@endphp
<div class="mb-4 form-check custom-option custom-option-icon {{ $checked ? 'checked' : '' }}">
<label class="form-check-label custom-option-content" for="{{ $inputId }}">
<span class="custom-option-body">
{!! $visualContent !!}
<span class="custom-option-title">{{ $title }}</span>
@if($description)
<small>{{ $description }}</small>
@endif
</span>
<input
type="{{ $type }}"
class="form-check-input {{ $errorClass }}"
id="{{ $inputId }}"
name="{{ $name }}"
wire:model="{{ $model }}"
{{ $checked ? 'checked' : '' }}
{{ $disabled ? 'disabled' : '' }}
>
</label>
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
@error($model)
<span class="text-danger">{{ $message }}</span>
@enderror
</div>

View File

@ -0,0 +1,48 @@
@props([
'id' => uniqid(), // ID único del formulario
'uniqueId' => '', // ID único del formulario
'mode' => 'create', // Modo actual ('create', 'edit', 'delete')
'method' => 'POST', // Método del formulario (POST, GET, PUT, DELETE)
'action' => '', // URL de acción
'enctype' => false, // Para envío de archivos
'wireSubmit' => false, // Usar wire:submit.prevent
'class' => '', // Clases adicionales para el formulario
'actionPosition' => 'bottom', // Posición de acciones: top, bottom, both, none
])
@php
$formAttributes = [
'id' => $id,
'method' => $method,
'action' => $action,
'class' => "fv-plugins-bootstrap5 $class",
];
if ($wireSubmit) {
$formAttributes['wire:submit.prevent'] = $wireSubmit;
}
if ($enctype) {
$formAttributes['enctype'] = 'multipart/form-data';
}
@endphp
<form {{ $attributes->merge($formAttributes) }}>
<x-vuexy-admin::form.input :uid="$uniqueId" type="hidden" model="id" />
<x-vuexy-admin::form.input :uid="$uniqueId" type="hidden" model="mode" />
@if ($mode !== 'delete' && in_array($actionPosition, ['top', 'both']))
<div class="notification-container mb-4"></div>
<div class="form-actions mb-4">
{{ $actions ?? '' }}
</div>
@endif
<div class="form-body">
{{ $slot }}
</div>
@if (in_array($actionPosition, ['bottom', 'both']))
<div class="notification-container mb-4"></div>
<div class="form-actions mt-4">
{{ $actions ?? '' }}
</div>
@endif
</form>

View File

@ -0,0 +1,184 @@
@props([
'uid' => uniqid(),
'model' => '',
'label' => '',
'labelClass' => '',
'class' => '',
'align' => 'start',
'size' => '',
'mb-0' => false,
'parentClass' => '',
'prefix' => null,
'suffix' => null,
'icon' => null,
'clickableIcon' => null,
'inline' => false,
'labelCol' => 4,
'inputCol' => 8,
'floatLabel' => false,
'helperText' => '',
'attributes' => new \Illuminate\View\ComponentAttributeBag([]), // Atributos adicionales
])
@php
// Configuración dinámica de atributos y clases CSS
$livewireModel = $attributes->get('wire:model', $model); // Permitir uso directo de wire:model en el atributo
$name = $name ?: $livewireModel; // Si no se proporciona el nombre, toma el nombre del modelo
$inputId = $id ?: ($uid ? $name . '_' . $uid : $name); // ID generado si no se proporciona uno
// Obtener los atributos actuales en un array
$attributesArray = array_merge([
'type' => $type,
'id' => $inputId,
'name' => $name,
]);
// Agregar wire:model solo si existe
if ($livewireModel) {
$attributesArray['wire:model'] = $livewireModel;
}
$attributesArray = array_merge($attributesArray, $attributes->getAttributes());
// Reconstruir el ComponentAttributeBag con los atributos modificados
$inputAttributes = new \Illuminate\View\ComponentAttributeBag($attributesArray);
dump($inputAttributes);
// Manejo de errores de validación
$errorKey = $livewireModel ?: $name;
$errorClass = $errors->has($errorKey) ? 'is-invalid' : '';
// Definir el tamaño del input basado en la clase seleccionada
$sizeClass = $size === 'small' ? 'form-control-sm' : ($size === 'large' ? 'form-control-lg' : '');
// Alineación del texto
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => ''
};
// Clases combinadas para el input
$fullClass = trim("form-control $sizeClass $alignClass $errorClass $class");
// Detectar si se necesita usar input-group
$requiresInputGroup = $prefix || $suffix || $icon || $clickableIcon;
@endphp
{{-- Input oculto sin estilos --}}
@if($type === 'hidden')
<input type="hidden" id="{{ $inputId }}" name="{{ $name }}" {{ $livewireModel ? "wire:model=$livewireModel" : '' }}
/>
@elseif($floatLabel)
{{-- Input con etiqueta flotante --}}
<div class="form-floating mb-4 {{ $parentClass }}">
<input
type="{{ $type }}"
id="{{ $inputId }}"
name="{{ $name }}"
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
class="{{ $fullClass }}"
placeholder="{{ $placeholder ?: 'Ingrese ' . strtolower($label) }}"
{{ $step ? "step=$step" : '' }}
{{ $max ? "maxlength=$max" : '' }}
{{ $min ? "minlength=$min" : '' }}
{{ $pattern ? "pattern=$pattern" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $multiple ? 'multiple' : '' }} />
<label for="{{ $inputId }}">{{ $label }}</label>
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
@if ($errors->has($errorKey))
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
@else
{{-- Input con formato clásico --}}
<div class="{{ $inline ? 'row' : '' }} {{ $parentClass }} fv-row mb-4">
@isset($label)
<label for="{{ $inputId }}" class="{{ $inline ? 'col-form-label col-md-' . $labelCol : 'form-label' }} {{ $labelClass }}">{{ $label }}</label>
@endisset
<div class="{{ $inline ? 'col-md-' . $inputCol : '' }}">
@if ($requiresInputGroup)
<div class="input-group input-group-merge">
@if ($prefix)
<span class="input-group-text">{{ $prefix }}</span>
@endif
@if ($icon)
<span class="input-group-text"><i class="{{ $icon }}"></i></span>
@endif
<input
type="{{ $type }}"
id="{{ $inputId }}"
name="{{ $name }}"
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
class="{{ $fullClass }}"
placeholder="{{ $placeholder ?: 'Ingrese ' . strtolower($label) }}"
{{ $step ? "step=$step" : '' }}
{{ $max ? "maxlength=$max" : '' }}
{{ $min ? "minlength=$min" : '' }}
{{ $pattern ? "pattern=$pattern" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $multiple ? 'multiple' : '' }} />
@if ($suffix)
<span class="input-group-text">{{ $suffix }}</span>
@endif
@if ($clickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $clickableIcon }}"></i>
</button>
@endif
</div>
@else
{{-- Input sin prefijo o íconos --}}
<input
type="{{ $type }}"
id="{{ $inputId }}"
name="{{ $name }}"
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
class="{{ $fullClass }}"
placeholder="{{ $placeholder ?: 'Ingrese ' . strtolower($label) }}"
{{ $step ? "step=$step" : '' }}
{{ $max ? "maxlength=$max" : '' }}
{{ $min ? "minlength=$min" : '' }}
{{ $pattern ? "pattern=$pattern" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $multiple ? 'multiple' : '' }} />
@endif
@if ($helperText)
<small class="form-text text-muted">{{ $helperText }}</small>
@endif
@if ($errors->has($errorKey))
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
</div>
@endif

View File

@ -0,0 +1,169 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelo de Livewire
'model' => '',
// Etiqueta y Clases
'label' => '',
'labelClass' => '',
'placeholder' => '',
// Clases Generales
'align' => 'start',
'size' => '', // Tamaño del input (sm, lg)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Elementos opcionales antes/después del input
'prefix' => null,
'suffix' => null,
// Íconos dentro del input
'icon' => null,
'clickableIcon' => null,
// Configuración especial
'phoneMode' => false, // "national", "international", "both"
// Diseño de disposición (columnas)
'inline' => false,
'labelCol' => 4,
'inputCol' => 8,
// Configuración de etiquetas flotantes y texto de ayuda
'floatLabel' => false,
'helperText' => '',
// Atributos adicionales
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Configuración de Name, ID y Model**
$livewireModel = $attributes->get('wire:model', $model);
$name = $attributes->get('name', $livewireModel);
$inputId = $attributes->get('id', $name . '_' . $uid);
$type = $attributes->get('type', 'text');
// **Definir formato de teléfono según `phoneMode`**
if ($phoneMode) {
$type = 'tel';
$attributes = $attributes->merge([
'autocomplete' => 'tel',
'inputmode' => 'tel',
]);
switch ($phoneMode) {
case 'national':
$attributes = $attributes->merge([
'pattern' => '^(?:\D*\d){10,}$',
'placeholder' => $placeholder !== false ? ($placeholder ?: 'Ej. (55) 1234-5678') : null,
]);
break;
case 'international':
$attributes = $attributes->merge([
'pattern' => '^\+?[1-9]\d{1,14}$',
'placeholder' => $placeholder !== false ? ($placeholder ?: 'Ej. +52 1 55 1234-5678') : null,
]);
break;
case 'both':
$attributes = $attributes->merge([
'pattern' => '(^(?:\D*\d){10,}$)|(^\+?[1-9]\d{1,14}$)',
'placeholder' => $placeholder !== false ? ($placeholder ?: 'Ej. (55) 1234-5678 o +52 1 55 1234-5678') : null,
]);
break;
}
}
// **Manejo del Placeholder si no lo estableció `phoneMode`**
if (!$attributes->has('placeholder')) {
if ($placeholder === false) {
// No agregar `placeholder`
$placeholderAttr = [];
} elseif (empty($placeholder)) {
// Generar automáticamente desde el `label`
$placeholderAttr = ['placeholder' => 'Ingrese ' . strtolower($label)];
} else {
// Usar `placeholder` definido manualmente
$placeholderAttr = ['placeholder' => $placeholder];
}
// Fusionar el placeholder si no fue definido en `phoneMode`
$attributes = $attributes->merge($placeholderAttr);
}
// **Manejo de errores**
$errorKey = $livewireModel ?: $name;
$hasError = $errors->has($errorKey);
$errorClass = $hasError ? 'is-invalid' : '';
// **Clases dinámicas**
$sizeClass = match ($size) {
'sm' => 'form-control-sm',
'lg' => 'form-control-lg',
default => '',
};
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => '',
};
// **Fusionar atributos**
$inputAttributes = $attributes->merge([
'type' => $type,
'id' => $inputId,
'name' => $name,
])->class("form-control $sizeClass $alignClass $errorClass");
@endphp
{{-- Estructura del Input --}}
<div class="{{ $mb0 ? '' : 'mb-4' }} {{ $parentClass }}">
{{-- Etiqueta --}}
@if ($label)
<label for="{{ $inputId }}" class="form-label {{ $labelClass }}">{{ $label }}</label>
@endif
{{-- Input con Prefijos, Sufijos o Íconos --}}
@if ($prefix || $suffix || $icon || $clickableIcon)
<div class="input-group input-group-merge">
@isset($prefix)
<span class="input-group-text">{{ $prefix }}</span>
@endisset
@isset($icon)
<span class="input-group-text"><i class="{{ $icon }}"></i></span>
@endisset
<input {!! $inputAttributes !!} {{ $livewireModel ? "wire:model=$livewireModel" : '' }} />
@isset($suffix)
<span class="input-group-text">{{ $suffix }}</span>
@endisset
@isset($clickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $clickableIcon }}"></i>
</button>
@endisset
</div>
@else
{{-- Input Simple --}}
<input {!! $inputAttributes !!} {{ $livewireModel ? "wire:model=$livewireModel" : '' }} />
@endif
{{-- Texto de ayuda --}}
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
{{-- Mensajes de error --}}
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>

View File

@ -0,0 +1,149 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelos para Livewire
'radioModel' => '',
'textModel' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start',
'size' => '', // Tamaño del input (sm, lg)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Elementos opcionales antes/después del input
'prefix' => null,
'suffix' => null,
// Íconos dentro del input
'icon' => null, // Ícono fijo
'clickableIcon' => null, // Ícono con botón
// Configuración del radio
'checked' => false,
'disabled' => false,
'name' => '', // Grupo del radio
// Configuración del input de texto
'disableOnOffRadio' => true, // Deshabilita input hasta que el radio esté seleccionado
'focusOnCheck' => true, // Hace foco en el input al seleccionar el radio
// Texto de ayuda
'helperText' => '',
// Atributos adicionales para el input de texto
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Generación de Name, ID y Model**
$livewireRadio = $radioModel ? "wire:model=$radioModel" : '';
$livewireText = $textModel ? "wire:model=$textModel" : '';
$nameRadio = $attributes->get('name', $radioModel ?: $name);
$nameText = $attributes->get('name', $textModel);
$radioId = $attributes->get('id', 'radio_' . $uid);
$textInputId = 'txt_' . $uid;
// **Placeholder del input de texto**
$placeholder = $attributes->get('placeholder', 'Ingrese información');
// **Clases dinámicas**
$sizeClass = match ($size) {
'sm' => 'form-control-sm',
'lg' => 'form-control-lg',
default => '',
};
$fullClass = trim("form-control $sizeClass");
// **Fusionar atributos del input de texto**
$inputAttributes = $attributes->merge([
'id' => $textInputId,
'name' => $nameText,
'placeholder' => $placeholder,
])->class($fullClass);
@endphp
{{-- ============================ RADIO BUTTON CON INPUT GROUP ============================ --}}
<div class="{{ $mb0 ? '' : 'mb-4' }} {{ $parentClass }}">
@if ($label)
<label for="{{ $radioId }}" class="{{ $labelClass }}">{{ $label }}</label>
@endif
<div class="input-group">
{{-- Radio dentro del input-group-text --}}
<div class="input-group-text">
<input
id="{{ $radioId }}"
name="{{ $nameRadio }}"
type="radio"
{{ $livewireRadio }}
{{ $disabled ? 'disabled' : '' }}
class="form-check-input mt-0"
onchange="toggleRadioInputState('{{ $radioId }}', '{{ $textInputId }}', {{ $focusOnCheck ? 'true' : 'false' }}, {{ $disableOnOffRadio ? 'true' : 'false' }})"
>
</div>
{{-- Prefijo opcional --}}
@isset($prefix)
<span class="input-group-text">{{ $prefix }}</span>
@endisset
{{-- Ícono fijo opcional --}}
@isset($icon)
<span class="input-group-text"><i class="{{ $icon }}"></i></span>
@endisset
{{-- Input de texto dentro del mismo grupo --}}
<input
{{ $livewireText }}
{!! $inputAttributes !!}
@if ($disableOnOffRadio) disabled @endif
aria-label="Text input with radio button"
>
{{-- Sufijo opcional --}}
@isset($suffix)
<span class="input-group-text">{{ $suffix }}</span>
@endisset
{{-- Ícono clickeable opcional --}}
@isset($clickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $clickableIcon }}"></i>
</button>
@endisset
</div>
{{-- Texto de ayuda opcional --}}
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
</div>
{{-- ============================ JAVASCRIPT PURO ============================ --}}
<script>
function toggleRadioInputState(radioId, inputId, focusOnCheck, disableOnOffRadio) {
let radio = document.getElementById(radioId);
let textInput = document.getElementById(inputId);
if (!radio || !textInput) return;
// Deshabilitar o habilitar el input de texto
if (disableOnOffRadio) {
textInput.disabled = !radio.checked;
}
// Enfocar automáticamente el input si está habilitado
if (focusOnCheck && radio.checked) {
textInput.focus();
}
}
</script>

View File

@ -0,0 +1,125 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelo de Livewire (si aplica)
'model' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start',
'size' => '', // Tamaño del radio (sm, lg)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Modo de visualización
'switch' => false, // Convertir en switch
'switchType' => 'default', // 'default' o 'square'
'color' => 'primary', // Colores personalizados (Bootstrap)
'stacked' => false, // Apilar radios en lugar de inline
'inline' => false, // Mostrar radios en línea
// Texto de ayuda
'helperText' => '',
// Atributos adicionales
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Configuración de valores base**
$livewireModel = $attributes->get('wire:model', $model);
$name = $attributes->get('name', $livewireModel);
$inputId = $attributes->get('id', $name . '_' . $uid);
$checked = $attributes->get('checked', false);
$disabled = $attributes->get('disabled', false);
// **Manejo de errores**
$errorKey = $livewireModel ?: $name;
$hasError = $errors->has($errorKey);
$errorClass = $hasError ? 'is-invalid' : '';
// **Clases dinámicas**
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => '',
};
$sizeClass = match ($size) {
'sm' => 'form-check-sm',
'lg' => 'form-check-lg',
default => '',
};
$switchTypeClass = $switchType === 'square' ? 'switch-square' : '';
$switchColorClass = "switch-{$color}";
$radioColorClass = "form-check-{$color}";
// **Fusionar atributos con clases dinámicas**
$radioAttributes = $attributes->merge([
'id' => $inputId,
'name' => $name,
'type' => 'radio',
]);
// **Detectar si se usa `inline` o `stacked`**
$layoutClass = $stacked ? 'switches-stacked' : 'form-check';
@endphp
@if ($switch)
{{-- ============================ MODO SWITCH ============================ --}}
<div class="{{ $alignClass }} {{ $inline ? 'd-inline-block' : '' }} {{ $parentClass }} {{ $mb0 ? '' : 'mb-4' }}">
<label class="switch {{ $switchTypeClass }} {{ $switchColorClass }} {{ $sizeClass }} {{ $labelClass }}">
<input
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $checked ? 'checked' : '' }}
{!! $radioAttributes->class("switch-input $errorClass")->toHtml() !!}
/>
{{-- El Switch Slider --}}
<span class="switch-toggle-slider">
<span class="switch-on"></span>
<span class="switch-off"></span>
</span>
{{-- Etiqueta textual a la derecha del switch --}}
<span class="switch-label">
{{ $label }}
</span>
</label>
{{-- Texto de ayuda y error --}}
@isset($helperText)
<div class="form-text">{{ $helperText }}</div>
@endisset
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
@else
{{-- ============================ MODO RADIO ============================ --}}
<div class="{{ $layoutClass }} {{ $radioColorClass }} {{ $alignClass }} {{ $sizeClass }} {{ $inline ? 'form-check-inline' : '' }} {{ $parentClass }} {{ $mb0 ? '' : 'mb-4' }}">
<input
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $checked ? 'checked' : '' }}
{!! $radioAttributes->class("form-check-input $errorClass")->toHtml() !!}
>
<label for="{{ $inputId }}" class="form-check-label {{ $labelClass }}">
{{ $label }}
</label>
{{-- Texto de ayuda y error --}}
@isset($helperText)
<div class="form-text">{{ $helperText }}</div>
@endisset
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
@endif

View File

@ -0,0 +1,86 @@
@props([
'uid' => uniqid(),
'id' => '',
'model' => '',
'name' => '',
'label' => '',
'labelClass' => 'form-label',
'placeholder' => '',
'options' => [],
'selected' => null,
'class' => '',
'parentClass' => '',
'multiple' => false,
'disabled' => false,
'prefixLabel' => null,
'suffixLabel' => null,
'buttonBefore' => null,
'buttonAfter' => null,
'inline' => false, // Si es en línea
'labelCol' => 2, // Columnas que ocupa el label (Bootstrap grid)
'inputCol' => 10, // Columnas que ocupa el input (Bootstrap grid)
'helperText' => '', // Texto de ayuda opcional
'select2' => false, // Activar Select2 automáticamente
])
@php
$name = $name ?: $model;
$inputId = $id ?: ($uid ? str_replace('.', '_', $name) . '_' . $uid : $name);
$placeholder = $placeholder ?: 'Seleccione ' . strtolower($label);
$errorClass = $errors->has($model) ? 'is-invalid' : '';
$options = is_array($options) ? collect($options) : $options;
$select2Class = $select2 ? 'select2' : ''; // Agrega la clase select2 si está habilitado
@endphp
<div class="{{ $inline ? 'row' : 'mb-4' }} {{ $parentClass }} fv-row">
@if($label != null)
<label for="{{ $inputId }}" class="{{ $inline ? 'col-md-' . $labelCol : '' }} {{ $labelClass }}">{{ $label }}</label>
@endif
<div class="{{ $inline ? 'col-md-' . $inputCol : '' }}">
<div class="input-group {{ $prefixLabel || $suffixLabel || $buttonBefore || $buttonAfter ? 'input-group-merge' : '' }}">
@if ($buttonBefore)
<button class="btn btn-outline-primary waves-effect" type="button">{{ $buttonBefore }}</button>
@endif
@if ($prefixLabel)
<label class="input-group-text" for="{{ $inputId }}">{{ $prefixLabel }}</label>
@endif
<select
id="{{ $inputId }}"
name="{{ $name }}"
class="form-select {{ $errorClass }} {{ $class }} {{ $select2Class }}"
{{ $multiple ? 'multiple' : '' }}
{{ $disabled ? 'disabled' : '' }}
{{ $model ? "wire:model=$model" : '' }}
{{ $select2 ? 'data-live-search="true"' : '' }}
>
@if (!$multiple && $placeholder)
<option value="">{{ $placeholder }}</option>
@endif
@foreach ($options as $key => $value)
<option value="{{ $key }}" {{ (string) $key === (string) $selected ? 'selected' : '' }}>
{{ $value }}
</option>
@endforeach
</select>
@if ($suffixLabel)
<label class="input-group-text" for="{{ $inputId }}">{{ $suffixLabel }}</label>
@endif
@if ($buttonAfter)
<button class="btn btn-outline-primary waves-effect" type="button">{{ $buttonAfter }}</button>
@endif
</div>
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
@if ($errors->has($model))
<span class="text-danger">{{ $errors->first($model) }}</span>
@endif
</div>
</div>

View File

@ -0,0 +1,151 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelo de Livewire (si aplica)
'model' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start',
'size' => '', // Tamaño del select (small, large)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Activar Select2 automáticamente
'select2' => false,
// Prefijos y sufijos en input-group
'prefixLabel' => null,
'suffixLabel' => null,
'buttonBefore' => null,
'buttonAfter' => null,
// Diseño de disposición (columnas)
'inline' => false,
'labelCol' => 4,
'inputCol' => 8,
// Texto de ayuda
'helperText' => '',
// Opciones del select
'options' => [],
// Atributos adicionales
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Configuración de valores base**
$livewireModel = $attributes->get('wire:model', $model);
$name = $attributes->get('name', $livewireModel);
$inputId = $attributes->get('id', $name . '_' . $uid);
$placeholder = $attributes->get('placeholder', 'Seleccione ' . strtolower($label));
$selected = $attributes->get('selected', null);
// **Manejo de errores**
$errorKey = $livewireModel ?: $name;
$hasError = $errors->has($errorKey);
$errorClass = $hasError ? 'is-invalid' : '';
// **Clases dinámicas**
$select2Class = $select2 ? 'select2' : ''; // Agrega la clase `select2` si está habilitado
$sizeClass = match ($size) {
'sm' => 'form-select-sm',
'lg' => 'form-select-lg',
default => '',
};
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => '',
};
// **Construcción de clases dinámicas**
$fullClass = trim("form-select $select2Class $sizeClass $alignClass $errorClass");
$fullLabelClass = trim(($inline ? 'col-form-label col-md-' . $labelCol : 'form-label') . ' ' . $labelClass);
$containerClass = trim(($inline ? 'row' : '') . ' fv-row ' . ($mb0 ? '' : 'mb-4') . " $alignClass $parentClass");
// **Fusionar atributos con clases dinámicas**
$selectAttributes = $attributes->merge([
'id' => $inputId,
'name' => $name,
])->class($fullClass);
// **Detectar si se necesita input-group**
$requiresInputGroup = $prefixLabel || $suffixLabel || $buttonBefore || $buttonAfter;
@endphp
<div class="{{ $containerClass }}">
{{-- Etiqueta del select --}}
@if($label)
<label for="{{ $inputId }}" class="{{ $fullLabelClass }}">{{ $label }}</label>
@endif
@if($inline)
<div class="col-md-{{ $inputCol }}">
@endif
{{-- Input Group (Si tiene prefijo/sufijo) --}}
@if ($requiresInputGroup)
<div class="input-group input-group-merge">
@isset($buttonBefore)
<button class="btn btn-outline-primary waves-effect" type="button">{{ $buttonBefore }}</button>
@endisset
@if($prefixLabel)
<label class="input-group-text" for="{{ $inputId }}">{{ $prefixLabel }}</label>
@endif
{{-- Select --}}
<select {!! $selectAttributes !!} {{ $attributes->get('multiple') ? 'multiple' : '' }} {{ $attributes->get('disabled') ? 'disabled' : '' }} {{ $livewireModel ? "wire:model=$livewireModel" : '' }} {{ $select2 ? 'data-live-search="true"' : '' }}>
@if (!$attributes->get('multiple') && $placeholder)
<option value="">{{ $placeholder }}</option>
@endif
@foreach ($options as $key => $value)
<option value="{{ $key }}" {{ (string) $key === (string) $selected ? 'selected' : '' }}>
{{ $value }}
</option>
@endforeach
</select>
@if($suffixLabel)
<label class="input-group-text" for="{{ $inputId }}">{{ $suffixLabel }}</label>
@endif
@isset($buttonAfter)
<button class="btn btn-outline-primary waves-effect" type="button">{{ $buttonAfter }}</button>
@endisset
</div>
@else
{{-- Select sin prefijo/sufijo --}}
<select {!! $selectAttributes !!} {{ $attributes->get('multiple') ? 'multiple' : '' }} {{ $attributes->get('disabled') ? 'disabled' : '' }} {{ $livewireModel ? "wire:model=$livewireModel" : '' }} {{ $select2 ? 'data-live-search="true"' : '' }}>
@if (!$attributes->get('multiple') && $placeholder)
<option value="">{{ $placeholder }}</option>
@endif
@foreach ($options as $key => $value)
<option value="{{ $key }}" {{ (string) $key === (string) $selected ? 'selected' : '' }}>
{{ $value }}
</option>
@endforeach
</select>
@endif
{{-- Texto de ayuda --}}
@isset($helperText)
<div class="form-text">{{ $helperText }}</div>
@endisset
{{-- Errores de validación --}}
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
@if($inline)
</div>
@endif
</div>

View File

@ -0,0 +1,178 @@
@props([
// Identificador único
'uid' => uniqid(),
// Modelo para Livewire
'model' => '',
// Etiqueta y clases de la etiqueta
'label' => '',
'labelClass' => '',
// Clases generales
'align' => 'start', // Alineación del textarea (start, end)
'size' => 'md', // Tamaño del textarea (sm, md, lg)
'mb0' => false, // Remover margen inferior
'parentClass' => '',
// Elementos opcionales antes/después del textarea
'prefix' => null,
'suffix' => null,
// Íconos dentro del input
'prefixIcon' => null, // Ícono fijo a la izquierda
'prefixClickableIcon' => null, // Ícono con botón a la izquierda
'suffixIcon' => null, // Ícono fijo a la derecha
'suffixClickableIcon' => null, // Ícono con botón a la derecha
// Configuración del textarea
'rows' => 3,
'maxlength' => null,
'autosize' => false, // Autoajuste de altura
'resize' => true, // Permitir redimensionar
// Soporte para etiquetas flotantes
'floating' => false, // Si la etiqueta es flotante
// Texto de ayuda
'helperText' => '',
// Atributos adicionales para el textarea
'attributes' => new \Illuminate\View\ComponentAttributeBag([]),
])
@php
// **Generación de Name, ID y Model**
$livewireModel = $attributes->get('wire:model', $model);
$name = $attributes->get('name', $livewireModel);
$inputId = $attributes->get('id', $name . '_' . $uid);
// **Placeholder obligatorio si es flotante**
$placeholder = $attributes->get('placeholder', $floating ? ' ' : 'Ingrese ' . strtolower($label));
// **Manejo de errores**
$errorKey = $livewireModel ?: $name;
$hasError = $errors->has($errorKey);
$errorClass = $hasError ? 'is-invalid' : '';
// **Clases dinámicas**
$sizeClass = match ($size) {
'sm' => 'form-control-sm',
'lg' => 'form-control-lg',
default => '',
};
$alignClass = match ($align) {
'center' => 'text-center',
'end' => 'text-end',
default => '',
};
// **Control de redimensionamiento**
$resizeClass = $resize ? '' : 'resize-none';
// **Fusionar atributos del textarea**
$textareaAttributes = $attributes->merge([
'id' => $inputId,
'name' => $name,
'rows' => $rows,
'placeholder' => $placeholder,
'maxlength' => $maxlength,
])->class("form-control $sizeClass $resizeClass $errorClass");
// **Clases del contenedor flotante**
$floatingClass = $floating ? 'form-floating' : '';
// **Detectar si necesita Input-Group**
$requiresInputGroup = $prefix || $suffix || $prefixIcon || $suffixIcon || $prefixClickableIcon || $suffixClickableIcon;
@endphp
{{-- ============================ TEXTAREA ============================ --}}
<div class="{{ $mb0 ? '' : 'mb-4' }} {{ $parentClass }} {{ $alignClass }} {{ $floatingClass }}">
@if (!$floating && $label)
<label for="{{ $inputId }}" class="{{ $labelClass }}">{{ $label }}</label>
@endif
@if ($requiresInputGroup)
{{-- Textarea con Input-Group --}}
<div class="input-group">
{{-- Prefijos (Izquierda) --}}
@if ($prefix)
<span class="input-group-text">{{ $prefix }}</span>
@endif
@if ($prefixIcon)
<span class="input-group-text"><i class="{{ $prefixIcon }}"></i></span>
@endif
@if ($prefixClickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $prefixClickableIcon }}"></i>
</button>
@endif
{{-- Textarea --}}
<textarea
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{!! $textareaAttributes !!}
></textarea>
{{-- Sufijos (Derecha) --}}
@if ($suffixClickableIcon)
<button type="button" class="input-group-text cursor-pointer">
<i class="{{ $suffixClickableIcon }}"></i>
</button>
@endif
@if ($suffixIcon)
<span class="input-group-text"><i class="{{ $suffixIcon }}"></i></span>
@endif
@if ($suffix)
<span class="input-group-text">{{ $suffix }}</span>
@endif
</div>
@else
{{-- Textarea simple o flotante --}}
<textarea
{{ $livewireModel ? "wire:model=$livewireModel" : '' }}
{!! $textareaAttributes !!}
></textarea>
@endif
{{-- Etiqueta flotante --}}
@if ($floating)
<label for="{{ $inputId }}">{{ $label }}</label>
@endif
{{-- Longitud máxima permitida --}}
@if ($maxlength)
<small class="form-text float-end text-muted">Máximo {{ $maxlength }} caracteres</small>
@endif
{{-- Texto de ayuda --}}
@if ($helperText)
<div class="form-text">{{ $helperText }}</div>
@endif
{{-- Mensaje de error --}}
@if ($hasError)
<span class="text-danger">{{ $errors->first($errorKey) }}</span>
@endif
</div>
{{-- ============================ JAVASCRIPT PARA AUTOSIZE ============================ --}}
@if ($autosize)
<script>
document.addEventListener('DOMContentLoaded', function () {
let textarea = document.getElementById('{{ $inputId }}');
if (textarea) {
textarea.addEventListener('input', function () {
this.style.height = 'auto';
this.style.height = this.scrollHeight + 'px';
});
textarea.dispatchEvent(new Event('input')); // Auto-trigger on load
}
});
</script>
@endif