@varialkit/banner 0.1.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/docs.md +53 -0
- package/examples.tsx +195 -0
- package/package.json +27 -0
- package/src/Banner.scss +171 -0
- package/src/Banner.tsx +111 -0
- package/src/Banner.types.ts +38 -0
- package/src/index.ts +2 -0
package/docs.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Banner
|
|
2
|
+
|
|
3
|
+
Banners highlight important information inline within a page. Use them for status messages, policy updates, or
|
|
4
|
+
contextual guidance that should remain visible.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Banner } from "@solara/banner";
|
|
10
|
+
|
|
11
|
+
export function Example() {
|
|
12
|
+
return (
|
|
13
|
+
<Banner
|
|
14
|
+
variant="info"
|
|
15
|
+
title="Heads up"
|
|
16
|
+
description="We have updated our terms of service."
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
| --- | --- | --- | --- |
|
|
26
|
+
| `title` | `string` | — | Headline text for the banner. |
|
|
27
|
+
| `description` | `string` | — | Supporting description text. |
|
|
28
|
+
| `variant` | `"neutral" | "info" | "success" | "warning" | "destructive"` | `"neutral"` | Visual tone of the banner. |
|
|
29
|
+
| `icon` | `ReactNode` | — | Optional icon rendered before the content. |
|
|
30
|
+
| `iconName` | `SolaraIconName | IconProps` | — | Optional icon name or props for the leading icon. |
|
|
31
|
+
| `showIcon` | `boolean` | `true` | Toggle the leading icon on or off. |
|
|
32
|
+
| `action` | `ReactNode` | — | Optional action node rendered on the right. |
|
|
33
|
+
| `actions` | `ReactNode` | — | Optional action nodes rendered on the right (preferred). |
|
|
34
|
+
| `onDismiss` | `() => void` | — | Shows a dismiss button when provided. |
|
|
35
|
+
| `showTitle` | `boolean` | `true` | Toggles the title visibility. |
|
|
36
|
+
| `showDescription` | `boolean` | `true` | Toggles the description visibility. |
|
|
37
|
+
| `showAccentBar` | `boolean` | `true` | Toggles the left accent bar. |
|
|
38
|
+
| `radius` | `"default" | "none" | "full"` | `"default"` | Controls the banner corner radius. |
|
|
39
|
+
| `fullWidth` | `boolean` | `false` | Stretches the banner to full width. |
|
|
40
|
+
|
|
41
|
+
Banner also accepts standard `div` props such as `className` and `role`.
|
|
42
|
+
|
|
43
|
+
## Icons
|
|
44
|
+
|
|
45
|
+
You can pass an icon name or full icon props. Icons inherit the banner text color.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<Banner
|
|
49
|
+
title="Heads up"
|
|
50
|
+
description="We have updated our terms of service."
|
|
51
|
+
iconName="data_spreadsheet_search_24"
|
|
52
|
+
/>
|
|
53
|
+
```
|
package/examples.tsx
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { iconNames } from "@solara/icons";
|
|
3
|
+
import type { SolaraIconName } from "@solara/icons";
|
|
4
|
+
import { Button } from "@solara/button";
|
|
5
|
+
import { Banner } from "./src/Banner";
|
|
6
|
+
import type { BannerProps } from "./src/Banner.types";
|
|
7
|
+
|
|
8
|
+
const DemoAction = ({ label = "Review" }: { label?: string }) => (
|
|
9
|
+
<Button label={label} variant="ghost" size="small" />
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export const stories = {
|
|
13
|
+
playground: {
|
|
14
|
+
title: "Playground",
|
|
15
|
+
description: "Tweak the props to explore the Banner API.",
|
|
16
|
+
render: (
|
|
17
|
+
props: BannerProps & {
|
|
18
|
+
showIcon?: boolean;
|
|
19
|
+
showAction?: boolean;
|
|
20
|
+
dismissible?: boolean;
|
|
21
|
+
}
|
|
22
|
+
) => {
|
|
23
|
+
const {
|
|
24
|
+
showIcon = false,
|
|
25
|
+
showAction = false,
|
|
26
|
+
dismissible = false,
|
|
27
|
+
...bannerProps
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Banner
|
|
32
|
+
{...bannerProps}
|
|
33
|
+
iconName={(props.iconName as SolaraIconName) || undefined}
|
|
34
|
+
showIcon={showIcon}
|
|
35
|
+
actions={
|
|
36
|
+
showAction ? (
|
|
37
|
+
<>
|
|
38
|
+
<DemoAction label="Review" />
|
|
39
|
+
<DemoAction label="Later" />
|
|
40
|
+
</>
|
|
41
|
+
) : undefined
|
|
42
|
+
}
|
|
43
|
+
onDismiss={dismissible ? () => undefined : undefined}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
controls: [
|
|
48
|
+
{ name: "title", type: "text" },
|
|
49
|
+
{ name: "description", type: "text" },
|
|
50
|
+
{ name: "showTitle", type: "boolean", label: "Show title" },
|
|
51
|
+
{ name: "showDescription", type: "boolean", label: "Show description" },
|
|
52
|
+
{ name: "showAccentBar", type: "boolean", label: "Show accent bar" },
|
|
53
|
+
{
|
|
54
|
+
name: "variant",
|
|
55
|
+
type: "select",
|
|
56
|
+
options: ["neutral", "info", "success", "warning", "destructive"],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "radius",
|
|
60
|
+
type: "select",
|
|
61
|
+
options: ["default", "none", "full"],
|
|
62
|
+
},
|
|
63
|
+
{ name: "showIcon", type: "boolean", label: "Show icon" },
|
|
64
|
+
{
|
|
65
|
+
name: "iconName",
|
|
66
|
+
label: "Icon Name",
|
|
67
|
+
type: "select",
|
|
68
|
+
options: ["", ...iconNames],
|
|
69
|
+
},
|
|
70
|
+
{ name: "showAction", type: "boolean", label: "Show action" },
|
|
71
|
+
{ name: "dismissible", type: "boolean", label: "Dismissible" },
|
|
72
|
+
{ name: "fullWidth", type: "boolean" },
|
|
73
|
+
],
|
|
74
|
+
initialProps: {
|
|
75
|
+
title: "New policy update",
|
|
76
|
+
description: "Please review the latest guidelines before continuing.",
|
|
77
|
+
showTitle: true,
|
|
78
|
+
showDescription: true,
|
|
79
|
+
showAccentBar: true,
|
|
80
|
+
variant: "neutral",
|
|
81
|
+
radius: "default",
|
|
82
|
+
showIcon: false,
|
|
83
|
+
iconName: "",
|
|
84
|
+
showAction: false,
|
|
85
|
+
dismissible: false,
|
|
86
|
+
fullWidth: false,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
variants: {
|
|
90
|
+
title: "Variants",
|
|
91
|
+
showProps: false,
|
|
92
|
+
render: () => (
|
|
93
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
94
|
+
<Banner title="Neutral" description="Default informational messaging." />
|
|
95
|
+
<Banner
|
|
96
|
+
title="Info"
|
|
97
|
+
description="Share helpful context and details."
|
|
98
|
+
variant="info"
|
|
99
|
+
iconName="data_spreadsheet_search_24"
|
|
100
|
+
/>
|
|
101
|
+
<Banner
|
|
102
|
+
title="Success"
|
|
103
|
+
description="Confirm that an action completed successfully."
|
|
104
|
+
variant="success"
|
|
105
|
+
iconName="arrow_line_up_16"
|
|
106
|
+
/>
|
|
107
|
+
<Banner
|
|
108
|
+
title="Warning"
|
|
109
|
+
description="Let users know about a potential issue."
|
|
110
|
+
variant="warning"
|
|
111
|
+
iconName="arrow_chevron_down_16"
|
|
112
|
+
/>
|
|
113
|
+
<Banner
|
|
114
|
+
title="Destructive"
|
|
115
|
+
description="Call attention to errors or blocking issues."
|
|
116
|
+
variant="destructive"
|
|
117
|
+
iconName="arrow_swap_16"
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
),
|
|
121
|
+
code: `import { Banner } from "@solara/banner";
|
|
122
|
+
|
|
123
|
+
export function Example() {
|
|
124
|
+
return (
|
|
125
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
126
|
+
<Banner title="Neutral" description="Default informational messaging." />
|
|
127
|
+
<Banner
|
|
128
|
+
title="Info"
|
|
129
|
+
description="Share helpful context and details."
|
|
130
|
+
variant="info"
|
|
131
|
+
iconName="data_spreadsheet_search_24"
|
|
132
|
+
/>
|
|
133
|
+
<Banner
|
|
134
|
+
title="Success"
|
|
135
|
+
description="Confirm that an action completed successfully."
|
|
136
|
+
variant="success"
|
|
137
|
+
iconName="arrow_line_up_16"
|
|
138
|
+
/>
|
|
139
|
+
<Banner
|
|
140
|
+
title="Warning"
|
|
141
|
+
description="Let users know about a potential issue."
|
|
142
|
+
variant="warning"
|
|
143
|
+
iconName="arrow_chevron_down_16"
|
|
144
|
+
/>
|
|
145
|
+
<Banner
|
|
146
|
+
title="Destructive"
|
|
147
|
+
description="Call attention to errors or blocking issues."
|
|
148
|
+
variant="destructive"
|
|
149
|
+
iconName="arrow_swap_16"
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
`,
|
|
155
|
+
},
|
|
156
|
+
actions: {
|
|
157
|
+
title: "With Action",
|
|
158
|
+
description: "Banners can include a call to action and dismiss control.",
|
|
159
|
+
showProps: false,
|
|
160
|
+
render: () => (
|
|
161
|
+
<Banner
|
|
162
|
+
title="Finish setup"
|
|
163
|
+
description="Verify your email to complete onboarding."
|
|
164
|
+
variant="info"
|
|
165
|
+
actions={
|
|
166
|
+
<>
|
|
167
|
+
<Button label="Verify" variant="ghost" size="small" />
|
|
168
|
+
<Button label="Remind me" variant="ghost" size="small" />
|
|
169
|
+
</>
|
|
170
|
+
}
|
|
171
|
+
onDismiss={() => undefined}
|
|
172
|
+
/>
|
|
173
|
+
),
|
|
174
|
+
code: `import { Banner } from "@solara/banner";
|
|
175
|
+
import { Button } from "@solara/button";
|
|
176
|
+
|
|
177
|
+
export function Example() {
|
|
178
|
+
return (
|
|
179
|
+
<Banner
|
|
180
|
+
title="Finish setup"
|
|
181
|
+
description="Verify your email to complete onboarding."
|
|
182
|
+
variant="info"
|
|
183
|
+
actions={
|
|
184
|
+
<>
|
|
185
|
+
<Button label="Verify" variant="ghost" size="small" />
|
|
186
|
+
<Button label="Remind me" variant="ghost" size="small" />
|
|
187
|
+
</>
|
|
188
|
+
}
|
|
189
|
+
onDismiss={() => undefined}
|
|
190
|
+
/>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
`,
|
|
194
|
+
},
|
|
195
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@varialkit/banner",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./examples": "./examples.tsx"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@varialkit/icons": "0.1.1",
|
|
13
|
+
"@varialkit/button": "0.1.1"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"docs.md",
|
|
18
|
+
"examples.tsx"
|
|
19
|
+
],
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": "^19.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/react": "19.0.10",
|
|
25
|
+
"react": "19.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/Banner.scss
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
.solara-banner {
|
|
2
|
+
--banner-bg: var(--color-surface-100);
|
|
3
|
+
--banner-border: var(--color-divider-secondary);
|
|
4
|
+
--banner-title: var(--color-text-primary);
|
|
5
|
+
--banner-description: var(--color-text-secondary);
|
|
6
|
+
--banner-icon: var(--color-icon-secondary);
|
|
7
|
+
--banner-accent: var(--color-divider-secondary);
|
|
8
|
+
--banner-radius: var(--radius-3);
|
|
9
|
+
|
|
10
|
+
position: relative;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: calc(var(--space-3) * var(--spacing-multiplier));
|
|
14
|
+
padding: calc(var(--space-3) * var(--spacing-multiplier))
|
|
15
|
+
calc(var(--space-4) * var(--spacing-multiplier));
|
|
16
|
+
border-radius: var(--banner-radius);
|
|
17
|
+
border: 1px solid var(--banner-border);
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
background-color: var(--banner-bg);
|
|
20
|
+
color: var(--banner-title);
|
|
21
|
+
font-family: var(--font-body);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.solara-banner::before {
|
|
25
|
+
content: "";
|
|
26
|
+
position: absolute;
|
|
27
|
+
inset: 0 auto 0 0;
|
|
28
|
+
width: 4px;
|
|
29
|
+
background-color: var(--banner-accent);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.solara-banner--neutral {
|
|
33
|
+
--banner-bg: var(--color-surface-100);
|
|
34
|
+
--banner-border: var(--color-divider-secondary);
|
|
35
|
+
--banner-title: var(--color-text-primary);
|
|
36
|
+
--banner-description: var(--color-text-secondary);
|
|
37
|
+
--banner-icon: var(--color-icon-secondary);
|
|
38
|
+
--banner-accent: var(--color-divider-secondary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.solara-banner--info {
|
|
42
|
+
--banner-bg: var(--color-surface-info);
|
|
43
|
+
--banner-border: var(--color-divider-accent);
|
|
44
|
+
--banner-title: var(--color-text-info);
|
|
45
|
+
--banner-description: var(--color-text-info);
|
|
46
|
+
--banner-icon: var(--color-icon-info);
|
|
47
|
+
--banner-accent: var(--color-surface-status-bold-info);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.solara-banner--success {
|
|
51
|
+
--banner-bg: var(--color-surface-success);
|
|
52
|
+
--banner-border: var(--color-divider-secondary);
|
|
53
|
+
--banner-title: var(--color-text-success);
|
|
54
|
+
--banner-description: var(--color-text-success);
|
|
55
|
+
--banner-icon: var(--color-icon-success);
|
|
56
|
+
--banner-accent: var(--color-surface-status-bold-success);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.solara-banner--warning {
|
|
60
|
+
--banner-bg: var(--color-surface-warning);
|
|
61
|
+
--banner-border: var(--color-divider-secondary);
|
|
62
|
+
--banner-title: var(--color-text-warning);
|
|
63
|
+
--banner-description: var(--color-text-warning);
|
|
64
|
+
--banner-icon: var(--color-icon-warning);
|
|
65
|
+
--banner-accent: var(--color-surface-status-bold-warning);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.solara-banner--destructive {
|
|
69
|
+
--banner-bg: var(--color-surface-destructive);
|
|
70
|
+
--banner-border: var(--color-divider-alert);
|
|
71
|
+
--banner-title: var(--color-text-alert);
|
|
72
|
+
--banner-description: var(--color-text-alert);
|
|
73
|
+
--banner-icon: var(--color-icon-alert);
|
|
74
|
+
--banner-accent: var(--color-surface-status-bold-destructive);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.solara-banner--radius-default {
|
|
78
|
+
--banner-radius: var(--radius-3);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.solara-banner--radius-none {
|
|
82
|
+
--banner-radius: var(--radius-0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.solara-banner--radius-full {
|
|
86
|
+
--banner-radius: var(--radius-pill);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.solara-banner--full-width {
|
|
90
|
+
width: 100%;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.solara-banner--no-accent {
|
|
94
|
+
border-left: 1px solid var(--banner-border);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.solara-banner--no-accent::before {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.solara-banner__icon {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
width: calc(var(--space-5) * var(--spacing-multiplier));
|
|
106
|
+
height: calc(var(--space-5) * var(--spacing-multiplier));
|
|
107
|
+
color: var(--banner-icon);
|
|
108
|
+
flex-shrink: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.solara-banner__icon .solara-icon [stroke]:not([stroke="none"]) {
|
|
112
|
+
stroke: currentColor;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.solara-banner__icon .solara-icon [fill]:not([fill="none"]) {
|
|
116
|
+
fill: currentColor;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.solara-banner__content {
|
|
120
|
+
flex: 1;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.solara-banner__title {
|
|
125
|
+
font-size: var(--font-size-body-scaled);
|
|
126
|
+
line-height: var(--line-height-body-scaled);
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
margin-bottom: calc(var(--space-1) * var(--spacing-multiplier));
|
|
129
|
+
color: var(--banner-title);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.solara-banner__description {
|
|
133
|
+
font-size: var(--font-size-body-scaled);
|
|
134
|
+
line-height: var(--line-height-body-scaled);
|
|
135
|
+
color: var(--banner-description);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.solara-banner__end {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: calc(var(--space-2) * var(--spacing-multiplier));
|
|
142
|
+
margin-left: auto;
|
|
143
|
+
align-self: center;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.solara-banner__action {
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.solara-banner__dismiss {
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
width: calc(var(--space-6) * var(--spacing-multiplier));
|
|
156
|
+
height: calc(var(--space-6) * var(--spacing-multiplier));
|
|
157
|
+
border: none;
|
|
158
|
+
border-radius: var(--radius-2);
|
|
159
|
+
background: transparent;
|
|
160
|
+
color: var(--banner-icon);
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
opacity: 0.7;
|
|
163
|
+
transition: opacity 0.2s ease, background-color 0.2s ease;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.solara-banner__dismiss:hover,
|
|
167
|
+
.solara-banner__dismiss:focus-visible {
|
|
168
|
+
opacity: 1;
|
|
169
|
+
background-color: var(--color-surface-200);
|
|
170
|
+
outline: none;
|
|
171
|
+
}
|
package/src/Banner.tsx
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import { Icon } from "@solara/icons";
|
|
3
|
+
import type { IconProps } from "@solara/icons";
|
|
4
|
+
import type { BannerProps, BannerVariant } from "./Banner.types";
|
|
5
|
+
import "./Banner.scss";
|
|
6
|
+
|
|
7
|
+
const alertVariants = new Set<BannerVariant>(["warning", "destructive"]);
|
|
8
|
+
|
|
9
|
+
type BannerIcon = IconProps | IconProps["name"];
|
|
10
|
+
|
|
11
|
+
const normalizeIconProps = (icon: BannerIcon): IconProps =>
|
|
12
|
+
typeof icon === "string" ? { name: icon } : icon;
|
|
13
|
+
|
|
14
|
+
const resolveIconProps = (icon: BannerIcon): IconProps => {
|
|
15
|
+
const iconProps = normalizeIconProps(icon);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
...iconProps,
|
|
19
|
+
style: {
|
|
20
|
+
...iconProps.style,
|
|
21
|
+
color: "currentColor",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Banner component for prominent, inline messaging.
|
|
28
|
+
*/
|
|
29
|
+
export const Banner = forwardRef<HTMLDivElement, BannerProps>(
|
|
30
|
+
(
|
|
31
|
+
{
|
|
32
|
+
title,
|
|
33
|
+
description,
|
|
34
|
+
icon,
|
|
35
|
+
iconName,
|
|
36
|
+
showIcon = true,
|
|
37
|
+
action,
|
|
38
|
+
actions,
|
|
39
|
+
onDismiss,
|
|
40
|
+
dismissLabel = "Dismiss",
|
|
41
|
+
showTitle = true,
|
|
42
|
+
showDescription = true,
|
|
43
|
+
showAccentBar = true,
|
|
44
|
+
variant = "neutral",
|
|
45
|
+
radius = "default",
|
|
46
|
+
fullWidth = false,
|
|
47
|
+
className,
|
|
48
|
+
children,
|
|
49
|
+
role,
|
|
50
|
+
...props
|
|
51
|
+
},
|
|
52
|
+
ref
|
|
53
|
+
) => {
|
|
54
|
+
const resolvedRole = role ?? (alertVariants.has(variant) ? "alert" : "status");
|
|
55
|
+
|
|
56
|
+
const classes = [
|
|
57
|
+
"solara-banner",
|
|
58
|
+
`solara-banner--${variant}`,
|
|
59
|
+
`solara-banner--radius-${radius}`,
|
|
60
|
+
showAccentBar ? "solara-banner--accent" : "solara-banner--no-accent",
|
|
61
|
+
fullWidth ? "solara-banner--full-width" : null,
|
|
62
|
+
className,
|
|
63
|
+
]
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join(" ");
|
|
66
|
+
|
|
67
|
+
const resolvedActions = actions ?? action;
|
|
68
|
+
const resolvedIcon =
|
|
69
|
+
showIcon === false
|
|
70
|
+
? null
|
|
71
|
+
: icon
|
|
72
|
+
? icon
|
|
73
|
+
: iconName
|
|
74
|
+
? <Icon {...resolveIconProps(iconName)} />
|
|
75
|
+
: null;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div ref={ref} className={classes} role={resolvedRole} {...props}>
|
|
79
|
+
{resolvedIcon ? <div className="solara-banner__icon">{resolvedIcon}</div> : null}
|
|
80
|
+
<div className="solara-banner__content">
|
|
81
|
+
{title && showTitle ? (
|
|
82
|
+
<div className="solara-banner__title">{title}</div>
|
|
83
|
+
) : null}
|
|
84
|
+
{description && showDescription ? (
|
|
85
|
+
<div className="solara-banner__description">{description}</div>
|
|
86
|
+
) : null}
|
|
87
|
+
{children}
|
|
88
|
+
</div>
|
|
89
|
+
{resolvedActions || onDismiss ? (
|
|
90
|
+
<div className="solara-banner__end">
|
|
91
|
+
{resolvedActions ? (
|
|
92
|
+
<div className="solara-banner__action">{resolvedActions}</div>
|
|
93
|
+
) : null}
|
|
94
|
+
{onDismiss ? (
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
className="solara-banner__dismiss"
|
|
98
|
+
onClick={onDismiss}
|
|
99
|
+
aria-label={dismissLabel}
|
|
100
|
+
>
|
|
101
|
+
<span aria-hidden="true">×</span>
|
|
102
|
+
</button>
|
|
103
|
+
) : null}
|
|
104
|
+
</div>
|
|
105
|
+
) : null}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
Banner.displayName = "Banner";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { IconProps } from "@solara/icons";
|
|
3
|
+
|
|
4
|
+
export type BannerVariant = "neutral" | "info" | "success" | "warning" | "destructive";
|
|
5
|
+
export type BannerRadius = "default" | "none" | "full";
|
|
6
|
+
|
|
7
|
+
export type BannerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
8
|
+
/** Headline text for the banner. */
|
|
9
|
+
title?: string;
|
|
10
|
+
/** Supporting description text. */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Optional icon to show before the content. */
|
|
13
|
+
icon?: React.ReactNode;
|
|
14
|
+
/** Optional icon name or props for the leading icon. */
|
|
15
|
+
iconName?: IconProps | IconProps["name"];
|
|
16
|
+
/** Toggle the leading icon on or off. */
|
|
17
|
+
showIcon?: boolean;
|
|
18
|
+
/** Optional action node rendered on the right. */
|
|
19
|
+
action?: React.ReactNode;
|
|
20
|
+
/** Optional action nodes rendered on the right (preferred over `action`). */
|
|
21
|
+
actions?: React.ReactNode;
|
|
22
|
+
/** Callback when the dismiss button is clicked. */
|
|
23
|
+
onDismiss?: () => void;
|
|
24
|
+
/** Accessible label for the dismiss button. */
|
|
25
|
+
dismissLabel?: string;
|
|
26
|
+
/** Whether to show the title text. */
|
|
27
|
+
showTitle?: boolean;
|
|
28
|
+
/** Whether to show the description text. */
|
|
29
|
+
showDescription?: boolean;
|
|
30
|
+
/** Whether to show the left accent bar. */
|
|
31
|
+
showAccentBar?: boolean;
|
|
32
|
+
/** Visual tone of the banner. */
|
|
33
|
+
variant?: BannerVariant;
|
|
34
|
+
/** Corner radius for the banner. */
|
|
35
|
+
radius?: BannerRadius;
|
|
36
|
+
/** Stretch the banner to full width. */
|
|
37
|
+
fullWidth?: boolean;
|
|
38
|
+
};
|
package/src/index.ts
ADDED