diff --git a/app/Http/Controllers/CreateProjectController.php b/app/Http/Controllers/CreateProjectController.php index 4b887563..ba476c60 100755 --- a/app/Http/Controllers/CreateProjectController.php +++ b/app/Http/Controllers/CreateProjectController.php @@ -34,6 +34,108 @@ class CreateProjectController extends Controller { + public function deleteFurtherTasks(Request $request) + { + $receiveDataEquipment = Equipment::where('equipment_id', $request->equipmentID)->first(); + // Buscar os registros que correspondem ao equipmentID e que têm further_tasks_id nos selectedTasks + $tasksToDelete = OrderEquipmentTasks::where('equipment_id', $request->equipmentID) + ->whereIn('further_tasks_id', $request->selectedTasks) + ->get(); + + // Excluir esses registros + foreach ($tasksToDelete as $task) { + $task->delete(); + } + + // Se o treatmentFurtherTask for "DeleteFurtherTask", exclua os registros da tabela principal FurtherTasks + if ($request->treatmentFurtherTask == "DeleteFurtherTask") { + FurtherTasks::whereIn('further_tasks_id', $request->selectedTasks)->delete(); + } + + // Reordenar os registros restantes + $remainingTasks = OrderEquipmentTasks::where('equipment_id', $request->equipmentID) + ->orderBy('execution_order', 'asc') + ->get(); + + $executionOrder = 1; + foreach ($remainingTasks as $task) { + $task->execution_order = $executionOrder; + $task->save(); + $executionOrder++; + } + return redirect()->back()->with('success', 'Ordem de execução do equipamento: ' . $receiveDataEquipment->equipment_tag . ' Atulizada!'); + } + + public function addFurtherTasks(Request $request) + { + // Recebe e organiza os dados do equipameto recebido : ($request->equipmentID) e organiza em asc de acordo com a Ordem de execução + $equipmentId = $request->equipmentID; + $tasksToReorder = OrderEquipmentTasks::where('equipment_id', $equipmentId) + ->orderBy('execution_order', 'asc') + ->get(); + + $receiveDataEquipment = Equipment::where('equipment_id', $request->equipmentID)->first(); + + + // *Para Criar uma nova Tarefa complementar deve ser a soma dos dados das 2 tabelas para dar o numero da proxima tarefa e assim o numero da TC + // Obtenha a contagem de registros nas tabelas ElementalTasks e FurtherTasks + $elementalTasksCount = ElementalTasks::count(); + $furtherTasksCount = FurtherTasks::count(); + + // Calcule o valor de further_tasks_id + $newFurtherTaskId = $elementalTasksCount + $furtherTasksCount + 1; + + // Calcule o valor de further_tasks_name + $newFurtherTaskName = 'TC' . ($furtherTasksCount + 1); + + + + $insertPosition = $request->ArrayListElementsTasks + 1; + + // Incrementar a execution_order das tarefas após a posição de inserção + foreach ($tasksToReorder as $task) { + if ($task->execution_order >= $insertPosition) { + $task->execution_order += 1; + $task->save(); + } + } + + // Agora, insira a nova tarefa na posição desejada + $newOrderEquipmentTask = new OrderEquipmentTasks; + $newOrderEquipmentTask->equipment_id = $equipmentId; + $newOrderEquipmentTask->execution_order = $insertPosition; + $newOrderEquipmentTask->elemental_tasks_id = null; + + // Se o selectedFurtherTaskExisting for null quer dizer que e uma TC complementar criada e nova se nao for null quer dizer que vamos criar uma TC existente. + if ($request->selectedFurtherTaskExisting == 'null') { + + // Cria uma nova tarefa Complementar + $newFurtherTask = new FurtherTasks; + $newFurtherTask->further_tasks_id = $newFurtherTaskId; + $newFurtherTask->further_tasks_name = $newFurtherTaskName; + $newFurtherTask->further_tasks_description = $request->furtherTask; + $newFurtherTask->company_projects_id = $receiveDataEquipment->company_projects_id; + $newFurtherTask->save(); + + $newOrderEquipmentTask->further_tasks_id = $newFurtherTask->further_tasks_id; + } else { + $newOrderEquipmentTask->further_tasks_id = $request->selectedFurtherTaskExisting; + } + + $newOrderEquipmentTask->inspection = 'Nao'; + $newOrderEquipmentTask->save(); + + + + + + + + + return redirect()->back()->with('success', 'Ordem de execução do equipamento: ' . $receiveDataEquipment->equipment_tag . ' Atulizada!'); + } + + public function receiveEquipmentToAssociateTasks(Request $request) { // dd($request); diff --git a/app/Livewire/Articulado/AdditonalTask.php b/app/Livewire/Articulado/AdditonalTask.php new file mode 100644 index 00000000..9e9e1ea2 --- /dev/null +++ b/app/Livewire/Articulado/AdditonalTask.php @@ -0,0 +1,52 @@ +equipment = $equipment; + + $this->tasks = OrderEquipmentTasks::where('equipment_id', $this->equipment->equipment_id) + ->orderBy('execution_order', 'asc') + ->get(); + + // Coletando todos os registros onde further_tasks_id é diferente de null + $this->furtherTaskRecords = OrderEquipmentTasks::where('equipment_id', $this->equipment->equipment_id) + ->whereNotNull('further_tasks_id') + ->get(); + + // Coletando todos os further_tasks_id da coleção $tasks que são diferentes de null + $existingFurtherTaskIds = $this->tasks->whereNotNull('further_tasks_id')->pluck('further_tasks_id'); + + // Buscando furtherTasks que não estão presentes em $tasks + $this->furtherTasks = FurtherTasks::where('company_projects_id', $this->equipment->company_projects_id) + ->whereNotIn('further_tasks_id', $existingFurtherTaskIds) + ->get(); + } + + public function toggle() + { + + $this->showAdditionalTask = !$this->showAdditionalTask; + } + public function render() + { + return view('livewire.articulado.additonal-task'); + } +} diff --git a/app/Models/FurtherTasks.php b/app/Models/FurtherTasks.php index 09b11b76..71e8f356 100755 --- a/app/Models/FurtherTasks.php +++ b/app/Models/FurtherTasks.php @@ -15,5 +15,8 @@ class FurtherTasks extends Model protected $primaryKey = 'further_tasks_id'; - + public function orderEquipmentTasks() + { + return $this->hasMany(OrderEquipmentTasks::class,'further_tasks_id','further_tasks_id'); + } } diff --git a/app/Models/OrderEquipmentTasks.php b/app/Models/OrderEquipmentTasks.php index 1d02fe95..4ec4997b 100755 --- a/app/Models/OrderEquipmentTasks.php +++ b/app/Models/OrderEquipmentTasks.php @@ -12,6 +12,8 @@ class OrderEquipmentTasks extends Model protected $table = 'ordered_equipment_tasks'; + // protected $primaryKey = 'ordered_equipment_tasks_id'; + public function equipment() { return $this->belongsTo(Equipment::class, 'equipment_id', 'equipment_id'); @@ -21,4 +23,9 @@ public function elementalTask() { return $this->belongsTo(ElementalTasks::class,'elemental_tasks_id','elemental_tasks_id'); } + + public function furtherTasks() + { + return $this->belongsTo(FurtherTasks::class,'further_tasks_id','further_tasks_id'); + } } diff --git a/composer.json b/composer.json index 8b575a69..4dce9de1 100755 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "laravel/framework": "^10.8", "laravel/sanctum": "^3.2", "laravel/tinker": "^2.8", + "livewire/livewire": "^3.0", "phpoffice/phpspreadsheet": "^1.28", "symfony/http-client": "^6.2", "symfony/mailgun-mailer": "^6.2", diff --git a/composer.lock b/composer.lock index 0adf23ac..eeff68f2 100755 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "51ebbc942093bdb678f8cecc025df588", + "content-hash": "a57e63e19cef626ac7d557917fce5eb3", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1930,6 +1930,79 @@ ], "time": "2022-04-17T13:12:02+00:00" }, + { + "name": "livewire/livewire", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "2e426e8d47e03c4777334ec0c8397341bcfa15f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/2e426e8d47e03c4777334ec0c8397341bcfa15f3", + "reference": "2e426e8d47e03c4777334ec0c8397341bcfa15f3", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0", + "illuminate/validation": "^10.0|^11.0", + "league/mime-type-detection": "^1.9", + "php": "^8.1", + "symfony/http-kernel": "^5.0|^6.0" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^10.0|^11.0", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench-dusk": "^7.0|^8.0", + "phpunit/phpunit": "^9.0", + "psy/psysh": "@stable" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Livewire\\LivewireServiceProvider" + ], + "aliases": { + "Livewire": "Livewire\\Livewire" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2023-08-25T18:13:03+00:00" + }, { "name": "maennchen/zipstream-php", "version": "v2.4.0", diff --git a/config/livewire.php b/config/livewire.php new file mode 100644 index 00000000..31afc042 --- /dev/null +++ b/config/livewire.php @@ -0,0 +1,158 @@ + 'App\\Livewire', + + /* + |--------------------------------------------------------------------------- + | View Path + |--------------------------------------------------------------------------- + | + | This value is used to specify where Livewire component Blade templates are + | stored when running file creation commands like `artisan make:livewire`. + | It is also used if you choose to omit a component's render() method. + | + */ + + 'view_path' => resource_path('views/livewire'), + + /* + |--------------------------------------------------------------------------- + | Layout + |--------------------------------------------------------------------------- + | The view that will be used as the layout when rendering a single component + | as an entire page via `Route::get('/post/create', CreatePost::class);`. + | In this case, the view returned by CreatePost will render into $slot. + | + */ + + 'layout' => 'components.layouts.app', + + /* + |--------------------------------------------------------------------------- + | Lazy Loading Placeholder + |--------------------------------------------------------------------------- + | Livewire allows you to lazy load components that would otherwise slow down + | the initial page load. Every component can have a custom placeholder or + | you can define the default placeholder view for all components below. + | + */ + + 'lazy_placeholder' => null, + + /* + |--------------------------------------------------------------------------- + | Temporary File Uploads + |--------------------------------------------------------------------------- + | + | Livewire handles file uploads by storing uploads in a temporary directory + | before the file is stored permanently. All file uploads are directed to + | a global endpoint for temporary storage. You may configure this below: + | + */ + + 'temporary_file_upload' => [ + 'disk' => null, // Example: 'local', 's3' | Default: 'default' + 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) + 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' + 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' + 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... + 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', + 'mov', 'avi', 'wmv', 'mp3', 'm4a', + 'jpg', 'jpeg', 'mpga', 'webp', 'wma', + ], + 'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated... + ], + + /* + |--------------------------------------------------------------------------- + | Render On Redirect + |--------------------------------------------------------------------------- + | + | This value determines if Livewire will run a component's `render()` method + | after a redirect has been triggered using something like `redirect(...)` + | Setting this to true will render the view once more before redirecting + | + */ + + 'render_on_redirect' => false, + + /* + |--------------------------------------------------------------------------- + | Eloquent Model Binding + |--------------------------------------------------------------------------- + | + | Previous versions of Livewire supported binding directly to eloquent model + | properties using wire:model by default. However, this behavior has been + | deemed too "magical" and has therefore been put under a feature flag. + | + */ + + 'legacy_model_binding' => false, + + /* + |--------------------------------------------------------------------------- + | Auto-inject Frontend Assets + |--------------------------------------------------------------------------- + | + | By default, Livewire automatically injects its JavaScript and CSS into the + | and of pages containing Livewire components. By disabling + | this behavior, you need to use @livewireStyles and @livewireScripts. + | + */ + + 'inject_assets' => true, + + /* + |--------------------------------------------------------------------------- + | Navigate (SPA mode) + |--------------------------------------------------------------------------- + | + | By adding `wire:navigate` to links in your Livewire application, Livewire + | will prevent the default link handling and instead request those pages + | via AJAX, creating an SPA-like effect. Configure this behavior here. + | + */ + + 'navigate' => [ + 'show_progress_bar' => true, + ], + + /* + |--------------------------------------------------------------------------- + | HTML Morph Markers + |--------------------------------------------------------------------------- + | + | Livewire intelligently "morphs" existing HTML into the newly rendered HTML + | after each update. To make this process more reliable, Livewire injects + | "markers" into the rendered Blade surrounding @if, @class & @foreach. + | + */ + + 'inject_morph_markers' => true, + + /* + |--------------------------------------------------------------------------- + | Pagination Theme + |--------------------------------------------------------------------------- + | + | When enabling Livewire's pagination feature by using the `WithPagination` + | trait, Livewire will use Tailwind templates to render pagination views + | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" + | + */ + + 'pagination_theme' => 'tailwind', +]; diff --git a/public/assets/dist/css/adminlte.css b/public/assets/dist/css/adminlte.css index b7600469..466c03d7 100644 --- a/public/assets/dist/css/adminlte.css +++ b/public/assets/dist/css/adminlte.css @@ -54,6 +54,11 @@ :root { --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } +.scrollable-list { + max-height: calc(1.5em + .75rem + 2px + 2px + 1.5em + .75rem + 2px + 2px + 1.5em + .75rem + 2px + 2px + 1.5em + .75rem + 2px); /* Altura de 4 itens de lista */ + overflow-y: auto; +} + /* PT_Sans */ @font-face { font-family: 'PT_Sans'; diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php new file mode 100644 index 00000000..ed9bf3d3 --- /dev/null +++ b/resources/views/components/layouts/app.blade.php @@ -0,0 +1,13 @@ + + + + + + @livewireStyles + {{ $title ?? 'Page Title' }} + + + {{ $slot }} + @livewireScripts + + diff --git a/resources/views/livewire/articulado/additonal-task.blade.php b/resources/views/livewire/articulado/additonal-task.blade.php new file mode 100644 index 00000000..70215227 --- /dev/null +++ b/resources/views/livewire/articulado/additonal-task.blade.php @@ -0,0 +1,144 @@ + diff --git a/resources/views/projectsClients/articulated_2.blade.php b/resources/views/projectsClients/articulated_2.blade.php index 08c1fcd1..66cc514a 100755 --- a/resources/views/projectsClients/articulated_2.blade.php +++ b/resources/views/projectsClients/articulated_2.blade.php @@ -684,7 +684,6 @@ class="form-control card_inputs" id="isolationEquipment" -
@@ -1263,7 +1262,7 @@ class="form-control card_inputs" id="equipmentTag" placeholder="Tag..." aria-label="Tag Equipment" aria-describedby="form-tagEquipment"> - + {{-- --}}
@@ -1283,7 +1282,7 @@ class="form-control card_inputs" placeholder="Descrição Equipamento..." aria-label="Tag Equipment" aria-describedby="form-equipmentDescription"> - + {{-- --}} @@ -1307,7 +1306,7 @@ class="form-control card_inputs" placeholder="Número de série" aria-label="Serial Number Equipment" aria-describedby="form-serialNumberEquipment"> - + {{-- --}} @@ -1325,7 +1324,7 @@ class="form-control card_inputs" id="equipmentBrand" placeholder="Marca" aria-label="Serial Number Equipment" aria-describedby="form-equipmentBrand"> - + {{-- --}} @@ -1343,7 +1342,7 @@ class="form-control card_inputs" id="equipmentModel" placeholder="Modelo" aria-label="Serial Number Equipment" aria-describedby="form-equipmentModel"> - + {{-- --}} @@ -1364,7 +1363,7 @@ class="form-control card_inputs" id="dimension" placeholder="Dimensão" aria-label="Serial Number Equipment" aria-describedby="form-dimension"> - + {{-- --}} @@ -1381,7 +1380,7 @@ class="form-control card_inputs" id="rating" placeholder="Rating..." aria-label="Serial Number Equipment" aria-describedby="form-rating"> - + {{-- --}} @@ -1398,7 +1397,7 @@ class="form-control card_inputs" id="dim_certa" placeholder="Dim certa..." aria-label="Serial Number Equipment" aria-describedby="form-dim_certa"> - + {{-- --}} @@ -1421,7 +1420,7 @@ class="form-control card_inputs" placeholder="Main Equipment" aria-label="Main Equipment" aria-describedby="form-main_equipment"> - + {{-- --}} @@ -1437,7 +1436,7 @@ class="form-control card_inputs" id="p&id" placeholder="P&ID" aria-label="P & id" aria-describedby="form-p&id"> - + {{-- --}} @@ -1453,7 +1452,7 @@ class="form-control card_inputs" id="sap_number" placeholder="Nº SAP" aria-label="Numero Sap" aria-describedby="form-sap_number"> - + {{-- --}} @@ -1476,7 +1475,7 @@ class="form-control card_inputs" placeholder="SP (Bar) Cold" aria-label="SP (Bar) Cold" aria-describedby="form-SP_(Bar)_Cold"> - + {{-- --}} @@ -1494,7 +1493,7 @@ class="form-control card_inputs" placeholder="Back Presure (Bar)" aria-label="Back Presure (Bar)" aria-describedby="form-Back_Presure_(Bar)"> - + {{-- --}} @@ -1511,7 +1510,7 @@ class="form-control card_inputs" id="material" placeholder="Material" aria-label="Material" aria-describedby="form-material"> - + {{-- --}} @@ -1533,7 +1532,7 @@ class="form-control card_inputs" id="manufacturer" placeholder="Fabricante" aria-label="Fabricante" aria-describedby="form-manufacturer"> - + {{-- --}} @@ -1549,7 +1548,7 @@ class="form-control card_inputs" id="isolation" placeholder="Isolamento" aria-label="Isolamento" aria-describedby="form-isolation"> - + {{-- --}} @@ -1570,7 +1569,7 @@ class="form-control card_inputs" - + {{-- --}} @@ -1586,7 +1585,7 @@ class="form-control card_inputs" - + {{-- --}} @@ -1813,12 +1812,12 @@ class="form-control card_inputs"
-

Fluxo da Tarefa

+

Fluxo de Tarefas, Equipamento: {{$listEquipmentsProject->equipment_tag}}

-

Indique a tarefa :

+

Indique a tarefa complementar :

@@ -1873,11 +1872,11 @@ class="btn btn-outline-success ">Adicionar
- + {{-- --}}
@@ -1897,7 +1896,7 @@ class="form-control card_inputs" placeholder="Descrição Equipamento..." aria-label="Tag Equipment" aria-describedby="form-equipmentDescription"> - + {{-- --}}
@@ -1922,7 +1921,7 @@ class="form-control card_inputs" placeholder="Número de série" aria-label="Serial Number Equipment" aria-describedby="form-serialNumberEquipment"> - + {{-- --}} @@ -1940,7 +1939,7 @@ class="form-control card_inputs" id="equipmentBrand" placeholder="Marca" aria-label="Marca Equipamento" aria-describedby="form-equipmentBrand"> - + {{-- --}} @@ -1958,7 +1957,7 @@ class="form-control card_inputs" id="equipmentModel" placeholder="Modelo" aria-label="Modelo Equipamento" aria-describedby="form-equipmentModel"> - + {{-- --}} @@ -1979,7 +1978,7 @@ class="form-control card_inputs" id="dimension" placeholder="Dimensão" aria-label="Dimensao Equipamento" aria-describedby="form-dimension"> - + {{-- --}} @@ -1995,7 +1994,7 @@ class="form-control card_inputs" id="rating" placeholder="Rating..." aria-label="Rating Equipamento" aria-describedby="form-rating"> - + {{-- --}} @@ -2011,7 +2010,7 @@ class="form-control card_inputs" id="dim_certa" placeholder="Dim certa..." aria-label="Dim certa Equipamento" aria-describedby="form-dim_certa"> - + {{-- --}} @@ -2034,7 +2033,7 @@ class="form-control card_inputs" placeholder="Main Equipment" aria-label="Main Equipment" aria-describedby="form-main_equipment"> - + {{-- --}} @@ -2051,7 +2050,7 @@ class="form-control card_inputs" id="P_idEquipment" placeholder="P&ID" aria-label="P&ID" aria-describedby="form-P_IidEquipment"> - + {{-- --}} @@ -2068,7 +2067,7 @@ class="form-control card_inputs" id="NumberSapEquipment" placeholder="Nº SAP" aria-label="Numero SAP Equipamento" aria-describedby="form-NumberSapEquipment"> - + {{-- --}} @@ -2091,7 +2090,7 @@ class="form-control card_inputs" placeholder="Material" aria-label="Material Equipamento" aria-describedby="form-materialEquipment"> - + {{-- --}} @@ -2109,7 +2108,7 @@ class="form-control card_inputs" placeholder="Fabricante" aria-label="Fabricante Equipamento" aria-describedby="form-manufacturerEquipment"> - + {{-- --}} @@ -2127,7 +2126,7 @@ class="form-control card_inputs" placeholder="Isolamento" aria-label="Isolamento Equipamento" aria-describedby="form-isolationEquipment"> - + {{-- --}} @@ -2148,7 +2147,7 @@ class="form-control card_inputs" - + {{-- --}} @@ -2164,7 +2163,7 @@ class="form-control card_inputs" - + {{-- --}} @@ -2197,6 +2196,7 @@ class="form-control card_inputs"
+ @@ -2360,60 +2360,9 @@ class="form-control card_inputs" - + @livewire('articulado.additonal-task',['equipment' => $listEquipmentsProject], key($listEquipmentsProject->equipment_id)) - -
-
-
-
-
-

Fluxo da Tarefa

-

-
-
-
-

Indique a tarefa :

- -
-
-

Selecione Após qual tarefa :

- -
-
-
-
-
-
- Adicionar -
-
-
-
-
-
{{-- ./description --}} {{-- ./card-body --}} @@ -2444,7 +2393,7 @@ class="form-control card_inputs" id="equipmentTag" placeholder="Tag..." aria-label="Tag Equipment" aria-describedby="form-tagEquipment"> - + {{-- --}} @@ -2464,7 +2413,7 @@ class="form-control card_inputs" placeholder="Descrição Equipamento..." aria-label="equipmentDescription" aria-describedby="form-equipmentDescription"> - + {{-- --}} @@ -2488,7 +2437,7 @@ class="form-control card_inputs" placeholder="Número de série" aria-label="Serial Number Equipment" aria-describedby="form-serialNumberEquipment"> - + {{-- --}} @@ -2506,7 +2455,7 @@ class="form-control card_inputs" id="equipmentBrand" placeholder="Modelo" aria-label="Marca Equipamento" aria-describedby="form-equipmentBrand"> - + {{-- --}} @@ -2524,7 +2473,7 @@ class="form-control card_inputs" id="equipmentModel" placeholder="Modelo" aria-label="Modelo Equipamento" aria-describedby="form-equipmentModel"> - + {{-- --}} @@ -2544,7 +2493,7 @@ class="form-control card_inputs" id="dimension" placeholder="Dimensão" aria-label="Dimensao Equipamento" aria-describedby="form-dimension"> - + {{-- --}} @@ -2561,7 +2510,7 @@ class="form-control card_inputs" id="rating" placeholder="Rating..." aria-label="Rating Equipamento" aria-describedby="form-rating"> - + {{-- --}} @@ -2578,7 +2527,7 @@ class="form-control card_inputs" id="dim_certa" placeholder="Dim certa..." aria-label="Dim certa Equipamento" aria-describedby="form-dim_certa"> - + {{-- --}} @@ -2600,7 +2549,7 @@ class="form-control card_inputs" placeholder="Main Equipment" aria-label="Main Equipment" aria-describedby="form-main_equipment"> - + {{-- --}} @@ -2617,7 +2566,7 @@ class="form-control card_inputs" id="p&id" placeholder="P&ID" aria-label="P&ID" aria-describedby="form-P_IidEquipment"> - + {{-- --}} @@ -2634,7 +2583,7 @@ class="form-control card_inputs" id="sap_number" placeholder="Nº SAP" aria-label="Numero SAP Equipamento" aria-describedby="form-NumberSapEquipment"> - + {{-- --}} @@ -2655,7 +2604,7 @@ class="form-control card_inputs" id="material" placeholder="Material" aria-label="Material Equipamento" aria-describedby="form-materialEquipment"> - + {{-- --}} @@ -2673,7 +2622,7 @@ class="form-control card_inputs" placeholder="Fabricante" aria-label="Fabricante Equipamento" aria-describedby="form-manufacturerEquipment"> - + {{-- --}} @@ -2690,7 +2639,7 @@ class="form-control card_inputs" id="isolation" placeholder="Isolamento" aria-label="Isolamento Equipamento" aria-describedby="form-isolationEquipment"> - + {{-- --}} @@ -2712,7 +2661,7 @@ class="form-control card_inputs" placeholder="Fabricante do atuador" aria-label="Fabricante do Atuador" aria-describedby="form-actuatorManufacturer"> - + {{-- --}} @@ -2730,7 +2679,7 @@ class="form-control card_inputs" placeholder="Modelo do atuador" aria-label="Modelo do atuador" aria-describedby="form-ActuatorModel"> - + {{-- --}} @@ -2748,7 +2697,7 @@ class="form-control card_inputs" placeholder="N.º de série do atuador" aria-label="Numero de série do atuado" aria-describedby="form-actuatorSerialNumber"> - + {{-- --}} @@ -2770,7 +2719,7 @@ class="form-control card_inputs" placeholder="Fabricante do posicionador" aria-label="Fabricante do posicionador" aria-describedby="form-PositionerManufacturer"> - + {{-- --}} @@ -2788,7 +2737,7 @@ class="form-control card_inputs" placeholder="N.º de série do posicionador" aria-label="Numero de série do posicionador" aria-describedby="form-PositionerSerialNumber"> - + {{-- --}} @@ -3013,11 +2962,13 @@ class="form-control card_inputs"
+
+
-

Fluxo da Tarefa

+

Fluxo de Tarefas, Equipamento:

@@ -3598,7 +3549,6 @@ function(task) { }); - + + + {{ $slot }} + + @stack('scripts') + + + + diff --git a/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/changed-tracked-layout.blade.php b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/changed-tracked-layout.blade.php new file mode 100644 index 00000000..ca54436b --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/changed-tracked-layout.blade.php @@ -0,0 +1,13 @@ + + + + + + + {{ $slot }} + + @stack('scripts') + + + + diff --git a/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout-with-navigate-outside.blade.php b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout-with-navigate-outside.blade.php new file mode 100644 index 00000000..c326730c --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout-with-navigate-outside.blade.php @@ -0,0 +1,15 @@ + + + + + + + Go to second page (outside) + + {{ $slot }} + + @stack('scripts') + + + + diff --git a/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout.blade.php b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout.blade.php new file mode 100644 index 00000000..e496e85d --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/layout.blade.php @@ -0,0 +1,13 @@ + + + + + + + {{ $slot }} + + @stack('scripts') + + + + diff --git a/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/test-navigate-asset.js b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/test-navigate-asset.js new file mode 100644 index 00000000..a9c56bdf --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/test-navigate-asset.js @@ -0,0 +1,8 @@ + +if (! window._lw_dusk_asset_count) { + window._lw_dusk_asset_count = 1 +} else { + window._lw_dusk_asset_count++ +} + + diff --git a/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/tracked-layout.blade.php b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/tracked-layout.blade.php new file mode 100644 index 00000000..420f6c6e --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNavigate/test-views/tracked-layout.blade.php @@ -0,0 +1,13 @@ + + + + + + + {{ $slot }} + + @stack('scripts') + + + + diff --git a/vendor/livewire/livewire/src/Features/SupportNestingComponents/SupportNestingComponents.php b/vendor/livewire/livewire/src/Features/SupportNestingComponents/SupportNestingComponents.php new file mode 100644 index 00000000..951b8257 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportNestingComponents/SupportNestingComponents.php @@ -0,0 +1,91 @@ +"; + + static::setParentChild($parent, $key, $tag, $childId); + + $hijack($finish($html)); + } + }); + + on('mount', function ($component, $params, $key, $parent) { + $start = null; + if ($parent && config('app.debug')) $start = microtime(true); + + static::setParametersToMatchingProperties($component, $params); + + return function ($html) use ($component, $key, $parent, $start) { + if ($parent) { + if (config('app.debug')) trigger('profile', 'child:'.$component->getId(), $parent->getId(), [$start, microtime(true)]); + + preg_match('/<([a-zA-Z0-9\-]*)/', $html, $matches, PREG_OFFSET_CAPTURE); + $tag = $matches[1][0]; + static::setParentChild($parent, $key, $tag, $component->getId()); + } + }; + }); + } + + function hydrate($memo) + { + $children = $memo['children']; + + static::setPreviouslyRenderedChildren($this->component, $children); + } + + function dehydrate($context) + { + $skipRender = $this->storeGet('skipRender'); + + if ($skipRender) $this->keepRenderedChildren(); + + $context->addMemo('children', $this->getChildren()); + } + + function getChildren() { return $this->storeGet('children', []); } + function setChild($key, $tag, $id) { $this->storePush('children', [$tag, $id], $key); } + + static function setParentChild($parent, $key, $tag, $id) { store($parent)->push('children', [$tag, $id], $key); } + static function setPreviouslyRenderedChildren($component, $children) { store($component)->set('previousChildren', $children); } + static function hasPreviouslyRenderedChild($parent, $key) { + return array_key_exists($key, store($parent)->get('previousChildren', [])); + } + + static function getPreviouslyRenderedChild($parent, $key) + { + return store($parent)->get('previousChildren')[$key]; + } + + function keepRenderedChildren() + { + $this->storeSet('children', $this->storeGet('previousChildren')); + } + + static function setParametersToMatchingProperties($component, $params) + { + // Assign all public component properties that have matching parameters. + collect(array_intersect_key($params, Utils::getPublicPropertiesDefinedOnSubclass($component))) + ->each(function ($value, $property) use ($component) { + $component->{$property} = $value; + }); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPageComponents/BaseLayout.php b/vendor/livewire/livewire/src/Features/SupportPageComponents/BaseLayout.php new file mode 100644 index 00000000..8de9e7a6 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPageComponents/BaseLayout.php @@ -0,0 +1,14 @@ +mount($this::class, $params); + }); + + $layoutConfig = $layoutConfig ?: new LayoutConfig; + + $layoutConfig->normalizeViewNameAndParamsForBladeComponents(); + + return SupportPageComponents::renderContentsIntoLayout($html, $layoutConfig); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPageComponents/LayoutConfig.php b/vendor/livewire/livewire/src/Features/SupportPageComponents/LayoutConfig.php new file mode 100644 index 00000000..44e00b39 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPageComponents/LayoutConfig.php @@ -0,0 +1,58 @@ +view = $view ?: config('livewire.layout'); + $this->viewContext = new ViewContext; + } + + function mergeParams($toMerge) + { + $this->params = array_merge($toMerge, $this->params); + } + + function normalizeViewNameAndParamsForBladeComponents() + { + // If a user passes the class name of a Blade component to the + // layout macro (or uses inside their config), we need to + // convert it to it's "view" name so Blade doesn't break. + $view = $this->view; + $params = $this->params; + + $attributes = $params['attributes'] ?? []; + unset($params['attributes']); + + if (is_subclass_of($view, \Illuminate\View\Component::class)) { + $layout = app()->makeWith($view, $params); + $view = $layout->resolveView()->name(); + } else { + $layout = new AnonymousComponent($view, $params); + } + + $layout->withAttributes($attributes); + + $params = array_merge($params, $layout->data()); + + $this->view = $view; + $this->params = $params; + + // Remove default slot if present... + if (isset($this->slots['default'])) unset($this->slots['default']); + + return $this; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPageComponents/MissingLayoutException.php b/vendor/livewire/livewire/src/Features/SupportPageComponents/MissingLayoutException.php new file mode 100644 index 00000000..9b877524 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPageComponents/MissingLayoutException.php @@ -0,0 +1,13 @@ +layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->mergeParams($data); + + return $this; + }); + + View::macro('section', function ($section) { + if (! isset($this->layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->slotOrSection = $section; + + return $this; + }); + + View::macro('title', function ($title) { + if (! isset($this->layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->mergeParams(['title' => $title]); + + return $this; + }); + + View::macro('slot', function ($slot) { + if (! isset($this->layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->slotOrSection = $slot; + + return $this; + }); + + View::macro('extends', function ($view, $params = []) { + if (! isset($this->layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->type = 'extends'; + $this->layoutConfig->slotOrSection = 'content'; + $this->layoutConfig->view = $view; + $this->layoutConfig->mergeParams($params); + + return $this; + }); + + View::macro('layout', function ($view, $params = []) { + if (! isset($this->layoutConfig)) $this->layoutConfig = new LayoutConfig; + + $this->layoutConfig->type = 'component'; + $this->layoutConfig->slotOrSection = 'slot'; + $this->layoutConfig->view = $view; + $this->layoutConfig->mergeParams($params); + + return $this; + }); + } + + static function interceptTheRenderOfTheComponentAndRetreiveTheLayoutConfiguration($callback) + { + $layoutConfig = null; + $slots = []; + + // Only run this handler once for the parent-most component. Otherwise child components + // will run this handler too and override the configured layout... + $handler = once(function ($target, $view, $data) use (&$layoutConfig, &$slots) { + $layoutAttr = $target->getAttributes()->whereInstanceOf(BaseLayout::class)->first(); + $titleAttr = $target->getAttributes()->whereInstanceOf(BaseTitle::class)->first(); + + if ($layoutAttr) { + $view->layout($layoutAttr->name, $layoutAttr->params); + } + + if ($titleAttr) { + $view->title($titleAttr->content); + } + + $layoutConfig = $view->layoutConfig ?? new LayoutConfig; + + return function ($html, $replace, $viewContext) use ($view, $layoutConfig) { + // Gather up any slots and sections declared in the component template and store them + // to be later forwarded into the layout component itself... + $layoutConfig->viewContext = $viewContext; + }; + }); + + on('render', $handler); + + $callback(); + + off('render', $handler); + + return $layoutConfig; + } + + static function gatherMountMethodParamsFromRouteParameters($component) + { + // This allows for route parameters like "slug" in /post/{slug}, + // to be passed into a Livewire component's mount method... + $route = request()->route(); + + if (! $route) return []; + + try { + $params = (new ImplicitRouteBinding(app())) + ->resolveAllParameters($route, new $component); + } catch (ModelNotFoundException $exception) { + if (method_exists($route,'getMissing') && $route->getMissing()) { + abort( + $route->getMissing()(request()) + ); + } + + throw $exception; + } + + return $params; + } + + static function renderContentsIntoLayout($content, $layoutConfig) + { + try { + if ($layoutConfig->type === 'component') { + return Blade::render(<<<'HTML' + viewContext->mergeIntoNewEnvironment($__env); ?> + + @component($layout->view, $layout->params) + @slot($layout->slotOrSection) + {!! $content !!} + @endslot + + viewContext->slots) as $name => $slot) { + $__env->slot($name, attributes: $slot->attributes->getAttributes()); + echo $slot->toHtml(); + $__env->endSlot(); + } + ?> + @endcomponent + HTML, [ + 'content' => $content, + 'layout' => $layoutConfig, + ]); + } else { + return Blade::render(<<<'HTML' + viewContext->mergeIntoNewEnvironment($__env); ?> + + @extends($layout->view, $layout->params) + + @section($layout->slotOrSection) + {!! $content !!} + @endsection + HTML, [ + 'content' => $content, + 'layout' => $layoutConfig, + ]); + } + } catch (\Illuminate\View\ViewException $e) { + $layout = $layoutConfig->view; + + if (str($e->getMessage())->startsWith('View ['.$layout.'] not found.')) { + throw new MissingLayoutException($layout); + } else { + throw $e; + } + } + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/HandlesPagination.php b/vendor/livewire/livewire/src/Features/SupportPagination/HandlesPagination.php new file mode 100644 index 00000000..c535b4b1 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/HandlesPagination.php @@ -0,0 +1,71 @@ +paginators)->mapWithKeys(function ($page, $pageName) { + return ['paginators.'.$pageName => ['history' => true, 'as' => $pageName, 'keep' => false]]; + })->toArray(); + } + + public function getPage($pageName = 'page') + { + return $this->paginators[$pageName] ?? 1; + } + + public function previousPage($pageName = 'page') + { + $this->setPage(max(($this->paginators[$pageName] ?? 1) - 1, 1), $pageName); + } + + public function nextPage($pageName = 'page') + { + $this->setPage(($this->paginators[$pageName] ?? 1) + 1, $pageName); + } + + public function gotoPage($page, $pageName = 'page') + { + $this->setPage($page, $pageName); + } + + public function resetPage($pageName = 'page') + { + $this->setPage(1, $pageName); + } + + public function setPage($page, $pageName = 'page') + { + if (is_numeric($page)) { + $page = (int) ($page <= 0 ? 1 : $page); + } + + $beforePaginatorMethod = 'updatingPaginators'; + $afterPaginatorMethod = 'updatedPaginators'; + + $beforeMethod = 'updating' . $pageName; + $afterMethod = 'updated' . $pageName; + + if (method_exists($this, $beforePaginatorMethod)) { + $this->{$beforePaginatorMethod}($page, $pageName); + } + + if (method_exists($this, $beforeMethod)) { + $this->{$beforeMethod}($page, null); + } + + $this->paginators[$pageName] = $page; + + if (method_exists($this, $afterPaginatorMethod)) { + $this->{$afterPaginatorMethod}($page, $pageName); + } + + if (method_exists($this, $afterMethod)) { + $this->{$afterMethod}($page, null); + } + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/SupportPagination.php b/vendor/livewire/livewire/src/Features/SupportPagination/SupportPagination.php new file mode 100644 index 00000000..02a47240 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/SupportPagination.php @@ -0,0 +1,128 @@ +provide(function () { + $this->loadViewsFrom(__DIR__.'/views', 'livewire'); + + $paths = [__DIR__.'/views' => resource_path('views/vendor/livewire')]; + + $this->publishes($paths, 'livewire'); + $this->publishes($paths, 'livewire:pagination'); + }); + } + + protected $restoreOverriddenPaginationViews; + + function boot() + { + $this->setPageResolvers(); + + $this->overrideDefaultPaginationViews(); + } + + function destroy() + { + ($this->restoreOverriddenPaginationViews)(); + } + + function overrideDefaultPaginationViews() + { + $oldDefaultView = Paginator::$defaultView; + $oldDefaultSimpleView = Paginator::$defaultSimpleView; + + $this->restoreOverriddenPaginationViews = function () use ($oldDefaultView, $oldDefaultSimpleView) { + Paginator::defaultView($oldDefaultView); + Paginator::defaultSimpleView($oldDefaultSimpleView); + }; + + Paginator::defaultView($this->paginationView()); + Paginator::defaultSimpleView($this->paginationSimpleView()); + } + + protected function setPageResolvers() + { + CursorPaginator::currentCursorResolver(function ($pageName) { + $this->ensurePaginatorIsInitialized($pageName, defaultPage: ''); + + return Cursor::fromEncoded($this->component->paginators[$pageName]); + }); + + Paginator::currentPageResolver(function ($pageName) { + $this->ensurePaginatorIsInitialized($pageName); + + return (int) $this->component->paginators[$pageName]; + }); + } + + protected function ensurePaginatorIsInitialized($pageName, $defaultPage = 1) + { + if (isset($this->component->paginators[$pageName])) return; + + $queryStringDetails = $this->getQueryStringDetails($pageName); + + $this->component->paginators[$pageName] = $this->resolvePage($queryStringDetails['as'], $defaultPage); + + $this->addUrlHook($pageName, $queryStringDetails); + } + + protected function getQueryStringDetails($pageName) + { + $pageNameQueryString = data_get($this->getQueryString(), 'paginators.' . $pageName); + + $pageNameQueryString['as'] ??= $pageName; + $pageNameQueryString['history'] ??= true; + $pageNameQueryString['keep'] ??= false; + + return $pageNameQueryString; + } + + protected function resolvePage($alias, $default) + { + return request()->query($alias, $default); + } + + protected function addUrlHook($pageName, $queryStringDetails) + { + $key = 'paginators.' . $pageName; + $alias = $queryStringDetails['as']; + $history = $queryStringDetails['history']; + $keep = $queryStringDetails['keep']; + + $this->component->setPropertyAttribute($key, new BaseUrl(as: $alias, history: $history, keep: $keep)); + } + + protected function paginationView() + { + if (method_exists($this->component, 'paginationView')) { + return $this->component->paginationView(); + } + + return 'livewire::' . (property_exists($this->component, 'paginationTheme') ? invade($this->component)->paginationTheme : config('livewire.pagination_theme', 'tailwind')); + } + + protected function paginationSimpleView() + { + return 'livewire::simple-' . (property_exists($this->component, 'paginationTheme') ? invade($this->component)->paginationTheme : config('livewire.pagination_theme', 'tailwind')); + } + + protected function getQueryString() + { + $supportQueryStringHook = ComponentHookRegistry::getHook($this->component, SupportQueryString::class); + + return $supportQueryStringHook->getQueryString(); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/views/bootstrap.blade.php b/vendor/livewire/livewire/src/Features/SupportPagination/views/bootstrap.blade.php new file mode 100644 index 00000000..394b0ccf --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/views/bootstrap.blade.php @@ -0,0 +1,48 @@ +
+ @if ($paginator->hasPages()) + + @endif +
diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-bootstrap.blade.php b/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-bootstrap.blade.php new file mode 100644 index 00000000..745e7d24 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-bootstrap.blade.php @@ -0,0 +1,43 @@ +
+ @if ($paginator->hasPages()) + + @endif +
diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-tailwind.blade.php b/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-tailwind.blade.php new file mode 100644 index 00000000..e4c0c63b --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/views/simple-tailwind.blade.php @@ -0,0 +1,45 @@ +
+ @if ($paginator->hasPages()) + + @endif +
diff --git a/vendor/livewire/livewire/src/Features/SupportPagination/views/tailwind.blade.php b/vendor/livewire/livewire/src/Features/SupportPagination/views/tailwind.blade.php new file mode 100644 index 00000000..47b06ce9 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportPagination/views/tailwind.blade.php @@ -0,0 +1,114 @@ +
+ @if ($paginator->hasPages()) + + @endif +
diff --git a/vendor/livewire/livewire/src/Features/SupportQueryString/BaseUrl.php b/vendor/livewire/livewire/src/Features/SupportQueryString/BaseUrl.php new file mode 100644 index 00000000..e043adf7 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportQueryString/BaseUrl.php @@ -0,0 +1,47 @@ +query($this->urlName(), 'noexist'); + + if ($initialValue === 'noexist') return; + + $decoded = is_array($initialValue) + ? json_decode(json_encode($initialValue), true) + : json_decode($initialValue, true); + + $this->setValue($decoded === null ? $initialValue : $decoded); + } + + public function dehydrate($context) + { + if (! $context->mounting) return; + + $queryString = [ + 'as' => $this->as, + 'use' => $this->history ? 'push' : 'replace', + 'alwaysShow' => $this->keep, + ]; + + $context->pushEffect('url', $queryString, $this->getName()); + } + + public function urlName() + { + return $this->as ?? $this->getName(); + } +} + diff --git a/vendor/livewire/livewire/src/Features/SupportQueryString/SupportQueryString.php b/vendor/livewire/livewire/src/Features/SupportQueryString/SupportQueryString.php new file mode 100644 index 00000000..e7c59bfd --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportQueryString/SupportQueryString.php @@ -0,0 +1,61 @@ +getQueryString()) return; + + foreach ($queryString as $key => $value) { + $key = is_string($key) ? $key : $value; + $alias = $value['as'] ?? $key; + $history = $value['history'] ?? true; + $keep = $value['alwaysShow'] ?? $value['keep'] ?? false; + + $this->component->setPropertyAttribute($key, new BaseUrl(as: $alias, history: $history, keep: $keep)); + } + } + + public function getQueryString() + { + if (isset($this->queryString)) return $this->queryString; + + $component = $this->component; + + $componentQueryString = []; + + if (method_exists($component, 'queryString')) $componentQueryString = invade($component)->queryString(); + elseif (property_exists($component, 'queryString')) $componentQueryString = invade($component)->queryString; + + return $this->queryString = collect(class_uses_recursive($class = $component::class)) + ->map(function ($trait) use ($class, $component) { + $member = 'queryString' . class_basename($trait); + + if (method_exists($class, $member)) { + return invade($component)->{$member}(); + } + + if (property_exists($class, $member)) { + return invade($component)->{$member}; + } + + return []; + }) + ->values() + ->mapWithKeys(function ($value) { + return $value; + }) + ->merge($componentQueryString) + ->toArray(); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportReactiveProps/BaseReactive.php b/vendor/livewire/livewire/src/Features/SupportReactiveProps/BaseReactive.php new file mode 100644 index 00000000..594cc3e2 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportReactiveProps/BaseReactive.php @@ -0,0 +1,45 @@ +getName(); + + store($this->component)->push('reactiveProps', $property); + + $this->originalValueHash = crc32(json_encode($this->getValue())); + } + + public function hydrate() + { + if (SupportReactiveProps::hasPassedInProps($this->component->getId())) { + $updatedValue = SupportReactiveProps::getPassedInProp( + $this->component->getId(), $this->getName() + ); + + $this->setValue($updatedValue); + } + + $this->originalValueHash = crc32(json_encode($this->getValue())); + } + + public function dehydrate($context) + { + if ($this->originalValueHash !== crc32(json_encode($this->getValue()))) { + throw new CannotMutateReactivePropException($this->component->getName(), $this->getName()); + } + + $context->pushMemo('props', $this->getName()); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportReactiveProps/CannotMutateReactivePropException.php b/vendor/livewire/livewire/src/Features/SupportReactiveProps/CannotMutateReactivePropException.php new file mode 100644 index 00000000..ed1f5c9e --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportReactiveProps/CannotMutateReactivePropException.php @@ -0,0 +1,13 @@ + static::$pendingChildParams = []); + + on('mount.stub', function ($tag, $id, $params, $parent, $key) { + static::$pendingChildParams[$id] = $params; + }); + } + + static function hasPassedInProps($id) { + return isset(static::$pendingChildParams[$id]); + } + + static function getPassedInProp($id, $name) { + $params = static::$pendingChildParams[$id] ?? []; + + return $params[$name] ?? null; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportRedirects/HandlesRedirects.php b/vendor/livewire/livewire/src/Features/SupportRedirects/HandlesRedirects.php new file mode 100644 index 00000000..faa84604 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportRedirects/HandlesRedirects.php @@ -0,0 +1,41 @@ +set('redirect', $url); + + if ($navigate) store($this)->set('redirectUsingNavigate', true); + + $shouldSkipRender = ! config('livewire.render_on_redirect', false); + + $shouldSkipRender && $this->skipRender(); + } + + public function redirectRoute($name, $parameters = [], $absolute = true) + { + $to = route($name, $parameters, $absolute); + + store($this)->set('redirect', $to); + + $shouldSkipRender = ! config('livewire.render_on_redirect', false); + + $shouldSkipRender && $this->skipRender(); + } + + public function redirectAction($name, $parameters = [], $absolute = true) + { + $to = action($name, $parameters, $absolute); + + store($this)->set('redirect', $to); + + $shouldSkipRender = ! config('livewire.render_on_redirect', false); + + $shouldSkipRender && $this->skipRender(); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportRedirects/Redirector.php b/vendor/livewire/livewire/src/Features/SupportRedirects/Redirector.php new file mode 100644 index 00000000..e89ee68c --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportRedirects/Redirector.php @@ -0,0 +1,46 @@ +component->redirect($this->generator->to($path, [], $secure)); + + return $this; + } + + public function away($path, $status = 302, $headers = []) + { + return $this->to($path, $status, $headers); + } + + public function with($key, $value = null) + { + $key = is_array($key) ? $key : [$key => $value]; + + foreach ($key as $k => $v) { + $this->session->flash($k, $v); + } + + return $this; + } + + public function component(Component $component) + { + $this->component = $component; + + return $this; + } + + public function response($to) + { + return $this->createRedirect($to, 302, []); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportRedirects/SupportRedirects.php b/vendor/livewire/livewire/src/Features/SupportRedirects/SupportRedirects.php new file mode 100644 index 00000000..70b08852 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportRedirects/SupportRedirects.php @@ -0,0 +1,57 @@ +bind('redirect', function () { + $redirector = app(Redirector::class)->component($this->component); + + if (app()->has('session.store')) { + $redirector->setSession(app('session.store')); + } + + return $redirector; + }); + } + + public function dehydrate($context) + { + // Put the old redirector back into the container. + app()->instance('redirect', array_pop(static::$redirectorCacheStack)); + + $to = $this->storeGet('redirect'); + $usingNavigate = $this->storeGet('redirectUsingNavigate'); + + if (is_subclass_of($to, Component::class)) { + $to = url()->action($to); + } + + if ($to && ! app(HandleRequests::class)->isLivewireRequest()) { + abort(redirect($to)); + } + + if (! $to) { + // If there was no redirect. Clear flash session data. + if (app()->has('session.store')) { + session()->forget(session()->get('_flash.new')); + } + + return; + } + + $context->addEffect('redirect', $to); + $usingNavigate && $context->addEffect('redirectUsingNavigate', true); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportRedirects/TestsRedirects.php b/vendor/livewire/livewire/src/Features/SupportRedirects/TestsRedirects.php new file mode 100644 index 00000000..a86f059b --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportRedirects/TestsRedirects.php @@ -0,0 +1,41 @@ +action($uri); + } + + if (! app('livewire')->isLivewireRequest()) { + $this->lastState->getResponse()->assertRedirect($uri); + + return $this; + } + + PHPUnit::assertArrayHasKey( + 'redirect', + $this->effects, + 'Component did not perform a redirect.' + ); + + if (! is_null($uri)) { + PHPUnit::assertSame(url($uri), url($this->effects['redirect'])); + } + + return $this; + } + + public function assertNoRedirect() + { + PHPUnit::assertTrue(! isset($this->effects['redirect'])); + + return $this; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportStreaming/HandlesStreaming.php b/vendor/livewire/livewire/src/Features/SupportStreaming/HandlesStreaming.php new file mode 100644 index 00000000..0c350332 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportStreaming/HandlesStreaming.php @@ -0,0 +1,15 @@ +stream($name, $content, $replace); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportStreaming/SupportStreaming.php b/vendor/livewire/livewire/src/Features/SupportStreaming/SupportStreaming.php new file mode 100644 index 00000000..ce31f987 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportStreaming/SupportStreaming.php @@ -0,0 +1,42 @@ + $name, 'content' => $content, 'replace' => $replace]); + } + + public static function ensureStreamResponseStarted() + { + if (static::$response) return; + + static::$response = response()->stream(null , 200, [ + 'Cache-Control' => 'no-cache', + 'Content-Type' => 'text/event-stream', + 'X-Accel-Buffering' => 'no', + 'X-Livewire-Stream' => true, + ]); + + static::$response->sendHeaders(); + } + + public static function streamContent($body) + { + echo json_encode(['stream' => true, 'body' => $body, 'endStream' => true]); + + if (ob_get_level() > 0) { + ob_flush(); + } + + flush(); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTeleporting/SupportTeleporting.php b/vendor/livewire/livewire/src/Features/SupportTeleporting/SupportTeleporting.php new file mode 100644 index 00000000..8b7a8de4 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTeleporting/SupportTeleporting.php @@ -0,0 +1,19 @@ +directive('teleport', function ($expression) { + return ''; + }); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/ComponentState.php b/vendor/livewire/livewire/src/Features/SupportTesting/ComponentState.php new file mode 100644 index 00000000..6c40b452 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/ComponentState.php @@ -0,0 +1,79 @@ +component; + } + + function getSnapshot() + { + return $this->snapshot; + } + + function getSnapshotData() + { + return $this->untupleify($this->snapshot['data']); + } + + function getEffects() + { + return $this->effects; + } + + function getView() + { + return $this->view; + } + + function getResponse() + { + return $this->response; + } + + function untupleify($payload) { + $value = Utils::isSyntheticTuple($payload) ? $payload[0] : $payload; + + if (is_array($value)) { + foreach ($value as $key => $child) { + $value[$key] = $this->untupleify($child); + } + } + + return $value; + } + + function getHtml($stripInitialData = false) + { + $html = $this->html; + + if ($stripInitialData) { + $removeMe = (string) str($html)->betweenFirst( + 'wire:snapshot="', '"' + ); + + $html = str_replace($removeMe, '', $html); + + $removeMe = (string) str($html)->betweenFirst( + 'wire:effects="', '"' + ); + + $html = str_replace($removeMe, '', $html); + } + + return $html; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/DuskBrowserMacros.php b/vendor/livewire/livewire/src/Features/SupportTesting/DuskBrowserMacros.php new file mode 100644 index 00000000..85a60177 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/DuskBrowserMacros.php @@ -0,0 +1,339 @@ +resolver->format($selector); + + $actual = $this->resolver->findOrFail($selector)->getAttribute($attribute); + + PHPUnit::assertNull( + $actual, + "Did not see expected attribute [{$attribute}] within element [{$fullSelector}]." + ); + + return $this; + }; + } + + public function assertNotVisible() + { + return function ($selector) { + /** @var \Laravel\Dusk\Browser $this */ + $fullSelector = $this->resolver->format($selector); + + PHPUnit::assertFalse( + $this->resolver->findOrFail($selector)->isDisplayed(), + "Element [{$fullSelector}] is visible." + ); + + return $this; + }; + } + + public function assertNotPresent() + { + return function ($selector) { + /** @var \Laravel\Dusk\Browser $this */ + $fullSelector = $this->resolver->format($selector); + + PHPUnit::assertTrue( + is_null($this->resolver->find($selector)), + "Element [{$fullSelector}] is present." + ); + + return $this; + }; + } + + public function assertHasClass() + { + return function ($selector, $className) { + /** @var \Laravel\Dusk\Browser $this */ + $fullSelector = $this->resolver->format($selector); + + PHPUnit::assertContains( + $className, + explode(' ', $this->attribute($selector, 'class')), + "Element [{$fullSelector}] missing class [{$className}]." + ); + + return $this; + }; + } + + public function assertScript() + { + return function ($js, $expects = true) { + /** @var \Laravel\Dusk\Browser $this */ + PHPUnit::assertEquals($expects, head($this->script( + str($js)->start('return ') + ))); + + return $this; + }; + } + + public function runScript() + { + return function ($js) { + /** @var \Laravel\Dusk\Browser $this */ + $this->script([$js]); + + return $this; + }; + } + + public function scrollTo() + { + return function ($selector) { + $this->browser->scrollTo($selector); + return $this; + }; + } + + public function assertNotInViewPort() + { + return function ($selector) { + /** @var \Laravel\Dusk\Browser $this */ + return $this->assertInViewPort($selector, invert: true); + }; + } + + public function assertInViewPort() + { + return function ($selector, $invert = false) { + /** @var \Laravel\Dusk\Browser $this */ + + $fullSelector = $this->resolver->format($selector); + + $result = $this->script( + 'const rect = document.querySelector(\''.$fullSelector.'\').getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + );', + $selector + )[0]; + + PHPUnit::assertEquals($invert ? false : true, $result); + + return $this; + }; + } + + public function assertClassMissing() + { + return function ($selector, $className) { + /** @var \Laravel\Dusk\Browser $this */ + $fullSelector = $this->resolver->format($selector); + + PHPUnit::assertNotContains( + $className, + explode(' ', $this->attribute($selector, 'class')), + "Element [{$fullSelector}] has class [{$className}]." + ); + + return $this; + }; + } + + public function waitForLivewireToLoad() + { + return function () { + /** @var \Laravel\Dusk\Browser $this */ + return $this->waitUsing(6, 25, function () { + return $this->driver->executeScript("return !! window.Livewire.initialRenderIsFinished"); + }); + }; + } + + public function waitForLivewire() + { + return function ($callback = null) { + /** @var \Laravel\Dusk\Browser $this */ + $id = str()->random(); + + $this->script([ + "window.duskIsWaitingForLivewireRequest{$id} = true", + "window.Livewire.hook('request', ({ respond }) => { + window.duskIsWaitingForLivewireRequest{$id} = true + + respond(() => { + queueMicrotask(() => { + delete window.duskIsWaitingForLivewireRequest{$id} + }) + }) + })", + ]); + + if ($callback) { + $callback($this); + + return $this->waitUsing(6, 25, function () use ($id) { + return $this->driver->executeScript("return window.duskIsWaitingForLivewireRequest{$id} === undefined"); + }, 'Livewire request was never triggered'); + } + + // If no callback is passed, make ->waitForLivewire a higher-order method. + return new class($this, $id) { + protected $browser; + protected $id; + + public function __construct($browser, $id) { $this->browser = $browser; $this->id = $id; } + + public function __call($method, $params) + { + return tap($this->browser->{$method}(...$params), function ($browser) { + $browser->waitUsing(6, 25, function () use ($browser) { + return $browser->driver->executeScript("return window.duskIsWaitingForLivewireRequest{$this->id} === undefined"); + }, 'Livewire request was never triggered'); + }); + } + }; + }; + } + + public function waitForNavigate() + { + return function ($callback = null) { + /** @var \Laravel\Dusk\Browser $this */ + $id = str()->random(); + + $this->script([ + "window.duskIsWaitingForLivewireNavigate{$id} = true", + "window.handler{$id} = () => { + window.duskIsWaitingForLivewireNavigate{$id} = true + + document.removeEventListener('livewire:navigated', window.handler{$id}) + + queueMicrotask(() => { + delete window.duskIsWaitingForLivewireNavigate{$id} + }) + }", + "document.addEventListener('livewire:navigated', window.handler{$id})", + ]); + + if ($callback) { + $callback($this); + + return $this->waitUsing(6, 25, function () use ($id) { + return $this->driver->executeScript("return window.duskIsWaitingForLivewireNavigate{$id} === undefined"); + }, 'Livewire navigate was never triggered'); + } + + // If no callback is passed, make ->waitForNavigate a higher-order method. + return new class($this, $id) { + protected $browser; + protected $id; + public function __construct($browser, $id) { $this->browser = $browser; $this->id = $id; } + + public function __call($method, $params) + { + return tap($this->browser->{$method}(...$params), function ($browser) { + $browser->waitUsing(6, 25, function () use ($browser) { + return $browser->driver->executeScript("return window.duskIsWaitingForLivewireNavigate{$this->id} === undefined"); + }, 'Livewire navigate was never triggered'); + }); + } + }; + }; + } + + public function online() + { + return function () { + /** @var \Laravel\Dusk\Browser $this */ + return tap($this)->script("window.dispatchEvent(new Event('online'))"); + }; + } + + public function offline() + { + return function () { + /** @var \Laravel\Dusk\Browser $this */ + return tap($this)->script("window.dispatchEvent(new Event('offline'))"); + }; + } + + public function selectMultiple() + { + return function ($field, $values = []) { + $element = $this->resolver->resolveForSelection($field); + + $options = $element->findElements(WebDriverBy::tagName('option')); + + if (empty($values)) { + $maxSelectValues = sizeof($options) - 1; + $minSelectValues = rand(0, $maxSelectValues); + foreach (range($minSelectValues, $maxSelectValues) as $optValue) { + $options[$optValue]->click(); + } + } else { + foreach ($options as $option) { + $optValue = (string)$option->getAttribute('value'); + if (in_array($optValue, $values)) { + $option->click(); + } + } + } + + return $this; + }; + } + + public function assertConsoleLogHasWarning() + { + return function($expectedMessage){ + $logs = $this->driver->manage()->getLog('browser'); + + $containsError = false; + + foreach ($logs as $log) { + if (! isset($log['message']) || ! isset($log['level']) || $log['level'] !== 'WARNING') continue; + + + if(str($log['message'])->contains($expectedMessage)) { + $containsError = true; + } + } + + PHPUnit::assertTrue($containsError, "Console log error message \"{$expectedMessage}\" not found"); + + return $this; + }; + } + + public function assertConsoleLogMissingWarning() + { + return function($expectedMessage){ + $logs = $this->driver->manage()->getLog('browser'); + + $containsError = false; + + foreach ($logs as $log) { + if (! isset($log['message']) || ! isset($log['level']) || $log['level'] !== 'WARNING') continue; + + + if(str($log['message'])->contains($expectedMessage)) { + $containsError = true; + } + } + + PHPUnit::assertFalse($containsError, "Console log error message \"{$expectedMessage}\" was found"); + + return $this; + }; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/DuskTestable.php b/vendor/livewire/livewire/src/Features/SupportTesting/DuskTestable.php new file mode 100644 index 00000000..6949fd21 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/DuskTestable.php @@ -0,0 +1,180 @@ +call(app('livewire')->new($class)); + })->middleware('web'); + + on('browser.testCase.setUp', function ($testCase) { + static::$currentTestCase = $testCase; + static::$isTestProcess = true; + + $tweakApplication = $testCase::tweakApplicationHook(); + + invade($testCase)->tweakApplication(function () use ($tweakApplication) { + config()->set('app.debug', true); + + if (is_callable($tweakApplication)) $tweakApplication(); + + static::loadTestComponents(); + }); + }); + + on('browser.testCase.tearDown', function () { + static::wipeRuntimeComponentRegistration(); + + static::$browser && static::$browser->quit(); + + static::$currentTestCase = null; + }); + + if (isset($_SERVER['CI']) && class_exists(\Orchestra\Testbench\Dusk\Options::class)) { + \Orchestra\Testbench\Dusk\Options::withoutUI(); + } + + \Laravel\Dusk\Browser::mixin(new DuskBrowserMacros); + } + + /** + * @return Browser + */ + static function create($components, $params = [], $queryParams = []) + { + if (static::$shortCircuitCreateCall) { + throw new class ($components) extends \Exception { + public $components; + public $isDuskShortcircuit = true; + function __construct($components) { + $this->components = $components; + } + }; + } + + $components = (array) $components; + + $firstComponent = array_shift($components); + + $id = 'a'.str()->random(10); + + $components = [$id => $firstComponent, ...$components]; + + [$class, $method] = static::findTestClassAndMethodThatCalledThis(); + + static::registerComponentsForNextTest([$id, $class, $method]); + + $testCase = invade(static::$currentTestCase); + + static::$browser = $testCase->newBrowser($testCase->createWebDriver()); + + return static::$browser->visit('/livewire-dusk/'.$id.'?'.Arr::query($queryParams)); + } + + static function actingAs(\Illuminate\Contracts\Auth\Authenticatable $user, $driver = null) + { + // + } + + static function findTestClassAndMethodThatCalledThis() + { + $traces = debug_backtrace(options: DEBUG_BACKTRACE_IGNORE_ARGS, limit: 10); + + foreach ($traces as $trace) { + if (is_subclass_of($trace['class'], TestCase::class)) { + return [$trace['class'], $trace['function']]; + } + } + + throw new \Exception; + } + + static function loadTestComponents() + { + if (static::$isTestProcess) return; + + $tmp = __DIR__ . '/_runtime_components.json'; + + if (file_exists($tmp)) { + // We can't just "require" this file because of race conditions... + [$id, $testClass, $method] = json_decode(file_get_contents($tmp), associative: true); + + if (! method_exists($testClass, $method)) return; + + static::$shortCircuitCreateCall = true; + + $components = null; + + try { (new $testClass)->$method(); } catch (\Exception $e) { + if (! $e->isDuskShortcircuit) throw $e; + $components = $e->components; + } + + $components = is_array($components) ? $components : [$components]; + + $firstComponent = array_shift($components); + + $components = [$id => $firstComponent, ...$components]; + + static::$shortCircuitCreateCall = false; + + foreach ($components as $name => $class) { + if (is_object($class)) $class = $class::class; + + if (is_numeric($name)) { + app('livewire')->component($class); + } else { + app('livewire')->component($name, $class); + } + } + } + } + + static function registerComponentsForNextTest($components) + { + $tmp = __DIR__ . '/_runtime_components.json'; + + file_put_contents($tmp, json_encode($components, JSON_PRETTY_PRINT)); + } + + static function wipeRuntimeComponentRegistration() + { + $tmp = __DIR__ . '/_runtime_components.json'; + + file_exists($tmp) && unlink($tmp); + } + + function breakIntoATinkerShell($browsers, $e) + { + $sh = new \Psy\Shell(); + + $sh->add(new \Laravel\Dusk\Console\DuskCommand($this, $e)); + + $sh->setScopeVariables([ + 'browsers' => $browsers, + ]); + + $sh->addInput('dusk'); + + $sh->setBoundObject($this); + + $sh->run(); + + return $sh->getScopeVariables(false); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php b/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php new file mode 100644 index 00000000..3f24ad77 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php @@ -0,0 +1,71 @@ +makeInitialRequest($name, $params, $fromQueryString); + } + + function makeInitialRequest($name, $params, $fromQueryString = []) { + $uri = '/livewire-unit-test-endpoint/'.str()->random(20); + + $this->registerRouteBeforeExistingRoutes($uri, function () use ($name, $params) { + return \Illuminate\Support\Facades\Blade::render('@livewire($name, $params)', [ + 'name' => $name, + 'params' => $params, + ]); + }); + + [$response, $componentInstance, $componentView] = $this->extractComponentAndBladeView(function () use ($uri, $fromQueryString) { + return $this->requestBroker->temporarilyDisableExceptionHandlingAndMiddleware(function ($requestBroker) use ($uri, $fromQueryString) { + return $requestBroker->call('GET', $uri, $fromQueryString); + }); + }); + + app('livewire')->flushState(); + + $html = $response->getContent(); + + // Set "original" to Blade view for assertions like "assertViewIs()"... + $response->original = $componentView; + + $snapshot = Utils::extractAttributeDataFromHtml($html, 'wire:snapshot'); + $effects = Utils::extractAttributeDataFromHtml($html, 'wire:effects'); + + return new ComponentState($componentInstance, $response, $componentView, $html, $snapshot, $effects); + } + + private function registerRouteBeforeExistingRoutes($path, $closure) + { + // To prevent this route from overriding wildcard routes registered within the application, + // We have to make sure that this route is registered before other existing routes. + $livewireTestingRoute = new \Illuminate\Routing\Route(['GET', 'HEAD'], $path, $closure); + + $existingRoutes = app('router')->getRoutes(); + + // Make an empty collection. + $runningCollection = new \Illuminate\Routing\RouteCollection; + + // Add this testing route as the first one. + $runningCollection->add($livewireTestingRoute); + + // Now add the existing routes after it. + foreach ($existingRoutes as $route) { + $runningCollection->add($route); + } + + // Now set this route collection as THE route collection for the app. + app('router')->setRoutes($runningCollection); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/MakesAssertions.php b/vendor/livewire/livewire/src/Features/SupportTesting/MakesAssertions.php new file mode 100644 index 00000000..5c54db49 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/MakesAssertions.php @@ -0,0 +1,140 @@ +html($stripInitialData) + ); + } + + return $this; + } + + function assertDontSee($values, $escape = true, $stripInitialData = true) + { + foreach (Arr::wrap($values) as $value) { + PHPUnit::assertStringNotContainsString( + $escape ? e($value): $value, + $this->html($stripInitialData) + ); + } + + return $this; + } + + function assertSeeHtml($values) + { + foreach (Arr::wrap($values) as $value) { + PHPUnit::assertStringContainsString( + $value, + $this->html() + ); + } + + return $this; + } + + function assertSeeHtmlInOrder($values) + { + PHPUnit::assertThat( + $values, + new SeeInOrder($this->html()) + ); + + return $this; + } + + function assertDontSeeHtml($values) + { + foreach (Arr::wrap($values) as $value) { + PHPUnit::assertStringNotContainsString( + $value, + $this->html() + ); + } + + return $this; + } + + function assertSeeText($value, $escape = true) + { + $value = Arr::wrap($value); + + $values = $escape ? array_map('e', ($value)) : $value; + + $content = $this->html(); + + tap(strip_tags($content), function ($content) use ($values) { + foreach ($values as $value) { + PHPUnit::assertStringContainsString((string) $value, $content); + } + }); + + return $this; + } + + function assertSet($name, $value, $strict = false) + { + $actual = $this->get($name); + + if (! is_string($value) && is_callable($value)) { + PHPUnit::assertTrue($value($actual)); + } else { + $strict ? PHPUnit::assertSame($value, $actual) : PHPUnit::assertEquals($value, $actual); + } + + return $this; + } + + function assertNotSet($name, $value, $strict = false) + { + $actual = $this->get($name); + + $strict ? PHPUnit::assertNotSame($value, $actual) : PHPUnit::assertNotEquals($value, $actual); + + return $this; + } + + function assertCount($name, $value) + { + PHPUnit::assertCount($value, $this->get($name)); + + return $this; + } + + function assertSnapshotSet($name, $value, $strict = false) + { + $data = $this->lastState->getSnapshotData(); + + if (is_callable($value)) { + PHPUnit::assertTrue($value(data_get($data, $name))); + } else { + $strict ? PHPUnit::assertSame($value, data_get($data, $name)) : PHPUnit::assertEquals($value, data_get($data, $name)); + } + + return $this; + } + + function assertSnapshotNotSet($name, $value, $strict = false) + { + $data = $this->lastState->getSnapshotData(); + + if (is_callable($value)) { + PHPUnit::assertFalse($value(data_get($data, $name))); + } else { + $strict ? PHPUnit::assertNotSame($value, data_get($data, $name)) : PHPUnit::assertNotEquals($value, data_get($data, $name)); + } + + return $this; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/Render.php b/vendor/livewire/livewire/src/Features/SupportTesting/Render.php new file mode 100644 index 00000000..299085c4 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/Render.php @@ -0,0 +1,30 @@ +app = $app; + } + + function temporarilyDisableExceptionHandlingAndMiddleware($callback) + { + $cachedHandler = app(ExceptionHandler::class); + + $cachedShouldSkipMiddleware = $this->app->shouldSkipMiddleware(); + + $this->withoutExceptionHandling([HttpException::class, AuthorizationException::class])->withoutMiddleware(); + + $result = $callback($this); + + $this->app->instance(ExceptionHandler::class, $cachedHandler); + + if (! $cachedShouldSkipMiddleware) { + unset($this->app['middleware.disable']); + } + + return $result; + } + + function withoutHandling($except = []) + { + return $this->withoutExceptionHandling($except); + } +} + diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/SubsequentRender.php b/vendor/livewire/livewire/src/Features/SupportTesting/SubsequentRender.php new file mode 100644 index 00000000..ca4542fe --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/SubsequentRender.php @@ -0,0 +1,77 @@ +makeSubsequentRequest($calls, $updates); + } + + function makeSubsequentRequest($calls = [], $updates = []) { + $uri = app('livewire')->getUpdateUri(); + + $encodedSnapshot = json_encode($this->lastState->getSnapshot()); + + $payload = [ + 'components' => [ + [ + 'snapshot' => $encodedSnapshot, + 'calls' => $calls, + 'updates' => $updates, + ], + ], + ]; + + [$response, $componentInstance, $componentView] = $this->extractComponentAndBladeView(function () use ($uri, $payload) { + return $this->requestBroker->temporarilyDisableExceptionHandlingAndMiddleware(function ($requestBroker) use ($uri, $payload) { + return $requestBroker->withHeaders(['X-Livewire' => true])->post($uri, $payload); + }); + }); + + app('livewire')->flushState(); + + if (! $response->isOk()) { + return new ComponentState( + $componentInstance, + $response, + null, + '', + [], + [], + ); + } + + $json = $response->json(); + + // Set "original" to Blade view for assertions like "assertViewIs()"... + $response->original = $componentView; + + $componentResponsePayload = $json['components'][0]; + + $snapshot = json_decode($componentResponsePayload['snapshot'], true); + + $effects = $componentResponsePayload['effects']; + + // If no new HTML has been rendered, let's forward the last known HTML... + $html = $effects['html'] ?? $this->lastState->getHtml(stripInitialData: true); + $view = $componentView ?? $this->lastState->getView(); + + return new ComponentState( + $componentInstance, + $response, + $view, + $html, + $snapshot, + $effects, + ); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/SupportTesting.php b/vendor/livewire/livewire/src/Features/SupportTesting/SupportTesting.php new file mode 100644 index 00000000..ed1c5900 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/SupportTesting.php @@ -0,0 +1,111 @@ +environment('testing')) return; + + if (class_exists('Laravel\Dusk\Browser')) { + DuskTestable::provide(); + } + + static::registerTestingMacros(); + } + + function dehydrate($context) + { + $target = $this->component; + + $errors = $target->getErrorBag(); + + if (! $errors->isEmpty()) { + $this->storeSet('testing.errors', $errors); + } + } + + function hydrate() + { + $this->storeSet('testing.validator', null); + } + + function exception($e, $stopPropagation) { + if (! $e instanceof ValidationException) return; + + $this->storeSet('testing.validator', $e->validator); + } + + protected static function registerTestingMacros() + { + // Usage: $this->assertSeeLivewire('counter'); + \Illuminate\Testing\TestResponse::macro('assertSeeLivewire', function ($component) { + if (is_subclass_of($component, Component::class)) { + $component = app(ComponentRegistry::class)->getName($component); + } + $escapedComponentName = trim(htmlspecialchars(json_encode(['name' => $component])), '{}'); + + \PHPUnit\Framework\Assert::assertStringContainsString( + $escapedComponentName, + $this->getContent(), + 'Cannot find Livewire component ['.$component.'] rendered on page.' + ); + + return $this; + }); + + // Usage: $this->assertDontSeeLivewire('counter'); + \Illuminate\Testing\TestResponse::macro('assertDontSeeLivewire', function ($component) { + if (is_subclass_of($component, Component::class)) { + $component = app(ComponentRegistry::class)->getName($component); + } + $escapedComponentName = trim(htmlspecialchars(json_encode(['name' => $component])), '{}'); + + \PHPUnit\Framework\Assert::assertStringNotContainsString( + $escapedComponentName, + $this->getContent(), + 'Found Livewire component ['.$component.'] rendered on page.' + ); + + return $this; + }); + + if (class_exists(\Illuminate\Testing\TestView::class)) { + \Illuminate\Testing\TestView::macro('assertSeeLivewire', function ($component) { + if (is_subclass_of($component, Component::class)) { + $component = app(ComponentRegistry::class)->getName($component); + } + $escapedComponentName = trim(htmlspecialchars(json_encode(['name' => $component])), '{}'); + + \PHPUnit\Framework\Assert::assertStringContainsString( + $escapedComponentName, + $this->rendered, + 'Cannot find Livewire component ['.$component.'] rendered on page.' + ); + + return $this; + }); + + \Illuminate\Testing\TestView::macro('assertDontSeeLivewire', function ($component) { + if (is_subclass_of($component, Component::class)) { + $component = app(ComponentRegistry::class)->getName($component); + } + $escapedComponentName = trim(htmlspecialchars(json_encode(['name' => $component])), '{}'); + + \PHPUnit\Framework\Assert::assertStringNotContainsString( + $escapedComponentName, + $this->rendered, + 'Found Livewire component ['.$component.'] rendered on page.' + ); + + return $this; + }); + } + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportTesting/Testable.php b/vendor/livewire/livewire/src/Features/SupportTesting/Testable.php new file mode 100644 index 00000000..f8ee3f4d --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportTesting/Testable.php @@ -0,0 +1,280 @@ + $value) { + if (is_numeric($key)) { + app('livewire')->isDiscoverable($name) || app('livewire')->component($value); + } else { + app('livewire')->component($key, $value); + } + } + } elseif (is_object($name)) { + $anonymousClassComponent = $name; + + $name = str()->random(10); + + app('livewire')->component($name, $anonymousClassComponent); + } else { + app('livewire')->isDiscoverable($name) || app('livewire')->component($name); + } + + return $name; + } + + static function actingAs(\Illuminate\Contracts\Auth\Authenticatable $user, $driver = null) + { + if (isset($user->wasRecentlyCreated) && $user->wasRecentlyCreated) { + $user->wasRecentlyCreated = false; + } + + auth()->guard($driver)->setUser($user); + + auth()->shouldUse($driver); + } + + function id() { + return $this->lastState->getComponent()->getId(); + } + + function get($key) + { + return data_get($this->lastState->getComponent(), $key); + } + + function html($stripInitialData = false) + { + return $this->lastState->getHtml($stripInitialData); + } + + function updateProperty($name, $value = null) + { + return $this->set($name, $value); + } + + function fill($values) + { + foreach ($values as $name => $value) { + $this->set($name, $value); + } + + return $this; + } + + function toggle($name) + { + return $this->set($name, ! $this->get($name)); + } + + function set($name, $value = null) + { + if (is_array($name)) { + foreach ($name as $key => $value) { + $this->setProperty($key, $value); + } + } else { + $this->setProperty($name, $value); + } + + return $this; + } + + function setProperty($name, $value) + { + if ($value instanceof \Illuminate\Http\UploadedFile) { + return $this->upload($name, [$value]); + } elseif (is_array($value) && isset($value[0]) && $value[0] instanceof \Illuminate\Http\UploadedFile) { + return $this->upload($name, $value, $isMultiple = true); + } + + return $this->update(updates: [$name => $value]); + } + + function runAction($method, ...$params) + { + return $this->call($method, ...$params); + } + + function call($method, ...$params) + { + if ($method === '$refresh') { + return $this->commit(); + } + + if ($method === '$set') { + return $this->set(...$params); + } + + return $this->update(calls: [ + [ + 'method' => $method, + 'params' => $params, + 'path' => '', + ] + ]); + } + + function commit() + { + return $this->update(); + } + + function update($calls = [], $updates = []) + { + $newState = SubsequentRender::make( + $this->requestBroker, + $this->lastState, + $calls, + $updates, + ); + + $this->lastState = $newState; + + return $this; + } + + /** @todo Move me outta here and into the file upload folder somehow... */ + function upload($name, $files, $isMultiple = false) + { + // This methhod simulates the calls Livewire's JavaScript + // normally makes for file uploads. + $this->call( + '_startUpload', + $name, + collect($files)->map(function ($file) { + return [ + 'name' => $file->name, + 'size' => $file->getSize(), + 'type' => $file->getMimeType(), + ]; + })->toArray(), + $isMultiple, + ); + + // This is where either the pre-signed S3 url or the regular Livewire signed + // upload url would do its thing and return a hashed version of the uploaded + // file in a tmp directory. + $storage = \Livewire\Features\SupportFileUploads\FileUploadConfiguration::storage(); + try { + $fileHashes = (new \Livewire\Features\SupportFileUploads\FileUploadController)->validateAndStore($files, \Livewire\Features\SupportFileUploads\FileUploadConfiguration::disk()); + } catch (\Illuminate\Validation\ValidationException $e) { + $this->call('_uploadErrored', $name, json_encode(['errors' => $e->errors()]), $isMultiple); + + return $this; + } + + // We are going to encode the file size in the filename so that when we create + // a new TemporaryUploadedFile instance we can fake a specific file size. + $newFileHashes = collect($files)->zip($fileHashes)->mapSpread(function ($file, $fileHash) { + return (string) str($fileHash)->replaceFirst('.', "-size={$file->getSize()}."); + })->toArray(); + + collect($fileHashes)->zip($newFileHashes)->mapSpread(function ($fileHash, $newFileHash) use ($storage) { + $storage->move('/'.\Livewire\Features\SupportFileUploads\FileUploadConfiguration::path($fileHash), '/'.\Livewire\Features\SupportFileUploads\FileUploadConfiguration::path($newFileHash)); + }); + + // Now we finish the upload with a final call to the Livewire component + // with the temporarily uploaded file path. + $this->call('_finishUpload', $name, $newFileHashes, $isMultiple); + + return $this; + } + + function viewData($key) + { + return $this->lastState->getView()->getData()[$key]; + } + + function getData() + { + return $this->lastState->getSnapshotData(); + } + + function instance() + { + return $this->lastState->getComponent(); + } + + function dump() + { + dump($this->lastState->getHtml()); + + return $this; + } + + function dd() + { + dd($this->lastState->getHtml()); + } + + function tap($callback) + { + $callback($this); + + return $this; + } + + function __get($property) + { + if ($property === 'effects') return $this->lastState->getEffects(); + if ($property === 'snapshot') return $this->lastState->getSnapshot(); + if ($property === 'target') return $this->lastState->getComponent(); + + return $this->instance()->$property; + } + + function __call($method, $params) + { + if (static::hasMacro($method)) { + return $this->macroCall($method, $params); + } + + $this->lastState->getResponse()->{$method}(...$params); + + return $this; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportValidation/BaseRule.php b/vendor/livewire/livewire/src/Features/SupportValidation/BaseRule.php new file mode 100644 index 00000000..1a46592f --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportValidation/BaseRule.php @@ -0,0 +1,78 @@ + 'required', 'foo.*' => 'required'])] + if (is_array($this->rule) && count($this->rule) > 0 && ! is_numeric(array_keys($this->rule)[0])) { + $rules = $this->rule; + } else { + $rules[$this->getName()] = $this->rule; + } + + // @todo: make this more robust (account for FormObjects that + // aren't named "form")... + if (str($this->getName())->startsWith('form.')) { + $name = (string) str($this->getName())->after('form.'); + + $this->component->addValidationAttributesFromOutside([$this->getName() => $name]); + } + + if ($this->attribute) { + if (is_array($this->attribute)) { + $this->component->addValidationAttributesFromOutside($this->attribute); + } else { + $this->component->addValidationAttributesFromOutside([$this->getName() => $this->attribute]); + } + } + + if ($this->as) { + if (is_array($this->as)) { + $this->component->addValidationAttributesFromOutside($this->translate ? trans($this->as) : $this->as); + } else { + $this->component->addValidationAttributesFromOutside([$this->getName() => $this->translate ? trans($this->as) : $this->as]); + } + } + + if ($this->message) { + if (is_array($this->message)) { + $this->component->addMessagesFromOutside($this->translate ? trans($this->message) : $this->message); + } else { + $this->component->addMessagesFromOutside([$this->getName() => $this->translate ? trans($this->message) : $this->message]); + } + } + + $this->component->addRulesFromOutside($rules); + } + + function update($fullPath, $newValue) + { + if ($this->onUpdate === false) return; + + return function () { + wrap($this->component)->validateOnly($this->getName()); + }; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportValidation/HandlesValidation.php b/vendor/livewire/livewire/src/Features/SupportValidation/HandlesValidation.php new file mode 100644 index 00000000..3b42d471 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportValidation/HandlesValidation.php @@ -0,0 +1,412 @@ +rulesFromOutside = array_merge_recursive($this->rulesFromOutside, $rules); + } + + public function addMessagesFromOutside($messages) + { + $this->messagesFromOutside = array_merge($this->messagesFromOutside, $messages); + } + + public function addValidationAttributesFromOutside($validationAttributes) + { + $this->validationAttributesFromOutside = array_merge($this->validationAttributesFromOutside, $validationAttributes); + } + + public function getErrorBag() + { + return store($this)->get('errorBag', new MessageBag); + } + + public function addError($name, $message) + { + return $this->getErrorBag()->add($name, $message); + } + + public function setErrorBag($bag) + { + return store($this)->set('errorBag', $bag instanceof MessageBag + ? $bag + : new MessageBag($bag) + ); + } + + public function resetErrorBag($field = null) + { + $fields = (array) $field; + + if (empty($fields)) { + $errorBag = new MessageBag; + + $this->setErrorBag($errorBag); + + return $errorBag; + } + + $this->setErrorBag( + $this->errorBagExcept($fields) + ); + } + + public function clearValidation($field = null) + { + $this->resetErrorBag($field); + } + + public function resetValidation($field = null) + { + $this->resetErrorBag($field); + } + + public function errorBagExcept($field) + { + $fields = (array) $field; + + return new MessageBag( + collect($this->getErrorBag()) + ->reject(function ($messages, $messageKey) use ($fields) { + return collect($fields)->some(function ($field) use ($messageKey) { + return str($messageKey)->is($field); + }); + }) + ->toArray() + ); + } + + public function getRules() + { + $rulesFromComponent = []; + + if (method_exists($this, 'rules')) $rulesFromComponent = $this->rules(); + else if (property_exists($this, 'rules')) $rulesFromComponent = $this->rules; + + return array_merge($rulesFromComponent, $this->rulesFromOutside); + } + + protected function getMessages() + { + $messages = []; + + if (method_exists($this, 'messages')) $messages = $this->messages(); + elseif (property_exists($this, 'messages')) $messages = $this->messages; + + return array_merge($messages, $this->messagesFromOutside); + } + + protected function getValidationAttributes() + { + $validationAttributes = []; + + if (method_exists($this, 'validationAttributes')) $validationAttributes = $this->validationAttributes(); + elseif (property_exists($this, 'validationAttributes')) $validationAttributes = $this->validationAttributes; + + return array_merge($validationAttributes, $this->validationAttributesFromOutside); + } + + protected function getValidationCustomValues() + { + if (method_exists($this, 'validationCustomValues')) return $this->validationCustomValues(); + if (property_exists($this, 'validationCustomValues')) return $this->validationCustomValues; + + return []; + } + + public function rulesForModel($name) + { + if (empty($this->getRules())) return collect(); + + return collect($this->getRules()) + ->filter(function ($value, $key) use ($name) { + return Utils::beforeFirstDot($key) === $name; + }); + } + + public function hasRuleFor($dotNotatedProperty) + { + $propertyWithStarsInsteadOfNumbers = $this->ruleWithNumbersReplacedByStars($dotNotatedProperty); + + // If property has numeric indexes in it, + if ($dotNotatedProperty !== $propertyWithStarsInsteadOfNumbers) { + return collect($this->getRules())->keys()->contains($propertyWithStarsInsteadOfNumbers); + } + + return collect($this->getRules()) + ->keys() + ->map(function ($key) { + return (string) str($key)->before('.*'); + })->contains($dotNotatedProperty); + } + + public function ruleWithNumbersReplacedByStars($dotNotatedProperty) + { + // Convert foo.0.bar.1 -> foo.*.bar.* + return (string) str($dotNotatedProperty) + // Replace all numeric indexes with an array wildcard: (.0., .10., .007.) => .*. + // In order to match overlapping numerical indexes (foo.1.2.3.4.name), + // We need to use a positive look-behind, that's technically all the magic here. + // For better understanding, see: https://regexr.com/5d1n3 + ->replaceMatches('/(?<=(\.))\d+\./', '*.') + // Replace all numeric indexes at the end of the name with an array wildcard + // (Same as the previous regex, but ran only at the end of the string) + // For better undestanding, see: https://regexr.com/5d1n6 + ->replaceMatches('/\.\d+$/', '.*'); + } + + public function missingRuleFor($dotNotatedProperty) + { + return ! $this->hasRuleFor($dotNotatedProperty); + } + + public function withValidator($callback) + { + $this->withValidatorCallback = $callback; + + return $this; + } + + protected function checkRuleMatchesProperty($rules, $data) + { + collect($rules) + ->keys() + ->each(function($ruleKey) use ($data) { + throw_unless( + array_key_exists(Utils::beforeFirstDot($ruleKey), $data), + new \Exception('No property found for validation: ['.$ruleKey.']') + ); + }); + } + + public function validate($rules = null, $messages = [], $attributes = []) + { + [$rules, $messages, $attributes] = $this->providedOrGlobalRulesMessagesAndAttributes($rules, $messages, $attributes); + + $data = $this->prepareForValidation( + $this->getDataForValidation($rules) + ); + + $this->checkRuleMatchesProperty($rules, $data); + + $ruleKeysToShorten = $this->getModelAttributeRuleKeysToShorten($data, $rules); + + $data = $this->unwrapDataForValidation($data); + + $validator = Validator::make($data, $rules, $messages, $attributes); + + if ($this->withValidatorCallback) { + call_user_func($this->withValidatorCallback, $validator); + + $this->withValidatorCallback = null; + } + + $this->shortenModelAttributesInsideValidator($ruleKeysToShorten, $validator); + + $customValues = $this->getValidationCustomValues(); + if (!empty($customValues)) { + $validator->addCustomValues($customValues); + } + + $validatedData = $validator->validate(); + + $this->resetErrorBag(); + + return $validatedData; + } + + public function validateOnly($field, $rules = null, $messages = [], $attributes = [], $dataOverrides = []) + { + [$rules, $messages, $attributes] = $this->providedOrGlobalRulesMessagesAndAttributes($rules, $messages, $attributes); + + // Loop through rules and swap any wildcard '*' with keys from field, then filter down to only + // rules that match the field, but return the rules without wildcard characters replaced, + // so that custom attributes and messages still work as they need wildcards to work. + $rulesForField = collect($rules) + ->filter(function($value, $rule) use ($field) { + if(! str($field)->is($rule)) { + return false; + } + + $fieldArray = str($field)->explode('.'); + $ruleArray = str($rule)->explode('.'); + + for($i = 0; $i < count($fieldArray); $i++) { + if(isset($ruleArray[$i]) && $ruleArray[$i] === '*') { + $ruleArray[$i] = $fieldArray[$i]; + } + } + + $rule = $ruleArray->join('.'); + + return $field === $rule; + }); + + $ruleForField = $rulesForField->keys()->first(); + + $rulesForField = $rulesForField->toArray(); + + $ruleKeysForField = array_keys($rulesForField); + + $data = array_merge($this->getDataForValidation($rules), $dataOverrides); + + $data = $this->prepareForValidation($data); + + $this->checkRuleMatchesProperty($rules, $data); + + $ruleKeysToShorten = $this->getModelAttributeRuleKeysToShorten($data, $rules); + + $data = $this->unwrapDataForValidation($data); + + // If a matching rule is found, then filter collections down to keys specified in the field, + // while leaving all other data intact. If a key isn't specified and instead there is a + // wildcard '*' then leave that whole collection intact. This ensures that any rules + // that depend on other fields/ properties still work. + if ($ruleForField) { + $ruleArray = str($ruleForField)->explode('.'); + $fieldArray = str($field)->explode('.'); + + $data = $this->filterCollectionDataDownToSpecificKeys($data, $ruleArray, $fieldArray); + } + + $validator = Validator::make($data, $rulesForField, $messages, $attributes); + + if ($this->withValidatorCallback) { + call_user_func($this->withValidatorCallback, $validator); + + $this->withValidatorCallback = null; + } + + $this->shortenModelAttributesInsideValidator($ruleKeysToShorten, $validator); + + $customValues = $this->getValidationCustomValues(); + if (!empty($customValues)) { + $validator->addCustomValues($customValues); + } + + try { + $result = $validator->validate(); + } catch (ValidationException $e) { + $messages = $e->validator->getMessageBag(); + + invade($e->validator)->messages = $messages->merge( + $this->errorBagExcept($ruleKeysForField) + ); + + throw $e; + } + + $this->resetErrorBag($ruleKeysForField); + + return $result; + } + + protected function filterCollectionDataDownToSpecificKeys($data, $ruleKeys, $fieldKeys) + { + + // Filter data down to specified keys in collections, but leave all other data intact + if (count($ruleKeys)) { + $ruleKey = $ruleKeys->shift(); + $fieldKey = $fieldKeys->shift(); + + if ($fieldKey == '*') { + // If the specified field has a '*', then loop through the collection and keep the whole collection intact. + foreach ($data as $key => $value) { + $data[$key] = $this->filterCollectionDataDownToSpecificKeys($value, $ruleKeys, $fieldKeys); + } + } else { + // Otherwise filter collection down to a specific key + $keyData = data_get($data, $fieldKey, null); + + if ($ruleKey == '*') { + $data = []; + } + + data_set($data, $fieldKey, $this->filterCollectionDataDownToSpecificKeys($keyData, $ruleKeys, $fieldKeys)); + } + } + + return $data; + } + + protected function getModelAttributeRuleKeysToShorten($data, $rules) + { + // If a model ($foo) is a property, and the validation rule is + // "foo.bar", then set the attribute to just "bar", so that + // the validation message is shortened and more readable. + + $toShorten = []; + + foreach ($rules as $key => $value) { + $propertyName = Utils::beforeFirstDot($key); + + if ($data[$propertyName] instanceof Model) { + $toShorten[] = $key; + } + } + + return $toShorten; + } + + protected function shortenModelAttributesInsideValidator($ruleKeys, $validator) + { + foreach ($ruleKeys as $key) { + if (str($key)->snake()->replace('_', ' ')->is($validator->getDisplayableAttribute($key))) { + $validator->addCustomAttributes([$key => $validator->getDisplayableAttribute(Utils::afterFirstDot($key))]); + } + } + } + + protected function providedOrGlobalRulesMessagesAndAttributes($rules, $messages, $attributes) + { + $rules = is_null($rules) ? $this->getRules() : $rules; + + throw_if(empty($rules), new MissingRulesException($this->getName())); + + $messages = empty($messages) ? $this->getMessages() : $messages; + $attributes = empty($attributes) ? $this->getValidationAttributes() : $attributes; + + return [$rules, $messages, $attributes]; + } + + protected function getDataForValidation($rules) + { + return Utils::getPublicPropertiesDefinedOnSubclass($this); + } + + protected function unwrapDataForValidation($data) + { + return collect($data)->map(function ($value) { + // @todo: this logic should be contained within "SupportWireables"... + if ($value instanceof Wireable) return $value->toLivewire(); + else if ($value instanceof Arrayable) return $value->toArray(); + + return $value; + })->all(); + } + + protected function prepareForValidation($attributes) + { + return $attributes; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportValidation/SupportValidation.php b/vendor/livewire/livewire/src/Features/SupportValidation/SupportValidation.php new file mode 100644 index 00000000..2cfdaf0f --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportValidation/SupportValidation.php @@ -0,0 +1,54 @@ +component->setErrorBag( + $memo['errors'] ?? [] + ); + } + + function render($view, $data) + { + $errors = (new ViewErrorBag)->put('default', $this->component->getErrorBag()); + + $revert = Utils::shareWithViews('errors', $errors); + + return function () use ($revert) { + // After the component has rendered, let's revert our global + // sharing of the "errors" variable with blade views... + $revert(); + }; + } + + function dehydrate($context) + { + $errors = $this->component->getErrorBag()->toArray(); + + // Only persist errors that were born from properties on the component + // and not from custom validators (Validator::make) that were run. + $context->addMemo('errors', collect($errors) + ->filter(function ($value, $key) { + return Utils::hasProperty($this->component, $key); + }) + ->toArray() + ); + } + + function exception($e, $stopPropagation) + { + if (! $e instanceof ValidationException) return; + + $this->component->setErrorBag($e->validator->errors()); + + $stopPropagation(); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportValidation/TestsValidation.php b/vendor/livewire/livewire/src/Features/SupportValidation/TestsValidation.php new file mode 100644 index 00000000..2f01a59f --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportValidation/TestsValidation.php @@ -0,0 +1,92 @@ +target)->get('testing.validator'); + + if ($validator) return $validator->errors(); + + $errors = store($this->target)->get('testing.errors'); + + if ($errors) return $errors; + + return new MessageBag; + } + + function failedRules() + { + $validator = store($this->target)->get('testing.validator'); + + return $validator ? $validator->failed() : []; + } + + public function assertHasErrors($keys = []) + { + $errors = $this->errors(); + + PHPUnit::assertTrue($errors->isNotEmpty(), 'Component has no errors.'); + + $keys = (array) $keys; + + foreach ($keys as $key => $value) { + if (is_int($key)) { + PHPUnit::assertTrue($errors->has($value), "Component missing error: $value"); + } else { + $failed = $this->failedRules() ?: []; + $rules = array_keys(Arr::get($failed, $key, [])); + + foreach ((array) $value as $rule) { + if (Str::contains($rule, ':')){ + $rule = Str::before($rule, ':'); + } + + PHPUnit::assertContains(Str::studly($rule), $rules, "Component has no [{$rule}] errors for [{$key}] attribute."); + } + } + } + + return $this; + } + + public function assertHasNoErrors($keys = []) + { + $errors = $this->errors(); + + if (empty($keys)) { + PHPUnit::assertTrue($errors->isEmpty(), 'Component has errors: "'.implode('", "', $errors->keys()).'"'); + + return $this; + } + + $keys = (array) $keys; + + foreach ($keys as $key => $value) { + if (is_int($key)) { + PHPUnit::assertFalse($errors->has($value), "Component has error: $value"); + } else { + $failed = $this->failedRules() ?: []; + $rules = array_keys(Arr::get($failed, $key, [])); + + foreach ((array) $value as $rule) { + if (Str::contains($rule, ':')){ + $rule = Str::before($rule, ':'); + } + + PHPUnit::assertNotContains(Str::studly($rule), $rules, "Component has [{$rule}] errors for [{$key}] attribute."); + } + } + } + + return $this; + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/BaseModelable.php b/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/BaseModelable.php new file mode 100644 index 00000000..16e56bba --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/BaseModelable.php @@ -0,0 +1,44 @@ +getName(); + + store($this->component)->push('bindings', $inner, $outer); + + $this->setValue(data_get($parent, $outer)); + } + } + + // This update hook is for the following scenario: + // An modelable value has changed in the browser. + // A network request is triggered from the parent. + // The request contains both parent and child component updates. + // The parent finishes it's request and the "updated" value is + // overridden in the parent's lifecycle (ex. a form field being reset). + // Without this hook, the child's value will not honor that change + // and will instead still be updated to the old value, even though + // the parent changed the bound value. This hook detects if the parent + // has provided a value during this request and ensures that it is the + // final value for the child's request... + function update($fullPath, $newValue) + { + if (store($this->component)->get('hasBeenSeeded', false)) { + $oldValue = $this->getValue(); + + return function () use ($oldValue) { + $this->setValue($oldValue); + }; + } + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/SupportWireModelingNestedComponents.php b/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/SupportWireModelingNestedComponents.php new file mode 100644 index 00000000..fad1aa68 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportWireModelingNestedComponents/SupportWireModelingNestedComponents.php @@ -0,0 +1,85 @@ + static::$outersByComponentId = []); + + // On a subsequent request, a parent encounters a child component + // with wire:model on it, and that child has already been mounted + // in a previous request, capture the value being passed in so we + // can later set the child's property if it exists in this request. + on('mount.stub', function ($tag, $id, $params, $parent, $key) { + if (! isset($params['wire:model'])) return; + + $outer = $params['wire:model']; + + static::$outersByComponentId[$id] = [$outer => data_get($parent, $outer)]; + }); + } + + public function hydrate($memo) + { + if (! isset($memo['bindings'])) return; + + $bindings = $memo['bindings']; + + // Store the bindings for later dehydration... + store($this->component)->set('bindings', $bindings); + + // If this child's parent already rendered its stub, retrieve + // the memo'd value and set it. + if (! isset(static::$outersByComponentId[$memo['id']])) return; + + $outers = static::$outersByComponentId[$memo['id']]; + + foreach ($bindings as $outer => $inner) { + store($this->component)->set('hasBeenSeeded', true); + + $this->component->$inner = $outers[$outer]; + } + } + + public function render($view, $data) + { + return function ($html, $replaceHtml) { + $bindings = store($this->component)->get('bindings', false); + + if (! $bindings) return; + + // Currently we can only support a single wire:model bound value, + // so we'll just get the first one. But in the future we will + // likely want to support named bindings, so we'll keep + // this value as an array. + $outer = array_keys($bindings)[0]; + $inner = array_values($bindings)[0]; + + // Attach the necessary Alpine directives so that the child and + // parent's JS, ephemeral, values are bound. + $replaceHtml(Utils::insertAttributesIntoHtmlRoot($html, [ + 'x-model' => '$wire.$parent.'.$outer, + 'x-modelable' => '$wire.'.$inner, + ])); + }; + } + + public function dehydrate($context) + { + $bindings = store($this->component)->get('bindings', false); + + if (! $bindings) return; + + // Add the bindings metadata to the paylad for later reference... + $context->addMemo('bindings', $bindings); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportWireables/SupportWireables.php b/vendor/livewire/livewire/src/Features/SupportWireables/SupportWireables.php new file mode 100644 index 00000000..f60fa4c4 --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportWireables/SupportWireables.php @@ -0,0 +1,13 @@ +propertySynthesizer(WireableSynth::class); + } +} diff --git a/vendor/livewire/livewire/src/Features/SupportWireables/WireableSynth.php b/vendor/livewire/livewire/src/Features/SupportWireables/WireableSynth.php new file mode 100644 index 00000000..4f034b5e --- /dev/null +++ b/vendor/livewire/livewire/src/Features/SupportWireables/WireableSynth.php @@ -0,0 +1,38 @@ +toLivewire(); + + foreach ($data as $key => $child) { + $data[$key] = $dehydrateChild($key, $child); + } + + return [ + $data, + ['class' => get_class($target)], + ]; + } + + function hydrate($value, $meta, $hydrateChild) { + foreach ($value as $key => $child) { + $value[$key] = $hydrateChild($key, $child); + } + + return $meta['class']::fromLivewire($value); + } +} diff --git a/vendor/livewire/livewire/src/Form.php b/vendor/livewire/livewire/src/Form.php new file mode 100644 index 00000000..aac5d5ae --- /dev/null +++ b/vendor/livewire/livewire/src/Form.php @@ -0,0 +1,10 @@ +getParameters() as $parameter) { + static::substituteNameBindingForCallParameter($parameter, $parameters, $paramIndex); + static::substituteImplicitBindingForCallParameter($container, $parameter, $parameters); + static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies); + } + + return array_values(array_merge($dependencies, $parameters)); + } + + protected static function substituteNameBindingForCallParameter($parameter, array &$parameters, int &$paramIndex) + { + // check if we have a candidate for name/value binding + if (! array_key_exists($paramIndex, $parameters)) { + return; + } + + if ($parameter->isVariadic()) { + // this last param will pick up the rest - reindex any remaining parameters + $parameters = array_merge( + array_filter($parameters, function ($key) { return ! is_int($key); }, ARRAY_FILTER_USE_KEY), + array_values(array_filter($parameters, function ($key) { return is_int($key); }, ARRAY_FILTER_USE_KEY)) + ); + + return; + } + + // stop if this one is due for dependency injection + if (! is_null($className = static::getClassForDependencyInjection($parameter)) && ! $parameters[$paramIndex] instanceof $className) { + return; + } + + if (! array_key_exists($paramName = $parameter->getName(), $parameters)) { + // have a parameter value that is bound by sequential order + // and not yet bound by name, so bind it to parameter name + + $parameters[$paramName] = $parameters[$paramIndex]; + unset($parameters[$paramIndex]); + $paramIndex++; + } + } + + protected static function substituteImplicitBindingForCallParameter($container, $parameter, array &$parameters) + { + $paramName = $parameter->getName(); + + // check if we have a candidate for implicit binding + if (is_null($className = static::getClassForImplicitBinding($parameter))) { + return; + } + + // Check if the value we have for this param is an instance + // of the desired class, attempt implicit binding if not + if (array_key_exists($paramName, $parameters) && ! $parameters[$paramName] instanceof $className) { + $parameters[$paramName] = static::getImplicitBinding($container, $className, $parameters[$paramName]); + } elseif (array_key_exists($className, $parameters) && ! $parameters[$className] instanceof $className) { + $parameters[$className] = static::getImplicitBinding($container, $className, $parameters[$className]); + } + } + + protected static function getClassForDependencyInjection($parameter) + { + if (! is_null($className = static::getParameterClassName($parameter)) && ! static::implementsInterface($parameter)) { + return $className; + } + } + + protected static function getClassForImplicitBinding($parameter) + { + if (! is_null($className = static::getParameterClassName($parameter)) && static::implementsInterface($parameter)) { + return $className; + } + + return null; + } + + protected static function getImplicitBinding($container, $className, $value) + { + $model = $container->make($className)->resolveRouteBinding($value); + + if (! $model) { + throw (new ModelNotFoundException)->setModel($className, [$value]); + } + + return $model; + } + + public static function getParameterClassName($parameter) + { + $type = $parameter->getType(); + + if(!$type){ + return null; + } + + if(! $type instanceof ReflectionNamedType){ + return null; + } + + return (! $type->isBuiltin()) ? $type->getName() : null; + } + + public static function implementsInterface($parameter) + { + return (new ReflectionClass($parameter->getType()->getName()))->implementsInterface(ImplicitlyBindable::class); + } +} diff --git a/vendor/livewire/livewire/src/Livewire.php b/vendor/livewire/livewire/src/Livewire.php new file mode 100644 index 00000000..b502569c --- /dev/null +++ b/vendor/livewire/livewire/src/Livewire.php @@ -0,0 +1,43 @@ +provider = $provider; + } + + function provide($callback) + { + \Closure::bind($callback, $this->provider, $this->provider::class)(); + } + + function component($name, $class = null) + { + app(ComponentRegistry::class)->component($name, $class); + } + + function componentHook($hook) + { + ComponentHookRegistry::register($hook); + } + + function propertySynthesizer($synth) + { + app(HandleComponents::class)->registerPropertySynthesizer($synth); + } + + function directive($name, $callback) + { + app(ExtendBlade::class)->livewireOnlyDirective($name, $callback); + } + + function precompiler($callback) + { + app(ExtendBlade::class)->livewireOnlyPrecompiler($callback); + } + + function new($name, $id = null) + { + return app(ComponentRegistry::class)->new($name, $id); + } + + function isDiscoverable($componentNameOrClass) + { + return app(ComponentRegistry::class)->isDiscoverable($componentNameOrClass); + } + + function resolveMissingComponent($resolver) + { + return app(ComponentRegistry::class)->resolveMissingComponent($resolver); + } + + function mount($name, $params = [], $key = null) + { + return app(HandleComponents::class)->mount($name, $params, $key); + } + + function snapshot($component) + { + return app(HandleComponents::class)->snapshot($component); + } + + function fromSnapshot($snapshot) + { + return app(HandleComponents::class)->fromSnapshot($snapshot); + } + + function listen($eventName, $callback) { + return on($eventName, $callback); + } + + function current() + { + return last(app(HandleComponents::class)::$componentStack); + } + + function update($snapshot, $diff, $calls) + { + return app(HandleComponents::class)->update($snapshot, $diff, $calls); + } + + function updateProperty($component, $path, $value) + { + $dummyContext = new ComponentContext($component, false); + + return app(HandleComponents::class)->updateProperty($component, $path, $value, $dummyContext); + } + + function isLivewireRequest() + { + return app(HandleRequests::class)->isLivewireRequest(); + } + + function componentHasBeenRendered() + { + return SupportAutoInjectedAssets::$hasRenderedAComponentThisRequest; + } + + function forceAssetInjection() + { + SupportAutoInjectedAssets::$forceAssetInjection = true; + } + + function setUpdateRoute($callback) + { + return app(HandleRequests::class)->setUpdateRoute($callback); + } + + function getUpdateUri() + { + return app(HandleRequests::class)->getUpdateUri(); + } + + function setScriptRoute($callback) + { + return app(FrontendAssets::class)->setScriptRoute($callback); + } + + function useScriptTagAttributes($attributes) + { + return app(FrontendAssets::class)->useScriptTagAttributes($attributes); + } + + protected $queryParamsForTesting = []; + + function withUrlParams($params) + { + return $this->withQueryParams($params); + } + + function withQueryParams($params) + { + $this->queryParamsForTesting = $params; + + return $this; + } + + function test($name, $params = []) + { + return Testable::create($name, $params, $this->queryParamsForTesting); + } + + function visit($name) + { + return DuskTestable::create($name, $params = [], $this->queryParamsForTesting); + } + + function actingAs(\Illuminate\Contracts\Auth\Authenticatable $user, $driver = null) + { + Testable::actingAs($user, $driver); + + return $this; + } + + function isRunningServerless() + { + return in_array($_ENV['SERVER_SOFTWARE'] ?? null, [ + 'vapor', + 'bref', + ]); + } + + function addPersistentMiddleware($middleware) + { + app(PersistentMiddleware::class)->addPersistentMiddleware($middleware); + } + + function setPersistentMiddleware($middleware) + { + app(PersistentMiddleware::class)->setPersistentMiddleware($middleware); + } + + function getPersistentMiddleware() + { + return app(PersistentMiddleware::class)->getPersistentMiddleware(); + } + + function flushState() + { + trigger('flush-state'); + } + + function originalUrl() + { + if ($this->isLivewireRequest()) { + return url()->to($this->originalPath()); + } + + return url()->current(); + } + + function originalPath() + { + if ($this->isLivewireRequest()) { + $snapshot = json_decode(request('components.0.snapshot'), true); + + return data_get($snapshot, 'memo.path', 'POST'); + } + + return request()->path(); + } + + function originalMethod() + { + if ($this->isLivewireRequest()) { + $snapshot = json_decode(request('components.0.snapshot'), true); + + return data_get($snapshot, 'memo.method', 'POST'); + } + + return request()->method(); + } +} diff --git a/vendor/livewire/livewire/src/LivewireServiceProvider.php b/vendor/livewire/livewire/src/LivewireServiceProvider.php new file mode 100644 index 00000000..3169d96a --- /dev/null +++ b/vendor/livewire/livewire/src/LivewireServiceProvider.php @@ -0,0 +1,126 @@ +registerLivewireSingleton(); + $this->registerConfig(); + $this->bootEventBus(); + $this->registerMechanisms(); + } + + public function boot() + { + $this->bootMechanisms(); + $this->bootFeatures(); + } + + protected function registerLivewireSingleton() + { + $this->app->alias(LivewireManager::class, 'livewire'); + + $this->app->singleton(LivewireManager::class); + + app('livewire')->setProvider($this); + } + + protected function registerConfig() + { + $config = __DIR__.'/../config/livewire.php'; + + $this->publishes([$config => base_path('config/livewire.php')], ['livewire', 'livewire:config']); + + $this->mergeConfigFrom($config, 'livewire'); + } + + protected function bootEventBus() + { + (new \Livewire\EventBus)->boot(); + } + + protected function getMechanisms() + { + return [ + \Livewire\Mechanisms\PersistentMiddleware\PersistentMiddleware::class, + \Livewire\Mechanisms\HandleComponents\HandleComponents::class, + \Livewire\Mechanisms\HandleRequests\HandleRequests::class, + \Livewire\Mechanisms\FrontendAssets\FrontendAssets::class, + \Livewire\Mechanisms\ExtendBlade\ExtendBlade::class, + \Livewire\Mechanisms\CompileLivewireTags::class, + \Livewire\Mechanisms\ComponentRegistry::class, + \Livewire\Mechanisms\RenderComponent::class, + \Livewire\Mechanisms\DataStore::class, + ]; + } + + protected function registerMechanisms() + { + foreach ($this->getMechanisms() as $mechanism) { + (new $mechanism)->register($this); + } + } + + protected function bootMechanisms() + { + if (class_exists(AboutCommand::class) && class_exists(InstalledVersions::class)) { + AboutCommand::add('Livewire', [ + 'Livewire' => InstalledVersions::getPrettyVersion('livewire/livewire'), + ]); + } + + foreach ($this->getMechanisms() as $mechanism) { + (new $mechanism)->boot($this); + } + } + + protected function bootFeatures() + { + foreach([ + \Livewire\Features\SupportWireModelingNestedComponents\SupportWireModelingNestedComponents::class, + \Livewire\Features\SupportMultipleRootElementDetection\SupportMultipleRootElementDetection::class, + \Livewire\Features\SupportDisablingBackButtonCache\SupportDisablingBackButtonCache::class, + \Livewire\Features\SupportMorphAwareIfStatement\SupportMorphAwareIfStatement::class, + \Livewire\Features\SupportAutoInjectedAssets\SupportAutoInjectedAssets::class, + \Livewire\Features\SupportComputed\SupportLegacyComputedPropertySyntax::class, + \Livewire\Features\SupportNestingComponents\SupportNestingComponents::class, + \Livewire\Features\SupportBladeAttributes\SupportBladeAttributes::class, + \Livewire\Features\SupportConsoleCommands\SupportConsoleCommands::class, + \Livewire\Features\SupportPageComponents\SupportPageComponents::class, + \Livewire\Features\SupportReactiveProps\SupportReactiveProps::class, + \Livewire\Features\SupportFileDownloads\SupportFileDownloads::class, + \Livewire\Features\SupportJsEvaluation\SupportJsEvaluation::class, + \Livewire\Features\SupportQueryString\SupportQueryString::class, + \Livewire\Features\SupportFileUploads\SupportFileUploads::class, + \Livewire\Features\SupportTeleporting\SupportTeleporting::class, + \Livewire\Features\SupportLazyLoading\SupportLazyLoading::class, + \Livewire\Features\SupportFormObjects\SupportFormObjects::class, + \Livewire\Features\SupportAttributes\SupportAttributes::class, + \Livewire\Features\SupportPagination\SupportPagination::class, + \Livewire\Features\SupportValidation\SupportValidation::class, + \Livewire\Features\SupportRedirects\SupportRedirects::class, + \Livewire\Features\SupportStreaming\SupportStreaming::class, + \Livewire\Features\SupportNavigate\SupportNavigate::class, + \Livewire\Features\SupportEntangle\SupportEntangle::class, + \Livewire\Features\SupportLocales\SupportLocales::class, + \Livewire\Features\SupportTesting\SupportTesting::class, + \Livewire\Features\SupportModels\SupportModels::class, + \Livewire\Features\SupportEvents\SupportEvents::class, + + // Some features we want to have priority over others... + \Livewire\Features\SupportLifecycleHooks\SupportLifecycleHooks::class, + \Livewire\Features\SupportLegacyModels\SupportLegacyModels::class, + \Livewire\Features\SupportWireables\SupportWireables::class, + ] as $feature) { + app('livewire')->componentHook($feature); + } + + ComponentHookRegistry::boot(); + } +} + + diff --git a/vendor/livewire/livewire/src/Mechanisms/CompileLivewireTags.php b/vendor/livewire/livewire/src/Mechanisms/CompileLivewireTags.php new file mode 100644 index 00000000..7c4a84eb --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/CompileLivewireTags.php @@ -0,0 +1,102 @@ +precompiler($this->compileLivewireSelfClosingTags(...)); + } + + protected function compileLivewireSelfClosingTags($value) + { + $pattern = '/'.Regexes::$livewireOpeningTagOrSelfClosingTag.'/x'; + + return preg_replace_callback($pattern, function (array $matches) { + $attributes = $this->getAttributesFromAttributeString($matches['attributes']); + + $keys = array_keys($attributes); + $values = array_values($attributes); + $attributesCount = count($attributes); + + for ($i=0; $i < $attributesCount; $i++) { + if ($keys[$i] === ':' && $values[$i] === 'true') { + if (isset($values[$i + 1]) && $values[$i + 1] === 'true') { + $attributes[$keys[$i + 1]] = '$'.$keys[$i + 1]; + unset($attributes[':']); + } + } + } + + // Convert all kebab-cased to camelCase. + $attributes = collect($attributes)->mapWithKeys(function ($value, $key) { + // Skip snake_cased attributes. + if (str($key)->contains('_')) return [$key => $value]; + + return [(string) str($key)->camel() => $value]; + })->toArray(); + + // Convert all snake_cased attributes to camelCase, and merge with + // existing attributes so both snake and camel are available. + $attributes = collect($attributes)->mapWithKeys(function ($value, $key) { + // Skip snake_cased attributes + if (! str($key)->contains('_')) return [$key => false]; + + return [(string) str($key)->camel() => $value]; + })->filter()->merge($attributes)->toArray(); + + $component = $matches[1]; + + if ($component === 'styles') return '@livewireStyles'; + if ($component === 'scripts') return '@livewireScripts'; + if ($component === 'dynamic-component' || $component === 'is') { + if (! isset($attributes['component']) && ! isset($attributes['is'])) { + throw new ComponentAttributeMissingOnDynamicComponentException; + } + + // Does not need quotes as resolved with quotes already. + $component = $attributes['component'] ?? $attributes['is']; + + unset($attributes['component'], $attributes['is']); + } else { + // Add single quotes to the component name to compile it as string in quotes + $component = "'{$component}'"; + } + + return $this->componentString($component, $attributes); + }, $value); + } + + protected function componentString(string $component, array $attributes) + { + if (isset($attributes['key']) || isset($attributes['wire:key'])) { + $key = $attributes['key'] ?? $attributes['wire:key']; + unset($attributes['key'], $attributes['wire:key']); + + return "@livewire({$component}, [".$this->attributesToString($attributes, escapeBound: false)."], key({$key}))"; + } + + return "@livewire({$component}, [".$this->attributesToString($attributes, escapeBound: false).'])'; + } + + protected function attributesToString(array $attributes, $escapeBound = true) + { + return collect($attributes) + ->map(function (string $value, string $attribute) use ($escapeBound) { + return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) + ? "'{$attribute}' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute({$value})" + : "'{$attribute}' => {$value}"; + }) + ->implode(','); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/ComponentRegistry.php b/vendor/livewire/livewire/src/Mechanisms/ComponentRegistry.php new file mode 100644 index 00000000..93c892e2 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/ComponentRegistry.php @@ -0,0 +1,223 @@ +singleton($this::class); + } + + function boot() + { + // + } + + function component($name, $class = null) + { + if (is_null($class)) { + $this->nonAliasedClasses[] = $name; + } else { + $this->aliases[$name] = $class; + } + } + + function new($nameOrClass, $id = null) + { + [$class, $name] = $this->getNameAndClass($nameOrClass); + + $component = new $class; + + $component->setId($id ?: str()->random(20)); + + $component->setName($name); + + // // Parameters passed in automatically set public properties by the same name... + // foreach ($params as $key => $value) { + // if (! property_exists($component, $key)) continue; + + // // Typed properties shouldn't be set back to "null". It will throw an error... + // if ((new \ReflectionProperty($component, $key))->getType() && is_null($value)) continue; + + // $component->$key = $value; + // } + + return $component; + } + + function isDiscoverable($classOrName) + { + if (is_object($classOrName)) { + $classOrName = get_class($classOrName); + } + + if (class_exists($name = $classOrName)) { + $name = $this->generateNameFromClass($classOrName); + } + + $class = $this->generateClassFromName($name); + + if (class_exists($class) && is_subclass_of($class, Component::class)) { + return true; + } + + return false; + } + + function getName($nameOrClassOrComponent) + { + [$class, $name] = $this->getNameAndClass($nameOrClassOrComponent); + + return $name; + } + + function getClass($nameOrClassOrComponent) + { + [$class, $name] = $this->getNameAndClass($nameOrClassOrComponent); + + return $class; + } + + function resolveMissingComponent($resolver) + { + $this->missingComponentResolvers[] = $resolver; + } + + protected function getNameAndClass($nameComponentOrClass) + { + // If a component itself was passed in, just take the class name... + $nameOrClass = is_object($nameComponentOrClass) ? $nameComponentOrClass::class : $nameComponentOrClass; + + // If a component class was passed in, use that... + if (class_exists($nameOrClass)) { + $class = $nameOrClass; + // Otherwise, assume it was a simple name... + } else { + $class = $this->nameToClass($nameOrClass); + + // If class can't be found, see if there is an index component in a subfolder... + if(! class_exists($class)) { + $class = $class . '\\Index'; + } + + if(! class_exists($class)) { + foreach ($this->missingComponentResolvers as $resolve) { + if ($resolved = $resolve($nameOrClass)) { + $this->component($nameOrClass, $resolved); + + $class = $this->aliases[$nameOrClass]; + + break; + } + } + } + } + + // Now that we have a class, we can check that it's actually a Livewire component... + if (! is_subclass_of($class, Component::class)) { + throw new ComponentNotFoundException( + "Unable to find component: [{$nameOrClass}]" + ); + } + + // Convert it to a name even if a name was passed in to make sure we're using deterministic names... + $name = $this->classToName($class); + + return [$class, $name]; + } + + protected function nameToClass($name) + { + // Check the aliases... + if (isset($this->aliases[$name])) { + if (is_object($this->aliases[$name])) return $this->aliases[$name]::class; + + return $this->aliases[$name]; + } + + // Hash check the non-aliased classes... + foreach ($this->nonAliasedClasses as $class) { + if (crc32($class) === $name) { + return $class; + } + } + + // Reverse generate a class from a name... + return $this->generateClassFromName($name); + } + + protected function classToName($class) + { + // Check the aliases... + $resolvedAliases = array_map(fn ($i) => is_object($i) ? get_class($i) : $i, $this->aliases); + + if ($name = array_search($class, $resolvedAliases)) return $name; + + // Check existance in non-aliased classes and hash... + foreach ($this->nonAliasedClasses as $oneOff) { + if (crc32($oneOff) === $hash = crc32($class)) { + return $hash; + } + } + + // Generate name from class... + return $this->generateNameFromClass($class); + } + + protected function generateClassFromName($name) + { + $rootNamespace = config('livewire.class_namespace'); + + $class = collect(str($name)->explode('.')) + ->map(fn ($segment) => (string) str($segment)->studly()) + ->join('\\'); + + return '\\' . $rootNamespace . '\\' . $class; + } + + protected function generateNameFromClass($class) + { + $namespace = str_replace( + ['/', '\\'], + '.', + trim(trim(config('livewire.class_namespace')), '\\') + ); + + $class = str_replace( + ['/', '\\'], + '.', + trim(trim($class, '/'), '\\') + ); + + $namespace = collect(explode('.', $namespace)) + ->map(fn ($i) => \Illuminate\Support\Str::kebab($i)) + ->implode('.'); + + $fullName = str(collect(explode('.', $class)) + ->map(fn ($i) => \Illuminate\Support\Str::kebab($i)) + ->implode('.')); + + if ($fullName->startsWith('.')) { + $fullName = $fullName->substr(1); + } + + // If using an index component in a sub folder, remove the '.index' so the name is the subfolder name... + if ($fullName->endsWith('.index')) { + $fullName = $fullName->replaceLast('.index', ''); + } + + if ($fullName->startsWith($namespace)) { + return (string) $fullName->substr(strlen($namespace) + 1); + } + + return (string) $fullName; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/DataStore.php b/vendor/livewire/livewire/src/Mechanisms/DataStore.php new file mode 100644 index 00000000..9a1ef049 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/DataStore.php @@ -0,0 +1,99 @@ +singleton($this::class); + } + + function boot() + { + // + } + + protected $lookup; + + function __construct() + { + $this->lookup = new WeakMap; + } + + function set($instance, $key, $value) + { + if (! isset($this->lookup[$instance])) { + $this->lookup[$instance] = []; + } + + $this->lookup[$instance][$key] = $value; + } + + function has($instance, $key, $iKey = null) { + if (! isset($this->lookup[$instance])) { + return false; + } + + if (! isset($this->lookup[$instance][$key])) { + return false; + } + + if ($iKey !== null) { + return !! $this->lookup[$instance][$key][$iKey] ?? false; + } + + return true; + } + + function get($instance, $key, $default = null) + { + if (! isset($this->lookup[$instance])) { + return value($default); + } + + if (! isset($this->lookup[$instance][$key])) { + return value($default); + } + + return $this->lookup[$instance][$key]; + } + + function find($instance, $key, $iKey = null, $default = null) + { + if (! isset($this->lookup[$instance])) { + return value($default); + } + + if (! isset($this->lookup[$instance][$key])) { + return value($default); + } + + if ($iKey !== null && ! isset($this->lookup[$instance][$key][$iKey])) { + return value($default); + } + + return $iKey !== null + ? $this->lookup[$instance][$key][$iKey] + : $this->lookup[$instance][$key]; + } + + function push($instance, $key, $value, $iKey = null) + { + if (! isset($this->lookup[$instance])) { + $this->lookup[$instance] = []; + } + + if (! isset($this->lookup[$instance][$key])) { + $this->lookup[$instance][$key] = []; + } + + if ($iKey) { + $this->lookup[$instance][$key][$iKey] = $value; + } else { + $this->lookup[$instance][$key][] = $value; + } + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendBlade.php b/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendBlade.php new file mode 100644 index 00000000..1e7e8ddb --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendBlade.php @@ -0,0 +1,134 @@ +singleton($this::class, fn () => $this); + + Blade::directive('this', fn() => "window.Livewire.find('{{ \$_instance->getId() }}')"); + + on('render', function ($target, $view) { + $this->startLivewireRendering($target); + + $undo = $this->livewireifyBladeCompiler(); + + $this->renderCounter++; + + return function ($html) use ($view, $undo, $target) { + $this->endLivewireRendering(); + + $this->renderCounter--; + + if ($this->renderCounter === 0) { + $undo(); + } + + return $html; + }; + }); + + // This is a custom view engine that gets used when rendering + // Livewire views. Things like letting certain exceptions bubble + // to the handler, and registering custom directives like: "@this". + app()->make('view.engine.resolver')->register('blade', function () { + return new ExtendedCompilerEngine(app('blade.compiler')); + }); + } + + function livewireOnlyDirective($name, $handler) + { + $this->directives[$name] = $handler; + } + + function livewireOnlyPrecompiler($handler) + { + $this->precompilers[] = $handler; + } + + function livewireifyBladeCompiler() { + $removals = []; + + if ($this->renderCounter === 0) { + $customDirectives = app('blade.compiler')->getCustomDirectives(); + $precompilers = invade(app('blade.compiler'))->precompilers; + + foreach ($this->directives as $name => $handler) { + if (! isset($customDirectives[$name])) { + $customDirectives[$name] = $handler; + + invade(app('blade.compiler'))->customDirectives = $customDirectives; + + $removals[] = function () use ($name) { + $customDirectives = app('blade.compiler')->getCustomDirectives(); + + unset($customDirectives[$name]); + + invade(app('blade.compiler'))->customDirectives = $customDirectives; + }; + } + } + + foreach ($this->precompilers as $handler) { + if (array_search($handler, $precompilers) === false) { + array_unshift($precompilers, $handler); + + invade(app('blade.compiler'))->precompilers = $precompilers; + + $removals[] = function () use ($handler) { + $precompilers = invade(app('blade.compiler'))->precompilers; + + $index = array_search($handler, $precompilers); + + if ($index === false) return; + + unset($precompilers[$index]); + + invade(app('blade.compiler'))->precompilers = $precompilers; + }; + } + } + } + + return function () use ($removals) { + while ($removals) array_pop($removals)(); + }; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php b/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php new file mode 100644 index 00000000..ef66cf05 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php @@ -0,0 +1,78 @@ +handleViewException($e, $obLevel); + } catch (\Throwable $e) { + $this->handleViewException($e, $obLevel); + } + + return ltrim(ob_get_clean()); + } + + // Errors thrown while a view is rendering are caught by the Blade + // compiler and wrapped in an "ErrorException". This makes Livewire errors + // harder to read, AND causes issues like `abort(404)` not actually working. + protected function handleViewException(\Throwable $e, $obLevel) + { + if ($this->shouldBypassExceptionForLivewire($e, $obLevel)) { + // This is because there is no "parent::parent::". + \Illuminate\View\Engines\PhpEngine::handleViewException($e, $obLevel); + + return; + } + + parent::handleViewException($e, $obLevel); + } + + public function shouldBypassExceptionForLivewire(\Throwable $e, $obLevel) + { + $uses = array_flip(class_uses_recursive($e)); + + return ( + // Don't wrap "abort(403)". + $e instanceof \Illuminate\Auth\Access\AuthorizationException + // Don't wrap "abort(404)". + || $e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + // Don't wrap "abort(500)". + || $e instanceof \Symfony\Component\HttpKernel\Exception\HttpException + // Don't wrap most Livewire exceptions. + || isset($uses[\Livewire\Exceptions\BypassViewHandler::class]) + ); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/FrontendAssets/FrontendAssets.php b/vendor/livewire/livewire/src/Mechanisms/FrontendAssets/FrontendAssets.php new file mode 100644 index 00000000..6111387e --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/FrontendAssets/FrontendAssets.php @@ -0,0 +1,192 @@ +singleton($this::class); + } + + public function boot() + { + app($this::class)->setScriptRoute(function ($handle) { + return Route::get('/livewire/livewire.js', $handle); + }); + + Blade::directive('livewireScripts', [static::class, 'livewireScripts']); + Blade::directive('livewireScriptConfig', [static::class, 'livewireScriptConfig']); + Blade::directive('livewireStyles', [static::class, 'livewireStyles']); + + on('flush-state', function () { + $instance = app(static::class); + + $instance->hasRenderedScripts = false; + $instance->hasRenderedStyles = false; + }); + } + + function useScriptTagAttributes($attributes) + { + $this->scriptTagAttributes = array_merge($this->scriptTagAttributes, $attributes); + } + + function setScriptRoute($callback) + { + $route = $callback([self::class, 'returnJavaScriptAsFile']); + + $this->javaScriptRoute = $route; + } + + public static function livewireScripts($expression) + { + return '{!! \Livewire\Mechanisms\FrontendAssets\FrontendAssets::scripts('.$expression.') !!}'; + } + + public static function livewireScriptConfig($expression) + { + return '{!! \Livewire\Mechanisms\FrontendAssets\FrontendAssets::scriptConfig('.$expression.') !!}'; + } + + public static function livewireStyles($expression) + { + return '{!! \Livewire\Mechanisms\FrontendAssets\FrontendAssets::styles('.$expression.') !!}'; + } + + public function returnJavaScriptAsFile() + { + return Utils::pretendResponseIsFile(__DIR__.'/../../../dist/livewire.js'); + } + + public function maps() + { + return Utils::pretendResponseIsFile(__DIR__.'/../../../dist/livewire.js.map'); + } + + public static function styles($options = []) + { + app(static::class)->hasRenderedStyles = true; + + $nonce = isset($options['nonce']) ? "nonce=\"{$options['nonce']}\"" : ''; + + $html = << + + HTML; + + return static::minify($html); + } + + public static function scripts($options = []) + { + app(static::class)->hasRenderedScripts = true; + + $debug = config('app.debug'); + + $scripts = static::js($options); + + // HTML Label. + $html = $debug ? [''] : []; + + $html[] = $scripts; + + return implode("\n", $html); + } + + public static function js($options) + { + // Use the default endpoint... + $url = app(static::class)->javaScriptRoute->uri; + + // Use the configured one... + $url = config('livewire.asset_url') ?: $url; + + // Use the legacy passed in one... + $url = $options['asset_url'] ?? $url; + + // Use the new passed in one... + $url = $options['url'] ?? $url; + + $url = rtrim($url, '/'); + + $url = (string) str($url)->start('/'); + + // Add the build manifest hash to it... + $manifest = json_decode(file_get_contents(__DIR__.'/../../../dist/manifest.json'), true); + $versionHash = $manifest['/livewire.js']; + $url = "{$url}?id={$versionHash}"; + + $token = app()->has('session.store') ? csrf_token() : ''; + + $nonce = isset($options['nonce']) ? "nonce=\"{$options['nonce']}\"" : ''; + + $progressBar = config('livewire.navigate.show_progress_bar', true) ? '' : 'data-no-progress-bar'; + + $updateUri = app('livewire')->getUpdateUri(); + + $extraAttributes = Utils::stringifyHtmlAttributes( + app(static::class)->scriptTagAttributes, + ); + + return << + HTML; + } + + public static function scriptConfig($options = []) + { + app(static::class)->hasRenderedScripts = true; + + $nonce = isset($options['nonce']) ? " nonce=\"{$options['nonce']}\"" : ''; + + $progressBar = config('livewire.navigate.show_progress_bar', true); + + $attributes = json_encode([ + 'csrf' => app()->has('session.store') ? csrf_token() : '', + 'uri' => app('livewire')->getUpdateUri(), + 'progressBar' => $progressBar, + ]); + + return <<window.livewireScriptConfig = {$attributes}; + HTML; + } + + protected static function minify($subject) + { + return preg_replace('~(\v|\t|\s{2,})~m', '', $subject); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/BaseRenderless.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/BaseRenderless.php new file mode 100644 index 00000000..a8a15fc3 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/BaseRenderless.php @@ -0,0 +1,14 @@ +component->skipRender(); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Checksum.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Checksum.php new file mode 100644 index 00000000..ecbd46b9 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Checksum.php @@ -0,0 +1,31 @@ +getKey(); + + $checksum = hash_hmac('sha256', json_encode($snapshot), $hashKey); + + trigger('checksum.generate', $checksum, $snapshot); + + return $checksum; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ComponentContext.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ComponentContext.php new file mode 100644 index 00000000..7087c3a8 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ComponentContext.php @@ -0,0 +1,55 @@ + $iValue) $this->addEffect($iKey, $iValue); + + return; + } + + $this->effects[$key] = $value; + } + + public function pushEffect($key, $value, $iKey = null) + { + if (! isset($this->effects[$key])) $this->effects[$key] = []; + + if ($iKey) { + $this->effects[$key][$iKey] = $value; + } else { + $this->effects[$key][] = $value; + } + } + + public function addMemo($key, $value) + { + $this->memo[$key] = $value; + } + + public function pushMemo($key, $value, $iKey = null) + { + if (! isset($this->memo[$key])) $this->memo[$key] = []; + + if ($iKey) { + $this->memo[$key][$iKey] = $value; + } else { + $this->memo[$key][] = $value; + } + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/CorruptComponentPayloadException.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/CorruptComponentPayloadException.php new file mode 100644 index 00000000..bd495049 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/CorruptComponentPayloadException.php @@ -0,0 +1,18 @@ +singleton($this::class); + } + + public function boot() + { + // + } + + public function registerPropertySynthesizer($synth) + { + foreach ((array) $synth as $class) { + array_unshift($this->propertySynthesizers, $class); + } + } + + public function mount($name, $params = [], $key = null) + { + $parent = app('livewire')->current(); + + if ($html = $this->shortCircuitMount($name, $params, $key, $parent)) return $html; + + $component = app('livewire')->new($name); + + $this->pushOntoComponentStack($component); + + $context = new ComponentContext($component, mounting: true); + + if (config('app.debug')) $start = microtime(true); + $finish = trigger('mount', $component, $params, $key, $parent); + if (config('app.debug')) trigger('profile', 'mount', $component->getId(), [$start, microtime(true)]); + + if (config('app.debug')) $start = microtime(true); + $html = $this->render($component, '
'); + if (config('app.debug')) trigger('profile', 'render', $component->getId(), [$start, microtime(true)]); + + if (config('app.debug')) $start = microtime(true); + trigger('dehydrate', $component, $context); + + $snapshot = $this->snapshot($component, $context); + if (config('app.debug')) trigger('profile', 'dehydrate', $component->getId(), [$start, microtime(true)]); + + trigger('destroy', $component, $context); + + $html = Utils::insertAttributesIntoHtmlRoot($html, [ + 'wire:snapshot' => $snapshot, + 'wire:effects' => $context->effects, + ]); + + $this->popOffComponentStack(); + + return $finish($html, $snapshot); + } + + protected function shortCircuitMount($name, $params, $key, $parent) + { + $newHtml = null; + + trigger('pre-mount', $name, $params, $key, $parent, function ($html) use (&$newHtml) { + $newHtml = $html; + }); + + return $newHtml; + } + + public function update($snapshot, $updates, $calls) + { + $data = $snapshot['data']; + $memo = $snapshot['memo']; + + if (config('app.debug')) $start = microtime(true); + [ $component, $context ] = $this->fromSnapshot($snapshot); + + $this->pushOntoComponentStack($component); + + trigger('hydrate', $component, $memo, $context); + + $this->updateProperties($component, $updates, $data, $context); + if (config('app.debug')) trigger('profile', 'hydrate', $component->getId(), [$start, microtime(true)]); + + $this->callMethods($component, $calls, $context); + + if (config('app.debug')) $start = microtime(true); + if ($html = $this->render($component)) { + $context->addEffect('html', $html); + if (config('app.debug')) trigger('profile', 'render', $component->getId(), [$start, microtime(true)]); + } + + if (config('app.debug')) $start = microtime(true); + trigger('dehydrate', $component, $context); + + $snapshot = $this->snapshot($component, $context); + if (config('app.debug')) trigger('profile', 'dehydrate', $component->getId(), [$start, microtime(true)]); + + trigger('destroy', $component, $context); + + $this->popOffComponentStack(); + + return [ $snapshot, $context->effects ]; + } + + public function fromSnapshot($snapshot) + { + Checksum::verify($snapshot); + + trigger('snapshot-verified', $snapshot); + + $data = $snapshot['data']; + $name = $snapshot['memo']['name']; + $id = $snapshot['memo']['id']; + + $component = app('livewire')->new($name, id: $id); + + $context = new ComponentContext($component); + + $this->hydrateProperties($component, $data, $context); + + return [ $component, $context ]; + } + + public function snapshot($component, $context = null) + { + $context ??= new ComponentContext($component); + + $data = $this->dehydrateProperties($component, $context); + + $snapshot = [ + 'data' => $data, + 'memo' => [ + 'id' => $component->getId(), + 'name' => $component->getName(), + ...$context->memo, + ], + ]; + + $snapshot['checksum'] = Checksum::generate($snapshot); + + return $snapshot; + } + + protected function dehydrateProperties($component, $context) + { + $data = Utils::getPublicPropertiesDefinedOnSubclass($component); + + foreach ($data as $key => $value) { + $data[$key] = $this->dehydrate($value, $context, $key); + } + + return $data; + } + + protected function dehydrate($target, $context, $path) + { + if (Utils::isAPrimitive($target)) return $target; + + $synth = $this->propertySynth($target, $context, $path); + + [ $data, $meta ] = $synth->dehydrate($target, function ($name, $child) use ($context, $path) { + return $this->dehydrate($child, $context, "{$path}.{$name}"); + }); + + $meta['s'] = $synth::getKey(); + + return [ $data, $meta ]; + } + + protected function hydrateProperties($component, $data, $context) + { + foreach ($data as $key => $value) { + if (! property_exists($component, $key)) continue; + + $child = $this->hydrate($value, $context, $key); + + // Typed properties shouldn't be set back to "null". It will throw an error... + if ((new \ReflectionProperty($component, $key))->getType() && is_null($child)) continue; + + $component->$key = $child; + } + } + + protected function hydrate($valueOrTuple, $context, $path) + { + if (! Utils::isSyntheticTuple($value = $tuple = $valueOrTuple)) return $value; + + [$value, $meta] = $tuple; + + $synth = $this->propertySynth($meta['s'], $context, $path); + + return $synth->hydrate($value, $meta, function ($name, $child) use ($context, $path) { + return $this->hydrate($child, $context, "{$path}.{$name}"); + }); + } + + protected function render($component, $default = null) + { + if ($html = store($component)->get('skipRender', false)) { + $html = value(is_string($html) ? $html : $default); + + if (! $html) return; + + return Utils::insertAttributesIntoHtmlRoot($html, [ + 'wire:id' => $component->getId(), + ]); + } + + [ $view, $properties ] = $this->getView($component); + + return $this->trackInRenderStack($component, function () use ($component, $view, $properties) { + $finish = trigger('render', $component, $view, $properties); + + $revertA = Utils::shareWithViews('__livewire', $component); + $revertB = Utils::shareWithViews('_instance', $component); // @deprecated + + $slots = $pushes = $prepends = $sections = null; + + $viewContext = new ViewContext; + + $html = $view->render(function ($view) use ($viewContext) { + // Extract leftover slots, sections, and pushes before they get flushed... + $viewContext->extractFromEnvironment($view->getFactory()); + }); + + $revertA(); $revertB(); + + $html = Utils::insertAttributesIntoHtmlRoot($html, [ + 'wire:id' => $component->getId(), + ]); + + $replaceHtml = function ($newHtml) use (&$html) { + $html = $newHtml; + }; + + $html = $finish($html, $replaceHtml, $viewContext); + + return $html; + }); + } + + protected function getView($component) + { + $viewPath = config('livewire.view_path', resource_path('views/livewire')); + + $dotName = $component->getName(); + + $fileName = str($dotName)->replace('.', '/')->__toString(); + + $viewOrString = method_exists($component, 'render') + ? wrap($component)->render() + : View::file($viewPath . '/' . $fileName . '.blade.php'); + + $properties = Utils::getPublicPropertiesDefinedOnSubclass($component); + + $view = Utils::generateBladeView($viewOrString, $properties); + + return [ $view, $properties ]; + } + + protected function trackInRenderStack($component, $callback) + { + array_push(static::$renderStack, $component); + + return tap($callback(), function () { + array_pop(static::$renderStack); + }); + } + + protected function updateProperties($component, $updates, $data, $context) + { + foreach ($updates as $path => $value) { + $value = $this->hydrateForUpdate($data, $path, $value, $context); + + $this->updateProperty($component, $path, $value, $context); + } + } + + public function updateProperty($component, $path, $value, $context) + { + $segments = explode('.', $path); + + $property = array_shift($segments); + + $finish = trigger('update', $component, $path, $value); + + // If this isn't a "deep" set, set it directly, otherwise we have to + // recursively get up and set down the value through the synths... + if (empty($segments)) { + if ($value !== '__rm__') $this->setComponentPropertyAwareOfTypes($component, $property, $value); + } else { + $propertyValue = $component->$property; + + $this->setComponentPropertyAwareOfTypes($component, $property, + $this->recursivelySetValue($property, $propertyValue, $value, $segments, 0, $context) + ); + } + + $finish(); + } + + protected function hydrateForUpdate($raw, $path, $value, $context) + { + $meta = $this->getMetaForPath($raw, $path); + $component = $context->component; + + // If we have meta data already for this property, let's use that to get a synth... + if ($meta) { + return $this->hydrate([$value, $meta], $context, $path); + } + + // If we don't, let's check to see if it's a typed property and fetch the synth that way... + $parent = str($path)->contains('.') + ? data_get($context->component, str($path)->beforeLast('.')->toString()) + : $context->component; + + $childKey = str($path)->afterLast('.'); + + if ($parent && is_object($parent) && property_exists($parent, $childKey) && Utils::propertyIsTyped($parent, $childKey)) { + $type = Utils::getProperty($parent, $childKey)->getType(); + + $types = $type instanceof ReflectionUnionType ? $type->getTypes() : [$type]; + + foreach ($types as $type) { + $synth = $this->getSynthesizerByType($type->getName(), $context, $path); + + if ($synth) return $synth->hydrateFromType($type->getName(), $value); + } + } + + return $value; + } + + protected function getMetaForPath($raw, $path) + { + $segments = explode('.', $path); + + $first = array_shift($segments); + + [$data, $meta] = Utils::isSyntheticTuple($raw) ? $raw : [$raw, null]; + + if ($path !== '') { + $value = $data[$first] ?? null; + + return $this->getMetaForPath($value, implode('.', $segments)); + } + + return $meta; + } + + protected function recursivelySetValue($baseProperty, $target, $leafValue, $segments, $index = 0, $context = null) + { + $isLastSegment = count($segments) === $index + 1; + + $property = $segments[$index]; + + $path = implode('.', array_slice($segments, 0, $index + 1)); + + $synth = $this->propertySynth($target, $context, $path); + + if ($isLastSegment) { + $toSet = $leafValue; + } else { + $propertyTarget = $synth->get($target, $property); + + // "$path" is a dot-notated key. This means we may need to drill + // down and set a value on a deeply nested object. That object + // may not exist, so let's find the first one that does... + + // Here's we've determined we're trying to set a deeply nested + // value on an object/array that doesn't exist, so we need + // to build up that non-existant nesting structure first. + if ($propertyTarget === null) $propertyTarget = []; + + $toSet = $this->recursivelySetValue($baseProperty, $propertyTarget, $leafValue, $segments, $index + 1, $context); + } + + $method = ($leafValue === '__rm__' && $isLastSegment) ? 'unset' : 'set'; + + $pathThusFar = collect([$baseProperty, ...$segments])->slice(0, $index + 1)->join('.'); + $fullPath = collect([$baseProperty, ...$segments])->join('.'); + + $synth->$method($target, $property, $toSet, $pathThusFar, $fullPath); + + return $target; + } + + protected function setComponentPropertyAwareOfTypes($component, $property, $value) + { + try { + $component->$property = $value; + } catch (\TypeError $e) { + // If an "int" is being set to empty string, unset the property (making it null). + // This is common in the case of `wire:model`ing an int to a text field... + // If a value is being set to "null", do the same... + if ($value === '' || $value === null) { + unset($component->$property); + } else { + throw $e; + } + } + } + + protected function callMethods($root, $calls, $context) + { + $returns = []; + + foreach ($calls as $idx => $call) { + $method = $call['method']; + $params = $call['params']; + + + $earlyReturnCalled = false; + $earlyReturn = null; + $returnEarly = function ($return = null) use (&$earlyReturnCalled, &$earlyReturn) { + $earlyReturnCalled = true; + $earlyReturn = $return; + }; + + $finish = trigger('call', $root, $method, $params, $context, $returnEarly); + + if ($earlyReturnCalled) { + $returns[] = $finish($earlyReturn); + + continue; + } + + $methods = Utils::getPublicMethodsDefinedBySubClass($root); + + // Also remove "render" from the list... + $methods = array_values(array_diff($methods, ['render'])); + + // @todo: put this in a better place: + $methods[] = '__dispatch'; + + if (! in_array($method, $methods)) { + throw new MethodNotFoundException($method); + } + + if (config('app.debug')) $start = microtime(true); + $return = wrap($root)->{$method}(...$params); + if (config('app.debug')) trigger('profile', 'call'.$idx, $root->getId(), [$start, microtime(true)]); + + $returns[] = $finish($return); + } + + $context->addEffect('returns', $returns); + } + + protected function propertySynth($keyOrTarget, $context, $path): Synth + { + return is_string($keyOrTarget) + ? $this->getSynthesizerByKey($keyOrTarget, $context, $path) + : $this->getSynthesizerByTarget($keyOrTarget, $context, $path); + } + + protected function getSynthesizerByKey($key, $context, $path) + { + foreach ($this->propertySynthesizers as $synth) { + if ($synth::getKey() === $key) { + return new $synth($context, $path); + } + } + + throw new \Exception('No synthesizer found for key: "'.$key.'"'); + } + + protected function getSynthesizerByTarget($target, $context, $path) + { + foreach ($this->propertySynthesizers as $synth) { + if ($synth::match($target)) { + return new $synth($context, $path); + } + } + + throw new \Exception('Property type not supported in Livewire for property: ['.json_encode($target).']'); + } + + protected function getSynthesizerByType($type, $context, $path) + { + foreach ($this->propertySynthesizers as $synth) { + if ($synth::matchByType($type)) { + return new $synth($context, $path); + } + } + + return null; + } + + protected function pushOntoComponentStack($component) + { + array_push($this::$componentStack, $component); + } + + protected function popOffComponentStack() + { + array_pop($this::$componentStack); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/ArraySynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/ArraySynth.php new file mode 100644 index 00000000..23ed08e2 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/ArraySynth.php @@ -0,0 +1,43 @@ + $child) { + $target[$key] = $dehydrateChild($key, $child); + } + + return [$target, []]; + } + + function hydrate($value, $meta, $hydrateChild) { + // If we are "hydrating" a value about to be used in an update, + // Let's make sure it's actually an array before try to set it. + // This is most common in the case of "__rm__" values, but also + // applies to any non-array value... + if (! is_array($value)) { + return $value; + } + + foreach ($value as $key => $child) { + $value[$key] = $hydrateChild($key, $child); + } + + return $value; + } + + function set(&$target, $key, $value) { + $target[$key] = $value; + } + + function unset(&$target, $key) { + unset($target[$key]); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CarbonSynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CarbonSynth.php new file mode 100644 index 00000000..e10e9ab3 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CarbonSynth.php @@ -0,0 +1,40 @@ + DateTime::class, + 'nativeImmutable' => DateTimeImmutable::class, + 'carbon' => Carbon::class, + 'carbonImmutable' => CarbonImmutable::class, + 'illuminate' => \Illuminate\Support\Carbon::class, + ]; + + public static $key = 'cbn'; + + static function match($target) { + foreach (static::$types as $type => $class) { + if ($target instanceof $class) return true; + } + + return false; + } + + function dehydrate($target) { + return [ + $target->format(DateTimeInterface::ATOM), + ['type' => array_search(get_class($target), static::$types)], + ]; + } + + function hydrate($value, $meta) { + return new static::$types[$meta['type']]($value); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CollectionSynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CollectionSynth.php new file mode 100644 index 00000000..d39845fc --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/CollectionSynth.php @@ -0,0 +1,51 @@ +all(); + + foreach ($data as $key => $child) { + $data[$key] = $dehydrateChild($key, $child); + } + + return [ + $data, + ['class' => get_class($target)] + ]; + } + + function hydrate($value, $meta, $hydrateChild) { + foreach ($value as $key => $child) { + $value[$key] = $hydrateChild($key, $child); + } + + return new $meta['class']($value); + } + + function &get(&$target, $key) { + // We need this "$reader" callback to get a reference to + // the items property inside collections. Otherwise, + // we'd receive a copy instead of the reference. + $reader = function & ($object, $property) { + $value = & \Closure::bind(function & () use ($property) { + return $this->$property; + }, $object, $object)->__invoke(); + + return $value; + }; + + $items =& $reader($target, 'items'); + + return $items[$key]; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/EnumSynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/EnumSynth.php new file mode 100644 index 00000000..d032245a --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/EnumSynth.php @@ -0,0 +1,36 @@ +value, + ['class' => get_class($target)] + ]; + } + + function hydrate($value, $meta) { + if ($value === null) return null; + + $class = $meta['class']; + + return $class::from($value); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/IntSynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/IntSynth.php new file mode 100644 index 00000000..a07435a7 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/IntSynth.php @@ -0,0 +1,22 @@ + $child) { + $data[$key] = $dehydrateChild($key, $child); + } + + return [$data, []]; + } + + function hydrate($value, $meta, $hydrateChild) { + $obj = new stdClass; + + foreach ($value as $key => $child) { + $obj->$key = $hydrateChild($key, $child); + } + + return $obj; + } + + function set(&$target, $key, $value) { + $target->$key = $value; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/StringableSynth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/StringableSynth.php new file mode 100644 index 00000000..2065f66b --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/StringableSynth.php @@ -0,0 +1,21 @@ +__toString(), []]; + } + + function hydrate($value) { + return str($value); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/Synth.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/Synth.php new file mode 100644 index 00000000..f5310f0f --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/Synthesizers/Synth.php @@ -0,0 +1,66 @@ +$key; + } + + function __call($method, $params) { + if ($method === 'dehydrate') { + throw new \Exception('You must define a "dehydrate" method'); + } + + if ($method === 'hydrate') { + throw new \Exception('You must define a "hydrate" method'); + } + + if ($method === 'hydrateFromType') { + throw new \Exception('You must define a "hydrate" method'); + } + + if ($method === 'get') { + throw new \Exception('This synth doesn\'t support getting properties: '.get_class($this)); + } + + if ($method === 'set') { + throw new \Exception('This synth doesn\'t support setting properties: '.get_class($this)); + } + + if ($method === 'unset') { + throw new \Exception('This synth doesn\'t support unsetting properties: '.get_class($this)); + } + + if ($method === 'call') { + throw new \Exception('This synth doesn\'t support calling methods: '.get_class($this)); + } + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ViewContext.php b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ViewContext.php new file mode 100644 index 00000000..c6464114 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleComponents/ViewContext.php @@ -0,0 +1,35 @@ +slots = $factory->slots; + $this->pushes = $factory->pushes; + $this->prepends = $factory->prepends; + $this->sections = $factory->sections; + } + + function mergeIntoNewEnvironment($__env) + { + $factory = invade($__env); + + $factory->slots = $this->slots; + $factory->pushes = $this->pushes; + $factory->prepends = $this->prepends; + $factory->sections = $this->sections; + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/HandleRequests/HandleRequests.php b/vendor/livewire/livewire/src/Mechanisms/HandleRequests/HandleRequests.php new file mode 100644 index 00000000..1559b8f9 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/HandleRequests/HandleRequests.php @@ -0,0 +1,101 @@ +singleton($this::class); + } + + function boot() + { + app($this::class)->setUpdateRoute(function ($handle) { + return Route::post('/livewire/update', $handle)->middleware('web'); + }); + + $this->skipRequestPayloadTamperingMiddleware(); + } + + function getUpdateUri() + { + return (string) str($this->updateRoute->uri)->start('/'); + } + + function skipRequestPayloadTamperingMiddleware() + { + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::skipWhen(function () { + return $this->isLivewireRequest(); + }); + + \Illuminate\Foundation\Http\Middleware\TrimStrings::skipWhen(function () { + return $this->isLivewireRequest(); + }); + } + + function setUpdateRoute($callback) + { + $route = $callback([self::class, 'handleUpdate']); + + // Append `livewire.update` to the existing name, if any. + $route->name('livewire.update'); + + $this->updateRoute = $route; + } + + function isLivewireRequest() + { + return request()->hasHeader('X-Livewire'); + } + + function isLivewireRoute() + { + // @todo: Rename this back to `isLivewireRequest` once the need for it in tests has been fixed. + $route = request()->route(); + + if (! $route) return false; + + /* + * Check to see if route name ends with `livewire.update`, as if + * a custom update route is used and they add a name, then when + * we call `->name('livewire.update')` on the route it will + * suffix the existing name with `livewire.update`. + */ + return $route->named('*livewire.update'); + } + + function handleUpdate() + { + $components = request('components'); + + $responses = []; + + foreach ($components as $component) { + $snapshot = json_decode($component['snapshot'], associative: true); + $updates = $component['updates']; + $calls = $component['calls']; + + [ $snapshot, $effects ] = app('livewire')->update($snapshot, $updates, $calls); + + $responses[] = [ + 'snapshot' => json_encode($snapshot), + 'effects' => $effects, + ]; + } + + $response = [ + 'components' => $responses, + ]; + + $finish = trigger('profile.response', $response); + + return $finish($response); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/PersistentMiddleware/PersistentMiddleware.php b/vendor/livewire/livewire/src/Mechanisms/PersistentMiddleware/PersistentMiddleware.php new file mode 100644 index 00000000..7b2428ea --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/PersistentMiddleware/PersistentMiddleware.php @@ -0,0 +1,178 @@ +singleton($this::class, fn () => $this); + } + + function boot() + { + on('dehydrate', function ($component, $context) { + [$path, $method] = $this->extractPathAndMethodFromRequest(); + + $context->addMemo('path', $path); + $context->addMemo('method', $method); + }); + + on('snapshot-verified', function ($snapshot) { + // Only apply middleware to requests hitting the Livewire update endpoint, and not any fake requests such as a test. + if (! app(HandleRequests::class)->isLivewireRoute()) return; + + $this->extractPathAndMethodFromSnapshot($snapshot); + + $this->applyPersistentMiddleware(); + }); + + on('flush-state', function() { + // Only flush these at the end of a full request, so that child components have access to this data. + $this->path = null; + $this->method = null; + }); + } + + function addPersistentMiddleware($middleware) + { + static::$persistentMiddleware = array_merge(static::$persistentMiddleware, (array) $middleware); + } + + function setPersistentMiddleware($middleware) + { + static::$persistentMiddleware = (array) $middleware; + } + + function getPersistentMiddleware() + { + return static::$persistentMiddleware; + } + + protected function extractPathAndMethodFromRequest() + { + if (app(HandleRequests::class)->isLivewireRoute()) { + return [$this->path, $this->method]; + } + + return [request()->path(), request()->method()]; + } + + protected function extractPathAndMethodFromSnapshot($snapshot) + { + if ( + ! isset($snapshot['memo']['path']) + || ! isset($snapshot['memo']['method']) + ) return; + + // Store these locally, so dynamically added child components can use this data. + $this->path = $snapshot['memo']['path']; + $this->method = $snapshot['memo']['method']; + } + + protected function applyPersistentMiddleware() + { + $request = $this->makeFakeRequest(); + + $middleware = $this->getApplicablePersistentMiddleware($request); + + // Only send through pipeline if there are middleware found + if (is_null($middleware)) return; + + Utils::applyMiddleware($request, $middleware); + } + + protected function makeFakeRequest() + { + $originalPath = $this->formatPath($this->path); + $originalMethod = $this->method; + + $currentPath = $this->formatPath(request()->path()); + + // Clone server bag to ensure changes below don't overwrite the original. + $serverBag = clone request()->server; + + // Replace the Livewire endpoint path with the path from the original request. + $serverBag->set( + 'REQUEST_URI', + str_replace($currentPath, $originalPath, $serverBag->get('REQUEST_URI')) + ); + + $serverBag->set('REQUEST_METHOD', $originalMethod); + + /** + * Make the fake request from the current request with path and method changed so + * all other request data, such as headers, are available in the fake request, + * but merge in the new server bag with the updated `REQUEST_URI`. + */ + $request = request()->duplicate( + server: $serverBag->all() + ); + + return $request; + } + + protected function formatPath($path) + { + return '/' . ltrim($path, '/'); + } + + protected function getApplicablePersistentMiddleware($request) + { + $route = $this->getRouteFromRequest($request); + + if (! $route) return []; + + $middleware = app('router')->gatherRouteMiddleware($route); + + return $this->filterMiddlewareByPersistentMiddleware($middleware); + } + + protected function getRouteFromRequest($request) + { + $route = app('router')->getRoutes()->match($request); + + $request->setRouteResolver(fn() => $route); + + return $route; + } + + protected function filterMiddlewareByPersistentMiddleware($middleware) + { + $middleware = collect($middleware); + + $persistentMiddleware = collect(app(PersistentMiddleware::class)->getPersistentMiddleware()); + + return $middleware + ->filter(function ($value, $key) use ($persistentMiddleware) { + return $persistentMiddleware->contains(function($iValue, $iKey) use ($value) { + // Some middlewares can be closures. + if (! is_string($value)) return false; + + // Ensure any middleware arguments aren't included in the comparison + return Str::before($value, ':') == $iValue; + }); + }) + ->values() + ->all(); + } +} diff --git a/vendor/livewire/livewire/src/Mechanisms/RenderComponent.php b/vendor/livewire/livewire/src/Mechanisms/RenderComponent.php new file mode 100644 index 00000000..c1c4dd29 --- /dev/null +++ b/vendor/livewire/livewire/src/Mechanisms/RenderComponent.php @@ -0,0 +1,61 @@ +singleton($this::class); + } + + function boot() + { + Blade::directive('livewire', [static::class, 'livewire']); + } + + public static function livewire($expression) + { + $key = "'" . Str::random(7) . "'"; + + $pattern = "/,\s*?key\(([\s\S]*)\)/"; //everything between ",key(" and ")" + + $expression = preg_replace_callback($pattern, function ($match) use (&$key) { + $key = trim($match[1]) ?: $key; + return ""; + }, $expression); + + // If we are inside a Livewire component, we know we're rendering a child. + // Therefore, we must create a more deterministic view cache key so that + // Livewire children are properly tracked across load balancers. + // if (LivewireManager::$currentCompilingViewPath !== null) { + // // $key = '[hash of Blade view path]-[current @livewire directive count]' + // $key = "'l" . crc32(LivewireManager::$currentCompilingViewPath) . "-" . LivewireManager::$currentCompilingChildCounter . "'"; + + // // We'll increment count, so each cache key inside a compiled view is unique. + // LivewireManager::$currentCompilingChildCounter++; + // } + + return <<mount(\$__name, \$__params, $key, \$__slots ?? [], get_defined_vars()); + +echo \$__html; + +unset(\$__html); +unset(\$__name); +unset(\$__params); +unset(\$__split); +if (isset(\$__slots)) unset(\$__slots); +?> +EOT; + } +} diff --git a/vendor/livewire/livewire/src/Pipe.php b/vendor/livewire/livewire/src/Pipe.php new file mode 100644 index 00000000..4164962a --- /dev/null +++ b/vendor/livewire/livewire/src/Pipe.php @@ -0,0 +1,36 @@ +target = $target; + } + + function __invoke(...$params) { + if (empty($params)) return $this->target; + + [ $before, $through, $after ] = [ [], null, [] ]; + + foreach ($params as $key => $param) { + if (! $through) { + if (is_callable($param)) $through = $param; + + else $before[$key] = $param; + } else { + $after[$key] = $param; + } + } + + $params = [ ...$before, $this->target, ...$after ]; + + $this->target = $through(...$params); + + return $this; + } +} + diff --git a/vendor/livewire/livewire/src/Transparency.php b/vendor/livewire/livewire/src/Transparency.php new file mode 100644 index 00000000..ddb77765 --- /dev/null +++ b/vendor/livewire/livewire/src/Transparency.php @@ -0,0 +1,44 @@ +target; + } + + function offsetExists(mixed $offset): bool + { + return isset($this->target[$offset]); + } + + function offsetGet(mixed $offset): mixed + { + return $this->target[$offset]; + } + + function offsetSet(mixed $offset, mixed $value): void + { + $this->target[$offset] = $value; + } + + function offsetUnset(mixed $offset): void + { + unset($this->target[$offset]); + } + + function getIterator(): Traversable + { + return (function () { + foreach ($this->target as $key => $value) { + yield $key => $value; + } + })(); + } +} diff --git a/vendor/livewire/livewire/src/WireDirective.php b/vendor/livewire/livewire/src/WireDirective.php new file mode 100644 index 00000000..80e5918a --- /dev/null +++ b/vendor/livewire/livewire/src/WireDirective.php @@ -0,0 +1,59 @@ +name; + } + + public function directive() + { + return $this->directive; + } + + public function value() + { + return $this->value; + } + + public function modifiers() + { + return str($this->directive) + ->replace("wire:{$this->name}", '') + ->explode('.') + ->filter()->values(); + } + + public function hasModifier($modifier) + { + return $this->modifiers()->contains($modifier); + } + + public function toHtml() + { + return (new ComponentAttributeBag([$this->directive => $this->value]))->toHtml(); + } + + public function toString() + { + return (string) $this; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/vendor/livewire/livewire/src/Wireable.php b/vendor/livewire/livewire/src/Wireable.php new file mode 100644 index 00000000..ad4d21d4 --- /dev/null +++ b/vendor/livewire/livewire/src/Wireable.php @@ -0,0 +1,10 @@ +fallback = $fallback; + + return $this; + } + + function __call($method, $params) + { + if (! method_exists($this->target, $method)) return value($this->fallback); + + try { + return ImplicitlyBoundMethod::call(app(), [$this->target, $method], $params); + } catch (\Throwable $e) { + $shouldPropagate = true; + + $stopPropagation = function () use (&$shouldPropagate) { + $shouldPropagate = false; + }; + + trigger('exception', $this->target, $e, $stopPropagation); + + $shouldPropagate && throw $e; + } + } +} + + + + diff --git a/vendor/livewire/livewire/src/helpers.php b/vendor/livewire/livewire/src/helpers.php new file mode 100644 index 00000000..e815f848 --- /dev/null +++ b/vendor/livewire/livewire/src/helpers.php @@ -0,0 +1,170 @@ +obj = $obj; + $this->reflected = new ReflectionClass($obj); + } + + public function &__get($name) + { + $getProperty = function &() use ($name) { + return $this->{$name}; + }; + + $getProperty = $getProperty->bindTo($this->obj, get_class($this->obj)); + + return $getProperty(); + } + + public function __set($name, $value) + { + $setProperty = function () use ($name, &$value) { + $this->{$name} = $value; + }; + + $setProperty = $setProperty->bindTo($this->obj, get_class($this->obj)); + + $setProperty(); + } + + public function __call($name, $params) + { + $method = $this->reflected->getMethod($name); + + $method->setAccessible(true); + + return $method->invoke($this->obj, ...$params); + } + }; +} + +function once($fn) +{ + $hasRun = false; + + return function (...$params) use ($fn, &$hasRun) { + if ($hasRun) return; + + $hasRun = true; + + return $fn(...$params); + }; +} + +function of(...$params) +{ + return $params; +} + +function revert(&$variable) +{ + $cache = $variable; + + return function () use (&$variable, $cache) { + $variable = $cache; + }; +} + +function wrap($subject) { + return new Wrapped($subject); +} + +function pipe($subject) { + return new Pipe($subject); +} + +function trigger($name, ...$params) { + return app(\Livewire\EventBus::class)->trigger($name, ...$params); +} + +function on($name, $callback) { + return app(\Livewire\EventBus::class)->on($name, $callback); +} + +function after($name, $callback) { + return app(\Livewire\EventBus::class)->after($name, $callback); +} + +function before($name, $callback) { + return app(\Livewire\EventBus::class)->before($name, $callback); +} + +function off($name, $callback) { + return app(\Livewire\EventBus::class)->off($name, $callback); +} + +function memoize($target) { + static $memo = new \WeakMap; + + return new class ($target, $memo) { + function __construct( + protected $target, + protected &$memo, + ) {} + + function __call($method, $params) + { + $this->memo[$this->target] ??= []; + + $signature = $method . crc32(json_encode($params)); + + return $this->memo[$this->target][$signature] + ??= $this->target->$method(...$params); + } + }; +} + +function store($instance = null) +{ + if (! $instance) $instance = app(\Livewire\Mechanisms\DataStore::class); + + return new class ($instance) { + function __construct(protected $instance) {} + + function get($key, $default = null) { + return app(\Livewire\Mechanisms\DataStore::class)->get($this->instance, $key, $default); + } + + function set($key, $value) { + return app(\Livewire\Mechanisms\DataStore::class)->set($this->instance, $key, $value); + } + + function push($key, $value, $iKey = null) + { + return app(\Livewire\Mechanisms\DataStore::class)->push($this->instance, $key, $value, $iKey); + } + + function find($key, $iKey = null, $default = null) + { + return app(\Livewire\Mechanisms\DataStore::class)->find($this->instance, $key, $iKey, $default); + } + + function has($key, $iKey = null) + { + return app(\Livewire\Mechanisms\DataStore::class)->has($this->instance, $key, $iKey); + } + }; +}