Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const CSS = {
footerEndSlotOnly: "footer--end-only",
textArea: "text-area",
textAreaOnly: "text-area--only",
wrapper: "wrapper",
};

export const IDS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@ describe("calcite-text-area", () => {
shadowSelector: `.${CSS.textArea}::placeholder`,
targetProp: "color",
},
"--calcite-text-area-corner-radius": {
shadowSelector: `.${CSS.wrapper}`,
targetProp: "borderRadius",
},
"--calcite-text-area-shadow": {
shadowSelector: `.${CSS.wrapper}`,
targetProp: "boxShadow",
},
"--calcite-text-area-footer-background-color": {
shadowSelector: `.${CSS.footer}`,
targetProp: "backgroundColor",
},
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,28 @@
* @prop --calcite-text-area-min-width: Specifies the component's text area minimum width.
* @prop --calcite-text-area-text-color: Specifies the component's text color.
* @prop --calcite-text-area-footer-border-color: Specifies the footer's border color.
* @prop --calcite-text-area-corner-radius: Specifies component's corner radius.
* @prop --calcite-text-area-shadow: Specifies the component's shadow.
* @prop --calcite-text-area-footer-background-color: Specifies the footer's background color.

*/

:host {
@apply inline-block relative w-full h-full box-border;

--calcite-internal-text-area-border-color: var(--calcite-text-area-border-color, var(--calcite-color-border-input));
--calcite-internal-text-area-footer-border-color: var(
--calcite-text-area-footer-border-color,
var(--calcite-internal-text-area-border-color)
);
--calcite-internal-text-area-corner-radius: var(
--calcite-text-area-corner-radius,
var(--calcite-corner-radius-default)
);
--calcite-internal-text-area-shadow: var(--calcite-text-area-shadow, var(--calcite-shadow-none));
--calcite-internal-text-area-footer-background-color: var(
--calcite-text-area-footer-background-color,
var(--calcite-text-area-background-color, var(--calcite-color-foreground-1))
);

// avoids host to shrink beyond the textarea
min-block-size: var(
Expand All @@ -33,19 +45,27 @@
min-inline-size: var(--calcite-text-area-min-width, theme("spacing.48"));
}

.wrapper {
@apply h-full w-full box-border;
box-shadow: var(--calcite-internal-text-area-shadow);
border-radius: var(--calcite-internal-text-area-corner-radius);
}

.text-area,
.footer {
font-size: var(--calcite-text-area-font-size, var(--calcite-font-size--1));
background-color: var(--calcite-text-area-background-color, var(--calcite-color-foreground-1));
padding-block: var(--calcite-internal-text-area-padding-block);
padding-inline: var(--calcite-internal-text-area-padding-inline);
}

.footer {
background-color: var(--calcite-internal-text-area-footer-background-color);
border-radius: 0 0 var(--calcite-internal-text-area-corner-radius) var(--calcite-internal-text-area-corner-radius);
}

.text-area {
@apply relative font-sans block box-border w-full m-0;

--calcite-internal-text-area-border-block-end-color: var(--calcite-internal-text-area-border-color);

border: var(--calcite-border-width-sm) solid var(--calcite-internal-text-area-border-color);
border-block-end-color: var(--calcite-internal-text-area-border-block-end-color);
color: var(--calcite-text-area-text-color, var(--calcite-color-text-1));
Expand All @@ -57,7 +77,8 @@
);
max-inline-size: var(--calcite-text-area-max-width);
min-inline-size: var(--calcite-text-area-min-width, theme("spacing.48"));

background-color: var(--calcite-text-area-background-color, var(--calcite-color-foreground-1));
border-radius: var(--calcite-internal-text-area-corner-radius) var(--calcite-internal-text-area-corner-radius) 0 0;
&::placeholder {
@apply font-normal;
}
Expand All @@ -82,6 +103,10 @@
min-inline-size: theme("spacing.72");
}

&.text-area--only {
border-radius: var(--calcite-internal-text-area-corner-radius);
}

&:not(.text-area--only, .text-area--invalid) {
--calcite-internal-text-area-border-block-end-color: var(
--calcite-text-area-divider-color,
Expand Down Expand Up @@ -112,10 +137,14 @@
}

.readonly {
background-color: var(--calcite-color-background);
background-color: var(--calcite-text-area-background-color, var(--calcite-color-background));
font-weight: var(--calcite-font-weight-medium);
}

.footer.readonly {
background-color: var(--calcite-internal-text-area-footer-background-color, var(--calcite-color-background));
}

.content,
.hide {
@apply hidden;
Expand Down
146 changes: 74 additions & 72 deletions packages/calcite-components/src/components/text-area/text-area.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -468,82 +468,84 @@ export class TextArea
const hasFooter = this.startSlotHasElements || this.endSlotHasElements || !!this.maxLength;
return (
<InteractiveContainer disabled={this.disabled}>
<textarea
aria-describedby={this.guid}
aria-errormessage={IDS.validationMessage}
ariaInvalid={this.status === "invalid" || this.isCharacterLimitExceeded()}
ariaLabel={getLabelText(this)}
autofocus={this.el.autofocus}
class={{
[CSS.textArea]: true,
[CSS.readOnly]: this.readOnly,
[CSS.textAreaInvalid]: this.isCharacterLimitExceeded(),
[CSS.footerSlotted]: this.endSlotHasElements && this.startSlotHasElements,
[CSS.textAreaOnly]: !hasFooter,
}}
cols={this.columns}
disabled={this.disabled}
maxLength={this.limitText ? this.maxLength : undefined}
name={this.name}
onChange={this.handleChange}
onInput={this.handleInput}
placeholder={this.placeholder}
readOnly={this.readOnly}
ref={this.setTextAreaEl}
required={this.required}
rows={this.rows}
spellcheck={this.el.spellcheck}
value={this.value}
wrap={this.wrap}
/>
<span class={{ [CSS.content]: true }}>
<slot onSlotChange={this.contentSlotChangeHandler} />
</span>
<footer
class={{
[CSS.footer]: true,
[CSS.readOnly]: this.readOnly,
[CSS.hide]: !hasFooter,
}}
ref={this.footerEl}
>
<div
<div class={CSS.wrapper}>
<textarea
aria-describedby={this.guid}
aria-errormessage={IDS.validationMessage}
ariaInvalid={this.status === "invalid" || this.isCharacterLimitExceeded()}
ariaLabel={getLabelText(this)}
autofocus={this.el.autofocus}
class={{
[CSS.textArea]: true,
[CSS.readOnly]: this.readOnly,
[CSS.textAreaInvalid]: this.isCharacterLimitExceeded(),
[CSS.footerSlotted]: this.endSlotHasElements && this.startSlotHasElements,
[CSS.textAreaOnly]: !hasFooter,
}}
cols={this.columns}
disabled={this.disabled}
maxLength={this.limitText ? this.maxLength : undefined}
name={this.name}
onChange={this.handleChange}
onInput={this.handleInput}
placeholder={this.placeholder}
readOnly={this.readOnly}
ref={this.setTextAreaEl}
required={this.required}
rows={this.rows}
spellcheck={this.el.spellcheck}
value={this.value}
wrap={this.wrap}
/>
<span class={{ [CSS.content]: true }}>
<slot onSlotChange={this.contentSlotChangeHandler} />
</span>
<footer
class={{
[CSS.container]: true,
[CSS.footerEndSlotOnly]: !this.startSlotHasElements && this.endSlotHasElements,
[CSS.footer]: true,
[CSS.readOnly]: this.readOnly,
[CSS.hide]: !hasFooter,
}}
ref={this.footerEl}
>
<slot
name={SLOTS.footerStart}
onSlotChange={(event) =>
(this.startSlotHasElements = slotChangeHasAssignedElement(event))
}
/>
<slot
name={SLOTS.footerEnd}
onSlotChange={(event) =>
(this.endSlotHasElements = slotChangeHasAssignedElement(event))
}
<div
class={{
[CSS.container]: true,
[CSS.footerEndSlotOnly]: !this.startSlotHasElements && this.endSlotHasElements,
}}
>
<slot
name={SLOTS.footerStart}
onSlotChange={(event) =>
(this.startSlotHasElements = slotChangeHasAssignedElement(event))
}
/>
<slot
name={SLOTS.footerEnd}
onSlotChange={(event) =>
(this.endSlotHasElements = slotChangeHasAssignedElement(event))
}
/>
</div>
{this.renderCharacterLimit()}
</footer>
<HiddenFormInputSlot component={this} />
{this.isCharacterLimitExceeded() && (
<span ariaLive="polite" class={CSS.assistiveText} id={this.guid}>
{this.replacePlaceholdersInMessages()}
</span>
)}
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
ref={this.setValidationRef}
scale={this.scale}
status={this.status}
/>
</div>
{this.renderCharacterLimit()}
</footer>
<HiddenFormInputSlot component={this} />
{this.isCharacterLimitExceeded() && (
<span ariaLive="polite" class={CSS.assistiveText} id={this.guid}>
{this.replacePlaceholdersInMessages()}
</span>
)}
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
ref={this.setValidationRef}
scale={this.scale}
status={this.status}
/>
) : null}
) : null}
</div>
</InteractiveContainer>
);
}
Expand Down
46 changes: 45 additions & 1 deletion packages/calcite-components/src/demos/text-area.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
--calcite-text-area-divider-color: yellow;
--calcite-text-area-text-color: brown;
--calcite-text-area-footer-border-color: orange;
--calcite-text-area-corner-radius: 12px;
}

.themed-realistic {
--calcite-text-area-border-color: transparent;
--calcite-text-area-divider-color: transparent;
--calcite-text-area-footer-background-color: var(--calcite-color-foreground-1);
--calcite-text-area-shadow: var(--calcite-shadow-sm);
--calcite-text-area-corner-radius: 4px;
}

.parent {
Expand Down Expand Up @@ -278,7 +287,7 @@
<div class="parent">
<div class="child-text">Custom Themed</div>
<div class="child">
<calcite-text-area placeholder="themed..." max-length="10" class="themed" scale="s"></calcite-text-area>
<calcite-text-area placeholder="themed..." class="themed" scale="s"></calcite-text-area>
</div>
<div class="child">
<calcite-text-area placeholder="themed..." max-length="10" class="themed" scale="m"></calcite-text-area>
Expand All @@ -288,6 +297,41 @@
</div>
</div>

<div class="parent">
<div class="child-text">Realistic Themed</div>
<div class="child">
<calcite-text-area placeholder="themed..." class="themed-realistic" scale="s">
<div slot="footer-start">
<calcite-button scale="s" kind="neutral" icon-start="layers" round></calcite-button>
<calcite-button scale="s" kind="neutral" icon-start="map" round></calcite-button>
</div>
<calcite-button scale="s" icon-end="arrow-right" round slot="footer-end"
>Ask</calcite-button
></calcite-text-area
>
</div>
<div class="child">
<calcite-text-area read-only placeholder="themed..." class="themed-realistic" scale="m">
<div slot="footer-start">
<calcite-button kind="neutral" icon-start="layers" round></calcite-button>
<calcite-button kind="neutral" icon-start="map" round></calcite-button>
</div>
<calcite-button icon-end="arrow-right" round slot="footer-end">Ask</calcite-button></calcite-text-area
>
</div>
<div class="child">
<calcite-text-area placeholder="themed..." class="themed-realistic" scale="l">
<div slot="footer-start">
<calcite-button scale="l" kind="neutral" icon-start="layers" round></calcite-button>
<calcite-button scale="l" kind="neutral" icon-start="map" round></calcite-button>
</div>
<calcite-button scale="l" icon-end="arrow-right" round slot="footer-end"
>Ask</calcite-button
></calcite-text-area
>
</div>
</div>

<div class="parent">
<div class="child-text">With Slotted at footer-start</div>
<div class="child">
Expand Down
Loading