ember-nav-stack 6.1.2 → 7.0.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.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.js +627 -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 +107 -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 +78 -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
|
@@ -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
|
-
}
|
package/addon/utils/component.js
DELETED
|
@@ -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';
|
package/app/services/gesture.js
DELETED
|
@@ -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
|
-
}
|
package/app/utils/animation.js
DELETED
|
@@ -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
|
-
};
|
package/config/environment.js
DELETED
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
package/vendor/wobble-shim.js
DELETED