Skip to content

Commit 6e02930

Browse files
committed
✨ Add Textarea component
1 parent b0207e9 commit 6e02930

File tree

12 files changed

+343
-23
lines changed

12 files changed

+343
-23
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ import { Accordion } from 'webcoreui/react'
210210
- [Switch](https://github.com/Frontendland/webcoreui/tree/main/src/components/Switch)
211211
- [Table](https://github.com/Frontendland/webcoreui/tree/main/src/components/Table)
212212
- [Tabs](https://github.com/Frontendland/webcoreui/tree/main/src/components/Tabs)
213+
- [Textarea](https://github.com/Frontendland/webcoreui/tree/main/src/components/Textarea)
213214
- [ThemeSwitcher](https://github.com/Frontendland/webcoreui/tree/main/src/components/ThemeSwitcher)
214215
- [Timeline](https://github.com/Frontendland/webcoreui/blob/main/src/pages/timeline.astro)
215216
- [Toast](https://github.com/Frontendland/webcoreui/tree/main/src/components/Toast)

scripts/buildTypes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ const buildTypes = type => {
2828
'Input',
2929
'Radio',
3030
'Switch',
31-
'Slider'
31+
'Slider',
32+
'Textarea'
3233
]
3334

3435
return componentsWithSvelteSpecificTypes.includes(component)

scripts/createComponent.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ const templates = {
120120
121121
{sections.map(section => (
122122
<h1>{section.title}</h1>
123+
<Fragment>
124+
{section.subTitle && <h2 set:html={section.subTitle} />}
125+
</Fragment>
123126
<div class="grid md-2 lg-3">
124127
<ComponentWrapper title="Default">
125128
<section.component />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
import type { TextareaProps } from './textarea'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
4+
5+
import styles from './textarea.module.scss'
6+
7+
interface Props extends TextareaProps {}
8+
9+
const {
10+
label,
11+
placeholder,
12+
subText,
13+
value = '',
14+
disabled,
15+
className
16+
} = Astro.props
17+
18+
const classes = [
19+
styles.textarea,
20+
className
21+
]
22+
23+
const useLabel = !!(label || subText)
24+
---
25+
26+
<ConditionalWrapper condition={useLabel}>
27+
<label slot="wrapper" class={styles['label-wrapper']}>
28+
{label && (
29+
<div class={styles.label}>{label}</div>
30+
)}
31+
children
32+
{subText && (
33+
<div class={styles.subtext}>
34+
<Fragment set:html={subText} />
35+
</div>
36+
)}
37+
</label>
38+
39+
<textarea
40+
placeholder={placeholder}
41+
disabled={disabled}
42+
class:list={classes}
43+
>{value}</textarea>
44+
</ConditionalWrapper>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script lang="ts">
2+
import type { SvelteTextareaProps } from './textarea'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
4+
5+
import styles from './textarea.module.scss'
6+
import { classNames } from '../../utils/classNames'
7+
8+
export let label: SvelteTextareaProps['label'] = ''
9+
export let placeholder: SvelteTextareaProps['placeholder'] = ''
10+
export let subText: SvelteTextareaProps['subText'] = ''
11+
export let value: SvelteTextareaProps['value'] = ''
12+
export let disabled: SvelteTextareaProps['disabled'] = false
13+
export let className: SvelteTextareaProps['className'] = ''
14+
export let onChange: SvelteTextareaProps['onChange'] = () => {}
15+
export let onKeyUp: SvelteTextareaProps['onKeyUp'] = () => {}
16+
17+
const classes = classNames([
18+
styles.textarea,
19+
className
20+
])
21+
22+
const useLabel = !!(label || subText)
23+
</script>
24+
25+
<ConditionalWrapper
26+
condition={useLabel}
27+
element="label"
28+
class={styles['label-wrapper']}
29+
>
30+
{#if label}
31+
<div class={styles.label}>{label}</div>
32+
{/if}
33+
<textarea
34+
placeholder={placeholder}
35+
disabled={disabled}
36+
class={classes}
37+
on:change={onChange}
38+
on:keyup={onKeyUp}
39+
>{value}</textarea>
40+
{#if subText}
41+
<div class={styles.subtext}>
42+
{@html subText}
43+
</div>
44+
{/if}
45+
</ConditionalWrapper>

src/components/Textarea/Textarea.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
import type { ReactTextareaProps } from './textarea'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
4+
5+
import styles from './textarea.module.scss'
6+
import { classNames } from '../../utils/classNames'
7+
8+
const Textarea = ({
9+
label,
10+
placeholder,
11+
subText,
12+
value = '',
13+
disabled,
14+
className,
15+
...rest
16+
}: ReactTextareaProps) => {
17+
const classes = classNames([
18+
styles.textarea,
19+
className
20+
])
21+
22+
const useLabel = !!(label || subText)
23+
24+
return (
25+
<ConditionalWrapper condition={useLabel} wrapper={children => (
26+
<label className={styles['label-wrapper']}>
27+
{label && (
28+
<div className={styles.label}>{label}</div>
29+
)}
30+
{children}
31+
{subText && (
32+
<div
33+
className={styles.subtext}
34+
dangerouslySetInnerHTML={{ __html: subText }}
35+
/>
36+
)}
37+
</label>
38+
)}>
39+
<textarea
40+
placeholder={placeholder}
41+
disabled={disabled}
42+
className={classes}
43+
defaultValue={value}
44+
{...rest}
45+
/>
46+
</ConditionalWrapper>
47+
)
48+
}
49+
50+
export default Textarea
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@import '../../scss/config.scss';
2+
3+
.textarea {
4+
@include size('w100%');
5+
@include background(transparent);
6+
@include border-radius(sm);
7+
@include border(primary-50);
8+
@include spacing(p-sm);
9+
@include typography(regular, primary, hlg);
10+
11+
min-height: 50px;
12+
resize: vertical;
13+
14+
&[disabled] {
15+
@include typography(primary-30);
16+
cursor: no-drop;
17+
}
18+
19+
&::placeholder {
20+
@include typography(primary-30);
21+
}
22+
}
23+
24+
.label-wrapper {
25+
@include layout(flex, column);
26+
27+
.label {
28+
@include typography(primary-20);
29+
@include spacing(mb-xs);
30+
}
31+
32+
.subtext {
33+
@include typography(md, primary-30);
34+
@include spacing(mt-xs);
35+
}
36+
}

src/components/Textarea/textarea.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export type TextareaProps = {
2+
label?: string
3+
placeholder?: string
4+
subText?: string
5+
value?: string
6+
disabled?: boolean
7+
className?: string
8+
}
9+
10+
export type SvelteTextareaProps = {
11+
onChange?: (e: any) => any
12+
onKeyUp?: (e: any) => any
13+
} & TextareaProps
14+
15+
export type ReactTextareaProps = {
16+
onChange?: (e: any) => any
17+
onKeyUp?: (e: any) => any
18+
} & TextareaProps

src/pages/index.astro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Spinner from '@components/Spinner/Spinner.astro'
2020
import Switch from '@components/Switch/Switch.astro'
2121
import Table from '@components/Table/Table.astro'
2222
import Tabs from '@components/Tabs/Tabs.astro'
23+
import Textarea from '@components/Textarea/Textarea.astro'
2324
import ThemeSwitcher from '@components/ThemeSwitcher/ThemeSwitcher.astro'
2425
import Timeline from '@components/Timeline/Timeline.astro'
2526
import TimelineItem from '@components/TimelineItem/TimelineItem.astro'
@@ -171,6 +172,9 @@ const tabItems = [{
171172
<CardWrapper title="Tabs" href="/tabs">
172173
<Tabs items={tabItems} theme="boxed" />
173174
</CardWrapper>
175+
<CardWrapper title="Textarea" href="/textarea">
176+
<Textarea placeholder="Get in touch with us" />
177+
</CardWrapper>
174178
<CardWrapper title="ThemeSwitcher" href="/theme-switcher">
175179
<ThemeSwitcher themes={themes} />
176180
</CardWrapper>

src/pages/textarea.astro

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
import Layout from '@static/Layout.astro'
3+
import ComponentWrapper from '@static/ComponentWrapper.astro'
4+
5+
import AstroTextarea from '@components/Textarea/Textarea.astro'
6+
import SvelteTextarea from '@components/Textarea/Textarea.svelte'
7+
import ReactTextarea from '@components/Textarea/Textarea.tsx'
8+
9+
import { getSections } from '@helpers'
10+
11+
const sections = getSections({
12+
title: 'textareas',
13+
components: [AstroTextarea, SvelteTextarea, ReactTextarea],
14+
showSubTitle: true
15+
})
16+
---
17+
18+
<Layout>
19+
<h1>Textarea</h1>
20+
<div class="grid md-2 lg-3">
21+
<ComponentWrapper type="Astro">
22+
<AstroTextarea value="Textarea in Astro" />
23+
</ComponentWrapper>
24+
25+
<ComponentWrapper type="Svelte">
26+
<SvelteTextarea value="Textarea in Svelte" />
27+
</ComponentWrapper>
28+
29+
<ComponentWrapper type="React">
30+
<ReactTextarea value="Textarea in React" />
31+
</ComponentWrapper>
32+
</div>
33+
34+
{sections.map(section => (
35+
<h1>{section.title}</h1>
36+
<Fragment>
37+
{section.subTitle && <h2 set:html={section.subTitle} />}
38+
</Fragment>
39+
<div class="grid md-2 lg-3">
40+
<ComponentWrapper title="Default">
41+
<section.component />
42+
</ComponentWrapper>
43+
44+
<ComponentWrapper title="With placeholder">
45+
<section.component placeholder="Enter your message here" />
46+
</ComponentWrapper>
47+
48+
<ComponentWrapper title="With label">
49+
<section.component
50+
label="Get in touch"
51+
placeholder="Enter your message here"
52+
/>
53+
</ComponentWrapper>
54+
55+
<ComponentWrapper title="With label & subtext">
56+
<section.component
57+
label="Get in touch"
58+
placeholder="Enter your message here"
59+
subText="For business inquiries, please reach out to <a href='#'>sales</a>."
60+
/>
61+
</ComponentWrapper>
62+
63+
<ComponentWrapper title="Disabled">
64+
<section.component
65+
label="Get in touch"
66+
placeholder="Enter your message here"
67+
subText="For business inquiries, please reach out to <a href='#'>sales</a>."
68+
disabled={true}
69+
value="Disabled textarea with predefined value"
70+
/>
71+
</ComponentWrapper>
72+
73+
<ComponentWrapper title="No resize">
74+
<section.component
75+
label="Get in touch"
76+
placeholder="Enter your message here"
77+
subText="For business inquiries, please reach out to <a href='#'>sales</a>."
78+
className="no-resize"
79+
/>
80+
</ComponentWrapper>
81+
</div>
82+
))}
83+
</Layout>
84+
85+
<style is:global>
86+
.no-resize {
87+
resize: none;
88+
}
89+
</style>

0 commit comments

Comments
 (0)