If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeRight now, the search preview results are hiding and showing correctly... but there are no CSS transitions yet. Why not?
Because... adding the transition is... actually up to us! By defining it in CSS.
Go back to assets/styles/app.css
and head to the bottom. I'm going to paste in three new CSS rules.
... lines 1 - 154 | |
.fade-enter-active, .fade-leave-active { | |
transition: opacity 2000ms; | |
} | |
.fade-enter-from, .fade-leave-to { | |
opacity: 0; | |
} | |
.fade-enter-to, .fade-leave-from { | |
opacity: 1; | |
} |
This 2000 milliseconds here is probably too slow... but it will make it easy to see how the feature works.
Before we talk about what's going on... it should already work! Move over and refresh the page. Type and... beautiful! It faded in! When I click off, it fades out! Amazing!
But this... deserves some explanation. Back in the controller, we defined six classes that useTransition
should use. The option keys come from useTransition
, but I totally made up the class names on the right. Though... they make sense! Because we're going to create a "fade" transition, each class starts with fade
and then matches the option name it relates to.
Anyways, move back to the CSS file where we define the style for these classes. Here's the magic. When we call this.enter()
, useTransition
immediately adds the fade-enter-active
class. That doesn't cause a transition, but it establishes that, if the opacity changes, we want it to transition over 2000 milliseconds.
One frame later, it adds another class - fade-enter-from
- and removes the d-none
class. The result is that the element is now "shown"... but with an opacity set to 0. One frame after that, it removes fade-enter-from
but adds fade-enter-to
. Thanks to this, our browser starts transitioning the opacity from 0 to 1! Awesome!
So... what happens next? useTransition
is smart. It detects that a transition is currently happening and will take 2000 milliseconds. So... it waits. Yup! It literally waits for two seconds for the transition to finish. And then it removes both fade-enter-to
and fade-enter-active
because its work is done. The element faded in and is now fully visible.
Isn't that amazing? stimulus-use
didn't invent this idea: you'll see it in other libraries like Vue. But it is so handy.
In our controller, when we call this.leave()
to hide the element, a similar process happens. First, fade-leave-active
is added to the element, which establishes that we want a 2000 millisecond transition on opacity
. Next, it adds fade-leave-from
, which makes sure that the opacity is definitely set to 1
, which it already was. One frame later, it removes fade-leave-from
and replaces it with fade-leave-to
. The result is that the element starts a 2 second opacity transition from 1 to 0. Two seconds later, after the transition has finished, useTransition
adds the d-none
class and removes both fade-leave-to
and fade-leave-active
. The element is now fully hidden.
How cool is that? Learning how this works is fun. But the result is even better. And in your day-to-day use, it's really simple. Now that we have these three CSS rules defined, we could reuse this exact useTransition
in any other controller to add, fade in and fade out functionality to it. Heck, you could even create a re-usable JavaScript module that sets up the behavior and these options automatically for you!
Next: there's one last thing I want to talk about. Stimulus is used by a lot of people, including the Ruby on Rails world. And so, it turns out that there are a bunch of pre-made Stimulus controllers that you can download and use directly in your app! Yay! Let's install one and learn how to register it with our Stimulus application.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
"doctrine/annotations": "^1.0", // 1.11.1
"doctrine/doctrine-bundle": "^2.2", // 2.2.3
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
"doctrine/orm": "^2.8", // 2.8.1
"phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
"sensio/framework-extra-bundle": "^5.6", // v5.6.1
"symfony/asset": "5.2.*", // v5.2.3
"symfony/console": "5.2.*", // v5.2.3
"symfony/dotenv": "5.2.*", // v5.2.3
"symfony/flex": "^1.3.1", // v1.18.5
"symfony/form": "5.2.*", // v5.2.3
"symfony/framework-bundle": "5.2.*", // v5.2.3
"symfony/property-access": "5.2.*", // v5.2.3
"symfony/property-info": "5.2.*", // v5.2.3
"symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
"symfony/security-bundle": "5.2.*", // v5.2.3
"symfony/serializer": "5.2.*", // v5.2.3
"symfony/twig-bundle": "5.2.*", // v5.2.3
"symfony/ux-chartjs": "^1.1", // v1.2.0
"symfony/validator": "5.2.*", // v5.2.3
"symfony/webpack-encore-bundle": "^1.9", // v1.11.1
"symfony/yaml": "5.2.*", // v5.2.3
"twig/extra-bundle": "^2.12|^3.0", // v3.2.1
"twig/intl-extra": "^3.2", // v3.2.1
"twig/twig": "^2.12|^3.0" // v3.2.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
"symfony/debug-bundle": "^5.2", // v5.2.3
"symfony/maker-bundle": "^1.27", // v1.30.0
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/stopwatch": "^5.2", // v5.2.3
"symfony/var-dumper": "^5.2", // v5.2.3
"symfony/web-profiler-bundle": "^5.2" // v5.2.3
}
}
// package.json
{
"devDependencies": {
"@babel/preset-react": "^7.0.0", // 7.12.13
"@popperjs/core": "^2.9.1", // 2.9.1
"@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
"@symfony/webpack-encore": "^1.0.0", // 1.0.4
"bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
"core-js": "^3.0.0", // 3.8.3
"jquery": "^3.6.0", // 3.6.0
"react": "^17.0.1", // 17.0.1
"react-dom": "^17.0.1", // 17.0.1
"regenerator-runtime": "^0.13.2", // 0.13.7
"stimulus": "^2.0.0", // 2.0.0
"stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
"stimulus-use": "^0.24.0-1", // 0.24.0-1
"sweetalert2": "^10.13.0", // 10.14.0
"webpack-bundle-analyzer": "^4.4.0", // 4.4.0
"webpack-notifier": "^1.6.0" // 1.13.0
}
}