Skip to content

Commit 78f97e5

Browse files
authored
Merge pull request #554 from upfluence/ap/dra-3516
Smart Textarea: Add new component
2 parents 1a71c18 + 729b072 commit 78f97e5

File tree

12 files changed

+431
-9
lines changed

12 files changed

+431
-9
lines changed

addon/components/o-s-s/smart/tag-input.hbs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
</div>
1111
{{else}}
1212
<div class="fx-col">
13-
<span class="hidden-span">
14-
{{if (or this.inputValue this.isInputFocused) this.inputValue this.placeholder}}
15-
</span>
13+
<span class="hidden-span">{{this.hiddenSpanValue}}</span>
1614
<Input
1715
class="displayed-input"
1816
@value={{this.inputValue}}

addon/components/o-s-s/smart/tag-input.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export default class OSSSmartTagInput extends Component<OSSSmartTagInputArgs> {
4545
return this.args.placeholder ?? this.intl.t('oss-components.smart.tag_input.placeholder');
4646
}
4747

48+
get hiddenSpanValue(): string {
49+
return this.inputValue || this.isInputFocused ? this.inputValue : this.placeholder;
50+
}
51+
4852
@action
4953
registerElement(element: HTMLElement): void {
5054
this.element = element;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<div
2+
class="oss-smart-text-area-container {{if @hasError ' oss-smart-text-area-container--errored'}}"
3+
{{did-insert this.registerElement}}
4+
{{did-update this.handleUpdate @loading}}
5+
...attributes
6+
>
7+
<div class="fx-row">
8+
{{#if @loading}}
9+
<div class="upf-input loading-placeholder fx-row fx-xalign-center">
10+
<pre class="smart_text_animated">{{or @value @placeholder}}</pre>
11+
</div>
12+
{{/if}}
13+
<Textarea
14+
@value={{@value}}
15+
placeholder={{@placeholder}}
16+
disabled={{@disabled}}
17+
rows={{this.rows}}
18+
class="oss-textarea {{this.computedClass}}"
19+
{{on "keyup" (fn this._onChange @value)}}
20+
/>
21+
</div>
22+
23+
</div>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { hbs } from 'ember-cli-htmlbars';
2+
import { action } from '@storybook/addon-actions';
3+
4+
const ResizeTypes = ['horizontal', 'vertical', 'none', null];
5+
6+
export default {
7+
title: 'Components/OSS::Smart::TextArea',
8+
component: 'smart text-area',
9+
argTypes: {
10+
value: {
11+
description: 'Value of the textarea',
12+
table: {
13+
type: {
14+
summary: 'string'
15+
},
16+
defaultValue: { summary: 'undefined' }
17+
},
18+
control: { type: 'text' }
19+
},
20+
rows: {
21+
description: 'Number of rows dispayed in the textarea',
22+
table: {
23+
type: {
24+
summary: 'number'
25+
},
26+
defaultValue: { summary: 2 }
27+
},
28+
control: { type: 'number' }
29+
},
30+
resize: {
31+
description: 'Define direction in which textarea can be resized (By default the resize is set to Both)',
32+
table: {
33+
type: {
34+
summary: ResizeTypes.join('|')
35+
},
36+
defaultValue: { summary: 'both' }
37+
},
38+
options: ResizeTypes,
39+
control: { type: 'select' }
40+
},
41+
disabled: {
42+
description: 'Disable the default textarea',
43+
table: {
44+
type: {
45+
summary: 'boolean'
46+
},
47+
defaultValue: { summary: false }
48+
},
49+
control: { type: 'boolean' }
50+
},
51+
placeholder: {
52+
description: 'Placeholder of the textarea',
53+
table: {
54+
type: {
55+
summary: 'string'
56+
},
57+
defaultValue: { summary: 'undefined' }
58+
},
59+
control: { type: 'text' }
60+
},
61+
loading: {
62+
description: 'Indicates if the textarea is in a loading state',
63+
table: {
64+
type: {
65+
summary: 'boolean'
66+
},
67+
defaultValue: { summary: false }
68+
},
69+
control: { type: 'boolean' }
70+
},
71+
hasError: {
72+
description: 'Indicates if the textarea has an error',
73+
table: {
74+
type: {
75+
summary: 'boolean'
76+
},
77+
defaultValue: { summary: false }
78+
},
79+
control: { type: 'boolean' }
80+
},
81+
onChange: {
82+
description: 'Method called every time the textarea is updated',
83+
table: {
84+
category: 'Actions',
85+
type: {
86+
summary: 'onChange(value: string): void'
87+
}
88+
}
89+
}
90+
},
91+
parameters: {
92+
docs: {
93+
description: {
94+
component: 'The OSS version of the textarea component.'
95+
}
96+
}
97+
}
98+
};
99+
100+
const defaultArgs = {
101+
value: 'John',
102+
rows: 2,
103+
resize: null,
104+
disabled: false,
105+
placeholder: 'this is the placeholder',
106+
hasError: false,
107+
loading: false,
108+
onChange: action('onChange')
109+
};
110+
111+
const DefaultUsageTemplate = (args) => ({
112+
template: hbs`
113+
<OSS::Smart::TextArea @value={{this.value}} @disabled={{this.disabled}} @placeholder={{this.placeholder}}
114+
@hasError={{this.hasError}} @onChange={{this.onChange}} @rows={{this.rows}}
115+
@resize={{this.resize}} @loading={{this.loading}}/>
116+
`,
117+
context: args
118+
});
119+
120+
export const BasicUsage = DefaultUsageTemplate.bind({});
121+
BasicUsage.args = defaultArgs;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { action } from '@ember/object';
2+
import { isEmpty } from '@ember/utils';
3+
import { tracked } from '@glimmer/tracking';
4+
import { runSmartGradientAnimation } from '@upfluence/oss-components/utils/run-smart-gradient-animation';
5+
import type { OSSTextAreaArgs, textAreaResizeOptions } from '../text-area';
6+
import OSSTextArea from '../text-area';
7+
8+
interface OSSSmartImmersiveInputComponentSignature extends OSSTextAreaArgs {
9+
value: string;
10+
loading: boolean;
11+
hasError?: boolean;
12+
}
13+
14+
export default class OSSSmartTextareaComponent extends OSSTextArea<OSSSmartImmersiveInputComponentSignature> {
15+
@tracked declare element: HTMLElement;
16+
@tracked declare textAreaElement: HTMLTextAreaElement;
17+
18+
@action
19+
handleUpdate(): void {
20+
if (!this.args.loading && !isEmpty(this.args.value)) {
21+
runSmartGradientAnimation(this.element);
22+
}
23+
}
24+
25+
@action
26+
registerElement(element: HTMLElement): void {
27+
this.element = element;
28+
}
29+
30+
get resize(): textAreaResizeOptions | undefined {
31+
return this.args.resize ?? 'none';
32+
}
33+
34+
get computedClass(): string {
35+
let computedClass = super.computedClass;
36+
37+
if (this.args.loading) {
38+
computedClass += ' oss-textarea--loading';
39+
}
40+
41+
return computedClass;
42+
}
43+
}

addon/components/o-s-s/text-area.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ import { assert } from '@ember/debug';
22
import { action } from '@ember/object';
33
import Component from '@glimmer/component';
44

5-
interface OSSTextAreaArgs {
5+
export type textAreaResizeOptions = 'vertical' | 'horizontal' | 'none';
6+
7+
export interface OSSTextAreaArgs {
68
rows?: number;
7-
resize?: 'vertical' | 'horizontal' | 'none';
9+
resize?: textAreaResizeOptions;
810
value?: string;
911
disabled?: boolean;
1012
errorMessage?: string;
1113
placeholder?: string;
1214
onChange?(value: string): void;
1315
}
1416

15-
export default class OSSTextArea extends Component<OSSTextAreaArgs> {
17+
export default class OSSTextArea<T extends OSSTextAreaArgs> extends Component<T> {
1618
constructor(owner: unknown, args: OSSTextAreaArgs) {
1719
super(owner, args);
1820

@@ -28,11 +30,15 @@ export default class OSSTextArea extends Component<OSSTextAreaArgs> {
2830
return this.args.rows || 2;
2931
}
3032

33+
get resize(): textAreaResizeOptions | undefined {
34+
return this.args.resize;
35+
}
36+
3137
get computedClass(): string {
3238
const classes: string[] = [];
33-
if (this.args.resize === 'vertical') classes.push('oss-textarea--resize-v');
34-
else if (this.args.resize === 'horizontal') classes.push('oss-textarea--resize-h');
35-
else if (this.args.resize === 'none') classes.push('oss-textarea--resize-none');
39+
if (this.resize === 'vertical') classes.push('oss-textarea--resize-v');
40+
else if (this.resize === 'horizontal') classes.push('oss-textarea--resize-h');
41+
else if (this.resize === 'none') classes.push('oss-textarea--resize-none');
3642

3743
return classes.join(' ');
3844
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from '@upfluence/oss-components/components/o-s-s/smart/text-area';
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.oss-smart-text-area-container {
2+
.oss-textarea-container;
3+
flex: 1;
4+
5+
> div {
6+
position: relative;
7+
}
8+
9+
.loading-placeholder {
10+
position: absolute;
11+
height: 100%;
12+
width: 100%;
13+
pointer-events: none;
14+
display: flex;
15+
align-items: flex-start;
16+
flex-direction: column;
17+
}
18+
19+
.smart_text_animated {
20+
background: var(--color-gray-400);
21+
background: linear-gradient(
22+
130deg,
23+
var(--color-gray-400) 18%,
24+
var(--color-gray-300) 25%,
25+
rgba(250, 198, 255, 1) 56%,
26+
var(--color-primary-100) 62%,
27+
var(--color-white) 66%,
28+
rgba(250, 198, 255, 0.58) 68%,
29+
var(--color-gray-300) 73%,
30+
var(--color-gray-400) 85%
31+
);
32+
-webkit-background-clip: text;
33+
background-clip: text;
34+
color: transparent;
35+
animation: smart_loading_text_animation 3.5s ease-in-out infinite;
36+
background-size: 600% 100%;
37+
padding: var(--spacing-px-6) 0px;
38+
39+
border: 0;
40+
font-family: var(--font-family-stack);
41+
.font-size-sm;
42+
}
43+
44+
.oss-textarea {
45+
transition: all 0s;
46+
}
47+
48+
.oss-textarea--loading {
49+
visibility: hidden;
50+
}
51+
}

app/styles/oss-components.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
@import 'atoms/smart/logo';
5757
@import 'atoms/smart/tag';
5858
@import 'atoms/smart/tag-input';
59+
@import 'atoms/smart-textarea';
5960
@import 'molecules/progress-bar';
6061
@import 'molecules/select';
6162
@import 'molecules/nav-tab';

tests/dummy/app/controllers/smart.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export default class Smart extends Controller {
5959
];
6060
@tracked smartTags: { value: string; type: TagType }[] = [];
6161
@tracked inputValue: string = '';
62+
@tracked textAreaValue: string = '';
63+
@tracked multilinePlaceholder: string = 'Small placeholder\nwith multiple\nlines';
6264

6365
constructor() {
6466
super(...arguments);
@@ -190,6 +192,12 @@ export default class Smart extends Controller {
190192
return '';
191193
}
192194

195+
@action
196+
onTextAreaChange(value: string): void {
197+
console.log('Text area value changed:', value);
198+
this.textAreaValue = value;
199+
}
200+
193201
@action
194202
fakeLoadData(): void {
195203
this.loading = true;

0 commit comments

Comments
 (0)