react-contenteditable icon indicating copy to clipboard operation
react-contenteditable copied to clipboard

How to control events in the content?

Open petkovic43 opened this issue 4 years ago • 3 comments

Hello, I'm having trouble changing the content from a user action within the content itself. I am creating an input similar to facebook, that when the user informs a url, the system obtains the page's meta tags and a card is created as in the image below. However, by clicking on the "x" on the card I would like it to be removed, but I can't change the text.current anymore. Does anyone have any idea how to solve? Follow my code

Captura de Tela 2020-06-17 às 16 33 07

import React, { useRef, useState, memo } from 'react';
import ContentEditable from 'react-contenteditable'
import sanitizeHtml from 'sanitize-html';
import { MdPhotoCamera, MdOndemandVideo, MdLink } from 'react-icons/md'
import api from 'lib/api';

const clean = content => sanitizeHtml(content, {
  allowedTags: ['h5', 'p', 'a', 'nl', 'hr', 'br', 'div', 'iframe', 'img', 'button', 'span'],
  disallowedTagsMode: 'discard',
  allowedAttributes: {
    a: ['href', 'name'],
    iframe: ['src', 'width', 'height', 'frameborder', 'allow', 'allowfullscreen'],
    img: ['src', 'style', 'class', 'alt'],
    div: ['contenteditable', 'class', 'style'],
    h5: ['class'],
    p: ['class'],
    button: ['class', 'type'],
    span: ['aria-hidden', 'class']
  },
  // Lots of these won't come up by default because we don't allow them
  selfClosing: ['img', 'br', 'hr'],
  // URL schemes we permit
  allowedSchemes: ['http', 'https', 'data'],
  allowedSchemesByTag: {},
  allowedSchemesAppliedToAttributes: ['href', 'src'],
  allowProtocolRelative: true,
  enforceHtmlBoundary: false,
  allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
  transformTags: {
    'img': (tagName, attribs) => ({
      tagName,
      attribs: {
        ...attribs,
        style: 'max-width: 100%;',
      }
    }),
    'iframe': (tagName, attribs) => ({
      // My own custom magic goes here
      tagName,
      attribs: {
        ...attribs,
        width: '100%;',
        height: '300',
        frameborder: '0',
        allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",
        allowfullscreen: true
      }
    })
  }
});

const NewPost = () => {
  const [loading, setLoading] = useState(false)
  const text = useRef(clean(''))
  const [loadedDiv, setLoadedDiv] = useState(false)
  const handleChange = async ({ target: { value } }) => {
    const href = value.match(/href="([^"]*")/g)
    if (href) {
      if (!loadedDiv) {
        setLoading(true)
        text.current = clean([value, bounce].join(''));
        const { data } = await api.get(`/meta?url=${href[0].split('"')[1]}`)
        text.current = clean([value, card(data)].join(''));
        setLoading(false)
        setLoadedDiv(true)
      } else {
        text.current = clean(value);
      }
    } else {
      setLoadedDiv(false)
      text.current = clean(value);
    }
  };


  const handleClick = ({ target: { className } }) => {
    console.log(className)
    if (className === "close text-white position-absolute pos-top pos-right p-2 m-1 mr-2" || className === 'close_card_url') {
      console.log(text);
      text.current = '';
    }
  }


  return (
    <div className="card border mb-g">
      <div className="card-body pl-4 pt-4 pr-4 pb-0">
        <div className="d-flex flex-column">

          <div className="d-flex flex-row border-top-0 border-left-0 border-right-0 mb-3">
            <div className="d-inline-block align-middle mr-3">
              <span className="profile-image rounded-circle d-block" style={{ backgroundImage: 'url("img/demo/avatars/avatar-admin.png")', backgroundSize: 'cover' }} />
            </div>
            <h5 className="mb-0 flex-1 text-dark fw-500">
              Nome
            <small className="m-0 l-h-n">
                No que voçê está pensando?
            </small>
            </h5>
            <span className="text-muted fs-xs opacity-70">
              Agora
          </span>
          </div>

          <ContentEditable
            className="form-control p-3 h-auto border"
            onClick={handleClick}
            html={text.current}
            disabled={loading}
            onChange={handleChange} />

          <div className="py-2 d-flex flex-row align-items-center flex-wrap flex-shrink-0">
            <button className="btn btn-icon mr-2" onClick={e => e.preventDefault()}>
              <MdLink className="text-muted" size={24} />
            </button>
            <button className="btn btn-icon mr-2" onClick={e => e.preventDefault()}>
              <MdPhotoCamera className="text-muted" size={24} />
            </button>
            <button className="btn btn-icon" onClick={e => e.preventDefault()}>
              <MdOndemandVideo className="text-muted" size={24} />
            </button>

            <button className="btn btn-dark shadow-0 ml-auto px-3">Publicar</button>
          </div>
        </div>
      </div>
    </div>
  )
}

export default memo(NewPost);

petkovic43 avatar Jun 17 '20 19:06 petkovic43

You can use innerRef to get a reference to the editable DOM element. But in my opinion, your ssmart card should not be part of the contenteditable element.

lovasoa avatar Jun 18 '20 10:06 lovasoa

@lovasoa i Have similar query I want to change the content with events based on conditions . i am able to alter text.current but the same is not rendered in the UI.

coinsider this example for illustration purpose.

     const handleChange = evt => {
            text.current = evt.target.value + 'A'
        console.log(text.current);   // gives  evt.target.value + 'A'   but renders only evt.target.value
  }
     <ContentEditable 
                 html={text.current} 
                 onChange={handleChange} 
                 onBlur={handleBlur}
                '/>

hussamkhatib avatar Dec 22 '20 10:12 hussamkhatib

same problem here. any updated?

@lovasoa i Have similar query I want to change the content with events based on conditions . i am able to alter text.current but the same is not rendered in the UI.

coinsider this example for illustration purpose.

     const handleChange = evt => {
            text.current = evt.target.value + 'A'
        console.log(text.current);   // gives  evt.target.value + 'A'   but renders only evt.target.value
  }
     <ContentEditable 
                 html={text.current} 
                 onChange={handleChange} 
                 onBlur={handleBlur}
                '/>

MatheusRoichman avatar Feb 12 '24 18:02 MatheusRoichman