@workos-inc/widgets 1.20.2-next.1792700156145 → 1.20.2

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.
Files changed (202) hide show
  1. package/CHANGELOG.md +4 -10
  2. package/dist/cjs/admin-portal-audit-log-streaming.client.cjs +2 -1
  3. package/dist/cjs/admin-portal-audit-log-streaming.client.cjs.map +1 -1
  4. package/dist/cjs/admin-portal-domain-verification.client.cjs +3 -2
  5. package/dist/cjs/admin-portal-domain-verification.client.cjs.map +1 -1
  6. package/dist/cjs/admin-portal-sso-connection.client.cjs +2 -1
  7. package/dist/cjs/admin-portal-sso-connection.client.cjs.map +1 -1
  8. package/dist/cjs/api/api-provider.cjs +23 -1
  9. package/dist/cjs/api/api-provider.cjs.map +1 -1
  10. package/dist/cjs/api/api-provider.d.cts.map +1 -1
  11. package/dist/cjs/api/claims.cjs.map +1 -1
  12. package/dist/cjs/api/claims.d.cts +2 -1
  13. package/dist/cjs/api/claims.d.cts.map +1 -1
  14. package/dist/cjs/api/client-graphql/client.cjs +3 -2
  15. package/dist/cjs/api/client-graphql/client.cjs.map +1 -1
  16. package/dist/cjs/api/client-graphql/hooks.cjs +3 -20
  17. package/dist/cjs/api/client-graphql/hooks.cjs.map +1 -1
  18. package/dist/cjs/api/client-graphql/hooks.d.cts +3 -12
  19. package/dist/cjs/api/client-graphql/hooks.d.cts.map +1 -1
  20. package/dist/cjs/api/client-graphql/query-keys.cjs +25 -0
  21. package/dist/cjs/api/client-graphql/query-keys.cjs.map +1 -0
  22. package/dist/cjs/api/client-graphql/query-keys.d.cts +16 -0
  23. package/dist/cjs/api/client-graphql/query-keys.d.cts.map +1 -0
  24. package/dist/cjs/components/copy-text.cjs +1 -1
  25. package/dist/cjs/components/copy-text.cjs.map +1 -1
  26. package/dist/cjs/components/form.cjs +9 -13
  27. package/dist/cjs/components/form.cjs.map +1 -1
  28. package/dist/cjs/components/form.d.cts.map +1 -1
  29. package/dist/cjs/components/otp-input.cjs +1 -0
  30. package/dist/cjs/components/otp-input.cjs.map +1 -1
  31. package/dist/cjs/directory-sync.client.cjs +2 -1
  32. package/dist/cjs/directory-sync.client.cjs.map +1 -1
  33. package/dist/cjs/lib/api/api-key.cjs +4 -3
  34. package/dist/cjs/lib/api/api-key.cjs.map +1 -1
  35. package/dist/cjs/lib/api/me.cjs +2 -1
  36. package/dist/cjs/lib/api/me.cjs.map +1 -1
  37. package/dist/cjs/lib/api/pipes.cjs +7 -6
  38. package/dist/cjs/lib/api/pipes.cjs.map +1 -1
  39. package/dist/cjs/lib/api/user-api-key.cjs +3 -2
  40. package/dist/cjs/lib/api/user-api-key.cjs.map +1 -1
  41. package/dist/cjs/lib/i18n/intl-context.cjs.map +1 -1
  42. package/dist/cjs/lib/i18n/intl-context.d.cts +1 -0
  43. package/dist/cjs/lib/i18n/intl-context.d.cts.map +1 -1
  44. package/dist/cjs/lib/i18n/use-translation.cjs +2 -2
  45. package/dist/cjs/lib/i18n/use-translation.cjs.map +1 -1
  46. package/dist/cjs/lib/use-composed-event-handlers.cjs +15 -0
  47. package/dist/cjs/lib/use-composed-event-handlers.cjs.map +1 -0
  48. package/dist/cjs/lib/use-composed-event-handlers.d.cts +6 -0
  49. package/dist/cjs/lib/use-composed-event-handlers.d.cts.map +1 -0
  50. package/dist/cjs/lib/use-debounce.cjs +124 -116
  51. package/dist/cjs/lib/use-debounce.cjs.map +1 -1
  52. package/dist/cjs/lib/use-debounce.d.cts +53 -48
  53. package/dist/cjs/lib/use-debounce.d.cts.map +1 -1
  54. package/dist/cjs/lib/use-widgets-invalidator.cjs +3 -3
  55. package/dist/cjs/lib/use-widgets-invalidator.cjs.map +1 -1
  56. package/dist/cjs/lib/use-widgets-invalidator.d.cts.map +1 -1
  57. package/dist/cjs/lib/utils.cjs +0 -7
  58. package/dist/cjs/lib/utils.cjs.map +1 -1
  59. package/dist/cjs/lib/utils.d.cts +0 -3
  60. package/dist/cjs/lib/utils.d.cts.map +1 -1
  61. package/dist/cjs/organization-switcher.client.cjs +2 -1
  62. package/dist/cjs/organization-switcher.client.cjs.map +1 -1
  63. package/dist/cjs/pipes-admin.client.cjs +2 -1
  64. package/dist/cjs/pipes-admin.client.cjs.map +1 -1
  65. package/dist/cjs/pipes.client.cjs +2 -1
  66. package/dist/cjs/pipes.client.cjs.map +1 -1
  67. package/dist/cjs/user-security.client.cjs +2 -1
  68. package/dist/cjs/user-security.client.cjs.map +1 -1
  69. package/dist/cjs/user-sessions.client.cjs +2 -1
  70. package/dist/cjs/user-sessions.client.cjs.map +1 -1
  71. package/dist/cjs/widgets/api-keys/api-keys-search.cjs +1 -1
  72. package/dist/cjs/widgets/api-keys/api-keys-search.cjs.map +1 -1
  73. package/dist/cjs/widgets/api-keys/user-api-keys-search.cjs +1 -1
  74. package/dist/cjs/widgets/api-keys/user-api-keys-search.cjs.map +1 -1
  75. package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs +19 -5
  76. package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs.map +1 -1
  77. package/dist/cjs/widgets/organization-switcher/organization-switcher.d.cts +2 -1
  78. package/dist/cjs/widgets/organization-switcher/organization-switcher.d.cts.map +1 -1
  79. package/dist/cjs/widgets/pipes/pipes.cjs +3 -2
  80. package/dist/cjs/widgets/pipes/pipes.cjs.map +1 -1
  81. package/dist/cjs/widgets/user-sessions/logout-all-sessions-dialog.cjs +2 -1
  82. package/dist/cjs/widgets/user-sessions/logout-all-sessions-dialog.cjs.map +1 -1
  83. package/dist/cjs/widgets/user-sessions/logout-dialog.cjs +2 -1
  84. package/dist/cjs/widgets/user-sessions/logout-dialog.cjs.map +1 -1
  85. package/dist/cjs/widgets/users-management/invite-user-dialog.cjs +5 -6
  86. package/dist/cjs/widgets/users-management/invite-user-dialog.cjs.map +1 -1
  87. package/dist/cjs/widgets/users-management/users-management-utils.cjs +4 -3
  88. package/dist/cjs/widgets/users-management/users-management-utils.cjs.map +1 -1
  89. package/dist/cjs/widgets/users-management/users-management.cjs.map +1 -1
  90. package/dist/cjs/widgets/users-management/users-search.cjs +1 -1
  91. package/dist/cjs/widgets/users-management/users-search.cjs.map +1 -1
  92. package/dist/esm/admin-portal-audit-log-streaming.client.js +2 -1
  93. package/dist/esm/admin-portal-audit-log-streaming.client.js.map +1 -1
  94. package/dist/esm/admin-portal-domain-verification.client.js +2 -1
  95. package/dist/esm/admin-portal-domain-verification.client.js.map +1 -1
  96. package/dist/esm/admin-portal-sso-connection.client.js +2 -1
  97. package/dist/esm/admin-portal-sso-connection.client.js.map +1 -1
  98. package/dist/esm/api/api-provider.d.ts.map +1 -1
  99. package/dist/esm/api/api-provider.js +23 -1
  100. package/dist/esm/api/api-provider.js.map +1 -1
  101. package/dist/esm/api/claims.d.ts +2 -1
  102. package/dist/esm/api/claims.d.ts.map +1 -1
  103. package/dist/esm/api/claims.js.map +1 -1
  104. package/dist/esm/api/client-graphql/client.js +3 -2
  105. package/dist/esm/api/client-graphql/client.js.map +1 -1
  106. package/dist/esm/api/client-graphql/hooks.d.ts +3 -12
  107. package/dist/esm/api/client-graphql/hooks.d.ts.map +1 -1
  108. package/dist/esm/api/client-graphql/hooks.js +1 -18
  109. package/dist/esm/api/client-graphql/hooks.js.map +1 -1
  110. package/dist/esm/api/client-graphql/query-keys.d.ts +16 -0
  111. package/dist/esm/api/client-graphql/query-keys.d.ts.map +1 -0
  112. package/dist/esm/api/client-graphql/query-keys.js +23 -0
  113. package/dist/esm/api/client-graphql/query-keys.js.map +1 -0
  114. package/dist/esm/components/copy-text.js +1 -1
  115. package/dist/esm/components/copy-text.js.map +1 -1
  116. package/dist/esm/components/form.d.ts.map +1 -1
  117. package/dist/esm/components/form.js +9 -13
  118. package/dist/esm/components/form.js.map +1 -1
  119. package/dist/esm/components/otp-input.js +1 -0
  120. package/dist/esm/components/otp-input.js.map +1 -1
  121. package/dist/esm/directory-sync.client.js +2 -1
  122. package/dist/esm/directory-sync.client.js.map +1 -1
  123. package/dist/esm/lib/api/api-key.js +2 -1
  124. package/dist/esm/lib/api/api-key.js.map +1 -1
  125. package/dist/esm/lib/api/me.js +2 -1
  126. package/dist/esm/lib/api/me.js.map +1 -1
  127. package/dist/esm/lib/api/pipes.js +2 -1
  128. package/dist/esm/lib/api/pipes.js.map +1 -1
  129. package/dist/esm/lib/api/user-api-key.js +2 -1
  130. package/dist/esm/lib/api/user-api-key.js.map +1 -1
  131. package/dist/esm/lib/i18n/intl-context.d.ts +1 -0
  132. package/dist/esm/lib/i18n/intl-context.d.ts.map +1 -1
  133. package/dist/esm/lib/i18n/intl-context.js.map +1 -1
  134. package/dist/esm/lib/i18n/use-translation.js +2 -2
  135. package/dist/esm/lib/i18n/use-translation.js.map +1 -1
  136. package/dist/esm/lib/use-composed-event-handlers.d.ts +6 -0
  137. package/dist/esm/lib/use-composed-event-handlers.d.ts.map +1 -0
  138. package/dist/esm/lib/use-composed-event-handlers.js +12 -0
  139. package/dist/esm/lib/use-composed-event-handlers.js.map +1 -0
  140. package/dist/esm/lib/use-debounce.d.ts +53 -48
  141. package/dist/esm/lib/use-debounce.d.ts.map +1 -1
  142. package/dist/esm/lib/use-debounce.js +124 -116
  143. package/dist/esm/lib/use-debounce.js.map +1 -1
  144. package/dist/esm/lib/use-widgets-invalidator.d.ts.map +1 -1
  145. package/dist/esm/lib/use-widgets-invalidator.js +2 -2
  146. package/dist/esm/lib/use-widgets-invalidator.js.map +1 -1
  147. package/dist/esm/lib/utils.d.ts +0 -3
  148. package/dist/esm/lib/utils.d.ts.map +1 -1
  149. package/dist/esm/lib/utils.js +1 -7
  150. package/dist/esm/lib/utils.js.map +1 -1
  151. package/dist/esm/organization-switcher.client.js +2 -1
  152. package/dist/esm/organization-switcher.client.js.map +1 -1
  153. package/dist/esm/pipes-admin.client.js +2 -1
  154. package/dist/esm/pipes-admin.client.js.map +1 -1
  155. package/dist/esm/pipes.client.js +2 -1
  156. package/dist/esm/pipes.client.js.map +1 -1
  157. package/dist/esm/user-security.client.js +2 -1
  158. package/dist/esm/user-security.client.js.map +1 -1
  159. package/dist/esm/user-sessions.client.js +2 -1
  160. package/dist/esm/user-sessions.client.js.map +1 -1
  161. package/dist/esm/widgets/api-keys/api-keys-search.js +1 -1
  162. package/dist/esm/widgets/api-keys/api-keys-search.js.map +1 -1
  163. package/dist/esm/widgets/api-keys/user-api-keys-search.js +1 -1
  164. package/dist/esm/widgets/api-keys/user-api-keys-search.js.map +1 -1
  165. package/dist/esm/widgets/organization-switcher/organization-switcher.d.ts +2 -1
  166. package/dist/esm/widgets/organization-switcher/organization-switcher.d.ts.map +1 -1
  167. package/dist/esm/widgets/organization-switcher/organization-switcher.js +19 -5
  168. package/dist/esm/widgets/organization-switcher/organization-switcher.js.map +1 -1
  169. package/dist/esm/widgets/pipes/pipes.js +2 -1
  170. package/dist/esm/widgets/pipes/pipes.js.map +1 -1
  171. package/dist/esm/widgets/user-sessions/logout-all-sessions-dialog.js +2 -1
  172. package/dist/esm/widgets/user-sessions/logout-all-sessions-dialog.js.map +1 -1
  173. package/dist/esm/widgets/user-sessions/logout-dialog.js +2 -1
  174. package/dist/esm/widgets/user-sessions/logout-dialog.js.map +1 -1
  175. package/dist/esm/widgets/users-management/invite-user-dialog.js +5 -6
  176. package/dist/esm/widgets/users-management/invite-user-dialog.js.map +1 -1
  177. package/dist/esm/widgets/users-management/users-management-utils.js +2 -1
  178. package/dist/esm/widgets/users-management/users-management-utils.js.map +1 -1
  179. package/dist/esm/widgets/users-management/users-management.js.map +1 -1
  180. package/dist/esm/widgets/users-management/users-search.js +1 -1
  181. package/dist/esm/widgets/users-management/users-search.js.map +1 -1
  182. package/package.json +1 -1
  183. package/src/api/api-provider.tsx +39 -1
  184. package/src/api/claims.ts +2 -1
  185. package/src/api/client-graphql/client.ts +8 -2
  186. package/src/api/client-graphql/hooks.ts +8 -27
  187. package/src/api/client-graphql/query-keys.ts +29 -0
  188. package/src/components/copy-text.tsx +1 -1
  189. package/src/components/form.tsx +33 -29
  190. package/src/components/otp-input.tsx +2 -0
  191. package/src/lib/i18n/intl-context.tsx +4 -0
  192. package/src/lib/i18n/use-translation.ts +1 -4
  193. package/src/lib/use-composed-event-handlers.ts +25 -0
  194. package/src/lib/use-debounce.ts +289 -237
  195. package/src/lib/use-widgets-invalidator.ts +0 -4
  196. package/src/lib/utils.ts +0 -12
  197. package/src/widgets/api-keys/api-keys-search.tsx +6 -3
  198. package/src/widgets/api-keys/user-api-keys-search.tsx +6 -3
  199. package/src/widgets/organization-switcher/organization-switcher.tsx +32 -8
  200. package/src/widgets/users-management/invite-user-dialog.tsx +9 -11
  201. package/src/widgets/users-management/users-management.tsx +1 -1
  202. package/src/widgets/users-management/users-search.tsx +6 -3
@@ -1,8 +1,55 @@
1
1
  // Fork of https://github.com/xnimorz/use-debounce
2
2
  // MIT License, Copyright (c) 2018 Nikita Mostovoy
3
3
  import * as React from "react";
4
+ import { useLayoutEffect } from "./use-layout-effect.js";
4
5
  import { canUseDOM } from "./utils.js";
5
6
 
7
+ interface Options {
8
+ /**
9
+ * The number of milliseconds to delay. if omitted, invocation is deferred to
10
+ * the next animation frame (or the next tick where that's unavailable).
11
+ */
12
+ wait?: number;
13
+ /**
14
+ * Controls if the function should be invoked on the leading edge of the
15
+ * timeout.
16
+ * @default false
17
+ */
18
+ leading?: boolean;
19
+ /**
20
+ * Controls if the function should be invoked on the trailing edge of the
21
+ * timeout.
22
+ * @default true
23
+ */
24
+ trailing?: boolean;
25
+ /**
26
+ * Controls if the function should be invoked when the React component
27
+ * unmounts or the page is closed. This is usually desirable whenever `func`
28
+ * has persistent side-effects such as persists data.
29
+ *
30
+ * NOTE: If the callback calls `fetch()`, you usually also want to specify the
31
+ * `keepalive=true` option for `fetch()` so it can finish in the background
32
+ * after the page is closed.
33
+ *
34
+ * This option has no effect if `trailing == false`.
35
+ *
36
+ * @default false
37
+ */
38
+ flushOnExit?: boolean;
39
+ /**
40
+ * The maximum time the given function is allowed to be delayed before it's
41
+ * invoked. Pass `null` and the maximum wait time is equal to the `wait` time.
42
+ */
43
+ maxWait?: number | null;
44
+ /**
45
+ * If the setting is set to true, all debouncing and timers will happen on the
46
+ * server side as well.
47
+ *
48
+ * @default false
49
+ */
50
+ debounceOnServer?: boolean;
51
+ }
52
+
6
53
  /**
7
54
  * Creates a debounced function that delays invoking `func` until after `wait`
8
55
  * milliseconds have elapsed since the last time the debounced function was
@@ -33,34 +80,25 @@ import { canUseDOM } from "./utils.js";
33
80
  * article](https://css-tricks.com/debouncing-throttling-explained-examples/)
34
81
  * for details over the differences between `debounce` and `throttle`.
35
82
  *
36
- * @category Function
37
- * @param {Function} func The function to debounce.
38
- * @param {number} [wait=0] The number of milliseconds to delay; if omitted,
39
- * `requestAnimationFrame` is used (if available, otherwise it will be
40
- * setTimeout(...,0)).
41
- * @param {Object} [options={}] The options object. Controls if `func` should be
42
- * invoked on the leading edge of the timeout.
43
- * @param {boolean} [options.leading=false] The maximum time `func` is allowed
44
- * to be delayed before it's invoked.
45
- * @param {number} [options.maxWait] Controls if `func` should be invoked the
46
- * trailing edge of the timeout.
47
- * @param {boolean} [options.trailing=true]
48
- * @returns {Function} Returns the new debounced function.
83
+ * @param func The function to debounce
84
+ * @param options The options object
85
+ * @returns Returns the new debounced function.
49
86
  * @example
50
87
  * ```tsx
51
88
  * // Avoid costly calculations while the window size is in flux.
52
- * const resizeHandler = useDebouncedCallback(calculateLayout, 150);
89
+ * const resizeHandler = useDebouncedCallback(calculateLayout, { wait: 150 });
53
90
  * window.addEventListener('resize', resizeHandler)
54
91
  *
55
92
  * // Invoke `sendMail` when clicked, debouncing subsequent calls.
56
- * const clickHandler = useDebouncedCallback(sendMail, 300, {
93
+ * const clickHandler = useDebouncedCallback(sendMail, {
94
+ * wait: 300,
57
95
  * leading: true,
58
96
  * trailing: false,
59
97
  * })
60
98
  * <button onClick={clickHandler}>click me</button>
61
99
  *
62
100
  * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
63
- * const debounced = useDebouncedCallback(batchLog, 250, { 'maxWait': 1000 })
101
+ * const debounced = useDebouncedCallback(batchLog, { wait: 250, maxWait: 1000 })
64
102
  * const source = new EventSource('/stream')
65
103
  * source.addEventListener('message', debounced)
66
104
  *
@@ -72,258 +110,272 @@ import { canUseDOM } from "./utils.js";
72
110
  * ```
73
111
  */
74
112
  function useDebouncedCallback<T extends (...args: any) => ReturnType<T>>(
75
- this: unknown,
76
113
  func: T,
77
- wait?: number,
78
- options?: Options,
114
+ options: Options = {},
79
115
  ): DebouncedState<T> {
80
- const lastCallTime = React.useRef<number>(null);
81
- const lastInvokeTime = React.useRef(0);
82
- const timerId = React.useRef<number>(null);
83
- const lastArgs = React.useRef<unknown[]>(null);
84
- const lastThis = React.useRef<unknown>(undefined);
85
- const result = React.useRef<ReturnType<T>>(undefined);
86
- const funcRef = React.useRef(func);
87
- const mounted = React.useRef(true);
88
- const visibilityListener = React.useRef<VoidFunction>(null);
89
- const debouncedRef = React.useRef<DebouncedState<T>>(undefined);
90
- // Always keep the latest version of debounce callback, with no wait time.
91
- // TODO: Fix this!
92
- // eslint-disable-next-line react-hooks/exhaustive-deps
93
- funcRef.current = func;
94
-
95
- // Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
96
- const useRAF = !wait && wait !== 0 && canUseDOM;
97
-
98
- if (typeof func !== "function") {
99
- throw new TypeError("Expected a function");
100
- }
101
-
102
- wait = +(wait || 0);
103
- options = options || {};
104
-
105
- const leading = !!options.leading;
106
- const trailing = "trailing" in options ? !!options.trailing : true; // `true` by default
116
+ const {
117
+ wait = 0,
118
+ leading = false,
119
+ trailing = true,
120
+ maxWait: maxWaitOption,
121
+ debounceOnServer = false,
122
+ } = options;
123
+ // Bypass `requestAnimationFrame` by explicitly setting `wait: 0`.
124
+ const useRAF = options.wait === undefined && canUseDOM;
107
125
  const flushOnExit = !!options.flushOnExit && trailing;
108
- const maxing = "maxWait" in options;
109
- const debounceOnServer =
110
- "debounceOnServer" in options ? !!options.debounceOnServer : false; // `false` by default
111
- const maxWait = maxing ? Math.max(+(options.maxWait || 0), wait) : null;
126
+ const maxWait =
127
+ maxWaitOption === undefined ? null : Math.max(maxWaitOption ?? 0, wait);
128
+
129
+ const stateRef = React.useRef<DebounceState<T>>({
130
+ func,
131
+ lastCallTime: null,
132
+ lastInvokeTime: 0,
133
+ timerId: null,
134
+ lastArgs: null,
135
+ lastThis: undefined,
136
+ result: undefined,
137
+ mounted: true,
138
+ visibilityListener: null,
139
+ debounced: undefined,
140
+ });
112
141
 
113
- // You may have a question, why we have so many code under the useMemo definition.
114
- //
115
- // This was made as we want to escape from useCallback hell and
116
- // not to initialize a number of functions each time useDebouncedCallback is called.
117
- //
118
- // It means that we have less garbage for our GC calls which improves performance.
119
- // Also, it makes this library smaller.
120
- //
121
- // And the last reason, that the code without lots of useCallback with deps is easier to read.
122
- // You have only one place for that.
123
142
  const debounced = React.useMemo(() => {
124
- const invokeFunc = (time: number) => {
125
- const args = lastArgs.current;
126
- const thisArg = lastThis.current;
127
- lastArgs.current = lastThis.current = null;
128
- lastInvokeTime.current = time;
129
-
130
- return (result.current = funcRef.current.apply(thisArg, args || []));
143
+ const config: DebounceConfig = {
144
+ wait,
145
+ maxWait,
146
+ leading,
147
+ trailing,
148
+ flushOnExit,
149
+ useRAF,
150
+ debounceOnServer,
131
151
  };
132
-
133
- const startTimer = (pendingFunc: () => void, wait: number) => {
134
- if (useRAF) {
135
- cancelAnimationFrame(timerId.current!);
136
- }
137
- timerId.current = useRAF
138
- ? requestAnimationFrame(pendingFunc)
139
- : (setTimeout(pendingFunc, wait) as unknown as number);
152
+ const debounced: DebouncedState<T> = function (
153
+ this: unknown,
154
+ ...args: Parameters<T>
155
+ ) {
156
+ return debounce(stateRef.current, config, this, args);
140
157
  };
158
+ debounced.cancel = () => cancel(stateRef.current, config);
159
+ debounced.isPending = () => !!stateRef.current.timerId;
160
+ debounced.flush = () => flush(stateRef.current, config);
161
+ return debounced;
162
+ }, [wait, maxWait, leading, trailing, flushOnExit, useRAF, debounceOnServer]);
141
163
 
142
- const shouldInvoke = (time: number) => {
143
- if (!mounted.current) return false;
164
+ useLayoutEffect(() => {
165
+ stateRef.current.func = func;
166
+ stateRef.current.debounced = debounced;
167
+ });
144
168
 
145
- const timeSinceLastCall = time - (lastCallTime.current || 0);
146
- const timeSinceLastInvoke = time - lastInvokeTime.current;
147
-
148
- // Either this is the first call, activity has stopped and we're at the
149
- // trailing edge, the system time has gone backwards and we're treating
150
- // it as the trailing edge, or we've hit the `maxWait` limit.
151
- return (
152
- !lastCallTime.current ||
153
- timeSinceLastCall >= wait ||
154
- timeSinceLastCall < 0 ||
155
- (maxing && timeSinceLastInvoke >= (maxWait || 0))
156
- );
169
+ React.useEffect(() => {
170
+ const state = stateRef.current;
171
+ state.mounted = true;
172
+ return () => {
173
+ if (flushOnExit) {
174
+ state.debounced?.flush();
175
+ }
176
+ if (state.visibilityListener) {
177
+ globalThis.document?.removeEventListener?.(
178
+ "visibilitychange",
179
+ state.visibilityListener,
180
+ );
181
+ state.visibilityListener = null;
182
+ }
183
+ state.mounted = false;
157
184
  };
185
+ }, [flushOnExit]);
158
186
 
159
- const trailingEdge = (time: number) => {
160
- timerId.current = null;
161
-
162
- // Only invoke if we have `lastArgs` which means `func` has been
163
- // debounced at least once.
164
- if (trailing && lastArgs.current) {
165
- return invokeFunc(time);
166
- }
187
+ return debounced;
188
+ }
167
189
 
168
- lastArgs.current = lastThis.current = null;
169
- return result.current;
170
- };
190
+ /**
191
+ * Mutable timer state for a hook instance. It outlives any single debounced
192
+ * function, so changing options doesn't drop pending invocations.
193
+ */
194
+ interface DebounceState<T extends (...args: any) => ReturnType<T>> {
195
+ func: T;
196
+ lastCallTime: number | null;
197
+ lastInvokeTime: number;
198
+ timerId: number | null;
199
+ lastArgs: Parameters<T> | null;
200
+ lastThis: unknown;
201
+ result: ReturnType<T> | undefined;
202
+ mounted: boolean;
203
+ visibilityListener: VoidFunction | null;
204
+ debounced: DebouncedState<T> | undefined;
205
+ }
171
206
 
172
- const timerExpired = () => {
173
- const time = Date.now();
207
+ interface DebounceConfig {
208
+ wait: number;
209
+ maxWait: number | null;
210
+ leading: boolean;
211
+ trailing: boolean;
212
+ flushOnExit: boolean;
213
+ useRAF: boolean;
214
+ debounceOnServer: boolean;
215
+ }
174
216
 
175
- if (shouldInvoke(time)) {
176
- return trailingEdge(time);
177
- }
178
- // https://github.com/xnimorz/use-debounce/issues/97
179
- if (!mounted.current) {
180
- return;
181
- }
182
- // Remaining wait calculation
183
- const timeSinceLastCall = time - (lastCallTime.current || 0);
184
- const timeSinceLastInvoke = time - lastInvokeTime.current;
185
- const timeWaiting = wait - timeSinceLastCall;
186
- const remainingWait = maxing
187
- ? Math.min(timeWaiting, (maxWait || 0) - timeSinceLastInvoke)
188
- : timeWaiting;
217
+ function invokeFunc<T extends (...args: any) => ReturnType<T>>(
218
+ state: DebounceState<T>,
219
+ time: number,
220
+ ) {
221
+ const args = state.lastArgs;
222
+ const thisArg = state.lastThis;
223
+ state.lastArgs = state.lastThis = null;
224
+ state.lastInvokeTime = time;
189
225
 
190
- // Restart the timer
191
- startTimer(timerExpired, remainingWait);
192
- };
226
+ return (state.result = state.func.apply(thisArg, args || []));
227
+ }
193
228
 
194
- const func: DebouncedState<T> = (...args: Parameters<T>): ReturnType<T> => {
195
- if (!canUseDOM && !debounceOnServer) {
196
- // TODO: Fixme
197
- return undefined!;
198
- }
229
+ function startTimer<T extends (...args: any) => ReturnType<T>>(
230
+ state: DebounceState<T>,
231
+ config: DebounceConfig,
232
+ wait: number,
233
+ ) {
234
+ const onTimer = () => timerExpired(state, config);
235
+ if (config.useRAF) {
236
+ cancelAnimationFrame(state.timerId!);
237
+ }
238
+ state.timerId = config.useRAF
239
+ ? requestAnimationFrame(onTimer)
240
+ : (setTimeout(onTimer, wait) as unknown as number);
241
+ }
199
242
 
200
- const time = Date.now();
201
- const isInvoking = shouldInvoke(time);
243
+ function shouldInvoke<T extends (...args: any) => ReturnType<T>>(
244
+ state: DebounceState<T>,
245
+ { wait, maxWait }: DebounceConfig,
246
+ time: number,
247
+ ) {
248
+ if (!state.mounted) return false;
249
+
250
+ const timeSinceLastCall = time - (state.lastCallTime || 0);
251
+ const timeSinceLastInvoke = time - state.lastInvokeTime;
252
+
253
+ // Either this is the first call, activity has stopped and we're at the
254
+ // trailing edge, the system time has gone backwards and we're treating
255
+ // it as the trailing edge, or we've hit the `maxWait` limit.
256
+ return (
257
+ !state.lastCallTime ||
258
+ timeSinceLastCall >= wait ||
259
+ timeSinceLastCall < 0 ||
260
+ (maxWait !== null && timeSinceLastInvoke >= maxWait)
261
+ );
262
+ }
202
263
 
203
- lastArgs.current = args;
204
- lastThis.current = this;
205
- lastCallTime.current = time;
264
+ function trailingEdge<T extends (...args: any) => ReturnType<T>>(
265
+ state: DebounceState<T>,
266
+ config: DebounceConfig,
267
+ time: number,
268
+ ) {
269
+ state.timerId = null;
270
+
271
+ // Only invoke if we have `lastArgs` which means `func` has been
272
+ // debounced at least once.
273
+ if (config.trailing && state.lastArgs) {
274
+ return invokeFunc(state, time);
275
+ }
206
276
 
207
- if (flushOnExit && !visibilityListener.current) {
208
- visibilityListener.current = () => {
209
- if (globalThis.document?.visibilityState === "hidden") {
210
- debouncedRef.current?.flush();
211
- }
212
- };
213
- globalThis.document?.addEventListener?.(
214
- "visibilitychange",
215
- visibilityListener.current,
216
- );
217
- }
218
- if (isInvoking) {
219
- if (!timerId.current && mounted.current) {
220
- // Reset any `maxWait` timer.
221
- lastInvokeTime.current = lastCallTime.current;
222
- // Start the timer for the trailing edge.
223
- startTimer(timerExpired, wait);
224
- // Invoke the leading edge.
225
- return leading ? invokeFunc(lastCallTime.current) : result.current!;
226
- }
227
- if (maxing) {
228
- // Handle invocations in a tight loop.
229
- startTimer(timerExpired, wait);
230
- return invokeFunc(lastCallTime.current);
231
- }
232
- }
233
- if (!timerId.current) {
234
- startTimer(timerExpired, wait);
235
- }
236
- return result.current!;
237
- };
277
+ state.lastArgs = state.lastThis = null;
278
+ return state.result;
279
+ }
238
280
 
239
- func.cancel = () => {
240
- if (timerId.current) {
241
- useRAF
242
- ? cancelAnimationFrame(timerId.current!)
243
- : clearTimeout(timerId.current!);
244
- }
245
- lastInvokeTime.current = 0;
246
- lastArgs.current =
247
- lastCallTime.current =
248
- lastThis.current =
249
- timerId.current =
250
- null;
251
- };
281
+ function timerExpired<T extends (...args: any) => ReturnType<T>>(
282
+ state: DebounceState<T>,
283
+ config: DebounceConfig,
284
+ ) {
285
+ const time = Date.now();
252
286
 
253
- func.isPending = () => {
254
- return !!timerId.current;
255
- };
287
+ if (shouldInvoke(state, config, time)) {
288
+ trailingEdge(state, config, time);
289
+ return;
290
+ }
291
+ // https://github.com/xnimorz/use-debounce/issues/97
292
+ if (!state.mounted) {
293
+ return;
294
+ }
295
+ // Remaining wait calculation
296
+ const timeSinceLastCall = time - (state.lastCallTime || 0);
297
+ const timeSinceLastInvoke = time - state.lastInvokeTime;
298
+ const timeWaiting = config.wait - timeSinceLastCall;
299
+ const remainingWait =
300
+ config.maxWait !== null
301
+ ? Math.min(timeWaiting, config.maxWait - timeSinceLastInvoke)
302
+ : timeWaiting;
303
+
304
+ // Restart the timer
305
+ startTimer(state, config, remainingWait);
306
+ }
256
307
 
257
- func.flush = () => {
258
- return !timerId.current ? result.current : trailingEdge(Date.now());
259
- };
308
+ function debounce<T extends (...args: any) => ReturnType<T>>(
309
+ state: DebounceState<T>,
310
+ config: DebounceConfig,
311
+ thisArg: unknown,
312
+ args: Parameters<T>,
313
+ ): ReturnType<T> | undefined {
314
+ if (!canUseDOM && !config.debounceOnServer) {
315
+ return undefined;
316
+ }
260
317
 
261
- return func;
262
- }, [
263
- leading,
264
- maxing,
265
- wait,
266
- maxWait,
267
- trailing,
268
- flushOnExit,
269
- useRAF,
270
- debounceOnServer,
271
- ]);
318
+ const time = Date.now();
319
+ const isInvoking = shouldInvoke(state, config, time);
272
320
 
273
- // Store reference to debounced function for cleanup
274
- debouncedRef.current = debounced;
321
+ state.lastArgs = args;
322
+ state.lastThis = thisArg;
323
+ state.lastCallTime = time;
275
324
 
276
- React.useEffect(() => {
277
- mounted.current = true;
278
- return () => {
279
- if (flushOnExit) {
280
- debouncedRef.current?.flush();
281
- }
282
- if (visibilityListener.current) {
283
- globalThis.document?.removeEventListener?.(
284
- "visibilitychange",
285
- visibilityListener.current,
286
- );
287
- visibilityListener.current = null;
325
+ if (config.flushOnExit && !state.visibilityListener) {
326
+ state.visibilityListener = () => {
327
+ if (globalThis.document?.visibilityState === "hidden") {
328
+ state.debounced?.flush();
288
329
  }
289
- mounted.current = false;
290
330
  };
291
- }, [flushOnExit]);
292
-
293
- return debounced;
331
+ globalThis.document?.addEventListener?.(
332
+ "visibilitychange",
333
+ state.visibilityListener,
334
+ );
335
+ }
336
+ if (isInvoking) {
337
+ if (!state.timerId && state.mounted) {
338
+ // Reset any `maxWait` timer.
339
+ state.lastInvokeTime = time;
340
+ // Start the timer for the trailing edge.
341
+ startTimer(state, config, config.wait);
342
+ // Invoke the leading edge.
343
+ return config.leading ? invokeFunc(state, time) : state.result;
344
+ }
345
+ if (config.maxWait !== null) {
346
+ // Handle invocations in a tight loop.
347
+ startTimer(state, config, config.wait);
348
+ return invokeFunc(state, time);
349
+ }
350
+ }
351
+ if (!state.timerId) {
352
+ startTimer(state, config, config.wait);
353
+ }
354
+ return state.result;
294
355
  }
295
356
 
296
- interface CallOptions {
297
- /**
298
- * Controls if the function should be invoked on the leading edge of the timeout.
299
- */
300
- leading?: boolean;
301
- /**
302
- * Controls if the function should be invoked on the trailing edge of the timeout.
303
- */
304
- trailing?: boolean;
305
- /**
306
- * Controls if the function should be invoked when the React component unmounts or
307
- * the page is closed. This is usually desirable whenever `func` has persistent side-effects
308
- * such as persists data.
309
- *
310
- * NOTE: If the callback calls `fetch()`, you usually also want to specify the `keepalive=true`
311
- * option for `fetch()` so it can finish in the background after the page is closed.
312
- *
313
- * This option has no effect if `trailing == false`.
314
- */
315
- flushOnExit?: boolean;
357
+ function cancel<T extends (...args: any) => ReturnType<T>>(
358
+ state: DebounceState<T>,
359
+ config: DebounceConfig,
360
+ ) {
361
+ if (state.timerId) {
362
+ if (config.useRAF) {
363
+ cancelAnimationFrame(state.timerId);
364
+ } else {
365
+ clearTimeout(state.timerId);
366
+ }
367
+ }
368
+ state.lastInvokeTime = 0;
369
+ state.lastArgs = state.lastCallTime = state.lastThis = state.timerId = null;
316
370
  }
317
371
 
318
- interface Options extends CallOptions {
319
- /**
320
- * The maximum time the given function is allowed to be delayed before it's invoked.
321
- */
322
- maxWait?: number;
323
- /**
324
- * If the setting is set to true, all debouncing and timers will happen on the server side as well
325
- */
326
- debounceOnServer?: boolean;
372
+ function flush<T extends (...args: any) => ReturnType<T>>(
373
+ state: DebounceState<T>,
374
+ config: DebounceConfig,
375
+ ) {
376
+ return !state.timerId
377
+ ? state.result
378
+ : trailingEdge(state, config, Date.now());
327
379
  }
328
380
 
329
381
  interface ControlFunctions<ReturnT> {
@@ -7,10 +7,6 @@ function useWidgetsInvalidatorImpl() {
7
7
 
8
8
  return React.useCallback(async () => {
9
9
  await queryClient.invalidateQueries({ queryKey: ["authToken"] });
10
- // TODO: Unclear why, but invalidating other widgets' queries immediately
11
- // after the authToken invalidation (even though it's async?) doesn't work,
12
- // and We end up with stale data. Waiting for the next tick fixes this.
13
- await new Promise((resolve) => setTimeout(resolve, 1));
14
10
  return queryClient.invalidateQueries({
15
11
  predicate: (query) => isClientGraphqlQueryKey(query.queryKey),
16
12
  });
package/src/lib/utils.ts CHANGED
@@ -4,18 +4,6 @@ export const canUseDOM = !!(
4
4
  window.document.createElement
5
5
  );
6
6
 
7
- export function composeEventHandlers<E extends { defaultPrevented: boolean }>(
8
- originalEventHandler?: (event: E) => void,
9
- ourEventHandler?: (event: E) => void,
10
- ) {
11
- return function handleEvent(event: E) {
12
- originalEventHandler?.(event);
13
- if (!event || !event.defaultPrevented) {
14
- return ourEventHandler?.(event);
15
- }
16
- };
17
- }
18
-
19
7
  export function getBestName({
20
8
  firstName,
21
9
  lastName,
@@ -20,9 +20,12 @@ const ApiKeysSearch = React.forwardRef<HTMLInputElement, ApiKeysSearchProps>(
20
20
  const { dispatch } = useApiKeysContext();
21
21
  const translate = useTranslation();
22
22
 
23
- const filter = useDebouncedCallback((value) => {
24
- dispatch({ type: "FILTER_BY_SEARCH", searchQuery: value });
25
- }, 200);
23
+ const filter = useDebouncedCallback(
24
+ (value) => {
25
+ dispatch({ type: "FILTER_BY_SEARCH", searchQuery: value });
26
+ },
27
+ { wait: 200 },
28
+ );
26
29
 
27
30
  const resetSearch = () => {
28
31
  clearSearch();
@@ -22,9 +22,12 @@ const UserApiKeysSearch = React.forwardRef<
22
22
  const { dispatch } = useUserApiKeysContext();
23
23
  const translate = useTranslation();
24
24
 
25
- const filter = useDebouncedCallback((value) => {
26
- dispatch({ type: "FILTER_BY_SEARCH", searchQuery: value });
27
- }, 200);
25
+ const filter = useDebouncedCallback(
26
+ (value) => {
27
+ dispatch({ type: "FILTER_BY_SEARCH", searchQuery: value });
28
+ },
29
+ { wait: 200 },
30
+ );
28
31
 
29
32
  const resetSearch = () => {
30
33
  clearSearch();