├── .changeset ├── README.md └── config.json ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ └── feature.md ├── pull_request_template.md └── workflows │ ├── build-and-lint.yml │ ├── build-pr.yml │ ├── deploy-pr.yml │ ├── deploy.yml │ ├── playwright.yml │ └── pr-cleanup.yml ├── .gitignore ├── .husky └── pre-push ├── .nvmrc ├── .yarn └── releases │ └── yarn-berry.cjs ├── .yarnrc.yml ├── README.md ├── lerna.json ├── package.json ├── packages ├── api │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── playground │ │ ├── index.html │ │ └── playground.js │ ├── rollup.config.js │ ├── src │ │ ├── EmbeddedChatApi.ts │ │ ├── cloneArray.ts │ │ ├── index.ts │ │ └── utils │ │ │ └── constants.ts │ └── tsconfig.json ├── auth │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── playground │ │ ├── index.html │ │ └── playground.js │ ├── rollup.config.js │ ├── src │ │ ├── Api.ts │ │ ├── IRocketChatAuthOptions.ts │ │ ├── RocketChatAuth.ts │ │ ├── auth.ts │ │ ├── getAuthorizationUrl.ts │ │ ├── handleSecureLogin.ts │ │ ├── index.ts │ │ ├── loginWithOAuthServiceToken.ts │ │ ├── loginWithPassword.ts │ │ ├── loginWithResumeToken.ts │ │ ├── loginWithRocketChatOAuth.ts │ │ └── utils │ │ │ ├── constants.ts │ │ │ ├── getRCAppBaseURL.ts │ │ │ ├── getRCAppInfo.ts │ │ │ ├── getRCAuthorizeURL.ts │ │ │ └── tokenRequestHandler.ts │ └── tsconfig.json ├── docs │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── blog │ │ ├── EmbeddedChat-2022.md │ │ ├── EmbeddedChat-2023.md │ │ └── EmbeddedChat-2024.md │ ├── docs │ │ ├── Development │ │ │ ├── dev_launch.md │ │ │ ├── theming_technical.md │ │ │ └── ui-elements.md │ │ ├── Usage │ │ │ ├── authentication.md │ │ │ ├── ec_rc_setup.md │ │ │ ├── embeddedchat_setup.md │ │ │ ├── layout_editor.md │ │ │ └── theming.md │ │ └── introduction.md │ ├── docusaurus.config.js │ ├── package.json │ ├── sidebars.js │ ├── src │ │ ├── assets │ │ │ ├── EC-Logo.png │ │ │ ├── app.png │ │ │ ├── easy.png │ │ │ └── pencil.png │ │ ├── components │ │ │ └── HomepageFeatures │ │ │ │ ├── index.js │ │ │ │ └── styles.module.css │ │ ├── css │ │ │ └── custom.css │ │ └── pages │ │ │ ├── index.js │ │ │ ├── index.module.css │ │ │ └── markdown-page.md │ ├── static │ │ ├── .nojekyll │ │ └── img │ │ │ ├── EC-Logo.png │ │ │ └── favicon.ico │ └── yarn.lock ├── e2e-react │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tests │ │ └── example.spec.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── htmlembed │ ├── .babelrc │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── postbuild.cjs │ ├── public │ │ └── index.html │ ├── rollup.config.js │ ├── src │ │ ├── EmbeddedChat.jsx │ │ └── index.js │ └── vite.config.js ├── layout_editor │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── CHANGELOG.md │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.jsx │ │ ├── components │ │ │ ├── SortableMenu │ │ │ │ ├── Menu.jsx │ │ │ │ ├── Menu.styles.js │ │ │ │ ├── MenuItem.jsx │ │ │ │ └── index.js │ │ │ └── SurfaceMenu │ │ │ │ ├── SurfaceItem.jsx │ │ │ │ ├── SurfaceMenu.jsx │ │ │ │ └── SurfaceMenu.styles.js │ │ ├── data │ │ │ ├── members.json │ │ │ └── messages.json │ │ ├── hooks │ │ │ ├── useDisplayNameColor.js │ │ │ └── useThemeGenerator.js │ │ ├── lib │ │ │ ├── isMessageLastSequential.js │ │ │ └── isMessageSequential.js │ │ ├── main.jsx │ │ ├── main.tsx │ │ ├── store │ │ │ ├── chatInputItemsStore.js │ │ │ ├── headerItemsStore.js │ │ │ ├── layoutStore.js │ │ │ └── messageItemsStore.js │ │ ├── theme │ │ │ └── DefaultTheme.js │ │ ├── views │ │ │ ├── ChatHeader │ │ │ │ ├── ChatHeader.jsx │ │ │ │ └── ChatHeader.styles.js │ │ │ ├── ChatInput │ │ │ │ ├── ChatInput.jsx │ │ │ │ ├── ChatInput.styles.js │ │ │ │ ├── ChatInputToolbar.jsx │ │ │ │ └── Formatters.jsx │ │ │ ├── ChatLayout │ │ │ │ ├── ChatLayout.jsx │ │ │ │ └── ChatLayout.styles.js │ │ │ ├── Chatbody │ │ │ │ ├── ChatBody.jsx │ │ │ │ └── ChatBody.styles.js │ │ │ ├── DemoSidebar │ │ │ │ ├── DemoSidebar.jsx │ │ │ │ └── DemoSidebar.styles.js │ │ │ ├── GlobalStyles.jsx │ │ │ ├── LayoutEditor.jsx │ │ │ ├── LayoutEditor.style.js │ │ │ ├── Markdown │ │ │ │ ├── Markdown.jsx │ │ │ │ └── index.js │ │ │ ├── Message │ │ │ │ ├── BubbleVariant │ │ │ │ │ ├── Bubble.styles.js │ │ │ │ │ ├── BubbleThreadBtn.js │ │ │ │ │ └── useBubbleStyles.js │ │ │ │ ├── Message.jsx │ │ │ │ ├── Message.styles.js │ │ │ │ ├── MessageAvatarContainer.jsx │ │ │ │ ├── MessageBody.jsx │ │ │ │ ├── MessageBodyContainer.jsx │ │ │ │ ├── MessageDivider.jsx │ │ │ │ ├── MessageHeader.jsx │ │ │ │ ├── MessageToolbox.jsx │ │ │ │ └── index.js │ │ │ ├── MessageList │ │ │ │ └── MessageList.jsx │ │ │ └── ThemeLab │ │ │ │ ├── ColorManager.jsx │ │ │ │ ├── FontManager.jsx │ │ │ │ ├── LayoutSetting.jsx │ │ │ │ ├── ThemeLab.jsx │ │ │ │ ├── ThemeLab.styles.js │ │ │ │ └── ThemeSetting.jsx │ │ └── vite-env.d.ts │ ├── tests │ │ └── example.spec.ts │ └── vite.config.ts ├── markups │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── CHANGELOG.md │ ├── babel.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── Markup.js │ │ ├── MarkupInteractionContext.js │ │ ├── blocks │ │ │ ├── HeadingBlock.js │ │ │ ├── OrderedListBlock.js │ │ │ ├── ParagraphBlock.js │ │ │ ├── QuoteBlock.js │ │ │ ├── TaskListBlock.js │ │ │ ├── UnOrderedListBlock.js │ │ │ └── blocks.styles.js │ │ ├── elements │ │ │ ├── BigEmoji.js │ │ │ ├── BoldSpan.js │ │ │ ├── CodeBlock.js │ │ │ ├── CodeElement.js │ │ │ ├── ColorElement.js │ │ │ ├── Emoji.js │ │ │ ├── InlineElements.js │ │ │ ├── ItalicSpan.js │ │ │ ├── LinkSpan.js │ │ │ ├── PlainSpan.js │ │ │ ├── StrikeSpan.js │ │ │ ├── TimestampElement.js │ │ │ └── elements.styles.js │ │ ├── index.js │ │ └── mentions │ │ │ ├── ChannelMention.js │ │ │ └── UserMention.js │ └── tsconfig.json ├── rc-app │ ├── .editorconfig │ ├── .gitignore │ ├── .rcappsconfig │ ├── CHANGELOG.md │ ├── EmbeddedChatApp.ts │ ├── README.md │ ├── app.json │ ├── endpoints │ │ ├── AuthTokenEndpoint.ts │ │ ├── CallbackEndpoint.ts │ │ └── InfoEndpoint.ts │ ├── icon.png │ ├── lib │ │ ├── extractTokenCookie.ts │ │ ├── getAllowedOrigins.ts │ │ ├── getCallbackContent.ts │ │ ├── getCallbackUrl.ts │ │ ├── getEnvironmentVariables.ts │ │ ├── getTokenUrl.ts │ │ ├── isAllowedOrigin.ts │ │ └── isValidCssDimension.ts │ ├── package.json │ ├── settings │ │ └── settings.ts │ ├── tsconfig.json │ └── tslint.json ├── react-native │ ├── .eslintrc.json │ ├── .gitignore │ ├── .storybook │ │ ├── index.js │ │ ├── main.js │ │ ├── preview.js │ │ └── storybook.requires.js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── app.config.js │ ├── babel.config.js │ ├── metro.config.js │ ├── package.json │ ├── src │ │ ├── App.jsx │ │ ├── assets │ │ │ └── fonts │ │ │ │ └── custom.ttf │ │ ├── components │ │ │ ├── ActivityIndicator │ │ │ │ ├── ActivityIndicator.js │ │ │ │ └── index.js │ │ │ ├── Avatar │ │ │ │ ├── Avatar.js │ │ │ │ └── index.js │ │ │ ├── Box │ │ │ │ ├── Box.js │ │ │ │ └── index.js │ │ │ ├── Button │ │ │ │ ├── Button.js │ │ │ │ └── index.js │ │ │ ├── ChatInput │ │ │ │ ├── ChatInput.js │ │ │ │ ├── RightButtons.js │ │ │ │ ├── buttons │ │ │ │ │ ├── ActionsButton.js │ │ │ │ │ ├── BaseButton.js │ │ │ │ │ ├── CancelEditingButton.js │ │ │ │ │ ├── SendButton.js │ │ │ │ │ ├── ToggleEmojiButton.js │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── CustomIcon │ │ │ │ ├── CustomIcon.js │ │ │ │ ├── index.js │ │ │ │ ├── mappedIcons.js │ │ │ │ └── selection.json │ │ │ ├── EmbeddedChat.js │ │ │ ├── FontProvider │ │ │ │ ├── FontProvider.js │ │ │ │ └── index.js │ │ │ ├── Markdown │ │ │ │ ├── Markdown.js │ │ │ │ └── index.js │ │ │ ├── Markup │ │ │ │ ├── Markup.js │ │ │ │ ├── blocks │ │ │ │ │ ├── CodeBlock.js │ │ │ │ │ ├── HeadingBlock.js │ │ │ │ │ ├── OrderedListBlock.js │ │ │ │ │ ├── ParagraphBlock.js │ │ │ │ │ ├── QuoteBlock.js │ │ │ │ │ ├── TaskListBlock.js │ │ │ │ │ └── UnOrderedListBlock.js │ │ │ │ ├── elements │ │ │ │ │ ├── BigEmoji.js │ │ │ │ │ ├── BoldSpan.js │ │ │ │ │ ├── CodeElement.js │ │ │ │ │ ├── CodeLine.js │ │ │ │ │ ├── ColorElement.js │ │ │ │ │ ├── Divider.js │ │ │ │ │ ├── Emoji.js │ │ │ │ │ ├── InlineElements.js │ │ │ │ │ ├── ItalicSpan.js │ │ │ │ │ ├── LinkSpan.js │ │ │ │ │ ├── Mention.js │ │ │ │ │ ├── PlainSpan.js │ │ │ │ │ └── StrikeSpan.js │ │ │ │ ├── index.js │ │ │ │ ├── mentions │ │ │ │ │ └── ChannelMention.js │ │ │ │ └── styles.js │ │ │ ├── Message │ │ │ │ ├── Message.js │ │ │ │ ├── MessageAvatar.js │ │ │ │ ├── MessageBody.js │ │ │ │ ├── MessageContent.js │ │ │ │ ├── MessageHeader.js │ │ │ │ ├── MessageMetrics.js │ │ │ │ └── index.js │ │ │ ├── MessageActionsSheet │ │ │ │ ├── ActionItem.js │ │ │ │ ├── MessageActionsSheet.js │ │ │ │ └── index.js │ │ │ ├── MessageList │ │ │ │ ├── MessageList.js │ │ │ │ └── index.js │ │ │ ├── Styles.js │ │ │ ├── TextInput │ │ │ │ ├── FormTextInput.js │ │ │ │ ├── TextInput.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── contexts │ │ │ ├── MessageContext.js │ │ │ ├── RCInstance.js │ │ │ └── Router.js │ │ ├── lib │ │ │ ├── auth.js │ │ │ ├── cloneArray.js │ │ │ ├── constants │ │ │ │ ├── colors.js │ │ │ │ └── index.js │ │ │ ├── createPendingMessage.js │ │ │ ├── isMessageSequential.js │ │ │ ├── messageListHelpers.js │ │ │ ├── openLink.js │ │ │ └── shortnameToUnicode │ │ │ │ ├── ascii.js │ │ │ │ ├── emojis.js │ │ │ │ └── index.js │ │ ├── store │ │ │ ├── channelStore.js │ │ │ ├── index.js │ │ │ ├── memberStore.js │ │ │ ├── messageStore.js │ │ │ └── userStore.js │ │ ├── theme │ │ │ ├── DefaultTheme.js │ │ │ └── useComponentOverrides.js │ │ └── views │ │ │ ├── ChatRoomView │ │ │ ├── ChatRoomView.js │ │ │ └── index.js │ │ │ └── LoginView │ │ │ ├── LoginView.js │ │ │ └── index.js │ ├── start-app.js │ └── yarn.lock ├── react │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .gitpod.yml │ ├── .prettierrc │ ├── .storybook │ │ ├── main.js │ │ └── preview.js │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── docker-compose.yml │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── context │ │ │ └── RCInstance.js │ │ ├── hooks │ │ │ ├── uiKit │ │ │ │ ├── useContextualBarContextValue.js │ │ │ │ ├── useMessageBlockContextValue.js │ │ │ │ ├── useModalBlockContextValue.js │ │ │ │ ├── useUiKitActionManager.js │ │ │ │ └── useUiKitView.js │ │ │ ├── useDisplayNameColor.js │ │ │ ├── useDropBox.js │ │ │ ├── useFetchChatData.js │ │ │ ├── useMediaRecorder.js │ │ │ ├── useRCAuth.js │ │ │ ├── useSearchMentionUser.js │ │ │ ├── useSetExclusiveState.js │ │ │ ├── useSetMessageList.js │ │ │ └── useShowCommands.js │ │ ├── index.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── auth.js │ │ │ ├── cloneArray.js │ │ │ ├── createPendingMessage.js │ │ │ ├── emoji.js │ │ │ ├── formatSelection.js │ │ │ ├── formatTimestamp.js │ │ │ ├── formatTimestampGetDate.js │ │ │ ├── isMessageLastSequential.js │ │ │ ├── isMessageSequential.js │ │ │ ├── messageListHelpers.js │ │ │ ├── overrideECProps.js │ │ │ ├── reaction.js │ │ │ └── textFormat.js │ │ ├── store │ │ │ ├── attachmentwindow.js │ │ │ ├── channelStore.js │ │ │ ├── fileStore.js │ │ │ ├── index.js │ │ │ ├── inviteStore.js │ │ │ ├── loginStore.js │ │ │ ├── memberStore.js │ │ │ ├── mentionsStore.js │ │ │ ├── messageStore.js │ │ │ ├── pinnedMessageStore.js │ │ │ ├── searchMessageStore.js │ │ │ ├── settingsStore.js │ │ │ ├── sidebarStore.js │ │ │ ├── starredMessageStore.js │ │ │ ├── threadsMessageStore.js │ │ │ ├── totpmodalStore.js │ │ │ ├── uiKitStore.js │ │ │ └── userStore.js │ │ ├── stories │ │ │ ├── CurveVariantStories │ │ │ │ ├── Colorful.stories.js │ │ │ │ └── Simplistic.stories.js │ │ │ ├── EmbeddedChat.stories.js │ │ │ ├── EmbeddedChatAuthToken.stories.js │ │ │ ├── EmbeddedChatSecureAuth.stories.js │ │ │ ├── EmbeddedChatWithOAuth.stories.js │ │ │ ├── EmbeddedChatWithRemote.stories.js │ │ │ ├── ModernVariantStories │ │ │ │ └── Simplistic.stories.js │ │ │ └── RCVariantStories │ │ │ │ ├── Colorful.stories.js │ │ │ │ └── Simplistic.stories.js │ │ ├── theme │ │ │ ├── AzureSky.js │ │ │ ├── CurveVariant │ │ │ │ ├── AquaBreeze.js │ │ │ │ ├── AzureSky.js │ │ │ │ ├── BlushCandy.js │ │ │ │ └── PineWhisper.js │ │ │ ├── DefaultTheme.js │ │ │ ├── MintMeadow.js │ │ │ ├── ModernVariant │ │ │ │ └── StormySeas.js │ │ │ └── RoseEmber.js │ │ └── views │ │ │ ├── AttachmentHandler │ │ │ ├── Attachment.js │ │ │ ├── AttachmentMetadata.js │ │ │ ├── Attachments.js │ │ │ ├── AudioAttachment.js │ │ │ ├── ImageAttachment.js │ │ │ ├── TextAttachment.js │ │ │ ├── VideoAttachment.js │ │ │ └── index.js │ │ │ ├── AttachmentPreview │ │ │ ├── AttachmentPreview.js │ │ │ ├── AttachmentPreview.styles.js │ │ │ ├── CheckPreviewType.js │ │ │ └── PreviewType │ │ │ │ ├── audio.js │ │ │ │ ├── default.js │ │ │ │ └── image.js │ │ │ ├── ChannelState │ │ │ ├── ChannelState.js │ │ │ └── ChannelState.styles.js │ │ │ ├── ChatBody │ │ │ ├── ChatBody.js │ │ │ ├── ChatBody.styles.js │ │ │ ├── RecentMessageButton.js │ │ │ └── index.js │ │ │ ├── ChatHeader │ │ │ ├── ChatHeader.js │ │ │ ├── ChatHeader.styles.js │ │ │ └── index.js │ │ │ ├── ChatInput │ │ │ ├── AudioMessageRecorder.js │ │ │ ├── ChatInput.js │ │ │ ├── ChatInput.styles.js │ │ │ ├── ChatInputFormattingToolbar.js │ │ │ ├── InsertLinkToolBox.js │ │ │ ├── VideoMessageRecoder.js │ │ │ └── index.js │ │ │ ├── ChatLayout │ │ │ ├── ChatLayout.js │ │ │ ├── ChatLayout.styles.js │ │ │ └── index.js │ │ │ ├── CommandList │ │ │ ├── CommandList.style.js │ │ │ ├── CommandsList.js │ │ │ └── index.js │ │ │ ├── ContextualBarBlock │ │ │ └── uiKit │ │ │ │ └── UiKitContextualBar.js │ │ │ ├── DynamicHeader │ │ │ ├── DynamicHeader.js │ │ │ ├── DynamicHeader.styles.js │ │ │ └── index.js │ │ │ ├── EmbeddedChat.js │ │ │ ├── EmbeddedChat.styles.js │ │ │ ├── EmojiPicker │ │ │ ├── EmojiPicker.js │ │ │ ├── EmojiPicker.styles.js │ │ │ └── index.js │ │ │ ├── EmojiReaction │ │ │ ├── EmojiReaction.js │ │ │ └── index.js │ │ │ ├── FileMessage │ │ │ ├── FileMessage.js │ │ │ ├── FileMetrics.js │ │ │ ├── FilePreviewContainer.js │ │ │ ├── FilePreviewHeader.js │ │ │ └── Files.styles.js │ │ │ ├── GlobalStyles.js │ │ │ ├── ImageGallery │ │ │ ├── ImageGallery.js │ │ │ ├── ImageGallery.styles.js │ │ │ └── Swiper.js │ │ │ ├── LinkPreview │ │ │ ├── LinkPreview.js │ │ │ ├── LinkPreview.styles.js │ │ │ └── index.js │ │ │ ├── LoginForm │ │ │ ├── LoginForm.js │ │ │ └── LoginForm.styles.js │ │ │ ├── Markdown │ │ │ ├── Markdown.js │ │ │ └── index.js │ │ │ ├── Mentions │ │ │ ├── MembersList.js │ │ │ └── MembersList.styles.js │ │ │ ├── Message │ │ │ ├── BubbleVariant │ │ │ │ ├── Bubble.styles.js │ │ │ │ ├── BubbleThreadBtn.js │ │ │ │ └── useBubbleStyles.js │ │ │ ├── Message.js │ │ │ ├── Message.styles.js │ │ │ ├── MessageAvatarContainer.js │ │ │ ├── MessageBody.js │ │ │ ├── MessageBodyContainer.js │ │ │ ├── MessageDivider.js │ │ │ ├── MessageHeader.js │ │ │ ├── MessageMetrics.js │ │ │ ├── MessageReactions.js │ │ │ ├── MessageToolbox.js │ │ │ ├── index.js │ │ │ └── uiKit │ │ │ │ └── UiKitMessageBlock.js │ │ │ ├── MessageAggregators │ │ │ ├── FileGallery.js │ │ │ ├── MentionedMessages.js │ │ │ ├── PinnedMessages.js │ │ │ ├── SearchMessages.js │ │ │ ├── StarredMessages.js │ │ │ ├── ThreadedMessages.js │ │ │ └── common │ │ │ │ ├── LoadingIndicator.js │ │ │ │ ├── MessageAggregator.js │ │ │ │ ├── MessageAggregator.styles.js │ │ │ │ └── NoMessageIndicator.js │ │ │ ├── MessageList │ │ │ ├── MessageList.js │ │ │ └── index.js │ │ │ ├── ModalBlock │ │ │ └── uiKit │ │ │ │ ├── ModalBlock.js │ │ │ │ └── UiKitModal.js │ │ │ ├── QuoteMessage │ │ │ ├── QuoteMessage.js │ │ │ └── QuoteMessage.styles.js │ │ │ ├── ReportMessage │ │ │ ├── MessageReportWindow.js │ │ │ ├── ReportMessage.styles.js │ │ │ ├── ReportWindowButtons.js │ │ │ └── index.js │ │ │ ├── RoomInformation │ │ │ ├── RoomInformation.js │ │ │ └── RoomInformation.styles.js │ │ │ ├── RoomMembers │ │ │ ├── InviteMembers.js │ │ │ ├── RoomMember.js │ │ │ ├── RoomMemberItem.js │ │ │ └── RoomMembers.styles.js │ │ │ ├── SurfaceMenu │ │ │ ├── SurfaceItem.js │ │ │ └── SurfaceMenu.js │ │ │ ├── Thread │ │ │ ├── ThreadMessageList.js │ │ │ └── index.js │ │ │ ├── TotpModal │ │ │ └── TwoFactorTotpModal.js │ │ │ ├── TypingUsers │ │ │ ├── TypingUsers.js │ │ │ └── index.js │ │ │ └── UserInformation │ │ │ ├── UserInfoField.js │ │ │ ├── UserInformation.js │ │ │ └── UserInformation.styles.js │ └── tools │ │ └── theme-generator.js ├── ui-elements │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── .storybook │ │ ├── main.js │ │ └── preview.js │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── components │ │ │ ├── ActionButton │ │ │ │ ├── ActionButton.js │ │ │ │ ├── ActionButton.stories.js │ │ │ │ └── index.js │ │ │ ├── Avatar │ │ │ │ ├── Avatar.js │ │ │ │ ├── Avatar.stories.js │ │ │ │ ├── Avatar.styles.js │ │ │ │ ├── AvatarContainer.js │ │ │ │ └── index.js │ │ │ ├── Box │ │ │ │ ├── Box.js │ │ │ │ ├── Box.stories.js │ │ │ │ ├── Box.style.js │ │ │ │ └── index.js │ │ │ ├── Button │ │ │ │ ├── Button.js │ │ │ │ ├── Button.stories.js │ │ │ │ ├── Button.styles.js │ │ │ │ └── index.js │ │ │ ├── CheckBox │ │ │ │ ├── CheckBox.js │ │ │ │ ├── CheckBox.stories.js │ │ │ │ ├── CheckBox.styles.js │ │ │ │ └── index.js │ │ │ ├── Divider │ │ │ │ ├── Divider.js │ │ │ │ ├── Divider.stories.js │ │ │ │ ├── Divider.styles.js │ │ │ │ └── index.js │ │ │ ├── Flex │ │ │ │ ├── Flex.stories.js │ │ │ │ ├── FlexContainer.js │ │ │ │ ├── FlexItem.js │ │ │ │ └── index.js │ │ │ ├── GenericModal │ │ │ │ ├── GenericModal.js │ │ │ │ ├── GenericModal.stories.js │ │ │ │ └── index.js │ │ │ ├── Grid │ │ │ │ ├── Grid.js │ │ │ │ ├── Grid.stories.js │ │ │ │ ├── Grid.styles.js │ │ │ │ ├── GridItem.js │ │ │ │ └── index.js │ │ │ ├── Heading │ │ │ │ ├── Heading.js │ │ │ │ ├── Heading.stories.js │ │ │ │ └── index.js │ │ │ ├── Icon │ │ │ │ ├── Icon.js │ │ │ │ ├── Icon.stories.js │ │ │ │ ├── icons │ │ │ │ │ ├── Arc.js │ │ │ │ │ ├── ArrowBack.js │ │ │ │ │ ├── ArrowCollapse.js │ │ │ │ │ ├── ArrowDown.js │ │ │ │ │ ├── ArrowExpand.js │ │ │ │ │ ├── At.js │ │ │ │ │ ├── Attachment.js │ │ │ │ │ ├── Avatar.js │ │ │ │ │ ├── Away.js │ │ │ │ │ ├── Back.js │ │ │ │ │ ├── Bold.js │ │ │ │ │ ├── Busy.js │ │ │ │ │ ├── Check.js │ │ │ │ │ ├── ChevronDown.js │ │ │ │ │ ├── ChevronLeft.js │ │ │ │ │ ├── CircleArrowDown.js │ │ │ │ │ ├── CircleCheck.js │ │ │ │ │ ├── CircleCross.js │ │ │ │ │ ├── Clip.js │ │ │ │ │ ├── Clipboard.js │ │ │ │ │ ├── Clock.js │ │ │ │ │ ├── Code.js │ │ │ │ │ ├── Cog.js │ │ │ │ │ ├── Computer.js │ │ │ │ │ ├── Copy.js │ │ │ │ │ ├── Cross.js │ │ │ │ │ ├── DisableRecorder.js │ │ │ │ │ ├── Download.js │ │ │ │ │ ├── Edit.js │ │ │ │ │ ├── Emoji.js │ │ │ │ │ ├── ErrorCircle.js │ │ │ │ │ ├── EyeClose.js │ │ │ │ │ ├── EyeOpen.js │ │ │ │ │ ├── File.js │ │ │ │ │ ├── FormatText.js │ │ │ │ │ ├── Google.js │ │ │ │ │ ├── Hash.js │ │ │ │ │ ├── HashLock.js │ │ │ │ │ ├── Info.js │ │ │ │ │ ├── Italic.js │ │ │ │ │ ├── Kebab.js │ │ │ │ │ ├── Key.js │ │ │ │ │ ├── Link.js │ │ │ │ │ ├── Lock.js │ │ │ │ │ ├── Magnifier.js │ │ │ │ │ ├── Members.js │ │ │ │ │ ├── Mic.js │ │ │ │ │ ├── Mobile.js │ │ │ │ │ ├── Multiline.js │ │ │ │ │ ├── Offline.js │ │ │ │ │ ├── Online.js │ │ │ │ │ ├── Pin.js │ │ │ │ │ ├── PinFilled.js │ │ │ │ │ ├── Plus.js │ │ │ │ │ ├── Quote.js │ │ │ │ │ ├── ReplyDirectly.js │ │ │ │ │ ├── Report.js │ │ │ │ │ ├── Send.js │ │ │ │ │ ├── Star.js │ │ │ │ │ ├── StarFilled.js │ │ │ │ │ ├── Strike.js │ │ │ │ │ ├── Team.js │ │ │ │ │ ├── Thread.js │ │ │ │ │ ├── Trash.js │ │ │ │ │ ├── User.js │ │ │ │ │ ├── VideoRecoder.js │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ ├── Input │ │ │ │ ├── Input.js │ │ │ │ ├── Input.stories.js │ │ │ │ ├── Input.styles.js │ │ │ │ └── index.js │ │ │ ├── ListBox │ │ │ │ ├── ListBox.js │ │ │ │ ├── ListBox.stories.js │ │ │ │ ├── ListBox.styles.js │ │ │ │ └── index.js │ │ │ ├── Menu │ │ │ │ ├── Menu.js │ │ │ │ ├── Menu.stories.js │ │ │ │ ├── Menu.styles.js │ │ │ │ ├── MenuItem.js │ │ │ │ └── index.js │ │ │ ├── MessageGenericPreview │ │ │ │ ├── MessageGenericPreview.js │ │ │ │ ├── MessageGenericPreview.styles.js │ │ │ │ ├── MessageGenericPreviewContent.js │ │ │ │ ├── MessageGenericPreviewCoverImage.js │ │ │ │ ├── MessageGenericPreviewDescription.js │ │ │ │ ├── MessageGenericPreviewFooter.js │ │ │ │ ├── MessageGenericPreviewThumb.js │ │ │ │ ├── MessageGenericPreviewTitle.js │ │ │ │ └── index.js │ │ │ ├── Modal │ │ │ │ ├── Modal.js │ │ │ │ ├── Modal.stories.js │ │ │ │ ├── Modal.styles.js │ │ │ │ ├── ModalBackdrop.js │ │ │ │ ├── ModalClose.js │ │ │ │ ├── ModalContent.js │ │ │ │ ├── ModalFooter.js │ │ │ │ ├── ModalHeader.js │ │ │ │ ├── ModalThumb.js │ │ │ │ ├── ModalTitle.js │ │ │ │ └── index.js │ │ │ ├── MultiSelect │ │ │ │ ├── MultiSelect.js │ │ │ │ ├── MultiSelect.stories.js │ │ │ │ ├── MultiSelect.styles.js │ │ │ │ └── index.js │ │ │ ├── Popup │ │ │ │ ├── Popup.js │ │ │ │ ├── Popup.stories.js │ │ │ │ ├── Popup.styles.js │ │ │ │ ├── PopupHeader.js │ │ │ │ └── index.js │ │ │ ├── Sidebar │ │ │ │ ├── MinimalSidebar.js │ │ │ │ ├── Sidebar.js │ │ │ │ ├── Sidebar.stories.js │ │ │ │ ├── Sidebar.styles.js │ │ │ │ ├── SidebarContent.js │ │ │ │ ├── SidebarFooter.js │ │ │ │ ├── SidebarHeader.js │ │ │ │ └── index.js │ │ │ ├── Skeleton │ │ │ │ ├── Skeleton.js │ │ │ │ ├── Skeleton.stories.js │ │ │ │ ├── Skeleton.styles.js │ │ │ │ └── index.js │ │ │ ├── StaticSelect │ │ │ │ ├── StaticSelect.js │ │ │ │ ├── StaticSelect.stories.js │ │ │ │ ├── StaticSelect.styles.js │ │ │ │ └── index.js │ │ │ ├── Throbber │ │ │ │ ├── Throbber.js │ │ │ │ ├── Throbber.stories.js │ │ │ │ ├── Throbber.styles.js │ │ │ │ └── index.js │ │ │ ├── ToastBar │ │ │ │ ├── ToastBar.js │ │ │ │ ├── ToastBar.stories.js │ │ │ │ ├── ToastBar.styles.js │ │ │ │ ├── ToastBarProvider.js │ │ │ │ ├── ToastContainer.js │ │ │ │ └── index.js │ │ │ ├── Tooltip │ │ │ │ ├── Tooltip.js │ │ │ │ ├── Tooltip.stories.js │ │ │ │ ├── Tooltip.styles.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── context │ │ │ ├── ThemeContextProvider.js │ │ │ └── ToastContext.js │ │ ├── hooks │ │ │ ├── index.js │ │ │ ├── useComponentOverrides.js │ │ │ ├── useTheme.js │ │ │ └── useToastBarDispatch.js │ │ ├── index.js │ │ ├── lib │ │ │ ├── appendClassNames.js │ │ │ ├── color.js │ │ │ ├── index.js │ │ │ └── reactPortal.js │ │ └── theme │ │ │ └── DefaultTheme.js │ ├── tools │ │ └── icons-generator.js │ └── tsconfig.json └── ui-kit │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── CHANGELOG.md │ ├── babel.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── blocks │ │ ├── ActionsBlock.Action.js │ │ ├── ActionsBlock.js │ │ ├── ContextBlock.js │ │ ├── ContextBlockItem.js │ │ ├── DividerBlock.js │ │ ├── ImageBlock.js │ │ ├── InputBlock.js │ │ ├── PreviewBlock.js │ │ ├── SectionBlock.Fields.js │ │ ├── SectionBlock.js │ │ └── blocks.styles.js │ ├── contexts │ │ ├── SurfaceContext.js │ │ └── UiKitContext.js │ ├── elements │ │ ├── ButtonElement.js │ │ ├── ContextElement │ │ │ ├── ContextElement.js │ │ │ ├── ContextElementItem.js │ │ │ └── index.js │ │ ├── DatePickerElement.js │ │ ├── ImageElement.js │ │ ├── LinearScaleElement.js │ │ ├── MultiStaticSelectElement.js │ │ ├── OverflowElement.js │ │ ├── PlainTextInputElement.js │ │ ├── StaticSelectElement.js │ │ └── elements.styles.js │ ├── hooks │ │ ├── useSurfaceType.js │ │ └── useUiKitState.js │ ├── index.js │ ├── surfaces │ │ ├── ContextualBarSurface.js │ │ ├── ContextualBarSurfaceRenderer.js │ │ ├── FuselageSurfaceRenderer.js │ │ ├── MessageSurface.js │ │ ├── MessageSurfaceRenderer.js │ │ ├── ModalSurface.js │ │ ├── ModalSurfaceRenderer.js │ │ ├── Surface.js │ │ ├── createSurfaceRenderer.js │ │ └── index.js │ └── utils │ │ ├── UiKitComponent.js │ │ ├── extractInitialStateFromLayout.js │ │ ├── fromTextObjectToString.js │ │ ├── getInitialValue.js │ │ ├── hasElement.js │ │ └── hasElements.js │ └── tsconfig.json ├── scripts └── node-check.js └── yarn.lock /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "develop", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | /.yarn/releases/* binary 3 | /.yarn/plugins/**/* binary 4 | /.pnp.* binary linguist-generated 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Create a report to help us improve 4 | labels: bug 5 | --- 6 | 7 | ### Description: 8 | 9 | 10 | 11 | ### Steps to reproduce: 12 | 13 | 1. 14 | 2. 15 | 3. 16 | 17 | ### Expected behavior: 18 | 19 | 20 | 21 | ### Actual behavior: 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature 3 | about: Create a feature request 4 | labels: enhancement 5 | --- 6 | 7 | As an EmbeddedChat developer 8 | 9 | **I need to**: 10 | 11 | **So That**: 12 | 13 | **Acceptance Criteria** 14 | 15 | - [ ] TODO 1 16 | - [ ] TODO 2 17 | - [ ] TODO 3 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Brief Title 2 | 3 | ## Acceptance Criteria fulfillment 4 | 5 | - [ ] Task 1 6 | - [ ] Task 2 7 | - [ ] Task 3 8 | 9 | Fixes # (issue) 10 | 11 | ## Video/Screenshots 12 | 13 | ## PR Test Details 14 | 15 | **Note**: The PR will be ready for live testing at https://rocketchat.github.io/EmbeddedChat/pulls/pr- after approval. Contributors are requested to replace `` with the actual PR number. 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .parcel-cache 3 | 4 | 5 | # yarn 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/sdks 12 | !.yarn/versions 13 | 14 | .babelrc 15 | 16 | .vscode 17 | 18 | .gitpod.yml 19 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npm run lint 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.19.0 -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-berry.cjs 4 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "useWorkspaces": true, 4 | "version": "0.0.0", 5 | "packages": [ 6 | "packages/*", 7 | "!packages/docs" 8 | ], 9 | "npmClient": "yarn" 10 | } 11 | -------------------------------------------------------------------------------- /packages/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log -------------------------------------------------------------------------------- /packages/api/src/cloneArray.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Deep Cloning upto 2 levels 3 | * @param {*} array 4 | * @returns 5 | */ 6 | const cloneArray = (array: any[]) => { 7 | const newArray = [...array].map((item) => 8 | typeof item === "object" ? { ...item } : item 9 | ); 10 | return newArray; 11 | }; 12 | export default cloneArray; 13 | -------------------------------------------------------------------------------- /packages/api/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EmbeddedChatApi } from "./EmbeddedChatApi"; 2 | -------------------------------------------------------------------------------- /packages/api/src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const ROCKETCHAT_APP_ID = "4c977b2e-eda2-4627-8bfe-2d0358304a79"; 2 | -------------------------------------------------------------------------------- /packages/auth/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log -------------------------------------------------------------------------------- /packages/auth/src/IRocketChatAuthOptions.ts: -------------------------------------------------------------------------------- 1 | export interface IRocketChatAuthOptions { 2 | host: string; 3 | saveToken: (token: string) => Promise; 4 | getToken: () => Promise; 5 | deleteToken: () => Promise; 6 | } 7 | -------------------------------------------------------------------------------- /packages/auth/src/auth.ts: -------------------------------------------------------------------------------- 1 | import { IRocketChatAuthOptions } from "./IRocketChatAuthOptions"; 2 | import RocketChatAuth from "./RocketChatAuth"; 3 | 4 | const rocketChatAuth = ({ 5 | host, 6 | saveToken, 7 | getToken, 8 | deleteToken, 9 | }: IRocketChatAuthOptions) => { 10 | return new RocketChatAuth({ 11 | host, 12 | saveToken, 13 | getToken, 14 | deleteToken, 15 | }); 16 | }; 17 | 18 | export { rocketChatAuth }; 19 | -------------------------------------------------------------------------------- /packages/auth/src/getAuthorizationUrl.ts: -------------------------------------------------------------------------------- 1 | const getAuthorizationUrl = (oauthService: any) => { 2 | if (oauthService.authorizePath?.startsWith("http")) { 3 | return oauthService.authorizePath; 4 | } 5 | if (oauthService.serverURL) { 6 | return new URL( 7 | oauthService.authorizePath, 8 | oauthService.serverURL 9 | ).toString(); 10 | } else { 11 | return oauthService.authorizePath; 12 | } 13 | }; 14 | 15 | export default getAuthorizationUrl; 16 | -------------------------------------------------------------------------------- /packages/auth/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./auth"; 2 | export { default as RocketChatAuth } from "./RocketChatAuth"; 3 | export * from "./IRocketChatAuthOptions"; 4 | export { ApiError } from "./Api"; 5 | -------------------------------------------------------------------------------- /packages/auth/src/loginWithOAuthServiceToken.ts: -------------------------------------------------------------------------------- 1 | import { Api } from "./Api"; 2 | 3 | const loginWithOAuthServiceToken = async ( 4 | config: { 5 | api: Api; 6 | }, 7 | credentials: { 8 | service: string; 9 | access_token: string; 10 | [key: string]: string; 11 | } 12 | ) => { 13 | const response = await config.api.post("/api/v1/login", credentials); 14 | return response.data; 15 | }; 16 | 17 | export default loginWithOAuthServiceToken; 18 | -------------------------------------------------------------------------------- /packages/auth/src/loginWithPassword.ts: -------------------------------------------------------------------------------- 1 | import { Api } from "./Api"; 2 | 3 | const loginWithPassword = async ( 4 | config: { 5 | api: Api; 6 | }, 7 | { 8 | user, 9 | password, 10 | code, 11 | }: { 12 | user: string; 13 | password: string; 14 | code?: string | number; 15 | } 16 | ) => { 17 | const response = await config.api.post("/api/v1/login", { 18 | user, 19 | password, 20 | code, 21 | }); 22 | return response.data; 23 | }; 24 | 25 | export default loginWithPassword; 26 | -------------------------------------------------------------------------------- /packages/auth/src/loginWithResumeToken.ts: -------------------------------------------------------------------------------- 1 | import { Api } from "./Api"; 2 | 3 | const loginWithResumeToken = async ( 4 | config: { 5 | api: Api; 6 | }, 7 | credentials: { 8 | resume: string; 9 | } 10 | ) => { 11 | const response = await config.api.post("/api/v1/login", credentials); 12 | return response.data; 13 | }; 14 | 15 | export default loginWithResumeToken; 16 | -------------------------------------------------------------------------------- /packages/auth/src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const ROCKETCHAT_APP_ID = "4c977b2e-eda2-4627-8bfe-2d0358304a79"; 2 | -------------------------------------------------------------------------------- /packages/auth/src/utils/getRCAppBaseURL.ts: -------------------------------------------------------------------------------- 1 | import { ROCKETCHAT_APP_ID } from "./constants"; 2 | 3 | export const getRCAppBaseURL = (host: string) => { 4 | const url = new URL(`api/apps/public/${ROCKETCHAT_APP_ID}`, host); 5 | return url.toString(); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/auth/src/utils/getRCAppInfo.ts: -------------------------------------------------------------------------------- 1 | import { getRCAppBaseURL } from "./getRCAppBaseURL"; 2 | 3 | export const getRCAppInfo = async (host: string) => { 4 | const rcAppBaseUrl = getRCAppBaseURL(host); 5 | const infoUrl = rcAppBaseUrl + "/info"; 6 | const response = await fetch(infoUrl.toString()); 7 | if (!response.ok) { 8 | return null; 9 | } 10 | const info = await response.json(); 11 | return info; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/auth/src/utils/getRCAuthorizeURL.ts: -------------------------------------------------------------------------------- 1 | export const getRCAuthorizeURL = ( 2 | host: string, 3 | redirectUri: string, 4 | clientId: string 5 | ) => { 6 | const url = new URL(`oauth/authorize`, host); 7 | url.searchParams.set("response_type", "code"); 8 | url.searchParams.set("client_id", clientId); 9 | url.searchParams.set("redirect_uri", redirectUri); 10 | url.searchParams.set("state", encodeURIComponent(window.location.origin)); 11 | return url.toString(); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/auth/src/utils/tokenRequestHandler.ts: -------------------------------------------------------------------------------- 1 | import { ApiError } from "../Api"; 2 | 3 | export async function tokenRequestHandler( 4 | method: string = "GET", 5 | url: string, 6 | token?: string 7 | ): Promise { 8 | try { 9 | const headers: { [key: string]: string } = { 10 | "Content-Type": "application/json", 11 | }; 12 | 13 | const response = await fetch(url, { 14 | method, 15 | headers, 16 | credentials: "include", 17 | ...(token ? { body: JSON.stringify({ token }) } : {}), 18 | }); 19 | 20 | if (!response.ok) { 21 | throw new ApiError(response, "Failed Api Request for " + url); 22 | } 23 | 24 | return response.json(); 25 | } catch (error) { 26 | console.error(`Error with ${method} request:`, error); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | .yarn/* 22 | -------------------------------------------------------------------------------- /packages/docs/README.md: -------------------------------------------------------------------------------- 1 | # EmbeddedChat Documentation 2 | 3 | This is the official documentation website of EmbeddedChat 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn install 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn dev 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | -------------------------------------------------------------------------------- /packages/docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/docs/src/assets/EC-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/src/assets/EC-Logo.png -------------------------------------------------------------------------------- /packages/docs/src/assets/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/src/assets/app.png -------------------------------------------------------------------------------- /packages/docs/src/assets/easy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/src/assets/easy.png -------------------------------------------------------------------------------- /packages/docs/src/assets/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/src/assets/pencil.png -------------------------------------------------------------------------------- /packages/docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /packages/docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /packages/docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/static/.nojekyll -------------------------------------------------------------------------------- /packages/docs/static/img/EC-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/static/img/EC-Logo.png -------------------------------------------------------------------------------- /packages/docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /packages/e2e-react/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/e2e-react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | /test-results/ 26 | /playwright-report/ 27 | /blob-report/ 28 | /playwright/.cache/ 29 | -------------------------------------------------------------------------------- /packages/e2e-react/README.md: -------------------------------------------------------------------------------- 1 | # E2E EmbeddedChat setup 2 | -------------------------------------------------------------------------------- /packages/e2e-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/e2e-react/src/App.tsx: -------------------------------------------------------------------------------- 1 | // @ts-expect-error no types served yet 2 | import { EmbeddedChat } from "@embeddedchat/react"; 3 | 4 | function App() { 5 | return ( 6 | 10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /packages/e2e-react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | 5 | ReactDOM.createRoot(document.getElementById("root")!).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /packages/e2e-react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/e2e-react/tests/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | 3 | test("EmbeddedChat should render", async ({ page }) => { 4 | await page.goto("/"); 5 | await expect(page.locator(".ec-embedded-chat")).toBeVisible(); 6 | }); 7 | 8 | test("EmbeddedChat has a title", async ({ page }) => { 9 | await page.goto("/"); 10 | await expect(page.locator(".ec-chat-header--channelName")).toHaveText( 11 | "Login to chat" 12 | ); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/e2e-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /packages/e2e-react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/e2e-react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | define: { 8 | 'process.env': {} 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /packages/htmlembed/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/htmlembed/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /packages/htmlembed/postbuild.cjs: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const files = fs.readdirSync(path.join(__dirname, 'public'), { recursive: true }); 5 | 6 | files.forEach((file) => { 7 | const inPath = path.join(__dirname, 'public', file); 8 | const outPath = path.join(__dirname, 'dist', file); 9 | console.log("Copy: ",inPath); 10 | if (fs.statSync(inPath).isDirectory()) { 11 | if (!fs.existsSync(outPath)) { 12 | fs.mkdirSync(outPath, {recursive: true}); 13 | } 14 | } else { 15 | fs.writeFileSync( 16 | outPath, 17 | fs.readFileSync(inPath) 18 | ); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /packages/htmlembed/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from '@rollup/plugin-babel'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import { terser } from 'rollup-plugin-terser'; 5 | 6 | export default { 7 | input: 'src/index.js', 8 | output: { 9 | file: 'dist/embeddedchat.js', 10 | format: 'umd', 11 | name: 'EmbeddedChat', 12 | }, 13 | plugins: [ 14 | resolve(), 15 | commonjs(), 16 | babel({ 17 | babelHelpers: 'bundled', 18 | presets: ['@babel/preset-react'], 19 | }), 20 | terser(), 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /packages/htmlembed/src/index.js: -------------------------------------------------------------------------------- 1 | import EmbeddedChat from "./EmbeddedChat"; 2 | 3 | export default EmbeddedChat; 4 | -------------------------------------------------------------------------------- /packages/htmlembed/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js' 4 | import path from 'path'; 5 | 6 | export default defineConfig({ 7 | plugins: [react(), cssInjectedByJsPlugin()], 8 | build: { 9 | minify: true, 10 | cssCodeSplit: false, 11 | lib: { 12 | entry: path.resolve(__dirname, 'src/index.js'), 13 | name: 'EmbeddedChat', 14 | formats: ['umd'], 15 | fileName: () => 'embeddedchat.js', 16 | } 17 | }, 18 | define: { 'process.env.NODE_ENV': '"production"', 'process.env': '{}' } 19 | }) -------------------------------------------------------------------------------- /packages/layout_editor/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'plugin:react/recommended', 6 | 'plugin:react-hooks/recommended', 7 | 'prettier', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 11 | settings: { react: { version: '18.2' } }, 12 | plugins: ['react-refresh'], 13 | rules: { 14 | 'react/jsx-no-target-blank': 'off', 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | 'react/prop-types': 'off', 20 | 'react/no-unknown-property': ['error', { ignore: ['css'] }], 21 | 'no-undef': 'error', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/layout_editor/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/layout_editor/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } -------------------------------------------------------------------------------- /packages/layout_editor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # layout_editor 2 | 3 | ## 0.1.2 4 | 5 | ### Patch Changes 6 | 7 | - Version Bump 8 | - Updated dependencies 9 | - @embeddedchat/ui-elements@0.1.2 10 | - @embeddedchat/markups@0.1.2 11 | 12 | ## 0.1.1 13 | 14 | ### Patch Changes 15 | 16 | - Version Bump 17 | - Updated dependencies 18 | - @embeddedchat/ui-elements@0.1.1 19 | - @embeddedchat/markups@0.1.1 20 | 21 | ## 0.1.0 22 | 23 | ### Minor Changes 24 | 25 | - 5f604c59: - Introduced 'layout-editor' for real-time drag-and-drop layout customization and theme generation in EmbeddedChat. 26 | 27 | See [#607](https://github.com/RocketChat/EmbeddedChat/pull/607). 28 | 29 | ### Patch Changes 30 | 31 | - Updated dependencies [5f604c59] 32 | - Updated dependencies [5f604c59] 33 | - Updated dependencies [5f604c59] 34 | - @embeddedchat/ui-elements@0.1.0 35 | - @embeddedchat/markups@0.1.0 36 | -------------------------------------------------------------------------------- /packages/layout_editor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | EmbeddedChat Layout Designer 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/layout_editor/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LayoutEditor from './views/LayoutEditor'; 3 | import { ThemeProvider, ToastBarProvider } from '@embeddedchat/ui-elements'; 4 | import DefaultTheme from './theme/DefaultTheme'; 5 | 6 | const App = () => { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /packages/layout_editor/src/components/SortableMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default as Menu } from './Menu'; 2 | -------------------------------------------------------------------------------- /packages/layout_editor/src/components/SurfaceMenu/SurfaceMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SurfaceItem from './SurfaceItem'; 3 | import { SortableContext } from '@dnd-kit/sortable'; 4 | 5 | const SurfaceMenu = ({ options, ...props }) => { 6 | return ( 7 | 8 | {options?.map((item, idx) => ( 9 | 10 | ))} 11 | 12 | ); 13 | }; 14 | 15 | export default SurfaceMenu; 16 | -------------------------------------------------------------------------------- /packages/layout_editor/src/data/members.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "a9bAtEYzGoTHvtxws", 4 | "status": "away", 5 | "_updatedAt": "2024-07-15T18:47:50.807Z", 6 | "name": "Sidharth Mohanty", 7 | "username": "sidmohanty11" 8 | }, 9 | { 10 | "_id": "c9bAtEYzGoTHvtxws", 11 | "status": "online", 12 | "_updatedAt": "2024-07-15T18:47:50.807Z", 13 | "name": "Zishan Ahmad", 14 | "username": "spiral_memory" 15 | } 16 | ] -------------------------------------------------------------------------------- /packages/layout_editor/src/hooks/useDisplayNameColor.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@embeddedchat/ui-elements'; 2 | 3 | const simpleHash = (str) => { 4 | if (!str) return 0; 5 | let hash = 0; 6 | for (let i = 0; i < str.length; i += 1) { 7 | const char = str.charCodeAt(i); 8 | hash = (hash << 5) - hash + char; 9 | } 10 | return hash; 11 | }; 12 | 13 | const useDisplayNameColor = () => { 14 | const { theme, mode } = useTheme(); 15 | 16 | const getDisplayNameColor = (username) => { 17 | const hash = simpleHash(username); 18 | const { saturation, luminance } = theme.contrastParams[mode]; 19 | const hue = Math.abs(hash) % 360; 20 | return `hsl(${hue}, ${saturation}%, ${luminance}%)`; 21 | }; 22 | 23 | return getDisplayNameColor; 24 | }; 25 | 26 | export default useDisplayNameColor; 27 | -------------------------------------------------------------------------------- /packages/layout_editor/src/lib/isMessageLastSequential.js: -------------------------------------------------------------------------------- 1 | import isMessageSequential from './isMessageSequential'; 2 | 3 | const isMessageLastSequential = (current, next) => { 4 | if (!next) { 5 | return true; 6 | } 7 | 8 | if (current.u._id !== next.u._id) { 9 | return true; 10 | } 11 | 12 | if (!isMessageSequential(next, current, 300)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | 19 | export default isMessageLastSequential; 20 | -------------------------------------------------------------------------------- /packages/layout_editor/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.jsx'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root')).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /packages/layout_editor/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.jsx'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root')!).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /packages/layout_editor/src/store/chatInputItemsStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useChatInputItemsStore = create((set) => ({ 4 | surfaceItems: ['emoji', 'formatter', 'link', 'audio', 'video', 'file'], 5 | formatters: ['bold', 'italic', 'strike', 'code', 'multiline'], 6 | setSurfaceItems: (items) => set({ surfaceItems: items }), 7 | setFormatters: (items) => set({ formatters: items }), 8 | })); 9 | 10 | export default useChatInputItemsStore; 11 | -------------------------------------------------------------------------------- /packages/layout_editor/src/store/headerItemsStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useHeaderItemsStore = create((set) => ({ 4 | surfaceItems: ['minmax', 'close', 'thread', 'mentions', 'starred', 'pinned'], 5 | menuItems: ['files', 'members', 'search', 'rInfo', 'logout'], 6 | setSurfaceItems: (items) => set({ surfaceItems: items }), 7 | setMenuItems: (items) => set({ menuItems: items }), 8 | })); 9 | 10 | export default useHeaderItemsStore; 11 | -------------------------------------------------------------------------------- /packages/layout_editor/src/store/layoutStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useLayoutStore = create((set) => ({ 4 | themeLabOpen: false, 5 | setThemeLabOpen: (themeLabOpen) => { 6 | set({ themeLabOpen }); 7 | }, 8 | 9 | messageView: 'flat', 10 | setMessageView: (messageView) => { 11 | set({ messageView }); 12 | }, 13 | 14 | displayName: 'normal', 15 | setDisplayName: (displayName) => { 16 | set({ displayName }); 17 | }, 18 | 19 | sidebarWidth: '350px', 20 | setSidebarWidth: (sidebarWidth) => { 21 | set({ sidebarWidth }); 22 | }, 23 | })); 24 | 25 | export default useLayoutStore; 26 | -------------------------------------------------------------------------------- /packages/layout_editor/src/store/messageItemsStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useMessageItemsStore = create((set) => ({ 4 | surfaceItems: [ 5 | 'reaction', 6 | 'reply', 7 | 'quote', 8 | 'star', 9 | 'pin', 10 | 'edit', 11 | 'delete', 12 | 'report', 13 | ], 14 | 15 | menuItems: [], 16 | setSurfaceItems: (items) => set({ surfaceItems: items }), 17 | setMenuItems: (items) => set({ menuItems: items }), 18 | })); 19 | 20 | export default useMessageItemsStore; 21 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/ChatLayout/ChatLayout.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const getChatLayoutStyles = () => { 4 | const styles = { 5 | layout: css` 6 | height: 100%; 7 | display: flex; 8 | overflow: hidden; 9 | `, 10 | 11 | chatMain: css` 12 | display: flex; 13 | flex-direction: column; 14 | height: 100%; 15 | flex: 1; 16 | position: relative; 17 | `, 18 | }; 19 | 20 | return styles; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Chatbody/ChatBody.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Box } from "@embeddedchat/ui-elements"; 4 | import { getChatbodyStyles } from "./ChatBody.styles"; 5 | import MessageList from "../MessageList/MessageList"; 6 | 7 | const ChatBody = () => { 8 | const styles = getChatbodyStyles(); 9 | 10 | return ( 11 | 12 | 13 | 14 | ); 15 | }; 16 | 17 | export default ChatBody; 18 | 19 | ChatBody.propTypes = { 20 | anonymousMode: PropTypes.bool, 21 | showRoles: PropTypes.bool, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Chatbody/ChatBody.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from "@emotion/react"; 2 | 3 | export const getChatbodyStyles = () => { 4 | const styles = { 5 | chatbodyContainer: css` 6 | flex: 1; 7 | word-break: break-all; 8 | overflow: auto; 9 | overflow-x: hidden; 10 | display: flex; 11 | flex-direction: column-reverse; 12 | max-height: 600px; 13 | position: relative; 14 | padding-top: 70px; 15 | margin-top: 0.25rem; 16 | `, 17 | }; 18 | 19 | return styles; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/LayoutEditor.style.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const getEditorStyles = (theme) => { 4 | const styles = { 5 | embeddedchat: css` 6 | width: 75%; 7 | position: relative; 8 | background: ${theme.colors.background}; 9 | color: ${theme.colors.foreground}; 10 | display: flex; 11 | flex: 1; 12 | flex-direction: column; 13 | border: ${`1.5px solid ${theme.colors.border}`}; 14 | border-radius: ${theme.radius}; 15 | overflow: hidden; 16 | `, 17 | 18 | layoutEditor: css` 19 | background: ${theme.colors.background}; 20 | color: ${theme.colors.foreground}; 21 | height: 100vh; 22 | display: flex; 23 | gap: 0.25rem; 24 | `, 25 | }; 26 | 27 | return styles; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Markdown/Markdown.jsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Box } from "@embeddedchat/ui-elements"; 4 | import { Markup, MarkupInteractionContext } from "@embeddedchat/markups"; 5 | 6 | const Markdown = ({ body }) => { 7 | const username = "spiral_memory"; 8 | 9 | const value = useMemo(() => { 10 | const members = []; 11 | 12 | return { members, username }; 13 | }, [username]); 14 | 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | }; 23 | 24 | Markdown.propTypes = { 25 | body: PropTypes.any, 26 | isReaction: PropTypes.bool, 27 | }; 28 | 29 | export default Markdown; 30 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Markdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as Markdown } from './Markdown'; 2 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/BubbleVariant/useBubbleStyles.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@embeddedchat/ui-elements"; 2 | import { bubbleStyles, bubbleStylesMe } from "./Bubble.styles"; 3 | 4 | const useBubbleStyles = (isMe = false) => { 5 | const theme = useTheme(); 6 | const styles = bubbleStyles(theme); 7 | const meStyles = bubbleStylesMe(theme); 8 | 9 | const mergedStyles = {}; 10 | 11 | Object.keys(styles).forEach((key) => { 12 | mergedStyles[key] = [styles[key], isMe && meStyles[`${key}Me`]].filter( 13 | Boolean 14 | ); 15 | }); 16 | 17 | return mergedStyles; 18 | }; 19 | 20 | export default useBubbleStyles; 21 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/MessageAvatarContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Icon, useTheme } from "@embeddedchat/ui-elements"; 3 | import { getMessageAvatarContainerStyles } from "./Message.styles"; 4 | 5 | const MessageAvatarContainer = ({ message, sequential }) => { 6 | const styles = getMessageAvatarContainerStyles(useTheme()); 7 | 8 | return ( 9 | 10 | {!sequential ? ( 11 | 16 | ) : null} 17 | 18 | ); 19 | }; 20 | 21 | export default MessageAvatarContainer; 22 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/MessageBody.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box } from "@embeddedchat/ui-elements"; 3 | import { getMessageBodyStyles } from "./Message.styles"; 4 | 5 | export const MessageBody = ({ 6 | children, 7 | variantStyles = {}, 8 | isText = true, 9 | sequential = false, 10 | lastSequential = false, 11 | ...props 12 | }) => { 13 | const styles = getMessageBodyStyles(); 14 | const messageBodyStyles = 15 | (isText ? variantStyles.messageBody : variantStyles.attachmentBody) || 16 | styles.messageBody; 17 | 18 | return ( 19 | 28 | {children} 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/MessageBodyContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box } from "@embeddedchat/ui-elements"; 3 | 4 | import { getMessageBodyContainerStyles } from "./Message.styles"; 5 | 6 | const MessageBodyContainer = ({ children, variantStyles = {} }) => { 7 | const styles = getMessageBodyContainerStyles(); 8 | 9 | return ( 10 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export default MessageBodyContainer; 20 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/MessageDivider.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, useTheme } from "@embeddedchat/ui-elements"; 3 | 4 | import { getMessageDividerStyles } from "./Message.styles"; 5 | 6 | export const MessageDivider = ({ children, ...props }) => { 7 | const styles = getMessageDividerStyles(useTheme()); 8 | return ( 9 | 15 | {children && ( 16 | <> 17 | 18 | 22 | {children} 23 | 24 | 25 | )} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/layout_editor/src/views/Message/index.js: -------------------------------------------------------------------------------- 1 | export { default as Message } from './Message'; 2 | -------------------------------------------------------------------------------- /packages/layout_editor/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /packages/layout_editor/tests/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test('EmbeddedChat should render', async ({ page }) => { 4 | await page.goto('/'); 5 | await expect(page.locator('.ec-embedded-chat')).toBeVisible(); 6 | }); 7 | 8 | test('EmbeddedChat has a title', async ({ page }) => { 9 | await page.goto('/'); 10 | await expect(page.locator('.ec-chat-header--channelName')).toHaveText( 11 | 'Login to chat' 12 | ); 13 | }); 14 | 15 | test('EmbeddedChat has messages', async ({ page }) => { 16 | await page.goto('/'); 17 | 18 | await page.waitForSelector('.ec-message'); 19 | expect(await page.locator('.ec-message').count()).toBeGreaterThan(0); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/layout_editor/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [ 7 | react({ 8 | jsxImportSource: '@emotion/react', 9 | babel: { 10 | plugins: ['@emotion/babel-plugin'], 11 | }, 12 | }), 13 | ], 14 | base: process.env.LAYOUT_EDITOR_BASE_URL || '/EmbeddedChat/layout_editor', 15 | }); 16 | -------------------------------------------------------------------------------- /packages/markups/.eslintignore: -------------------------------------------------------------------------------- 1 | rollup.config.js 2 | *.test.js 3 | dist/ -------------------------------------------------------------------------------- /packages/markups/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log -------------------------------------------------------------------------------- /packages/markups/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/markups/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @embeddedchat/markups 2 | 3 | ## 0.1.2 4 | 5 | ### Patch Changes 6 | 7 | - Version Bump 8 | - Updated dependencies 9 | - @embeddedchat/ui-elements@0.1.2 10 | 11 | ## 0.1.1 12 | 13 | ### Patch Changes 14 | 15 | - Version Bump 16 | - Updated dependencies 17 | - @embeddedchat/ui-elements@0.1.1 18 | 19 | ## 0.1.0 20 | 21 | ### Minor Changes 22 | 23 | - 5f604c59: - Separated components, markups, and ui-kit into individual monorepos. 24 | 25 | See [#604](https://github.com/RocketChat/EmbeddedChat/pull/604). 26 | 27 | ### Patch Changes 28 | 29 | - 5f604c59: - Optimized package size for bundling. 30 | 31 | See [#606](https://github.com/RocketChat/EmbeddedChat/pull/606). 32 | 33 | - Updated dependencies [5f604c59] 34 | - Updated dependencies [5f604c59] 35 | - Updated dependencies [5f604c59] 36 | - @embeddedchat/ui-elements@0.1.0 37 | -------------------------------------------------------------------------------- /packages/markups/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: false, 7 | bugfixes: true, 8 | targets: { browsers: '> 0.25%, ie 11, not op_mini all, not dead' }, 9 | }, 10 | ], 11 | '@babel/preset-react', 12 | '@emotion/babel-preset-css-prop', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/markups/src/MarkupInteractionContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const MarkupInteractionContext = createContext(); 4 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/HeadingBlock.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import PlainSpan from '../elements/PlainSpan'; 4 | 5 | const HeadingBlock = ({ contents, level = 1 }) => { 6 | const HeadingTag = `h${level}`; 7 | return ( 8 | 9 | {contents.map((content, index) => ( 10 | 11 | ))} 12 | 13 | ); 14 | }; 15 | 16 | export default HeadingBlock; 17 | 18 | HeadingBlock.propTypes = { 19 | contents: PropTypes.arrayOf(PropTypes.object), 20 | level: PropTypes.number, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/OrderedListBlock.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import InlineElements from '../elements/InlineElements'; 4 | 5 | const OrderedListBlock = ({ items }) => ( 6 |
    7 | {items.map((item, index) => ( 8 |
  1. 9 | 10 |
  2. 11 | ))} 12 |
13 | ); 14 | 15 | export default OrderedListBlock; 16 | 17 | OrderedListBlock.propTypes = { 18 | items: PropTypes.arrayOf(PropTypes.shape), 19 | }; 20 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/ParagraphBlock.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { css } from '@emotion/react'; 4 | import InlineElements from '../elements/InlineElements'; 5 | 6 | const ParagraphBlock = ({ contents }) => ( 7 |

12 | 13 |

14 | ); 15 | 16 | export default ParagraphBlock; 17 | 18 | ParagraphBlock.propTypes = { 19 | contents: PropTypes.any, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/QuoteBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ParagraphBlock from './ParagraphBlock'; 4 | 5 | const QuoteBlock = ({ contents }) => ( 6 |
7 | {contents.map((paragraph, index) => ( 8 | 9 | ))} 10 |
11 | ); 12 | 13 | export default QuoteBlock; 14 | 15 | QuoteBlock.propTypes = { 16 | contents: PropTypes.arrayOf(PropTypes.shape), 17 | }; 18 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/TaskListBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { css } from '@emotion/react'; 4 | import { CheckBox } from '@embeddedchat/ui-elements'; 5 | import InlineElements from '../elements/InlineElements'; 6 | import { TaskListBlockStyles as styles } from './blocks.styles'; 7 | 8 | const TaskListBlock = ({ tasks }) => ( 9 |
    10 | {tasks.map((item, index) => ( 11 |
  • 12 | 18 | 19 |
  • 20 | ))} 21 |
22 | ); 23 | 24 | export default TaskListBlock; 25 | 26 | TaskListBlock.propTypes = { 27 | tasks: PropTypes.arrayOf(PropTypes.object), 28 | }; 29 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/UnOrderedListBlock.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import InlineElements from '../elements/InlineElements'; 4 | 5 | const UnOrderedListBlock = ({ items }) => ( 6 |
    7 | {items.map((item, index) => ( 8 |
  • 9 | 10 |
  • 11 | ))} 12 |
13 | ); 14 | 15 | export default UnOrderedListBlock; 16 | 17 | UnOrderedListBlock.propTypes = { 18 | items: PropTypes.arrayOf(PropTypes.shape), 19 | }; 20 | -------------------------------------------------------------------------------- /packages/markups/src/blocks/blocks.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const TaskListBlockStyles = { 4 | li: css` 5 | display: flex; 6 | flex-direction: row; 7 | align-items: flex-start; 8 | justify-content: flex-start; 9 | gap: 0.5em; 10 | `, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/markups/src/elements/BigEmoji.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Emoji from './Emoji'; 4 | 5 | const BigEmoji = ({ contents }) => ( 6 | <> 7 | {contents.map((content, index) => ( 8 | 9 | ))} 10 | 11 | ); 12 | 13 | export default BigEmoji; 14 | 15 | BigEmoji.propTypes = { 16 | contents: PropTypes.any, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/markups/src/elements/CodeElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import PlainSpan from './PlainSpan'; 4 | import { InlineElementsStyles } from './elements.styles'; 5 | 6 | const CodeElement = ({ contents }) => { 7 | const styles = InlineElementsStyles(); 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default CodeElement; 16 | 17 | CodeElement.propTypes = { 18 | contents: PropTypes.any, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/markups/src/elements/ColorElement.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | import { ColorElementStyles as styles } from './elements.styles'; 5 | 6 | const ColorElement = ({ r, g, b, a }) => ( 7 | 8 | 9 | rgba({r}, {g}, {b}, {(a / 255) * 100}%) 10 | 11 | ); 12 | 13 | export default ColorElement; 14 | 15 | ColorElement.propTypes = { 16 | r: PropTypes.number, 17 | g: PropTypes.number, 18 | b: PropTypes.number, 19 | a: PropTypes.number, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/markups/src/elements/ItalicSpan.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import PlainSpan from './PlainSpan'; 4 | import BoldSpan from './BoldSpan'; 5 | import StrikeSpan from './StrikeSpan'; 6 | 7 | const ItalicSpan = ({ contents }) => ( 8 | 9 | {contents.map((content, index) => { 10 | switch (content.type) { 11 | case 'PLAIN_TEXT': 12 | return ; 13 | 14 | case 'STRIKE': 15 | return ; 16 | 17 | case 'BOLD': 18 | return ; 19 | 20 | default: 21 | return null; 22 | } 23 | })} 24 | 25 | ); 26 | 27 | export default ItalicSpan; 28 | 29 | ItalicSpan.propTypes = { 30 | contents: PropTypes.any, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/markups/src/elements/PlainSpan.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const PlainSpan = ({ contents }) => <>{contents}; 5 | 6 | export default PlainSpan; 7 | 8 | PlainSpan.propTypes = { 9 | contents: PropTypes.string, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/markups/src/elements/StrikeSpan.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import PlainSpan from './PlainSpan'; 4 | import BoldSpan from './BoldSpan'; 5 | import ItalicSpan from './ItalicSpan'; 6 | 7 | const StrikeSpan = ({ contents }) => ( 8 | 9 | {contents.map((content, index) => { 10 | switch (content.type) { 11 | case 'PLAIN_TEXT': 12 | return ; 13 | 14 | case 'ITALIC': 15 | return ; 16 | 17 | case 'BOLD': 18 | return ; 19 | 20 | default: 21 | return null; 22 | } 23 | })} 24 | 25 | ); 26 | 27 | export default StrikeSpan; 28 | 29 | StrikeSpan.propTypes = { 30 | contents: PropTypes.any, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/markups/src/index.js: -------------------------------------------------------------------------------- 1 | export { MarkupInteractionContext } from './MarkupInteractionContext'; 2 | 3 | export { default as Markup } from './Markup'; 4 | -------------------------------------------------------------------------------- /packages/markups/src/mentions/ChannelMention.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ChannelMention = ({ mention }) => <>#{mention}; 5 | 6 | export default ChannelMention; 7 | 8 | ChannelMention.propTypes = { 9 | mention: PropTypes.string, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/markups/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "./types", 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "rootDir": "src", 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "include": ["src/**/*"], 17 | "exclude": ["node_modules", "dist", "stories", "**/*.stories.js"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/rc-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | indent_style = space 9 | indent_size = 4 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /packages/rc-app/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore modules pulled in from npm 2 | node_modules/ 3 | 4 | # rc-apps package output 5 | dist/ 6 | 7 | # JetBrains IDEs 8 | out/ 9 | .idea/ 10 | .idea_modules/ 11 | 12 | # macOS 13 | .DS_Store 14 | .AppleDouble 15 | .LSOverride 16 | ._* 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | -------------------------------------------------------------------------------- /packages/rc-app/.rcappsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "url": "http://localhost:3000", 3 | "username": "", 4 | "password": "", 5 | "ignoredFiles": [ 6 | "**/README.md", 7 | "**/package-lock.json", 8 | "**/package.json", 9 | "**/tslint.json", 10 | "**/tsconfig.json", 11 | "**/*.js", 12 | "**/*.js.map", 13 | "**/*.d.ts", 14 | "**/*.spec.ts", 15 | "**/*.test.ts", 16 | "**/dist/**", 17 | "**/.*" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/rc-app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "4c977b2e-eda2-4627-8bfe-2d0358304a79", 3 | "version": "0.0.1", 4 | "requiredApiVersion": "^1.36.0", 5 | "iconFile": "icon.png", 6 | "author": { 7 | "name": "ec innovators", 8 | "homepage": "https://github.com/RocketChat/EmbeddedChat/graphs/contributors", 9 | "support": "https://github.com/RocketChat/EmbeddedChat" 10 | }, 11 | "name": "EmbeddedChat", 12 | "nameSlug": "embeddedchat", 13 | "classFile": "EmbeddedChatApp.ts", 14 | "description": "Easily manage EmbeddedChat configs and OAuth with Rocket.Chat.", 15 | "implements": [], 16 | "permissions": [ 17 | { 18 | "name": "server-setting.read" 19 | }, 20 | { 21 | "name": "api" 22 | }, 23 | { 24 | "name": "networking" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /packages/rc-app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/rc-app/icon.png -------------------------------------------------------------------------------- /packages/rc-app/lib/extractTokenCookie.ts: -------------------------------------------------------------------------------- 1 | export const extractTokenCookie = ( 2 | cookieHeader: string 3 | ): string | undefined => { 4 | const cookies = cookieHeader.split(";").map((cookie) => cookie.trim()); 5 | let token: string | undefined; 6 | 7 | for (const cookie of cookies) { 8 | const [key, value] = cookie.split("="); 9 | 10 | if (key.trim() === "ec-token") { 11 | token = decodeURIComponent(value); 12 | break; 13 | } 14 | } 15 | 16 | return token; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/rc-app/lib/getAllowedOrigins.ts: -------------------------------------------------------------------------------- 1 | export const getAllowedOrigins = async (read) => { 2 | const allowedOrigins = await read 3 | .getEnvironmentReader() 4 | .getSettings() 5 | .getValueById("allowed-origins"); 6 | if (!allowedOrigins) { 7 | return []; 8 | } 9 | const origins = allowedOrigins 10 | .split(",") 11 | .filter((domain) => !!domain.trim()); 12 | return origins; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/rc-app/lib/getCallbackUrl.ts: -------------------------------------------------------------------------------- 1 | import { IApp } from "@rocket.chat/apps-engine/definition/IApp"; 2 | import { URL } from "url"; 3 | 4 | export const getCallbackUrl = async (app: IApp) => { 5 | const serverURL = await app 6 | .getAccessors() 7 | .environmentReader.getServerSettings() 8 | .getValueById("Site_Url"); 9 | const callbackEndPoint = app 10 | .getAccessors() 11 | .providedApiEndpoints.find((endpoint) => endpoint.path === "callback"); 12 | if (callbackEndPoint) { 13 | const webhookURL = new URL( 14 | callbackEndPoint.computedPath || "", 15 | serverURL 16 | ); 17 | return webhookURL.toString(); 18 | } 19 | return ""; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/rc-app/lib/getEnvironmentVariables.ts: -------------------------------------------------------------------------------- 1 | export const getEnvironmentValues = async ( 2 | readEnvironment: any, 3 | keys: { [key: string]: string } 4 | ): Promise => { 5 | const promises = Object.keys(keys).map((key) => 6 | readEnvironment.getValueById(keys[key]) 7 | ); 8 | return await Promise.all(promises); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/rc-app/lib/getTokenUrl.ts: -------------------------------------------------------------------------------- 1 | import { IRead } from "@rocket.chat/apps-engine/definition/accessors"; 2 | import { URL } from "url"; 3 | 4 | export const getTokenUrl = async (read: IRead) => { 5 | const serverURL = await read 6 | .getEnvironmentReader() 7 | .getServerSettings() 8 | .getValueById("Site_Url"); 9 | const url = new URL("/oauth/token", serverURL); 10 | return url.toString(); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/rc-app/lib/isAllowedOrigin.ts: -------------------------------------------------------------------------------- 1 | import { IRead } from "@rocket.chat/apps-engine/definition/accessors"; 2 | import { getAllowedOrigins } from "./getAllowedOrigins"; 3 | 4 | export const isAllowedOrigin = async (read: IRead, origin: string) => { 5 | const allowedOrigins = await getAllowedOrigins(read); 6 | if (allowedOrigins.length === 0) { 7 | // all origins are allowed 8 | return true; 9 | } 10 | return allowedOrigins.includes(origin); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/rc-app/lib/isValidCssDimension.ts: -------------------------------------------------------------------------------- 1 | export const isValidCssDimension = (value: string): boolean => { 2 | const cssDimensionRegex = 3 | /^(auto|inherit|initial|unset|0|[+-]?(\d+|\d*\.\d+)(px|em|rem|%|vh|vw|vmin|vmax|cm|mm|in|pt|pc|ch|ex))$/; 4 | return cssDimensionRegex.test(value); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/rc-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@embeddedchat/rc-app", 3 | "version": "0.1.2", 4 | "private": true, 5 | "devDependencies": { 6 | "@rocket.chat/apps-engine": "^1.36.0", 7 | "@types/node": "14.14.6", 8 | "tslint": "^5.10.0", 9 | "typescript": "^4.0.5" 10 | }, 11 | "scripts": { 12 | "format": "prettier --write '*/**.ts'", 13 | "format:check": "prettier --check '*/**.ts'" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/rc-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "removeComments": true, 9 | "strictNullChecks": true, 10 | "noImplicitReturns": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true 13 | }, 14 | "include": [ 15 | "**/*.ts", 16 | "EmbeddedChatApp.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/rc-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": [true, "generic"], 5 | "member-access": true, 6 | "no-console": [false], 7 | "no-duplicate-variable": true, 8 | "object-literal-sort-keys": false, 9 | "quotemark": [true, "single"], 10 | "max-line-length": [true, { 11 | "limit": 160, 12 | "ignore-pattern": "^import | *export .*? {" 13 | }] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-native/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "parserOptions": { 4 | "ecmaFeatures": { 5 | "jsx": true 6 | } 7 | }, 8 | "env": { 9 | "browser": true, 10 | "react-native/react-native": true 11 | }, 12 | "plugins": ["react", "react-native"], 13 | "extends": ["eslint:recommended", "plugin:react/recommended"], 14 | "rules": { 15 | "react-native/no-unused-styles": 2, 16 | "react-native/split-platform-components": 2, 17 | "react-native/no-raw-text": 2, 18 | "no-undef": "error", 19 | "react/prop-types": "off", 20 | "no-unused-vars": "warn" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/react-native/.storybook/index.js: -------------------------------------------------------------------------------- 1 | import { getStorybookUI } from '@storybook/react-native'; 2 | 3 | import './storybook.requires'; 4 | 5 | const StorybookUIRoot = getStorybookUI({}); 6 | 7 | export default StorybookUIRoot; 8 | -------------------------------------------------------------------------------- /packages/react-native/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../components/**/*.stories.?(ts|tsx|js|jsx)"], 3 | addons: [ 4 | "@storybook/addon-ondevice-controls", 5 | "@storybook/addon-ondevice-actions", 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /packages/react-native/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | 3 | export const parameters = { 4 | controls: { 5 | matchers: { 6 | color: /(background|color)$/i, 7 | date: /Date$/, 8 | }, 9 | }, 10 | }; 11 | 12 | export const decorators = [ 13 | (Story) => ( 14 | 21 | 22 | 23 | ), 24 | ]; 25 | -------------------------------------------------------------------------------- /packages/react-native/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @embeddedchat/react-native 2 | 3 | ## 0.0.5 4 | 5 | ### Patch Changes 6 | 7 | - Version Bump 8 | - Updated dependencies 9 | - @embeddedchat/api@0.1.2 10 | 11 | ## 0.0.4 12 | 13 | ### Patch Changes 14 | 15 | - Version Bump 16 | - Updated dependencies 17 | - @embeddedchat/api@0.1.1 18 | 19 | ## 0.0.3 20 | 21 | ### Patch Changes 22 | 23 | - 5f604c59: - Restructured Auto Login, added loading screens, and cleaned up code. 24 | 25 | See [#594](https://github.com/RocketChat/EmbeddedChat/pull/594). 26 | 27 | - Updated dependencies [5f604c59] 28 | - Updated dependencies [5f604c59] 29 | - Updated dependencies [5f604c59] 30 | - @embeddedchat/api@0.1.0 31 | 32 | ## 0.0.2 33 | 34 | ### Patch Changes 35 | 36 | - Updated dependencies 37 | - @embeddedchat/api@0.0.2 38 | -------------------------------------------------------------------------------- /packages/react-native/app.config.js: -------------------------------------------------------------------------------- 1 | export default ({ config }) => ({ 2 | ...config, 3 | name: "Storybook Tutorial Template", 4 | slug: "storybook-tutorial-template", 5 | extra: { 6 | storybookEnabled: process.env.STORYBOOK_ENABLED, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/react-native/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | plugins: ['react-native-reanimated/plugin'] 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/react-native/src/assets/fonts/custom.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RocketChat/EmbeddedChat/70d714c667a05a2ebb301cc646461a95e7589ff2/packages/react-native/src/assets/fonts/custom.ttf -------------------------------------------------------------------------------- /packages/react-native/src/components/ActivityIndicator/index.js: -------------------------------------------------------------------------------- 1 | export { default as ActivityIndicator } from './ActivityIndicator'; -------------------------------------------------------------------------------- /packages/react-native/src/components/Avatar/index.js: -------------------------------------------------------------------------------- 1 | export { default as Avatar } from './Avatar'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Box/index.js: -------------------------------------------------------------------------------- 1 | export { default as Box } from './Box'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default as Button } from './Button'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/RightButtons.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Platform, View } from "react-native" 3 | import { ActionsButton, SendButton } from "./buttons" 4 | import styles from "./styles" 5 | 6 | const RightButtons = React.memo( 7 | ({ showSend, submit, showMessageBoxActions, isActionsEnabled }) => { 8 | const isIOS = Platform.OS === 'ios'; 9 | if (showSend) { 10 | return 11 | } 12 | if (isActionsEnabled) { 13 | return 14 | } 15 | return !isIOS ? : null 16 | } 17 | ) 18 | 19 | RightButtons.displayName = 'RightButtons' 20 | 21 | export default RightButtons 22 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/buttons/ActionsButton.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import BaseButton from "./BaseButton" 4 | 5 | const ActionsButton = ({ onPress }) => ( 6 | 11 | ) 12 | 13 | export default ActionsButton 14 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/buttons/CancelEditingButton.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import BaseButton from "./BaseButton" 4 | 5 | const CancelEditingButton = ({ onPress }) => ( 6 | 11 | ) 12 | 13 | export default CancelEditingButton 14 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/buttons/SendButton.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@emotion/react" 2 | import React from "react" 3 | import { colors } from "../../../lib/constants"; 4 | import BaseButton from "./BaseButton" 5 | 6 | const SendButton = ({ onPress }) => { 7 | const theme = useTheme(); 8 | return ( 9 | 15 | ) 16 | } 17 | 18 | export default SendButton 19 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/buttons/ToggleEmojiButton.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import BaseButton from "./BaseButton" 4 | 5 | const ToggleEmojiButton = ({ show, open, close }) => { 6 | if (show) { 7 | return ( 8 | 13 | ) 14 | } 15 | return ( 16 | 21 | ) 22 | } 23 | 24 | export default ToggleEmojiButton 25 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/buttons/index.js: -------------------------------------------------------------------------------- 1 | import CancelEditingButton from './CancelEditingButton'; 2 | import ToggleEmojiButton from './ToggleEmojiButton'; 3 | import SendButton from './SendButton'; 4 | import ActionsButton from './ActionsButton'; 5 | 6 | export { CancelEditingButton, ToggleEmojiButton, SendButton, ActionsButton }; 7 | -------------------------------------------------------------------------------- /packages/react-native/src/components/ChatInput/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatInput } from './ChatInput'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/CustomIcon/CustomIcon.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { createIconSetFromIcoMoon } from "@expo/vector-icons" 3 | import { useTheme } from "@emotion/react" 4 | 5 | const icoMoonConfig = require("./selection.json") 6 | 7 | export const IconSet = createIconSetFromIcoMoon( 8 | icoMoonConfig, 9 | "custom", 10 | "custom.ttf" 11 | ) 12 | 13 | const CustomIcon = ({ name, size, color, ...props }) => { 14 | const theme = useTheme(); 15 | const _color = theme.palette[color] ? ( 16 | theme.palette.mode === 'light' ? 17 | theme.palette[color].main : ( 18 | theme.palette[color].dark || theme.palette[color].main 19 | ) 20 | ) : color 21 | return ( 22 | 23 | ) 24 | } 25 | export default CustomIcon; 26 | -------------------------------------------------------------------------------- /packages/react-native/src/components/CustomIcon/index.js: -------------------------------------------------------------------------------- 1 | export { default as CustomIcon } from './CustomIcon'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/FontProvider/FontProvider.js: -------------------------------------------------------------------------------- 1 | import { useFonts } from 'expo-font'; 2 | 3 | const FontProvider = ({children}) => { 4 | const [fontsLoaded] = useFonts({ 5 | 'custom': require('../../assets/fonts/custom.ttf'), // font for rocketchat icons 6 | }); 7 | if (!fontsLoaded) { 8 | return null; 9 | } 10 | return children; 11 | } 12 | 13 | export default FontProvider; 14 | -------------------------------------------------------------------------------- /packages/react-native/src/components/FontProvider/index.js: -------------------------------------------------------------------------------- 1 | export { default as FontProvider } from './FontProvider'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markdown/Markdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Markup } from '../Markup'; 3 | import { Box } from '../Box'; 4 | 5 | const Markdown = ({ body }) => { 6 | 7 | if (!body || !body.md) return <>; 8 | 9 | return ( 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | export default Markdown; 17 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as Markdown } from './Markdown'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/blocks/HeadingBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import PlainSpan from '../elements/PlainSpan'; 4 | import styles from '../styles'; 5 | 6 | const HeadingBlock = ({ value, level = 1 }) => { 7 | const textStyle = styles[`heading${level}`]; 8 | return ( 9 | 10 | {value.map((value, index) => ( 11 | 12 | ))} 13 | 14 | ); 15 | }; 16 | 17 | export default HeadingBlock; 18 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/blocks/OrderedListBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, FlatList, Text } from 'react-native'; 3 | import InlineElements from '../elements/InlineElements'; 4 | import styles from '../styles'; 5 | 6 | const OrderedListBlock = ({ value }) => ( 7 | 8 | { 11 | return ( 12 | 13 | {index + 1}. 14 | 15 | 16 | ); 17 | }} 18 | /> 19 | 20 | ); 21 | 22 | export default OrderedListBlock; 23 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/blocks/ParagraphBlock.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@emotion/react'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | import { View } from "react-native" 5 | import { colors } from '../../../lib/constants'; 6 | 7 | import InlineElements from '../elements/InlineElements'; 8 | import styles from "../styles" 9 | 10 | const ParagraphBlock = ({ value }) => { 11 | const theme = useTheme(); 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default ParagraphBlock; 20 | 21 | ParagraphBlock.propTypes = { 22 | value: PropTypes.any, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/blocks/UnOrderedListBlock.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { View, FlatList, Text } from 'react-native'; 3 | import React from 'react'; 4 | import InlineElements from '../elements/InlineElements'; 5 | import styles from '../styles'; 6 | 7 | const UnOrderedListBlock = ({ value }) => ( 8 | 9 | { 12 | return ( 13 | 14 | {`\u2022`} 15 | 16 | 17 | ); 18 | }} 19 | /> 20 | 21 | ); 22 | 23 | export default UnOrderedListBlock; 24 | 25 | UnOrderedListBlock.propTypes = { 26 | items: PropTypes.arrayOf(PropTypes.shape), 27 | }; 28 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/BigEmoji.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Emoji from './Emoji'; 4 | 5 | const BigEmoji = ({ value }) => ( 6 | <> 7 | {value.map((content, index) => ( 8 | 9 | ))} 10 | 11 | ); 12 | 13 | export default BigEmoji; 14 | 15 | BigEmoji.propTypes = { 16 | value: PropTypes.any, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/CodeElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import PropTypes from 'prop-types'; 4 | import PlainSpan from './PlainSpan'; 5 | import styles from '../styles'; 6 | 7 | const CodeElement = ({ value }) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default CodeElement; 14 | 15 | CodeElement.propTypes = { 16 | value: PropTypes.any, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/CodeLine.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@emotion/react" 2 | import React from "react" 3 | import { Text } from "react-native" 4 | import { colors } from "../../../lib/constants" 5 | 6 | import styles from "../styles" 7 | 8 | const CodeLine = ({ value }) => { 9 | const theme = useTheme() 10 | if (value.type !== "PLAIN_TEXT") { 11 | return null 12 | } 13 | 14 | return ( 15 | 16 | {value.value} 17 | 18 | ) 19 | } 20 | 21 | export default CodeLine 22 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/ColorElement.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { Text } from 'react-native' 4 | 5 | const ColorElement = ({ r, g, b, a }) => ( 6 | 7 | 17 | {`rgba({r}, {g}, {b}, ${(a / 255) * 100}%)`} 18 | 19 | ); 20 | 21 | export default ColorElement; 22 | 23 | ColorElement.propTypes = { 24 | r: PropTypes.number, 25 | g: PropTypes.number, 26 | b: PropTypes.number, 27 | a: PropTypes.number, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/Divider.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | 4 | const Divider = ({ style = {}, ...props }) => { 5 | return ( 6 | 16 | ); 17 | }; 18 | 19 | export default Divider; 20 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/elements/PlainSpan.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from 'react-native'; 3 | import styles from '../styles'; 4 | 5 | const PlainSpan = ({ value, style, ...otherProps }) => ( 6 | 7 | {value} 8 | 9 | ); 10 | 11 | export default PlainSpan; 12 | 13 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/index.js: -------------------------------------------------------------------------------- 1 | export { default as Markup } from './Markup'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Markup/mentions/ChannelMention.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | const ChannelMention = ({ mention }) => #{mention}; 5 | 6 | export default ChannelMention; 7 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Message/MessageContent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import useComponentOverrides from '../../theme/useComponentOverrides'; 4 | import { Box } from '../Box'; 5 | import MessageBody from './MessageBody'; 6 | import MessageHeader from "./MessageHeader"; 7 | import MessageMetrics from './MessageMetrics'; 8 | 9 | const MessageContent = ({style}) => { 10 | const { styleOverrides } = useComponentOverrides('MessageContent', style); 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | const styles = StyleSheet.create({ 21 | container: { 22 | flex: 1, 23 | flexDirection: 'column', 24 | alignItems: 'flex-start', 25 | } 26 | }) 27 | export default MessageContent; 28 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Message/MessageMetrics.js: -------------------------------------------------------------------------------- 1 | const MessageMetrics = () => { 2 | return ( 3 | null 4 | ); 5 | } 6 | 7 | export default MessageMetrics; 8 | -------------------------------------------------------------------------------- /packages/react-native/src/components/Message/index.js: -------------------------------------------------------------------------------- 1 | export { default as Message } from './Message'; 2 | export { default as MessageContent } from './MessageContent'; 3 | export { default as MessageHeader } from './MessageHeader'; 4 | export { default as MessageBody } from './MessageHeader'; 5 | -------------------------------------------------------------------------------- /packages/react-native/src/components/MessageActionsSheet/index.js: -------------------------------------------------------------------------------- 1 | export { default as MessageActionsSheet } from './MessageActionsSheet'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/components/MessageList/index.js: -------------------------------------------------------------------------------- 1 | export { default as MessageList } from './MessageList'; -------------------------------------------------------------------------------- /packages/react-native/src/components/TextInput/TextInput.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@emotion/react" 2 | import React from "react" 3 | import { TextInput as RNTextInput } from "react-native" 4 | 5 | import { colors } from "../../lib/constants" 6 | import useComponentOverrides from "../../theme/useComponentOverrides" 7 | 8 | const TextInput = React.forwardRef(({ style, ...props }, ref) => { 9 | const theme = useTheme(); 10 | const { styleOverrides } = useComponentOverrides('TextInput', style); 11 | return ( 12 | 19 | ) 20 | }) 21 | 22 | TextInput.displayName = 'TextInput'; 23 | 24 | export default TextInput; 25 | -------------------------------------------------------------------------------- /packages/react-native/src/components/TextInput/index.js: -------------------------------------------------------------------------------- 1 | export { default as TextInput } from './TextInput'; 2 | export { default as FormTextInput } from './FormTextInput'; 3 | -------------------------------------------------------------------------------- /packages/react-native/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as EmbeddedChat } from './EmbeddedChat'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/contexts/MessageContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext } from "react"; 2 | 3 | export const MessageContext = createContext(); 4 | 5 | export const MessageContextProvider = ({children, value}) => ( 6 | 7 | {children} 8 | 9 | ); 10 | 11 | /** 12 | * @typedef {Object} MessageContextValue 13 | * @property {any} message 14 | * @returns {MessageContextValue} 15 | */ 16 | export const useMessageContext = () => { 17 | const messageContext = useContext(MessageContext); 18 | return messageContext; 19 | } 20 | -------------------------------------------------------------------------------- /packages/react-native/src/lib/auth.js: -------------------------------------------------------------------------------- 1 | import Storage from 'react-native-storage'; 2 | import AsyncStorage from '@react-native-async-storage/async-storage'; 3 | 4 | const storage = new Storage({ 5 | size: 1, 6 | storageBackend: AsyncStorage, 7 | defaultExpires: 1000 * 3600 * 24 * 15, // 15 day 8 | enableCache: true, 9 | }); 10 | 11 | export const getToken = async () => { 12 | let token; 13 | try { 14 | token = await storage.load({ key: 'ecToken' }); 15 | } catch (e) { 16 | return null; 17 | } 18 | return token; 19 | } 20 | 21 | export const saveToken = async (token) => { 22 | await storage.save({ key: 'ecToken', data: token }) 23 | }; 24 | 25 | export const deleteToken = async () => { 26 | try { 27 | await storage.remove({ key: 'ecToken' }); 28 | } catch (e) { 29 | console.error(e); 30 | } 31 | } -------------------------------------------------------------------------------- /packages/react-native/src/lib/cloneArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Deep Cloning upto 2 levels 3 | * @param {*} array 4 | * @returns 5 | */ 6 | const cloneArray = (array) => { 7 | const newArray = [...array].map((item) => 8 | typeof item === 'object' ? { ...item } : item 9 | ); 10 | return newArray; 11 | }; 12 | export default cloneArray; 13 | -------------------------------------------------------------------------------- /packages/react-native/src/lib/constants/index.js: -------------------------------------------------------------------------------- 1 | export * from './colors'; 2 | -------------------------------------------------------------------------------- /packages/react-native/src/lib/openLink.js: -------------------------------------------------------------------------------- 1 | import { Linking } from "react-native" 2 | 3 | export const openLink = async (url) => { 4 | try { 5 | await Linking.openURL(url) 6 | } catch { 7 | // do nothing 8 | } 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/react-native/src/store/channelStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useChannelStore = create((set) => ({ 4 | showChannelinfo: false, 5 | setShowChannelinfo: (showChannelinfo) => set(() => ({ showChannelinfo })), 6 | channelInfo: {}, 7 | setChannelInfo: (channelInfo) => set(() => ({ channelInfo })), 8 | })); 9 | 10 | export default useChannelStore; 11 | -------------------------------------------------------------------------------- /packages/react-native/src/store/index.js: -------------------------------------------------------------------------------- 1 | export { default as useChannelStore } from './channelStore'; 2 | export { default as useMessageStore } from './messageStore'; 3 | export { default as useUserStore } from './userStore'; 4 | export { default as useMemberStore } from './memberStore'; 5 | -------------------------------------------------------------------------------- /packages/react-native/src/store/memberStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useMemberStore = create((set) => ({ 4 | members: [], 5 | showMembers: false, 6 | toggleShowMembers: () => 7 | set((state) => ({ showMembers: !state.showMembers })), 8 | setMembersHandler: (memberList) => set(() => ({ members: memberList })), 9 | })); 10 | 11 | export default useMemberStore; 12 | -------------------------------------------------------------------------------- /packages/react-native/src/theme/useComponentOverrides.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@emotion/react'; 2 | import { useMemo } from 'react'; 3 | import { StyleSheet } from 'react-native'; 4 | const useComponentOverrides = (component, style = {}) => { 5 | const theme = useTheme(); 6 | 7 | let receivedStyle = useMemo(() => { 8 | if (Array.isArray(style)) { 9 | return StyleSheet.flatten(style); 10 | } 11 | return style; 12 | }, [style]); 13 | 14 | const styleOverrides = useMemo( 15 | () => ({ 16 | ...receivedStyle, 17 | ...((theme?.components && theme?.components[component]?.styleOverrides) || {}), 18 | }), 19 | [receivedStyle, theme?.components?.[component]?.styleOverrides] 20 | ); 21 | return { styleOverrides }; 22 | }; 23 | 24 | export default useComponentOverrides; 25 | -------------------------------------------------------------------------------- /packages/react-native/src/views/ChatRoomView/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatRoomView } from './ChatRoomView'; -------------------------------------------------------------------------------- /packages/react-native/src/views/LoginView/index.js: -------------------------------------------------------------------------------- 1 | export { default as LoginView } from './LoginView' 2 | -------------------------------------------------------------------------------- /packages/react-native/start-app.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | 3 | import App from './src/App'; 4 | 5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 6 | // It also ensures that whether you load the app in Expo Go or in a native build, 7 | // the environment is set up appropriately 8 | registerRootComponent(App); 9 | -------------------------------------------------------------------------------- /packages/react/.eslintignore: -------------------------------------------------------------------------------- 1 | rollup.config.js 2 | *.test.js 3 | dist/ -------------------------------------------------------------------------------- /packages/react/.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - name: Initialize RC server 3 | init: | 4 | docker-compose up -d 5 | - name: Initialize playground 6 | init: | 7 | npm install 8 | cd playground 9 | npm install 10 | cd .. 11 | command: npm run dev 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/react/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/react/.storybook/main.js: -------------------------------------------------------------------------------- 1 | /** @type { import('@storybook/react-webpack5').StorybookConfig } */ 2 | const config = { 3 | stories: ['../src/**/stories/*.stories.@(js|jsx|ts|tsx)', '../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], 4 | addons: [ 5 | '@storybook/addon-links', 6 | '@storybook/addon-essentials', 7 | '@storybook/addon-interactions', 8 | { 9 | name: '@storybook/addon-styling', 10 | options: { 11 | sass: { 12 | // Require your Sass preprocessor here 13 | implementation: require('sass'), 14 | }, 15 | }, 16 | }, 17 | ], 18 | framework: { 19 | name: '@storybook/react-webpack5', 20 | options: {}, 21 | }, 22 | docs: { 23 | autodocs: 'tag', 24 | }, 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /packages/react/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import 'normalize.css'; 2 | 3 | /** @type { import('@storybook/react').Preview } */ 4 | const preview = { 5 | parameters: { 6 | actions: { argTypesRegex: '^on[A-Z].*' }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | }, 14 | }; 15 | 16 | export default preview; 17 | -------------------------------------------------------------------------------- /packages/react/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: false, 7 | bugfixes: true, 8 | targets: { browsers: '> 0.25%, ie 11, not op_mini all, not dead' }, 9 | }, 10 | ], 11 | '@babel/preset-react', 12 | '@emotion/babel-preset-css-prop', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/react/src/hooks/uiKit/useMessageBlockContextValue.js: -------------------------------------------------------------------------------- 1 | import useUiKitActionManager from './useUiKitActionManager'; 2 | 3 | export const useMessageBlockContextValue = (rid, mid) => { 4 | const { emitInteraction } = useUiKitActionManager(); 5 | return { 6 | action: async ({ appId, actionId, blockId, value }) => { 7 | await emitInteraction(appId, { 8 | type: 'blockAction', 9 | actionId, 10 | payload: { 11 | blockId, 12 | value, 13 | }, 14 | container: { 15 | type: 'message', 16 | id: mid, 17 | }, 18 | rid, 19 | mid, 20 | }); 21 | }, 22 | rid, 23 | values: {}, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useDisplayNameColor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | import { useTheme } from '@embeddedchat/ui-elements'; 3 | 4 | const simpleHash = (str) => { 5 | if (!str) return 0; 6 | let hash = 0; 7 | for (let i = 0; i < str.length; i += 1) { 8 | const char = str.charCodeAt(i); 9 | hash = (hash << 5) - hash + char; 10 | } 11 | return hash; 12 | }; 13 | 14 | const useDisplayNameColor = () => { 15 | const { theme, mode } = useTheme(); 16 | 17 | const getDisplayNameColor = (username) => { 18 | const hash = simpleHash(username); 19 | const { saturation, luminance } = theme.contrastParams[mode]; 20 | const hue = Math.abs(hash) % 360; 21 | return `hsl(${hue}, ${saturation}%, ${luminance}%)`; 22 | }; 23 | 24 | return getDisplayNameColor; 25 | }; 26 | 27 | export default useDisplayNameColor; 28 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useDropBox.js: -------------------------------------------------------------------------------- 1 | import useAttachmentWindowStore from '../store/attachmentwindow'; 2 | 3 | const useDropBox = () => { 4 | const data = useAttachmentWindowStore((state) => state.data); 5 | const setData = useAttachmentWindowStore((state) => state.setData); 6 | const toggle = useAttachmentWindowStore((state) => state.toggle); 7 | 8 | const handleDrag = (e) => { 9 | e.preventDefault(); 10 | }; 11 | 12 | const handleDragDrop = (e) => { 13 | e.preventDefault(); 14 | toggle(); 15 | setData(e.dataTransfer.files[0]); 16 | }; 17 | 18 | return { 19 | data, 20 | handleDrag, 21 | handleDragDrop, 22 | }; 23 | }; 24 | 25 | export default useDropBox; 26 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useSetMessageList.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useMemo } from 'react'; 2 | 3 | export const useSetMessageList = (messages, shouldRender) => { 4 | const [loading, setLoading] = useState(true); 5 | 6 | const messageList = useMemo( 7 | () => messages.filter(shouldRender), 8 | [messages, shouldRender] 9 | ); 10 | 11 | useEffect(() => { 12 | setLoading(false); 13 | }, [messages, shouldRender]); 14 | 15 | return { loading, messageList }; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useShowCommands.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | 3 | const useShowCommands = (commands, setFilteredCommands, setShowCommandList) => 4 | useCallback( 5 | async (e) => { 6 | const getFilteredCommands = (cmd) => 7 | commands.filter((c) => c.command.startsWith(cmd.replace('/', ''))); 8 | 9 | const cursor = e.target.selectionStart; 10 | const tokens = e.target.value.slice(0, cursor).split(/\s+/); 11 | 12 | if (tokens.length === 1 && tokens[0].startsWith('/')) { 13 | setFilteredCommands(getFilteredCommands(tokens[0])); 14 | setShowCommandList(true); 15 | } else { 16 | setFilteredCommands([]); 17 | setShowCommandList(false); 18 | } 19 | }, 20 | [commands, setFilteredCommands, setShowCommandList] 21 | ); 22 | 23 | export default useShowCommands; 24 | -------------------------------------------------------------------------------- /packages/react/src/index.js: -------------------------------------------------------------------------------- 1 | export { default as EmbeddedChat } from './views/EmbeddedChat'; 2 | -------------------------------------------------------------------------------- /packages/react/src/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { RCComponent } from './index'; 5 | 6 | describe('Rendering of RCComponent Component', () => { 7 | test('renders the RCComponent component', () => { 8 | render(); 9 | }); 10 | 11 | test('if isClosable = true, and setClosableState is not provided.. throw an error', () => { 12 | expect(() => { 13 | render(); 14 | }).toThrowError(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/react/src/lib/cloneArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Deep Cloning upto 2 levels 3 | * @param {*} array 4 | * @returns 5 | */ 6 | const cloneArray = (array) => { 7 | const newArray = [...array].map((item) => 8 | typeof item === 'object' ? { ...item } : item 9 | ); 10 | return newArray; 11 | }; 12 | export default cloneArray; 13 | -------------------------------------------------------------------------------- /packages/react/src/lib/emoji.js: -------------------------------------------------------------------------------- 1 | import emojione from 'emoji-toolkit'; 2 | 3 | export const parseEmoji = (text) => { 4 | const regx = /:([^:]*):/g; 5 | const regx_data = text.match(regx); 6 | if (regx_data) { 7 | const result = regx_data[regx_data.length - 1]; 8 | const d = emojione.shortnameToUnicode(result); 9 | if (d !== undefined) text = text.replace(result, d); 10 | } 11 | return text; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/react/src/lib/formatTimestamp.js: -------------------------------------------------------------------------------- 1 | const formatTimestamp = (timestamp) => { 2 | const date = new Date(timestamp); 3 | const now = new Date(); 4 | 5 | const isDifferentDay = 6 | date.getDate() !== now.getDate() || 7 | date.getMonth() !== now.getMonth() || 8 | date.getFullYear() !== now.getFullYear(); 9 | 10 | const options = { hour: 'numeric', minute: 'numeric', hour12: true }; 11 | const formattedTime = date.toLocaleTimeString('en-US', options); 12 | 13 | return isDifferentDay 14 | ? `${date.toLocaleDateString('en-US', { 15 | weekday: 'long', 16 | })} ${formattedTime}` 17 | : formattedTime; 18 | }; 19 | 20 | export default formatTimestamp; 21 | -------------------------------------------------------------------------------- /packages/react/src/lib/formatTimestampGetDate.js: -------------------------------------------------------------------------------- 1 | const formatTimestampGetDate = (timestamp) => { 2 | const date = new Date(timestamp); 3 | return date.toLocaleDateString('en-US', { 4 | year: 'numeric', 5 | month: 'long', 6 | day: 'numeric', 7 | }); 8 | }; 9 | 10 | export default formatTimestampGetDate; 11 | -------------------------------------------------------------------------------- /packages/react/src/lib/isMessageLastSequential.js: -------------------------------------------------------------------------------- 1 | import isMessageSequential from './isMessageSequential'; 2 | 3 | const isMessageLastSequential = (current, next) => { 4 | if (!next) { 5 | return true; 6 | } 7 | 8 | if (current.u._id !== next.u._id) { 9 | return true; 10 | } 11 | 12 | if (!isMessageSequential(next, current, 300)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | 19 | export default isMessageLastSequential; 20 | -------------------------------------------------------------------------------- /packages/react/src/lib/isMessageSequential.js: -------------------------------------------------------------------------------- 1 | import { differenceInSeconds, isSameDay } from 'date-fns'; 2 | 3 | const isMessageSequential = (current, previous, groupingRange) => { 4 | if (!previous) { 5 | return false; 6 | } 7 | 8 | if (current.t || previous.t) { 9 | return false; 10 | } 11 | 12 | if (current.groupable === false) { 13 | return false; 14 | } 15 | 16 | if (current.u._id !== previous.u._id) { 17 | return false; 18 | } 19 | 20 | if (current.alias !== previous.alias) { 21 | return false; 22 | } 23 | 24 | const isTimeDiffSmall = 25 | differenceInSeconds(new Date(current.ts), new Date(previous.ts)) < 26 | groupingRange; 27 | 28 | const isMessageNewDay = 29 | !previous || !isSameDay(new Date(current.ts), new Date(previous.ts)); 30 | 31 | return isTimeDiffSmall && !isMessageNewDay; 32 | }; 33 | 34 | export default isMessageSequential; 35 | -------------------------------------------------------------------------------- /packages/react/src/lib/reaction.js: -------------------------------------------------------------------------------- 1 | export const serializeReactions = (reactions) => { 2 | const arr = []; 3 | 4 | const emojis = Object.keys(reactions); 5 | emojis.forEach((emoji) => 6 | arr.push({ 7 | name: emoji, 8 | count: reactions[emoji].usernames.length, 9 | usernames: reactions[emoji].usernames, 10 | }) 11 | ); 12 | 13 | return arr; 14 | }; 15 | 16 | export const isSameUser = (reaction, username) => 17 | reaction.usernames.find((u) => u === username); 18 | -------------------------------------------------------------------------------- /packages/react/src/lib/textFormat.js: -------------------------------------------------------------------------------- 1 | export const formatter = [ 2 | { name: 'bold', pattern: '*{{text}}*', tooltip: 'Bold' }, 3 | { name: 'italic', pattern: '_{{text}}_', tooltip: 'Italic' }, 4 | { name: 'strike', pattern: '~{{text}}~', tooltip: 'Strikethrough' }, 5 | { name: 'code', pattern: '`{{text}}`', tooltip: 'Inline code' }, 6 | { 7 | name: 'multiline', 8 | pattern: '```\n{{text}}\n```', 9 | tooltip: 'Multi-line code', 10 | }, 11 | ]; 12 | -------------------------------------------------------------------------------- /packages/react/src/store/attachmentwindow.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useAttachmentWindowStore = create((set) => ({ 4 | attachmentWindowOpen: false, 5 | data: null, 6 | toggle: () => 7 | set((state) => ({ attachmentWindowOpen: !state.attachmentWindowOpen })), 8 | setData: (file) => { 9 | set({ data: file }); 10 | }, 11 | })); 12 | 13 | export default useAttachmentWindowStore; 14 | -------------------------------------------------------------------------------- /packages/react/src/store/channelStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useChannelStore = create((set) => ({ 4 | showChannelinfo: false, 5 | isChannelPrivate: false, 6 | isChannelReadOnly: false, 7 | isChannelArchived: false, 8 | isRoomTeam: false, 9 | setShowChannelinfo: (showChannelinfo) => set(() => ({ showChannelinfo })), 10 | channelInfo: {}, 11 | setChannelInfo: (channelInfo) => set(() => ({ channelInfo })), 12 | setIsChannelPrivate: (isChannelPrivate) => set(() => ({ isChannelPrivate })), 13 | setIsChannelArchived: (isChannelArchived) => 14 | set(() => ({ isChannelArchived })), 15 | setIsRoomTeam: (isRoomTeam) => set(() => ({ isRoomTeam })), 16 | setIsChannelReadOnly: (isChannelReadOnly) => 17 | set(() => ({ isChannelReadOnly })), 18 | })); 19 | 20 | export default useChannelStore; 21 | -------------------------------------------------------------------------------- /packages/react/src/store/fileStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useFileStore = create((set) => ({ 4 | showAllFiles: false, 5 | setShowAllFiles: (showAllFiles) => set(() => ({ showAllFiles })), 6 | })); 7 | 8 | export default useFileStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/index.js: -------------------------------------------------------------------------------- 1 | export { default as useMessageStore } from './messageStore'; 2 | export { default as useUserStore } from './userStore'; 3 | export { default as useMemberStore } from './memberStore'; 4 | export { default as totpModalStore } from './totpmodalStore'; 5 | export { default as useSearchMessageStore } from './searchMessageStore'; 6 | export { default as useLoginStore } from './loginStore'; 7 | export { default as useChannelStore } from './channelStore'; 8 | export { default as useThreadsMessageStore } from './threadsMessageStore'; 9 | export { default as useFileStore } from './fileStore'; 10 | export { default as useMentionsStore } from './mentionsStore'; 11 | export { default as usePinnedMessageStore } from './pinnedMessageStore'; 12 | export { default as useStarredMessageStore } from './starredMessageStore'; 13 | export { default as useSidebarStore } from './sidebarStore'; 14 | -------------------------------------------------------------------------------- /packages/react/src/store/inviteStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useInviteStore = create((set) => ({ 4 | showInvite: false, 5 | toggleInviteView: () => set((state) => ({ showInvite: !state.showInvite })), 6 | })); 7 | 8 | export default useInviteStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/loginStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useLoginStore = create((set) => ({ 4 | isLoginIn: false, 5 | setIsLoginIn: (isLoginIn) => set(() => ({ isLoginIn })), 6 | isLoginModalOpen: false, 7 | setIsLoginModalOpen: (isLoginModalOpen) => set(() => ({ isLoginModalOpen })), 8 | })); 9 | 10 | export default useLoginStore; 11 | -------------------------------------------------------------------------------- /packages/react/src/store/memberStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useMemberStore = create((set) => ({ 4 | members: [], 5 | showMembers: false, 6 | setShowMembers: (showMembers) => set(() => ({ showMembers })), 7 | memberRoles: {}, 8 | admins: [], 9 | setMemberRoles: (memberRoles) => set((state) => ({ ...state, memberRoles })), 10 | setAdmins: (admins) => set(() => ({ admins })), 11 | setMembersHandler: (memberList) => set(() => ({ members: memberList })), 12 | })); 13 | 14 | export default useMemberStore; 15 | -------------------------------------------------------------------------------- /packages/react/src/store/mentionsStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useMentionsStore = create((set) => ({ 4 | showMentions: false, 5 | setShowMentions: (showMentions) => set(() => ({ showMentions })), 6 | })); 7 | 8 | export default useMentionsStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/pinnedMessageStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const usePinnedMessageStore = create((set) => ({ 4 | showPinned: false, 5 | setShowPinned: (showPinned) => set(() => ({ showPinned })), 6 | })); 7 | 8 | export default usePinnedMessageStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/searchMessageStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useSearchMessageStore = create((set) => ({ 4 | showSearch: false, 5 | setShowSearch: (showSearch) => set(() => ({ showSearch })), 6 | })); 7 | 8 | export default useSearchMessageStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/settingsStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useSettingsStore = create((set) => ({ 4 | messageLimit: 5000, 5 | setMessageLimit: (messageLimit) => set(() => ({ messageLimit })), 6 | })); 7 | 8 | export default useSettingsStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/sidebarStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useSidebarStore = create((set) => ({ 4 | showSidebar: false, 5 | setShowSidebar: (showSidebar) => set(() => ({ showSidebar })), 6 | })); 7 | 8 | export default useSidebarStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/starredMessageStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useStarredMessageStore = create((set) => ({ 4 | showStarred: false, 5 | setShowStarred: (showStarred) => set(() => ({ showStarred })), 6 | starredMessages: [], 7 | setStarredMessages: (messages) => set(() => ({ starredMessages: messages })), 8 | })); 9 | 10 | export default useStarredMessageStore; 11 | -------------------------------------------------------------------------------- /packages/react/src/store/threadsMessageStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useThreadsMessageStore = create((set) => ({ 4 | showAllThreads: false, 5 | setShowAllThreads: (showAllThreads) => set(() => ({ showAllThreads })), 6 | })); 7 | 8 | export default useThreadsMessageStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/totpmodalStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const totpModalStore = create((set) => ({ 4 | isTotpModalOpen: false, 5 | setIsTotpModalOpen: (isTotpModalOpen) => set(() => ({ isTotpModalOpen })), 6 | })); 7 | 8 | export default totpModalStore; 9 | -------------------------------------------------------------------------------- /packages/react/src/store/uiKitStore.js: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | const useUiKitStore = create((set) => ({ 4 | uiKitModalOpen: false, 5 | setUiKitModalOpen: (isOpen) => set({ uiKitModalOpen: isOpen }), 6 | 7 | uiKitContextualBarOpen: false, 8 | setUiKitContextualBarOpen: (isOpen) => 9 | set({ uiKitContextualBarOpen: isOpen }), 10 | 11 | uiKitModalData: null, 12 | setUiKitModalData: (data) => set({ uiKitModalData: data }), 13 | 14 | uiKitContextualBarData: null, 15 | setUiKitContextualBarData: (data) => set({ uiKitContextualBarData: data }), 16 | })); 17 | 18 | export default useUiKitStore; 19 | -------------------------------------------------------------------------------- /packages/react/src/stories/EmbeddedChat.stories.js: -------------------------------------------------------------------------------- 1 | import { EmbeddedChat } from '..'; 2 | 3 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 4 | export default { 5 | title: 'EmbeddedChat/Simple', 6 | component: EmbeddedChat, 7 | }; 8 | 9 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 10 | export const Simple = { 11 | args: { 12 | host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000', 13 | roomId: process.env.RC_ROOM_ID || 'GENERAL', 14 | channelName: 'general', 15 | anonymousMode: false, 16 | headerColor: 'white', 17 | toastBarPosition: 'bottom right', 18 | showRoles: true, 19 | enableThreads: true, 20 | hideHeader: false, 21 | auth: { 22 | flow: 'PASSWORD', 23 | }, 24 | dark: false, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/react/src/stories/EmbeddedChatSecureAuth.stories.js: -------------------------------------------------------------------------------- 1 | import { EmbeddedChat } from '..'; 2 | 3 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 4 | export default { 5 | title: 'EmbeddedChat/SecureAuth', 6 | component: EmbeddedChat, 7 | }; 8 | 9 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 10 | export const Secure_Auth = { 11 | args: { 12 | host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000', 13 | roomId: process.env.RC_ROOM_ID || 'GENERAL', 14 | channelName: 'general', 15 | anonymousMode: true, 16 | headerColor: 'white', 17 | toastBarPosition: 'bottom right', 18 | showRoles: true, 19 | enableThreads: true, 20 | hideHeader: false, 21 | auth: { 22 | flow: 'PASSWORD', 23 | }, 24 | secure: true, 25 | dark: false, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/react/src/stories/EmbeddedChatWithOAuth.stories.js: -------------------------------------------------------------------------------- 1 | import { EmbeddedChat } from '..'; 2 | 3 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 4 | export default { 5 | title: 'EmbeddedChat/WithOAuth', 6 | component: EmbeddedChat, 7 | }; 8 | 9 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 10 | export const WithOAuth = { 11 | args: { 12 | host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000', 13 | roomId: process.env.RC_ROOM_ID || 'GENERAL', 14 | channelName: 'general', 15 | anonymousMode: true, 16 | headerColor: 'white', 17 | toastBarPosition: 'bottom right', 18 | showRoles: true, 19 | showAvatar: true, 20 | enableThreads: true, 21 | auth: { 22 | flow: 'OAUTH', 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react/src/stories/EmbeddedChatWithRemote.stories.js: -------------------------------------------------------------------------------- 1 | import { EmbeddedChat } from '..'; 2 | 3 | export default { 4 | title: 'EmbeddedChat/WithRemoteOpt', 5 | component: EmbeddedChat, 6 | parameters: { 7 | controls: { disable: true }, 8 | }, 9 | }; 10 | 11 | export const With_Remote_Opt = { 12 | args: { 13 | host: process.env.STORYBOOK_RC_HOST || 'http://localhost:3000', 14 | roomId: process.env.RC_ROOM_ID || 'GENERAL', 15 | channelName: 'general', 16 | anonymousMode: true, 17 | headerColor: 'white', 18 | toastBarPosition: 'bottom right', 19 | showRoles: true, 20 | enableThreads: true, 21 | hideHeader: false, 22 | remoteOpt: true, 23 | auth: { 24 | flow: 'PASSWORD', 25 | }, 26 | dark: false, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/react/src/views/AttachmentHandler/Attachments.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Attachment from './Attachment'; 4 | import RCContext from '../../context/RCInstance'; 5 | 6 | const Attachments = ({ attachments, type, variantStyles = {}, msg }) => { 7 | const { RCInstance } = useContext(RCContext); 8 | let host = RCInstance.getHost(); 9 | host = host.replace(/\/$/, ''); 10 | 11 | return attachments.map((attachment, idx) => ( 12 | 20 | )); 21 | }; 22 | 23 | export default Attachments; 24 | 25 | Attachments.propTypes = { 26 | attachment: PropTypes.object, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/react/src/views/AttachmentHandler/index.js: -------------------------------------------------------------------------------- 1 | export { default as Attachments } from './Attachments'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/AttachmentPreview/PreviewType/audio.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | 5 | function PreviewAudio({ previewURL }) { 6 | return ( 7 | 8 | 10 | ); 11 | } 12 | 13 | export default PreviewAudio; 14 | 15 | PreviewAudio.propTypes = { 16 | previewURL: PropTypes.string, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/react/src/views/AttachmentPreview/PreviewType/default.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { css } from '@emotion/react'; 4 | import { Box, Icon } from '@embeddedchat/ui-elements'; 5 | 6 | function PreviewDefault({ data }) { 7 | return ( 8 | 9 | 16 | 17 | 22 | {data.name} 23 | 24 | 25 | 26 | ); 27 | } 28 | 29 | export default PreviewDefault; 30 | 31 | PreviewDefault.propTypes = { 32 | data: PropTypes.object, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/react/src/views/AttachmentPreview/PreviewType/image.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Box, useTheme } from '@embeddedchat/ui-elements'; 4 | 5 | function PreviewImage({ previewURL }) { 6 | const { theme } = useTheme(); 7 | return ( 8 | 9 | 17 | 18 | ); 19 | } 20 | 21 | export default PreviewImage; 22 | 23 | PreviewImage.propTypes = { 24 | previewURL: PropTypes.string, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react/src/views/ChannelState/ChannelState.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const getChannelStateStyles = (theme) => { 4 | const styles = { 5 | channelStateContainer: css` 6 | font-size: 0.75rem; 7 | padding: 0.2rem 2rem; 8 | z-index: 1200; 9 | display: flex; 10 | justify-content: space-between; 11 | `, 12 | 13 | channelStateMessage: css` 14 | background-color: ${theme.secondary}; 15 | display: flex; 16 | gap: 0.1rem; 17 | padding: 1.5px 5px; 18 | justify-content: center; 19 | align-items: center; 20 | border-radius: ${theme.radius}; 21 | color: ${theme.secondaryForeground}; 22 | `, 23 | }; 24 | 25 | return styles; 26 | }; 27 | 28 | export default getChannelStateStyles; 29 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatBody/RecentMessageButton.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Box, Button, Icon } from '@embeddedchat/ui-elements'; 3 | import { getRecentMessageStyles } from './ChatBody.styles'; 4 | 5 | const RecentMessageButton = ({ visible, onClick, text }) => { 6 | const [clicked, setClicked] = useState(false); 7 | const styles = getRecentMessageStyles(); 8 | return ( 9 | 23 | ); 24 | }; 25 | 26 | export default RecentMessageButton; 27 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatBody/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatBody } from './ChatBody'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatHeader/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatHeader } from './ChatHeader'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatInput/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatInput } from './ChatInput'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatLayout/ChatLayout.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const styles = { 4 | layout: css` 5 | display: flex; 6 | height: 100%; 7 | overflow: hidden; 8 | `, 9 | 10 | chatMain: css` 11 | display: flex; 12 | flex: 1; 13 | flex-direction: column; 14 | position: relative; 15 | min-width: 0; 16 | `, 17 | 18 | sidebar: css` 19 | width: 350px; 20 | `, 21 | }; 22 | 23 | export default styles; 24 | -------------------------------------------------------------------------------- /packages/react/src/views/ChatLayout/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChatLayout } from './ChatLayout'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/CommandList/index.js: -------------------------------------------------------------------------------- 1 | export { default as CommandsList } from './CommandsList'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/DynamicHeader/DynamicHeader.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const useDynamicHeaderStyles = () => { 4 | const styles = { 5 | container: css` 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | width: 100%; 10 | z-index: 1200; 11 | padding-block-start: 10px; 12 | `, 13 | 14 | clearSpacing: css` 15 | margin-bottom: 1px; 16 | padding: 0; 17 | text-overflow: ellipsis; 18 | white-space: nowrap; 19 | overflow: hidden; 20 | `, 21 | }; 22 | 23 | return styles; 24 | }; 25 | 26 | export default useDynamicHeaderStyles; 27 | -------------------------------------------------------------------------------- /packages/react/src/views/DynamicHeader/index.js: -------------------------------------------------------------------------------- 1 | export { default as DynamicHeader } from './DynamicHeader'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/EmbeddedChat.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const styles = { 4 | embeddedchat: (theme, dark) => css` 5 | background: ${theme.schemes[dark ? 'dark' : 'light'].background}; 6 | color: ${theme.schemes[dark ? 'dark' : 'light'].foreground}; 7 | display: flex; 8 | flex-direction: column; 9 | border: ${`1.5px solid ${theme.schemes[dark ? 'dark' : 'light'].border}`}; 10 | border-radius: ${theme.radius}; 11 | overflow: hidden; 12 | `, 13 | fullscreen: css` 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | width: 100vw !important; 18 | height: 100vh !important; 19 | max-width: unset !important; 20 | max-height: unset !important; 21 | border-radius: 0; 22 | border: none; 23 | `, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/react/src/views/EmojiPicker/index.js: -------------------------------------------------------------------------------- 1 | export { default as EmojiPicker } from './EmojiPicker'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/EmojiReaction/EmojiReaction.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import emojione from 'emoji-toolkit'; 4 | import { css } from '@emotion/react'; 5 | import { Box } from '@embeddedchat/ui-elements'; 6 | import DOMPurify from 'dompurify'; 7 | 8 | const EmojiReaction = ({ body }) => { 9 | const emojiHtml = emojione.toImage(body); 10 | emojione.imageTitleTag = false; 11 | return ( 12 | 18 | ); 19 | }; 20 | 21 | EmojiReaction.propTypes = { 22 | body: PropTypes.string.isRequired, 23 | }; 24 | 25 | export default EmojiReaction; 26 | -------------------------------------------------------------------------------- /packages/react/src/views/EmojiReaction/index.js: -------------------------------------------------------------------------------- 1 | export { default as MessageEmoji } from './EmojiReaction'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/FileMessage/FilePreviewContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Avatar } from '@embeddedchat/ui-elements'; 3 | import { filePreviewContainerStyles as styles } from './Files.styles'; 4 | 5 | const FilePreviewContainer = ({ file }) => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default FilePreviewContainer; 12 | -------------------------------------------------------------------------------- /packages/react/src/views/ImageGallery/Swiper.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | /* eslint-disable import/no-unresolved */ 3 | import { register } from 'swiper/element/bundle'; 4 | 5 | export function Swiper(props) { 6 | const swiperRef = useRef(null); 7 | const { children, ...rest } = props; 8 | 9 | useEffect(() => { 10 | register(); 11 | const params = { 12 | ...rest, 13 | }; 14 | 15 | Object.assign(swiperRef.current, params); 16 | swiperRef.current.initialize(); 17 | }, []); 18 | 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | export function SwiperSlide(props) { 26 | const { children, ...rest } = props; 27 | 28 | return {children}; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react/src/views/LinkPreview/LinkPreview.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const getLinkPreviewStyles = (theme) => { 4 | const styles = { 5 | arrowDropDown: css` 6 | cursor: pointer; 7 | display: flex; 8 | align-items: center; 9 | `, 10 | 11 | linkPreviewContainer: css` 12 | max-width: 16rem; 13 | border: 1px solid ${theme.colors.border}; 14 | border-radius: ${theme.radius}; 15 | margin-bottom: 0.75rem; 16 | overflow: hidden; 17 | `, 18 | 19 | textStyle: css` 20 | text-overflow: ellipsis; 21 | white-space: nowrap; 22 | overflow: hidden; 23 | margin-block-start: 0rem; 24 | margin-block-end: 0rem; 25 | `, 26 | }; 27 | 28 | return styles; 29 | }; 30 | 31 | export default getLinkPreviewStyles; 32 | -------------------------------------------------------------------------------- /packages/react/src/views/LinkPreview/index.js: -------------------------------------------------------------------------------- 1 | export { default as LinkPreview } from './LinkPreview'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/Markdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as Markdown } from './Markdown'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/Message/BubbleVariant/useBubbleStyles.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@embeddedchat/ui-elements'; 2 | import { getBubbleStyles, getBubbleStylesMe } from './Bubble.styles'; 3 | 4 | const useBubbleStyles = (isMe = false) => { 5 | const { theme } = useTheme(); 6 | const styles = getBubbleStyles(theme); 7 | const meStyles = getBubbleStylesMe(theme); 8 | 9 | const mergedStyles = {}; 10 | 11 | Object.keys(styles).forEach((key) => { 12 | mergedStyles[key] = [styles[key], isMe && meStyles[`${key}Me`]].filter( 13 | Boolean 14 | ); 15 | }); 16 | 17 | return mergedStyles; 18 | }; 19 | 20 | export default useBubbleStyles; 21 | -------------------------------------------------------------------------------- /packages/react/src/views/Message/index.js: -------------------------------------------------------------------------------- 1 | export { default as Message } from './Message'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/Message/uiKit/UiKitMessageBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | UiKitComponent, 4 | UiKitContext, 5 | UiKitMessage, 6 | } from '@embeddedchat/ui-kit'; 7 | import { useMessageBlockContextValue } from '../../../hooks/uiKit/useMessageBlockContextValue'; 8 | 9 | const UiKitMessageBlock = ({ rid, mid, blocks }) => { 10 | const contextValue = useMessageBlockContextValue(rid, mid); 11 | 12 | return ( 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default UiKitMessageBlock; 20 | -------------------------------------------------------------------------------- /packages/react/src/views/MessageAggregators/PinnedMessages.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useComponentOverrides } from '@embeddedchat/ui-elements'; 3 | import { MessageAggregator } from './common/MessageAggregator'; 4 | 5 | const PinnedMessages = () => { 6 | const { variantOverrides } = useComponentOverrides('PinnedMessages'); 7 | const viewType = variantOverrides.viewType || 'Sidebar'; 8 | return ( 9 | msg.pinned} 14 | viewType={viewType} 15 | /> 16 | ); 17 | }; 18 | 19 | export default PinnedMessages; 20 | -------------------------------------------------------------------------------- /packages/react/src/views/MessageAggregators/common/LoadingIndicator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Throbber } from '@embeddedchat/ui-elements'; 3 | import useMessageAggregatorStyles from './MessageAggregator.styles'; 4 | 5 | const LoadingIndicator = () => { 6 | const styles = useMessageAggregatorStyles(); 7 | return ( 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default LoadingIndicator; 15 | -------------------------------------------------------------------------------- /packages/react/src/views/MessageAggregators/common/MessageAggregator.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const getMessageAggregatorStyles = () => { 4 | const styles = { 5 | listContainerStyles: css` 6 | flex: 1; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: initial; 10 | align-items: initial; 11 | max-width: 100%; 12 | `, 13 | 14 | noMessageStyles: css` 15 | justify-content: center; 16 | align-items: center; 17 | `, 18 | 19 | centerColumnStyles: css` 20 | display: flex; 21 | flex-direction: column; 22 | align-items: center; 23 | `, 24 | }; 25 | 26 | return styles; 27 | }; 28 | 29 | export default getMessageAggregatorStyles; 30 | -------------------------------------------------------------------------------- /packages/react/src/views/MessageAggregators/common/NoMessageIndicator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box, Icon } from '@embeddedchat/ui-elements'; 4 | import useMessageAggregatorStyles from './MessageAggregator.styles'; 5 | 6 | const NoMessagesIndicator = ({ iconName, message }) => { 7 | const styles = useMessageAggregatorStyles(); 8 | return ( 9 | 10 | 17 | 24 | {message} 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default NoMessagesIndicator; 31 | -------------------------------------------------------------------------------- /packages/react/src/views/MessageList/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './MessageList'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/ReportMessage/ReportMessage.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const styles = { 4 | conatiner: css` 5 | display: flex; 6 | justify-content: center; 7 | flex-direction: column; 8 | margin-bottom: 0.125rem; 9 | padding: 0.6rem 0.4rem; 10 | `, 11 | }; 12 | 13 | export default styles; 14 | -------------------------------------------------------------------------------- /packages/react/src/views/ReportMessage/index.js: -------------------------------------------------------------------------------- 1 | export { default as Message } from './MessageReportWindow'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/SurfaceMenu/SurfaceItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Tooltip, ActionButton } from '@embeddedchat/ui-elements'; 3 | 4 | const SurfaceItem = ({ item, size }) => ( 5 | 6 | 15 | 16 | ); 17 | 18 | export default SurfaceItem; 19 | -------------------------------------------------------------------------------- /packages/react/src/views/SurfaceMenu/SurfaceMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SurfaceItem from './SurfaceItem'; 3 | 4 | const SurfaceMenu = ({ options, size = 'medium' }) => ( 5 | <> 6 | {options?.map((item, idx) => ( 7 | 8 | ))} 9 | 10 | ); 11 | 12 | export default SurfaceMenu; 13 | -------------------------------------------------------------------------------- /packages/react/src/views/Thread/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ThreadMessageList'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/TypingUsers/index.js: -------------------------------------------------------------------------------- 1 | export { default as TypingUsers } from './TypingUsers'; 2 | -------------------------------------------------------------------------------- /packages/react/src/views/UserInformation/UserInfoField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | 5 | const UserInfoField = ({ 6 | label, 7 | value, 8 | isAdmin, 9 | authenticatedUserId, 10 | currentUserInfo, 11 | }) => { 12 | const shouldRender = isAdmin || authenticatedUserId === currentUserInfo._id; 13 | return shouldRender ? ( 14 | 19 | 25 | {label} 26 | 27 | 32 | {value} 33 | 34 | 35 | ) : null; 36 | }; 37 | 38 | export default UserInfoField; 39 | -------------------------------------------------------------------------------- /packages/ui-elements/.eslintignore: -------------------------------------------------------------------------------- 1 | rollup.config.js 2 | *.test.js 3 | dist/ -------------------------------------------------------------------------------- /packages/ui-elements/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log -------------------------------------------------------------------------------- /packages/ui-elements/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui-elements/.storybook/main.js: -------------------------------------------------------------------------------- 1 | /** @type { import('@storybook/react-webpack5').StorybookConfig } */ 2 | const config = { 3 | stories: ['../src/**/stories/*.stories.@(js|jsx|ts|tsx)', '../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], 4 | addons: [ 5 | '@storybook/addon-links', 6 | '@storybook/addon-essentials', 7 | '@storybook/addon-interactions', 8 | { 9 | name: '@storybook/addon-styling', 10 | options: { 11 | sass: { 12 | // Require your Sass preprocessor here 13 | implementation: require('sass'), 14 | }, 15 | }, 16 | }, 17 | ], 18 | framework: { 19 | name: '@storybook/react-webpack5', 20 | options: {}, 21 | }, 22 | docs: { 23 | autodocs: 'tag', 24 | }, 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /packages/ui-elements/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import 'normalize.css'; 2 | 3 | /** @type { import('@storybook/react').Preview } */ 4 | const preview = { 5 | parameters: { 6 | actions: { argTypesRegex: '^on[A-Z].*' }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | }, 14 | }; 15 | 16 | export default preview; 17 | -------------------------------------------------------------------------------- /packages/ui-elements/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @embeddedchat/ui-elements 2 | 3 | ## 0.1.2 4 | 5 | ### Patch Changes 6 | 7 | - Version Bump 8 | 9 | ## 0.1.1 10 | 11 | ### Patch Changes 12 | 13 | - Version Bump 14 | 15 | ## 0.1.0 16 | 17 | ### Minor Changes 18 | 19 | - 5f604c59: - Separated components, markups, and ui-kit into individual monorepos. 20 | 21 | See [#604](https://github.com/RocketChat/EmbeddedChat/pull/604). 22 | 23 | - 5f604c59: - Introduced 'layout-editor' for real-time drag-and-drop layout customization and theme generation in EmbeddedChat. 24 | 25 | See [#607](https://github.com/RocketChat/EmbeddedChat/pull/607). 26 | 27 | ### Patch Changes 28 | 29 | - 5f604c59: - Optimized package size for bundling. 30 | 31 | See [#606](https://github.com/RocketChat/EmbeddedChat/pull/606). 32 | -------------------------------------------------------------------------------- /packages/ui-elements/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: false, 7 | bugfixes: true, 8 | targets: { browsers: '> 0.25%, ie 11, not op_mini all, not dead' }, 9 | }, 10 | ], 11 | '@babel/preset-react', 12 | '@emotion/babel-preset-css-prop', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/ActionButton/index.js: -------------------------------------------------------------------------------- 1 | export { default as ActionButton } from './ActionButton'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Avatar/Avatar.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import { Avatar } from './Avatar'; 4 | import DefaultTheme from '../../theme/DefaultTheme'; 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 7 | export default { 8 | title: 'Components/Avatar', 9 | component: Avatar, 10 | }; 11 | 12 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 13 | export const Default = { 14 | render: (args) => ( 15 | 16 | 17 | 18 | ), 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Avatar/Avatar.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const getAvatarStyles = (theme) => { 4 | const styles = { 5 | imageAvatar: (size) => css` 6 | border-radius: ${theme.radius}; 7 | height: ${size}; 8 | width: ${size}; 9 | `, 10 | 11 | fallbackContainer: (size) => css` 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | background-color: ${theme.colors.primary}; 16 | color: ${theme.colors.primaryForeground}; 17 | border-radius: ${theme.radius}; 18 | height: ${size}; 19 | width: ${size}; 20 | `, 21 | }; 22 | 23 | return styles; 24 | }; 25 | 26 | export const avatarContainerStyles = { 27 | avatarContainer: css` 28 | display: inline-flex; 29 | vertical-align: middle; 30 | cursor: pointer; 31 | `, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Avatar/AvatarContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { appendClassNames } from '../../lib/appendClassNames'; 3 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 4 | import { Box } from '../Box'; 5 | import { avatarContainerStyles as styles } from './Avatar.styles'; 6 | 7 | export const AvatarContainer = ({ 8 | title, 9 | children, 10 | className = '', 11 | style = {}, 12 | ...props 13 | }) => { 14 | const { classNames, styleOverrides } = useComponentOverrides( 15 | 'AvatarContainer', 16 | className, 17 | style 18 | ); 19 | props.className = appendClassNames('ec-avatar-container', [classNames]); 20 | 21 | props.style = styleOverrides; 22 | 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Avatar/index.js: -------------------------------------------------------------------------------- 1 | export * from './Avatar'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Box/Box.style.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const styles = { 4 | box: css` 5 | margin: 0; 6 | padding: 0; 7 | border-width: 0; 8 | box-sizing: border-box; 9 | border-style: solid; 10 | border-color: currentColor; 11 | outline: none; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | flex: 0 1 auto; 15 | `, 16 | }; 17 | 18 | export default styles; 19 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Box/index.js: -------------------------------------------------------------------------------- 1 | export { default as Box } from './Box'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default as Button } from './Button'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/CheckBox/CheckBox.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import { CheckBox } from '.'; 4 | import DefaultTheme from '../../theme/DefaultTheme'; 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 7 | export default { 8 | title: 'Components/CheckBox', 9 | component: CheckBox, 10 | }; 11 | 12 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 13 | export const Default = { 14 | args: { 15 | checked: true, 16 | }, 17 | render: (args) => ( 18 | 19 | 20 | 21 | ), 22 | }; 23 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/CheckBox/CheckBox.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const getCheckBoxStyles = (theme) => { 4 | const styles = { 5 | main: (checked) => css` 6 | display: inline-block; 7 | color: ${theme.colors.primaryForeground}; 8 | background-color: ${checked ? theme.colors.primary : 'none'}; 9 | height: 1rem; 10 | width: 1rem; 11 | box-sizing: border-box; 12 | border: ${!checked ? `2px solid ${theme.colors.border}` : `none`}; 13 | border-radius: ${theme.radius}; 14 | cursor: pointer; 15 | outline: none; 16 | &:active { 17 | outline: 0.3px solid ${theme.colors.ring}; 18 | } 19 | `, 20 | }; 21 | 22 | return styles; 23 | }; 24 | 25 | export default getCheckBoxStyles; 26 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/CheckBox/index.js: -------------------------------------------------------------------------------- 1 | export { default as CheckBox } from './CheckBox'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Divider/Divider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 4 | import useDividerStyles from './Divider.styles'; 5 | import { useTheme } from '../../hooks'; 6 | 7 | const Divider = ({ className = '', style = {}, ...props }) => { 8 | const { classNames, styleOverrides } = useComponentOverrides('Divider'); 9 | const { theme } = useTheme(); 10 | const styles = useDividerStyles(theme); 11 | 12 | return ( 13 |
19 | ); 20 | }; 21 | 22 | Divider.propTypes = { 23 | className: PropTypes.string, 24 | style: PropTypes.object, 25 | }; 26 | 27 | export default Divider; 28 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Divider/Divider.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import Divider from './Divider'; 4 | import DefaultTheme from '../../theme/DefaultTheme'; 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 7 | export default { 8 | title: 'Components/Divider', 9 | component: Divider, 10 | }; 11 | 12 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 13 | export const Default = { 14 | render: (args) => ( 15 | 16 | 17 | 18 | ), 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Divider/Divider.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const getDividerStyles = (theme) => { 4 | const styles = { 5 | divider: css` 6 | height: 2px; 7 | margin: 0 8px 8px; 8 | border: 0; 9 | border-radius: ${theme.radius}; 10 | background-color: ${theme.colors.secondary}; 11 | `, 12 | }; 13 | 14 | return styles; 15 | }; 16 | 17 | export default getDividerStyles; 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Divider/index.js: -------------------------------------------------------------------------------- 1 | export { default as Divider } from './Divider'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Flex/index.js: -------------------------------------------------------------------------------- 1 | import FlexContainer from './FlexContainer'; 2 | import FlexItem from './FlexItem'; 3 | 4 | export const Flex = { 5 | Container: FlexContainer, 6 | Item: FlexItem, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/GenericModal/GenericModal.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import GenericModal from './GenericModal'; 4 | import DefaultTheme from '../../theme/DefaultTheme'; 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 7 | export default { 8 | title: 'Components/GenericModal', 9 | component: GenericModal, 10 | }; 11 | 12 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 13 | export const Default = { 14 | args: { 15 | children: 'This is a modal content', 16 | onClose: () => {}, 17 | }, 18 | render: (args) => ( 19 | 20 | 21 | 22 | ), 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/GenericModal/index.js: -------------------------------------------------------------------------------- 1 | export { default as GenericModal } from './GenericModal'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Grid/index.js: -------------------------------------------------------------------------------- 1 | export { default as Grid } from './Grid'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Heading/index.js: -------------------------------------------------------------------------------- 1 | export { default as Heading } from './Heading'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Arc.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Arc = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Arc; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/ArrowBack.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ArrowBack = (props) => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default ArrowBack; 16 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Away.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Away = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Away; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Back.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Back = (props) => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default Back; 16 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Check.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Check = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Check; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/ChevronDown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ChevronDown = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default ChevronDown; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/ChevronLeft.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ChevronLeft = (props) => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default ChevronLeft; 16 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Clock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Clock = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Clock; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Cross.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Cross = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Cross; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/DisableRecorder.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const DisabledRecorder = (props) => ( 4 | 13 | {' '} 17 | 18 | ); 19 | 20 | export default DisabledRecorder; 21 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Edit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Edit = (props) => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default Edit; 16 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/EyeClose.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const EyeClose = (props) => ( 4 | 17 | 18 | 19 | 20 | ); 21 | 22 | export default EyeClose; 23 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/EyeOpen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const EyeOpen = (props) => ( 4 | 17 | 18 | 19 | 20 | ); 21 | 22 | export default EyeOpen; 23 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/FormatText.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function FormatText(props) { 4 | return ( 5 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default FormatText; 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Info.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Info = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Info; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Italic.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Italic = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Italic; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Kebab.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Kebab = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Kebab; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Magnifier.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Magnifier = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Magnifier; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Mobile.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Mobile = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Mobile; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Offline.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Offline = (props) => ( 4 | 12 | 19 | 20 | ); 21 | 22 | export default Offline; 23 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Online.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Online = (props) => ( 4 | 17 | ); 18 | 19 | export default Online; 20 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/PinFilled.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const PinFilled = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default PinFilled; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Plus.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Plus = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Plus; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/ReplyDirectly.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ReplyDirectly = (props) => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default ReplyDirectly; 16 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Report.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Report = (props) => ( 4 | 10 | 11 | 12 | 13 | 14 | ); 15 | 16 | export default Report; 17 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/StarFilled.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const StarFilled = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default StarFilled; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/Trash.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Trash = (props) => ( 4 | 10 | 11 | 12 | ); 13 | 14 | export default Trash; 15 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/icons/VideoRecoder.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const VideoRecorder = (props) => ( 4 | 13 | 17 | 18 | ); 19 | 20 | export default VideoRecorder; 21 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Icon/index.js: -------------------------------------------------------------------------------- 1 | export { default as Icon } from './Icon'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Input/index.js: -------------------------------------------------------------------------------- 1 | export { default as Input } from './Input'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/ListBox/index.js: -------------------------------------------------------------------------------- 1 | export { default as ListBox } from './ListBox'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Menu/index.js: -------------------------------------------------------------------------------- 1 | export { default as Menu } from './Menu'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/MessageGenericPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getMessageGenericPreviewStyles } from './MessageGenericPreview.styles'; 3 | import { Box } from '../Box'; 4 | import { useComponentOverrides, useTheme } from '../../hooks'; 5 | 6 | const MessageGenericPreview = (props, className = '', style = {}) => { 7 | const { classNames, styleOverrides } = useComponentOverrides( 8 | 'MessageGenericPreview' 9 | ); 10 | const { theme } = useTheme(); 11 | const styles = getMessageGenericPreviewStyles(theme); 12 | 13 | return ( 14 | 20 | ); 21 | }; 22 | 23 | export default MessageGenericPreview; 24 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/MessageGenericPreview.styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const getMessageGenericPreviewStyles = (theme) => { 4 | const styles = { 5 | container: css` 6 | display: flex; 7 | overflow: hidden; 8 | flex-direction: column; 9 | padding: 0.75rem; 10 | border: 1px solid ${theme.colors.border}; 11 | border-radius: ${theme.radius}; 12 | background-color: ${theme.colors.background}; 13 | `, 14 | }; 15 | 16 | return styles; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/MessageGenericPreviewDescription.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '../Box'; 3 | import { useComponentOverrides } from '../../hooks'; 4 | 5 | const MessageGenericPreviewDescription = ({ 6 | children, 7 | clamp = false, 8 | className = '', 9 | style = {}, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides( 13 | 'MessageGenericPreviewDescription' 14 | ); 15 | 16 | return ( 17 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default MessageGenericPreviewDescription; 30 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/MessageGenericPreviewFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box } from '../Box'; 4 | import { useComponentOverrides } from '../../hooks'; 5 | 6 | const MessageGenericPreviewFooter = ({ 7 | children, 8 | className = '', 9 | style = {}, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides( 13 | 'MessageGenericPreviewFooter' 14 | ); 15 | 16 | return ( 17 | 25 | {children} 26 | 27 | ); 28 | }; 29 | 30 | export default MessageGenericPreviewFooter; 31 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/MessageGenericPreviewThumb.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '../Box'; 3 | import { useComponentOverrides } from '../../hooks'; 4 | 5 | const MessageGenericPreviewThumb = (className = '', style = {}, ...props) => { 6 | const { classNames, styleOverrides } = useComponentOverrides( 7 | 'MessageGenericPreviewThumb' 8 | ); 9 | return ( 10 | 15 | ); 16 | }; 17 | 18 | export default MessageGenericPreviewThumb; 19 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MessageGenericPreview/index.js: -------------------------------------------------------------------------------- 1 | export { default as MessageGenericPreview } from './MessageGenericPreview'; 2 | export { default as MessageGenericPreviewCoverImage } from './MessageGenericPreviewCoverImage'; 3 | export { default as MessageGenericPreviewThumb } from './MessageGenericPreviewThumb'; 4 | export { default as MessageGenericPreviewTitle } from './MessageGenericPreviewTitle'; 5 | export { default as MessageGenericPreviewContent } from './MessageGenericPreviewContent'; 6 | export { default as MessageGenericPreviewDescription } from './MessageGenericPreviewDescription'; 7 | export { default as MessageGenericPreviewFooter } from './MessageGenericPreviewFooter'; 8 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalBackdrop.js: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import { Box } from '../Box'; 3 | import { getModalBackdropStyles } from './Modal.styles'; 4 | import { useTheme } from '../../hooks'; 5 | 6 | const ModalBackdrop = forwardRef(({ children, onClick = () => {} }, ref) => { 7 | const { theme } = useTheme(); 8 | const styles = getModalBackdropStyles(theme); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }); 16 | ModalBackdrop.displayName = 'ModalBackdrop'; 17 | export { ModalBackdrop }; 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalClose.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | import React from 'react'; 3 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 4 | import { ActionButton } from '../ActionButton'; 5 | import { Box } from '../Box'; 6 | 7 | export const ModalClose = ({ 8 | className = '', 9 | style = {}, 10 | children, 11 | onClick = () => {}, 12 | tabIndex, 13 | ...props 14 | }) => { 15 | const { classNames, styleOverrides } = useComponentOverrides('ModalClose'); 16 | 17 | return ( 18 | 26 | 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalContent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 3 | import { Box } from '../Box'; 4 | import { ModalContentStyles as styles } from './Modal.styles'; 5 | 6 | export const ModalContent = ({ 7 | className = '', 8 | style = {}, 9 | children, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides('ModalContent'); 13 | 14 | return ( 15 | 21 | {children} 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 3 | import { Box } from '../Box'; 4 | import { ModalFooterStyles as styles } from './Modal.styles'; 5 | 6 | export const ModalFooter = ({ 7 | className = '', 8 | style = {}, 9 | children, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides('ModalFooter'); 13 | 14 | return ( 15 | 21 | {children} 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 3 | import { Box } from '../Box'; 4 | import { ModalHeaderStyles as styles } from './Modal.styles'; 5 | 6 | export const ModalHeader = ({ 7 | className = '', 8 | style = {}, 9 | children, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides('ModalHeader'); 13 | 14 | return ( 15 | 21 | {children} 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalThumb.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 3 | import { Box } from '../Box'; 4 | import { Avatar } from '../Avatar'; 5 | import { ModalThumbStyles as styles } from './Modal.styles'; 6 | 7 | export const ModalThumb = ({ className = '', style = {}, url, ...props }) => { 8 | const { classNames, styleOverrides } = useComponentOverrides('ModalThumb'); 9 | return ( 10 | 16 | 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/ModalTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useComponentOverrides from '../../hooks/useComponentOverrides'; 3 | import { Box } from '../Box'; 4 | import { ModalTitleStyles as styles } from './Modal.styles'; 5 | 6 | export const ModalTitle = ({ 7 | className = '', 8 | style = {}, 9 | children, 10 | ...props 11 | }) => { 12 | const { classNames, styleOverrides } = useComponentOverrides('ModalTitle'); 13 | return ( 14 | 20 | {children} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Modal/index.js: -------------------------------------------------------------------------------- 1 | import { Modal } from './Modal'; 2 | import { ModalClose } from './ModalClose'; 3 | import { ModalContent } from './ModalContent'; 4 | import { ModalFooter } from './ModalFooter'; 5 | import { ModalHeader } from './ModalHeader'; 6 | import { ModalTitle } from './ModalTitle'; 7 | import { ModalBackdrop } from './ModalBackdrop'; 8 | import { ModalThumb } from './ModalThumb'; 9 | 10 | Modal.Footer = ModalFooter; 11 | Modal.Header = ModalHeader; 12 | Modal.Content = ModalContent; 13 | Modal.Thumb = ModalThumb; 14 | Modal.Title = ModalTitle; 15 | Modal.Close = ModalClose; 16 | 17 | export { 18 | Modal, 19 | ModalHeader, 20 | ModalContent, 21 | ModalFooter, 22 | ModalThumb, 23 | ModalTitle, 24 | ModalClose, 25 | ModalBackdrop, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MultiSelect/MultiSelect.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import DefaultTheme from '../../theme/DefaultTheme'; 4 | import MultiSelect from './MultiSelect'; 5 | 6 | export default { 7 | title: 'Components/MultiSelect', 8 | component: MultiSelect, 9 | }; 10 | 11 | export const Default = { 12 | args: { 13 | options: [ 14 | { value: 'option1', label: 'Option 1' }, 15 | { value: 'option2', label: 'Option 2' }, 16 | { value: 'option3', label: 'Option 3' }, 17 | ], 18 | placeholder: 'Select options...', 19 | value: '', 20 | disabled: false, 21 | }, 22 | render: (args) => ( 23 | 24 | 25 | 26 | ), 27 | }; 28 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/MultiSelect/index.js: -------------------------------------------------------------------------------- 1 | export { default as MultiSelect } from './MultiSelect'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Popup/index.js: -------------------------------------------------------------------------------- 1 | export { default as Popup } from './Popup'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Sidebar/MinimalSidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '../Box'; 3 | import { getSidebarStyles } from './Sidebar.styles'; 4 | import { useTheme } from '../../hooks'; 5 | 6 | const MinimalSidebar = ({ children }) => { 7 | const { theme } = useTheme(); 8 | const styles = getSidebarStyles(theme); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }; 16 | 17 | export default MinimalSidebar; 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Sidebar/SidebarFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '../Box'; 3 | 4 | const SidebarFooter = ({ children, ...props }) => ( 5 | {children} 6 | ); 7 | 8 | export default SidebarFooter; 9 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Sidebar/index.js: -------------------------------------------------------------------------------- 1 | export { default as MinimalSidebar } from './MinimalSidebar'; 2 | export { default as SidebarContent } from './SidebarContent'; 3 | export { default as SidebarFooter } from './SidebarFooter'; 4 | export { default as SidebarHeader } from './SidebarHeader'; 5 | export { default as Sidebar } from './Sidebar'; 6 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Skeleton/Skeleton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '../Box'; 3 | import getSkeletonStyles from './Skeleton.styles'; 4 | import { useTheme } from '../../hooks'; 5 | 6 | const Skeleton = ({ variant = 'text', height, width, ...props }) => { 7 | const { theme } = useTheme(); 8 | const styles = getSkeletonStyles(theme); 9 | return ( 10 | 16 | ); 17 | }; 18 | export default Skeleton; 19 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Skeleton/Skeleton.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import DefaultTheme from '../../theme/DefaultTheme'; 4 | 5 | import { Skeleton } from '.'; 6 | 7 | export default { 8 | title: 'Components/Skeleton', 9 | component: Skeleton, 10 | }; 11 | 12 | const Template = (args) => ( 13 | 14 | 15 | 16 | ); 17 | 18 | export const RectVariant = Template.bind({}); 19 | RectVariant.args = { 20 | variant: 'rect', 21 | width: '50%', 22 | height: 100, 23 | }; 24 | 25 | export const CircleVariant = Template.bind({}); 26 | CircleVariant.args = { 27 | variant: 'circle', 28 | width: 16, 29 | height: 16, 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Skeleton/index.js: -------------------------------------------------------------------------------- 1 | export { default as Skeleton } from './Skeleton'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/StaticSelect/StaticSelect.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import DefaultTheme from '../../theme/DefaultTheme'; 4 | import StaticSelect from './StaticSelect'; 5 | 6 | export default { 7 | title: 'Components/StaticSelect', 8 | component: StaticSelect, 9 | }; 10 | 11 | export const Default = { 12 | args: { 13 | options: [ 14 | { value: 'option1', label: 'Option 1' }, 15 | { value: 'option2', label: 'Option 2' }, 16 | { value: 'option3', label: 'Option 3' }, 17 | ], 18 | placeholder: 'Select an option...', 19 | value: '', 20 | disabled: false, 21 | }, 22 | render: (args) => ( 23 | 24 | 25 | 26 | ), 27 | }; 28 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/StaticSelect/index.js: -------------------------------------------------------------------------------- 1 | export { default as StaticSelect } from './StaticSelect'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Throbber/Throbber.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import DefaultTheme from '../../theme/DefaultTheme'; 4 | import { Throbber } from '.'; 5 | 6 | // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction 7 | export default { 8 | title: 'Components/Throbber', 9 | component: Throbber, 10 | }; 11 | 12 | // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args 13 | export const Default = { 14 | render: () => ( 15 | 16 | 17 | 18 | ), 19 | }; 20 | 21 | export const Disabled = { 22 | render: () => ( 23 | 24 | 25 | 26 | ), 27 | }; 28 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Throbber/index.js: -------------------------------------------------------------------------------- 1 | export { default as Throbber } from './Throbber'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/ToastBar/index.js: -------------------------------------------------------------------------------- 1 | export { default as ToastBar } from './ToastBar'; 2 | export { default as ToastBarProvider } from './ToastBarProvider'; 3 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Tooltip/Tooltip.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from '../../context/ThemeContextProvider'; 3 | import DefaultTheme from '../../theme/DefaultTheme'; 4 | import Tooltip from './Tooltip'; 5 | import { ActionButton } from '../ActionButton'; 6 | import { Icon } from '../Icon'; 7 | 8 | export default { 9 | title: 'Components/Tooltip', 10 | component: Tooltip, 11 | }; 12 | 13 | const Template = (args) => ( 14 | 15 | 16 | 17 | ); 18 | 19 | export const Default = Template.bind({}); 20 | Default.args = { 21 | children: ( 22 | {}}> 23 | 24 | 25 | ), 26 | text: 'Pinned', 27 | position: 'bottom', 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ui-elements/src/components/Tooltip/index.js: -------------------------------------------------------------------------------- 1 | export { default as Tooltip } from './Tooltip'; 2 | -------------------------------------------------------------------------------- /packages/ui-elements/src/context/ToastContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const ToastContext = createContext(); 4 | 5 | export default ToastContext; 6 | -------------------------------------------------------------------------------- /packages/ui-elements/src/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { default as useComponentOverrides } from './useComponentOverrides'; 2 | export { default as useTheme } from './useTheme'; 3 | export { default as useToastBarDispatch } from './useToastBarDispatch'; 4 | -------------------------------------------------------------------------------- /packages/ui-elements/src/hooks/useToastBarDispatch.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import ToastContext from '../context/ToastContext'; 3 | 4 | const useToastBarDispatch = () => { 5 | const { dispatchToast } = useContext(ToastContext); 6 | return dispatchToast; 7 | }; 8 | 9 | export default useToastBarDispatch; 10 | -------------------------------------------------------------------------------- /packages/ui-elements/src/index.js: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export { ThemeProvider } from './context/ThemeContextProvider'; 3 | export * from './hooks'; 4 | export * from './lib'; 5 | -------------------------------------------------------------------------------- /packages/ui-elements/src/lib/appendClassNames.js: -------------------------------------------------------------------------------- 1 | const isStringArray = (value) => Array.isArray(value); 2 | 3 | export const appendClassNames = (className, currentClassNames) => { 4 | if (isStringArray(currentClassNames)) { 5 | return [className, ...currentClassNames]; 6 | } 7 | 8 | if (currentClassNames) { 9 | return `${className} ${currentClassNames}`; 10 | } 11 | 12 | return className; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/ui-elements/src/lib/color.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | export function darken(color, amount = 0) { 4 | color = Color(color); 5 | 6 | return color.darken(amount).hexa(); 7 | } 8 | 9 | export function lighten(color, amount = 0) { 10 | color = Color(color); 11 | return color.lighten(amount).hexa(); 12 | } 13 | 14 | export function alpha(color, amount) { 15 | color = Color(color); 16 | return color.alpha(amount).hexa(); 17 | } 18 | -------------------------------------------------------------------------------- /packages/ui-elements/src/lib/index.js: -------------------------------------------------------------------------------- 1 | export { appendClassNames } from './appendClassNames'; 2 | export { darken, lighten, alpha } from './color'; 3 | export { default as ReactPortal } from './reactPortal'; 4 | -------------------------------------------------------------------------------- /packages/ui-elements/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "./types", 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "rootDir": "src", 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "include": ["src/**/*"], 17 | "exclude": ["node_modules", "dist", "stories", "**/*.stories.js"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui-kit/.eslintignore: -------------------------------------------------------------------------------- 1 | rollup.config.js 2 | *.test.js 3 | dist/ -------------------------------------------------------------------------------- /packages/ui-kit/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log -------------------------------------------------------------------------------- /packages/ui-kit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui-kit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @embeddedchat/ui-kit 2 | 3 | ## 0.1.2 4 | 5 | ### Patch Changes 6 | 7 | - Version Bump 8 | - Updated dependencies 9 | - @embeddedchat/markups@0.1.2 10 | 11 | ## 0.1.1 12 | 13 | ### Patch Changes 14 | 15 | - Version Bump 16 | - Updated dependencies 17 | - @embeddedchat/markups@0.1.1 18 | 19 | ## 0.1.0 20 | 21 | ### Minor Changes 22 | 23 | - 5f604c59: - Separated components, markups, and ui-kit into individual monorepos. 24 | 25 | See [#604](https://github.com/RocketChat/EmbeddedChat/pull/604). 26 | 27 | ### Patch Changes 28 | 29 | - 5f604c59: - Optimized package size for bundling. 30 | 31 | See [#606](https://github.com/RocketChat/EmbeddedChat/pull/606). 32 | 33 | - Updated dependencies [5f604c59] 34 | - Updated dependencies [5f604c59] 35 | - @embeddedchat/markups@0.1.0 36 | -------------------------------------------------------------------------------- /packages/ui-kit/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: false, 7 | bugfixes: true, 8 | targets: { browsers: '> 0.25%, ie 11, not op_mini all, not dead' }, 9 | }, 10 | ], 11 | '@babel/preset-react', 12 | '@emotion/babel-preset-css-prop', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/ui-kit/src/blocks/DividerBlock.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { Divider } from '@embeddedchat/ui-elements'; 3 | 4 | const DividerBlock = ({ className }) => ; 5 | 6 | export default memo(DividerBlock); 7 | -------------------------------------------------------------------------------- /packages/ui-kit/src/blocks/SectionBlock.Fields.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as UiKit from '@rocket.chat/ui-kit'; 3 | import { Grid } from '@embeddedchat/ui-elements'; 4 | 5 | const breakpoints = { 6 | xs: 4, 7 | sm: 4, 8 | md: 4, 9 | lg: 6, 10 | xl: 6, 11 | }; 12 | 13 | const Fields = ({ fields, surfaceRenderer }) => ( 14 | 15 | {fields.map((field, i) => ( 16 | 17 | {surfaceRenderer.renderTextObject(field, 0, UiKit.BlockContext.NONE)} 18 | 19 | ))} 20 | 21 | ); 22 | 23 | export default Fields; 24 | -------------------------------------------------------------------------------- /packages/ui-kit/src/contexts/SurfaceContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const SurfaceContext = createContext('message'); 4 | -------------------------------------------------------------------------------- /packages/ui-kit/src/contexts/UiKitContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const UiKitContext = createContext({ 4 | action: () => undefined, 5 | updateState: () => undefined, 6 | appId: 'core', 7 | values: {}, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/ui-kit/src/elements/ContextElement/ContextElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | import { ContextElementItem } from './ContextElementItem'; 5 | 6 | export const ContextElement = ({ block, surfaceRenderer, className }) => ( 7 | 17 | {block.elements.map((element, i) => ( 18 | 24 | ))} 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /packages/ui-kit/src/elements/ContextElement/index.js: -------------------------------------------------------------------------------- 1 | export * from './ContextElement'; 2 | -------------------------------------------------------------------------------- /packages/ui-kit/src/elements/ImageElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as UiKit from '@rocket.chat/ui-kit'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | import { ImageElementStyles as styles } from './elements.styles'; 5 | 6 | const ImageElement = ({ block, context }) => { 7 | const size = 8 | (context === UiKit.BlockContext.SECTION && 88) || 9 | (context === UiKit.BlockContext.CONTEXT && 20) || 10 | undefined; 11 | 12 | if (!size) { 13 | return null; 14 | } 15 | 16 | return ; 17 | }; 18 | 19 | export default ImageElement; 20 | -------------------------------------------------------------------------------- /packages/ui-kit/src/hooks/useSurfaceType.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { SurfaceContext } from '../contexts/SurfaceContext'; 3 | 4 | export const useSurfaceType = () => useContext(SurfaceContext); 5 | -------------------------------------------------------------------------------- /packages/ui-kit/src/index.js: -------------------------------------------------------------------------------- 1 | export * from './hooks/useUiKitState'; 2 | export * from './contexts/UiKitContext'; 3 | export * from './surfaces'; 4 | export { UiKitComponent } from './utils/UiKitComponent'; 5 | export { extractInitialStateFromLayout } from './utils/extractInitialStateFromLayout'; 6 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/ContextualBarSurface.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | import { Surface } from './Surface'; 5 | 6 | const ContextualBarSurface = ({ children }) => ( 7 | 8 | 15 | {children} 16 | 17 | 18 | ); 19 | 20 | export default ContextualBarSurface; 21 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/ContextualBarSurfaceRenderer.js: -------------------------------------------------------------------------------- 1 | import { FuselageSurfaceRenderer } from './FuselageSurfaceRenderer'; 2 | 3 | class ContextualBarSurfaceRenderer extends FuselageSurfaceRenderer { 4 | constructor() { 5 | super([ 6 | 'actions', 7 | 'context', 8 | 'divider', 9 | 'image', 10 | 'input', 11 | 'section', 12 | 'preview', 13 | 'callout', 14 | 'tab_navigation', 15 | ]); 16 | } 17 | } 18 | 19 | export { ContextualBarSurfaceRenderer }; 20 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/MessageSurface.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '@embeddedchat/ui-elements'; 3 | import { Surface } from './Surface'; 4 | 5 | const MessageSurface = ({ children }) => ( 6 | 7 | {children} 8 | 9 | ); 10 | 11 | export default MessageSurface; 12 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/MessageSurfaceRenderer.js: -------------------------------------------------------------------------------- 1 | import { FuselageSurfaceRenderer } from './FuselageSurfaceRenderer'; 2 | 3 | export class FuselageMessageSurfaceRenderer extends FuselageSurfaceRenderer { 4 | constructor() { 5 | super([ 6 | 'actions', 7 | 'context', 8 | 'divider', 9 | 'image', 10 | 'input', 11 | 'section', 12 | 'preview', 13 | 'video_conf', 14 | ]); 15 | } 16 | 17 | video_conf() { 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/ModalSurface.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/react'; 3 | import { Box } from '@embeddedchat/ui-elements'; 4 | import { Surface } from './Surface'; 5 | 6 | const ModalSurface = ({ children }) => ( 7 | 8 | 16 | {children} 17 | 18 | 19 | ); 20 | 21 | export default ModalSurface; 22 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/ModalSurfaceRenderer.js: -------------------------------------------------------------------------------- 1 | import { FuselageSurfaceRenderer } from './FuselageSurfaceRenderer'; 2 | 3 | export class ModalSurfaceRenderer extends FuselageSurfaceRenderer { 4 | constructor() { 5 | super([ 6 | 'actions', 7 | 'context', 8 | 'divider', 9 | 'image', 10 | 'input', 11 | 'section', 12 | 'preview', 13 | 'callout', 14 | ]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/Surface.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SurfaceContext } from '../contexts/SurfaceContext'; 3 | 4 | export const Surface = ({ children, type }) => ( 5 | {children} 6 | ); 7 | -------------------------------------------------------------------------------- /packages/ui-kit/src/surfaces/createSurfaceRenderer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const createSurfaceRenderer = (SurfaceComponent, surfaceRenderer) => 4 | function Surface(blocks, conditions = {}) { 5 | return ( 6 | 7 | {surfaceRenderer.render(blocks, { 8 | engine: 'rocket.chat', 9 | ...conditions, 10 | })} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/ui-kit/src/utils/UiKitComponent.js: -------------------------------------------------------------------------------- 1 | export const UiKitComponent = ({ render, blocks }) => render(blocks); 2 | -------------------------------------------------------------------------------- /packages/ui-kit/src/utils/fromTextObjectToString.js: -------------------------------------------------------------------------------- 1 | import * as UiKit from '@rocket.chat/ui-kit'; 2 | import { renderToStaticMarkup } from 'react-dom/server'; 3 | 4 | export const fromTextObjectToString = (surfaceRenderer, textObject, index) => { 5 | const element = surfaceRenderer.renderTextObject( 6 | textObject, 7 | index, 8 | UiKit.BlockContext.NONE 9 | ); 10 | 11 | if (!element) { 12 | return undefined; 13 | } 14 | 15 | return renderToStaticMarkup(element); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/ui-kit/src/utils/getInitialValue.js: -------------------------------------------------------------------------------- 1 | export const Value = { value: undefined, blockId: undefined }; 2 | 3 | const hasInitialValue = (element) => 'initialValue' in element; 4 | 5 | const hasInitialTime = (element) => 'initialTime' in element; 6 | 7 | const hasInitialDate = (element) => 'initialDate' in element; 8 | 9 | const hasInitialOption = (element) => 'initialOption' in element; 10 | 11 | const hasInitialOptions = (element) => 'initialOptions' in element; 12 | 13 | export const getInitialValue = (element) => 14 | (hasInitialValue(element) && element.initialValue) || 15 | (hasInitialTime(element) && element.initialTime) || 16 | (hasInitialDate(element) && element.initialDate) || 17 | (hasInitialOption(element) && element.initialOption.value) || 18 | (hasInitialOptions(element) && 19 | element.initialOptions.map((option) => option.value)) || 20 | undefined; 21 | -------------------------------------------------------------------------------- /packages/ui-kit/src/utils/hasElement.js: -------------------------------------------------------------------------------- 1 | const LayoutBlockWithElement = (block) => 'element' in block; 2 | export const hasElement = (block) => LayoutBlockWithElement(block); 3 | -------------------------------------------------------------------------------- /packages/ui-kit/src/utils/hasElements.js: -------------------------------------------------------------------------------- 1 | const LayoutBlockWithElements = (block) => 2 | 'elements' in block && Array.isArray(block.elements); 3 | 4 | export const hasElements = (block) => LayoutBlockWithElements(block); 5 | -------------------------------------------------------------------------------- /packages/ui-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "./types", 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "rootDir": "src", 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "include": ["src/**/*"], 17 | "exclude": ["node_modules", "dist", "stories", "**/*.stories.js"] 18 | } 19 | -------------------------------------------------------------------------------- /scripts/node-check.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const nvmrcPath = path.join(__dirname, '../.nvmrc'); 5 | const expectedVersion = fs.readFileSync(nvmrcPath).toString().trim(); 6 | 7 | if (process.version !== expectedVersion) { 8 | console.error(`Error: Required Node.js version is ${expectedVersion}, but found ${process.version}. You can use nvm to manage multiple node versions on a system.`); 9 | process.exit(1); 10 | } 11 | --------------------------------------------------------------------------------