grafana/public/app/core/components/Layers/LayerName.tsx

160 lines
4.1 KiB
TypeScript
Raw Normal View History

2021-11-09 12:19:46 -06:00
import { css, cx } from '@emotion/css';
import React, { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Icon, Input, FieldValidationMessage, useStyles2 } from '@grafana/ui';
2021-11-09 12:19:46 -06:00
export interface LayerNameProps {
name: string;
onChange: (v: string) => void;
verifyLayerNameUniqueness?: (nameToCheck: string) => boolean;
overrideStyles?: boolean;
2021-11-09 12:19:46 -06:00
}
export const LayerName = ({ name, onChange, verifyLayerNameUniqueness, overrideStyles }: LayerNameProps) => {
const styles = useStyles2(getStyles);
2021-11-09 12:19:46 -06:00
const [isEditing, setIsEditing] = useState<boolean>(false);
const [validationError, setValidationError] = useState<string | null>(null);
const onEditLayer = (event: React.SyntheticEvent) => {
setIsEditing(true);
};
const onEndEditName = (newName: string) => {
setIsEditing(false);
if (validationError) {
setValidationError(null);
return;
}
if (name !== newName) {
onChange(newName);
2021-11-09 12:19:46 -06:00
}
};
const onInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const newName = event.currentTarget.value.trim();
if (newName.length === 0) {
setValidationError('An empty layer name is not allowed');
return;
}
2021-12-06 23:04:58 -06:00
if (verifyLayerNameUniqueness && !verifyLayerNameUniqueness(newName) && newName !== name) {
2021-11-15 13:18:08 -06:00
setValidationError('Layer name already exists');
return;
2021-11-09 12:19:46 -06:00
}
if (validationError) {
setValidationError(null);
}
};
const onEditLayerBlur = (event: React.SyntheticEvent<HTMLInputElement>) => {
onEndEditName(event.currentTarget.value.trim());
};
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
2021-11-09 12:19:46 -06:00
if (event.key === 'Enter') {
onEndEditName(event.currentTarget.value);
2021-11-09 12:19:46 -06:00
}
};
const onFocus = (event: React.FocusEvent<HTMLInputElement>) => {
event.target.select();
};
return (
<>
<div className={styles.wrapper}>
{!isEditing && (
<button
className={styles.layerNameWrapper}
title="Edit layer name"
onClick={onEditLayer}
data-testid="layer-name-div"
>
<span className={overrideStyles ? '' : styles.layerName}>{name}</span>
2021-11-09 12:19:46 -06:00
<Icon name="pen" className={styles.layerEditIcon} size="sm" />
</button>
)}
{isEditing && (
<>
<Input
type="text"
defaultValue={name}
2021-11-09 12:19:46 -06:00
onBlur={onEditLayerBlur}
autoFocus
onKeyDown={onKeyDown}
onFocus={onFocus}
invalid={validationError !== null}
onChange={onInputChange}
className={styles.layerNameInput}
data-testid="layer-name-input"
/>
{validationError && <FieldValidationMessage horizontal>{validationError}</FieldValidationMessage>}
</>
)}
</div>
</>
);
};
const getStyles = (theme: GrafanaTheme2) => {
2021-11-09 12:19:46 -06:00
return {
wrapper: css`
label: Wrapper;
display: flex;
align-items: center;
margin-left: ${theme.spacing(0.5)};
2021-11-09 12:19:46 -06:00
`,
layerNameWrapper: css`
display: flex;
cursor: pointer;
border: 1px solid transparent;
border-radius: ${theme.shape.borderRadius(2)};
2021-11-09 12:19:46 -06:00
align-items: center;
padding: 0 0 0 ${theme.spacing(0.5)};
2021-11-09 12:19:46 -06:00
margin: 0;
background: transparent;
&:hover {
background: ${theme.colors.action.hover};
border: 1px dashed ${theme.colors.border.strong};
2021-11-09 12:19:46 -06:00
}
&:focus {
border: 2px solid ${theme.colors.primary.border};
2021-11-09 12:19:46 -06:00
}
&:hover,
&:focus {
.query-name-edit-icon {
visibility: visible;
}
}
`,
layerName: css`
font-weight: ${theme.typography.fontWeightMedium};
color: ${theme.colors.primary.text};
2021-11-09 12:19:46 -06:00
cursor: pointer;
overflow: hidden;
margin-left: ${theme.spacing(0.5)};
2021-11-09 12:19:46 -06:00
`,
layerEditIcon: cx(
css`
margin-left: ${theme.spacing(2)};
2021-11-09 12:19:46 -06:00
visibility: hidden;
`,
'query-name-edit-icon'
),
layerNameInput: css`
max-width: 300px;
margin: -4px 0;
`,
};
};