add rotation form positionning
This commit is contained in:
parent
9858054841
commit
751ee980e0
8 changed files with 97 additions and 22 deletions
|
|
@ -3,6 +3,8 @@ import React, { FC, PropsWithChildren } from 'react';
|
|||
import cn from 'classnames/bind';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
ReactModal.setAppElement('#reactRoot');
|
||||
|
||||
import styles from './Modal.module.css';
|
||||
|
||||
export interface ModalProps {
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@
|
|||
|
||||
.empty {
|
||||
height: 28px;
|
||||
background: #5f505633;
|
||||
|
||||
/* background: #5f505633;
|
||||
border: 1px dashed #5c474d;
|
||||
color: rgba(209, 14, 92, 0.5);
|
||||
color: rgba(209, 14, 92, 0.5); */
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
top: 0;
|
||||
transition: transform 500ms ease;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { makeRequest } from 'network';
|
|||
import { getDateTime, getUTCString } from 'pages/schedule/Schedule.helpers';
|
||||
import { SelectOption } from 'state/types';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { getCoords, waitForElement } from 'utils/DOM';
|
||||
import { useDebouncedCallback } from 'utils/hooks';
|
||||
|
||||
import { RotationCreateData } from './RotationForm.types';
|
||||
|
|
@ -57,8 +58,6 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
const { onHide, onCreate, startMoment, currentTimezone, scheduleId, onUpdate, onDelete, layerPriority, shiftId } =
|
||||
props;
|
||||
|
||||
const [isOpen, setIsOpen] = useState<boolean>(true);
|
||||
|
||||
const [repeatEveryValue, setRepeatEveryValue] = useState<number>(1);
|
||||
const [repeatEveryPeriod, setRepeatEveryPeriod] = useState<number>(0);
|
||||
const [selectedDays, setSelectedDays] = useState<string[]>([]);
|
||||
|
|
@ -70,6 +69,24 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
dateTime(startOfDay.add(1, 'month').format('YYYY-MM-DD HH:mm:ss'))
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const shift = store.scheduleStore.shifts[shiftId];
|
||||
|
||||
const [offsetTop, setOffsetTop] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
waitForElement(`#layer${shiftId === 'new' ? layerPriority : shift?.priority_level}`).then((elm) => {
|
||||
const modal = document.querySelector(`.${cx('draggable')}`) as HTMLDivElement;
|
||||
|
||||
const coords = getCoords(elm);
|
||||
|
||||
// setOffsetTop(Math.max(coords.top + elm.offsetHeight, 0));
|
||||
|
||||
setOffsetTop(coords.top - modal?.offsetHeight - 70);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [userGroups, setUserGroups] = useState([[]]);
|
||||
|
||||
const getUser = (pk: User['pk']) => {
|
||||
|
|
@ -85,10 +102,6 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const shift = store.scheduleStore.shifts[shiftId];
|
||||
|
||||
useEffect(() => {
|
||||
if (shiftId !== 'new') {
|
||||
store.scheduleStore.updateOncallShift(shiftId);
|
||||
|
|
@ -137,7 +150,7 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
|
||||
const handleChange = useDebouncedCallback(() => {
|
||||
store.scheduleStore.updateRotationPreview(scheduleId, shiftId, getFromString(startMoment), false, params);
|
||||
}, 1000);
|
||||
}, 500);
|
||||
|
||||
useEffect(handleChange, [params]);
|
||||
|
||||
|
|
@ -172,11 +185,11 @@ const RotationForm: FC<RotationFormProps> = observer((props) => {
|
|||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
isOpen
|
||||
width="430px"
|
||||
onDismiss={onHide}
|
||||
contentElement={(props, children) => (
|
||||
<Draggable handle=".drag-handler" positionOffset={{ x: 0, y: 0 }}>
|
||||
<Draggable handle=".drag-handler" defaultClassName={cx('draggable')} positionOffset={{ x: 0, y: offsetTop }}>
|
||||
<div {...props}>{children}</div>
|
||||
</Draggable>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { Timezone } from 'models/timezone/timezone.types';
|
|||
import { User } from 'models/user/user.types';
|
||||
import { getDateTime, getUTCString } from 'pages/schedule/Schedule.helpers';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { getCoords, waitForElement } from 'utils/DOM';
|
||||
import { useDebouncedCallback } from 'utils/hooks';
|
||||
|
||||
import { RotationCreateData } from './RotationForm.types';
|
||||
|
|
@ -53,6 +54,18 @@ const ScheduleOverrideForm: FC<RotationFormProps> = (props) => {
|
|||
|
||||
const store = useStore();
|
||||
|
||||
const [offsetTop, setOffsetTop] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
waitForElement('#overrides-list').then((elm) => {
|
||||
const modal = document.querySelector(`.${cx('draggable')}`) as HTMLDivElement;
|
||||
|
||||
const coords = getCoords(elm);
|
||||
|
||||
setOffsetTop(coords.top - modal?.offsetHeight - 10);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [shiftStart, setShiftStart] = useState<DateTime>(dateTime(startOfDay.format('YYYY-MM-DD HH:mm:ss')));
|
||||
const [shiftEnd, setShiftEnd] = useState<DateTime>(
|
||||
dateTime(startOfDay.add(12, 'hours').format('YYYY-MM-DD HH:mm:ss'))
|
||||
|
|
@ -117,16 +130,17 @@ const ScheduleOverrideForm: FC<RotationFormProps> = (props) => {
|
|||
|
||||
const handleChange = useDebouncedCallback(() => {
|
||||
store.scheduleStore.updateRotationPreview(scheduleId, shiftId, getFromString(startMoment), true, params);
|
||||
}, 1000);
|
||||
}, 500);
|
||||
|
||||
useEffect(handleChange, [params]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen
|
||||
width="430px"
|
||||
onDismiss={onHide}
|
||||
contentElement={(props, children) => (
|
||||
<Draggable handle=".drag-handler" positionOffset={{ x: 0, y: 0 }}>
|
||||
<Draggable handle=".drag-handler" defaultClassName={cx('draggable')} positionOffset={{ x: 0, y: offsetTop }}>
|
||||
<div {...props}>{children}</div>
|
||||
</Draggable>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,11 @@ class Rotations extends Component<RotationsProps, RotationsState> {
|
|||
{layers && layers.length ? (
|
||||
layers.map((layer, layerIndex) => (
|
||||
<div key={layer.priority}>
|
||||
<div className={cx('layer')}>
|
||||
<div id={`layer${layer.priority}`} className={cx('layer')}>
|
||||
<div className={cx('layer-title')}>
|
||||
<HorizontalGroup spacing="sm" justify="center">
|
||||
Layer {layer.priority} <Icon name="info-circle" />
|
||||
<span>Layer {layer.priority}</span>
|
||||
<Icon name="info-circle" />
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
<div className={cx('rotations')}>
|
||||
|
|
@ -119,10 +120,11 @@ class Rotations extends Component<RotationsProps, RotationsState> {
|
|||
))
|
||||
) : (
|
||||
<div>
|
||||
<div className={cx('layer')}>
|
||||
<div id={`layer1`} className={cx('layer')}>
|
||||
<div className={cx('layer-title')}>
|
||||
<HorizontalGroup spacing="sm" justify="center">
|
||||
Layer 1 <Icon name="info-circle" />
|
||||
<span>Layer 1</span>
|
||||
<Icon name="info-circle" />
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
<div className={cx('header-plus-content')}>
|
||||
|
|
@ -151,7 +153,7 @@ class Rotations extends Component<RotationsProps, RotationsState> {
|
|||
this.handleAddLayer(nextPriority);
|
||||
}}
|
||||
>
|
||||
Add rotations layer +
|
||||
+ Add rotations layer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class ScheduleOverrides extends Component<ScheduleOverridesProps, ScheduleOverri
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={cx('root')}>
|
||||
<div id="overrides-list" className={cx('root')}>
|
||||
<div className={cx('header')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<div className={cx('title')}>Overrides</div>
|
||||
|
|
@ -93,9 +93,9 @@ class ScheduleOverrides extends Component<ScheduleOverridesProps, ScheduleOverri
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx('add-rotations-layer')} onClick={this.handleAddOverride}>
|
||||
Add override +
|
||||
</div>
|
||||
{/* <div className={cx('add-rotations-layer')} onClick={this.handleAddOverride}>
|
||||
+ Add override
|
||||
</div>*/}
|
||||
</div>
|
||||
{shiftIdToShowOverrideForm && (
|
||||
<ScheduleOverrideForm
|
||||
|
|
|
|||
38
grafana-plugin/src/utils/DOM.ts
Normal file
38
grafana-plugin/src/utils/DOM.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
export const waitForElement = (selector: string) => {
|
||||
return new Promise((resolve) => {
|
||||
if (document.querySelector(selector)) {
|
||||
return resolve(document.querySelector(selector));
|
||||
}
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
if (document.querySelector(selector)) {
|
||||
resolve(document.querySelector(selector));
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getCoords = (elem) => {
|
||||
// crossbrowser version
|
||||
var box = elem.getBoundingClientRect();
|
||||
|
||||
var body = document.body;
|
||||
var docEl = document.documentElement;
|
||||
|
||||
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
||||
|
||||
var clientTop = docEl.clientTop || body.clientTop || 0;
|
||||
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
||||
|
||||
var top = box.top + scrollTop - clientTop;
|
||||
var left = box.left + scrollLeft - clientLeft;
|
||||
|
||||
return { top: Math.round(top), left: Math.round(left) };
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue