@oscarpalmer/mora 0.14.0 → 0.16.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/batch.ts CHANGED
@@ -2,7 +2,7 @@ import {type Effect, runEffect} from './effect';
2
2
  import {isEffect} from './helpers/is';
3
3
  import type {Subscription} from './subscription';
4
4
 
5
- export function flushEffects(): void {
5
+ export function flushHandlers(): void {
6
6
  while (batchDepth === 0 && batchedHandlers.size > 0) {
7
7
  const handlers = [...batchedHandlers];
8
8
 
@@ -33,7 +33,7 @@ export function stopBatch(): void {
33
33
  batchDepth -= 1;
34
34
  }
35
35
 
36
- flushEffects();
36
+ flushHandlers();
37
37
  }
38
38
 
39
39
  export const batchedHandlers = new Set<Effect | Subscription<unknown>>();
package/src/helpers/is.ts CHANGED
@@ -3,7 +3,7 @@ import type {Effect} from '../effect';
3
3
  import type {ReactiveArray} from '../value/array';
4
4
  import type {Computed} from '../value/computed';
5
5
  import type {Reactive} from '../value/reactive';
6
- import {signal, type Signal} from '../value/signal';
6
+ import type {Signal} from '../value/signal';
7
7
 
8
8
  /**
9
9
  * Is the value a reactive array?
@@ -1,7 +1,62 @@
1
+ import {batchDepth, batchedHandlers, flushHandlers} from '../batch';
1
2
  import {activeEffect} from '../effect';
2
- import {activeComputed} from '../value/computed';
3
+ import {type InternalComputed, activeComputed} from '../value/computed';
3
4
  import type {ReactiveState} from '../value/reactive';
4
- import {emitValue} from './emit';
5
+
6
+ export function differentArrays(first: unknown[], second: unknown[]): boolean {
7
+ let {length} = first;
8
+
9
+ if (length !== second.length) {
10
+ return true;
11
+ }
12
+
13
+ let offset = 0;
14
+
15
+ if (length >= 100) {
16
+ offset = Math.round(length / 10);
17
+ offset = offset > 25 ? 25 : offset;
18
+
19
+ for (let index = 0; index < offset; index += 1) {
20
+ if (!Object.is(first[index], second[index])) {
21
+ return true;
22
+ }
23
+ }
24
+ }
25
+
26
+ length -= offset;
27
+
28
+ for (let index = offset; index < length; index += 1) {
29
+ if (!Object.is(first[index], second[index])) {
30
+ return true;
31
+ }
32
+ }
33
+
34
+ return false;
35
+ }
36
+
37
+ export function differentValues(first: unknown, second: unknown): boolean {
38
+ return Array.isArray(first) && Array.isArray(second)
39
+ ? differentArrays(first, second)
40
+ : !Object.is(first, second);
41
+ }
42
+
43
+ export function emitValue<Value>(state: ReactiveState<Value>): void {
44
+ for (const computed of state.computeds) {
45
+ (computed as unknown as InternalComputed).effect.dirty = true;
46
+ }
47
+
48
+ for (const effect of state.effects) {
49
+ batchedHandlers.add(effect);
50
+ }
51
+
52
+ for (const [, subscription] of state.subscriptions) {
53
+ batchedHandlers.add(subscription as never);
54
+ }
55
+
56
+ if (batchDepth === 0) {
57
+ flushHandlers();
58
+ }
59
+ }
5
60
 
6
61
  export function getValue<Value>(state: ReactiveState<Value>): Value {
7
62
  if (activeComputed != null) {
@@ -14,14 +69,3 @@ export function getValue<Value>(state: ReactiveState<Value>): Value {
14
69
 
15
70
  return state.value;
16
71
  }
17
-
18
- export function setValue<Value>(
19
- state: ReactiveState<Value>,
20
- value: Value,
21
- ): void {
22
- if (!Object.is(state.value, value)) {
23
- state.value = value;
24
-
25
- emitValue(state);
26
- }
27
- }
@@ -1,6 +1,6 @@
1
- import {emitArrayChanges, emitValue} from '../helpers/emit';
2
1
  import {arrayName} from '../helpers/is';
3
- import {getValue} from '../helpers/value';
2
+ import {differentArrays, emitValue, getValue} from '../helpers/value';
3
+ import {type Computed, computed} from './computed';
4
4
  import {Reactive, type ReactiveState} from './reactive';
5
5
  import {type Signal, signal} from './signal';
6
6
 
@@ -43,6 +43,20 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
43
43
  this.#size.set(value.length);
44
44
  }
45
45
 
46
+ /**
47
+ * Get an indexed item
48
+ */
49
+ at(index: number): Item | undefined {
50
+ return this.get().at(index);
51
+ }
52
+
53
+ /**
54
+ * Clear the array
55
+ */
56
+ clear(): void {
57
+ this.length = 0;
58
+ }
59
+
46
60
  /**
47
61
  * @inheritdoc
48
62
  */
@@ -50,6 +64,24 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
50
64
  return getValue(this.state);
51
65
  }
52
66
 
67
+ /**
68
+ * Create a computed, filtered array
69
+ */
70
+ filter(
71
+ callback: (item: Item, index: number, array: Item[]) => boolean,
72
+ ): Computed<Item[]> {
73
+ return computed(() => this.get().filter(callback));
74
+ }
75
+
76
+ /**
77
+ * Create a computed, mapped array
78
+ */
79
+ map<Mapped>(
80
+ callback: (item: Item, index: number, array: Item[]) => Mapped,
81
+ ): Computed<Mapped[]> {
82
+ return computed(() => this.get().map(callback));
83
+ }
84
+
53
85
  /**
54
86
  * @inheritdoc
55
87
  */
@@ -64,6 +96,20 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
64
96
  return length === true ? this.#size.peek() : [...this.state.value];
65
97
  }
66
98
 
99
+ /**
100
+ * Remove and return the last item of the array
101
+ */
102
+ pop(): Item | undefined {
103
+ return this.state.value.pop();
104
+ }
105
+
106
+ /**
107
+ * Add items to the end of the array
108
+ */
109
+ push(...items: Item[]): number {
110
+ return this.state.value.push(...items);
111
+ }
112
+
67
113
  /**
68
114
  * Set the value
69
115
  */
@@ -88,6 +134,31 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
88
134
  this.state.value[first] = second as Item;
89
135
  }
90
136
  }
137
+
138
+ /**
139
+ * Remove and return the first item of the array
140
+ */
141
+ shift(): Item | undefined {
142
+ return this.state.value.shift();
143
+ }
144
+
145
+ /**
146
+ * Remove and return items from the array _(and optionally add new items)_
147
+ */
148
+ splice(from: number, to?: number, ...items: Item[]): Item[] {
149
+ return this.state.value.splice(
150
+ from,
151
+ to ?? this.state.value.length,
152
+ ...items,
153
+ );
154
+ }
155
+
156
+ /**
157
+ * Add items to the beginning of the array
158
+ */
159
+ unshift(...items: Item[]): number {
160
+ return this.state.value.unshift(...items);
161
+ }
91
162
  }
92
163
 
93
164
  /**
@@ -142,7 +213,7 @@ function updateArray<Item>(
142
213
  if (
143
214
  affectsLength
144
215
  ? array.length !== previousLength
145
- : emitArrayChanges(previousArray, array)
216
+ : differentArrays(previousArray, array)
146
217
  ) {
147
218
  emitValue(state);
148
219
 
@@ -3,7 +3,7 @@ import {type Effect, activeEffect, effect, runEffect} from '../effect';
3
3
  import {computedName} from '../helpers/is';
4
4
  import {Reactive, type ReactiveState} from './reactive';
5
5
 
6
- type ComputedEffect = {
6
+ export type ComputedEffect = {
7
7
  dirty: boolean;
8
8
  instance: Effect;
9
9
  };
@@ -59,7 +59,7 @@ export class Computed<Value> extends Reactive<Value> {
59
59
  * @inheritdoc
60
60
  */
61
61
  get(): Value {
62
- if (activeComputed != null && activeComputed !== this) {
62
+ if (activeComputed != null && this !== activeComputed) {
63
63
  this.state.computeds.add(activeComputed);
64
64
  }
65
65
 
@@ -1,5 +1,5 @@
1
1
  import {signalName} from '../helpers/is';
2
- import {getValue, setValue} from '../helpers/value';
2
+ import {differentValues, emitValue, getValue} from '../helpers/value';
3
3
  import {Reactive} from './reactive';
4
4
 
5
5
  export class Signal<Value> extends Reactive<Value> {
@@ -18,7 +18,11 @@ export class Signal<Value> extends Reactive<Value> {
18
18
  * Set the value
19
19
  */
20
20
  set(value: Value): void {
21
- setValue(this.state, value);
21
+ if (differentValues(this.state.value, value)) {
22
+ this.state.value = value;
23
+
24
+ emitValue(this.state);
25
+ }
22
26
  }
23
27
 
24
28
  /**
package/types/batch.d.cts CHANGED
@@ -57,7 +57,7 @@ declare class Subscription<Value> {
57
57
  * Unsubscribe from changes
58
58
  */
59
59
  export type Unsubscribe = () => void;
60
- export declare function flushEffects(): void;
60
+ export declare function flushHandlers(): void;
61
61
  /**
62
62
  * Start batching effects _(use `stopBatch` to flush and run batched effects)_
63
63
  */
package/types/batch.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type Effect } from './effect';
2
2
  import type { Subscription } from './subscription';
3
- export declare function flushEffects(): void;
3
+ export declare function flushHandlers(): void;
4
4
  /**
5
5
  * Start batching effects _(use `stopBatch` to flush and run batched effects)_
6
6
  */
@@ -68,10 +68,26 @@ declare class ReactiveArray<Item> extends Reactive<Item[]> {
68
68
  */
69
69
  set length(value: number);
70
70
  constructor(value: Item[]);
71
+ /**
72
+ * Get an indexed item
73
+ */
74
+ at(index: number): Item | undefined;
75
+ /**
76
+ * Clear the array
77
+ */
78
+ clear(): void;
71
79
  /**
72
80
  * @inheritdoc
73
81
  */
74
82
  get(): Item[];
83
+ /**
84
+ * Create a computed, filtered array
85
+ */
86
+ filter(callback: (item: Item, index: number, array: Item[]) => boolean): Computed<Item[]>;
87
+ /**
88
+ * Create a computed, mapped array
89
+ */
90
+ map<Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped): Computed<Mapped[]>;
75
91
  /**
76
92
  * @inheritdoc
77
93
  */
@@ -80,6 +96,14 @@ declare class ReactiveArray<Item> extends Reactive<Item[]> {
80
96
  * Get the length of the array _(without reactivity)_
81
97
  */
82
98
  peek(length: true): number;
99
+ /**
100
+ * Remove and return the last item of the array
101
+ */
102
+ pop(): Item | undefined;
103
+ /**
104
+ * Add items to the end of the array
105
+ */
106
+ push(...items: Item[]): number;
83
107
  /**
84
108
  * Set the value
85
109
  */
@@ -92,6 +116,18 @@ declare class ReactiveArray<Item> extends Reactive<Item[]> {
92
116
  * Set the length of the array
93
117
  */
94
118
  set(property: "length", value: number): void;
119
+ /**
120
+ * Remove and return the first item of the array
121
+ */
122
+ shift(): Item | undefined;
123
+ /**
124
+ * Remove and return items from the array _(and optionally add new items)_
125
+ */
126
+ splice(from: number, to?: number, ...items: Item[]): Item[];
127
+ /**
128
+ * Add items to the beginning of the array
129
+ */
130
+ unshift(...items: Item[]): number;
95
131
  }
96
132
  declare class Signal<Value> extends Reactive<Value> {
97
133
  constructor(value: Value);
@@ -2,7 +2,7 @@ import type { Effect } from '../effect';
2
2
  import type { ReactiveArray } from '../value/array';
3
3
  import type { Computed } from '../value/computed';
4
4
  import type { Reactive } from '../value/reactive';
5
- import { type Signal } from '../value/signal';
5
+ import type { Signal } from '../value/signal';
6
6
  /**
7
7
  * Is the value a reactive array?
8
8
  */
@@ -57,7 +57,9 @@ declare class Subscription<Value> {
57
57
  * Unsubscribe from changes
58
58
  */
59
59
  export type Unsubscribe = () => void;
60
+ export declare function differentArrays(first: unknown[], second: unknown[]): boolean;
61
+ export declare function differentValues(first: unknown, second: unknown): boolean;
62
+ export declare function emitValue<Value>(state: ReactiveState<Value>): void;
60
63
  export declare function getValue<Value>(state: ReactiveState<Value>): Value;
61
- export declare function setValue<Value>(state: ReactiveState<Value>, value: Value): void;
62
64
 
63
65
  export {};
@@ -1,3 +1,5 @@
1
1
  import type { ReactiveState } from '../value/reactive';
2
+ export declare function differentArrays(first: unknown[], second: unknown[]): boolean;
3
+ export declare function differentValues(first: unknown, second: unknown): boolean;
4
+ export declare function emitValue<Value>(state: ReactiveState<Value>): void;
2
5
  export declare function getValue<Value>(state: ReactiveState<Value>): Value;
3
- export declare function setValue<Value>(state: ReactiveState<Value>, value: Value): void;
package/types/index.d.cts CHANGED
@@ -84,10 +84,26 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
84
84
  */
85
85
  set length(value: number);
86
86
  constructor(value: Item[]);
87
+ /**
88
+ * Get an indexed item
89
+ */
90
+ at(index: number): Item | undefined;
91
+ /**
92
+ * Clear the array
93
+ */
94
+ clear(): void;
87
95
  /**
88
96
  * @inheritdoc
89
97
  */
90
98
  get(): Item[];
99
+ /**
100
+ * Create a computed, filtered array
101
+ */
102
+ filter(callback: (item: Item, index: number, array: Item[]) => boolean): Computed<Item[]>;
103
+ /**
104
+ * Create a computed, mapped array
105
+ */
106
+ map<Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped): Computed<Mapped[]>;
91
107
  /**
92
108
  * @inheritdoc
93
109
  */
@@ -96,6 +112,14 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
96
112
  * Get the length of the array _(without reactivity)_
97
113
  */
98
114
  peek(length: true): number;
115
+ /**
116
+ * Remove and return the last item of the array
117
+ */
118
+ pop(): Item | undefined;
119
+ /**
120
+ * Add items to the end of the array
121
+ */
122
+ push(...items: Item[]): number;
99
123
  /**
100
124
  * Set the value
101
125
  */
@@ -108,6 +132,18 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
108
132
  * Set the length of the array
109
133
  */
110
134
  set(property: "length", value: number): void;
135
+ /**
136
+ * Remove and return the first item of the array
137
+ */
138
+ shift(): Item | undefined;
139
+ /**
140
+ * Remove and return items from the array _(and optionally add new items)_
141
+ */
142
+ splice(from: number, to?: number, ...items: Item[]): Item[];
143
+ /**
144
+ * Add items to the beginning of the array
145
+ */
146
+ unshift(...items: Item[]): number;
111
147
  }
112
148
  /**
113
149
  * Create a reactive array
@@ -68,10 +68,26 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
68
68
  */
69
69
  set length(value: number);
70
70
  constructor(value: Item[]);
71
+ /**
72
+ * Get an indexed item
73
+ */
74
+ at(index: number): Item | undefined;
75
+ /**
76
+ * Clear the array
77
+ */
78
+ clear(): void;
71
79
  /**
72
80
  * @inheritdoc
73
81
  */
74
82
  get(): Item[];
83
+ /**
84
+ * Create a computed, filtered array
85
+ */
86
+ filter(callback: (item: Item, index: number, array: Item[]) => boolean): Computed<Item[]>;
87
+ /**
88
+ * Create a computed, mapped array
89
+ */
90
+ map<Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped): Computed<Mapped[]>;
75
91
  /**
76
92
  * @inheritdoc
77
93
  */
@@ -80,6 +96,14 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
80
96
  * Get the length of the array _(without reactivity)_
81
97
  */
82
98
  peek(length: true): number;
99
+ /**
100
+ * Remove and return the last item of the array
101
+ */
102
+ pop(): Item | undefined;
103
+ /**
104
+ * Add items to the end of the array
105
+ */
106
+ push(...items: Item[]): number;
83
107
  /**
84
108
  * Set the value
85
109
  */
@@ -92,6 +116,18 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
92
116
  * Set the length of the array
93
117
  */
94
118
  set(property: "length", value: number): void;
119
+ /**
120
+ * Remove and return the first item of the array
121
+ */
122
+ shift(): Item | undefined;
123
+ /**
124
+ * Remove and return items from the array _(and optionally add new items)_
125
+ */
126
+ splice(from: number, to?: number, ...items: Item[]): Item[];
127
+ /**
128
+ * Add items to the beginning of the array
129
+ */
130
+ unshift(...items: Item[]): number;
95
131
  }
96
132
  /**
97
133
  * Create a reactive array
@@ -1,3 +1,4 @@
1
+ import { type Computed } from './computed';
1
2
  import { Reactive } from './reactive';
2
3
  export declare class ReactiveArray<Item> extends Reactive<Item[]> {
3
4
  #private;
@@ -10,10 +11,26 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
10
11
  */
11
12
  set length(value: number);
12
13
  constructor(value: Item[]);
14
+ /**
15
+ * Get an indexed item
16
+ */
17
+ at(index: number): Item | undefined;
18
+ /**
19
+ * Clear the array
20
+ */
21
+ clear(): void;
13
22
  /**
14
23
  * @inheritdoc
15
24
  */
16
25
  get(): Item[];
26
+ /**
27
+ * Create a computed, filtered array
28
+ */
29
+ filter(callback: (item: Item, index: number, array: Item[]) => boolean): Computed<Item[]>;
30
+ /**
31
+ * Create a computed, mapped array
32
+ */
33
+ map<Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped): Computed<Mapped[]>;
17
34
  /**
18
35
  * @inheritdoc
19
36
  */
@@ -22,6 +39,14 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
22
39
  * Get the length of the array _(without reactivity)_
23
40
  */
24
41
  peek(length: true): number;
42
+ /**
43
+ * Remove and return the last item of the array
44
+ */
45
+ pop(): Item | undefined;
46
+ /**
47
+ * Add items to the end of the array
48
+ */
49
+ push(...items: Item[]): number;
25
50
  /**
26
51
  * Set the value
27
52
  */
@@ -34,6 +59,18 @@ export declare class ReactiveArray<Item> extends Reactive<Item[]> {
34
59
  * Set the length of the array
35
60
  */
36
61
  set(property: 'length', value: number): void;
62
+ /**
63
+ * Remove and return the first item of the array
64
+ */
65
+ shift(): Item | undefined;
66
+ /**
67
+ * Remove and return items from the array _(and optionally add new items)_
68
+ */
69
+ splice(from: number, to?: number, ...items: Item[]): Item[];
70
+ /**
71
+ * Add items to the beginning of the array
72
+ */
73
+ unshift(...items: Item[]): number;
37
74
  }
38
75
  /**
39
76
  * Create a reactive array
@@ -1,6 +1,6 @@
1
1
  import { type Effect } from '../effect';
2
2
  import { Reactive, type ReactiveState } from './reactive';
3
- type ComputedEffect = {
3
+ export type ComputedEffect = {
4
4
  dirty: boolean;
5
5
  instance: Effect;
6
6
  };
@@ -21,4 +21,3 @@ export declare class Computed<Value> extends Reactive<Value> {
21
21
  * Create a computed value
22
22
  */
23
23
  export declare function computed<Value>(callback: () => Value): Computed<Value>;
24
- export {};
@@ -1,42 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const batch = require("../batch.cjs");
4
- function emitArrayChanges(first, second) {
5
- let { length } = first;
6
- if (length !== second.length) {
7
- return true;
8
- }
9
- let offset = 0;
10
- if (length >= 100) {
11
- offset = Math.round(length / 10);
12
- offset = offset > 25 ? 25 : offset;
13
- for (let index = 0; index < offset; index += 1) {
14
- if (!Object.is(first[index], second[index])) {
15
- return true;
16
- }
17
- }
18
- }
19
- length -= offset;
20
- for (let index = offset; index < length; index += 1) {
21
- if (!Object.is(first[index], second[index])) {
22
- return true;
23
- }
24
- }
25
- return false;
26
- }
27
- function emitValue(state) {
28
- for (const computed of state.computeds) {
29
- computed.effect.dirty = true;
30
- }
31
- for (const effect of state.effects) {
32
- batch.batchedHandlers.add(effect);
33
- }
34
- for (const [, subscription] of state.subscriptions) {
35
- batch.batchedHandlers.add(subscription);
36
- }
37
- if (batch.batchDepth === 0) {
38
- batch.flushEffects();
39
- }
40
- }
41
- exports.emitArrayChanges = emitArrayChanges;
42
- exports.emitValue = emitValue;
@@ -1,42 +0,0 @@
1
- import { batchedHandlers, batchDepth, flushEffects } from "../batch.js";
2
- function emitArrayChanges(first, second) {
3
- let { length } = first;
4
- if (length !== second.length) {
5
- return true;
6
- }
7
- let offset = 0;
8
- if (length >= 100) {
9
- offset = Math.round(length / 10);
10
- offset = offset > 25 ? 25 : offset;
11
- for (let index = 0; index < offset; index += 1) {
12
- if (!Object.is(first[index], second[index])) {
13
- return true;
14
- }
15
- }
16
- }
17
- length -= offset;
18
- for (let index = offset; index < length; index += 1) {
19
- if (!Object.is(first[index], second[index])) {
20
- return true;
21
- }
22
- }
23
- return false;
24
- }
25
- function emitValue(state) {
26
- for (const computed of state.computeds) {
27
- computed.effect.dirty = true;
28
- }
29
- for (const effect of state.effects) {
30
- batchedHandlers.add(effect);
31
- }
32
- for (const [, subscription] of state.subscriptions) {
33
- batchedHandlers.add(subscription);
34
- }
35
- if (batchDepth === 0) {
36
- flushEffects();
37
- }
38
- }
39
- export {
40
- emitArrayChanges,
41
- emitValue
42
- };