ember-nav-stack 6.1.2 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -21
- package/addon-main.cjs +4 -0
- package/dist/_app_/components/nav-stack-inner-wrapper.js +1 -0
- package/dist/_app_/components/nav-stack.js +1 -0
- package/dist/_app_/components/to-nav-stack.js +1 -0
- package/dist/_app_/helpers/nav-layer-indices.js +1 -0
- package/dist/_app_/modifiers/back-swipe.js +1 -0
- package/dist/_app_/services/gesture.js +1 -0
- package/dist/_app_/services/nav-stacks.js +1 -0
- package/dist/_app_/templates/stackable.js +1 -0
- package/dist/back-swipe-gesture.js +261 -0
- package/dist/back-swipe-gesture.js.map +1 -0
- package/dist/components/nav-stack-inner-wrapper.js +10 -0
- package/dist/components/nav-stack-inner-wrapper.js.map +1 -0
- package/dist/components/nav-stack.js +700 -0
- package/dist/components/nav-stack.js.map +1 -0
- package/dist/components/to-nav-stack.js +22 -0
- package/dist/components/to-nav-stack.js.map +1 -0
- package/dist/helpers/nav-layer-indices.js +21 -0
- package/dist/helpers/nav-layer-indices.js.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/modifiers/back-swipe.js +40 -0
- package/dist/modifiers/back-swipe.js.map +1 -0
- package/dist/routes/stackable-route.js +99 -0
- package/dist/routes/stackable-route.js.map +1 -0
- package/{addon → dist}/services/gesture.js +7 -9
- package/dist/services/gesture.js.map +1 -0
- package/dist/services/nav-stacks.js +137 -0
- package/dist/services/nav-stacks.js.map +1 -0
- package/dist/styles/nav-stack.css +399 -0
- package/dist/templates/stackable.js +8 -0
- package/dist/templates/stackable.js.map +1 -0
- package/{addon-test-support → dist/test-support}/in-viewport.js +7 -10
- package/dist/test-support/in-viewport.js.map +1 -0
- package/dist/test-support/index.js +2 -0
- package/dist/test-support/index.js.map +1 -0
- package/{addon → dist}/utils/animation.js +17 -40
- package/dist/utils/animation.js.map +1 -0
- package/{addon → dist}/utils/back-swipe-recognizer.js +29 -49
- package/dist/utils/back-swipe-recognizer.js.map +1 -0
- package/dist/utils/clone-store.js +88 -0
- package/dist/utils/clone-store.js.map +1 -0
- package/dist/utils/component.js +121 -0
- package/dist/utils/component.js.map +1 -0
- package/dist/utils/header-style.js +46 -0
- package/dist/utils/header-style.js.map +1 -0
- package/dist/utils/transition-decision.js +71 -0
- package/dist/utils/transition-decision.js.map +1 -0
- package/dist/utils/waiter-state.js +130 -0
- package/dist/utils/waiter-state.js.map +1 -0
- package/package.json +79 -91
- package/.vscode/settings.json +0 -2
- package/CHANGELOG.md +0 -208
- package/MODULE_REPORT.md +0 -27
- package/RELEASE.md +0 -54
- package/addon/components/nav-stack/component.js +0 -690
- package/addon/components/nav-stack/template.hbs +0 -37
- package/addon/components/to-nav-stack.js +0 -32
- package/addon/helpers/nav-layer-indices.js +0 -29
- package/addon/routes/stackable-route.js +0 -61
- package/addon/services/nav-stacks.js +0 -157
- package/addon/utils/component.js +0 -40
- package/app/components/nav-stack/component.js +0 -1
- package/app/components/nav-stack/template.js +0 -1
- package/app/components/to-nav-stack.js +0 -1
- package/app/helpers/nav-layer-indices.js +0 -1
- package/app/services/gesture.js +0 -1
- package/app/services/nav-stacks.js +0 -1
- package/app/styles/nav-stack.scss +0 -117
- package/app/templates/stackable.hbs +0 -8
- package/app/utils/animation.js +0 -1
- package/config/deploy.js +0 -29
- package/config/environment.js +0 -5
- package/config/release.js +0 -21
- package/docs/ember-nav-stack-waiters-plan.md +0 -125
- package/index.js +0 -15
- package/tsconfig.json +0 -6
- package/vendor/wobble-shim.js +0 -3
package/README.md
CHANGED
|
@@ -1,43 +1,187 @@
|
|
|
1
1
|
# ember-nav-stack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
iOS-style stack navigation for Ember. Routes push and pop horizontally across one or more visual layers, with spring-driven animations and an interactive back-swipe gesture. Inspired by [ember-elsewhere](https://github.com/ef4/ember-elsewhere).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Compatibility
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
- **ember-source**: `>= 4.12.0` (declared as a peer dependency)
|
|
8
|
+
- **Node**: `>= 20.19`
|
|
9
|
+
- **ember-cli**: any version compatible with the host app
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
* Ember CLI v3.24 or above
|
|
12
|
-
* Node.js v12 or above
|
|
11
|
+
## Installation
|
|
13
12
|
|
|
13
|
+
```
|
|
14
|
+
ember install ember-nav-stack
|
|
15
|
+
```
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
------------------------------------------------------------------------------
|
|
17
|
+
Then import the addon's stylesheet from your app's entry point:
|
|
17
18
|
|
|
19
|
+
```js
|
|
20
|
+
// app/app.js
|
|
21
|
+
import 'ember-nav-stack/styles/nav-stack.css';
|
|
18
22
|
```
|
|
19
|
-
|
|
23
|
+
|
|
24
|
+
(Or `@import 'ember-nav-stack/styles/nav-stack.css';` from your top-level CSS.) Unlike the pre-v6 versions of this addon, v6+ ships as a [v2 addon](https://github.com/embroider-build/embroider/blob/main/docs/v2-addon-format.md) and does not auto-merge styles into your build.
|
|
25
|
+
|
|
26
|
+
## Concepts
|
|
27
|
+
|
|
28
|
+
The addon revolves around three pieces:
|
|
29
|
+
|
|
30
|
+
- **`NavStacksService`** (`nav-stacks`) — a shared store of stack contents keyed by layer index. Items are pushed and popped via `<ToNavStack>` (most consumers never call the service directly).
|
|
31
|
+
- **`<NavStack>`** — renders one visual stack: the current item, the previous-item header, the spring animations between stack states, and the back-swipe gesture.
|
|
32
|
+
- **`<ToNavStack>`** — declarative push. Mounting one adds an item to the stack at the given layer; unmounting removes it.
|
|
33
|
+
|
|
34
|
+
A typical app renders one `<NavStack>` per layer index (often just one for the base layer, with modal/overlay layers as needed) and uses `<ToNavStack>` from inside each route's template to declare the route's content as a stack item.
|
|
35
|
+
|
|
36
|
+
## Two patterns: ad-hoc components vs. routed pages
|
|
37
|
+
|
|
38
|
+
### 1. Ad-hoc `<NavStack>` + `<ToNavStack>` directly
|
|
39
|
+
|
|
40
|
+
Use this when the stack content isn't tied to routes — e.g. a modal flow inside a single page, a multi-step form.
|
|
41
|
+
|
|
42
|
+
```hbs
|
|
43
|
+
{{!-- app/templates/application.hbs --}}
|
|
44
|
+
<div style="width:320px;height:480px;position:relative">
|
|
45
|
+
<NavStack @layer={{0}} @back={{this.handleBack}} />
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<ToNavStack
|
|
49
|
+
@layer={{0}}
|
|
50
|
+
@item={{component "my-first-step" model=this.model}}
|
|
51
|
+
@header={{component "my-first-step/header" model=this.model}}
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
{{#if this.isAtSecondStep}}
|
|
55
|
+
<ToNavStack
|
|
56
|
+
@layer={{0}}
|
|
57
|
+
@item={{component "my-second-step" model=this.model}}
|
|
58
|
+
@header={{component "my-second-step/header" model=this.model}}
|
|
59
|
+
/>
|
|
60
|
+
{{/if}}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Each `<ToNavStack>` describes one stack item — its body (`@item`) and its header (`@header`) as curried components. Mounting more `<ToNavStack>` instances pushes them onto the stack in mount order; unmounting them pops.
|
|
64
|
+
|
|
65
|
+
### 2. `StackableRoute` for route-driven stacks
|
|
66
|
+
|
|
67
|
+
When your stack maps to routes (one stack item per route in a parent → child → grandchild URL), extend `StackableRoute` and rely on the convention:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// app/routes/page.js
|
|
71
|
+
import StackableRoute from 'ember-nav-stack/routes/stackable-route';
|
|
72
|
+
|
|
73
|
+
export default class PageRoute extends StackableRoute {
|
|
74
|
+
// Optionally:
|
|
75
|
+
// templateName — the route's template name; defaults to 'stackable'
|
|
76
|
+
// which auto-renders <ToNavStack> for this route
|
|
77
|
+
// newLayer = true — this route is on a new visual layer (e.g. a modal
|
|
78
|
+
// stack above the base navigation)
|
|
79
|
+
// routableTemplateName — override the routing-component name (see below)
|
|
80
|
+
}
|
|
20
81
|
```
|
|
21
82
|
|
|
83
|
+
`StackableRoute` does three things for you:
|
|
84
|
+
|
|
85
|
+
1. **`templateName = 'stackable'`** by default. The shipped `stackable` template renders `<ToNavStack @layer={{this.layerIndex}} @item={{component this.routeComponent ...}} @header={{component this.headerComponent ...}} />` plus `{{outlet}}`, so you don't write that boilerplate per route.
|
|
86
|
+
2. **Computes `layerIndex`** by walking up the route tree (via the public `RouterService.currentRoute` / `activeTransition.to` chain). Routes that opt into a new layer with `newLayer = true` get parent's `layerIndex + 1`.
|
|
87
|
+
3. **Provides a `back` action** that transitions to the parent route. Wire it to your back button via `{{on "click" (route-action "back")}}` (or your preferred action-delivery mechanism).
|
|
88
|
+
|
|
89
|
+
### The `routable-components/...` convention
|
|
90
|
+
|
|
91
|
+
`StackableRoute` derives the component names for `@item` and `@header` from the route's name:
|
|
22
92
|
|
|
23
|
-
|
|
24
|
-
|
|
93
|
+
| Route name | `routeComponent` (item) | `headerComponent` |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `page` | `routable-components/page` | `routable-components/page/header` |
|
|
96
|
+
| `yapp.page.schedule-item` | `routable-components/yapp/page/schedule-item` | `routable-components/yapp/page/schedule-item/header` |
|
|
25
97
|
|
|
26
|
-
|
|
98
|
+
So a route called `page` resolves to component `routable-components/page` (i.e. `app/components/routable-components/page.{js,hbs}` in classic layout, or whatever your resolver does for that name). Headers live at `<route-component-name>/header`.
|
|
27
99
|
|
|
28
|
-
|
|
29
|
-
------------------------------------------------------------------------------
|
|
100
|
+
Override per-route via `routableTemplateName = 'something-else'` if a single component should back multiple routes.
|
|
30
101
|
|
|
31
|
-
|
|
102
|
+
## `<NavStack>` arguments
|
|
32
103
|
|
|
104
|
+
| Arg | Required | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `@layer` | yes | Integer ≥ 0. Identifies which layer this NavStack renders. Items pushed via `<ToNavStack @layer={{n}}>` appear here when `n === @layer`. |
|
|
107
|
+
| `@back` | no | Callback invoked when a back-swipe completes successfully. Typically wired via `{{route-action "back"}}` when using `StackableRoute`. Without this, the back-swipe gesture is disabled. |
|
|
108
|
+
| `@footer` | no | A curried component to render below the stack (e.g. a tab bar). Often only set on layer 0. |
|
|
109
|
+
| `@birdsEyeDebugging` | no | If truthy, applies an `.is-birdsEyeDebugging` class for a debug view that shows all layers at once. |
|
|
110
|
+
| `@extractComponentKey` | no | Override how `NavStack` derives a stable identity for a stack item's component (used to detect "did the root component change?" → cut vs. slide). The default reads the curried component's resolved name + bound `model.id`. Most apps don't need to override. |
|
|
111
|
+
| `@onActiveItemChange` | no | Callback invoked whenever the top of the stack changes identity (push, pop, root swap, initial render). Receives `{ isInitialRender, previousDepth, currentDepth, previousTopKey, currentTopKey }`, where the `*TopKey` values are the top stack item's component name. Use this — not subclassing — to react to navigation (e.g. moving keyboard focus to the newly active heading, recording analytics page views). |
|
|
112
|
+
|
|
113
|
+
## `<ToNavStack>` arguments
|
|
114
|
+
|
|
115
|
+
| Arg | Required | Description |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `@layer` | yes | Integer ≥ 0. Which `<NavStack>` should receive this item. |
|
|
118
|
+
| `@item` | yes | A curried component (`{{component 'my-page' model=this.model}}`) rendered as the stack item's body. |
|
|
119
|
+
| `@header` | no | A curried component rendered in the stack item's header slot. Receives `@model`, `@controller`, and `@back` as arguments when the parent `<NavStack>` has them. |
|
|
120
|
+
|
|
121
|
+
## Animation behavior
|
|
122
|
+
|
|
123
|
+
`NavStack` chooses one of these transitions automatically based on observed stack state:
|
|
124
|
+
|
|
125
|
+
| Situation | Animation |
|
|
126
|
+
|---|---|
|
|
127
|
+
| First render of the stack | `cut` (no animation, snap into place) |
|
|
128
|
+
| Layer > 0 appearing from empty | `slideUp` (slides up from below) |
|
|
129
|
+
| Layer > 0 disappearing to empty | `slideDown` (slides down off-screen) |
|
|
130
|
+
| Same root, deeper push | `slideForward` (new page slides in from the right) |
|
|
131
|
+
| Same root, shallower pop | `slideBack` (current page slides off to the right) |
|
|
132
|
+
| Root component changed | `cut` (instant swap — used for tab switches) |
|
|
133
|
+
|
|
134
|
+
The decision is made by `decideTransition` (`src/utils/transition-decision.js`), which you can unit-test in isolation if you're forking or debugging. See its source comment for the full truth table.
|
|
135
|
+
|
|
136
|
+
## Disabling animation (e.g. for tests)
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
// config/environment.js
|
|
140
|
+
ENV['ember-nav-stack'] = { suppressAnimation: true };
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
When set, `<NavStack>` skips its springs and snaps directly to the post-transition position. Useful in test environments that don't need to wait on rAF-driven animations.
|
|
144
|
+
|
|
145
|
+
## Test waiters
|
|
146
|
+
|
|
147
|
+
`ember-nav-stack` registers waiters for stack recomputes, transition animations, and the initial render so Ember's built-in test helpers (`visit`, `click`, `settled`, etc.) wait correctly. **You should not need custom "wait for nav stack idle" helpers or manual `waitFor` calls** when navigation is your only async source.
|
|
148
|
+
|
|
149
|
+
If you do need a programmatic hook:
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { getOwner } from '@ember/application';
|
|
153
|
+
|
|
154
|
+
let navStacks = getOwner(this).lookup('service:nav-stacks');
|
|
155
|
+
await navStacks.waitUntilTransitionIdle();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## CSS classes you can target
|
|
159
|
+
|
|
160
|
+
The addon owns these class names. Consumers can style them (or query them in tests) but should not rely on internal structure:
|
|
161
|
+
|
|
162
|
+
- `.NavStack` — the stack container element
|
|
163
|
+
- `.NavStack--layer0`, `.NavStack--layer1`, … — added per layer index
|
|
164
|
+
- `.NavStack--withFooter` — added when `@footer` is set
|
|
165
|
+
- `.NavStack-item`, `.NavStack-item-0`, `.NavStack-item-1`, … — per-item containers
|
|
166
|
+
- `.NavStack-itemContainer` — the horizontally-translated container that holds items
|
|
167
|
+
- `.NavStack-header` — wrapper for the current + parent header
|
|
168
|
+
- `.NavStack-currentHeaderContainer`, `.NavStack-parentItemHeaderContainer` — the two live header slots
|
|
169
|
+
- `.NavStack-footer` — wrapper for `@footer`
|
|
170
|
+
|
|
171
|
+
`.NavStack-gestureBackTargetHeader` may briefly appear during a completed back-swipe; it's an overlay clone that gets cleaned up automatically.
|
|
172
|
+
|
|
173
|
+
## Test support
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
import { isInViewport, getElementInViewportRatio } from 'ember-nav-stack/test-support';
|
|
177
|
+
```
|
|
33
178
|
|
|
34
|
-
|
|
35
|
-
------------------------------------------------------------------------------
|
|
179
|
+
Two helpers for asserting on the visible position of stack items in tests. See `src/test-support/in-viewport.js` for details.
|
|
36
180
|
|
|
37
|
-
|
|
181
|
+
## Contributing
|
|
38
182
|
|
|
183
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development notes.
|
|
39
184
|
|
|
40
|
-
License
|
|
41
|
-
------------------------------------------------------------------------------
|
|
185
|
+
## License
|
|
42
186
|
|
|
43
187
|
This project is licensed under the [MIT License](LICENSE.md).
|
package/addon-main.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/components/nav-stack-inner-wrapper";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/components/nav-stack";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/components/to-nav-stack";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/helpers/nav-layer-indices";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/modifiers/back-swipe";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/services/gesture";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/services/nav-stacks";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "ember-nav-stack/templates/stackable";
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import Hammer from 'hammerjs';
|
|
2
|
+
import { run } from '@ember/runloop';
|
|
3
|
+
import { Spring } from 'wobble';
|
|
4
|
+
import { macroCondition, isTesting } from '@embroider/macros';
|
|
5
|
+
import BackSwipeRecognizer from './utils/back-swipe-recognizer.js';
|
|
6
|
+
import { setTransform } from './utils/animation.js';
|
|
7
|
+
import { currentTransitionPercentage, styleHeaderElements, HEADER_PARALLAX_OFFSET } from './utils/header-style.js';
|
|
8
|
+
|
|
9
|
+
// Spring configuration for the swipe-driven horizontal animation. Tuned to
|
|
10
|
+
// match the programmatic spring used by `NavStack#horizontalTransition`.
|
|
11
|
+
const SWIPE_SPRING_CONFIG = {
|
|
12
|
+
stiffness: 1000,
|
|
13
|
+
damping: 500,
|
|
14
|
+
mass: 3
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Encapsulates the back-swipe gesture state and behavior. NavStack
|
|
18
|
+
// instantiates one of these in its `did-insert` callback and tears it down
|
|
19
|
+
// on `will-destroy`. The gesture owns:
|
|
20
|
+
//
|
|
21
|
+
// - The Hammer.js manager + BackSwipeRecognizer
|
|
22
|
+
// - The pan handler context (element refs, startingX, backX, thresholdX)
|
|
23
|
+
// - The swipe-driven Spring (cancellation, onUpdate, onStop)
|
|
24
|
+
// - The target-header overlay snapshot used to bridge the re-render gap
|
|
25
|
+
// after a completed back-swipe (see PR #79 for the bug fix)
|
|
26
|
+
// - Registration with the gesture service for cross-component recognizer
|
|
27
|
+
// ordering (preferRecognizer / stopPreferringRecognizer)
|
|
28
|
+
//
|
|
29
|
+
// NavStack communicates with the gesture via three callbacks supplied at
|
|
30
|
+
// construction time:
|
|
31
|
+
//
|
|
32
|
+
// getCanNavigateBack() → boolean
|
|
33
|
+
// Returns whether back navigation is currently allowed (typically
|
|
34
|
+
// `stackDepth > 1` and `args.back` defined). Re-evaluated on every
|
|
35
|
+
// `setupContext()` call so the gesture stays in sync with stack changes.
|
|
36
|
+
//
|
|
37
|
+
// onBack()
|
|
38
|
+
// Invoked when a back-swipe completes successfully. Wraps the
|
|
39
|
+
// consumer's `@back` callback.
|
|
40
|
+
//
|
|
41
|
+
// onBackSwipeOverlay(clone)
|
|
42
|
+
// Invoked with the cloned target-header element immediately before
|
|
43
|
+
// `onBack`. NavStack stores the clone so its `handleStackDepthChange`
|
|
44
|
+
// can schedule cleanup once the stack pop actually lands (see the
|
|
45
|
+
// gesture-back overlay machinery in PR #79).
|
|
46
|
+
//
|
|
47
|
+
// NavStack drives the gesture's pan-recognizer state during programmatic
|
|
48
|
+
// transitions via `disablePan()` (called from `transitionDidBegin`) and
|
|
49
|
+
// `setupContext()` (called from `transitionDidEnd` to re-grab DOM refs and
|
|
50
|
+
// re-enable the recognizer once the animation completes).
|
|
51
|
+
class BackSwipeGesture {
|
|
52
|
+
constructor({
|
|
53
|
+
element,
|
|
54
|
+
navStacksService,
|
|
55
|
+
gestureService,
|
|
56
|
+
getCanNavigateBack,
|
|
57
|
+
onBack,
|
|
58
|
+
onBackSwipeOverlay
|
|
59
|
+
}) {
|
|
60
|
+
this.element = element;
|
|
61
|
+
this.navStacksService = navStacksService;
|
|
62
|
+
this.gestureService = gestureService;
|
|
63
|
+
this.getCanNavigateBack = getCanNavigateBack;
|
|
64
|
+
this.onBack = onBack;
|
|
65
|
+
this.onBackSwipeOverlay = onBackSwipeOverlay;
|
|
66
|
+
this.hammer = new Hammer.Manager(element, {
|
|
67
|
+
inputClass: Hammer.TouchMouseInput,
|
|
68
|
+
recognizers: [[BackSwipeRecognizer]]
|
|
69
|
+
});
|
|
70
|
+
this.setupContext();
|
|
71
|
+
this.hammer.on('pan', this._handlePanEvent);
|
|
72
|
+
this.gestureService.register(this, this.hammer.get('pan'));
|
|
73
|
+
}
|
|
74
|
+
teardown() {
|
|
75
|
+
if (this.hammer) {
|
|
76
|
+
this.gestureService.unregister(this, this.hammer.get('pan'));
|
|
77
|
+
this.hammer.off('pan');
|
|
78
|
+
this.hammer.destroy();
|
|
79
|
+
this.hammer = null;
|
|
80
|
+
}
|
|
81
|
+
// If a swipe completed and produced an overlay but the consumer hasn't
|
|
82
|
+
// claimed it via `onBackSwipeOverlay` yet (or we're being destroyed in
|
|
83
|
+
// mid-stride), drop it so it doesn't leak into the DOM.
|
|
84
|
+
if (this._unclaimedOverlay?.parentNode) {
|
|
85
|
+
this._unclaimedOverlay.parentNode.removeChild(this._unclaimedOverlay);
|
|
86
|
+
}
|
|
87
|
+
this._unclaimedOverlay = null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Called by NavStack when a programmatic transition starts. Disables the
|
|
91
|
+
// pan recognizer so a new swipe can't start while the animation is
|
|
92
|
+
// running. A swipe-driven spring that's already in flight is left to
|
|
93
|
+
// complete on its own — the programmatic transition's spring tracks its
|
|
94
|
+
// active spring separately (NavStack#_horizontalSpring) and cancels any
|
|
95
|
+
// prior horizontal spring before starting.
|
|
96
|
+
disablePan() {
|
|
97
|
+
this.hammer?.get('pan').set({
|
|
98
|
+
enable: false
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Called by NavStack after a programmatic transition completes (and also
|
|
103
|
+
// by the constructor for initial wiring). Re-grabs DOM refs (item width
|
|
104
|
+
// may have changed), recomputes startingX/backX/thresholdX, and
|
|
105
|
+
// re-enables the recognizer.
|
|
106
|
+
setupContext() {
|
|
107
|
+
this.containerElement = this.element.querySelector('.NavStack-itemContainer');
|
|
108
|
+
this.currentHeaderElement = this.element.querySelector('.NavStack-currentHeaderContainer');
|
|
109
|
+
this.parentHeaderElement = this.element.querySelector('.NavStack-parentItemHeaderContainer');
|
|
110
|
+
this.startingX = this._getX(this.containerElement);
|
|
111
|
+
let currentStackItemElement = this.element.querySelector('.NavStack-item:last-child');
|
|
112
|
+
if (!currentStackItemElement) {
|
|
113
|
+
this.canNavigateBack = false;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let itemWidth = currentStackItemElement.getBoundingClientRect().width;
|
|
117
|
+
this.backX = this.startingX + itemWidth;
|
|
118
|
+
this.thresholdX = itemWidth / 2;
|
|
119
|
+
this.canNavigateBack = this.getCanNavigateBack();
|
|
120
|
+
this.hammer?.get('pan').set({
|
|
121
|
+
enable: true,
|
|
122
|
+
threshold: 9
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Called by the gesture service when another gesture-aware component
|
|
127
|
+
// registers — we must require its pan recognizer to fail before ours can
|
|
128
|
+
// succeed (or vice versa). Matches the public surface the service
|
|
129
|
+
// expects on registered objects.
|
|
130
|
+
preferRecognizer(recognizer) {
|
|
131
|
+
this.hammer?.get('pan').requireFailure(recognizer);
|
|
132
|
+
}
|
|
133
|
+
stopPreferringRecognizer(recognizer) {
|
|
134
|
+
this.hammer?.get('pan').dropRequireFailure(recognizer);
|
|
135
|
+
}
|
|
136
|
+
_handlePanEvent = ev => {
|
|
137
|
+
if (this._activeSpring) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Clamp the gesture's effective displacement at 0 so it can never travel
|
|
141
|
+
// left of its starting position. Without this, sliding the finger past
|
|
142
|
+
// origin produces a negative `deltaX`, and `currentTransitionPercentage`
|
|
143
|
+
// — which takes Math.abs of the delta — interprets it as forward
|
|
144
|
+
// progress: parent header fades in and current header fades out as if a
|
|
145
|
+
// forward transition were under way, the opposite of the back-swipe the
|
|
146
|
+
// user actually started. Clamping makes the gesture latch at origin
|
|
147
|
+
// until the finger crosses back rightward.
|
|
148
|
+
let effectiveDeltaX = Math.max(0, ev.deltaX);
|
|
149
|
+
setTransform(this.containerElement, `translateX(${this.startingX + effectiveDeltaX}px)`);
|
|
150
|
+
let ratio = currentTransitionPercentage(this.startingX, this.backX, this.startingX + effectiveDeltaX);
|
|
151
|
+
styleHeaderElements(ratio, true, this.currentHeaderElement, this.parentHeaderElement);
|
|
152
|
+
if (this.currentHeaderElement) {
|
|
153
|
+
this.currentHeaderElement.style.opacity = ratio;
|
|
154
|
+
}
|
|
155
|
+
if (this.parentHeaderElement) {
|
|
156
|
+
this.parentHeaderElement.style.opacity = 1 - ratio;
|
|
157
|
+
}
|
|
158
|
+
if (ev.isFinal) {
|
|
159
|
+
this._handlePanEnd(ev);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
_handlePanEnd(ev) {
|
|
163
|
+
let effectiveDeltaX = Math.max(0, ev.deltaX);
|
|
164
|
+
let shouldNavigateBack = this._adjustX(ev.center.x) >= this.thresholdX && this.canNavigateBack;
|
|
165
|
+
let initialVelocity = ev.velocityX;
|
|
166
|
+
let fromValue = this.startingX + effectiveDeltaX;
|
|
167
|
+
let toValue = shouldNavigateBack ? this.backX : this.startingX;
|
|
168
|
+
this.navStacksService.notifyTransitionStart();
|
|
169
|
+
let finalize = () => {
|
|
170
|
+
if (shouldNavigateBack) {
|
|
171
|
+
// The cross-fade ended with parentHeaderElement (target page header)
|
|
172
|
+
// visible. onBack() will eventually re-render the live header
|
|
173
|
+
// containers, swapping content: currentHeaderContainer becomes the
|
|
174
|
+
// target page's header. Snapshot the visible target header into an
|
|
175
|
+
// overlay clone so it stays on screen across that re-render.
|
|
176
|
+
// Cleanup is owned by the parent NavStack and runs once the stack
|
|
177
|
+
// pop actually lands (see PR #79 for the full lifecycle).
|
|
178
|
+
let overlay = this._cloneTargetHeaderOverlay();
|
|
179
|
+
if (this.currentHeaderElement) {
|
|
180
|
+
this.currentHeaderElement.style.opacity = 0;
|
|
181
|
+
setTransform(this.currentHeaderElement, `translateX(${HEADER_PARALLAX_OFFSET}px)`);
|
|
182
|
+
}
|
|
183
|
+
if (this.parentHeaderElement) {
|
|
184
|
+
this.parentHeaderElement.style.opacity = 0;
|
|
185
|
+
setTransform(this.parentHeaderElement, `translateX(${-HEADER_PARALLAX_OFFSET}px)`);
|
|
186
|
+
}
|
|
187
|
+
this.navStacksService.notifyTransitionEnd();
|
|
188
|
+
this._activeSpring = null;
|
|
189
|
+
this._unclaimedOverlay = overlay;
|
|
190
|
+
this.onBackSwipeOverlay?.(overlay);
|
|
191
|
+
this._unclaimedOverlay = null;
|
|
192
|
+
run(this.onBack);
|
|
193
|
+
} else {
|
|
194
|
+
setTransform(this.containerElement, `translateX(${this.startingX}px)`);
|
|
195
|
+
if (this.currentHeaderElement) {
|
|
196
|
+
this.currentHeaderElement.style.opacity = 1;
|
|
197
|
+
setTransform(this.currentHeaderElement, 'translateX(0px)');
|
|
198
|
+
}
|
|
199
|
+
if (this.parentHeaderElement) {
|
|
200
|
+
this.parentHeaderElement.style.opacity = 0;
|
|
201
|
+
setTransform(this.parentHeaderElement, `translateX(${-HEADER_PARALLAX_OFFSET}px)`);
|
|
202
|
+
}
|
|
203
|
+
this.navStacksService.notifyTransitionEnd();
|
|
204
|
+
this._activeSpring = null;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
if (fromValue === toValue && initialVelocity === 0) {
|
|
208
|
+
finalize();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
let spring = new Spring({
|
|
212
|
+
initialVelocity,
|
|
213
|
+
fromValue,
|
|
214
|
+
toValue,
|
|
215
|
+
...SWIPE_SPRING_CONFIG
|
|
216
|
+
});
|
|
217
|
+
this._activeSpring = spring;
|
|
218
|
+
spring.onUpdate(s => {
|
|
219
|
+
setTransform(this.containerElement, `translateX(${s.currentValue}px)`);
|
|
220
|
+
styleHeaderElements(currentTransitionPercentage(this.startingX, this.backX, s.currentValue), false, this.parentHeaderElement, this.currentHeaderElement);
|
|
221
|
+
// If the spring's momentum carries it past the back-threshold after
|
|
222
|
+
// the user released below the threshold, retarget to backX so the
|
|
223
|
+
// animation completes the back-swipe rather than snapping back.
|
|
224
|
+
if (!shouldNavigateBack && s.currentValue >= this.startingX + this.thresholdX) {
|
|
225
|
+
shouldNavigateBack = true;
|
|
226
|
+
spring.updateConfig({
|
|
227
|
+
toValue: this.backX
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}).onStop(() => finalize()).start();
|
|
231
|
+
}
|
|
232
|
+
_cloneTargetHeaderOverlay() {
|
|
233
|
+
let liveParent = this.element.querySelector('.NavStack-parentItemHeaderContainer');
|
|
234
|
+
if (!liveParent) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
let clone = liveParent.cloneNode(true);
|
|
238
|
+
clone.classList.remove('NavStack-parentItemHeaderContainer');
|
|
239
|
+
clone.classList.add('NavStack-gestureBackTargetHeader');
|
|
240
|
+
clone.style.opacity = '1';
|
|
241
|
+
setTransform(clone, 'translateX(0px)');
|
|
242
|
+
// Append last so it paints on top of currentHeaderContainer.
|
|
243
|
+
this.element.querySelector('.NavStack-header').appendChild(clone);
|
|
244
|
+
return clone;
|
|
245
|
+
}
|
|
246
|
+
_getX(element) {
|
|
247
|
+
return this._adjustX(element.getBoundingClientRect().left);
|
|
248
|
+
}
|
|
249
|
+
_adjustX(x) {
|
|
250
|
+
if (macroCondition(isTesting())) {
|
|
251
|
+
let testingEl = document.querySelector('#ember-testing');
|
|
252
|
+
if (testingEl) {
|
|
253
|
+
return x - testingEl.getBoundingClientRect().left;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return x;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export { BackSwipeGesture as default };
|
|
261
|
+
//# sourceMappingURL=back-swipe-gesture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"back-swipe-gesture.js","sources":["../src/back-swipe-gesture.js"],"sourcesContent":["import Hammer from 'hammerjs';\nimport { run } from '@ember/runloop';\nimport { Spring } from 'wobble';\nimport { macroCondition, isTesting } from '@embroider/macros';\nimport BackSwipeRecognizer from './utils/back-swipe-recognizer.js';\nimport { setTransform } from './utils/animation.js';\nimport {\n HEADER_PARALLAX_OFFSET,\n currentTransitionPercentage,\n styleHeaderElements,\n} from './utils/header-style.js';\n\n// Spring configuration for the swipe-driven horizontal animation. Tuned to\n// match the programmatic spring used by `NavStack#horizontalTransition`.\nconst SWIPE_SPRING_CONFIG = {\n stiffness: 1000,\n damping: 500,\n mass: 3,\n};\n\n// Encapsulates the back-swipe gesture state and behavior. NavStack\n// instantiates one of these in its `did-insert` callback and tears it down\n// on `will-destroy`. The gesture owns:\n//\n// - The Hammer.js manager + BackSwipeRecognizer\n// - The pan handler context (element refs, startingX, backX, thresholdX)\n// - The swipe-driven Spring (cancellation, onUpdate, onStop)\n// - The target-header overlay snapshot used to bridge the re-render gap\n// after a completed back-swipe (see PR #79 for the bug fix)\n// - Registration with the gesture service for cross-component recognizer\n// ordering (preferRecognizer / stopPreferringRecognizer)\n//\n// NavStack communicates with the gesture via three callbacks supplied at\n// construction time:\n//\n// getCanNavigateBack() → boolean\n// Returns whether back navigation is currently allowed (typically\n// `stackDepth > 1` and `args.back` defined). Re-evaluated on every\n// `setupContext()` call so the gesture stays in sync with stack changes.\n//\n// onBack()\n// Invoked when a back-swipe completes successfully. Wraps the\n// consumer's `@back` callback.\n//\n// onBackSwipeOverlay(clone)\n// Invoked with the cloned target-header element immediately before\n// `onBack`. NavStack stores the clone so its `handleStackDepthChange`\n// can schedule cleanup once the stack pop actually lands (see the\n// gesture-back overlay machinery in PR #79).\n//\n// NavStack drives the gesture's pan-recognizer state during programmatic\n// transitions via `disablePan()` (called from `transitionDidBegin`) and\n// `setupContext()` (called from `transitionDidEnd` to re-grab DOM refs and\n// re-enable the recognizer once the animation completes).\nexport default class BackSwipeGesture {\n constructor({\n element,\n navStacksService,\n gestureService,\n getCanNavigateBack,\n onBack,\n onBackSwipeOverlay,\n }) {\n this.element = element;\n this.navStacksService = navStacksService;\n this.gestureService = gestureService;\n this.getCanNavigateBack = getCanNavigateBack;\n this.onBack = onBack;\n this.onBackSwipeOverlay = onBackSwipeOverlay;\n\n this.hammer = new Hammer.Manager(element, {\n inputClass: Hammer.TouchMouseInput,\n recognizers: [[BackSwipeRecognizer]],\n });\n this.setupContext();\n this.hammer.on('pan', this._handlePanEvent);\n this.gestureService.register(this, this.hammer.get('pan'));\n }\n\n teardown() {\n if (this.hammer) {\n this.gestureService.unregister(this, this.hammer.get('pan'));\n this.hammer.off('pan');\n this.hammer.destroy();\n this.hammer = null;\n }\n // If a swipe completed and produced an overlay but the consumer hasn't\n // claimed it via `onBackSwipeOverlay` yet (or we're being destroyed in\n // mid-stride), drop it so it doesn't leak into the DOM.\n if (this._unclaimedOverlay?.parentNode) {\n this._unclaimedOverlay.parentNode.removeChild(this._unclaimedOverlay);\n }\n this._unclaimedOverlay = null;\n }\n\n // Called by NavStack when a programmatic transition starts. Disables the\n // pan recognizer so a new swipe can't start while the animation is\n // running. A swipe-driven spring that's already in flight is left to\n // complete on its own — the programmatic transition's spring tracks its\n // active spring separately (NavStack#_horizontalSpring) and cancels any\n // prior horizontal spring before starting.\n disablePan() {\n this.hammer?.get('pan').set({ enable: false });\n }\n\n // Called by NavStack after a programmatic transition completes (and also\n // by the constructor for initial wiring). Re-grabs DOM refs (item width\n // may have changed), recomputes startingX/backX/thresholdX, and\n // re-enables the recognizer.\n setupContext() {\n this.containerElement = this.element.querySelector(\n '.NavStack-itemContainer',\n );\n this.currentHeaderElement = this.element.querySelector(\n '.NavStack-currentHeaderContainer',\n );\n this.parentHeaderElement = this.element.querySelector(\n '.NavStack-parentItemHeaderContainer',\n );\n this.startingX = this._getX(this.containerElement);\n let currentStackItemElement = this.element.querySelector(\n '.NavStack-item:last-child',\n );\n if (!currentStackItemElement) {\n this.canNavigateBack = false;\n return;\n }\n let itemWidth = currentStackItemElement.getBoundingClientRect().width;\n this.backX = this.startingX + itemWidth;\n this.thresholdX = itemWidth / 2;\n this.canNavigateBack = this.getCanNavigateBack();\n this.hammer?.get('pan').set({ enable: true, threshold: 9 });\n }\n\n // Called by the gesture service when another gesture-aware component\n // registers — we must require its pan recognizer to fail before ours can\n // succeed (or vice versa). Matches the public surface the service\n // expects on registered objects.\n preferRecognizer(recognizer) {\n this.hammer?.get('pan').requireFailure(recognizer);\n }\n\n stopPreferringRecognizer(recognizer) {\n this.hammer?.get('pan').dropRequireFailure(recognizer);\n }\n\n _handlePanEvent = (ev) => {\n if (this._activeSpring) {\n return;\n }\n // Clamp the gesture's effective displacement at 0 so it can never travel\n // left of its starting position. Without this, sliding the finger past\n // origin produces a negative `deltaX`, and `currentTransitionPercentage`\n // — which takes Math.abs of the delta — interprets it as forward\n // progress: parent header fades in and current header fades out as if a\n // forward transition were under way, the opposite of the back-swipe the\n // user actually started. Clamping makes the gesture latch at origin\n // until the finger crosses back rightward.\n let effectiveDeltaX = Math.max(0, ev.deltaX);\n setTransform(\n this.containerElement,\n `translateX(${this.startingX + effectiveDeltaX}px)`,\n );\n let ratio = currentTransitionPercentage(\n this.startingX,\n this.backX,\n this.startingX + effectiveDeltaX,\n );\n styleHeaderElements(\n ratio,\n true,\n this.currentHeaderElement,\n this.parentHeaderElement,\n );\n if (this.currentHeaderElement) {\n this.currentHeaderElement.style.opacity = ratio;\n }\n if (this.parentHeaderElement) {\n this.parentHeaderElement.style.opacity = 1 - ratio;\n }\n if (ev.isFinal) {\n this._handlePanEnd(ev);\n }\n };\n\n _handlePanEnd(ev) {\n let effectiveDeltaX = Math.max(0, ev.deltaX);\n let shouldNavigateBack =\n this._adjustX(ev.center.x) >= this.thresholdX && this.canNavigateBack;\n let initialVelocity = ev.velocityX;\n let fromValue = this.startingX + effectiveDeltaX;\n let toValue = shouldNavigateBack ? this.backX : this.startingX;\n this.navStacksService.notifyTransitionStart();\n\n let finalize = () => {\n if (shouldNavigateBack) {\n // The cross-fade ended with parentHeaderElement (target page header)\n // visible. onBack() will eventually re-render the live header\n // containers, swapping content: currentHeaderContainer becomes the\n // target page's header. Snapshot the visible target header into an\n // overlay clone so it stays on screen across that re-render.\n // Cleanup is owned by the parent NavStack and runs once the stack\n // pop actually lands (see PR #79 for the full lifecycle).\n let overlay = this._cloneTargetHeaderOverlay();\n if (this.currentHeaderElement) {\n this.currentHeaderElement.style.opacity = 0;\n setTransform(\n this.currentHeaderElement,\n `translateX(${HEADER_PARALLAX_OFFSET}px)`,\n );\n }\n if (this.parentHeaderElement) {\n this.parentHeaderElement.style.opacity = 0;\n setTransform(\n this.parentHeaderElement,\n `translateX(${-HEADER_PARALLAX_OFFSET}px)`,\n );\n }\n this.navStacksService.notifyTransitionEnd();\n this._activeSpring = null;\n this._unclaimedOverlay = overlay;\n this.onBackSwipeOverlay?.(overlay);\n this._unclaimedOverlay = null;\n run(this.onBack);\n } else {\n setTransform(this.containerElement, `translateX(${this.startingX}px)`);\n if (this.currentHeaderElement) {\n this.currentHeaderElement.style.opacity = 1;\n setTransform(this.currentHeaderElement, 'translateX(0px)');\n }\n if (this.parentHeaderElement) {\n this.parentHeaderElement.style.opacity = 0;\n setTransform(\n this.parentHeaderElement,\n `translateX(${-HEADER_PARALLAX_OFFSET}px)`,\n );\n }\n this.navStacksService.notifyTransitionEnd();\n this._activeSpring = null;\n }\n };\n\n if (fromValue === toValue && initialVelocity === 0) {\n finalize();\n return;\n }\n let spring = new Spring({\n initialVelocity,\n fromValue,\n toValue,\n ...SWIPE_SPRING_CONFIG,\n });\n this._activeSpring = spring;\n spring\n .onUpdate((s) => {\n setTransform(this.containerElement, `translateX(${s.currentValue}px)`);\n styleHeaderElements(\n currentTransitionPercentage(\n this.startingX,\n this.backX,\n s.currentValue,\n ),\n false,\n this.parentHeaderElement,\n this.currentHeaderElement,\n );\n // If the spring's momentum carries it past the back-threshold after\n // the user released below the threshold, retarget to backX so the\n // animation completes the back-swipe rather than snapping back.\n if (\n !shouldNavigateBack &&\n s.currentValue >= this.startingX + this.thresholdX\n ) {\n shouldNavigateBack = true;\n spring.updateConfig({ toValue: this.backX });\n }\n })\n .onStop(() => finalize())\n .start();\n }\n\n _cloneTargetHeaderOverlay() {\n let liveParent = this.element.querySelector(\n '.NavStack-parentItemHeaderContainer',\n );\n if (!liveParent) {\n return null;\n }\n let clone = liveParent.cloneNode(true);\n clone.classList.remove('NavStack-parentItemHeaderContainer');\n clone.classList.add('NavStack-gestureBackTargetHeader');\n clone.style.opacity = '1';\n setTransform(clone, 'translateX(0px)');\n // Append last so it paints on top of currentHeaderContainer.\n this.element.querySelector('.NavStack-header').appendChild(clone);\n return clone;\n }\n\n _getX(element) {\n return this._adjustX(element.getBoundingClientRect().left);\n }\n\n _adjustX(x) {\n if (macroCondition(isTesting())) {\n let testingEl = document.querySelector('#ember-testing');\n if (testingEl) {\n return x - testingEl.getBoundingClientRect().left;\n }\n }\n return x;\n }\n}\n"],"names":["SWIPE_SPRING_CONFIG","stiffness","damping","mass","BackSwipeGesture","constructor","element","navStacksService","gestureService","getCanNavigateBack","onBack","onBackSwipeOverlay","hammer","Hammer","Manager","inputClass","TouchMouseInput","recognizers","BackSwipeRecognizer","setupContext","on","_handlePanEvent","register","get","teardown","unregister","off","destroy","_unclaimedOverlay","parentNode","removeChild","disablePan","set","enable","containerElement","querySelector","currentHeaderElement","parentHeaderElement","startingX","_getX","currentStackItemElement","canNavigateBack","itemWidth","getBoundingClientRect","width","backX","thresholdX","threshold","preferRecognizer","recognizer","requireFailure","stopPreferringRecognizer","dropRequireFailure","ev","_activeSpring","effectiveDeltaX","Math","max","deltaX","setTransform","ratio","currentTransitionPercentage","styleHeaderElements","style","opacity","isFinal","_handlePanEnd","shouldNavigateBack","_adjustX","center","x","initialVelocity","velocityX","fromValue","toValue","notifyTransitionStart","finalize","overlay","_cloneTargetHeaderOverlay","HEADER_PARALLAX_OFFSET","notifyTransitionEnd","run","spring","Spring","onUpdate","s","currentValue","updateConfig","onStop","start","liveParent","clone","cloneNode","classList","remove","add","appendChild","left","macroCondition","isTesting","testingEl","document"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import templateOnly from '@ember/component/template-only';
|
|
2
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
3
|
+
import { setComponentTemplate } from '@ember/component';
|
|
4
|
+
|
|
5
|
+
var TEMPLATE = precompileTemplate("{{#if @class}}\n <div class={{concat \"NavStack-innerWrapper \" @class}}>\n {{yield}}\n </div>\n{{else}}\n {{yield}}\n{{/if}}");
|
|
6
|
+
|
|
7
|
+
var navStackInnerWrapper = setComponentTemplate(TEMPLATE, templateOnly());
|
|
8
|
+
|
|
9
|
+
export { navStackInnerWrapper as default };
|
|
10
|
+
//# sourceMappingURL=nav-stack-inner-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-stack-inner-wrapper.js","sources":["../../src/components/nav-stack-inner-wrapper.js"],"sourcesContent":["import templateOnly from '@ember/component/template-only';\n\n// Private template-only helper. Wraps its block in a\n// `<div class=\"NavStack-innerWrapper [class]\">` when `@class` is truthy, or\n// passes the block through unwrapped when it's falsy. This is the structural\n// mechanism behind `<NavStack @innerWrapperClass>`: omitting the arg leaves\n// the addon's DOM identical to pre-v7.1 (so consumer CSS using direct-child\n// selectors on `.NavStack` continues to match), while providing it inserts\n// the extra wrapper element on demand.\n//\n// Not part of the addon's public API — invoked only from `<NavStack>`'s\n// own template. Subject to change without notice.\nexport default templateOnly();\n"],"names":["setComponentTemplate","TEMPLATE","templateOnly"],"mappings":";;;;;;AAYA,2BAAAA,oBAAA,CAAAC,QAAA,EAAeC,YAAY,EAAE,CAAA;;;;"}
|