@liveblocks/react-ui 2.25.0-aiprivatebeta8 → 3.0.0

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 (181) hide show
  1. package/_private/package.json +2 -2
  2. package/dist/_private/index.cjs +14 -12
  3. package/dist/_private/index.cjs.map +1 -1
  4. package/dist/_private/index.d.cts +146 -113
  5. package/dist/_private/index.d.ts +146 -113
  6. package/dist/_private/index.js +10 -5
  7. package/dist/_private/index.js.map +1 -1
  8. package/dist/components/AiChat.cjs +85 -121
  9. package/dist/components/AiChat.cjs.map +1 -1
  10. package/dist/components/AiChat.js +87 -123
  11. package/dist/components/AiChat.js.map +1 -1
  12. package/dist/components/AiTool.cjs +188 -0
  13. package/dist/components/AiTool.cjs.map +1 -0
  14. package/dist/components/AiTool.js +186 -0
  15. package/dist/components/AiTool.js.map +1 -0
  16. package/dist/components/Comment.cjs +259 -238
  17. package/dist/components/Comment.cjs.map +1 -1
  18. package/dist/components/Comment.js +261 -240
  19. package/dist/components/Comment.js.map +1 -1
  20. package/dist/components/Composer.cjs +42 -30
  21. package/dist/components/Composer.cjs.map +1 -1
  22. package/dist/components/Composer.js +44 -32
  23. package/dist/components/Composer.js.map +1 -1
  24. package/dist/components/InboxNotificationList.cjs +11 -3
  25. package/dist/components/InboxNotificationList.cjs.map +1 -1
  26. package/dist/components/InboxNotificationList.js +12 -4
  27. package/dist/components/InboxNotificationList.js.map +1 -1
  28. package/dist/components/Thread.cjs +7 -1
  29. package/dist/components/Thread.cjs.map +1 -1
  30. package/dist/components/Thread.js +8 -2
  31. package/dist/components/Thread.js.map +1 -1
  32. package/dist/components/internal/AiChatAssistantMessage.cjs +75 -199
  33. package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
  34. package/dist/components/internal/AiChatAssistantMessage.js +77 -201
  35. package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
  36. package/dist/components/internal/AiChatComposer.cjs +1 -1
  37. package/dist/components/internal/AiChatComposer.cjs.map +1 -1
  38. package/dist/components/internal/AiChatComposer.js +1 -1
  39. package/dist/components/internal/AiChatComposer.js.map +1 -1
  40. package/dist/components/internal/AiChatUserMessage.cjs +17 -10
  41. package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
  42. package/dist/components/internal/AiChatUserMessage.js +17 -10
  43. package/dist/components/internal/AiChatUserMessage.js.map +1 -1
  44. package/dist/components/internal/Button.cjs.map +1 -1
  45. package/dist/components/internal/Button.js.map +1 -1
  46. package/dist/components/internal/CodeBlock.cjs +75 -0
  47. package/dist/components/internal/CodeBlock.cjs.map +1 -0
  48. package/dist/components/internal/CodeBlock.js +73 -0
  49. package/dist/components/internal/CodeBlock.js.map +1 -0
  50. package/dist/components/internal/Dropdown.cjs +1 -1
  51. package/dist/components/internal/Dropdown.cjs.map +1 -1
  52. package/dist/components/internal/Dropdown.js +2 -2
  53. package/dist/components/internal/Dropdown.js.map +1 -1
  54. package/dist/components/internal/Emoji.cjs +12 -4
  55. package/dist/components/internal/Emoji.cjs.map +1 -1
  56. package/dist/components/internal/Emoji.js +12 -4
  57. package/dist/components/internal/Emoji.js.map +1 -1
  58. package/dist/components/internal/EmojiPicker.cjs +1 -1
  59. package/dist/components/internal/EmojiPicker.cjs.map +1 -1
  60. package/dist/components/internal/EmojiPicker.js +2 -2
  61. package/dist/components/internal/EmojiPicker.js.map +1 -1
  62. package/dist/components/internal/InboxNotificationThread.cjs +5 -2
  63. package/dist/components/internal/InboxNotificationThread.cjs.map +1 -1
  64. package/dist/components/internal/InboxNotificationThread.js +6 -3
  65. package/dist/components/internal/InboxNotificationThread.js.map +1 -1
  66. package/dist/components/internal/Prose.cjs +37 -0
  67. package/dist/components/internal/Prose.cjs.map +1 -0
  68. package/dist/components/internal/Prose.js +35 -0
  69. package/dist/components/internal/Prose.js.map +1 -0
  70. package/dist/components/internal/Tooltip.cjs +1 -1
  71. package/dist/components/internal/Tooltip.cjs.map +1 -1
  72. package/dist/components/internal/Tooltip.js +2 -2
  73. package/dist/components/internal/Tooltip.js.map +1 -1
  74. package/dist/config.cjs +9 -9
  75. package/dist/config.cjs.map +1 -1
  76. package/dist/config.js +8 -8
  77. package/dist/config.js.map +1 -1
  78. package/dist/icons/CrossCircleFill.cjs +25 -0
  79. package/dist/icons/CrossCircleFill.cjs.map +1 -0
  80. package/dist/icons/CrossCircleFill.js +23 -0
  81. package/dist/icons/CrossCircleFill.js.map +1 -0
  82. package/dist/icons/MinusCircle.cjs +23 -0
  83. package/dist/icons/MinusCircle.cjs.map +1 -0
  84. package/dist/icons/MinusCircle.js +21 -0
  85. package/dist/icons/MinusCircle.js.map +1 -0
  86. package/dist/icons/index.cjs +4 -0
  87. package/dist/icons/index.cjs.map +1 -1
  88. package/dist/icons/index.js +2 -0
  89. package/dist/icons/index.js.map +1 -1
  90. package/dist/index.cjs +3 -3
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.cts +270 -22
  93. package/dist/index.d.ts +270 -22
  94. package/dist/index.js +2 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/overrides.cjs +6 -4
  97. package/dist/overrides.cjs.map +1 -1
  98. package/dist/overrides.js +6 -4
  99. package/dist/overrides.js.map +1 -1
  100. package/dist/primitives/AiMessage/contexts.cjs +18 -0
  101. package/dist/primitives/AiMessage/contexts.cjs.map +1 -0
  102. package/dist/primitives/AiMessage/contexts.js +15 -0
  103. package/dist/primitives/AiMessage/contexts.js.map +1 -0
  104. package/dist/primitives/AiMessage/index.cjs +77 -0
  105. package/dist/primitives/AiMessage/index.cjs.map +1 -0
  106. package/dist/primitives/AiMessage/index.js +75 -0
  107. package/dist/primitives/AiMessage/index.js.map +1 -0
  108. package/dist/primitives/AiMessage/tool-invocation.cjs +70 -0
  109. package/dist/primitives/AiMessage/tool-invocation.cjs.map +1 -0
  110. package/dist/primitives/AiMessage/tool-invocation.js +68 -0
  111. package/dist/primitives/AiMessage/tool-invocation.js.map +1 -0
  112. package/dist/primitives/{internal/Collapsible → Collapsible}/index.cjs +39 -17
  113. package/dist/primitives/Collapsible/index.cjs.map +1 -0
  114. package/dist/primitives/{internal/Collapsible → Collapsible}/index.js +37 -15
  115. package/dist/primitives/Collapsible/index.js.map +1 -0
  116. package/dist/primitives/Comment/index.cjs +5 -4
  117. package/dist/primitives/Comment/index.cjs.map +1 -1
  118. package/dist/primitives/Comment/index.js +5 -4
  119. package/dist/primitives/Comment/index.js.map +1 -1
  120. package/dist/primitives/Composer/index.cjs +49 -41
  121. package/dist/primitives/Composer/index.cjs.map +1 -1
  122. package/dist/primitives/Composer/index.js +50 -42
  123. package/dist/primitives/Composer/index.js.map +1 -1
  124. package/dist/primitives/Composer/slate/plugins/mentions.cjs +4 -4
  125. package/dist/primitives/Composer/slate/plugins/mentions.cjs.map +1 -1
  126. package/dist/primitives/Composer/slate/plugins/mentions.js +4 -4
  127. package/dist/primitives/Composer/slate/plugins/mentions.js.map +1 -1
  128. package/dist/primitives/Composer/utils.cjs +3 -6
  129. package/dist/primitives/Composer/utils.cjs.map +1 -1
  130. package/dist/primitives/Composer/utils.js +3 -6
  131. package/dist/primitives/Composer/utils.js.map +1 -1
  132. package/dist/primitives/{internal/Markdown.cjs → Markdown.cjs} +105 -65
  133. package/dist/primitives/Markdown.cjs.map +1 -0
  134. package/dist/primitives/{internal/Markdown.js → Markdown.js} +106 -65
  135. package/dist/primitives/Markdown.js.map +1 -0
  136. package/dist/primitives/index.cjs +4 -6
  137. package/dist/primitives/index.cjs.map +1 -1
  138. package/dist/primitives/index.d.cts +20 -93
  139. package/dist/primitives/index.d.ts +20 -93
  140. package/dist/primitives/index.js +4 -6
  141. package/dist/primitives/index.js.map +1 -1
  142. package/dist/utils/ErrorBoundary.cjs +48 -0
  143. package/dist/utils/ErrorBoundary.cjs.map +1 -0
  144. package/dist/utils/ErrorBoundary.js +45 -0
  145. package/dist/utils/ErrorBoundary.js.map +1 -0
  146. package/dist/utils/use-controllable-state.cjs +25 -2
  147. package/dist/utils/use-controllable-state.cjs.map +1 -1
  148. package/dist/utils/use-controllable-state.js +25 -3
  149. package/dist/utils/use-controllable-state.js.map +1 -1
  150. package/dist/utils/use-visible.cjs +65 -45
  151. package/dist/utils/use-visible.cjs.map +1 -1
  152. package/dist/utils/use-visible.js +66 -46
  153. package/dist/utils/use-visible.js.map +1 -1
  154. package/dist/version.cjs +1 -1
  155. package/dist/version.cjs.map +1 -1
  156. package/dist/version.js +1 -1
  157. package/dist/version.js.map +1 -1
  158. package/package.json +16 -5
  159. package/primitives/package.json +2 -2
  160. package/src/styles/constants.css +1 -1
  161. package/src/styles/dark/index.css +7 -3
  162. package/src/styles/index.css +572 -252
  163. package/src/styles/utils.css +1 -1
  164. package/styles/dark/attributes.css +1 -1
  165. package/styles/dark/attributes.css.map +1 -1
  166. package/styles/dark/media-query.css +1 -1
  167. package/styles/dark/media-query.css.map +1 -1
  168. package/styles.css +1 -1
  169. package/styles.css.map +1 -1
  170. package/dist/components/AiToolDebugger.cjs +0 -74
  171. package/dist/components/AiToolDebugger.cjs.map +0 -1
  172. package/dist/components/AiToolDebugger.js +0 -72
  173. package/dist/components/AiToolDebugger.js.map +0 -1
  174. package/dist/primitives/internal/Collapsible/index.cjs.map +0 -1
  175. package/dist/primitives/internal/Collapsible/index.js.map +0 -1
  176. package/dist/primitives/internal/Emoji.cjs +0 -32
  177. package/dist/primitives/internal/Emoji.cjs.map +0 -1
  178. package/dist/primitives/internal/Emoji.js +0 -30
  179. package/dist/primitives/internal/Emoji.js.map +0 -1
  180. package/dist/primitives/internal/Markdown.cjs.map +0 -1
  181. package/dist/primitives/internal/Markdown.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { ElementType, ComponentPropsWithoutRef, FormEvent, ReactNode, ComponentType } from 'react';
3
- import { CommentBody as CommentBody$1, CommentAttachment, CommentMixedAttachment } from '@liveblocks/core';
2
+ import { ElementType, ComponentPropsWithoutRef, ReactNode, ComponentType, FormEvent } from 'react';
3
+ import { MentionData, CommentBody as CommentBody$1, CommentAttachment, CommentMixedAttachment } from '@liveblocks/core';
4
4
 
5
5
  type Direction = "ltr" | "rtl";
6
6
  type SlotProp = {
@@ -22,85 +22,12 @@ type ComposerBodyMarks = {
22
22
  [K in ComposerBodyMark]: boolean;
23
23
  };
24
24
 
25
- interface AiChatComposerFormProps extends ComponentPropsWithSlot<"form"> {
26
- /**
27
- * The event handler called when a chat message is submitted.
28
- */
29
- onComposerSubmit?: (message: {
30
- /**
31
- * The submitted message text.
32
- */
33
- text: string;
34
- }, event: FormEvent<HTMLFormElement>) => void;
35
- /**
36
- * Whether the composer is disabled.
37
- */
38
- disabled?: boolean;
39
- }
40
- interface AiChatComposerEditorProps extends Omit<ComponentPropsWithoutRef<"div">, "defaultValue"> {
41
- /**
42
- * The editor's initial value.
43
- */
44
- defaultValue?: string;
45
- /**
46
- * The text to display when the editor is empty.
47
- */
48
- placeholder?: string;
49
- /**
50
- * Whether the editor is disabled.
51
- */
52
- disabled?: boolean;
53
- /**
54
- * Whether to focus the editor on mount.
55
- */
56
- autoFocus?: boolean;
57
- }
58
-
59
- /**
60
- * Surrounds the chat composer's content and handles submissions.
61
- *
62
- * @example
63
- * <AiChatComposer.Form onComposerSubmit={({ text }) => {}}>
64
- * <AiChatComposer.Editor />
65
- * <AiChatComposer.Submit />
66
- * </AiChatComposer.Form>
67
- */
68
- declare const AiChatComposerForm: react.ForwardRefExoticComponent<AiChatComposerFormProps & react.RefAttributes<HTMLFormElement>>;
69
- /**
70
- * Displays the chat composer's editor.
71
- *
72
- * @example
73
- * <AiChatComposer.Editor placeholder="Write a message…" />
74
- */
75
- declare const AiChatComposerEditor: react.ForwardRefExoticComponent<AiChatComposerEditorProps & react.RefAttributes<HTMLDivElement>>;
76
- /**
77
- * A button to submit a chat message.
78
- *
79
- * @example
80
- * <AiChatComposer.Submit>Send</AiChatComposer.Submit>
81
- */
82
- declare const AiChatComposerSubmit: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & SlotProp & react.RefAttributes<HTMLButtonElement>>;
83
-
84
- declare const index$2_AiChatComposerEditor: typeof AiChatComposerEditor;
85
- declare const index$2_AiChatComposerForm: typeof AiChatComposerForm;
86
- declare const index$2_AiChatComposerSubmit: typeof AiChatComposerSubmit;
87
- declare namespace index$2 {
88
- export {
89
- index$2_AiChatComposerEditor as AiChatComposerEditor,
90
- index$2_AiChatComposerForm as AiChatComposerForm,
91
- index$2_AiChatComposerSubmit as AiChatComposerSubmit,
92
- AiChatComposerEditor as Editor,
93
- AiChatComposerForm as Form,
94
- AiChatComposerSubmit as Submit,
95
- };
96
- }
97
-
98
25
  type CommentMentionProps = ComponentPropsWithSlot<"span">;
99
26
  type CommentBodyMentionProps = {
100
27
  /**
101
- * The mention's user ID.
28
+ * The mention to display.
102
29
  */
103
- userId: string;
30
+ mention: MentionData;
104
31
  };
105
32
  type CommentLinkProps = ComponentPropsWithSlot<"a">;
106
33
  interface CommentBodyLinkProps {
@@ -127,7 +54,7 @@ interface CommentBodyComponents {
127
54
  */
128
55
  Link: ComponentType<CommentBodyLinkProps>;
129
56
  }
130
- interface CommentBodyProps extends Omit<ComponentPropsWithSlot<"div">, "children"> {
57
+ interface CommentBodyProps extends ComponentPropsWithSlot<"div"> {
131
58
  /**
132
59
  * The comment body to display.
133
60
  * If not defined, the component will render `null`.
@@ -143,7 +70,7 @@ interface CommentBodyProps extends Omit<ComponentPropsWithSlot<"div">, "children
143
70
  * Displays mentions within `Comment.Body`.
144
71
  *
145
72
  * @example
146
- * <Comment.Mention>@{userId}</Comment.Mention>
73
+ * <Comment.Mention>@{mention.id}</Comment.Mention>
147
74
  */
148
75
  declare const CommentMention: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & SlotProp & react.RefAttributes<HTMLSpanElement>>;
149
76
  /**
@@ -175,9 +102,9 @@ interface ComposerEditorMentionProps {
175
102
  */
176
103
  isSelected: boolean;
177
104
  /**
178
- * The mention's user ID.
105
+ * The mention to display.
179
106
  */
180
- userId: string;
107
+ mention: MentionData;
181
108
  }
182
109
  interface ComposerEditorLinkProps {
183
110
  /**
@@ -195,13 +122,13 @@ interface ComposerEditorLinkProps {
195
122
  }
196
123
  type ComposerEditorMentionSuggestionsProps = {
197
124
  /**
198
- * The list of suggested user IDs.
125
+ * The list of mention suggestions.
199
126
  */
200
- userIds: string[];
127
+ mentions: MentionData[];
201
128
  /**
202
- * The currently selected user ID.
129
+ * The currently selected mention's ID.
203
130
  */
204
- selectedUserId?: string;
131
+ selectedMentionId?: string;
205
132
  };
206
133
  type ComposerEditorFloatingToolbarProps = Record<string, never>;
207
134
  type ComposerMentionProps = ComponentPropsWithSlot<"span">;
@@ -232,7 +159,7 @@ interface ComposerEditorComponents {
232
159
  */
233
160
  FloatingToolbar?: ComponentType<ComposerEditorFloatingToolbarProps>;
234
161
  }
235
- interface ComposerEditorProps extends Omit<ComponentPropsWithoutRef<"div">, "defaultValue"> {
162
+ interface ComposerEditorProps extends Omit<ComponentPropsWithoutRef<"div">, "defaultValue" | "children"> {
236
163
  /**
237
164
  * The reading direction of the editor and related elements.
238
165
  */
@@ -329,7 +256,7 @@ declare const ComposerFloatingToolbar: react.ForwardRefExoticComponent<Omit<reac
329
256
  * Displays mentions within `Composer.Editor`.
330
257
  *
331
258
  * @example
332
- * <Composer.Mention>@{userId}</Composer.Mention>
259
+ * <Composer.Mention>@{mention.id}</Composer.Mention>
333
260
  */
334
261
  declare const ComposerMention: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & SlotProp & react.RefAttributes<HTMLSpanElement>>;
335
262
  /**
@@ -348,9 +275,9 @@ declare const ComposerSuggestions: react.ForwardRefExoticComponent<Omit<react.De
348
275
  *
349
276
  * @example
350
277
  * <Composer.SuggestionsList>
351
- * {userIds.map((userId) => (
352
- * <Composer.SuggestionsListItem key={userId} value={userId}>
353
- * @{userId}
278
+ * {mentions.map((mention) => (
279
+ * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>
280
+ * @{mention.id}
354
281
  * </Composer.SuggestionsListItem>
355
282
  * ))}
356
283
  * </Composer.SuggestionsList>
@@ -360,8 +287,8 @@ declare const ComposerSuggestionsList: react.ForwardRefExoticComponent<Omit<reac
360
287
  * Displays a suggestion within `Composer.SuggestionsList`.
361
288
  *
362
289
  * @example
363
- * <Composer.SuggestionsListItem key={userId} value={userId}>
364
- * @{userId}
290
+ * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>
291
+ * @{mention.id}
365
292
  * </Composer.SuggestionsListItem>
366
293
  */
367
294
  declare const ComposerSuggestionsListItem: react.ForwardRefExoticComponent<ComposerSuggestionsListItemProps & react.RefAttributes<HTMLLIElement>>;
@@ -567,4 +494,4 @@ interface TimestampProps extends Omit<ComponentPropsWithSlot<"time">, "children"
567
494
  */
568
495
  declare const Timestamp: react.ForwardRefExoticComponent<TimestampProps & react.RefAttributes<HTMLTimeElement>>;
569
496
 
570
- export { index$2 as AiChatComposer, AttachmentTooLargeError, index$1 as Comment, CommentBodyComponents, CommentBodyLinkProps, CommentBodyMentionProps, CommentBodyProps, CommentLinkProps, CommentMentionProps, index as Composer, ComposerAttachFilesProps, ComposerAttachmentsDropAreaProps, ComposerBodyMark, ComposerBodyMarks, ComposerContext, ComposerEditorComponents, ComposerEditorFloatingToolbarProps, ComposerEditorLinkProps, ComposerEditorMentionProps, ComposerEditorMentionSuggestionsProps, ComposerEditorProps, ComposerFloatingToolbarProps, ComposerFormProps, ComposerLinkProps, ComposerMarkToggleProps, ComposerMentionProps, ComposerSubmitComment, ComposerSubmitProps, ComposerSuggestionsListItemProps, ComposerSuggestionsListProps, FileSize, FileSizeProps, Timestamp, TimestampProps, useComposer };
497
+ export { AttachmentTooLargeError, index$1 as Comment, CommentBodyComponents, CommentBodyLinkProps, CommentBodyMentionProps, CommentBodyProps, CommentLinkProps, CommentMentionProps, index as Composer, ComposerAttachFilesProps, ComposerAttachmentsDropAreaProps, ComposerBodyMark, ComposerBodyMarks, ComposerContext, ComposerEditorComponents, ComposerEditorFloatingToolbarProps, ComposerEditorLinkProps, ComposerEditorMentionProps, ComposerEditorMentionSuggestionsProps, ComposerEditorProps, ComposerFloatingToolbarProps, ComposerFormProps, ComposerLinkProps, ComposerMarkToggleProps, ComposerMentionProps, ComposerSubmitComment, ComposerSubmitProps, ComposerSuggestionsListItemProps, ComposerSuggestionsListProps, FileSize, FileSizeProps, Timestamp, TimestampProps, useComposer };
@@ -1,9 +1,7 @@
1
- import * as index from './AiChatComposer/index.js';
2
- export { index as AiChatComposer };
3
- import * as index$1 from './Comment/index.js';
4
- export { index$1 as Comment };
5
- import * as index$2 from './Composer/index.js';
6
- export { index$2 as Composer };
1
+ import * as index from './Comment/index.js';
2
+ export { index as Comment };
3
+ import * as index$1 from './Composer/index.js';
4
+ export { index$1 as Composer };
7
5
  export { useComposer } from './Composer/contexts.js';
8
6
  export { AttachmentTooLargeError } from './Composer/utils.js';
9
7
  export { FileSize } from './FileSize.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+
6
+ const ErrorBoundaryContext = react.createContext(null);
7
+ class ErrorBoundary extends react.Component {
8
+ constructor(props) {
9
+ super(props);
10
+ this.state = { error: null };
11
+ }
12
+ static getDerivedStateFromError(error) {
13
+ return { error };
14
+ }
15
+ componentDidCatch(error, errorInfo) {
16
+ console.error(error, errorInfo);
17
+ }
18
+ reset() {
19
+ this.setState({ error: null });
20
+ }
21
+ render() {
22
+ if (this.state.error === null)
23
+ return this.props.children;
24
+ const error = this.state.error;
25
+ const reset = this.reset.bind(this);
26
+ const fallback = this.props.fallback;
27
+ const Fallback = typeof fallback === "function" ? fallback : () => fallback ?? null;
28
+ return /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundaryContext.Provider, {
29
+ value: { error, reset },
30
+ children: /* @__PURE__ */ jsxRuntime.jsx(Fallback, {
31
+ error: this.state.error
32
+ })
33
+ });
34
+ }
35
+ }
36
+ function useErrorBoundary() {
37
+ const context = react.useContext(ErrorBoundaryContext);
38
+ if (context === null) {
39
+ throw new Error(
40
+ "useErrorBoundary must be used within an ErrorBoundary component"
41
+ );
42
+ }
43
+ return context;
44
+ }
45
+
46
+ exports.ErrorBoundary = ErrorBoundary;
47
+ exports.useErrorBoundary = useErrorBoundary;
48
+ //# sourceMappingURL=ErrorBoundary.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorBoundary.cjs","sources":["../../src/utils/ErrorBoundary.tsx"],"sourcesContent":["import type { ComponentType, ErrorInfo, ReactNode } from \"react\";\nimport { Component, createContext, useContext } from \"react\";\n\nconst ErrorBoundaryContext = createContext<{\n error: Error;\n reset: () => void;\n} | null>(null);\n\nexport interface ErrorBoundaryProps {\n children?: ReactNode;\n fallback?: ReactNode | ComponentType<{ error: Error }>;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n console.error(error, errorInfo);\n }\n\n reset() {\n this.setState({ error: null });\n }\n\n render(): React.ReactNode {\n if (this.state.error === null) return this.props.children;\n\n const error = this.state.error;\n const reset = this.reset.bind(this);\n const fallback = this.props.fallback;\n const Fallback =\n typeof fallback === \"function\" ? fallback : () => fallback ?? null;\n return (\n <ErrorBoundaryContext.Provider value={{ error, reset }}>\n <Fallback error={this.state.error} />\n </ErrorBoundaryContext.Provider>\n );\n }\n}\n\nexport function useErrorBoundary(): {\n error: Error;\n reset: () => void;\n} {\n const context = useContext(ErrorBoundaryContext);\n\n if (context === null) {\n throw new Error(\n \"useErrorBoundary must be used within an ErrorBoundary component\"\n );\n }\n\n return context;\n}\n"],"names":["createContext","Component","jsx","useContext"],"mappings":";;;;;AAGA,MAAM,oBAAA,GAAuBA,oBAGnB,IAAI,CAAA,CAAA;AAWP,MAAM,sBAAsBC,eAGjC,CAAA;AAAA,EACA,YAAY,KAA2B,EAAA;AACrC,IAAA,KAAA,CAAM,KAAK,CAAA,CAAA;AACX,IAAK,IAAA,CAAA,KAAA,GAAQ,EAAE,KAAA,EAAO,IAAK,EAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,OAAO,yBAAyB,KAAc,EAAA;AAC5C,IAAA,OAAO,EAAE,KAAM,EAAA,CAAA;AAAA,GACjB;AAAA,EAEA,iBAAA,CAAkB,OAAc,SAA4B,EAAA;AAC1D,IAAQ,OAAA,CAAA,KAAA,CAAM,OAAO,SAAS,CAAA,CAAA;AAAA,GAChC;AAAA,EAEA,KAAQ,GAAA;AACN,IAAA,IAAA,CAAK,QAAS,CAAA,EAAE,KAAO,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,GAC/B;AAAA,EAEA,MAA0B,GAAA;AACxB,IAAI,IAAA,IAAA,CAAK,MAAM,KAAU,KAAA,IAAA;AAAM,MAAA,OAAO,KAAK,KAAM,CAAA,QAAA,CAAA;AAEjD,IAAM,MAAA,KAAA,GAAQ,KAAK,KAAM,CAAA,KAAA,CAAA;AACzB,IAAA,MAAM,KAAQ,GAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAClC,IAAM,MAAA,QAAA,GAAW,KAAK,KAAM,CAAA,QAAA,CAAA;AAC5B,IAAA,MAAM,WACJ,OAAO,QAAA,KAAa,UAAa,GAAA,QAAA,GAAW,MAAM,QAAY,IAAA,IAAA,CAAA;AAChE,IACE,uBAAAC,cAAA,CAAC,qBAAqB,QAArB,EAAA;AAAA,MAA8B,KAAA,EAAO,EAAE,KAAA,EAAO,KAAM,EAAA;AAAA,MACnD,QAAC,kBAAAA,cAAA,CAAA,QAAA,EAAA;AAAA,QAAS,KAAA,EAAO,KAAK,KAAM,CAAA,KAAA;AAAA,OAAO,CAAA;AAAA,KACrC,CAAA,CAAA;AAAA,GAEJ;AACF,CAAA;AAEO,SAAS,gBAGd,GAAA;AACA,EAAM,MAAA,OAAA,GAAUC,iBAAW,oBAAoB,CAAA,CAAA;AAE/C,EAAA,IAAI,YAAY,IAAM,EAAA;AACpB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iEAAA;AAAA,KACF,CAAA;AAAA,GACF;AAEA,EAAO,OAAA,OAAA,CAAA;AACT;;;;;"}
@@ -0,0 +1,45 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, Component, useContext } from 'react';
3
+
4
+ const ErrorBoundaryContext = createContext(null);
5
+ class ErrorBoundary extends Component {
6
+ constructor(props) {
7
+ super(props);
8
+ this.state = { error: null };
9
+ }
10
+ static getDerivedStateFromError(error) {
11
+ return { error };
12
+ }
13
+ componentDidCatch(error, errorInfo) {
14
+ console.error(error, errorInfo);
15
+ }
16
+ reset() {
17
+ this.setState({ error: null });
18
+ }
19
+ render() {
20
+ if (this.state.error === null)
21
+ return this.props.children;
22
+ const error = this.state.error;
23
+ const reset = this.reset.bind(this);
24
+ const fallback = this.props.fallback;
25
+ const Fallback = typeof fallback === "function" ? fallback : () => fallback ?? null;
26
+ return /* @__PURE__ */ jsx(ErrorBoundaryContext.Provider, {
27
+ value: { error, reset },
28
+ children: /* @__PURE__ */ jsx(Fallback, {
29
+ error: this.state.error
30
+ })
31
+ });
32
+ }
33
+ }
34
+ function useErrorBoundary() {
35
+ const context = useContext(ErrorBoundaryContext);
36
+ if (context === null) {
37
+ throw new Error(
38
+ "useErrorBoundary must be used within an ErrorBoundary component"
39
+ );
40
+ }
41
+ return context;
42
+ }
43
+
44
+ export { ErrorBoundary, useErrorBoundary };
45
+ //# sourceMappingURL=ErrorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorBoundary.js","sources":["../../src/utils/ErrorBoundary.tsx"],"sourcesContent":["import type { ComponentType, ErrorInfo, ReactNode } from \"react\";\nimport { Component, createContext, useContext } from \"react\";\n\nconst ErrorBoundaryContext = createContext<{\n error: Error;\n reset: () => void;\n} | null>(null);\n\nexport interface ErrorBoundaryProps {\n children?: ReactNode;\n fallback?: ReactNode | ComponentType<{ error: Error }>;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n console.error(error, errorInfo);\n }\n\n reset() {\n this.setState({ error: null });\n }\n\n render(): React.ReactNode {\n if (this.state.error === null) return this.props.children;\n\n const error = this.state.error;\n const reset = this.reset.bind(this);\n const fallback = this.props.fallback;\n const Fallback =\n typeof fallback === \"function\" ? fallback : () => fallback ?? null;\n return (\n <ErrorBoundaryContext.Provider value={{ error, reset }}>\n <Fallback error={this.state.error} />\n </ErrorBoundaryContext.Provider>\n );\n }\n}\n\nexport function useErrorBoundary(): {\n error: Error;\n reset: () => void;\n} {\n const context = useContext(ErrorBoundaryContext);\n\n if (context === null) {\n throw new Error(\n \"useErrorBoundary must be used within an ErrorBoundary component\"\n );\n }\n\n return context;\n}\n"],"names":[],"mappings":";;;AAGA,MAAM,oBAAA,GAAuB,cAGnB,IAAI,CAAA,CAAA;AAWP,MAAM,sBAAsB,SAGjC,CAAA;AAAA,EACA,YAAY,KAA2B,EAAA;AACrC,IAAA,KAAA,CAAM,KAAK,CAAA,CAAA;AACX,IAAK,IAAA,CAAA,KAAA,GAAQ,EAAE,KAAA,EAAO,IAAK,EAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,OAAO,yBAAyB,KAAc,EAAA;AAC5C,IAAA,OAAO,EAAE,KAAM,EAAA,CAAA;AAAA,GACjB;AAAA,EAEA,iBAAA,CAAkB,OAAc,SAA4B,EAAA;AAC1D,IAAQ,OAAA,CAAA,KAAA,CAAM,OAAO,SAAS,CAAA,CAAA;AAAA,GAChC;AAAA,EAEA,KAAQ,GAAA;AACN,IAAA,IAAA,CAAK,QAAS,CAAA,EAAE,KAAO,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,GAC/B;AAAA,EAEA,MAA0B,GAAA;AACxB,IAAI,IAAA,IAAA,CAAK,MAAM,KAAU,KAAA,IAAA;AAAM,MAAA,OAAO,KAAK,KAAM,CAAA,QAAA,CAAA;AAEjD,IAAM,MAAA,KAAA,GAAQ,KAAK,KAAM,CAAA,KAAA,CAAA;AACzB,IAAA,MAAM,KAAQ,GAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAClC,IAAM,MAAA,QAAA,GAAW,KAAK,KAAM,CAAA,QAAA,CAAA;AAC5B,IAAA,MAAM,WACJ,OAAO,QAAA,KAAa,UAAa,GAAA,QAAA,GAAW,MAAM,QAAY,IAAA,IAAA,CAAA;AAChE,IACE,uBAAA,GAAA,CAAC,qBAAqB,QAArB,EAAA;AAAA,MAA8B,KAAA,EAAO,EAAE,KAAA,EAAO,KAAM,EAAA;AAAA,MACnD,QAAC,kBAAA,GAAA,CAAA,QAAA,EAAA;AAAA,QAAS,KAAA,EAAO,KAAK,KAAM,CAAA,KAAA;AAAA,OAAO,CAAA;AAAA,KACrC,CAAA,CAAA;AAAA,GAEJ;AACF,CAAA;AAEO,SAAS,gBAGd,GAAA;AACA,EAAM,MAAA,OAAA,GAAU,WAAW,oBAAoB,CAAA,CAAA;AAE/C,EAAA,IAAI,YAAY,IAAM,EAAA;AACpB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iEAAA;AAAA,KACF,CAAA;AAAA,GACF;AAEA,EAAO,OAAA,OAAA,CAAA;AACT;;;;"}
@@ -2,15 +2,16 @@
2
2
 
3
3
  var core = require('@liveblocks/core');
4
4
  var react = require('react');
5
+ var useRerender = require('./use-rerender.cjs');
5
6
 
6
- function useControllableState(value, onChange, defaultValue) {
7
+ function useControllableState(defaultValue, value, onChange) {
7
8
  const [uncontrolledValue, setUncontrolledValue] = react.useState(defaultValue);
8
9
  const isControlled = value !== void 0;
9
10
  const wasControlled = react.useRef(isControlled);
10
11
  react.useEffect(() => {
11
12
  if (process.env.NODE_ENV !== "production" && wasControlled.current !== isControlled) {
12
13
  core.console.warn(
13
- `A component is changing from ${wasControlled ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}.`
14
+ `A component is changing from ${wasControlled.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}.`
14
15
  );
15
16
  }
16
17
  wasControlled.current = isControlled;
@@ -29,6 +30,28 @@ function useControllableState(value, onChange, defaultValue) {
29
30
  );
30
31
  return [currentValue, setValue];
31
32
  }
33
+ function useSemiControllableState(value, onChange) {
34
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(value);
35
+ const lastChange = react.useRef("controlled");
36
+ const lastValue = react.useRef(value);
37
+ const [rerender] = useRerender.useRerender();
38
+ if (!Object.is(lastValue.current, value)) {
39
+ lastValue.current = value;
40
+ lastChange.current = "controlled";
41
+ }
42
+ const setValue = react.useCallback(
43
+ (value2) => {
44
+ lastChange.current = "uncontrolled";
45
+ setUncontrolledValue(value2);
46
+ rerender();
47
+ onChange?.(value2);
48
+ },
49
+ [onChange, rerender]
50
+ );
51
+ const currentValue = lastChange.current === "uncontrolled" ? uncontrolledValue : value;
52
+ return [currentValue, setValue];
53
+ }
32
54
 
33
55
  exports.useControllableState = useControllableState;
56
+ exports.useSemiControllableState = useSemiControllableState;
34
57
  //# sourceMappingURL=use-controllable-state.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-controllable-state.cjs","sources":["../../src/utils/use-controllable-state.ts"],"sourcesContent":["import { console } from \"@liveblocks/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function useControllableState<T>(\n value?: T,\n onChange?: (value: T) => void,\n defaultValue?: T\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const wasControlled = useRef(isControlled);\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n wasControlled.current !== isControlled\n ) {\n console.warn(\n `A component is changing from ${\n wasControlled ? \"controlled\" : \"uncontrolled\"\n } to ${isControlled ? \"controlled\" : \"uncontrolled\"}.`\n );\n }\n\n wasControlled.current = isControlled;\n }, [isControlled]);\n\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const setValue = useCallback(\n (value: T) => {\n if (isControlled) {\n return onChange?.(value);\n } else {\n setUncontrolledValue(value);\n\n return onChange?.(value);\n }\n },\n [isControlled, onChange]\n );\n\n return [currentValue, setValue] as const;\n}\n"],"names":["useState","useRef","useEffect","console","useCallback","value"],"mappings":";;;;;AAGgB,SAAA,oBAAA,CACd,KACA,EAAA,QAAA,EACA,YACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAIA,eAAS,YAAY,CAAA,CAAA;AACvE,EAAA,MAAM,eAAe,KAAU,KAAA,KAAA,CAAA,CAAA;AAC/B,EAAM,MAAA,aAAA,GAAgBC,aAAO,YAAY,CAAA,CAAA;AAEzC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IACE,QAAQ,GAAI,CAAA,QAAA,KAAa,YACzB,IAAA,aAAA,CAAc,YAAY,YAC1B,EAAA;AACA,MAAQC,YAAA,CAAA,IAAA;AAAA,QACN,CACE,6BAAA,EAAA,aAAA,GAAgB,YAAe,GAAA,cAAA,CAAA,IAAA,EAC1B,eAAe,YAAe,GAAA,cAAA,CAAA,CAAA,CAAA;AAAA,OACvC,CAAA;AAAA,KACF;AAEA,IAAA,aAAA,CAAc,OAAU,GAAA,YAAA,CAAA;AAAA,GAC1B,EAAG,CAAC,YAAY,CAAC,CAAA,CAAA;AAEjB,EAAM,MAAA,YAAA,GAAe,eAAe,KAAQ,GAAA,iBAAA,CAAA;AAE5C,EAAA,MAAM,QAAW,GAAAC,iBAAA;AAAA,IACf,CAACC,MAAa,KAAA;AACZ,MAAA,IAAI,YAAc,EAAA;AAChB,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OAClB,MAAA;AACL,QAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAE1B,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OACzB;AAAA,KACF;AAAA,IACA,CAAC,cAAc,QAAQ,CAAA;AAAA,GACzB,CAAA;AAEA,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC;;;;"}
1
+ {"version":3,"file":"use-controllable-state.cjs","sources":["../../src/utils/use-controllable-state.ts"],"sourcesContent":["import { console } from \"@liveblocks/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useRerender } from \"./use-rerender\";\n\n/**\n * Hold a state in a \"controlled\" or \"uncontrolled\" way.\n */\nexport function useControllableState<T>(\n /**\n * The default uncontrolled value.\n */\n defaultValue: T,\n\n /**\n * The controlled value.\n *\n * If `undefined`, the returned value is uncontrolled.\n * If set, this controlled value is used and returned as is.\n */\n value: T | undefined,\n\n /**\n * The event handler called when the value changes.\n */\n onChange: ((value: T) => void) | undefined\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const wasControlled = useRef(isControlled);\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n wasControlled.current !== isControlled\n ) {\n console.warn(\n `A component is changing from ${\n wasControlled.current ? \"controlled\" : \"uncontrolled\"\n } to ${isControlled ? \"controlled\" : \"uncontrolled\"}.`\n );\n }\n\n wasControlled.current = isControlled;\n }, [isControlled]);\n\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const setValue = useCallback(\n (value: T) => {\n if (isControlled) {\n return onChange?.(value);\n } else {\n setUncontrolledValue(value);\n\n return onChange?.(value);\n }\n },\n [isControlled, onChange]\n );\n\n return [currentValue, setValue] as const;\n}\n\n/**\n * @experimental\n *\n * Hold a value in a \"semi-controlled\" way: a controlled value that can be\n * overridden by uncontrolled changes in a \"most recent wins\" way.\n *\n * @example\n *\n * A `Collapsible` component uses `useSemiControllableState` to control\n * its \"open\" state, it accepts two optional props: `open` and `onOpenChange`.\n *\n * Internally, it passes them to `useSemiControllableState`:\n *\n * ```tsx\n * const [isOpen, setIsOpen] = useSemiControllableState(\n * open ?? true, // Defaults to `true` if `open` is not provided\n * onOpenChange\n * );\n *\n * // ... `isOpen` and `setIsOpen` are used in the component's implementation ...\n * ```\n *\n * And finally here's how it could be used in a \"semi-controlled\" way:\n *\n * ```tsx\n * const status: `\"loading\" | \"success\" | \"error\"`;\n *\n * <Collapsible open={status === \"success\"} />\n * ```\n *\n * Like with a traditional controlled value, the collapsible will start closed\n * and will automatically open when `status` becomes `\"success\"`.\n *\n * But unlike with a traditional controlled value, the collapsible can still\n * open/close when it's clicked on by the user, overriding `open={status === \"success\"}`.\n *\n * It's possible to use it as a traditional uncontrolled value:\n *\n * ```tsx\n * const defaultOpen = false;\n *\n * <Collapsible open={defaultOpen} />\n * ```\n *\n * Or to sync the uncontrolled value like with a traditional uncontrolled value:\n *\n * ```tsx\n * const [isOpen, setIsOpen] = useState(false);\n *\n * // `isOpen` is synced with the uncontrolled value when the user clicks\n * <Collapsible open={isOpen} onOpenChange={setIsOpen} />\n * ```\n *\n * But with the caveat that it will still be possible to change the\n * uncontrolled value:\n *\n * ```tsx\n * const open = false;\n *\n * // Clicking on the collapsible will still open/close it, unlike with a\n * // traditional controlled value.\n * <Collapsible open={open} />\n * ```\n */\nexport function useSemiControllableState<T>(\n /**\n * The controlled value.\n *\n * When this value changes, it becomes the current value.\n * But unlike a traditional controlled value, it can be overridden by\n * uncontrolled changes.\n */\n value: T,\n\n /**\n * The event handler called when the uncontrolled value changes.\n */\n onChange: ((value: T) => void) | undefined\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(value);\n const lastChange = useRef<\"uncontrolled\" | \"controlled\">(\"controlled\");\n const lastValue = useRef(value);\n const [rerender] = useRerender();\n\n // Listen to `value` changes during the render phase to avoid\n // having to always sync `uncontrolledValue` on every change.\n if (!Object.is(lastValue.current, value)) {\n lastValue.current = value;\n lastChange.current = \"controlled\";\n }\n\n const setValue = useCallback(\n (value: T) => {\n lastChange.current = \"uncontrolled\";\n setUncontrolledValue(value);\n\n // If the new `uncontrolledValue` is the same as last time it was the \"last change\",\n // `setUncontrolledValue` won't trigger a re-render, but the fact that it's becoming\n // uncontrolled again is a change that needs a re-render.\n rerender();\n\n onChange?.(value);\n },\n [onChange, rerender]\n );\n\n const currentValue =\n lastChange.current === \"uncontrolled\" ? uncontrolledValue : value;\n\n return [currentValue, setValue] as const;\n}\n"],"names":["useState","useRef","useEffect","console","useCallback","value","useRerender"],"mappings":";;;;;;AAQgB,SAAA,oBAAA,CAId,YAQA,EAAA,KAAA,EAKA,QACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAIA,eAAS,YAAY,CAAA,CAAA;AACvE,EAAA,MAAM,eAAe,KAAU,KAAA,KAAA,CAAA,CAAA;AAC/B,EAAM,MAAA,aAAA,GAAgBC,aAAO,YAAY,CAAA,CAAA;AAEzC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IACE,QAAQ,GAAI,CAAA,QAAA,KAAa,YACzB,IAAA,aAAA,CAAc,YAAY,YAC1B,EAAA;AACA,MAAQC,YAAA,CAAA,IAAA;AAAA,QACN,gCACE,aAAc,CAAA,OAAA,GAAU,YAAe,GAAA,cAAA,CAAA,IAAA,EAClC,eAAe,YAAe,GAAA,cAAA,CAAA,CAAA,CAAA;AAAA,OACvC,CAAA;AAAA,KACF;AAEA,IAAA,aAAA,CAAc,OAAU,GAAA,YAAA,CAAA;AAAA,GAC1B,EAAG,CAAC,YAAY,CAAC,CAAA,CAAA;AAEjB,EAAM,MAAA,YAAA,GAAe,eAAe,KAAQ,GAAA,iBAAA,CAAA;AAE5C,EAAA,MAAM,QAAW,GAAAC,iBAAA;AAAA,IACf,CAACC,MAAa,KAAA;AACZ,MAAA,IAAI,YAAc,EAAA;AAChB,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OAClB,MAAA;AACL,QAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAE1B,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OACzB;AAAA,KACF;AAAA,IACA,CAAC,cAAc,QAAQ,CAAA;AAAA,GACzB,CAAA;AAEA,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC,CAAA;AAkEgB,SAAA,wBAAA,CAQd,OAKA,QACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAIL,eAAS,KAAK,CAAA,CAAA;AAChE,EAAM,MAAA,UAAA,GAAaC,aAAsC,YAAY,CAAA,CAAA;AACrE,EAAM,MAAA,SAAA,GAAYA,aAAO,KAAK,CAAA,CAAA;AAC9B,EAAM,MAAA,CAAC,QAAQ,CAAA,GAAIK,uBAAY,EAAA,CAAA;AAI/B,EAAA,IAAI,CAAC,MAAO,CAAA,EAAA,CAAG,SAAU,CAAA,OAAA,EAAS,KAAK,CAAG,EAAA;AACxC,IAAA,SAAA,CAAU,OAAU,GAAA,KAAA,CAAA;AACpB,IAAA,UAAA,CAAW,OAAU,GAAA,YAAA,CAAA;AAAA,GACvB;AAEA,EAAA,MAAM,QAAW,GAAAF,iBAAA;AAAA,IACf,CAACC,MAAa,KAAA;AACZ,MAAA,UAAA,CAAW,OAAU,GAAA,cAAA,CAAA;AACrB,MAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAK1B,MAAS,QAAA,EAAA,CAAA;AAET,MAAA,QAAA,GAAWA,MAAK,CAAA,CAAA;AAAA,KAClB;AAAA,IACA,CAAC,UAAU,QAAQ,CAAA;AAAA,GACrB,CAAA;AAEA,EAAA,MAAM,YACJ,GAAA,UAAA,CAAW,OAAY,KAAA,cAAA,GAAiB,iBAAoB,GAAA,KAAA,CAAA;AAE9D,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC;;;;;"}
@@ -1,14 +1,15 @@
1
1
  import { console } from '@liveblocks/core';
2
2
  import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { useRerender } from './use-rerender.js';
3
4
 
4
- function useControllableState(value, onChange, defaultValue) {
5
+ function useControllableState(defaultValue, value, onChange) {
5
6
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
6
7
  const isControlled = value !== void 0;
7
8
  const wasControlled = useRef(isControlled);
8
9
  useEffect(() => {
9
10
  if (process.env.NODE_ENV !== "production" && wasControlled.current !== isControlled) {
10
11
  console.warn(
11
- `A component is changing from ${wasControlled ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}.`
12
+ `A component is changing from ${wasControlled.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}.`
12
13
  );
13
14
  }
14
15
  wasControlled.current = isControlled;
@@ -27,6 +28,27 @@ function useControllableState(value, onChange, defaultValue) {
27
28
  );
28
29
  return [currentValue, setValue];
29
30
  }
31
+ function useSemiControllableState(value, onChange) {
32
+ const [uncontrolledValue, setUncontrolledValue] = useState(value);
33
+ const lastChange = useRef("controlled");
34
+ const lastValue = useRef(value);
35
+ const [rerender] = useRerender();
36
+ if (!Object.is(lastValue.current, value)) {
37
+ lastValue.current = value;
38
+ lastChange.current = "controlled";
39
+ }
40
+ const setValue = useCallback(
41
+ (value2) => {
42
+ lastChange.current = "uncontrolled";
43
+ setUncontrolledValue(value2);
44
+ rerender();
45
+ onChange?.(value2);
46
+ },
47
+ [onChange, rerender]
48
+ );
49
+ const currentValue = lastChange.current === "uncontrolled" ? uncontrolledValue : value;
50
+ return [currentValue, setValue];
51
+ }
30
52
 
31
- export { useControllableState };
53
+ export { useControllableState, useSemiControllableState };
32
54
  //# sourceMappingURL=use-controllable-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-controllable-state.js","sources":["../../src/utils/use-controllable-state.ts"],"sourcesContent":["import { console } from \"@liveblocks/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function useControllableState<T>(\n value?: T,\n onChange?: (value: T) => void,\n defaultValue?: T\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const wasControlled = useRef(isControlled);\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n wasControlled.current !== isControlled\n ) {\n console.warn(\n `A component is changing from ${\n wasControlled ? \"controlled\" : \"uncontrolled\"\n } to ${isControlled ? \"controlled\" : \"uncontrolled\"}.`\n );\n }\n\n wasControlled.current = isControlled;\n }, [isControlled]);\n\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const setValue = useCallback(\n (value: T) => {\n if (isControlled) {\n return onChange?.(value);\n } else {\n setUncontrolledValue(value);\n\n return onChange?.(value);\n }\n },\n [isControlled, onChange]\n );\n\n return [currentValue, setValue] as const;\n}\n"],"names":["value"],"mappings":";;;AAGgB,SAAA,oBAAA,CACd,KACA,EAAA,QAAA,EACA,YACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,YAAY,CAAA,CAAA;AACvE,EAAA,MAAM,eAAe,KAAU,KAAA,KAAA,CAAA,CAAA;AAC/B,EAAM,MAAA,aAAA,GAAgB,OAAO,YAAY,CAAA,CAAA;AAEzC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IACE,QAAQ,GAAI,CAAA,QAAA,KAAa,YACzB,IAAA,aAAA,CAAc,YAAY,YAC1B,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,CACE,6BAAA,EAAA,aAAA,GAAgB,YAAe,GAAA,cAAA,CAAA,IAAA,EAC1B,eAAe,YAAe,GAAA,cAAA,CAAA,CAAA,CAAA;AAAA,OACvC,CAAA;AAAA,KACF;AAEA,IAAA,aAAA,CAAc,OAAU,GAAA,YAAA,CAAA;AAAA,GAC1B,EAAG,CAAC,YAAY,CAAC,CAAA,CAAA;AAEjB,EAAM,MAAA,YAAA,GAAe,eAAe,KAAQ,GAAA,iBAAA,CAAA;AAE5C,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAACA,MAAa,KAAA;AACZ,MAAA,IAAI,YAAc,EAAA;AAChB,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OAClB,MAAA;AACL,QAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAE1B,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OACzB;AAAA,KACF;AAAA,IACA,CAAC,cAAc,QAAQ,CAAA;AAAA,GACzB,CAAA;AAEA,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC;;;;"}
1
+ {"version":3,"file":"use-controllable-state.js","sources":["../../src/utils/use-controllable-state.ts"],"sourcesContent":["import { console } from \"@liveblocks/core\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useRerender } from \"./use-rerender\";\n\n/**\n * Hold a state in a \"controlled\" or \"uncontrolled\" way.\n */\nexport function useControllableState<T>(\n /**\n * The default uncontrolled value.\n */\n defaultValue: T,\n\n /**\n * The controlled value.\n *\n * If `undefined`, the returned value is uncontrolled.\n * If set, this controlled value is used and returned as is.\n */\n value: T | undefined,\n\n /**\n * The event handler called when the value changes.\n */\n onChange: ((value: T) => void) | undefined\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const wasControlled = useRef(isControlled);\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n wasControlled.current !== isControlled\n ) {\n console.warn(\n `A component is changing from ${\n wasControlled.current ? \"controlled\" : \"uncontrolled\"\n } to ${isControlled ? \"controlled\" : \"uncontrolled\"}.`\n );\n }\n\n wasControlled.current = isControlled;\n }, [isControlled]);\n\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const setValue = useCallback(\n (value: T) => {\n if (isControlled) {\n return onChange?.(value);\n } else {\n setUncontrolledValue(value);\n\n return onChange?.(value);\n }\n },\n [isControlled, onChange]\n );\n\n return [currentValue, setValue] as const;\n}\n\n/**\n * @experimental\n *\n * Hold a value in a \"semi-controlled\" way: a controlled value that can be\n * overridden by uncontrolled changes in a \"most recent wins\" way.\n *\n * @example\n *\n * A `Collapsible` component uses `useSemiControllableState` to control\n * its \"open\" state, it accepts two optional props: `open` and `onOpenChange`.\n *\n * Internally, it passes them to `useSemiControllableState`:\n *\n * ```tsx\n * const [isOpen, setIsOpen] = useSemiControllableState(\n * open ?? true, // Defaults to `true` if `open` is not provided\n * onOpenChange\n * );\n *\n * // ... `isOpen` and `setIsOpen` are used in the component's implementation ...\n * ```\n *\n * And finally here's how it could be used in a \"semi-controlled\" way:\n *\n * ```tsx\n * const status: `\"loading\" | \"success\" | \"error\"`;\n *\n * <Collapsible open={status === \"success\"} />\n * ```\n *\n * Like with a traditional controlled value, the collapsible will start closed\n * and will automatically open when `status` becomes `\"success\"`.\n *\n * But unlike with a traditional controlled value, the collapsible can still\n * open/close when it's clicked on by the user, overriding `open={status === \"success\"}`.\n *\n * It's possible to use it as a traditional uncontrolled value:\n *\n * ```tsx\n * const defaultOpen = false;\n *\n * <Collapsible open={defaultOpen} />\n * ```\n *\n * Or to sync the uncontrolled value like with a traditional uncontrolled value:\n *\n * ```tsx\n * const [isOpen, setIsOpen] = useState(false);\n *\n * // `isOpen` is synced with the uncontrolled value when the user clicks\n * <Collapsible open={isOpen} onOpenChange={setIsOpen} />\n * ```\n *\n * But with the caveat that it will still be possible to change the\n * uncontrolled value:\n *\n * ```tsx\n * const open = false;\n *\n * // Clicking on the collapsible will still open/close it, unlike with a\n * // traditional controlled value.\n * <Collapsible open={open} />\n * ```\n */\nexport function useSemiControllableState<T>(\n /**\n * The controlled value.\n *\n * When this value changes, it becomes the current value.\n * But unlike a traditional controlled value, it can be overridden by\n * uncontrolled changes.\n */\n value: T,\n\n /**\n * The event handler called when the uncontrolled value changes.\n */\n onChange: ((value: T) => void) | undefined\n) {\n const [uncontrolledValue, setUncontrolledValue] = useState(value);\n const lastChange = useRef<\"uncontrolled\" | \"controlled\">(\"controlled\");\n const lastValue = useRef(value);\n const [rerender] = useRerender();\n\n // Listen to `value` changes during the render phase to avoid\n // having to always sync `uncontrolledValue` on every change.\n if (!Object.is(lastValue.current, value)) {\n lastValue.current = value;\n lastChange.current = \"controlled\";\n }\n\n const setValue = useCallback(\n (value: T) => {\n lastChange.current = \"uncontrolled\";\n setUncontrolledValue(value);\n\n // If the new `uncontrolledValue` is the same as last time it was the \"last change\",\n // `setUncontrolledValue` won't trigger a re-render, but the fact that it's becoming\n // uncontrolled again is a change that needs a re-render.\n rerender();\n\n onChange?.(value);\n },\n [onChange, rerender]\n );\n\n const currentValue =\n lastChange.current === \"uncontrolled\" ? uncontrolledValue : value;\n\n return [currentValue, setValue] as const;\n}\n"],"names":["value"],"mappings":";;;;AAQgB,SAAA,oBAAA,CAId,YAQA,EAAA,KAAA,EAKA,QACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,YAAY,CAAA,CAAA;AACvE,EAAA,MAAM,eAAe,KAAU,KAAA,KAAA,CAAA,CAAA;AAC/B,EAAM,MAAA,aAAA,GAAgB,OAAO,YAAY,CAAA,CAAA;AAEzC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IACE,QAAQ,GAAI,CAAA,QAAA,KAAa,YACzB,IAAA,aAAA,CAAc,YAAY,YAC1B,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,gCACE,aAAc,CAAA,OAAA,GAAU,YAAe,GAAA,cAAA,CAAA,IAAA,EAClC,eAAe,YAAe,GAAA,cAAA,CAAA,CAAA,CAAA;AAAA,OACvC,CAAA;AAAA,KACF;AAEA,IAAA,aAAA,CAAc,OAAU,GAAA,YAAA,CAAA;AAAA,GAC1B,EAAG,CAAC,YAAY,CAAC,CAAA,CAAA;AAEjB,EAAM,MAAA,YAAA,GAAe,eAAe,KAAQ,GAAA,iBAAA,CAAA;AAE5C,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAACA,MAAa,KAAA;AACZ,MAAA,IAAI,YAAc,EAAA;AAChB,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OAClB,MAAA;AACL,QAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAE1B,QAAA,OAAO,WAAWA,MAAK,CAAA,CAAA;AAAA,OACzB;AAAA,KACF;AAAA,IACA,CAAC,cAAc,QAAQ,CAAA;AAAA,GACzB,CAAA;AAEA,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC,CAAA;AAkEgB,SAAA,wBAAA,CAQd,OAKA,QACA,EAAA;AACA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AAChE,EAAM,MAAA,UAAA,GAAa,OAAsC,YAAY,CAAA,CAAA;AACrE,EAAM,MAAA,SAAA,GAAY,OAAO,KAAK,CAAA,CAAA;AAC9B,EAAM,MAAA,CAAC,QAAQ,CAAA,GAAI,WAAY,EAAA,CAAA;AAI/B,EAAA,IAAI,CAAC,MAAO,CAAA,EAAA,CAAG,SAAU,CAAA,OAAA,EAAS,KAAK,CAAG,EAAA;AACxC,IAAA,SAAA,CAAU,OAAU,GAAA,KAAA,CAAA;AACpB,IAAA,UAAA,CAAW,OAAU,GAAA,YAAA,CAAA;AAAA,GACvB;AAEA,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAACA,MAAa,KAAA;AACZ,MAAA,UAAA,CAAW,OAAU,GAAA,cAAA,CAAA;AACrB,MAAA,oBAAA,CAAqBA,MAAK,CAAA,CAAA;AAK1B,MAAS,QAAA,EAAA,CAAA;AAET,MAAA,QAAA,GAAWA,MAAK,CAAA,CAAA;AAAA,KAClB;AAAA,IACA,CAAC,UAAU,QAAQ,CAAA;AAAA,GACrB,CAAA;AAEA,EAAA,MAAM,YACJ,GAAA,UAAA,CAAW,OAAY,KAAA,cAAA,GAAiB,iBAAoB,GAAA,KAAA,CAAA;AAE9D,EAAO,OAAA,CAAC,cAAc,QAAQ,CAAA,CAAA;AAChC;;;;"}
@@ -3,68 +3,88 @@
3
3
  var react = require('react');
4
4
  var useLatest = require('./use-latest.cjs');
5
5
 
6
- let intersectionObserver;
7
- const intersectionCallbacks = /* @__PURE__ */ new WeakMap();
8
- function observe(element, callback) {
9
- if (!intersectionObserver) {
10
- intersectionObserver = new IntersectionObserver((entries) => {
11
- for (const entry of entries) {
12
- const callback2 = intersectionCallbacks.get(entry.target);
13
- callback2?.(entry);
6
+ let optionlessIntersectionObserver;
7
+ const optionlessIntersectionCallbacks = /* @__PURE__ */ new WeakMap();
8
+ const individualIntersectionObservers = /* @__PURE__ */ new WeakMap();
9
+ function observe(element, callback, options) {
10
+ if (!options) {
11
+ if (!optionlessIntersectionObserver) {
12
+ optionlessIntersectionObserver = new IntersectionObserver((entries) => {
13
+ for (const entry of entries) {
14
+ const callback2 = optionlessIntersectionCallbacks.get(entry.target);
15
+ callback2?.(entry);
16
+ }
17
+ });
18
+ }
19
+ optionlessIntersectionCallbacks.set(element, callback);
20
+ optionlessIntersectionObserver.observe(element);
21
+ } else {
22
+ const observer = new IntersectionObserver(
23
+ (entries) => {
24
+ for (const entry of entries) {
25
+ callback?.(entry);
26
+ }
27
+ },
28
+ {
29
+ root: options.root?.current,
30
+ rootMargin: typeof options.rootMargin === "number" ? `${options.rootMargin}px` : options.rootMargin
14
31
  }
15
- });
32
+ );
33
+ individualIntersectionObservers.set(element, observer);
34
+ observer.observe(element);
16
35
  }
17
- intersectionCallbacks.set(element, callback);
18
- intersectionObserver.observe(element);
19
36
  }
20
- function unobserve(element) {
21
- intersectionCallbacks.delete(element);
22
- intersectionObserver?.unobserve(element);
23
- }
24
- function useVisible(ref, options) {
25
- const [isVisible, setVisible] = react.useState(false);
26
- const enabled = options?.enabled ?? true;
27
- react.useEffect(() => {
28
- const element = ref.current;
29
- if (!element) {
30
- return;
31
- }
32
- if (enabled) {
33
- observe(element, (entry) => {
34
- setVisible(entry.isIntersecting);
35
- });
36
- } else {
37
- unobserve(element);
38
- }
39
- return () => {
40
- unobserve(element);
41
- };
42
- }, [enabled]);
43
- return isVisible;
37
+ function unobserve(element, options) {
38
+ if (!options) {
39
+ optionlessIntersectionCallbacks.delete(element);
40
+ optionlessIntersectionObserver?.unobserve(element);
41
+ } else {
42
+ const observer = individualIntersectionObservers.get(element);
43
+ observer?.unobserve(element);
44
+ individualIntersectionObservers.delete(element);
45
+ }
44
46
  }
45
- function useVisibleCallback(ref, callback, options) {
47
+ function useIntersectionCallback(ref, callback, options) {
46
48
  const enabled = options?.enabled ?? true;
47
49
  const latestCallback = useLatest.useLatest(callback);
50
+ const { root, rootMargin } = options ?? {};
48
51
  react.useEffect(() => {
49
52
  const element = ref.current;
50
53
  if (!element) {
51
54
  return;
52
55
  }
56
+ const observeOptions = {
57
+ root,
58
+ rootMargin
59
+ };
53
60
  if (enabled) {
54
- observe(element, (entry) => {
55
- if (entry.isIntersecting) {
56
- latestCallback.current();
57
- }
58
- });
61
+ observe(
62
+ element,
63
+ (entry) => {
64
+ latestCallback.current(entry.isIntersecting, entry);
65
+ },
66
+ observeOptions
67
+ );
59
68
  } else {
60
- unobserve(element);
69
+ unobserve(element, observeOptions);
61
70
  }
62
71
  return () => {
63
- unobserve(element);
72
+ unobserve(element, observeOptions);
64
73
  };
65
- }, [enabled]);
74
+ }, [ref, enabled, latestCallback, root, rootMargin]);
75
+ }
76
+ function useVisible(ref, options) {
77
+ const [isVisible, setVisible] = react.useState(
78
+ options?.initialValue !== void 0 ? options.initialValue : false
79
+ );
80
+ useIntersectionCallback(
81
+ ref,
82
+ (isIntersecting) => setVisible(isIntersecting),
83
+ options
84
+ );
85
+ return isVisible;
66
86
  }
67
87
 
88
+ exports.useIntersectionCallback = useIntersectionCallback;
68
89
  exports.useVisible = useVisible;
69
- exports.useVisibleCallback = useVisibleCallback;
70
90
  //# sourceMappingURL=use-visible.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-visible.cjs","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ntype Options = { enabled?: boolean };\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet intersectionObserver: IntersectionObserver | undefined;\nconst intersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback\n) {\n if (!intersectionObserver) {\n intersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = intersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n intersectionCallbacks.set(element, callback);\n intersectionObserver.observe(element);\n}\n\nfunction unobserve(element: Element) {\n intersectionCallbacks.delete(element);\n intersectionObserver?.unobserve(element);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible(ref: RefObject<Element>, options?: Options) {\n const [isVisible, setVisible] = useState(false);\n const enabled = options?.enabled ?? true;\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n if (enabled) {\n observe(element, (entry) => {\n setVisible(entry.isIntersecting);\n });\n } else {\n unobserve(element);\n }\n\n return () => {\n unobserve(element);\n };\n }, [enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n\n return isVisible;\n}\n\nexport function useVisibleCallback<T extends (...args: any[]) => void>(\n ref: RefObject<Element>,\n callback: T,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n if (enabled) {\n observe(element, (entry) => {\n if (entry.isIntersecting) {\n latestCallback.current();\n }\n });\n } else {\n unobserve(element);\n }\n\n return () => {\n unobserve(element);\n };\n }, [enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n"],"names":["callback","useState","useEffect","useLatest"],"mappings":";;;;;AAUA,IAAI,oBAAA,CAAA;AACJ,MAAM,qBAAA,uBAA4B,OAGhC,EAAA,CAAA;AAEF,SAAS,OAAA,CACP,SACA,QACA,EAAA;AACA,EAAA,IAAI,CAAC,oBAAsB,EAAA;AACzB,IAAuB,oBAAA,GAAA,IAAI,oBAAqB,CAAA,CAAC,OAAY,KAAA;AAC3D,MAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,QAAA,MAAMA,SAAW,GAAA,qBAAA,CAAsB,GAAI,CAAA,KAAA,CAAM,MAAM,CAAA,CAAA;AAEvD,QAAAA,YAAW,KAAK,CAAA,CAAA;AAAA,OAClB;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAEA,EAAsB,qBAAA,CAAA,GAAA,CAAI,SAAS,QAAQ,CAAA,CAAA;AAC3C,EAAA,oBAAA,CAAqB,QAAQ,OAAO,CAAA,CAAA;AACtC,CAAA;AAEA,SAAS,UAAU,OAAkB,EAAA;AACnC,EAAA,qBAAA,CAAsB,OAAO,OAAO,CAAA,CAAA;AACpC,EAAA,oBAAA,EAAsB,UAAU,OAAO,CAAA,CAAA;AACzC,CAAA;AAKgB,SAAA,UAAA,CAAW,KAAyB,OAAmB,EAAA;AACrE,EAAA,MAAM,CAAC,SAAA,EAAW,UAAU,CAAA,GAAIC,eAAS,KAAK,CAAA,CAAA;AAC9C,EAAM,MAAA,OAAA,GAAU,SAAS,OAAW,IAAA,IAAA,CAAA;AAEpC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,GAAI,CAAA,OAAA,CAAA;AAEpB,IAAA,IAAI,CAAC,OAAS,EAAA;AACZ,MAAA,OAAA;AAAA,KACF;AAEA,IAAA,IAAI,OAAS,EAAA;AACX,MAAQ,OAAA,CAAA,OAAA,EAAS,CAAC,KAAU,KAAA;AAC1B,QAAA,UAAA,CAAW,MAAM,cAAc,CAAA,CAAA;AAAA,OAChC,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAA,SAAA,CAAU,OAAO,CAAA,CAAA;AAAA,KACnB;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,CAAU,OAAO,CAAA,CAAA;AAAA,KACnB,CAAA;AAAA,GACF,EAAG,CAAC,OAAO,CAAC,CAAA,CAAA;AAEZ,EAAO,OAAA,SAAA,CAAA;AACT,CAAA;AAEgB,SAAA,kBAAA,CACd,GACA,EAAA,QAAA,EACA,OACA,EAAA;AACA,EAAM,MAAA,OAAA,GAAU,SAAS,OAAW,IAAA,IAAA,CAAA;AACpC,EAAM,MAAA,cAAA,GAAiBC,oBAAU,QAAQ,CAAA,CAAA;AAEzC,EAAAD,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,GAAI,CAAA,OAAA,CAAA;AAEpB,IAAA,IAAI,CAAC,OAAS,EAAA;AACZ,MAAA,OAAA;AAAA,KACF;AAEA,IAAA,IAAI,OAAS,EAAA;AACX,MAAQ,OAAA,CAAA,OAAA,EAAS,CAAC,KAAU,KAAA;AAC1B,QAAA,IAAI,MAAM,cAAgB,EAAA;AACxB,UAAA,cAAA,CAAe,OAAQ,EAAA,CAAA;AAAA,SACzB;AAAA,OACD,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAA,SAAA,CAAU,OAAO,CAAA,CAAA;AAAA,KACnB;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,CAAU,OAAO,CAAA,CAAA;AAAA,KACnB,CAAA;AAAA,GACF,EAAG,CAAC,OAAO,CAAC,CAAA,CAAA;AACd;;;;;"}
1
+ {"version":3,"file":"use-visible.cjs","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ninterface ObserveOptions {\n rootMargin?: string | number;\n root?: RefObject<Element>;\n}\n\ninterface Options extends ObserveOptions {\n enabled?: boolean;\n}\n\ninterface VisibleOptions<T = boolean> extends Options {\n initialValue?: T;\n}\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet optionlessIntersectionObserver: IntersectionObserver | undefined;\nconst optionlessIntersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nconst individualIntersectionObservers = new WeakMap<\n Element,\n IntersectionObserver\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback,\n options?: ObserveOptions\n) {\n // Observers without options share a common IntersectionObserver instance, ones with options have their own\n if (!options) {\n if (!optionlessIntersectionObserver) {\n optionlessIntersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = optionlessIntersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n optionlessIntersectionCallbacks.set(element, callback);\n optionlessIntersectionObserver.observe(element);\n } else {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callback?.(entry);\n }\n },\n {\n root: options.root?.current,\n rootMargin:\n typeof options.rootMargin === \"number\"\n ? `${options.rootMargin}px`\n : options.rootMargin,\n }\n );\n\n individualIntersectionObservers.set(element, observer);\n observer.observe(element);\n }\n}\n\nfunction unobserve(element: Element, options?: ObserveOptions) {\n if (!options) {\n optionlessIntersectionCallbacks.delete(element);\n optionlessIntersectionObserver?.unobserve(element);\n } else {\n const observer = individualIntersectionObservers.get(element);\n\n observer?.unobserve(element);\n individualIntersectionObservers.delete(element);\n }\n}\n\n/**\n * Observe when an element enters or exits the viewport.\n *\n * If you only need to get a stateful visibility value, use the higher level hook `useVisible` instead.\n */\nexport function useIntersectionCallback(\n ref: RefObject<Element>,\n callback: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n const { root, rootMargin } = options ?? {};\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const observeOptions: ObserveOptions = {\n root,\n rootMargin,\n };\n\n if (enabled) {\n observe(\n element,\n (entry) => {\n // The intersection observer entry might be useful in some cases but the main information\n // is whether the element is intersecting or not so we pass that as the first argument.\n latestCallback.current(entry.isIntersecting, entry);\n },\n observeOptions\n );\n } else {\n unobserve(element, observeOptions);\n }\n\n return () => {\n unobserve(element, observeOptions);\n };\n }, [ref, enabled, latestCallback, root, rootMargin]);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible<T extends boolean | null = boolean>(\n ref: RefObject<Element>,\n options?: VisibleOptions<T>\n) {\n const [isVisible, setVisible] = useState(\n options?.initialValue !== undefined ? options.initialValue : false\n );\n\n useIntersectionCallback(\n ref,\n (isIntersecting) => setVisible(isIntersecting),\n options\n );\n\n return isVisible;\n}\n"],"names":["callback","useLatest","useEffect","useState"],"mappings":";;;;;AAqBA,IAAI,8BAAA,CAAA;AACJ,MAAM,+BAAA,uBAAsC,OAG1C,EAAA,CAAA;AAEF,MAAM,+BAAA,uBAAsC,OAG1C,EAAA,CAAA;AAEF,SAAS,OAAA,CACP,OACA,EAAA,QAAA,EACA,OACA,EAAA;AAEA,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAA,IAAI,CAAC,8BAAgC,EAAA;AACnC,MAAiC,8BAAA,GAAA,IAAI,oBAAqB,CAAA,CAAC,OAAY,KAAA;AACrE,QAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,UAAA,MAAMA,SAAW,GAAA,+BAAA,CAAgC,GAAI,CAAA,KAAA,CAAM,MAAM,CAAA,CAAA;AAEjE,UAAAA,YAAW,KAAK,CAAA,CAAA;AAAA,SAClB;AAAA,OACD,CAAA,CAAA;AAAA,KACH;AAEA,IAAgC,+BAAA,CAAA,GAAA,CAAI,SAAS,QAAQ,CAAA,CAAA;AACrD,IAAA,8BAAA,CAA+B,QAAQ,OAAO,CAAA,CAAA;AAAA,GACzC,MAAA;AACL,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACnB,CAAC,OAAY,KAAA;AACX,QAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,UAAA,QAAA,GAAW,KAAK,CAAA,CAAA;AAAA,SAClB;AAAA,OACF;AAAA,MACA;AAAA,QACE,IAAA,EAAM,QAAQ,IAAM,EAAA,OAAA;AAAA,QACpB,UAAA,EACE,OAAO,OAAQ,CAAA,UAAA,KAAe,WAC1B,CAAG,EAAA,OAAA,CAAQ,iBACX,OAAQ,CAAA,UAAA;AAAA,OAChB;AAAA,KACF,CAAA;AAEA,IAAgC,+BAAA,CAAA,GAAA,CAAI,SAAS,QAAQ,CAAA,CAAA;AACrD,IAAA,QAAA,CAAS,QAAQ,OAAO,CAAA,CAAA;AAAA,GAC1B;AACF,CAAA;AAEA,SAAS,SAAA,CAAU,SAAkB,OAA0B,EAAA;AAC7D,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAA,+BAAA,CAAgC,OAAO,OAAO,CAAA,CAAA;AAC9C,IAAA,8BAAA,EAAgC,UAAU,OAAO,CAAA,CAAA;AAAA,GAC5C,MAAA;AACL,IAAM,MAAA,QAAA,GAAW,+BAAgC,CAAA,GAAA,CAAI,OAAO,CAAA,CAAA;AAE5D,IAAA,QAAA,EAAU,UAAU,OAAO,CAAA,CAAA;AAC3B,IAAA,+BAAA,CAAgC,OAAO,OAAO,CAAA,CAAA;AAAA,GAChD;AACF,CAAA;AAOgB,SAAA,uBAAA,CACd,GACA,EAAA,QAAA,EACA,OACA,EAAA;AACA,EAAM,MAAA,OAAA,GAAU,SAAS,OAAW,IAAA,IAAA,CAAA;AACpC,EAAM,MAAA,cAAA,GAAiBC,oBAAU,QAAQ,CAAA,CAAA;AACzC,EAAA,MAAM,EAAE,IAAA,EAAM,UAAW,EAAA,GAAI,WAAW,EAAC,CAAA;AAEzC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,GAAI,CAAA,OAAA,CAAA;AAEpB,IAAA,IAAI,CAAC,OAAS,EAAA;AACZ,MAAA,OAAA;AAAA,KACF;AAEA,IAAA,MAAM,cAAiC,GAAA;AAAA,MACrC,IAAA;AAAA,MACA,UAAA;AAAA,KACF,CAAA;AAEA,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,OAAA;AAAA,QACE,OAAA;AAAA,QACA,CAAC,KAAU,KAAA;AAGT,UAAe,cAAA,CAAA,OAAA,CAAQ,KAAM,CAAA,cAAA,EAAgB,KAAK,CAAA,CAAA;AAAA,SACpD;AAAA,QACA,cAAA;AAAA,OACF,CAAA;AAAA,KACK,MAAA;AACL,MAAA,SAAA,CAAU,SAAS,cAAc,CAAA,CAAA;AAAA,KACnC;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,CAAU,SAAS,cAAc,CAAA,CAAA;AAAA,KACnC,CAAA;AAAA,KACC,CAAC,GAAA,EAAK,SAAS,cAAgB,EAAA,IAAA,EAAM,UAAU,CAAC,CAAA,CAAA;AACrD,CAAA;AAKgB,SAAA,UAAA,CACd,KACA,OACA,EAAA;AACA,EAAM,MAAA,CAAC,SAAW,EAAA,UAAU,CAAI,GAAAC,cAAA;AAAA,IAC9B,OAAS,EAAA,YAAA,KAAiB,KAAY,CAAA,GAAA,OAAA,CAAQ,YAAe,GAAA,KAAA;AAAA,GAC/D,CAAA;AAEA,EAAA,uBAAA;AAAA,IACE,GAAA;AAAA,IACA,CAAC,cAAmB,KAAA,UAAA,CAAW,cAAc,CAAA;AAAA,IAC7C,OAAA;AAAA,GACF,CAAA;AAEA,EAAO,OAAA,SAAA,CAAA;AACT;;;;;"}