UI: Segment Input (#20904)

* Add input segment

* Rename story

* Cleanup

* Fix lint error

* More cleanup

* Export ui component

* Use measure text util
This commit is contained in:
Erik Sundell 2019-12-06 10:24:34 +01:00 committed by GitHub
parent a533e00622
commit 047abc87c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 4 deletions

View File

@ -4,6 +4,8 @@ import { SelectableValue } from '@grafana/data';
import { SegmentSelect, useExpandableLabel, SegmentProps } from './';
export interface SegmentSyncProps<T> extends SegmentProps<T> {
value?: SelectableValue<T>;
onChange: (item: SelectableValue<T>) => void;
options: Array<SelectableValue<T>>;
}

View File

@ -5,7 +5,9 @@ import { SelectableValue } from '@grafana/data';
import { useExpandableLabel, SegmentProps } from '.';
export interface SegmentAsyncProps<T> extends SegmentProps<T> {
value?: SelectableValue<T>;
loadOptions: (query?: string) => Promise<Array<SelectableValue<T>>>;
onChange: (item: SelectableValue<T>) => void;
}
export function SegmentAsync<T>({

View File

@ -0,0 +1,29 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
const SegmentStories = storiesOf('UI/Segment/SegmentInput', module);
import { SegmentInput } from '.';
import { UseState } from '../../utils/storybook/UseState';
SegmentStories.add('Segment Input', () => {
return (
<UseState initialState={'some text'}>
{(value, updateValue) => (
<>
<div className="gf-form-inline">
<div className="gf-form">
<span className="gf-form-label width-8 query-keyword">Segment Name</span>
</div>
<SegmentInput
value={value}
onChange={text => {
updateValue(text as string);
action('Segment value changed')(text);
}}
/>
</div>
</>
)}
</UseState>
);
});

View File

@ -0,0 +1,48 @@
import React, { useRef, useState } from 'react';
import { cx, css } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { measureText } from '../../utils/measureText';
import { useExpandableLabel, SegmentProps } from '.';
export interface SegmentInputProps<T> extends SegmentProps<T> {
value: string | number;
onChange: (text: string | number) => void;
}
const FONT_SIZE = 14;
export function SegmentInput<T>({
value,
onChange,
Component,
className,
}: React.PropsWithChildren<SegmentInputProps<T>>) {
const ref = useRef(null);
const [inputWidth, setInputWidth] = useState<number>(measureText(value.toString(), FONT_SIZE).width);
const [Label, , expanded, setExpanded] = useExpandableLabel(false);
useClickAway(ref, () => setExpanded(false));
if (!expanded) {
return <Label Component={Component || <a className={cx('gf-form-label', 'query-part', className)}>{value}</a>} />;
}
const inputWidthStyle = css`
width: ${Math.max(inputWidth + 20, 32)}px;
`;
return (
<input
ref={ref}
autoFocus
className={cx(`gf-form gf-form-input`, inputWidthStyle)}
value={value}
onChange={item => {
const { width } = measureText(item.target.value, FONT_SIZE);
setInputWidth(width);
onChange(item.target.value);
}}
onBlur={() => setExpanded(false)}
onKeyDown={e => [13, 27].includes(e.keyCode) && setExpanded(false)}
/>
);
}

View File

@ -1,5 +1,6 @@
export { Segment } from './Segment';
export { SegmentAsync } from './SegmentAsync';
export { SegmentSelect } from './SegmentSelect';
export { SegmentInput } from './SegmentInput';
export { SegmentProps } from './types';
export { useExpandableLabel } from './useExpandableLabel';

View File

@ -1,9 +1,6 @@
import { ReactElement } from 'react';
import { SelectableValue } from '@grafana/data';
export interface SegmentProps<T> {
onChange: (item: SelectableValue<T>) => void;
value?: SelectableValue<T>;
Component?: ReactElement;
className?: string;
allowCustomValue?: boolean;

View File

@ -105,7 +105,7 @@ export { DataSourceHttpSettings } from './DataSourceSettings/DataSourceHttpSetti
export { Spinner } from './Spinner/Spinner';
export { FadeTransition } from './transitions/FadeTransition';
export { SlideOutTransition } from './transitions/SlideOutTransition';
export { Segment, SegmentAsync, SegmentSelect } from './Segment/';
export { Segment, SegmentAsync, SegmentInput, SegmentSelect } from './Segment/';
export { default as Chart } from './Chart';
export { Icon } from './Icon/Icon';