@stencil/store 2.1.3 → 2.2.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.
package/dist/index.cjs CHANGED
@@ -78,6 +78,8 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
78
78
  set: [],
79
79
  reset: [],
80
80
  };
81
+ // Track onChange listeners to enable removeListener functionality
82
+ const changeListeners = new Map();
81
83
  const reset = () => {
82
84
  // When resetting the state, the default state may be a function - unwrap it to invoke it.
83
85
  // otherwise, the state won't be properly reset
@@ -131,17 +133,21 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
131
133
  };
132
134
  };
133
135
  const onChange = (propName, cb) => {
134
- const unSet = on('set', (key, newValue) => {
136
+ const setHandler = (key, newValue) => {
135
137
  if (key === propName) {
136
138
  cb(newValue);
137
139
  }
138
- });
139
- // We need to unwrap the defaultState because it might be a function.
140
- // Otherwise we might not be sending the right reset value.
141
- const unReset = on('reset', () => cb(unwrap(defaultState)[propName]));
140
+ };
141
+ const resetHandler = () => cb(unwrap(defaultState)[propName]);
142
+ // Register the handlers
143
+ const unSet = on('set', setHandler);
144
+ const unReset = on('reset', resetHandler);
145
+ // Track the relationship between the user callback and internal handlers
146
+ changeListeners.set(cb, { setHandler, resetHandler, propName });
142
147
  return () => {
143
148
  unSet();
144
149
  unReset();
150
+ changeListeners.delete(cb);
145
151
  };
146
152
  };
147
153
  const use = (...subscriptions) => {
@@ -166,6 +172,15 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
166
172
  const oldValue = states.get(key);
167
173
  handlers.set.forEach((cb) => cb(key, oldValue, oldValue));
168
174
  };
175
+ const removeListener = (propName, listener) => {
176
+ const listenerInfo = changeListeners.get(listener);
177
+ if (listenerInfo && listenerInfo.propName === propName) {
178
+ // Remove the specific handlers that were created for this listener
179
+ removeFromArray(handlers.set, listenerInfo.setHandler);
180
+ removeFromArray(handlers.reset, listenerInfo.resetHandler);
181
+ changeListeners.delete(listener);
182
+ }
183
+ };
169
184
  return {
170
185
  state,
171
186
  get,
@@ -176,6 +191,7 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
176
191
  dispose,
177
192
  reset,
178
193
  forceUpdate,
194
+ removeListener,
179
195
  };
180
196
  };
181
197
  const removeFromArray = (array, item) => {
package/dist/index.js CHANGED
@@ -76,6 +76,8 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
76
76
  set: [],
77
77
  reset: [],
78
78
  };
79
+ // Track onChange listeners to enable removeListener functionality
80
+ const changeListeners = new Map();
79
81
  const reset = () => {
80
82
  // When resetting the state, the default state may be a function - unwrap it to invoke it.
81
83
  // otherwise, the state won't be properly reset
@@ -129,17 +131,21 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
129
131
  };
130
132
  };
131
133
  const onChange = (propName, cb) => {
132
- const unSet = on('set', (key, newValue) => {
134
+ const setHandler = (key, newValue) => {
133
135
  if (key === propName) {
134
136
  cb(newValue);
135
137
  }
136
- });
137
- // We need to unwrap the defaultState because it might be a function.
138
- // Otherwise we might not be sending the right reset value.
139
- const unReset = on('reset', () => cb(unwrap(defaultState)[propName]));
138
+ };
139
+ const resetHandler = () => cb(unwrap(defaultState)[propName]);
140
+ // Register the handlers
141
+ const unSet = on('set', setHandler);
142
+ const unReset = on('reset', resetHandler);
143
+ // Track the relationship between the user callback and internal handlers
144
+ changeListeners.set(cb, { setHandler, resetHandler, propName });
140
145
  return () => {
141
146
  unSet();
142
147
  unReset();
148
+ changeListeners.delete(cb);
143
149
  };
144
150
  };
145
151
  const use = (...subscriptions) => {
@@ -164,6 +170,15 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
164
170
  const oldValue = states.get(key);
165
171
  handlers.set.forEach((cb) => cb(key, oldValue, oldValue));
166
172
  };
173
+ const removeListener = (propName, listener) => {
174
+ const listenerInfo = changeListeners.get(listener);
175
+ if (listenerInfo && listenerInfo.propName === propName) {
176
+ // Remove the specific handlers that were created for this listener
177
+ removeFromArray(handlers.set, listenerInfo.setHandler);
178
+ removeFromArray(handlers.reset, listenerInfo.resetHandler);
179
+ changeListeners.delete(listener);
180
+ }
181
+ };
167
182
  return {
168
183
  state,
169
184
  get,
@@ -174,6 +189,7 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
174
189
  dispose,
175
190
  reset,
176
191
  forceUpdate,
192
+ removeListener,
177
193
  };
178
194
  };
179
195
  const removeFromArray = (array, item) => {
package/dist/types.d.ts CHANGED
@@ -40,11 +40,15 @@ export interface ObservableMap<T> {
40
40
  * Note: Proxy objects are not supported by IE11 (not even with a polyfill)
41
41
  * so you need to use the store.get and store.set methods of the API if you wish to support IE11.
42
42
  *
43
+ * @returns The proxied object
43
44
  */
44
45
  state: T;
45
46
  /**
46
47
  * Only useful if you need to support IE11.
47
48
  *
49
+ * @param propName - The property name to get
50
+ * @returns The value of the property
51
+ *
48
52
  * @example
49
53
  * const { state, get } = createStore({ hola: 'hello', adios: 'goodbye' });
50
54
  * console.log(state.hola); // If you don't need to support IE11, use this way.
@@ -54,6 +58,10 @@ export interface ObservableMap<T> {
54
58
  /**
55
59
  * Only useful if you need to support IE11.
56
60
  *
61
+ * @param propName - The property name to set
62
+ * @param value - The value to set
63
+ * @returns void
64
+ *
57
65
  * @example
58
66
  * const { state, get } = createStore({ hola: 'hello', adios: 'goodbye' });
59
67
  * state.hola = 'ola'; // If you don't need to support IE11, use this way.
@@ -63,6 +71,10 @@ export interface ObservableMap<T> {
63
71
  /**
64
72
  * Register a event listener, you can listen to `set`, `get` and `reset` events.
65
73
  *
74
+ * @param eventName - The event name to listen for
75
+ * @param callback - The callback to call when the event occurs
76
+ * @returns A function to unsubscribe from the listener
77
+ *
66
78
  * @example
67
79
  * store.on('set', (prop, value) => {
68
80
  * console.log(`Prop ${prop} changed to: ${value}`);
@@ -71,6 +83,10 @@ export interface ObservableMap<T> {
71
83
  on: OnHandler<T>;
72
84
  /**
73
85
  * Easily listen for value changes of the specified key.
86
+ *
87
+ * @param propName - The property name to listen for
88
+ * @param cb - The callback to call when the property changes
89
+ * @returns A function to unsubscribe from the listener
74
90
  */
75
91
  onChange: OnChangeHandler<T>;
76
92
  /**
@@ -79,19 +95,37 @@ export interface ObservableMap<T> {
79
95
  *
80
96
  * This method is intended for plugins to reset
81
97
  * all their internal state between tests.
98
+ *
99
+ * @returns void
82
100
  */
83
101
  dispose(): void;
84
102
  /**
85
103
  * Resets the state to its original state.
104
+ *
105
+ * @returns void
86
106
  */
87
107
  reset(): void;
88
108
  /**
89
109
  * Registers a subscription that will be called whenever the user gets, sets, or
90
110
  * resets a value.
111
+ *
112
+ * @param plugins - The plugins to use
113
+ * @returns A function to unsubscribe from the plugins
91
114
  */
92
115
  use(...plugins: Subscription<T>[]): () => void;
93
116
  /**
94
117
  * Force a rerender of the specified key, just like the value changed.
118
+ *
119
+ * @param key - The property name to force an update for
120
+ * @returns void
121
+ */
122
+ forceUpdate(key: keyof T): void;
123
+ /**
124
+ * Remove a listener
125
+ *
126
+ * @param propName - The property name to remove the listener from
127
+ * @param listener - The listener to remove
128
+ * @returns void
95
129
  */
96
- forceUpdate(key: keyof T): any;
130
+ removeListener(propName: keyof T, listener: (value: any) => void): void;
97
131
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stencil/store",
3
3
  "author": "StencilJS Team",
4
- "version": "2.1.3",
4
+ "version": "2.2.1",
5
5
  "description": "Store is a lightweight shared state library by the StencilJS core team. Implements a simple key/value map that efficiently re-renders components when necessary.",
6
6
  "license": "MIT",
7
7
  "homepage": "https://stenciljs.com/docs/stencil-store",
@@ -55,13 +55,13 @@
55
55
  "@ionic/prettier-config": "^4.0.0",
56
56
  "@rollup/plugin-typescript": "^12.1.2",
57
57
  "@stencil/core": "^4.27.1",
58
- "@types/node": "^22.13.5",
58
+ "@types/node": "^24.0.3",
59
59
  "@vitest/coverage-v8": "^3.0.7",
60
60
  "np": "^10.2.0",
61
- "npm-run-all2": "^7.0.2",
61
+ "npm-run-all2": "^8.0.1",
62
62
  "prettier": "^3.5.2",
63
63
  "rollup": "^4.34.8",
64
- "typescript": "~5.8.2",
64
+ "typescript": "~5.9.2",
65
65
  "vitest": "^3.0.7"
66
66
  },
67
67
  "prettier": "@ionic/prettier-config"