back-testing-react 1.0.4 → 1.0.6
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/dist/cjs/index.js +17 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +17 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +35 -10
- package/package.json +5 -1
- package/src/components/back-testing-cat-legend/back-testing-cat-legend.css +1 -1
- package/src/components/back-testing-cat-legend/back-testing-cat-legend.tsx +7 -7
- package/src/components/back-testing-map/back-testing-map.css +1 -1
- package/src/components/back-testing-map/back-testing-map.service.ts +21 -5
- package/src/components/back-testing-map/back-testing-map.tsx +51 -18
- package/src/components/back-testing-map/back-testing-map.types.ts +74 -11
- package/src/components/back-testing-payout-info/back-testing-payout-info.css +112 -0
- package/src/components/back-testing-payout-info/back-testing-payout-info.tsx +371 -0
- package/src/components/back-testing-payout-info/back-testing-payout-info.types.ts +9 -0
- package/src/components/back-testing-stepper/back-testing-stepper.css +30 -0
- package/src/components/back-testing-stepper/back-testing-stepper.tsx +322 -0
- package/src/components/back-testing-stepper/back-testing-stepper.types.ts +41 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.css +57 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.tsx +93 -0
- package/src/components/back-testing-stepper/inputs/distance-input/distance-input.types.ts +41 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.css +26 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.tsx +43 -0
- package/src/components/back-testing-stepper/inputs/file-input/file-input.types.ts +10 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.css +48 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.tsx +55 -0
- package/src/components/back-testing-stepper/inputs/number-input/number-input.types.ts +12 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.css +13 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.stories.tsx +27 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.tsx +63 -0
- package/src/components/back-testing-stepper/inputs/select-input/select-input.types.ts +8 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.css +35 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.stories.tsx +18 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.tsx +32 -0
- package/src/components/back-testing-stepper/inputs/text-input/text-input.types.ts +6 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.css +16 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.stories.tsx +20 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.tsx +151 -0
- package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.types.ts +11 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.css +0 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.tsx +188 -0
- package/src/components/back-testing-stepper/steps/input-anemometer/input-anemometer.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.scss +0 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.tsx +225 -0
- package/src/components/back-testing-stepper/steps/input-ciac/input-ciac.types.ts +17 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.css +0 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.tsx +135 -0
- package/src/components/back-testing-stepper/steps/input-location/input-location.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.css +0 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.tsx +173 -0
- package/src/components/back-testing-stepper/steps/input-proxy/input-proxy.types.ts +16 -0
- package/src/components/back-testing-stepper/steps/step.types.ts +58 -0
- package/src/components/back-testing-storm-legend/back-testing-storm-legend.stories.tsx +56 -33
- package/src/components/back-testing-wizard/back-testing-wizard.css +36 -0
- package/src/components/back-testing-wizard/back-testing-wizard.stories.tsx +734 -0
- package/src/components/back-testing-wizard/back-testing-wizard.tsx +102 -0
- package/src/components/back-testing-wizard/back-testing-wizard.types.ts +12 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.number-input-container{
|
|
2
|
+
padding: 0;
|
|
3
|
+
font-family: system-ui, sans-serif;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.number-input-container .label{
|
|
7
|
+
font-weight: 700;
|
|
8
|
+
padding-bottom: 5px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.number-input-container .flex-container{
|
|
12
|
+
border: 0.5px solid lightgray;
|
|
13
|
+
display:flex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.number-input-container .flex-container .prefix{
|
|
17
|
+
background-color: lightgray;
|
|
18
|
+
padding: 8px 15px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.number-input-container .flex-container.error{
|
|
22
|
+
border: 0.5px solid rgb(211,47,47);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.number-input-container .flex-container.error:focus-within{
|
|
26
|
+
border: 0.5px solid rgb(211,47,47) !important;
|
|
27
|
+
box-shadow: inset 1px 1px rgb(211,47,47), inset -1px -1px rgb(211,47,47);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.number-input-container .flex-container.error:focus-within .prefix{
|
|
31
|
+
box-shadow: inset 1px 1px rgb(211,47,47), inset 0px -1px rgb(211,47,47);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.number-input-container .flex-container:hover:not(.error:focus-within){
|
|
35
|
+
border: 0.5px solid black;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.number-input-container input{
|
|
39
|
+
width: 100%;
|
|
40
|
+
padding: 8px 10px;
|
|
41
|
+
font-size: 1rem;
|
|
42
|
+
border: 0;
|
|
43
|
+
background-color: rgba(255,255,255,0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.number-input-container input:focus-visible{
|
|
47
|
+
outline: 0;
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import NumberInput from "./number-input";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: NumberInput,
|
|
7
|
+
} as Meta<typeof NumberInput>;
|
|
8
|
+
|
|
9
|
+
const Template: StoryFn<typeof NumberInput> = (args) => <NumberInput {...args} />;
|
|
10
|
+
|
|
11
|
+
export const NumberInputTest = Template.bind({});
|
|
12
|
+
NumberInputTest.args = {
|
|
13
|
+
label:"Limit of Insurance:",
|
|
14
|
+
value:145000,
|
|
15
|
+
error:false,
|
|
16
|
+
errorMessage:"Invalid input",
|
|
17
|
+
prefix:"$",
|
|
18
|
+
step:1,
|
|
19
|
+
min:0
|
|
20
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./number-input.css";
|
|
5
|
+
import { NumberInputProps } from "./number-input.types";
|
|
6
|
+
|
|
7
|
+
function NumberInput(props: NumberInputProps){
|
|
8
|
+
|
|
9
|
+
const [inputValue, setInputValue] = React.useState(props.value);
|
|
10
|
+
|
|
11
|
+
function handleChange(e: number){
|
|
12
|
+
if(Number.isNaN(e)){
|
|
13
|
+
setInputValue("")
|
|
14
|
+
if(props.onChange){
|
|
15
|
+
props.onChange(0)
|
|
16
|
+
}
|
|
17
|
+
}else{
|
|
18
|
+
setInputValue(e)
|
|
19
|
+
if(props.onChange){
|
|
20
|
+
props.onChange(e)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return(
|
|
26
|
+
<div className="number-input-container">
|
|
27
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
28
|
+
<div className={"flex-container" + (props.error ? " error" : "")}>
|
|
29
|
+
{ props.prefix != undefined && <label className="prefix">{props.prefix}</label>}
|
|
30
|
+
<input
|
|
31
|
+
type='number'
|
|
32
|
+
value={inputValue}
|
|
33
|
+
min={props.min}
|
|
34
|
+
max={props.max}
|
|
35
|
+
onChange={(e) => handleChange(e.target.valueAsNumber)}
|
|
36
|
+
step={props.step}/>
|
|
37
|
+
{ props.postfix != undefined && <label className="prefix">{props.postfix}</label>}
|
|
38
|
+
</div>
|
|
39
|
+
{ props.error && <span style={{
|
|
40
|
+
color: "rgb(211,47,47)",
|
|
41
|
+
fontSize:'0.75rem',
|
|
42
|
+
margin:'0 14px',
|
|
43
|
+
marginTop:'3px',
|
|
44
|
+
display:'inline-block',
|
|
45
|
+
width:'calc(100%-28px)',
|
|
46
|
+
lineHeight: 1.66,
|
|
47
|
+
letterSpacing: '0.03333em',
|
|
48
|
+
fontFamily:'"Roboto","Helvetica","Arial",sans-serif' }}>
|
|
49
|
+
{props.errorMessage}
|
|
50
|
+
</span>}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default NumberInput
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import SelectInput from "./select-input";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: SelectInput,
|
|
7
|
+
} as Meta<typeof SelectInput>;
|
|
8
|
+
|
|
9
|
+
enum PayoutOption {
|
|
10
|
+
STANDARD_74_119 = 234875,
|
|
11
|
+
RESIDENTIAL_65_110 = 481734,
|
|
12
|
+
RESIDENTIAL_65_130 = 598652,
|
|
13
|
+
CIAC_10_25_50_75_100 = 861543,
|
|
14
|
+
CIAC_30_60_100_100_100 = 329134,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Template: StoryFn<typeof SelectInput> = (args) => <SelectInput {...args} />;
|
|
18
|
+
let payoutOption = PayoutOption.CIAC_10_25_50_75_100;
|
|
19
|
+
|
|
20
|
+
export const SelectInputTest = Template.bind({});
|
|
21
|
+
SelectInputTest.args = {
|
|
22
|
+
disabled:false,
|
|
23
|
+
onLoad:((e:PayoutOption) => payoutOption = e),
|
|
24
|
+
onChange:((e:PayoutOption) => payoutOption = e),
|
|
25
|
+
defaultValue:payoutOption,
|
|
26
|
+
items:[{key:"CIAC 10/25/50/75/100",value:PayoutOption.CIAC_10_25_50_75_100},{key:"CIAC 30/60/100/100/100",value:PayoutOption.CIAC_30_60_100_100_100}]
|
|
27
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select, SelectChangeEvent } from "@mui/material";
|
|
4
|
+
import "./select-input.css"
|
|
5
|
+
import React, { useEffect } from "react";
|
|
6
|
+
import { SelectInputProps } from "./select-input.types";
|
|
7
|
+
|
|
8
|
+
const ITEM_HEIGHT = 48;
|
|
9
|
+
const ITEM_PADDING_TOP = 8;
|
|
10
|
+
const MenuProps = {
|
|
11
|
+
PaperProps: {
|
|
12
|
+
style: {
|
|
13
|
+
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
|
14
|
+
width: 250,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function SelectInput(props:SelectInputProps){
|
|
20
|
+
|
|
21
|
+
const [val, setVal] = React.useState<number>(props.defaultValue);
|
|
22
|
+
|
|
23
|
+
const handleChange = (event: SelectChangeEvent<number>) => {
|
|
24
|
+
setVal(event.target.value as number);
|
|
25
|
+
if( props.onChange != undefined){
|
|
26
|
+
props.onChange(event.target.value as number);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if( props.onLoad != undefined){
|
|
32
|
+
props.onLoad(props.defaultValue)
|
|
33
|
+
}
|
|
34
|
+
},[props.defaultValue,props.onLoad])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="select-input-container">
|
|
39
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
40
|
+
<FormControl sx={{ m: 0, width: '100%' }}>
|
|
41
|
+
<Select
|
|
42
|
+
id="select-combobox"
|
|
43
|
+
disabled={props.disabled}
|
|
44
|
+
sx={{borderRadius: 0, p: 0}}
|
|
45
|
+
value={val}
|
|
46
|
+
onChange={handleChange}
|
|
47
|
+
input={<OutlinedInput sx={{p:0}}/>}
|
|
48
|
+
MenuProps={MenuProps}
|
|
49
|
+
>
|
|
50
|
+
{props.items.map((item) => (
|
|
51
|
+
<MenuItem
|
|
52
|
+
key={item.key}
|
|
53
|
+
value={item.value}>
|
|
54
|
+
{item.key}
|
|
55
|
+
</MenuItem>
|
|
56
|
+
))}
|
|
57
|
+
</Select>
|
|
58
|
+
</FormControl>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default SelectInput
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.text-input-container{
|
|
2
|
+
padding: 0;
|
|
3
|
+
font-family: system-ui, sans-serif;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.text-input-container .label{
|
|
7
|
+
font-weight: 700;
|
|
8
|
+
padding-bottom: 5px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.text-input-container .flex-container{
|
|
12
|
+
border: 0.5px solid lightgray;
|
|
13
|
+
display:flex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.text-input-container .flex-container .prefix{
|
|
17
|
+
background-color: lightgray;
|
|
18
|
+
padding: 8px 15px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.text-input-container .flex-container:hover{
|
|
22
|
+
border: 0.5px solid black;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.text-input-container input{
|
|
26
|
+
width: 100%;
|
|
27
|
+
padding: 8px 10px;
|
|
28
|
+
font-size: 1rem;
|
|
29
|
+
border: 0;
|
|
30
|
+
background-color: rgba(255,255,255,0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.text-input-container input:focus-visible{
|
|
34
|
+
outline: 0;
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import TextInput from "./text-input";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: TextInput,
|
|
7
|
+
} as Meta<typeof TextInput>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const Template: StoryFn<typeof TextInput> = (args) => <TextInput {...args} />;
|
|
11
|
+
let value = "";
|
|
12
|
+
|
|
13
|
+
export const TextInputTest = Template.bind({});
|
|
14
|
+
TextInputTest.args = {
|
|
15
|
+
label:"Anemometer Code:",
|
|
16
|
+
value:value,
|
|
17
|
+
onChange:((e:string) => value = e),
|
|
18
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./text-input.css"
|
|
5
|
+
import { TextInputProps } from "./text-input.types";
|
|
6
|
+
|
|
7
|
+
function TextInput(props: TextInputProps){
|
|
8
|
+
|
|
9
|
+
const [inputValue, setInputValue] = React.useState(props.value);
|
|
10
|
+
|
|
11
|
+
function handleChange(e: string){
|
|
12
|
+
setInputValue(e)
|
|
13
|
+
if(props.onChange != undefined){
|
|
14
|
+
props.onChange(e)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return(
|
|
19
|
+
<div className="text-input-container">
|
|
20
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
21
|
+
<div className="flex-container">
|
|
22
|
+
{ props.prefix != undefined && <label className="prefix">{props.prefix}</label>}
|
|
23
|
+
<input
|
|
24
|
+
type='text'
|
|
25
|
+
value={inputValue}
|
|
26
|
+
onChange={(e) => handleChange(e.target.value)}/>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default TextInput
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.search {
|
|
2
|
+
font-family: system-ui, sans-serif;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.search .label{
|
|
6
|
+
font-weight: 700;
|
|
7
|
+
padding-bottom: 5px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.search .MuiInputBase-root.MuiOutlinedInput-root{
|
|
11
|
+
border-radius: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.search .MuiInputBase-root.MuiOutlinedInput-root input{
|
|
15
|
+
padding: 8px 10px;
|
|
16
|
+
}
|
package/src/components/back-testing-stepper/inputs/worldmap-search/worldmap-search.stories.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StoryFn, Meta } from "@storybook/react";
|
|
2
|
+
import WorldMapSearch from "./worldmap-search";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/Inputs",
|
|
6
|
+
component: WorldMapSearch,
|
|
7
|
+
} as Meta<typeof WorldMapSearch>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const Template: StoryFn<typeof WorldMapSearch> = (args) => <WorldMapSearch {...args} />;
|
|
11
|
+
let value = "";
|
|
12
|
+
|
|
13
|
+
export const WorldMapSearchTest = Template.bind({});
|
|
14
|
+
WorldMapSearchTest.args = {
|
|
15
|
+
label:"Risk Location Address:",
|
|
16
|
+
error:false,
|
|
17
|
+
errorMessage:"",
|
|
18
|
+
value:value,
|
|
19
|
+
onChange:((e:string) => value = e)
|
|
20
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { useSearchBoxCore } from "@mapbox/search-js-react";
|
|
2
|
+
import "mapbox-gl/dist/mapbox-gl.css";
|
|
3
|
+
import React, { ChangeEvent, useEffect } from "react";
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import Box from '@mui/material/Box';
|
|
6
|
+
import IconButton from '@mui/material/IconButton';
|
|
7
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
8
|
+
import ClearIcon from '@mui/icons-material/Clear';
|
|
9
|
+
import Paper from '@mui/material/Paper';
|
|
10
|
+
import MenuList from '@mui/material/MenuList';
|
|
11
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
12
|
+
import ListItemIcon from '@mui/material/ListItemIcon';
|
|
13
|
+
import Typography from '@mui/material/Typography';
|
|
14
|
+
import PlaceIcon from '@mui/icons-material/Place';
|
|
15
|
+
import "./worldmap-search.css";
|
|
16
|
+
import { TextField } from "@mui/material";
|
|
17
|
+
import { WorldMapSearchProps } from "./worldmap-search.types";
|
|
18
|
+
|
|
19
|
+
function useOutsideAlerter(ref:any,closeMenuFunction:any) {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
function handleClickOutside(event: any) {
|
|
22
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
23
|
+
closeMenuFunction(false);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Bind the event listener
|
|
27
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
28
|
+
return () => {
|
|
29
|
+
// Unbind the event listener on clean up
|
|
30
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
31
|
+
};
|
|
32
|
+
}, [ref]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function WorldMapSearch(props: WorldMapSearchProps){
|
|
36
|
+
|
|
37
|
+
const wrapperRef = React.useRef(null);
|
|
38
|
+
const [inputValue, setInputValue] = React.useState(props.value);
|
|
39
|
+
const [openMenu, toggleMenu] = React.useState(false);
|
|
40
|
+
const [searchSuggestions, setSearchSuggestions] = React.useState<any[]>([]);
|
|
41
|
+
const searchBoxCore = useSearchBoxCore({ accessToken: props.accessToken });
|
|
42
|
+
const sessionToken = uuidv4();
|
|
43
|
+
|
|
44
|
+
useOutsideAlerter(wrapperRef,toggleMenuView);
|
|
45
|
+
|
|
46
|
+
async function handleChange(e: ChangeEvent<HTMLInputElement>){
|
|
47
|
+
var value = e.target.value;
|
|
48
|
+
setInputValue(e.target.value)
|
|
49
|
+
if(props.onChange != undefined){
|
|
50
|
+
props.onChange(e.target.value)
|
|
51
|
+
}
|
|
52
|
+
if(value.length > 0){
|
|
53
|
+
const response = await searchBoxCore.suggest(value, {
|
|
54
|
+
sessionToken: sessionToken
|
|
55
|
+
});
|
|
56
|
+
setSearchSuggestions(response.suggestions)
|
|
57
|
+
}else{
|
|
58
|
+
setSearchSuggestions([])
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleClearLocationInput(e:any){
|
|
63
|
+
setInputValue("")
|
|
64
|
+
setSearchSuggestions([])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isLocationMenuDisplayed(){
|
|
68
|
+
return openMenu && searchSuggestions && searchSuggestions.length > 0 && inputValue && inputValue.length > 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createLocationContext(context: any){
|
|
72
|
+
let retVal: string = '';
|
|
73
|
+
const country: string = context["country"]?.name;
|
|
74
|
+
const region: string = context["region"]?.name;
|
|
75
|
+
const place: string = context["place"]?.name;
|
|
76
|
+
retVal = place ? retVal + place + ", ": retVal;
|
|
77
|
+
retVal = region ? retVal + region + ", ": retVal;
|
|
78
|
+
retVal = country ? retVal + country: retVal;
|
|
79
|
+
retVal = retVal.trim()
|
|
80
|
+
if(retVal.endsWith(',')){
|
|
81
|
+
retVal = retVal.substring(0, retVal.length - 1)
|
|
82
|
+
}
|
|
83
|
+
return retVal.trim();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function retrieveSuggestion(suggestion: any){
|
|
87
|
+
const response = await searchBoxCore.retrieve(suggestion, {
|
|
88
|
+
sessionToken: sessionToken
|
|
89
|
+
})
|
|
90
|
+
setInputValue(response.features[0].properties.name);
|
|
91
|
+
if(props.onChange != undefined){
|
|
92
|
+
props.onChange(response.features[0].properties.name)
|
|
93
|
+
}
|
|
94
|
+
if (props.onLocationRetrieved != undefined){
|
|
95
|
+
props.onLocationRetrieved(response)
|
|
96
|
+
}
|
|
97
|
+
toggleMenuView(false)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toggleMenuView(val: boolean){
|
|
101
|
+
toggleMenu(val);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return(
|
|
105
|
+
<div ref={wrapperRef} className="search">
|
|
106
|
+
{ props.label != undefined && <div className="label"><label>{props.label}</label></div>}
|
|
107
|
+
<TextField
|
|
108
|
+
variant="outlined"
|
|
109
|
+
error={props.error}
|
|
110
|
+
helperText={props.errorMessage}
|
|
111
|
+
onChange={handleChange}
|
|
112
|
+
onFocus={(e) => toggleMenuView(true)}
|
|
113
|
+
id="risk-location"
|
|
114
|
+
sx={{ m: 0, width: '100%', borderRadius: '0'}}
|
|
115
|
+
value={inputValue}
|
|
116
|
+
slotProps={{
|
|
117
|
+
input: {
|
|
118
|
+
endAdornment: <InputAdornment position="end">
|
|
119
|
+
<IconButton
|
|
120
|
+
aria-label="clear risk-location"
|
|
121
|
+
onClick={handleClearLocationInput}
|
|
122
|
+
edge="end"
|
|
123
|
+
>
|
|
124
|
+
<ClearIcon/>
|
|
125
|
+
</IconButton>
|
|
126
|
+
</InputAdornment>,
|
|
127
|
+
},
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
{isLocationMenuDisplayed() && <Paper sx={{ width: '100%', maxWidth: '100%', marginTop: '10px' }}>
|
|
131
|
+
<MenuList>
|
|
132
|
+
{searchSuggestions.map((option: any) => (
|
|
133
|
+
<MenuItem key={option.mapbox_id} onClick={ () => retrieveSuggestion(option)}>
|
|
134
|
+
<ListItemIcon>
|
|
135
|
+
<PlaceIcon fontSize="small" />
|
|
136
|
+
</ListItemIcon>
|
|
137
|
+
<Box sx={{ overflow: "hidden" }}>
|
|
138
|
+
<Typography variant="body2" title={option.name} component="div" sx={{ fontWeight: 700, textOverflow: "ellipsis", overflow: "hidden" }}>
|
|
139
|
+
{option.name}
|
|
140
|
+
</Typography>
|
|
141
|
+
<Typography component="div" title={createLocationContext(option.context)} sx={{ color: 'text.secondary', fontSize: 13, textOverflow: "ellipsis", overflow: "hidden" }}>{createLocationContext(option.context)}</Typography>
|
|
142
|
+
</Box>
|
|
143
|
+
</MenuItem>
|
|
144
|
+
))}
|
|
145
|
+
</MenuList>
|
|
146
|
+
</Paper>}
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default WorldMapSearch
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SearchBoxRetrieveResponse } from "@mapbox/search-js-core"
|
|
2
|
+
|
|
3
|
+
export interface WorldMapSearchProps {
|
|
4
|
+
error?: boolean,
|
|
5
|
+
errorMessage?: string,
|
|
6
|
+
value?: string,
|
|
7
|
+
onChange?: (e:string) => void,
|
|
8
|
+
accessToken : string,
|
|
9
|
+
label?: string
|
|
10
|
+
onLocationRetrieved?: (e: SearchBoxRetrieveResponse) => void
|
|
11
|
+
}
|
|
File without changes
|