@api-client/ui 0.1.6 → 0.1.7
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/build/src/reactive/reactive.d.ts.map +1 -1
- package/build/src/reactive/reactive.js +43 -1
- package/build/src/reactive/reactive.js.map +1 -1
- package/demo/elements/md/notification/snack.html +1 -1
- package/demo/elements/user/user-avatar.ts +4 -0
- package/package.json +1 -1
- package/src/reactive/reactive.ts +43 -1
- package/test/core/live_data.spec.ts +33 -3
- package/test/elements/data-table/DataTable.browser.test.ts +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;CAClB;AAOD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,EAC7C,IAAI,SAAK,GACR,CAAC,
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;CAClB;AAOD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,EAC7C,IAAI,SAAK,GACR,CAAC,CA2GH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAQ9D"}
|
|
@@ -12,17 +12,59 @@ export function createReactive(target, onChange, path = '') {
|
|
|
12
12
|
}
|
|
13
13
|
const handler = {
|
|
14
14
|
get(target, prop, receiver) {
|
|
15
|
+
// Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.
|
|
15
16
|
const value = Reflect.get(target, prop, receiver);
|
|
17
|
+
// If the retrieved value is a function, bind it to the original 'target' object.
|
|
18
|
+
// This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called
|
|
19
|
+
// with the correct 'this' context, which is the original unwrapped object.
|
|
20
|
+
// Special handling for Set and Map mutating methods
|
|
21
|
+
if (target instanceof Set) {
|
|
22
|
+
if (['add', 'delete', 'clear'].includes(String(prop))) {
|
|
23
|
+
return (...args) => {
|
|
24
|
+
const oldValue = new Set(target); // shallow copy before mutation
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
26
|
+
const result = value.apply(target, args);
|
|
27
|
+
onChange({
|
|
28
|
+
path,
|
|
29
|
+
value: target,
|
|
30
|
+
oldValue,
|
|
31
|
+
});
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (target instanceof Map) {
|
|
37
|
+
if (['set', 'delete', 'clear'].includes(String(prop))) {
|
|
38
|
+
return (...args) => {
|
|
39
|
+
const oldValue = new Map(target); // shallow copy before mutation
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
41
|
+
const result = value.apply(target, args);
|
|
42
|
+
onChange({
|
|
43
|
+
path,
|
|
44
|
+
value: target,
|
|
45
|
+
oldValue,
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === 'function' && !Array.isArray(target)) {
|
|
52
|
+
// If the value is a function, bind it to the original target object.
|
|
53
|
+
return value.bind(target);
|
|
54
|
+
}
|
|
55
|
+
// If the value is an object (and not null, and not a function due to the check above),
|
|
56
|
+
// then recursively create a reactive proxy for it.
|
|
16
57
|
if (typeof value === 'object' && value !== null) {
|
|
17
58
|
const finalPath = path ? `${path}.${String(prop)}` : String(prop);
|
|
18
59
|
return createReactive(value, onChange, finalPath);
|
|
19
60
|
}
|
|
61
|
+
// For primitive values or null, return the value directly.
|
|
20
62
|
return value;
|
|
21
63
|
},
|
|
22
64
|
set(target, prop, value, receiver) {
|
|
23
65
|
const oldValue = Reflect.get(target, prop, receiver);
|
|
24
66
|
const success = Reflect.set(target, prop, value); // do not set the receiver here
|
|
25
|
-
//
|
|
67
|
+
// The receiver is the proxy itself and if we set the value on the proxy, it will
|
|
26
68
|
// create an infinite loop.
|
|
27
69
|
if (success && oldValue !== value) {
|
|
28
70
|
// Check if the old value was proxied and remove it from the cache
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"AAMA,kBAAkB;AAClB,MAAM,SAAS,GAAG,IAAI,OAAO,EAAkB,CAAA;AAC/C,kBAAkB;AAClB,MAAM,aAAa,GAAG,IAAI,OAAO,EAAkB,CAAA;AAEnD,MAAM,UAAU,cAAc,CAC5B,MAAS,EACT,QAA6C,EAC7C,IAAI,GAAG,EAAE;IAET,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,gBAAgB;QAChB,OAAO,SAAS,CAAC,GAAG,CAAC,MAAM,CAAM,CAAA,CAAC,wBAAwB;IAC5D,CAAC;IACD,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,MAAM,OAAO,GAAoB;QAC/B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACjE,OAAO,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;YACnD,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ;YAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACpD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA,CAAC,+BAA+B;YAChF,
|
|
1
|
+
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../../src/reactive/reactive.ts"],"names":[],"mappings":"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","sourcesContent":["export interface ChangeEventDetail {\n path: string\n value: unknown\n oldValue: unknown\n}\n\n// target -> Proxy\nconst cachedRaw = new WeakMap<object, object>()\n// Proxy -> target\nconst cachedProxies = new WeakMap<object, object>()\n\nexport function createReactive<T extends object>(\n target: T,\n onChange: (detail: ChangeEventDetail) => void,\n path = ''\n): T {\n if (cachedRaw.has(target)) {\n // return target\n return cachedRaw.get(target) as T // Return existing proxy\n }\n if (cachedProxies.has(target)) {\n return target\n }\n\n const handler: ProxyHandler<T> = {\n get(target, prop, receiver) {\n // Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.\n const value = Reflect.get(target, prop, receiver)\n // If the retrieved value is a function, bind it to the original 'target' object.\n // This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called\n // with the correct 'this' context, which is the original unwrapped object.\n // Special handling for Set and Map mutating methods\n if (target instanceof Set) {\n if (['add', 'delete', 'clear'].includes(String(prop))) {\n return (...args: unknown[]) => {\n const oldValue = new Set(target) // shallow copy before mutation\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n const result = (value as Function).apply(target, args)\n onChange({\n path,\n value: target,\n oldValue,\n })\n return result\n }\n }\n }\n if (target instanceof Map) {\n if (['set', 'delete', 'clear'].includes(String(prop))) {\n return (...args: unknown[]) => {\n const oldValue = new Map(target) // shallow copy before mutation\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n const result = (value as Function).apply(target, args)\n onChange({\n path,\n value: target,\n oldValue,\n })\n return result\n }\n }\n }\n if (typeof value === 'function' && !Array.isArray(target)) {\n // If the value is a function, bind it to the original target object.\n return value.bind(target)\n }\n // If the value is an object (and not null, and not a function due to the check above),\n // then recursively create a reactive proxy for it.\n if (typeof value === 'object' && value !== null) {\n const finalPath = path ? `${path}.${String(prop)}` : String(prop)\n return createReactive(value, onChange, finalPath)\n }\n // For primitive values or null, return the value directly.\n return value\n },\n set(target, prop, value, receiver) {\n const oldValue = Reflect.get(target, prop, receiver)\n const success = Reflect.set(target, prop, value) // do not set the receiver here\n // The receiver is the proxy itself and if we set the value on the proxy, it will\n // create an infinite loop.\n if (success && oldValue !== value) {\n // Check if the old value was proxied and remove it from the cache\n if (typeof oldValue === 'object' && oldValue !== null) {\n const oldProxy = cachedRaw.get(oldValue)\n if (oldProxy) {\n cachedRaw.delete(oldValue)\n cachedProxies.delete(oldProxy)\n }\n }\n onChange({\n path: path ? `${path}.${String(prop)}` : String(prop),\n value,\n oldValue,\n })\n }\n return success\n },\n deleteProperty(target, prop) {\n const oldValue = Reflect.get(target, prop)\n const success = Reflect.deleteProperty(target, prop)\n if (success) {\n // Check if the old value was proxied and remove it from the cache\n if (typeof oldValue === 'object' && oldValue !== null) {\n const oldProxy = cachedRaw.get(oldValue)\n if (oldProxy) {\n cachedRaw.delete(oldValue)\n cachedProxies.delete(oldProxy)\n }\n }\n onChange({\n path: path ? `${path}.${String(prop)}` : String(prop),\n value: undefined,\n oldValue,\n })\n }\n return success\n },\n }\n const proxy = new Proxy(target, handler)\n cachedRaw.set(target, proxy)\n cachedProxies.set(proxy, target)\n return proxy\n}\n\n/**\n * Removes the proxy and cleans up the cached data.\n *\n * @param reactiveObject The reactive object to unhook.\n * @returns true if the object was reactive and was unhooked, false otherwise.\n */\nexport function unhookReactive(reactiveObject: object): boolean {\n const raw = cachedProxies.get(reactiveObject)\n if (!raw) {\n return false // Not a reactive object\n }\n cachedRaw.delete(raw)\n cachedProxies.delete(reactiveObject)\n return true\n}\n"]}
|
|
@@ -22,6 +22,8 @@ class ComponentDemoPage extends DemoPage {
|
|
|
22
22
|
kind: UserKind,
|
|
23
23
|
status: 'active',
|
|
24
24
|
grantType: 'editor',
|
|
25
|
+
created: new Date('2023-01-01T00:00:00Z').getTime(),
|
|
26
|
+
updated: new Date('2023-01-02T00:00:00Z').getTime(),
|
|
25
27
|
}
|
|
26
28
|
this.user2 = {
|
|
27
29
|
key: '2',
|
|
@@ -30,6 +32,8 @@ class ComponentDemoPage extends DemoPage {
|
|
|
30
32
|
kind: UserKind,
|
|
31
33
|
status: 'active',
|
|
32
34
|
grantType: 'editor',
|
|
35
|
+
created: new Date('2023-01-01T00:00:00Z').getTime(),
|
|
36
|
+
updated: new Date('2023-01-02T00:00:00Z').getTime(),
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
|
package/package.json
CHANGED
package/src/reactive/reactive.ts
CHANGED
|
@@ -24,17 +24,59 @@ export function createReactive<T extends object>(
|
|
|
24
24
|
|
|
25
25
|
const handler: ProxyHandler<T> = {
|
|
26
26
|
get(target, prop, receiver) {
|
|
27
|
+
// Retrieve the original value. If 'prop' is a getter, 'receiver' (the proxy) is used as 'this'.
|
|
27
28
|
const value = Reflect.get(target, prop, receiver)
|
|
29
|
+
// If the retrieved value is a function, bind it to the original 'target' object.
|
|
30
|
+
// This ensures that methods (e.g., Set.prototype.add, Map.prototype.set) are called
|
|
31
|
+
// with the correct 'this' context, which is the original unwrapped object.
|
|
32
|
+
// Special handling for Set and Map mutating methods
|
|
33
|
+
if (target instanceof Set) {
|
|
34
|
+
if (['add', 'delete', 'clear'].includes(String(prop))) {
|
|
35
|
+
return (...args: unknown[]) => {
|
|
36
|
+
const oldValue = new Set(target) // shallow copy before mutation
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
38
|
+
const result = (value as Function).apply(target, args)
|
|
39
|
+
onChange({
|
|
40
|
+
path,
|
|
41
|
+
value: target,
|
|
42
|
+
oldValue,
|
|
43
|
+
})
|
|
44
|
+
return result
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (target instanceof Map) {
|
|
49
|
+
if (['set', 'delete', 'clear'].includes(String(prop))) {
|
|
50
|
+
return (...args: unknown[]) => {
|
|
51
|
+
const oldValue = new Map(target) // shallow copy before mutation
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
53
|
+
const result = (value as Function).apply(target, args)
|
|
54
|
+
onChange({
|
|
55
|
+
path,
|
|
56
|
+
value: target,
|
|
57
|
+
oldValue,
|
|
58
|
+
})
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (typeof value === 'function' && !Array.isArray(target)) {
|
|
64
|
+
// If the value is a function, bind it to the original target object.
|
|
65
|
+
return value.bind(target)
|
|
66
|
+
}
|
|
67
|
+
// If the value is an object (and not null, and not a function due to the check above),
|
|
68
|
+
// then recursively create a reactive proxy for it.
|
|
28
69
|
if (typeof value === 'object' && value !== null) {
|
|
29
70
|
const finalPath = path ? `${path}.${String(prop)}` : String(prop)
|
|
30
71
|
return createReactive(value, onChange, finalPath)
|
|
31
72
|
}
|
|
73
|
+
// For primitive values or null, return the value directly.
|
|
32
74
|
return value
|
|
33
75
|
},
|
|
34
76
|
set(target, prop, value, receiver) {
|
|
35
77
|
const oldValue = Reflect.get(target, prop, receiver)
|
|
36
78
|
const success = Reflect.set(target, prop, value) // do not set the receiver here
|
|
37
|
-
//
|
|
79
|
+
// The receiver is the proxy itself and if we set the value on the proxy, it will
|
|
38
80
|
// create an infinite loop.
|
|
39
81
|
if (success && oldValue !== value) {
|
|
40
82
|
// Check if the old value was proxied and remove it from the cache
|
|
@@ -61,10 +61,40 @@ describe('LiveData Basic', () => {
|
|
|
61
61
|
|
|
62
62
|
liveData.value.items.push('orange')
|
|
63
63
|
|
|
64
|
-
assert.equal(spy.callCount, 1)
|
|
64
|
+
assert.equal(spy.callCount, 1, 'called once for the push')
|
|
65
65
|
const args = spy.args[0][0].detail
|
|
66
|
-
assert.equal(args.path, 'items.2')
|
|
67
|
-
assert.equal(args.value, 'orange')
|
|
66
|
+
assert.equal(args.path, 'items.2', 'path is correct')
|
|
67
|
+
assert.equal(args.value, 'orange', 'value is correct')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should handle sets', () => {
|
|
71
|
+
const data = { set: new Set(['apple', 'banana']) }
|
|
72
|
+
const liveData = new LiveData(data)
|
|
73
|
+
|
|
74
|
+
const spy = sinon.spy()
|
|
75
|
+
liveData.addEventListener('update', spy)
|
|
76
|
+
|
|
77
|
+
liveData.value.set.add('orange')
|
|
78
|
+
|
|
79
|
+
assert.equal(spy.callCount, 1, 'called once for the add')
|
|
80
|
+
const args = spy.args[0][0].detail
|
|
81
|
+
assert.equal(args.path, 'set', 'path is correct')
|
|
82
|
+
assert.deepEqual(args.value, data.set, 'value is correct')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should handle maps', () => {
|
|
86
|
+
const data = { map: new Map([['a', 'b']]) }
|
|
87
|
+
const liveData = new LiveData(data)
|
|
88
|
+
|
|
89
|
+
const spy = sinon.spy()
|
|
90
|
+
liveData.addEventListener('update', spy)
|
|
91
|
+
|
|
92
|
+
liveData.value.map.set('c', 'd')
|
|
93
|
+
|
|
94
|
+
assert.equal(spy.callCount, 1, 'called once for the add')
|
|
95
|
+
const args = spy.args[0][0].detail
|
|
96
|
+
assert.equal(args.path, 'map', 'path is correct')
|
|
97
|
+
assert.deepEqual(args.value, data.map, 'value is correct')
|
|
68
98
|
})
|
|
69
99
|
|
|
70
100
|
it('should handle arrays with array index', () => {
|
|
@@ -73,11 +73,8 @@ describe('DataTable', () => {
|
|
|
73
73
|
describe('Basic tests', () => {
|
|
74
74
|
it('renders the table with correct number of rows and columns', async () => {
|
|
75
75
|
const items = generateItems(3)
|
|
76
|
-
// console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
|
|
77
76
|
const element = await basicFixture<TestItem>(items)
|
|
78
|
-
// console.log('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')
|
|
79
77
|
await nextFrame()
|
|
80
|
-
// console.log('ccccccccccccccccccccccccccccccccccc')
|
|
81
78
|
const rows = element.shadowRoot!.querySelectorAll('tr')
|
|
82
79
|
assert.equal(rows.length, items.value.length + 1, 'has all rows') // +1 for header row
|
|
83
80
|
|