Raycast 中文文档
  • 介绍
  • 链接
    • 官网
    • 官网 API 文档
    • 社区
    • GitHub
    • Store
    • Icon 生成器
    • 扩展图标模板
  • 基础
    • 起步
    • 创建您的第一个扩展
    • 贡献一个扩展
    • 过审一个扩展
    • 发布一个扩展
    • 调试一个扩展
    • 安装一个扩展
  • 团队
    • 开始
    • 发布私人扩展
    • 协作开发私有扩展
  • 例子
    • Doppler 共享 Secrets
    • Hacker News
    • Todo 列表
    • Spotify Controls
  • 资料
    • 最佳实践
    • 工具
      • CLI
      • ESLint
      • VS Code(社区工具)
    • 文件结构
    • 生命周期
      • 参数
      • 后台刷新
      • Deeplinks
    • Manifest
    • 安全性
    • 术语
    • 版本控制
  • API 参考
    • AI
    • Cache
    • Command
    • Clipboard
    • Environment
    • Feedback
      • Alert
      • HUD
      • Toast
    • Keyboard
    • Menu Bar Commands
    • OAuth
    • Preferences
    • Storage
    • System Utilities
    • 用户界面
      • Action Panel
      • Actions
      • Detail
      • Form
      • List
      • Grid
      • Colors
      • Icons & Images
      • Navigation
    • 窗口 & 搜索栏
  • 公共包
    • 起步
    • 功能
      • 执行 AppleScript
    • 图标
      • getAvatarIcon
      • getFavicon
      • getProgressIcon
    • React hooks
      • useCachedState
      • usePromise
      • useCachedPromise
      • useFetch
      • useForm
      • useExec
      • useSQL
      • useAI
  • 迁移
  • FAQ
由 GitBook 提供支持
在本页
  • 渲染 todo 列表
  • 创建一个 todo
  • 完成一个 todo
  • 删除一个 todo
  1. 例子

Todo 列表

此示例展示了如何将列表与表单结合使用。

上一页Hacker News下一页Spotify Controls

最后更新于1年前

该示例的源代码可以在 找到。

没有 Todo 列表的示例什么也不是!让我们在 Raycast 中将其合在一起。此示例将展示如何展示列表、导航到表单,也包括创建新元素以及更新列表。

渲染 todo 列表

让我们从一组 todo 项开始,并简单地将它们渲染为 Raycast 中的列表:

import { List } from "@raycast/api";
import { useState } from "react";

interface Todo {
  title: string;
  isCompleted: boolean;
}

export default function Command() {
  const [todos, setTodos] = useState<Todo[]>([
    { title: "Write a todo list extension", isCompleted: false },
    { title: "Explain it to others", isCompleted: false },
  ]);

  return (
    <List>
      {todos.map((todo, index) => (
        <List.Item key={index} title={todo.title} />
      ))}
    </List>
  );
}

为此,我们定义了一个 TypeScript 接口来描述带有 title 和 isCompleted 标志的 Todo,稍后我们将使用该标志来完成 todo。我们使用 React 的 useState 钩子来创建 todo 的本地状态。我们可以稍后更新它们并且列表将被重新渲染,最后我们呈现所有 todo 的列表。

创建一个 todo

静态的 todo 列表并不是那么有趣,让我们用表单创建一个新的。为此,我们创建一个新的 React 组件来展示表单:

function CreateTodoForm(props: { onCreate: (todo: Todo) => void }) {
  const { pop } = useNavigation();

  function handleSubmit(values: { title: string }) {
    props.onCreate({ title: values.title, isCompleted: false });
    pop();
  }

  return (
    <Form
      actions={
        <ActionPanel>
          <Action.SubmitForm title="Create Todo" onSubmit={handleSubmit} />
        </ActionPanel>
      }
    >
      <Form.TextField id="title" title="Title" />
    </Form>
  );
}

function CreateTodoAction(props: { onCreate: (todo: Todo) => void }) {
  return (
    <Action.Push
      icon={Icon.Pencil}
      title="Create Todo"
      shortcut={{ modifiers: ["cmd"], key: "n" }}
      target={<CreateTodoForm onCreate={props.onCreate} />}
    />
  );
}

<CreateTodoForm> 显示标题的单个文本字段。提交表单后,它会调用 onCreate 回调并自行关闭。

要使用该操作,我们将其添加到 <List> 组件中。这使得该操作在列表为空时可用,这正是我们想要创建的第一个 todo。

export default function Command() {
  const [todos, setTodos] = useState<Todo[]>([]);

  function handleCreate(todo: Todo) {
    const newTodos = [...todos, todo];
    setTodos(newTodos);
  }

  return (
    <List
      actions={
        <ActionPanel>
          <CreateTodoAction onCreate={handleCreate} />
        </ActionPanel>
      }
    >
      {todos.map((todo, index) => (
        <List.Item key={index} title={todo.title} />
      ))}
    </List>
  );
}

完成一个 todo

现在我们可以创建新的 todo,我们还想确保可以勾选 todo 列表上的某些内容。为此,我们创建一个 <ToggleTodoAction> 并将其分配给 <List.Item>:

export default function Command() {
  const [todos, setTodos] = useState<Todo[]>([]);

  // ...

  function handleToggle(index: number) {
    const newTodos = [...todos];
    newTodos[index].isCompleted = !newTodos[index].isCompleted;
    setTodos(newTodos);
  }

  return (
    <List
      actions={
        <ActionPanel>
          <CreateTodoAction onCreate={handleCreate} />
        </ActionPanel>
      }
    >
      {todos.map((todo, index) => (
        <List.Item
          key={index}
          icon={todo.isCompleted ? Icon.Checkmark : Icon.Circle}
          title={todo.title}
          actions={
            <ActionPanel>
              <ActionPanel.Section>
                <ToggleTodoAction todo={todo} onToggle={() => handleToggle(index)} />
              </ActionPanel.Section>
            </ActionPanel>
          }
        />
      ))}
    </List>
  );
}

function ToggleTodoAction(props: { todo: Todo; onToggle: () => void }) {
  return (
    <Action
      icon={props.todo.isCompleted ? Icon.Circle : Icon.Checkmark}
      title={props.todo.isCompleted ? "Uncomplete Todo" : "Complete Todo"}
      onAction={props.onToggle}
    />
  );
}

在本例中,我们将 <ToggleTodoAction> 添加到列表项。通过这样做,我们可以使用 index 来切换适当的 todo。我们还在 todo 中添加了一个反映 isCompleted 状态的图标。

删除一个 todo

与切换 todo 类似,我们还添加了删除 todo 的功能。您可以按照相同的步骤创建一个新的 <DeleteTodoAction> 并将其添加到 <List.Item>中。

export default function Command() {
  const [todos, setTodos] = useState<Todo[]>([]);

  // ...

  function handleDelete(index: number) {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  }

  return (
    <List
      actions={
        <ActionPanel>
          <CreateTodoAction onCreate={handleCreate} />
        </ActionPanel>
      }
    >
      {todos.map((todo, index) => (
        <List.Item
          key={index}
          icon={todo.isCompleted ? Icon.Checkmark : Icon.Circle}
          title={todo.title}
          actions={
            <ActionPanel>
              <ActionPanel.Section>
                <ToggleTodoAction todo={todo} onToggle={() => handleToggle(index)} />
              </ActionPanel.Section>
              <ActionPanel.Section>
                <CreateTodoAction onCreate={handleCreate} />
                <DeleteTodoAction onDelete={() => handleDelete(index)} />
              </ActionPanel.Section>
            </ActionPanel>
          }
        />
      ))}
    </List>
  );
}

// ...

function DeleteTodoAction(props: { onDelete: () => void }) {
  return (
    <Action
      icon={Icon.Trash}
      title="Delete Todo"
      shortcut={{ modifiers: ["ctrl"], key: "x" }}
      onAction={props.onDelete}
    />
  );
}

我们还为 <DeleteTodoAction> 提供了键盘快捷键。这样用户可以更快地删除 todo。此外,我们还将 <CreateTodoAction> 添加到 <List.Item> 中。这确保用户在已有 todo 时也可以创建新的 todo。

最后,我们的命令如下所示:

import { Action, ActionPanel, Form, Icon, List, useNavigation } from "@raycast/api";
import { useState } from "react";

interface Todo {
  title: string;
  isCompleted: boolean;
}

export default function Command() {
  const [todos, setTodos] = useState<Todo[]>([
    { title: "Write a todo list extension", isCompleted: false },
    { title: "Explain it to others", isCompleted: false },
  ]);

  function handleCreate(todo: Todo) {
    const newTodos = [...todos, todo];
    setTodos(newTodos);
  }

  function handleToggle(index: number) {
    const newTodos = [...todos];
    newTodos[index].isCompleted = !newTodos[index].isCompleted;
    setTodos(newTodos);
  }

  function handleDelete(index: number) {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  }

  return (
    <List
      actions={
        <ActionPanel>
          <CreateTodoAction onCreate={handleCreate} />
        </ActionPanel>
      }
    >
      {todos.map((todo, index) => (
        <List.Item
          key={index}
          icon={todo.isCompleted ? Icon.Checkmark : Icon.Circle}
          title={todo.title}
          actions={
            <ActionPanel>
              <ActionPanel.Section>
                <ToggleTodoAction todo={todo} onToggle={() => handleToggle(index)} />
              </ActionPanel.Section>
              <ActionPanel.Section>
                <CreateTodoAction onCreate={handleCreate} />
                <DeleteTodoAction onDelete={() => handleDelete(index)} />
              </ActionPanel.Section>
            </ActionPanel>
          }
        />
      ))}
    </List>
  );
}

function CreateTodoForm(props: { onCreate: (todo: Todo) => void }) {
  const { pop } = useNavigation();

  function handleSubmit(values: { title: string }) {
    props.onCreate({ title: values.title, isCompleted: false });
    pop();
  }

  return (
    <Form
      actions={
        <ActionPanel>
          <Action.SubmitForm title="Create Todo" onSubmit={handleSubmit} />
        </ActionPanel>
      }
    >
      <Form.TextField id="title" title="Title" />
    </Form>
  );
}

function CreateTodoAction(props: { onCreate: (todo: Todo) => void }) {
  return (
    <Action.Push
      icon={Icon.Pencil}
      title="Create Todo"
      shortcut={{ modifiers: ["cmd"], key: "n" }}
      target={<CreateTodoForm onCreate={props.onCreate} />}
    />
  );
}

function ToggleTodoAction(props: { todo: Todo; onToggle: () => void }) {
  return (
    <Action
      icon={props.todo.isCompleted ? Icon.Circle : Icon.Checkmark}
      title={props.todo.isCompleted ? "Uncomplete Todo" : "Complete Todo"}
      onAction={props.onToggle}
    />
  );
}

function DeleteTodoAction(props: { onDelete: () => void }) {
  return (
    <Action
      icon={Icon.Trash}
      title="Delete Todo"
      shortcut={{ modifiers: ["ctrl"], key: "x" }}
      onAction={props.onDelete}
    />
  );
}

这就是一个示例。您在 Raycast 中创建了一个 todo 列表,就是这么简单。作为后续步骤,您可以将 <CreateTodoForm> 提取到单独的命令中。然后,您还可以从 Raycast 的根搜索创建 todo,甚至可以分配全局热键来打开表单。此外,todo 不会保留。如果您关闭命令并重新打开它,它们就会消失。想要保留的话,您可以使用 或将其 中。

storage
写入文件
这里
示例:一个简单的 Todo 列表
创建 todo 表单