feat: TextInput/TextArea borderless+ariaLabel+onKeyDown, Alert info, иконка info (0.11.0)
Для инлайн-ячеек таблиц СТ и info-баннеров при перекраске форм. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@tnsu/ui-kit",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.0",
|
||||
"description": "TNSU shared UI components for SPFx web parts",
|
||||
"author": "Timur Gorodetskiy",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -28,3 +28,9 @@
|
||||
color: $red-500;
|
||||
border-color: #fecaca;
|
||||
}
|
||||
|
||||
.info {
|
||||
background: $navy-50;
|
||||
color: $navy-800;
|
||||
border-color: $navy-100;
|
||||
}
|
||||
|
||||
+5
-3
@@ -3,7 +3,7 @@ import styles from './Alert.module.scss';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { IconName } from '../Icon/iconNames';
|
||||
|
||||
type AlertVariant = 'warn' | 'ok' | 'err';
|
||||
type AlertVariant = 'warn' | 'ok' | 'err' | 'info';
|
||||
|
||||
interface IAlertProps {
|
||||
variant: AlertVariant;
|
||||
@@ -13,13 +13,15 @@ interface IAlertProps {
|
||||
const variantClass: Record<AlertVariant, string> = {
|
||||
warn: styles.warn,
|
||||
ok: styles.ok,
|
||||
err: styles.err
|
||||
err: styles.err,
|
||||
info: styles.info
|
||||
};
|
||||
|
||||
const variantIcon: Record<AlertVariant, IconName> = {
|
||||
warn: 'warning',
|
||||
ok: 'check',
|
||||
err: 'close'
|
||||
err: 'close',
|
||||
info: 'info'
|
||||
};
|
||||
|
||||
export const Alert: React.FC<IAlertProps> = ({ variant, children }) => {
|
||||
|
||||
@@ -147,6 +147,10 @@ export const IconSprite: React.FC = () => (
|
||||
<path d="M12 2 1 21h22z" strokeLinejoin="round" />
|
||||
<path d="M12 9v5M12 17h.01" />
|
||||
</symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 11v5M12 7.5h.01" />
|
||||
</symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24">
|
||||
<path d="M5 12l5 5L20 7" />
|
||||
</symbol>
|
||||
|
||||
@@ -35,6 +35,7 @@ export const ICON_NAMES = [
|
||||
'search',
|
||||
'menu',
|
||||
'warning',
|
||||
'info',
|
||||
'check',
|
||||
'plus'
|
||||
] as const;
|
||||
|
||||
@@ -21,3 +21,17 @@
|
||||
color: $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
// Инлайн-ввод в ячейке таблицы: без рамки/фона, компактный, без ручного ресайза.
|
||||
.borderless {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
padding: 6px 8px;
|
||||
min-height: 32px;
|
||||
resize: none;
|
||||
|
||||
&:focus {
|
||||
background: $white;
|
||||
border-color: $gold-500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,20 +10,25 @@ interface ITextAreaProps {
|
||||
rows?: number;
|
||||
/** Только чтение (режим согласующего). */
|
||||
readOnly?: boolean;
|
||||
/** Без рамки/фона — для инлайн-ввода в ячейках таблиц. */
|
||||
borderless?: boolean;
|
||||
/** a11y-подпись, когда видимого label нет. */
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
/** Многострочный ввод (SharePoint Note). */
|
||||
export const TextArea: React.FC<ITextAreaProps> = ({ id, value, onChange, placeholder, rows = 3, readOnly }) => {
|
||||
export const TextArea: React.FC<ITextAreaProps> = ({ id, value, onChange, placeholder, rows = 3, readOnly, borderless, ariaLabel }) => {
|
||||
const contextReadOnly = useFormReadOnly();
|
||||
const effectiveReadOnly = readOnly ?? contextReadOnly;
|
||||
return (
|
||||
<textarea
|
||||
id={id}
|
||||
className={styles.textarea}
|
||||
className={[styles.textarea, borderless ? styles.borderless : ''].filter(Boolean).join(' ')}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
rows={rows}
|
||||
readOnly={effectiveReadOnly}
|
||||
aria-label={ariaLabel}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -19,3 +19,16 @@
|
||||
color: $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
// Инлайн-ввод в ячейке таблицы: без рамки/фона, компактнее; рамка/фон проявляются на фокусе.
|
||||
.borderless {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus {
|
||||
background: $white;
|
||||
border-color: $gold-500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ interface ITextInputProps {
|
||||
*/
|
||||
min?: string | number;
|
||||
max?: string | number;
|
||||
/** Без рамки/фона — для инлайн-ввода в ячейках таблиц (рамка проявляется на фокусе). */
|
||||
borderless?: boolean;
|
||||
/** a11y-подпись, когда видимого label нет (ячейки таблиц). */
|
||||
ariaLabel?: string;
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
/** Однострочный ввод. type='number'|'date' — для числовых полей и дат. */
|
||||
@@ -30,20 +36,27 @@ export const TextInput: React.FC<ITextInputProps> = ({
|
||||
placeholder,
|
||||
readOnly,
|
||||
min,
|
||||
max
|
||||
max,
|
||||
borderless,
|
||||
ariaLabel,
|
||||
onKeyDown,
|
||||
autoFocus
|
||||
}) => {
|
||||
const contextReadOnly = useFormReadOnly();
|
||||
const effectiveReadOnly = readOnly ?? contextReadOnly;
|
||||
return (
|
||||
<input
|
||||
id={id}
|
||||
className={styles.input}
|
||||
className={[styles.input, borderless ? styles.borderless : ''].filter(Boolean).join(' ')}
|
||||
type={type}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
readOnly={effectiveReadOnly}
|
||||
aria-label={ariaLabel}
|
||||
min={min}
|
||||
max={max}
|
||||
autoFocus={autoFocus}
|
||||
onKeyDown={onKeyDown}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user