@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.
@@ -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,CAiEH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAQ9D"}
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
- // he receiver is the proxy itself and if we set the value on the proxy, it will
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":"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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 const value = Reflect.get(target, prop, receiver)\n if (typeof value === 'object' && value !== null) {\n const finalPath = path ? `${path}.${String(prop)}` : String(prop)\n return createReactive(value, onChange, finalPath)\n }\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 // he 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"]}
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"]}
@@ -16,6 +16,6 @@
16
16
  <body>
17
17
  <div id="app"></div>
18
18
 
19
- <script type="module" src="./snack.ts"></script>
19
+ <script type="module" src="../../../../.tmp/demo/demo/elements/md/notification/snack.js"></script>
20
20
  </body>
21
21
  </html>
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -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
- // he receiver is the proxy itself and if we set the value on the proxy, it will
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