Grafana UI: Add ref to DatePickerWithInput (#104346)
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
co-authored by
Ashley Harrison
parent
06343fcda9
commit
52b120cb67
+66
-66
@@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { autoUpdate, flip, shift, useClick, useDismiss, useFloating, useInteractions } from '@floating-ui/react';
|
||||
import { ChangeEvent, useState } from 'react';
|
||||
import { ChangeEvent, forwardRef, useImperativeHandle, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, dateTime } from '@grafana/data';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { DatePicker } from '../DatePicker/DatePicker';
|
||||
export const formatDate = (date: Date | string) => dateTime(date).format('L');
|
||||
|
||||
/** @public */
|
||||
export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'value' | 'onChange'> {
|
||||
export interface DatePickerWithInputProps extends Omit<InputProps, 'value' | 'onChange'> {
|
||||
/** Value selected by the DatePicker */
|
||||
value?: Date | string;
|
||||
/** The minimum date the value can be set to */
|
||||
@@ -27,78 +27,78 @@ export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'valu
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DatePickerWithInput = ({
|
||||
value,
|
||||
minDate,
|
||||
maxDate,
|
||||
onChange,
|
||||
closeOnSelect,
|
||||
placeholder = 'Date',
|
||||
...rest
|
||||
}: DatePickerWithInputProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const styles = useStyles2(getStyles);
|
||||
export const DatePickerWithInput = forwardRef<HTMLInputElement, DatePickerWithInputProps>(
|
||||
({ value, minDate, maxDate, onChange, closeOnSelect, placeholder = 'Date', ...rest }, ref) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// the order of middleware is important!
|
||||
// see https://floating-ui.com/docs/arrow#order
|
||||
const middleware = [
|
||||
flip({
|
||||
// see https://floating-ui.com/docs/flip#combining-with-shift
|
||||
crossAxis: false,
|
||||
boundary: document.body,
|
||||
}),
|
||||
shift(),
|
||||
];
|
||||
// the order of middleware is important!
|
||||
// see https://floating-ui.com/docs/arrow#order
|
||||
const middleware = [
|
||||
flip({
|
||||
// see https://floating-ui.com/docs/flip#combining-with-shift
|
||||
crossAxis: false,
|
||||
boundary: document.body,
|
||||
}),
|
||||
shift(),
|
||||
];
|
||||
|
||||
const { context, refs, floatingStyles } = useFloating({
|
||||
open,
|
||||
placement: 'bottom-start',
|
||||
onOpenChange: setOpen,
|
||||
middleware,
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy: 'fixed',
|
||||
});
|
||||
const { context, refs, floatingStyles } = useFloating<HTMLInputElement>({
|
||||
open,
|
||||
placement: 'bottom-start',
|
||||
onOpenChange: setOpen,
|
||||
middleware,
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy: 'fixed',
|
||||
});
|
||||
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Input
|
||||
ref={refs.setReference}
|
||||
type="text"
|
||||
autoComplete={'off'}
|
||||
placeholder={placeholder}
|
||||
value={value ? formatDate(value) : value}
|
||||
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
|
||||
// Allow resetting the date
|
||||
if (ev.target.value === '') {
|
||||
onChange('');
|
||||
}
|
||||
}}
|
||||
className={styles.input}
|
||||
{...rest}
|
||||
{...getReferenceProps()}
|
||||
/>
|
||||
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<DatePicker
|
||||
isOpen={open}
|
||||
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
onChange={(ev) => {
|
||||
onChange(ev);
|
||||
if (closeOnSelect) {
|
||||
setOpen(false);
|
||||
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => refs.domReference.current, [
|
||||
refs.domReference,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Input
|
||||
ref={refs.setReference}
|
||||
type="text"
|
||||
autoComplete={'off'}
|
||||
placeholder={placeholder}
|
||||
value={value ? formatDate(value) : value}
|
||||
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
|
||||
// Allow resetting the date
|
||||
if (ev.target.value === '') {
|
||||
onChange('');
|
||||
}
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
className={styles.input}
|
||||
{...rest}
|
||||
{...getReferenceProps()}
|
||||
/>
|
||||
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<DatePicker
|
||||
isOpen={open}
|
||||
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
onChange={(ev) => {
|
||||
onChange(ev);
|
||||
if (closeOnSelect) {
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DatePickerWithInput.displayName = 'DatePickerWithInput';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user