@hemerson/design-system 0.2.0 → 0.3.1
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 +31 -2
- package/dist/appearance/appearance.d.ts +23 -0
- package/dist/appearance/index.d.ts +2 -0
- package/dist/appearance/useAppearance.d.ts +13 -0
- package/dist/appearance-J15aUjQp.js +57 -0
- package/dist/appearance-J15aUjQp.js.map +1 -0
- package/dist/appearance.js +1 -0
- package/dist/components/AppearanceToggles/AppearanceToggles.d.ts +17 -0
- package/dist/components/Dialog/Dialog.d.ts +3 -1
- package/dist/components/Toast/Toast.d.ts +7 -3
- package/dist/components/Toast/ToastProvider.d.ts +29 -0
- package/dist/components/Toast/ToastView.d.ts +14 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +434 -272
- package/dist/index.js.map +1 -1
- package/dist/internal/Icon.d.ts +31 -8
- package/dist/styles.css +18 -6
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -26,9 +26,37 @@ export function Toolbar() {
|
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
- **Themes:**
|
|
30
|
-
- **
|
|
29
|
+
- **Themes and transparency:** apps open dark. Call `applyStoredAppearance()` before the first render to restore what the visitor chose last time, and put `ThemeToggle` and `TransparencyToggle` in your toolbar. Without React, `setTheme` and `setTransparency` do the same job. Under the hood these set `data-theme` and `data-transparency` on `<html>`.
|
|
30
|
+
- **Toasts:** wrap the app in `ToastProvider` and call `useToast()` to stack up to three at a time.
|
|
31
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
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// main.tsx, before the first render, so the page never flashes the wrong theme
|
|
35
|
+
import { applyStoredAppearance } from "@hemerson/design-system/appearance";
|
|
36
|
+
|
|
37
|
+
applyStoredAppearance();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { ThemeToggle, TransparencyToggle } from "@hemerson/design-system";
|
|
42
|
+
|
|
43
|
+
<ThemeToggle /> // dark ↔ light, remembered
|
|
44
|
+
<TransparencyToggle /> // solid glass, remembered
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { ToastProvider, useToast } from "@hemerson/design-system";
|
|
49
|
+
|
|
50
|
+
<ToastProvider>
|
|
51
|
+
<App />
|
|
52
|
+
</ToastProvider>;
|
|
53
|
+
|
|
54
|
+
// anywhere inside
|
|
55
|
+
const toast = useToast();
|
|
56
|
+
toast.success("Boleto salvo", "Fulano de Tal · R$ 1.234,56");
|
|
57
|
+
toast.error("Não foi possível salvar");
|
|
58
|
+
```
|
|
59
|
+
|
|
32
60
|
- **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
61
|
|
|
34
62
|
## What's inside
|
|
@@ -36,6 +64,7 @@ export function Toolbar() {
|
|
|
36
64
|
| Export | Contents |
|
|
37
65
|
| --- | --- |
|
|
38
66
|
| `@hemerson/design-system` | React components |
|
|
67
|
+
| `@hemerson/design-system/appearance` | `applyStoredAppearance()`, `setTheme()`, `setTransparency()`, without React |
|
|
39
68
|
| `@hemerson/design-system/glass` | `enableGlassRefraction()`, the Chromium edge lens, without React |
|
|
40
69
|
| `@hemerson/design-system/styles.css` | Fonts, tokens and component styles |
|
|
41
70
|
| `@hemerson/design-system/tokens.css` | CSS custom properties for both themes |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type Theme = "dark" | "light";
|
|
2
|
+
export type Transparency = "full" | "reduced";
|
|
3
|
+
export interface Appearance {
|
|
4
|
+
theme: Theme;
|
|
5
|
+
transparency: Transparency;
|
|
6
|
+
}
|
|
7
|
+
export declare const APPEARANCE_STORAGE_KEYS: {
|
|
8
|
+
readonly theme: "hds-theme";
|
|
9
|
+
readonly transparency: "hds-transparency";
|
|
10
|
+
};
|
|
11
|
+
/** The saved choices, or dark with full transparency when nothing is saved. */
|
|
12
|
+
export declare function readStoredAppearance(): Appearance;
|
|
13
|
+
/** What <html> currently shows. */
|
|
14
|
+
export declare function getAppearance(): Appearance;
|
|
15
|
+
/**
|
|
16
|
+
* Puts the saved theme and transparency on <html>, dark when nothing is saved.
|
|
17
|
+
* Call it as early as possible, before the first paint, so the page never flashes the other theme.
|
|
18
|
+
*/
|
|
19
|
+
export declare function applyStoredAppearance(): Appearance;
|
|
20
|
+
export declare function setTheme(theme: Theme): void;
|
|
21
|
+
export declare function setTransparency(transparency: Transparency): void;
|
|
22
|
+
/** Runs `listener` whenever the theme or transparency changes through these helpers. */
|
|
23
|
+
export declare function subscribeToAppearance(listener: () => void): () => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Appearance, type Theme, type Transparency } from "./appearance";
|
|
2
|
+
export interface UseAppearance extends Appearance {
|
|
3
|
+
setTheme: (theme: Theme) => void;
|
|
4
|
+
setTransparency: (transparency: Transparency) => void;
|
|
5
|
+
toggleTheme: () => void;
|
|
6
|
+
toggleTransparency: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The page's theme and transparency, kept in sync across every component that uses it.
|
|
10
|
+
* The first use applies the saved choice (dark when nothing is saved), unless the app already
|
|
11
|
+
* called applyStoredAppearance() at startup.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useAppearance(): UseAppearance;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region src/appearance/appearance.ts
|
|
2
|
+
var e = {
|
|
3
|
+
theme: "hds-theme",
|
|
4
|
+
transparency: "hds-transparency"
|
|
5
|
+
}, t = /* @__PURE__ */ new Set();
|
|
6
|
+
function n(e) {
|
|
7
|
+
try {
|
|
8
|
+
return localStorage.getItem(e);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function r(e, t) {
|
|
14
|
+
try {
|
|
15
|
+
localStorage.setItem(e, t);
|
|
16
|
+
} catch {}
|
|
17
|
+
}
|
|
18
|
+
function i() {
|
|
19
|
+
return {
|
|
20
|
+
theme: n(e.theme) === "light" ? "light" : "dark",
|
|
21
|
+
transparency: n(e.transparency) === "reduced" ? "reduced" : "full"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function a() {
|
|
25
|
+
let e = document.documentElement;
|
|
26
|
+
return {
|
|
27
|
+
theme: e.getAttribute("data-theme") === "light" ? "light" : "dark",
|
|
28
|
+
transparency: e.getAttribute("data-transparency") === "reduced" ? "reduced" : "full"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function o({ theme: e, transparency: n }) {
|
|
32
|
+
let r = document.documentElement;
|
|
33
|
+
r.setAttribute("data-theme", e), n === "reduced" ? r.setAttribute("data-transparency", "reduced") : r.removeAttribute("data-transparency"), t.forEach((e) => e());
|
|
34
|
+
}
|
|
35
|
+
function s() {
|
|
36
|
+
let e = i();
|
|
37
|
+
return o(e), e;
|
|
38
|
+
}
|
|
39
|
+
function c(t) {
|
|
40
|
+
r(e.theme, t), o({
|
|
41
|
+
...a(),
|
|
42
|
+
theme: t
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function l(t) {
|
|
46
|
+
r(e.transparency, t), o({
|
|
47
|
+
...a(),
|
|
48
|
+
transparency: t
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function u(e) {
|
|
52
|
+
return t.add(e), () => t.delete(e);
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { c as a, i, s as n, l as o, a as r, u as s, e as t };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=appearance-J15aUjQp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance-J15aUjQp.js","names":["APPEARANCE_STORAGE_KEYS","listeners","read","key","write","value","readStoredAppearance","getAppearance","root","apply","theme","transparency","listener","applyStoredAppearance","appearance","setTheme","setTransparency","subscribeToAppearance"],"sources":["../src/appearance/appearance.ts"],"sourcesContent":["/*\n * Theme and transparency for the whole page, kept on <html> and remembered in localStorage.\n * Projects open in the dark theme whatever the system setting: light, and reduced\n * transparency, are the visitor's own choices. No React here, so plain HTML pages can use it.\n */\n\nexport type Theme = \"dark\" | \"light\";\nexport type Transparency = \"full\" | \"reduced\";\n\nexport interface Appearance {\n theme: Theme;\n transparency: Transparency;\n}\n\nexport const APPEARANCE_STORAGE_KEYS = {\n theme: \"hds-theme\",\n transparency: \"hds-transparency\",\n} as const;\n\nconst listeners = new Set<() => void>();\n\nfunction read(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n // Private windows and blocked storage: fall back to the defaults\n return null;\n }\n}\n\nfunction write(key: string, value: string) {\n try {\n localStorage.setItem(key, value);\n } catch {\n // The choice still applies for this visit\n }\n}\n\n/** The saved choices, or dark with full transparency when nothing is saved. */\nexport function readStoredAppearance(): Appearance {\n return {\n theme: read(APPEARANCE_STORAGE_KEYS.theme) === \"light\" ? \"light\" : \"dark\",\n transparency: read(APPEARANCE_STORAGE_KEYS.transparency) === \"reduced\" ? \"reduced\" : \"full\",\n };\n}\n\n/** What <html> currently shows. */\nexport function getAppearance(): Appearance {\n const root = document.documentElement;\n return {\n theme: root.getAttribute(\"data-theme\") === \"light\" ? \"light\" : \"dark\",\n transparency: root.getAttribute(\"data-transparency\") === \"reduced\" ? \"reduced\" : \"full\",\n };\n}\n\nfunction apply({ theme, transparency }: Appearance) {\n const root = document.documentElement;\n root.setAttribute(\"data-theme\", theme);\n if (transparency === \"reduced\") {\n root.setAttribute(\"data-transparency\", \"reduced\");\n } else {\n root.removeAttribute(\"data-transparency\");\n }\n listeners.forEach((listener) => listener());\n}\n\n/**\n * Puts the saved theme and transparency on <html>, dark when nothing is saved.\n * Call it as early as possible, before the first paint, so the page never flashes the other theme.\n */\nexport function applyStoredAppearance(): Appearance {\n const appearance = readStoredAppearance();\n apply(appearance);\n return appearance;\n}\n\nexport function setTheme(theme: Theme) {\n write(APPEARANCE_STORAGE_KEYS.theme, theme);\n apply({ ...getAppearance(), theme });\n}\n\nexport function setTransparency(transparency: Transparency) {\n write(APPEARANCE_STORAGE_KEYS.transparency, transparency);\n apply({ ...getAppearance(), transparency });\n}\n\n/** Runs `listener` whenever the theme or transparency changes through these helpers. */\nexport function subscribeToAppearance(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n"],"mappings":";AAcA,IAAaA,IAA0B;CACrC,OAAO;CACP,cAAc;AAChB,GAEMC,oBAAY,IAAI,IAAgB;AAEtC,SAASC,EAAK,GAA4B;CACxC,IAAI;EACF,OAAO,aAAa,QAAQC,CAAG;CACjC,QAAQ;EAEN,OAAO;CACT;AACF;AAEA,SAASC,EAAM,GAAa,GAAe;CACzC,IAAI;EACF,aAAa,QAAQD,GAAKE,CAAK;CACjC,QAAQ,CAER;AACF;AAGA,SAAgBC,IAAmC;CACjD,OAAO;EACL,OAAOJ,EAAKF,EAAwB,KAAK,MAAM,UAAU,UAAU;EACnE,cAAcE,EAAKF,EAAwB,YAAY,MAAM,YAAY,YAAY;CACvF;AACF;AAGA,SAAgBO,IAA4B;CAC1C,IAAMC,IAAO,SAAS;CACtB,OAAO;EACL,OAAOA,EAAK,aAAa,YAAY,MAAM,UAAU,UAAU;EAC/D,cAAcA,EAAK,aAAa,mBAAmB,MAAM,YAAY,YAAY;CACnF;AACF;AAEA,SAASC,EAAM,EAAE,UAAO,mBAA4B;CAClD,IAAMD,IAAO,SAAS;CAOtB,AANAA,EAAK,aAAa,cAAcE,CAAK,GACjCC,MAAiB,YACnBH,EAAK,aAAa,qBAAqB,SAAS,IAEhDA,EAAK,gBAAgB,mBAAmB,GAE1CP,EAAU,SAAS,MAAaW,EAAS,CAAC;AAC5C;AAMA,SAAgBC,IAAoC;CAClD,IAAMC,IAAaR,EAAqB;CAExC,OADAG,EAAMK,CAAU,GACTA;AACT;AAEA,SAAgBC,EAAS,GAAc;CAErC,AADAX,EAAMJ,EAAwB,OAAOU,CAAK,GAC1CD,EAAM;EAAE,GAAGF,EAAc;EAAG;CAAM,CAAC;AACrC;AAEA,SAAgBS,EAAgB,GAA4B;CAE1D,AADAZ,EAAMJ,EAAwB,cAAcW,CAAY,GACxDF,EAAM;EAAE,GAAGF,EAAc;EAAG;CAAa,CAAC;AAC5C;AAGA,SAAgBU,EAAsB,GAAkC;CAEtE,OADAhB,EAAU,IAAIW,CAAQ,SACTX,EAAU,OAAOW,CAAQ;AACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as APPEARANCE_STORAGE_KEYS, n as applyStoredAppearance, r as getAppearance, i as readStoredAppearance, a as setTheme, o as setTransparency, s as subscribeToAppearance } from "./appearance-J15aUjQp.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ButtonProps } from "../Button/Button";
|
|
2
|
+
type ToggleProps = Omit<ButtonProps, "onClick" | "children" | "icon" | "iconOnly" | "aria-label" | "aria-pressed">;
|
|
3
|
+
export interface ThemeToggleProps extends ToggleProps {
|
|
4
|
+
/** Accessible name while dark. Default "Mudar para o tema claro". */
|
|
5
|
+
lightLabel?: string;
|
|
6
|
+
/** Accessible name while light. Default "Mudar para o tema escuro". */
|
|
7
|
+
darkLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Switches between the dark theme (the default) and the light one, and remembers the choice. */
|
|
10
|
+
export declare function ThemeToggle({ lightLabel, darkLabel, variant, ...rest }: ThemeToggleProps): import("react").JSX.Element;
|
|
11
|
+
export interface TransparencyToggleProps extends ToggleProps {
|
|
12
|
+
/** Accessible name. Default "Reduzir transparência"; the pressed state says whether it is on. */
|
|
13
|
+
label?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Turns every glass surface solid, or back to glass, and remembers the choice. */
|
|
16
|
+
export declare function TransparencyToggle({ label, variant, ...rest }: TransparencyToggleProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -12,7 +12,9 @@ export interface DialogProps {
|
|
|
12
12
|
closeLabel?: string;
|
|
13
13
|
/** `panel` focuses the panel itself instead of its first control. */
|
|
14
14
|
initialFocus?: "first" | "panel";
|
|
15
|
+
/** md (default, 560px) for forms and confirmations · lg (880px) for wide content such as a print preview. */
|
|
16
|
+
size?: "md" | "lg";
|
|
15
17
|
className?: string;
|
|
16
18
|
}
|
|
17
19
|
/** 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;
|
|
20
|
+
export declare function Dialog({ open, onClose, title, description, children, footer, closeLabel, initialFocus, size, className, }: DialogProps): import("react").ReactPortal | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type ToastTone } from "./ToastView";
|
|
3
|
+
export type { ToastTone } from "./ToastView";
|
|
3
4
|
export interface ToastProps {
|
|
4
5
|
open: boolean;
|
|
5
6
|
onClose?: () => void;
|
|
@@ -12,5 +13,8 @@ export interface ToastProps {
|
|
|
12
13
|
closeLabel?: string;
|
|
13
14
|
className?: string;
|
|
14
15
|
}
|
|
15
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* A short glass notice at the bottom of the screen that confirms an action or reports a problem.
|
|
18
|
+
* To raise toasts from anywhere in an app, wrap it in ToastProvider and use `useToast()` instead.
|
|
19
|
+
*/
|
|
16
20
|
export declare function Toast({ open, onClose, tone, title, description, duration, closeLabel, className, }: ToastProps): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type ToastTone } from "./ToastView";
|
|
3
|
+
export interface ToastOptions {
|
|
4
|
+
title: ReactNode;
|
|
5
|
+
description?: ReactNode;
|
|
6
|
+
tone?: ToastTone;
|
|
7
|
+
/** Milliseconds before it closes itself; 0 keeps it open until dismissed. Default 5000. */
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ToastApi {
|
|
11
|
+
/** Shows a toast and returns its id, for `dismiss`. */
|
|
12
|
+
show: (options: ToastOptions) => string;
|
|
13
|
+
success: (title: ReactNode, description?: ReactNode) => string;
|
|
14
|
+
error: (title: ReactNode, description?: ReactNode) => string;
|
|
15
|
+
warning: (title: ReactNode, description?: ReactNode) => string;
|
|
16
|
+
info: (title: ReactNode, description?: ReactNode) => string;
|
|
17
|
+
dismiss: (id: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface ToastProviderProps {
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
/** Most toasts shown at once; the oldest goes first. Default 3. */
|
|
22
|
+
max?: number;
|
|
23
|
+
/** Accessible name of each close button. Default "Fechar aviso". */
|
|
24
|
+
closeLabel?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Shows the toasts raised with `useToast()` anywhere inside it, stacked at the bottom of the screen. */
|
|
27
|
+
export declare function ToastProvider({ children, max, closeLabel }: ToastProviderProps): import("react").JSX.Element;
|
|
28
|
+
/** Raises toasts from anywhere inside a ToastProvider: `toast.success("Boleto salvo")`. */
|
|
29
|
+
export declare function useToast(): ToastApi;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type ToastTone = "neutral" | "success" | "warning" | "danger";
|
|
3
|
+
export interface ToastViewProps {
|
|
4
|
+
tone: ToastTone;
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
/** Milliseconds before it closes itself; 0 keeps it open. */
|
|
8
|
+
duration: number;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
closeLabel: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
/** One toast: the glass card, its icon and close button, and a timer that pauses on hover or focus. */
|
|
14
|
+
export declare function ToastView({ tone, title, description, duration, onClose, closeLabel, className }: ToastViewProps): import("react").JSX.Element;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { Dialog } from "./components/Dialog/Dialog";
|
|
|
16
16
|
export type { DialogProps } from "./components/Dialog/Dialog";
|
|
17
17
|
export { Toast } from "./components/Toast/Toast";
|
|
18
18
|
export type { ToastProps, ToastTone } from "./components/Toast/Toast";
|
|
19
|
+
export { ToastProvider, useToast } from "./components/Toast/ToastProvider";
|
|
20
|
+
export type { ToastApi, ToastOptions, ToastProviderProps } from "./components/Toast/ToastProvider";
|
|
19
21
|
export { Input } from "./components/Input/Input";
|
|
20
22
|
export type { InputProps } from "./components/Input/Input";
|
|
21
23
|
export { Select } from "./components/Select/Select";
|
|
@@ -28,6 +30,11 @@ export { Badge } from "./components/Badge/Badge";
|
|
|
28
30
|
export type { BadgeProps, BadgeTone } from "./components/Badge/Badge";
|
|
29
31
|
export { Table } from "./components/Table/Table";
|
|
30
32
|
export type { TableColumn, TableProps } from "./components/Table/Table";
|
|
33
|
+
export { ThemeToggle, TransparencyToggle } from "./components/AppearanceToggles/AppearanceToggles";
|
|
34
|
+
export type { ThemeToggleProps, TransparencyToggleProps } from "./components/AppearanceToggles/AppearanceToggles";
|
|
35
|
+
export { useAppearance } from "./appearance/useAppearance";
|
|
36
|
+
export type { UseAppearance } from "./appearance/useAppearance";
|
|
37
|
+
export * from "./appearance/index";
|
|
31
38
|
export { Container } from "./components/Container/Container";
|
|
32
39
|
export type { ContainerProps } from "./components/Container/Container";
|
|
33
40
|
export { SectionTitle } from "./components/SectionTitle/SectionTitle";
|