- vừa được xem lúc

Tạo Dropbox với Nextjs 14 Typescript

0 0 16

Người đăng: huyi

Theo Viblo Asia

**Tôi định nghĩa dropbox là 1 cái hộp được thả ra với phương hướng bất kì là cơ sở cho dropdown, autocomplete. phương hướng bất kì là nó tự động dựa vạo vị trí mà nó đang ở để xác định thả box ra ngoài trong phạm vị xem được hợp lý ví dụ như: box nó ở gần cuối trang thì nó phải xuất hiển quay ngược lên trên , nó ở bên phải , thì nó phải lệch box sang trái **

image.png image.png

  1. Component
  • dropbox content thì tạo bằng createPortal
  • keyId thì dùng mặt định useId React 19
  • impact là lớp thao tác vs UI dev tự code, content cũng vậy
  • position là lớp css hiển thị box nó nằm trong logic

L.Master chỉ là cái tag div bình thường thui.

'use client';
import { $Master } from '@/libs';
import { L } from '@/libs/es';
import cx from 'classnames';
import { isEmpty } from 'lodash';
import React, { cloneElement, memo, useId } from 'react';
import { createPortal } from 'react-dom';
import useLogic from './logic';
import styles from './styles.module.scss'; type P = ReturnType<typeof useLogic>;
interface $Props { keyId?: string; // không truyền củng được || default dùng useId React 19 impact?(args: Pick<P, 'show' | 'onToggle'>): JSX.Element; contents?(args: Pick<P, 'show' | 'onToggle' | 'dropBoxRect'>): JSX.Element; // dropBoxRect trả về Rect clsDropbox?: string; // class css clsContent?: string; // class css isOutlet?: boolean; // nhấn ra ngoài để tắt setting?: $Master; // css
} function Dropdown(props: $Props) { const { clsDropbox, clsContent, impact, contents, keyId = useId(), isOutlet, setting, } = props; const args = useLogic({ keyId }); const { onToggle, position, show } = args; return cloneElement( <L.Master id={keyId} className={cx(styles.Dropbox, clsDropbox)} ></L.Master>, { ...setting }, <> {impact && cloneElement(impact?.(args), { id: `DropImpact_${keyId}`, })} {React.useMemo( () => show && !isEmpty(position) && createPortal( <L.Master id={`DropContent_${keyId}`} onMouseLeave={isOutlet ? onToggle : undefined} style={{ ...position, }} className={cx(styles.DropContent, clsContent)} > {contents?.(args)} </L.Master>, document.body ), [show, position, keyId, isOutlet] )} </> );
} export default memo(Dropdown); 
  1. Logic Tạo cái use hook xử lý logic
  • tính nó gần bottom hông , nó gần trái hông
  • dùng transform
'use client';
import { $listenEvent } from '@/libs';
import { debounce } from 'lodash';
import React, { CSSProperties, useEffect, useState } from 'react'; type $Args = { keyId: string;
}; export default function useLogic({ keyId }: $Args) { const [show, setShow] = useState({ [keyId]: false, }); const [position, setPosition] = useState({ [keyId]: {} as CSSProperties, }); const [dropBoxRect, setDropBoxRect] = useState<DOMRect>(); const onPosition = React.useCallback( debounce(() => { const DropImpact = document.getElementById(`DropImpact_${keyId}`); const DropContent = document.getElementById(`DropContent_${keyId}`); const DropImpactRect = DropImpact?.getBoundingClientRect(); const DropContentRect = DropContent?.getBoundingClientRect(); const { innerHeight, innerWidth } = window; if (DropImpactRect) { const isNearBottom = innerHeight / 2 - DropImpactRect.y < 0; const isNearLeft = innerWidth / 2 - DropImpactRect.x < -200; setPosition((prev) => ({ ...prev, [keyId]: { transform: `translate(${ isNearLeft ? Number(DropImpactRect?.x) + Number(DropImpactRect?.width) - Number(DropContentRect?.width) : Number(DropImpactRect.left) }px, ${ isNearBottom ? Number(DropImpactRect.y) - Number(DropContentRect?.height) : Number(DropImpactRect.bottom) }px)`, top: -1, left: -1, }, })); } }, 300), [] ); useEffect(() => { if (!show[keyId]) { onPosition(); $listenEvent.add(onPosition); } return () => { onPosition(); setPosition({}); $listenEvent.remove(onPosition); }; }, []); const onGetRect = React.useCallback(() => { if (!dropBoxRect) { const Dropbox = document.getElementById(`${keyId}`); const DropboxRect = Dropbox?.getBoundingClientRect(); setDropBoxRect(DropboxRect); } }, [dropBoxRect]); useEffect(() => { if (!dropBoxRect) { onGetRect(); $listenEvent.add(onGetRect); } return () => { onGetRect(); $listenEvent.remove(onGetRect); }; }, [dropBoxRect]); const onToggle = () => { setShow((prev) => ({ ...prev, [keyId]: !prev[keyId] })); }; const onSetShow = (status: boolean) => { setShow((prev) => ({ ...prev, [keyId]: status })); }; return { onSetShow, // set status cho key thay cho onOpen, onClose onToggle, position: position[keyId], // vị trí box được mở show: show[keyId], // trạng thái mở box dropBoxRect, // rect của dropbox };
} 

Mình đặt nhân tử chung ra ngoài làm thừa số chung:

export const $listenEvent = { add(fnc: () => void) { document.addEventListener('wheel', fnc); document.addEventListener('resize', fnc); document.addEventListener('orientationchange', fnc); document.addEventListener('load', fnc); document.addEventListener('reload', fnc); }, remove(fnc: () => void) { document.removeEventListener('wheel', fnc); document.removeEventListener('resize', fnc); document.removeEventListener('orientationchange', fnc); document.removeEventListener('load', fnc); document.removeEventListener('reload', fnc); },
}; 
  1. SCSS Đơn giản vầy thui, muốn thêm hiệu transform , transition, visiable , tự thêm vô
.Dropbox { display: flex;
} .DropContent { position: fixed; z-index: 999; min-height: 250px; display: flex; content: ''; padding: 0.5rem 0; flex: 1;
} 
  1. Cách dùng :
<L.Dropbox {...{ setting: { width: '100%', }, clsDropbox: styles.clsDropbox, impact(args) { return ( <L.Magic hover width={'fit-content'} shadow padding={'1rem'} onClick={args.onToggle} className={styles.impact} > Toggle dropdown </L.Magic> ); }, contents({ dropBoxRect }) { return ( <L.Section background={'white'} shadow minWidth={dropBoxRect?.width} minHeight={'100%'} padding={'1rem'} > <L.Txt> contents contents contents contents contents contents </L.Txt> </L.Section> ); }, }} />

Bình luận

Bài viết tương tự

- vừa được xem lúc

Client Side Form Validations sử dụng TypeScript

Validate một bộ dữ liệu trên form trước khi submit đến server là một trong những nhiệm vụ phổ biến nhất trong ứng dụng web. Trong ASP.

0 0 45

- vừa được xem lúc

Định nghĩa Props trong React Function Component với Typescript

Giới thiệu. Hướng dẫn này sẽ cung cấp cho bạn cú pháp bạn cần để xác định đúng các props cần nhập vào trong components của bạn, so sánh và đối chiếu xác định các props dưới dạng class hoặc interface.

0 0 4.1k

- vừa được xem lúc

Nên dùng Javascript hay Typescript?

Một câu hỏi được rất nhiều anh em dev quan tâm đó là nên dùng Javascript hay Typescript. Trong dự án thì viết Typescript nhưng mọi người lại hay viết Javascript vậy thì nên sử dụng cái nào bây giờ ??.

0 0 38

- vừa được xem lúc

Hướng Dẫn Tạo Mega Menu Và Dropdown Menu Với Tailwind CSS

Trong video này mình sẽ hướng dẫn các bạn cách tạo mega menu và dropdown menu với Tailwind CSS. . Like và Subscribe ủng hộ mình nhé . .

0 0 165

- vừa được xem lúc

Những điều nhỏ nhỏ làm nên những việc lớn lớn trong HTML

Bài viết này chúng ta sẽ cùng nhau tìm hiểu về những điều nhỏ nhặt trong HTML nhưng lại có tác dụng không hề nhỏ khi biết được công dụng của nó nhé! Bắt đầu thôi nào. <img src="user.

0 0 13

- vừa được xem lúc

Tạo modal với Nextjs 14

Tạo context API: xử lý kèm logic:. . "use client";. import { $ModalKeys } from "@/collects/modals";.

0 0 23