@semantic-ui/reactivity
Advanced tools
Comparing version 0.0.16 to 0.0.17
{ | ||
"name": "@semantic-ui/reactivity", | ||
"version": "0.0.16", | ||
"type": "module", | ||
@@ -10,7 +9,8 @@ "main": "src/index.js", | ||
"dependencies": { | ||
"@semantic-ui/utils": "^0.0.16" | ||
"@semantic-ui/utils": "^0.0.17" | ||
}, | ||
"devDependencies": { | ||
"vitest": "^1.5.2" | ||
} | ||
}, | ||
"version": "0.0.17" | ||
} |
@@ -1,2 +0,2 @@ | ||
import { clone, isObject, isEqual, wrapFunction, findIndex, unique, isNumber } from '@semantic-ui/utils'; | ||
import { clone, isObject, isEqual, wrapFunction, isClassInstance, isArray, findIndex, unique, isNumber } from '@semantic-ui/utils'; | ||
import { Reaction } from './reaction.js'; | ||
@@ -7,11 +7,24 @@ import { Dependency } from './dependency.js'; | ||
constructor(initialValue, equalityFunction) { | ||
this.currentValue = this.clone(initialValue); | ||
constructor(initialValue, { equalityFunction, canClone = true, cloneFunction } = {}) { | ||
this.dependency = new Dependency(); | ||
this.equalityFunction = equalityFunction | ||
// allow user to opt out of value cloning | ||
this.canClone = canClone; | ||
// allow custom equality function | ||
this.equalityFunction = (equalityFunction) | ||
? wrapFunction(equalityFunction) | ||
: ReactiveVar.equalityFunction; | ||
: ReactiveVar.equalityFunction | ||
; | ||
// allow custom clone function | ||
this.clone = (cloneFunction) | ||
? wrapFunction(cloneFunction) | ||
: ReactiveVar.cloneFunction | ||
; | ||
this.currentValue = this.maybeClone(initialValue); | ||
} | ||
static equalityFunction = isEqual; | ||
static cloneFunction = clone; | ||
@@ -25,3 +38,3 @@ get value() { | ||
return (Array.isArray(value) || typeof value == 'object') | ||
? this.clone(value) | ||
? this.maybeClone(value) | ||
: value | ||
@@ -31,7 +44,14 @@ ; | ||
clone(value) { | ||
if (value instanceof ReactiveVar) { | ||
canCloneValue(value) { | ||
return (this.canClone === true && !isClassInstance(value)); | ||
} | ||
maybeClone(value) { | ||
if (!this.canCloneValue(value)) { | ||
return value; | ||
} | ||
return clone(value); | ||
if(isArray(value)) { | ||
return value = value.map(value => this.maybeClone(value)); | ||
} | ||
return this.clone(value); | ||
} | ||
@@ -41,3 +61,3 @@ | ||
if (!this.equalityFunction(this.currentValue, newValue)) { | ||
this.currentValue = this.clone(newValue); | ||
this.currentValue = this.maybeClone(newValue); | ||
this.dependency.changed({ value: newValue, trace: new Error().stack}); // Pass context | ||
@@ -74,3 +94,3 @@ } | ||
push(value) { | ||
let arr = this.value; | ||
const arr = this.value; | ||
arr.push(value); | ||
@@ -80,3 +100,3 @@ this.set(arr); | ||
unshift(value) { | ||
let arr = this.value; | ||
const arr = this.value; | ||
arr.unshift(value); | ||
@@ -86,3 +106,3 @@ this.set(arr); | ||
splice(...args) { | ||
let arr = this.value; | ||
const arr = this.value; | ||
arr.splice(...args); | ||
@@ -92,7 +112,7 @@ this.set(arr); | ||
map(mapFunction) { | ||
const newValue = this.map(mapFunction); | ||
const newValue = Array.prototype.map.call(this.currentValue, mapFunction); | ||
this.set(newValue); | ||
} | ||
filter(filterFunction) { | ||
const newValue = this.filter((value) => !filterFunction(value)); | ||
const newValue = Array.prototype.filter.call(this.currentValue, filterFunction); | ||
this.set(newValue); | ||
@@ -123,3 +143,3 @@ } | ||
} | ||
const newValue = this.clone(this.currentValue).map((object, currentIndex) => { | ||
const newValue = this.currentValue.map((object, currentIndex) => { | ||
if(index == 'all' || currentIndex == index) { | ||
@@ -163,5 +183,15 @@ object[property] = value; | ||
} | ||
setProperty(id, property, value) { | ||
const index = this.getIndex(id); | ||
return this.setArrayProperty(index, property, value); | ||
setProperty(idOrProperty, property, value) { | ||
if(arguments.length == 3) { | ||
const id = idOrProperty; | ||
const index = this.getIndex(id); | ||
return this.setArrayProperty(index, property, value); | ||
} | ||
else { | ||
value = property; | ||
property = idOrProperty; | ||
const obj = this.currentValue; | ||
obj[property] = value; | ||
this.set(obj); | ||
} | ||
} | ||
@@ -168,0 +198,0 @@ replaceItem(id, item) { |
@@ -75,3 +75,3 @@ import { beforeEach, describe, it, expect, vi } from 'vitest'; | ||
let obj = { name: 'Sally', age: 22 }; | ||
let reactiveObj = new ReactiveVar(obj, customIsEqual); | ||
let reactiveObj = new ReactiveVar(obj, { equalityFunction: customIsEqual }); | ||
const callback = vi.fn(); | ||
@@ -78,0 +78,0 @@ |
@@ -42,3 +42,3 @@ import { describe, it, expect, vi } from 'vitest'; | ||
}; | ||
const reactiveVar = new ReactiveVar('initial', isEqual); | ||
const reactiveVar = new ReactiveVar('initial', { equalityFunction: isEqual }); | ||
reactiveVar.value = 'initial'; | ||
@@ -263,11 +263,11 @@ reactiveVar.subscribe(callback); | ||
it('changeItems should apply a transformation to each item', () => { | ||
it('map should apply a transformation to each item', () => { | ||
const numbers = new ReactiveVar([1, 2, 3]); | ||
numbers.changeItems(num => num * 2); | ||
numbers.map(num => num * 2); | ||
expect(numbers.get()).toEqual([2, 4, 6]); | ||
}); | ||
it('removeItems should remove items based on a filter', () => { | ||
it('filter should remove items based on a filter', () => { | ||
const numbers = new ReactiveVar([1, 2, 3, 4, 5]); | ||
numbers.removeItems(num => num % 2 === 0); // Remove even numbers | ||
numbers.filter(num => num % 2 === 1); // Remove even numbers | ||
expect(numbers.get()).toEqual([1, 3, 5]); | ||
@@ -380,92 +380,56 @@ }); | ||
it('should maintain reactivity when using a ReactiveVar inside another ReactiveVar', () => { | ||
const callback = vi.fn(); | ||
const innerCallback = vi.fn(); | ||
const innerVar = new ReactiveVar(1); | ||
const outerCallback = vi.fn(); | ||
const outerVar = new ReactiveVar(innerVar); | ||
outerVar.subscribe(callback); | ||
outerVar.subscribe(outerCallback); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); | ||
expect(outerCallback).toHaveBeenCalledTimes(1); | ||
innerVar.subscribe(innerCallback); | ||
innerVar.set(2); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(2); | ||
expect(callback).toHaveBeenCalledWith(innerVar); | ||
expect(innerCallback).toHaveBeenCalledTimes(2); | ||
}); | ||
it('should maintain reactivity when using an array of ReactiveVars', () => { | ||
const callback = vi.fn(); | ||
const innerVar1 = new ReactiveVar(1); | ||
const innerVar2 = new ReactiveVar(2); | ||
const outerVar = new ReactiveVar([innerVar1, innerVar2]); | ||
outerVar.subscribe(callback); | ||
it('should not retrigger reactivity on array when updating objects', () => { | ||
const outerCallback = vi.fn(); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); | ||
const innerCallback1 = vi.fn(); | ||
const innerCallback2 = vi.fn(); | ||
innerVar1.set(3); | ||
Reaction.flush(); | ||
const data1 = { id: 1, text: 'test object'}; | ||
const data2 = { id: 2, text: 'test object 2'}; | ||
expect(callback).toHaveBeenCalledTimes(2); | ||
expect(callback).toHaveBeenCalledWith([innerVar1, innerVar2]); | ||
const innerVar1 = new ReactiveVar(data1); | ||
const innerVar2 = new ReactiveVar(data2); | ||
innerVar2.set(4); | ||
Reaction.flush(); | ||
innerVar1.subscribe(innerCallback1); | ||
innerVar2.subscribe(innerCallback2); | ||
expect(callback).toHaveBeenCalledTimes(3); | ||
expect(callback).toHaveBeenCalledWith([innerVar1, innerVar2]); | ||
}); | ||
const outerVar = new ReactiveVar([]); | ||
outerVar.subscribe(outerCallback); | ||
it('should not leak memory when cloning ReactiveVars', () => { | ||
const innerVar = new ReactiveVar(1); | ||
const outerVar = new ReactiveVar(innerVar); | ||
Reaction.flush(); | ||
expect(outerCallback).toHaveBeenCalledTimes(1); | ||
// Test case 1: Update the inner ReactiveVar | ||
const clonedInnerVar1 = outerVar.value; | ||
innerVar.set(2); | ||
expect(clonedInnerVar1.value).toBe(1); // Cloned value should not change | ||
outerVar.push(innerVar1); | ||
Reaction.flush(); | ||
expect(outerCallback).toHaveBeenCalledTimes(2); | ||
// Test case 2: Update the outer ReactiveVar | ||
const clonedInnerVar2 = outerVar.value; | ||
outerVar.set(new ReactiveVar(3)); | ||
expect(clonedInnerVar2.value).toBe(2); // Cloned value should not change | ||
// Test case 3: Subscribe and unsubscribe from the outer ReactiveVar | ||
const callback = vi.fn(); | ||
const subscription = outerVar.subscribe(callback); | ||
outerVar.set(new ReactiveVar(4)); | ||
outerVar.push(innerVar2); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); | ||
subscription.stop(); | ||
outerVar.set(new ReactiveVar(5)); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); // Callback should not be called after unsubscribing | ||
}); | ||
expect(outerCallback).toHaveBeenCalledTimes(3); | ||
it('should not leak memory when cloning arrays of ReactiveVars', () => { | ||
const innerVar1 = new ReactiveVar(1); | ||
const innerVar2 = new ReactiveVar(2); | ||
const outerVar = new ReactiveVar([innerVar1, innerVar2]); | ||
innerVar1.setProperty('text', 'hello world'); | ||
expect(innerCallback1).toHaveBeenCalledTimes(1); | ||
expect(outerCallback).toHaveBeenCalledTimes(3); | ||
// Test case 1: Update an inner ReactiveVar | ||
const clonedArray1 = outerVar.value; | ||
innerVar1.set(3); | ||
expect(clonedArray1[0].value).toBe(1); // Cloned value should not change | ||
innerVar2.setProperty('text', 'hello world 2'); | ||
expect(innerCallback2).toHaveBeenCalledTimes(1); | ||
expect(outerCallback).toHaveBeenCalledTimes(3); | ||
// Test case 2: Update the outer ReactiveVar | ||
const clonedArray2 = outerVar.value; | ||
outerVar.set([new ReactiveVar(4), new ReactiveVar(5)]); | ||
expect(clonedArray2[0].value).toBe(3); // Cloned value should not change | ||
expect(clonedArray2[1].value).toBe(2); // Cloned value should not change | ||
// Test case 3: Subscribe and unsubscribe from the outer ReactiveVar | ||
const callback = vi.fn(); | ||
const subscription = outerVar.subscribe(callback); | ||
outerVar.set([new ReactiveVar(6), new ReactiveVar(7)]); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); | ||
subscription.stop(); | ||
outerVar.set([new ReactiveVar(8), new ReactiveVar(9)]); | ||
Reaction.flush(); | ||
expect(callback).toHaveBeenCalledTimes(1); // Callback should not be called after unsubscribing | ||
}); | ||
@@ -472,0 +436,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42842
901
+ Added@semantic-ui/utils@0.0.17(transitive)
- Removed@semantic-ui/utils@0.0.16(transitive)
Updated@semantic-ui/utils@^0.0.17