@liquefy/cascade.ui 1.0.0 → 2.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/src/Portal.js DELETED
@@ -1,132 +0,0 @@
1
- import { Component, flush, withoutRecording, frozen } from "@liquefy/cascade.component";
2
- import { wrapper } from "./Layout.js";
3
-
4
- /**
5
- * Portals - ported from flow.ui/basic/src/Portals.js: a place in the tree
6
- * (portal()) whose content is supplied from somewhere else entirely
7
- * (portalContents()) - a page putting its own buttons in the app's top
8
- * bar, say, which the page knows nothing else about.
9
- *
10
- * - portal({ style, children }): a div, showing whatever is currently
11
- * assigned to it - or its own children (a default) when nothing is, or
12
- * nothing but an empty list (an empty cart).
13
- * - portalContents({ portal, children }): builds nothing where it
14
- * stands; while it's shown, its children are shown in `portal` - a
15
- * portal, or the name of one to inherit() ("topBarPortal"), found when
16
- * it builds, so it can be created before it has a place in the tree
17
- * instead, and once it isn't (it's hidden, or its page is switched
18
- * away from), they're taken back out - see Component.onShow()/onHide().
19
- * The last one shown wins.
20
- *
21
- * The same mechanism as OverlayFrame/Overlay (see OverlayFrame.js - an
22
- * overlay frame is a portal with a modal layer): the contents are
23
- * typically rendered *after* the portal (a page's work area comes after
24
- * the top bar), so assigning them is a write backwards in the pipeline -
25
- * setState() puts it at the baseline, where the portal's build reads it,
26
- * and flush() gets it rebuilt in the same wave, so the portal fills in the
27
- * same frame, not the next.
28
- *
29
- * Who holds the portal: something that creates it itself and owns it (not
30
- * one built in some build() and also referenced - see
31
- * cascade.component/README.md), typically providing it for inherit() too,
32
- * so that whatever is rendered below can find it without being handed it.
33
- */
34
- export function portal(...parameters) {
35
- return new Portal(...parameters);
36
- }
37
-
38
- export function portalContents(...parameters) {
39
- return new PortalContents(...parameters);
40
- }
41
-
42
- export class Portal extends Component {
43
- setProperties({ style, children }) {
44
- this.style = frozen(style || null);
45
- this.defaultContents = frozen(children || []);
46
- }
47
-
48
- // What some PortalContents has assigned is state: changed only by
49
- // show()/hide(), never reset by a rebuild.
50
- initializeState() {
51
- return { contents: null };
52
- }
53
-
54
- initialUnobservables() {
55
- return { assignedBy: null };
56
- }
57
-
58
- // Called from a PortalContents render: what is shown now is read
59
- // without recording, so that render never depends on it - two contents
60
- // taking turns would otherwise keep rerunning each other.
61
- show(contentsProvider, contents) {
62
- this.unobservable.assignedBy = contentsProvider;
63
- const current = withoutRecording(() => this.contents);
64
- if (current !== contents) flush(() => this.setState({ contents }));
65
- }
66
-
67
- // Only the PortalContents that assigned what's shown can take it back -
68
- // one that has since been taken over by another doesn't clear its
69
- // successor's.
70
- hide(contentsProvider) {
71
- const u = this.unobservable;
72
- if (u.assignedBy !== contentsProvider) return;
73
- u.assignedBy = null;
74
- flush(() => this.setState({ contents: null }));
75
- }
76
-
77
- build() {
78
- const assigned = this.contents && this.contents.length > 0;
79
- return wrapper({ key: "portal", style: this.style || {} }, assigned ? this.contents : this.defaultContents);
80
- }
81
- }
82
-
83
- export class PortalContents extends Component {
84
- setProperties({ portal, children }) {
85
- this.portal = portal || null;
86
- this.portalChildren = frozen(children || []);
87
- }
88
-
89
- initialUnobservables() {
90
- return { shownIn: null };
91
- }
92
-
93
- // Build-only - so it's also placed like any other component by one that
94
- // places its subtree itself (cascade.DOM's FlipAnimationContainer), where
95
- // it expands to nothing. A build only runs while something is showing
96
- // this component (rendering or expanding it pulls it), so building is
97
- // where the contents are assigned - again whenever they change.
98
- build() {
99
- this.assign();
100
- return null;
101
- }
102
-
103
- // Shown again (see Component.onShow()): an up-to-date build doesn't
104
- // rerun, so assign the contents here. Read without recording - this runs
105
- // inside whoever is rendering.
106
- onShow() {
107
- withoutRecording(() => this.assign());
108
- }
109
-
110
- // No longer shown (hidden, its page switched away from, dropped): take
111
- // the contents back.
112
- onHide() {
113
- const u = this.unobservable;
114
- if (u.shownIn) {
115
- u.shownIn.hide(this);
116
- u.shownIn = null;
117
- }
118
- }
119
-
120
- assign() {
121
- const u = this.unobservable;
122
- const portal = typeof(this.portal) === "string" ? this.inherit(this.portal) : this.portal;
123
- if (u.shownIn && u.shownIn !== portal) {
124
- u.shownIn.hide(this);
125
- u.shownIn = null;
126
- }
127
- if (portal) {
128
- portal.show(this, this.portalChildren);
129
- u.shownIn = portal;
130
- }
131
- }
132
- }