@hemerson/design-system 0.0.0-stage → 0.2.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 +91 -2
- package/dist/components/Badge/Badge.d.ts +14 -0
- package/dist/components/Button/Button.d.ts +17 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Card/Card.d.ts +19 -0
- package/dist/components/Checkbox/Checkbox.d.ts +8 -0
- package/dist/components/Container/Container.d.ts +9 -0
- package/dist/components/Dialog/Dialog.d.ts +18 -0
- package/dist/components/FloatingButton/FloatingButton.d.ts +9 -0
- package/dist/components/GlassPanel/GlassPanel.d.ts +13 -0
- package/dist/components/GlowAvatar/GlowAvatar.d.ts +10 -0
- package/dist/components/Input/Input.d.ts +12 -0
- package/dist/components/Loading/Loading.d.ts +11 -0
- package/dist/components/Menu/Menu.d.ts +25 -0
- package/dist/components/NavBar/NavBar.d.ts +20 -0
- package/dist/components/ScrollArea/ScrollArea.d.ts +8 -0
- package/dist/components/SectionTitle/SectionTitle.d.ts +12 -0
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +21 -0
- package/dist/components/Select/Select.d.ts +16 -0
- package/dist/components/Separator/Separator.d.ts +8 -0
- package/dist/components/Sidebar/Sidebar.d.ts +21 -0
- package/dist/components/Table/Table.d.ts +26 -0
- package/dist/components/Toast/Toast.d.ts +16 -0
- package/dist/enableGlassRefraction-CgDeg-2E.js +88 -0
- package/dist/enableGlassRefraction-CgDeg-2E.js.map +1 -0
- package/dist/glass/enableGlassRefraction.d.ts +10 -0
- package/dist/glass/index.d.ts +1 -0
- package/dist/glass/lens.d.ts +15 -0
- package/dist/glass.js +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +680 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Icon.d.ts +17 -0
- package/dist/internal/cx.d.ts +2 -0
- package/dist/internal/field.d.ts +16 -0
- package/dist/styles.css +1297 -0
- package/dist/tokens.css +190 -0
- package/dist/tokens.json +538 -0
- package/package.json +95 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hemerson Oliveira
|
|
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
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Hemerson Design System
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@hemerson/design-system)
|
|
4
|
+
[](https://github.com/hemerson-git/hemerson-design-system/actions/workflows/ci.yml)
|
|
5
|
+
[](https://hemerson-git.github.io/hemerson-design-system/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
A dark-first design system with a sage-teal accent and liquid-glass navigation, built for React. It ships design tokens for a dark and a light theme, a stylesheet, and React components with a Storybook to browse them.
|
|
9
|
+
|
|
10
|
+
**[Browse the components in Storybook →](https://hemerson-git.github.io/hemerson-design-system/)**
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @hemerson/design-system
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
React 18.2 or later is required.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import "@hemerson/design-system/styles.css";
|
|
22
|
+
import { Button } from "@hemerson/design-system";
|
|
23
|
+
|
|
24
|
+
export function Toolbar() {
|
|
25
|
+
return <Button variant="primary">Novo boleto</Button>;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **Themes:** set `data-theme="dark"` (the default) or `data-theme="light"` on `<html>`.
|
|
30
|
+
- **Reduced transparency:** set `data-transparency="reduced"` on `<html>` to turn every glass surface solid. Offer it as a setting in your app.
|
|
31
|
+
- **Edge lens:** call `enableGlassRefraction()` from `@hemerson/design-system/glass` once at startup to bend the content at the glass edges in Chromium. Other browsers keep frosted glass.
|
|
32
|
+
- **Fonts:** `styles.css` loads Raleway and JetBrains Mono from Google Fonts. To self-host them, import `tokens.css` and your own font files instead.
|
|
33
|
+
|
|
34
|
+
## What's inside
|
|
35
|
+
|
|
36
|
+
| Export | Contents |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `@hemerson/design-system` | React components |
|
|
39
|
+
| `@hemerson/design-system/glass` | `enableGlassRefraction()`, the Chromium edge lens, without React |
|
|
40
|
+
| `@hemerson/design-system/styles.css` | Fonts, tokens and component styles |
|
|
41
|
+
| `@hemerson/design-system/tokens.css` | CSS custom properties for both themes |
|
|
42
|
+
| `@hemerson/design-system/tokens.json` | The tokens as data |
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install
|
|
48
|
+
npm run dev # Storybook on http://localhost:6006
|
|
49
|
+
npm test # unit tests
|
|
50
|
+
npm run typecheck
|
|
51
|
+
npm run lint
|
|
52
|
+
npm run build # the package, into dist/
|
|
53
|
+
npm run build-storybook
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Tokens live in [`tokens/tokens.json`](tokens/tokens.json); `npm run tokens` turns them into `src/styles/tokens.css`. Each component lives in `src/components/<Name>/` with its stories and tests.
|
|
57
|
+
|
|
58
|
+
## Releases
|
|
59
|
+
|
|
60
|
+
Versions follow [semver](https://semver.org) and are managed with [Changesets](https://github.com/changesets/changesets). Every change to the package comes with a changeset (`npx changeset`). Merging to `master` opens a "version packages" pull request that bumps the version and updates the [changelog](CHANGELOG.md); merging that pull request publishes to npm from GitHub Actions. Storybook redeploys on every push to `master`.
|
|
61
|
+
|
|
62
|
+
## Contributing
|
|
63
|
+
|
|
64
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to set up the project and how contributors are credited.
|
|
65
|
+
|
|
66
|
+
## Contributors
|
|
67
|
+
|
|
68
|
+
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
69
|
+
[](#contributors)
|
|
70
|
+
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
71
|
+
|
|
72
|
+
Thanks to everyone who has contributed ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
73
|
+
|
|
74
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
75
|
+
<!-- prettier-ignore-start -->
|
|
76
|
+
<!-- markdownlint-disable -->
|
|
77
|
+
<table>
|
|
78
|
+
<tbody>
|
|
79
|
+
<tr>
|
|
80
|
+
<td align="center" valign="top" width="14.28%"><a href="https://hemerson-dev.vercel.app/pt-BR"><img src="https://avatars.githubusercontent.com/u/48595787?v=4?s=100" width="100px;" alt="Hemerson Oliveira"/><br /><sub><b>Hemerson Oliveira</b></sub></a><br /><a href="https://github.com/hemerson-git/hemerson-design-system/commits?author=hemerson-git" title="Code">💻</a> <a href="#design-hemerson-git" title="Design">🎨</a> <a href="https://github.com/hemerson-git/hemerson-design-system/commits?author=hemerson-git" title="Documentation">📖</a> <a href="https://github.com/hemerson-git/hemerson-design-system/commits?author=hemerson-git" title="Tests">⚠️</a> <a href="#infra-hemerson-git" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-hemerson-git" title="Maintenance">🚧</a></td>
|
|
81
|
+
</tr>
|
|
82
|
+
</tbody>
|
|
83
|
+
</table>
|
|
84
|
+
|
|
85
|
+
<!-- markdownlint-restore -->
|
|
86
|
+
<!-- prettier-ignore-end -->
|
|
87
|
+
|
|
88
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type BadgeTone = "neutral" | "accent" | "success" | "warning" | "danger";
|
|
3
|
+
export interface BadgeProps {
|
|
4
|
+
tone?: BadgeTone;
|
|
5
|
+
/** A Lucide icon, or `false` for none. Status tones show their own icon by default. */
|
|
6
|
+
icon?: ReactNode | false;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A small pill that states a status or a category in one or two words.
|
|
12
|
+
* The word stays in ink; the tone shows in the icon and the tint, so color never carries the meaning alone.
|
|
13
|
+
*/
|
|
14
|
+
export declare function Badge({ tone, icon, children, className }: BadgeProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type ButtonVariant = "primary" | "secondary" | "glass" | "ghost" | "danger";
|
|
3
|
+
export type ButtonSize = "sm" | "md" | "lg";
|
|
4
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
/** primary (one per view) · secondary · glass (over content or in glass bars) · ghost · danger (destructive). */
|
|
6
|
+
variant?: ButtonVariant;
|
|
7
|
+
/** sm 32px · md 40px · lg 56px, as on the portfolio. */
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
/** A Lucide icon, shown before the label. */
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
/** Shows only the icon. Requires `aria-label`. */
|
|
12
|
+
iconOnly?: boolean;
|
|
13
|
+
/** Shows a spinner, disables the button and sets `aria-busy`. */
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Triggers an action. Labels are set uppercase and bold, verb first. */
|
|
17
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ElementType, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
export interface CardProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
|
|
3
|
+
/** The element to render. Default `article`; use `a` with `href` for a link card. */
|
|
4
|
+
as?: ElementType;
|
|
5
|
+
href?: string;
|
|
6
|
+
/** An uppercase label above the title. */
|
|
7
|
+
eyebrow?: ReactNode;
|
|
8
|
+
title?: ReactNode;
|
|
9
|
+
media?: {
|
|
10
|
+
src: string;
|
|
11
|
+
alt?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Actions or badges at the bottom. */
|
|
14
|
+
footer?: ReactNode;
|
|
15
|
+
/** The whole card is clickable; it scales up slightly on hover. */
|
|
16
|
+
interactive?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** A solid raised block that groups one item's content. */
|
|
19
|
+
export declare function Card({ as: Tag, eyebrow, title, media, footer, interactive, className, children, ...rest }: CardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
|
|
3
|
+
/** States the "yes" ("Pago"), never a negative. */
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
hint?: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/** A checkbox whose whole label is clickable. */
|
|
8
|
+
export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ElementType, HTMLAttributes } from "react";
|
|
2
|
+
export interface ContainerProps extends HTMLAttributes<HTMLElement> {
|
|
3
|
+
/** The element to render. Default `section`. */
|
|
4
|
+
as?: ElementType;
|
|
5
|
+
/** Adds the sidebar's width as left padding from 768px up. */
|
|
6
|
+
withSidebar?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Centres page content at content-max, with a section's vertical rhythm and the side gutter. */
|
|
9
|
+
export declare function Container({ as: Tag, withSidebar, className, ...rest }: ContainerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface DialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
/** Called on Escape, on the close button and on a click outside the panel. */
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/** The buttons, main action last. */
|
|
10
|
+
footer?: ReactNode;
|
|
11
|
+
/** Accessible name of the close button. Default "Fechar". */
|
|
12
|
+
closeLabel?: string;
|
|
13
|
+
/** `panel` focuses the panel itself instead of its first control. */
|
|
14
|
+
initialFocus?: "first" | "panel";
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/** A modal panel of thick glass for a focused task or a confirmation. */
|
|
18
|
+
export declare function Dialog({ open, onClose, title, description, children, footer, closeLabel, initialFocus, className, }: DialogProps): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes } from "react";
|
|
2
|
+
export type FloatingButtonPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
3
|
+
export interface FloatingButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
+
/** Accessible name and tooltip. */
|
|
5
|
+
label: string;
|
|
6
|
+
position?: FloatingButtonPosition;
|
|
7
|
+
}
|
|
8
|
+
/** A round glass button fixed to a corner of the viewport, for one persistent action. */
|
|
9
|
+
export declare const FloatingButton: import("react").ForwardRefExoticComponent<FloatingButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ElementType, type HTMLAttributes } from "react";
|
|
2
|
+
export type GlassTone = "regular" | "clear" | "thick" | "accent";
|
|
3
|
+
export interface GlassPanelProps extends HTMLAttributes<HTMLElement> {
|
|
4
|
+
/** The element to render. Default `div`. */
|
|
5
|
+
as?: ElementType;
|
|
6
|
+
/**
|
|
7
|
+
* regular (default, the only tone for text) · clear (icon-only controls over the app's own surfaces)
|
|
8
|
+
* · thick (dialogs and sheets) · accent (one prominent floating control per view).
|
|
9
|
+
*/
|
|
10
|
+
tone?: GlassTone;
|
|
11
|
+
}
|
|
12
|
+
/** Liquid glass: the material of every surface that floats above the content. */
|
|
13
|
+
export declare const GlassPanel: import("react").ForwardRefExoticComponent<GlassPanelProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface GlowAvatarProps {
|
|
2
|
+
src: string;
|
|
3
|
+
/** The person's name. */
|
|
4
|
+
alt: string;
|
|
5
|
+
/** Diameter in px. Default 96. */
|
|
6
|
+
size?: number;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
/** A round photo ringed in the accent with a soft glow behind it. One per view. */
|
|
10
|
+
export declare function GlowAvatar({ src, alt, size, className }: GlowAvatarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
3
|
+
/** Always visible, written as a noun ("Beneficiário"). */
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
hint?: ReactNode;
|
|
6
|
+
/** Says how to fix the value; also marks the input invalid. */
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
/** Mono, tabular figures and a decimal keyboard, for amounts and codes. */
|
|
9
|
+
numeric?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** A labelled text field on a solid, recessed well. */
|
|
12
|
+
export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LoadingProps {
|
|
2
|
+
/** sm 16px · md 24px · lg 32px. */
|
|
3
|
+
size?: "sm" | "md" | "lg";
|
|
4
|
+
/** Announced to screen readers. Default "Carregando…". */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Shows the label beside the spinner. */
|
|
7
|
+
showLabel?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
/** A spinner that tells assistive technology something is loading. */
|
|
11
|
+
export declare function Loading({ size, label, showLabel, className }: LoadingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type ButtonVariant } from "../Button/Button";
|
|
3
|
+
export type MenuItem = {
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
onSelect?: () => void;
|
|
7
|
+
danger?: boolean;
|
|
8
|
+
separator?: never;
|
|
9
|
+
} | {
|
|
10
|
+
separator: true;
|
|
11
|
+
};
|
|
12
|
+
export interface MenuProps {
|
|
13
|
+
/** Trigger text, or its accessible name with `iconOnly`. */
|
|
14
|
+
label: string;
|
|
15
|
+
icon?: ReactNode;
|
|
16
|
+
iconOnly?: boolean;
|
|
17
|
+
items: MenuItem[];
|
|
18
|
+
/** Which edge of the trigger the list lines up with. Default `end`. */
|
|
19
|
+
align?: "start" | "end";
|
|
20
|
+
/** Default `glass`. */
|
|
21
|
+
triggerVariant?: ButtonVariant;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
/** A button that opens a glass list of actions. */
|
|
25
|
+
export declare function Menu({ label, icon, iconOnly, items, align, triggerVariant, className }: MenuProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
export interface NavBarProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
|
|
3
|
+
/** Product or page name on the left. */
|
|
4
|
+
title?: ReactNode;
|
|
5
|
+
/** Buttons on the right. */
|
|
6
|
+
actions?: ReactNode;
|
|
7
|
+
/** Name of the navigation landmark. Default "Principal". */
|
|
8
|
+
navLabel?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NavBarLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
11
|
+
/** Marks the current page. */
|
|
12
|
+
active?: boolean;
|
|
13
|
+
}
|
|
14
|
+
declare function NavBarLink({ active, className, ...rest }: NavBarLinkProps): import("react").JSX.Element;
|
|
15
|
+
declare function NavBarRoot({ title, actions, navLabel, className, children, ...rest }: NavBarProps): import("react").JSX.Element;
|
|
16
|
+
/** The app's top bar: a floating glass capsule with the product name, the main sections and the global actions. */
|
|
17
|
+
export declare const NavBar: typeof NavBarRoot & {
|
|
18
|
+
Link: typeof NavBarLink;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
export interface ScrollAreaProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
/** Accessible name of the region. */
|
|
4
|
+
label: string;
|
|
5
|
+
maxHeight?: number | string;
|
|
6
|
+
}
|
|
7
|
+
/** A focusable scrollable region with the brand's slim scrollbars. */
|
|
8
|
+
export declare function ScrollArea({ label, maxHeight, className, style, ...rest }: ScrollAreaProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface SectionTitleProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
/** Shown zero-padded (3 → "03") before the heading, only when sections are a sequence. */
|
|
5
|
+
index?: number;
|
|
6
|
+
/** Heading level. Default `h2`. */
|
|
7
|
+
as?: "h1" | "h2" | "h3" | "h4";
|
|
8
|
+
id?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/** A page-section heading led by a glowing mono index, the portfolio's signature heading. */
|
|
12
|
+
export declare function SectionTitle({ children, index, as: Tag, id, className }: SectionTitleProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface SegmentedOption<T extends string | number = string> {
|
|
3
|
+
value: T;
|
|
4
|
+
label?: ReactNode;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
/** Accessible name when the label is only an icon or a flag. */
|
|
7
|
+
ariaLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SegmentedControlProps<T extends string | number = string> {
|
|
10
|
+
options: SegmentedOption<T>[];
|
|
11
|
+
/** The selected value, for a controlled control. */
|
|
12
|
+
value?: T;
|
|
13
|
+
/** The initially selected value, for an uncontrolled control. */
|
|
14
|
+
defaultValue?: T;
|
|
15
|
+
onChange?: (value: T) => void;
|
|
16
|
+
/** Name of the group. */
|
|
17
|
+
label: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Switches between two to four exclusive options with a lit thumb that slides between them. */
|
|
21
|
+
export declare function SegmentedControl<T extends string | number = string>({ options, value, defaultValue, onChange, label, className, }: SegmentedControlProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode, type SelectHTMLAttributes } from "react";
|
|
2
|
+
export interface SelectOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
options: SelectOption[];
|
|
10
|
+
/** Shown as a disabled first option. */
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
hint?: ReactNode;
|
|
13
|
+
error?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
/** A labelled native dropdown, styled like Input, for choosing one option from a short list. */
|
|
16
|
+
export declare const Select: import("react").ForwardRefExoticComponent<SelectProps & import("react").RefAttributes<HTMLSelectElement>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SeparatorProps {
|
|
2
|
+
orientation?: "horizontal" | "vertical";
|
|
3
|
+
/** Hides the line from screen readers when it only decorates. */
|
|
4
|
+
decorative?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
/** A 1px line that divides groups of content. */
|
|
8
|
+
export declare function Separator({ orientation, decorative, className }: SeparatorProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface SidebarItem {
|
|
3
|
+
/** Accessible name and tooltip. */
|
|
4
|
+
label: string;
|
|
5
|
+
icon: ReactNode;
|
|
6
|
+
href?: string;
|
|
7
|
+
onClick?: () => void;
|
|
8
|
+
active?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface SidebarProps {
|
|
11
|
+
items: SidebarItem[];
|
|
12
|
+
/** Items pinned to the bottom, such as settings. */
|
|
13
|
+
footer?: ReactNode;
|
|
14
|
+
/** A free-floating capsule instead of a rail attached to the edge. */
|
|
15
|
+
floating?: boolean;
|
|
16
|
+
/** Name of the navigation landmark. Default "Navegação". */
|
|
17
|
+
label?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/** A slim glass rail of icon buttons for the app's main sections. */
|
|
21
|
+
export declare function Sidebar({ items, footer, floating, label, className }: SidebarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface TableColumn<Row> {
|
|
3
|
+
key: string;
|
|
4
|
+
header: ReactNode;
|
|
5
|
+
/** Mono, tabular figures, right-aligned. */
|
|
6
|
+
numeric?: boolean;
|
|
7
|
+
align?: "start" | "center";
|
|
8
|
+
/** Renders the cell from the row; by default the cell shows `row[key]`. */
|
|
9
|
+
render?: (row: Row) => ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export interface TableProps<Row> {
|
|
12
|
+
columns: TableColumn<Row>[];
|
|
13
|
+
rows: Row[];
|
|
14
|
+
/** Key of each row's unique id. Default `id`. */
|
|
15
|
+
rowKey?: keyof Row & string;
|
|
16
|
+
/** Names the table and its scroll region. */
|
|
17
|
+
caption?: string;
|
|
18
|
+
/** Keeps the caption for screen readers only. */
|
|
19
|
+
hideCaption?: boolean;
|
|
20
|
+
/** Shown when there are no rows. Default "Nada para mostrar.". */
|
|
21
|
+
empty?: ReactNode;
|
|
22
|
+
maxHeight?: number | string;
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
/** A solid data table with a sticky header, roomy rows and aligned numbers. */
|
|
26
|
+
export declare function Table<Row extends object>({ columns, rows, rowKey, caption, hideCaption, empty, maxHeight, className, }: TableProps<Row>): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type ToastTone = "neutral" | "success" | "warning" | "danger";
|
|
3
|
+
export interface ToastProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
tone?: ToastTone;
|
|
7
|
+
title: ReactNode;
|
|
8
|
+
description?: ReactNode;
|
|
9
|
+
/** Milliseconds before it closes itself; 0 keeps it open. Default 5000. */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Accessible name of the close button. Default "Fechar aviso". */
|
|
12
|
+
closeLabel?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A short glass notice at the bottom of the screen that confirms an action or reports a problem. */
|
|
16
|
+
export declare function Toast({ open, onClose, tone, title, description, duration, closeLabel, className, }: ToastProps): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//#region src/glass/lens.ts
|
|
2
|
+
function e(e, t, n, r) {
|
|
3
|
+
let i = new Uint8ClampedArray(e * t * 4), a = Math.max(0, Math.min(n, e / 2, t / 2));
|
|
4
|
+
for (let n = 0; n < t; n++) for (let o = 0; o < e; o++) {
|
|
5
|
+
let s = o + .5 - e / 2, c = n + .5 - t / 2, l = Math.abs(s) - (e / 2 - a), u = Math.abs(c) - (t / 2 - a), d, f = 0, p = 0;
|
|
6
|
+
if (l > 0 && u > 0) {
|
|
7
|
+
let e = Math.hypot(l, u);
|
|
8
|
+
d = a - e, f = l / e * Math.sign(s), p = u / e * Math.sign(c);
|
|
9
|
+
} else l > u ? (d = e / 2 - Math.abs(s), f = Math.sign(s)) : (d = t / 2 - Math.abs(c), p = Math.sign(c));
|
|
10
|
+
let m = Math.max(0, 1 - d / r), h = m * m, g = (n * e + o) * 4;
|
|
11
|
+
i[g] = 128 - f * h * 127, i[g + 1] = 128 - p * h * 127, i[g + 2] = 128, i[g + 3] = 255;
|
|
12
|
+
}
|
|
13
|
+
return i;
|
|
14
|
+
}
|
|
15
|
+
function t(e) {
|
|
16
|
+
let t = Math.max(4, Math.min(16, e / 3)), n = t * .45;
|
|
17
|
+
return {
|
|
18
|
+
rim: t,
|
|
19
|
+
maxShift: n,
|
|
20
|
+
scale: n / (127 / 255)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/glass/enableGlassRefraction.ts
|
|
25
|
+
var n = "http://www.w3.org/2000/svg", r = ".hds-glass:not(.hds-glass--thick)", i = null, a = /* @__PURE__ */ new Map();
|
|
26
|
+
function o() {
|
|
27
|
+
return (navigator.userAgentData?.brands ?? []).some(({ brand: e }) => e.includes("Chromium"));
|
|
28
|
+
}
|
|
29
|
+
function s() {
|
|
30
|
+
return i?.isConnected || (i = document.createElementNS(n, "svg"), i.setAttribute("id", "hds-glass-defs"), i.setAttribute("width", "0"), i.setAttribute("height", "0"), i.setAttribute("aria-hidden", "true"), i.style.position = "absolute", document.body.appendChild(i)), i;
|
|
31
|
+
}
|
|
32
|
+
function c(r, i, o) {
|
|
33
|
+
let c = `${r}x${i}r${o}`, l = a.get(c);
|
|
34
|
+
if (l) return l;
|
|
35
|
+
let u = document.createElement("canvas");
|
|
36
|
+
u.width = r, u.height = i;
|
|
37
|
+
let d = u.getContext("2d");
|
|
38
|
+
if (!d) return null;
|
|
39
|
+
let { rim: f, scale: p } = t(i), m = d.createImageData(r, i);
|
|
40
|
+
m.data.set(e(r, i, o, f)), d.putImageData(m, 0, 0);
|
|
41
|
+
let h = `hds-glass-lens-${a.size}`, g = document.createElementNS(n, "filter");
|
|
42
|
+
Object.entries({
|
|
43
|
+
id: h,
|
|
44
|
+
x: "0",
|
|
45
|
+
y: "0",
|
|
46
|
+
width: String(r),
|
|
47
|
+
height: String(i),
|
|
48
|
+
filterUnits: "userSpaceOnUse",
|
|
49
|
+
"color-interpolation-filters": "sRGB"
|
|
50
|
+
}).forEach(([e, t]) => g.setAttribute(e, t));
|
|
51
|
+
let _ = document.createElementNS(n, "feImage");
|
|
52
|
+
_.setAttribute("href", u.toDataURL()), _.setAttribute("x", "0"), _.setAttribute("y", "0"), _.setAttribute("width", String(r)), _.setAttribute("height", String(i)), _.setAttribute("result", "map");
|
|
53
|
+
let v = document.createElementNS(n, "feDisplacementMap");
|
|
54
|
+
return v.setAttribute("in", "SourceGraphic"), v.setAttribute("in2", "map"), v.setAttribute("scale", p.toFixed(2)), v.setAttribute("xChannelSelector", "R"), v.setAttribute("yChannelSelector", "G"), g.append(_, v), s().appendChild(g), a.set(c, h), h;
|
|
55
|
+
}
|
|
56
|
+
function l(e) {
|
|
57
|
+
let t = Math.round(e.offsetWidth), n = Math.round(e.offsetHeight);
|
|
58
|
+
if (t < 8 || n < 8) return;
|
|
59
|
+
let r = parseFloat(getComputedStyle(e).borderTopLeftRadius) || 0, i = c(t, n, Math.min(r, n / 2));
|
|
60
|
+
i && e.style.setProperty("--hds-glass-lens", `url(#${i})`);
|
|
61
|
+
}
|
|
62
|
+
var u = !1;
|
|
63
|
+
function d() {
|
|
64
|
+
if (typeof document > "u") return !1;
|
|
65
|
+
let e = window.matchMedia?.("(prefers-reduced-transparency: reduce)").matches ?? !1;
|
|
66
|
+
if (!o() || e) return !1;
|
|
67
|
+
if (u) return !0;
|
|
68
|
+
u = !0;
|
|
69
|
+
let t = new ResizeObserver((e) => {
|
|
70
|
+
for (let t of e) l(t.target);
|
|
71
|
+
}), n = (e) => {
|
|
72
|
+
e.querySelectorAll(r).forEach((e) => {
|
|
73
|
+
l(e), t.observe(e);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
return n(document), new MutationObserver((e) => {
|
|
77
|
+
for (let i of e) i.addedNodes.forEach((e) => {
|
|
78
|
+
e instanceof HTMLElement && (e.matches(r) && (l(e), t.observe(e)), n(e));
|
|
79
|
+
});
|
|
80
|
+
}).observe(document.body, {
|
|
81
|
+
childList: !0,
|
|
82
|
+
subtree: !0
|
|
83
|
+
}), document.documentElement.setAttribute("data-glass-refraction", ""), !0;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { d as t };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=enableGlassRefraction-CgDeg-2E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableGlassRefraction-CgDeg-2E.js","names":["lensDisplacementMap","data","width","height","r","radius","y","x","px","py","qx","qy","distance","nx","ny","length","t","rim","strength","i","lensSettings","maxShift","SVG","GLASS","defs","filters","isChromium","brand","ensureDefs","filterFor","key","existing","canvas","context","image","id","filter","name","value","map","displace","scale","applyLens","element","active","enableGlassRefraction","reducesTransparency","resizeObserver","entry","entries","watch","mutation","mutations","node"],"sources":["../src/glass/lens.ts","../src/glass/enableGlassRefraction.ts"],"sourcesContent":["/**\n * A displacement map for the edge lens: near the rim the backdrop is sampled from further\n * inside the shape, so content appears to bend towards the edge; the middle is untouched.\n *\n * Red holds the horizontal shift and green the vertical one, with 128 meaning \"no shift\".\n * The shift never exceeds `maxShift`, and `maxShift` stays below the rim width, so content\n * is compressed towards the edge but never flipped.\n */\nexport function lensDisplacementMap(width: number, height: number, radius: number, rim: number): Uint8ClampedArray {\n const data = new Uint8ClampedArray(width * height * 4);\n const r = Math.max(0, Math.min(radius, width / 2, height / 2));\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n // Distance to the edge of the rounded rectangle and the outward normal there\n const px = x + 0.5 - width / 2;\n const py = y + 0.5 - height / 2;\n const qx = Math.abs(px) - (width / 2 - r);\n const qy = Math.abs(py) - (height / 2 - r);\n let distance: number;\n let nx = 0;\n let ny = 0;\n\n if (qx > 0 && qy > 0) {\n const length = Math.hypot(qx, qy);\n distance = r - length;\n nx = (qx / length) * Math.sign(px);\n ny = (qy / length) * Math.sign(py);\n } else if (qx > qy) {\n distance = width / 2 - Math.abs(px);\n nx = Math.sign(px);\n } else {\n distance = height / 2 - Math.abs(py);\n ny = Math.sign(py);\n }\n\n const t = Math.max(0, 1 - distance / rim);\n const strength = t * t;\n const i = (y * width + x) * 4;\n data[i] = 128 - nx * strength * 127;\n data[i + 1] = 128 - ny * strength * 127;\n data[i + 2] = 128;\n data[i + 3] = 255;\n }\n }\n\n return data;\n}\n\n/** Rim width and the filter scale that keep the largest shift at 45% of the rim. */\nexport function lensSettings(height: number) {\n const rim = Math.max(4, Math.min(16, height / 3));\n const maxShift = rim * 0.45;\n // feDisplacementMap moves by scale × (channel / 255 − 0.5); the map uses at most ±127\n const scale = maxShift / (127 / 255);\n return { rim, maxShift, scale };\n}\n","import { lensDisplacementMap, lensSettings } from \"./lens\";\n\nconst SVG = \"http://www.w3.org/2000/svg\";\nconst GLASS = \".hds-glass:not(.hds-glass--thick)\";\n\nlet defs: SVGSVGElement | null = null;\nconst filters = new Map<string, string>();\n\nfunction isChromium() {\n const brands = (navigator as Navigator & { userAgentData?: { brands: { brand: string }[] } }).userAgentData?.brands ?? [];\n return brands.some(({ brand }) => brand.includes(\"Chromium\"));\n}\n\nfunction ensureDefs() {\n if (defs?.isConnected) return defs;\n defs = document.createElementNS(SVG, \"svg\");\n defs.setAttribute(\"id\", \"hds-glass-defs\");\n defs.setAttribute(\"width\", \"0\");\n defs.setAttribute(\"height\", \"0\");\n defs.setAttribute(\"aria-hidden\", \"true\");\n defs.style.position = \"absolute\";\n document.body.appendChild(defs);\n return defs;\n}\n\n/** One SVG filter per element size and corner radius, shared by elements that match. */\nfunction filterFor(width: number, height: number, radius: number): string | null {\n const key = `${width}x${height}r${radius}`;\n const existing = filters.get(key);\n if (existing) return existing;\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const context = canvas.getContext(\"2d\");\n if (!context) return null;\n\n const { rim, scale } = lensSettings(height);\n const image = context.createImageData(width, height);\n image.data.set(lensDisplacementMap(width, height, radius, rim));\n context.putImageData(image, 0, 0);\n\n const id = `hds-glass-lens-${filters.size}`;\n const filter = document.createElementNS(SVG, \"filter\");\n const attributes: Record<string, string> = {\n id,\n x: \"0\",\n y: \"0\",\n width: String(width),\n height: String(height),\n filterUnits: \"userSpaceOnUse\",\n \"color-interpolation-filters\": \"sRGB\",\n };\n Object.entries(attributes).forEach(([name, value]) => filter.setAttribute(name, value));\n\n const map = document.createElementNS(SVG, \"feImage\");\n map.setAttribute(\"href\", canvas.toDataURL());\n map.setAttribute(\"x\", \"0\");\n map.setAttribute(\"y\", \"0\");\n map.setAttribute(\"width\", String(width));\n map.setAttribute(\"height\", String(height));\n map.setAttribute(\"result\", \"map\");\n\n const displace = document.createElementNS(SVG, \"feDisplacementMap\");\n displace.setAttribute(\"in\", \"SourceGraphic\");\n displace.setAttribute(\"in2\", \"map\");\n displace.setAttribute(\"scale\", scale.toFixed(2));\n displace.setAttribute(\"xChannelSelector\", \"R\");\n displace.setAttribute(\"yChannelSelector\", \"G\");\n\n filter.append(map, displace);\n ensureDefs().appendChild(filter);\n filters.set(key, id);\n return id;\n}\n\nfunction applyLens(element: HTMLElement) {\n const width = Math.round(element.offsetWidth);\n const height = Math.round(element.offsetHeight);\n if (width < 8 || height < 8) return;\n\n const radius = parseFloat(getComputedStyle(element).borderTopLeftRadius) || 0;\n const id = filterFor(width, height, Math.min(radius, height / 2));\n if (id) element.style.setProperty(\"--hds-glass-lens\", `url(#${id})`);\n}\n\nlet active = false;\n\n/**\n * Turns on the edge lens: glass bends the backdrop near its rim, like Apple's Liquid Glass.\n * Every glass element present now or added later gets a lens sized to it.\n *\n * Only Chromium renders SVG filters in backdrop-filter. Firefox accepts them and then\n * drops the whole backdrop-filter, blur included, so other browsers keep plain frosted\n * glass. Nothing happens when the user reduces transparency.\n * Returns whether the lens was turned on.\n */\nexport function enableGlassRefraction(): boolean {\n if (typeof document === \"undefined\") return false;\n\n const reducesTransparency = window.matchMedia?.(\"(prefers-reduced-transparency: reduce)\").matches ?? false;\n if (!isChromium() || reducesTransparency) return false;\n if (active) return true;\n active = true;\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) applyLens(entry.target as HTMLElement);\n });\n const watch = (root: ParentNode) => {\n root.querySelectorAll<HTMLElement>(GLASS).forEach((element) => {\n applyLens(element);\n resizeObserver.observe(element);\n });\n };\n\n watch(document);\n new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n mutation.addedNodes.forEach((node) => {\n if (!(node instanceof HTMLElement)) return;\n if (node.matches(GLASS)) {\n applyLens(node);\n resizeObserver.observe(node);\n }\n watch(node);\n });\n }\n }).observe(document.body, { childList: true, subtree: true });\n\n document.documentElement.setAttribute(\"data-glass-refraction\", \"\");\n return true;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns on the edge lens: glass bends the backdrop near its rim, like Apple's Liquid Glass.
|
|
3
|
+
* Every glass element present now or added later gets a lens sized to it.
|
|
4
|
+
*
|
|
5
|
+
* Only Chromium renders SVG filters in backdrop-filter. Firefox accepts them and then
|
|
6
|
+
* drops the whole backdrop-filter, blur included, so other browsers keep plain frosted
|
|
7
|
+
* glass. Nothing happens when the user reduces transparency.
|
|
8
|
+
* Returns whether the lens was turned on.
|
|
9
|
+
*/
|
|
10
|
+
export declare function enableGlassRefraction(): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { enableGlassRefraction } from "./enableGlassRefraction";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A displacement map for the edge lens: near the rim the backdrop is sampled from further
|
|
3
|
+
* inside the shape, so content appears to bend towards the edge; the middle is untouched.
|
|
4
|
+
*
|
|
5
|
+
* Red holds the horizontal shift and green the vertical one, with 128 meaning "no shift".
|
|
6
|
+
* The shift never exceeds `maxShift`, and `maxShift` stays below the rim width, so content
|
|
7
|
+
* is compressed towards the edge but never flipped.
|
|
8
|
+
*/
|
|
9
|
+
export declare function lensDisplacementMap(width: number, height: number, radius: number, rim: number): Uint8ClampedArray;
|
|
10
|
+
/** Rim width and the filter scale that keep the largest shift at 45% of the rim. */
|
|
11
|
+
export declare function lensSettings(height: number): {
|
|
12
|
+
rim: number;
|
|
13
|
+
maxShift: number;
|
|
14
|
+
scale: number;
|
|
15
|
+
};
|
package/dist/glass.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as enableGlassRefraction } from "./enableGlassRefraction-CgDeg-2E.js";
|