1656662700
作为开发人员,我们经常努力尽可能优化我们的工作流程,通过利用终端等工具来节省时间。命令面板就是这样一种工具,它可以显示 Web 或桌面应用程序中的最近活动,从而实现快速导航、轻松访问命令和快捷方式等。
为了提高您的工作效率,命令面板本质上是一个采用模式形式的 UI 组件。命令面板在具有许多移动部件的大型复杂应用程序中特别有用,例如,您可能需要单击几次或浏览多个下拉列表才能访问资源。
在本教程中,我们将探索如何使用Headless UI Combobox 组件和 Tailwind CSS从头开始构建功能齐全的命令面板。
作为开发人员,您以前很有可能使用过命令调色板。最受欢迎的是VS Code 命令面板,但还有许多其他示例,包括 GitHub 命令面板、Linear、Figma、Slack、monkeytype等。
GitHub 最近发布了一个命令调色板功能,在撰写本文时仍处于公开测试阶段。它使您可以快速跳转到不同的页面、搜索命令并根据您当前的上下文获取建议。您还可以通过切换到其中一个选项或使用特殊字符来缩小您正在寻找的资源的范围:
如果您不熟悉Linear,它是一个类似于 Jira 和 Asana 的项目管理工具,可提供非常棒的用户体验。Linear 有一个非常直观的命令面板,可让您通过其键盘优先设计访问整个应用程序的功能。在本教程中,我们将构建一个类似于 Linear 的命令面板:
一些现代应用程序正在将命令选项板作为一项功能实现,但是什么是好的命令选项板组件?以下是需要注意的事项的简要清单:
ctrl + k
在下一节中,我们将构建自己的组件,其中包含上面列出的所有功能。让我们开始吧!
命令面板实际上并不像看起来那么复杂,任何人都可以快速构建一个。我为本教程准备了一个入门项目,以便您轻松学习。启动项目是复制线性问题页面的 React 和 Vite SPA。
首先,将存储库克隆到本地目录,安装必要的依赖项,然后启动开发服务器。该项目使用 Yarn,但如果您更习惯使用 npm 或 pnPm,您可以yarn.lock
在运行之前删除文件npm install
或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
如果您访问localhost:3000
,您将看到以下页面:
CommandPalette
接下来,我们将构建组件。我们将使用 Headless UIcombobox
和dialog
组件。combobox
将是我们命令面板的基础组件。它具有诸如焦点管理和键盘交互等内置功能。我们将使用该dialog
组件以模式呈现我们的命令调色板。
为了给组件设置样式,我们将使用 Tailwind CSS。Tailwind 是一个 CSS 实用程序库,可让您轻松地在 HTML 或 JSX 文件中添加内联样式。启动项目已包含 Tailwind 的配置。
安装必要的依赖项,如下所示:
$ yarn add @headlessui/react @heroicons/react
在components
文件夹中,创建一个CommandPalette.jsx
文件并添加以下代码块:
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>
);
};
这里正在发生一些事情。首先,我们导入Dialog
和Combobox
组件。Dialog
被呈现为 的包装器Combobox
,我们初始化一个本地状态,调用isOpen
它来控制模态。
Dialog.Overlay
我们在组件内部渲染一个Dialog
作为模态的叠加层。您可以随意设置它的样式,但在这里,我们只是使用backdrop-blur
. 然后,我们渲染Combobox
组件并将处理函数传递给onChange
道具。每当在Combobox
. 您通常希望在此处导航到页面或执行操作,但现在,我们只需关闭Dialog
.
Combobox.Input
将处理搜索功能,我们将在本节后面添加。Combobox.Options
呈现一个ul
包含我们将呈现的结果列表的元素。我们传入一个static
prop,表明我们要忽略组件的内部管理状态。
接下来,我们CommandPalette
在App.jsx
文件中渲染我们的:
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>
);
};
让我们谈谈我们的命令面板将如何工作。我们在文件中有一个预定义命令的列表data/seed.json
。这些命令将在打开时显示在调色板中,并且可以根据搜索查询进行过滤。相当简单,对吧?
CommandGroup
CommandPalette
接收一个commands
prop,它是我们从中导入的命令列表seed.json
。现在,在文件夹中创建一个CommandGroup.jsx
文件components
并添加以下代码:
// 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>
);
};
我们只是使用CommandGroup
组件来避免一些重复的代码。如果您查看线性命令选项板,您会看到命令是根据上下文分组的。为了实现这一点,我们需要过滤掉属于同一组的命令,并为每个组重复该逻辑。
该CommandGroup
组件接收两个 propscommands
和group
. 我们将根据当前组过滤命令并使用Combobox.Option
组件呈现它们。使用渲染道具,我们可以获取active
项目并相应地对其进行样式设置,从而允许我们在保持代码清洁CommandGroup
的同时为每个组渲染。CommandPalette
请注意,我们mapCommandGroupToIcon
在上面的代码块中的某处有一个函数。这是因为每个组都有不同的图标,该函数只是为当前组渲染正确图标的助手。CommandGroup
现在,在同一文件中的组件下方添加函数:
const mapCommandGroupToIcon = (group) => {
switch (group) {
case "issue":
return <PlusIcon className="w-4 h-4 text-white"/>;
case "project":
现在,我们需要CommandGroup
在CommandPalette
.
导入组件如下:
import { CommandGroup } from "./CommandGroup";
Combobox.Options
在每个组内渲染它:
<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>
您应该看到现在正在呈现的命令列表。下一步是连接搜索功能。
在 中创建局部状态变量CommandPalette.jsx
:
// CommandPalette.jsx
const [query, setQuery] = useState("");
将状态更新处理程序传递onChange
给Combobox.Input
. 将query
使用您在输入框中键入的每个字符进行更新:
<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)}
/>
一个好的命令面板的关键属性之一是广泛的搜索功能。我们可以将搜索查询与命令进行简单的字符串比较,但这不会考虑拼写错误和上下文。一个不会引入太多复杂性的更好的解决方案是模糊搜索。
我们将为此使用Fuse.js库。Fuse.js 是一个功能强大、轻量级、零依赖的模糊搜索库。如果您不熟悉模糊搜索,它是一种字符串匹配技术,它倾向于近似匹配而不是精确匹配,这意味着即使查询有拼写错误或拼写错误,您也可以获得正确的建议。
首先,安装 Fuse.js 库:
$ yarn add fuse.js
在中CommandPalette.jsx
,使用命令列表实例化Fuse
类:
// CommandPalette.jsx
const fuse = new Fuse(commands, { includeScore: true, keys: ["name"] });
该类Fuse
接受一系列命令和配置选项。该keys
字段是我们在命令列表中注册哪些字段将被 Fuse.js 索引的地方。现在,创建一个处理搜索并返回过滤结果的函数:
// CommandPalette.jsx
const filteredCommands =
query === ""
? commands
: fuse.search(query).map((res) => ({ ...res.item }));
我们检查是否query
为空,返回所有命令,如果不是,则fuse.search
使用查询运行该方法。此外,我们正在映射结果以创建一个新对象。这是为了保持一致性,因为 Fuse.js 返回的结果有一些新字段,与我们已有的结构不匹配。
现在,将 传递filteredCommands
给每个组件中的commands
道具。CommandGroup
它应该类似于下面的代码:
// 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"/>
尝试在命令面板中搜索并查看结果是否被过滤:
我们有一个功能齐全的命令面板,但您可能会注意到它始终处于打开状态。我们需要能够控制它的打开状态。让我们定义一个键盘事件,它将侦听组合键并更新打开状态。将以下代码添加到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);
};
}, []);
我们使用useEffect
Hook 在组件挂载时注册keydown
键盘事件,并在组件卸载时使用清理功能移除侦听器。
在 Hook 中,我们检查组合键是否匹配ctrl + k
。如果是,则打开状态设置为true
。您也可以使用不同的组合键,但重要的是不要使用与本机浏览器快捷方式冲突的组合。
而已!您可以在完成项目分支上找到该项目的完成版本。
我们已经探索了如何从头开始构建命令选项板组件。但是,您可能不希望每次需要命令调色板时都自己构建。这就是预构建组件可能有用的地方。大多数组件库不提供命令调色板,但react-command-palette是一个编写良好的组件,可访问且与浏览器兼容。
要使用此组件,请将其作为依赖项安装在您的项目中:
$ yarn add react-command-palette
导入组件并将您的命令列表传递给它,如下所示:
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>
);
}
您可以使用许多配置选项来自定义外观和行为以满足您的要求。例如,theme
配置允许您从许多内置主题中进行选择或创建自己的自定义主题。
在本文中,您了解了命令选项板、它们的理想用例以及构成良好命令选项板的功能。您还详细探索了如何使用 Headless UI 组合框组件和 Tailwind CSS 构建一个。
如果您只想在应用程序中快速发布此功能,那么像 react-command-palette 这样的预构建组件是不错的选择。感谢您的阅读,如果您有任何问题,请务必发表评论。
来源:https ://blog.logrocket.com/react-command-palette-tailwind-css-headless-ui/.
1627084440
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
1626053833
Check out the course article and get the github repo here:
Learning Resources:
https://www.w3schools.com/css/css_rwd_mediaqueries.asp
https://tailwindcss.com/docs/breakpoints
https://material-ui.com/customization/breakpoints/#custom-breakpoints
Social Media:
https://twitter.com/LevelUpDev1
https://www.instagram.com/levelupdeveloper
#tailwindcss #materialui #css
#css #breakpoints #tailwind #material ui #css
1617809880
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
1596530868
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
1602955151
In this tutorial I would like to introduce you to one of the fastest growing and promising CSS Frameworks at the moment, Tailwind CSS. It is different from other frameworks, such as Bootstrap, because it is built on a new way of building user interfaces using a utility-first CSS classes structure, as opposed to the OOCSS structure from other frameworks.
By the end of this guide you will be able to install, configure and build a responsive hero section (live demo) using the utility-first classes from Tailwind CSS and configure the project using the recommended PostCSS powered Tailwind configuration file for better maintainability and versatility.
Here’s the table of contents for this tutorial for Tailwind CSS:
Read the full tutorial from Themesberg.
#tailwind #tailwind-css #tailwind-css-tutorial #tutorial #open-source