rootzz-layout 0.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 +287 -0
- package/dist/index.cjs +857 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +462 -0
- package/dist/index.d.ts +462 -0
- package/dist/index.mjs +836 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +1004 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eduzz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# rootzz-layout
|
|
2
|
+
|
|
3
|
+
Shell de aplicação da Eduzz para React: **Topbar + Sidebar + área de conteúdo**.
|
|
4
|
+
Leve (zero dependências de runtime), com tema claro/escuro, drawer mobile, e **sem conflitar com o Tailwind** (ou qualquer CSS) do projeto consumidor.
|
|
5
|
+
|
|
6
|
+
> **Para agentes de IA:** a API é **composta**. Use `<RootzzLayout>` como casca e
|
|
7
|
+
> `RootzzLayout.Topbar`, `RootzzLayout.Sidebar`, `RootzzLayout.Content` como filhos —
|
|
8
|
+
> cada um com suas próprias props. Copie o exemplo de [Uso rápido](#uso-rápido), troque os
|
|
9
|
+
> dados. Todas as props têm JSDoc — confie no autocompletar/tipos.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Por que não quebra o seu projeto
|
|
14
|
+
|
|
15
|
+
- **CSS já compilado e prefixado.** Você importa **um** arquivo (`rootzz-layout/styles.css`). Não precisa de Tailwind, PostCSS ou config no seu projeto.
|
|
16
|
+
- **Classes prefixadas com `rzl-`** → não colidem com `flex`, `bg-*`, etc. do seu app.
|
|
17
|
+
- **Tudo escopado em `.rootzz-layout`** (inclusive os defaults internos do Tailwind). Nada vaza para fora do shell. O reset do Tailwind (preflight) **não** é incluído.
|
|
18
|
+
- **Customização por CSS variables** (`--rzl-*`) — sem precisar tocar no seu build.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Instalação
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add rootzz-layout
|
|
26
|
+
# ou: npm i rootzz-layout / yarn add rootzz-layout
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`react` e `react-dom` (>= 17) são peer dependencies. Importe o CSS **uma vez** na raiz do app:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import 'rootzz-layout/styles.css';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Builds suportados: **ESM** (`.mjs`), **CJS** (`.cjs`) e **tipos** (`.d.ts`). Funciona em Vite, Next.js (App e Pages Router), CRA, etc. Os componentes já vêm com a diretiva `"use client"` (Next App Router).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Uso rápido
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { RootzzLayout, SearchBar, IconButton } from 'rootzz-layout';
|
|
43
|
+
import 'rootzz-layout/styles.css';
|
|
44
|
+
|
|
45
|
+
export default function App() {
|
|
46
|
+
return (
|
|
47
|
+
<RootzzLayout>
|
|
48
|
+
<RootzzLayout.Topbar
|
|
49
|
+
logo={{ src: 'https://cdn.eduzzcdn.com/topbar/orbita-new.svg', alt: 'MyEduzz', href: '/' }}
|
|
50
|
+
apps // botão de apps (busca a lista oficial da Eduzz no CDN)
|
|
51
|
+
showThemeToggle // botão de tema claro/escuro
|
|
52
|
+
search={<SearchBar placeholder="Buscar…" shortcut="⌘K" />}
|
|
53
|
+
actions={<IconButton aria-label="Notificações" badge={8}>{/* ícone */}</IconButton>}
|
|
54
|
+
user={{
|
|
55
|
+
name: 'Designers da Eduzz',
|
|
56
|
+
initials: 'Dd',
|
|
57
|
+
menu: [
|
|
58
|
+
{ label: 'Meu perfil', onClick: () => {} },
|
|
59
|
+
{ type: 'divider' },
|
|
60
|
+
{ label: 'Sair', danger: true, onClick: () => {} },
|
|
61
|
+
],
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
|
|
65
|
+
<RootzzLayout.Sidebar
|
|
66
|
+
activeKey="resumo"
|
|
67
|
+
menu={[
|
|
68
|
+
{ key: 'resumo', label: 'Resumo', href: '/' },
|
|
69
|
+
{ key: 'compras', label: 'Minhas Compras', href: '/compras' },
|
|
70
|
+
{
|
|
71
|
+
type: 'group',
|
|
72
|
+
key: 'vendas',
|
|
73
|
+
label: 'Vendas',
|
|
74
|
+
defaultCollapsed: true,
|
|
75
|
+
items: [{ key: 'pedidos', label: 'Pedidos', href: '/pedidos' }],
|
|
76
|
+
},
|
|
77
|
+
]}
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<RootzzLayout.Content>
|
|
81
|
+
<h1>Novo produto</h1>
|
|
82
|
+
</RootzzLayout.Content>
|
|
83
|
+
</RootzzLayout>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A ordem dos filhos não importa — a Topbar fica sempre no topo e a Sidebar à esquerda do conteúdo.
|
|
89
|
+
Cada subcomponente também é exportado individualmente (`Topbar`, `Sidebar`, `Content`).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## API
|
|
94
|
+
|
|
95
|
+
### `<RootzzLayout>` — casca (configs de nível de shell)
|
|
96
|
+
|
|
97
|
+
| Prop | Tipo | Padrão | Descrição |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| `children` | `ReactNode` | — | Os subcomponentes: `.Topbar`, `.Sidebar`, `.Content`. |
|
|
100
|
+
| `theme` | `'light' \| 'dark'` | — | Tema **controlado** (você gerencia via `onThemeChange`). |
|
|
101
|
+
| `defaultTheme` | `'light' \| 'dark'` | `'light'` | Tema inicial quando **não-controlado**. |
|
|
102
|
+
| `onThemeChange` | `(theme) => void` | — | Disparado quando o tema muda. |
|
|
103
|
+
| `linkComponent` | `ElementType` | `'a'` | Componente de link (topbar + sidebar). Passe o `Link` do seu router para SPA. |
|
|
104
|
+
| `className` / `style` / `id` | — | — | Aplicados ao elemento raiz `.rootzz-layout`. Use `style` para sobrescrever tokens. |
|
|
105
|
+
|
|
106
|
+
### `<RootzzLayout.Topbar>`
|
|
107
|
+
|
|
108
|
+
| Prop | Tipo | Padrão | Descrição |
|
|
109
|
+
| --- | --- | --- | --- |
|
|
110
|
+
| `logo` | `LogoConfig \| ReactNode` | — | `{src, alt?, href?, onClick?, height?}` ou um nó React. |
|
|
111
|
+
| `apps` | `boolean \| string \| AppItem[] \| {url?, items?}` | — | `true` busca a lista oficial no CDN; array = lista estática; string/`{url}` busca de uma URL; `false`/omitido esconde. |
|
|
112
|
+
| `search` | `ReactNode` | — | Barra de busca opcional (centro). Use [`<SearchBar>`](#searchbar) ou um nó próprio. |
|
|
113
|
+
| `actions` | `ReactNode` | — | Ícones/ações custom à direita. Use [`<IconButton>`](#iconbutton). |
|
|
114
|
+
| `user` | `UserConfig` | — | `{name, email?, avatarUrl?, initials?, menu?, onClick?}`. |
|
|
115
|
+
| `showThemeToggle` | `boolean` | `false` | Exibe o botão de tema claro/escuro. |
|
|
116
|
+
| `showMenuButton` | `boolean` | `true` | Exibe o hambúrguer no mobile (desligue se não houver Sidebar). |
|
|
117
|
+
| `appsLabel` / `menuButtonLabel` | `string` | — | Rótulos acessíveis. |
|
|
118
|
+
|
|
119
|
+
### `<RootzzLayout.Sidebar>`
|
|
120
|
+
|
|
121
|
+
| Prop | Tipo | Padrão | Descrição |
|
|
122
|
+
| --- | --- | --- | --- |
|
|
123
|
+
| `menu` | `MenuItem[]` | — | Itens (links, grupos colapsáveis, divisores). |
|
|
124
|
+
| `activeKey` | `string` | — | `key` do item ativo. |
|
|
125
|
+
| `header` / `footer` | `ReactNode` | — | Conteúdo fixo no topo / rodapé. |
|
|
126
|
+
| `mobileTitle` | `ReactNode` | — | Título exibido no topo do drawer mobile. |
|
|
127
|
+
| `children` | `ReactNode` | — | Conteúdo custom (substitui `menu`). |
|
|
128
|
+
| `ariaLabel` / `className` | — | — | Acessibilidade / classe extra. |
|
|
129
|
+
|
|
130
|
+
### `<RootzzLayout.Content>`
|
|
131
|
+
|
|
132
|
+
| Prop | Tipo | Padrão | Descrição |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| `children` | `ReactNode` | — | Conteúdo da página. |
|
|
135
|
+
| `maxWidth` | `number \| string \| false` | token | Largura máx. `false` = 100%. Omitido usa `--rzl-content-max-width`. |
|
|
136
|
+
| `noPadding` | `boolean` | `false` | Remove o padding padrão. |
|
|
137
|
+
| `className` | `string` | — | Classe extra no wrapper interno. |
|
|
138
|
+
|
|
139
|
+
### Tipos do menu
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// Item simples (link ou botão)
|
|
143
|
+
{ key: string; label: ReactNode; icon?: ReactNode; href?: string; target?: string;
|
|
144
|
+
onClick?: (e) => void; active?: boolean; badge?: ReactNode; disabled?: boolean }
|
|
145
|
+
|
|
146
|
+
// Grupo colapsável
|
|
147
|
+
{ type: 'group'; key: string; label: ReactNode; icon?: ReactNode; items: MenuLink[];
|
|
148
|
+
defaultCollapsed?: boolean; collapsible?: boolean }
|
|
149
|
+
|
|
150
|
+
// Divisor
|
|
151
|
+
{ type: 'divider'; key?: string }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
O item ativo é resolvido por `Sidebar.activeKey === item.key` **ou** `item.active === true`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Customização (tema)
|
|
159
|
+
|
|
160
|
+
Sobrescreva as **CSS variables** `--rzl-*` no seu CSS:
|
|
161
|
+
|
|
162
|
+
```css
|
|
163
|
+
.rootzz-layout {
|
|
164
|
+
--rzl-primary: #6d2bcf;
|
|
165
|
+
--rzl-sidebar-width: 280px;
|
|
166
|
+
--rzl-radius: 12px;
|
|
167
|
+
--rzl-duration: 160ms;
|
|
168
|
+
--rzl-font: 'Inter', sans-serif;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
…ou pontualmente via `style`:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<RootzzLayout style={{ ['--rzl-primary' as string]: '#10b981' }}>…</RootzzLayout>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Tokens disponíveis
|
|
179
|
+
|
|
180
|
+
| Token | Padrão (claro) | Uso |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| `--rzl-primary` / `--rzl-primary-hover` / `--rzl-primary-contrast` | `#2b4acf` / `#2540b5` / `#fff` | Cor de marca |
|
|
183
|
+
| `--rzl-active-bg` / `--rzl-active-fg` | `#eef1fc` / `#2b4acf` | Item ativo da sidebar |
|
|
184
|
+
| `--rzl-bg` / `--rzl-surface` | `#fff` / `#fff` | Conteúdo / topbar e sidebar |
|
|
185
|
+
| `--rzl-fg` / `--rzl-fg-muted` / `--rzl-fg-subtle` | `#0f1324` / 65% / 50% | Textos |
|
|
186
|
+
| `--rzl-border` / `--rzl-hover-bg` | `#e5e7eb` / 4.5% | Linhas / hover |
|
|
187
|
+
| `--rzl-danger` / `--rzl-danger-bg` | `#e11d48` / 8% | Ações destrutivas |
|
|
188
|
+
| `--rzl-badge-bg` / `--rzl-badge-fg` | `#e11d48` / `#fff` | Badge (ex.: sino) |
|
|
189
|
+
| `--rzl-overlay` | `rgba(15,19,36,.45)` | Overlay mobile |
|
|
190
|
+
| `--rzl-topbar-height` / `--rzl-sidebar-width` / `--rzl-content-max-width` | `60px` / `260px` / `1200px` | Dimensões |
|
|
191
|
+
| `--rzl-radius` / `--rzl-radius-lg` | `8px` / `12px` | Arredondamento |
|
|
192
|
+
| `--rzl-duration` / `--rzl-ease` | `200ms` / `cubic-bezier(.4,0,.2,1)` | Movimento |
|
|
193
|
+
| `--rzl-font` | Google Sans + fallback | Fonte |
|
|
194
|
+
|
|
195
|
+
O tema **escuro** redefine esses tokens em `.rootzz-layout[data-theme="dark"]`.
|
|
196
|
+
Animações respeitam `prefers-reduced-motion`.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Tema claro/escuro
|
|
201
|
+
|
|
202
|
+
O estado do tema fica no `<RootzzLayout>`; o botão de alternância (`showThemeToggle`) fica na Topbar.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
// Não-controlado (a lib gerencia)
|
|
206
|
+
<RootzzLayout defaultTheme="light">
|
|
207
|
+
<RootzzLayout.Topbar showThemeToggle … />
|
|
208
|
+
…
|
|
209
|
+
</RootzzLayout>
|
|
210
|
+
|
|
211
|
+
// Controlado (você gerencia o estado)
|
|
212
|
+
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
213
|
+
<RootzzLayout theme={theme} onThemeChange={setTheme}>
|
|
214
|
+
<RootzzLayout.Topbar showThemeToggle … />
|
|
215
|
+
…
|
|
216
|
+
</RootzzLayout>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Integração com router (SPA)
|
|
222
|
+
|
|
223
|
+
Por padrão os links são `<a>`. Para navegação SPA, passe o `Link` do seu router no `<RootzzLayout>`:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
// Next.js
|
|
227
|
+
import Link from 'next/link';
|
|
228
|
+
<RootzzLayout linkComponent={Link}>…</RootzzLayout>
|
|
229
|
+
|
|
230
|
+
// React Router v6
|
|
231
|
+
import { Link } from 'react-router-dom';
|
|
232
|
+
const RouterLink = ({ href, ...p }) => <Link to={href} {...p} />;
|
|
233
|
+
<RootzzLayout linkComponent={RouterLink}>…</RootzzLayout>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Menu de aplicativos
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
<RootzzLayout.Topbar apps /> // lista oficial da Eduzz (CDN)
|
|
242
|
+
<RootzzLayout.Topbar apps={[{ label: 'App', icon: '/i.svg', url: 'https://…' }]} /> // estática
|
|
243
|
+
<RootzzLayout.Topbar apps="https://meu-cdn/apps.json" /> // URL custom
|
|
244
|
+
<RootzzLayout.Topbar apps={false} /> // sem botão de apps
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Formato de `AppItem`: `{ application?, label, icon, description?, url }`.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## `<SearchBar>` e `<IconButton>`
|
|
252
|
+
|
|
253
|
+
Helpers para a Topbar (também exportados):
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
<RootzzLayout.Topbar
|
|
257
|
+
search={<SearchBar placeholder="Buscar…" shortcut="⌘K" value={q} onChange={e => setQ(e.target.value)} />}
|
|
258
|
+
actions={<IconButton aria-label="Notificações" badge={8}>{<MeuIconeSino />}</IconButton>}
|
|
259
|
+
/>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Outros exports: `LayoutProvider`, hooks `useLayout` e `useApps`, util `cx`, e todos os tipos.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Mobile
|
|
267
|
+
|
|
268
|
+
Abaixo de `768px`: a sidebar vira **drawer** com botão hambúrguer na topbar, **overlay** com fade, scroll travado e fechamento por clique no overlay, `Esc` ou ao navegar. Tudo automático.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Desenvolvimento
|
|
273
|
+
|
|
274
|
+
Requer **Node 18+** e **pnpm**.
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
pnpm install
|
|
278
|
+
pnpm run example # playground em http://localhost:5183 — watch: TS e CSS recarregam ao vivo
|
|
279
|
+
pnpm run build # typecheck + build (ESM/CJS/d.ts/css) + testes Playwright
|
|
280
|
+
pnpm run test # testes e2e (Playwright)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Os testes rodam automaticamente no `build` e no `prepublishOnly` — a publicação trava se algo quebrar.
|
|
284
|
+
|
|
285
|
+
## Licença
|
|
286
|
+
|
|
287
|
+
MIT © Eduzz
|