Position Draft js emoji popover on top - reactjs

I'm using the #draft-js-plugins/emoji but I'm having problem positioning the emoji popover on top of the editor. Currently it's showing on bottom like this I want to show this popover on top rather than the bottom.
I found an answer here, but it's not working for me.
const {
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
plugins,
} = useMemo(() => {
const mentionPlugin = createMentionPlugin();
// eslint-disable-next-line no-shadow
const { MentionSuggestions } = mentionPlugin;
const emojiPlugin = createEmojiPlugin({
positionSuggestions: (settings) => {
return {
left: settings.decoratorRect.left + 'px',
top: settings.decoratorRect.top - 40 + 'px', // change this value (40) for manage the distance between cursor and bottom edge of popover
display: 'block',
transform: 'scale(1) translateY(-100%)', // transition popover on the value of its height
transformOrigin: '1em 0% 0px',
transition: 'all 0.25s cubic-bezier(0.3, 1.2, 0.2, 1)',
position: 'fixed',
};
},
useNativeArt: true,
});
// eslint-disable-next-line no-shadow
const { EmojiSuggestions, EmojiSelect } = emojiPlugin;
// eslint-disable-next-line no-shadow
const plugins = [mentionPlugin, emojiPlugin];
return {
plugins,
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
};
}, []);
<div className="flex w-100 items-center">
<div
className={`${editorStyles.editor} flex w-100 pa2`}
onClick={() => {
ref.current!.focus();
}}
>
<Editor
editorKey={'editor'}
editorState={editorState}
handleKeyCommand={(cmd) => handleKeyCommand(cmd)}
keyBindingFn={myKeyBindingFn}
onChange={onChange}
plugins={plugins}
ref={ref}
placeholder="Type your message and hit ENTER to send"
/>
<MentionSuggestions
open={open}
onOpenChange={onOpenChange}
suggestions={suggestions}
onSearchChange={onSearchChange}
onAddMention={() => {
// get the mention object selected
}}
/>
<EmojiSuggestions />
</div>
<div style={{ margin: '0 .5rem' }}>
<EmojiSelect closeOnEmojiSelect />
</div>
<SendIcon
className="pointer"
style={{ color: '#3F61C5', margin: '0 .5rem' }}
onClick={handleSend}
/>
</div>
I also inspected the elements, the css I wrote in
positionSuggestions are not there. Any way I can place this popover on top?

I had the same issue and I have resolved it using the CSS option.
Create a copy of this file in your project: https://github.com/draft-js-plugins/draft-js-plugins/blob/master/packages/emoji/src/theme.ts
Find emojiSelectPopover class and replace it with below:
emojiSelectPopover: css`
padding: 0 0.3em;
position: absolute;
z-index: 1000;
box-sizing: content-box;
background: #fff;
border: 1px solid #e0e0e0;
box-shadow: 0 4px 30px 0 gainsboro;
transform: scale(1) translateY(-100%);
transform-origin: 1em 0% 0px;
top: 65%;
#media (min-width: 320px) and (max-width: 480px) {
right: 0;
top: 50%;
}
Use your custom theme file(which you have created in #1) in the configuration:
const emojiPlugin = createEmojiPlugin({
selectButtonContent: '😊',
theme: defaultTheme // This should be imported from your created theme file
});
For your scenario, you need to move right: 0; outside of the media query as your emoji button is close to the right side of the browser.

Related

How to stop propagation of scroll event in case of a modal view?

I have this modal React component:
import React, { useEffect, useRef } from 'react'
import useOnClickOutside from '../../hooks/useOnClickOutside'
import Button from '../codrop/Button'
import styles from './Modal.module.scss'
var Scroll = require('react-scroll')
var scroll = Scroll.animateScroll
interface Props {
children?: React.ReactNode
title?: string
completion?: Function
[key: string]: any
isMyFlex?: Boolean
}
const Modal = ({ children, title, completion, isMyFlex, ...rest }: Props) => {
return (
<div className="overlay">
<div className="cnt-box cnt-call2 modal">
<div className="caption" style={{ paddingRight: 'unset' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<h2 style={{ fontSize: '30px', fontWeight: '500' }}>{title}</h2>
<span
onClick={(e) => {
completion?.()
}}
style={{
fontFamily: 'Icons',
fontSize: '2rem',
cursor: 'pointer',
}}
>
c
</span>
</div>
{children}
</div>
</div>
</div>
)
}
export default Modal
And following two style:
.overlay {
position: fixed; /* Sit on top of the page content */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); /* Black background with opacity */
z-index: 2000; /* Specify a stack order in case you're using a different order for other elements */
overflow: hidden;
}
.modal {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 470px;
background-color: #fff;
border-radius: 6px;
padding: 30px 50px;
}
When modal / overlay is present, and I scroll in mobil, I see on the bottom, as some pixlel is visilbe from background, that the background i scrolling, though not the foreground.
Is it a way to prevent this scrolling?
I tried to add overflow: hidden; to the overlay style, did not help.
I tried hide overflow when component appears, but has no effect, why?
const Modal = ({ children, title, completion, isMyFlex, ...rest }: Props) => {
useEffect(() => {
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = 'unset'
}
}, [])
By default, trying to scroll an element that has already reached the bottom will scroll the parent. You can use the overscroll-behavior CSS property on the modal to prevent this behavior:
overscroll-behavior: contain;
See https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior for the documentation.

Set navbar fixed on scroll with transition in react.js

I set my navbar fixed on scroll but I want it to reappear with transition instead of bumping right into the screen. I have tried many solutions but the transition effect won't kick in.
I have the following react code:
const [sticky, setSticky] = useState('');
React.useEffect(() => {
window.addEventListener('scroll', stickNavbar);
return () => window.removeEventListener('scroll', stickNavbar);
}, []);
const stickNavbar = () => {
if (window !== undefined) {
let windowHeight = window.scrollY;
windowHeight > 150 ? setSticky({ position: "fixed", top: "0"}) : setSticky({ position: "relative"});
}
};
return(
<Navbar>
<Container style={sticky} fluid className="menu">
</Container>
</Navbar>
)
CSS
.menu {
max-width: 100%;
height: 7rem;
border-bottom: 2px solid rgb(228, 228, 228);
background-color:#f8f9fa;
transition: top 1s;
}
The postition property has to change in order for the effect to kick in. Therefore I added negative values for the top attribute and the same positive amount for margin. To cancel the effect for backward transition I set the css value to 0s.
const stickNavbar = () => {
if (window !== undefined) {
let windowHeight = window.scrollY;
windowHeight > 150 ? setSticky({ position: "fixed", top: "0", marginTop:"0",
transition: "top 1s"}) : setSticky({});
}
};
.menu {
max-width: 100%;
height: 7rem;
border-bottom: 2px solid rgb(228, 228, 228);
background-color:#f8f9fa;
position: relative;
top:-30px;
margin-top: 30px;
transition: top 0s;
z-index: 99999;
}

How to loop a Draggable slider in React

I have a draggable horizontal slider in my current project and I would like to setting up it also to loop continuously. By loop continuously I mean it should respond to the process of showing images one after another when dragging? Right now, I do have only 3 images in my slider and when I drag slider to the left, slider with its 3rd image and a blank white space starts showing just after. Here at this point I want images to get start again continuously from the very beginning i.e. from the 1st image with aim to cover the white blank space.
Apart, one error I'm getting with my existing code is that when I start to drag slider to right side, suddenly a scroll comes up on browser and keep going in never ending state. By never ending state, I mean it still remain on screen when I drag all my 3 images fully in right direction.
So these are the two things I want to apply and want to resolve in my current project. I'm sharing my code below.
src > Routes > Home > Components > Carousel > Components > SliderDataItems > index.js
import React, { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { ZoomInOutlined } from '#ant-design/icons'
import { Images } from '../../../../../../Shared/Assets';
import ImagesIcon from '../../../../../../Components/Cells/ImagesIcon'
gsap.registerPlugin(Draggable);
const pictures = [
{
img: Images.xgallery1,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery2,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery4,
icon: <ZoomInOutlined />
},
];
const Slide = ({ img, icon }) => {
return (
<div className="slide">
<div className="image">
<ImagesIcon src={img} />
<div className="icon">
{icon}
</div>
</div>
</div>
);
};
export const Slider = () => {
const sliderRef = useRef(null);
useEffect(() => {
Draggable.create(sliderRef.current, {
type: "x"
});
}, []);
return (
<div className="slider" ref={sliderRef}>
{pictures.map((item, index) => {
return (
<Slide key={index} img={item.img} icon={item.icon} />
);
})}
</div>
);
};
export default Slider;
src > Routes > Home > Components > Carousel > style.scss
.slider {
display: flex;
cursor: unset !important;
overflow: hidden !important;
.slide {
.image {
position: relative;
img {
width: 100% !important;
height: auto !important;
object-fit: cover;
}
.icon {
transition: 0.5s ease;
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
text-align: center;
span {
svg {
font-size: 30px;
color: #fff;
}
}
}
}
}
.image:hover .icon {
opacity: 1;
}
}
.image:after {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(211, 208, 208, 0.6);
opacity: 0;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.image:hover:after {
opacity: 1;
}
Here's the link of demo (kindly see just above the footer section) for your reference.
Thank you for any help.
For draggle Slider there is a very lightweight JS Carousel package - siema
It is a great, lightweight carousel that is made with JS. There are also other packages built on top of this purely made for React.
In your case, I would offer to try out react-siema.
With it, you can simply use the carousel like that and it will be draggable by default. Plus, no need to load any css.

Button doesn't disappear immediately; need to move mouse during animation using clip-path

I am setting the clipPath property from circle(0%) to circle(100%) using GSAP timeline.
let t1 = useRef();
useEffect(() => {
t1.current = gsap.timeline({
defaults: { duration: 0.5, ease: "Back.easeOut.config(2)" },
});
t1.current.paused(true); //to ensure animation doesn't play immediately
t1.current.to(".overlay", { clipPath: "circle(100%)" });
});
const handleClick = () => {
t1.current.play(); //start the animation
};
const handleClose = () => {
t1.current.reverse(0.2); //reverse the animation from 0.2 seconds
};
Complete React Component code:
import React, { useEffect, useRef } from "react";
import { gsap } from "gsap";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faWindowClose } from "#fortawesome/free-solid-svg-icons";
export default function GSAPFullScreen() {
let t1 = useRef();
useEffect(() => {
t1.current = gsap.timeline({
defaults: { duration: 0.5, ease: "Back.easeOut.config(2)" },
});
t1.current.paused(true); //to ensure animation doesn't play immediately
t1.current.to(".overlay", { clipPath: "circle(100%)" });
}, []);
const handleClick = () => {
t1.current.play(); //start the animation
};
const handleClose = () => {
t1.current.reverse(0.2); //reverse the animation from 0.2 seconds
};
return (
<>
<div
className="overlay"
style={{
clipPath: "circle(0%)",
width: "100%",
height: "100%",
position: "fixed",
overflowY: "scroll",
overflowX: "hidden",
backgroundColor: "purple",
}}
>
<FontAwesomeIcon
icon={faWindowClose}
size="2x"
style={{
position: "absolute",
top: "2rem",
right: "2rem",
color: "white",
cursor: "pointer",
}}
onClick={handleClose}
/>
<div className="container md" style={{ color: "white" }}>
<br />
<div style={{ fontWeight: "bold" }}>This is an amazing Question</div>
<div>What is your question? Can you guess?</div>
<div>Option 1</div>
<div>Option 2</div>
<div>Option 3</div>
<div>Option 4</div>
</div>
</div>
<div className="container" style={{ height: "100vh" }}>
<div className="flex">
<button className="lg p-1 btn" onClick={() => handleClick()}>
Launch Animation
</button>
</div>
</div>
</>
);
}
Relevant CSS:
.container {
max-width: 1100px; /* Ensures heading is in center beyond 1100px*/
margin: 0 auto; /* Ensures to keep the 1100px container in middle of the screen;
until 1100px it will be on the side and this property will not have any affect*/
overflow: auto; /* This removes the space on the top of the heading which was created because of margin: 10px 0 on h1*/
padding: 0 40px;
}
.btn {
display: inline-block;
padding: 10px 30px;
cursor: pointer;
background: var(--primary-color);
color: #fff;
border: none;
border-radius: 5px;
}
.md {
font-size: 2rem;
}
.lg {
font-size: 3rem;
}
.flex {
display: flex;
justify-content: center; /* aligns along the main axis*/
align-items: center;
height: 100%;
}
.p-1 {
padding: 1rem; /*1 rem is usually 16px depending the size at root*/
}
.btn:hover:enabled{
transform: scale(0.98); /*reduces the size of button a bit*/
}
When the button has the pseudo class :hover a transform will be applied to the element, which means that it the stacking context is changed (see also Stacking without the z-index property).
To fix this you can add z-index: 1 to the overlay class or remove the transform from the :hover class (Not ideal).

ReactDOM.createPortal modal is mounted on DOM but nothing is displayed on the screen

this is a typescript-next.js project. I have this Modal component:
interface ModalProps {
onCancelModal: () => void;
onAcceptModal: () => void;
acceptEnabled: boolean;
isLoading?: boolean;
title: string;
}
const Modal: React.FC<ModalProps> = (props) => {
let containerRef = useRef<HTMLDivElement | null>(null);
console.log("container", containerRef);
useEffect(() => {
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
if (!containerRef.current) {
containerRef.current = rootContainer;
}
return () => rootContainer.remove();
}, []);
return containerRef.current
? ReactDOM.createPortal(
<div className="modal">
<header className="modal__header">
<h1>{props.title}</h1>
</header>
<div className="modal__content">{props.children}</div>
<div className="modal__actions">
<Button design="danger" mode="flat" onClick={props.onCancelModal}>
Cancel
</Button>
<Button
mode="raised"
onClick={props.onAcceptModal}
disabled={!props.acceptEnabled}
loading={props.isLoading}
>
Accept
</Button>
</div>
</div>,
containerRef.current
)
: null;
};
export default Modal;
I pass a custom error to ErrorHandler component:
const ErrorHandler: React.FC<ErrorHandlerProps> = (props) => (
<Fragment>
{props.error && <Backdrop onClick={props.onHandle} />}
{props.error && (
<Modal
title="An Error Occurred"
onCancelModal={props.onHandle}
onAcceptModal={props.onHandle}
acceptEnabled
>
<p>{props.error}</p>
</Modal>
)}
</Fragment>
);
However, Modal component is successfully mounted on the DOM but nothing displays on the screen.
EDIT
I have backdrop and modal components.
// css for backdrop
.backdrop {
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
z-index: 100;
position: fixed;
left: 0;
top: 0;
transition: opacity 0.3s ease-out;
opacity: 1;
}
// css for Modal
.modal {
position: fixed;
width: 90%;
left: 5%;
top: 20vh;
background: white;
border-radius: 5px;
z-index: 200;// I changed this to 999999 but didnot solve the issue
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
}
.modal__header {
border-bottom: 2px solid #3b0062;
}
.modal__header h1 {
font-size: 1.5rem;
color: #3b0062;
margin: 1rem;
}
.modal__content {
padding: 1rem;
}
.modal__actions {
padding: 1rem;
text-align: right;
}
.modal__actions button {
margin: 0 0.5rem;
}
#media (min-width: 768px) {
.modal {
width: 40rem;
left: calc((100% - 40rem) / 2);
}
}
I found the answer after i refresh my memory. I realized that there is another .modal className on elements-styles tab. It points me to the /node_modules/bootstrap/scss/_modal.scss file which also has modal className and it was overriding my custom className.
.modal {
position: fixed;
top: 0;
left: 0;
z-index: $zindex-modal;
display: none;
width: 100%;
height: 100%;
overflow: hidden;
// Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951.
outline: 0;
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
// See also https://github.com/twbs/bootstrap/issues/17695
}

Resources