国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

React組件封裝:文字、表情評論框

這篇具有很好參考價值的文章主要介紹了React組件封裝:文字、表情評論框。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1.需求描述

根據(jù)項目需求,采用Antd組件庫需要封裝一個評論框,具有以下功能:

    • 支持文字輸入
    • 支持常用表情包選擇
    • 支持發(fā)布評論
    • 支持自定義表情包

2.封裝代碼

?./InputComment.tsx

  1 import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
  2 import { SmileOutlined } from '@ant-design/icons';
  3 import { Row, Col, Button, Tooltip, message } from 'antd';
  4 
  5 import styles from './index.less';
  6 
  7 import {setCursorPostionEnd} from "./util";
  8 
  9 const emojiPath = '/emojiImages/';
 10 const emojiSuffix = '.png';
 11 const emojiList = [...Array(15).keys()].map((_, index: number) => {
 12   return { id: index + 1, path: emojiPath + (index + 1) + emojiSuffix };
 13 });
 14 
 15 type Props = {
 16   uniqueId: string; // 唯一鍵
 17   item?: object; // 攜帶參數(shù)
 18   okClick: Function; // 發(fā)布
 19   okText?: string;
 20 };
 21 
 22 const InputComment = forwardRef((props: Props, ref) => {
 23   const { uniqueId: id, okClick, okText } = props;
 24   const inputBoxRef = useRef<any>(null);
 25   const [textCount, setTextCount] = useState(0);
 26   let rangeOfInputBox: any;
 27   const uniqueId = 'uniqueId_' + id;
 28 
 29   const setCaretForEmoji = (target: any) => {
 30     if (target?.tagName?.toLowerCase() === 'img') {
 31       const range = new Range();
 32       range.setStartBefore(target);
 33       range.collapse(true);
 34       // inputBoxRef?.current?.removeAllRanges();
 35       // inputBoxRef?.current?.addRange(range);
 36       const sel = window.getSelection();
 37       sel?.removeAllRanges();
 38       sel?.addRange(range);
 39     }
 40   };
 41 
 42   /**
 43    * 輸入框點擊
 44    */
 45   const inputBoxClick = (event: any) => {
 46     const target = event.target;
 47     setCaretForEmoji(target);
 48   };
 49 
 50   /**
 51    * emoji點擊
 52    */
 53   const emojiClick = (item: any) => {
 54     const emojiEl = document.createElement('img');
 55     emojiEl.src = item.path;
 56     const dom = document.getElementById(uniqueId);
 57     const html = dom?.innerHTML;
 58 
 59     // rangeOfInputBox未定義并且存在內容時,將光標移動到末尾
 60     if (!rangeOfInputBox && !!html) {
 61       dom.innerHTML = html + `<img src="${item.path}"/>`;
 62       setCursorPostionEnd(dom)
 63     } else {
 64       if (!rangeOfInputBox) {
 65         rangeOfInputBox = new Range();
 66         rangeOfInputBox.selectNodeContents(inputBoxRef.current);
 67       }
 68 
 69       if (rangeOfInputBox.collapsed) {
 70         rangeOfInputBox.insertNode(emojiEl);
 71       } else {
 72         rangeOfInputBox.deleteContents();
 73         rangeOfInputBox.insertNode(emojiEl);
 74       }
 75       rangeOfInputBox.collapse(false);
 76 
 77       const sel = window.getSelection();
 78       sel?.removeAllRanges();
 79       sel?.addRange(rangeOfInputBox);
 80     }
 81   };
 82 
 83   /**
 84    * 選擇變化事件
 85    */
 86   document.onselectionchange = (e) => {
 87     if (inputBoxRef?.current) {
 88       const element = inputBoxRef?.current;
 89       const doc = element.ownerDocument || element.document;
 90       const win = doc.defaultView || doc.parentWindow;
 91       const selection = win.getSelection();
 92 
 93       if (selection?.rangeCount > 0) {
 94         const range = selection?.getRangeAt(0);
 95         if (inputBoxRef?.current?.contains(range?.commonAncestorContainer)) {
 96           rangeOfInputBox = range;
 97         }
 98       }
 99     }
100   };
101 
102   /**
103    * 獲取內容長度
104    */
105   const getContentCount = (content: string) => {
106     return content
107       .replace(/&nbsp;/g, ' ')
108       .replace(/<br>/g, '')
109       .replace(/<\/?[^>]*>/g, '占位').length;
110   };
111 
112   /**
113    * 發(fā)送
114    */
115   const okSubmit = () => {
116     const content = inputBoxRef.current.innerHTML;
117     if (!content) {
118       return message.warning('溫馨提示:請?zhí)顚懺u論內容!');
119     } else if (getContentCount(content) > 1000) {
120       return message.warning(`溫馨提示:評論或回復內容小于1000字!`);
121     }
122 
123     okClick(content);
124   };
125 
126   /**
127    * 清空輸入框內容
128    */
129   const clearInputBoxContent = () => {
130     inputBoxRef.current.innerHTML = '';
131   };
132 
133   // 將子組件的方法 暴露給父組件
134   useImperativeHandle(ref, () => ({
135     clearInputBoxContent,
136   }));
137 
138   // 監(jiān)聽變化
139   useEffect(() => {
140     const dom = document.getElementById(uniqueId);
141     const observer = new MutationObserver(() => {
142       const content = dom?.innerHTML ?? '';
143       // console.log('Content changed:', content);
144       setTextCount(getContentCount(content));
145     });
146 
147     if (dom) {
148       observer.observe(dom, {
149         attributes: true,
150         childList: true,
151         characterData: true,
152         subtree: true,
153       });
154     }
155   }, []);
156 
157   return (
158     <div style={{ marginTop: 10, marginBottom: 10 }} className={styles.inputComment}>
159       {textCount === 0 ? (
160         <div className="input-placeholder">
161           {okText === '確認' ? '回復' : '發(fā)布'}評論,內容小于1000字!
162         </div>
163       ) : null}
164 
165       <div
166         ref={inputBoxRef}
167         id={uniqueId}
168         contentEditable={true}
169         placeholder="adsadadsa"
170         className="ant-input input-box"
171         onClick={inputBoxClick}
172       />
173       <div className="input-emojis">
174         <div className="input-count">{textCount}/1000</div>
175 
176         <Row wrap={false}>
177           <Col flex="auto">
178             <Row wrap={true} gutter={[0, 10]} align="middle" style={{ userSelect: 'none' }}>
179               {emojiList.map((item, index: number) => {
180                 return (
181                   <Col
182                     flex="none"
183                     onClick={() => {
184                       emojiClick(item);
185                     }}
186                  
187           <Col flex="none" style={{ marginTop: 5 }}>
188             <Button
189               type="primary"
190               disabled={textCount === 0}
191               onClick={() => {
192                 okSubmit();
193               }}
194             >
195               {okText || '發(fā)布'}
196             </Button>
197           </Col>
198         </Row>
199       </div>
200     </div>
201   );
202 });
203 
204 export default InputComment;

./util.ts

 1 /**
 2  * 光標放到文字末尾(獲取焦點時)
 3  * @param el 
 4  */
 5 export function setCursorPostionEnd(el:any) {
 6   if (window.getSelection) {
 7     // ie11 10 9 ff safari
 8     el.focus() // 解決ff不獲取焦點無法定位問題
 9     const range = window.getSelection() // 創(chuàng)建range
10     range?.selectAllChildren(el) // range 選擇obj下所有子內容
11     range?.collapseToEnd() // 光標移至最后
12   } else if (document?.selection) {
13     // ie10 9 8 7 6 5
14     const range = document?.selection?.createRange() // 創(chuàng)建選擇對象
15     // var range = document.body.createTextRange();
16     range.moveToElementText(el) // range定位到obj
17     range.collapse(false) // 光標移至最后
18     range.select()
19   }
20 }

?./index.less

 1 .inputComment {
 2   position: relative;
 3 
 4   :global {
 5     .input-placeholder {
 6       position: absolute;
 7       top: 11px;
 8       left: 13px;
 9       z-index: 0;
10       color: #dddddd;
11     }
12 
13     .input-box {
14       height: 100px;
15       padding: 10px;
16       overflow: auto;
17       background-color: transparent;
18       border: 1px solid #dddddd;
19       border-top-left-radius: 3px;
20       border-top-right-radius: 3px;
21       resize: vertical;
22 
23       img {
24         height: 18px;
25         vertical-align: middle;
26       }
27     }
28 
29     .input-emojis {
30       margin-top: -7px;
31       padding: 10px;
32       border: 1px solid #dddddd;
33       border-top: 0;
34       border-bottom-right-radius: 3px;
35       border-bottom-left-radius: 3px;
36     }
37 
38     .input-count {
39       float: right;
40       margin-top: -30px;
41       color: #00000073;
42       font-size: 12px;
43     }
44   }
45 }

?文章來源地址http://www.zghlxwxcb.cn/news/detail-844263.html

?3.問題解決

    • 同一頁面有多個評論框時,光標位置不準確?答:從組件外部傳入唯一ID標識,進行區(qū)分。
    • 表情包存放位置?答:表情包存放在/public/emojiImages/**.png,命名規(guī)則1、2、3、4……

4.組件展示

React組件封裝:文字、表情評論框

?

到了這里,關于React組件封裝:文字、表情評論框的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 前端(十五)——開源一個用react封裝的圖片預覽組件

    前端(十五)——開源一個用react封裝的圖片預覽組件

    ??博主:小貓娃來啦 ??文章核心:開源一個react封裝的圖片預覽組件 Gitee:點此跳轉下載 CSDN:點此跳轉下載 裝依賴 運行 打開 創(chuàng)建一個React函數(shù)組件并命名為 ImageGallery 。 在組件內部,使用useState鉤子來定義狀態(tài)變量,并初始化為合適的初始值。 selectedImageUrl 來追蹤當前選

    2024年02月10日
    瀏覽(26)
  • Taro React組件開發(fā)(12) —— RuiVerifyPoint 行為驗證碼之文字點選

    Taro React組件開發(fā)(12) —— RuiVerifyPoint 行為驗證碼之文字點選

    1. 效果預覽 2. 使用場景 賬號登錄,比如驗證碼發(fā)送,防止無限調用發(fā)送接口,所以在發(fā)送之前,需要行為驗證! 3. 插件選擇 AJ-Captcha行為驗證碼文檔 AJ-Captcha行為驗證碼代碼倉庫 為什么要選用【AJ-Captcha行為驗證碼】呢?因為我們管理后臺使用的是 pigx ,它在后端采用的是【

    2024年02月07日
    瀏覽(16)
  • React + Typescript + Antd:封裝通用的字典組件DXSelect

    在開發(fā)中,我們經(jīng)常遇到這樣的場景,在表單中,有個下拉框,選擇對應的數(shù)據(jù)。 那么這個下拉框的選項,就是字典。一搬的做法是,通過antd的Select來實現(xiàn),代碼如下:

    2024年02月15日
    瀏覽(41)
  • react使用hook封裝一個search+input+checkbox組件
  • react Hook+antd封裝一個優(yōu)雅的彈窗組件

    前言 在之前學vue2的時候封裝過一個全局的彈窗組件,可以全局任意地方通過this調用,這次大創(chuàng)項目是用react技術棧,看了一下項目需求,突然發(fā)現(xiàn)彈窗還是比較多的,主要分為基礎的彈窗以及form表單式的彈窗,如果只是無腦的去寫代碼,那些項目也沒啥必要了。正好react和

    2024年02月13日
    瀏覽(23)
  • 封裝React組件DragLine,鼠標拖拽的邊框改變元素寬度

    原文合集地址如下,有需要的朋友可以關注 本文地址 合集地址 在項目中,設計說想做個面板,其寬度隨鼠標拖拽而變化,有最大最小值?;谶@個小功能封裝一個可拖拽組件,在需要的地方引入即可。 這里只是實現(xiàn)x方向的拖拽,y軸拖拽思路差不多。 既然是鼠標操作,那肯

    2024年02月16日
    瀏覽(17)
  • 【看表情包學Linux】文件描述符

    【看表情包學Linux】文件描述符

    ?? ??? 爆笑 教程 ????《看表情包學Linux》?? ? 猛戳訂閱 ? ? ?? ?? 寫在前面: 在上一章中,我們已經(jīng)把 fd 的基本原理搞清楚了。本章我們將開始探索 fd 的應用特征,探索 文件描述符的分配原則。講解重定向,上一章是如何使用 fflush 把內容變出來的,介紹 dup2 函數(shù),

    2023年04月15日
    瀏覽(17)
  • react 基于 dnd-kit 封裝的拖拽排序組件

    react 基于 dnd-kit 封裝的拖拽排序組件

    官網(wǎng)地址 https://docs.dndkit.com/introduction/installation 安裝依賴 簡單使用 建議直接看官網(wǎng),已經(jīng)描述得很詳細了:https://docs.dndkit.com/presets/sortable 效果展示 注意事項 如果傳入的是一個函數(shù)式組件,需要用一個html元素包裹住 這里的排序默認是讀取 list 中的 id 作為 key 值的,如果

    2024年02月16日
    瀏覽(23)
  • react18+antd5.x(1):Notification組件的二次封裝

    react18+antd5.x(1):Notification組件的二次封裝

    antdesign已經(jīng)給我們提供了很好的組件使用體驗,但是我們還需要根據(jù)自己的項目業(yè)務進行更好的封裝,減少我們的代碼量,提升開發(fā)體驗 開起來和官網(wǎng)的使用沒什么區(qū)別,但是我們在使用的時候,進行了二次封裝,更利于我們進行開發(fā) MyNotification.jsx,是我們的業(yè)務頁面 Notif

    2024年02月11日
    瀏覽(23)
  • 【看表情包學Linux】文件描述符 | 重定向 Redirection | dup2 函數(shù) | 緩沖區(qū)的理解 (Cache)

    【看表情包學Linux】文件描述符 | 重定向 Redirection | dup2 函數(shù) | 緩沖區(qū)的理解 (Cache)

    ?? ??? 爆笑 教程 ????《看表情包學Linux》?? ? 猛戳訂閱 ? ? ?? ?? 寫在前面: 在上一章中,我們已經(jīng)把 fd 的基本原理搞清楚了。本章我們將開始探索 fd 的應用特征,探索 文件描述符的分配原則。講解重定向,上一章是如何使用 fflush 把內容變出來的,介紹 dup2 函數(shù),

    2023年04月25日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包