You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.1 KiB
168 lines
4.1 KiB
2 years ago
|
import styles from './Text.module.scss';
|
||
|
|
||
|
import { openNotification } from '../../utils';
|
||
|
|
||
|
import { IconButton, Modal, Input, HorizontalGroup, Button, VerticalGroup } from '@grafana/ui';
|
||
|
|
||
|
import cn from 'classnames/bind';
|
||
|
|
||
|
import CopyToClipboard from 'react-copy-to-clipboard';
|
||
|
import React, { FC, HTMLAttributes, ChangeEvent, useState, useCallback } from 'react';
|
||
|
|
||
|
export type TextType = 'primary' | 'secondary' | 'disabled' | 'link' | 'success' | 'warning';
|
||
|
|
||
|
interface TextProps extends HTMLAttributes<HTMLElement> {
|
||
|
type?: TextType;
|
||
|
strong?: boolean;
|
||
|
underline?: boolean;
|
||
|
size?: 'small' | 'medium' | 'large';
|
||
|
keyboard?: boolean;
|
||
|
className?: string;
|
||
|
wrap?: boolean;
|
||
|
copyable?: boolean;
|
||
|
editable?: boolean;
|
||
|
onTextChange?: (value?: string) => void;
|
||
|
clearBeforeEdit?: boolean;
|
||
|
hidden?: boolean;
|
||
|
editModalTitle?: string;
|
||
|
}
|
||
|
|
||
|
interface TextInterface extends React.FC<TextProps> {
|
||
|
Title: React.FC<TitleProps>;
|
||
|
}
|
||
|
|
||
|
const PLACEHOLDER = '**********';
|
||
|
|
||
|
const cx = cn.bind(styles);
|
||
|
|
||
|
const Text: TextInterface = (props) => {
|
||
|
const {
|
||
|
type,
|
||
|
size = 'medium',
|
||
|
strong = false,
|
||
|
underline = false,
|
||
|
children,
|
||
|
onClick,
|
||
|
keyboard = false,
|
||
|
className,
|
||
|
wrap = true,
|
||
|
copyable = false,
|
||
|
editable = false,
|
||
|
onTextChange,
|
||
|
clearBeforeEdit = false,
|
||
|
hidden = false,
|
||
|
editModalTitle = 'New value',
|
||
|
style,
|
||
|
} = props;
|
||
|
|
||
|
const [isEditMode, setIsEditMode] = useState<boolean>(false);
|
||
|
const [value, setValue] = useState<string | undefined>();
|
||
|
|
||
|
const handleEditClick = useCallback(() => {
|
||
|
setValue(clearBeforeEdit || hidden ? '' : (children as string));
|
||
|
|
||
|
setIsEditMode(true);
|
||
|
}, [clearBeforeEdit, hidden, children]);
|
||
|
|
||
|
const handleCancelEdit = useCallback(() => {
|
||
|
setIsEditMode(false);
|
||
|
}, []);
|
||
|
|
||
|
const handleConfirmEdit = useCallback(() => {
|
||
|
setIsEditMode(false);
|
||
|
//@ts-ignore
|
||
|
onTextChange(value);
|
||
|
}, [value, onTextChange]);
|
||
|
|
||
|
const handleInputChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||
|
setValue(e.target.value);
|
||
|
}, []);
|
||
|
|
||
|
return (
|
||
|
<span
|
||
|
onClick={onClick}
|
||
|
className={cx('root', 'text', className, {
|
||
|
[`text--${type}`]: true,
|
||
|
[`text--${size}`]: true,
|
||
|
'text--strong': strong,
|
||
|
'text--underline': underline,
|
||
|
'no-wrap': !wrap,
|
||
|
keyboard,
|
||
|
})}
|
||
|
style={style}
|
||
|
>
|
||
|
{hidden ? PLACEHOLDER : children}
|
||
|
{editable && (
|
||
|
<IconButton
|
||
|
onClick={handleEditClick}
|
||
|
variant="primary"
|
||
|
className={cx('icon-button')}
|
||
|
tooltip="Edit"
|
||
|
tooltipPlacement="top"
|
||
|
name="edit"
|
||
|
/>
|
||
|
)}
|
||
|
{copyable && (
|
||
|
<CopyToClipboard
|
||
|
text={children as string}
|
||
|
onCopy={() => {
|
||
|
openNotification('Text copied');
|
||
|
}}
|
||
|
>
|
||
|
<IconButton
|
||
|
variant="primary"
|
||
|
className={cx('icon-button')}
|
||
|
tooltip="Copy to clipboard"
|
||
|
tooltipPlacement="top"
|
||
|
name="copy"
|
||
|
/>
|
||
|
</CopyToClipboard>
|
||
|
)}
|
||
|
{isEditMode && (
|
||
|
<Modal onDismiss={handleCancelEdit} closeOnEscape isOpen title={editModalTitle}>
|
||
|
<VerticalGroup>
|
||
|
<Input
|
||
|
autoFocus
|
||
|
ref={(node) => {
|
||
|
if (node) {
|
||
|
node.focus();
|
||
|
}
|
||
|
}}
|
||
|
value={value}
|
||
|
onChange={handleInputChange}
|
||
|
/>
|
||
|
<HorizontalGroup justify="flex-end">
|
||
|
<Button variant="secondary" onClick={handleCancelEdit}>
|
||
|
Cancel
|
||
|
</Button>
|
||
|
<Button variant="primary" onClick={handleConfirmEdit}>
|
||
|
Ok
|
||
|
</Button>
|
||
|
</HorizontalGroup>
|
||
|
</VerticalGroup>
|
||
|
</Modal>
|
||
|
)}
|
||
|
</span>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
interface TitleProps extends TextProps {
|
||
|
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||
|
}
|
||
|
|
||
|
const Title: FC<TitleProps> = (props) => {
|
||
|
const { level, className, style, ...restProps } = props;
|
||
|
// @ts-ignore
|
||
|
const Tag: keyof JSX.IntrinsicElements = `h${level}`;
|
||
|
|
||
|
return (
|
||
|
<Tag className={cx('title', className)} style={style}>
|
||
|
<Text {...restProps} />
|
||
|
</Tag>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
Text.Title = Title;
|
||
|
|
||
|
export default Text;
|