analytica-frontend-lib 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +79 -1
- package/dist/index.d.ts +79 -1
- package/dist/index.js +263 -0
- package/dist/index.mjs +265 -0
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -14,6 +14,27 @@ Instale a biblioteca em seu projeto com o comando:
|
|
|
14
14
|
yarn add analytica-frontend-lib
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
### Importe os componentes
|
|
18
|
+
|
|
19
|
+
Para usar os componentes, basta importá-los no seu projeto:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { Text } from 'analytica-frontend-lib'
|
|
23
|
+
|
|
24
|
+
const MyComponent = () => {
|
|
25
|
+
return <Text>Olá mundo!</Text>
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Importando Estilos CSS
|
|
30
|
+
|
|
31
|
+
Para usar os estilos e variáveis CSS da lib, importe o arquivo de estilos:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// No seu arquivo layout.tsx (Next.js) ou main.tsx (Vite)
|
|
35
|
+
import 'analytica-frontend-lib/styles.css';
|
|
36
|
+
```
|
|
37
|
+
|
|
17
38
|
---
|
|
18
39
|
|
|
19
40
|
## Etapas para criar um novo componente
|
|
@@ -33,6 +54,10 @@ yarn add analytica-frontend-lib
|
|
|
33
54
|
- Os componentes devem ser totalmente compatíveis com Next.js versão 15+.
|
|
34
55
|
- Testes unitários são obrigatórios, com coverage mínimo de 80%.
|
|
35
56
|
- Siga o princípio da responsabilidade única (`single responsibility`): construa componentes compostos por componentes menores.
|
|
57
|
+
- No arquivo `package.json` da biblioteca temos:
|
|
58
|
+
- `peerDependencies`: Framework core (React, Next.js)
|
|
59
|
+
- `dependencies`: Bibliotecas específicas usadas pelos componentes
|
|
60
|
+
- `devDependencies`: Ferramentas de build, testes, linting
|
|
36
61
|
|
|
37
62
|
## Ladle
|
|
38
63
|
|
package/dist/index.css
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -197,4 +197,82 @@ declare const TableHead: react.ForwardRefExoticComponent<TdHTMLAttributes<HTMLTa
|
|
|
197
197
|
declare const TableCell: react.ForwardRefExoticComponent<TdHTMLAttributes<HTMLTableCellElement> & react.RefAttributes<HTMLTableCellElement>>;
|
|
198
198
|
declare const TableCaption: react.ForwardRefExoticComponent<HTMLAttributes<HTMLTableCaptionElement> & react.RefAttributes<HTMLTableCaptionElement>>;
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
interface DropdownMenuProps {
|
|
201
|
+
children: ReactNode;
|
|
202
|
+
open?: boolean;
|
|
203
|
+
onOpenChange?: (open: boolean) => void;
|
|
204
|
+
}
|
|
205
|
+
declare const DropdownMenu: ({ children, open, onOpenChange }: DropdownMenuProps) => react_jsx_runtime.JSX.Element;
|
|
206
|
+
declare const DropdownMenuTrigger: react.ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & react.RefAttributes<HTMLButtonElement>>;
|
|
207
|
+
declare const MenuLabel: react.ForwardRefExoticComponent<HTMLAttributes<HTMLFieldSetElement> & {
|
|
208
|
+
inset?: boolean;
|
|
209
|
+
} & react.RefAttributes<HTMLFieldSetElement>>;
|
|
210
|
+
declare const MenuContent: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
211
|
+
align?: "start" | "center" | "end";
|
|
212
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
213
|
+
sideOffset?: number;
|
|
214
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
215
|
+
declare const MenuItem: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
216
|
+
inset?: boolean;
|
|
217
|
+
size?: "small" | "medium";
|
|
218
|
+
iconLeft?: ReactNode;
|
|
219
|
+
iconRight?: ReactNode;
|
|
220
|
+
disabled?: boolean;
|
|
221
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
222
|
+
declare const MenuSeparator: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & react.RefAttributes<HTMLDivElement>>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* NavButton component for Analytica Ensino platforms
|
|
226
|
+
*
|
|
227
|
+
* Um botão de navegação com ícone e texto para navegação principal.
|
|
228
|
+
* Ideal para menus de navegação, sidebar, tabs de navegação, etc.
|
|
229
|
+
* Compatível com Next.js 15 e React 19.
|
|
230
|
+
* Suporta forwardRef para acesso programático ao elemento DOM.
|
|
231
|
+
*
|
|
232
|
+
* @param icon - O ícone a ser exibido no botão
|
|
233
|
+
* @param label - O texto/label a ser exibido
|
|
234
|
+
* @param selected - Estado de seleção do botão
|
|
235
|
+
* @param className - Classes CSS adicionais
|
|
236
|
+
* @param props - Todos os outros atributos HTML padrão de button
|
|
237
|
+
* @returns Um elemento button estilizado para navegação
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```tsx
|
|
241
|
+
* <NavButton
|
|
242
|
+
* icon={<HomeIcon />}
|
|
243
|
+
* label="Início"
|
|
244
|
+
* selected={false}
|
|
245
|
+
* onClick={() => navigate('/')}
|
|
246
|
+
* />
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```tsx
|
|
251
|
+
* // Usando ref para foco programático
|
|
252
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
253
|
+
*
|
|
254
|
+
* const handleFocus = () => {
|
|
255
|
+
* buttonRef.current?.focus();
|
|
256
|
+
* };
|
|
257
|
+
*
|
|
258
|
+
* <NavButton
|
|
259
|
+
* ref={buttonRef}
|
|
260
|
+
* icon={<HomeIcon />}
|
|
261
|
+
* label="Dashboard"
|
|
262
|
+
* selected={isActive}
|
|
263
|
+
* onClick={() => setActiveTab('dashboard')}
|
|
264
|
+
* />
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare const NavButton: react.ForwardRefExoticComponent<{
|
|
268
|
+
/** Ícone a ser exibido no botão */
|
|
269
|
+
icon: ReactNode;
|
|
270
|
+
/** Texto/label a ser exibido ao lado do ícone */
|
|
271
|
+
label: string;
|
|
272
|
+
/** Estado de seleção do botão */
|
|
273
|
+
selected?: boolean;
|
|
274
|
+
/** Additional CSS classes to apply */
|
|
275
|
+
className?: string;
|
|
276
|
+
} & ButtonHTMLAttributes<HTMLButtonElement> & react.RefAttributes<HTMLButtonElement>>;
|
|
277
|
+
|
|
278
|
+
export { Button, DropdownMenu, DropdownMenuTrigger, IconRoundedButton, MenuContent, MenuItem, MenuLabel, MenuSeparator, NavButton, SelectionButton, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Text };
|
package/dist/index.d.ts
CHANGED
|
@@ -197,4 +197,82 @@ declare const TableHead: react.ForwardRefExoticComponent<TdHTMLAttributes<HTMLTa
|
|
|
197
197
|
declare const TableCell: react.ForwardRefExoticComponent<TdHTMLAttributes<HTMLTableCellElement> & react.RefAttributes<HTMLTableCellElement>>;
|
|
198
198
|
declare const TableCaption: react.ForwardRefExoticComponent<HTMLAttributes<HTMLTableCaptionElement> & react.RefAttributes<HTMLTableCaptionElement>>;
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
interface DropdownMenuProps {
|
|
201
|
+
children: ReactNode;
|
|
202
|
+
open?: boolean;
|
|
203
|
+
onOpenChange?: (open: boolean) => void;
|
|
204
|
+
}
|
|
205
|
+
declare const DropdownMenu: ({ children, open, onOpenChange }: DropdownMenuProps) => react_jsx_runtime.JSX.Element;
|
|
206
|
+
declare const DropdownMenuTrigger: react.ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & react.RefAttributes<HTMLButtonElement>>;
|
|
207
|
+
declare const MenuLabel: react.ForwardRefExoticComponent<HTMLAttributes<HTMLFieldSetElement> & {
|
|
208
|
+
inset?: boolean;
|
|
209
|
+
} & react.RefAttributes<HTMLFieldSetElement>>;
|
|
210
|
+
declare const MenuContent: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
211
|
+
align?: "start" | "center" | "end";
|
|
212
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
213
|
+
sideOffset?: number;
|
|
214
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
215
|
+
declare const MenuItem: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
216
|
+
inset?: boolean;
|
|
217
|
+
size?: "small" | "medium";
|
|
218
|
+
iconLeft?: ReactNode;
|
|
219
|
+
iconRight?: ReactNode;
|
|
220
|
+
disabled?: boolean;
|
|
221
|
+
} & react.RefAttributes<HTMLDivElement>>;
|
|
222
|
+
declare const MenuSeparator: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & react.RefAttributes<HTMLDivElement>>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* NavButton component for Analytica Ensino platforms
|
|
226
|
+
*
|
|
227
|
+
* Um botão de navegação com ícone e texto para navegação principal.
|
|
228
|
+
* Ideal para menus de navegação, sidebar, tabs de navegação, etc.
|
|
229
|
+
* Compatível com Next.js 15 e React 19.
|
|
230
|
+
* Suporta forwardRef para acesso programático ao elemento DOM.
|
|
231
|
+
*
|
|
232
|
+
* @param icon - O ícone a ser exibido no botão
|
|
233
|
+
* @param label - O texto/label a ser exibido
|
|
234
|
+
* @param selected - Estado de seleção do botão
|
|
235
|
+
* @param className - Classes CSS adicionais
|
|
236
|
+
* @param props - Todos os outros atributos HTML padrão de button
|
|
237
|
+
* @returns Um elemento button estilizado para navegação
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```tsx
|
|
241
|
+
* <NavButton
|
|
242
|
+
* icon={<HomeIcon />}
|
|
243
|
+
* label="Início"
|
|
244
|
+
* selected={false}
|
|
245
|
+
* onClick={() => navigate('/')}
|
|
246
|
+
* />
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```tsx
|
|
251
|
+
* // Usando ref para foco programático
|
|
252
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
253
|
+
*
|
|
254
|
+
* const handleFocus = () => {
|
|
255
|
+
* buttonRef.current?.focus();
|
|
256
|
+
* };
|
|
257
|
+
*
|
|
258
|
+
* <NavButton
|
|
259
|
+
* ref={buttonRef}
|
|
260
|
+
* icon={<HomeIcon />}
|
|
261
|
+
* label="Dashboard"
|
|
262
|
+
* selected={isActive}
|
|
263
|
+
* onClick={() => setActiveTab('dashboard')}
|
|
264
|
+
* />
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare const NavButton: react.ForwardRefExoticComponent<{
|
|
268
|
+
/** Ícone a ser exibido no botão */
|
|
269
|
+
icon: ReactNode;
|
|
270
|
+
/** Texto/label a ser exibido ao lado do ícone */
|
|
271
|
+
label: string;
|
|
272
|
+
/** Estado de seleção do botão */
|
|
273
|
+
selected?: boolean;
|
|
274
|
+
/** Additional CSS classes to apply */
|
|
275
|
+
className?: string;
|
|
276
|
+
} & ButtonHTMLAttributes<HTMLButtonElement> & react.RefAttributes<HTMLButtonElement>>;
|
|
277
|
+
|
|
278
|
+
export { Button, DropdownMenu, DropdownMenuTrigger, IconRoundedButton, MenuContent, MenuItem, MenuLabel, MenuSeparator, NavButton, SelectionButton, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Text };
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Button: () => Button,
|
|
24
|
+
DropdownMenu: () => DropdownMenu,
|
|
25
|
+
DropdownMenuTrigger: () => DropdownMenuTrigger,
|
|
24
26
|
IconRoundedButton: () => IconRoundedButton,
|
|
27
|
+
MenuContent: () => MenuContent,
|
|
28
|
+
MenuItem: () => MenuItem,
|
|
29
|
+
MenuLabel: () => MenuLabel,
|
|
30
|
+
MenuSeparator: () => MenuSeparator,
|
|
31
|
+
NavButton: () => NavButton,
|
|
25
32
|
SelectionButton: () => SelectionButton,
|
|
26
33
|
Table: () => Table,
|
|
27
34
|
TableBody: () => TableBody,
|
|
@@ -340,10 +347,266 @@ var TableCaption = (0, import_react2.forwardRef)(({ className, ...props }, ref)
|
|
|
340
347
|
}
|
|
341
348
|
));
|
|
342
349
|
TableCaption.displayName = "TableCaption";
|
|
350
|
+
|
|
351
|
+
// src/components/DropdownMenu/DropdownMenu.tsx
|
|
352
|
+
var import_react3 = require("react");
|
|
353
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
354
|
+
var DropdownMenuContext = (0, import_react3.createContext)(
|
|
355
|
+
void 0
|
|
356
|
+
);
|
|
357
|
+
var DropdownMenu = ({ children, open, onOpenChange }) => {
|
|
358
|
+
const [internalOpen, setInternalOpen] = (0, import_react3.useState)(false);
|
|
359
|
+
const isControlled = open !== void 0;
|
|
360
|
+
const currentOpen = isControlled ? open : internalOpen;
|
|
361
|
+
const setOpen = (0, import_react3.useCallback)(
|
|
362
|
+
(newOpen) => {
|
|
363
|
+
if (onOpenChange) onOpenChange(newOpen);
|
|
364
|
+
if (!isControlled) setInternalOpen(newOpen);
|
|
365
|
+
},
|
|
366
|
+
[isControlled, onOpenChange]
|
|
367
|
+
);
|
|
368
|
+
const menuRef = (0, import_react3.useRef)(null);
|
|
369
|
+
const handleEscape = (event) => {
|
|
370
|
+
if (event.key === "Escape") {
|
|
371
|
+
setOpen(false);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const handleClickOutside = (event) => {
|
|
375
|
+
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
376
|
+
setOpen(false);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
(0, import_react3.useEffect)(() => {
|
|
380
|
+
if (currentOpen) {
|
|
381
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
382
|
+
document.addEventListener("keydown", handleEscape);
|
|
383
|
+
}
|
|
384
|
+
return () => {
|
|
385
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
386
|
+
document.removeEventListener("keydown", handleEscape);
|
|
387
|
+
};
|
|
388
|
+
}, [currentOpen]);
|
|
389
|
+
const value = (0, import_react3.useMemo)(
|
|
390
|
+
() => ({ open: currentOpen, setOpen }),
|
|
391
|
+
[currentOpen, setOpen]
|
|
392
|
+
);
|
|
393
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DropdownMenuContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "relative", ref: menuRef, children }) });
|
|
394
|
+
};
|
|
395
|
+
var DropdownMenuTrigger = (0, import_react3.forwardRef)(({ className, children, onClick, ...props }, ref) => {
|
|
396
|
+
const context = (0, import_react3.useContext)(DropdownMenuContext);
|
|
397
|
+
if (!context)
|
|
398
|
+
throw new Error("DropdownMenuTrigger must be used within a DropdownMenu");
|
|
399
|
+
const { open, setOpen } = context;
|
|
400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
401
|
+
"button",
|
|
402
|
+
{
|
|
403
|
+
ref,
|
|
404
|
+
className: `border border-border-200 cursor-pointer bg-background-muted hover:bg-background-200 transition-colors px-4 py-2 rounded-sm ${className}`,
|
|
405
|
+
onClick: (e) => {
|
|
406
|
+
e.stopPropagation();
|
|
407
|
+
setOpen(!open);
|
|
408
|
+
if (onClick) onClick(e);
|
|
409
|
+
},
|
|
410
|
+
"aria-expanded": open,
|
|
411
|
+
...props,
|
|
412
|
+
children
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
|
|
417
|
+
var ITEM_SIZE_CLASSES = {
|
|
418
|
+
small: "text-sm",
|
|
419
|
+
medium: "text-md"
|
|
420
|
+
};
|
|
421
|
+
var SIDE_CLASSES = {
|
|
422
|
+
top: "bottom-full",
|
|
423
|
+
right: "top-full",
|
|
424
|
+
bottom: "top-full",
|
|
425
|
+
left: "top-full"
|
|
426
|
+
};
|
|
427
|
+
var ALIGN_CLASSES = {
|
|
428
|
+
start: "left-0",
|
|
429
|
+
center: "left-1/2 -translate-x-1/2",
|
|
430
|
+
end: "right-0"
|
|
431
|
+
};
|
|
432
|
+
var MenuLabel = (0, import_react3.forwardRef)(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
433
|
+
"fieldset",
|
|
434
|
+
{
|
|
435
|
+
ref,
|
|
436
|
+
role: "group",
|
|
437
|
+
className: `text-sm w-full ${inset ? "pl-8" : ""} ${className ?? ""}`,
|
|
438
|
+
...props
|
|
439
|
+
}
|
|
440
|
+
));
|
|
441
|
+
MenuLabel.displayName = "MenuLabel";
|
|
442
|
+
var MenuContent = (0, import_react3.forwardRef)(
|
|
443
|
+
({
|
|
444
|
+
className,
|
|
445
|
+
align = "start",
|
|
446
|
+
side = "bottom",
|
|
447
|
+
sideOffset = 4,
|
|
448
|
+
children,
|
|
449
|
+
...props
|
|
450
|
+
}, ref) => {
|
|
451
|
+
const { open } = (0, import_react3.useContext)(DropdownMenuContext);
|
|
452
|
+
const [isVisible, setIsVisible] = (0, import_react3.useState)(open);
|
|
453
|
+
(0, import_react3.useEffect)(() => {
|
|
454
|
+
if (open) {
|
|
455
|
+
setIsVisible(true);
|
|
456
|
+
} else {
|
|
457
|
+
const timer = setTimeout(() => setIsVisible(false), 200);
|
|
458
|
+
return () => clearTimeout(timer);
|
|
459
|
+
}
|
|
460
|
+
}, [open]);
|
|
461
|
+
if (!isVisible) return null;
|
|
462
|
+
const getPositionClasses = () => {
|
|
463
|
+
const vertical = SIDE_CLASSES[side];
|
|
464
|
+
const horizontal = ALIGN_CLASSES[align];
|
|
465
|
+
return `absolute ${vertical} ${horizontal}`;
|
|
466
|
+
};
|
|
467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
468
|
+
"div",
|
|
469
|
+
{
|
|
470
|
+
ref,
|
|
471
|
+
role: "menu",
|
|
472
|
+
className: `
|
|
473
|
+
bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md border-border-100
|
|
474
|
+
${open ? "animate-in fade-in-0 zoom-in-95" : "animate-out fade-out-0 zoom-out-95"}
|
|
475
|
+
${getPositionClasses()}
|
|
476
|
+
${className}
|
|
477
|
+
`,
|
|
478
|
+
style: {
|
|
479
|
+
marginTop: side === "bottom" ? sideOffset : void 0,
|
|
480
|
+
marginBottom: side === "top" ? sideOffset : void 0,
|
|
481
|
+
marginLeft: side === "right" ? sideOffset : void 0,
|
|
482
|
+
marginRight: side === "left" ? sideOffset : void 0
|
|
483
|
+
},
|
|
484
|
+
...props,
|
|
485
|
+
children
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
MenuContent.displayName = "MenuContent";
|
|
491
|
+
var MenuItem = (0, import_react3.forwardRef)(
|
|
492
|
+
({
|
|
493
|
+
className,
|
|
494
|
+
inset,
|
|
495
|
+
size = "small",
|
|
496
|
+
children,
|
|
497
|
+
iconRight,
|
|
498
|
+
iconLeft,
|
|
499
|
+
disabled = false,
|
|
500
|
+
onClick,
|
|
501
|
+
...props
|
|
502
|
+
}, ref) => {
|
|
503
|
+
const sizeClasses = ITEM_SIZE_CLASSES[size];
|
|
504
|
+
const handleClick = (e) => {
|
|
505
|
+
if (disabled) {
|
|
506
|
+
e.preventDefault();
|
|
507
|
+
e.stopPropagation();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
onClick?.(e);
|
|
511
|
+
};
|
|
512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
513
|
+
"div",
|
|
514
|
+
{
|
|
515
|
+
ref,
|
|
516
|
+
role: "menuitem",
|
|
517
|
+
"aria-disabled": disabled,
|
|
518
|
+
className: `
|
|
519
|
+
relative flex select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0
|
|
520
|
+
${inset && "pl-8"}
|
|
521
|
+
${sizeClasses}
|
|
522
|
+
${className}
|
|
523
|
+
${disabled ? "cursor-not-allowed text-text-400" : "cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
|
|
524
|
+
`,
|
|
525
|
+
onClick: handleClick,
|
|
526
|
+
onKeyDown: (e) => {
|
|
527
|
+
if (e.key === "Enter" || e.key === " ") handleClick(e);
|
|
528
|
+
},
|
|
529
|
+
tabIndex: disabled ? -1 : 0,
|
|
530
|
+
...props,
|
|
531
|
+
children: [
|
|
532
|
+
iconLeft,
|
|
533
|
+
children,
|
|
534
|
+
iconRight
|
|
535
|
+
]
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
MenuItem.displayName = "MenuItem";
|
|
541
|
+
var MenuSeparator = (0, import_react3.forwardRef)(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
542
|
+
"div",
|
|
543
|
+
{
|
|
544
|
+
ref,
|
|
545
|
+
className: `my-1 h-px bg-border-200 ${className}`,
|
|
546
|
+
...props
|
|
547
|
+
}
|
|
548
|
+
));
|
|
549
|
+
MenuSeparator.displayName = "MenuSeparator";
|
|
550
|
+
|
|
551
|
+
// src/components/NavButton/NavButton.tsx
|
|
552
|
+
var import_react4 = require("react");
|
|
553
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
554
|
+
var NavButton = (0, import_react4.forwardRef)(
|
|
555
|
+
({ icon, label, selected = false, className = "", disabled, ...props }, ref) => {
|
|
556
|
+
const baseClasses = [
|
|
557
|
+
"flex",
|
|
558
|
+
"flex-col",
|
|
559
|
+
"items-center",
|
|
560
|
+
"justify-center",
|
|
561
|
+
"gap-0.5",
|
|
562
|
+
"px-12",
|
|
563
|
+
"py-1",
|
|
564
|
+
"rounded-sm",
|
|
565
|
+
"cursor-pointer",
|
|
566
|
+
"text-text-950",
|
|
567
|
+
"text-xs",
|
|
568
|
+
"font-medium",
|
|
569
|
+
"hover:text-text",
|
|
570
|
+
"hover:bg-primary-600",
|
|
571
|
+
"focus-visible:outline-none",
|
|
572
|
+
"focus-visible:ring-2",
|
|
573
|
+
"focus-visible:ring-offset-0",
|
|
574
|
+
"focus-visible:ring-indicator-info",
|
|
575
|
+
"disabled:opacity-50",
|
|
576
|
+
"disabled:cursor-not-allowed",
|
|
577
|
+
"disabled:pointer-events-none"
|
|
578
|
+
];
|
|
579
|
+
const stateClasses = selected ? ["bg-primary-50", "text-primary-950"] : [];
|
|
580
|
+
const allClasses = [...baseClasses, ...stateClasses].join(" ");
|
|
581
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
582
|
+
"button",
|
|
583
|
+
{
|
|
584
|
+
ref,
|
|
585
|
+
type: "button",
|
|
586
|
+
className: `${allClasses} ${className}`,
|
|
587
|
+
disabled,
|
|
588
|
+
"aria-pressed": selected,
|
|
589
|
+
...props,
|
|
590
|
+
children: [
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "flex items-center justify-center w-5 h-5", children: icon }),
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "whitespace-nowrap", children: label })
|
|
593
|
+
]
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
NavButton.displayName = "NavButton";
|
|
343
599
|
// Annotate the CommonJS export names for ESM import in node:
|
|
344
600
|
0 && (module.exports = {
|
|
345
601
|
Button,
|
|
602
|
+
DropdownMenu,
|
|
603
|
+
DropdownMenuTrigger,
|
|
346
604
|
IconRoundedButton,
|
|
605
|
+
MenuContent,
|
|
606
|
+
MenuItem,
|
|
607
|
+
MenuLabel,
|
|
608
|
+
MenuSeparator,
|
|
609
|
+
NavButton,
|
|
347
610
|
SelectionButton,
|
|
348
611
|
Table,
|
|
349
612
|
TableBody,
|
package/dist/index.mjs
CHANGED
|
@@ -303,9 +303,274 @@ var TableCaption = forwardRef2(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
303
303
|
}
|
|
304
304
|
));
|
|
305
305
|
TableCaption.displayName = "TableCaption";
|
|
306
|
+
|
|
307
|
+
// src/components/DropdownMenu/DropdownMenu.tsx
|
|
308
|
+
import {
|
|
309
|
+
createContext,
|
|
310
|
+
useState,
|
|
311
|
+
useCallback,
|
|
312
|
+
useContext,
|
|
313
|
+
forwardRef as forwardRef3,
|
|
314
|
+
useEffect,
|
|
315
|
+
useRef,
|
|
316
|
+
useMemo
|
|
317
|
+
} from "react";
|
|
318
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
319
|
+
var DropdownMenuContext = createContext(
|
|
320
|
+
void 0
|
|
321
|
+
);
|
|
322
|
+
var DropdownMenu = ({ children, open, onOpenChange }) => {
|
|
323
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
324
|
+
const isControlled = open !== void 0;
|
|
325
|
+
const currentOpen = isControlled ? open : internalOpen;
|
|
326
|
+
const setOpen = useCallback(
|
|
327
|
+
(newOpen) => {
|
|
328
|
+
if (onOpenChange) onOpenChange(newOpen);
|
|
329
|
+
if (!isControlled) setInternalOpen(newOpen);
|
|
330
|
+
},
|
|
331
|
+
[isControlled, onOpenChange]
|
|
332
|
+
);
|
|
333
|
+
const menuRef = useRef(null);
|
|
334
|
+
const handleEscape = (event) => {
|
|
335
|
+
if (event.key === "Escape") {
|
|
336
|
+
setOpen(false);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
const handleClickOutside = (event) => {
|
|
340
|
+
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
341
|
+
setOpen(false);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
if (currentOpen) {
|
|
346
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
347
|
+
document.addEventListener("keydown", handleEscape);
|
|
348
|
+
}
|
|
349
|
+
return () => {
|
|
350
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
351
|
+
document.removeEventListener("keydown", handleEscape);
|
|
352
|
+
};
|
|
353
|
+
}, [currentOpen]);
|
|
354
|
+
const value = useMemo(
|
|
355
|
+
() => ({ open: currentOpen, setOpen }),
|
|
356
|
+
[currentOpen, setOpen]
|
|
357
|
+
);
|
|
358
|
+
return /* @__PURE__ */ jsx6(DropdownMenuContext.Provider, { value, children: /* @__PURE__ */ jsx6("div", { className: "relative", ref: menuRef, children }) });
|
|
359
|
+
};
|
|
360
|
+
var DropdownMenuTrigger = forwardRef3(({ className, children, onClick, ...props }, ref) => {
|
|
361
|
+
const context = useContext(DropdownMenuContext);
|
|
362
|
+
if (!context)
|
|
363
|
+
throw new Error("DropdownMenuTrigger must be used within a DropdownMenu");
|
|
364
|
+
const { open, setOpen } = context;
|
|
365
|
+
return /* @__PURE__ */ jsx6(
|
|
366
|
+
"button",
|
|
367
|
+
{
|
|
368
|
+
ref,
|
|
369
|
+
className: `border border-border-200 cursor-pointer bg-background-muted hover:bg-background-200 transition-colors px-4 py-2 rounded-sm ${className}`,
|
|
370
|
+
onClick: (e) => {
|
|
371
|
+
e.stopPropagation();
|
|
372
|
+
setOpen(!open);
|
|
373
|
+
if (onClick) onClick(e);
|
|
374
|
+
},
|
|
375
|
+
"aria-expanded": open,
|
|
376
|
+
...props,
|
|
377
|
+
children
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
|
|
382
|
+
var ITEM_SIZE_CLASSES = {
|
|
383
|
+
small: "text-sm",
|
|
384
|
+
medium: "text-md"
|
|
385
|
+
};
|
|
386
|
+
var SIDE_CLASSES = {
|
|
387
|
+
top: "bottom-full",
|
|
388
|
+
right: "top-full",
|
|
389
|
+
bottom: "top-full",
|
|
390
|
+
left: "top-full"
|
|
391
|
+
};
|
|
392
|
+
var ALIGN_CLASSES = {
|
|
393
|
+
start: "left-0",
|
|
394
|
+
center: "left-1/2 -translate-x-1/2",
|
|
395
|
+
end: "right-0"
|
|
396
|
+
};
|
|
397
|
+
var MenuLabel = forwardRef3(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
398
|
+
"fieldset",
|
|
399
|
+
{
|
|
400
|
+
ref,
|
|
401
|
+
role: "group",
|
|
402
|
+
className: `text-sm w-full ${inset ? "pl-8" : ""} ${className ?? ""}`,
|
|
403
|
+
...props
|
|
404
|
+
}
|
|
405
|
+
));
|
|
406
|
+
MenuLabel.displayName = "MenuLabel";
|
|
407
|
+
var MenuContent = forwardRef3(
|
|
408
|
+
({
|
|
409
|
+
className,
|
|
410
|
+
align = "start",
|
|
411
|
+
side = "bottom",
|
|
412
|
+
sideOffset = 4,
|
|
413
|
+
children,
|
|
414
|
+
...props
|
|
415
|
+
}, ref) => {
|
|
416
|
+
const { open } = useContext(DropdownMenuContext);
|
|
417
|
+
const [isVisible, setIsVisible] = useState(open);
|
|
418
|
+
useEffect(() => {
|
|
419
|
+
if (open) {
|
|
420
|
+
setIsVisible(true);
|
|
421
|
+
} else {
|
|
422
|
+
const timer = setTimeout(() => setIsVisible(false), 200);
|
|
423
|
+
return () => clearTimeout(timer);
|
|
424
|
+
}
|
|
425
|
+
}, [open]);
|
|
426
|
+
if (!isVisible) return null;
|
|
427
|
+
const getPositionClasses = () => {
|
|
428
|
+
const vertical = SIDE_CLASSES[side];
|
|
429
|
+
const horizontal = ALIGN_CLASSES[align];
|
|
430
|
+
return `absolute ${vertical} ${horizontal}`;
|
|
431
|
+
};
|
|
432
|
+
return /* @__PURE__ */ jsx6(
|
|
433
|
+
"div",
|
|
434
|
+
{
|
|
435
|
+
ref,
|
|
436
|
+
role: "menu",
|
|
437
|
+
className: `
|
|
438
|
+
bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md border-border-100
|
|
439
|
+
${open ? "animate-in fade-in-0 zoom-in-95" : "animate-out fade-out-0 zoom-out-95"}
|
|
440
|
+
${getPositionClasses()}
|
|
441
|
+
${className}
|
|
442
|
+
`,
|
|
443
|
+
style: {
|
|
444
|
+
marginTop: side === "bottom" ? sideOffset : void 0,
|
|
445
|
+
marginBottom: side === "top" ? sideOffset : void 0,
|
|
446
|
+
marginLeft: side === "right" ? sideOffset : void 0,
|
|
447
|
+
marginRight: side === "left" ? sideOffset : void 0
|
|
448
|
+
},
|
|
449
|
+
...props,
|
|
450
|
+
children
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
MenuContent.displayName = "MenuContent";
|
|
456
|
+
var MenuItem = forwardRef3(
|
|
457
|
+
({
|
|
458
|
+
className,
|
|
459
|
+
inset,
|
|
460
|
+
size = "small",
|
|
461
|
+
children,
|
|
462
|
+
iconRight,
|
|
463
|
+
iconLeft,
|
|
464
|
+
disabled = false,
|
|
465
|
+
onClick,
|
|
466
|
+
...props
|
|
467
|
+
}, ref) => {
|
|
468
|
+
const sizeClasses = ITEM_SIZE_CLASSES[size];
|
|
469
|
+
const handleClick = (e) => {
|
|
470
|
+
if (disabled) {
|
|
471
|
+
e.preventDefault();
|
|
472
|
+
e.stopPropagation();
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
onClick?.(e);
|
|
476
|
+
};
|
|
477
|
+
return /* @__PURE__ */ jsxs4(
|
|
478
|
+
"div",
|
|
479
|
+
{
|
|
480
|
+
ref,
|
|
481
|
+
role: "menuitem",
|
|
482
|
+
"aria-disabled": disabled,
|
|
483
|
+
className: `
|
|
484
|
+
relative flex select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0
|
|
485
|
+
${inset && "pl-8"}
|
|
486
|
+
${sizeClasses}
|
|
487
|
+
${className}
|
|
488
|
+
${disabled ? "cursor-not-allowed text-text-400" : "cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
|
|
489
|
+
`,
|
|
490
|
+
onClick: handleClick,
|
|
491
|
+
onKeyDown: (e) => {
|
|
492
|
+
if (e.key === "Enter" || e.key === " ") handleClick(e);
|
|
493
|
+
},
|
|
494
|
+
tabIndex: disabled ? -1 : 0,
|
|
495
|
+
...props,
|
|
496
|
+
children: [
|
|
497
|
+
iconLeft,
|
|
498
|
+
children,
|
|
499
|
+
iconRight
|
|
500
|
+
]
|
|
501
|
+
}
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
MenuItem.displayName = "MenuItem";
|
|
506
|
+
var MenuSeparator = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
507
|
+
"div",
|
|
508
|
+
{
|
|
509
|
+
ref,
|
|
510
|
+
className: `my-1 h-px bg-border-200 ${className}`,
|
|
511
|
+
...props
|
|
512
|
+
}
|
|
513
|
+
));
|
|
514
|
+
MenuSeparator.displayName = "MenuSeparator";
|
|
515
|
+
|
|
516
|
+
// src/components/NavButton/NavButton.tsx
|
|
517
|
+
import { forwardRef as forwardRef4 } from "react";
|
|
518
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
519
|
+
var NavButton = forwardRef4(
|
|
520
|
+
({ icon, label, selected = false, className = "", disabled, ...props }, ref) => {
|
|
521
|
+
const baseClasses = [
|
|
522
|
+
"flex",
|
|
523
|
+
"flex-col",
|
|
524
|
+
"items-center",
|
|
525
|
+
"justify-center",
|
|
526
|
+
"gap-0.5",
|
|
527
|
+
"px-12",
|
|
528
|
+
"py-1",
|
|
529
|
+
"rounded-sm",
|
|
530
|
+
"cursor-pointer",
|
|
531
|
+
"text-text-950",
|
|
532
|
+
"text-xs",
|
|
533
|
+
"font-medium",
|
|
534
|
+
"hover:text-text",
|
|
535
|
+
"hover:bg-primary-600",
|
|
536
|
+
"focus-visible:outline-none",
|
|
537
|
+
"focus-visible:ring-2",
|
|
538
|
+
"focus-visible:ring-offset-0",
|
|
539
|
+
"focus-visible:ring-indicator-info",
|
|
540
|
+
"disabled:opacity-50",
|
|
541
|
+
"disabled:cursor-not-allowed",
|
|
542
|
+
"disabled:pointer-events-none"
|
|
543
|
+
];
|
|
544
|
+
const stateClasses = selected ? ["bg-primary-50", "text-primary-950"] : [];
|
|
545
|
+
const allClasses = [...baseClasses, ...stateClasses].join(" ");
|
|
546
|
+
return /* @__PURE__ */ jsxs5(
|
|
547
|
+
"button",
|
|
548
|
+
{
|
|
549
|
+
ref,
|
|
550
|
+
type: "button",
|
|
551
|
+
className: `${allClasses} ${className}`,
|
|
552
|
+
disabled,
|
|
553
|
+
"aria-pressed": selected,
|
|
554
|
+
...props,
|
|
555
|
+
children: [
|
|
556
|
+
/* @__PURE__ */ jsx7("span", { className: "flex items-center justify-center w-5 h-5", children: icon }),
|
|
557
|
+
/* @__PURE__ */ jsx7("span", { className: "whitespace-nowrap", children: label })
|
|
558
|
+
]
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
);
|
|
563
|
+
NavButton.displayName = "NavButton";
|
|
306
564
|
export {
|
|
307
565
|
Button,
|
|
566
|
+
DropdownMenu,
|
|
567
|
+
DropdownMenuTrigger,
|
|
308
568
|
IconRoundedButton,
|
|
569
|
+
MenuContent,
|
|
570
|
+
MenuItem,
|
|
571
|
+
MenuLabel,
|
|
572
|
+
MenuSeparator,
|
|
573
|
+
NavButton,
|
|
309
574
|
SelectionButton,
|
|
310
575
|
Table,
|
|
311
576
|
TableBody,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "analytica-frontend-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "Repositório público dos componentes utilizados nas plataformas da Analytica Ensino",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
".": {
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"require": "./dist/index.cjs"
|
|
15
|
-
}
|
|
15
|
+
},
|
|
16
|
+
"./styles.css": "./dist/index.css"
|
|
16
17
|
},
|
|
17
18
|
"scripts": {
|
|
18
19
|
"lint": "npx eslint \"{src,app}/**/*.{js,jsx,ts,tsx}\" --fix",
|
|
@@ -47,11 +48,6 @@
|
|
|
47
48
|
"publishConfig": {
|
|
48
49
|
"access": "public"
|
|
49
50
|
},
|
|
50
|
-
"peerDependencies": {
|
|
51
|
-
"next": ">=15.0.0",
|
|
52
|
-
"react": ">=19.0.0",
|
|
53
|
-
"react-dom": ">=19.0.0"
|
|
54
|
-
},
|
|
55
51
|
"author": "Analytica Ensino LTDA",
|
|
56
52
|
"license": "MIT",
|
|
57
53
|
"repository": {
|
|
@@ -62,10 +58,13 @@
|
|
|
62
58
|
"bugs": {
|
|
63
59
|
"url": "https://github.com/analytica-ensino/analytica-frontend-lib/issues"
|
|
64
60
|
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"next": ">=15.0.0",
|
|
63
|
+
"react": ">=19.0.0",
|
|
64
|
+
"react-dom": ">=19.0.0"
|
|
65
|
+
},
|
|
65
66
|
"dependencies": {
|
|
66
|
-
"phosphor-react": "^1.4.1"
|
|
67
|
-
"react": "^19.1.0",
|
|
68
|
-
"react-dom": "^19.1.0"
|
|
67
|
+
"phosphor-react": "^1.4.1"
|
|
69
68
|
},
|
|
70
69
|
"devDependencies": {
|
|
71
70
|
"@eslint/eslintrc": "^3.3.1",
|
|
@@ -95,6 +94,8 @@
|
|
|
95
94
|
"polished": "^4.3.1",
|
|
96
95
|
"postcss": "^8.5.4",
|
|
97
96
|
"prettier": "^3.5.3",
|
|
97
|
+
"react": "^19.1.0",
|
|
98
|
+
"react-dom": "^19.1.0",
|
|
98
99
|
"sonarqube-scanner": "^4.3.0",
|
|
99
100
|
"tailwindcss": "^4.1.8",
|
|
100
101
|
"ts-jest": "^29.3.4",
|