diff options
Diffstat (limited to 'ui/src')
| -rw-r--r-- | ui/src/components/dotfiles/Vars.tsx | 194 | ||||
| -rw-r--r-- | ui/src/pages/Dotfiles.tsx | 90 | ||||
| -rw-r--r-- | ui/src/state/models.ts | 6 | ||||
| -rw-r--r-- | ui/src/state/store.ts | 9 |
4 files changed, 296 insertions, 3 deletions
diff --git a/ui/src/components/dotfiles/Vars.tsx b/ui/src/components/dotfiles/Vars.tsx new file mode 100644 index 00000000..00317b23 --- /dev/null +++ b/ui/src/components/dotfiles/Vars.tsx @@ -0,0 +1,194 @@ +import { useEffect, useState } from "react"; + +import DataTable from "@/components/ui/data-table"; +import { Button } from "@/components/ui/button"; +import { MoreHorizontal } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +import { ColumnDef } from "@tanstack/react-table"; + +import { invoke } from "@tauri-apps/api/core"; +import Drawer from "@/components/Drawer"; + +import { Var } from "@/state/models"; +import { useStore } from "@/state/store"; + +function deleteVar(name: string, refreshVars: () => void) { + invoke("delete_var", { name: name }) + .then(() => { + refreshVars(); + }) + .catch(() => { + console.error("Failed to delete var"); + }); +} + +function AddVar({ onAdd: onAdd }: { onAdd?: () => void }) { + let [name, setName] = useState(""); + let [value, setValue] = useState(""); + let [exp, setExport] = useState(false); + + // simple form to add vars + return ( + <div className="p-4"> + <h2 className="text-xl font-semibold leading-6 text-gray-900">Add var</h2> + <p className="mt-2">Add a new var to your shell</p> + + <form + className="mt-4" + onSubmit={(e) => { + e.preventDefault(); + + invoke("set_var", { name: name, value: value, export: exp }) + .then(() => { + console.log("Added var"); + + if (onAdd) onAdd(); + }) + .catch(() => { + console.error("Failed to add var"); + }); + }} + > + <input + className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" + type="text" + value={name} + onChange={(e) => setName(e.target.value)} + placeholder="Var name" + /> + + <input + className="mt-4 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" + autoComplete="off" + autoCapitalize="off" + autoCorrect="off" + spellCheck="false" + type="text" + value={value} + onChange={(e) => setValue(e.target.value)} + placeholder="Var value" + /> + + <div> + <label> + <input + className="mt-4 bg-gray-50 mr-2 inline" + autoComplete="off" + autoCapitalize="off" + autoCorrect="off" + spellCheck="false" + type="checkbox" + value={exp} + onChange={(e) => setExport(e.target.checked)} + /> + Export the var and make it visible to subprocesses + </label> + </div> + + <input + type="submit" + className="block mt-4 rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600" + value="Add var" + /> + </form> + </div> + ); +} + +export default function Vars() { + const vars = useStore((state) => state.vars); + const refreshVars = useStore((state) => state.refreshVars); + + let [varDrawerOpen, setVarDrawerOpen] = useState(false); + + const columns: ColumnDef<Var>[] = [ + { + accessorKey: "name", + header: "Name", + }, + { + accessorKey: "value", + header: "Value", + }, + { + id: "actions", + cell: ({ row }: any) => { + const shell_var = row.original; + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost" className="h-8 w-8 p-0 float-right"> + <span className="sr-only">Open menu</span> + <MoreHorizontal className="h-4 w-4 text-right" /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="end"> + <DropdownMenuLabel>Actions</DropdownMenuLabel> + <DropdownMenuItem + onClick={() => deleteVar(shell_var.name, refreshVars)} + > + Delete + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ); + }, + }, + ]; + + useEffect(() => { + refreshVars(); + }, []); + + return ( + <div className="pt-10"> + <div className="sm:flex sm:items-center"> + <div className="sm:flex-auto"> + <h1 className="text-base font-semibold leading-6 text-gray-900"> + Vars + </h1> + <p className="mt-2 text-sm text-gray-700"> + Configure environment variables here + </p> + </div> + <div className="mt-4 sm:ml-16 sm:mt-0 flex-row"> + <Drawer + open={varDrawerOpen} + onOpenChange={setVarDrawerOpen} + width="30%" + trigger={ + <button + type="button" + className="block rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600" + > + Add + </button> + } + > + <AddVar + onAdd={() => { + refreshVars(); + setVarDrawerOpen(false); + }} + /> + </Drawer> + </div> + </div> + <div className="mt-8 flow-root"> + <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> + <div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> + <DataTable columns={columns} data={vars} /> + </div> + </div> + </div> + </div> + ); +} diff --git a/ui/src/pages/Dotfiles.tsx b/ui/src/pages/Dotfiles.tsx index 6b0870b3..29b6b54a 100644 --- a/ui/src/pages/Dotfiles.tsx +++ b/ui/src/pages/Dotfiles.tsx @@ -1,6 +1,35 @@ +import { useState } from "react"; import Aliases from "@/components/dotfiles/Aliases"; +import Vars from "@/components/dotfiles/Vars"; -function Header() { +enum Section { + Aliases, + Vars, + Scripts, +} + +function renderDotfiles(current: Section) { + switch (current) { + case Section.Aliases: + return <Aliases />; + case Section.Vars: + return <Vars />; + case Section.Scripts: + return <div />; + } +} + +interface HeaderProps { + current: Section; + setCurrent: (section: Section) => void; +} + +interface TabsProps { + current: Section; + setCurrent: (section: Section) => void; +} + +function Header({ current, setCurrent }: HeaderProps) { return ( <div className="md:flex md:items-center md:justify-between"> <div className="min-w-0 flex-1"> @@ -8,17 +37,72 @@ function Header() { Dotfiles </h2> </div> + + <Tabs current={current} setCurrent={setCurrent} /> + </div> + ); +} + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +function Tabs({ current, setCurrent }: TabsProps) { + let tabs = [ + { + name: "Aliases", + isCurrent: () => current === Section.Aliases, + section: Section.Aliases, + }, + { + name: "Vars", + isCurrent: () => current === Section.Vars, + section: Section.Vars, + }, + { + name: "Scripts", + isCurrent: () => current === Section.Scripts, + section: Section.Scripts, + }, + ]; + + return ( + <div> + <div className="mt-4"> + <nav className="flex space-x-4" aria-label="Tabs"> + {tabs.map((tab) => ( + <button + onClick={() => { + setCurrent(tab.section); + }} + key={tab.name} + className={classNames( + tab.isCurrent() + ? "bg-gray-100 text-gray-700" + : "text-gray-500 hover:text-gray-700", + "rounded-md px-3 py-2 text-sm font-medium", + )} + aria-current={tab.isCurrent() ? "page" : undefined} + > + {tab.name} + </button> + ))} + </nav> + </div> </div> ); } export default function Dotfiles() { + let [current, setCurrent] = useState(Section.Aliases); + console.log(current); + return ( <div className="pl-60"> <div className="p-10"> - <Header /> + <Header current={current} setCurrent={setCurrent} /> Manage your shell aliases, variables and paths - <Aliases /> + {renderDotfiles(current)} </div> </div> ); diff --git a/ui/src/state/models.ts b/ui/src/state/models.ts index f11ce651..5afcb804 100644 --- a/ui/src/state/models.ts +++ b/ui/src/state/models.ts @@ -32,3 +32,9 @@ export interface Alias { name: string; value: string; } + +export interface Var { + name: string; + value: string; + export: bool; +} diff --git a/ui/src/state/store.ts b/ui/src/state/store.ts index 08410ba8..7e237d70 100644 --- a/ui/src/state/store.ts +++ b/ui/src/state/store.ts @@ -19,10 +19,12 @@ interface AtuinState { user: User; homeInfo: HomeInfo; aliases: Alias[]; + vars: Var[]; shellHistory: ShellHistory[]; refreshHomeInfo: () => void; refreshAliases: () => void; + refreshVars: () => void; refreshShellHistory: (query?: string) => void; } @@ -30,6 +32,7 @@ export const useStore = create<AtuinState>()((set) => ({ user: DefaultUser, homeInfo: DefaultHomeInfo, aliases: [], + vars: [], shellHistory: [], refreshAliases: () => { @@ -38,6 +41,12 @@ export const useStore = create<AtuinState>()((set) => ({ }); }, + refreshVars: () => { + invoke("vars").then((vars: any) => { + set({ vars: vars }); + }); + }, + refreshShellHistory: (query?: string) => { if (query) { invoke("search", { query: query }) |
