mobx-mantle 0.3.7 → 0.6.1

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.
@@ -0,0 +1,419 @@
1
+ import {
2
+ toValue
3
+ } from "../chunk-LP5TAGSI.js";
4
+
5
+ // src/behaviors/index.ts
6
+ import { runInAction } from "mobx";
7
+ import { Behavior, createBehavior } from "mobx-mantle";
8
+ import { toValue as toValue2 } from "mobx-mantle";
9
+ var builtin = (Def) => createBehavior(Def, { autoObservable: true });
10
+ var EventListenerBehavior = class extends Behavior {
11
+ onCreate(target, type, handler, options) {
12
+ this.effect(() => {
13
+ const el = toValue2(target);
14
+ const eventType = toValue2(type);
15
+ if (!el) return;
16
+ const listener = (event) => runInAction(() => handler(event));
17
+ el.addEventListener(eventType, listener, options);
18
+ return () => el.removeEventListener(eventType, listener, options);
19
+ });
20
+ }
21
+ };
22
+ var withEventListener = builtin(EventListenerBehavior);
23
+ var IntervalBehavior = class extends Behavior {
24
+ onCreate(callback, delay = 1e3) {
25
+ this.effect(() => {
26
+ const ms = toValue2(delay);
27
+ if (ms == null || ms < 0) return;
28
+ const id = setInterval(() => runInAction(callback), ms);
29
+ return () => clearInterval(id);
30
+ });
31
+ }
32
+ };
33
+ var withInterval = builtin(IntervalBehavior);
34
+ var TimeoutBehavior = class extends Behavior {
35
+ constructor() {
36
+ super(...arguments);
37
+ /** True while the timeout is scheduled and hasn't fired */
38
+ this.pending = false;
39
+ }
40
+ onCreate(callback, delay) {
41
+ this.effect(() => {
42
+ const ms = toValue2(delay);
43
+ if (ms == null || ms < 0) return;
44
+ runInAction(() => {
45
+ this.pending = true;
46
+ });
47
+ const id = setTimeout(() => runInAction(() => {
48
+ this.pending = false;
49
+ callback();
50
+ }), ms);
51
+ return () => {
52
+ this.pending = false;
53
+ clearTimeout(id);
54
+ };
55
+ });
56
+ }
57
+ };
58
+ var withTimeout = builtin(TimeoutBehavior);
59
+ var AsyncBehavior = class extends Behavior {
60
+ constructor() {
61
+ super(...arguments);
62
+ this.value = void 0;
63
+ this.error = void 0;
64
+ this.loading = false;
65
+ this._runId = 0;
66
+ }
67
+ onCreate(fn) {
68
+ this._fn = fn;
69
+ }
70
+ /**
71
+ * Run the async function. Out-of-order completions are discarded: only the
72
+ * most recent run may write value/error/loading.
73
+ */
74
+ async run(...args) {
75
+ const id = ++this._runId;
76
+ this.loading = true;
77
+ this.error = void 0;
78
+ try {
79
+ const result = await this._fn(...args);
80
+ if (id === this._runId) {
81
+ runInAction(() => {
82
+ this.value = result;
83
+ this.loading = false;
84
+ });
85
+ }
86
+ return result;
87
+ } catch (e) {
88
+ if (id === this._runId) {
89
+ runInAction(() => {
90
+ this.error = e;
91
+ this.loading = false;
92
+ });
93
+ }
94
+ return void 0;
95
+ }
96
+ }
97
+ /** Discard any in-flight run's result */
98
+ cancel() {
99
+ this._runId++;
100
+ this.loading = false;
101
+ }
102
+ };
103
+ var withAsync = builtin(AsyncBehavior);
104
+ var FetchBehavior = class extends Behavior {
105
+ constructor() {
106
+ super(...arguments);
107
+ /** Current URL — a one-way mirror of the url argument (this.sync) */
108
+ this.url = void 0;
109
+ /** The request state machine is a nested withAsync behavior */
110
+ this.request = withAsync(async (url, init) => {
111
+ const response = await fetch(url, init);
112
+ if (!response.ok) {
113
+ throw new Error(`HTTP ${response.status} for ${url}`);
114
+ }
115
+ return await response.json();
116
+ });
117
+ }
118
+ onCreate(url, init) {
119
+ this._init = init;
120
+ this.url = this.sync(url);
121
+ this.watch(
122
+ () => this.url,
123
+ (next) => {
124
+ if (next) this.request.run(next, this._init);
125
+ },
126
+ { fireImmediately: true }
127
+ );
128
+ }
129
+ get data() {
130
+ return this.request.value;
131
+ }
132
+ get loading() {
133
+ return this.request.loading;
134
+ }
135
+ get error() {
136
+ return this.request.error;
137
+ }
138
+ };
139
+ var withFetch = builtin(FetchBehavior);
140
+ var LocalStorageBehavior = class extends Behavior {
141
+ onCreate(key, initialValue) {
142
+ let stored;
143
+ if (typeof localStorage !== "undefined") {
144
+ try {
145
+ const raw = localStorage.getItem(key);
146
+ if (raw !== null) stored = JSON.parse(raw);
147
+ } catch {
148
+ }
149
+ }
150
+ this.value = stored !== void 0 ? stored : initialValue;
151
+ this.watch(
152
+ () => JSON.stringify(this.value),
153
+ (json) => {
154
+ try {
155
+ localStorage.setItem(key, json);
156
+ } catch {
157
+ }
158
+ }
159
+ );
160
+ this.storageSync = withEventListener(
161
+ () => typeof window === "undefined" ? null : window,
162
+ "storage",
163
+ (event) => {
164
+ const e = event;
165
+ if (e.key !== key || e.newValue === null) return;
166
+ try {
167
+ this.value = JSON.parse(e.newValue);
168
+ } catch {
169
+ }
170
+ }
171
+ );
172
+ }
173
+ };
174
+ var withLocalStorage = builtin(LocalStorageBehavior);
175
+ var ElementSizeBehavior = class extends Behavior {
176
+ constructor() {
177
+ super(...arguments);
178
+ this.width = null;
179
+ this.height = null;
180
+ }
181
+ onCreate(target) {
182
+ this.target = target;
183
+ }
184
+ measure(entry) {
185
+ const element = this.target.current;
186
+ if (!element) return;
187
+ const borderBox = entry?.borderBoxSize;
188
+ const box = Array.isArray(borderBox) ? borderBox[0] : borderBox;
189
+ let width = null;
190
+ let height = null;
191
+ if (box && Number.isFinite(box.inlineSize) && Number.isFinite(box.blockSize)) {
192
+ const writingMode = getComputedStyle(element).writingMode;
193
+ const vertical = writingMode.startsWith("vertical") || writingMode.startsWith("sideways");
194
+ width = vertical ? box.blockSize : box.inlineSize;
195
+ height = vertical ? box.inlineSize : box.blockSize;
196
+ } else if (element instanceof HTMLElement) {
197
+ width = element.offsetWidth;
198
+ height = element.offsetHeight;
199
+ }
200
+ if (width !== null && width !== this.width) this.width = width;
201
+ if (height !== null && height !== this.height) this.height = height;
202
+ }
203
+ onLayoutMount() {
204
+ const element = this.target.current;
205
+ if (!element) {
206
+ this.width = null;
207
+ this.height = null;
208
+ return;
209
+ }
210
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver((entries) => {
211
+ const entry = entries.find((candidate) => candidate.target === element);
212
+ this.measure(entry);
213
+ });
214
+ observer?.observe(element, { box: "border-box" });
215
+ this.measure();
216
+ return () => {
217
+ observer?.disconnect();
218
+ this.width = null;
219
+ this.height = null;
220
+ };
221
+ }
222
+ };
223
+ var withElementSize = builtin(ElementSizeBehavior);
224
+ var WindowSizeBehavior = class extends Behavior {
225
+ constructor() {
226
+ super(...arguments);
227
+ this.width = typeof window !== "undefined" ? window.innerWidth : 0;
228
+ this.height = typeof window !== "undefined" ? window.innerHeight : 0;
229
+ this.resize = withEventListener(
230
+ () => typeof window === "undefined" ? null : window,
231
+ "resize",
232
+ () => this.measure()
233
+ );
234
+ }
235
+ measure() {
236
+ this.width = window.innerWidth;
237
+ this.height = window.innerHeight;
238
+ }
239
+ };
240
+ var withWindowSize = builtin(WindowSizeBehavior);
241
+ var MediaQueryBehavior = class extends Behavior {
242
+ constructor() {
243
+ super(...arguments);
244
+ this.matches = false;
245
+ }
246
+ onCreate(query) {
247
+ this.effect(() => {
248
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
249
+ const mql = window.matchMedia(toValue2(query));
250
+ runInAction(() => {
251
+ this.matches = mql.matches;
252
+ });
253
+ const onChange = (event) => {
254
+ runInAction(() => {
255
+ this.matches = event.matches;
256
+ });
257
+ };
258
+ mql.addEventListener("change", onChange);
259
+ return () => mql.removeEventListener("change", onChange);
260
+ });
261
+ }
262
+ };
263
+ var withMediaQuery = builtin(MediaQueryBehavior);
264
+ var DebounceBehavior = class extends Behavior {
265
+ constructor() {
266
+ super(...arguments);
267
+ this._timer = null;
268
+ }
269
+ onCreate(source, delay = 300) {
270
+ this.value = toValue2(source);
271
+ this.watch(source, (next) => {
272
+ if (this._timer !== null) clearTimeout(this._timer);
273
+ this._timer = setTimeout(() => runInAction(() => {
274
+ this._timer = null;
275
+ this.value = next;
276
+ }), delay);
277
+ });
278
+ }
279
+ onUnmount() {
280
+ if (this._timer !== null) {
281
+ clearTimeout(this._timer);
282
+ this._timer = null;
283
+ }
284
+ }
285
+ };
286
+ var withDebounce = builtin(DebounceBehavior);
287
+ var ThrottleBehavior = class extends Behavior {
288
+ constructor() {
289
+ super(...arguments);
290
+ this._timer = null;
291
+ this._lastEmit = 0;
292
+ }
293
+ onCreate(source, delay = 300) {
294
+ this.value = toValue2(source);
295
+ this.watch(
296
+ source,
297
+ (next) => {
298
+ const now = Date.now();
299
+ const remaining = delay - (now - this._lastEmit);
300
+ if (remaining <= 0) {
301
+ this._lastEmit = now;
302
+ this.value = next;
303
+ } else if (this._timer === null) {
304
+ this._timer = setTimeout(() => runInAction(() => {
305
+ this._timer = null;
306
+ this._lastEmit = Date.now();
307
+ this.value = toValue2(source);
308
+ }), remaining);
309
+ }
310
+ }
311
+ );
312
+ }
313
+ onUnmount() {
314
+ if (this._timer !== null) {
315
+ clearTimeout(this._timer);
316
+ this._timer = null;
317
+ }
318
+ }
319
+ };
320
+ var withThrottle = builtin(ThrottleBehavior);
321
+ var DocumentTitleBehavior = class extends Behavior {
322
+ onCreate(title) {
323
+ this.effect(() => {
324
+ if (typeof document === "undefined") return;
325
+ const previous = document.title;
326
+ document.title = toValue2(title);
327
+ return () => {
328
+ document.title = previous;
329
+ };
330
+ });
331
+ }
332
+ };
333
+ var withDocumentTitle = builtin(DocumentTitleBehavior);
334
+ var PageVisibilityBehavior = class extends Behavior {
335
+ constructor() {
336
+ super(...arguments);
337
+ this.visible = typeof document !== "undefined" ? document.visibilityState !== "hidden" : true;
338
+ this.visibilityListener = withEventListener(
339
+ () => typeof document === "undefined" ? null : document,
340
+ "visibilitychange",
341
+ () => {
342
+ this.visible = document.visibilityState !== "hidden";
343
+ }
344
+ );
345
+ }
346
+ };
347
+ var withPageVisibility = builtin(PageVisibilityBehavior);
348
+ var AutosaveBehavior = class extends Behavior {
349
+ constructor() {
350
+ super(...arguments);
351
+ /** Posting rides on withAsync (out-of-order completions discarded) */
352
+ this.saver = withAsync(async (url, data) => {
353
+ const response = await fetch(url, {
354
+ method: "POST",
355
+ headers: { "Content-Type": "application/json" },
356
+ body: JSON.stringify(data)
357
+ });
358
+ if (!response.ok) {
359
+ throw new Error(`HTTP ${response.status} for ${url}`);
360
+ }
361
+ return response;
362
+ });
363
+ /** Current save endpoint — a one-way mirror of the url argument (this.sync) */
364
+ this.url = "";
365
+ this._lastSaved = void 0;
366
+ }
367
+ onCreate(url, data, interval = 5e3) {
368
+ this.url = this.sync(url);
369
+ this._data = data;
370
+ this.timer = withInterval(() => this.save(), interval);
371
+ }
372
+ /** Save now. Skipped when the payload hasn't changed since the last save. */
373
+ save() {
374
+ const payload = this._data();
375
+ const serialized = JSON.stringify(payload);
376
+ if (serialized === this._lastSaved) return;
377
+ this._lastSaved = serialized;
378
+ this.saver.run(this.url, payload);
379
+ }
380
+ get saving() {
381
+ return this.saver.loading;
382
+ }
383
+ get error() {
384
+ return this.saver.error;
385
+ }
386
+ };
387
+ var withAutosave = builtin(AutosaveBehavior);
388
+ export {
389
+ AsyncBehavior,
390
+ AutosaveBehavior,
391
+ DebounceBehavior,
392
+ DocumentTitleBehavior,
393
+ ElementSizeBehavior,
394
+ EventListenerBehavior,
395
+ FetchBehavior,
396
+ IntervalBehavior,
397
+ LocalStorageBehavior,
398
+ MediaQueryBehavior,
399
+ PageVisibilityBehavior,
400
+ ThrottleBehavior,
401
+ TimeoutBehavior,
402
+ WindowSizeBehavior,
403
+ toValue,
404
+ withAsync,
405
+ withAutosave,
406
+ withDebounce,
407
+ withDocumentTitle,
408
+ withElementSize,
409
+ withEventListener,
410
+ withFetch,
411
+ withInterval,
412
+ withLocalStorage,
413
+ withMediaQuery,
414
+ withPageVisibility,
415
+ withThrottle,
416
+ withTimeout,
417
+ withWindowSize
418
+ };
419
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/behaviors/index.ts"],"sourcesContent":["import { runInAction } from 'mobx';\n/**\n * Standard library of built-in behaviors. Every argument documented as\n * MaybeGetter accepts either a plain value (frozen at construction) or a\n * getter (a live connection — re-resolved whenever its observable sources\n * change):\n *\n * ```tsx\n * media = withMediaQuery('(max-width: 768px)'); // static\n * media = withMediaQuery(() => this.props.breakpoint); // live\n * ```\n *\n * Author-side conventions on display throughout this file:\n * - `this.sync(arg)` — normalize a MaybeGetter into an ordinary field that\n * stays current (see withFetch, withAutosave)\n * - `this.watch(arg, cb)` — a MaybeGetter is a valid watch source directly\n * (see withDebounce, withThrottle)\n * - `toValue(arg)` — one-off reads inside effect bodies, where the getter\n * call doubles as dependency tracking (see withEventListener, withInterval)\n *\n * Several of these behaviors are themselves compositions of the others\n * (withFetch = withAsync + watch, withWindowSize = withEventListener + state,\n * withAutosave = withInterval + withAsync) — the same nesting available to\n * user-defined behaviors.\n */\nimport { Behavior, createBehavior } from 'mobx-mantle';\nimport { toValue, type MaybeGetter } from 'mobx-mantle';\n\n// Built-ins are written in auto-observable style (no decorators). Pin that\n// here so they work the same whatever the app sets for `autoObservable`.\nconst builtin = <T extends new (...args: any[]) => any>(Def: T) =>\n createBehavior(Def, { autoObservable: true });\n\n// ---------------------------------------------------------------------------\n// withEventListener\n// ---------------------------------------------------------------------------\n\nexport class EventListenerBehavior extends Behavior {\n onCreate(\n target: MaybeGetter<EventTarget | null | undefined>,\n type: MaybeGetter<string>,\n handler: (event: Event) => void,\n options?: AddEventListenerOptions\n ) {\n this.effect(() => {\n const el = toValue(target);\n const eventType = toValue(type);\n if (!el) return;\n const listener = (event: Event) => runInAction(() => handler(event));\n el.addEventListener(eventType, listener, options);\n return () => el.removeEventListener(eventType, listener, options);\n });\n }\n}\n\n/**\n * Attach an event listener for the mounted lifetime of the host. Re-attaches\n * when a getter target or type changes.\n *\n * @example keys = withEventListener(window, 'keydown', (e) => this.handleKey(e));\n */\nexport const withEventListener = builtin(EventListenerBehavior);\n\n// ---------------------------------------------------------------------------\n// withInterval\n// ---------------------------------------------------------------------------\n\nexport class IntervalBehavior extends Behavior {\n onCreate(callback: () => void, delay: MaybeGetter<number | null> = 1000) {\n this.effect(() => {\n const ms = toValue(delay);\n if (ms == null || ms < 0) return; // null pauses the interval\n const id = setInterval(() => runInAction(callback), ms);\n return () => clearInterval(id);\n });\n }\n}\n\n/**\n * Run a callback on an interval for the mounted lifetime of the host.\n * A getter delay reschedules on change; resolving to null pauses.\n *\n * @example poll = withInterval(() => this.refresh(), 10_000);\n */\nexport const withInterval = builtin(IntervalBehavior);\n\n// ---------------------------------------------------------------------------\n// withTimeout\n// ---------------------------------------------------------------------------\n\nexport class TimeoutBehavior extends Behavior {\n /** True while the timeout is scheduled and hasn't fired */\n pending = false;\n\n onCreate(callback: () => void, delay: MaybeGetter<number | null>) {\n this.effect(() => {\n const ms = toValue(delay);\n if (ms == null || ms < 0) return; // null cancels/pauses\n runInAction(() => { this.pending = true; });\n const id = setTimeout(() => runInAction(() => {\n this.pending = false;\n callback();\n }), ms);\n return () => {\n this.pending = false;\n clearTimeout(id);\n };\n });\n }\n}\n\n/**\n * Run a callback once after a delay (measured from mount). A getter delay\n * reschedules on change; resolving to null cancels.\n *\n * @example toast = withTimeout(() => (this.visible = false), 3000);\n */\nexport const withTimeout = builtin(TimeoutBehavior);\n\n// ---------------------------------------------------------------------------\n// withAsync\n// ---------------------------------------------------------------------------\n\nexport class AsyncBehavior<T = unknown> extends Behavior {\n value: T | undefined = undefined;\n error: unknown = undefined;\n loading = false;\n\n private _fn!: (...args: any[]) => Promise<T>;\n private _runId = 0;\n\n onCreate(fn: (...args: any[]) => Promise<T>) {\n this._fn = fn;\n }\n\n /**\n * Run the async function. Out-of-order completions are discarded: only the\n * most recent run may write value/error/loading.\n */\n async run(...args: any[]): Promise<T | undefined> {\n const id = ++this._runId;\n this.loading = true;\n this.error = undefined;\n try {\n const result = await this._fn(...args);\n if (id === this._runId) {\n runInAction(() => {\n this.value = result;\n this.loading = false;\n });\n }\n return result;\n } catch (e) {\n if (id === this._runId) {\n runInAction(() => {\n this.error = e;\n this.loading = false;\n });\n }\n return undefined;\n }\n }\n\n /** Discard any in-flight run's result */\n cancel(): void {\n this._runId++;\n this.loading = false;\n }\n}\n\n/**\n * Track an async function's latest run as observable value/error/loading state.\n *\n * @example search = withAsync((q: string) => api.search(q)); // later: this.search.run(this.query)\n */\nexport const withAsync = builtin(AsyncBehavior) as unknown as {\n <T>(fn: (...args: any[]) => Promise<T>): AsyncBehavior<T>;\n new <T>(fn: (...args: any[]) => Promise<T>): AsyncBehavior<T>;\n};\n\n// ---------------------------------------------------------------------------\n// withFetch — composition: withAsync + watch\n// ---------------------------------------------------------------------------\n\nexport class FetchBehavior<T = unknown> extends Behavior {\n /** Current URL — a one-way mirror of the url argument (this.sync) */\n url: string | null | undefined = undefined;\n\n /** The request state machine is a nested withAsync behavior */\n request = withAsync<T>(async (url: string, init?: RequestInit) => {\n const response = await fetch(url, init);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} for ${url}`);\n }\n return (await response.json()) as T;\n });\n\n private _init?: RequestInit;\n\n onCreate(url: MaybeGetter<string | null | undefined>, init?: RequestInit) {\n this._init = init;\n this.url = this.sync(url);\n this.watch(\n () => this.url,\n (next) => {\n if (next) this.request.run(next, this._init);\n },\n { fireImmediately: true }\n );\n }\n\n get data(): T | undefined {\n return this.request.value;\n }\n get loading(): boolean {\n return this.request.loading;\n }\n get error(): unknown {\n return this.request.error;\n }\n}\n\n/**\n * Fetch JSON from a URL. A getter URL refetches on change (the first fetch\n * fires at mount); a URL resolving to null/undefined skips fetching.\n * Out-of-order responses are discarded by the underlying withAsync.\n *\n * @example user = withFetch<User>(() => `/api/users/${this.props.userId}`);\n */\nexport const withFetch = builtin(FetchBehavior) as unknown as {\n <T = unknown>(url: MaybeGetter<string | null | undefined>, init?: RequestInit): FetchBehavior<T>;\n new <T = unknown>(url: MaybeGetter<string | null | undefined>, init?: RequestInit): FetchBehavior<T>;\n};\n\n// ---------------------------------------------------------------------------\n// withLocalStorage — composition: withEventListener for cross-tab sync\n// ---------------------------------------------------------------------------\n\nexport class LocalStorageBehavior<T = unknown> extends Behavior {\n value!: T;\n\n /** Cross-tab sync rides on a nested withEventListener */\n storageSync!: EventListenerBehavior;\n\n onCreate(key: string, initialValue: T) {\n let stored: T | undefined;\n if (typeof localStorage !== 'undefined') {\n try {\n const raw = localStorage.getItem(key);\n if (raw !== null) stored = JSON.parse(raw) as T;\n } catch {\n // corrupted entry or blocked storage — fall back to initialValue\n }\n }\n this.value = stored !== undefined ? stored : initialValue;\n\n // Persist on change (tracking starts at mount)\n this.watch(\n () => JSON.stringify(this.value),\n (json) => {\n try {\n localStorage.setItem(key, json);\n } catch {\n // quota exceeded or blocked storage — value stays in memory\n }\n }\n );\n\n this.storageSync = withEventListener(\n () => (typeof window === 'undefined' ? null : window),\n 'storage',\n (event) => {\n const e = event as StorageEvent;\n if (e.key !== key || e.newValue === null) return;\n try {\n this.value = JSON.parse(e.newValue) as T;\n } catch {\n // malformed external write — ignore\n }\n }\n );\n }\n}\n\n/**\n * An observable value persisted to localStorage under `key`, hydrated from\n * storage at construction and kept in sync across tabs. Values must be\n * JSON-serializable.\n *\n * @example theme = withLocalStorage('theme', 'dark'); // read/write this.theme.value\n */\nexport const withLocalStorage = builtin(LocalStorageBehavior) as unknown as {\n <T>(key: string, initialValue: T): LocalStorageBehavior<T>;\n new <T>(key: string, initialValue: T): LocalStorageBehavior<T>;\n};\n\n// ---------------------------------------------------------------------------\n// withElementSize\n// ---------------------------------------------------------------------------\n\nexport interface ElementSizeRef {\n readonly current: Element | null;\n}\n\n/**\n * Observe one ref's border-box dimensions for the mounted lifetime of its\n * host. Setup and teardown both live in onLayoutMount so React StrictMode's\n * effect replay always reacquires the observer against the retained DOM node.\n */\nexport class ElementSizeBehavior extends Behavior {\n width: number | null = null;\n height: number | null = null;\n\n private target!: ElementSizeRef;\n\n onCreate(target: ElementSizeRef) {\n this.target = target;\n }\n\n measure(entry?: ResizeObserverEntry) {\n const element = this.target.current;\n if (!element) return;\n\n // ResizeObserver reports layout-space box sizes. Unlike\n // getBoundingClientRect(), these values do not include transforms on the\n // element or its ancestors.\n const borderBox = entry?.borderBoxSize;\n const box = Array.isArray(borderBox) ? borderBox[0] : borderBox;\n let width: number | null = null;\n let height: number | null = null;\n\n if (box && Number.isFinite(box.inlineSize) && Number.isFinite(box.blockSize)) {\n const writingMode = getComputedStyle(element).writingMode;\n const vertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');\n width = vertical ? box.blockSize : box.inlineSize;\n height = vertical ? box.inlineSize : box.blockSize;\n } else if (element instanceof HTMLElement) {\n // Initial layout-mount measurement and older-browser fallback. These\n // are physical border-box dimensions and are also transform-independent.\n width = element.offsetWidth;\n height = element.offsetHeight;\n }\n\n if (width !== null && width !== this.width) this.width = width;\n if (height !== null && height !== this.height) this.height = height;\n }\n\n onLayoutMount() {\n const element = this.target.current;\n if (!element) {\n this.width = null;\n this.height = null;\n return;\n }\n\n const observer =\n typeof ResizeObserver === 'undefined'\n ? null\n : new ResizeObserver((entries) => {\n const entry = entries.find((candidate) => candidate.target === element);\n this.measure(entry);\n });\n observer?.observe(element, { box: 'border-box' });\n this.measure();\n\n return () => {\n observer?.disconnect();\n this.width = null;\n this.height = null;\n };\n }\n}\n\n/**\n * Reactive layout-space border-box width and height for an element ref.\n * CSS transforms on the element or its ancestors do not affect the reported\n * size, so responsive components behave consistently inside scaled containers.\n * Values are null before layout mount, while no element is attached, and\n * after cleanup.\n *\n * @example\n * root = this.ref<HTMLDivElement>();\n * size = withElementSize(this.root);\n */\nexport const withElementSize = builtin(ElementSizeBehavior);\n\n// ---------------------------------------------------------------------------\n// withWindowSize — composition: withEventListener\n// ---------------------------------------------------------------------------\n\nexport class WindowSizeBehavior extends Behavior {\n width = typeof window !== 'undefined' ? window.innerWidth : 0;\n height = typeof window !== 'undefined' ? window.innerHeight : 0;\n\n resize = withEventListener(\n () => (typeof window === 'undefined' ? null : window),\n 'resize',\n () => this.measure()\n );\n\n measure() {\n this.width = window.innerWidth;\n this.height = window.innerHeight;\n }\n}\n\n/**\n * Observable window inner width/height (0×0 during SSR).\n *\n * @example size = withWindowSize(); // this.size.width × this.size.height\n */\nexport const withWindowSize = builtin(WindowSizeBehavior);\n\n// ---------------------------------------------------------------------------\n// withMediaQuery\n// ---------------------------------------------------------------------------\n\nexport class MediaQueryBehavior extends Behavior {\n matches = false;\n\n onCreate(query: MaybeGetter<string>) {\n this.effect(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n const mql = window.matchMedia(toValue(query));\n runInAction(() => { this.matches = mql.matches; });\n const onChange = (event: MediaQueryListEvent) => {\n runInAction(() => { this.matches = event.matches; });\n };\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n });\n }\n}\n\n/**\n * Observable match state for a media query (false during SSR).\n *\n * @example isMobile = withMediaQuery('(max-width: 768px)'); // this.isMobile.matches\n */\nexport const withMediaQuery = builtin(MediaQueryBehavior);\n\n// ---------------------------------------------------------------------------\n// withDebounce\n// ---------------------------------------------------------------------------\n\nexport class DebounceBehavior<T = unknown> extends Behavior {\n value!: T;\n\n private _timer: ReturnType<typeof setTimeout> | null = null;\n\n onCreate(source: MaybeGetter<T>, delay: number = 300) {\n this.value = toValue(source);\n // A MaybeGetter is a valid watch source directly (and a plain value\n // dev-warns here — debouncing a constant is always a mistake).\n // Every change restarts the timer, so the value settles only after\n // `delay` ms of quiet. (MobX's reaction `delay` would fire from the\n // first change instead, batching the rest.)\n this.watch(source, (next) => {\n if (this._timer !== null) clearTimeout(this._timer);\n this._timer = setTimeout(() => runInAction(() => {\n this._timer = null;\n this.value = next;\n }), delay);\n });\n }\n\n onUnmount() {\n if (this._timer !== null) {\n clearTimeout(this._timer);\n this._timer = null;\n }\n }\n}\n\n/**\n * A debounced mirror of a reactive source: updates settle after `delay` ms of quiet.\n *\n * @example debouncedQuery = withDebounce(() => this.query, 300);\n */\nexport const withDebounce = builtin(DebounceBehavior) as unknown as {\n <T>(source: MaybeGetter<T>, delay?: number): DebounceBehavior<T>;\n new <T>(source: MaybeGetter<T>, delay?: number): DebounceBehavior<T>;\n};\n\n// ---------------------------------------------------------------------------\n// withThrottle\n// ---------------------------------------------------------------------------\n\nexport class ThrottleBehavior<T = unknown> extends Behavior {\n value!: T;\n\n private _timer: ReturnType<typeof setTimeout> | null = null;\n private _lastEmit = 0;\n\n onCreate(source: MaybeGetter<T>, delay: number = 300) {\n this.value = toValue(source);\n this.watch(\n source,\n (next) => {\n const now = Date.now();\n const remaining = delay - (now - this._lastEmit);\n if (remaining <= 0) {\n this._lastEmit = now;\n this.value = next;\n } else if (this._timer === null) {\n // Trailing edge: emit the latest value when the window closes\n this._timer = setTimeout(() => runInAction(() => {\n this._timer = null;\n this._lastEmit = Date.now();\n this.value = toValue(source);\n }), remaining);\n }\n }\n );\n }\n\n onUnmount() {\n if (this._timer !== null) {\n clearTimeout(this._timer);\n this._timer = null;\n }\n }\n}\n\n/**\n * A throttled mirror of a reactive source: at most one update per `delay` ms, trailing edge included.\n *\n * @example scrollY = withThrottle(() => this.rawScrollY, 100);\n */\nexport const withThrottle = builtin(ThrottleBehavior) as unknown as {\n <T>(source: MaybeGetter<T>, delay?: number): ThrottleBehavior<T>;\n new <T>(source: MaybeGetter<T>, delay?: number): ThrottleBehavior<T>;\n};\n\n// ---------------------------------------------------------------------------\n// withDocumentTitle\n// ---------------------------------------------------------------------------\n\nexport class DocumentTitleBehavior extends Behavior {\n onCreate(title: MaybeGetter<string>) {\n this.effect(() => {\n if (typeof document === 'undefined') return;\n const previous = document.title;\n document.title = toValue(title);\n return () => {\n document.title = previous;\n };\n });\n }\n}\n\n/**\n * Set document.title for the mounted lifetime; restores the previous title on unmount.\n *\n * @example title = withDocumentTitle(() => `Inbox (${this.unread})`);\n */\nexport const withDocumentTitle = builtin(DocumentTitleBehavior);\n\n// ---------------------------------------------------------------------------\n// withPageVisibility — composition: withEventListener\n// ---------------------------------------------------------------------------\n\nexport class PageVisibilityBehavior extends Behavior {\n visible = typeof document !== 'undefined' ? document.visibilityState !== 'hidden' : true;\n\n visibilityListener = withEventListener(\n () => (typeof document === 'undefined' ? null : document),\n 'visibilitychange',\n () => {\n this.visible = document.visibilityState !== 'hidden';\n }\n );\n}\n\n/**\n * Observable page visibility (true during SSR).\n *\n * @example page = withPageVisibility(); // pause work when !this.page.visible\n */\nexport const withPageVisibility = builtin(PageVisibilityBehavior);\n\n// ---------------------------------------------------------------------------\n// withAutosave — flagship composition: withInterval + withAsync\n// ---------------------------------------------------------------------------\n\nexport class AutosaveBehavior extends Behavior {\n /** Posting rides on withAsync (out-of-order completions discarded) */\n saver = withAsync(async (url: string, data: unknown) => {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} for ${url}`);\n }\n return response;\n });\n\n /** Scheduling rides on withInterval (assigned in onCreate, still collected) */\n timer!: IntervalBehavior;\n\n /** Current save endpoint — a one-way mirror of the url argument (this.sync) */\n url = '';\n\n private _data!: () => unknown;\n private _lastSaved: string | undefined = undefined;\n\n onCreate(url: MaybeGetter<string>, data: () => unknown, interval: MaybeGetter<number | null> = 5000) {\n this.url = this.sync(url);\n this._data = data;\n this.timer = withInterval(() => this.save(), interval);\n }\n\n /** Save now. Skipped when the payload hasn't changed since the last save. */\n save(): void {\n const payload = this._data();\n const serialized = JSON.stringify(payload);\n if (serialized === this._lastSaved) return;\n this._lastSaved = serialized;\n this.saver.run(this.url, payload);\n }\n\n get saving(): boolean {\n return this.saver.loading;\n }\n get error(): unknown {\n return this.saver.error;\n }\n}\n\n/**\n * Periodically POST a data snapshot as JSON while mounted, skipping saves\n * when nothing changed. A getter interval reschedules on change; resolving\n * to null pauses autosaving. Call save() for an immediate flush.\n *\n * @example autosave = withAutosave('/api/draft', () => this.doc, 5000);\n */\nexport const withAutosave = builtin(AutosaveBehavior);\n\nexport { toValue, type MaybeGetter } from '../reactive-args';\n"],"mappings":"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","names":["toValue"]}
@@ -0,0 +1,9 @@
1
+ // src/reactive-args.ts
2
+ function toValue(value) {
3
+ return typeof value === "function" ? value() : value;
4
+ }
5
+
6
+ export {
7
+ toValue
8
+ };
9
+ //# sourceMappingURL=chunk-LP5TAGSI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reactive-args.ts"],"sourcesContent":["/**\n * A value, or a getter producing it. Behavior arguments typed as MaybeGetter\n * let consumers choose between a snapshot and a live connection:\n *\n * ```tsx\n * fetcher = withFetch('/api/items'); // frozen at construction\n * fetcher = withFetch(() => this.props.url); // tracks prop changes\n * ```\n *\n * Rule of thumb for consumers: not sure which to pass? Pass the arrow — it is\n * never wrong, only occasionally redundant. Reserve plain values for things\n * you intend to freeze.\n *\n * Behavior authors normalize the argument once with `this.sync()` (into a\n * self-updating field), or read it in place with `toValue()` — the same\n * convention as Vue's MaybeRefOrGetter/toValue and Solid's\n * MaybeAccessor/access.\n *\n * Note: an argument that is legitimately a function (a callback rather than\n * a getter) cannot also be a MaybeGetter — the two are indistinguishable at\n * runtime. Choose per parameter.\n */\nexport type MaybeGetter<T> = T | (() => T);\n\n/**\n * Unwrap a MaybeGetter: call it if it is a getter, return it as-is otherwise.\n * Inside a tracked context (watch expression, effect body) calling the getter\n * also records its observable reads as dependencies — that is what makes a\n * getter argument live.\n */\nexport function toValue<T>(value: MaybeGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : value;\n}\n"],"mappings":";AA8BO,SAAS,QAAW,OAA0B;AACnD,SAAO,OAAO,UAAU,aAAc,MAAkB,IAAI;AAC9D;","names":[]}
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/reactive-args.ts
2
+ function toValue(value) {
3
+ return typeof value === "function" ? value() : value;
4
+ }
5
+
6
+
7
+
8
+ exports.toValue = toValue;
9
+ //# sourceMappingURL=chunk-WNF647U6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["c:\\Users\\me\\Documents\\Projects\\mobx-mantle\\dist\\chunk-WNF647U6.cjs"],"names":[],"mappings":"AAAA;AACA,SAAS,OAAO,CAAC,KAAK,EAAE;AACxB,EAAE,OAAO,OAAO,MAAM,IAAI,WAAW,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK;AACtD;AACA;AACA;AACE;AACF,0BAAC","file":"C:\\Users\\me\\Documents\\Projects\\mobx-mantle\\dist\\chunk-WNF647U6.cjs","sourcesContent":[null]}