@rendra-ui/web 2.1.0
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/LICENSE +21 -0
- package/README.md +433 -0
- package/bin/rendra.mjs +114 -0
- package/dist/base.css +246 -0
- package/dist/button-DIcpJwK9.js +143 -0
- package/dist/button-DIcpJwK9.js.map +1 -0
- package/dist/chart.js +520 -0
- package/dist/chart.js.map +1 -0
- package/dist/cli/auditar.js +125 -0
- package/dist/cli/auditar.js.map +1 -0
- package/dist/cli/codigos.js +15 -0
- package/dist/cli/codigos.js.map +1 -0
- package/dist/cli/help.js +17 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/trocar.js +242 -0
- package/dist/cli/trocar.js.map +1 -0
- package/dist/cli/typescript-loader.js +27 -0
- package/dist/cli/typescript-loader.js.map +1 -0
- package/dist/cn-DVPheE99.js +22 -0
- package/dist/cn-DVPheE99.js.map +1 -0
- package/dist/components-Cm2ezlPT.js +800 -0
- package/dist/components-Cm2ezlPT.js.map +1 -0
- package/dist/components.css +3365 -0
- package/dist/document-viewer.js +204 -0
- package/dist/document-viewer.js.map +1 -0
- package/dist/empty-state-DtyZAfYb.js +168 -0
- package/dist/empty-state-DtyZAfYb.js.map +1 -0
- package/dist/index.js +10530 -0
- package/dist/index.js.map +1 -0
- package/dist/rendra-provider-cCnquOZy.js +88 -0
- package/dist/rendra-provider-cCnquOZy.js.map +1 -0
- package/dist/rich-text-editor.js +439 -0
- package/dist/rich-text-editor.js.map +1 -0
- package/dist/router-bridge.js +51 -0
- package/dist/router-bridge.js.map +1 -0
- package/dist/tokens.css +732 -0
- package/dist/tooltip-DyeuGPCc.js +105 -0
- package/dist/tooltip-DyeuGPCc.js.map +1 -0
- package/dist/types/brand/apply-theme.d.ts +9 -0
- package/dist/types/brand/brand-context.d.ts +21 -0
- package/dist/types/brand/brand-provider.d.ts +49 -0
- package/dist/types/brand/palette.d.ts +63 -0
- package/dist/types/brand/public.d.ts +6 -0
- package/dist/types/brand/theme.d.ts +80 -0
- package/dist/types/brand/types.d.ts +54 -0
- package/dist/types/brand/use-brand.d.ts +1 -0
- package/dist/types/catalog/components.d.ts +81 -0
- package/dist/types/chart.d.ts +1 -0
- package/dist/types/components/app-shell/app-shell.d.ts +36 -0
- package/dist/types/components/app-shell/command-search.d.ts +1 -0
- package/dist/types/components/app-shell/header.d.ts +1 -0
- package/dist/types/components/app-shell/layout.d.ts +53 -0
- package/dist/types/components/app-shell/mega-menu.d.ts +1 -0
- package/dist/types/components/app-shell/navigation-utils.d.ts +18 -0
- package/dist/types/components/app-shell/notifications.d.ts +6 -0
- package/dist/types/components/app-shell/shell-context.d.ts +48 -0
- package/dist/types/components/app-shell/sidebar.d.ts +1 -0
- package/dist/types/components/app-shell/types.d.ts +53 -0
- package/dist/types/components/layout/index.d.ts +4 -0
- package/dist/types/components/layout/page-header.d.ts +38 -0
- package/dist/types/components/layout/primitives.d.ts +71 -0
- package/dist/types/components/layout/tokens.d.ts +43 -0
- package/dist/types/components/rendra-provider.d.ts +48 -0
- package/dist/types/components/rendra-router-bridge.d.ts +18 -0
- package/dist/types/components/ui/accordion.d.ts +16 -0
- package/dist/types/components/ui/action-bar.d.ts +35 -0
- package/dist/types/components/ui/alert.d.ts +26 -0
- package/dist/types/components/ui/avatar.d.ts +23 -0
- package/dist/types/components/ui/badge.d.ts +15 -0
- package/dist/types/components/ui/brand-feedback-icon.d.ts +26 -0
- package/dist/types/components/ui/brand-logo.d.ts +8 -0
- package/dist/types/components/ui/breadcrumb.d.ts +19 -0
- package/dist/types/components/ui/button-group.d.ts +27 -0
- package/dist/types/components/ui/button.d.ts +30 -0
- package/dist/types/components/ui/calendar.d.ts +34 -0
- package/dist/types/components/ui/card.d.ts +32 -0
- package/dist/types/components/ui/chart.d.ts +57 -0
- package/dist/types/components/ui/chat.d.ts +137 -0
- package/dist/types/components/ui/checkbox.d.ts +37 -0
- package/dist/types/components/ui/checklist.d.ts +16 -0
- package/dist/types/components/ui/color-picker.d.ts +13 -0
- package/dist/types/components/ui/data-toolbar.d.ts +37 -0
- package/dist/types/components/ui/date-picker.d.ts +41 -0
- package/dist/types/components/ui/document-viewer.d.ts +19 -0
- package/dist/types/components/ui/drawer.d.ts +30 -0
- package/dist/types/components/ui/dropdown-menu.d.ts +20 -0
- package/dist/types/components/ui/empty-state.d.ts +15 -0
- package/dist/types/components/ui/error-page.d.ts +13 -0
- package/dist/types/components/ui/field.d.ts +42 -0
- package/dist/types/components/ui/form.d.ts +42 -0
- package/dist/types/components/ui/image-cropper.d.ts +55 -0
- package/dist/types/components/ui/image-viewer.d.ts +14 -0
- package/dist/types/components/ui/info-hint.d.ts +9 -0
- package/dist/types/components/ui/input.d.ts +91 -0
- package/dist/types/components/ui/kanban.d.ts +98 -0
- package/dist/types/components/ui/list.d.ts +31 -0
- package/dist/types/components/ui/modal.d.ts +27 -0
- package/dist/types/components/ui/otp-input.d.ts +17 -0
- package/dist/types/components/ui/overlay-shell.d.ts +14 -0
- package/dist/types/components/ui/pagination.d.ts +18 -0
- package/dist/types/components/ui/picker-panel.d.ts +24 -0
- package/dist/types/components/ui/popover.d.ts +12 -0
- package/dist/types/components/ui/progress.d.ts +16 -0
- package/dist/types/components/ui/qr-code.d.ts +13 -0
- package/dist/types/components/ui/radio-group.d.ts +27 -0
- package/dist/types/components/ui/rating.d.ts +19 -0
- package/dist/types/components/ui/rendra-credit.d.ts +12 -0
- package/dist/types/components/ui/repeatable-field.d.ts +22 -0
- package/dist/types/components/ui/rich-text-editor.d.ts +21 -0
- package/dist/types/components/ui/select.d.ts +65 -0
- package/dist/types/components/ui/separator.d.ts +7 -0
- package/dist/types/components/ui/skeleton.d.ts +3 -0
- package/dist/types/components/ui/slider.d.ts +18 -0
- package/dist/types/components/ui/sortable-handle.d.ts +16 -0
- package/dist/types/components/ui/spinner.d.ts +7 -0
- package/dist/types/components/ui/stat-card.d.ts +20 -0
- package/dist/types/components/ui/switch.d.ts +15 -0
- package/dist/types/components/ui/table.d.ts +134 -0
- package/dist/types/components/ui/tabs.d.ts +25 -0
- package/dist/types/components/ui/textarea.d.ts +10 -0
- package/dist/types/components/ui/timeline.d.ts +18 -0
- package/dist/types/components/ui/toast.d.ts +18 -0
- package/dist/types/components/ui/tooltip.d.ts +17 -0
- package/dist/types/components/ui/upload.d.ts +50 -0
- package/dist/types/components/ui/widget-grid.d.ts +22 -0
- package/dist/types/components/ui/wizard.d.ts +38 -0
- package/dist/types/document-viewer.d.ts +1 -0
- package/dist/types/hooks/use-breakpoint.d.ts +21 -0
- package/dist/types/hooks/use-debounced-value.d.ts +5 -0
- package/dist/types/hooks/use-fill-height.d.ts +6 -0
- package/dist/types/hooks/use-lookup.d.ts +30 -0
- package/dist/types/hooks/use-reduced-motion.d.ts +1 -0
- package/dist/types/index.d.ts +76 -0
- package/dist/types/lib/cn.d.ts +2 -0
- package/dist/types/lib/contrast.d.ts +2 -0
- package/dist/types/lib/control.d.ts +20 -0
- package/dist/types/lib/lookup.d.ts +22 -0
- package/dist/types/lib/masks.d.ts +46 -0
- package/dist/types/lib/qr-encode.d.ts +48 -0
- package/dist/types/lib/shape.d.ts +8 -0
- package/dist/types/lib/sortable.d.ts +72 -0
- package/dist/types/lib/validators.d.ts +14 -0
- package/dist/types/rendra-router-bridge.d.ts +1 -0
- package/dist/types/rich-text-editor.d.ts +1 -0
- package/dist/types/widget-grid.d.ts +1 -0
- package/dist/use-breakpoint-BmXOZtF9.js +49 -0
- package/dist/use-breakpoint-BmXOZtF9.js.map +1 -0
- package/dist/widget-grid.js +118 -0
- package/dist/widget-grid.js.map +1 -0
- package/package.json +182 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Motor de reordenação por pointer events (etapa A6 do plano da v2), reaproveitado pela
|
|
4
|
+
* List (arraste da alça) e pelo Kanban (arraste do card no toque, que o HTML5 drag and
|
|
5
|
+
* drop nativo não cobre). Pointer events funcionam iguais para mouse, caneta e toque, com
|
|
6
|
+
* setPointerCapture mantendo os eventos de mover e soltar no mesmo elemento que começou o
|
|
7
|
+
* arraste, mesmo que o ponteiro saia dele no meio do gesto.
|
|
8
|
+
*/
|
|
9
|
+
/** Item de uma lista reordenável: id estável e o dado original associado. */
|
|
10
|
+
export interface SortableItem<T = unknown> {
|
|
11
|
+
id: string;
|
|
12
|
+
data: T;
|
|
13
|
+
}
|
|
14
|
+
export interface UseSortableOptions<T> {
|
|
15
|
+
items: SortableItem<T>[];
|
|
16
|
+
/** Chamado com a lista já reordenada. Sem ela, o motor não reordena nada. */
|
|
17
|
+
onReorder?: (items: SortableItem<T>[]) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface UseSortableResult {
|
|
20
|
+
/** Id do item sendo arrastado no momento, ou null fora de um arraste. */
|
|
21
|
+
draggingId: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Espalhe no elemento (um <button>) que serve de alça do arraste: pointer down inicia o
|
|
24
|
+
* arraste (mouse e toque) e as setas ↑/↓ movem uma posição pelo teclado. Usada pelo
|
|
25
|
+
* componente SortableHandle (src/components/ui/sortable-handle.tsx), reaproveitado pela
|
|
26
|
+
* List e pelo Upload, para não duplicar a alça inteira em cada um.
|
|
27
|
+
*/
|
|
28
|
+
handleProps: (id: string) => {
|
|
29
|
+
onPointerDown: (e: ReactPointerEvent<HTMLElement>) => void;
|
|
30
|
+
onKeyDown: (e: ReactKeyboardEvent<HTMLElement>) => void;
|
|
31
|
+
className: string;
|
|
32
|
+
};
|
|
33
|
+
/** Espalhe no elemento da linha, para o motor localizar o alvo do arraste sob o ponteiro. */
|
|
34
|
+
itemProps: (id: string) => Record<string, string>;
|
|
35
|
+
/** Move o item uma posição para cima (-1) ou para baixo (1); usado pelo teclado (setas). */
|
|
36
|
+
moveBy: (id: string, delta: -1 | 1) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Acha, sob o ponto x,y da tela, o elemento mais próximo com o atributo dado (o motor da
|
|
40
|
+
* List usa "data-sortable-id"; o Kanban passa o atributo dele, por coluna ou por card) e
|
|
41
|
+
* se o ponto está antes ou depois do meio dele. Sem elemento sob o ponto, devolve null.
|
|
42
|
+
*/
|
|
43
|
+
export declare function findDragTarget(x: number, y: number, attr?: string): {
|
|
44
|
+
id: string;
|
|
45
|
+
before: boolean;
|
|
46
|
+
} | null;
|
|
47
|
+
/**
|
|
48
|
+
* A partir do resultado de findDragTarget (id do alvo e se o ponto está antes do meio
|
|
49
|
+
* dele), calcula a posição de inserção numa lista de ids na mesma ordem em que aparecem
|
|
50
|
+
* na tela. Usada pelo useSortable (List e Upload) e diretamente pelo Kanban, para os dois
|
|
51
|
+
* nunca recalcularem essa conta cada um do seu jeito.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveDropIndex(ids: string[], found: {
|
|
54
|
+
id: string;
|
|
55
|
+
before: boolean;
|
|
56
|
+
} | null): number;
|
|
57
|
+
/**
|
|
58
|
+
* Inicia um arraste por pointer events num nó já capturado (setPointerCapture): entrega a
|
|
59
|
+
* posição do ponteiro a cada movimento e avisa quando solta, sem depender de um item
|
|
60
|
+
* específico. Usado tanto pelo useSortable quanto diretamente pelo Kanban.
|
|
61
|
+
*/
|
|
62
|
+
export declare function bindPointerDrag(node: HTMLElement, e: ReactPointerEvent<HTMLElement> | PointerEvent, handlers: {
|
|
63
|
+
onMove: (x: number, y: number) => void;
|
|
64
|
+
onEnd: () => void;
|
|
65
|
+
}): void;
|
|
66
|
+
/**
|
|
67
|
+
* Motor de reordenação de uma lista única por alça: arraste (pointer events, mouse e
|
|
68
|
+
* toque) e teclado (setas, uma posição por vez). Sem onReorder, a lista é só leitura e o
|
|
69
|
+
* motor não inicia arraste nenhum (quem decide se a alça aparece é o componente que usa o
|
|
70
|
+
* hook, como a List).
|
|
71
|
+
*/
|
|
72
|
+
export declare function useSortable<T>({ items, onReorder }: UseSortableOptions<T>): UseSortableResult;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare function isValidCpf(value: string): boolean;
|
|
3
|
+
export declare function isValidCnpj(value: string): boolean;
|
|
4
|
+
export declare function isValidDateBR(value: string): boolean;
|
|
5
|
+
export declare const zBR: {
|
|
6
|
+
required: (label?: string) => z.ZodString;
|
|
7
|
+
email: () => z.ZodString;
|
|
8
|
+
cpf: () => z.ZodString;
|
|
9
|
+
cnpj: () => z.ZodString;
|
|
10
|
+
cpfCnpj: () => z.ZodString;
|
|
11
|
+
phone: () => z.ZodString;
|
|
12
|
+
cep: () => z.ZodString;
|
|
13
|
+
dateBR: () => z.ZodString;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RendraRouterBridge, type RouteHandle } from './components/rendra-router-bridge';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components/ui/rich-text-editor';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components/ui/widget-grid';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
//#region src/hooks/use-breakpoint.ts
|
|
4
|
+
/**
|
|
5
|
+
* Pontos de quebra, espelhando os padrões do Tailwind (sm 40rem, md 48rem, lg 64rem...).
|
|
6
|
+
* Único hook de detecção de tela do sistema. Para adaptação ao espaço disponível
|
|
7
|
+
* dentro de um bloco (e não à tela), use container queries (@container) no CSS.
|
|
8
|
+
*/
|
|
9
|
+
var breakpoints = {
|
|
10
|
+
sm: 640,
|
|
11
|
+
md: 768,
|
|
12
|
+
lg: 1024,
|
|
13
|
+
xl: 1280,
|
|
14
|
+
"2xl": 1536
|
|
15
|
+
};
|
|
16
|
+
var order = [
|
|
17
|
+
"sm",
|
|
18
|
+
"md",
|
|
19
|
+
"lg",
|
|
20
|
+
"xl",
|
|
21
|
+
"2xl"
|
|
22
|
+
];
|
|
23
|
+
function subscribe(cb) {
|
|
24
|
+
const lists = order.map((bp) => matchMedia(`(min-width: ${breakpoints[bp]}px)`));
|
|
25
|
+
lists.forEach((mq) => mq.addEventListener("change", cb));
|
|
26
|
+
return () => lists.forEach((mq) => mq.removeEventListener("change", cb));
|
|
27
|
+
}
|
|
28
|
+
function getCurrent() {
|
|
29
|
+
let current = "base";
|
|
30
|
+
for (const bp of order) if (matchMedia(`(min-width: ${breakpoints[bp]}px)`).matches) current = bp;
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
function useBreakpoint() {
|
|
34
|
+
const current = useSyncExternalStore(subscribe, getCurrent, () => "lg");
|
|
35
|
+
const rank = current === "base" ? -1 : order.indexOf(current);
|
|
36
|
+
const up = (bp) => rank >= order.indexOf(bp);
|
|
37
|
+
return {
|
|
38
|
+
current,
|
|
39
|
+
up,
|
|
40
|
+
/** Abaixo de 768px: componentes se reconstroem no formato mobile. */
|
|
41
|
+
isMobile: !up("md"),
|
|
42
|
+
isTablet: up("md") && !up("lg"),
|
|
43
|
+
isDesktop: up("lg")
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { useBreakpoint as n, breakpoints as t };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=use-breakpoint-BmXOZtF9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-breakpoint-BmXOZtF9.js","names":[],"sources":["../src/hooks/use-breakpoint.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react'\n\n/**\n * Pontos de quebra, espelhando os padrões do Tailwind (sm 40rem, md 48rem, lg 64rem...).\n * Único hook de detecção de tela do sistema. Para adaptação ao espaço disponível\n * dentro de um bloco (e não à tela), use container queries (@container) no CSS.\n */\nexport const breakpoints = { sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536 } as const\nexport type Breakpoint = keyof typeof breakpoints\n\nconst order: Breakpoint[] = ['sm', 'md', 'lg', 'xl', '2xl']\n\nfunction subscribe(cb: () => void) {\n const lists = order.map((bp) => matchMedia(`(min-width: ${breakpoints[bp]}px)`))\n lists.forEach((mq) => mq.addEventListener('change', cb))\n return () => lists.forEach((mq) => mq.removeEventListener('change', cb))\n}\n\nfunction getCurrent(): Breakpoint | 'base' {\n let current: Breakpoint | 'base' = 'base'\n for (const bp of order) if (matchMedia(`(min-width: ${breakpoints[bp]}px)`).matches) current = bp\n return current\n}\n\nexport function useBreakpoint() {\n const current = useSyncExternalStore(subscribe, getCurrent, () => 'lg' as const)\n const rank = current === 'base' ? -1 : order.indexOf(current)\n const up = (bp: Breakpoint) => rank >= order.indexOf(bp)\n return {\n current,\n up,\n /** Abaixo de 768px: componentes se reconstroem no formato mobile. */\n isMobile: !up('md'),\n isTablet: up('md') && !up('lg'),\n isDesktop: up('lg'),\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,cAAc;CAAE,IAAI;CAAK,IAAI;CAAK,IAAI;CAAM,IAAI;CAAM,OAAO;AAAK;AAG/E,IAAM,QAAsB;CAAC;CAAM;CAAM;CAAM;CAAM;AAAK;AAE1D,SAAS,UAAU,IAAgB;CACjC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,eAAe,YAAY,IAAI,IAAI,CAAC;CAC/E,MAAM,SAAS,OAAO,GAAG,iBAAiB,UAAU,EAAE,CAAC;CACvD,aAAa,MAAM,SAAS,OAAO,GAAG,oBAAoB,UAAU,EAAE,CAAC;AACzE;AAEA,SAAS,aAAkC;CACzC,IAAI,UAA+B;CACnC,KAAK,MAAM,MAAM,OAAO,IAAI,WAAW,eAAe,YAAY,IAAI,IAAI,CAAC,CAAC,SAAS,UAAU;CAC/F,OAAO;AACT;AAEA,SAAgB,gBAAgB;CAC9B,MAAM,UAAU,qBAAqB,WAAW,kBAAkB,IAAa;CAC/E,MAAM,OAAO,YAAY,SAAS,KAAK,MAAM,QAAQ,OAAO;CAC5D,MAAM,MAAM,OAAmB,QAAQ,MAAM,QAAQ,EAAE;CACvD,OAAO;EACL;EACA;;EAEA,UAAU,CAAC,GAAG,IAAI;EAClB,UAAU,GAAG,IAAI,KAAK,CAAC,GAAG,IAAI;EAC9B,WAAW,GAAG,IAAI;CACpB;AACF"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
|
|
2
|
+
import { t as cn } from "./cn-DVPheE99.js";
|
|
3
|
+
import { GripVertical } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { ResponsiveGridLayout, useContainerWidth } from "react-grid-layout";
|
|
7
|
+
import "react-grid-layout/css/styles.css";
|
|
8
|
+
//#region src/components/ui/widget-grid.tsx
|
|
9
|
+
var breakpoints = {
|
|
10
|
+
lg: 1024,
|
|
11
|
+
md: 640,
|
|
12
|
+
sm: 0
|
|
13
|
+
};
|
|
14
|
+
var cols = {
|
|
15
|
+
lg: 12,
|
|
16
|
+
md: 6,
|
|
17
|
+
sm: 1
|
|
18
|
+
};
|
|
19
|
+
var ROW = 40;
|
|
20
|
+
/** Arrumação padrão: os widgets em sequência, quebrando a linha quando não cabem. */
|
|
21
|
+
function flow(widgets, columns) {
|
|
22
|
+
let x = 0;
|
|
23
|
+
let y = 0;
|
|
24
|
+
let rowH = 0;
|
|
25
|
+
return widgets.map((wd) => {
|
|
26
|
+
const w = Math.min(columns, columns === 12 ? wd.w : Math.max(1, Math.round(wd.w / 12 * columns)));
|
|
27
|
+
if (x + w > columns) {
|
|
28
|
+
x = 0;
|
|
29
|
+
y += rowH;
|
|
30
|
+
rowH = 0;
|
|
31
|
+
}
|
|
32
|
+
const item = {
|
|
33
|
+
i: wd.id,
|
|
34
|
+
x,
|
|
35
|
+
y,
|
|
36
|
+
w,
|
|
37
|
+
h: wd.h,
|
|
38
|
+
minW: Math.min(w, wd.minW ?? Math.min(3, columns)),
|
|
39
|
+
minH: wd.minH ?? 3
|
|
40
|
+
};
|
|
41
|
+
x += w;
|
|
42
|
+
rowH = Math.max(rowH, wd.h);
|
|
43
|
+
return item;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
var read = (key) => {
|
|
47
|
+
if (!key) return null;
|
|
48
|
+
try {
|
|
49
|
+
const raw = localStorage.getItem(key);
|
|
50
|
+
return raw ? JSON.parse(raw) : null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
function WidgetGrid({ widgets, editable = false, storageKey, className }) {
|
|
56
|
+
const { width, containerRef, mounted } = useContainerWidth({ measureBeforeMount: true });
|
|
57
|
+
const defaults = useMemo(() => ({
|
|
58
|
+
lg: flow(widgets, 12),
|
|
59
|
+
md: flow(widgets, 6),
|
|
60
|
+
sm: flow(widgets, 1)
|
|
61
|
+
}), [widgets]);
|
|
62
|
+
const [layouts, setLayouts] = useState(() => read(storageKey) ?? defaults);
|
|
63
|
+
const mobile = width < breakpoints.md;
|
|
64
|
+
const save = (_, all) => {
|
|
65
|
+
setLayouts(all);
|
|
66
|
+
if (!storageKey || !editable) return;
|
|
67
|
+
try {
|
|
68
|
+
localStorage.setItem(storageKey, JSON.stringify(all));
|
|
69
|
+
} catch {}
|
|
70
|
+
};
|
|
71
|
+
return /* @__PURE__ */ jsx("div", {
|
|
72
|
+
ref: containerRef,
|
|
73
|
+
"data-rendra": "WDG-001",
|
|
74
|
+
className: cn("widget-grid min-w-0", editable && !mobile && "is-editing", className),
|
|
75
|
+
children: mounted && /* @__PURE__ */ jsx(ResponsiveGridLayout, {
|
|
76
|
+
width,
|
|
77
|
+
breakpoints,
|
|
78
|
+
cols,
|
|
79
|
+
layouts,
|
|
80
|
+
rowHeight: ROW,
|
|
81
|
+
margin: [16, 16],
|
|
82
|
+
containerPadding: [0, 0],
|
|
83
|
+
dragConfig: {
|
|
84
|
+
enabled: editable && !mobile,
|
|
85
|
+
handle: ".widget-handle",
|
|
86
|
+
bounded: false,
|
|
87
|
+
threshold: 3
|
|
88
|
+
},
|
|
89
|
+
resizeConfig: {
|
|
90
|
+
enabled: editable && !mobile,
|
|
91
|
+
handles: ["se"]
|
|
92
|
+
},
|
|
93
|
+
onLayoutChange: save,
|
|
94
|
+
children: widgets.map((wd) => /* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "relative flex min-w-0 flex-col [&>*]:min-h-0 [&>*]:flex-1",
|
|
96
|
+
children: [editable && !mobile && /* @__PURE__ */ jsx("span", {
|
|
97
|
+
className: "widget-handle absolute top-2 left-1/2 z-10 flex h-6 -translate-x-1/2 cursor-grab items-center rounded-full border bg-card px-2 text-muted-foreground shadow-sm active:cursor-grabbing",
|
|
98
|
+
"aria-label": "Arrastar widget",
|
|
99
|
+
role: "img",
|
|
100
|
+
children: /* @__PURE__ */ jsx(GripVertical, {
|
|
101
|
+
className: "size-icon-sm rotate-90",
|
|
102
|
+
"aria-hidden": true
|
|
103
|
+
})
|
|
104
|
+
}), wd.content]
|
|
105
|
+
}, wd.id))
|
|
106
|
+
})
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** Apaga a arrumação salva (volta ao padrão na próxima renderização). */
|
|
110
|
+
function resetWidgetLayout(storageKey) {
|
|
111
|
+
try {
|
|
112
|
+
localStorage.removeItem(storageKey);
|
|
113
|
+
} catch {}
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { WidgetGrid, resetWidgetLayout };
|
|
117
|
+
|
|
118
|
+
//# sourceMappingURL=widget-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-grid.js","names":[],"sources":["../src/components/ui/widget-grid.tsx"],"sourcesContent":["import { GripVertical } from 'lucide-react'\nimport { useMemo, useState, type ReactNode } from 'react'\nimport {\n ResponsiveGridLayout,\n useContainerWidth,\n type Layout,\n type LayoutItem,\n type ResponsiveLayouts,\n} from 'react-grid-layout'\nimport 'react-grid-layout/css/styles.css'\nimport { cn } from '@/lib/cn'\n\n/*\n * Grade de widgets do painel (react-grid-layout, código aberto). Fixa por padrão; com\n * editable, cada widget ganha a alça de arrastar (no topo) e o canto de redimensionar, e os\n * outros se reorganizam sozinhos. A arrumação fica salva no navegador (storageKey).\n * Colunas: 12 no desktop, 6 no tablet, 1 no celular (empilhado, sem edição).\n */\n\nexport interface Widget {\n id: string\n /** Largura em colunas de 12 e altura em linhas (cada linha tem 40px). */\n w: number\n h: number\n minW?: number\n minH?: number\n content: ReactNode\n}\n\nexport interface WidgetGridProps {\n widgets: Widget[]\n /** Libera arrastar e redimensionar. */\n editable?: boolean\n /** Chave no navegador para lembrar a arrumação de cada pessoa. */\n storageKey?: string\n className?: string\n}\n\ntype Bp = 'lg' | 'md' | 'sm'\nconst breakpoints = { lg: 1024, md: 640, sm: 0 } as const\nconst cols = { lg: 12, md: 6, sm: 1 } as const\nconst ROW = 40\n\n/** Arrumação padrão: os widgets em sequência, quebrando a linha quando não cabem. */\nfunction flow(widgets: Widget[], columns: number): LayoutItem[] {\n let x = 0\n let y = 0\n let rowH = 0\n return widgets.map((wd) => {\n const w = Math.min(\n columns,\n columns === 12 ? wd.w : Math.max(1, Math.round((wd.w / 12) * columns)),\n )\n if (x + w > columns) {\n x = 0\n y += rowH\n rowH = 0\n }\n const item: LayoutItem = {\n i: wd.id,\n x,\n y,\n w,\n h: wd.h,\n minW: Math.min(w, wd.minW ?? Math.min(3, columns)),\n minH: wd.minH ?? 3,\n }\n x += w\n rowH = Math.max(rowH, wd.h)\n return item\n })\n}\n\nconst read = (key?: string): ResponsiveLayouts<Bp> | null => {\n if (!key) return null\n try {\n const raw = localStorage.getItem(key)\n return raw ? (JSON.parse(raw) as ResponsiveLayouts<Bp>) : null\n } catch {\n return null\n }\n}\n\nexport function WidgetGrid({ widgets, editable = false, storageKey, className }: WidgetGridProps) {\n // Mede antes de desenhar: sem isso, a grade nasce com a largura padrão de 1280px e os\n // widgets deslizam até o lugar certo, passando da borda da tela durante a transição.\n const { width, containerRef, mounted } = useContainerWidth({ measureBeforeMount: true })\n const defaults = useMemo<ResponsiveLayouts<Bp>>(\n () => ({ lg: flow(widgets, 12), md: flow(widgets, 6), sm: flow(widgets, 1) }),\n [widgets],\n )\n const [layouts, setLayouts] = useState<ResponsiveLayouts<Bp>>(() => read(storageKey) ?? defaults)\n const mobile = width < breakpoints.md\n\n const save = (_: Layout, all: ResponsiveLayouts<Bp>) => {\n setLayouts(all)\n if (!storageKey || !editable) return\n try {\n localStorage.setItem(storageKey, JSON.stringify(all))\n } catch {\n // segue só em memória\n }\n }\n\n return (\n <div\n ref={containerRef}\n data-rendra=\"WDG-001\"\n className={cn('widget-grid min-w-0', editable && !mobile && 'is-editing', className)}\n >\n {mounted && (\n <ResponsiveGridLayout\n width={width}\n breakpoints={breakpoints}\n cols={cols}\n layouts={layouts}\n rowHeight={ROW}\n margin={[16, 16]}\n containerPadding={[0, 0]}\n dragConfig={{\n enabled: editable && !mobile,\n handle: '.widget-handle',\n bounded: false,\n threshold: 3,\n }}\n resizeConfig={{ enabled: editable && !mobile, handles: ['se'] }}\n onLayoutChange={save}\n >\n {widgets.map((wd) => (\n <div key={wd.id} className=\"relative flex min-w-0 flex-col [&>*]:min-h-0 [&>*]:flex-1\">\n {editable && !mobile && (\n <span\n className=\"widget-handle absolute top-2 left-1/2 z-10 flex h-6 -translate-x-1/2 cursor-grab items-center rounded-full border bg-card px-2 text-muted-foreground shadow-sm active:cursor-grabbing\"\n aria-label=\"Arrastar widget\"\n role=\"img\"\n >\n <GripVertical className=\"size-icon-sm rotate-90\" aria-hidden />\n </span>\n )}\n {wd.content}\n </div>\n ))}\n </ResponsiveGridLayout>\n )}\n </div>\n )\n}\n\n/** Apaga a arrumação salva (volta ao padrão na próxima renderização). */\nexport function resetWidgetLayout(storageKey: string) {\n try {\n localStorage.removeItem(storageKey)\n } catch {\n // nada a fazer\n }\n}\n"],"mappings":";;;;;;;;AAuCA,IAAM,cAAc;CAAE,IAAI;CAAM,IAAI;CAAK,IAAI;AAAE;AAC/C,IAAM,OAAO;CAAE,IAAI;CAAI,IAAI;CAAG,IAAI;AAAE;AACpC,IAAM,MAAM;;AAGZ,SAAS,KAAK,SAAmB,SAA+B;CAC9D,IAAI,IAAI;CACR,IAAI,IAAI;CACR,IAAI,OAAO;CACX,OAAO,QAAQ,KAAK,OAAO;EACzB,MAAM,IAAI,KAAK,IACb,SACA,YAAY,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,KAAK,MAAO,GAAG,IAAI,KAAM,OAAO,CAAC,CACvE;EACA,IAAI,IAAI,IAAI,SAAS;GACnB,IAAI;GACJ,KAAK;GACL,OAAO;EACT;EACA,MAAM,OAAmB;GACvB,GAAG,GAAG;GACN;GACA;GACA;GACA,GAAG,GAAG;GACN,MAAM,KAAK,IAAI,GAAG,GAAG,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC;GACjD,MAAM,GAAG,QAAQ;EACnB;EACA,KAAK;EACL,OAAO,KAAK,IAAI,MAAM,GAAG,CAAC;EAC1B,OAAO;CACT,CAAC;AACH;AAEA,IAAM,QAAQ,QAA+C;CAC3D,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI;EACF,MAAM,MAAM,aAAa,QAAQ,GAAG;EACpC,OAAO,MAAO,KAAK,MAAM,GAAG,IAA8B;CAC5D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,WAAW,EAAE,SAAS,WAAW,OAAO,YAAY,aAA8B;CAGhG,MAAM,EAAE,OAAO,cAAc,YAAY,kBAAkB,EAAE,oBAAoB,KAAK,CAAC;CACvF,MAAM,WAAW,eACR;EAAE,IAAI,KAAK,SAAS,EAAE;EAAG,IAAI,KAAK,SAAS,CAAC;EAAG,IAAI,KAAK,SAAS,CAAC;CAAE,IAC3E,CAAC,OAAO,CACV;CACA,MAAM,CAAC,SAAS,cAAc,eAAsC,KAAK,UAAU,KAAK,QAAQ;CAChG,MAAM,SAAS,QAAQ,YAAY;CAEnC,MAAM,QAAQ,GAAW,QAA+B;EACtD,WAAW,GAAG;EACd,IAAI,CAAC,cAAc,CAAC,UAAU;EAC9B,IAAI;GACF,aAAa,QAAQ,YAAY,KAAK,UAAU,GAAG,CAAC;EACtD,QAAQ,CAER;CACF;CAEA,OACE,oBAAC,OAAD;EACE,KAAK;EACL,eAAY;EACZ,WAAW,GAAG,uBAAuB,YAAY,CAAC,UAAU,cAAc,SAAS;EAElF,UAAA,WACC,oBAAC,sBAAD;GACS;GACM;GACP;GACG;GACT,WAAW;GACX,QAAQ,CAAC,IAAI,EAAE;GACf,kBAAkB,CAAC,GAAG,CAAC;GACvB,YAAY;IACV,SAAS,YAAY,CAAC;IACtB,QAAQ;IACR,SAAS;IACT,WAAW;GACb;GACA,cAAc;IAAE,SAAS,YAAY,CAAC;IAAQ,SAAS,CAAC,IAAI;GAAE;GAC9D,gBAAgB;GAEf,UAAA,QAAQ,KAAK,OACZ,qBAAC,OAAD;IAAiB,WAAU;IAA3B,UAAA,CACG,YAAY,CAAC,UACZ,oBAAC,QAAD;KACE,WAAU;KACV,cAAW;KACX,MAAK;KAEL,UAAA,oBAAC,cAAD;MAAc,WAAU;MAAyB,eAAA;KAAa,CAAA;IAC1D,CAAA,GAEP,GAAG,OACD;GAXK,GAAA,GAAG,EAWR,CACN;EACmB,CAAA;CAErB,CAAA;AAET;;AAGA,SAAgB,kBAAkB,YAAoB;CACpD,IAAI;EACF,aAAa,WAAW,UAAU;CACpC,QAAQ,CAER;AACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rendra-ui/web",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Design system e boilerplate React (Vite, TypeScript, Tailwind v4, Radix), com CLI de catálogo, auditoria e troca de variante.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Bruno Magalhaes",
|
|
8
|
+
"email": "contato@brunomagalhaes.me",
|
|
9
|
+
"url": "https://www.brunomagalhaes.me"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/bsmagalhaes/rendra-design-system#readme",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/bsmagalhaes/rendra-design-system.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/bsmagalhaes/rendra-design-system/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"design-system",
|
|
21
|
+
"react",
|
|
22
|
+
"tailwindcss",
|
|
23
|
+
"radix",
|
|
24
|
+
"component-library",
|
|
25
|
+
"boilerplate",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/types/index.d.ts",
|
|
33
|
+
"bin": {
|
|
34
|
+
"rendra": "bin/rendra.mjs"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"bin"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": [
|
|
41
|
+
"dist/*.css"
|
|
42
|
+
],
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist/types/index.d.ts",
|
|
46
|
+
"import": "./dist/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./router-bridge": {
|
|
49
|
+
"types": "./dist/types/rendra-router-bridge.d.ts",
|
|
50
|
+
"import": "./dist/router-bridge.js"
|
|
51
|
+
},
|
|
52
|
+
"./document-viewer": {
|
|
53
|
+
"types": "./dist/types/document-viewer.d.ts",
|
|
54
|
+
"import": "./dist/document-viewer.js"
|
|
55
|
+
},
|
|
56
|
+
"./rich-text-editor": {
|
|
57
|
+
"types": "./dist/types/rich-text-editor.d.ts",
|
|
58
|
+
"import": "./dist/rich-text-editor.js"
|
|
59
|
+
},
|
|
60
|
+
"./chart": {
|
|
61
|
+
"types": "./dist/types/chart.d.ts",
|
|
62
|
+
"import": "./dist/chart.js"
|
|
63
|
+
},
|
|
64
|
+
"./widget-grid": {
|
|
65
|
+
"types": "./dist/types/widget-grid.d.ts",
|
|
66
|
+
"import": "./dist/widget-grid.js"
|
|
67
|
+
},
|
|
68
|
+
"./tokens.css": "./dist/tokens.css",
|
|
69
|
+
"./base.css": "./dist/base.css",
|
|
70
|
+
"./components.css": "./dist/components.css",
|
|
71
|
+
"./package.json": "./package.json"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"radix-ui": "^1.6.7",
|
|
75
|
+
"react": "^19.3.0",
|
|
76
|
+
"react-dom": "^19.3.0",
|
|
77
|
+
"typescript": "^5.9.0"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"typescript": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"overrides": {
|
|
85
|
+
"eslint-plugin-jsx-a11y": {
|
|
86
|
+
"eslint": "$eslint"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"dev": "vite",
|
|
91
|
+
"build": "tsc -b && vite build",
|
|
92
|
+
"build:lib": "vite build --config vite.lib.config.ts && tsc -p tsconfig.build.json && node scripts/fix-dts-aliases.mjs && node scripts/build-lib.mjs",
|
|
93
|
+
"verify:pack": "node scripts/verify-pack.mjs",
|
|
94
|
+
"preview": "vite preview",
|
|
95
|
+
"typecheck": "tsc -b",
|
|
96
|
+
"lint": "eslint .",
|
|
97
|
+
"format": "prettier --write .",
|
|
98
|
+
"format:check": "prettier --check .",
|
|
99
|
+
"check:rules": "node scripts/check-design-rules.mjs",
|
|
100
|
+
"test:layout": "playwright test --project=layout",
|
|
101
|
+
"shots": "node scripts/screenshots.mjs",
|
|
102
|
+
"test:layout:report": "playwright show-report",
|
|
103
|
+
"audit:layout": "node scripts/audit-layout.mjs",
|
|
104
|
+
"storybook": "storybook dev -p 6006",
|
|
105
|
+
"build-storybook": "storybook build",
|
|
106
|
+
"docs:images": "node scripts/readme-images.mjs",
|
|
107
|
+
"test": "vitest run",
|
|
108
|
+
"test:watch": "vitest",
|
|
109
|
+
"test:coverage": "vitest run --coverage",
|
|
110
|
+
"test:a11y": "playwright test --project=a11y",
|
|
111
|
+
"test:visual": "playwright test --project=visual",
|
|
112
|
+
"test:visual:update": "playwright test --project=visual --update-snapshots",
|
|
113
|
+
"test:css-layers": "playwright test --project=css-layers",
|
|
114
|
+
"registry:build": "node scripts/build-registry.mjs",
|
|
115
|
+
"palettes:build": "node scripts/build-palettes.mjs"
|
|
116
|
+
},
|
|
117
|
+
"dependencies": {
|
|
118
|
+
"@tanstack/react-table": "^8.21.3",
|
|
119
|
+
"@tiptap/extension-image": "^3.31.3",
|
|
120
|
+
"@tiptap/extension-placeholder": "^3.31.3",
|
|
121
|
+
"@tiptap/extension-table": "^3.31.3",
|
|
122
|
+
"@tiptap/extension-text-align": "^3.31.3",
|
|
123
|
+
"@tiptap/pm": "^3.31.3",
|
|
124
|
+
"@tiptap/react": "^3.31.3",
|
|
125
|
+
"@tiptap/starter-kit": "^3.31.3",
|
|
126
|
+
"class-variance-authority": "^0.7.1",
|
|
127
|
+
"clsx": "^2.1.1",
|
|
128
|
+
"cmdk": "^1.1.1",
|
|
129
|
+
"date-fns": "^4.4.0",
|
|
130
|
+
"imask": "^7.6.1",
|
|
131
|
+
"lucide-react": "^1.48.0",
|
|
132
|
+
"pdfjs-dist": "^6.3.289",
|
|
133
|
+
"radix-ui": "^1.6.7",
|
|
134
|
+
"react": "^19.3.0",
|
|
135
|
+
"react-day-picker": "^10.0.1",
|
|
136
|
+
"react-dom": "^19.3.0",
|
|
137
|
+
"react-grid-layout": "^2.2.4",
|
|
138
|
+
"react-hook-form": "^7.89.0",
|
|
139
|
+
"recharts": "^3.10.1",
|
|
140
|
+
"sonner": "^2.0.8",
|
|
141
|
+
"tailwind-merge": "^3.7.0",
|
|
142
|
+
"zod": "^4.6.5"
|
|
143
|
+
},
|
|
144
|
+
"devDependencies": {
|
|
145
|
+
"@axe-core/playwright": "^4.13.0",
|
|
146
|
+
"@eslint/js": "^10.0.1",
|
|
147
|
+
"@fontsource/dm-sans": "^5.3.0",
|
|
148
|
+
"@fontsource/inter": "^5.3.0",
|
|
149
|
+
"@fontsource/poppins": "^5.3.0",
|
|
150
|
+
"@hookform/resolvers": "^5.9.1",
|
|
151
|
+
"@playwright/test": "^1.63.0",
|
|
152
|
+
"@storybook/addon-a11y": "^10.6.0",
|
|
153
|
+
"@storybook/addon-docs": "^10.6.0",
|
|
154
|
+
"@storybook/react-vite": "^10.6.0",
|
|
155
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
156
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
157
|
+
"@testing-library/react": "^16.3.3",
|
|
158
|
+
"@testing-library/user-event": "^14.6.7",
|
|
159
|
+
"@types/node": "^26.6.3",
|
|
160
|
+
"@types/react": "^19.3.0",
|
|
161
|
+
"@types/react-dom": "^19.3.0",
|
|
162
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
163
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
164
|
+
"eslint": "^10.11.0",
|
|
165
|
+
"eslint-config-prettier": "^10.1.8",
|
|
166
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
167
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
168
|
+
"globals": "^17.12.0",
|
|
169
|
+
"jsdom": "^30.1.1",
|
|
170
|
+
"prettier": "^3.9.9",
|
|
171
|
+
"prettier-plugin-tailwindcss": "^0.8.1",
|
|
172
|
+
"react-router": "^8.4.0",
|
|
173
|
+
"storybook": "^10.6.0",
|
|
174
|
+
"tailwindcss": "^4.3.3",
|
|
175
|
+
"tw-animate-css": "^1.4.0",
|
|
176
|
+
"typescript": "5.9",
|
|
177
|
+
"typescript-eslint": "^8.70.1",
|
|
178
|
+
"vite": "^8.3.1",
|
|
179
|
+
"vite-plugin-svgr": "^5.2.0",
|
|
180
|
+
"vitest": "^5.0.2"
|
|
181
|
+
}
|
|
182
|
+
}
|