├── .github
└── workflows
│ └── .github-actions.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── config
└── toaster.php
├── phpunit.xml
├── src
├── Helpers
│ └── functions.php
├── Interfaces
│ ├── SessionStore.php
│ └── ViewBinder.php
├── LaravelSessionStore.php
├── Toast.php
├── Toaster.php
├── ToasterGroup.php
├── ToasterServiceProvider.php
└── ToasterViewBinder.php
└── tests
├── TestCase.php
└── ToasterTest.php
/.github/workflows/.github-actions.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - dev
8 | tags:
9 | - 5.*
10 | pull_request:
11 | branches: [ master ]
12 |
13 | workflow_dispatch:
14 |
15 | jobs:
16 | phpunit:
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | version: ['8.1', '8.2', '8.3', '8.4']
21 | runs-on: ubuntu-latest
22 |
23 | steps:
24 | - name: Checkout the repository
25 | uses: actions/checkout@v2
26 | with:
27 | fetch-depth: 0
28 |
29 | - name: Setup PHP
30 | uses: shivammathur/setup-php@v2
31 | with:
32 | php-version: ${{ matrix.version }}
33 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, exif, iconv
34 | coverage: xdebug
35 |
36 | - name: Install composer packages
37 | run: |
38 | php -v
39 | composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
40 |
41 | - name: Execute tests
42 | run: |
43 | php -v
44 | ./vendor/phpunit/phpunit/phpunit --version
45 | ./vendor/phpunit/phpunit/phpunit --coverage-clover=coverage.xml
46 | export CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}
47 | bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'
48 |
49 | - name: Upload code coverage
50 | run: |
51 | export CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}
52 | bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | node_modules/
3 | .idea
4 | bootstrap/compiled.php
5 | app/storage/
6 | bootstrap/cache/
7 | .env.*.php
8 | .env.php
9 | .env
10 | *.DS_Store
11 | .phpunit.result.cache
12 | composer.lock
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
5 |
6 | ## [5.0.2] - 26-02-2025
7 | - Laravel 12 compatibility
8 | - Update phpunit configuration
9 | ## [5.0.1] - 13-03-2024
10 | - Laravel 11 compatibility
11 | ## [5.0.0] - 01-05-2023
12 | ### Changed
13 | - Laravel 10 compatibility
14 | - Remove old travis config and replace with GH actions
15 | ## [4.0.1] - 14-02-2022
16 | ### Changed
17 | - Laravel 9 compatibility
18 | - PHP 8 support
19 | ## [4.0.0] - 13-10-2020
20 | ### Changed
21 | - Laravel 8.0 compatibility
22 | - PHP 7.3+ required
23 | ## [3.0.3] - 26-03-2020
24 | ### Changed
25 | - Laravel 7.0 compatibility
26 | ## [3.0.2] - 22-10-2019
27 | ### Changed
28 | - Laravel 6.0 compatibility
29 | ## [3.0.1] - 10-03-2019
30 | ### Changed
31 | - Laravel 5.8 tests
32 | ## [3.0.0] - 27-05-2018
33 | #### BREAKING CHANGES, PLEASE SEE [DOCUMENTATION](https://docs.laralabs.uk/toaster)
34 | ### Added
35 | - Groups - groups of messages can now be created
36 | - Group Helper Functions - width(), classes(), position(), max() and reverse()
37 | - Message Helper Functions - duration() and speed()
38 | - Vue component npm package created to remove need for excess code/instructions [laralabs-vue-toaster](https://github.com/Laralabs/vue-toaster)
39 | - @toastcomponent blade directive to echo component markup into view
40 | - 'toast_stagger' config option - stagger message duration using the 'toast_lifetime' and 'toast_interval' config options
41 | ### Changed
42 | - Overhaul to work with a more advanced frontend component [euvl/vue-notification](https://github.com/euvl/vue-notification)
43 | ### Removed
44 | - expires() function.
45 | - 'js_namespace' config option removed, namespace will always be 'toaster'
46 | ## [2.0.1] - 29-01-2018
47 | ### Changed
48 | - composer.json updated to fix packagist
49 | ## [2.0.0] - 19-09-2017
50 | ### Added
51 | - Support for using redirect()
52 | - @toaster blade directive
53 | ### Changed
54 | - Unit tests altered to reflect changes
55 | ### Removed
56 | - toast() function no longer needed
57 | - 'bind_js_vars_to_this_view' config option
58 | ## [1.1.0] - 12-09-2017
59 | ### Added
60 | - update() function to mass update previous message properties
61 | - Unit Tests
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Matt Clinton
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | The toaster package for Laravel provides a quick 'n' easy method for creating toast messages and binding the data to your view as a JS variable.
13 |
14 | You can then access them in your favourite JS component or use laralabs-vue-toaster, built for this package.
15 |
16 | ```
17 | composer require laralabs/toaster
18 | ```
19 |
20 | ## Documentation
21 |
22 | Full documentation can be found at the link below:
23 |
24 | [Toaster Documentation](https://docs.laralabs.uk/toaster)
25 |
26 | ## Preview
27 | > Example of Toaster being used with [laralabs-vue-toaster](https://github.com/Laralabs/vue-toaster), this setup is included within the documentation.
28 |
29 |
30 |
31 |
32 | ## Demo
33 |
34 | A live demo is available [here](https://toaster.laralabs.uk)
35 |
36 | ## Support
37 | Please raise an issue on GitHub if there is a problem.
38 |
39 | ## License
40 | This is open-sourced software licensed under the [MIT License](http://opensource.org/licenses/MIT).
41 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laralabs/toaster",
3 | "description": "Easily generate and bind message JSON data to the view for use in frontend toast components",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Matt Clinton",
8 | "email": "matt@laralabs.uk"
9 | }
10 | ],
11 | "minimum-stability": "dev",
12 | "require": {
13 | "php": "^8.1",
14 | "illuminate/support": "^10.0|^11.0|^12.0",
15 | "illuminate/database": "^10.0|^11.0|^12.0",
16 | "illuminate/contracts": "^10.0|^11.0|^12.0",
17 | "illuminate/session": "^10.0|^11.0|^12.0"
18 | },
19 | "require-dev": {
20 | "roave/security-advisories": "dev-latest",
21 | "mockery/mockery": "^1.0",
22 | "orchestra/testbench": "^8.0|^9.0|^10.0",
23 | "phpunit/phpunit": "^9.0|^10.5|^11.5.3"
24 | },
25 | "autoload": {
26 | "psr-4": {
27 | "Laralabs\\Toaster\\": "src/",
28 | "Laralabs\\Toaster\\Tests\\": "tests/"
29 | },
30 | "files": [
31 | "src/Helpers/functions.php"
32 | ]
33 | },
34 | "scripts": {
35 | "test": "./vendor/bin/phpunit",
36 | "test:coverage": [
37 | "@putenv XDEBUG_MODE=coverage",
38 | "vendor/bin/phpunit --log-junit=coverage/phpunit.junit.xml --coverage-cobertura=coverage/cobertura.xml --coverage-text"
39 | ]
40 | },
41 | "extra": {
42 | "laravel": {
43 | "providers": [
44 | "Laralabs\\Toaster\\ToasterServiceProvider"
45 | ],
46 | "aliases": {
47 | "Toaster": "Laralabs\\Toaster\\Facades\\Toaster"
48 | }
49 | }
50 | },
51 | "config": {
52 | "allow-plugins": {
53 | "dealerdirect/phpcodesniffer-composer-installer": true
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/config/toaster.php:
--------------------------------------------------------------------------------
1 | 10,
13 |
14 | /*
15 | |--------------------------------------------------------------------------
16 | | Toast Stagger
17 | |--------------------------------------------------------------------------
18 | |
19 | | Enable/Disable stagger function, this uses the Toast Lifetime and
20 | | Toast Interval defined below.
21 | |
22 | */
23 | 'toast_stagger' => true,
24 |
25 | /*
26 | |--------------------------------------------------------------------------
27 | | Toast Stagger All
28 | |--------------------------------------------------------------------------
29 | |
30 | | If true, stagger all toasts starting with the first group added.
31 | | If false, reset the lifetime for each group.
32 | |
33 | */
34 | 'toast_stagger_all' => true,
35 |
36 | /*
37 | |--------------------------------------------------------------------------
38 | | Toast Lifetime
39 | |--------------------------------------------------------------------------
40 | |
41 | | When a toast is not set as important, this is the amount of time it
42 | | stays visible for (Milliseconds).
43 | |
44 | */
45 | 'toast_lifetime' => 2000,
46 |
47 | /*
48 | |--------------------------------------------------------------------------
49 | | Toast Interval
50 | |--------------------------------------------------------------------------
51 | |
52 | | The amount of time between each toast closing (Milliseconds).
53 | |
54 | */
55 | 'toast_interval' => 500,
56 |
57 | /*
58 | |--------------------------------------------------------------------------
59 | | Toast Position
60 | |--------------------------------------------------------------------------
61 | |
62 | | The position of the toast on the page, i.e. 'top left', 'top right',
63 | | 'bottom right' and 'bottom left'.
64 | |
65 | */
66 | 'toast_position' => 'top right',
67 |
68 | /*
69 | |--------------------------------------------------------------------------
70 | | Toast Width
71 | |--------------------------------------------------------------------------
72 | |
73 | | Classes specified here will be added to the toast component.
74 | |
75 | */
76 | 'toast_width' => '300px',
77 |
78 | /*
79 | |--------------------------------------------------------------------------
80 | | Toast Classes
81 | |--------------------------------------------------------------------------
82 | |
83 | | Classes specified here will be added to the toast component.
84 | |
85 | */
86 | 'toast_classes' => [],
87 |
88 | /*
89 | |--------------------------------------------------------------------------
90 | | Reverse Order
91 | |--------------------------------------------------------------------------
92 | |
93 | | Show toasts in reverse order.
94 | |
95 | */
96 | 'reverse_order' => false,
97 |
98 | /*
99 | |--------------------------------------------------------------------------
100 | | Animation Type
101 | |--------------------------------------------------------------------------
102 | |
103 | | The animation type used, i.e. 'css' or 'velocity'.
104 | |
105 | */
106 | 'animation_type' => 'css',
107 |
108 | /*
109 | |--------------------------------------------------------------------------
110 | | Animation Speed
111 | |--------------------------------------------------------------------------
112 | |
113 | | The animation type used, i.e. 'css' or 'velocity'.
114 | |
115 | */
116 | 'animation_speed' => 300,
117 | ];
118 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | ./tests
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | src
25 |
26 |
27 | vendor
28 | tests
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Helpers/functions.php:
--------------------------------------------------------------------------------
1 | session = $session;
23 | }
24 |
25 | /**
26 | * Flash a message to the session.
27 | *
28 | * @param $name
29 | * @param $data
30 | */
31 | public function flash($name, $data)
32 | {
33 | $this->session->flash($name, $data);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Toast.php:
--------------------------------------------------------------------------------
1 | update($attributes);
64 | }
65 |
66 | /**
67 | * Update the attributes.
68 | *
69 | * @param array $attributes
70 | *
71 | * @return $this
72 | */
73 | public function update($attributes = [])
74 | {
75 | $attributes = array_filter($attributes);
76 |
77 | foreach ($attributes as $key => $attribute) {
78 | $this->$key = $attribute;
79 | }
80 |
81 | return $this;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Toaster.php:
--------------------------------------------------------------------------------
1 | session = $session;
54 | $this->groups = collect();
55 | $this->lifetime = config('toaster.toast_lifetime');
56 | $this->interval = config('toaster.toast_interval');
57 | $this->limit = config('toaster.max_toasts');
58 | $this->position = config('toaster.toast_position');
59 | $this->currentGroup = 'default';
60 | }
61 |
62 | /**
63 | * Set message info theme.
64 | *
65 | * @return Toaster
66 | */
67 | public function info()
68 | {
69 | $this->groups->last()->updateLastMessage(['type' => 'info']);
70 |
71 | return $this;
72 | }
73 |
74 | /**
75 | * Set message success theme.
76 | *
77 | * @return Toaster
78 | */
79 | public function success()
80 | {
81 | $this->groups->last()->updateLastMessage(['type' => 'success']);
82 |
83 | return $this;
84 | }
85 |
86 | /**
87 | * Set message error theme.
88 | *
89 | * @return Toaster
90 | */
91 | public function error()
92 | {
93 | $this->groups->last()->updateLastMessage(['type' => 'error']);
94 |
95 | return $this;
96 | }
97 |
98 | /**
99 | * Set message warning theme.
100 | *
101 | * @return Toaster
102 | */
103 | public function warning()
104 | {
105 | $this->groups->last()->updateLastMessage(['type' => 'warn']);
106 |
107 | return $this;
108 | }
109 |
110 | /**
111 | * Set message title.
112 | *
113 | * @param $value string
114 | *
115 | * @return Toaster
116 | */
117 | public function title(string $value)
118 | {
119 | $this->groups->last()->updateLastMessage(['title' => $value]);
120 |
121 | return $this;
122 | }
123 |
124 | /**
125 | * Set message as important.
126 | *
127 | * @return Toaster
128 | */
129 | public function important()
130 | {
131 | $this->groups->last()->updateLastMessage(['duration' => -1, 'customDuration' => true]);
132 |
133 | return $this;
134 | }
135 |
136 | /**
137 | * Set message duration.
138 | *
139 | * @param $value int
140 | *
141 | * @throws \InvalidArgumentException
142 | *
143 | * @return Toaster
144 | */
145 | public function duration(int $value)
146 | {
147 | $this->groups->last()->updateLastMessage(['duration' => $value, 'customDuration' => true]);
148 |
149 | return $this;
150 | }
151 |
152 | /**
153 | * Set message animation speed.
154 | *
155 | * @param $value int
156 | *
157 | * @return Toaster
158 | */
159 | public function speed(int $value)
160 | {
161 | $this->groups->last()->updateLastMessage(['speed' => $value]);
162 |
163 | return $this;
164 | }
165 |
166 | /**
167 | * Add a message to the toaster.
168 | *
169 | * @param $message string
170 | * @param $title null|string
171 | * @param $properties null|array
172 | *
173 | * @throws \Exception
174 | *
175 | * @return Toaster
176 | */
177 | public function add(string $message, $title = null, $properties = null)
178 | {
179 | if (is_array($properties)) {
180 | $properties['message'] = $message;
181 | $properties['title'] = is_null($title) ? isset($properties['title']) ? $properties['title'] : $title : $title;
182 | $properties['group'] = isset($properties['group']) ? $properties['group'] : $this->currentGroup;
183 | $group = $properties['group'];
184 | $message = new Toast($properties);
185 | } else {
186 | $group = $this->currentGroup;
187 | $message = new Toast(compact('message', 'title', 'group'));
188 | }
189 |
190 | if ($this->groups->count() < 1) {
191 | $this->group($this->currentGroup);
192 | }
193 |
194 | try {
195 | $this->groups->where('name', '=', $group)->first()->add($message);
196 |
197 | return $this->flash();
198 | } catch (\Throwable $e) {
199 | throw new \Exception('No group found with the specified name');
200 | }
201 | }
202 |
203 | /**
204 | * Create a new group or update existing group.
205 | *
206 | * @param $name
207 | * @param $properties null|array
208 | *
209 | * @return Toaster
210 | */
211 | public function group($name, $properties = null)
212 | {
213 | if ($group = $this->groups->where('name', '=', $name)->first()) {
214 | if (is_array($properties)) {
215 | $group->updateProperties($properties);
216 | }
217 | } else {
218 | $group = new ToasterGroup($name, $properties);
219 | $this->groups->push($group);
220 | }
221 |
222 | $this->currentGroup = $name;
223 |
224 | return $this;
225 | }
226 |
227 | /**
228 | * Set group width.
229 | *
230 | * @param string $width
231 | *
232 | * @return Toaster
233 | */
234 | public function width(string $width)
235 | {
236 | $this->groups->last()->updateProperty('width', $width);
237 |
238 | return $this;
239 | }
240 |
241 | /**
242 | * Set group classes.
243 | *
244 | * @param array $classes
245 | *
246 | * @return Toaster
247 | */
248 | public function classes(array $classes)
249 | {
250 | $this->groups->last()->updateProperty('classes', $classes);
251 |
252 | return $this;
253 | }
254 |
255 | /**
256 | * Set group position.
257 | *
258 | * @param string $position
259 | *
260 | * @return Toaster
261 | */
262 | public function position(string $position)
263 | {
264 | $this->groups->last()->updateProperty('position', $position);
265 |
266 | return $this;
267 | }
268 |
269 | /**
270 | * Set group max.
271 | *
272 | * @param int $max
273 | *
274 | * @return Toaster
275 | */
276 | public function max(int $max)
277 | {
278 | $this->groups->last()->updateProperty('max', $max);
279 |
280 | return $this;
281 | }
282 |
283 | /**
284 | * Set group reverse order.
285 | *
286 | * @param bool $reverse
287 | *
288 | * @return Toaster
289 | */
290 | public function reverse(bool $reverse)
291 | {
292 | $this->groups->last()->updateProperty('reverse', $reverse);
293 |
294 | return $this;
295 | }
296 |
297 | /**
298 | * Updates the previous message.
299 | *
300 | * @param array $attributes
301 | *
302 | * @return Toaster
303 | */
304 | public function update(array $attributes)
305 | {
306 | $this->groups->last()->updateLastMessage($attributes);
307 |
308 | return $this;
309 | }
310 |
311 | /**
312 | * Clear all registered groups.
313 | *
314 | * @return Toaster
315 | */
316 | public function clear()
317 | {
318 | $this->groups = collect();
319 | $this->flash();
320 |
321 | return $this;
322 | }
323 |
324 | /**
325 | * Stagger messages with lifetime and interval.
326 | *
327 | *
328 | * @param bool $all
329 | */
330 | protected function stagger($all = true)
331 | {
332 | $current = $this->lifetime - $this->interval;
333 |
334 | foreach ($this->groups->all() as $group) {
335 | $current = $all ? $current : $this->lifetime - $this->interval;
336 | foreach ($group->messages->all() as $message) {
337 | $current = $current + $this->interval;
338 | $message->duration = $message->customDuration ? $message->duration : $current;
339 | $current = $message->customDuration ? $current - $this->interval : $current;
340 | }
341 | }
342 | }
343 |
344 | /**
345 | * Flash all messages to the session.
346 | *
347 | * @return Toaster
348 | */
349 | public function flash()
350 | {
351 | if (config('toaster.toast_stagger')) {
352 | config('toaster.toast_stagger_all') ? $this->stagger() : $this->stagger(false);
353 | }
354 |
355 | $this->session->flash('toaster', $this->parse());
356 |
357 | return $this;
358 | }
359 |
360 | /**
361 | * Parse groups and messages into array.
362 | *
363 | * @return array
364 | */
365 | protected function parse()
366 | {
367 | $payload = ['data' => []];
368 |
369 | foreach ($this->groups->all() as $group) {
370 | $payload['data'][$group->name] = array_merge($group->properties, ['messages' => $group->messages->toArray()]);
371 | }
372 |
373 | return $payload;
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/src/ToasterGroup.php:
--------------------------------------------------------------------------------
1 | name = $name;
25 | $this->properties = [
26 | 'name' => $name,
27 | 'width' => config('toaster.toast_width'),
28 | 'classes' => config('toaster.toast_classes'),
29 | 'animation_type' => config('toaster.animation_type'),
30 | 'animation_name' => null,
31 | 'velocity_config' => 'velocity',
32 | 'position' => config('toaster.toast_position'),
33 | 'max' => config('toaster.max_toasts'),
34 | 'reverse' => config('toaster.reverse_order'),
35 | ];
36 | if (is_array($properties)) {
37 | $this->properties = array_merge($this->properties, $properties);
38 | }
39 | $this->messages = collect();
40 | }
41 |
42 | /**
43 | * @param $toast
44 | *
45 | * @return \Illuminate\Support\Collection
46 | */
47 | public function add($toast)
48 | {
49 | return $this->messages->push($toast);
50 | }
51 |
52 | /**
53 | * Update property.
54 | *
55 | * @param $key
56 | * @param $value
57 | *
58 | * @return \Laralabs\Toaster\Toaster
59 | */
60 | public function updateProperty($key, $value)
61 | {
62 | $this->properties[$key] = $value;
63 |
64 | return app('toaster')->flash();
65 | }
66 |
67 | /**
68 | * Update properties.
69 | *
70 | * @param $properties
71 | *
72 | * @return \Laralabs\Toaster\Toaster
73 | */
74 | public function updateProperties($properties)
75 | {
76 | foreach ($properties as $key => $value) {
77 | $this->properties[$key] = $value;
78 | }
79 |
80 | return app('toaster')->flash();
81 | }
82 |
83 | /**
84 | * Modify the most recently added message.
85 | *
86 | * @param array $overrides
87 | *
88 | * @throws \Exception
89 | *
90 | * @return \Laralabs\Toaster\Toaster
91 | */
92 | public function updateLastMessage($overrides = [])
93 | {
94 | if ($this->messages->count() > 0) {
95 | $this->messages->last()->update($overrides);
96 |
97 | return app('toaster')->flash();
98 | }
99 |
100 | throw new \Exception('Use the add() function to add a message before attempting to modify it');
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/ToasterServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind(
18 | 'Laralabs\Toaster\Interfaces\SessionStore',
19 | 'Laralabs\Toaster\LaravelSessionStore'
20 | );
21 |
22 | $this->app->singleton('toaster', function () {
23 | return $this->app->make('Laralabs\Toaster\Toaster');
24 | });
25 |
26 | $this->app->singleton('toasterViewBinder', function () {
27 | return $this->app->make('Laralabs\Toaster\ToasterViewBinder');
28 | });
29 |
30 | $this->mergeConfigFrom(
31 | __DIR__.'/../config/toaster.php',
32 | 'toaster'
33 | );
34 | }
35 |
36 | /**
37 | * Bootstrap the application events.
38 | *
39 | * @return void
40 | */
41 | public function boot()
42 | {
43 | $this->publishes([
44 | __DIR__.'/../config/toaster.php' => config_path('toaster.php'),
45 | ], 'config');
46 |
47 | Blade::directive('toaster', function () {
48 | return "bind(); ?>";
49 | });
50 |
51 | Blade::directive('toastcomponent', function () {
52 | return "component(); ?>";
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/ToasterViewBinder.php:
--------------------------------------------------------------------------------
1 | router = $router;
35 | $this->store = $store;
36 |
37 | $this->namespace = 'toaster';
38 | }
39 |
40 | /**
41 | * Generates a JS variable.
42 | *
43 | * @return mixed
44 | */
45 | public function generateJs()
46 | {
47 | if ($this->store->has('toaster')) {
48 | $data = $this->store->get('toaster');
49 | reset($data);
50 | $js = 'window.'.$this->namespace.' = window.'.$this->namespace.' || {};'.$this->namespace.'.'.key($data).' = ';
51 | $js = $js.json_encode($data[key($data)]);
52 |
53 | return $js;
54 | }
55 |
56 | return 'window.'.$this->namespace.' = window.'.$this->namespace.' || {};'.$this->namespace.'.data = {};';
57 | }
58 |
59 | /**
60 | * Generate component data.
61 | *
62 | * @return array
63 | */
64 | protected function generateComponents()
65 | {
66 | $components = [];
67 |
68 | if ($this->store->has('toaster')) {
69 | $data = $this->store->get('toaster');
70 |
71 | foreach ($data['data'] as $group => $properties) {
72 | unset($properties['messages']);
73 | $components[$group] = $properties;
74 | }
75 |
76 | $this->store->forget('toaster');
77 |
78 | return $components;
79 | }
80 |
81 | return $components;
82 | }
83 |
84 | /**
85 | * Return the JavaScript variable to the view.
86 | *
87 | * @return string
88 | */
89 | public function bind()
90 | {
91 | return '';
92 | }
93 |
94 | /**
95 | * Generate vue-notification component markup.
96 | *
97 | * @return string
98 | */
99 | public function component()
100 | {
101 | $components = '';
102 |
103 | foreach ($this->generateComponents() as $group => $props) {
104 | $components = $components.' '.
105 | ''.' '.PHP_EOL;
113 | }
114 |
115 | return $components.' ';
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | session = app('Laralabs\Toaster\Interfaces\SessionStore');
75 |
76 | $this->toaster = new Toaster($this->session);
77 | $this->binder = app('toasterViewBinder');
78 |
79 | $this->limit = config('toaster.max_toasts');
80 | $this->position = config('toaster.toast_position');
81 | $this->lifetime = config('toaster.toast_lifetime');
82 | $this->interval = config('toaster.toast_interval');
83 | $this->width = config('toaster.toast_width');
84 | $this->classes = config('toaster.toast_classes');
85 | $this->reverse = config('toaster.reverse_order');
86 | $this->animationType = config('toaster.animation_type');
87 | $this->animationSpeed = config('toaster.animation_speed');
88 | }
89 |
90 | protected function tearDown(): void
91 | {
92 | parent::tearDown();
93 |
94 | \Mockery::close();
95 | }
96 |
97 | protected function getPackageProviders($app)
98 | {
99 | return [
100 | ToasterServiceProvider::class,
101 | ];
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/tests/ToasterTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Toaster::class, $toaster);
18 | }
19 |
20 | /** @test */
21 | public function it_displays_default_toast_and_can_clear_all(): void
22 | {
23 | $this->toaster->add('cheese');
24 |
25 | $this->assertCount(1, $this->toaster->groups);
26 |
27 | $group = $this->toaster->groups->first();
28 |
29 | $this->assertInstanceOf(ToasterGroup::class, $group);
30 | $this->assertCount(1, $group->messages);
31 |
32 | $toast = $group->messages->first();
33 |
34 | $this->assertEquals('cheese', $toast->message);
35 | $this->assertEquals('info', $toast->type);
36 | $this->assertEquals('', $toast->title);
37 | $this->assertEquals($this->lifetime, $toast->duration);
38 | $this->assertEquals($this->animationSpeed, $toast->speed);
39 |
40 | $this->assertSessionHas('toaster', [
41 | 'data' => [
42 | 'default' => [
43 | 'name' => 'default',
44 | 'width' => $this->width,
45 | 'classes' => [],
46 | 'animation_type' => $this->animationType,
47 | 'animation_name' => null,
48 | 'position' => $this->position,
49 | 'max' => $this->limit,
50 | 'reverse' => $this->reverse,
51 | 'messages' => $this->toaster->groups->first()->messages->toArray(),
52 | 'velocity_config' => 'velocity',
53 | ],
54 | ],
55 | ]);
56 |
57 | $this->toaster->clear();
58 |
59 | $this->assertSessionHas('toaster', [
60 | 'data' => [],
61 | ]);
62 | }
63 |
64 | /** @test */
65 | public function it_can_create_a_toaster_group_and_set_name(): void
66 | {
67 | $this->toaster->group('toastie');
68 |
69 | $group = $this->toaster->groups->first();
70 |
71 | $this->assertInstanceOf(ToasterGroup::class, $group);
72 | $this->assertEquals('toastie', $group->name);
73 |
74 | $this->toaster->clear();
75 | }
76 |
77 | /** @test */
78 | public function it_can_create_a_toaster_group_and_set_properties(): void
79 | {
80 | $properties = [
81 | 'name' => 'toastie',
82 | 'width' => '500px',
83 | 'classes' => ['salt', 'pepper'],
84 | 'animation_type' => 'css',
85 | 'animation_name' => 'animation-name',
86 | 'velocity_config' => 'velocity',
87 | 'position' => 'bottom left',
88 | 'max' => 15,
89 | 'reverse' => true,
90 | ];
91 |
92 | $this->toaster->group($properties['name'], $properties);
93 |
94 | $group = $this->toaster->groups->last();
95 |
96 | $this->assertInstanceOf(ToasterGroup::class, $group);
97 | $this->assertEquals('toastie', $group->name);
98 | $this->assertEquals($properties, $group->properties);
99 |
100 | $this->toaster->clear();
101 | }
102 |
103 | /** @test */
104 | public function it_can_update_existing_group_properties(): void
105 | {
106 | $properties = [
107 | 'name' => 'toastie',
108 | 'width' => '500px',
109 | 'classes' => ['salt', 'pepper'],
110 | 'animation_type' => 'css',
111 | 'animation_name' => 'animation-name',
112 | 'velocity_config' => 'velocity',
113 | 'position' => 'bottom left',
114 | 'max' => 15,
115 | 'reverse' => true,
116 | ];
117 |
118 | $this->toaster->group('toastie')->group($properties['name'], $properties);
119 |
120 | $group = $this->toaster->groups->last();
121 |
122 | $this->assertInstanceOf(ToasterGroup::class, $group);
123 | $this->assertCount(1, $this->toaster->groups);
124 | $this->assertEquals('toastie', $group->name);
125 | $this->assertEquals($properties, $group->properties);
126 |
127 | $this->toaster->clear();
128 | }
129 |
130 | /** @test */
131 | public function it_can_set_group_width(): void
132 | {
133 | $this->toaster->group('toastie')->width('100%');
134 |
135 | $group = $this->toaster->groups->first();
136 |
137 | $this->assertInstanceOf(ToasterGroup::class, $group);
138 | $this->assertEquals('toastie', $group->name);
139 | $this->assertEquals('100%', $group->properties['width']);
140 |
141 | $this->toaster->clear();
142 | }
143 |
144 | /** @test */
145 | public function it_can_set_group_classes(): void
146 | {
147 | $this->toaster->group('toastie')->classes(['salt', 'pepper']);
148 |
149 | $group = $this->toaster->groups->first();
150 |
151 | $this->assertInstanceOf(ToasterGroup::class, $group);
152 | $this->assertEquals('toastie', $group->name);
153 | $this->assertEquals(['salt', 'pepper'], $group->properties['classes']);
154 |
155 | $this->toaster->clear();
156 | }
157 |
158 | /** @test */
159 | public function it_can_set_group_position(): void
160 | {
161 | $this->toaster->group('toastie')->position('top left');
162 |
163 | $group = $this->toaster->groups->first();
164 |
165 | $this->assertInstanceOf(ToasterGroup::class, $group);
166 | $this->assertEquals('toastie', $group->name);
167 | $this->assertEquals('top left', $group->properties['position']);
168 |
169 | $this->toaster->clear();
170 | }
171 |
172 | /** @test */
173 | public function it_can_set_group_max_toasts(): void
174 | {
175 | $this->toaster->group('toastie')->max(10);
176 |
177 | $group = $this->toaster->groups->first();
178 |
179 | $this->assertInstanceOf(ToasterGroup::class, $group);
180 | $this->assertEquals('toastie', $group->name);
181 | $this->assertEquals(10, $group->properties['max']);
182 |
183 | $this->toaster->clear();
184 | }
185 |
186 | /** @test */
187 | public function it_can_set_group_reverse_order(): void
188 | {
189 | $this->toaster->group('toastie')->reverse(true);
190 |
191 | $group = $this->toaster->groups->first();
192 |
193 | $this->assertInstanceOf(ToasterGroup::class, $group);
194 | $this->assertEquals('toastie', $group->name);
195 | $this->assertEquals(true, $group->properties['reverse']);
196 |
197 | $this->toaster->clear();
198 | }
199 |
200 | /** @test */
201 | public function it_can_mass_update_last_toast_properties(): void
202 | {
203 | $properties = [
204 | 'group' => 'toastie',
205 | 'message' => 'cheese',
206 | 'type' => 'warn',
207 | 'title' => 'Toastie Ingredients',
208 | 'duration' => $this->lifetime,
209 | 'speed' => $this->animationSpeed,
210 | ];
211 |
212 | $this->toaster->group($properties['group'])
213 | ->add('cheese')
214 | ->update($properties);
215 |
216 | $group = $this->toaster->groups->last();
217 | $toast = $group->messages->last();
218 |
219 | $this->assertInstanceOf(ToasterGroup::class, $group);
220 | $this->assertEquals('toastie', $group->name);
221 |
222 | foreach ($properties as $property => $value) {
223 | $this->assertEquals($value, $toast->$property);
224 | }
225 |
226 | $this->toaster->clear();
227 | }
228 |
229 | /** @test */
230 | public function it_can_add_toast_with_properties(): void
231 | {
232 | $properties = [
233 | 'group' => 'toastie',
234 | 'message' => 'cheese',
235 | 'type' => 'warn',
236 | 'title' => 'Toastie Ingredients',
237 | 'duration' => $this->lifetime,
238 | 'speed' => $this->animationSpeed,
239 | ];
240 |
241 | $this->toaster->group($properties['group'])
242 | ->add('cheese', null, $properties);
243 |
244 | $group = $this->toaster->groups->last();
245 | $toast = $group->messages->last();
246 |
247 | $this->assertInstanceOf(ToasterGroup::class, $group);
248 | $this->assertEquals('toastie', $group->name);
249 |
250 | foreach ($properties as $property => $value) {
251 | $this->assertEquals($value, $toast->$property);
252 | }
253 |
254 | $this->toaster->clear();
255 | }
256 |
257 | /** @test */
258 | public function it_can_add_toast_to_specified_group(): void
259 | {
260 | $properties = [
261 | 'group' => 'toastie',
262 | 'message' => 'cheese',
263 | 'type' => 'warn',
264 | 'title' => 'Toastie Ingredients',
265 | 'duration' => $this->lifetime + $this->interval,
266 | 'speed' => $this->animationSpeed,
267 | ];
268 |
269 | $this->toaster->group('toastie')->add('ham')
270 | ->group('toastie-two')
271 | ->add('cheese')
272 | ->add('cheese', null, $properties);
273 |
274 | $group = $this->toaster->groups->first();
275 | $toast = $group->messages->last();
276 |
277 | $this->assertInstanceOf(ToasterGroup::class, $group);
278 | $this->assertEquals('toastie', $group->name);
279 |
280 | foreach ($properties as $property => $value) {
281 | $this->assertEquals($value, $toast->$property);
282 | }
283 |
284 | $this->toaster->clear();
285 | }
286 |
287 | /** @test */
288 | public function it_throws_exception_for_invalid_group(): void
289 | {
290 | $this->expectExceptionMessage('No group found with the specified name');
291 |
292 | $properties = [
293 | 'group' => 'toastie-invalid-group',
294 | 'message' => 'cheese',
295 | 'type' => 'warn',
296 | 'title' => 'Toastie Ingredients',
297 | 'duration' => $this->lifetime + $this->interval,
298 | 'speed' => $this->animationSpeed,
299 | ];
300 |
301 | $this->toaster->group('toastie')->add('ham')
302 | ->group('toastie-two')
303 | ->add('cheese')
304 | ->add('cheese', null, $properties);
305 |
306 | $this->toaster->clear();
307 | }
308 |
309 | /** @test */
310 | public function it_can_stagger_groups(): void
311 | {
312 | Config::set('toaster.toast_stagger_all', false);
313 |
314 | $this->toaster->group('toastie')
315 | ->add('ham')
316 | ->add('cheese')
317 | ->group('toastie-two')
318 | ->add('cheese')
319 | ->add('tomato');
320 |
321 | foreach ($this->toaster->groups->all() as $group) {
322 | $current = $this->lifetime - $this->interval;
323 | foreach ($group->messages->all() as $message) {
324 | $current = $current + $this->interval;
325 | $message->customDuration ? null : $this->assertEquals($current, $message->duration);
326 | $current = $message->customDuration ? $current - $this->interval : $current;
327 | }
328 | }
329 | }
330 |
331 | /** @test */
332 | public function it_can_stagger_all_groups_and_retain_custom_duration(): void
333 | {
334 | Config::set('toaster.toast_stagger_all', true);
335 |
336 | $this->toaster->group('toastie')
337 | ->add('ham')
338 | ->add('cheese')
339 | ->group('toastie-two')
340 | ->add('cheese')->duration(10000)
341 | ->add('tomato');
342 |
343 | $current = $this->lifetime - $this->interval;
344 | foreach ($this->toaster->groups->all() as $group) {
345 | $current = config('toaster.toast_stagger_all') ? $current : $this->lifetime - $this->interval;
346 | foreach ($group->messages->all() as $message) {
347 | $current = $current + $this->interval;
348 | $message->customDuration ? $this->assertEquals(10000, $message->duration) : $this->assertEquals($current, $message->duration);
349 | $current = $message->customDuration ? $current - $this->interval : $current;
350 | }
351 | }
352 | }
353 |
354 | /** @test */
355 | public function it_displays_multiple_toast(): void
356 | {
357 | $this->toaster->group('toastie')
358 | ->add('ham')
359 | ->add('cheese');
360 |
361 | $this->assertCount(2, $this->toaster->groups->first()->messages);
362 |
363 | $this->toaster->clear();
364 | }
365 |
366 | /** @test */
367 | public function it_sets_default_duration_and_speed(): void
368 | {
369 | $this->toaster->group('toastie')->add('cheese');
370 |
371 | $this->assertCount(1, $this->toaster->groups->first()->messages);
372 |
373 | $toast = $this->toaster->groups->first()->messages->first();
374 |
375 | $this->assertEquals($this->lifetime, $toast->duration);
376 | $this->assertEquals($this->animationSpeed, $toast->speed);
377 |
378 | $this->toaster->clear();
379 | }
380 |
381 | /** @test */
382 | public function it_sets_custom_duration_and_speed(): void
383 | {
384 | $this->toaster->group('toastie')->add('cheese')->duration(5000)->speed(800);
385 |
386 | $this->assertCount(1, $this->toaster->groups->first()->messages);
387 |
388 | $toast = $this->toaster->groups->first()->messages->first();
389 |
390 | $this->assertEquals('cheese', $toast->message);
391 | $this->assertTrue($toast->customDuration);
392 | $this->assertEquals(5000, $toast->duration);
393 | $this->assertEquals(800, $toast->speed);
394 |
395 | $this->toaster->clear();
396 | }
397 |
398 | /** @test */
399 | public function it_sets_info_toast(): void
400 | {
401 | $this->toaster->group('toastie')->add('cheese')->info();
402 |
403 | $this->assertCount(1, $this->toaster->groups->first()->messages);
404 |
405 | $toast = $this->toaster->groups->first()->messages->first();
406 |
407 | $this->assertEquals('cheese', $toast->message);
408 | $this->assertEquals('info', $toast->type);
409 |
410 | $this->toaster->clear();
411 | }
412 |
413 | /** @test */
414 | public function it_sets_success_toast(): void
415 | {
416 | $this->toaster->group('toastie')->add('cheese')->success();
417 |
418 | $this->assertCount(1, $this->toaster->groups->first()->messages);
419 |
420 | $toast = $this->toaster->groups->first()->messages->first();
421 |
422 | $this->assertEquals('cheese', $toast->message);
423 | $this->assertEquals('success', $toast->type);
424 |
425 | $this->toaster->clear();
426 | }
427 |
428 | /** @test */
429 | public function it_sets_warning_toast(): void
430 | {
431 | $this->toaster->group('toastie')->add('cheese')->warning();
432 |
433 | $this->assertCount(1, $this->toaster->groups->first()->messages);
434 |
435 | $toast = $this->toaster->groups->first()->messages->first();
436 |
437 | $this->assertEquals('cheese', $toast->message);
438 | $this->assertEquals('warn', $toast->type);
439 |
440 | $this->toaster->clear();
441 | }
442 |
443 | /** @test */
444 | public function it_sets_error_toast(): void
445 | {
446 | $this->toaster->group('toastie')->add('cheese')->error();
447 |
448 | $this->assertCount(1, $this->toaster->groups->first()->messages);
449 |
450 | $toast = $this->toaster->groups->first()->messages->first();
451 |
452 | $this->assertEquals('cheese', $toast->message);
453 | $this->assertEquals('error', $toast->type);
454 |
455 | $this->toaster->clear();
456 | }
457 |
458 | /** @test */
459 | public function it_sets_important_toast(): void
460 | {
461 | $this->toaster->group('toastie')->add('cheese')->important();
462 |
463 | $this->assertCount(1, $this->toaster->groups->first()->messages);
464 |
465 | $toast = $this->toaster->groups->first()->messages->first();
466 |
467 | $this->assertEquals('cheese', $toast->message);
468 | $this->assertEquals('info', $toast->type);
469 | $this->assertEquals(-1, $toast->duration);
470 |
471 | $this->toaster->clear();
472 | }
473 |
474 | /** @test */
475 | public function it_sets_toast_title(): void
476 | {
477 | $this->toaster->group('toastie')->add('cheese')->title('Toastie Ingredients');
478 |
479 | $this->assertCount(1, $this->toaster->groups->first()->messages);
480 |
481 | $toast = $this->toaster->groups->first()->messages->first();
482 |
483 | $this->assertEquals('cheese', $toast->message);
484 | $this->assertEquals('info', $toast->type);
485 | $this->assertEquals('Toastie Ingredients', $toast->title);
486 |
487 | $this->toaster->clear();
488 | }
489 |
490 | /** @test */
491 | public function it_generates_correctly_structured_json(): void
492 | {
493 | $this->toaster->group('toastie')->add('ham')->success()->add('cheese');
494 |
495 | $validJson = 'window.toaster = window.toaster || {};toaster.data = {"toastie":{"name":"toastie","width":"300px","classes":[],"animation_type":"css","animation_name":null,"velocity_config":"velocity","position":"top right","max":10,"reverse":false,"messages":[{"group":"toastie","message":"ham","type":"success","title":"","duration":2000,"speed":300,"customDuration":null},{"group":"toastie","message":"cheese","type":"info","title":"","duration":2500,"speed":300,"customDuration":null}]}}';
496 |
497 | $this->assertEquals($validJson, $this->binder->generateJs());
498 |
499 | $this->toaster->clear();
500 | }
501 |
502 | /** @test */
503 | public function it_generates_correct_component_html(): void
504 | {
505 | $this->toaster->group('toastie')->add('ham')->success()->add('cheese');
506 |
507 | $validComponent = ' '.PHP_EOL.' ';
508 |
509 | $this->assertEquals($validComponent, $this->binder->component());
510 | }
511 |
512 | /** @test */
513 | public function it_has_mandatory_message_argument(): void
514 | {
515 | $this->expectException('ArgumentCountError');
516 |
517 | $this->toaster->add();
518 | }
519 |
520 | /** @test */
521 | public function it_aborts_editing_non_message(): void
522 | {
523 | $this->expectExceptionMessage('Use the add() function to add a message before attempting to modify it');
524 |
525 | $this->toaster->group('toastie')->success();
526 | }
527 |
528 | protected function assertSessionHas($name, $value = null): void
529 | {
530 | $this->assertTrue(Session::has($name), "Session doesn't contain '$name'");
531 | if ($value) {
532 | $this->assertEquals($value, Session::get($name), "Session '$name' are not equal to".print_r($value).'');
533 | }
534 | }
535 | }
536 |
--------------------------------------------------------------------------------