oneslash-design-system 1.1.31 → 1.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
interface TextareaProps {
|
|
5
|
+
id: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
value: string;
|
|
8
|
+
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
9
|
+
onBlur?: (e: React.FocusEvent<HTMLTextAreaElement>) => void;
|
|
10
|
+
onFocus?: (e: React.FocusEvent<HTMLTextAreaElement>) => void;
|
|
11
|
+
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
12
|
+
autoFocus?: boolean;
|
|
13
|
+
maxRows?: number;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
error?: boolean;
|
|
16
|
+
size?: 'large' | 'medium' | 'small';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function Textarea({
|
|
20
|
+
id,
|
|
21
|
+
label,
|
|
22
|
+
value,
|
|
23
|
+
onChange,
|
|
24
|
+
onBlur,
|
|
25
|
+
onFocus,
|
|
26
|
+
onKeyDown,
|
|
27
|
+
autoFocus = false,
|
|
28
|
+
maxRows = 6,
|
|
29
|
+
disabled = false,
|
|
30
|
+
error = false,
|
|
31
|
+
size = 'medium',
|
|
32
|
+
}: TextareaProps) {
|
|
33
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
34
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const textarea = textareaRef.current;
|
|
38
|
+
if (!textarea) return;
|
|
39
|
+
|
|
40
|
+
const adjustHeight = () => {
|
|
41
|
+
textarea.style.height = 'auto'; // Reset height to calculate scrollHeight
|
|
42
|
+
const lineHeight = parseInt(getComputedStyle(textarea).lineHeight);
|
|
43
|
+
const maxHeight = lineHeight * maxRows;
|
|
44
|
+
const scrollHeight = textarea.scrollHeight;
|
|
45
|
+
|
|
46
|
+
// Set height to scrollHeight, capped at maxRows, but at least 1 line
|
|
47
|
+
textarea.style.height = `${Math.max(Math.min(scrollHeight, maxHeight), lineHeight)}px`;
|
|
48
|
+
|
|
49
|
+
// Enable vertical scroll if content exceeds maxRows
|
|
50
|
+
textarea.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Set initial rows to 1 for single-line height
|
|
54
|
+
textarea.rows = 1;
|
|
55
|
+
|
|
56
|
+
adjustHeight();
|
|
57
|
+
textarea.addEventListener('input', adjustHeight);
|
|
58
|
+
|
|
59
|
+
return () => textarea.removeEventListener('input', adjustHeight);
|
|
60
|
+
}, [maxRows]);
|
|
61
|
+
|
|
62
|
+
// Define classes for size: text size and padding
|
|
63
|
+
const sizeClasses = {
|
|
64
|
+
large: 'text-body1 p-[7px] leading-[22px]', // body1 (16px), padding 8px(7 + border 1) height 40
|
|
65
|
+
medium: 'text-body1 p-[3px] leading-[22px]', // body1 (16px), padding 4px(3 + border 1), height 32
|
|
66
|
+
small: 'text-body2 p-[3px] leading-[18px]', // body2 (14px), padding 4px(3 + border 1), height 28
|
|
67
|
+
}[size];
|
|
68
|
+
|
|
69
|
+
const baseClasses = 'w-full border rounded-[8px]';
|
|
70
|
+
const bgColor = 'bg-light-background-default dark:bg-dark-background-default transition-colors duration-200 ease-in-out';
|
|
71
|
+
const borderColor = 'border-light-outlinedBorder-active dark:border-dark-outlinedBorder-active';
|
|
72
|
+
const containerClasses = `
|
|
73
|
+
${bgColor}
|
|
74
|
+
${borderColor}
|
|
75
|
+
${baseClasses}
|
|
76
|
+
${sizeClasses}
|
|
77
|
+
${disabled ? 'bg-gray-200 cursor-not-allowed' : ''}
|
|
78
|
+
${error ? 'border-red-500 focus:ring-red-500' : ''}
|
|
79
|
+
${isFocused ? 'focus:border-light-accent-main focus:dark:border-dark-accent-main outline-none' : ''}
|
|
80
|
+
${!disabled && !error ? 'hover:border-light-outlinedBorder-hover' : ''}
|
|
81
|
+
border-gray-300
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="flex flex-col w-full">
|
|
86
|
+
{label && (
|
|
87
|
+
<label htmlFor={id} className="mb-1 text-body2 text-light-text-secondary dark:text-dark-text-secondary">
|
|
88
|
+
{label}
|
|
89
|
+
</label>
|
|
90
|
+
)}
|
|
91
|
+
<div className="relative">
|
|
92
|
+
<textarea
|
|
93
|
+
ref={textareaRef}
|
|
94
|
+
id={id}
|
|
95
|
+
rows={1}
|
|
96
|
+
className={containerClasses}
|
|
97
|
+
value={value}
|
|
98
|
+
onChange={onChange}
|
|
99
|
+
onFocus={(e) => {
|
|
100
|
+
setIsFocused(true);
|
|
101
|
+
if (onFocus) onFocus(e);
|
|
102
|
+
}}
|
|
103
|
+
onBlur={(e) => {
|
|
104
|
+
setIsFocused(false);
|
|
105
|
+
if (onBlur) onBlur(e);
|
|
106
|
+
}}
|
|
107
|
+
onKeyDown={onKeyDown}
|
|
108
|
+
autoFocus={autoFocus}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
autoComplete="off"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
{error && (
|
|
114
|
+
<p className="mt-1 text-light-error-main text-body2">
|
|
115
|
+
This field is required
|
|
116
|
+
</p>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
package/components/userImage.tsx
CHANGED
|
@@ -6,57 +6,18 @@ interface UserImageProps {
|
|
|
6
6
|
userImgUrl?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
//read initials from userHandle
|
|
10
|
-
function getInitials(userHandle: string): string {
|
|
11
|
-
const words = userHandle.trim().split(/\s+/);
|
|
12
|
-
return words.slice(0, 2).map(word => word.charAt(0).toUpperCase()).join('');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getColorSeed(userHandle: string): string {
|
|
16
|
-
const words = userHandle.trim().split(/\s+/);
|
|
17
|
-
let letters = words.map(word => word.charAt(0).toLowerCase());
|
|
18
|
-
if (letters.length === 1 && words[0].length >= 2) {
|
|
19
|
-
letters.push(words[0].charAt(1).toLowerCase());
|
|
20
|
-
} else if (letters.length > 2) {
|
|
21
|
-
letters = letters.slice(0, 2);
|
|
22
|
-
} else if (letters.length === 0) {
|
|
23
|
-
letters = ['x', 'x'];
|
|
24
|
-
}
|
|
25
|
-
return letters.join('');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getHash(str: string): number {
|
|
29
|
-
let hash = 0;
|
|
30
|
-
for (let i = 0; i < str.length; i++) {
|
|
31
|
-
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
32
|
-
}
|
|
33
|
-
return hash;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
9
|
export default function UserImage({
|
|
37
10
|
userHandle,
|
|
38
11
|
userImgUrl,
|
|
39
12
|
}: UserImageProps) {
|
|
40
13
|
|
|
41
|
-
const
|
|
42
|
-
const seed = getColorSeed(userHandle);
|
|
43
|
-
const hue = Math.abs(getHash(seed)) % 360;
|
|
44
|
-
|
|
45
|
-
// Light mode: lighter pastel
|
|
46
|
-
const lightBg = `hsl(${hue}, 20%, 80%)`;
|
|
47
|
-
// Dark mode: darker variant
|
|
48
|
-
const darkBg = `hsl(${hue}, 20%, 30%)`;
|
|
14
|
+
const defaultInitial = userHandle.charAt(0).toUpperCase();
|
|
49
15
|
|
|
50
16
|
return (
|
|
51
|
-
<div
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} as React.CSSProperties}
|
|
56
|
-
className={`flex items-center justify-center w-6 h-6 rounded-full overflow-hidden
|
|
57
|
-
bg-[var(--light-bg)] dark:bg-[var(--dark-bg)]
|
|
58
|
-
text-light-text-secondary dark:text-dark-text-secondary`}
|
|
59
|
-
>
|
|
17
|
+
<div
|
|
18
|
+
className="flex items-center justify-center w-6 h-6 rounded-full overflow-hidden
|
|
19
|
+
bg-light-background-accent200 dark:bg-dark-background-accent200
|
|
20
|
+
text-light-text-secondary dark:text-dark-text-secondary ">
|
|
60
21
|
{userImgUrl ? (
|
|
61
22
|
<img
|
|
62
23
|
src={userImgUrl}
|
|
@@ -64,9 +25,9 @@ export default function UserImage({
|
|
|
64
25
|
className="w-full h-full object-cover rounded-full"
|
|
65
26
|
/>
|
|
66
27
|
) : (
|
|
67
|
-
<span className="text-body1
|
|
68
|
-
|
|
69
|
-
|
|
28
|
+
<span className="text-body1">
|
|
29
|
+
{defaultInitial}
|
|
30
|
+
</span>
|
|
70
31
|
)}
|
|
71
32
|
</div>
|
|
72
33
|
);
|
|
Binary file
|