mobx-mantle 0.6.1 → 0.7.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/dist/index.cjs CHANGED
@@ -60,6 +60,9 @@ function withInjectionScope(resolve, construct) {
60
60
  function captureServiceScope(owner) {
61
61
  scopes.set(owner, _nullishCoalesce(constructingScope, () => ( { resolve: globalConfig.resolveService, values: /* @__PURE__ */ new WeakSet() })));
62
62
  }
63
+ function getServiceResolver(owner) {
64
+ return _optionalChain([scopes, 'access', _2 => _2.get, 'call', _3 => _3(owner), 'optionalAccess', _4 => _4.resolve]);
65
+ }
63
66
  function resolveService(owner, token) {
64
67
  return resolveInScope(scopes.get(owner), token);
65
68
  }
@@ -70,7 +73,7 @@ function inject(token) {
70
73
  return resolveInScope(constructingScope, token);
71
74
  }
72
75
  function resolveInScope(scope, token) {
73
- if (!_optionalChain([scope, 'optionalAccess', _2 => _2.resolve])) throw new Error("[mobx-mantle] Service injection requires configure({ resolveService }), a ServiceProvider, or withInjectionScope() during construction.");
76
+ if (!_optionalChain([scope, 'optionalAccess', _5 => _5.resolve])) throw new Error("[mobx-mantle] Service injection requires configure({ resolveService }), a ServiceProvider, or withInjectionScope() during construction.");
74
77
  const value = scope.resolve(token);
75
78
  if ((typeof value !== "object" || value === null) && typeof value !== "function") {
76
79
  throw new Error("[mobx-mantle] Services must be objects or functions; wrap primitive configuration in an object.");
@@ -79,7 +82,7 @@ function resolveInScope(scope, token) {
79
82
  return value;
80
83
  }
81
84
  function isServiceValue(owner, value) {
82
- return _nullishCoalesce(_optionalChain([scopes, 'access', _3 => _3.get, 'call', _4 => _4(owner), 'optionalAccess', _5 => _5.values, 'access', _6 => _6.has, 'call', _7 => _7(value)]), () => ( false));
85
+ return _nullishCoalesce(_optionalChain([scopes, 'access', _6 => _6.get, 'call', _7 => _7(owner), 'optionalAccess', _8 => _8.values, 'access', _9 => _9.has, 'call', _10 => _10(value)]), () => ( false));
83
86
  }
84
87
  function withOwnerServiceScope(owner, construct) {
85
88
  const scope = scopes.get(owner);
@@ -113,7 +116,7 @@ function isModel(model, type) {
113
116
 
114
117
  var useIsomorphicLayoutEffect = typeof window !== "undefined" ? _react.useLayoutEffect : _react.useEffect;
115
118
  var uncommittedReactionRegistry = typeof FinalizationRegistry !== "undefined" ? new FinalizationRegistry((adm) => {
116
- _optionalChain([adm, 'access', _8 => _8.reaction, 'optionalAccess', _9 => _9.dispose, 'call', _10 => _10()]);
119
+ _optionalChain([adm, 'access', _11 => _11.reaction, 'optionalAccess', _12 => _12.dispose, 'call', _13 => _13()]);
117
120
  adm.reaction = null;
118
121
  }) : void 0;
119
122
  if (process.env.NODE_ENV !== "production" && !uncommittedReactionRegistry) {
@@ -122,10 +125,11 @@ if (process.env.NODE_ENV !== "production" && !uncommittedReactionRegistry) {
122
125
  );
123
126
  }
124
127
  var isServer = typeof window === "undefined";
128
+ var trackedComponents = /* @__PURE__ */ new WeakSet();
125
129
  function createReaction(adm) {
126
130
  adm.reaction = new (0, _mobx.Reaction)(`mantle-observer(${adm.name})`, () => {
127
131
  adm.stateVersion = /* @__PURE__ */ Symbol();
128
- _optionalChain([adm, 'access', _11 => _11.onStoreChange, 'optionalCall', _12 => _12()]);
132
+ _optionalChain([adm, 'access', _14 => _14.onStoreChange, 'optionalCall', _15 => _15()]);
129
133
  });
130
134
  if (adm.holder) {
131
135
  adm.holder.current = adm.reaction;
@@ -144,7 +148,7 @@ function useMantleObserver(render, name, holder) {
144
148
  name,
145
149
  holder,
146
150
  subscribe(onStoreChange) {
147
- _optionalChain([uncommittedReactionRegistry, 'optionalAccess', _13 => _13.unregister, 'call', _14 => _14(adm2)]);
151
+ _optionalChain([uncommittedReactionRegistry, 'optionalAccess', _16 => _16.unregister, 'call', _17 => _17(adm2)]);
148
152
  adm2.onStoreChange = onStoreChange;
149
153
  if (!adm2.reaction) {
150
154
  createReaction(adm2);
@@ -153,7 +157,7 @@ function useMantleObserver(render, name, holder) {
153
157
  }
154
158
  return () => {
155
159
  adm2.onStoreChange = null;
156
- _optionalChain([adm2, 'access', _15 => _15.reaction, 'optionalAccess', _16 => _16.dispose, 'call', _17 => _17()]);
160
+ _optionalChain([adm2, 'access', _18 => _18.reaction, 'optionalAccess', _19 => _19.dispose, 'call', _20 => _20()]);
157
161
  adm2.reaction = null;
158
162
  if (adm2.holder) {
159
163
  adm2.holder.current = null;
@@ -170,7 +174,7 @@ function useMantleObserver(render, name, holder) {
170
174
  adm.holder = holder;
171
175
  if (!adm.reaction) {
172
176
  createReaction(adm);
173
- _optionalChain([uncommittedReactionRegistry, 'optionalAccess', _18 => _18.register, 'call', _19 => _19(admRef, adm, adm)]);
177
+ _optionalChain([uncommittedReactionRegistry, 'optionalAccess', _21 => _21.register, 'call', _22 => _22(admRef, adm, adm)]);
174
178
  }
175
179
  _react.useSyncExternalStore.call(void 0, adm.subscribe, adm.getSnapshot, adm.getSnapshot);
176
180
  let result;
@@ -193,7 +197,9 @@ function observer(fc) {
193
197
  const name = fc.displayName || fc.name || "observed";
194
198
  const Observed = (props) => useMantleObserver(() => fc(props), name);
195
199
  Observed.displayName = `observer(${name})`;
196
- return _react.memo.call(void 0, Observed);
200
+ const Memoized = _react.memo.call(void 0, Observed);
201
+ trackedComponents.add(Memoized);
202
+ return Memoized;
197
203
  }
198
204
 
199
205
  // src/decorators.ts
@@ -213,7 +219,7 @@ function annotation(value, kind) {
213
219
  map = key.metadata[ANNOTATIONS];
214
220
  name = key.name;
215
221
  } else {
216
- if (typeof _target === "function" || kind === "method" && typeof _optionalChain([descriptor, 'optionalAccess', _20 => _20.value]) !== "function" || kind === "getter" && !_optionalChain([descriptor, 'optionalAccess', _21 => _21.get]) || kind === "field" && descriptor) {
222
+ if (typeof _target === "function" || kind === "method" && typeof _optionalChain([descriptor, 'optionalAccess', _23 => _23.value]) !== "function" || kind === "getter" && !_optionalChain([descriptor, 'optionalAccess', _24 => _24.get]) || kind === "field" && descriptor) {
217
223
  throw new Error(`[mobx-mantle] This decorator requires a public instance ${kind}.`);
218
224
  }
219
225
  map = _nullishCoalesce(legacy.get(_target), () => ( /* @__PURE__ */ Object.create(null)));
@@ -302,7 +308,7 @@ function registerReactive(host, create) {
302
308
  host._reactiveSpecs.push(spec);
303
309
  return () => {
304
310
  spec.stopped = true;
305
- _optionalChain([spec, 'access', _22 => _22.dispose, 'optionalCall', _23 => _23()]);
311
+ _optionalChain([spec, 'access', _25 => _25.dispose, 'optionalCall', _26 => _26()]);
306
312
  };
307
313
  }
308
314
  function activateSpecs(host) {
@@ -461,7 +467,7 @@ var Behavior = class {
461
467
  * ```
462
468
  */
463
469
  watch(source, callback, options) {
464
- const expr = toWatchExpression(source, _optionalChain([options, 'optionalAccess', _24 => _24.fireImmediately]), this.constructor.name);
470
+ const expr = toWatchExpression(source, _optionalChain([options, 'optionalAccess', _27 => _27.fireImmediately]), this.constructor.name);
465
471
  return registerReactive(this, () => {
466
472
  const dispose = _mobx.reaction.call(void 0,
467
473
  expr,
@@ -473,8 +479,8 @@ var Behavior = class {
473
479
  }
474
480
  },
475
481
  {
476
- delay: _optionalChain([options, 'optionalAccess', _25 => _25.delay]),
477
- fireImmediately: _optionalChain([options, 'optionalAccess', _26 => _26.fireImmediately])
482
+ delay: _optionalChain([options, 'optionalAccess', _28 => _28.delay]),
483
+ fireImmediately: _optionalChain([options, 'optionalAccess', _29 => _29.fireImmediately])
478
484
  }
479
485
  );
480
486
  return this._addCleanup(dispose);
@@ -506,7 +512,7 @@ var Behavior = class {
506
512
  let cleanup;
507
513
  const dispose = _mobx.autorun.call(void 0,
508
514
  () => {
509
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _27 => _27()]));
515
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _30 => _30()]));
510
516
  cleanup = void 0;
511
517
  try {
512
518
  const result = fn();
@@ -517,10 +523,10 @@ var Behavior = class {
517
523
  reportError(e, { phase: "effect", name: this.constructor.name, isBehavior: true });
518
524
  }
519
525
  },
520
- { delay: _optionalChain([options, 'optionalAccess', _28 => _28.delay]) }
526
+ { delay: _optionalChain([options, 'optionalAccess', _31 => _31.delay]) }
521
527
  );
522
528
  return this._addCleanup(() => {
523
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _29 => _29()]));
529
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _32 => _32()]));
524
530
  dispose();
525
531
  });
526
532
  });
@@ -643,7 +649,7 @@ function createBehavior(Def, options) {
643
649
  }
644
650
  }
645
651
  this._pendingSyncs = null;
646
- const autoObservable = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _30 => _30.autoObservable]), () => ( globalConfig.autoObservable));
652
+ const autoObservable = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _33 => _33.autoObservable]), () => ( globalConfig.autoObservable));
647
653
  const annotations = getAnnotations(this);
648
654
  if (annotations) {
649
655
  _mobx.makeObservable.call(void 0, this, annotations);
@@ -706,7 +712,7 @@ function createBehavior(Def, options) {
706
712
  }
707
713
  function isBehavior(value) {
708
714
  if (value === null || typeof value !== "object") return false;
709
- return _optionalChain([value, 'access', _31 => _31.constructor, 'optionalAccess', _32 => _32[BEHAVIOR_MARKER]]) === true;
715
+ return _optionalChain([value, 'access', _34 => _34.constructor, 'optionalAccess', _35 => _35[BEHAVIOR_MARKER]]) === true;
710
716
  }
711
717
  function childBehaviors(inst) {
712
718
  return Array.isArray(inst._behaviors) ? inst._behaviors : [];
@@ -752,8 +758,8 @@ function unmountBehavior(behavior, visited = /* @__PURE__ */ new WeakSet()) {
752
758
  const inst = behavior.instance;
753
759
  if (visited.has(inst)) return;
754
760
  visited.add(inst);
755
- _mobx.runInAction.call(void 0, () => _optionalChain([behavior, 'access', _33 => _33.layoutCleanup, 'optionalCall', _34 => _34()]));
756
- _mobx.runInAction.call(void 0, () => _optionalChain([behavior, 'access', _35 => _35.cleanup, 'optionalCall', _36 => _36()]));
761
+ _mobx.runInAction.call(void 0, () => _optionalChain([behavior, 'access', _36 => _36.layoutCleanup, 'optionalCall', _37 => _37()]));
762
+ _mobx.runInAction.call(void 0, () => _optionalChain([behavior, 'access', _38 => _38.cleanup, 'optionalCall', _39 => _39()]));
757
763
  if ("onUnmount" in inst && typeof inst.onUnmount === "function") {
758
764
  try {
759
765
  _mobx.runInAction.call(void 0, () => inst.onUnmount());
@@ -798,6 +804,18 @@ function warnUncollectedBehaviors(host, hostName, entries, visited = /* @__PURE_
798
804
  var componentRefs = /* @__PURE__ */ new WeakSet();
799
805
  var ComponentParentContext = _react2.default.createContext(void 0);
800
806
  var componentParents = /* @__PURE__ */ new WeakMap();
807
+ var ownerScopes = /* @__PURE__ */ new WeakMap();
808
+ function ownerScope(owner) {
809
+ let scope = ownerScopes.get(owner);
810
+ if (!scope) {
811
+ scope = { handle: { current: owner }, resolver: getServiceResolver(owner), substitutions: void 0 };
812
+ ownerScopes.set(owner, scope);
813
+ }
814
+ return scope;
815
+ }
816
+ function withRef(props, ref) {
817
+ return ref == null ? props : { ...props, ref };
818
+ }
801
819
  var activeComponentConstruction;
802
820
  function constructComponent(ComponentClass, props, parentHandle) {
803
821
  const previousScope = activeComponentConstruction;
@@ -846,7 +864,7 @@ var PropsBox = class {
846
864
  this._atom = _mobx.createAtom.call(void 0, name);
847
865
  }
848
866
  get() {
849
- const own = _optionalChain([this, 'access', _37 => _37._renderReaction, 'optionalAccess', _38 => _38.current]);
867
+ const own = _optionalChain([this, 'access', _40 => _40._renderReaction, 'optionalAccess', _41 => _41.current]);
850
868
  if (!own || _mobx._getGlobalState.call(void 0, ).trackingDerivation !== own) {
851
869
  this._atom.reportObserved();
852
870
  }
@@ -885,7 +903,7 @@ var Component = class {
885
903
  }
886
904
  /** Return the immediate ancestor in the live Mantle view tree. */
887
905
  getParent() {
888
- return _optionalChain([componentParents, 'access', _39 => _39.get, 'call', _40 => _40(this), 'optionalAccess', _41 => _41.current]);
906
+ return _optionalChain([componentParents, 'access', _42 => _42.get, 'call', _43 => _43(this), 'optionalAccess', _44 => _44.current]);
889
907
  }
890
908
  /** Find the nearest parent with the supplied logical model identity. */
891
909
  findParent(type) {
@@ -918,6 +936,34 @@ var Component = class {
918
936
  componentRefs.add(r);
919
937
  return r;
920
938
  }
939
+ /**
940
+ * A tracked view of this instance, for handing to code you don't control
941
+ * (a table's row renderer, an error boundary's fallback, a map popup).
942
+ * Created once, so its identity is stable. Re-renders on its own when the
943
+ * state it reads changes. Forwards refs. Carries this model's context, so
944
+ * `findParent()` and services resolve inside it even when a library renders
945
+ * it in its own React root.
946
+ *
947
+ * @example
948
+ * ```tsx
949
+ * row = this.view((props: RowProps) => (
950
+ * <tr {...props} className={props.id === this.activeId ? 'active' : ''} />
951
+ * ));
952
+ * ```
953
+ */
954
+ view(render) {
955
+ const owner = this;
956
+ const name = `${owner.constructor.name}.${render.name || "view"}`;
957
+ const View = _react.forwardRef.call(void 0, (props, ref) => {
958
+ const scope = ownerScope(owner);
959
+ const rendered = useMantleObserver(() => render(withRef(props, ref)), name);
960
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ComponentParentContext.Provider, { value: scope.handle, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ServiceContext.Provider, { value: scope.resolver, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ModelContext.Provider, { value: scope.substitutions, children: rendered }) }) });
961
+ });
962
+ View.displayName = name;
963
+ const Memoized = _react.memo.call(void 0, View);
964
+ trackedComponents.add(Memoized);
965
+ return Memoized;
966
+ }
921
967
  /**
922
968
  * Register a cleanup function to run automatically on unmount.
923
969
  * Returns a function that can be called for early cleanup.
@@ -979,7 +1025,7 @@ var Component = class {
979
1025
  * ```
980
1026
  */
981
1027
  watch(source, callback, options) {
982
- const expr = toWatchExpression(source, _optionalChain([options, 'optionalAccess', _42 => _42.fireImmediately]), this.constructor.name);
1028
+ const expr = toWatchExpression(source, _optionalChain([options, 'optionalAccess', _45 => _45.fireImmediately]), this.constructor.name);
983
1029
  return registerReactive(this, () => {
984
1030
  const dispose = _mobx.reaction.call(void 0,
985
1031
  expr,
@@ -991,8 +1037,8 @@ var Component = class {
991
1037
  }
992
1038
  },
993
1039
  {
994
- delay: _optionalChain([options, 'optionalAccess', _43 => _43.delay]),
995
- fireImmediately: _optionalChain([options, 'optionalAccess', _44 => _44.fireImmediately])
1040
+ delay: _optionalChain([options, 'optionalAccess', _46 => _46.delay]),
1041
+ fireImmediately: _optionalChain([options, 'optionalAccess', _47 => _47.fireImmediately])
996
1042
  }
997
1043
  );
998
1044
  return this._addCleanup(dispose);
@@ -1037,7 +1083,7 @@ var Component = class {
1037
1083
  let cleanup;
1038
1084
  const dispose = _mobx.autorun.call(void 0,
1039
1085
  () => {
1040
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _45 => _45()]));
1086
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _48 => _48()]));
1041
1087
  cleanup = void 0;
1042
1088
  try {
1043
1089
  const result = fn();
@@ -1048,10 +1094,10 @@ var Component = class {
1048
1094
  reportError(e, { phase: "effect", name: this.constructor.name, isBehavior: false });
1049
1095
  }
1050
1096
  },
1051
- { delay: _optionalChain([options, 'optionalAccess', _46 => _46.delay]) }
1097
+ { delay: _optionalChain([options, 'optionalAccess', _49 => _49.delay]) }
1052
1098
  );
1053
1099
  return this._addCleanup(() => {
1054
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _47 => _47()]));
1100
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _50 => _50()]));
1055
1101
  dispose();
1056
1102
  });
1057
1103
  });
@@ -1108,6 +1154,7 @@ var BASE_EXCLUDES = /* @__PURE__ */ new Set([
1108
1154
  "onUnmount",
1109
1155
  "render",
1110
1156
  "ref",
1157
+ "view",
1111
1158
  "addCleanup",
1112
1159
  "_addCleanup",
1113
1160
  "watch",
@@ -1140,7 +1187,7 @@ function makeComponentObservable(instance, autoBind) {
1140
1187
  if (BASE_EXCLUDES.has(key)) continue;
1141
1188
  if (key in annotations) continue;
1142
1189
  const value = instance[key];
1143
- if (typeof value === "function" || isServiceValue(instance, value)) continue;
1190
+ if (typeof value === "function" || isServiceValue(instance, value) || trackedComponents.has(value)) continue;
1144
1191
  if (isBehavior(value)) {
1145
1192
  annotations[key] = _mobx.observable.ref;
1146
1193
  continue;
@@ -1205,14 +1252,15 @@ function createComponent(ComponentClass, templateOrOptions) {
1205
1252
  }
1206
1253
  if (!vmRef.current) {
1207
1254
  applyMobxActionPolicy();
1208
- const substitution = _optionalChain([substitutions, 'optionalAccess', _48 => _48.get, 'call', _49 => _49(ComponentClass)]);
1255
+ const substitution = _optionalChain([substitutions, 'optionalAccess', _51 => _51.get, 'call', _52 => _52(ComponentClass)]);
1209
1256
  if (substitution && !substitution.template && !template) throw new Error("[mobx-mantle] A substituted integrated model requires an explicit test template.");
1210
1257
  const instance = withInjectionScope(resolver, () => substitution ? constructComponent(SubstituteComponent, props, parentHandle) : constructComponent(ComponentClass, props, parentHandle));
1211
- substitutedTemplate.current = _optionalChain([substitution, 'optionalAccess', _50 => _50.template]);
1258
+ substitutedTemplate.current = _optionalChain([substitution, 'optionalAccess', _53 => _53.template]);
1212
1259
  setModelIdentity(instance, ComponentClass);
1213
1260
  if (substitution) installSubstituteState(instance, substitution.state());
1214
1261
  instance.forwardRef = ref;
1215
1262
  instance._propsBox._renderReaction = renderReactionRef.current;
1263
+ ownerScopes.set(instance, { handle: componentHandle, resolver: getServiceResolver(instance), substitutions });
1216
1264
  if (!substitution) instance._collectBehaviors();
1217
1265
  const decoratorAnnotations = getAnnotations(instance);
1218
1266
  if (substitution) {
@@ -1238,12 +1286,12 @@ function createComponent(ComponentClass, templateOrOptions) {
1238
1286
  ownKeys: () => Reflect.ownKeys(instance.props),
1239
1287
  getOwnPropertyDescriptor: (_, key) => Reflect.getOwnPropertyDescriptor(instance.props, key)
1240
1288
  });
1241
- withOwnerServiceScope(instance, () => _mobx.runInAction.call(void 0, () => _optionalChain([instance, 'access', _51 => _51.onCreate, 'optionalCall', _52 => _52(reactiveProps)])));
1289
+ withOwnerServiceScope(instance, () => _mobx.runInAction.call(void 0, () => _optionalChain([instance, 'access', _54 => _54.onCreate, 'optionalCall', _55 => _55(reactiveProps)])));
1242
1290
  if (process.env.NODE_ENV !== "production" && autoObservable && !decoratorAnnotations && !substitution) {
1243
1291
  for (const key of Object.keys(instance)) {
1244
1292
  if (BASE_EXCLUDES.has(key) || key.startsWith("_")) continue;
1245
1293
  const value = instance[key];
1246
- if (typeof value === "function" || isServiceValue(instance, value)) continue;
1294
+ if (typeof value === "function" || isServiceValue(instance, value) || trackedComponents.has(value)) continue;
1247
1295
  if (!_mobx.isObservableProp.call(void 0, instance, key)) {
1248
1296
  console.warn(
1249
1297
  `[mobx-mantle] ${ComponentClass.name}.${key} was first assigned in onCreate() and is not reactive. Declare it as a class field so it can be made observable.`
@@ -1281,7 +1329,7 @@ function createComponent(ComponentClass, templateOrOptions) {
1281
1329
  vm._layoutMountBehaviors();
1282
1330
  let cleanup;
1283
1331
  try {
1284
- const result = _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _53 => _53.onLayoutMount, 'optionalCall', _54 => _54()]));
1332
+ const result = _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _56 => _56.onLayoutMount, 'optionalCall', _57 => _57()]));
1285
1333
  if (process.env.NODE_ENV !== "production" && result instanceof Promise) {
1286
1334
  console.error(
1287
1335
  `[mobx-mantle] ${ComponentClass.name}.onLayoutMount() returned a Promise. Lifecycle methods must be synchronous. Use a sync onLayoutMount that calls an async method instead.`
@@ -1292,14 +1340,14 @@ function createComponent(ComponentClass, templateOrOptions) {
1292
1340
  reportError(e, { phase: "onLayoutMount", name: ComponentClass.name, isBehavior: false });
1293
1341
  }
1294
1342
  return () => {
1295
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _55 => _55()]));
1343
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _58 => _58()]));
1296
1344
  };
1297
1345
  }, [vm]);
1298
1346
  _react.useEffect.call(void 0, () => {
1299
1347
  vm._mountBehaviors();
1300
1348
  let cleanup;
1301
1349
  try {
1302
- const result = _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _56 => _56.onMount, 'optionalCall', _57 => _57()]));
1350
+ const result = _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _59 => _59.onMount, 'optionalCall', _60 => _60()]));
1303
1351
  if (process.env.NODE_ENV !== "production" && result instanceof Promise) {
1304
1352
  console.error(
1305
1353
  `[mobx-mantle] ${ComponentClass.name}.onMount() returned a Promise. Lifecycle methods must be synchronous. Use a sync onMount that calls an async method instead.`
@@ -1313,9 +1361,9 @@ function createComponent(ComponentClass, templateOrOptions) {
1313
1361
  warnUncollectedBehaviors(vm, ComponentClass.name, vm._behaviors);
1314
1362
  }
1315
1363
  return () => {
1316
- _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _58 => _58()]));
1364
+ _mobx.runInAction.call(void 0, () => _optionalChain([cleanup, 'optionalCall', _61 => _61()]));
1317
1365
  try {
1318
- _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _59 => _59.onUnmount, 'optionalCall', _60 => _60()]));
1366
+ _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _62 => _62.onUnmount, 'optionalCall', _63 => _63()]));
1319
1367
  } catch (e) {
1320
1368
  reportError(e, { phase: "onUnmount", name: ComponentClass.name, isBehavior: false });
1321
1369
  }
@@ -1326,7 +1374,7 @@ function createComponent(ComponentClass, templateOrOptions) {
1326
1374
  }, [vm]);
1327
1375
  _react.useEffect.call(void 0, () => {
1328
1376
  try {
1329
- _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _61 => _61.onUpdate, 'optionalCall', _62 => _62()]));
1377
+ _mobx.runInAction.call(void 0, () => _optionalChain([vm, 'access', _64 => _64.onUpdate, 'optionalCall', _65 => _65()]));
1330
1378
  } catch (e) {
1331
1379
  reportError(e, { phase: "onUpdate", name: ComponentClass.name, isBehavior: false });
1332
1380
  }
@@ -1348,6 +1396,24 @@ function createComponent(ComponentClass, templateOrOptions) {
1348
1396
  function createForwardRef(ComponentClass, templateOrOptions) {
1349
1397
  return createComponent(ComponentClass, templateOrOptions);
1350
1398
  }
1399
+ function createView(ModelClass, template) {
1400
+ const name = `${ModelClass.name}.${template.name || "view"}`;
1401
+ const View = _react.forwardRef.call(void 0, (props, ref) => {
1402
+ let vm = _optionalChain([_react.useContext.call(void 0, ComponentParentContext), 'optionalAccess', _66 => _66.current]);
1403
+ while (vm && !isModel(vm, ModelClass)) vm = vm.getParent();
1404
+ if (!vm) {
1405
+ throw new Error(
1406
+ `[mobx-mantle] createView(${ModelClass.name}): no ${ModelClass.name} is mounted above this view.`
1407
+ );
1408
+ }
1409
+ const model = vm;
1410
+ return useMantleObserver(() => template(withRef({ ...props, vm: model }, ref)), name);
1411
+ });
1412
+ View.displayName = name;
1413
+ const Memoized = _react.memo.call(void 0, View);
1414
+ trackedComponents.add(Memoized);
1415
+ return Memoized;
1416
+ }
1351
1417
 
1352
1418
  // src/useBehavior.ts
1353
1419
 
@@ -1419,10 +1485,10 @@ function shared(store, key, options = {}) {
1419
1485
  const fields = options.fields ? [...options.fields] : ownDataFields(store);
1420
1486
  const storage = options.persist === false ? null : safeStorage();
1421
1487
  const channel = typeof BroadcastChannel === "function" ? new BroadcastChannel(`mantle-shared:${key}`) : null;
1422
- _optionalChain([channel, 'optionalAccess', _63 => _63.unref, 'optionalCall', _64 => _64()]);
1488
+ _optionalChain([channel, 'optionalAccess', _67 => _67.unref, 'optionalCall', _68 => _68()]);
1423
1489
  let stored = null;
1424
1490
  try {
1425
- stored = _nullishCoalesce(_optionalChain([storage, 'optionalAccess', _65 => _65.getItem, 'call', _66 => _66(key)]), () => ( null));
1491
+ stored = _nullishCoalesce(_optionalChain([storage, 'optionalAccess', _69 => _69.getItem, 'call', _70 => _70(key)]), () => ( null));
1426
1492
  } catch (e5) {
1427
1493
  stored = null;
1428
1494
  }
@@ -1434,10 +1500,10 @@ function shared(store, key, options = {}) {
1434
1500
  if (json === current) return;
1435
1501
  current = json;
1436
1502
  try {
1437
- _optionalChain([storage, 'optionalAccess', _67 => _67.setItem, 'call', _68 => _68(key, json)]);
1503
+ _optionalChain([storage, 'optionalAccess', _71 => _71.setItem, 'call', _72 => _72(key, json)]);
1438
1504
  } catch (e6) {
1439
1505
  }
1440
- _optionalChain([channel, 'optionalAccess', _69 => _69.postMessage, 'call', _70 => _70(json)]);
1506
+ _optionalChain([channel, 'optionalAccess', _73 => _73.postMessage, 'call', _74 => _74(json)]);
1441
1507
  }
1442
1508
  );
1443
1509
  const receive = (json) => {
@@ -1451,13 +1517,13 @@ function shared(store, key, options = {}) {
1451
1517
  const onStorage = (event) => {
1452
1518
  if (event.key === key && event.newValue !== null) receive(event.newValue);
1453
1519
  };
1454
- _optionalChain([channel, 'optionalAccess', _71 => _71.addEventListener, 'call', _72 => _72("message", onMessage)]);
1520
+ _optionalChain([channel, 'optionalAccess', _75 => _75.addEventListener, 'call', _76 => _76("message", onMessage)]);
1455
1521
  window.addEventListener("storage", onStorage);
1456
1522
  registry.set(store, {
1457
1523
  dispose() {
1458
1524
  stopPublish();
1459
- _optionalChain([channel, 'optionalAccess', _73 => _73.removeEventListener, 'call', _74 => _74("message", onMessage)]);
1460
- _optionalChain([channel, 'optionalAccess', _75 => _75.close, 'call', _76 => _76()]);
1525
+ _optionalChain([channel, 'optionalAccess', _77 => _77.removeEventListener, 'call', _78 => _78("message", onMessage)]);
1526
+ _optionalChain([channel, 'optionalAccess', _79 => _79.close, 'call', _80 => _80()]);
1461
1527
  window.removeEventListener("storage", onStorage);
1462
1528
  registry.delete(store);
1463
1529
  }
@@ -1465,7 +1531,7 @@ function shared(store, key, options = {}) {
1465
1531
  return store;
1466
1532
  }
1467
1533
  function unshare(store) {
1468
- _optionalChain([registry, 'access', _77 => _77.get, 'call', _78 => _78(store), 'optionalAccess', _79 => _79.dispose, 'call', _80 => _80()]);
1534
+ _optionalChain([registry, 'access', _81 => _81.get, 'call', _82 => _82(store), 'optionalAccess', _83 => _83.dispose, 'call', _84 => _84()]);
1469
1535
  }
1470
1536
  function shareStores(stores, options = {}) {
1471
1537
  const { prefix = "shared", ...rest } = options;
@@ -1497,5 +1563,6 @@ function shareStores(stores, options = {}) {
1497
1563
 
1498
1564
 
1499
1565
 
1500
- exports.Behavior = Behavior; exports.Component = Component; exports.ModelProvider = ModelProvider; exports.ServiceProvider = ServiceProvider; exports.ViewModel = Component; exports.action = action2; exports.computed = computed2; exports.configure = configure; exports.createBehavior = createBehavior; exports.createComponent = createComponent; exports.createForwardRef = createForwardRef; exports.createServiceToken = createServiceToken; exports.inject = inject; exports.isModel = isModel; exports.observable = observable2; exports.observer = observer; exports.shareStores = shareStores; exports.shared = shared; exports.toValue = _chunkWNF647U6cjs.toValue; exports.unshare = unshare; exports.useBehavior = useBehavior; exports.withInjectionScope = withInjectionScope;
1566
+
1567
+ exports.Behavior = Behavior; exports.Component = Component; exports.ModelProvider = ModelProvider; exports.ServiceProvider = ServiceProvider; exports.ViewModel = Component; exports.action = action2; exports.computed = computed2; exports.configure = configure; exports.createBehavior = createBehavior; exports.createComponent = createComponent; exports.createForwardRef = createForwardRef; exports.createServiceToken = createServiceToken; exports.createView = createView; exports.inject = inject; exports.isModel = isModel; exports.observable = observable2; exports.observer = observer; exports.shareStores = shareStores; exports.shared = shared; exports.toValue = _chunkWNF647U6cjs.toValue; exports.unshare = unshare; exports.useBehavior = useBehavior; exports.withInjectionScope = withInjectionScope;
1501
1568
  //# sourceMappingURL=index.cjs.map