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
5 changes: 5 additions & 0 deletions .changeset/rude-forks-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@radui/ui": minor
---

Addition of color and radius api support in TextArea and new styling
12 changes: 7 additions & 5 deletions src/components/ui/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
'use client';
import React from 'react';
import clsx from 'clsx';
import TextAreaRoot from './fragments/TextAreaRoot';
import TextAreaInput from './fragments/TextAreaInput';
import TextAreaRoot, { TextAreaRootProps } from './fragments/TextAreaRoot';
import TextAreaInput, { TextAreaInputProps } from './fragments/TextAreaInput';

export type TextAreaProps = React.ComponentPropsWithoutRef<'div'> & {
export type TextAreaProps = React.ComponentPropsWithoutRef<'div'> & TextAreaRootProps & TextAreaInputProps & {
customRootClass?: string;
readonly ?: boolean;
disabled ?: boolean;
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove extra spaces before the optional type indicators.

The readonly and disabled props have unnecessary spaces before the ? operator.

🔎 Proposed fix
-    readonly ?: boolean;
-    disabled ?: boolean;
+    readonly?: boolean;
+    disabled?: boolean;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
readonly ?: boolean;
disabled ?: boolean;
readonly?: boolean;
disabled?: boolean;
🤖 Prompt for AI Agents
In src/components/ui/TextArea/TextArea.tsx around lines 9 to 10, the prop
declarations use an extra space before the optional operator (`readonly ?:
boolean;` and `disabled ?: boolean;`); remove the space so the declarations read
`readonly?: boolean;` and `disabled?: boolean;` to match TypeScript syntax and
project linting.

};

type TextAreaComponent = React.ForwardRefExoticComponent<TextAreaProps & React.RefAttributes<React.ElementRef<'div'>>> & {
Input: typeof TextAreaInput;
Root: typeof TextAreaRoot;
};

const TextArea = React.forwardRef<React.ElementRef<'div'>, TextAreaProps>(({ customRootClass = '', className = '', children, ...props }, ref) => {
const TextArea = React.forwardRef<React.ElementRef<'div'>, TextAreaProps>(({ customRootClass = '', placeholder = '', className = '', disabled = false, readonly = false, children, ...props }, ref) => {
return (
<TextAreaRoot ref={ref} customRootClass={customRootClass} className={clsx(className)} {...props}>
<TextAreaInput placeholder="enter text">
<TextAreaInput placeholder={placeholder} disabled={disabled} readOnly={readonly}>
{children}
</TextAreaInput>
{children}
Expand Down
12 changes: 8 additions & 4 deletions src/components/ui/TextArea/fragments/TextAreaRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { customClassSwitcher } from '~/core';
import clsx from 'clsx';
import { useCreateDataAttribute } from '~/core/hooks/createDataAttribute';
import { useCreateDataAttribute, useComposeAttributes, useCreateDataAccentColorAttribute } from '~/core/hooks/createDataAttribute';

const COMPONENT_NAME = 'TextArea';

Expand All @@ -10,15 +10,19 @@ export type TextAreaRootProps = React.ComponentPropsWithoutRef<'div'> & {
variant?: string;
size?: string;
resize?: 'none' | 'vertical' | 'horizontal' | 'both';
color?: string;
radius?: string;
};

const TextAreaRoot = React.forwardRef<React.ElementRef<'div'>, TextAreaRootProps>(
({ children, customRootClass = '', className = '', variant = '', size = '', resize = 'both', ...props }, ref) => {
({ children, customRootClass = '', className = '', variant = '', size = '', resize = 'both', color = '', radius = '', ...props }, ref) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);
const dataAttributes = useCreateDataAttribute('textarea', { variant, size, resize });
const dataAttributes = useCreateDataAttribute('textarea', { variant, size, resize, radius});
const accentAttributes = useCreateDataAccentColorAttribute(color);
const composedAttributes = useComposeAttributes(dataAttributes(), accentAttributes());

return (
<div ref={ref} className={clsx(rootClass, className)} {...props} {...dataAttributes()}>
<div ref={ref} className={clsx(rootClass, className)} {...props} {...composedAttributes()}>
{children}
</div>
);
Expand Down
41 changes: 41 additions & 0 deletions src/components/ui/TextArea/stories/TextArea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ import React from 'react';
import TextArea from '../TextArea';
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';

const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
const Radius = ['none', 'small', 'medium', 'large'];
Comment on lines +5 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove undefined from the Resizes array or handle it separately.

Including undefined in the Resizes array will cause a React key warning when the array is mapped in the Resize story (line 55), since undefined cannot be used as a valid key.

🔎 Proposed fix
-const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
+const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both'> = ['none', 'vertical', 'horizontal', 'both'];

If you need to test the default/undefined behavior, create a separate story instance rather than including it in the mapped array.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
const Radius = ['none', 'small', 'medium', 'large'];
const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both'> = ['none', 'vertical', 'horizontal', 'both'];
const Radius = ['none', 'small', 'medium', 'large'];
🤖 Prompt for AI Agents
In src/components/ui/TextArea/stories/TextArea.stories.tsx around lines 5 to 8,
the Resizes array includes undefined which will produce React key warnings when
mapped; remove undefined from the Resizes array and, if you need to demonstrate
the default/undefined behavior, create a separate story instance (or add a
specific case) that renders the TextArea without a resize prop instead of
including undefined in the mapped list.

const Template = (args:any) => {
return <SandboxEditor className="space-y-4 pt-4">
<TextArea {...args} >

</TextArea>

<TextArea disabled placeholder='this textarea is disabled'/>


<TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify the className 'w-10' for the readonly textarea.

The className='w-10' applies a width of 2.5rem (40px), which is extremely narrow for a textarea. This appears to be a typo.

Consider removing the className or using w-full for full width:

🔎 Proposed fix
-        <TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
+        <TextArea readonly placeholder='this textarea is readonly'/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
<TextArea readonly placeholder='this textarea is readonly'/>
🤖 Prompt for AI Agents
In src/components/ui/TextArea/stories/TextArea.stories.tsx around line 18, the
readonly TextArea is given className='w-10' which sets a very narrow width
(2.5rem) likely by typo; remove the className or replace it with a more
appropriate utility such as 'w-full' (or no className) so the readonly textarea
renders at a usable width.

</SandboxEditor>;
};

Expand All @@ -23,3 +32,35 @@ export const All = {
placeholder: 'Type something here'
}
};

export const Size = () =>{
return <SandboxEditor className="space-y-4 pt-4">
{Sizes.map((size) => (
<TextArea key={size} size={size} placeholder={size} />
))}
</SandboxEditor>;
}

export const Variant = () =>{
return <SandboxEditor className="space-y-4 pt-4">
{Variants.map((variant) => (
<TextArea key={variant} variant={variant} placeholder={variant} />
))}
</SandboxEditor>;
}

export const Resize = () =>{
return <SandboxEditor className="space-y-4 pt-4">
{Resizes.map((resize) => (
<TextArea key={resize} resize={resize} placeholder={resize} />
))}
</SandboxEditor>;
}

export const TextAreaRadius = () =>{
return <SandboxEditor className="space-y-4 pt-4">
{Radius.map((radius) => (
<TextArea key={radius} radius={radius} placeholder={radius} />
))}
</SandboxEditor>;
}
177 changes: 142 additions & 35 deletions styles/themes/components/textarea.scss
Original file line number Diff line number Diff line change
@@ -1,36 +1,143 @@
.rad-ui-text-area {
textarea {
border: 2px solid var(--rad-ui-color-accent-600);
font-size: 16px;
padding: 8px;
line-height: 1.5;
border-radius: 8px;
color: var(--rad-ui-color-gray-1000);
min-height: 100px;
background-color: var(--rad-ui-color-accent-100);

&::placeholder {
color: var(--rad-ui-color-accent-500);
}

&:focus {
outline: 2px solid var(--rad-ui-color-accent-500);
}
}

&[data-textarea-resize="horizontal"] textarea {
resize: horizontal;
}

&[data-textarea-resize="vertical"] textarea {
resize: vertical;
}

&[data-textarea-resize="none"] textarea {
resize: none;
}

&[data-textarea-resize="both"] textarea {
resize: both;
}
.rad-ui-text-area textarea {
display: block;
width: 100%;
min-height: 100px;

box-sizing: border-box;
resize: none;

font-family: inherit;
font-size: 16px;
line-height: 1.5;

padding: 10px 12px;
border-radius: 8px;

color: var(--rad-ui-color-accent-1000);
background-color: var(--rad-ui-color-accent-100);

border: 2px solid var(--rad-ui-color-accent-400);

transition:
border-color 0.15s ease,
box-shadow 0.15s ease,
background-color 0.15s ease;
}

.rad-ui-text-area textarea::placeholder {
color: var(--rad-ui-color-accent-500);
}

.rad-ui-text-area textarea:focus {
outline: none;
border-color: var(--rad-ui-color-accent-500);
box-shadow: 0 0 0 2px var(--rad-ui-color-accent-200);
}


.rad-ui-text-area textarea::-webkit-scrollbar {
width: 10px;
}

.rad-ui-text-area textarea::-webkit-scrollbar-track {
background: transparent;
}

.rad-ui-text-area textarea::-webkit-scrollbar-thumb {
background-color: var(--rad-ui-color-accent-400);
border-radius: 6px;
border: 3px solid transparent;
background-clip: content-box;
}

.rad-ui-text-area textarea::-webkit-scrollbar-thumb:hover {
background-color: var(--rad-ui-color-accent-500);
}


.rad-ui-text-area[data-textarea-resize="horizontal"] textarea {
resize: horizontal;
}

.rad-ui-text-area[data-textarea-resize="vertical"] textarea {
resize: vertical;
}

.rad-ui-text-area[data-textarea-resize="none"] textarea {
resize: none;
}

.rad-ui-text-area[data-textarea-resize="both"] textarea {
resize: both;
}

.rad-ui-text-area[data-textarea-radius="none"] textarea {
border-radius: 0;
}

.rad-ui-text-area[data-textarea-radius="small"] textarea {
border-radius: 4px;
}

.rad-ui-text-area[data-textarea-radius="medium"] textarea {
border-radius: 8px;
}

.rad-ui-text-area[data-textarea-radius="large"] textarea {
border-radius: 12px;
}

.rad-ui-text-area[data-textarea-radius="full"] textarea {
border-radius: 16px;
}

.rad-ui-text-area[data-textarea-size="small"] textarea {
font-size: 12px;
}

.rad-ui-text-area[data-textarea-size="medium"] textarea {
font-size: 14px;
}

.rad-ui-text-area[data-textarea-size="large"] textarea {
font-size: 16px;
}

.rad-ui-text-area[data-textarea-variant="soft"] textarea {
border: none;
background-color: var(--rad-ui-color-accent-200);
color: var(--rad-ui-color-accent-1000);
}

.rad-ui-text-area[data-textarea-variant="outline"] textarea {
background-color: transparent;
border: 2px solid var(--rad-ui-color-accent-400);
}

.rad-ui-text-area[data-textarea-variant="solid"] textarea {
background-color: var(--rad-ui-color-accent-100);
border: 2px solid var(--rad-ui-color-accent-400);
}


.rad-ui-text-area[data-textarea-variant="ghost"] textarea {
background-color: transparent;
border: 2px solid transparent;
}

.rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus {
border-color: var(--rad-ui-color-accent-500);
}


.rad-ui-text-area textarea:disabled,
.rad-ui-text-area textarea:read-only {
background-color: var(--rad-ui-color-accent-200);
color: var(--rad-ui-color-accent-600);
cursor: not-allowed;
}

.rad-ui-text-area textarea:disabled::placeholder,
.rad-ui-text-area textarea:read-only::placeholder {
color: var(--rad-ui-color-accent-500);
}
Comment on lines +133 to 143
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix CSS specificity ordering to resolve pipeline failures.

The linter reports that .rad-ui-text-area textarea:disabled and .rad-ui-text-area textarea:read-only selectors should come before .rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus to maintain proper specificity ordering.

🔎 Proposed fix

Move the disabled and read-only styles before the ghost variant styles:

 .rad-ui-text-area[data-textarea-variant="solid"] textarea {
   background-color: var(--rad-ui-color-accent-100);
   border: 2px solid var(--rad-ui-color-accent-400);
 }

+.rad-ui-text-area textarea:disabled,
+.rad-ui-text-area textarea:read-only {
+  background-color: var(--rad-ui-color-accent-200);
+  color: var(--rad-ui-color-accent-600);
+  cursor: not-allowed;
+}
+
+.rad-ui-text-area textarea:disabled::placeholder,
+.rad-ui-text-area textarea:read-only::placeholder {
+  color: var(--rad-ui-color-accent-500);
+}

 .rad-ui-text-area[data-textarea-variant="ghost"] textarea {
   background-color: transparent;
   border: 2px solid transparent;
 }

 .rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus {
   border-color: var(--rad-ui-color-accent-500);
 }

-.rad-ui-text-area textarea:disabled,
-.rad-ui-text-area textarea:read-only {
-  background-color: var(--rad-ui-color-accent-200);
-  color: var(--rad-ui-color-accent-600);
-  cursor: not-allowed;
-}
-
-.rad-ui-text-area textarea:disabled::placeholder,
-.rad-ui-text-area textarea:read-only::placeholder {
-  color: var(--rad-ui-color-accent-500);
-}

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: Lint

[error] 133-133: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:disabled" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".


[error] 134-134: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:read-only" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".

🤖 Prompt for AI Agents
In styles/themes/components/textarea.scss around lines 133 to 143, the
disabled/read-only selector block is placed after higher-specificity
ghost-variant focus rules which breaks the linter's specificity ordering; move
the entire block for `.rad-ui-text-area textarea:disabled, .rad-ui-text-area
textarea:read-only { ... }` and its placeholder rules immediately before the
`.rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus` rules
(preserving all property names and values) so disabled/read-only rules appear
earlier than the ghost-variant focus selectors to satisfy specificity ordering.

Loading