@1e99/components 0.0.0-stage → 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/README.md +260 -2
- package/dist/index.d.ts +71 -0
- package/dist/index.js +189 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -4
package/README.md
CHANGED
|
@@ -1,3 +1,261 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @1e99/components
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Componentes React compartilhados pelos produtos 1e99. `Navbar`,
|
|
4
|
+
`GoogleSignInButton` e `Selector` foram extraídos fielmente de
|
|
5
|
+
`domain-website` e `curriculo`, sem introduzir dependência de Next.js,
|
|
6
|
+
autenticação, i18n ou geração de PDF.
|
|
7
|
+
|
|
8
|
+
## Instalação futura
|
|
9
|
+
|
|
10
|
+
Após a primeira publicação pública, instale o pacote no app consumidor:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @1e99/components
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
O pacote declara `react`, `react-dom`, `react-icons` e `tailwindcss` como
|
|
17
|
+
peer dependencies. Os apps 1e99 já usam `react-icons` 5.7.0; ele fornece os
|
|
18
|
+
ícones Google e Lucide que o pacote renderiza, sem duplicar SVGs manualmente.
|
|
19
|
+
|
|
20
|
+
### Tailwind CSS 4
|
|
21
|
+
|
|
22
|
+
O Tailwind 4 não lê automaticamente classes presentes em `node_modules`.
|
|
23
|
+
Adicione esta diretiva à folha CSS que já contém `@import "tailwindcss"`, com
|
|
24
|
+
o caminho relativo àquela folha:
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
@source "../../node_modules/@1e99/components/dist";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Nos consumidores atuais, a alteração futura será em:
|
|
31
|
+
|
|
32
|
+
- `domain-website/src/app/globals.css`;
|
|
33
|
+
- `curriculo/src/app/brand.css`.
|
|
34
|
+
|
|
35
|
+
Os dois projetos já fornecem os tokens usados pela navbar, incluindo
|
|
36
|
+
`surface-page`, `content-primary`, `content-secondary`, `border-subtle`,
|
|
37
|
+
`action-primary`, `surface-raised`, `content-inverse`, `shadow-card`,
|
|
38
|
+
`shadow-float`, `shadow-focus`, `ease-brand`, `shell`, `control` e `card`.
|
|
39
|
+
O pacote não redefine esses tokens nem importa CSS global no aplicativo.
|
|
40
|
+
|
|
41
|
+
## Uso
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import {useState} from "react";
|
|
45
|
+
import {
|
|
46
|
+
GoogleSignInButton,
|
|
47
|
+
Navbar,
|
|
48
|
+
Selector,
|
|
49
|
+
} from "@1e99/components";
|
|
50
|
+
|
|
51
|
+
type LocaleOption = {
|
|
52
|
+
label: string;
|
|
53
|
+
languageTag: string;
|
|
54
|
+
value: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const localeOptions = [
|
|
58
|
+
{ value: "pt", label: "Português", languageTag: "pt-BR" },
|
|
59
|
+
{ value: "en", label: "English", languageTag: "en" },
|
|
60
|
+
{ value: "es", label: "Español", languageTag: "es" },
|
|
61
|
+
] satisfies readonly LocaleOption[];
|
|
62
|
+
|
|
63
|
+
function Header() {
|
|
64
|
+
const [locale, setLocale] = useState("pt");
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Navbar
|
|
68
|
+
brand={<a href="/"><BrandLogo /></a>}
|
|
69
|
+
items={[
|
|
70
|
+
{ href: "/apps", label: "Apps" },
|
|
71
|
+
{ href: "/about", label: "Sobre" },
|
|
72
|
+
]}
|
|
73
|
+
navigationLabel="Navegação principal"
|
|
74
|
+
renderLink={({ href, children, className }) => (
|
|
75
|
+
<Link className={className} href={href}>{children}</Link>
|
|
76
|
+
)}
|
|
77
|
+
actions={
|
|
78
|
+
<div className="hidden items-center gap-3 md:flex">
|
|
79
|
+
<Selector
|
|
80
|
+
label={t("language")}
|
|
81
|
+
onValueChange={setLocale}
|
|
82
|
+
options={localeOptions}
|
|
83
|
+
renderOption={({ option }) => (
|
|
84
|
+
<span lang={option.languageTag}>{option.label}</span>
|
|
85
|
+
)}
|
|
86
|
+
renderTrigger={({ selectedOption, value }) => (
|
|
87
|
+
<span lang={selectedOption?.languageTag}>
|
|
88
|
+
{selectedOption?.label ?? value}
|
|
89
|
+
</span>
|
|
90
|
+
)}
|
|
91
|
+
value={locale}
|
|
92
|
+
/>
|
|
93
|
+
<GoogleSignInButton
|
|
94
|
+
label={t("signInWithGoogle")}
|
|
95
|
+
onClick={startGoogleLogin}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
}
|
|
99
|
+
width="content"
|
|
100
|
+
layer="above"
|
|
101
|
+
linkTreatment="underlined"
|
|
102
|
+
linkWeight="semibold"
|
|
103
|
+
mobileNavigation={{
|
|
104
|
+
menuLabel: t("openNavigation"),
|
|
105
|
+
navigationLabel: t("mobileNavigation"),
|
|
106
|
+
actions: <MobileAccountActions />,
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Prop | Opções | Uso nos projetos existentes |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| `width` | `content` (padrão), `wide` | domain-website / curriculo |
|
|
116
|
+
| `layer` | `standard` (padrão), `above` | curriculo / domain-website |
|
|
117
|
+
| `linkTreatment` | `plain` (padrão), `underlined` | curriculo / domain-website |
|
|
118
|
+
| `linkWeight` | `semibold` (padrão), `bold` | domain-website / curriculo |
|
|
119
|
+
| `mobileNavigation` | ausente ou objeto de disclosure | curriculo / domain-website |
|
|
120
|
+
|
|
121
|
+
`brand` e `actions` são slots e, junto da navegação desktop, permanecem como
|
|
122
|
+
regiões irmãs no container — o mesmo layout dos dois headers fonte. O objeto
|
|
123
|
+
`mobileNavigation` reproduz o menu do domain (botão azul de 48px e painel
|
|
124
|
+
elevado); ele fica ausente no perfil do currículo. `renderLink` recebe `href`,
|
|
125
|
+
`children` e `className`, permitindo usar `next/link`, `next-intl/link` ou
|
|
126
|
+
outro roteador sem acoplar o pacote a ele.
|
|
127
|
+
|
|
128
|
+
### Controles de header
|
|
129
|
+
|
|
130
|
+
`GoogleSignInButton` é o único tratamento visual para entrar com Google e
|
|
131
|
+
segue o botão do `domain-website` em todos os usos. `label` é obrigatório e
|
|
132
|
+
deve receber a string já traduzida pelo consumidor; também aceita atributos
|
|
133
|
+
comportamentais nativos de botão (`onClick`, `disabled`, `aria-*`). Não aceita
|
|
134
|
+
variante nem `className`; autenticação e estado de carregamento são do
|
|
135
|
+
consumidor.
|
|
136
|
+
|
|
137
|
+
`Selector` é um disclosure genérico para uma coleção controlada. Cada opção
|
|
138
|
+
tem apenas `value` estável e `label` como contrato-base; o consumidor pode
|
|
139
|
+
acrescentar campos ao tipar sua própria coleção, como `languageTag`, descrição
|
|
140
|
+
ou ícone. `value` e `onValueChange` são controlados pelo consumidor: ao clicar
|
|
141
|
+
em uma opção, o componente informa o novo valor e fecha o disclosure, mas não
|
|
142
|
+
navega nem atualiza estado por conta própria.
|
|
143
|
+
|
|
144
|
+
`renderOption` recebe `option`, `isSelected` e `select`; ele renderiza somente
|
|
145
|
+
o conteúdo interno do botão que o pacote controla. `renderTrigger` recebe
|
|
146
|
+
`selectedOption` e `value` para personalizar o conteúdo do gatilho. Dessa
|
|
147
|
+
forma, atributos como `lang` ficam no conteúdo do aplicativo consumidor, não
|
|
148
|
+
no tipo genérico do pacote. O componente preserva botões com `aria-pressed`,
|
|
149
|
+
`select-none`, as densidades `standard` (padrão), `compact` e
|
|
150
|
+
`compact-mobile` e `whiteSurface`. Sem `triggerAriaLabel`, o nome acessível do
|
|
151
|
+
gatilho vem do conteúdo selecionado visível; use essa prop quando o consumidor
|
|
152
|
+
precisar de um nome mais descritivo e atualize-o junto do valor controlado.
|
|
153
|
+
|
|
154
|
+
`Navbar` não recebe props de idioma nem de autenticação. `items`, `actions` e
|
|
155
|
+
`mobileNavigation.actions` são os pontos de composição para qualquer controle
|
|
156
|
+
ou CTA do produto. No Storybook, `Navigation/Navbar` inclui o playground de
|
|
157
|
+
referência com o logo oficial fixo; seus args serializáveis controlam itens,
|
|
158
|
+
seletor, botão Google e menu móvel. O seletor e o botão também possuem stories
|
|
159
|
+
próprias em `Controls/Selector` e `Controls/GoogleSignInButton`.
|
|
160
|
+
|
|
161
|
+
## Desenvolvimento e testes
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
pnpm lint
|
|
165
|
+
pnpm lint:fix
|
|
166
|
+
pnpm format
|
|
167
|
+
pnpm storybook
|
|
168
|
+
pnpm test
|
|
169
|
+
pnpm test:stories
|
|
170
|
+
pnpm build
|
|
171
|
+
pnpm build-storybook
|
|
172
|
+
pnpm pack:check
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`pnpm lint` aplica o padrão Google para TypeScript e React; `pnpm lint:fix`
|
|
176
|
+
corrige automaticamente o que for seguro, e `pnpm format` é seu atalho. O
|
|
177
|
+
Husky executa `pnpm lint` e `pnpm test` antes de cada commit local, portanto
|
|
178
|
+
ambos devem passar antes de o histórico ser criado.
|
|
179
|
+
|
|
180
|
+
`pnpm test` roda testes de contrato em Vitest + Testing Library. As stories
|
|
181
|
+
exibem o playground configurável da navbar, o seletor genérico e o botão
|
|
182
|
+
Google, incluindo o SVG oficial somente como asset de demonstração do
|
|
183
|
+
Storybook. Elas são verificadas em Chromium por `pnpm test:stories`, com os
|
|
184
|
+
addons oficiais de acessibilidade e Vitest.
|
|
185
|
+
|
|
186
|
+
Em uma máquina Linux nova, instale as dependências do Chromium uma vez antes
|
|
187
|
+
do teste de stories:
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
pnpm exec playwright install chromium
|
|
191
|
+
sudo pnpm exec playwright install-deps chromium
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Organização do código
|
|
195
|
+
|
|
196
|
+
Cada componente é uma unidade em `src/components/<nome>/`: implementação,
|
|
197
|
+
testes e story (quando houver) ficam juntos. `src/index.ts` é o único ponto de
|
|
198
|
+
entrada público; mudar a organização interna não altera como os aplicativos
|
|
199
|
+
importam `@1e99/components`. Testes transversais ficam em `test/`.
|
|
200
|
+
|
|
201
|
+
## Publicação
|
|
202
|
+
|
|
203
|
+
O destino recomendado é o npm público, como `@1e99/components`: ele evita
|
|
204
|
+
configuração de registry e token em cada consumidor. GitHub Packages é uma
|
|
205
|
+
alternativa para distribuição privada, mas exige registry e token GitHub nos
|
|
206
|
+
aplicativos.
|
|
207
|
+
|
|
208
|
+
`.github/workflows/ci.yml` valida pull requests e pushes para `main` com lint,
|
|
209
|
+
tipos, testes, build, Storybook e prévia do tarball. `.github/workflows/publish.yml`
|
|
210
|
+
repete essas verificações e publica somente quando uma tag `vX.Y.Z` corresponde
|
|
211
|
+
exatamente à versão em `package.json` e aponta para um commit ancestral de
|
|
212
|
+
`main`. Os actions usados nos workflows estão fixados em commits revisáveis.
|
|
213
|
+
|
|
214
|
+
### Primeiro release pelo CI
|
|
215
|
+
|
|
216
|
+
O bootstrap usa temporariamente o secret de repositório `NPM_TOKEN`. Crie-o no
|
|
217
|
+
npm como token granular, com **Bypass two-factor authentication**, permissão
|
|
218
|
+
**Read and write (publish and stage)** limitada ao escopo `@1e99` e expiração
|
|
219
|
+
curta. Salve-o em GitHub → `1e99/components` → Settings → Secrets and variables
|
|
220
|
+
→ Actions como `NPM_TOKEN`. Nunca grave ou compartilhe esse valor por chat,
|
|
221
|
+
issues, commits ou logs.
|
|
222
|
+
|
|
223
|
+
Como `@1e99/components` ainda não existe antes do primeiro publish, o escopo
|
|
224
|
+
inteiro é uma exceção temporária de bootstrap: revogue o token assim que OIDC
|
|
225
|
+
for configurado. O npm anuncia a remoção do publish direto com tokens granular
|
|
226
|
+
de bypass de 2FA em janeiro de 2027; se o primeiro release for adiado além
|
|
227
|
+
disso, use o fluxo de staged publishing do npm em vez de manter um token.
|
|
228
|
+
|
|
229
|
+
Antes de criar a tag pública, confirme a política de licença: o pacote está
|
|
230
|
+
como `UNLICENSED` e essa decisão precisa de aprovação explícita para publicação
|
|
231
|
+
pública.
|
|
232
|
+
|
|
233
|
+
Envie os commits e a tag correspondente à versão:
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
git push origin main
|
|
237
|
+
git tag v0.1.0
|
|
238
|
+
git push origin v0.1.0
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
O workflow recusa a tag se `package.json` não estiver em `0.1.0` e publica com
|
|
242
|
+
`publishConfig.access: public` se todas as verificações passarem.
|
|
243
|
+
Configure também regras do GitHub para exigir o CI antes de merge em `main` e
|
|
244
|
+
restringir a criação e atualização de tags `v*` a mantenedores autorizados.
|
|
245
|
+
|
|
246
|
+
### Migrar para Trusted Publishing
|
|
247
|
+
|
|
248
|
+
Depois que a primeira release for publicada, habilite 2FA na conta npm e abra
|
|
249
|
+
as configurações de `@1e99/components` no npm. Em **Trusted Publisher**, escolha
|
|
250
|
+
GitHub Actions e informe organização `1e99`, repositório `components` e arquivo
|
|
251
|
+
de workflow `publish.yml`; permita publicação direta com `npm publish`.
|
|
252
|
+
|
|
253
|
+
Em seguida, apague o secret `NPM_TOKEN` do GitHub e revogue o token no npm. As
|
|
254
|
+
tags futuras usarão OIDC com credenciais efêmeras do GitHub Actions e gerarão
|
|
255
|
+
provenance automaticamente. Para uma próxima versão, atualize a versão e envie
|
|
256
|
+
a tag criada pelo gerenciador:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
pnpm version patch
|
|
260
|
+
git push origin main --follow-tags
|
|
261
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type GoogleSignInButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'className' | 'type'> & {
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
declare function GoogleSignInButton({ label, ...buttonProps }: GoogleSignInButtonProps): react.JSX.Element;
|
|
8
|
+
|
|
9
|
+
type SelectorOption = {
|
|
10
|
+
label: ReactNode;
|
|
11
|
+
value: string;
|
|
12
|
+
};
|
|
13
|
+
type SelectorDensity = 'compact' | 'compact-mobile' | 'standard';
|
|
14
|
+
type SelectorRenderOptionContext<TOption extends SelectorOption> = {
|
|
15
|
+
isSelected: boolean;
|
|
16
|
+
option: TOption;
|
|
17
|
+
select: () => void;
|
|
18
|
+
};
|
|
19
|
+
type SelectorRenderTriggerContext<TOption extends SelectorOption> = {
|
|
20
|
+
selectedOption: TOption | undefined;
|
|
21
|
+
value: string;
|
|
22
|
+
};
|
|
23
|
+
type SelectorBaseProps = {
|
|
24
|
+
label: string;
|
|
25
|
+
triggerAriaLabel?: string;
|
|
26
|
+
value: string;
|
|
27
|
+
};
|
|
28
|
+
type SelectorProps<TOption extends SelectorOption = SelectorOption> = SelectorBaseProps & {
|
|
29
|
+
density?: SelectorDensity;
|
|
30
|
+
onValueChange: (value: string) => void;
|
|
31
|
+
options: readonly TOption[];
|
|
32
|
+
renderOption?: (context: SelectorRenderOptionContext<TOption>) => ReactNode;
|
|
33
|
+
renderTrigger?: (context: SelectorRenderTriggerContext<TOption>) => ReactNode;
|
|
34
|
+
whiteSurface?: boolean;
|
|
35
|
+
};
|
|
36
|
+
declare function Selector<TOption extends SelectorOption = SelectorOption>({ density, label, onValueChange, options, renderOption, renderTrigger, triggerAriaLabel, value, whiteSurface, }: SelectorProps<TOption>): react.JSX.Element;
|
|
37
|
+
|
|
38
|
+
type NavbarItem = {
|
|
39
|
+
href: string;
|
|
40
|
+
label: ReactNode;
|
|
41
|
+
};
|
|
42
|
+
type NavbarLinkRenderer = (props: {
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
className: string;
|
|
45
|
+
href: string;
|
|
46
|
+
}) => ReactNode;
|
|
47
|
+
type NavbarWidth = 'content' | 'wide';
|
|
48
|
+
type NavbarLinkTreatment = 'plain' | 'underlined';
|
|
49
|
+
type NavbarLinkWeight = 'bold' | 'semibold';
|
|
50
|
+
type NavbarLayer = 'above' | 'standard';
|
|
51
|
+
type NavbarMobileNavigation = {
|
|
52
|
+
actions?: ReactNode;
|
|
53
|
+
menuLabel: string;
|
|
54
|
+
navigationLabel: string;
|
|
55
|
+
};
|
|
56
|
+
type NavbarProps = {
|
|
57
|
+
actions?: ReactNode;
|
|
58
|
+
brand: ReactNode;
|
|
59
|
+
items: readonly NavbarItem[];
|
|
60
|
+
layer?: NavbarLayer;
|
|
61
|
+
linkTreatment?: NavbarLinkTreatment;
|
|
62
|
+
linkWeight?: NavbarLinkWeight;
|
|
63
|
+
mobileNavigation?: NavbarMobileNavigation;
|
|
64
|
+
navigationLabel: string;
|
|
65
|
+
renderLink?: NavbarLinkRenderer;
|
|
66
|
+
width?: NavbarWidth;
|
|
67
|
+
};
|
|
68
|
+
declare function Navbar({ actions, brand, items, layer, linkTreatment, linkWeight, mobileNavigation, navigationLabel, renderLink, width, }: NavbarProps): react.JSX.Element;
|
|
69
|
+
|
|
70
|
+
export { GoogleSignInButton, Navbar, Selector };
|
|
71
|
+
export type { GoogleSignInButtonProps, NavbarItem, NavbarLayer, NavbarLinkRenderer, NavbarLinkTreatment, NavbarLinkWeight, NavbarMobileNavigation, NavbarProps, NavbarWidth, SelectorDensity, SelectorOption, SelectorProps, SelectorRenderOptionContext, SelectorRenderTriggerContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// src/components/google-sign-in-button/google-sign-in-button.tsx
|
|
2
|
+
import { FcGoogle } from "react-icons/fc";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var buttonClassName = "inline-flex min-h-12 w-full cursor-pointer items-center justify-center gap-3 rounded-control border border-border-subtle bg-surface-raised px-control-x py-control-y text-label font-semibold whitespace-nowrap text-content-primary shadow-card transition-[translate,scale,background-color] duration-200 ease-brand hover:-translate-y-0.5 hover:bg-brand-cloud active:scale-[0.97] focus-visible:outline-none focus-visible:shadow-focus disabled:cursor-wait disabled:opacity-70 motion-reduce:transform-none";
|
|
5
|
+
function GoogleSignInButton({ label, ...buttonProps }) {
|
|
6
|
+
return /* @__PURE__ */ jsxs("button", { ...buttonProps, className: buttonClassName, type: "button", children: [
|
|
7
|
+
/* @__PURE__ */ jsx(
|
|
8
|
+
FcGoogle,
|
|
9
|
+
{
|
|
10
|
+
"aria-hidden": "true",
|
|
11
|
+
className: "size-5 shrink-0",
|
|
12
|
+
"data-testid": "google-mark"
|
|
13
|
+
}
|
|
14
|
+
),
|
|
15
|
+
label
|
|
16
|
+
] });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/components/selector/selector.tsx
|
|
20
|
+
import { LuChevronDown } from "react-icons/lu";
|
|
21
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
22
|
+
var standardTriggerClassName = "flex w-full cursor-pointer list-none select-none items-center justify-between rounded-control border border-border-subtle py-control-y text-label font-semibold text-content-primary shadow-card transition-colors duration-200 ease-brand hover:bg-brand-cloud focus-visible:outline-none focus-visible:shadow-focus [&::-webkit-details-marker]:hidden";
|
|
23
|
+
var optionClassName = "flex min-h-12 w-full select-none items-center rounded-control px-3 text-label font-semibold text-content-secondary transition-colors duration-200 ease-brand hover:bg-brand-cloud hover:text-content-primary focus-visible:outline-none focus-visible:shadow-focus";
|
|
24
|
+
function closeSelectorDisclosure(element) {
|
|
25
|
+
element.closest("details")?.removeAttribute("open");
|
|
26
|
+
}
|
|
27
|
+
function getNavigationLayout(density) {
|
|
28
|
+
if (density === "compact") {
|
|
29
|
+
return { trigger: "h-12 gap-2", wrapper: "w-auto" };
|
|
30
|
+
}
|
|
31
|
+
if (density === "compact-mobile") {
|
|
32
|
+
return {
|
|
33
|
+
trigger: "h-12 gap-2 px-3 md:gap-4 md:px-4 md:min-w-32",
|
|
34
|
+
wrapper: "w-auto"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return { trigger: "min-h-12 gap-4 px-4 md:min-w-32", wrapper: "w-full md:w-auto" };
|
|
38
|
+
}
|
|
39
|
+
function SelectorTrigger({
|
|
40
|
+
densityClassName,
|
|
41
|
+
renderTrigger,
|
|
42
|
+
selectedOption,
|
|
43
|
+
triggerAriaLabel,
|
|
44
|
+
value
|
|
45
|
+
}) {
|
|
46
|
+
const triggerContent = renderTrigger?.({ selectedOption, value }) ?? selectedOption?.label ?? value;
|
|
47
|
+
return /* @__PURE__ */ jsxs2("summary", { "aria-label": triggerAriaLabel, className: `${standardTriggerClassName} ${densityClassName}`, children: [
|
|
48
|
+
/* @__PURE__ */ jsx2("span", { children: triggerContent }),
|
|
49
|
+
/* @__PURE__ */ jsx2(
|
|
50
|
+
LuChevronDown,
|
|
51
|
+
{
|
|
52
|
+
"aria-hidden": "true",
|
|
53
|
+
className: "selector-disclosure-arrow size-5 shrink-0 text-content-secondary transition-transform duration-200 ease-brand motion-reduce:transition-none group-open:rotate-180"
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
function Selector({
|
|
59
|
+
density = "standard",
|
|
60
|
+
label,
|
|
61
|
+
onValueChange,
|
|
62
|
+
options,
|
|
63
|
+
renderOption,
|
|
64
|
+
renderTrigger,
|
|
65
|
+
triggerAriaLabel,
|
|
66
|
+
value,
|
|
67
|
+
whiteSurface = false
|
|
68
|
+
}) {
|
|
69
|
+
const selectedOption = options.find((option) => option.value === value);
|
|
70
|
+
const layout = getNavigationLayout(density);
|
|
71
|
+
const surfaceClassName = whiteSurface ? "bg-white" : "bg-surface-raised";
|
|
72
|
+
return /* @__PURE__ */ jsxs2("details", { className: `selector-disclosure group relative ${layout.wrapper}`, children: [
|
|
73
|
+
/* @__PURE__ */ jsx2(
|
|
74
|
+
SelectorTrigger,
|
|
75
|
+
{
|
|
76
|
+
densityClassName: layout.trigger,
|
|
77
|
+
renderTrigger,
|
|
78
|
+
selectedOption,
|
|
79
|
+
triggerAriaLabel,
|
|
80
|
+
value
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
/* @__PURE__ */ jsx2(
|
|
84
|
+
"ul",
|
|
85
|
+
{
|
|
86
|
+
"aria-label": label,
|
|
87
|
+
className: `mt-2 w-full overflow-hidden rounded-control border border-border-subtle ${surfaceClassName} p-1 shadow-float md:absolute md:top-full md:right-0 md:z-60 md:min-w-44`,
|
|
88
|
+
children: options.map((option) => {
|
|
89
|
+
const isSelected = option.value === value;
|
|
90
|
+
const select = () => onValueChange(option.value);
|
|
91
|
+
const optionContent = renderOption?.({ isSelected, option, select }) ?? option.label;
|
|
92
|
+
return /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
|
|
93
|
+
"button",
|
|
94
|
+
{
|
|
95
|
+
"aria-pressed": isSelected,
|
|
96
|
+
className: `${optionClassName} text-left ${isSelected ? "bg-brand-cloud text-content-primary" : ""}`,
|
|
97
|
+
onClick: (event) => {
|
|
98
|
+
select();
|
|
99
|
+
closeSelectorDisclosure(event.currentTarget);
|
|
100
|
+
},
|
|
101
|
+
type: "button",
|
|
102
|
+
children: optionContent
|
|
103
|
+
}
|
|
104
|
+
) }, option.value);
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/components/navbar/navbar.tsx
|
|
112
|
+
import { Fragment } from "react";
|
|
113
|
+
import { LuMenu } from "react-icons/lu";
|
|
114
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
115
|
+
var baseLinkClassName = "text-label text-content-secondary transition-colors duration-200 ease-brand hover:text-content-primary focus-visible:rounded-control focus-visible:outline-none focus-visible:shadow-focus";
|
|
116
|
+
var underlinedLinkClassName = "relative after:absolute after:-bottom-1.5 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-action-primary after:transition-transform after:duration-200 after:ease-brand hover:after:scale-x-100 focus-visible:after:scale-x-100";
|
|
117
|
+
var mobileLinkClassName = "flex min-h-12 items-center rounded-control px-3 text-label font-semibold text-content-secondary transition-colors duration-200 ease-brand hover:text-content-primary focus-visible:outline-none focus-visible:shadow-focus";
|
|
118
|
+
function Navbar({
|
|
119
|
+
actions,
|
|
120
|
+
brand,
|
|
121
|
+
items,
|
|
122
|
+
layer = "standard",
|
|
123
|
+
linkTreatment = "plain",
|
|
124
|
+
linkWeight = "semibold",
|
|
125
|
+
mobileNavigation,
|
|
126
|
+
navigationLabel,
|
|
127
|
+
renderLink,
|
|
128
|
+
width = "content"
|
|
129
|
+
}) {
|
|
130
|
+
const linkClassName = [
|
|
131
|
+
baseLinkClassName,
|
|
132
|
+
linkWeight === "bold" ? "font-bold" : "font-semibold",
|
|
133
|
+
linkTreatment === "underlined" ? underlinedLinkClassName : ""
|
|
134
|
+
].filter(Boolean).join(" ");
|
|
135
|
+
const containerWidthClassName = width === "wide" ? "max-w-7xl" : "max-w-5xl";
|
|
136
|
+
const layerClassName = layer === "above" ? "z-50" : "z-40";
|
|
137
|
+
const renderLinks = (className) => items.map((item) => /* @__PURE__ */ jsx3(Fragment, { children: renderLink ? renderLink({
|
|
138
|
+
children: item.label,
|
|
139
|
+
className,
|
|
140
|
+
href: item.href
|
|
141
|
+
}) : /* @__PURE__ */ jsx3("a", { className, href: item.href, children: item.label }) }, item.href));
|
|
142
|
+
return /* @__PURE__ */ jsx3(
|
|
143
|
+
"header",
|
|
144
|
+
{
|
|
145
|
+
className: `sticky top-0 ${layerClassName} border-b border-border-subtle bg-surface-page/90 text-label font-semibold backdrop-blur-lg`,
|
|
146
|
+
children: /* @__PURE__ */ jsxs3(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
className: `mx-auto flex min-h-20 ${containerWidthClassName} items-center justify-between gap-4 px-shell`,
|
|
150
|
+
children: [
|
|
151
|
+
/* @__PURE__ */ jsx3("div", { className: "shrink-0", children: brand }),
|
|
152
|
+
/* @__PURE__ */ jsx3("nav", { "aria-label": navigationLabel, className: "hidden items-center gap-6 lg:flex", children: renderLinks(linkClassName) }),
|
|
153
|
+
actions,
|
|
154
|
+
mobileNavigation ? /* @__PURE__ */ jsxs3("details", { className: "relative md:hidden", children: [
|
|
155
|
+
/* @__PURE__ */ jsxs3(
|
|
156
|
+
"summary",
|
|
157
|
+
{
|
|
158
|
+
"aria-label": mobileNavigation.menuLabel,
|
|
159
|
+
className: "flex size-12 cursor-pointer list-none items-center justify-center rounded-control bg-action-primary text-content-inverse shadow-card transition-[translate,scale,background-color] duration-200 ease-brand active:scale-[0.97] focus-visible:outline-none focus-visible:shadow-focus [&::-webkit-details-marker]:hidden",
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsx3("span", { className: "sr-only", children: mobileNavigation.menuLabel }),
|
|
162
|
+
/* @__PURE__ */ jsx3(LuMenu, { "aria-hidden": "true", className: "size-6" })
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
/* @__PURE__ */ jsxs3(
|
|
167
|
+
"nav",
|
|
168
|
+
{
|
|
169
|
+
"aria-label": mobileNavigation.navigationLabel,
|
|
170
|
+
className: "absolute top-14 right-0 w-72 rounded-card border border-border-subtle bg-surface-raised p-3 shadow-float",
|
|
171
|
+
children: [
|
|
172
|
+
renderLinks(mobileLinkClassName),
|
|
173
|
+
mobileNavigation.actions ? /* @__PURE__ */ jsx3("div", { className: "mt-2 space-y-3 border-t border-border-subtle pt-3", children: mobileNavigation.actions }) : null
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] }) : null
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
export {
|
|
185
|
+
GoogleSignInButton,
|
|
186
|
+
Navbar,
|
|
187
|
+
Selector
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/google-sign-in-button/google-sign-in-button.tsx","../src/components/selector/selector.tsx","../src/components/navbar/navbar.tsx"],"sourcesContent":["import type {ButtonHTMLAttributes, ReactNode} from 'react';\nimport {FcGoogle} from 'react-icons/fc';\n\nexport type GoogleSignInButtonProps = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'children' | 'className' | 'type'\n> & {\n label: ReactNode;\n};\n\nconst buttonClassName =\n 'inline-flex min-h-12 w-full cursor-pointer items-center justify-center gap-3 rounded-control border border-border-subtle bg-surface-raised px-control-x py-control-y text-label font-semibold whitespace-nowrap text-content-primary shadow-card transition-[translate,scale,background-color] duration-200 ease-brand hover:-translate-y-0.5 hover:bg-brand-cloud active:scale-[0.97] focus-visible:outline-none focus-visible:shadow-focus disabled:cursor-wait disabled:opacity-70 motion-reduce:transform-none';\n\nexport function GoogleSignInButton(\n {label, ...buttonProps}: GoogleSignInButtonProps,\n) {\n return (\n <button {...buttonProps} className={buttonClassName} type=\"button\">\n <FcGoogle\n aria-hidden=\"true\"\n className=\"size-5 shrink-0\"\n data-testid=\"google-mark\"\n />\n {label}\n </button>\n );\n}\n","import {type ReactNode} from 'react';\nimport {LuChevronDown} from 'react-icons/lu';\n\nexport type SelectorOption = {\n label: ReactNode;\n value: string;\n};\n\nexport type SelectorDensity = 'compact' | 'compact-mobile' | 'standard';\n\nexport type SelectorRenderOptionContext<TOption extends SelectorOption> = {\n isSelected: boolean;\n option: TOption;\n select: () => void;\n};\n\nexport type SelectorRenderTriggerContext<TOption extends SelectorOption> = {\n selectedOption: TOption | undefined;\n value: string;\n};\n\ntype SelectorBaseProps = {\n label: string;\n triggerAriaLabel?: string;\n value: string;\n};\n\nexport type SelectorProps<TOption extends SelectorOption = SelectorOption> =\n SelectorBaseProps & {\n density?: SelectorDensity;\n onValueChange: (value: string) => void;\n options: readonly TOption[];\n renderOption?: (context: SelectorRenderOptionContext<TOption>) => ReactNode;\n renderTrigger?: (\n context: SelectorRenderTriggerContext<TOption>,\n ) => ReactNode;\n whiteSurface?: boolean;\n };\n\nconst standardTriggerClassName =\n 'flex w-full cursor-pointer list-none select-none items-center justify-between rounded-control border border-border-subtle py-control-y text-label font-semibold text-content-primary shadow-card transition-colors duration-200 ease-brand hover:bg-brand-cloud focus-visible:outline-none focus-visible:shadow-focus [&::-webkit-details-marker]:hidden';\n\nconst optionClassName =\n 'flex min-h-12 w-full select-none items-center rounded-control px-3 text-label font-semibold text-content-secondary transition-colors duration-200 ease-brand hover:bg-brand-cloud hover:text-content-primary focus-visible:outline-none focus-visible:shadow-focus';\n\nfunction closeSelectorDisclosure(element: HTMLElement) {\n element.closest('details')?.removeAttribute('open');\n}\n\nfunction getNavigationLayout(density: SelectorDensity) {\n if (density === 'compact') {\n return {trigger: 'h-12 gap-2', wrapper: 'w-auto'};\n }\n\n if (density === 'compact-mobile') {\n return {\n trigger: 'h-12 gap-2 px-3 md:gap-4 md:px-4 md:min-w-32',\n wrapper: 'w-auto',\n };\n }\n\n return {trigger: 'min-h-12 gap-4 px-4 md:min-w-32', wrapper: 'w-full md:w-auto'};\n}\n\nfunction SelectorTrigger<TOption extends SelectorOption>({\n densityClassName,\n renderTrigger,\n selectedOption,\n triggerAriaLabel,\n value,\n}: {\n densityClassName: string;\n renderTrigger?: (context: SelectorRenderTriggerContext<TOption>) => ReactNode;\n selectedOption: TOption | undefined;\n triggerAriaLabel?: string;\n value: string;\n}) {\n const triggerContent = renderTrigger?.({selectedOption, value}) ??\n selectedOption?.label ?? value;\n\n return (\n <summary aria-label={triggerAriaLabel} className={`${standardTriggerClassName} ${densityClassName}`}>\n <span>{triggerContent}</span>\n <LuChevronDown\n aria-hidden=\"true\"\n className=\"selector-disclosure-arrow size-5 shrink-0 text-content-secondary transition-transform duration-200 ease-brand motion-reduce:transition-none group-open:rotate-180\"\n />\n </summary>\n );\n}\n\nexport function Selector<TOption extends SelectorOption = SelectorOption>({\n density = 'standard',\n label,\n onValueChange,\n options,\n renderOption,\n renderTrigger,\n triggerAriaLabel,\n value,\n whiteSurface = false,\n}: SelectorProps<TOption>) {\n const selectedOption = options.find((option) => option.value === value);\n const layout = getNavigationLayout(density);\n const surfaceClassName = whiteSurface ? 'bg-white' : 'bg-surface-raised';\n\n return (\n <details className={`selector-disclosure group relative ${layout.wrapper}`}>\n <SelectorTrigger\n densityClassName={layout.trigger}\n renderTrigger={renderTrigger}\n selectedOption={selectedOption}\n triggerAriaLabel={triggerAriaLabel}\n value={value}\n />\n <ul\n aria-label={label}\n className={`mt-2 w-full overflow-hidden rounded-control border border-border-subtle ${surfaceClassName} p-1 shadow-float md:absolute md:top-full md:right-0 md:z-60 md:min-w-44`}\n >\n {options.map((option) => {\n const isSelected = option.value === value;\n const select = () => onValueChange(option.value);\n const optionContent = renderOption?.({isSelected, option, select}) ??\n option.label;\n\n return (\n <li key={option.value}>\n <button\n aria-pressed={isSelected}\n className={`${optionClassName} text-left ${isSelected ? 'bg-brand-cloud text-content-primary' : ''}`}\n onClick={(event) => {\n select();\n closeSelectorDisclosure(event.currentTarget);\n }}\n type=\"button\"\n >\n {optionContent}\n </button>\n </li>\n );\n })}\n </ul>\n </details>\n );\n}\n","import {Fragment, type ReactNode} from 'react';\nimport {LuMenu} from 'react-icons/lu';\n\nexport type NavbarItem = {\n href: string;\n label: ReactNode;\n};\n\nexport type NavbarLinkRenderer = (props: {\n children: ReactNode;\n className: string;\n href: string;\n}) => ReactNode;\n\nexport type NavbarWidth = 'content' | 'wide';\nexport type NavbarLinkTreatment = 'plain' | 'underlined';\nexport type NavbarLinkWeight = 'bold' | 'semibold';\nexport type NavbarLayer = 'above' | 'standard';\nexport type NavbarMobileNavigation = {\n actions?: ReactNode;\n menuLabel: string;\n navigationLabel: string;\n};\n\nexport type NavbarProps = {\n actions?: ReactNode;\n brand: ReactNode;\n items: readonly NavbarItem[];\n layer?: NavbarLayer;\n linkTreatment?: NavbarLinkTreatment;\n linkWeight?: NavbarLinkWeight;\n mobileNavigation?: NavbarMobileNavigation;\n navigationLabel: string;\n renderLink?: NavbarLinkRenderer;\n width?: NavbarWidth;\n};\n\nconst baseLinkClassName =\n 'text-label text-content-secondary transition-colors duration-200 ease-brand hover:text-content-primary focus-visible:rounded-control focus-visible:outline-none focus-visible:shadow-focus';\n\nconst underlinedLinkClassName =\n 'relative after:absolute after:-bottom-1.5 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-action-primary after:transition-transform after:duration-200 after:ease-brand hover:after:scale-x-100 focus-visible:after:scale-x-100';\n\nconst mobileLinkClassName =\n 'flex min-h-12 items-center rounded-control px-3 text-label font-semibold text-content-secondary transition-colors duration-200 ease-brand hover:text-content-primary focus-visible:outline-none focus-visible:shadow-focus';\n\nexport function Navbar({\n actions,\n brand,\n items,\n layer = 'standard',\n linkTreatment = 'plain',\n linkWeight = 'semibold',\n mobileNavigation,\n navigationLabel,\n renderLink,\n width = 'content',\n}: NavbarProps) {\n const linkClassName = [\n baseLinkClassName,\n linkWeight === 'bold' ? 'font-bold' : 'font-semibold',\n linkTreatment === 'underlined' ? underlinedLinkClassName : '',\n ]\n .filter(Boolean)\n .join(' ');\n const containerWidthClassName = width === 'wide' ? 'max-w-7xl' : 'max-w-5xl';\n const layerClassName = layer === 'above' ? 'z-50' : 'z-40';\n const renderLinks = (className: string) =>\n items.map((item) => (\n <Fragment key={item.href}>\n {renderLink ? (\n renderLink({\n children: item.label,\n className,\n href: item.href,\n })\n ) : (\n <a className={className} href={item.href}>\n {item.label}\n </a>\n )}\n </Fragment>\n ));\n\n return (\n <header\n className={`sticky top-0 ${layerClassName} border-b border-border-subtle bg-surface-page/90 text-label font-semibold backdrop-blur-lg`}\n >\n <div\n className={`mx-auto flex min-h-20 ${containerWidthClassName} items-center justify-between gap-4 px-shell`}\n >\n <div className=\"shrink-0\">{brand}</div>\n <nav aria-label={navigationLabel} className=\"hidden items-center gap-6 lg:flex\">\n {renderLinks(linkClassName)}\n </nav>\n {actions}\n {mobileNavigation ? (\n <details className=\"relative md:hidden\">\n <summary\n aria-label={mobileNavigation.menuLabel}\n className=\"flex size-12 cursor-pointer list-none items-center justify-center rounded-control bg-action-primary text-content-inverse shadow-card transition-[translate,scale,background-color] duration-200 ease-brand active:scale-[0.97] focus-visible:outline-none focus-visible:shadow-focus [&::-webkit-details-marker]:hidden\"\n >\n <span className=\"sr-only\">{mobileNavigation.menuLabel}</span>\n <LuMenu aria-hidden=\"true\" className=\"size-6\" />\n </summary>\n <nav\n aria-label={mobileNavigation.navigationLabel}\n className=\"absolute top-14 right-0 w-72 rounded-card border border-border-subtle bg-surface-raised p-3 shadow-float\"\n >\n {renderLinks(mobileLinkClassName)}\n {mobileNavigation.actions ? (\n <div className=\"mt-2 space-y-3 border-t border-border-subtle pt-3\">\n {mobileNavigation.actions}\n </div>\n ) : null}\n </nav>\n </details>\n ) : null}\n </div>\n </header>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1e99/components",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared React components for 1e99 applications.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/1e99/components.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "UNLICENSED",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"format": "pnpm lint:fix",
|
|
27
|
+
"lint": "eslint . --max-warnings 0",
|
|
28
|
+
"lint:fix": "eslint . --fix --max-warnings 0",
|
|
29
|
+
"prepare": "husky",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run --project unit",
|
|
32
|
+
"test:watch": "vitest --project unit",
|
|
33
|
+
"storybook": "storybook dev -p 6006",
|
|
34
|
+
"build-storybook": "storybook build",
|
|
35
|
+
"test:stories": "vitest run --project storybook",
|
|
36
|
+
"pack:check": "pnpm build && npm pack --dry-run"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": "^19.0.0",
|
|
40
|
+
"react-dom": "^19.0.0",
|
|
41
|
+
"react-icons": "^5.7.0",
|
|
42
|
+
"tailwindcss": "^4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.39.5",
|
|
46
|
+
"@storybook/addon-a11y": "^10.6.3",
|
|
47
|
+
"@storybook/addon-vitest": "^10.6.3",
|
|
48
|
+
"@storybook/react-vite": "^10.6.3",
|
|
49
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
50
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
51
|
+
"@testing-library/react": "^16.3.2",
|
|
52
|
+
"@testing-library/user-event": "^14.6.1",
|
|
53
|
+
"@types/node": "26.6.5",
|
|
54
|
+
"@types/react": "^19.2.14",
|
|
55
|
+
"@types/react-dom": "^19.2.3",
|
|
56
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
57
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
58
|
+
"eslint": "^9.39.5",
|
|
59
|
+
"eslint-config-google": "^0.14.0",
|
|
60
|
+
"eslint-plugin-react": "^7.37.5",
|
|
61
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
62
|
+
"globals": "^16.5.0",
|
|
63
|
+
"husky": "^9.1.7",
|
|
64
|
+
"jsdom": "^28.0.0",
|
|
65
|
+
"playwright": "1.64.1",
|
|
66
|
+
"react": "^19.2.4",
|
|
67
|
+
"react-dom": "^19.2.4",
|
|
68
|
+
"react-icons": "5.7.0",
|
|
69
|
+
"storybook": "^10.6.3",
|
|
70
|
+
"tailwindcss": "^4.3.3",
|
|
71
|
+
"tsup": "^9.0.0",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"typescript-eslint": "^8.72.0",
|
|
74
|
+
"vite": "^8.3.5",
|
|
75
|
+
"vitest": "^5.0.5"
|
|
76
|
+
},
|
|
77
|
+
"packageManager": "pnpm@12.9.1",
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"access": "public"
|
|
80
|
+
}
|
|
81
|
+
}
|