Hoang  Kim

Hoang Kim

1656662400

Tạo Bảng Lệnh Với Tailwind CSS Và Headless UI Combobox

Là nhà phát triển, chúng tôi thường cố gắng tối ưu hóa quy trình công việc của mình nhiều nhất có thể, tiết kiệm thời gian bằng cách tận dụng các công cụ như thiết bị đầu cuối. Bảng lệnh là một trong những công cụ hiển thị hoạt động gần đây trong ứng dụng web hoặc máy tính để bàn, cho phép điều hướng nhanh chóng, dễ dàng truy cập vào các lệnh và phím tắt, trong số những thứ khác.

Để nâng cao mức năng suất của bạn, bảng lệnh về cơ bản là một thành phần giao diện người dùng có dạng một phương thức. Ví dụ, một bảng lệnh đặc biệt hữu ích trong các ứng dụng lớn, phức tạp với nhiều bộ phận chuyển động, nơi bạn có thể mất vài lần nhấp chuột hoặc lướt qua nhiều menu thả xuống để truy cập tài nguyên.

Trong hướng dẫn này, chúng ta sẽ khám phá cách tạo một bảng lệnh đầy đủ chức năng từ đầu bằng cách sử dụng thành phần Headless UI Combobox và Tailwind CSS.

Các trường hợp sử dụng trong thế giới thực cho bảng lệnh

Là một nhà phát triển, có khả năng rất cao là bạn đã sử dụng bảng lệnh trước đó. Phổ biến nhất là bảng lệnh VS Code , nhưng có nhiều ví dụ khác, bao gồm Bảng lệnh GitHub, Linear, Figma, Slack, Monkeytype , v.v.

Ứng dụng GitHub

GitHub gần đây đã phát hành một tính năng bảng lệnh vẫn đang trong giai đoạn thử nghiệm công khai tại thời điểm viết bài. Nó cho phép bạn nhanh chóng chuyển đến các trang khác nhau, tìm kiếm lệnh và nhận đề xuất dựa trên ngữ cảnh hiện tại của bạn. Bạn cũng có thể thu hẹp phạm vi tài nguyên bạn đang tìm kiếm bằng cách chuyển sang một trong các tùy chọn hoặc sử dụng một ký tự đặc biệt:

Bảng lệnh Github

Ứng dụng tuyến tính

Nếu bạn không quen thuộc với Linear , thì đó là một công cụ quản lý dự án tương tự như Jira và Asana mang lại trải nghiệm người dùng thực sự tuyệt vời. Linear có một bảng lệnh rất trực quan cho phép bạn truy cập toàn bộ chức năng của ứng dụng với thiết kế ưu tiên bàn phím. Trong hướng dẫn này, chúng tôi sẽ xây dựng một bảng lệnh tương tự như Linear:

Bảng lệnh ứng dụng tuyến tính

Các tính năng cơ bản của bảng lệnh

Một số ứng dụng hiện đại đang triển khai bảng lệnh như một tính năng, nhưng điều gì tạo nên một thành phần bảng lệnh tốt? Dưới đây là danh sách ngắn gọn những điều cần chú ý:

  • Một phím tắt đơn giản để mở bảng màu, tức làctrl + k
  • Nó có thể được truy cập từ bất kỳ đâu trong ứng dụng
  • Nó có các tính năng tìm kiếm mở rộng, chẳng hạn như tìm kiếm mờ
  • Các lệnh truyền đạt ý định và dễ hiểu
  • Nó cung cấp quyền truy cập vào mọi phần của ứng dụng từ một nơi

Trong phần tiếp theo, chúng tôi sẽ xây dựng thành phần của riêng mình bao gồm tất cả các tính năng được liệt kê ở trên. Hãy vào đó!

 

Xây dựng thành phần

Bảng lệnh thực ra không quá phức tạp và bất kỳ ai cũng có thể tạo một bảng lệnh một cách nhanh chóng. Tôi đã chuẩn bị một dự án bắt đầu cho hướng dẫn này để bạn có thể dễ dàng làm theo. Dự án khởi động là một React và Vite SPA sao chép trang Các vấn đề tuyến tính.

Thiết lập dự án

Để bắt đầu, hãy sao chép kho lưu trữ vào thư mục cục bộ của bạn, cài đặt các phụ thuộc cần thiết và khởi động máy chủ phát triển. Dự án sử dụng Yarn, nhưng nếu bạn cảm thấy thoải mái hơn với npm hoặc pnPm, bạn có thể xóa yarn.locktệp trước khi chạy npm installhoặc pnpm install:

// clone repository
$ git clone https://github.com/Mayowa-Ojo/command-palette
// switch to the 'starter-project' branch
$ git checkout starter-project
// install dependencies
$ yarn
// start dev server
$ yarn dev

Nếu bạn truy cập localhost:3000, bạn sẽ thấy trang sau:

Bản sao kho lưu trữ Github

Thành CommandPalettephần

Tiếp theo, chúng ta sẽ xây dựng thành phần. Chúng tôi sẽ sử dụng giao diện người dùng Headless comboboxdialogcác thành phần. comboboxsẽ là thành phần cơ sở cho bảng lệnh của chúng ta. Nó có các tính năng tích hợp như quản lý tiêu điểm và tương tác bàn phím. Chúng tôi sẽ sử dụng dialogthành phần để hiển thị bảng lệnh của chúng tôi theo một phương thức.

Để tạo kiểu cho các thành phần, chúng tôi sẽ sử dụng Tailwind CSS. Tailwind là một thư viện tiện ích CSS cho phép bạn dễ dàng thêm các kiểu nội tuyến trong các tệp HTML hoặc JSX của mình. Dự án khởi động đã bao gồm cấu hình cho Tailwind.

Cài đặt các phụ thuộc cần thiết như sau:

$ yarn add @headlessui/react @heroicons/react

Trong componentsthư mục, hãy tạo một CommandPalette.jsxtệp và thêm khối mã sau:

import { Dialog, Combobox } from "@headlessui/react";

export const CommandPalette = ({ commands }) => {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Dialog
      open={isOpen}
      onClose={setIsOpen}
      className="fixed inset-0 p-4 pt-[15vh] overflow-y-auto"
    >
      <Dialog.Overlay className="fixed inset-0 backdrop-blur-[1px]" />
      <Combobox
         as="div"
         className="bg-accent-dark max-w-2xl mx-auto rounded-lg shadow-2xl relative flex flex-col"
         onChange={(command) => {
            // we have access to the selected command
            // a redirect can happen here or any action can be executed
            setIsOpen(false);
         }}
      >
         <div className="mx-4 mt-4 px-2 h-[25px] text-xs text-slate-100 bg-primary/30 rounded self-start flex items-center flex-shrink-0">
            Issue
         </div>
         <div className="flex items-center text-lg font-medium border-b border-slate-500">
            <Combobox.Input
               className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
               placeholder="Type a command or search..."
            />
         </div>
         <Combobox.Options
            className="max-h-72 overflow-y-auto flex flex-col"
            static
         ></Combobox.Options>
      </Combobox>
   </Dialog>
  );
};

Một vài điều đang xảy ra ở đây. Đầu tiên, chúng tôi nhập khẩu DialogComboboxcác thành phần. Dialogđược hiển thị như một trình bao bọc xung quanh Comboboxvà chúng tôi khởi tạo một trạng thái cục bộ được gọi isOpenđể điều khiển phương thức.

Chúng tôi kết xuất một Dialog.Overlaybên trong Dialogthành phần để phục vụ như lớp phủ cho phương thức. Bạn có thể tạo kiểu này theo cách bạn muốn, nhưng ở đây, chúng tôi chỉ đang sử dụng backdrop-blur. Sau đó, chúng tôi kết xuất Comboboxthành phần và chuyển một hàm xử lý cho phần onChangemềm hỗ trợ. Trình xử lý này được gọi bất cứ khi nào một mục được chọn trong Combobox. Bạn thường muốn điều hướng đến một trang hoặc thực hiện một hành động ở đây, nhưng hiện tại, chúng tôi chỉ đóng Dialog.

Combobox.Inputsẽ xử lý chức năng tìm kiếm mà chúng tôi sẽ bổ sung sau trong phần này. Combobox.Optionshiển thị một ulphần tử bao bọc danh sách kết quả mà chúng tôi sẽ hiển thị. Chúng tôi chuyển vào một staticchỗ dựa cho biết chúng tôi muốn bỏ qua trạng thái được quản lý nội bộ của thành phần.

Tiếp theo, chúng tôi kết xuất của chúng tôi CommandPalettetrong App.jsxtệp:

const App = () => {
   return (
      <div className="flex w-full bg-primary h-screen max-h-screen min-h-screen overflow-hidden">
         <Drawer teams={teams} />
         <AllIssues issues={issues} />
         <CommandPalette commands={commands}/>
      </div>
   );
};

Hãy nói về cách bảng lệnh của chúng ta sẽ hoạt động. Chúng tôi có một danh sách các lệnh được xác định trước trong data/seed.jsontệp. Các lệnh này sẽ được hiển thị trong bảng màu khi nó được mở ra và có thể được lọc dựa trên truy vấn tìm kiếm. Khá đơn giản, phải không?

Thành CommandGroupphần

CommandPalettenhận được một chỗ commandsdựa, là danh sách các lệnh mà chúng tôi đã nhập từ đó seed.json. Bây giờ, hãy tạo một CommandGroup.jsxtệp trong componentsthư mục và thêm mã sau:

// CommandGroup.jsx
import React from "react";
import clsx from "clsx";
import { Combobox } from "@headlessui/react";
import { PlusIcon, ArrowSmRightIcon } from "@heroicons/react/solid";
import {
   CogIcon,
   UserCircleIcon,
   FastForwardIcon,
} from "@heroicons/react/outline";
import { ProjectIcon } from "../icons/ProjectIcon";
import { ViewsIcon } from "../icons/ViewsIcon";
import { TemplatesIcon } from "../icons/TemplatesIcon";
import { TeamIcon } from "../icons/TeamIcon";

export const CommandGroup = ({ commands, group }) => {
   return (
      <React.Fragment>
         {/* only show the header when there are commands belonging to this group */}
         {commands.filter((command) => command.group === group).length >= 1 && (
            <div className="flex items-center h-6 flex-shrink-0 bg-accent/50">
               <span className="text-xs text-slate-100 px-3.5">{group}</span>
            </div>
         )}
         {commands
            .filter((command) => command.group === group)
            .map((command, idx) => (
               <Combobox.Option key={idx} value={command}>
                  {({ active }) => (
                     <div
                        className={clsx(
                           "w-full h-[46px] text-white flex items-center hover:bg-primary/40 cursor-default transition-colors duration-100 ease-in",
                           active ? "bg-primary/40" : ""
                        )}
                     >
                        <div className="px-3.5 flex items-center w-full">
                           <div className="mr-3 flex items-center justify-center w-4">
                              {mapCommandGroupToIcon(
                                 command.group.toLowerCase()
                              )}
                           </div>
                           <span className="text-sm text-left flex flex-auto">
                              {command.name}
                           </span>
                           <span className="text-[10px]">{command.shortcut}</span>
                        </div>
                     </div>
                  )}
               </Combobox.Option>
            ))}
      </React.Fragment>
   );
};

Chúng tôi chỉ đơn giản sử dụng CommandGroupthành phần để tránh một số mã lặp lại. Nếu bạn nhìn vào bảng lệnh Linear, bạn sẽ thấy rằng các lệnh được nhóm lại dựa trên ngữ cảnh. Để thực hiện điều này, chúng ta cần lọc ra các lệnh thuộc cùng một nhóm và lặp lại logic đó cho mỗi nhóm.

Thành CommandGroupphần nhận được hai đạo cụ, commandsgroup. Chúng tôi sẽ lọc các lệnh dựa trên nhóm hiện tại và hiển thị chúng bằng cách sử dụng Combobox.Optionthành phần. Sử dụng các đạo cụ kết xuất, chúng tôi có thể lấy activemục và tạo kiểu cho phù hợp, cho phép chúng tôi kết xuất CommandGroupcho từng nhóm trong CommandPalettekhi vẫn giữ mã sạch.

Lưu ý rằng chúng ta có một mapCommandGroupToIconhàm ở đâu đó trong khối mã ở trên. Điều này là do mỗi nhóm có một biểu tượng khác nhau và chức năng này chỉ là một công cụ trợ giúp để hiển thị biểu tượng chính xác cho nhóm hiện tại. Bây giờ, hãy thêm hàm ngay bên dưới CommandGroupthành phần trong cùng một tệp:

const mapCommandGroupToIcon = (group) => {
   switch (group) {
      case "issue":
         return <PlusIcon className="w-4 h-4 text-white"/>;
      case "project":

Bây giờ, chúng ta cần kết xuất CommandGroupthành phần trong CommandPalette.
Nhập thành phần như sau:

import { CommandGroup } from "./CommandGroup";

Kết xuất nó bên trong Combobox.Optionscho mỗi nhóm:

<Combobox.Options
   className="max-h-72 overflow-y-auto flex flex-col"
   static
>
   <CommandGroup commands={commands} group="Issue"/>
   <CommandGroup commands={commands} group="Project"/>
   <CommandGroup commands={commands} group="Views"/>
   <CommandGroup commands={commands} group="Team"/>
   <CommandGroup commands={commands} group="Templates"/>
   <CommandGroup commands={commands} group="Navigation"/>
   <CommandGroup commands={commands} group="Settings"/>
   <CommandGroup commands={commands} group="Account"/>
</Combobox.Options>

Bạn sẽ thấy danh sách các lệnh đang được hiển thị ngay bây giờ. Bước tiếp theo là kết nối chức năng tìm kiếm.

Triển khai chức năng tìm kiếm

Tạo một biến trạng thái cục bộ trong CommandPalette.jsx:

// CommandPalette.jsx
const [query, setQuery] = useState("");

Chuyển trình xử lý cập nhật trạng thái đến phần onChangehỗ trợ Combobox.Input. querySẽ được cập nhật với mọi ký tự bạn nhập vào ô nhập liệu :

<Combobox.Input
  className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
  placeholder="Type a command or search..."
  onChange={(e) => setQuery(e.target.value)}
/>

Một trong những đặc tính chính của một bảng lệnh tốt là chức năng tìm kiếm mở rộng. Chúng tôi chỉ có thể thực hiện một phép so sánh chuỗi đơn giản của truy vấn tìm kiếm với các lệnh, tuy nhiên điều đó sẽ không tính đến lỗi chính tả và ngữ cảnh. Một giải pháp tốt hơn nhiều mà không giới thiệu quá nhiều phức tạp là tìm kiếm mờ.

Chúng tôi sẽ sử dụng thư viện Fuse.js cho việc này. Fuse.js là một thư viện tìm kiếm mờ, nhẹ, mạnh mẽ và không có phụ thuộc. Nếu bạn không quen với tìm kiếm mờ, thì đó là một kỹ thuật đối sánh chuỗi ủng hộ đối sánh gần đúng hơn đối sánh chính xác, ngụ ý rằng bạn có thể nhận được đề xuất chính xác ngay cả khi truy vấn có lỗi chính tả hoặc lỗi chính tả.

Đầu tiên, hãy cài đặt thư viện Fuse.js:

$ yarn add fuse.js

Trong CommandPalette.jsx, khởi tạo Fuselớp bằng danh sách các lệnh:

// CommandPalette.jsx
const fuse = new Fuse(commands, { includeScore: true, keys: ["name"] });

Lớp Fusechấp nhận một loạt các lệnh và các tùy chọn cấu hình. Trường keyslà nơi chúng ta đăng ký những trường nào trong danh sách lệnh sẽ được lập chỉ mục bởi Fuse.js. Bây giờ, hãy tạo một hàm sẽ xử lý tìm kiếm và trả về kết quả đã lọc:

// CommandPalette.jsx
const filteredCommands =
  query === ""
     ? commands
     : fuse.search(query).map((res) => ({ ...res.item }));

Chúng tôi kiểm tra xem querycó trống không, trả về tất cả các lệnh và nếu không, hãy chạy fuse.searchphương thức với truy vấn. Ngoài ra, chúng tôi đang ánh xạ các kết quả để tạo một đối tượng mới. Điều này là để duy trì tính nhất quán vì kết quả trả về bởi Fuse.js có một số trường mới và sẽ không khớp với cấu trúc mà chúng ta đã có.

Bây giờ, hãy chuyển filteredCommandscho phần commandshỗ trợ trong mỗi CommandGroupthành phần. Nó sẽ giống như mã bên dưới:

// CommandPalette.jsx
<CommandGroup commands={filteredCommands} group="Issue"/>
<CommandGroup commands={filteredCommands} group="Project"/>
<CommandGroup commands={filteredCommands} group="Views"/>
<CommandGroup commands={filteredCommands} group="Team"/>
<CommandGroup commands={filteredCommands} group="Templates"/>
<CommandGroup commands={filteredCommands} group="Navigation"/>
<CommandGroup commands={filteredCommands} group="Settings"/>
<CommandGroup commands={filteredCommands} group="Account"/>

Hãy thử tìm kiếm trong bảng lệnh và xem kết quả có đang được lọc hay không:

Bộ lọc tìm kiếm bảng lệnh

Chúng tôi có một bảng lệnh đầy đủ chức năng, nhưng bạn có thể nhận thấy rằng nó luôn mở. Chúng ta cần có khả năng kiểm soát trạng thái mở của nó. Hãy xác định một sự kiện bàn phím sẽ lắng nghe tổ hợp phím và cập nhật trạng thái mở. Thêm mã sau vào CommandPalette.jsx:

// CommandPalette.jsx
useEffect(() => {
  const onKeydown = (e) => {
     if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setIsOpen(true);
     }
  };
  window.addEventListener("keydown", onKeydown);
  return () => {
     window.removeEventListener("keydown", onKeydown);
  };
}, []);

Chúng tôi đang sử dụng useEffectHook để đăng ký keydownsự kiện bàn phím khi thành phần được gắn kết và chúng tôi sử dụng chức năng dọn dẹp để xóa trình nghe khi thành phần ngắt kết nối.

Trong Hook, chúng tôi kiểm tra xem tổ hợp phím có khớp không ctrl + k. Nếu có, thì trạng thái mở được đặt thành true. Bạn cũng có thể sử dụng một tổ hợp phím khác, nhưng điều quan trọng là không sử dụng các tổ hợp xung đột với các phím tắt của trình duyệt gốc.

Đó là nó! Bạn có thể tìm thấy phiên bản hoàn chỉnh của dự án này trên nhánh dự án đã hoàn thành .

react-command-Palette: Thành phần dựng sẵn

Chúng tôi đã khám phá cách xây dựng một thành phần bảng lệnh từ đầu. Tuy nhiên, có lẽ bạn không nên tự xây dựng mỗi khi bạn cần một bảng lệnh. Đó là nơi mà một thành phần dựng sẵn có thể hữu ích. Hầu hết các thư viện thành phần không cung cấp bảng lệnh, nhưng bảng phản ứng lệnh là một thành phần được viết tốt, có thể truy cập và tương thích với trình duyệt.

Để sử dụng thành phần này, hãy cài đặt nó làm thành phần phụ thuộc trong dự án của bạn:

$ yarn add react-command-palette

Nhập thành phần và chuyển danh sách lệnh của bạn cho nó như sau:

import React from "react";
import CommandPalette from 'react-command-palette';

const commands = [{
  name: "Foo",
  command() {}
},{
  name: "Bar",
  command() {}
}]

export default function App() {
  return (
    <div>
      <CommandPalette commands={commands} />
    </div>
  );
}

Có rất nhiều tùy chọn cấu hình mà bạn có thể sử dụng để tùy chỉnh giao diện và hành vi đáp ứng yêu cầu của mình. Ví dụ: themecấu hình cho phép bạn chọn từ một số chủ đề tích hợp sẵn hoặc tạo chủ đề tùy chỉnh của riêng bạn.

Bước tiếp theo

Trong bài viết này, bạn đã tìm hiểu về các bảng lệnh, các trường hợp sử dụng lý tưởng cho chúng và những tính năng nào tạo nên một bảng lệnh tốt. Bạn cũng đã khám phá các bước chi tiết về cách tạo một công cụ bằng cách sử dụng thành phần hộp kết hợp Headless UI và Tailwind CSS.

Nếu bạn chỉ muốn nhanh chóng đưa tính năng này vào ứng dụng của mình, thì một thành phần được xây dựng sẵn như react-command-Palette là cách để thực hiện. Cảm ơn bạn đã đọc, và hãy để lại bình luận nếu bạn có bất kỳ câu hỏi nào.

Nguồn: https://blog.logrocket.com/react-command-palette-tailwind-css-headless-ui/

#tailwindcss #headless #react 

What is GEEK

Buddha Community

Tạo Bảng Lệnh Với Tailwind CSS Và Headless UI Combobox
Reggie  Hudson

Reggie Hudson

1627084440

Learn Headless UI with React: Listbox Component (Tailwind CSS)

Using Headless UI in React, we easily create a react ready listbox component and then incorporate the Headless UI Transition component as well.

Headless UI Documentation: https://github.com/tailwindlabs/headlessui/blob/develop/packages/%40headlessui-react/README.md

📚 Library(s) needed:
npm install tailwindcss
npm install @headlessui/react

🖥️ Source code: https://devascend.com/github?link=https://github.com/DevAscend/YT-HeadlessUI-React-Tutorials

💡 Have a video request?
Suggest it in the Dev Ascend Discord community server or leave it in the comments below!

🕐 Timestamps:
00:00 Introduction
00:34 Creating the Listbox component
05:45 Incorporating the Transition component

#headless #ui #react

#headless #ui #react #tailwind css #css

Hoang  Kim

Hoang Kim

1656662400

Tạo Bảng Lệnh Với Tailwind CSS Và Headless UI Combobox

Là nhà phát triển, chúng tôi thường cố gắng tối ưu hóa quy trình công việc của mình nhiều nhất có thể, tiết kiệm thời gian bằng cách tận dụng các công cụ như thiết bị đầu cuối. Bảng lệnh là một trong những công cụ hiển thị hoạt động gần đây trong ứng dụng web hoặc máy tính để bàn, cho phép điều hướng nhanh chóng, dễ dàng truy cập vào các lệnh và phím tắt, trong số những thứ khác.

Để nâng cao mức năng suất của bạn, bảng lệnh về cơ bản là một thành phần giao diện người dùng có dạng một phương thức. Ví dụ, một bảng lệnh đặc biệt hữu ích trong các ứng dụng lớn, phức tạp với nhiều bộ phận chuyển động, nơi bạn có thể mất vài lần nhấp chuột hoặc lướt qua nhiều menu thả xuống để truy cập tài nguyên.

Trong hướng dẫn này, chúng ta sẽ khám phá cách tạo một bảng lệnh đầy đủ chức năng từ đầu bằng cách sử dụng thành phần Headless UI Combobox và Tailwind CSS.

Các trường hợp sử dụng trong thế giới thực cho bảng lệnh

Là một nhà phát triển, có khả năng rất cao là bạn đã sử dụng bảng lệnh trước đó. Phổ biến nhất là bảng lệnh VS Code , nhưng có nhiều ví dụ khác, bao gồm Bảng lệnh GitHub, Linear, Figma, Slack, Monkeytype , v.v.

Ứng dụng GitHub

GitHub gần đây đã phát hành một tính năng bảng lệnh vẫn đang trong giai đoạn thử nghiệm công khai tại thời điểm viết bài. Nó cho phép bạn nhanh chóng chuyển đến các trang khác nhau, tìm kiếm lệnh và nhận đề xuất dựa trên ngữ cảnh hiện tại của bạn. Bạn cũng có thể thu hẹp phạm vi tài nguyên bạn đang tìm kiếm bằng cách chuyển sang một trong các tùy chọn hoặc sử dụng một ký tự đặc biệt:

Bảng lệnh Github

Ứng dụng tuyến tính

Nếu bạn không quen thuộc với Linear , thì đó là một công cụ quản lý dự án tương tự như Jira và Asana mang lại trải nghiệm người dùng thực sự tuyệt vời. Linear có một bảng lệnh rất trực quan cho phép bạn truy cập toàn bộ chức năng của ứng dụng với thiết kế ưu tiên bàn phím. Trong hướng dẫn này, chúng tôi sẽ xây dựng một bảng lệnh tương tự như Linear:

Bảng lệnh ứng dụng tuyến tính

Các tính năng cơ bản của bảng lệnh

Một số ứng dụng hiện đại đang triển khai bảng lệnh như một tính năng, nhưng điều gì tạo nên một thành phần bảng lệnh tốt? Dưới đây là danh sách ngắn gọn những điều cần chú ý:

  • Một phím tắt đơn giản để mở bảng màu, tức làctrl + k
  • Nó có thể được truy cập từ bất kỳ đâu trong ứng dụng
  • Nó có các tính năng tìm kiếm mở rộng, chẳng hạn như tìm kiếm mờ
  • Các lệnh truyền đạt ý định và dễ hiểu
  • Nó cung cấp quyền truy cập vào mọi phần của ứng dụng từ một nơi

Trong phần tiếp theo, chúng tôi sẽ xây dựng thành phần của riêng mình bao gồm tất cả các tính năng được liệt kê ở trên. Hãy vào đó!

 

Xây dựng thành phần

Bảng lệnh thực ra không quá phức tạp và bất kỳ ai cũng có thể tạo một bảng lệnh một cách nhanh chóng. Tôi đã chuẩn bị một dự án bắt đầu cho hướng dẫn này để bạn có thể dễ dàng làm theo. Dự án khởi động là một React và Vite SPA sao chép trang Các vấn đề tuyến tính.

Thiết lập dự án

Để bắt đầu, hãy sao chép kho lưu trữ vào thư mục cục bộ của bạn, cài đặt các phụ thuộc cần thiết và khởi động máy chủ phát triển. Dự án sử dụng Yarn, nhưng nếu bạn cảm thấy thoải mái hơn với npm hoặc pnPm, bạn có thể xóa yarn.locktệp trước khi chạy npm installhoặc pnpm install:

// clone repository
$ git clone https://github.com/Mayowa-Ojo/command-palette
// switch to the 'starter-project' branch
$ git checkout starter-project
// install dependencies
$ yarn
// start dev server
$ yarn dev

Nếu bạn truy cập localhost:3000, bạn sẽ thấy trang sau:

Bản sao kho lưu trữ Github

Thành CommandPalettephần

Tiếp theo, chúng ta sẽ xây dựng thành phần. Chúng tôi sẽ sử dụng giao diện người dùng Headless comboboxdialogcác thành phần. comboboxsẽ là thành phần cơ sở cho bảng lệnh của chúng ta. Nó có các tính năng tích hợp như quản lý tiêu điểm và tương tác bàn phím. Chúng tôi sẽ sử dụng dialogthành phần để hiển thị bảng lệnh của chúng tôi theo một phương thức.

Để tạo kiểu cho các thành phần, chúng tôi sẽ sử dụng Tailwind CSS. Tailwind là một thư viện tiện ích CSS cho phép bạn dễ dàng thêm các kiểu nội tuyến trong các tệp HTML hoặc JSX của mình. Dự án khởi động đã bao gồm cấu hình cho Tailwind.

Cài đặt các phụ thuộc cần thiết như sau:

$ yarn add @headlessui/react @heroicons/react

Trong componentsthư mục, hãy tạo một CommandPalette.jsxtệp và thêm khối mã sau:

import { Dialog, Combobox } from "@headlessui/react";

export const CommandPalette = ({ commands }) => {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Dialog
      open={isOpen}
      onClose={setIsOpen}
      className="fixed inset-0 p-4 pt-[15vh] overflow-y-auto"
    >
      <Dialog.Overlay className="fixed inset-0 backdrop-blur-[1px]" />
      <Combobox
         as="div"
         className="bg-accent-dark max-w-2xl mx-auto rounded-lg shadow-2xl relative flex flex-col"
         onChange={(command) => {
            // we have access to the selected command
            // a redirect can happen here or any action can be executed
            setIsOpen(false);
         }}
      >
         <div className="mx-4 mt-4 px-2 h-[25px] text-xs text-slate-100 bg-primary/30 rounded self-start flex items-center flex-shrink-0">
            Issue
         </div>
         <div className="flex items-center text-lg font-medium border-b border-slate-500">
            <Combobox.Input
               className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
               placeholder="Type a command or search..."
            />
         </div>
         <Combobox.Options
            className="max-h-72 overflow-y-auto flex flex-col"
            static
         ></Combobox.Options>
      </Combobox>
   </Dialog>
  );
};

Một vài điều đang xảy ra ở đây. Đầu tiên, chúng tôi nhập khẩu DialogComboboxcác thành phần. Dialogđược hiển thị như một trình bao bọc xung quanh Comboboxvà chúng tôi khởi tạo một trạng thái cục bộ được gọi isOpenđể điều khiển phương thức.

Chúng tôi kết xuất một Dialog.Overlaybên trong Dialogthành phần để phục vụ như lớp phủ cho phương thức. Bạn có thể tạo kiểu này theo cách bạn muốn, nhưng ở đây, chúng tôi chỉ đang sử dụng backdrop-blur. Sau đó, chúng tôi kết xuất Comboboxthành phần và chuyển một hàm xử lý cho phần onChangemềm hỗ trợ. Trình xử lý này được gọi bất cứ khi nào một mục được chọn trong Combobox. Bạn thường muốn điều hướng đến một trang hoặc thực hiện một hành động ở đây, nhưng hiện tại, chúng tôi chỉ đóng Dialog.

Combobox.Inputsẽ xử lý chức năng tìm kiếm mà chúng tôi sẽ bổ sung sau trong phần này. Combobox.Optionshiển thị một ulphần tử bao bọc danh sách kết quả mà chúng tôi sẽ hiển thị. Chúng tôi chuyển vào một staticchỗ dựa cho biết chúng tôi muốn bỏ qua trạng thái được quản lý nội bộ của thành phần.

Tiếp theo, chúng tôi kết xuất của chúng tôi CommandPalettetrong App.jsxtệp:

const App = () => {
   return (
      <div className="flex w-full bg-primary h-screen max-h-screen min-h-screen overflow-hidden">
         <Drawer teams={teams} />
         <AllIssues issues={issues} />
         <CommandPalette commands={commands}/>
      </div>
   );
};

Hãy nói về cách bảng lệnh của chúng ta sẽ hoạt động. Chúng tôi có một danh sách các lệnh được xác định trước trong data/seed.jsontệp. Các lệnh này sẽ được hiển thị trong bảng màu khi nó được mở ra và có thể được lọc dựa trên truy vấn tìm kiếm. Khá đơn giản, phải không?

Thành CommandGroupphần

CommandPalettenhận được một chỗ commandsdựa, là danh sách các lệnh mà chúng tôi đã nhập từ đó seed.json. Bây giờ, hãy tạo một CommandGroup.jsxtệp trong componentsthư mục và thêm mã sau:

// CommandGroup.jsx
import React from "react";
import clsx from "clsx";
import { Combobox } from "@headlessui/react";
import { PlusIcon, ArrowSmRightIcon } from "@heroicons/react/solid";
import {
   CogIcon,
   UserCircleIcon,
   FastForwardIcon,
} from "@heroicons/react/outline";
import { ProjectIcon } from "../icons/ProjectIcon";
import { ViewsIcon } from "../icons/ViewsIcon";
import { TemplatesIcon } from "../icons/TemplatesIcon";
import { TeamIcon } from "../icons/TeamIcon";

export const CommandGroup = ({ commands, group }) => {
   return (
      <React.Fragment>
         {/* only show the header when there are commands belonging to this group */}
         {commands.filter((command) => command.group === group).length >= 1 && (
            <div className="flex items-center h-6 flex-shrink-0 bg-accent/50">
               <span className="text-xs text-slate-100 px-3.5">{group}</span>
            </div>
         )}
         {commands
            .filter((command) => command.group === group)
            .map((command, idx) => (
               <Combobox.Option key={idx} value={command}>
                  {({ active }) => (
                     <div
                        className={clsx(
                           "w-full h-[46px] text-white flex items-center hover:bg-primary/40 cursor-default transition-colors duration-100 ease-in",
                           active ? "bg-primary/40" : ""
                        )}
                     >
                        <div className="px-3.5 flex items-center w-full">
                           <div className="mr-3 flex items-center justify-center w-4">
                              {mapCommandGroupToIcon(
                                 command.group.toLowerCase()
                              )}
                           </div>
                           <span className="text-sm text-left flex flex-auto">
                              {command.name}
                           </span>
                           <span className="text-[10px]">{command.shortcut}</span>
                        </div>
                     </div>
                  )}
               </Combobox.Option>
            ))}
      </React.Fragment>
   );
};

Chúng tôi chỉ đơn giản sử dụng CommandGroupthành phần để tránh một số mã lặp lại. Nếu bạn nhìn vào bảng lệnh Linear, bạn sẽ thấy rằng các lệnh được nhóm lại dựa trên ngữ cảnh. Để thực hiện điều này, chúng ta cần lọc ra các lệnh thuộc cùng một nhóm và lặp lại logic đó cho mỗi nhóm.

Thành CommandGroupphần nhận được hai đạo cụ, commandsgroup. Chúng tôi sẽ lọc các lệnh dựa trên nhóm hiện tại và hiển thị chúng bằng cách sử dụng Combobox.Optionthành phần. Sử dụng các đạo cụ kết xuất, chúng tôi có thể lấy activemục và tạo kiểu cho phù hợp, cho phép chúng tôi kết xuất CommandGroupcho từng nhóm trong CommandPalettekhi vẫn giữ mã sạch.

Lưu ý rằng chúng ta có một mapCommandGroupToIconhàm ở đâu đó trong khối mã ở trên. Điều này là do mỗi nhóm có một biểu tượng khác nhau và chức năng này chỉ là một công cụ trợ giúp để hiển thị biểu tượng chính xác cho nhóm hiện tại. Bây giờ, hãy thêm hàm ngay bên dưới CommandGroupthành phần trong cùng một tệp:

const mapCommandGroupToIcon = (group) => {
   switch (group) {
      case "issue":
         return <PlusIcon className="w-4 h-4 text-white"/>;
      case "project":

Bây giờ, chúng ta cần kết xuất CommandGroupthành phần trong CommandPalette.
Nhập thành phần như sau:

import { CommandGroup } from "./CommandGroup";

Kết xuất nó bên trong Combobox.Optionscho mỗi nhóm:

<Combobox.Options
   className="max-h-72 overflow-y-auto flex flex-col"
   static
>
   <CommandGroup commands={commands} group="Issue"/>
   <CommandGroup commands={commands} group="Project"/>
   <CommandGroup commands={commands} group="Views"/>
   <CommandGroup commands={commands} group="Team"/>
   <CommandGroup commands={commands} group="Templates"/>
   <CommandGroup commands={commands} group="Navigation"/>
   <CommandGroup commands={commands} group="Settings"/>
   <CommandGroup commands={commands} group="Account"/>
</Combobox.Options>

Bạn sẽ thấy danh sách các lệnh đang được hiển thị ngay bây giờ. Bước tiếp theo là kết nối chức năng tìm kiếm.

Triển khai chức năng tìm kiếm

Tạo một biến trạng thái cục bộ trong CommandPalette.jsx:

// CommandPalette.jsx
const [query, setQuery] = useState("");

Chuyển trình xử lý cập nhật trạng thái đến phần onChangehỗ trợ Combobox.Input. querySẽ được cập nhật với mọi ký tự bạn nhập vào ô nhập liệu :

<Combobox.Input
  className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
  placeholder="Type a command or search..."
  onChange={(e) => setQuery(e.target.value)}
/>

Một trong những đặc tính chính của một bảng lệnh tốt là chức năng tìm kiếm mở rộng. Chúng tôi chỉ có thể thực hiện một phép so sánh chuỗi đơn giản của truy vấn tìm kiếm với các lệnh, tuy nhiên điều đó sẽ không tính đến lỗi chính tả và ngữ cảnh. Một giải pháp tốt hơn nhiều mà không giới thiệu quá nhiều phức tạp là tìm kiếm mờ.

Chúng tôi sẽ sử dụng thư viện Fuse.js cho việc này. Fuse.js là một thư viện tìm kiếm mờ, nhẹ, mạnh mẽ và không có phụ thuộc. Nếu bạn không quen với tìm kiếm mờ, thì đó là một kỹ thuật đối sánh chuỗi ủng hộ đối sánh gần đúng hơn đối sánh chính xác, ngụ ý rằng bạn có thể nhận được đề xuất chính xác ngay cả khi truy vấn có lỗi chính tả hoặc lỗi chính tả.

Đầu tiên, hãy cài đặt thư viện Fuse.js:

$ yarn add fuse.js

Trong CommandPalette.jsx, khởi tạo Fuselớp bằng danh sách các lệnh:

// CommandPalette.jsx
const fuse = new Fuse(commands, { includeScore: true, keys: ["name"] });

Lớp Fusechấp nhận một loạt các lệnh và các tùy chọn cấu hình. Trường keyslà nơi chúng ta đăng ký những trường nào trong danh sách lệnh sẽ được lập chỉ mục bởi Fuse.js. Bây giờ, hãy tạo một hàm sẽ xử lý tìm kiếm và trả về kết quả đã lọc:

// CommandPalette.jsx
const filteredCommands =
  query === ""
     ? commands
     : fuse.search(query).map((res) => ({ ...res.item }));

Chúng tôi kiểm tra xem querycó trống không, trả về tất cả các lệnh và nếu không, hãy chạy fuse.searchphương thức với truy vấn. Ngoài ra, chúng tôi đang ánh xạ các kết quả để tạo một đối tượng mới. Điều này là để duy trì tính nhất quán vì kết quả trả về bởi Fuse.js có một số trường mới và sẽ không khớp với cấu trúc mà chúng ta đã có.

Bây giờ, hãy chuyển filteredCommandscho phần commandshỗ trợ trong mỗi CommandGroupthành phần. Nó sẽ giống như mã bên dưới:

// CommandPalette.jsx
<CommandGroup commands={filteredCommands} group="Issue"/>
<CommandGroup commands={filteredCommands} group="Project"/>
<CommandGroup commands={filteredCommands} group="Views"/>
<CommandGroup commands={filteredCommands} group="Team"/>
<CommandGroup commands={filteredCommands} group="Templates"/>
<CommandGroup commands={filteredCommands} group="Navigation"/>
<CommandGroup commands={filteredCommands} group="Settings"/>
<CommandGroup commands={filteredCommands} group="Account"/>

Hãy thử tìm kiếm trong bảng lệnh và xem kết quả có đang được lọc hay không:

Bộ lọc tìm kiếm bảng lệnh

Chúng tôi có một bảng lệnh đầy đủ chức năng, nhưng bạn có thể nhận thấy rằng nó luôn mở. Chúng ta cần có khả năng kiểm soát trạng thái mở của nó. Hãy xác định một sự kiện bàn phím sẽ lắng nghe tổ hợp phím và cập nhật trạng thái mở. Thêm mã sau vào CommandPalette.jsx:

// CommandPalette.jsx
useEffect(() => {
  const onKeydown = (e) => {
     if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setIsOpen(true);
     }
  };
  window.addEventListener("keydown", onKeydown);
  return () => {
     window.removeEventListener("keydown", onKeydown);
  };
}, []);

Chúng tôi đang sử dụng useEffectHook để đăng ký keydownsự kiện bàn phím khi thành phần được gắn kết và chúng tôi sử dụng chức năng dọn dẹp để xóa trình nghe khi thành phần ngắt kết nối.

Trong Hook, chúng tôi kiểm tra xem tổ hợp phím có khớp không ctrl + k. Nếu có, thì trạng thái mở được đặt thành true. Bạn cũng có thể sử dụng một tổ hợp phím khác, nhưng điều quan trọng là không sử dụng các tổ hợp xung đột với các phím tắt của trình duyệt gốc.

Đó là nó! Bạn có thể tìm thấy phiên bản hoàn chỉnh của dự án này trên nhánh dự án đã hoàn thành .

react-command-Palette: Thành phần dựng sẵn

Chúng tôi đã khám phá cách xây dựng một thành phần bảng lệnh từ đầu. Tuy nhiên, có lẽ bạn không nên tự xây dựng mỗi khi bạn cần một bảng lệnh. Đó là nơi mà một thành phần dựng sẵn có thể hữu ích. Hầu hết các thư viện thành phần không cung cấp bảng lệnh, nhưng bảng phản ứng lệnh là một thành phần được viết tốt, có thể truy cập và tương thích với trình duyệt.

Để sử dụng thành phần này, hãy cài đặt nó làm thành phần phụ thuộc trong dự án của bạn:

$ yarn add react-command-palette

Nhập thành phần và chuyển danh sách lệnh của bạn cho nó như sau:

import React from "react";
import CommandPalette from 'react-command-palette';

const commands = [{
  name: "Foo",
  command() {}
},{
  name: "Bar",
  command() {}
}]

export default function App() {
  return (
    <div>
      <CommandPalette commands={commands} />
    </div>
  );
}

Có rất nhiều tùy chọn cấu hình mà bạn có thể sử dụng để tùy chỉnh giao diện và hành vi đáp ứng yêu cầu của mình. Ví dụ: themecấu hình cho phép bạn chọn từ một số chủ đề tích hợp sẵn hoặc tạo chủ đề tùy chỉnh của riêng bạn.

Bước tiếp theo

Trong bài viết này, bạn đã tìm hiểu về các bảng lệnh, các trường hợp sử dụng lý tưởng cho chúng và những tính năng nào tạo nên một bảng lệnh tốt. Bạn cũng đã khám phá các bước chi tiết về cách tạo một công cụ bằng cách sử dụng thành phần hộp kết hợp Headless UI và Tailwind CSS.

Nếu bạn chỉ muốn nhanh chóng đưa tính năng này vào ứng dụng của mình, thì một thành phần được xây dựng sẵn như react-command-Palette là cách để thực hiện. Cảm ơn bạn đã đọc, và hãy để lại bình luận nếu bạn có bất kỳ câu hỏi nào.

Nguồn: https://blog.logrocket.com/react-command-palette-tailwind-css-headless-ui/

#tailwindcss #headless #react 

What is Tailwind CSS useful for?

Let’s talk Tailwind.css. It’s new, it’s customizable, it’s responsive, and a utility-first CSS framework, let’s try it out!

The beauty of Tailwind.css is that to use it is pretty simple. In good old basic CSS, you will declare classes and specifically code the design elements that will come from the class you’ve created(i.e. Font-family, color). In Tailwind, you also use class names, but different from classic CSS, you pull from a pre-created library of class names that you will have access to once you download Tailwind into your app. It’s a matter of understanding what class names you’ll need, and that is going to take a little searching on the Tailwind Docs Website search bar. Nothing like a handy search!

#tailwind-ui #css #web-development #tailwind-css

Hire CSS Developer

Want to develop a website or re-design using CSS Development?

We build a website and we implemented CSS successfully if you are planning to Hire CSS Developer from HourlyDeveloper.io, We can fill your Page with creative colors and attractive Designs. We provide services in Web Designing, Website Redesigning and etc.

For more details…!!
Consult with our experts:- https://bit.ly/3hUdppS

#hire css developer #css development company #css development services #css development #css developer #css