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.
Files changed (79) hide show
  1. package/README.md +165 -21
  2. package/addon-main.cjs +4 -0
  3. package/dist/_app_/components/nav-stack-inner-wrapper.js +1 -0
  4. package/dist/_app_/components/nav-stack.js +1 -0
  5. package/dist/_app_/components/to-nav-stack.js +1 -0
  6. package/dist/_app_/helpers/nav-layer-indices.js +1 -0
  7. package/dist/_app_/modifiers/back-swipe.js +1 -0
  8. package/dist/_app_/services/gesture.js +1 -0
  9. package/dist/_app_/services/nav-stacks.js +1 -0
  10. package/dist/_app_/templates/stackable.js +1 -0
  11. package/dist/back-swipe-gesture.js +261 -0
  12. package/dist/back-swipe-gesture.js.map +1 -0
  13. package/dist/components/nav-stack-inner-wrapper.js +10 -0
  14. package/dist/components/nav-stack-inner-wrapper.js.map +1 -0
  15. package/dist/components/nav-stack.js +700 -0
  16. package/dist/components/nav-stack.js.map +1 -0
  17. package/dist/components/to-nav-stack.js +22 -0
  18. package/dist/components/to-nav-stack.js.map +1 -0
  19. package/dist/helpers/nav-layer-indices.js +21 -0
  20. package/dist/helpers/nav-layer-indices.js.map +1 -0
  21. package/dist/index.js +7 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/modifiers/back-swipe.js +40 -0
  24. package/dist/modifiers/back-swipe.js.map +1 -0
  25. package/dist/routes/stackable-route.js +99 -0
  26. package/dist/routes/stackable-route.js.map +1 -0
  27. package/{addon → dist}/services/gesture.js +7 -9
  28. package/dist/services/gesture.js.map +1 -0
  29. package/dist/services/nav-stacks.js +137 -0
  30. package/dist/services/nav-stacks.js.map +1 -0
  31. package/dist/styles/nav-stack.css +399 -0
  32. package/dist/templates/stackable.js +8 -0
  33. package/dist/templates/stackable.js.map +1 -0
  34. package/{addon-test-support → dist/test-support}/in-viewport.js +7 -10
  35. package/dist/test-support/in-viewport.js.map +1 -0
  36. package/dist/test-support/index.js +2 -0
  37. package/dist/test-support/index.js.map +1 -0
  38. package/{addon → dist}/utils/animation.js +17 -40
  39. package/dist/utils/animation.js.map +1 -0
  40. package/{addon → dist}/utils/back-swipe-recognizer.js +29 -49
  41. package/dist/utils/back-swipe-recognizer.js.map +1 -0
  42. package/dist/utils/clone-store.js +88 -0
  43. package/dist/utils/clone-store.js.map +1 -0
  44. package/dist/utils/component.js +121 -0
  45. package/dist/utils/component.js.map +1 -0
  46. package/dist/utils/header-style.js +46 -0
  47. package/dist/utils/header-style.js.map +1 -0
  48. package/dist/utils/transition-decision.js +71 -0
  49. package/dist/utils/transition-decision.js.map +1 -0
  50. package/dist/utils/waiter-state.js +130 -0
  51. package/dist/utils/waiter-state.js.map +1 -0
  52. package/package.json +79 -91
  53. package/.vscode/settings.json +0 -2
  54. package/CHANGELOG.md +0 -208
  55. package/MODULE_REPORT.md +0 -27
  56. package/RELEASE.md +0 -54
  57. package/addon/components/nav-stack/component.js +0 -690
  58. package/addon/components/nav-stack/template.hbs +0 -37
  59. package/addon/components/to-nav-stack.js +0 -32
  60. package/addon/helpers/nav-layer-indices.js +0 -29
  61. package/addon/routes/stackable-route.js +0 -61
  62. package/addon/services/nav-stacks.js +0 -157
  63. package/addon/utils/component.js +0 -40
  64. package/app/components/nav-stack/component.js +0 -1
  65. package/app/components/nav-stack/template.js +0 -1
  66. package/app/components/to-nav-stack.js +0 -1
  67. package/app/helpers/nav-layer-indices.js +0 -1
  68. package/app/services/gesture.js +0 -1
  69. package/app/services/nav-stacks.js +0 -1
  70. package/app/styles/nav-stack.scss +0 -117
  71. package/app/templates/stackable.hbs +0 -8
  72. package/app/utils/animation.js +0 -1
  73. package/config/deploy.js +0 -29
  74. package/config/environment.js +0 -5
  75. package/config/release.js +0 -21
  76. package/docs/ember-nav-stack-waiters-plan.md +0 -125
  77. package/index.js +0 -15
  78. package/tsconfig.json +0 -6
  79. package/vendor/wobble-shim.js +0 -3
@@ -1,32 +0,0 @@
1
- import { guidFor } from '@ember/object/internals';
2
- import Component from '@glimmer/component';
3
- // import { argument } from '@ember-decorators/argument';
4
- import { inject as service } from '@ember/service';
5
-
6
- export default class ToNavStack extends Component {
7
- // @argument('number')
8
- // layer;
9
-
10
- // @argument('any')
11
- // item = null;
12
-
13
- // @argument('any')
14
- // header = null;
15
-
16
- @service('nav-stacks') service;
17
-
18
- constructor() {
19
- super(...arguments);
20
- this.service.pushItem(
21
- guidFor(this),
22
- this.args.layer,
23
- this.args.item,
24
- this.args.header,
25
- );
26
- }
27
-
28
- willDestroy() {
29
- super.willDestroy(...arguments);
30
- this.service.removeItem(guidFor(this));
31
- }
32
- }
@@ -1,29 +0,0 @@
1
- /* eslint-disable ember/no-computed-properties-in-native-classes */
2
- /* eslint-disable ember/no-observers */
3
- import Helper from '@ember/component/helper';
4
- import { computed } from '@ember/object';
5
- import { observes } from '@ember-decorators/object';
6
- import { inject as service } from '@ember/service';
7
- export default class NavLayerIndices extends Helper {
8
- @service
9
- navStacks;
10
-
11
- compute() {
12
- let layerCount = this.layerCount;
13
- let indices = [];
14
- for (let i = 0; i < layerCount; i++) {
15
- indices.push(i);
16
- }
17
- return indices;
18
- }
19
-
20
- @computed('navStacks.stacks')
21
- get layerCount() {
22
- return Object.keys(this.navStacks.stacks).length;
23
- }
24
-
25
- @observes('layerCount')
26
- navStacksChanged() {
27
- this.recompute();
28
- }
29
- }
@@ -1,61 +0,0 @@
1
- import Route from '@ember/routing/route';
2
- import { action } from '@ember/object';
3
- import { inject as service } from '@ember/service';
4
-
5
- export function getParentRoute(router, route) {
6
- // eslint-disable-next-line ember/no-private-routing-service
7
- let routerMicroLib = router._routerMicrolib;
8
- let { routeInfos, handlerInfos } = routerMicroLib.state;
9
- routeInfos = routeInfos || handlerInfos; // routeInfos is in newer Ember versions
10
- if (!routeInfos) {
11
- return;
12
- }
13
- let routes = routeInfos.map((hi) => hi._handler || hi._route);
14
- let routeIndex = routes.indexOf(route);
15
- if (routeIndex > 0) {
16
- return routes[routes.indexOf(route) - 1];
17
- }
18
- }
19
-
20
- export default class StackableRoute extends Route {
21
- @service router;
22
- templateName = 'stackable';
23
-
24
- getRouteComponent(/* model */) {
25
- return `routable-components/${(
26
- this.routableTemplateName || this.routeName
27
- ).replace(/\./g, '/')}`;
28
- }
29
-
30
- getHeaderComponent(model) {
31
- return `${this.getRouteComponent(model)}/header`;
32
- }
33
-
34
- get layerIndex() {
35
- let parentRoute = getParentRoute(this._router, this);
36
- let parentRouteLayerIndex = parentRoute.get('layerIndex');
37
- let currentLayerIndex = parentRouteLayerIndex || 0;
38
- if (this.newLayer === true) {
39
- return currentLayerIndex + 1;
40
- }
41
- return currentLayerIndex;
42
- }
43
-
44
- setupController(controller, model) {
45
- super.setupController(controller, model);
46
- controller.setProperties({
47
- layerIndex: this.layerIndex,
48
- routeComponent: this.getRouteComponent(model),
49
- headerComponent: this.getHeaderComponent(model),
50
- routeName: this.routeName,
51
- });
52
- }
53
-
54
- getParentRouteName() {
55
- return getParentRoute(this._router, this).routeName;
56
- }
57
-
58
- @action back() {
59
- this.router.transitionTo(this.getParentRouteName());
60
- }
61
- }
@@ -1,157 +0,0 @@
1
- import { A } from '@ember/array';
2
- import Service from '@ember/service';
3
- import { next, scheduleOnce } from '@ember/runloop';
4
- import EmberObject from '@ember/object';
5
- import { Promise as EmberPromise } from 'rsvp';
6
- import { buildWaiter } from '@ember/test-waiters';
7
- import { set } from '@ember/object';
8
- let transitionWaiter = buildWaiter('ember-nav-stack:transition');
9
- let stackWaiter = buildWaiter('ember-nav-stack:stack-update');
10
-
11
- export default class NavStacks extends Service {
12
- transitionToken;
13
- _stackUpdateToken;
14
- _initialRenderToken;
15
-
16
- constructor() {
17
- super(...arguments);
18
- set(this, 'stacks', EmberObject.create());
19
- this._listeners = A([]);
20
- this._itemsById = {};
21
- this._counter = 1;
22
- this._runningTransitions = 0;
23
- this.isInitialRender = true;
24
- this._initialRenderToken = stackWaiter.beginAsync();
25
- }
26
-
27
- pushItem(sourceId, layer, component, headerComponent) {
28
- this._itemsById[sourceId] = {
29
- layer,
30
- component,
31
- headerComponent,
32
- order: this._counter++,
33
- };
34
- this._schedule();
35
- }
36
-
37
- removeItem(sourceId) {
38
- delete this._itemsById[sourceId];
39
- this._schedule();
40
- }
41
-
42
- register(layerContainerComponent) {
43
- this._listeners.pushObject(layerContainerComponent);
44
- }
45
-
46
- unregister(layerContainerComponent) {
47
- this._listeners.removeObject(layerContainerComponent);
48
- }
49
-
50
- notifyTransitionStart() {
51
- this._runningTransitions++;
52
- if (this._runningTransitions === 1) {
53
- this.transitionToken = transitionWaiter.beginAsync();
54
- }
55
- }
56
-
57
- notifyTransitionEnd() {
58
- this._runningTransitions--;
59
- if (this._runningTransitions < 0) {
60
- this._runningTransitions = 0;
61
- }
62
- if (this._runningTransitions === 0) {
63
- if (this.transitionToken) {
64
- transitionWaiter.endAsync(this.transitionToken);
65
- }
66
- this.transitionToken = undefined;
67
- }
68
- next(() => {
69
- this._maybeResolveIdle();
70
- });
71
- }
72
-
73
- runningTransitions() {
74
- return this._runningTransitions;
75
- }
76
-
77
- isRunningTransitions() {
78
- return this._runningTransitions > 0;
79
- }
80
-
81
- waitUntilTransitionIdle() {
82
- if (this._waitingPromise) {
83
- return this._waitingPromise;
84
- }
85
- return (this._waitingPromise = new EmberPromise((resolve) => {
86
- this._resolveWaiting = resolve;
87
- next(() => {
88
- this._maybeResolveIdle();
89
- });
90
- }));
91
- }
92
-
93
- didUpdate() {} // hook
94
-
95
- _maybeResolveIdle() {
96
- if (
97
- this._runningTransitions === 0 &&
98
- !this._stackUpdateToken &&
99
- !this._initialRenderToken &&
100
- this._resolveWaiting
101
- ) {
102
- let resolveWaiting = this._resolveWaiting;
103
- this._resolveWaiting = null;
104
- this._waitingPromise = null;
105
- resolveWaiting();
106
- }
107
- }
108
-
109
- _schedule() {
110
- if (!this._stackUpdateToken) {
111
- this._stackUpdateToken = stackWaiter.beginAsync();
112
- }
113
- scheduleOnce('afterRender', this, this._process);
114
- }
115
-
116
- _process() {
117
- let newStacks = {};
118
- let itemsById = this._itemsById;
119
- let wasInitialRender = this.isInitialRender === true;
120
-
121
- for (var sourceId in itemsById) {
122
- let { layer, component, headerComponent, order } = itemsById[sourceId];
123
- let layerName = `layer${layer}`;
124
- newStacks[layerName] = newStacks[layerName] || A();
125
- let newItem = component ? { component, headerComponent, order } : null;
126
-
127
- newStacks[layerName].push(newItem);
128
- }
129
- for (var layerName in newStacks) {
130
- newStacks[layerName] = newStacks[layerName].sortBy('order');
131
- }
132
- set(this, 'stacks', EmberObject.create(newStacks));
133
- if (this.isInitialRender === true) {
134
- next(this, this._clearIsInitialRender);
135
- }
136
- this._listeners.invoke('stackItemsDidChange');
137
- this.didUpdate();
138
- next(() => {
139
- if (wasInitialRender && this._initialRenderToken) {
140
- stackWaiter.endAsync(this._initialRenderToken);
141
- this._initialRenderToken = undefined;
142
- }
143
- if (this._stackUpdateToken) {
144
- stackWaiter.endAsync(this._stackUpdateToken);
145
- this._stackUpdateToken = undefined;
146
- }
147
- this._maybeResolveIdle();
148
- });
149
- }
150
-
151
- _clearIsInitialRender() {
152
- if (this.isDestroyed || this.isDestroying) {
153
- return;
154
- }
155
- set(this, 'isInitialRender', false);
156
- }
157
- }
@@ -1,40 +0,0 @@
1
- export function extractComponentKey(componentRef) {
2
- if (!componentRef) {
3
- return 'none';
4
- }
5
- let result = getComponentRefName(componentRef);
6
- let modelId = getComponentRefModelId(componentRef);
7
- if (modelId) {
8
- result += `:${modelId}`;
9
- }
10
- return result;
11
- }
12
-
13
- function getComponentRefName(componentRef) {
14
- let componentRefInner =
15
- componentRef.inner?.name ||
16
- componentRef[
17
- Object.getOwnPropertySymbols(componentRef).find(
18
- (s) => s.description === 'INNER',
19
- )
20
- ];
21
- let result = componentRef.name || componentRefInner?.name;
22
- return result;
23
- }
24
-
25
- function getComponentRefModelId(componentRef) {
26
- let componentRefArgs =
27
- componentRef.args ||
28
- componentRef[
29
- Object.getOwnPropertySymbols(componentRef).find(
30
- (s) => s.description === 'ARGS',
31
- )
32
- ];
33
- if (componentRefArgs.named.has && componentRefArgs.named.has('model')) {
34
- let model = componentRefArgs.named.get('model').value();
35
- if (model) {
36
- return model.id;
37
- }
38
- }
39
- return;
40
- }
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/components/nav-stack/component';
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/components/nav-stack/template';
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/components/to-nav-stack';
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/helpers/nav-layer-indices';
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/services/gesture';
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/services/nav-stacks';
@@ -1,117 +0,0 @@
1
- :root {
2
- --nav-stack-header-height: 44px;
3
- --nav-stack-footer-height: 50px;
4
- }
5
-
6
- $easing: cubic-bezier(.23, 1, .32, 1);
7
-
8
- .NavStack {
9
- position: absolute;
10
- left: 0px;
11
- top: 0;
12
- width: 100%;
13
- height: 100%;
14
- will-change: transform;
15
- overflow: hidden;
16
-
17
- &--layer0 {
18
- transform: translateY(0);
19
- z-index: 1;
20
- }
21
- &--layer1 {
22
- transform: translateY(100%);
23
- z-index: 2;
24
- }
25
- &--layer2 {
26
- transform: translateY(200%);
27
- z-index: 3;
28
- }
29
- &-itemContainer {
30
- position: absolute;
31
- top: var(--nav-stack-header-height);
32
- bottom: 0;
33
- left: 0;
34
- width: 500%;
35
- }
36
-
37
- &--withFooter &-itemContainer {
38
- bottom: var(--nav-stack-footer-height);
39
- }
40
-
41
- &-item {
42
- position: absolute;
43
- width: 20%;
44
- top: 0;
45
- bottom: 0;
46
- box-sizing: border-box;
47
- overflow:hidden;
48
-
49
- @for $i from 0 through 100 {
50
- &-#{$i} { left: 20% * $i; }
51
- }
52
- }
53
-
54
- &-header {
55
- position: absolute;
56
- height: var(--nav-stack-header-height);
57
- box-sizing: content-box;
58
- top: 0;
59
- left: 0;
60
- right: 0;
61
- background: rgba(0,0,0,.2);
62
- }
63
-
64
- &-headerContainer {
65
- position: relative;
66
- }
67
-
68
- &-headerComponent, &-headerContainerComponent {
69
- position: absolute;
70
- top: 0;
71
- bottom: 0;
72
- left: 0;
73
- right: 0;
74
- }
75
-
76
- &-headerContainerComponent {
77
- will-change: opacity transform;
78
- }
79
-
80
- &-parentItemHeaderContainer {
81
- opacity: 0;
82
- }
83
-
84
- &-footer {
85
- position: absolute;
86
- bottom: 0;
87
- height: var(--nav-stack-footer-height);
88
- box-sizing: content-box;
89
- left: 0;
90
- right: 0;
91
- background: rgba(0,0,0,.2);
92
- }
93
-
94
- &.is-birdsEyeDebugging {
95
- $item-width: 320px;
96
- $item-height: 480px;
97
- width: $item-width;
98
- height: $item-height;
99
-
100
- &.NavStack--layer1 {
101
- transform: translateY($item-height);
102
- }
103
-
104
- &.NavStack--layer2 {
105
- transform: translateY($item-height * 2);
106
- }
107
-
108
- .NavStack-itemContainer {
109
- left: 0px;
110
- width: $item-width * 5;
111
- }
112
-
113
- .NavStack-item {
114
- border: 1px dashed blue;
115
- }
116
- }
117
- }
@@ -1,8 +0,0 @@
1
- <ToNavStack
2
- @layer={{this.layerIndex}}
3
- @item={{component this.routeComponent model=this.model controller=this}}
4
- @header={{component this.headerComponent model=this.model controller=this}}
5
- />
6
-
7
-
8
- {{outlet}}
@@ -1 +0,0 @@
1
- export { default } from 'ember-nav-stack/utils/animation';
package/config/deploy.js DELETED
@@ -1,29 +0,0 @@
1
- /* eslint-env node */
2
- 'use strict';
3
-
4
- module.exports = function (deployTarget) {
5
- let ENV = {
6
- build: {},
7
- // include other plugin configuration that applies to all deploy targets here
8
- };
9
-
10
- if (deployTarget === 'development') {
11
- ENV.build.environment = 'development';
12
- // configure other plugins for development deploy target here
13
- }
14
-
15
- if (deployTarget === 'staging') {
16
- ENV.build.environment = 'production';
17
- // configure other plugins for staging deploy target here
18
- }
19
-
20
- if (deployTarget === 'production') {
21
- ENV.build.environment = 'production';
22
- // configure other plugins for production deploy target here
23
- }
24
-
25
- // Note: if you need to build some configuration asynchronously, you can return
26
- // a promise that resolves with the ENV object instead of returning the
27
- // ENV object synchronously.
28
- return ENV;
29
- };
@@ -1,5 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = function (/* environment, appConfig */) {
4
- return {};
5
- };
package/config/release.js DELETED
@@ -1,21 +0,0 @@
1
- /* jshint node:true */
2
- // var RSVP = require('rsvp');
3
-
4
- // For details on each option run `ember help release`
5
- module.exports = {
6
- // local: true,
7
- // remote: 'some_remote',
8
- // annotation: "Release %@",
9
- // message: "Bumped version to %@",
10
- // manifest: [ 'package.json', 'bower.json', 'someconfig.json' ],
11
- // publish: true,
12
- // strategy: 'date',
13
- // format: 'YYYY-MM-DD',
14
- // timezone: 'America/Los_Angeles',
15
- //
16
- // beforeCommit: function(project, versions) {
17
- // return new RSVP.Promise(function(resolve, reject) {
18
- // // Do custom things here...
19
- // });
20
- // }
21
- };
@@ -1,125 +0,0 @@
1
- # Ember Nav Stack – Robust Test Waiters Plan
2
-
3
- ## Goal
4
-
5
- - Make `ember-nav-stack`’s waiter coverage fully robust so `@ember/test-helpers` built-in waiting (from `visit`, `click`, etc.) is sufficient. Downstream apps should not need custom helpers like `waitForNavStackIdle` nor ad hoc `waitFor`/`waitUntil` for nav stabilization.
6
-
7
- ## Background
8
-
9
- - Current service file: `ember-nav-stack/addon/services/nav-stacks.js`.
10
- - Today it creates a single waiter (`buildWaiter('ember-nav-stack:transition-waiter')`) and brackets only animation transitions via `notifyTransitionStart`/`notifyTransitionEnd`.
11
- - Pushing/removing items drives updates through `scheduleOnce('afterRender', this, this._process)`, which recomputes `stacks`, notifies listeners, and toggles `isInitialRender`.
12
- - Gaps:
13
- - No waiter covers the coalesced “stack recompute + listener render” cycle triggered by `pushItem`/`removeItem`.
14
- - No waiter covers the very first materialization (“initial render”) of the stack DOM.
15
- - Consequence in consumers: tests inject extra waits (custom helpers and repeated `waitFor` calls) to stabilize views after navigation.
16
- - Ember test semantics: `visit`/`click` already await “settled” (routing + runloop + registered waiters). If nav-stack marks all relevant async spans, tests can assert immediately after these helpers.
17
-
18
- ## Design Overview
19
-
20
- - Introduce a second waiter to cover stack recompute + render: “stack-update waiter”.
21
- - Keep a dedicated “transition waiter” for CSS/animation-driven transitions (rename for clarity).
22
- - Add a one-time “initial render” waiter so tests don’t race the first DOM materialization.
23
- - End the stack waiter only after the listener-rendered DOM is visible (i.e., after `afterRender` and one `next()` tick).
24
- - Maintain aggregated semantics:
25
- - Transitions: first start begins the waiter; last end closes it.
26
- - Stack updates: coalesced updates begin once and end once per `_process` batch.
27
-
28
- ## Detailed Changes (Service)
29
-
30
- - File: `addon/services/nav-stacks.js`
31
-
32
- ### Waiters and Tokens
33
-
34
- - Module scope:
35
- - `let transitionWaiter = buildWaiter('ember-nav-stack:transition');`
36
- - `let stackWaiter = buildWaiter('ember-nav-stack:stack-update');`
37
- - Class state:
38
- - `transitionToken` (replaces generic `waiterToken`).
39
- - `_stackUpdateToken` (undefined when no coalesced update is pending).
40
- - `_initialRenderToken` (undefined after first materialization completes).
41
- - `_runningTransitions` counter (kept as-is).
42
-
43
- ### Initial Render Coverage
44
-
45
- - In `constructor()`:
46
- - `this._initialRenderToken = stackWaiter.beginAsync();`
47
- - Keep `this.isInitialRender = true` as today.
48
- - In `_process()` (after recomputing `stacks` and notifying listeners):
49
- - Schedule `next(() => { if (this.isInitialRender && this._initialRenderToken) { stackWaiter.endAsync(this._initialRenderToken); this._initialRenderToken = undefined; } this._maybeResolveIdle(); })`.
50
- - Keep the existing `next(this, this._clearIsInitialRender)` behavior.
51
-
52
- ### Stack Recompute + Render Coverage
53
-
54
- - In `_schedule()`:
55
- - Keep `scheduleOnce('afterRender', this, this._process)`.
56
- - Begin the stack-update waiter when scheduling a batch (if not already begun):
57
- - `if (!this._stackUpdateToken) { this._stackUpdateToken = stackWaiter.beginAsync(); }`
58
- - In `_process()` (after setting stacks and notifying listeners):
59
- - End the coalesced batch on the next tick to ensure DOM is fully flushed:
60
- - `next(() => { if (this._stackUpdateToken) { stackWaiter.endAsync(this._stackUpdateToken); this._stackUpdateToken = undefined; } this._maybeResolveIdle(); })`
61
-
62
- ### Transition Waiter Semantics
63
-
64
- - `notifyTransitionStart()`:
65
- - `if (++this._runningTransitions === 1) { this.transitionToken = transitionWaiter.beginAsync(); }`
66
- - `notifyTransitionEnd()`:
67
- - `if (--this._runningTransitions === 0) { transitionWaiter.endAsync(this.transitionToken); this.transitionToken = undefined; }`
68
- - `next(() => this._maybeResolveIdle());`
69
- - Optional: guard negative counts defensively if a caller misorders starts/ends.
70
-
71
- ## Why This Works
72
-
73
- - `@ember/test-helpers` waits for registered waiters between async helpers. With transitions and stack recompute wrapped, `visit`/`click` will naturally pause until:
74
- - Routing settles.
75
- - Data sources settle (handled by consumers’ waiters like Orbit).
76
- - Nav-stack animations settle (transition waiter).
77
- - Nav-stack recompute + listener render fully flush (stack-update waiter).
78
- - Tests can assert immediately after `await visit()` / `await click()` with no additional `settled()`/`waitFor`.
79
-
80
- ## Test Plan (Within ember-nav-stack)
81
-
82
- - Initial render waiter:
83
- - Mount a listener that renders an item. Assert `hasPendingWaiters()` is true until first flush completes.
84
- - Coalesced stack updates:
85
- - Call `pushItem` twice in one runloop; assert one `_process` and a single waiter span; no pending waiters after the `next` tick.
86
- - Transition waiter behavior:
87
- - Call `notifyTransitionStart()` n times, then `notifyTransitionEnd()` n times; assert waiter opens on first and closes on last.
88
- - Black-box flow:
89
- - Simulate a click that causes a stack push; after `await click`, assert DOM reflects new item without extra waits.
90
-
91
- ## Consumer Implications (e.g., yapp-core)
92
-
93
- - After upgrading ember-nav-stack:
94
- - Remove custom `waitForNavStackIdle` helper usages.
95
- - Remove redundant `waitFor`/`waitUntil` after navigation actions.
96
- - Continue relying on existing data-source waiters (e.g., Orbit) and ensure other async (e.g., translations) is wrapped by test-waiters (outside nav-stack scope).
97
-
98
- ## Performance & Production Impact
99
-
100
- - Waiter tokens are opened only on meaningful work (transition start, coalesced stack update, initial render) and coalesce naturally.
101
- - `@ember/test-waiters` strips most behavior in production builds, so runtime impact remains negligible in prod.
102
-
103
- ## Risks & Edge Cases
104
-
105
- - Ensure all transitions reliably call `notifyTransitionStart/End` (including cancellation/unmount paths).
106
- - Third-party animations not integrated with nav-stack are not covered; prefer routing/transitions to go through nav-stack or add integration points.
107
- - If a listener performs DOM-affecting async outside Ember’s runloop, either bring it into the runloop or instrument it with a narrow waiter in that component.
108
-
109
- ## Deliverables Checklist
110
-
111
- - [ ] Add `transitionWaiter` and `stackWaiter` in `addon/services/nav-stacks.js`.
112
- - [ ] Add `transitionToken`, `_stackUpdateToken`, `_initialRenderToken` fields.
113
- - [ ] Begin `_initialRenderToken` in `constructor`.
114
- - [ ] Begin `_stackUpdateToken` in `_schedule` (if absent).
115
- - [ ] End `_stackUpdateToken` and `_initialRenderToken` inside `next()` in `_process`.
116
- - [ ] Keep transition waiter around `notifyTransitionStart/End` (rename field for clarity).
117
- - [ ] Add unit/integration tests for the behaviors above.
118
- - [ ] README/Docs note: “With robust waiters, `visit`/`click` do not require extra waits.”
119
-
120
- ## Acceptance Criteria
121
-
122
- - In a consumer app: after `await visit()` or `await click()`, nav-stack content is immediately assertable without extra waits.
123
- - Reduction in test flakiness associated with navigation and stack updates.
124
- - No regressions in production behavior.
125
-
package/index.js DELETED
@@ -1,15 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- name: require('./package').name,
5
- options: {
6
- autoImport: {
7
- exclude: ['wobble'],
8
- },
9
- },
10
- included(app) {
11
- this._super.included.apply(this, arguments);
12
- app.import('vendor/wobble-shim.js');
13
- app.import('node_modules/wobble/dist/wobble.browser.js');
14
- },
15
- };
package/tsconfig.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "experimentalDecorators": true,
4
- "allowJs": true
5
- }
6
- }
@@ -1,3 +0,0 @@
1
- define('wobble', function() {
2
- return Wobble;
3
- });