Gathering detailed insights and metrics for @darksnow-ui/menus
Gathering detailed insights and metrics for @darksnow-ui/menus
Gathering detailed insights and metrics for @darksnow-ui/menus
Gathering detailed insights and metrics for @darksnow-ui/menus
npm install @darksnow-ui/menus
Typescript
Module System
Node Version
NPM Version
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
6
Sistema de menus robusto e acessível para React, construído sobre Radix UI. Oferece três componentes distintos (DropdownMenu, ContextMenu, FloatingMenu) com lógica compartilhada e configuração simples via JSON.
1# npm 2npm install @darksnow-ui/menus 3 4# yarn 5yarn add @darksnow-ui/menus 6 7# pnpm 8pnpm add @darksnow-ui/menus
1{ 2 "react": ">=18.0.0", 3 "react-dom": ">=18.0.0" 4}
1import { DropdownMenu } from "@darksnow-ui/menus"; 2 3const menuItems = [ 4 { type: "item", label: "New File", icon: "📄", shortcut: "Ctrl+N" }, 5 { type: "item", label: "Open", icon: "📁", shortcut: "Ctrl+O" }, 6 { type: "separator" }, 7 { type: "item", label: "Save", icon: "💾", shortcut: "Ctrl+S" }, 8]; 9 10function App() { 11 return ( 12 <DropdownMenu items={menuItems}> 13 <button>File Menu</button> 14 </DropdownMenu> 15 ); 16}
Menu ativado por clique, ideal para ações de botões e toolbars.
1import { DropdownMenu } from "@darksnow-ui/menus"; 2 3<DropdownMenu 4 items={menuItems} 5 align="start" // start | center | end 6 sideOffset={5} 7> 8 <button>Click me</button> 9</DropdownMenu>
Menu de contexto ativado por clique direito.
1import { ContextMenu } from "@darksnow-ui/menus"; 2 3<ContextMenu items={menuItems}> 4 <div className="p-20 border-2 border-dashed"> 5 Right click anywhere here 6 </div> 7</ContextMenu>
Menu com posicionamento manual, útil para casos customizados.
1import { FloatingMenu } from "@darksnow-ui/menus"; 2 3<FloatingMenu 4 items={menuItems} 5 open={isOpen} 6 onOpenChange={setIsOpen} 7 x={mousePosition.x} 8 y={mousePosition.y} 9/>
1{ 2 type: "item", 3 label: "Save File", 4 icon: "💾", // String, emoji ou React component 5 shortcut: "Ctrl+S", // Opcional 6 disabled: false, // Opcional 7 className: "text-blue-500", // Opcional - classes customizadas 8 onClick: (context) => { 9 console.log("Saving..."); 10 } 11}
1{ 2 type: "checkbox", 3 label: "Word Wrap", 4 checked: wordWrapEnabled, 5 icon: "📝", // Opcional 6 disabled: false, // Opcional 7 onClick: (context) => { 8 setWordWrapEnabled(!wordWrapEnabled); 9 } 10}
1// Grupo de opções mutuamente exclusivas 2[ 3 { type: "radio", label: "Small", checked: size === "sm", onClick: () => setSize("sm") }, 4 { type: "radio", label: "Medium", checked: size === "md", onClick: () => setSize("md") }, 5 { type: "radio", label: "Large", checked: size === "lg", onClick: () => setSize("lg") } 6]
1{ 2 type: "submenu", 3 label: "Recent Files", 4 icon: "📁", 5 disabled: false, // Opcional 6 items: [ 7 { type: "item", label: "project.tsx", onClick: () => openFile("project.tsx") }, 8 { type: "item", label: "README.md", onClick: () => openFile("README.md") }, 9 { type: "separator" }, 10 { type: "item", label: "Clear Recent", className: "text-red-500" } 11 ] 12}
1{ type: "separator" }
1{ 2 type: "label", 3 label: "Text Options", 4 inset: true // Adiciona padding à esquerda 5}
Passe dados contextuais que serão disponibilizados em todos os handlers:
1interface FileContext { 2 fileId: string; 3 fileName: string; 4 permissions: string[]; 5} 6 7const menuItems: MenuItemConfig<FileContext>[] = [ 8 { 9 type: "item", 10 label: "Delete", 11 icon: "🗑️", 12 // Desabilita baseado no contexto 13 disabled: (ctx) => !ctx.permissions.includes("delete"), 14 onClick: (context) => { 15 deleteFile(context.fileId); 16 } 17 } 18]; 19 20<ContextMenu<FileContext> 21 items={menuItems} 22 context={{ 23 fileId: "123", 24 fileName: "document.txt", 25 permissions: ["read", "write", "delete"] 26 }} 27> 28 <FileItem /> 29</ContextMenu>
1const editorMenu: MenuItemConfig[] = [ 2 // Seção de navegação 3 { type: "label", label: "Navigation" }, 4 { type: "item", label: "Go to Definition", shortcut: "F12", icon: "🔍" }, 5 { type: "item", label: "Find References", shortcut: "Shift+F12", icon: "📑" }, 6 7 { type: "separator" }, 8 9 // Seção de edição 10 { type: "label", label: "Edit" }, 11 { type: "item", label: "Rename Symbol", shortcut: "F2", icon: "✏️" }, 12 { 13 type: "submenu", 14 label: "Refactor", 15 icon: "🔧", 16 items: [ 17 { type: "item", label: "Extract Method", shortcut: "Ctrl+Alt+M" }, 18 { type: "item", label: "Extract Variable", shortcut: "Ctrl+Alt+V" }, 19 { type: "item", label: "Inline", shortcut: "Ctrl+Alt+N" } 20 ] 21 }, 22 23 { type: "separator" }, 24 25 // Opções de visualização 26 { type: "checkbox", label: "Show Minimap", checked: true }, 27 { type: "checkbox", label: "Word Wrap", checked: false }, 28 29 { type: "separator" }, 30 31 // Tema 32 { type: "label", label: "Theme" }, 33 { type: "radio", label: "Light", checked: false }, 34 { type: "radio", label: "Dark", checked: true }, 35 { type: "radio", label: "High Contrast", checked: false } 36];
1function FileExplorer({ files }) { 2 const getFileMenu = (file: File): MenuItemConfig[] => { 3 const items: MenuItemConfig[] = [ 4 { 5 type: "item", 6 label: "Open", 7 icon: "📄", 8 onClick: () => openFile(file.id) 9 }, 10 { 11 type: "item", 12 label: "Open in New Tab", 13 icon: "🗂️", 14 onClick: () => openInNewTab(file.id) 15 } 16 ]; 17 18 // Adiciona opções específicas para pastas 19 if (file.type === "folder") { 20 items.push( 21 { type: "separator" }, 22 { 23 type: "submenu", 24 label: "New", 25 icon: "➕", 26 items: [ 27 { type: "item", label: "File", icon: "📄", onClick: () => createFile(file.id) }, 28 { type: "item", label: "Folder", icon: "📁", onClick: () => createFolder(file.id) } 29 ] 30 } 31 ); 32 } 33 34 // Adiciona opções perigosas no final 35 items.push( 36 { type: "separator" }, 37 { 38 type: "item", 39 label: "Delete", 40 icon: "🗑️", 41 shortcut: "Delete", 42 className: "text-red-500 hover:bg-red-50", 43 disabled: file.protected, 44 onClick: () => confirmAndDelete(file.id) 45 } 46 ); 47 48 return items; 49 }; 50 51 return ( 52 <div> 53 {files.map(file => ( 54 <ContextMenu 55 key={file.id} 56 items={getFileMenu(file)} 57 context={{ file }} 58 > 59 <FileRow file={file} /> 60 </ContextMenu> 61 ))} 62 </div> 63 ); 64}
1const settingsMenu: MenuItemConfig[] = [ 2 { type: "label", label: "Appearance", inset: true }, 3 { 4 type: "submenu", 5 label: "Font Size", 6 items: [ 7 { type: "radio", label: "Small (12px)", checked: false }, 8 { type: "radio", label: "Medium (14px)", checked: true }, 9 { type: "radio", label: "Large (16px)", checked: false }, 10 { type: "radio", label: "Extra Large (18px)", checked: false } 11 ] 12 }, 13 { type: "checkbox", label: "Show Line Numbers", checked: true }, 14 { type: "checkbox", label: "Show Indent Guides", checked: true }, 15 16 { type: "separator" }, 17 18 { type: "label", label: "Editor", inset: true }, 19 { type: "checkbox", label: "Auto Save", checked: false }, 20 { type: "checkbox", label: "Format on Save", checked: true }, 21 { type: "checkbox", label: "Bracket Pair Colorization", checked: true }, 22 23 { type: "separator" }, 24 25 { type: "label", label: "Advanced", inset: true }, 26 { type: "item", label: "Configure Shortcuts...", icon: "⌨️" }, 27 { type: "item", label: "Export Settings", icon: "📤" }, 28 { type: "item", label: "Reset to Defaults", icon: "🔄", className: "text-orange-500" } 29];
1// Item individual 2{ 3 type: "item", 4 label: "Delete", 5 className: "text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20" 6} 7 8// Menu completo 9<DropdownMenu 10 items={items} 11 className="bg-slate-900 border-slate-700 min-w-[300px]" 12> 13 <button>Dark Theme Menu</button> 14</DropdownMenu>
O componente usa classes do Tailwind por padrão. Para customizar cores e estilos:
1/* Sobrescrever variáveis CSS */ 2:root { 3 --menu-bg: theme('colors.white'); 4 --menu-border: theme('colors.gray.200'); 5 --menu-text: theme('colors.gray.900'); 6 --menu-hover: theme('colors.gray.100'); 7} 8 9.dark { 10 --menu-bg: theme('colors.gray.900'); 11 --menu-border: theme('colors.gray.700'); 12 --menu-text: theme('colors.gray.100'); 13 --menu-hover: theme('colors.gray.800'); 14}
O componente segue todas as práticas de acessibilidade do Radix UI:
Enter/Space
: Selecionar itemArrow Up/Down
: Navegar entre itemsArrow Right
: Abrir submenuArrow Left
: Fechar submenuEscape
: Fechar menuTab
: Mover para próximo itemProp | Tipo | Padrão | Descrição |
---|---|---|---|
items | MenuItemConfig<T>[] | [] | Array de configuração dos items |
context | T | {} | Dados contextuais para handlers |
className | string | "" | Classes CSS adicionais |
onOpenChange | (open: boolean) => void | - | Callback de mudança de estado |
Prop | Tipo | Padrão | Descrição |
---|---|---|---|
children | ReactNode | - | Elemento trigger (obrigatório) |
align | "start" | "center" | "end" | "start" | Alinhamento do menu |
sideOffset | number | 5 | Distância do trigger em pixels |
Prop | Tipo | Padrão | Descrição |
---|---|---|---|
children | ReactNode | - | Área de clique direito (obrigatório) |
Prop | Tipo | Padrão | Descrição |
---|---|---|---|
open | boolean | false | Estado de abertura |
x | number | 0 | Posição horizontal |
y | number | 0 | Posição vertical |
children
do DropdownMenu/ContextMenu é um elemento válidoevent.preventDefault()
nos handlerscontent
do TailwindContribuições são bem-vindas! Por favor, abra uma issue primeiro para discutir mudanças maiores.
1# Clone o repositório 2git clone https://github.com/darksnow-ui/menus.git 3 4# Instale as dependências 5pnpm install 6 7# Execute os testes 8pnpm test 9 10# Build 11pnpm build
MIT © Darksnow UI
Feito com ❤️ usando Radix UI e Tailwind CSS
No vulnerabilities found.
No security vulnerabilities found.