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.
 
 
 
 
 

167 lines
4.1 KiB

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;