cloudmr-ux 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/index.css +17 -0
- package/dist/index.js +174 -0
- package/dist/index.js.map +1 -0
- package/dist/index.modern.js +167 -0
- package/dist/index.modern.js.map +1 -0
- package/package.json +99 -0
- package/src/.eslintrc +5 -0
- package/src/common/components/Cmr-components/avatar/Avatar.scss +0 -0
- package/src/common/components/Cmr-components/avatar/Avatar.tsx +25 -0
- package/src/common/components/Cmr-components/button/Button.scss +0 -0
- package/src/common/components/Cmr-components/button/Button.tsx +14 -0
- package/src/common/components/Cmr-components/checkbox/Checkbox.scss +11 -0
- package/src/common/components/Cmr-components/checkbox/Checkbox.tsx +29 -0
- package/src/common/components/Cmr-components/collapse/Collapse.scss +3 -0
- package/src/common/components/Cmr-components/collapse/Collapse.tsx +75 -0
- package/src/common/components/Cmr-components/dialogue/Confirmation.tsx +48 -0
- package/src/common/components/Cmr-components/dialogue/DeletionDialog.tsx +61 -0
- package/src/common/components/Cmr-components/dialogue/EditConfirmation.tsx +72 -0
- package/src/common/components/Cmr-components/double-slider/DualSlider.tsx +198 -0
- package/src/common/components/Cmr-components/double-slider/InvertibleDualSlider.tsx +224 -0
- package/src/common/components/Cmr-components/dropdown/Dropdown.scss +36 -0
- package/src/common/components/Cmr-components/dropdown/Dropdown.tsx +83 -0
- package/src/common/components/Cmr-components/gui-slider/ControlledSlider.tsx +139 -0
- package/src/common/components/Cmr-components/gui-slider/Slider.tsx +170 -0
- package/src/common/components/Cmr-components/header/Header.scss +20 -0
- package/src/common/components/Cmr-components/header/Header.tsx +101 -0
- package/src/common/components/Cmr-components/input/Input.scss +0 -0
- package/src/common/components/Cmr-components/input/Input.tsx +39 -0
- package/src/common/components/Cmr-components/input-number/InputNumber.scss +0 -0
- package/src/common/components/Cmr-components/input-number/InputNumber.tsx +29 -0
- package/src/common/components/Cmr-components/label/Label.scss +13 -0
- package/src/common/components/Cmr-components/label/Label.tsx +20 -0
- package/src/common/components/Cmr-components/option/Option.scss +0 -0
- package/src/common/components/Cmr-components/option/Option.tsx +24 -0
- package/src/common/components/Cmr-components/panel/Panel.scss +0 -0
- package/src/common/components/Cmr-components/panel/Panel.tsx +54 -0
- package/src/common/components/Cmr-components/progress/Progress.scss +0 -0
- package/src/common/components/Cmr-components/progress/Progress.tsx +38 -0
- package/src/common/components/Cmr-components/radio/Radio.scss +0 -0
- package/src/common/components/Cmr-components/radio/Radio.tsx +23 -0
- package/src/common/components/Cmr-components/radio-group/RadioGroup.scss +0 -0
- package/src/common/components/Cmr-components/radio-group/RadioGroup.tsx +32 -0
- package/src/common/components/Cmr-components/rename/edit.tsx +94 -0
- package/src/common/components/Cmr-components/select/Select.scss +3 -0
- package/src/common/components/Cmr-components/select/Select.tsx +33 -0
- package/src/common/components/Cmr-components/select-upload/SelectUpload.scss +0 -0
- package/src/common/components/Cmr-components/select-upload/SelectUpload.tsx +133 -0
- package/src/common/components/Cmr-components/slider/Slider.scss +0 -0
- package/src/common/components/Cmr-components/slider/Slider.tsx +66 -0
- package/src/common/components/Cmr-components/spin/Spin.scss +0 -0
- package/src/common/components/Cmr-components/spin/Spin.tsx +31 -0
- package/src/common/components/Cmr-components/tooltip/Tooltip.scss +0 -0
- package/src/common/components/Cmr-components/tooltip/Tooltip.tsx +50 -0
- package/src/common/components/Cmr-components/upload/Upload.scss +5 -0
- package/src/common/components/Cmr-components/upload/Upload.tsx +188 -0
- package/src/common/components/Cmr-components/upload/UploadWindow.tsx +355 -0
- package/src/index.js +8 -0
- package/src/index.test.js +7 -0
- package/src/styles.module.css +9 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, {cloneElement} from 'react';
|
|
2
|
+
import { Collapse } from 'antd';
|
|
3
|
+
import { CollapsibleType } from 'antd/es/collapse/CollapsePanel';
|
|
4
|
+
import { ExpandIconPosition } from 'antd/es/collapse/Collapse';
|
|
5
|
+
import './Collapse.scss';
|
|
6
|
+
|
|
7
|
+
interface CmrCollapseProps {
|
|
8
|
+
accordion?: boolean;
|
|
9
|
+
activeKey?: Array<string | number>|number;
|
|
10
|
+
bordered?: boolean;
|
|
11
|
+
collapsible?: CollapsibleType;
|
|
12
|
+
defaultActiveKey?: Array<string | number>;
|
|
13
|
+
destroyInactivePanel?: boolean;
|
|
14
|
+
expandIconPosition?: ExpandIconPosition;
|
|
15
|
+
ghost?: boolean;
|
|
16
|
+
onChange?: (key:Array<string | number>|number) => void;
|
|
17
|
+
children?: JSX.Element[]|JSX.Element;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const CmrCollapse = (props: CmrCollapseProps) => {
|
|
21
|
+
let {activeKey, defaultActiveKey, onChange, children}=props;
|
|
22
|
+
defaultActiveKey = (defaultActiveKey)?defaultActiveKey:[];
|
|
23
|
+
let [activeKeys, setActiveKeys] = React.useState(defaultActiveKey);
|
|
24
|
+
if(activeKey!=undefined&&activeKey!=activeKeys){
|
|
25
|
+
console.log(activeKey);
|
|
26
|
+
if(activeKey instanceof Array)
|
|
27
|
+
setActiveKeys(activeKey);
|
|
28
|
+
else setActiveKeys([activeKey]);
|
|
29
|
+
}
|
|
30
|
+
return (
|
|
31
|
+
<div className="cmr-collapse">
|
|
32
|
+
<div>
|
|
33
|
+
{(children&&Array.isArray(children))?children.map((child,index)=>{
|
|
34
|
+
let props = {expanded:activeKeys.indexOf(index)>=0,
|
|
35
|
+
panelKey: index,
|
|
36
|
+
onToggle: (key:number)=>{
|
|
37
|
+
let i = activeKeys.indexOf(key);
|
|
38
|
+
if(i<0) {
|
|
39
|
+
let newKeys = [...activeKeys];
|
|
40
|
+
newKeys.push(index);
|
|
41
|
+
setActiveKeys(newKeys);
|
|
42
|
+
if(onChange!=undefined)
|
|
43
|
+
onChange(newKeys);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
let newKeys = [...activeKeys];
|
|
47
|
+
newKeys.splice(i,1);
|
|
48
|
+
setActiveKeys(newKeys);
|
|
49
|
+
if(onChange!=undefined)
|
|
50
|
+
onChange(newKeys);
|
|
51
|
+
}
|
|
52
|
+
}};
|
|
53
|
+
return cloneElement(child, props)
|
|
54
|
+
}):((children?cloneElement(children,{expanded:activeKeys.indexOf(0)>=0,
|
|
55
|
+
panelKey: 0,
|
|
56
|
+
onToggle: (key:number)=>{
|
|
57
|
+
let i = activeKeys.indexOf(key);
|
|
58
|
+
if(i<0) {
|
|
59
|
+
let newKeys = [...activeKeys];
|
|
60
|
+
newKeys.push(0);
|
|
61
|
+
setActiveKeys(newKeys);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
let newKeys = [...activeKeys];
|
|
65
|
+
newKeys.splice(i,1);
|
|
66
|
+
setActiveKeys(newKeys);
|
|
67
|
+
}
|
|
68
|
+
if(onChange!=undefined)
|
|
69
|
+
onChange([0]);
|
|
70
|
+
}}):undefined))}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import TextField from '@mui/material/TextField';
|
|
3
|
+
import Dialog from '@mui/material/Dialog';
|
|
4
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
5
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
6
|
+
import DialogContentText from '@mui/material/DialogContentText';
|
|
7
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
8
|
+
import CmrButton from '../button/Button';
|
|
9
|
+
import {StyledComponentProps} from "@mui/material";
|
|
10
|
+
|
|
11
|
+
export default function Confirmation({ name,message,cancelText='Cancel',
|
|
12
|
+
color, open, setOpen, confirmCallback=()=>{},confirmText='Confirm', cancellable=false, cancelCallback=()=>{}, width}: { name: string | undefined; cancelText?:string; message:string|undefined;
|
|
13
|
+
color?: "inherit" | "primary" | "secondary" | "success" | "error" | "info" | "warning" | undefined, open:boolean, setOpen:(open:boolean)=>void, confirmCallback?:()=>void,
|
|
14
|
+
cancellable?:boolean, cancelCallback?:()=>void, width?:number, confirmText?:string}) {
|
|
15
|
+
const [text, setText] = React.useState('');
|
|
16
|
+
|
|
17
|
+
const handleClose = () => {
|
|
18
|
+
setOpen(false);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const handleConfirm=()=>{
|
|
22
|
+
confirmCallback();
|
|
23
|
+
handleClose();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const handleCancel=()=>{
|
|
27
|
+
cancelCallback();
|
|
28
|
+
handleClose();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Dialog open={open} onClose={handleClose}>
|
|
34
|
+
<DialogTitle>{name?name:'Confirmation'}</DialogTitle>
|
|
35
|
+
<DialogContent sx={{width:width}}>
|
|
36
|
+
<DialogContentText alignContent={'center'}>
|
|
37
|
+
{message}
|
|
38
|
+
</DialogContentText>
|
|
39
|
+
<DialogActions className={'mt-4'}>
|
|
40
|
+
{cancellable&&
|
|
41
|
+
<CmrButton variant={"outlined"} color={'inherit'} sx={{color:'#333'}} onClick={handleCancel}>{cancelText}</CmrButton>
|
|
42
|
+
}
|
|
43
|
+
<CmrButton variant={"contained"} color={color} onClick={handleConfirm}>{confirmText}</CmrButton>
|
|
44
|
+
</DialogActions>
|
|
45
|
+
</DialogContent>
|
|
46
|
+
</Dialog>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Button from '@mui/material/Button';
|
|
3
|
+
import TextField from '@mui/material/TextField';
|
|
4
|
+
import Dialog from '@mui/material/Dialog';
|
|
5
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
6
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
7
|
+
import DialogContentText from '@mui/material/DialogContentText';
|
|
8
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
9
|
+
|
|
10
|
+
export default function DeletionDialog(props: { name: string | undefined; deletionCallback: () => void; }) {
|
|
11
|
+
const [open, setOpen] = React.useState(true);
|
|
12
|
+
const [text, setText] = React.useState('');
|
|
13
|
+
|
|
14
|
+
const handleClickOpen = () => {
|
|
15
|
+
setOpen(true);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const handleClose = () => {
|
|
19
|
+
setOpen(false);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handleConfirm = () => {
|
|
23
|
+
if(text===props.name){
|
|
24
|
+
props.deletionCallback();
|
|
25
|
+
setOpen(false);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const handleTextFieldChange=(e: { target: { value: React.SetStateAction<string>; }; })=>{
|
|
30
|
+
setText( e.target.value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div>
|
|
35
|
+
<Dialog open={open} onClose={handleClose}>
|
|
36
|
+
<DialogTitle>Confirmation</DialogTitle>
|
|
37
|
+
<DialogContent>
|
|
38
|
+
<DialogContentText>
|
|
39
|
+
To delete the files, please type your full name below and confirm.
|
|
40
|
+
</DialogContentText>
|
|
41
|
+
|
|
42
|
+
<TextField
|
|
43
|
+
autoFocus
|
|
44
|
+
margin="dense"
|
|
45
|
+
id="name"
|
|
46
|
+
type="email"
|
|
47
|
+
placeholder = {props.name}
|
|
48
|
+
fullWidth
|
|
49
|
+
inputProps={{style: {fontSize: "16pt"}}}
|
|
50
|
+
variant="standard"
|
|
51
|
+
onChange={handleTextFieldChange}
|
|
52
|
+
/>
|
|
53
|
+
</DialogContent>
|
|
54
|
+
<DialogActions>
|
|
55
|
+
<button className='btn btn-secondary' onClick={handleClose}>Cancel</button>
|
|
56
|
+
<button className='btn btn-danger' onClick={handleConfirm}>Confirm</button>
|
|
57
|
+
</DialogActions>
|
|
58
|
+
</Dialog>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import TextField from '@mui/material/TextField';
|
|
3
|
+
import Dialog from '@mui/material/Dialog';
|
|
4
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
5
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
6
|
+
import DialogContentText from '@mui/material/DialogContentText';
|
|
7
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
8
|
+
import CmrButton from '../button/Button';
|
|
9
|
+
import { InputAdornment } from '@mui/material';
|
|
10
|
+
import {useEffect} from "react";
|
|
11
|
+
|
|
12
|
+
export interface EditConfirmationProps{
|
|
13
|
+
name?: string; // Equivalent to string | undefined
|
|
14
|
+
defaultText?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
color?: "inherit" | "primary" | "secondary" | "success" | "error" | "info" | "warning";
|
|
17
|
+
open: boolean;
|
|
18
|
+
setOpen: (open: boolean) => void;
|
|
19
|
+
confirmCallback?: (text: string) => void;
|
|
20
|
+
cancellable?: boolean;
|
|
21
|
+
cancelCallback?: (edit: string) => void;
|
|
22
|
+
suffix?:string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function EditConfirmation({ name,message, defaultText='',
|
|
26
|
+
color, open, setOpen, confirmCallback=()=>{}, cancellable=false, cancelCallback=()=>{},suffix=''}:EditConfirmationProps) {
|
|
27
|
+
const [text, setText] = React.useState(defaultText);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if(open)
|
|
30
|
+
setText(defaultText);
|
|
31
|
+
}, [open]);
|
|
32
|
+
const handleClose = () => {
|
|
33
|
+
setOpen(false);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const handleConfirm=()=>{
|
|
37
|
+
confirmCallback(text+suffix);
|
|
38
|
+
handleClose();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const handleCancel=()=>{
|
|
42
|
+
cancelCallback(text+suffix);
|
|
43
|
+
handleClose();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Dialog maxWidth="xs" fullWidth={true} open={open} onClose={handleCancel}>
|
|
49
|
+
<DialogTitle>{name?name:'Confirmation'}</DialogTitle>
|
|
50
|
+
<DialogContent>
|
|
51
|
+
<DialogContentText alignContent={'center'}>
|
|
52
|
+
{message}
|
|
53
|
+
</DialogContentText>
|
|
54
|
+
<DialogActions>
|
|
55
|
+
<TextField fullWidth variant='standard'
|
|
56
|
+
InputProps={{
|
|
57
|
+
endAdornment: (
|
|
58
|
+
<InputAdornment position="end" sx={{ whiteSpace: 'nowrap' }}>{suffix}</InputAdornment>
|
|
59
|
+
),
|
|
60
|
+
}}
|
|
61
|
+
defaultValue={text} onChange={(e)=>setText(e.target.value)}/>
|
|
62
|
+
</DialogActions>
|
|
63
|
+
<DialogActions>
|
|
64
|
+
{cancellable&&
|
|
65
|
+
<CmrButton variant={"outlined"} color={'inherit'} sx={{color:'#333'}} onClick={handleCancel}>Cancel</CmrButton>
|
|
66
|
+
}
|
|
67
|
+
<CmrButton variant={"contained"} color={color} onClick={handleConfirm}>Confirm</CmrButton>
|
|
68
|
+
</DialogActions>
|
|
69
|
+
</DialogContent>
|
|
70
|
+
</Dialog>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {Box} from "@mui/material";
|
|
2
|
+
import {useRef, useState} from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This dual slider (lil-gui styled) allows users to control the max and min of an interval simultaneously.
|
|
6
|
+
* The rendered interval (and number control) can be masked by a transformation - inverse pair.
|
|
7
|
+
* @param name
|
|
8
|
+
* @param min
|
|
9
|
+
* @param max
|
|
10
|
+
* @param setMin
|
|
11
|
+
* @param setMax
|
|
12
|
+
* @param transform transform and inverse are a pair that masks the rendered values and rendered inputs by a transformation
|
|
13
|
+
* @param inverse transform and inverse are a pair that masks the rendered values and rendered inputs by a transformation
|
|
14
|
+
* @constructor
|
|
15
|
+
*/
|
|
16
|
+
export const DualSlider = ({name,min,max,setMin,setMax,
|
|
17
|
+
transform=x=>x,inverse=x=>x}:
|
|
18
|
+
{name:string,min:number,max:number, setMin?:(min:number)=>void,
|
|
19
|
+
setMax?:(max:number)=>void,transform?:(x:number)=>number,
|
|
20
|
+
inverse?:(x:number)=>number})=>{
|
|
21
|
+
const [leftSliderPosition, setLeftSliderPosition] = useState(0); // Initial percentage for the left slider
|
|
22
|
+
const [rightSliderPosition, setRightSliderPosition] = useState(100); // Initial percentage for the right slider
|
|
23
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
24
|
+
const [leftEditing, setLeftEditing] = useState(false);
|
|
25
|
+
|
|
26
|
+
const [minOverride, setMinOverride] = useState<any>(undefined);
|
|
27
|
+
const [maxOverride, setMaxOverride] = useState<any>(undefined);
|
|
28
|
+
|
|
29
|
+
if(minOverride)
|
|
30
|
+
min = minOverride;
|
|
31
|
+
if(maxOverride)
|
|
32
|
+
max = maxOverride;
|
|
33
|
+
|
|
34
|
+
const a = transform((max-min)*leftSliderPosition/100+min);
|
|
35
|
+
const b = transform((max-min)*rightSliderPosition/100+min);
|
|
36
|
+
const left = Math.min(a,b);
|
|
37
|
+
const right = Math.max(a,b);
|
|
38
|
+
|
|
39
|
+
const sliderRef = useRef(null); // Ref for the parent box
|
|
40
|
+
|
|
41
|
+
const handleDragStart = (e:any, slider:string) => {
|
|
42
|
+
// Prevent default behavior
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
setLeftEditing(false);
|
|
45
|
+
setLeftIsNaN(false)
|
|
46
|
+
leftRef.current.blur();
|
|
47
|
+
|
|
48
|
+
setRightEditing(false);
|
|
49
|
+
setRightIsNaN(false);
|
|
50
|
+
rightRef.current.blur();
|
|
51
|
+
|
|
52
|
+
// Calculate initial positions
|
|
53
|
+
const startX = e.clientX;
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
const sliderWidth = sliderRef.current.offsetWidth;
|
|
56
|
+
|
|
57
|
+
const handleMouseMove = (e:any) => {
|
|
58
|
+
const moveX = e.clientX - startX;
|
|
59
|
+
const newPosition = ((moveX / sliderWidth) * 100) + (slider === 'left' ? leftSliderPosition : rightSliderPosition);
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
// Prevent the slider from going outside the parent box
|
|
63
|
+
const clampedPosition = Math.min(100, Math.max(0, newPosition));
|
|
64
|
+
|
|
65
|
+
// Update the position of the slider
|
|
66
|
+
if (slider === 'left') {
|
|
67
|
+
setLeftSliderPosition(clampedPosition);
|
|
68
|
+
const a = (max-min)*clampedPosition/100+min;
|
|
69
|
+
const b = (max-min)*rightSliderPosition/100+min;
|
|
70
|
+
setMin&&setMin(Math.min(a,b));
|
|
71
|
+
setMax&&setMax(Math.max(a,b));
|
|
72
|
+
|
|
73
|
+
} else if (slider === 'right') {
|
|
74
|
+
setRightSliderPosition(clampedPosition);
|
|
75
|
+
const a = (max-min)*leftSliderPosition/100+min;
|
|
76
|
+
const b = (max-min)*clampedPosition/100+min;
|
|
77
|
+
setMin&&setMin(Math.min(a,b));
|
|
78
|
+
setMax&&setMax(Math.max(a,b));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleMouseUp = () => {
|
|
84
|
+
// Remove event listeners once dragging is complete
|
|
85
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
86
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Add mouse move and mouse up listeners to document to handle drag
|
|
90
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
91
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const leftText = Math.abs(left)<0.01&&left!=0?Number(left).toExponential(3).toUpperCase():Number(left).toFixed(3);
|
|
95
|
+
const [leftEditedText,setLeftEditedText] = useState('');
|
|
96
|
+
const [leftIsNaN, setLeftIsNaN] = useState(false);
|
|
97
|
+
const leftRef = useRef<any>(null);
|
|
98
|
+
|
|
99
|
+
const [rightEditing, setRightEditing] = useState(false);
|
|
100
|
+
const [rightEditedText, setRightEditedText] = useState('');
|
|
101
|
+
const [rightIsNaN, setRightIsNaN] = useState(false);
|
|
102
|
+
const rightRef = useRef<any>(null);
|
|
103
|
+
|
|
104
|
+
// Logic to handle right value box editing...
|
|
105
|
+
const rightText = Math.abs(right) < 0.01 && right != 0 ? Number(right).toExponential(3).toUpperCase() : Number(right).toFixed(3);
|
|
106
|
+
|
|
107
|
+
return <Box sx={{display:'flex',flexDirection:'row', paddingLeft:'4px',paddingRight:'4px'}} height={20}>
|
|
108
|
+
<Box flex={0.322} fontSize={16} color={'#3D3D3D'} alignItems={'center'} display={'flex'} marginBottom={'1pt'}
|
|
109
|
+
fontFamily={'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif'}>
|
|
110
|
+
{name}
|
|
111
|
+
</Box>
|
|
112
|
+
<Box sx={{display:'flex',flexDirection:'row'}} flex={1}>
|
|
113
|
+
<input ref={leftRef} style={{backgroundColor:'#f0f0f0',width:'45px', borderRadius:'2px', outline:"none",borderStyle:'none',paddingLeft:'3px',paddingRight:'3px', lineHeight:'20px',
|
|
114
|
+
whiteSpace:'nowrap',fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif',fontSize:'11px',color:leftIsNaN?'red':'black'}} value={
|
|
115
|
+
(leftEditing)?leftEditedText:leftText
|
|
116
|
+
} onKeyDown={(e) => {
|
|
117
|
+
if (e.key === 'Enter'&&!leftIsNaN) {
|
|
118
|
+
//@ts-ignore
|
|
119
|
+
e.target.blur(); // This will cause the input to lose focus
|
|
120
|
+
}
|
|
121
|
+
}} onFocus={(e)=>{
|
|
122
|
+
setLeftEditedText(e.target.value);
|
|
123
|
+
setLeftEditing(true);
|
|
124
|
+
}} onChange={(event)=>{
|
|
125
|
+
setLeftIsNaN(isNaN(Number(event.target.value)));
|
|
126
|
+
setLeftEditedText(event.target.value);
|
|
127
|
+
}} onBlur={(e)=>{
|
|
128
|
+
let val = inverse(Number(leftEditedText));
|
|
129
|
+
if(isNaN(val)) {
|
|
130
|
+
return e.preventDefault();
|
|
131
|
+
}
|
|
132
|
+
setLeftEditing(false);
|
|
133
|
+
setMin&&setMin(val);
|
|
134
|
+
let newMin = min;
|
|
135
|
+
if(val<min) {
|
|
136
|
+
setMinOverride(val);
|
|
137
|
+
newMin = val;
|
|
138
|
+
}
|
|
139
|
+
let leftPosition = (val-newMin)/(max-newMin)*100;
|
|
140
|
+
setLeftSliderPosition(leftPosition);
|
|
141
|
+
}}/>
|
|
142
|
+
|
|
143
|
+
<Box sx={{ backgroundColor: '#f0f0f0', flex: 1, marginLeft: '4px', marginRight: '4px', borderRadius: '2px', position: 'relative',
|
|
144
|
+
overflow:'hidden', opacity:isHovering?0.75:1
|
|
145
|
+
}} ref={sliderRef}
|
|
146
|
+
onMouseEnter={() => setIsHovering(true)} onMouseLeave={() => setIsHovering(false)}>
|
|
147
|
+
{/* Central gray block with two vertical black components */}
|
|
148
|
+
<Box sx={{ position: 'absolute', left: `calc(${leftSliderPosition*0.97}% - 10px)`, width: '20px', height: '100%', cursor: 'ew-resize', zIndex: 1 }}
|
|
149
|
+
onMouseDown={(e) => handleDragStart(e, 'left')}>
|
|
150
|
+
{/* Visual representation of the slider */}
|
|
151
|
+
<Box sx={{ position: 'absolute', left: '10px', width: '2px', height: '100%', backgroundColor: 'black' }} />
|
|
152
|
+
</Box>
|
|
153
|
+
|
|
154
|
+
{/* Transparent hitbox for the right slider */}
|
|
155
|
+
<Box sx={{ position: 'absolute', right: `calc(${(100 - rightSliderPosition)*0.97}% - 10px)`, width: '20px', height: '100%', cursor: 'ew-resize', zIndex: 1 }}
|
|
156
|
+
onMouseDown={(e) => handleDragStart(e, 'right')}>
|
|
157
|
+
{/* Visual representation of the slider */}
|
|
158
|
+
<Box sx={{ position: 'absolute', right: '10px', width: '2px', height: '100%', backgroundColor: 'black' }} />
|
|
159
|
+
</Box>
|
|
160
|
+
</Box>
|
|
161
|
+
|
|
162
|
+
<input style={{backgroundColor: '#f0f0f0', width: '45px', borderRadius: '2px', outline: "none", borderStyle: 'none', paddingLeft: '3px', paddingRight: '3px', lineHeight: '20px',
|
|
163
|
+
whiteSpace: 'nowrap', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif', fontSize: '11px', color: rightIsNaN ? 'red' : 'black'}}
|
|
164
|
+
value={(rightEditing) ? rightEditedText : rightText}
|
|
165
|
+
ref={rightRef}
|
|
166
|
+
onKeyDown={(e) => {
|
|
167
|
+
if (e.key === 'Enter' && !rightIsNaN) {
|
|
168
|
+
//@ts-ignore
|
|
169
|
+
e.target.blur(); // This will cause the input to lose focus
|
|
170
|
+
}
|
|
171
|
+
}}
|
|
172
|
+
onFocus={(e) => {
|
|
173
|
+
setRightEditedText(e.target.value);
|
|
174
|
+
setRightEditing(true);
|
|
175
|
+
}}
|
|
176
|
+
onChange={(event) => {
|
|
177
|
+
setRightIsNaN(isNaN(Number(event.target.value)));
|
|
178
|
+
setRightEditedText(event.target.value);
|
|
179
|
+
}}
|
|
180
|
+
onBlur={(e) => {
|
|
181
|
+
let val = inverse(Number(rightEditedText));
|
|
182
|
+
if (isNaN(val)) {
|
|
183
|
+
return e.preventDefault();
|
|
184
|
+
}
|
|
185
|
+
setRightEditing(false);
|
|
186
|
+
setMax && setMax(val);
|
|
187
|
+
let newMax = max;
|
|
188
|
+
if (val > max) {
|
|
189
|
+
setMaxOverride(val);
|
|
190
|
+
newMax = val;
|
|
191
|
+
}
|
|
192
|
+
let rightPosition = (val - min) / (newMax - min) * 100;
|
|
193
|
+
setRightSliderPosition(rightPosition);
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
</Box>
|
|
197
|
+
</Box>
|
|
198
|
+
}
|