|
1 | 1 | # Transition Component API |
2 | 2 |
|
3 | | -Performing dynamic page transitions without full page reload. |
| 3 | +Matestack's `transition` component enables switching between pages without a full website reload. It works similar to links, but instead of reloading the complete website, including the layout like Rails usually does, it only asynchronously reloads the page without the layout and replaces it dynamically. |
| 4 | + |
| 5 | +{% hint style="info" %} |
| 6 | +Since `3.0.0` transitions require a wrapping `matestack_vue_js_app` component and a `page_switch` component as seen below |
| 7 | +{% endhint %} |
| 8 | + |
| 9 | +```ruby |
| 10 | +class Shop::Layout < Matestack::Ui::Layout |
| 11 | + |
| 12 | + def response |
| 13 | + matestack_vue_js_app do # required to wrap transitions and page_switch |
| 14 | + nav do |
| 15 | + transition 'Matestack Shop', path: root_path |
| 16 | + transition 'Products', path: products_path |
| 17 | + end |
| 18 | + page_switch do # required to wrap the yield |
| 19 | + yield |
| 20 | + end |
| 21 | + end |
| 22 | + end |
| 23 | + |
| 24 | +end |
| 25 | +``` |
| 26 | + |
| 27 | +Let's add the products page which simply lists all products and adds a transition to their show page for each one. |
| 28 | + |
| 29 | +```ruby |
| 30 | +class Shop::Pages::Products::Index < Matestack::Ui::Page |
| 31 | + |
| 32 | + def response |
| 33 | + Products.all.each do |product| |
| 34 | + div do |
| 35 | + paragraph product.name |
| 36 | + transition 'Details', path: product_path(product) |
| 37 | + end |
| 38 | + end |
| 39 | + end |
| 40 | + |
| 41 | +end |
| 42 | +``` |
4 | 43 |
|
5 | 44 | ## Parameters |
6 | 45 |
|
@@ -50,28 +89,122 @@ When a sub page of a parent `transition` component is currently active, the pare |
50 | 89 |
|
51 | 90 | Parent target: `/some_page` |
52 | 91 |
|
53 | | -Currently active: `/some_page/child_page` --> Parent gets `child-active` |
| 92 | +Currently active: `/some_page/child_page` --> Parent gets `child-active` |
54 | 93 |
|
55 | 94 | Query params do not interfere with this behavior. |
56 | 95 |
|
57 | 96 | ## Events |
58 | 97 |
|
59 | 98 | The `transition` component automatically emits events on: |
60 | 99 |
|
61 | | -* transition triggered by user action -> "page\_loading\_triggered" |
| 100 | +* transition triggered by user action -> "page\_loading\_triggered" |
62 | 101 | * _optional client side delay via `delay` attribute_ |
63 | | -* start to get new page from server -> "page\_loading" |
| 102 | +* start to get new page from server -> "page\_loading" |
64 | 103 | * _server side/network delay_ |
65 | | -* successfully received new page from server -> "page\_loaded" |
66 | | -* failed to receive new page from server -> "page\_loading\_error" |
| 104 | +* successfully received new page from server -> "page\_loaded" |
| 105 | +* failed to receive new page from server -> "page\_loading\_error" |
| 106 | + |
| 107 | +## DOM structure and loading state element |
| 108 | + |
| 109 | +`app/matestack/example_app/layout.rb` |
| 110 | + |
| 111 | +```ruby |
| 112 | +class ExampleApp::Layout < Matestack::Ui::Layout |
| 113 | + |
| 114 | + def response |
| 115 | + #... |
| 116 | + main do |
| 117 | + page_switch do |
| 118 | + yield |
| 119 | + end |
| 120 | + end |
| 121 | + #... |
| 122 | + end |
| 123 | + |
| 124 | + def my_loading_state_slot |
| 125 | + span class: "some-loading-spinner" do |
| 126 | + plain "loading..." |
| 127 | + end |
| 128 | + end |
| 129 | + |
| 130 | +end |
| 131 | +``` |
| 132 | + |
| 133 | +which will render: |
| 134 | + |
| 135 | +```markup |
| 136 | +<main> |
| 137 | + <div class="matestack-page-container"> |
| 138 | + <div class="loading-state-element-wrapper"> |
| 139 | + <span class="some-loading-spinner"> |
| 140 | + loading... |
| 141 | + </span> |
| 142 | + </div> |
| 143 | + <div class="matestack-page-wrapper"> |
| 144 | + <div><!--this div is necessary for conditonal switch to async template via v-if --> |
| 145 | + <div class="matestack-page-root"> |
| 146 | + your page markup |
| 147 | + </div> |
| 148 | + </div> |
| 149 | + </div> |
| 150 | + </div> |
| 151 | +</end> |
| 152 | +``` |
| 153 | + |
| 154 | +and during async page request triggered via transition: |
| 155 | + |
| 156 | +```markup |
| 157 | +<main> |
| 158 | + <div class="matestack-page-container loading"> |
| 159 | + <div class="loading-state-element-wrapper loading"> |
| 160 | + <span class="some-loading-spinner"> |
| 161 | + loading... |
| 162 | + </span> |
| 163 | + </div> |
| 164 | + <div class="matestack-page-wrapper loading"> |
| 165 | + <div><!--this div is necessary for conditonal switch to async template via v-if --> |
| 166 | + <div class="matestack-page-root"> |
| 167 | + your page markup |
| 168 | + </div> |
| 169 | + </div> |
| 170 | + </div> |
| 171 | + </div> |
| 172 | +</end> |
| 173 | +``` |
| 174 | + |
| 175 | +You can use the `loading` class and your loading state element to implement CSS based loading state effects. It may look like this (scss): |
| 176 | + |
| 177 | +```css |
| 178 | +.matestack-page-container{ |
| 179 | + |
| 180 | + .matestack-page-wrapper { |
| 181 | + opacity: 1; |
| 182 | + transition: opacity 0.2s ease-in-out; |
| 183 | + |
| 184 | + &.loading { |
| 185 | + opacity: 0; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + .loading-state-element-wrapper{ |
| 190 | + opacity: 0; |
| 191 | + transition: opacity 0.3s ease-in-out; |
| 192 | + |
| 193 | + &.loading { |
| 194 | + opacity: 1; |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | +} |
| 199 | +``` |
67 | 200 |
|
68 | 201 | ## Examples |
69 | 202 |
|
70 | 203 | The transition core component renders the HTML `<a>` tag and performs a page transition |
71 | 204 |
|
72 | 205 | ### Perform transition from one page to another without full page reload |
73 | 206 |
|
74 | | -First, we define our routes \(`config/routes.rb`\) and the corresponding endpoints in our example controller: |
| 207 | +First, we define our routes (`config/routes.rb`) and the corresponding endpoints in our example controller: |
75 | 208 |
|
76 | 209 | ```ruby |
77 | 210 | get 'my_example_app/page1', to: 'example_app_pages#page1', as: 'page1' |
@@ -156,8 +289,8 @@ class ExampleApp::Pages::SecondExamplePage < Matestack::Ui::Page |
156 | 289 | end |
157 | 290 | ``` |
158 | 291 |
|
159 | | -Now, we can visit our first example page via `localhost:3000/my_example_app/page1` and see our two buttons \(`Page 1` and `Page 2`\) and the content of page 1 \(`My Example App Layout` and `This is Page 1`\). |
| 292 | +Now, we can visit our first example page via `localhost:3000/my_example_app/page1` and see our two buttons (`Page 1` and `Page 2`) and the content of page 1 (`My Example App Layout` and `This is Page 1`). |
160 | 293 |
|
161 | | -After clicking on the `Page 2`-button, we get transferred to our second page \(`This is Page 2`\) without re-loading the whole page. |
| 294 | +After clicking on the `Page 2`-button, we get transferred to our second page (`This is Page 2`) without re-loading the whole page. |
162 | 295 |
|
163 | | -If we then click the other button available \(`Back to Page 1`\), we get transferred back to the first page, again without re-loading the whole page. This behavior can save quite some request payload \(and therefore loading time\) as only the relevant content on a page gets replaced! |
| 296 | +If we then click the other button available (`Back to Page 1`), we get transferred back to the first page, again without re-loading the whole page. This behavior can save quite some request payload (and therefore loading time) as only the relevant content on a page gets replaced! |
0 commit comments