@liveblocks/react-ui 2.5.2 → 2.7.0-beta2

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 (104) hide show
  1. package/dist/components/Comment.js +61 -3
  2. package/dist/components/Comment.js.map +1 -1
  3. package/dist/components/Comment.mjs +62 -5
  4. package/dist/components/Comment.mjs.map +1 -1
  5. package/dist/components/Composer.js +217 -101
  6. package/dist/components/Composer.js.map +1 -1
  7. package/dist/components/Composer.mjs +220 -104
  8. package/dist/components/Composer.mjs.map +1 -1
  9. package/dist/components/InboxNotification.js +20 -4
  10. package/dist/components/InboxNotification.js.map +1 -1
  11. package/dist/components/InboxNotification.mjs +20 -4
  12. package/dist/components/InboxNotification.mjs.map +1 -1
  13. package/dist/components/Thread.js +2 -0
  14. package/dist/components/Thread.js.map +1 -1
  15. package/dist/components/Thread.mjs +2 -0
  16. package/dist/components/Thread.mjs.map +1 -1
  17. package/dist/components/internal/Attachment.js +207 -0
  18. package/dist/components/internal/Attachment.js.map +1 -0
  19. package/dist/components/internal/Attachment.mjs +205 -0
  20. package/dist/components/internal/Attachment.mjs.map +1 -0
  21. package/dist/components/internal/InboxNotificationThread.js +10 -2
  22. package/dist/components/internal/InboxNotificationThread.js.map +1 -1
  23. package/dist/components/internal/InboxNotificationThread.mjs +11 -3
  24. package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
  25. package/dist/icons/Attachment.js +15 -0
  26. package/dist/icons/Attachment.js.map +1 -0
  27. package/dist/icons/Attachment.mjs +13 -0
  28. package/dist/icons/Attachment.mjs.map +1 -0
  29. package/dist/icons/Spinner.js +3 -9
  30. package/dist/icons/Spinner.js.map +1 -1
  31. package/dist/icons/Spinner.mjs +4 -10
  32. package/dist/icons/Spinner.mjs.map +1 -1
  33. package/dist/icons/{Missing.js → Warning.js} +3 -3
  34. package/dist/icons/{Missing.js.map → Warning.js.map} +1 -1
  35. package/dist/icons/{Missing.mjs → Warning.mjs} +3 -3
  36. package/dist/icons/{Missing.mjs.map → Warning.mjs.map} +1 -1
  37. package/dist/index.d.mts +70 -4
  38. package/dist/index.d.ts +70 -4
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/overrides.js +5 -0
  42. package/dist/overrides.js.map +1 -1
  43. package/dist/overrides.mjs +5 -0
  44. package/dist/overrides.mjs.map +1 -1
  45. package/dist/primitives/Composer/contexts.js +17 -3
  46. package/dist/primitives/Composer/contexts.js.map +1 -1
  47. package/dist/primitives/Composer/contexts.mjs +15 -4
  48. package/dist/primitives/Composer/contexts.mjs.map +1 -1
  49. package/dist/primitives/Composer/index.js +185 -26
  50. package/dist/primitives/Composer/index.js.map +1 -1
  51. package/dist/primitives/Composer/index.mjs +188 -31
  52. package/dist/primitives/Composer/index.mjs.map +1 -1
  53. package/dist/primitives/Composer/utils.js +224 -0
  54. package/dist/primitives/Composer/utils.js.map +1 -1
  55. package/dist/primitives/Composer/utils.mjs +222 -1
  56. package/dist/primitives/Composer/utils.mjs.map +1 -1
  57. package/dist/primitives/EmojiPicker/utils.js +2 -2
  58. package/dist/primitives/EmojiPicker/utils.js.map +1 -1
  59. package/dist/primitives/EmojiPicker/utils.mjs +1 -1
  60. package/dist/primitives/EmojiPicker/utils.mjs.map +1 -1
  61. package/dist/primitives/FileSize.js +33 -0
  62. package/dist/primitives/FileSize.js.map +1 -0
  63. package/dist/primitives/FileSize.mjs +31 -0
  64. package/dist/primitives/FileSize.mjs.map +1 -0
  65. package/dist/primitives/index.d.mts +83 -3
  66. package/dist/primitives/index.d.ts +83 -3
  67. package/dist/primitives/index.js +4 -0
  68. package/dist/primitives/index.js.map +1 -1
  69. package/dist/primitives/index.mjs +2 -0
  70. package/dist/primitives/index.mjs.map +1 -1
  71. package/dist/utils/download.js +14 -0
  72. package/dist/utils/download.js.map +1 -0
  73. package/dist/utils/download.mjs +12 -0
  74. package/dist/utils/download.mjs.map +1 -0
  75. package/dist/utils/format-file-size.js +45 -0
  76. package/dist/utils/format-file-size.js.map +1 -0
  77. package/dist/utils/format-file-size.mjs +43 -0
  78. package/dist/utils/format-file-size.mjs.map +1 -0
  79. package/dist/utils/intl.js +6 -0
  80. package/dist/utils/intl.js.map +1 -1
  81. package/dist/utils/intl.mjs +6 -1
  82. package/dist/utils/intl.mjs.map +1 -1
  83. package/dist/utils/use-initial.js +2 -1
  84. package/dist/utils/use-initial.js.map +1 -1
  85. package/dist/utils/use-initial.mjs +3 -2
  86. package/dist/utils/use-initial.mjs.map +1 -1
  87. package/dist/version.js +1 -1
  88. package/dist/version.js.map +1 -1
  89. package/dist/version.mjs +1 -1
  90. package/dist/version.mjs.map +1 -1
  91. package/package.json +4 -4
  92. package/src/styles/dark/index.css +1 -0
  93. package/src/styles/index.css +296 -62
  94. package/src/styles/utils.css +44 -0
  95. package/styles/dark/attributes.css +1 -1
  96. package/styles/dark/attributes.css.map +1 -1
  97. package/styles/dark/media-query.css +1 -1
  98. package/styles/dark/media-query.css.map +1 -1
  99. package/styles.css +1 -1
  100. package/styles.css.map +1 -1
  101. package/dist/utils/chunk.js +0 -12
  102. package/dist/utils/chunk.js.map +0 -1
  103. package/dist/utils/chunk.mjs +0 -10
  104. package/dist/utils/chunk.mjs.map +0 -1
@@ -5,6 +5,7 @@ var React = require('react');
5
5
 
6
6
  const ComposerContext = React.createContext(null);
7
7
  const ComposerEditorContext = React.createContext(null);
8
+ const ComposerAttachmentsContext = React.createContext(null);
8
9
  const ComposerSuggestionsContext = React.createContext(null);
9
10
  function useComposerEditorContext() {
10
11
  const composerEditorContext = React.useContext(ComposerEditorContext);
@@ -13,9 +14,15 @@ function useComposerEditorContext() {
13
14
  "Composer.Form is missing from the React tree."
14
15
  );
15
16
  }
16
- function useComposer() {
17
- const composerContext = React.useContext(ComposerContext);
18
- return core.nn(composerContext, "Composer.Form is missing from the React tree.");
17
+ function useComposerAttachmentsContextOrNull() {
18
+ return React.useContext(ComposerAttachmentsContext);
19
+ }
20
+ function useComposerAttachmentsContext() {
21
+ const composerAttachmentsContext = useComposerAttachmentsContextOrNull();
22
+ return core.nn(
23
+ composerAttachmentsContext,
24
+ "Composer.Form is missing from the React tree."
25
+ );
19
26
  }
20
27
  function useComposerSuggestionsContext(source = "useComposerSuggestionsContext") {
21
28
  const composerSuggestionsContext = React.useContext(ComposerSuggestionsContext);
@@ -24,11 +31,18 @@ function useComposerSuggestionsContext(source = "useComposerSuggestionsContext")
24
31
  `${source} can\u2019t be used outside of Composer.Editor.`
25
32
  );
26
33
  }
34
+ function useComposer() {
35
+ const composerContext = React.useContext(ComposerContext);
36
+ return core.nn(composerContext, "Composer.Form is missing from the React tree.");
37
+ }
27
38
 
39
+ exports.ComposerAttachmentsContext = ComposerAttachmentsContext;
28
40
  exports.ComposerContext = ComposerContext;
29
41
  exports.ComposerEditorContext = ComposerEditorContext;
30
42
  exports.ComposerSuggestionsContext = ComposerSuggestionsContext;
31
43
  exports.useComposer = useComposer;
44
+ exports.useComposerAttachmentsContext = useComposerAttachmentsContext;
45
+ exports.useComposerAttachmentsContextOrNull = useComposerAttachmentsContextOrNull;
32
46
  exports.useComposerEditorContext = useComposerEditorContext;
33
47
  exports.useComposerSuggestionsContext = useComposerSuggestionsContext;
34
48
  //# sourceMappingURL=contexts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contexts.js","sources":["../../../src/primitives/Composer/contexts.ts"],"sourcesContent":["import type { Placement } from \"@floating-ui/react-dom\";\nimport { nn } from \"@liveblocks/core\";\nimport type { Direction } from \"@radix-ui/react-dropdown-menu\";\nimport type { Dispatch, Ref, SetStateAction } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { Editor as SlateEditor, Element as SlateElement } from \"slate\";\n\nexport type ComposerContext = {\n /**\n * Whether the editor is currently focused.\n */\n isFocused: boolean;\n\n /**\n * Whether the editor is currently empty.\n */\n isEmpty: boolean;\n\n /**\n * Submit the composer programmatically.\n */\n submit: () => void;\n\n /**\n * Clear the editor programmatically.\n */\n clear: () => void;\n\n /**\n * Select the editor programmatically.\n */\n select: () => void;\n\n /**\n * Focus the editor programmatically.\n */\n focus: () => void;\n\n /**\n * Blur the editor programmatically.\n */\n blur: () => void;\n\n /**\n * Start creating a mention at the current selection.\n */\n createMention: () => void;\n\n /**\n * Insert text at the current selection.\n */\n insertText: (text: string) => void;\n};\n\nexport type ComposerEditorContext = {\n validate: (value: SlateElement[]) => void;\n editor: SlateEditor;\n setFocused: Dispatch<SetStateAction<boolean>>;\n};\n\nexport type ComposerSuggestionsContext = {\n dir?: Direction;\n id: string;\n itemId: (value?: string) => string | undefined;\n placement: Placement;\n selectedValue?: string;\n setSelectedValue: (value: string) => void;\n onItemSelect: (value: string) => void;\n ref: Ref<HTMLDivElement>;\n};\n\nexport const ComposerContext = createContext<ComposerContext | null>(null);\nexport const ComposerEditorContext =\n createContext<ComposerEditorContext | null>(null);\nexport const ComposerSuggestionsContext =\n createContext<ComposerSuggestionsContext | null>(null);\n\nexport function useComposerEditorContext() {\n const composerEditorContext = useContext(ComposerEditorContext);\n\n return nn(\n composerEditorContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposer(): ComposerContext {\n const composerContext = useContext(ComposerContext);\n\n return nn(composerContext, \"Composer.Form is missing from the React tree.\");\n}\n\nexport function useComposerSuggestionsContext(\n source = \"useComposerSuggestionsContext\"\n) {\n const composerSuggestionsContext = useContext(ComposerSuggestionsContext);\n\n return nn(\n composerSuggestionsContext,\n `${source} can’t be used outside of Composer.Editor.`\n );\n}\n"],"names":["createContext","useContext","nn"],"mappings":";;;;;AAuEa,MAAA,eAAA,GAAkBA,oBAAsC,IAAI,EAAA;AAC5D,MAAA,qBAAA,GACXA,oBAA4C,IAAI,EAAA;AACrC,MAAA,0BAAA,GACXA,oBAAiD,IAAI,EAAA;AAEhD,SAAS,wBAA2B,GAAA;AACzC,EAAM,MAAA,qBAAA,GAAwBC,iBAAW,qBAAqB,CAAA,CAAA;AAE9D,EAAO,OAAAC,OAAA;AAAA,IACL,qBAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,WAA+B,GAAA;AAC7C,EAAM,MAAA,eAAA,GAAkBD,iBAAW,eAAe,CAAA,CAAA;AAElD,EAAO,OAAAC,OAAA,CAAG,iBAAiB,+CAA+C,CAAA,CAAA;AAC5E,CAAA;AAEgB,SAAA,6BAAA,CACd,SAAS,+BACT,EAAA;AACA,EAAM,MAAA,0BAAA,GAA6BD,iBAAW,0BAA0B,CAAA,CAAA;AAExE,EAAO,OAAAC,OAAA;AAAA,IACL,0BAAA;AAAA,IACA,CAAG,EAAA,MAAA,CAAA,+CAAA,CAAA;AAAA,GACL,CAAA;AACF;;;;;;;;;"}
1
+ {"version":3,"file":"contexts.js","sources":["../../../src/primitives/Composer/contexts.ts"],"sourcesContent":["import type { Placement } from \"@floating-ui/react-dom\";\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { nn } from \"@liveblocks/core\";\nimport type { Direction } from \"@radix-ui/react-dropdown-menu\";\nimport type { Dispatch, Ref, SetStateAction } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { Editor as SlateEditor, Element as SlateElement } from \"slate\";\n\nexport type ComposerContext = {\n /**\n * Whether the composer is currently disabled.\n */\n isDisabled: boolean;\n\n /**\n * Whether the composer can currently be submitted.\n */\n canSubmit: boolean;\n\n /**\n * Whether the editor is currently focused.\n */\n isFocused: boolean;\n\n /**\n * Whether the editor is currently empty.\n */\n isEmpty: boolean;\n\n /**\n * Submit the composer programmatically.\n */\n submit: () => void;\n\n /**\n * Clear the composer programmatically.\n */\n clear: () => void;\n\n /**\n * Select the editor programmatically.\n */\n select: () => void;\n\n /**\n * Focus the editor programmatically.\n */\n focus: () => void;\n\n /**\n * Blur the editor programmatically.\n */\n blur: () => void;\n\n /**\n * Start creating a mention at the current selection.\n */\n createMention: () => void;\n\n /**\n * Insert text at the current selection.\n */\n insertText: (text: string) => void;\n\n /**\n * Open a file picker programmatically to create attachments.\n */\n attachFiles: () => void;\n\n /**\n * The composer's current attachments.\n */\n attachments: CommentMixedAttachment[];\n\n /**\n * Remove an attachment by its ID.\n */\n removeAttachment: (attachmentId: string) => void;\n};\n\nexport type ComposerEditorContext = {\n validate: (value: SlateElement[]) => void;\n editor: SlateEditor;\n setFocused: Dispatch<SetStateAction<boolean>>;\n};\n\nexport type ComposerAttachmentsContext = {\n canAddAttachments: boolean;\n createAttachments: (files: File[]) => void;\n isUploadingAttachments: boolean;\n maxAttachments: number;\n maxAttachmentSize: number;\n};\n\nexport type ComposerSuggestionsContext = {\n dir?: Direction;\n id: string;\n itemId: (value?: string) => string | undefined;\n placement: Placement;\n selectedValue?: string;\n setSelectedValue: (value: string) => void;\n onItemSelect: (value: string) => void;\n ref: Ref<HTMLDivElement>;\n};\n\nexport const ComposerContext = createContext<ComposerContext | null>(null);\nexport const ComposerEditorContext =\n createContext<ComposerEditorContext | null>(null);\nexport const ComposerAttachmentsContext =\n createContext<ComposerAttachmentsContext | null>(null);\nexport const ComposerSuggestionsContext =\n createContext<ComposerSuggestionsContext | null>(null);\n\nexport function useComposerEditorContext() {\n const composerEditorContext = useContext(ComposerEditorContext);\n\n return nn(\n composerEditorContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposerAttachmentsContextOrNull() {\n return useContext(ComposerAttachmentsContext);\n}\n\nexport function useComposerAttachmentsContext() {\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n\n return nn(\n composerAttachmentsContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposerSuggestionsContext(\n source = \"useComposerSuggestionsContext\"\n) {\n const composerSuggestionsContext = useContext(ComposerSuggestionsContext);\n\n return nn(\n composerSuggestionsContext,\n `${source} can’t be used outside of Composer.Editor.`\n );\n}\n\nexport function useComposer(): ComposerContext {\n const composerContext = useContext(ComposerContext);\n\n return nn(composerContext, \"Composer.Form is missing from the React tree.\");\n}\n"],"names":["createContext","useContext","nn"],"mappings":";;;;;AAyGa,MAAA,eAAA,GAAkBA,oBAAsC,IAAI,EAAA;AAC5D,MAAA,qBAAA,GACXA,oBAA4C,IAAI,EAAA;AACrC,MAAA,0BAAA,GACXA,oBAAiD,IAAI,EAAA;AAC1C,MAAA,0BAAA,GACXA,oBAAiD,IAAI,EAAA;AAEhD,SAAS,wBAA2B,GAAA;AACzC,EAAM,MAAA,qBAAA,GAAwBC,iBAAW,qBAAqB,CAAA,CAAA;AAE9D,EAAO,OAAAC,OAAA;AAAA,IACL,qBAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,mCAAsC,GAAA;AACpD,EAAA,OAAOD,iBAAW,0BAA0B,CAAA,CAAA;AAC9C,CAAA;AAEO,SAAS,6BAAgC,GAAA;AAC9C,EAAA,MAAM,6BAA6B,mCAAoC,EAAA,CAAA;AAEvE,EAAO,OAAAC,OAAA;AAAA,IACL,0BAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEgB,SAAA,6BAAA,CACd,SAAS,+BACT,EAAA;AACA,EAAM,MAAA,0BAAA,GAA6BD,iBAAW,0BAA0B,CAAA,CAAA;AAExE,EAAO,OAAAC,OAAA;AAAA,IACL,0BAAA;AAAA,IACA,CAAG,EAAA,MAAA,CAAA,+CAAA,CAAA;AAAA,GACL,CAAA;AACF,CAAA;AAEO,SAAS,WAA+B,GAAA;AAC7C,EAAM,MAAA,eAAA,GAAkBD,iBAAW,eAAe,CAAA,CAAA;AAElD,EAAO,OAAAC,OAAA,CAAG,iBAAiB,+CAA+C,CAAA,CAAA;AAC5E;;;;;;;;;;;;"}
@@ -3,6 +3,7 @@ import { createContext, useContext } from 'react';
3
3
 
4
4
  const ComposerContext = createContext(null);
5
5
  const ComposerEditorContext = createContext(null);
6
+ const ComposerAttachmentsContext = createContext(null);
6
7
  const ComposerSuggestionsContext = createContext(null);
7
8
  function useComposerEditorContext() {
8
9
  const composerEditorContext = useContext(ComposerEditorContext);
@@ -11,9 +12,15 @@ function useComposerEditorContext() {
11
12
  "Composer.Form is missing from the React tree."
12
13
  );
13
14
  }
14
- function useComposer() {
15
- const composerContext = useContext(ComposerContext);
16
- return nn(composerContext, "Composer.Form is missing from the React tree.");
15
+ function useComposerAttachmentsContextOrNull() {
16
+ return useContext(ComposerAttachmentsContext);
17
+ }
18
+ function useComposerAttachmentsContext() {
19
+ const composerAttachmentsContext = useComposerAttachmentsContextOrNull();
20
+ return nn(
21
+ composerAttachmentsContext,
22
+ "Composer.Form is missing from the React tree."
23
+ );
17
24
  }
18
25
  function useComposerSuggestionsContext(source = "useComposerSuggestionsContext") {
19
26
  const composerSuggestionsContext = useContext(ComposerSuggestionsContext);
@@ -22,6 +29,10 @@ function useComposerSuggestionsContext(source = "useComposerSuggestionsContext")
22
29
  `${source} can\u2019t be used outside of Composer.Editor.`
23
30
  );
24
31
  }
32
+ function useComposer() {
33
+ const composerContext = useContext(ComposerContext);
34
+ return nn(composerContext, "Composer.Form is missing from the React tree.");
35
+ }
25
36
 
26
- export { ComposerContext, ComposerEditorContext, ComposerSuggestionsContext, useComposer, useComposerEditorContext, useComposerSuggestionsContext };
37
+ export { ComposerAttachmentsContext, ComposerContext, ComposerEditorContext, ComposerSuggestionsContext, useComposer, useComposerAttachmentsContext, useComposerAttachmentsContextOrNull, useComposerEditorContext, useComposerSuggestionsContext };
27
38
  //# sourceMappingURL=contexts.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"contexts.mjs","sources":["../../../src/primitives/Composer/contexts.ts"],"sourcesContent":["import type { Placement } from \"@floating-ui/react-dom\";\nimport { nn } from \"@liveblocks/core\";\nimport type { Direction } from \"@radix-ui/react-dropdown-menu\";\nimport type { Dispatch, Ref, SetStateAction } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { Editor as SlateEditor, Element as SlateElement } from \"slate\";\n\nexport type ComposerContext = {\n /**\n * Whether the editor is currently focused.\n */\n isFocused: boolean;\n\n /**\n * Whether the editor is currently empty.\n */\n isEmpty: boolean;\n\n /**\n * Submit the composer programmatically.\n */\n submit: () => void;\n\n /**\n * Clear the editor programmatically.\n */\n clear: () => void;\n\n /**\n * Select the editor programmatically.\n */\n select: () => void;\n\n /**\n * Focus the editor programmatically.\n */\n focus: () => void;\n\n /**\n * Blur the editor programmatically.\n */\n blur: () => void;\n\n /**\n * Start creating a mention at the current selection.\n */\n createMention: () => void;\n\n /**\n * Insert text at the current selection.\n */\n insertText: (text: string) => void;\n};\n\nexport type ComposerEditorContext = {\n validate: (value: SlateElement[]) => void;\n editor: SlateEditor;\n setFocused: Dispatch<SetStateAction<boolean>>;\n};\n\nexport type ComposerSuggestionsContext = {\n dir?: Direction;\n id: string;\n itemId: (value?: string) => string | undefined;\n placement: Placement;\n selectedValue?: string;\n setSelectedValue: (value: string) => void;\n onItemSelect: (value: string) => void;\n ref: Ref<HTMLDivElement>;\n};\n\nexport const ComposerContext = createContext<ComposerContext | null>(null);\nexport const ComposerEditorContext =\n createContext<ComposerEditorContext | null>(null);\nexport const ComposerSuggestionsContext =\n createContext<ComposerSuggestionsContext | null>(null);\n\nexport function useComposerEditorContext() {\n const composerEditorContext = useContext(ComposerEditorContext);\n\n return nn(\n composerEditorContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposer(): ComposerContext {\n const composerContext = useContext(ComposerContext);\n\n return nn(composerContext, \"Composer.Form is missing from the React tree.\");\n}\n\nexport function useComposerSuggestionsContext(\n source = \"useComposerSuggestionsContext\"\n) {\n const composerSuggestionsContext = useContext(ComposerSuggestionsContext);\n\n return nn(\n composerSuggestionsContext,\n `${source} can’t be used outside of Composer.Editor.`\n );\n}\n"],"names":[],"mappings":";;;AAuEa,MAAA,eAAA,GAAkB,cAAsC,IAAI,EAAA;AAC5D,MAAA,qBAAA,GACX,cAA4C,IAAI,EAAA;AACrC,MAAA,0BAAA,GACX,cAAiD,IAAI,EAAA;AAEhD,SAAS,wBAA2B,GAAA;AACzC,EAAM,MAAA,qBAAA,GAAwB,WAAW,qBAAqB,CAAA,CAAA;AAE9D,EAAO,OAAA,EAAA;AAAA,IACL,qBAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,WAA+B,GAAA;AAC7C,EAAM,MAAA,eAAA,GAAkB,WAAW,eAAe,CAAA,CAAA;AAElD,EAAO,OAAA,EAAA,CAAG,iBAAiB,+CAA+C,CAAA,CAAA;AAC5E,CAAA;AAEgB,SAAA,6BAAA,CACd,SAAS,+BACT,EAAA;AACA,EAAM,MAAA,0BAAA,GAA6B,WAAW,0BAA0B,CAAA,CAAA;AAExE,EAAO,OAAA,EAAA;AAAA,IACL,0BAAA;AAAA,IACA,CAAG,EAAA,MAAA,CAAA,+CAAA,CAAA;AAAA,GACL,CAAA;AACF;;;;"}
1
+ {"version":3,"file":"contexts.mjs","sources":["../../../src/primitives/Composer/contexts.ts"],"sourcesContent":["import type { Placement } from \"@floating-ui/react-dom\";\nimport type { CommentMixedAttachment } from \"@liveblocks/core\";\nimport { nn } from \"@liveblocks/core\";\nimport type { Direction } from \"@radix-ui/react-dropdown-menu\";\nimport type { Dispatch, Ref, SetStateAction } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport type { Editor as SlateEditor, Element as SlateElement } from \"slate\";\n\nexport type ComposerContext = {\n /**\n * Whether the composer is currently disabled.\n */\n isDisabled: boolean;\n\n /**\n * Whether the composer can currently be submitted.\n */\n canSubmit: boolean;\n\n /**\n * Whether the editor is currently focused.\n */\n isFocused: boolean;\n\n /**\n * Whether the editor is currently empty.\n */\n isEmpty: boolean;\n\n /**\n * Submit the composer programmatically.\n */\n submit: () => void;\n\n /**\n * Clear the composer programmatically.\n */\n clear: () => void;\n\n /**\n * Select the editor programmatically.\n */\n select: () => void;\n\n /**\n * Focus the editor programmatically.\n */\n focus: () => void;\n\n /**\n * Blur the editor programmatically.\n */\n blur: () => void;\n\n /**\n * Start creating a mention at the current selection.\n */\n createMention: () => void;\n\n /**\n * Insert text at the current selection.\n */\n insertText: (text: string) => void;\n\n /**\n * Open a file picker programmatically to create attachments.\n */\n attachFiles: () => void;\n\n /**\n * The composer's current attachments.\n */\n attachments: CommentMixedAttachment[];\n\n /**\n * Remove an attachment by its ID.\n */\n removeAttachment: (attachmentId: string) => void;\n};\n\nexport type ComposerEditorContext = {\n validate: (value: SlateElement[]) => void;\n editor: SlateEditor;\n setFocused: Dispatch<SetStateAction<boolean>>;\n};\n\nexport type ComposerAttachmentsContext = {\n canAddAttachments: boolean;\n createAttachments: (files: File[]) => void;\n isUploadingAttachments: boolean;\n maxAttachments: number;\n maxAttachmentSize: number;\n};\n\nexport type ComposerSuggestionsContext = {\n dir?: Direction;\n id: string;\n itemId: (value?: string) => string | undefined;\n placement: Placement;\n selectedValue?: string;\n setSelectedValue: (value: string) => void;\n onItemSelect: (value: string) => void;\n ref: Ref<HTMLDivElement>;\n};\n\nexport const ComposerContext = createContext<ComposerContext | null>(null);\nexport const ComposerEditorContext =\n createContext<ComposerEditorContext | null>(null);\nexport const ComposerAttachmentsContext =\n createContext<ComposerAttachmentsContext | null>(null);\nexport const ComposerSuggestionsContext =\n createContext<ComposerSuggestionsContext | null>(null);\n\nexport function useComposerEditorContext() {\n const composerEditorContext = useContext(ComposerEditorContext);\n\n return nn(\n composerEditorContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposerAttachmentsContextOrNull() {\n return useContext(ComposerAttachmentsContext);\n}\n\nexport function useComposerAttachmentsContext() {\n const composerAttachmentsContext = useComposerAttachmentsContextOrNull();\n\n return nn(\n composerAttachmentsContext,\n \"Composer.Form is missing from the React tree.\"\n );\n}\n\nexport function useComposerSuggestionsContext(\n source = \"useComposerSuggestionsContext\"\n) {\n const composerSuggestionsContext = useContext(ComposerSuggestionsContext);\n\n return nn(\n composerSuggestionsContext,\n `${source} can’t be used outside of Composer.Editor.`\n );\n}\n\nexport function useComposer(): ComposerContext {\n const composerContext = useContext(ComposerContext);\n\n return nn(composerContext, \"Composer.Form is missing from the React tree.\");\n}\n"],"names":[],"mappings":";;;AAyGa,MAAA,eAAA,GAAkB,cAAsC,IAAI,EAAA;AAC5D,MAAA,qBAAA,GACX,cAA4C,IAAI,EAAA;AACrC,MAAA,0BAAA,GACX,cAAiD,IAAI,EAAA;AAC1C,MAAA,0BAAA,GACX,cAAiD,IAAI,EAAA;AAEhD,SAAS,wBAA2B,GAAA;AACzC,EAAM,MAAA,qBAAA,GAAwB,WAAW,qBAAqB,CAAA,CAAA;AAE9D,EAAO,OAAA,EAAA;AAAA,IACL,qBAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,mCAAsC,GAAA;AACpD,EAAA,OAAO,WAAW,0BAA0B,CAAA,CAAA;AAC9C,CAAA;AAEO,SAAS,6BAAgC,GAAA;AAC9C,EAAA,MAAM,6BAA6B,mCAAoC,EAAA,CAAA;AAEvE,EAAO,OAAA,EAAA;AAAA,IACL,0BAAA;AAAA,IACA,+CAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEgB,SAAA,6BAAA,CACd,SAAS,+BACT,EAAA;AACA,EAAM,MAAA,0BAAA,GAA6B,WAAW,0BAA0B,CAAA,CAAA;AAExE,EAAO,OAAA,EAAA;AAAA,IACL,0BAAA;AAAA,IACA,CAAG,EAAA,MAAA,CAAA,+CAAA,CAAA;AAAA,GACL,CAAA;AACF,CAAA;AAEO,SAAS,WAA+B,GAAA;AAC7C,EAAM,MAAA,eAAA,GAAkB,WAAW,eAAe,CAAA,CAAA;AAElD,EAAO,OAAA,EAAA,CAAG,iBAAiB,+CAA+C,CAAA,CAAA;AAC5E;;;;"}
@@ -41,6 +41,8 @@ const COMPOSER_SUGGESTIONS_LIST_NAME = "ComposerSuggestionsList";
41
41
  const COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = "ComposerSuggestionsListItem";
42
42
  const COMPOSER_SUBMIT_NAME = "ComposerSubmit";
43
43
  const COMPOSER_EDITOR_NAME = "ComposerEditor";
44
+ const COMPOSER_ATTACH_FILES_NAME = "ComposerAttachFiles";
45
+ const COMPOSER_ATTACHMENTS_DROP_AREA_NAME = "ComposerAttachmentsDropArea";
44
46
  const COMPOSER_FORM_NAME = "ComposerForm";
45
47
  const emptyCommentBody = {
46
48
  version: 1,
@@ -390,13 +392,16 @@ const ComposerEditor = React.forwardRef(
390
392
  dir,
391
393
  ...props
392
394
  }, forwardedRef) => {
393
- const self = react.useSelf();
394
- const isDisabled = React.useMemo(
395
- () => disabled || (self ? !self.canComment : false),
396
- [disabled, self?.canComment]
397
- );
398
395
  const { editor, validate, setFocused } = contexts.useComposerEditorContext();
399
- const { submit, focus, select, isEmpty, isFocused } = contexts.useComposer();
396
+ const {
397
+ submit,
398
+ focus,
399
+ select,
400
+ canSubmit,
401
+ isDisabled: isComposerDisabled,
402
+ isFocused
403
+ } = contexts.useComposer();
404
+ const isDisabled = isComposerDisabled || disabled;
400
405
  const initialBody = useInitial.useInitial(defaultValue ?? emptyCommentBody);
401
406
  const initialEditorValue = React.useMemo(() => {
402
407
  return utils.commentBodyToComposerBody(initialBody);
@@ -487,9 +492,11 @@ const ComposerEditor = React.forwardRef(
487
492
  event.preventDefault();
488
493
  slateReact.ReactEditor.blur(editor);
489
494
  }
490
- if (isKey.isKey(event, "Enter", { shift: false }) && !isEmpty) {
495
+ if (isKey.isKey(event, "Enter", { shift: false })) {
491
496
  event.preventDefault();
492
- submit();
497
+ if (canSubmit) {
498
+ submit();
499
+ }
493
500
  }
494
501
  if (isKey.isKey(event, "Enter", { shift: true })) {
495
502
  event.preventDefault();
@@ -516,7 +523,7 @@ const ComposerEditor = React.forwardRef(
516
523
  [
517
524
  createMention,
518
525
  editor,
519
- isEmpty,
526
+ canSubmit,
520
527
  mentionDraft,
521
528
  mentionSuggestions,
522
529
  selectedMentionSuggestionIndex,
@@ -622,14 +629,48 @@ const ComposerEditor = React.forwardRef(
622
629
  }));
623
630
  }
624
631
  );
632
+ const MAX_ATTACHMENTS = 5;
633
+ const MAX_ATTACHMENT_SIZE = 20 * 1024 * 1024;
625
634
  const ComposerForm = React.forwardRef(
626
- ({ children, onSubmit, onComposerSubmit, asChild, ...props }, forwardedRef) => {
635
+ ({
636
+ children,
637
+ onSubmit,
638
+ onComposerSubmit,
639
+ defaultAttachments = [],
640
+ disabled,
641
+ asChild,
642
+ ...props
643
+ }, forwardedRef) => {
627
644
  const Component = asChild ? reactSlot.Slot : "form";
628
645
  const editor = useInitial.useInitial(createComposerEditor);
646
+ const room = react.useRoom();
629
647
  const [isEmpty$1, setEmpty] = React.useState(true);
648
+ const [isSubmitting, setSubmitting] = React.useState(false);
630
649
  const [isFocused, setFocused] = React.useState(false);
650
+ const maxAttachments = MAX_ATTACHMENTS;
651
+ const maxAttachmentSize = MAX_ATTACHMENT_SIZE;
652
+ const {
653
+ attachments,
654
+ isUploadingAttachments,
655
+ addAttachment,
656
+ removeAttachment,
657
+ clearAttachments
658
+ } = utils.useComposerAttachmentsManager(defaultAttachments, {
659
+ maxFileSize: maxAttachmentSize
660
+ });
661
+ const numberOfAttachments = attachments.length;
662
+ const canAddAttachments = numberOfAttachments < maxAttachments;
663
+ const isDisabled = React.useMemo(() => {
664
+ const self = room.getSelf();
665
+ const canComment = self?.canComment ?? true;
666
+ return isSubmitting || disabled || !canComment;
667
+ }, [isSubmitting, disabled, room]);
668
+ const canSubmit = React.useMemo(() => {
669
+ return !isEmpty$1 && !isUploadingAttachments;
670
+ }, [isEmpty$1, isUploadingAttachments]);
631
671
  const ref = React.useRef(null);
632
672
  const mergedRefs = useRefs.useRefs(forwardedRef, ref);
673
+ const fileInputRef = React.useRef(null);
633
674
  const validate = React.useCallback(
634
675
  (value) => {
635
676
  setEmpty(isEmpty.isEmpty(editor, value));
@@ -637,12 +678,15 @@ const ComposerForm = React.forwardRef(
637
678
  [editor]
638
679
  );
639
680
  const submit = React.useCallback(() => {
681
+ if (!canSubmit) {
682
+ return;
683
+ }
640
684
  requestAnimationFrame(() => {
641
685
  if (ref.current) {
642
686
  requestSubmit.requestSubmit(ref.current);
643
687
  }
644
688
  });
645
- }, []);
689
+ }, [canSubmit]);
646
690
  const clear = React.useCallback(() => {
647
691
  slate.Transforms.delete(editor, {
648
692
  at: {
@@ -672,10 +716,6 @@ const ComposerForm = React.forwardRef(
672
716
  const blur = React.useCallback(() => {
673
717
  slateReact.ReactEditor.blur(editor);
674
718
  }, [editor]);
675
- const onSubmitEnd = React.useCallback(() => {
676
- clear();
677
- blur();
678
- }, [blur, clear]);
679
719
  const createMention = React.useCallback(() => {
680
720
  focus();
681
721
  mentions.insertMentionCharacter(editor);
@@ -687,6 +727,42 @@ const ComposerForm = React.forwardRef(
687
727
  },
688
728
  [editor, focus]
689
729
  );
730
+ const createAttachments = React.useCallback(
731
+ (files) => {
732
+ if (!files.length) {
733
+ return;
734
+ }
735
+ const numberOfAcceptedFiles = Math.max(
736
+ 0,
737
+ maxAttachments - numberOfAttachments
738
+ );
739
+ files.splice(numberOfAcceptedFiles);
740
+ for (const file of files) {
741
+ const attachment = room.prepareAttachment(file);
742
+ addAttachment(attachment);
743
+ }
744
+ },
745
+ [addAttachment, maxAttachments, numberOfAttachments, room]
746
+ );
747
+ const attachFiles = React.useCallback(() => {
748
+ if (fileInputRef.current) {
749
+ fileInputRef.current.click();
750
+ }
751
+ }, []);
752
+ const handleAttachmentsInputChange = React.useCallback(
753
+ (event) => {
754
+ if (event.target.files) {
755
+ createAttachments(Array.from(event.target.files));
756
+ }
757
+ },
758
+ [createAttachments]
759
+ );
760
+ const onSubmitEnd = React.useCallback(() => {
761
+ clear();
762
+ blur();
763
+ clearAttachments();
764
+ setSubmitting(false);
765
+ }, [blur, clear, clearAttachments]);
690
766
  const handleSubmit = React.useCallback(
691
767
  (event) => {
692
768
  const isEmpty2 = isEmpty.isEmpty(editor, editor.children);
@@ -702,16 +778,30 @@ const ComposerForm = React.forwardRef(
702
778
  const body = utils.composerBodyToCommentBody(
703
779
  editor.children
704
780
  );
705
- const comment = { body };
706
- const promise = onComposerSubmit(comment, event);
781
+ const commentAttachments = attachments.filter(
782
+ (attachment) => attachment.type === "attachment" || attachment.type === "localAttachment" && attachment.status === "uploaded"
783
+ ).map((attachment) => {
784
+ return {
785
+ id: attachment.id,
786
+ type: "attachment",
787
+ mimeType: attachment.mimeType,
788
+ size: attachment.size,
789
+ name: attachment.name
790
+ };
791
+ });
792
+ const promise = onComposerSubmit(
793
+ { body, attachments: commentAttachments },
794
+ event
795
+ );
707
796
  event.preventDefault();
708
797
  if (promise) {
798
+ setSubmitting(true);
709
799
  promise.then(onSubmitEnd);
710
800
  } else {
711
801
  onSubmitEnd();
712
802
  }
713
803
  },
714
- [editor, onComposerSubmit, onSubmit, onSubmitEnd]
804
+ [editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]
715
805
  );
716
806
  return /* @__PURE__ */ React.createElement(contexts.ComposerEditorContext.Provider, {
717
807
  value: {
@@ -719,34 +809,50 @@ const ComposerForm = React.forwardRef(
719
809
  validate,
720
810
  setFocused
721
811
  }
812
+ }, /* @__PURE__ */ React.createElement(contexts.ComposerAttachmentsContext.Provider, {
813
+ value: {
814
+ createAttachments,
815
+ isUploadingAttachments,
816
+ canAddAttachments,
817
+ maxAttachments,
818
+ maxAttachmentSize
819
+ }
722
820
  }, /* @__PURE__ */ React.createElement(contexts.ComposerContext.Provider, {
723
821
  value: {
822
+ isDisabled,
724
823
  isFocused,
725
824
  isEmpty: isEmpty$1,
825
+ canSubmit,
726
826
  submit,
727
827
  clear,
728
828
  select,
729
829
  focus,
730
830
  blur,
731
831
  createMention,
732
- insertText
832
+ insertText,
833
+ attachments,
834
+ attachFiles,
835
+ removeAttachment
733
836
  }
734
837
  }, /* @__PURE__ */ React.createElement(Component, {
735
838
  ...props,
736
839
  onSubmit: handleSubmit,
737
840
  ref: mergedRefs
738
- }, children)));
841
+ }, /* @__PURE__ */ React.createElement("input", {
842
+ type: "file",
843
+ multiple: true,
844
+ ref: fileInputRef,
845
+ onChange: handleAttachmentsInputChange,
846
+ tabIndex: -1,
847
+ style: { display: "none" }
848
+ }), /* @__PURE__ */ React.createElement(reactSlot.Slottable, null, children)))));
739
849
  }
740
850
  );
741
851
  const ComposerSubmit = React.forwardRef(
742
852
  ({ children, disabled, asChild, ...props }, forwardedRef) => {
743
853
  const Component = asChild ? reactSlot.Slot : "button";
744
- const { isEmpty } = contexts.useComposer();
745
- const self = react.useSelf();
746
- const isDisabled = React.useMemo(
747
- () => disabled || isEmpty || (self ? !self.canComment : false),
748
- [disabled, isEmpty, self?.canComment]
749
- );
854
+ const { canSubmit, isDisabled: isComposerDisabled } = contexts.useComposer();
855
+ const isDisabled = isComposerDisabled || disabled || !canSubmit;
750
856
  return /* @__PURE__ */ React.createElement(Component, {
751
857
  type: "submit",
752
858
  ...props,
@@ -755,7 +861,58 @@ const ComposerSubmit = React.forwardRef(
755
861
  }, children);
756
862
  }
757
863
  );
864
+ const ComposerAttachFiles = React.forwardRef(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {
865
+ const Component = asChild ? reactSlot.Slot : "button";
866
+ const { canAddAttachments } = contexts.useComposerAttachmentsContext();
867
+ const { isDisabled: isComposerDisabled, attachFiles } = contexts.useComposer();
868
+ const isDisabled = isComposerDisabled || !canAddAttachments || disabled;
869
+ const handleClick = React.useCallback(
870
+ (event) => {
871
+ onClick?.(event);
872
+ if (!event.isDefaultPrevented()) {
873
+ attachFiles();
874
+ }
875
+ },
876
+ [attachFiles, onClick]
877
+ );
878
+ return /* @__PURE__ */ React.createElement(Component, {
879
+ ...props,
880
+ onClick: handleClick,
881
+ ref: forwardedRef,
882
+ disabled: isDisabled
883
+ }, children);
884
+ });
885
+ const ComposerAttachmentsDropArea = React.forwardRef(
886
+ ({
887
+ onDragEnter,
888
+ onDragLeave,
889
+ onDragOver,
890
+ onDrop,
891
+ disabled,
892
+ asChild,
893
+ ...props
894
+ }, forwardedRef) => {
895
+ const Component = asChild ? reactSlot.Slot : "div";
896
+ const { isDisabled: isComposerDisabled } = contexts.useComposer();
897
+ const isDisabled = isComposerDisabled || disabled;
898
+ const [, dropAreaProps] = utils.useComposerAttachmentsDropArea({
899
+ onDragEnter,
900
+ onDragLeave,
901
+ onDragOver,
902
+ onDrop,
903
+ disabled: isDisabled
904
+ });
905
+ return /* @__PURE__ */ React.createElement(Component, {
906
+ ...dropAreaProps,
907
+ "data-disabled": isDisabled ? "" : void 0,
908
+ ...props,
909
+ ref: forwardedRef
910
+ });
911
+ }
912
+ );
758
913
  if (process.env.NODE_ENV !== "production") {
914
+ ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;
915
+ ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;
759
916
  ComposerEditor.displayName = COMPOSER_EDITOR_NAME;
760
917
  ComposerForm.displayName = COMPOSER_FORM_NAME;
761
918
  ComposerMention.displayName = COMPOSER_MENTION_NAME;
@@ -766,6 +923,8 @@ if (process.env.NODE_ENV !== "production") {
766
923
  ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;
767
924
  }
768
925
 
926
+ exports.AttachFiles = ComposerAttachFiles;
927
+ exports.AttachmentsDropArea = ComposerAttachmentsDropArea;
769
928
  exports.Editor = ComposerEditor;
770
929
  exports.Form = ComposerForm;
771
930
  exports.Link = ComposerLink;