-
Notifications
You must be signed in to change notification settings - Fork 906
use ui state #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use ui state #103
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { useCount, useExampleStore, useText } from "renderer/stores"; | ||
|
|
||
| /** | ||
| * Example component demonstrating Zustand usage patterns | ||
| * | ||
| * This component shows: | ||
| * 1. Using selector hooks for optimized re-renders | ||
| * 2. Accessing actions from the store | ||
| * 3. Direct store access for multiple values | ||
| */ | ||
| export function ZustandExample() { | ||
| // Optimized selectors - only re-renders when specific values change | ||
| const count = useCount(); | ||
| const text = useText(); | ||
|
|
||
| // Access actions directly from the store | ||
| const increment = useExampleStore((state) => state.increment); | ||
| const decrement = useExampleStore((state) => state.decrement); | ||
| const setText = useExampleStore((state) => state.setText); | ||
| const reset = useExampleStore((state) => state.reset); | ||
|
|
||
| return ( | ||
| <div className="p-4 space-y-4"> | ||
| <h2 className="text-xl font-bold">Zustand Example</h2> | ||
|
|
||
| {/* Counter Example */} | ||
| <div className="space-y-2"> | ||
| <p className="font-semibold">Counter: {count}</p> | ||
| <div className="space-x-2"> | ||
| <button | ||
| type="button" | ||
| onClick={increment} | ||
| className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" | ||
| > | ||
| Increment | ||
| </button> | ||
| <button | ||
| type="button" | ||
| onClick={decrement} | ||
| className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600" | ||
| > | ||
| Decrement | ||
| </button> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Text Input Example */} | ||
| <div className="space-y-2"> | ||
| <p className="font-semibold">Text: {text}</p> | ||
| <input | ||
| type="text" | ||
| value={text} | ||
| onChange={(e) => setText(e.target.value)} | ||
| placeholder="Type something..." | ||
| className="px-3 py-2 border rounded w-full" | ||
| /> | ||
| </div> | ||
|
|
||
| {/* Reset Button */} | ||
| <button | ||
| type="button" | ||
| onClick={reset} | ||
| className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600" | ||
| > | ||
| Reset All | ||
| </button> | ||
| </div> | ||
| ); | ||
| } |
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { motion } from "framer-motion"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useSidebarStore } from "renderer/stores"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function Sidebar() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { isSidebarOpen } = useSidebarStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <motion.aside | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial={false} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| animate={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: isSidebarOpen ? 256 : 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transition={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration: 0.2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ease: "easeInOut", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-full border-r border-sidebar-border bg-sidebar flex flex-col overflow-hidden" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pointerEvents: isSidebarOpen ? "auto" : "none", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add accessibility attributes for the collapsed state. When Apply this diff to improve accessibility: <motion.aside
initial={false}
animate={{
width: isSidebarOpen ? 256 : 0,
}}
transition={{
duration: 0.2,
ease: "easeInOut",
}}
className="h-full border-r border-sidebar-border bg-sidebar flex flex-col overflow-hidden"
style={{
pointerEvents: isSidebarOpen ? "auto" : "none",
}}
+ aria-hidden={!isSidebarOpen}
>📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <motion.div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial={false} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| animate={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opacity: isSidebarOpen ? 1 : 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transition={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration: 0.15, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ease: "easeInOut", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="p-4 flex-1 overflow-y-auto" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <nav className="space-y-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Add navigation items here */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="text-sm text-sidebar-foreground"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="font-medium mb-2">Navigation</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ul className="space-y-1"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <li className="px-3 py-2 rounded-md hover:bg-sidebar-accent cursor-pointer"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dashboard | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </li> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <li className="px-3 py-2 rounded-md hover:bg-sidebar-accent cursor-pointer"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Projects | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </li> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <li className="px-3 py-2 rounded-md hover:bg-sidebar-accent cursor-pointer"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Settings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </li> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ul> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </nav> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </motion.div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <motion.div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial={false} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| animate={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opacity: isSidebarOpen ? 1 : 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transition={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration: 0.15, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ease: "easeInOut", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="p-4 border-t border-sidebar-border" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ></motion.div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </motion.aside> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { Button } from "@superset/ui/button"; | ||
| import { HiMiniBars3, HiMiniBars3BottomLeft } from "react-icons/hi2"; | ||
| import { useSidebarStore } from "renderer/stores"; | ||
|
|
||
| export function SidebarControl() { | ||
| const { isSidebarOpen, toggleSidebar } = useSidebarStore(); | ||
|
|
||
| return ( | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| onClick={toggleSidebar} | ||
| aria-label="Toggle sidebar" | ||
| className="no-drag" | ||
| > | ||
| {isSidebarOpen ? ( | ||
| <HiMiniBars3BottomLeft className="size-4" /> | ||
| ) : ( | ||
| <HiMiniBars3 className="size-4" /> | ||
| )} | ||
| </Button> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { Button } from "@superset/ui/button"; | ||
| import { useTabsStore } from "renderer/stores/tabs"; | ||
|
|
||
| export function AddTabButton() { | ||
| const { addTab } = useTabsStore(); | ||
|
|
||
| return ( | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| onClick={addTab} | ||
| aria-label="Add new tab" | ||
| className="mt-2" | ||
| > | ||
| <span className="text-lg">+</span> | ||
| </Button> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,102 @@ | ||||||||||||||
| import { Button } from "@superset/ui/button"; | ||||||||||||||
| import { cn } from "@superset/ui/utils"; | ||||||||||||||
| import { useDrag, useDrop } from "react-dnd"; | ||||||||||||||
| import { HiMiniXMark } from "react-icons/hi2"; | ||||||||||||||
| import { useTabsStore } from "renderer/stores/tabs"; | ||||||||||||||
|
|
||||||||||||||
| const TAB_TYPE = "TAB"; | ||||||||||||||
|
|
||||||||||||||
| interface TabItemProps { | ||||||||||||||
| id: string; | ||||||||||||||
| title: string; | ||||||||||||||
| isActive: boolean; | ||||||||||||||
| index: number; | ||||||||||||||
| width: number; | ||||||||||||||
| onMouseEnter?: () => void; | ||||||||||||||
| onMouseLeave?: () => void; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export function TabItem({ | ||||||||||||||
| id, | ||||||||||||||
| title, | ||||||||||||||
| isActive, | ||||||||||||||
| index, | ||||||||||||||
| width, | ||||||||||||||
| onMouseEnter, | ||||||||||||||
| onMouseLeave, | ||||||||||||||
| }: TabItemProps) { | ||||||||||||||
| const { setActiveTab, removeTab, reorderTabs } = useTabsStore(); | ||||||||||||||
|
|
||||||||||||||
| const [{ isDragging }, drag] = useDrag( | ||||||||||||||
| () => ({ | ||||||||||||||
| type: TAB_TYPE, | ||||||||||||||
| item: { id, index }, | ||||||||||||||
| collect: (monitor) => ({ | ||||||||||||||
| isDragging: monitor.isDragging(), | ||||||||||||||
| }), | ||||||||||||||
| }), | ||||||||||||||
| [id, index], | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| const [, drop] = useDrop({ | ||||||||||||||
| accept: TAB_TYPE, | ||||||||||||||
| hover: (item: { id: string; index: number }) => { | ||||||||||||||
| if (item.index !== index) { | ||||||||||||||
| reorderTabs(item.index, index); | ||||||||||||||
| item.index = index; | ||||||||||||||
| } | ||||||||||||||
| }, | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| return ( | ||||||||||||||
| <div | ||||||||||||||
| className="group relative flex items-end shrink-0 h-full" | ||||||||||||||
| style={{ width: `${width}px` }} | ||||||||||||||
| > | ||||||||||||||
| {/* Active tab bottom border overlay */} | ||||||||||||||
| {isActive && <div className="absolute bottom-0 left-0 right-0 h-px" />} | ||||||||||||||
|
|
||||||||||||||
|
Comment on lines
+56
to
+58
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Active tab bottom border overlay lacks a visible style The active indicator div has only positioning/size classes and no color/background, so it’s likely invisible and not distinguishing the active tab. You can make it visibly match the rest of the design, e.g.: - {/* Active tab bottom border overlay */}
- {isActive && <div className="absolute bottom-0 left-0 right-0 h-px" />}
+ {/* Active tab bottom border overlay */}
+ {isActive && (
+ <div className="absolute bottom-0 left-0 right-0 h-px bg-border" />
+ )}(or use whatever 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| {/* Main tab button */} | ||||||||||||||
| <button | ||||||||||||||
| type="button" | ||||||||||||||
| ref={(node) => { | ||||||||||||||
| drag(drop(node)); | ||||||||||||||
| }} | ||||||||||||||
| onClick={() => setActiveTab(id)} | ||||||||||||||
| onMouseEnter={onMouseEnter} | ||||||||||||||
| onMouseLeave={onMouseLeave} | ||||||||||||||
| className={` | ||||||||||||||
| flex items-center gap-0.5 rounded-t-md transition-all w-full shrink-0 pr-6 pl-3 h-[80%] | ||||||||||||||
| ${ | ||||||||||||||
| isActive | ||||||||||||||
| ? "text-foreground bg-sidebar" | ||||||||||||||
| : "text-muted-foreground hover:text-foreground hover:bg-muted/30" | ||||||||||||||
| } | ||||||||||||||
| ${isDragging ? "opacity-30" : "opacity-100"} | ||||||||||||||
| `} | ||||||||||||||
| style={{ cursor: isDragging ? "grabbing" : "grab" }} | ||||||||||||||
| > | ||||||||||||||
| <span className="text-sm whitespace-nowrap truncate flex-1 text-left"> | ||||||||||||||
| {title} | ||||||||||||||
| </span> | ||||||||||||||
| </button> | ||||||||||||||
|
|
||||||||||||||
| <Button | ||||||||||||||
| type="button" | ||||||||||||||
| variant="ghost" | ||||||||||||||
| size="icon" | ||||||||||||||
| onClick={(e) => { | ||||||||||||||
| e.stopPropagation(); | ||||||||||||||
| removeTab(id); | ||||||||||||||
| }} | ||||||||||||||
| className={cn( | ||||||||||||||
| "mt-1 absolute right-1 top-1/2 -translate-y-1/2 size-5 ", | ||||||||||||||
| isActive ? "opacity-90" : "opacity-0 group-hover:opacity-90", | ||||||||||||||
| )} | ||||||||||||||
| aria-label="Close tab" | ||||||||||||||
| > | ||||||||||||||
| <HiMiniXMark className="size-4" /> | ||||||||||||||
| </Button> | ||||||||||||||
|
Comment on lines
+84
to
+99
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invisible close buttons remain focusable and clickable When a tab is inactive, the close Consider also disabling pointer and keyboard interaction when the button is visually hidden, for example: - className={cn(
- "mt-1 absolute right-1 top-1/2 -translate-y-1/2 size-5",
- isActive ? "opacity-100" : "opacity-0 group-hover:opacity-100",
- )}
+ className={cn(
+ "mt-1 absolute right-1 top-1/2 -translate-y-1/2 size-5",
+ isActive
+ ? "opacity-100"
+ : "pointer-events-none opacity-0 group-hover:opacity-100 group-hover:pointer-events-auto",
+ )}
+ tabIndex={isActive ? 0 : -1}This keeps the close button accessible when appropriate while avoiding focus on invisible controls. 🤖 Prompt for AI Agents |
||||||||||||||
| </div> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tighten wording and fix minor spelling in this section
The new guideline about preferring Zustand and minimizing effects is clear and matches the new store patterns. A couple of small polish tweaks you might consider in the same block:
communnication→communicationalias→aliases(to match plural usage)Content-wise this section looks good.
🧰 Tools
🪛 LanguageTool
[grammar] ~2-~2: Ensure spelling is correct
Context: ...ation details For Electron interprocess communnication, ALWAYS use trpc as defined in `src/lib...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~3-~3: Ensure spelling is correct
Context: ...as defined in
src/lib/trpcPlease use alias as defined intsconfig.jsonwhen poss...(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents