├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── composer.json ├── config └── breakpoints.php ├── phpunit.xml ├── readme.md ├── resources └── components │ ├── save-to-session.blade.php │ └── test-windowsize.blade.php ├── routes └── web.php ├── src ├── Helpers.php ├── LaravelWindowSizeController.php └── LaravelWindowSizeServiceProvider.php └── tests └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | .env 3 | .phpunit.result.cache 4 | /.idea 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/tanthammar/laravel-window-size). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 tanthammar 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tanthammar/laravel-window-size", 3 | "description": "Laravel blade directives and php helpers for serverside rendered content, based on browser window size WITHOUT css", 4 | "type": "package", 5 | "license": "MIT", 6 | "keywords": [ 7 | "laravel", 8 | "breakpoints" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Tina Hammar", 13 | "email": "tinahammar@gmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.0|^8.1|^8.2|^8.3|^8.4", 18 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Tanthammar\\LaravelWindowSize\\": "./src" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "Tanthammar\\LaravelWindowSize\\Tests\\": "tests" 28 | } 29 | }, 30 | "scripts": { 31 | "test": "vendor/bin/phpunit" 32 | }, 33 | "extra": { 34 | "laravel": { 35 | "providers": [ 36 | "Tanthammar\\LaravelWindowSize\\LaravelWindowSizeServiceProvider" 37 | ] 38 | } 39 | }, 40 | "require-dev": { 41 | "orchestra/testbench": "^6.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /config/breakpoints.php: -------------------------------------------------------------------------------- 1 | [ 5 | // 0 = undefined|false 6 | // @windowXs (>= 1px && < Sm) 7 | 'Sm' => 640, // => @windowSm (>= 640px && < Md) 8 | 'Md' => 768, // => @windowMd (>= 768px && < Lg) 9 | 'Lg' => 1024, // => @windowLg (>= 1024px && < Xl) 10 | 'Xl' => 1280, // => @windowXl (>= 1280px && < 2xl) 11 | '2xl' => 1536, // => @window2xl (>= 1536px) 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | tests 9 | 10 | 11 | 12 | 13 | ./app 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Laravel Window Size and Breakpoints 2 | Laravel `blade` directives and `php helpers` for **server side rendered content**, based on browser window size WITHOUT css. 3 | 4 | An example to show the purpose of this package: 5 | ```php 6 | >>](https://github.com/TinaHammar/livewire-window-size) 27 | 28 | 29 | ## Description 30 | The main purpose of this package is not to avoid duplicated html, 31 | but to avoid unnecessary server side code execution, just to render content that will never be seen. 32 | 33 | * Vanilla JS syncs the browsers inner `width` and `height`, in realtime (debounced 750ms), when the browser window is resized. 34 | * The corresponding Laravel controller will store the values to the Laravel `Session`. 35 | * The package has a `config/breakpoints` file where you set your breakpoints 36 | * The package provides multiple `@blade` directives and Laravel `helpers()` 37 | * You have access to the browser window width/height via `session('windowW')` and `session('windowH')` 38 | * You can change the config dynamically with Laravel `Config::set(...)` 39 | * The route is throttling the request `15:1`. (The route is: `/update-laravel-window-size`) 40 | 41 | # Important note 42 | It's important to understand the difference between the server side rendered breakpoints, that this package provides, and css media queries. 43 | 44 | When using css, the content of the page will update in realtime as the user resizes the window, 45 | whereas this package debounces a network request and updates the page on the next page request. 46 | 47 | It's important that you place the `` at first point of contact with the user, for your application to look its best. I have it in my `app.blade.php` and on the `login`/`register` pages. 48 | 49 | 50 | ## Installation 51 | ``` 52 | composer require tanthammar/laravel-window-size 53 | ``` 54 | 55 | ## Publish config 56 | ``` 57 | php artisan vendor:publish --tag=laravel-window-size 58 | ``` 59 | 60 | 61 | The default settings are based on TailwindCSS breakpoints 62 | ```php 63 | 'window-width' => [ 64 | // 0 = undefined|false 65 | // @windowXs (>= 1px && < Sm) 66 | 'Sm' => 640, // => @windowSm (>= 640px && < Md) 67 | 'Md' => 768, // => @windowMd (>= 768px && < Lg) 68 | 'Lg' => 1024, // => @windowLg (>= 1024px && < Xl) 69 | 'Xl' => 1280, // => @windowXl (>= 1280px && < 2xl) 70 | '2xl' => 1536, // => @window2xl (>= 1536px) 71 | ], 72 | ``` 73 | 74 | ## Add the component to your layout 75 | * Add this to all layouts where you want to keep track of the browser window size. 76 | * You will have access to the browser window width/height via `session('windowW')` and `session('windowH')` 77 | 78 | Example: `app.blade.php` 79 | ```blade 80 | 81 | ``` 82 | 83 | ## Blade directives 84 | @elsif..., @else..., @end..., @unless... and @endif works with all the directives. Explanation in [Laravel docs](https://laravel.com/docs/8.x/blade#custom-if-statements). 85 | ```blade 86 | //Browser width, with example values 87 | @windowWidthLessThan(400) 88 | @windowWidthGreaterThan(399) 89 | @windowWidthBetween(400, 1500) 90 | 91 | //Browser height, with example values 92 | @windowHeightLessThan(500) 93 | @windowHeightGreaterThan(499) 94 | @windowHeightBetween(400, 900) 95 | 96 | //Breakpoints based on config values 97 | @windowXs() 98 | @windowSm() 99 | @windowMd() 100 | @windowLg() 101 | @windowXl() 102 | @window2xl() 103 | ``` 104 | Example 105 | ```blade 106 | @windowXs() 107 |
This window is extra small
108 | @endif 109 | 110 | @window2xl() 111 |
This window is very large
112 | @endif 113 | ``` 114 | 115 | ## Helpers 116 | Same name as Blade directives 117 | ```php 118 | //Mobile device detection based on request header. 119 | mobileDevice() 120 | 121 | //Browser width, with example values 122 | windowWidthLessThan(400) 123 | windowWidthGreaterThan(399) 124 | windowWidthBetween(400, 1500) 125 | 126 | //Browser height, with example values 127 | windowHeightLessThan(500) 128 | windowHeightGreaterThan(499) 129 | windowHeightBetween(400, 900) 130 | 131 | //Breakpoints based on config values 132 | windowXs() 133 | windowSm() 134 | windowMd() 135 | windowLg() 136 | windowXl() 137 | window2xl() 138 | ``` 139 | 140 | Example php 141 | ```php 142 | if(windowXs()) { 143 | //execute a tiny Eloquent query and return a minimalistic view 144 | } 145 | if(window2xl()) { 146 | //execute a huge Eloquent query and return a gigantic view 147 | } 148 | ``` 149 | 150 | ## Blade directives test component 151 | Add this to any blade view to test the blade directives 152 | ```blade 153 | 154 | ``` 155 | 156 | ## 💬 Let's connect 157 | * [🔗 **Twitter**](https://twitter.com/TinaHammar) 158 | * 🔗 Please 💗 [sponsor me](https://github.com/sponsors/tanthammar) if you like my work. 159 | 160 | ## Credits 161 | * [Martin Krisell](https://github.com/Krisell) 162 | 163 | 164 | ## Changelog 165 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 166 | 167 | ## Contributing 168 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 169 | 170 | ## License 171 | The MIT License (MIT). Please see [License File](/LICENSE.md) for more information. 172 | -------------------------------------------------------------------------------- /resources/components/save-to-session.blade.php: -------------------------------------------------------------------------------- 1 | 52 | -------------------------------------------------------------------------------- /resources/components/test-windowsize.blade.php: -------------------------------------------------------------------------------- 1 | {{-- dev test :) --}} 2 |
3 | 4 |

---WIDTH---

5 | 6 |
session width: {{session('windowW')}}
7 | 8 | @windowWidthLessThan(400) 9 |
windowWIDTHLessThan 400
10 | @endif 11 | 12 | @windowWidthGreaterThan(399) 13 |
windowWIDTHGreaterThan 399
14 | @endif 15 | 16 | @windowWidthBetween(400, 1500) 17 |
windowWIDTHBetween 400, 1500
18 | @endif 19 | 20 |

---HEIGHT---

21 | 22 |
session height: {{session('windowH')}}
23 | 24 | @windowHeightLessThan(500) 25 |
windowHEIGHTLessThan 500
26 | @endif 27 | 28 | @windowHeightGreaterThan(499) 29 |
windowHEIGHTGreaterThan 499
30 | @endif 31 | 32 | @windowHeightBetween(400, 1500) 33 |
windowHEIGHTBetween 400, 1500
34 | @endif 35 | 36 |

---BREAKPOINTS---

37 | 38 | @windowXs() 39 |
Xs window
40 | @endif 41 | 42 | @windowSm() 43 |
Sm window
44 | @endif 45 | 46 | @windowMd() 47 |
Md window
48 | @endif 49 | 50 | @windowLg() 51 |
Lg window
52 | @endif 53 | 54 | @windowXl() 55 |
Xl window
56 | @endif 57 | 58 | @window2xl() 59 |
2xl window
60 | @endif 61 | 62 |

---END---

63 |
64 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | middleware('web', 'throttle:15,1'); 7 | -------------------------------------------------------------------------------- /src/Helpers.php: -------------------------------------------------------------------------------- 1 | header('User-Agent') 11 | ); 12 | } 13 | } 14 | 15 | if (!function_exists('windowWidthLessThan')) { 16 | function windowWidthLessThan(int $value): bool 17 | { 18 | return session('windowW') < $value; 19 | } 20 | } 21 | 22 | if (!function_exists('windowWidthGreaterThan')) { 23 | function windowWidthGreaterThan(int $value): bool 24 | { 25 | return session('windowW') > $value; 26 | } 27 | } 28 | 29 | if (!function_exists('windowWidthBetween')) { 30 | function windowWidthBetween(mixed ...$expression): bool 31 | { 32 | $between = collect(Arr::flatten($expression)); 33 | $w = session('windowW'); 34 | return ($w > $between->first() && $w < $between->last()); 35 | } 36 | } 37 | 38 | if (!function_exists('windowHeightLessThan')) { 39 | function windowHeightLessThan(int $value): bool 40 | { 41 | return session('windowH') < $value; 42 | } 43 | } 44 | 45 | if (!function_exists('windowHeightGreaterThan')) { 46 | function windowHeightGreaterThan(int $value): bool 47 | { 48 | return session('windowH') > $value; 49 | } 50 | } 51 | 52 | if (!function_exists('windowHeightBetween')) { 53 | function windowHeightBetween(mixed ...$expression): bool 54 | { 55 | $between = collect(Arr::flatten($expression)); 56 | $h = session('windowH'); 57 | return ($h > $between->first() && $h < $between->last()); 58 | } 59 | } 60 | 61 | if (!function_exists('windowXs')) { 62 | function windowXs(): bool 63 | { 64 | $w = session('windowW'); 65 | return ( 66 | $w > 0 67 | && $w < config('breakpoints.window-width.Xs', 640) 68 | ); 69 | } 70 | } 71 | 72 | if (!function_exists('windowSm')) { 73 | function windowSm(): bool 74 | { 75 | $w = session('windowW'); 76 | return ( 77 | $w >= config('breakpoints.window-width.Sm', 640) 78 | && $w < config('breakpoints.window-width.Md', 768) 79 | ); 80 | } 81 | } 82 | 83 | if (!function_exists('windowMd')) { 84 | function windowMd(): bool 85 | { 86 | $w = session('windowW'); 87 | return ( 88 | $w >= config('breakpoints.window-width.Md', 768) 89 | && $w < config('breakpoints.window-width.Lg', 1024) 90 | ); 91 | } 92 | } 93 | 94 | if (!function_exists('windowLg')) { 95 | function windowLg(): bool 96 | { 97 | $w = session('windowW'); 98 | return ( 99 | $w >= config('breakpoints.window-width.Lg', 1024) 100 | && $w < config('breakpoints.window-width.Xl', 1280) 101 | ); 102 | } 103 | } 104 | 105 | if (!function_exists('windowXl')) { 106 | function windowXl(): bool 107 | { 108 | $w = session('windowW'); 109 | return ( 110 | $w >= config('breakpoints.window-width.Xl', 1280) 111 | && $w < config('breakpoints.window-width.2xl', 1536) 112 | ); 113 | } 114 | } 115 | 116 | if (!function_exists('window2xl')) { 117 | function window2xl(): bool 118 | { 119 | return session('windowW') >= config('breakpoints.window-width.2xl', 1536); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/LaravelWindowSizeController.php: -------------------------------------------------------------------------------- 1 | input('width'); 19 | $h = (int)$request->input('height'); 20 | 21 | if ($w > 0) { 22 | session(['windowW' => $w]); 23 | } 24 | if ($h > 0) { 25 | session(['windowH' => $h]); 26 | } 27 | 28 | return response()->json(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/LaravelWindowSizeServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../config/breakpoints.php', 'breakpoints'); 19 | } 20 | 21 | public function boot(): void 22 | { 23 | $this->publishes([__DIR__ . '/../config/breakpoints.php' => config_path('breakpoints.php')], 'laravel-window-size'); 24 | $this->loadRoutesFrom(__DIR__ . '/../routes/web.php'); 25 | $this->loadViewsFrom(__DIR__ . '/../resources/', 'window-size'); 26 | $this->bladeDirectives(); 27 | } 28 | 29 | protected function bladeDirectives(): void 30 | { 31 | Blade::if('windowWidthLessThan', static function ($value) { 32 | return windowWidthLessThan($value); 33 | }); 34 | 35 | Blade::if('windowWidthGreaterThan', static function ($value) { 36 | return windowWidthGreaterThan($value); 37 | }); 38 | 39 | Blade::if('windowWidthBetween', static function (...$expression) { 40 | return windowWidthBetween($expression); 41 | }); 42 | 43 | Blade::if('windowHeightLessThan', static function ($value) { 44 | return windowHeightLessThan($value); 45 | }); 46 | 47 | Blade::if('windowHeightGreaterThan', static function ($value) { 48 | return windowHeightGreaterThan($value); 49 | }); 50 | 51 | Blade::if('windowHeightBetween', static function (...$expression) { 52 | return windowHeightBetween($expression); 53 | }); 54 | 55 | Blade::if('windowXs', static fn () => windowXs()); 56 | 57 | Blade::if('windowSm', static fn () => windowSm()); 58 | 59 | Blade::if('windowMd', static fn () => windowMd()); 60 | 61 | Blade::if('windowLg', static fn () => windowLg()); 62 | 63 | Blade::if('windowXl', static fn () => windowXl()); 64 | 65 | Blade::if('window2xl', static fn () => window2xl()); 66 | 67 | Blade::if('mobileDevice', static fn () => mobileDevice()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 |