PromucFlow_constructor/app/client/src/components/ads/MentionsInput.tsx
Pranav Kanade b778b83ac4
refactor: admin settings (#9906)
* refactor admin settings feature

* separated save-restart bar to separate component

* created new CE dir to facilitate code split

* created separate ee dir and exporting everything we have in ce file.

* little mod

* minor fix

* splitting settings types config

* using object literals for category types instead of enums

* CE: support use of component for each category

* minor style fix

* authentication page  UI changes implemented

* github signup doc url added back

* removed comments

* routing updates

* made subcategories listing in left pane optional

* added muted saml to auth listing

* added breadcrumbs and enabled button

* created separate component for auth page and auth config

* added callout and disconnect components

* updated breadcrumbs component

* minor updates to common components

* updated warning callout and added icon

* ce: test cases fixed

* updated test file name

* warning banner callout added on auth page

* updated callout banner for form login

* CE: Split config files

* CE: moved the window declaration in EE file as its dependency will be updated in EE

* CE: Splitting ApiConstants and SocialLogin constants

* CE: split login page

* CE: moved getSocialLoginButtonProps func to EE file as it's dependencies will be updated in EE

* added key icon

* CE: created a factory class to share social auths list

* Minor style fix for social btns

* Updated the third party auth styles

* Small fixes to styling

* ce: splitting forms constants

* breadcrumbs implemented for all pages in admin settings

* Settings breadcrumbs separated

* splitted settings breadcrumbs between ce and ee

* renamed default import

* minor style fix

* added login form config.

* updated login/signup pages to use form login disabled config

* removed common functionality outside

* implemented breadcrumb component from scratch without using blueprint

* removed unwanted code

* Small style update

* updated breadcrumb categories file name and breadcrumb icon

* added cypress tests for admin settings auth page

* added comments

* update locator for upgrade button

* added link for intercom on upgrade button

* removed unnecessary file

* minor style fix

* style fix for auth option cards

* split messages constant

* fixed imports for message constants splitting.

* added message constants

* updated unit test cases

* fixed messages import in cypress index

* fixed messages import again, cypress fails to read re-exported objs.

* added OIDC auth method on authentication page

* updated import statements from ee to @appsmith

* removed dead code

* updated read more link UI

* PR comments fixes

* some UI fixes

* used color and fonts from theme

* fixed some imports

* fixed some imports

* removed warning imports

* updated OIDC logo and auth method desc copies

* css changes

* css changes

* css changes

* updated cypress test for breadcrumb

* moved callout component to ads as calloutv2

* UI changes for form fields

* updated css for spacing between form fields

* added sub-text on auth pages

* added active class for breadcrumb item

* added config for disable signup toggle and fixed UI issues of restart banner

* fixed admin settings page bugs

* assigned true as default state for signup

* fixed messages import statements

* updated code for PR comments related suggestions

* reverted file path change in cypress support

* updated cypress test

* updated cypress test

Co-authored-by: Ankita Kinger <ankita@appsmith.com>
2022-02-11 23:38:46 +05:30

257 lines
6.7 KiB
TypeScript

import React, { useCallback, useMemo, useRef, useState } from "react";
import { DraftHandleValue, EditorState } from "draft-js";
import Editor from "@draft-js-plugins/editor";
import ProfileImage, { Profile } from "pages/common/ProfileImage";
import createMentionPlugin, { MentionData } from "@draft-js-plugins/mention";
import styled from "styled-components";
import "@draft-js-plugins/mention/lib/plugin.css";
import "draft-js/dist/Draft.css";
import { getTypographyByKey } from "constants/DefaultTheme";
import { EntryComponentProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Entry/Entry";
import Icon from "components/ads/Icon";
import { INVITE_A_NEW_USER, createMessage } from "@appsmith/constants/messages";
import { USER_PHOTO_URL } from "constants/userConstants";
import scrollIntoView from "scroll-into-view-if-needed";
import { useEffect } from "react";
const StyledMention = styled.span`
color: ${(props) => props.theme.colors.comments.mention};
`;
export function MentionComponent(props: {
children: React.ReactNode;
entityKey: string;
}) {
const { children } = props;
return <StyledMention>@{children}</StyledMention>;
}
export enum Trigger {
"@" = "@",
"+" = "+",
}
const StyledSuggestionsComponent = styled.div<{ isFocused?: boolean }>`
display: flex;
width: 250px;
padding: ${(props) =>
`${props.theme.spaces[4]}px ${props.theme.spaces[6]}px`};
& ${Profile} {
margin-right: ${(props) => props.theme.spaces[4]}px;
}
align-items: center;
&:hover {
background-color: ${(props) => props.theme.colors.mentionSuggestion.hover};
}
${(props) =>
props.isFocused
? `
background-color: ${props.theme.colors.mentionSuggestion.hover};
`
: ""}
`;
const Name = styled.div`
${(props) => getTypographyByKey(props, "h5")}
color: ${(props) => props.theme.colors.mentionSuggestion.nameText};
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
`;
const Username = styled.div`
${(props) => getTypographyByKey(props, "p3")}
color: ${(props) => props.theme.colors.mentionSuggestion.usernameText};
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
`;
const PlusCircle = styled.div`
width: 25px;
height: 25px;
display: flex;
border-radius: 50%;
align-items: center;
justify-content: center;
background-color: ${(props) =>
props.theme.colors.mentionsInput.mentionsInviteBtnPlusIcon};
& svg path {
stroke: #fff;
}
margin-right: ${(props) => props.theme.spaces[4]}px;
flex-shrink: 0;
`;
const SuggestionRightContainer = styled.div`
flex: 1;
min-width: 0;
`;
function SuggestionComponent(props: EntryComponentProps) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { theme, ...parentProps } = props;
const { user } = props.mention;
const mentionRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (parentProps.isFocused && mentionRef.current) {
scrollIntoView(mentionRef.current, {
scrollMode: "if-needed",
behavior: "smooth",
block: "nearest",
inline: "nearest",
});
}
}, [parentProps.isFocused]);
if (props.mention?.isInviteTrigger) {
return (
<StyledSuggestionsComponent ref={mentionRef} {...parentProps}>
<PlusCircle>
<Icon fillColor="#fff" name="plus" />
</PlusCircle>
<SuggestionRightContainer>
<Name>{createMessage(INVITE_A_NEW_USER)}</Name>
<Username>{props.mention.name}</Username>
</SuggestionRightContainer>
</StyledSuggestionsComponent>
);
}
return (
<StyledSuggestionsComponent ref={mentionRef} {...parentProps}>
<div style={{ flexShrink: 0 }}>
<ProfileImage
side={25}
source={`/api/${USER_PHOTO_URL}/${user?.username}`}
userName={user?.username || ""}
/>
</div>
<SuggestionRightContainer>
<Name>{props.mention.name}</Name>
<Username>{user?.username}</Username>
</SuggestionRightContainer>
</StyledSuggestionsComponent>
);
}
const StyledContainer = styled.div`
overflow: auto;
flex: 1;
.mentions-list {
position: absolute;
background: #fff;
border-radius: 2px;
box-shadow: 0px 4px 30px 0px rgb(220 220 220);
cursor: pointer;
display: flex;
flex-direction: column;
overflow: auto;
max-height: 300px;
z-index: 2;
}
`;
type Props = {
suggestions: Array<MentionData>;
onSubmit: (editorState: EditorState) => void;
editorState: EditorState;
setEditorState: (editorState: EditorState) => void;
readOnly?: boolean;
onSearchSuggestions: (props: { value: string; trigger: string }) => void;
autoFocus: boolean;
placeholder?: string;
onAddMention?: (mention: MentionData) => void;
};
function MentionsInput({
autoFocus,
editorState,
onAddMention,
onSearchSuggestions,
onSubmit,
placeholder,
setEditorState,
suggestions,
}: Props) {
const ref = useRef<Editor | null>(null);
const [open, setOpen] = useState(false);
const { MentionSuggestions, plugins } = useMemo(() => {
const mentionPlugin = createMentionPlugin({
mentionRegExp: "[\\w@\\._]",
mentionTrigger: ["@", "+"],
mentionComponent: MentionComponent,
theme: {
mentionSuggestions: "mentions-list",
},
});
const { MentionSuggestions } = mentionPlugin;
return { plugins: [mentionPlugin], MentionSuggestions };
}, []);
const onOpenChange = useCallback((_open: boolean) => {
setOpen(_open);
}, []);
const handleReturn = useCallback(
(e: React.KeyboardEvent, editorState: EditorState): DraftHandleValue => {
if (!e.nativeEvent.shiftKey && !open) {
onSubmit(editorState);
return "handled";
}
return "not-handled";
},
[open],
);
const focusInput = useCallback(() => {
// Forcing focus breaks plugins
// Ref: https://github.com/draft-js-plugins/draft-js-plugins/issues/800
setTimeout(() => ref.current?.focus());
}, []);
const setRef = useCallback((editorRef) => {
ref.current = editorRef;
if (autoFocus) focusInput();
}, []);
return (
<StyledContainer onClick={focusInput}>
<Editor
editorKey={"editor"}
editorState={editorState}
handleReturn={handleReturn}
onChange={setEditorState}
placeholder={placeholder}
plugins={plugins}
ref={setRef}
webDriverTestID="mentions-input"
/>
<MentionSuggestions
entryComponent={SuggestionComponent}
onAddMention={(mention: MentionData) => {
if (onAddMention) {
onAddMention(mention);
}
}}
onOpenChange={onOpenChange}
onSearchChange={onSearchSuggestions}
open={open}
suggestions={suggestions}
/>
</StyledContainer>
);
}
export default MentionsInput;