Skip to content

Commit 6c16a19

Browse files
committed
✨ Add Alert and ConditionalWrapper components
1 parent f32345d commit 6c16a19

22 files changed

+400
-7
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ import { Accordion } from 'webcoreui/react'
132132
## Components
133133

134134
- [Accordion](https://github.com/Frontendland/webcoreui/tree/main/src/components/Accordion)
135+
- [Alert](https://github.com/Frontendland/webcoreui/tree/main/src/components/Alert)
136+
- [ConditionalWrapper](https://github.com/Frontendland/webcoreui/tree/main/src/components/ConditionalWrapper)
135137
- [Badge](https://github.com/Frontendland/webcoreui/tree/main/src/components/Badge)
136138
- [Button](https://github.com/Frontendland/webcoreui/tree/main/src/components/Button)
137139
- [Card](https://github.com/Frontendland/webcoreui/tree/main/src/components/Card)

src/components/Alert/Alert.astro

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
import type { AlertProps } from './alert'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
4+
5+
import info from '../../icons/info.svg?raw'
6+
import success from '../../icons/check.svg?raw'
7+
import warning from '../../icons/warning.svg?raw'
8+
import alert from '../../icons/alert.svg?raw'
9+
10+
interface Props extends AlertProps {}
11+
12+
const iconMap = {
13+
info,
14+
success,
15+
warning,
16+
alert
17+
}
18+
19+
const {
20+
element = 'section',
21+
title,
22+
titleTag = 'strong',
23+
className,
24+
theme,
25+
...rest
26+
} = Astro.props
27+
28+
const Component = element
29+
const Title = titleTag
30+
const hasCustomIcon = Astro.slots.has('icon')
31+
32+
const classes = [
33+
'w-alert',
34+
(!hasCustomIcon && !theme) && 'col',
35+
theme,
36+
className
37+
].filter(Boolean).join(' ')
38+
39+
const props = {
40+
class: classes
41+
}
42+
---
43+
44+
<Component {...props} {...rest}>
45+
<slot name="icon" />
46+
47+
{!hasCustomIcon && theme && <Fragment set:html={iconMap[theme]} />}
48+
49+
<ConditionalWrapper condition={!!(hasCustomIcon || theme)}>
50+
<div class="alert-wrapper" slot="wrapper">
51+
children
52+
</div>
53+
{title && (
54+
<Title class:list="alert-title">{title}</Title>
55+
)}
56+
<div class="alert-body">
57+
<slot />
58+
</div>
59+
</ConditionalWrapper>
60+
</Component>
61+
62+
<style lang="scss">
63+
@import './alert.scss';
64+
</style>

src/components/Alert/Alert.svelte

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script lang="ts">
2+
import type { AlertProps } from './alert'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
4+
5+
import info from '../../icons/info.svg?raw'
6+
import success from '../../icons/check.svg?raw'
7+
import warning from '../../icons/warning.svg?raw'
8+
import alert from '../../icons/alert.svg?raw'
9+
10+
export let element: AlertProps['element'] = 'section'
11+
export let title: AlertProps['title'] = null
12+
export let titleTag: AlertProps['title'] = 'strong'
13+
export let className: AlertProps['className'] = null
14+
export let theme: AlertProps['theme'] = null
15+
16+
const iconMap = {
17+
info,
18+
success,
19+
warning,
20+
alert
21+
}
22+
23+
const hasCustomIcon = $$slots.icon
24+
25+
const classes = [
26+
'w-alert',
27+
(!hasCustomIcon && !theme) && 'col',
28+
theme,
29+
className
30+
].filter(Boolean).join(' ')
31+
</script>
32+
33+
<svelte:element this={element} class={classes} {...$$restProps}>
34+
<slot name="icon" />
35+
36+
{#if !hasCustomIcon && theme}
37+
{@html iconMap[theme]}
38+
{/if}
39+
40+
<ConditionalWrapper
41+
condition={!!(hasCustomIcon || theme)}
42+
element="div"
43+
class="alert-wrapper"
44+
>
45+
{#if title}
46+
<svelte:element this={titleTag} class="alert-title">
47+
{title}
48+
</svelte:element>
49+
{/if}
50+
<div class="alert-body">
51+
<slot />
52+
</div>
53+
</ConditionalWrapper>
54+
</svelte:element>
55+
56+
<style lang="scss">
57+
@import './alert.scss';
58+
</style>

src/components/Alert/Alert.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from 'react'
2+
import type { AlertProps } from './alert'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
4+
5+
import info from '../../icons/info.svg?raw'
6+
import success from '../../icons/check.svg?raw'
7+
import warning from '../../icons/warning.svg?raw'
8+
import alert from '../../icons/alert.svg?raw'
9+
10+
import './alert.scss'
11+
12+
const iconMap = {
13+
info,
14+
success,
15+
warning,
16+
alert
17+
}
18+
19+
const Alert = ({
20+
Element = 'section',
21+
title,
22+
TitleTag = 'strong',
23+
className,
24+
theme,
25+
children,
26+
icon,
27+
...rest
28+
}: AlertProps & { icon?: any }) => {
29+
const classes = [
30+
'w-alert',
31+
(!icon && !theme) && 'col',
32+
theme,
33+
className
34+
].filter(Boolean).join(' ')
35+
36+
return (
37+
<Element className={classes} {...rest}>
38+
{icon && icon}
39+
{!icon && theme && <div dangerouslySetInnerHTML={{ __html: iconMap[theme] }} />}
40+
41+
<ConditionalWrapper condition={!!(icon || theme)} wrapper={children => (
42+
<div className="alert-wrapper">
43+
{children}
44+
</div>
45+
)}>
46+
{title && (
47+
<TitleTag className="alert-title">{title}</TitleTag>
48+
)}
49+
<div className="alert-body">
50+
{children}
51+
</div>
52+
</ConditionalWrapper>
53+
</Element>
54+
)
55+
}
56+
57+
export default Alert

src/components/Alert/alert.scss

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@import '../../scss/config.scss';
2+
3+
.w-alert {
4+
border: 1px solid #252525;
5+
border-radius: 5px;
6+
padding: 15px;
7+
display: flex;
8+
9+
&.col {
10+
flex-direction: column;
11+
}
12+
13+
&:not(.col) {
14+
gap: 10px;
15+
}
16+
17+
&.info {
18+
border: 1px solid #0abde3;
19+
color: #0abde3;
20+
}
21+
22+
&.success {
23+
border: 1px solid #1dd1a1;
24+
color: #1dd1a1;
25+
}
26+
27+
&.warning {
28+
border: 1px solid #ff9f43;
29+
color: #ff9f43;
30+
}
31+
32+
&.alert {
33+
border: 1px solid #e73f40;
34+
color: #e73f40;
35+
}
36+
37+
svg {
38+
width: 20px;
39+
height: 20px;
40+
margin-top: 1px;
41+
}
42+
43+
.alert-title {
44+
display: block;
45+
margin-bottom: 5px;
46+
}
47+
48+
.alert-body {
49+
font-size: 16px;
50+
color: #BBB;
51+
}
52+
}

src/components/Alert/alert.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export type AlertProps = {
2+
element?: string
3+
title?: string | null
4+
titleTag?: string
5+
icon?: string | null
6+
className?: string | null
7+
theme?: 'info'
8+
| 'success'
9+
| 'warning'
10+
| 'alert'
11+
| null
12+
[key: string]: any
13+
}

src/components/Card/Card.astro

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const classes = [
1717
'w-card',
1818
className,
1919
secondary && 'secondary',
20-
'card'
2120
].filter(Boolean).join(' ')
2221
2322
const props = {

src/components/Card/Card.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
const classes = [
1212
'w-card',
1313
className,
14-
secondary && 'secondary',
15-
'card'
14+
secondary && 'secondary'
1615
].filter(Boolean).join(' ')
1716
</script>
1817

src/components/Card/Card.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ const Card = ({
1515
const classes = [
1616
'w-card',
1717
className,
18-
secondary && 'secondary',
19-
'card'
18+
secondary && 'secondary'
2019
].filter(Boolean).join(' ')
2120

2221
return (

src/components/Card/card.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.card {
1+
.w-card {
22
border: 1px solid #252525;
33
border-radius: 5px;
44
display: flex;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
import type { ConditionalWrapperProps } from './conditionalwrapper'
3+
4+
interface Props extends ConditionalWrapperProps {}
5+
6+
const { condition } = Astro.props
7+
8+
const wrapper = await Astro.slots.render('wrapper')
9+
const children = await Astro.slots.render('default')
10+
const wrapped = wrapper?.replace('children', children)
11+
12+
if (!Astro.slots.has('wrapper')) {
13+
console.error('Missing wrapper. Add slot="wrapper" to one of the elements.')
14+
}
15+
---
16+
17+
<Fragment set:html={condition ? wrapped : children} />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import type { ConditionalWrapperProps } from './conditionalwrapper'
3+
4+
export let condition: ConditionalWrapperProps['condition']
5+
export let element: string
6+
</script>
7+
8+
{#if condition}
9+
<svelte:element this={element} {...$$restProps}>
10+
<slot />
11+
</svelte:element>
12+
{:else}
13+
<slot />
14+
{/if}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import type { ConditionalWrapperProps } from './conditionalwrapper'
3+
4+
type ExtendedConditionalWrapperProps = {
5+
condition: ConditionalWrapperProps['condition']
6+
wrapper: (_: React.ReactNode) => any
7+
children: React.ReactNode
8+
};
9+
10+
const ConditionalWrapper = ({ condition, wrapper, children }: ExtendedConditionalWrapperProps) =>
11+
condition ? wrapper(children) : children
12+
13+
export default ConditionalWrapper
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type ConditionalWrapperProps = {
2+
condition: boolean
3+
}

src/env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/// <reference types="astro/client" />
1+
/// <reference types="astro/client" />

src/icons/alert.svg

Lines changed: 3 additions & 0 deletions
Loading

src/icons/check.svg

Lines changed: 4 additions & 0 deletions
Loading

src/icons/info.svg

Lines changed: 4 additions & 0 deletions
Loading

src/icons/warning.svg

Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)