├── resources ├── views │ ├── .gitkeep │ ├── functional │ │ ├── slot.blade.php │ │ ├── html.blade.php │ │ ├── script.blade.php │ │ ├── content.blade.php │ │ ├── persistent-layout.blade.php │ │ ├── teleport.blade.php │ │ ├── outside.blade.php │ │ ├── preloaded-modal.blade.php │ │ ├── flash.blade.php │ │ ├── state.blade.php │ │ ├── errors.blade.php │ │ ├── toggle.blade.php │ │ ├── rehydrate.blade.php │ │ ├── event.blade.php │ │ ├── lazy.blade.php │ │ ├── link.blade.php │ │ ├── dynamicHtml.blade.php │ │ ├── data.blade.php │ │ ├── dataStores.blade.php │ │ ├── dialog.blade.php │ │ ├── defer.blade.php │ │ ├── form.blade.php │ │ ├── transition.blade.php │ │ ├── vue-bridge.blade.php │ │ └── form-builder.blade.php │ ├── form │ │ ├── help.blade.php │ │ ├── error.blade.php │ │ ├── label.blade.php │ │ ├── radios.blade.php │ │ ├── checkboxes.blade.php │ │ ├── select-child.blade.php │ │ ├── group.blade.php │ │ ├── radio.blade.php │ │ ├── textarea.blade.php │ │ ├── wysiwyg.blade.php │ │ ├── checkbox.blade.php │ │ └── submit.blade.php │ ├── table │ │ ├── wrapper.blade.php │ │ ├── per-page-selector.blade.php │ │ ├── add-search-row.blade.php │ │ ├── global-search.blade.php │ │ ├── select-rows-dropdown.blade.php │ │ ├── table.blade.php │ │ ├── filters.blade.php │ │ ├── simple-pagination.blade.php │ │ └── controls.blade.php │ ├── transitions │ │ └── classes.blade.php │ └── components │ │ ├── button-with-dropdown.blade.php │ │ ├── dropdown.blade.php │ │ ├── modal-wrapper.blade.php │ │ ├── toast-wrapper.blade.php │ │ └── button.blade.php └── lang │ ├── ja.json │ ├── ar.json │ ├── en.json │ ├── fa.json │ ├── zh_CN.json │ ├── uk_UA.json │ ├── it.json │ ├── pt_BR.json │ ├── pt_PT.json │ ├── fr.json │ ├── id.json │ ├── ru.json │ ├── bn.json │ ├── ms.json │ ├── ur.json │ ├── es.json │ ├── de.json │ ├── ne.json │ ├── fil.json │ └── nl.json ├── lib ├── jodit.js ├── renderSpladeApp.js ├── Components │ ├── Button.vue │ ├── Script.vue │ ├── Transition.vue │ ├── jodit.scss │ ├── Flash.vue │ ├── PreloadedModal.vue │ ├── Teleport.vue │ ├── Dialog.vue │ ├── Errors.vue │ ├── flatpickr.styl │ ├── Render.vue │ ├── DynamicHtml.vue │ ├── State.vue │ ├── filepond.scss │ ├── Toast.vue │ ├── Toasts.vue │ ├── FormHelpers.js │ ├── Textarea.vue │ ├── JoditEditor.vue │ ├── DataStores.vue │ ├── Lazy.vue │ ├── OnClickOutside.vue │ └── Data.vue ├── main.js ├── EventBus.js ├── PreserveScrollDirective.js ├── server.js └── ServerError.vue ├── .vscode ├── extensions.json └── settings.json ├── stubs ├── resources │ ├── css │ │ └── app.css │ ├── views │ │ ├── docs.blade.php │ │ ├── components │ │ │ ├── panel.blade.php │ │ │ ├── layout.blade.php │ │ │ ├── application-mark.blade.php │ │ │ ├── nav-link.blade.php │ │ │ └── responsive-nav-link.blade.php │ │ ├── home.blade.php │ │ └── root.blade.php │ └── js │ │ ├── ssr.js │ │ └── app.js ├── postcss.config.js ├── vite.config.js ├── tailwind.config.js └── routes │ └── web.php ├── src ├── FileUploads │ ├── HasSpladeFileUploads.php │ ├── SpladeUploadedFile.php │ ├── SpladeFile.php │ └── TemporaryFileUpload.php ├── PaginationException.php ├── FormBuilder │ ├── Text.php │ ├── Color.php │ ├── Email.php │ ├── Date.php │ ├── Hidden.php │ ├── Time.php │ ├── Password.php │ ├── Datetime.php │ ├── Concerns │ │ ├── HasValue.php │ │ ├── CanBeInline.php │ │ └── HasOptions.php │ ├── Wysiwyg.php │ ├── Submit.php │ ├── Radio.php │ ├── Checkbox.php │ ├── Textarea.php │ ├── Radios.php │ ├── Checkboxes.php │ ├── Number.php │ └── Button.php ├── InvalidTransformerException.php ├── Table │ ├── LaravelExcelException.php │ ├── PowerJoinsException.php │ ├── SearchInput.php │ ├── Export.php │ ├── HasBulkActions.php │ ├── HasExports.php │ └── Filter.php ├── Bridge │ ├── CouldNotResolveComponentInstance.php │ └── MiddlewareException.php ├── DataStore.php ├── Components │ ├── Cell.php │ ├── Slot.php │ ├── Script.php │ ├── Outside.php │ ├── Teleport.php │ ├── TableWrapper.php │ ├── PassesVueVariablesThrough.php │ ├── ToastWrapper.php │ ├── ButtonWithDropdown.php │ ├── Content.php │ ├── Flash.php │ ├── State.php │ ├── Errors.php │ ├── Toast.php │ ├── Confirm.php │ ├── Dynamic.php │ ├── ModalWrapper.php │ ├── DynamicHtml.php │ ├── Dropdown.php │ ├── Form │ │ ├── Group.php │ │ ├── Radio.php │ │ ├── Radios.php │ │ ├── Checkbox.php │ │ ├── Checkboxes.php │ │ ├── Textarea.php │ │ └── Submit.php │ ├── SpladeComponent.php │ ├── Dialog.php │ ├── Lazy.php │ ├── Event.php │ ├── DataStores.php │ ├── ParsesJsonDataAttribute.php │ ├── Rehydrate.php │ ├── Link.php │ ├── SerializesNewModels.php │ ├── Transition.php │ ├── Toggle.php │ ├── Button.php │ └── Data.php ├── Http │ ├── EventRedirectController.php │ ├── TableExportController.php │ ├── ResolvableData.php │ ├── ConfirmPasswordController.php │ ├── TableBulkActionController.php │ ├── FileUploadController.php │ └── SpladeResponseData.php ├── Facades │ ├── Animation.php │ ├── SEO.php │ └── Toast.php ├── Commands │ ├── stubs │ │ ├── splade.request.stub │ │ ├── splade.form.stub │ │ └── splade.table.stub │ ├── CleanupTemporaryFileUploads.php │ ├── SsrTestCommand.php │ └── PublishFormStylesheetsCommand.php ├── EloquentSerializer.php ├── SpladeToastBuilder.php ├── Ssr.php ├── EventRefresh.php ├── TransitionAnimation.php ├── FormSelectOption.php ├── TransitionRepository.php ├── Meta.php └── AbstractForm.php ├── dev-setup.sh ├── phpstan.neon ├── pint.json ├── ExtractTransitionClasses.php ├── .eslintrc.cjs ├── LICENSE.md ├── config └── splade-seo.php ├── TestStubs.php └── package.json /resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/jodit.js: -------------------------------------------------------------------------------- 1 | import "./Components/jodit.scss"; -------------------------------------------------------------------------------- /resources/views/functional/slot.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} -------------------------------------------------------------------------------- /resources/views/functional/html.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_spladeEvaluatedHtml }} -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /resources/views/functional/script.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stubs/resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /resources/views/form/help.blade.php: -------------------------------------------------------------------------------- 1 | @if($help)

{{ $help }}

@endif -------------------------------------------------------------------------------- /resources/views/functional/content.blade.php: -------------------------------------------------------------------------------- 1 | <{{ $as }} {{ $attributes->except('v-html') }} v-html="@js($html)" /> -------------------------------------------------------------------------------- /resources/views/functional/persistent-layout.blade.php: -------------------------------------------------------------------------------- 1 | @include($originalName, $viewData($originalData, $slot, $__env)) -------------------------------------------------------------------------------- /resources/views/functional/teleport.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | -------------------------------------------------------------------------------- /resources/views/functional/outside.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | -------------------------------------------------------------------------------- /stubs/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/FileUploads/HasSpladeFileUploads.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/PaginationException.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/views/functional/state.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/views/functional/errors.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/FormBuilder/Text.php: -------------------------------------------------------------------------------- 1 | @endif -------------------------------------------------------------------------------- /src/FormBuilder/Color.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/Bridge/CouldNotResolveComponentInstance.php: -------------------------------------------------------------------------------- 1 | mergeVueBinding(':on', json_encode($on))->mergeVueBinding(':passthrough', $passthroughValue()) }}> 2 | {{ $slot ?? "" }} 3 | -------------------------------------------------------------------------------- /resources/views/functional/event.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/FormBuilder/Datetime.php: -------------------------------------------------------------------------------- 1 | mergeVueBinding(':show', $show)->mergeVueBinding(':passthrough', $passthroughValue()) }} :url="@js(request()->fullUrl())"> 2 | {{ $placeholder ?? "" }} 3 | -------------------------------------------------------------------------------- /dev-setup.sh: -------------------------------------------------------------------------------- 1 | composer update 2 | npm install 3 | npm pack 4 | cd app 5 | composer update 6 | touch database/database.sqlite 7 | php artisan key:generate 8 | php artisan migrate:fresh --seed 9 | npm update @protonemedia/laravel-splade 10 | npm install 11 | cd .. -------------------------------------------------------------------------------- /resources/views/functional/link.blade.php: -------------------------------------------------------------------------------- 1 | 5 | {{ $slot }} 6 | -------------------------------------------------------------------------------- /resources/views/functional/dynamicHtml.blade.php: -------------------------------------------------------------------------------- 1 | mergeVueBinding(':passthrough', $passthroughValue()) }} 5 | /> -------------------------------------------------------------------------------- /lib/renderSpladeApp.js: -------------------------------------------------------------------------------- 1 | import { h } from "vue"; 2 | import SpladeApp from "./SpladeApp.vue"; 3 | 4 | /** 5 | * A little helper function to render the SpladeApp in the main app.js file. 6 | */ 7 | export default function render(props) { 8 | return () => { 9 | return h(SpladeApp, props); 10 | }; 11 | } -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/nunomaduro/larastan/extension.neon 3 | - ./phpstan-baseline.neon 4 | 5 | parameters: 6 | 7 | paths: 8 | - src/ 9 | excludePaths: 10 | - src/TableExporter.php 11 | 12 | level: 5 13 | 14 | checkGenericClassInNonGenericObjectType: false 15 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "binary_operator_spaces": { 5 | "default": "align_single_space_minimal" 6 | }, 7 | "concat_space": { 8 | "spacing": "one" 9 | }, 10 | "not_operator_with_successor_space": false 11 | } 12 | } -------------------------------------------------------------------------------- /resources/views/form/label.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {!! $label !!} 3 | @if($attributes->has('required') || $attributes->has('data-required')) 4 | 5 | @endif 6 | 7 | -------------------------------------------------------------------------------- /src/DataStore.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $label }} 3 | 4 | @foreach($options as $value => $label) 5 | 6 | @endforeach 7 | -------------------------------------------------------------------------------- /resources/views/functional/data.blade.php: -------------------------------------------------------------------------------- 1 | 3 | @unless($store) 4 | 7 | @endunless 8 | -------------------------------------------------------------------------------- /lib/Components/Button.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/table/wrapper.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | {{ $slot }} 6 |
7 |
8 |
9 |
-------------------------------------------------------------------------------- /resources/views/transitions/classes.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /stubs/resources/views/docs.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ __('Docs') }} 4 | 5 | 6 | 7 |

Hi developer!

8 |

You'll find the docs at splade.dev/docs

9 |
10 |
-------------------------------------------------------------------------------- /stubs/resources/views/components/panel.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
class(['p-6 sm:px-20 bg-white border-b border-gray-200' => true]) }}> 5 | {{ $slot }} 6 |
7 |
8 |
9 |
-------------------------------------------------------------------------------- /stubs/resources/views/home.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ __('Home') }} 4 | 5 | 6 | 7 | 8 | 9 |
10 | Welcome to your Splade application! 11 |
12 |
13 |
-------------------------------------------------------------------------------- /src/Components/Cell.php: -------------------------------------------------------------------------------- 1 | value = $value; 17 | 18 | return $this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Components/Slot.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @spladeHead 8 | @vite('resources/js/app.js') 9 | 10 | 11 | @splade 12 | 13 | 14 | -------------------------------------------------------------------------------- /stubs/resources/js/ssr.js: -------------------------------------------------------------------------------- 1 | import { createServer } from "http"; 2 | import { createSSRApp } from "vue"; 3 | import { renderToString } from "@vue/server-renderer"; 4 | import { renderSpladeApp, SpladePlugin, startServer } from "@protonemedia/laravel-splade"; 5 | 6 | startServer(createServer, renderToString, (props) => { 7 | return createSSRApp({ 8 | render: renderSpladeApp(props) 9 | }) 10 | .use(SpladePlugin); 11 | }); -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | import "./Components/choices.scss"; 2 | import "./Components/filepond.scss"; 3 | import "./Components/flatpickr.styl"; 4 | 5 | import { Splade } from "./Splade.js"; 6 | import renderSpladeApp from "./renderSpladeApp.js"; 7 | import SpladePlugin from "./SpladePlugin.js"; 8 | import SpladeApp from "./SpladeApp.vue"; 9 | import { startServer } from "./server.js"; 10 | 11 | export { Splade, SpladeApp, renderSpladeApp, SpladePlugin, startServer }; -------------------------------------------------------------------------------- /src/Components/Script.php: -------------------------------------------------------------------------------- 1 | inline = $inline; 17 | 18 | return $this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/Components/Script.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/Outside.php: -------------------------------------------------------------------------------- 1 | $rawJson) 7 | @js($store): {!! $rawJson !!}@if(!$loop->last),@endif 8 | @endforeach 9 | }" 10 | > 11 | 14 | -------------------------------------------------------------------------------- /src/Components/TableWrapper.php: -------------------------------------------------------------------------------- 1 | 2 | import { TransitionRoot, TransitionChild } from "@headlessui/vue"; 3 | 4 | export default { 5 | render() { 6 | /* 7 | * This is just a wrapper around the Headless UI components 8 | * so it's easy to use them in Blade templates. 9 | */ 10 | return this.$slots.default({ 11 | TransitionRoot, 12 | TransitionChild, 13 | }); 14 | }, 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /src/Components/PassesVueVariablesThrough.php: -------------------------------------------------------------------------------- 1 | passthrough, 12 | '##SPLADE-PASSTHROUGH-', 13 | $this->passthrough ? 'APPEND' : 'NEW', 14 | '##', 15 | '}', 16 | ]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Components/ToastWrapper.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $label }} 3 | 4 | @foreach($options as $value => $label) 5 | 11 | @endforeach 12 | -------------------------------------------------------------------------------- /resources/views/table/per-page-selector.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/FormBuilder/Concerns/HasOptions.php: -------------------------------------------------------------------------------- 1 | 'Label 1', 'value2' => 'Label 2'] 13 | * @return $this 14 | */ 15 | public function options(array $options = []): self 16 | { 17 | $this->options = $options; 18 | 19 | return $this; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/EventRedirectController.php: -------------------------------------------------------------------------------- 1 | with($data['with'] ?? []); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Components/jodit.scss: -------------------------------------------------------------------------------- 1 | @import "jodit/build/jodit.css"; 2 | 3 | .jodit-container:not(.jodit_inline) { 4 | @apply shadow-sm rounded-md border-gray-300; 5 | } 6 | 7 | .jodit-toolbar__box:not(:empty) { 8 | @apply rounded-t-md; 9 | } 10 | 11 | .jodit-status-bar, 12 | .jodit-container:not(.jodit_inline) .jodit-workplace { 13 | @apply rounded-b-md; 14 | } 15 | 16 | .jodit-toolbar__box:not(:empty):not(:empty) { 17 | @apply bg-gray-50; 18 | } 19 | 20 | .jodit-container:not(.jodit_inline) .jodit-wysiwyg { 21 | @apply px-3 py-2; 22 | } -------------------------------------------------------------------------------- /resources/views/form/select-child.blade.php: -------------------------------------------------------------------------------- 1 | @php $attributes = $selectChild->attributes() @endphp 2 | 3 | @if($selectChild->isGroup()) 4 | 5 | @foreach($selectChild->getOptions() as $nestedSelectChild) 6 | @include('splade::form.select-child', ['selectChild' => $nestedSelectChild]) 7 | @endforeach 8 | 9 | @else 10 | 13 | @endif -------------------------------------------------------------------------------- /resources/views/functional/dialog.blade.php: -------------------------------------------------------------------------------- 1 | 2 | mergeVueBinding(':is', $panel ? 'DialogPanel' : 'Dialog') 5 | ->mergeVueBinding(':open', $open ? ('(' . e($open) . ') && isActivated' . $spladeId) : '', escape: false) 6 | ->mergeVueBinding(':unmount', $unmount) 7 | ->mergeVueBinding('@close', $close) 8 | }} 9 | > 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /stubs/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import "./bootstrap"; 2 | import "../css/app.css"; 3 | import "@protonemedia/laravel-splade/dist/style.css"; 4 | 5 | import { createApp } from "vue/dist/vue.esm-bundler.js"; 6 | import { renderSpladeApp, SpladePlugin } from "@protonemedia/laravel-splade"; 7 | 8 | const el = document.getElementById("app"); 9 | 10 | createApp({ 11 | render: renderSpladeApp({ el }) 12 | }) 13 | .use(SpladePlugin, { 14 | "max_keep_alive": 10, 15 | "transform_anchors": false, 16 | "progress_bar": true 17 | }) 18 | .mount(el); 19 | -------------------------------------------------------------------------------- /lib/Components/Flash.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/ButtonWithDropdown.php: -------------------------------------------------------------------------------- 1 | SpladeComponent::normalize('dropdown'), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/FormBuilder/Wysiwyg.php: -------------------------------------------------------------------------------- 1 | name, 18 | label: $this->label, 19 | help: $this->help 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /resources/views/components/button-with-dropdown.blade.php: -------------------------------------------------------------------------------- 1 | class('w-full bg-white border border-gray-300 rounded-md shadow-sm px-2.5 sm:px-4 py-2 inline-flex justify-center text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500') }}> 2 | 3 | {{ $button }} 4 | 5 | 6 |
7 | {{ $slot }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /stubs/resources/views/components/layout.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | @if(isset($header)) 6 |
7 |
8 |

9 | {{ $header }} 10 |

11 |
12 |
13 | @endif 14 | 15 | 16 |
17 | {{ $slot }} 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/Bridge/MiddlewareException.php: -------------------------------------------------------------------------------- 1 | response; 20 | } 21 | 22 | public static function fromResponse($response) 23 | { 24 | return tap(new static, fn (self $e) => $e->response = $response); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /stubs/resources/views/components/application-mark.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /stubs/resources/views/components/nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition' 6 | : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /ExtractTransitionClasses.php: -------------------------------------------------------------------------------- 1 | classes()); 13 | 14 | file_put_contents( 15 | __DIR__ . '/resources/views/transitions/classes.blade.php', 16 | "
\n\n
", 17 | ); 18 | -------------------------------------------------------------------------------- /src/Components/Content.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function rules() 26 | { 27 | return {{ form }}::rules(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/FormBuilder/Submit.php: -------------------------------------------------------------------------------- 1 | spinner = $enabled; 19 | 20 | return $this; 21 | } 22 | 23 | /** 24 | * Create a new form field 25 | */ 26 | public static function make(string $name = ''): static 27 | { 28 | return new static($name); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stubs/resources/views/components/responsive-nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'block pl-3 pr-4 py-2 border-l-4 border-indigo-400 text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition' 6 | : 'block pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /resources/views/functional/defer.blade.php: -------------------------------------------------------------------------------- 1 | mergeVueBinding(':manual', $manual) 3 | ->mergeVueBinding(':poll', $poll) 4 | ->mergeVueBinding(':watch-debounce', $watchDebounce) 5 | ->mergeVueBinding(':watch-value', $watchValue) 6 | ->mergeVueBinding(':url', $url) 7 | }} 8 | @if($data) :default="@js($data)" @else :default="{!! $json !!}" @endif 9 | @if($headers) :headers="@js($headers)" @else :headers="{!! $jsonHeaders !!}" @endif 10 | @if($requestData) :request="@js($requestData)" @else :request="{!! $requestJson !!}" @endif> 11 | 14 | -------------------------------------------------------------------------------- /src/FormBuilder/Radio.php: -------------------------------------------------------------------------------- 1 | name, 21 | value: $this->value ?? 1, 22 | label: $this->label, 23 | help: $this->help 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /stubs/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import laravel from "laravel-vite-plugin"; 3 | import vue from "@vitejs/plugin-vue"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: "resources/js/app.js", 9 | ssr: "resources/js/ssr.js", 10 | refresh: true, 11 | }), 12 | vue({ 13 | template: { 14 | transformAssetUrls: { 15 | base: null, 16 | includeAbsolute: false, 17 | }, 18 | }, 19 | }), 20 | ], 21 | ssr: { 22 | noExternal: ["vue", "@protonemedia/laravel-splade"] 23 | }, 24 | }); -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "eslint:recommended", 4 | "plugin:vue/vue3-recommended", 5 | "plugin:vue/vue3-essential", 6 | "plugin:vue/vue3-strongly-recommended", 7 | ], 8 | rules: { 9 | "no-undef": 0, 10 | "vue/multi-word-component-names": 0, 11 | "vue/no-v-html": 0, 12 | "vue/require-default-prop": 0, 13 | "vue/no-setup-props-destructure": 0, 14 | indent: ["error", 4], 15 | quotes: ["error", "double"], 16 | "object-curly-spacing": ["error", "always"], 17 | semi: ["error", "always"], 18 | "comma-spacing": ["error", { before: false, after: true }], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/Components/PreloadedModal.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/Toast.php: -------------------------------------------------------------------------------- 1 | toast->toArray()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/Components/Teleport.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | -------------------------------------------------------------------------------- /src/FormBuilder/Checkbox.php: -------------------------------------------------------------------------------- 1 | name, 21 | value: $this->value ?? true, 22 | label: $this->label, 23 | help: $this->help, 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /resources/lang/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "続行しますか?", 3 | "Browse": "ファイルを選択", 4 | "Cancel": "キャンセル", 5 | "Clear selection": "選択箇所を解除", 6 | "Confirm": "確認", 7 | "Drag and drop your files or Browse": "ファイルをドラッグ&ドロップするか選択", 8 | "Excel Export": "Excelにエクスポート", 9 | "Export results": "エクスポート結果", 10 | "Go to page :page": ":pageページに移動", 11 | "Item selected": "選択されたアイテム", 12 | "Items selected": "選択されたアイテム", 13 | "Pagination Navigation": "ナビゲーションのページ送り", 14 | "Search": "検索", 15 | "Select all on this page": "このページの全てを選択", 16 | "Select all results": "全ての結果を選択", 17 | "of": "件目、全:", 18 | "per page": "毎ページ", 19 | "results": "件", 20 | "to": "から" 21 | } 22 | -------------------------------------------------------------------------------- /src/Commands/CleanupTemporaryFileUploads.php: -------------------------------------------------------------------------------- 1 | info('Cleaning up temporary file uploads...'); 20 | 21 | $filesystem->deleteTemporaryFiles(); 22 | 23 | $this->info('Done!'); 24 | 25 | return static::SUCCESS; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Components/Dialog.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /src/FormBuilder/Textarea.php: -------------------------------------------------------------------------------- 1 | attributes['autosize'] = $autosize; 12 | 13 | return $this; 14 | } 15 | 16 | /** 17 | * Renders the SpladeTextarea 18 | * 19 | * @return \Closure|\Illuminate\Contracts\View\View|string 20 | */ 21 | public function toSpladecomponent() 22 | { 23 | return new SpladeTextarea( 24 | name: $this->name, 25 | label: $this->label, 26 | help: $this->help 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Http/TableExportController.php: -------------------------------------------------------------------------------- 1 | authorize($request)) { 20 | throw new UnauthorizedException; 21 | } 22 | 23 | return $tableInstance->makeExporter($export); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Components/Errors.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/Confirm.php: -------------------------------------------------------------------------------- 1 | Route::has('splade.confirmedPasswordStatus') ? route('splade.confirmedPasswordStatus') : '', 19 | 20 | 'confirmPasswordRoute' => Route::has('splade.confirmPassword') ? route('splade.confirmPassword') : '', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stubs/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import forms from "@tailwindcss/forms"; 2 | import typography from "@tailwindcss/typography"; 3 | 4 | /* @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", 8 | "./vendor/protonemedia/laravel-splade/lib/**/*.vue", 9 | "./vendor/protonemedia/laravel-splade/resources/views/**/*.blade.php", 10 | "./storage/framework/views/*.php", 11 | "./resources/views/**/*.blade.php", 12 | "./resources/js/**/*.vue", 13 | // "./app/Forms/*.php", 14 | // "./app/Tables/*.php", 15 | ], 16 | 17 | theme: { 18 | extend: {}, 19 | }, 20 | 21 | plugins: [forms, typography], 22 | }; 23 | -------------------------------------------------------------------------------- /lib/EventBus.js: -------------------------------------------------------------------------------- 1 | class EventBus { 2 | constructor(id) { 3 | this.id = id; 4 | this.events = {}; 5 | } 6 | 7 | on(event, callback) { 8 | if (!this.events[event]) { 9 | this.events[event] = []; 10 | } 11 | 12 | this.events[event].push(callback); 13 | } 14 | 15 | off(event, callback) { 16 | if (!this.events[event]) { 17 | return; 18 | } 19 | 20 | this.events[event] = this.events[event].filter(cb => cb !== callback); 21 | } 22 | 23 | emit(event, data) { 24 | if (!this.events[event]) { 25 | return; 26 | } 27 | 28 | this.events[event].forEach((callback) => { 29 | callback(data); 30 | }); 31 | } 32 | } 33 | 34 | export default EventBus; -------------------------------------------------------------------------------- /src/EloquentSerializer.php: -------------------------------------------------------------------------------- 1 | getSerializedPropertyValue($value); 20 | } 21 | 22 | /** 23 | * Get the restored value after deserialization. 24 | * 25 | * @param mixed $value 26 | * @return mixed 27 | */ 28 | public function restore($value) 29 | { 30 | return $this->getRestoredPropertyValue($value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/SpladeToastBuilder.php: -------------------------------------------------------------------------------- 1 | forwardCallTo( 28 | $this->splade->toast(), 29 | $method, 30 | $parameters 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Components/Dynamic.php: -------------------------------------------------------------------------------- 1 | name . '-->', 27 | '{{ $slot }}', 28 | '', 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/lang/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "هل أنت متأكد من المواصلة؟", 3 | "Browse": "تصفح", 4 | "Cancel": "إلغاء", 5 | "Clear selection": "حذف العناصر المحددة", 6 | "Confirm": "تأكيد", 7 | "Drag and drop your files or Browse": "اسحب الملفات وأسقطها أو تصفح", 8 | "Excel Export": "تصدير إكسيل", 9 | "Export results": "تصدير النتائج", 10 | "Go to page :page": "اذهب إلى الصفحة :page", 11 | "Item selected": "العنصر المحدد", 12 | "Items selected": "العناصر المحددة", 13 | "Pagination Navigation": "التنقل بين الصفحات", 14 | "Search": "بحث", 15 | "Select all on this page": "تحديد الكل في هاته الصفحة", 16 | "Select all results": "تحديد كل النتائج", 17 | "of": "من", 18 | "per page": "لكل صفحة", 19 | "results": "النتائج", 20 | "to": "إلى" 21 | } 22 | -------------------------------------------------------------------------------- /src/Components/ModalWrapper.php: -------------------------------------------------------------------------------- 1 | name, 23 | options: $this->options, 24 | label: $this->label, 25 | inline: $this->inline, 26 | help: $this->help 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/lang/fa.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "آیا مطمئن هستید که میخواهید ادامه دهید؟", 3 | "Browse": "مرور کردن", 4 | "Cancel": "لغو", 5 | "Clear selection": "پاک کردن انتخاب", 6 | "Confirm": "تأیید", 7 | "Drag and drop your files or Browse": "فایل های خود را بکشید و رها کنید یا مرور کنید", 8 | "Excel Export": "خروجی به اکسل", 9 | "Export results": "خروجی نتایج", 10 | "Go to page :page": "رفتن به صفحه :page", 11 | "Item selected": "مورد انتخاب شده", 12 | "Items selected": "موارد انتخاب شده", 13 | "Pagination Navigation": "پیمایش صفحه", 14 | "Search": "جستجو کردن", 15 | "Select all on this page": "انتخاب همه در این صفحه", 16 | "Select all results": "انتخاب همه نتایج", 17 | "of": "از", 18 | "per page": "هر صفحه", 19 | "results": "نتایج", 20 | "to": "به" 21 | } 22 | -------------------------------------------------------------------------------- /resources/lang/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "确定要继续吗?", 3 | "Browse": "浏览", 4 | "Cancel": "取消", 5 | "Clear selection": "清除选择项", 6 | "Confirm": "确认", 7 | "Drag and drop your files or Browse": "拖拽或浏览选择文件", 8 | "Excel Export": "导出 Excel", 9 | "Export results": "导出结果", 10 | "Go to page :page": "前往第 :page 页", 11 | "Item selected": "当前所选项目", 12 | "Items selected": "已选择项目", 13 | "No user is logged in": "没有用户登录", 14 | "Pagination Navigation": "分页导航", 15 | "Please confirm your password before continuing": "请在继续操作之前确认您的密码", 16 | "Remove search": "取消搜索", 17 | "Reset": "重置", 18 | "Search": "搜索", 19 | "Select all on this page": "选择当前页", 20 | "Select all results": "选择全部", 21 | "of": "的", 22 | "per page": "每页", 23 | "results": "个结果", 24 | "to": "到" 25 | } 26 | -------------------------------------------------------------------------------- /src/Components/DynamicHtml.php: -------------------------------------------------------------------------------- 1 | passthrough = implode(',', Form::splitByComma($passthrough)); 21 | } 22 | 23 | /** 24 | * Get the view / contents that represent the component. 25 | * 26 | * @return \Illuminate\Contracts\View\View|\Closure|string 27 | */ 28 | public function render() 29 | { 30 | return view('splade::functional.dynamicHtml'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/FormBuilder/Checkboxes.php: -------------------------------------------------------------------------------- 1 | name, 23 | options: $this->options, 24 | label: $this->label, 25 | inline: $this->inline, 26 | help: $this->help 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Commands/stubs/splade.form.stub: -------------------------------------------------------------------------------- 1 | action(route('...')) 16 | ->method('POST') 17 | ->class('space-y-4') 18 | ->fill([ 19 | // 20 | ]); 21 | } 22 | 23 | public function fields(): array 24 | { 25 | return [ 26 | Text::make('...') 27 | ->label(__('...')), 28 | 29 | // 30 | 31 | Submit::make() 32 | ->label(__('Save')), 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Components/Dropdown.php: -------------------------------------------------------------------------------- 1 | spladeId = Str::random(); 23 | } 24 | 25 | /** 26 | * Get the view / contents that represent the component. 27 | * 28 | * @return \Illuminate\Contracts\View\View|\Closure|string 29 | */ 30 | public function render() 31 | { 32 | return view('splade::components.dropdown'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /resources/lang/uk_UA.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Ви впевнені, що бажаєте продовжити?", 3 | "Browse": "Огляд", 4 | "Cancel": "Скасувати", 5 | "Clear selection": "Очистити вибір", 6 | "Confirm": "Підтвердити", 7 | "Drag and drop your files or Browse": "Перетягніть файли або перегляньте їх", 8 | "Excel Export": "Експорт Excel", 9 | "Export results": "Експорт результатів", 10 | "Go to page :page": "Перейти на сторінку :page", 11 | "Item selected": "Елемент обрано", 12 | "Items selected": "Вибрані елементи", 13 | "Pagination Navigation": "Навігація по сторінках", 14 | "Search": "Пошук", 15 | "Select all on this page": "Вибрати все на цій сторінці", 16 | "Select all results": "Вибрати всі результати", 17 | "of": "з", 18 | "per page": "на сторінку", 19 | "results": "результати", 20 | "to": "до" 21 | } 22 | -------------------------------------------------------------------------------- /lib/Components/flatpickr.styl: -------------------------------------------------------------------------------- 1 | $calendarBorderColor ?= #d1d5db // gray-300 2 | $dayHoverBackground ?= #f3f4f6 // gray-100 3 | $monthForeground ?= #111827 // gray-900 4 | $weekdaysForeground ?= #d1d5db // gray-300 5 | $dayForeground ?= #374151 // gray-700 6 | $arrow_hover_color ?= #6366f1 // indigo-500 7 | $selectedDayBackground ?= #6366f1 // indigo-500 8 | 9 | @import "flatpickr/src/style/flatpickr.styl" 10 | 11 | .flatpickr-calendar 12 | border-radius 0.375rem // .rounded-md 13 | margin-top 1px // .mt-px 14 | 15 | &:before, &:after 16 | display none // .hidden 17 | 18 | .flatpickr-wrapper 19 | width 100% // .w-full 20 | 21 | .flatpickr-current-month 22 | .flatpickr-monthDropdown-months 23 | input.cur-year 24 | font-size 1rem // .font-base 25 | 26 | .flatpickr-time 27 | input 28 | &:focus 29 | box-shadow none // .shadow-none -------------------------------------------------------------------------------- /resources/lang/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Sei sicuro di voler continuare?", 3 | "Browse": "Sfoglia", 4 | "Cancel": "Annulla", 5 | "Clear selection": "Elimina selezione", 6 | "Confirm": "Conferma", 7 | "Drag and drop your files or Browse": "Trascina qua i tuoi file oppure Sfoglia", 8 | "Excel Export": "Esporta Excel", 9 | "Export results": "Esporta risultati", 10 | "Go to page :page": "Vai alla pagina :page", 11 | "Item selected": "Elemento selezionato", 12 | "Items selected": "Elementi selezionati", 13 | "Pagination Navigation": "Naviga per pagine", 14 | "Search": "Cerca", 15 | "Select all on this page": "Seleziona tutto su questa pagina", 16 | "Select all results": "Seleziona tutti i risultati", 17 | "of": "di", 18 | "per page": "per pagina", 19 | "results": "risultati", 20 | "to": "a" 21 | } 22 | -------------------------------------------------------------------------------- /src/FileUploads/SpladeUploadedFile.php: -------------------------------------------------------------------------------- 1 | exists($this->temporaryFileUpload); 20 | } 21 | 22 | /** 23 | * Sets the temporary file upload. 24 | */ 25 | public function setTemporaryFileUpload(TemporaryFileUpload $temporaryFileUpload): self 26 | { 27 | $this->temporaryFileUpload = $temporaryFileUpload; 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/lang/pt_BR.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Você tem certeza que quer continuar?", 3 | "Browse": "Procurar", 4 | "Cancel": "Cancelar", 5 | "Clear selection": "Limpar seleção", 6 | "Confirm": "Confirmar", 7 | "Drag and drop your files or Browse": "Arraste e solte seus arquivos ou Procure", 8 | "Excel Export": "Exportar para Excel", 9 | "Export results": "Exportar resultados", 10 | "Go to page :page": "Ir para página :page:", 11 | "Item selected": "Item selecionado", 12 | "Items selected": "Itens selecionados", 13 | "Pagination Navigation": "Navegação de Páginas", 14 | "Search": "Pesquisar", 15 | "Select all on this page": "Selecionar todos nessa página", 16 | "Select all results": "Selecionar todos os resultados", 17 | "of": "de", 18 | "per page": "por página", 19 | "results": "resultados", 20 | "to": "até" 21 | } 22 | -------------------------------------------------------------------------------- /lib/Components/Render.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /resources/lang/pt_PT.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Tem a certeza que quer continuar?", 3 | "Browse": "Procurar", 4 | "Cancel": "Cancelar", 5 | "Clear selection": "Cancelar escolhas", 6 | "Confirm": "Confirmar", 7 | "Drag and drop your files or Browse": "Arraste e solte seus ficheiros ou Procurar", 8 | "Excel Export": "Exportar para Excel", 9 | "Export results": "Exportar resultados", 10 | "Go to page :page": "Ir para a página :page:", 11 | "Item selected": "Item selecionado", 12 | "Items selected": "Itens selecionados", 13 | "Pagination Navigation": "Navegação de Páginas", 14 | "Search": "Pesquisar", 15 | "Select all on this page": "Selecionar todos nesta página", 16 | "Select all results": "Selecionar todos os resultados", 17 | "of": "de", 18 | "per page": "por página", 19 | "results": "resultados", 20 | "to": "até" 21 | } 22 | -------------------------------------------------------------------------------- /resources/lang/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Êtes-vous sûr de vouloir continuer ?", 3 | "Browse": "Explorer", 4 | "Cancel": "Annuler", 5 | "Clear selection": "Effacer la sélection", 6 | "Confirm": "Confirmer", 7 | "Drag and drop your files or Browse": "Glissez et déposez vos fichiers ou parcourez-les", 8 | "Excel Export": "Exporter en Excel", 9 | "Export results": "Exporter les résultats", 10 | "Go to page :page": "Aller à la page :page", 11 | "Item selected": "Elément sélectionné", 12 | "Items selected": "Eléments sélectionnés", 13 | "Pagination Navigation": "Navigation par pagination", 14 | "Search": "Recherche", 15 | "Select all on this page": "Tout sélectionner sur cette page", 16 | "Select all results": "Sélectionner tous les résultats", 17 | "of": "de", 18 | "per page": "par page", 19 | "results": "résultats", 20 | "to": "à" 21 | } 22 | -------------------------------------------------------------------------------- /src/Ssr.php: -------------------------------------------------------------------------------- 1 | null]; 16 | 17 | $data = [ 18 | 'components' => $components, 19 | 'html' => $html, 20 | 'dynamics' => $dynamics, 21 | 'splade' => $splade->toArray(), 22 | ]; 23 | 24 | return rescue( 25 | callback: fn () => Http::post(config('splade.ssr.server'), $data)->throw()->json(), 26 | rescue: $default, 27 | report: false 28 | ) ?: $default; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/lang/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Apakah anda yakin ingin melanjutkan?", 3 | "Browse": "Temukan", 4 | "Cancel": "Batalkan", 5 | "Clear selection": "Hapus pilihan", 6 | "Confirm": "Konfimasi", 7 | "Drag and drop your files or Browse": "Tarik dan lepas file anda atau telusuri", 8 | "Excel Export": "Export Excel", 9 | "Export results": "Export hasil", 10 | "Go to page :page": "Pergi ke halaman :page", 11 | "Item selected": "Item yang dipilih", 12 | "Items selected": "Item yang dipilih", 13 | "Pagination Navigation": "Navigasi paginasi", 14 | "Remove search": "Hapus pencarian", 15 | "Reset": "Atur ulang", 16 | "Search": "Cari", 17 | "Select all on this page": "Pilih semua di halaman ini", 18 | "Select all results": "Pilih semua hasil", 19 | "of": "dari", 20 | "per page": "Per halaman", 21 | "results": "hasil", 22 | "to": "ke" 23 | } 24 | -------------------------------------------------------------------------------- /src/Table/SearchInput.php: -------------------------------------------------------------------------------- 1 | key, 34 | $this->columns, 35 | $this->label, 36 | $this->value, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /resources/lang/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Are you sure you want to continue?": "Вы уверены, что хотите продолжить?", 3 | "Browse": "Открыть", 4 | "Cancel": "Отмена", 5 | "Clear selection": "Сбросить выбор", 6 | "Confirm": "Подтвердить", 7 | "Drag and drop your files or Browse": "Перетащите или откройте Ваши файлы", 8 | "Excel Export": "Экспорт в Excel", 9 | "Export results": "Результаты экспорта", 10 | "Go to page :page": "Перейти к :page-й странице", 11 | "Item selected": "Выбранный элемент", 12 | "Items selected": "Выбранные элементы", 13 | "Pagination Navigation": "Навигация", 14 | "Remove search": "Сбросить поиск", 15 | "Reset": "Сброс", 16 | "Search": "Поиск", 17 | "Select all on this page": "Выбрать все на этой странице", 18 | "Select all results": "Выбрать все результаты", 19 | "of": "из", 20 | "per page": "на странице", 21 | "results": "результатов", 22 | "to": "по" 23 | } 24 | -------------------------------------------------------------------------------- /resources/views/form/group.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | [$gridClasses, $otherClasses] = collect(explode(' ', $attributes->get('class'))) 3 | ->partition(fn ($class) => Str::startsWith($class, ['gap', 'grid']) || Str::contains($class, [':gap', ':grid'])); 4 | @endphp 5 | 6 |
only(['v-if', 'v-show'])->class($otherClasses->all())->merge([ 7 | 'data-validation-key' => $validationKey(), 8 | ]) }}> 9 | @includeWhen($label, 'splade::form.label', ['label' => $label]) 10 | 11 |
except(['v-if', 'v-show', 'class'])->class([ 12 | 'flex flex-wrap space-x-6' => $inline && $gridClasses->isEmpty(), 13 | 'space-y-1' => !$inline && $gridClasses->isEmpty(), 14 | ])->class($gridClasses->all()) }} 15 | > 16 | {{ $slot }} 17 |
18 | 19 | @includeWhen($help, 'splade::form.help', ['help' => $help]) 20 | @includeWhen($showErrors, 'splade::form.error', ['name' => $validationKey()]) 21 |
-------------------------------------------------------------------------------- /src/Components/Form/Group.php: -------------------------------------------------------------------------------- 1 | only(['v-if', 'v-show', 'class']) }}> 2 | 20 | 21 | @includeWhen($help, 'splade::form.help', ['help' => $help]) 22 | @includeWhen($showErrors, 'splade::form.error', ['name' => $validationKey()]) 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /resources/views/form/textarea.blade.php: -------------------------------------------------------------------------------- 1 | only(['v-if', 'v-show', 'class']) }} 3 | :autosize="@js($attributes->has('autosize') ? (bool) $attributes->get('autosize') : $defaultAutosizeValue)" 4 | v-model="{{ $vueModel() }}" 5 | > 6 | 17 | 18 | @includeWhen($help, 'splade::form.help', ['help' => $help]) 19 | @includeWhen($showErrors, 'splade::form.error', ['name' => $validationKey()]) 20 | 21 | -------------------------------------------------------------------------------- /resources/views/form/wysiwyg.blade.php: -------------------------------------------------------------------------------- 1 | only(['v-if', 'v-show', 'class']) }} 3 | :options="@js($joditOptions())" 4 | :js-options="{!! $jsJoditOptions() !!}" 5 | v-model="{{ $vueModel() }}" 6 | :dusk="@js($attributes->get('dusk'))" 7 | > 8 | 19 | 20 | @includeWhen($help, 'splade::form.help', ['help' => $help]) 21 | @includeWhen($showErrors, 'splade::form.error', ['name' => $validationKey()]) 22 | 23 | -------------------------------------------------------------------------------- /resources/views/table/add-search-row.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | @foreach($table->searchInputs() as $searchInput) 9 | @if($searchInput->key === 'global') 10 | @continue 11 | @endif 12 | 13 | 19 | @endforeach 20 | -------------------------------------------------------------------------------- /resources/views/table/global-search.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 12 | 17 |
-------------------------------------------------------------------------------- /src/Http/ResolvableData.php: -------------------------------------------------------------------------------- 1 | callable = $callable; 17 | } 18 | 19 | /** 20 | * Helper method the wrap non-callable values into a callable. 21 | * 22 | * @param mixed $value 23 | */ 24 | public static function from($value): static 25 | { 26 | return new static( 27 | is_callable($value) ? $value : fn () => $value 28 | ); 29 | } 30 | 31 | /** 32 | * Resolves the callable based on the given boolean. 33 | * 34 | * @return mixed 35 | */ 36 | public function resolveIf(bool $shouldResolve) 37 | { 38 | return $shouldResolve 39 | ? App::call($this->callable) 40 | : null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /resources/views/functional/form.blade.php: -------------------------------------------------------------------------------- 1 | @php $data = $formData() @endphp 2 | 3 | rejectWhenBlank('id')->except('class') }} 4 | @if($data['data']) :default="@js($data['data'])" @else :default="{!! $data['json'] !!}" @endif 5 | :scroll-on-error="@js($scrollOnError)" 6 | :splade-id="@js($spladeId)" 7 | :submit-on-change="@js($submitOnChange)" 8 | :escape-validation-messages="@js($escapeValidationMessages)" 9 | :preserve-scroll="@js($preserveScroll)" 10 | :background="@js($background)" 11 | :debounce="@js($debounce)" 12 | :accept-header="@js($acceptHeader)" 13 | > 14 | 21 | -------------------------------------------------------------------------------- /resources/views/form/checkbox.blade.php: -------------------------------------------------------------------------------- 1 |
only(['v-if', 'v-show', 'class']) }}> 2 | 19 | 20 | @includeWhen($help, 'splade::form.help', ['help' => $help]) 21 | @includeWhen($showErrors, 'splade::form.error', ['name' => $validationKey()]) 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Components/SpladeComponent.php: -------------------------------------------------------------------------------- 1 | component = static::normalize($is); 18 | } 19 | 20 | /** 21 | * Prepends the 'x-' Blade Component prefix to the given name. 22 | */ 23 | public static function tag(string $name): string 24 | { 25 | return 'x-' . static::normalize($name); 26 | } 27 | 28 | /** 29 | * Prepends the configured prefix to the given component name. 30 | */ 31 | public static function normalize(string $name): string 32 | { 33 | $prefix = config('splade.blade.component_prefix'); 34 | 35 | if ($prefix) { 36 | $prefix .= '-'; 37 | } 38 | 39 | return $prefix . $name; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Components/DynamicHtml.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /lib/Components/State.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/functional/transition.blade.php: -------------------------------------------------------------------------------- 1 | 2 | mergeVueBinding('enter', "{$animate} ? '{$animation->enter}' : ''") 5 | ->mergeVueBinding('enter-from', "{$animate} ? '{$animation->enterFrom}' : ''") 6 | ->mergeVueBinding('enter-to', "{$animate} ? '{$animation->enterTo}' : ''") 7 | ->mergeVueBinding('leave', "{$animate} ? '{$animation->leave}' : ''") 8 | ->mergeVueBinding('leave-from', "{$animate} ? '{$animation->leaveFrom}' : ''") 9 | ->mergeVueBinding('leave-to', "{$animate} ? '{$animation->leaveTo}' : ''") 10 | ->mergeVueBinding(':is', $child ? 'TransitionChild' : 'TransitionRoot') 11 | ->mergeVueBinding(':show', $show) 12 | ->mergeVueBinding(':appear', $appear) 13 | ->mergeVueBinding(':unmount', $unmount) 14 | ->mergeVueBinding('@after-leave', $afterLeave) 15 | }} 16 | > 17 | {{ $slot }} 18 | 19 | -------------------------------------------------------------------------------- /src/Components/Dialog.php: -------------------------------------------------------------------------------- 1 | spladeId = Str::random(); 24 | } 25 | 26 | /** 27 | * Get the view / contents that represent the component. 28 | * 29 | * @return \Illuminate\Contracts\View\View|\Closure|string 30 | */ 31 | public function render() 32 | { 33 | return view('splade::functional.dialog', [ 34 | 'panel' => $this->panel, 35 | 'open' => $this->open, 36 | 'unmount' => $this->unmount, 37 | 'close' => $this->close, 38 | ]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Components/Form/Radio.php: -------------------------------------------------------------------------------- 1 | true, 21 | 'splade.preserveScroll' => $this->preserveScroll, 22 | ]; 23 | } 24 | 25 | /** 26 | * Preserve the scroll value on refresh. 27 | * 28 | * @return $this 29 | */ 30 | public function preserveScroll(bool $value = true): self 31 | { 32 | $this->preserveScroll = $value; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * Returns the same array, but this method is called when broadcasting. 39 | * 40 | * @return array 41 | */ 42 | public function jsonSerialize(): mixed 43 | { 44 | return $this->toArray(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Components/filepond.scss: -------------------------------------------------------------------------------- 1 | @import "filepond/dist/filepond.css"; 2 | @import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css"; 3 | 4 | .filepond--root { 5 | @apply mb-0; 6 | 7 | .filepond--drop-label { 8 | @apply min-h-[2.625rem]; 9 | } 10 | } 11 | 12 | .filepond--panel-root { 13 | @apply bg-white rounded-md border border-gray-300 shadow-sm; 14 | } 15 | 16 | .filepond--drop-label { 17 | @apply text-black justify-start text-gray-500 px-2; 18 | } 19 | 20 | .filepond--item-panel { 21 | @apply bg-indigo-500; 22 | } 23 | 24 | .filepond--file-action-button { 25 | @apply bg-indigo-700; 26 | } 27 | 28 | .filepond--file, 29 | .filepond--item-panel, 30 | .filepond--image-preview-wrapper { 31 | @apply rounded-md; 32 | } 33 | 34 | .filepond--image-preview-overlay-success { 35 | @apply text-gray-900; 36 | } 37 | 38 | .filepond--file-info { 39 | .filepond--file-info-sub { 40 | @apply opacity-75; 41 | } 42 | } 43 | 44 | [data-filepond-item-state*="invalid"] .filepond--item-panel, [data-filepond-item-state*="error"] .filepond--item-panel { 45 | @apply bg-red-500; 46 | } -------------------------------------------------------------------------------- /resources/views/functional/vue-bridge.blade.php: -------------------------------------------------------------------------------- 1 | @php $vueData = $_vueData() @endphp 2 | 3 | only($vueEvents = ['v-on:success', 'v-on:error', '@@success', '@@error']) }} 5 | :backend-route="@js(route('splade.withVueBridge'))" 6 | :default="@js($vueData['data'])" 7 | :initial-instance="@js($vueData['instance'])" 8 | :initial-signature="@js($vueData['signature'])" 9 | :methods="@js($vueData['methods'])" 10 | :original-url="@js($vueData['url'])" 11 | :verb="@js($vueData['verb'])" 12 | > 13 | 30 | -------------------------------------------------------------------------------- /lib/PreserveScrollDirective.js: -------------------------------------------------------------------------------- 1 | import { nextTick } from "vue"; 2 | import { Splade } from "./Splade.js"; 3 | import debounce from "lodash-es/debounce"; 4 | 5 | export default { 6 | created: (el, binding) => { 7 | if(Splade.isSsr) { 8 | return; 9 | } 10 | 11 | const key = `preserveScroll-${binding.arg}`; 12 | 13 | const scrollPosition = Splade.restore(key); 14 | 15 | if (scrollPosition) { 16 | nextTick(() => { 17 | if (typeof el.scrollTo === "function") { 18 | el.scrollTo(scrollPosition.left, scrollPosition.top); 19 | } else { 20 | el.scrollTop = scrollPosition.top; 21 | el.scrollLeft = scrollPosition.left; 22 | } 23 | }); 24 | } 25 | 26 | const rememberScroll = function () { 27 | Splade.remember(key, { 28 | top: el.scrollTop, 29 | left: el.scrollLeft, 30 | }); 31 | }; 32 | 33 | el.addEventListener("scroll", debounce(rememberScroll, 100)); 34 | 35 | rememberScroll(); 36 | } 37 | }; -------------------------------------------------------------------------------- /stubs/routes/web.php: -------------------------------------------------------------------------------- 1 | group(function () { 17 | Route::get('/', fn () => view('home'))->name('home'); 18 | Route::get('/docs', fn () => view('docs'))->name('docs'); 19 | 20 | // Registers routes to support the interactive components... 21 | Route::spladeWithVueBridge(); 22 | 23 | // Registers routes to support password confirmation in Form and Link components... 24 | Route::spladePasswordConfirmation(); 25 | 26 | // Registers routes to support Table Bulk Actions and Exports... 27 | Route::spladeTable(); 28 | 29 | // Registers routes to support async File Uploads with Filepond... 30 | Route::spladeUploads(); 31 | }); 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) protonemedia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/FormBuilder/Number.php: -------------------------------------------------------------------------------- 1 | attributes['min'] = $value; 17 | 18 | return $this; 19 | } 20 | 21 | /** 22 | * Set the max-value 23 | * 24 | * @return $this 25 | */ 26 | public function maxValue(int $value): self 27 | { 28 | $this->attributes['max'] = $value; 29 | 30 | return $this; 31 | } 32 | 33 | /** 34 | * Set the min-value to 0 35 | * 36 | * @return $this 37 | */ 38 | public function unsigned(): self 39 | { 40 | $this->minValue(0); 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * Set the step-size 47 | * 48 | * @param float|int $value 49 | * @return $this 50 | */ 51 | public function step($step = 1): self 52 | { 53 | $this->attributes['step'] = $step; 54 | 55 | return $this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/FileUploads/SpladeFile.php: -------------------------------------------------------------------------------- 1 | file = $file; 16 | 17 | if ($file instanceof UploadedFile) { 18 | $this->upload = $file; 19 | } 20 | 21 | if ($file instanceof ExistingFile) { 22 | $this->existing = $file; 23 | } 24 | } 25 | 26 | /** 27 | * Returns a boolean whether the file exists. 28 | */ 29 | public function exists(): bool 30 | { 31 | return $this->file instanceof ExistingFile; 32 | } 33 | 34 | /** 35 | * Returns a boolean whether the file is uploaded. 36 | */ 37 | public function doesntExist(): bool 38 | { 39 | return !$this->exists(); 40 | } 41 | 42 | /** 43 | * Returns true if the file is uploaded. 44 | */ 45 | public function new(): bool 46 | { 47 | return $this->file instanceof UploadedFile; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/TransitionAnimation.php: -------------------------------------------------------------------------------- 1 | name; 28 | } 29 | 30 | /** 31 | * Returns an array with all data. 32 | * 33 | * @return array 34 | */ 35 | public function toArray() 36 | { 37 | return [ 38 | 'enter' => $this->enter, 39 | 'enter-from' => $this->enterFrom, 40 | 'enter-to' => $this->enterTo, 41 | 'leave' => $this->leave, 42 | 'leave-from' => $this->leaveFrom, 43 | 'leave-to' => $this->leaveTo, 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Components/Form/Radios.php: -------------------------------------------------------------------------------- 1 | SpladeComponent::normalize('group'), 38 | 'radioComponent' => SpladeComponent::normalize('radio'), 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Http/ConfirmPasswordController.php: -------------------------------------------------------------------------------- 1 | recentlyConfirmed($request)) { 18 | return response()->noContent(200)->skipSpladeMiddleware(); 19 | } 20 | 21 | throw ValidationException::withMessages([ 22 | 'password' => __('The password confirmation has expired.'), 23 | ]); 24 | } 25 | 26 | /** 27 | * Confirm the given user's password. 28 | */ 29 | public function store(Request $request, PasswordValidator $passwordValidator): Response 30 | { 31 | $passwordValidator->validateRequest($request, 'password'); 32 | 33 | $request->session()->put('auth.password_confirmed_at', time()); 34 | 35 | return response()->noContent(200)->skipSpladeMiddleware(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/views/components/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | except('class') 2 | ->mergeVueBinding(':inline', $inline) 3 | ->mergeVueBinding(':teleport', $teleport) 4 | ->mergeVueBinding(':close-on-click', $closeOnClick) 5 | }} :splade-id="@js($spladeId)"> 6 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /src/Components/Lazy.php: -------------------------------------------------------------------------------- 1 | passthrough = implode(',', Form::splitByComma($passthrough)); 23 | } 24 | 25 | /** 26 | * Get the view / contents that represent the component. 27 | * 28 | * @return \Illuminate\Contracts\View\View|\Closure|string 29 | */ 30 | public function render() 31 | { 32 | $key = $this->splade->newLazyComponentKey(); 33 | 34 | return $this->splade->isLazyRequest() 35 | ? implode([ 36 | '', 37 | '{{ $slot }}', 38 | '', 39 | ]) : view('splade::functional.lazy', [ 40 | 'name' => $key, 41 | ]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Components/Form/Checkbox.php: -------------------------------------------------------------------------------- 1 | 2 | mergeVueBinding(':close-button', $closeButton) }} :name="@js($name)"> 3 | 23 | 24 | -------------------------------------------------------------------------------- /src/Facades/SEO.php: -------------------------------------------------------------------------------- 1 | listeners = $this->parseListeners($listen); 22 | } 23 | 24 | /** 25 | * Parses the given 'listen' attribute. 26 | * 27 | * @return void 28 | */ 29 | private function parseListeners(mixed $listen) 30 | { 31 | if (!is_string($listen)) { 32 | return $this->parseJsonData($listen); 33 | } 34 | 35 | return collect(explode(',', $listen)) 36 | ->map(fn ($key) => trim($key)) 37 | ->all(); 38 | } 39 | 40 | /** 41 | * Get the view / contents that represent the component. 42 | * 43 | * @return \Illuminate\Contracts\View\View|\Closure|string 44 | */ 45 | public function render() 46 | { 47 | return view('splade::functional.event'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Commands/SsrTestCommand.php: -------------------------------------------------------------------------------- 1 | info('Testing SSR server: ' . config('splade.ssr.server')); 24 | 25 | $dataTag = SpladeComponent::tag('data'); 26 | 27 | $result = (new Ssr)->render( 28 | components: '', 29 | html: Blade::render("<{$dataTag}>

Test

"), 30 | dynamics: [], 31 | splade: SpladeResponseData::make() 32 | ); 33 | 34 | if (Str::contains($result['body'] ?? '', '

Test

')) { 35 | $this->info('OK'); 36 | 37 | return static::SUCCESS; 38 | } 39 | 40 | $this->error('Wrong response.'); 41 | 42 | return static::FAILURE; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Components/Toast.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/DataStores.php: -------------------------------------------------------------------------------- 1 | keyBy->name; 31 | 32 | $this->scopes = $stores->map->name->implode(','); 33 | $this->remember = $stores->filter->remember->keys()->all(); 34 | $this->localStorage = $stores->filter->localStorage->keys()->all(); 35 | 36 | $this->parsed = (object) $stores->reject->json->map->data->all(); 37 | $this->raw = (object) $stores->map->json->filter()->all(); 38 | } 39 | 40 | /** 41 | * Get the view / contents that represent the component. 42 | * 43 | * @return \Illuminate\Contracts\View\View|\Closure|string 44 | */ 45 | public function render() 46 | { 47 | return view('splade::functional.dataStores'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /resources/views/functional/form-builder.blade.php: -------------------------------------------------------------------------------- 1 | getClass()) 7 | 8 | :confirm="$form->getOption('confirm')" 9 | :confirm-danger="$form->getOption('confirm_danger')" 10 | :confirm-text="$form->getOption('text')" 11 | :confirm-button="$form->getOption('confirm_button')" 12 | :cancel-button="$form->getOption('cancel_button')" 13 | :require-password="$form->getOption('require_password')" 14 | :require-password-once="$form->getOption('require_password_once')" 15 | 16 | :submit-on-change="$form->getOption('submit_on_change')" 17 | :background="$form->getOption('background')" 18 | :debounce="$form->getOption('debounce')" 19 | 20 | :stay="$form->getOption('stay')" 21 | :reset-on-success="$form->getOption('reset_on_success')" 22 | :restore-on-success="$form->getOption('restore_on_success')" 23 | 24 | :preserve-scroll="$form->getOption('preserve_scroll')" 25 | :blob="$form->getOption('blob')" 26 | 27 | {{ $attributes->rejectWhenBlank('id')->except('class') }} 28 | > 29 | @foreach($form->getFields() as $field) 30 | {!! $field->render() !!} 31 | @endforeach 32 | 33 | {{ $slot ?? '' }} 34 | 35 | -------------------------------------------------------------------------------- /src/Commands/PublishFormStylesheetsCommand.php: -------------------------------------------------------------------------------- 1 | ensureDirectoryExists(resource_path('css')); 21 | 22 | copy(__DIR__ . '/../../lib/Components/choices.scss', resource_path('css/choices.scss')); 23 | copy(__DIR__ . '/../../lib/Components/filepond.scss', resource_path('css/filepond.scss')); 24 | copy(__DIR__ . '/../../lib/Components/flatpickr.styl', resource_path('css/flatpickr.styl')); 25 | copy(__DIR__ . '/../../lib/Components/jodit.scss', resource_path('css/jodit.scss')); 26 | 27 | $this->comment('All done'); 28 | 29 | $this->comment('Please install the required pre-processors with "npm add -D sass stylus", and import the published stylesheet in "app.js".'); 30 | 31 | return self::SUCCESS; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Components/Form/Checkboxes.php: -------------------------------------------------------------------------------- 1 | SpladeComponent::normalize('group'), 43 | 'checkboxComponent' => SpladeComponent::normalize('checkbox'), 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Components/ParsesJsonDataAttribute.php: -------------------------------------------------------------------------------- 1 | toJson(), true); 30 | } 31 | 32 | if ($data instanceof JsonSerializable) { 33 | return json_decode(json_encode($data), true); 34 | } 35 | 36 | if ($data instanceof Arrayable) { 37 | return $data->toArray(); 38 | } 39 | 40 | if (is_array($data)) { 41 | return $data; 42 | } 43 | 44 | $decoded = rescue(fn () => json_decode($data, true), null, false); 45 | 46 | if ($decoded) { 47 | return $decoded; 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/FormSelectOption.php: -------------------------------------------------------------------------------- 1 | attributes); 23 | } 24 | 25 | /** 26 | * Returns the options array from the attributes. 27 | */ 28 | public function getOptions(): array 29 | { 30 | return $this->attributes['options'] ?? []; 31 | } 32 | 33 | /** 34 | * Returns a new ComponentAttributeBag instance with the 35 | * attributes, except the options as we don't want to 36 | * render those as an attribute. 37 | */ 38 | public function attributes(): ComponentAttributeBag 39 | { 40 | return (new ComponentAttributeBag($this->attributes))->except('options'); 41 | } 42 | 43 | /** 44 | * Returns the attributes as an array. 45 | * 46 | * @return array 47 | */ 48 | public function toArray() 49 | { 50 | return $this->attributes()->getAttributes(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Facades/Toast.php: -------------------------------------------------------------------------------- 1 | validate([ 18 | 'ids' => ['required', 'array', 'min:1'], 19 | ]); 20 | 21 | $action = base64_decode($action); 22 | 23 | /** @var AbstractTable $tableInstance */ 24 | $tableInstance = app(base64_decode($table)); 25 | 26 | if (!$tableInstance->authorize($request)) { 27 | throw new UnauthorizedException; 28 | } 29 | 30 | /** @var BulkAction */ 31 | $bulkActionInstance = $tableInstance->make()->getBulkActions()[$action]; 32 | 33 | if ($bulkActionInstance->requirePassword) { 34 | $passwordValidator->validateRequest($request, $bulkActionInstance->requirePassword); 35 | } 36 | 37 | $tableInstance->performBulkAction($action, $request->input('ids', [])); 38 | 39 | return redirect()->back(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Table/Export.php: -------------------------------------------------------------------------------- 1 | label); 30 | } 31 | 32 | /** 33 | * Generates a Signed URL to the export URL. 34 | */ 35 | public function getUrl(): string 36 | { 37 | /** @var Route $route */ 38 | $route = app('router')->getRoutes()->getByAction(TableExportController::class); 39 | 40 | return URL::signedRoute($route->getName(), array_merge( 41 | Arr::except(request()->query(), 'signature'), 42 | [ 43 | 'table' => base64_encode($this->tableClass), 44 | 'export' => base64_encode($this->key), 45 | 'slug' => $this->getSlug(), 46 | ] 47 | )); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Components/Rehydrate.php: -------------------------------------------------------------------------------- 1 | on = Form::splitByComma($on); 24 | } 25 | 26 | $this->passthrough = implode(',', Form::splitByComma($passthrough)); 27 | } 28 | 29 | /** 30 | * Get the view / contents that represent the component. 31 | * 32 | * @return \Illuminate\Contracts\View\View|\Closure|string 33 | */ 34 | public function render() 35 | { 36 | $key = $this->splade->newRehydrateComponentKey(); 37 | 38 | return $this->splade->isRehydrateRequest() 39 | ? implode([ 40 | '', 41 | '{{ $slot }}', 42 | '', 43 | ]) : view('splade::functional.rehydrate', [ 44 | 'name' => $key, 45 | 'on' => $this->on, 46 | ]); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Http/FileUploadController.php: -------------------------------------------------------------------------------- 1 | validate([ 19 | 'file' => ['required', 'file'], 20 | ]); 21 | 22 | $temporaryFileUpload = $filesystem->storeUploadedFileTemporarely($request->file('file')); 23 | 24 | return response($temporaryFileUpload->encryptAttributes())->skipSpladeMiddleware(); 25 | } 26 | 27 | /** 28 | * Decrypt the requested path and delete the file. 29 | */ 30 | public function delete(Request $request, Filesystem $filesystem): Response 31 | { 32 | $request->validate([ 33 | 'file' => ['required', 'string'], 34 | ]); 35 | 36 | $temporaryFileUpload = TemporaryFileUpload::fromEncryptedString($request->input('file')); 37 | 38 | if ($temporaryFileUpload) { 39 | $filesystem->delete($temporaryFileUpload); 40 | } 41 | 42 | return response()->noContent()->skipSpladeMiddleware(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Commands/stubs/splade.table.stub: -------------------------------------------------------------------------------- 1 | withGlobalSearch(columns: ['id']) 52 | ->column('id', sortable: true); 53 | 54 | // ->searchInput() 55 | // ->selectFilter() 56 | // ->withGlobalSearch() 57 | 58 | // ->bulkAction() 59 | // ->export() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Components/Link.php: -------------------------------------------------------------------------------- 1 | parseJsonData($data); 27 | 28 | if ($parsed) { 29 | $this->data = $parsed; 30 | } else { 31 | $this->jsonData = $data ?: '{}'; 32 | } 33 | 34 | // 35 | 36 | $parsed = $this->parseJsonData($headers); 37 | 38 | if ($parsed) { 39 | $this->headers = $parsed; 40 | } else { 41 | $this->jsonHeaders = $headers ?: '{}'; 42 | } 43 | } 44 | 45 | /** 46 | * Get the view / contents that represent the component. 47 | * 48 | * @return \Illuminate\Contracts\View\View|\Closure|string 49 | */ 50 | public function render() 51 | { 52 | return view('splade::functional.link', [ 53 | 'data' => $this->data, 54 | 'jsonData' => $this->jsonData, 55 | 'headers' => $this->headers, 56 | 'jsonHeaders' => $this->jsonHeaders, 57 | ]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /config/splade-seo.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'title' => env('APP_NAME', 'Laravel Splade'), 17 | 'description' => 'Splade provides a super easy way to build Single Page Applications (SPA) using standard Laravel Blade templates, enhanced with renderless Vue 3 components.', 18 | 'keywords' => ['Laravel', 'Splade'], 19 | ], 20 | 21 | 'title_prefix' => '', 22 | 'title_separator' => '', 23 | 'title_suffix' => '', 24 | 25 | 'auto_canonical_link' => true, 26 | 27 | 'open_graph' => [ 28 | 'auto_fill' => false, 29 | 'image' => null, 30 | 'site_name' => null, 31 | 'title' => null, 32 | 'type' => null, // 'WebPage' 33 | 'url' => null, 34 | ], 35 | 36 | 'twitter' => [ 37 | 'auto_fill' => false, 38 | 'card' => null, // 'summary_large_image', 39 | 'description' => null, 40 | 'image' => null, 41 | 'site' => null, // '@username', 42 | 'title' => null, 43 | ], 44 | 45 | ]; 46 | -------------------------------------------------------------------------------- /src/Components/Form/Textarea.php: -------------------------------------------------------------------------------- 1 | static::$defaultAutosize, 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Components/SerializesNewModels.php: -------------------------------------------------------------------------------- 1 | exists) { 21 | return $value; 22 | } 23 | 24 | if ($value instanceof Collection 25 | && $value instanceof QueueableCollection 26 | && $value->first(fn ($model) => !$model->exists) 27 | ) { 28 | return QueueableCollectionWithNewModels::from($value); 29 | } 30 | 31 | return $this->getSerializedPropertyValueTrait($value); 32 | } 33 | 34 | protected function getRestoredPropertyValue($value) 35 | { 36 | if ($value instanceof Model && !$value->exists) { 37 | return $value; 38 | } 39 | 40 | if ($value instanceof QueueableCollectionWithNewModels) { 41 | return $value->restore(); 42 | } 43 | 44 | return $this->getRestoredPropertyValueTrait($value); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Components/Form/Submit.php: -------------------------------------------------------------------------------- 1 | primary = !$this->danger && !$this->secondary; 33 | } 34 | 35 | /** 36 | * Get the view / contents that represent the component. 37 | * 38 | * @return \Illuminate\Contracts\View\View|\Closure|string 39 | */ 40 | public function render() 41 | { 42 | return view('splade::form.submit'); 43 | } 44 | 45 | /** 46 | * Returns a boolean whether there are background or text classes. 47 | */ 48 | public function hasCustomStyling(ComponentAttributeBag $attributes): bool 49 | { 50 | return Button::hasCustomStyling($attributes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Components/Transition.php: -------------------------------------------------------------------------------- 1 | animate = $animate ? 'true' : 'false'; 26 | } 27 | } 28 | 29 | /** 30 | * Get the view / contents that represent the component. 31 | * 32 | * @return \Illuminate\Contracts\View\View|\Closure|string 33 | */ 34 | public function render() 35 | { 36 | $transitionRepository = app(TransitionRepository::class); 37 | 38 | return view('splade::functional.transition', [ 39 | 'animation' => $transitionRepository->get($this->animation), 40 | 'child' => $this->child, 41 | 'show' => $this->show, 42 | 'appear' => $this->appear, 43 | 'unmount' => $this->unmount, 44 | 'afterLeave' => $this->afterLeave, 45 | 'animate' => $this->animate, 46 | ]); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Components/Toggle.php: -------------------------------------------------------------------------------- 1 | toggles = collect(explode(',', $data)) 22 | ->map(fn ($key) => trim($key)) 23 | ->filter() 24 | ->mapWithKeys(fn ($key) => [$key => false]) 25 | ->all(); 26 | } elseif (is_bool($data)) { 27 | $this->toggles = ['default' => $data]; 28 | } else { 29 | $this->toggles = $this->parseJsonData($data) ?: ['default' => false]; 30 | } 31 | } 32 | 33 | /** 34 | * Get the view / contents that represent the component. 35 | * 36 | * @return \Illuminate\Contracts\View\View|\Closure|string 37 | */ 38 | public function render() 39 | { 40 | $toggles = collect($this->toggles)->keys(); 41 | 42 | return view('splade::functional.toggle', [ 43 | 'props' => $toggles->all() === ['default'] 44 | ? implode(', ', ['toggle', 'setToggle', 'toggled']) 45 | : collect($this->toggles)->keys()->merge([ 46 | 'toggle', 'setToggle', 'toggled', 47 | ])->implode(', '), 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Components/Toasts.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/Components/FormHelpers.js: -------------------------------------------------------------------------------- 1 | // The methods in this file are completely stolen from Inertia.js: 2 | // https://github.com/inertiajs/inertia/tree/master/packages/inertia/src 3 | 4 | export function objectToFormData(source, form, parentKey) { 5 | source = source || {}; 6 | form = form || new FormData(); 7 | parentKey = parentKey || null; 8 | 9 | for (const key in source) { 10 | if (Object.prototype.hasOwnProperty.call(source, key)) { 11 | append(form, composeKey(parentKey, key), source[key]); 12 | } 13 | } 14 | 15 | return form; 16 | } 17 | 18 | function composeKey(parent, key) { 19 | return parent ? parent + "[" + key + "]" : key; 20 | } 21 | 22 | function append(form, key, value) { 23 | if (Array.isArray(value)) { 24 | return Array.from(value.keys()).forEach(index => append(form, composeKey(key, index.toString()), value[index])); 25 | } else if (value instanceof Date) { 26 | return form.append(key, value.toISOString()); 27 | } else if (value instanceof File) { 28 | return form.append(key, value, value.name); 29 | } else if (value instanceof Blob) { 30 | return form.append(key, value); 31 | } else if (typeof value === "boolean") { 32 | return form.append(key, value ? "1" : "0"); 33 | } else if (typeof value === "string") { 34 | return form.append(key, value); 35 | } else if (typeof value === "number") { 36 | return form.append(key, `${value}`); 37 | } else if (value === null || value === undefined) { 38 | return form.append(key, ""); 39 | } 40 | 41 | objectToFormData(value, form, key); 42 | } -------------------------------------------------------------------------------- /src/Components/Button.php: -------------------------------------------------------------------------------- 1 | primary = !$this->danger && !$this->secondary; 31 | } 32 | 33 | /** 34 | * Get the view / contents that represent the component. 35 | * 36 | * @return \Illuminate\Contracts\View\View|\Closure|string 37 | */ 38 | public function render() 39 | { 40 | return view('splade::components.button'); 41 | } 42 | 43 | /** 44 | * Returns a boolean whether there are background or text classes. 45 | */ 46 | public static function hasCustomStyling(ComponentAttributeBag $attributes): bool 47 | { 48 | $backgroundOrTextClasses = array_filter(explode(' ', $attributes->get(key: 'class', default: '')), function ($class) { 49 | return Str::contains($class, ['bg-', 'text-']); 50 | }); 51 | 52 | return !empty($backgroundOrTextClasses); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TestStubs.php: -------------------------------------------------------------------------------- 1 | get(), 1000); 53 | 54 | $needles = [ 55 | '
3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | function startServer(createServer, renderToString, createApp) { 2 | const args = {}; 3 | 4 | // Parse the CLI arguments 5 | process.argv.slice(2).forEach((argument) => { 6 | const splitted = argument.replace(/^-+/, "").split("="); 7 | 8 | args[splitted[0]] = (splitted.length === 2) ? splitted[1] : true; 9 | }); 10 | 11 | // Listen to port 9000 by default 12 | const port = args.port || 9000; 13 | 14 | createServer(async (request, response) => { 15 | if (request.method == "POST") { 16 | let data = ""; 17 | request.on("data", (chunk) => (data += chunk)); 18 | request.on("end", async () => { 19 | // This is the same JSON data that is on the initial 20 | // request in the Vue app data-* attributes. 21 | const json = JSON.parse(data); 22 | 23 | const app = createApp({ 24 | components: json.components, 25 | initialDynamics: json.dynamics, 26 | initialHtml: json.html, 27 | initialSpladeData: json.splade 28 | }); 29 | 30 | // Let Vue render the app and return the 31 | // rendered content as a JSON object. 32 | const body = await renderToString(app); 33 | 34 | response.writeHead(200, { "Content-Type": "application/json", "Server": "Splade SSR" }); 35 | response.write(JSON.stringify({ body })); 36 | response.end(); 37 | }); 38 | } 39 | }).listen(port, () => console.log(`Splade SSR server started on port ${port}.`)); 40 | } 41 | 42 | export { startServer }; -------------------------------------------------------------------------------- /src/Table/HasExports.php: -------------------------------------------------------------------------------- 1 | exports; 18 | } 19 | 20 | /** 21 | * Returns a boolean whether this table has exports. 22 | */ 23 | public function hasExports(): bool 24 | { 25 | return !empty($this->exports); 26 | } 27 | 28 | /** 29 | * Adds an export. 30 | * 31 | * @return $this 32 | */ 33 | public function export( 34 | ?string $label = null, 35 | ?string $filename = null, 36 | ?string $type = null, 37 | array $events = [], 38 | ): self { 39 | if (!class_exists(Excel::class)) { 40 | throw new LaravelExcelException( 41 | "To generate exports from a Splade Table, please install the 'maatwebsite/excel' package." 42 | ); 43 | } 44 | 45 | if ($filename === null) { 46 | $filename = $this->configurator 47 | ? Str::slug(class_basename($this->configurator)) . '.xlsx' 48 | : 'export.xlsx'; 49 | } 50 | 51 | $key = count($this->exports); 52 | 53 | $this->exports[$key] = new Export( 54 | key: $key, 55 | label: $label ?: __('Excel Export'), 56 | filename: $filename, 57 | type: $type ?: Excel::XLSX, 58 | tableClass: get_class($this->configurator), 59 | events: $events, 60 | ); 61 | 62 | return $this; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/Components/JoditEditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 67 | -------------------------------------------------------------------------------- /src/Components/Data.php: -------------------------------------------------------------------------------- 1 | parseJsonData($default); 30 | 31 | if ($parsed) { 32 | $this->data = $parsed; 33 | } else { 34 | $this->json = $default ?: '{}'; 35 | } 36 | 37 | if ($remember === true && $store) { 38 | $this->remember = $store; 39 | } 40 | } 41 | 42 | /** 43 | * Get the view / contents that represent the component. 44 | * 45 | * @return \Illuminate\Contracts\View\View|\Closure|string 46 | */ 47 | public function render() 48 | { 49 | if ($this->store) { 50 | Splade::addDataStore(new DataStore( 51 | name: $this->store, 52 | remember: $this->remember, 53 | localStorage: $this->localStorage, 54 | data: $this->data, 55 | json: $this->json, 56 | )); 57 | 58 | return ''; 59 | } 60 | 61 | return view('splade::functional.data', [ 62 | 'data' => $this->data, 63 | 'json' => $this->json, 64 | ]); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/Components/DataStores.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/form/submit.blade.php: -------------------------------------------------------------------------------- 1 | @php $customStyling = $hasCustomStyling($attributes) @endphp 2 | 3 |
4 | 31 |
32 | -------------------------------------------------------------------------------- /resources/views/table/select-rows-dropdown.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 |
11 |
12 | 18 | 19 | @if($showPaginator()) 20 | 26 | @endif 27 | 28 | 35 |
36 |
37 |
-------------------------------------------------------------------------------- /lib/ServerError.vue: -------------------------------------------------------------------------------- 1 |