cherry-styled-components 0.2.0 → 0.2.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/LICENSE +21 -0
- package/README.md +51 -15
- package/dist/index.js +2 -2
- package/dist/toast.d.ts +22 -4
- package/dist/toast.js +94 -55
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luan Gjokaj
|
|
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,33 +1,43 @@
|
|
|
1
1
|
# Cherry React Library
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/cherry-styled-components)
|
|
4
|
-
|
|
5
|
-
## Introduction
|
|
4
|
+
[](https://github.com/cherry-design-system/styled-components/blob/main/LICENSE)
|
|
6
5
|
|
|
7
6
|
Cherry Design System is a versatile foundation for projects. It offers a white label base, ready-to-use Figma designs, open-source React components, built-in support for theming and dark mode. Explore the [docs](https://cherry.al/) to create delightful user interfaces.
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Installation
|
|
8
|
+
## Installation
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
Install the package along with its peer dependencies:
|
|
14
11
|
|
|
15
12
|
```bash
|
|
16
|
-
npm install
|
|
13
|
+
npm install cherry-styled-components react react-dom styled-components
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
## Quick Start
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
npm run dev
|
|
23
|
-
```
|
|
18
|
+
Wrap your app in `CherryThemeProvider` and start using components. The provider injects the global styles and, when you pass `themeDark`, handles dark mode automatically:
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
```tsx
|
|
21
|
+
import {
|
|
22
|
+
Button,
|
|
23
|
+
CherryThemeProvider,
|
|
24
|
+
theme,
|
|
25
|
+
themeDark,
|
|
26
|
+
} from "cherry-styled-components";
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
export default function App() {
|
|
29
|
+
return (
|
|
30
|
+
<CherryThemeProvider theme={theme} themeDark={themeDark}>
|
|
31
|
+
<Button $variant="primary">Hello Cherry</Button>
|
|
32
|
+
</CherryThemeProvider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
29
35
|
```
|
|
30
36
|
|
|
37
|
+
The built-in `theme` and `themeDark` objects are a starting point; both are plain objects you can extend or replace to white-label the system. Styled props use a `$` prefix (`$variant`, `$size`, `$fullWidth`) so they never leak into the DOM.
|
|
38
|
+
|
|
39
|
+
The library ships form components (Button, Input, Select, Textarea, Toggle, Range, Password, Checkbox, Radio, Dropzone, AvatarDropzone), layout primitives (Container, Grid, Col, Flex, Box, MaxWidth, Space), and interactive components (Accordion, Modal, Toast, ThemeToggle, Icon, IconButton). See the [component docs](https://cherry.al/) for the full API.
|
|
40
|
+
|
|
31
41
|
## Theming
|
|
32
42
|
|
|
33
43
|
Cherry ships two theme providers:
|
|
@@ -97,11 +107,22 @@ if (!req.cookies.get("theme")?.value && hint) {
|
|
|
97
107
|
|
|
98
108
|
In a client-only app (like this repo's demo, see `src/main.tsx`), resolve the initial theme synchronously from the cookie, `localStorage`, or `matchMedia` before rendering and pass it as `$initial`.
|
|
99
109
|
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
To work on the library itself, clone the repo and use pnpm. Node.js v20+ is required for the dev tooling.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pnpm install # Install dependencies
|
|
116
|
+
pnpm run dev # Start the Vite dev server (demo app + previews)
|
|
117
|
+
pnpm run build # Build the library to dist/
|
|
118
|
+
pnpm run format # Format with Prettier
|
|
119
|
+
```
|
|
120
|
+
|
|
100
121
|
## Component Previews
|
|
101
122
|
|
|
102
123
|
The dev server ships with a preview route that renders a single component in isolation, centered on the page. It is meant for visual inspection and for taking automated screenshots (e.g. with Playwright).
|
|
103
124
|
|
|
104
|
-
With `
|
|
125
|
+
With `pnpm run dev` running:
|
|
105
126
|
|
|
106
127
|
- `/preview` shows an index page linking to every available preview.
|
|
107
128
|
- `/preview/<name>` renders one component centered inside a wrapper with the stable selector `#preview-box`.
|
|
@@ -150,6 +171,10 @@ Append `?theme=dark` or `?theme=light` to any preview URL to force a theme:
|
|
|
150
171
|
|
|
151
172
|
The value is persisted to the `theme` cookie and `localStorage`, exactly like a `ThemeToggle` click, and the demo resolves it synchronously before the first render. It persists for subsequent navigations in the same browser context, so always pass the parameter explicitly when comparing themes to avoid leakage from a previous visit.
|
|
152
173
|
|
|
174
|
+
### Custom theme colors
|
|
175
|
+
|
|
176
|
+
Append `?colors=<URI-encoded JSON>` to any preview URL to override theme colors. The JSON has the shape `{ "default": { "primary": "#BE123C", ... }, "dark": { ... } }` where `default` patches the light theme's colors and `dark` patches the dark theme's. Unknown keys and values that are not valid CSS colors are ignored, and the parameter only applies to preview routes.
|
|
177
|
+
|
|
153
178
|
### Taking screenshots
|
|
154
179
|
|
|
155
180
|
`scripts/screenshot-previews.cjs` captures every preview in both themes and writes the framed images used by the documentation site. With the dev server running in another terminal:
|
|
@@ -178,6 +203,13 @@ The script drives your installed Chrome through `playwright-core` (no browser do
|
|
|
178
203
|
| `PREVIEW_URL` | `http://localhost:5173/preview` |
|
|
179
204
|
| `OUT_DIR` | `../cherry-documentation/public/components` |
|
|
180
205
|
| `CHROME_PATH` | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` |
|
|
206
|
+
| `THEME_JSON` | none (path to a JSON file with `default`/`dark` color maps) |
|
|
207
|
+
|
|
208
|
+
`THEME_JSON` points at a file like the documentation site's `theme.json`; its colors are passed to the preview via `?colors=` and also drive the baked-in border color (`grayLight`). Example:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
THEME_JSON=../cherry-documentation/theme.json pnpm run screenshots
|
|
212
|
+
```
|
|
181
213
|
|
|
182
214
|
For one-off manual screenshots, target the `#preview-box` selector:
|
|
183
215
|
|
|
@@ -195,3 +227,7 @@ For help, discussion about best practices, or any other conversation that would
|
|
|
195
227
|
For casual chit-chat with others using Cherry:
|
|
196
228
|
|
|
197
229
|
[Join the Discord Server](https://discord.gg/6JvcWU5bke)
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
[MIT](https://github.com/cherry-design-system/styled-components/blob/main/LICENSE)
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,6 @@ import { Select, StyledIconWrapper } from "./select.js";
|
|
|
28
28
|
import { Space } from "./space.js";
|
|
29
29
|
import { Textarea } from "./textarea.js";
|
|
30
30
|
import { ThemeToggle } from "./theme-toggle.js";
|
|
31
|
-
import { StyledNotifications, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, useToastNotifications } from "./toast.js";
|
|
31
|
+
import { StyledNotificationItem, StyledNotifications, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, useToastNotifications } from "./toast.js";
|
|
32
32
|
import { Toggle } from "./toggle.js";
|
|
33
|
-
export { Accordion, AvatarDropzone, Box, Button, CherryThemeProvider, ClientThemeProvider, Col, Container, Dropzone, Flex, GlobalStyles, Grid, Icon, IconArrow, IconButton, IconCalendar, IconCheck, Input, MaxWidth, Modal, Password, Range, Select, Space, StyledComponentsRegistry, StyledIconWrapper, StyledInputWrapper, StyledLabel, StyledNotifications, Textarea, ThemeContext, ThemeToggle, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, Toggle, breakpoints, buttonStyles, colors, colorsDark, createThemeInitScript, createTypographyStyle, errorInteractiveStyles, fontSizes, fonts, formElementHeightStyles, fullWidthStyles, generateAlignContentStyles, generateAlignItemsStyles, generateColSpanStyles, generateColsStyles, generateDirectionStyles, generateGapStyles, generateJustifyContentStyles, generatePaddingStyles, iconButtonStyles, interactiveStyles, lineHeights, matchesAccept, mq, resetButton, resetInput, resolveTheme, shadows, shadowsDark, spacing, statusBorderStyles, styledBlockquote, styledButton, styledButtonBig, styledCode, styledH1, styledH2, styledH3, styledH4, styledH5, styledH6, styledHero1, styledHero2, styledHero3, styledInput, styledInputBig, styledSmall, styledStrong, styledText, theme, themeDark, themeInitScript, useOnClickOutside, useToastNotifications };
|
|
33
|
+
export { Accordion, AvatarDropzone, Box, Button, CherryThemeProvider, ClientThemeProvider, Col, Container, Dropzone, Flex, GlobalStyles, Grid, Icon, IconArrow, IconButton, IconCalendar, IconCheck, Input, MaxWidth, Modal, Password, Range, Select, Space, StyledComponentsRegistry, StyledIconWrapper, StyledInputWrapper, StyledLabel, StyledNotificationItem, StyledNotifications, Textarea, ThemeContext, ThemeToggle, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, Toggle, breakpoints, buttonStyles, colors, colorsDark, createThemeInitScript, createTypographyStyle, errorInteractiveStyles, fontSizes, fonts, formElementHeightStyles, fullWidthStyles, generateAlignContentStyles, generateAlignItemsStyles, generateColSpanStyles, generateColsStyles, generateDirectionStyles, generateGapStyles, generateJustifyContentStyles, generatePaddingStyles, iconButtonStyles, interactiveStyles, lineHeights, matchesAccept, mq, resetButton, resetInput, resolveTheme, shadows, shadowsDark, spacing, statusBorderStyles, styledBlockquote, styledButton, styledButtonBig, styledCode, styledH1, styledH2, styledH3, styledH4, styledH5, styledH6, styledHero1, styledHero2, styledHero3, styledInput, styledInputBig, styledSmall, styledStrong, styledText, theme, themeDark, themeInitScript, useOnClickOutside, useToastNotifications };
|
package/dist/toast.d.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { Theme } from './utils';
|
|
3
3
|
export type ToastColor = "info" | "success" | "error" | "warning";
|
|
4
|
+
export type ToastAlign = "center" | "left" | "right";
|
|
4
5
|
export interface ToastConfig {
|
|
5
6
|
color?: ToastColor;
|
|
6
7
|
autoHide?: number;
|
|
7
8
|
}
|
|
8
9
|
type Toast = {
|
|
10
|
+
id: number;
|
|
9
11
|
text: string;
|
|
10
|
-
status: "hidden" | "visible";
|
|
11
12
|
color: ToastColor;
|
|
12
13
|
autoHide: number;
|
|
13
14
|
};
|
|
14
15
|
declare const ToastNotificationsContext: React.Context<{
|
|
15
16
|
notifications: Toast[];
|
|
16
17
|
addNotification: (text: string, config?: ToastConfig) => void;
|
|
18
|
+
removeNotification: (id: number) => void;
|
|
17
19
|
}>;
|
|
18
20
|
declare function useToastNotifications(): {
|
|
19
21
|
notifications: Toast[];
|
|
20
22
|
addNotification: (text: string, config?: ToastConfig) => void;
|
|
23
|
+
removeNotification: (id: number) => void;
|
|
21
24
|
};
|
|
22
25
|
interface ToastNotificationsProviderProps {
|
|
23
26
|
children: React.ReactNode;
|
|
@@ -25,15 +28,30 @@ interface ToastNotificationsProviderProps {
|
|
|
25
28
|
declare function ToastNotificationsProvider({ children, }: ToastNotificationsProviderProps): React.JSX.Element;
|
|
26
29
|
export declare const StyledNotifications: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "theme" | "$align" | "$bottom"> & {
|
|
27
30
|
theme: Theme;
|
|
28
|
-
$align?:
|
|
31
|
+
$align?: ToastAlign;
|
|
29
32
|
$bottom?: boolean;
|
|
30
33
|
}, never> & Partial<Pick<import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "theme" | "$align" | "$bottom"> & {
|
|
31
34
|
theme: Theme;
|
|
32
|
-
$align?:
|
|
35
|
+
$align?: ToastAlign;
|
|
36
|
+
$bottom?: boolean;
|
|
37
|
+
}, never>>> & string;
|
|
38
|
+
export declare const StyledNotificationItem: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('styled-components').FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "theme" | "$align" | "$bottom" | "$color" | "$visible" | "$static"> & {
|
|
39
|
+
theme: Theme;
|
|
40
|
+
$color: ToastColor;
|
|
41
|
+
$visible: boolean;
|
|
42
|
+
$static: boolean;
|
|
43
|
+
$align?: ToastAlign;
|
|
44
|
+
$bottom?: boolean;
|
|
45
|
+
}, never> & Partial<Pick<import('styled-components').FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "theme" | "$align" | "$bottom" | "$color" | "$visible" | "$static"> & {
|
|
46
|
+
theme: Theme;
|
|
47
|
+
$color: ToastColor;
|
|
48
|
+
$visible: boolean;
|
|
49
|
+
$static: boolean;
|
|
50
|
+
$align?: ToastAlign;
|
|
33
51
|
$bottom?: boolean;
|
|
34
52
|
}, never>>> & string;
|
|
35
53
|
export interface ToastNotificationsProps {
|
|
36
|
-
$align?:
|
|
54
|
+
$align?: ToastAlign;
|
|
37
55
|
$bottom?: boolean;
|
|
38
56
|
}
|
|
39
57
|
declare function ToastNotifications({ $align, $bottom, }: ToastNotificationsProps): React.JSX.Element;
|
package/dist/toast.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Icon } from "./icon.js";
|
|
4
4
|
import { IconButton } from "./icon-button.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
6
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import styled, { css } from "styled-components";
|
|
8
8
|
//#region src/lib/toast.tsx
|
|
9
9
|
var statusIcons = {
|
|
@@ -12,48 +12,62 @@ var statusIcons = {
|
|
|
12
12
|
warning: "TriangleAlert",
|
|
13
13
|
info: "Info"
|
|
14
14
|
};
|
|
15
|
+
var EXIT_DURATION_MS = 400;
|
|
15
16
|
var ToastNotificationsContext = /*#__PURE__*/ createContext({
|
|
16
17
|
notifications: [],
|
|
17
|
-
addNotification: () => null
|
|
18
|
+
addNotification: () => null,
|
|
19
|
+
removeNotification: () => null
|
|
18
20
|
});
|
|
19
21
|
function useToastNotifications() {
|
|
20
22
|
return useContext(ToastNotificationsContext);
|
|
21
23
|
}
|
|
22
24
|
function ToastNotificationsProvider({ children }) {
|
|
23
25
|
const [notifications, setNotifications] = useState([]);
|
|
24
|
-
const
|
|
26
|
+
const nextId = useRef(0);
|
|
27
|
+
const addNotification = useCallback((text, config) => {
|
|
28
|
+
const id = nextId.current++;
|
|
25
29
|
setNotifications((prev) => [...prev, {
|
|
30
|
+
id,
|
|
26
31
|
text,
|
|
27
|
-
status: "hidden",
|
|
28
32
|
color: config?.color || "info",
|
|
29
33
|
autoHide: config?.autoHide || 0
|
|
30
34
|
}]);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
}, []);
|
|
36
|
+
const removeNotification = useCallback((id) => {
|
|
37
|
+
setNotifications((prev) => prev.filter((toast) => toast.id !== id));
|
|
38
|
+
}, []);
|
|
39
|
+
const value = useMemo(() => ({
|
|
40
|
+
notifications,
|
|
41
|
+
addNotification,
|
|
42
|
+
removeNotification
|
|
43
|
+
}), [
|
|
44
|
+
notifications,
|
|
45
|
+
addNotification,
|
|
46
|
+
removeNotification
|
|
47
|
+
]);
|
|
38
48
|
return /*#__PURE__*/ jsx(ToastNotificationsContext.Provider, {
|
|
39
|
-
value
|
|
40
|
-
notifications,
|
|
41
|
-
addNotification
|
|
42
|
-
},
|
|
49
|
+
value,
|
|
43
50
|
children
|
|
44
51
|
});
|
|
45
52
|
}
|
|
46
53
|
var StyledNotifications = styled.ul.withConfig({
|
|
47
54
|
displayName: "toast__StyledNotifications",
|
|
48
|
-
componentId: "sc-
|
|
55
|
+
componentId: "sc-1f36b7f7-0"
|
|
49
56
|
})([
|
|
50
57
|
`position:fixed;z-index:99999;margin:0;padding:0;list-style:none;`,
|
|
51
58
|
` `,
|
|
52
59
|
` `,
|
|
53
60
|
` `,
|
|
54
|
-
|
|
61
|
+
``
|
|
62
|
+
], ({ $align }) => $align === "center" && css([`left:50%;transform:translateX(-50%);`]), ({ $align }) => $align === "right" && css([`right:20px;`]), ({ $align }) => $align === "left" && css([`left:20px;`]), ({ $bottom }) => $bottom ? css([`bottom:20px;`]) : css([`top:20px;`]));
|
|
63
|
+
var StyledNotificationItem = styled.li.withConfig({
|
|
64
|
+
displayName: "toast__StyledNotificationItem",
|
|
65
|
+
componentId: "sc-1f36b7f7-1"
|
|
66
|
+
})([
|
|
67
|
+
`display:grid;grid-template-rows:0fr;justify-items:center;margin:0;padding:0;opacity:0;pointer-events:none;transform:translateY(`,
|
|
68
|
+
`);transition:opacity 0.2s ease,transform 0.2s ease,grid-template-rows 0.25s ease 0.15s,margin 0.25s ease 0.15s;`,
|
|
55
69
|
` `,
|
|
56
|
-
` & .item{display:inline-flex;align-items:center;gap:10px;max-width:min(420px,calc(100vw - 40px));padding:10px 10px 10px 18px;margin:0;border-radius:`,
|
|
70
|
+
` & .item-wrapper{min-height:0;}& .item{display:inline-flex;align-items:center;gap:10px;max-width:min(420px,calc(100vw - 40px));padding:10px 10px 10px 18px;margin:0;border-radius:`,
|
|
57
71
|
`;background:`,
|
|
58
72
|
`;border:solid 1px `,
|
|
59
73
|
`;box-shadow:`,
|
|
@@ -61,55 +75,80 @@ var StyledNotifications = styled.ul.withConfig({
|
|
|
61
75
|
`;font-size:`,
|
|
62
76
|
`;line-height:`,
|
|
63
77
|
`;font-weight:500;& .status-icon{display:inline-flex;flex-shrink:0;& svg{width:20px;height:20px;color:`,
|
|
64
|
-
`;}}& .message{flex:1;min-width:0;line-height:1.5;overflow-wrap:anywhere;}& .close-button{flex-shrink:0;}}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`;}}&.visible{opacity:1;pointer-events:all;transform:translateY(0);max-height:320px;transition:max-height 0.25s ease,margin 0.25s ease,opacity 0.2s ease 0.15s,transform 0.2s ease 0.15s;`,
|
|
69
|
-
`}&.static{position:relative;z-index:10;}}`
|
|
70
|
-
], ({ $align }) => $align === "center" && css([`left:50%;transform:translateX(-50%);`]), ({ $align }) => $align === "right" && css([`right:20px;`]), ({ $align }) => $align === "left" && css([`left:20px;`]), ({ $bottom }) => $bottom ? css([`bottom:20px;`]) : css([`top:20px;`]), ({ $align }) => $align === "right" && css([`justify-content:flex-end;`]), ({ $align }) => $align === "left" && css([`justify-content:flex-start;`]), ({ theme }) => theme.spacing.radius.xl, ({ theme }) => theme.colors.light, ({ theme }) => theme.colors.grayLight, ({ theme }) => theme.isDark ? theme.shadows.sm : theme.shadows.xs, ({ theme }) => theme.colors.dark, ({ theme }) => theme.fontSizes.small.lg, ({ theme }) => theme.lineHeights.small.lg, ({ theme }) => theme.colors.info, ({ theme }) => theme.colors.error, ({ theme }) => theme.colors.success, ({ theme }) => theme.colors.warning, ({ theme }) => theme.colors.info, ({ $bottom }) => $bottom ? css([`margin-top:10px;`]) : css([`margin-bottom:10px;`]));
|
|
78
|
+
`;}}& .message{flex:1;min-width:0;line-height:1.5;overflow-wrap:anywhere;}& .close-button{flex-shrink:0;}}`,
|
|
79
|
+
` `,
|
|
80
|
+
``
|
|
81
|
+
], ({ $bottom }) => $bottom ? "20px" : "-20px", ({ $align }) => $align === "right" && css([`justify-items:end;`]), ({ $align }) => $align === "left" && css([`justify-items:start;`]), ({ theme }) => theme.spacing.radius.xl, ({ theme }) => theme.colors.light, ({ theme }) => theme.colors.grayLight, ({ theme }) => theme.isDark ? theme.shadows.sm : theme.shadows.xs, ({ theme }) => theme.colors.dark, ({ theme }) => theme.fontSizes.small.lg, ({ theme }) => theme.lineHeights.small.lg, ({ theme, $color }) => theme.colors[$color], ({ $visible, $bottom }) => $visible && css([`opacity:1;pointer-events:auto;transform:translateY(0);grid-template-rows:1fr;transition:grid-template-rows 0.25s ease,margin 0.25s ease,opacity 0.2s ease 0.15s,transform 0.2s ease 0.15s;`, ``], $bottom ? css([`margin-top:10px;`]) : css([`margin-bottom:10px;`])), ({ $static }) => $static && css([`position:relative;z-index:10;`]));
|
|
71
82
|
function ToastNotifications({ $align = "center", $bottom }) {
|
|
72
83
|
const { notifications } = useToastNotifications();
|
|
73
84
|
return /*#__PURE__*/ jsx(StyledNotifications, {
|
|
74
85
|
$align,
|
|
75
86
|
$bottom,
|
|
76
|
-
|
|
87
|
+
"aria-live": "polite",
|
|
88
|
+
children: notifications.map((notification) => /*#__PURE__*/ jsx(NotificationItem, {
|
|
89
|
+
notification,
|
|
90
|
+
$align,
|
|
91
|
+
$bottom
|
|
92
|
+
}, notification.id))
|
|
77
93
|
});
|
|
78
94
|
}
|
|
79
|
-
function NotificationItem(notification) {
|
|
80
|
-
const
|
|
95
|
+
function NotificationItem({ notification, $align, $bottom }) {
|
|
96
|
+
const { removeNotification } = useToastNotifications();
|
|
97
|
+
const [phase, setPhase] = useState("entering");
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
let inner;
|
|
100
|
+
const outer = requestAnimationFrame(() => {
|
|
101
|
+
inner = requestAnimationFrame(() => setPhase((prev) => prev === "entering" ? "visible" : prev));
|
|
102
|
+
});
|
|
103
|
+
return () => {
|
|
104
|
+
cancelAnimationFrame(outer);
|
|
105
|
+
cancelAnimationFrame(inner);
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
81
108
|
useEffect(() => {
|
|
82
109
|
if (!notification.autoHide) return;
|
|
83
|
-
const timeout = setTimeout(() =>
|
|
110
|
+
const timeout = setTimeout(() => setPhase("exiting"), notification.autoHide);
|
|
84
111
|
return () => clearTimeout(timeout);
|
|
85
112
|
}, [notification.autoHide]);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (phase !== "exiting") return;
|
|
115
|
+
const timeout = setTimeout(() => removeNotification(notification.id), EXIT_DURATION_MS);
|
|
116
|
+
return () => clearTimeout(timeout);
|
|
117
|
+
}, [
|
|
118
|
+
phase,
|
|
119
|
+
notification.id,
|
|
120
|
+
removeNotification
|
|
121
|
+
]);
|
|
122
|
+
return /*#__PURE__*/ jsx(StyledNotificationItem, {
|
|
123
|
+
$visible: phase === "visible",
|
|
124
|
+
$color: notification.color,
|
|
125
|
+
$static: !notification.autoHide,
|
|
126
|
+
$align,
|
|
127
|
+
$bottom,
|
|
128
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
129
|
+
className: "item-wrapper",
|
|
130
|
+
children: /*#__PURE__*/ jsxs("span", {
|
|
131
|
+
className: "item",
|
|
132
|
+
children: [
|
|
133
|
+
/*#__PURE__*/ jsx("span", {
|
|
134
|
+
className: "status-icon",
|
|
135
|
+
children: /*#__PURE__*/ jsx(Icon, { name: statusIcons[notification.color] || statusIcons.info })
|
|
136
|
+
}),
|
|
137
|
+
/*#__PURE__*/ jsx("span", {
|
|
138
|
+
className: "message",
|
|
139
|
+
children: notification.text
|
|
140
|
+
}),
|
|
141
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
142
|
+
$size: "small",
|
|
143
|
+
className: "close-button",
|
|
144
|
+
"aria-label": "Dismiss notification",
|
|
145
|
+
onClick: () => setPhase("exiting"),
|
|
146
|
+
children: /*#__PURE__*/ jsx(Icon, { name: "X" })
|
|
147
|
+
})
|
|
148
|
+
]
|
|
149
|
+
})
|
|
111
150
|
})
|
|
112
151
|
});
|
|
113
152
|
}
|
|
114
153
|
//#endregion
|
|
115
|
-
export { StyledNotifications, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, useToastNotifications };
|
|
154
|
+
export { StyledNotificationItem, StyledNotifications, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, useToastNotifications };
|
package/package.json
CHANGED