@geops/rvf-mobility-web-component 0.1.83 → 0.1.85

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/index.js +206 -187
  3. package/package.json +2 -2
  4. package/src/Departure/Departure.tsx +6 -3
  5. package/src/FeatureDetails/FeatureDetails.tsx +4 -4
  6. package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +22 -0
  7. package/src/LayerTreeMenu/LayerTreeMenu.tsx +12 -4
  8. package/src/LayoutState/LayoutState.tsx +144 -63
  9. package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +2 -2
  10. package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +2 -40
  11. package/src/MapLayout/MapLayout.tsx +2 -7
  12. package/src/MobilityMap/MobilityMap.tsx +1 -6
  13. package/src/MobilityMap/MobilityMapAttributes.ts +28 -7
  14. package/src/NotificationDetails/NotificationDetails.tsx +21 -22
  15. package/src/NotificationsLayer/NotificationsLayer.tsx +47 -3
  16. package/src/OverlayDetails/OverlayDetails.tsx +5 -0
  17. package/src/RealtimeLayer/RealtimeLayer.tsx +44 -50
  18. package/src/RouteIcon/RouteIcon.tsx +25 -14
  19. package/src/RouteSchedule/RouteSchedule.tsx +14 -11
  20. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +6 -3
  21. package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +8 -2
  22. package/src/RouteStop/RouteStop.tsx +14 -29
  23. package/src/RouteStopPlatform/RouteStopPlatform.tsx +1 -3
  24. package/src/RouteStopProgress/RouteStopProgress.tsx +11 -18
  25. package/src/RouteStopStation/RouteStopStation.tsx +3 -3
  26. package/src/RvfRealtimeLayer/RvfRealtimeLayer.tsx +16 -3
  27. package/src/RvfSearch/RvfSearch.tsx +5 -4
  28. package/src/Search/Search.tsx +41 -20
  29. package/src/Search/SearchBase.tsx +169 -0
  30. package/src/Search/SearchHeadless.tsx +79 -0
  31. package/src/Search/index.tsx +2 -0
  32. package/src/SearchLinesResult/SearchLinesResult.tsx +43 -0
  33. package/src/SearchLinesResult/index.tsx +1 -0
  34. package/src/SearchLinesResults/SearchLinesResults.tsx +106 -0
  35. package/src/SearchLinesResults/index.tsx +1 -0
  36. package/src/SearchLnpStopsResult/SearchLnpStopsResult.tsx +42 -0
  37. package/src/SearchLnpStopsResult/index.tsx +1 -0
  38. package/src/SearchLnpStopsResults/SearchLnpStopsResults.tsx +106 -0
  39. package/src/SearchLnpStopsResults/index.tsx +1 -0
  40. package/src/SearchResult/SearchResult.tsx +25 -0
  41. package/src/SearchResult/index.tsx +1 -0
  42. package/src/SearchResults/SearchResults.tsx +81 -0
  43. package/src/SearchResults/index.tsx +1 -0
  44. package/src/SearchResultsHeader/SearchResultsHeader.tsx +36 -0
  45. package/src/SearchResultsHeader/index.tsx +1 -0
  46. package/src/SearchStopsResult/SearchStopsResult.tsx +43 -0
  47. package/src/SearchStopsResult/index.tsx +1 -0
  48. package/src/SearchStopsResults/SearchStopsResults.tsx +101 -0
  49. package/src/SearchStopsResults/index.tsx +1 -0
  50. package/src/SearchTrainsResult/SearchTrainsResult.tsx +42 -0
  51. package/src/SearchTrainsResult/index.tsx +1 -0
  52. package/src/SearchTrainsResults/SearchTrainsResults.tsx +109 -0
  53. package/src/SearchTrainsResults/index.tsx +1 -0
  54. package/src/SingleClickListener/SingleClickListener.tsx +38 -2
  55. package/src/Station/Station.tsx +23 -48
  56. package/src/StationHeader/StationHeader.tsx +3 -3
  57. package/src/StationsLayer/StationsLayer.tsx +9 -1
  58. package/src/StopsSearch/StopsSearch.tsx +2 -2
  59. package/src/ui/InputSearch/InputSearch.tsx +105 -0
  60. package/src/ui/InputSearch/index.tsx +1 -0
  61. package/src/utils/centerOnVehicle.ts +9 -2
  62. package/src/utils/constants.ts +8 -1
  63. package/src/utils/fullTrajectoryStyle.ts +4 -7
  64. package/src/utils/getBgColor.ts +4 -2
  65. package/src/utils/getDelayColorForVehicle.test.ts +21 -11
  66. package/src/utils/getDelayColorForVehicle.ts +7 -5
  67. package/src/utils/getDelayTextForVehicle.test.ts +12 -12
  68. package/src/utils/getDelayTextForVehicle.ts +4 -0
  69. package/src/utils/getMainColorForVehicle.ts +11 -4
  70. package/src/utils/getRadius.ts +9 -3
  71. package/src/utils/getTextColor.ts +1 -1
  72. package/src/utils/getTextColorForVehicle.ts +31 -0
  73. package/src/utils/getTextFontForVehicle.test.ts +1 -1
  74. package/src/utils/getTextFontForVehicle.tsx +11 -3
  75. package/src/utils/getTextForVehicle.ts +7 -1
  76. package/src/utils/hooks/useFit.tsx +69 -0
  77. package/src/utils/hooks/useFitOnFeatures.tsx +77 -0
  78. package/src/utils/hooks/useLayersConfig.tsx +3 -0
  79. package/src/utils/hooks/useLnp.tsx +39 -5
  80. package/src/utils/hooks/useMapContext.tsx +2 -5
  81. package/src/utils/hooks/useRealtimeDepartures.tsx +45 -0
  82. package/src/utils/hooks/useRealtimeRenderedTrajectory.tsx +42 -0
  83. package/src/utils/hooks/useRealtimeStation.tsx +39 -0
  84. package/src/utils/hooks/useRealtimeStopSequences.tsx +43 -0
  85. package/src/utils/hooks/useRealtimeTrainsByRouteIdentifier.tsx +71 -0
  86. package/src/utils/hooks/useRouteStop.tsx +7 -1
  87. package/src/utils/hooks/useSearchLines.tsx +34 -0
  88. package/src/utils/hooks/useSearchLnpStops.tsx +38 -0
  89. package/src/utils/hooks/useSearchStops.tsx +85 -0
  90. package/src/utils/hooks/useSearchTrains.tsx +83 -0
  91. package/src/utils/realtimeRVFStyle.ts +38 -30
  92. package/src/utils/translations.ts +17 -0
  93. package/tash +58 -0
  94. package/src/utils/centerOnStation.ts +0 -18
  95. package/src/utils/getDelayFontForVehicle.test.ts +0 -7
  96. package/src/utils/getDelayFontForVehicle.tsx +0 -8
  97. package/src/utils/hooks/useStation.tsx +0 -22
@@ -6,8 +6,14 @@ import RouteInfos from "../RouteInfos";
6
6
  import IconButton from "../ui/IconButton";
7
7
  import useMapContext from "../utils/hooks/useMapContext";
8
8
 
9
- function RouteScheduleHeader() {
10
- const { isFollowing, setIsFollowing, stopSequence } = useMapContext();
9
+ import type { RealtimeStopSequence } from "mobility-toolbox-js/types";
10
+
11
+ function RouteScheduleHeader({
12
+ stopSequence,
13
+ }: {
14
+ stopSequence: RealtimeStopSequence;
15
+ }) {
16
+ const { isFollowing, setIsFollowing } = useMapContext();
11
17
  return (
12
18
  <div className="flex items-center gap-x-4 bg-slate-100 p-4 py-2">
13
19
  <RouteIcon stopSequence={stopSequence} />
@@ -8,10 +8,14 @@ import RouteStopStation from "../RouteStopStation";
8
8
  import RouteStopTime from "../RouteStopTime";
9
9
  import getStopStatus from "../utils/getStopStatus";
10
10
  import useMapContext from "../utils/hooks/useMapContext";
11
+ import useRealtimeStation from "../utils/hooks/useRealtimeStation";
11
12
  import { RouteStopContext } from "../utils/hooks/useRouteStop";
12
13
 
13
- import type { RealtimeStation, RealtimeStop } from "mobility-toolbox-js/types";
14
- import type { JSX, PreactDOMAttributes } from "preact";
14
+ import type {
15
+ RealtimeStop,
16
+ RealtimeStopSequence,
17
+ } from "mobility-toolbox-js/types";
18
+ import type { HTMLAttributes, PreactDOMAttributes } from "preact";
15
19
 
16
20
  export type RouteScheduleStopProps = {
17
21
  classNameGreyOut?: string;
@@ -20,7 +24,8 @@ export type RouteScheduleStopProps = {
20
24
  stop?: {
21
25
  platform?: string;
22
26
  } & RealtimeStop;
23
- } & JSX.HTMLAttributes<HTMLButtonElement> &
27
+ stopSequence: RealtimeStopSequence;
28
+ } & HTMLAttributes<HTMLButtonElement> &
24
29
  PreactDOMAttributes;
25
30
 
26
31
  function RouteStop({
@@ -29,15 +34,13 @@ function RouteStop({
29
34
  index,
30
35
  invertColor = false,
31
36
  stop,
37
+ stopSequence,
32
38
  ...props
33
39
  }: RouteScheduleStopProps) {
34
- const { map, realtimeLayer, stopSequence } = useMapContext();
35
- const {
36
- // @ts-expect-error bad type definition
37
- stopUID,
38
- } = stop;
39
- const [station, setStation] = useState<RealtimeStation>();
40
+ const { map } = useMapContext();
41
+ const { stopUID } = stop;
40
42
  const [status, setStatus] = useState(getStopStatus(stopSequence, index));
43
+ const station = useRealtimeStation(stopUID);
41
44
 
42
45
  useEffect(() => {
43
46
  let interval = null;
@@ -54,27 +57,9 @@ function RouteStop({
54
57
  };
55
58
  }, [index, status.isInBetween, stopSequence]);
56
59
 
57
- useEffect(() => {
58
- if (!stopUID || !realtimeLayer?.api) {
59
- return;
60
- }
61
- realtimeLayer?.api?.subscribe(`station ${stopUID}`, ({ content }) => {
62
- if (content) {
63
- setStation(content);
64
- }
65
- });
66
-
67
- return () => {
68
- setStation(null);
69
- if (stopUID) {
70
- realtimeLayer?.api?.unsubscribe(`station ${stopUID}`);
71
- }
72
- };
73
- }, [stopUID, realtimeLayer?.api]);
74
-
75
60
  const routeStopState = useMemo(() => {
76
- return { index, invertColor, station, status, stop };
77
- }, [stop, status, index, invertColor, station]);
61
+ return { index, invertColor, station, status, stop, stopSequence };
62
+ }, [stop, status, index, invertColor, station, stopSequence]);
78
63
 
79
64
  let colorScheme = status.isPassed || status.isLeft ? classNameGreyOut : "";
80
65
 
@@ -1,7 +1,6 @@
1
1
  import { memo } from "preact/compat";
2
2
 
3
3
  import useI18n from "../utils/hooks/useI18n";
4
- import useMapContext from "../utils/hooks/useMapContext";
5
4
  import useRouteStop from "../utils/hooks/useRouteStop";
6
5
 
7
6
  import type { JSX, PreactDOMAttributes } from "preact";
@@ -10,8 +9,7 @@ export type RouteStopPlatformProps = JSX.HTMLAttributes<HTMLSpanElement> &
10
9
  PreactDOMAttributes;
11
10
 
12
11
  function RouteStopPlatform({ ...props }: RouteStopPlatformProps) {
13
- const { stop } = useRouteStop();
14
- const { stopSequence } = useMapContext();
12
+ const { stop, stopSequence } = useRouteStop();
15
13
  const { type } = stopSequence;
16
14
  const { t } = useI18n();
17
15
  const { platform } = stop || {};
@@ -1,40 +1,33 @@
1
1
  import { memo } from "preact/compat";
2
2
 
3
3
  import getMainColorForVehicle from "../utils/getMainColorForVehicle";
4
- import useMapContext from "../utils/hooks/useMapContext";
5
4
  import useRouteStop from "../utils/hooks/useRouteStop";
6
5
 
7
- import type { JSX, PreactDOMAttributes } from "preact";
6
+ import type { HTMLAttributes, PreactDOMAttributes } from "preact";
8
7
 
9
8
  export type RouteStopProgressProps = {
10
- lineColor?: string;
11
- svgProps?: JSX.HTMLAttributes<SVGElement> & PreactDOMAttributes;
12
- } & JSX.HTMLAttributes<HTMLDivElement> &
9
+ svgProps?: HTMLAttributes<SVGElement> & PreactDOMAttributes;
10
+ } & HTMLAttributes<HTMLDivElement> &
13
11
  PreactDOMAttributes;
14
12
 
15
- function RouteStopProgress({
16
- lineColor,
17
- svgProps,
18
- ...props
19
- }: RouteStopProgressProps) {
20
- const { stopSequence } = useMapContext();
21
- const { invertColor, status } = useRouteStop();
13
+ function RouteStopProgress({ svgProps, ...props }: RouteStopProgressProps) {
14
+ const { invertColor, status, stopSequence } = useRouteStop();
22
15
  const { isBoarding, isFirst, isLast, isLeft, isPassed, progress } = status;
23
16
  const y1 = isFirst ? "50%" : "-100%";
24
17
  const y2 = isLast ? "50%" : "100%";
25
18
  const yDone = `${progress}%`;
26
19
 
27
20
  const greyColor = "rgb(156, 163, 175)";
28
- const lineColorr = lineColor || getMainColorForVehicle(stopSequence);
21
+ const lineColor = getMainColorForVehicle(stopSequence);
29
22
 
30
- let colorScheme = isPassed ? greyColor : lineColorr;
31
- let invertColorScheme = isPassed ? lineColorr : greyColor;
23
+ let colorScheme = isPassed ? greyColor : lineColor;
24
+ let invertColorScheme = isPassed ? lineColor : greyColor;
32
25
  let progressDoneColor = greyColor;
33
26
 
34
27
  if (invertColor) {
35
- colorScheme = isPassed ? lineColorr : greyColor;
36
- invertColorScheme = isPassed ? greyColor : lineColorr;
37
- progressDoneColor = lineColorr;
28
+ colorScheme = isPassed ? lineColor : greyColor;
29
+ invertColorScheme = isPassed ? greyColor : lineColor;
30
+ progressDoneColor = lineColor;
38
31
  }
39
32
 
40
33
  const circleColor =
@@ -15,7 +15,7 @@ export type RouteStopStationProps = {
15
15
 
16
16
  function RouteStopStation({
17
17
  children,
18
- className = "flex items-center gap-2",
18
+ className = "flex items-center gap-2 w-full",
19
19
  classNameCancelled = "text-red-600 line-through",
20
20
  ...props
21
21
  }: RouteStopStationProps) {
@@ -29,8 +29,8 @@ function RouteStopStation({
29
29
  status.isCancelled ? classNameCancelled : "",
30
30
  )}
31
31
  >
32
- <RouteStopName />
33
- <RouteStopServices className="flex flex-wrap gap-1" />
32
+ <RouteStopName className={"grow"} />
33
+ <RouteStopServices className="flex shrink-0 flex-wrap gap-1" />
34
34
  {children}
35
35
  </div>
36
36
  );
@@ -8,6 +8,7 @@ import getTextColor from "../utils/getTextColor";
8
8
  import realtimeRVFStyle from "../utils/realtimeRVFStyle";
9
9
 
10
10
  import type { RealtimeLayerOptions } from "mobility-toolbox-js/ol";
11
+ import type { RealtimeTrajectory, ViewState } from "mobility-toolbox-js/types";
11
12
 
12
13
  const PRIORITY_FROM_TYPE = {
13
14
  bus: 25,
@@ -46,15 +47,27 @@ const defaultProps: Partial<RealtimeLayerOptions> = {
46
47
  },
47
48
  style: realtimeRVFStyle,
48
49
  styleOptions: {
49
- getBgColor: getBgColor,
50
+ getColor: (trajectory?: RealtimeTrajectory): string => {
51
+ return getBgColor(
52
+ trajectory?.properties?.type,
53
+ trajectory?.properties?.line,
54
+ );
55
+ },
50
56
  getMaxRadiusForStrokeAndDelay: () => {
51
57
  return 4;
52
58
  },
53
59
  getMaxRadiusForText: () => {
54
60
  return 8;
55
61
  },
56
- getRadius: getRadius,
57
- getTextColor: getTextColor,
62
+ getRadius: (
63
+ trajectory?: RealtimeTrajectory,
64
+ viewState?: ViewState,
65
+ ): number => {
66
+ return getRadius(trajectory?.properties?.type, viewState?.zoom);
67
+ },
68
+ getTextColor: (trajectory?: RealtimeTrajectory): string => {
69
+ return getTextColor(trajectory?.properties?.type);
70
+ },
58
71
  },
59
72
  };
60
73
  function RvfRealtimeLayer(props: Partial<RealtimeLayerOptions>) {
@@ -2,20 +2,21 @@ import { memo } from "preact/compat";
2
2
  import { useCallback } from "preact/hooks";
3
3
 
4
4
  import StopsSearch from "../StopsSearch";
5
- import centerOnStation from "../utils/centerOnStation";
6
5
  import { RVF_EXTENT_4326 } from "../utils/constants";
6
+ import useFit from "../utils/hooks/useFit";
7
7
  import useMapContext from "../utils/hooks/useMapContext";
8
8
 
9
9
  import type { StopsSearchProps } from "../StopsSearch/StopsSearch";
10
10
 
11
11
  function RvfSearch(props: Partial<StopsSearchProps>) {
12
- const { apikey, map, stopsurl } = useMapContext();
12
+ const { apikey, stopsurl } = useMapContext();
13
+ const fit = useFit();
13
14
 
14
15
  const onSelect = useCallback(
15
16
  (selected) => {
16
- return centerOnStation(selected, map);
17
+ return fit.current(selected);
17
18
  },
18
- [map],
19
+ [fit],
19
20
  );
20
21
 
21
22
  return (
@@ -1,29 +1,50 @@
1
1
  import { memo } from "preact/compat";
2
- import { useCallback } from "preact/hooks";
2
+ import { twMerge } from "tailwind-merge";
3
3
 
4
- import StopsSearch from "../StopsSearch";
5
- import centerOnStation from "../utils/centerOnStation";
6
- import useMapContext from "../utils/hooks/useMapContext";
4
+ import { SearchHeadless } from ".";
7
5
 
8
- import type { StopsSearchProps } from "../StopsSearch/StopsSearch";
9
-
10
- function Search(props: Partial<StopsSearchProps>) {
11
- const { apikey, map, stopsurl } = useMapContext();
12
-
13
- const onSelect = useCallback(
14
- (selected) => {
15
- return centerOnStation(selected, map);
16
- },
17
- [map],
18
- );
6
+ import type { SearchProps } from "./SearchHeadless";
19
7
 
8
+ /**
9
+ * This compoennt only define defult classNames for the Search component.
10
+ *
11
+ * Since the Search component could be used in different places (with or without toolbar),
12
+ * but the logic behind stays the same, we separate the logic and the styles in 2 components.
13
+ */
14
+ function Search({
15
+ childrenContainerClassName,
16
+ className,
17
+ inputContainerClassName,
18
+ resultClassName,
19
+ resultsContainerClassName,
20
+ withResultsClassName,
21
+ ...props
22
+ }: SearchProps) {
20
23
  return (
21
- <StopsSearch
22
- apikey={apikey}
23
- onselect={onSelect}
24
- url={stopsurl}
24
+ <SearchHeadless
25
+ childrenContainerClassName={twMerge(
26
+ "max-h-[300px] rounded-b-2xl bg-white shadow overflow-hidden",
27
+ childrenContainerClassName,
28
+ )}
29
+ className={twMerge(
30
+ "border-grey @container m-0 h-[48px] gap-2 rounded-2xl border p-2 text-base",
31
+ className,
32
+ )}
33
+ inputContainerClassName={twMerge("border-none", inputContainerClassName)}
34
+ resultClassName={twMerge(
35
+ "text-base **:hover:cursor-pointer p-2",
36
+ resultClassName,
37
+ )}
38
+ resultsContainerClassName={twMerge(
39
+ "min-h-[100px] max-h-[200px] border border-t-0",
40
+ resultsContainerClassName,
41
+ )}
42
+ withResultsClassName={twMerge(
43
+ "text-base !rounded-b-none",
44
+ withResultsClassName,
45
+ )}
25
46
  {...props}
26
- />
47
+ ></SearchHeadless>
27
48
  );
28
49
  }
29
50
  export default memo(Search);
@@ -0,0 +1,169 @@
1
+ import { memo } from "preact/compat";
2
+ import { useCallback, useMemo, useRef, useState } from "preact/hooks";
3
+
4
+ import InputSearch from "../ui/InputSearch";
5
+ import useI18n from "../utils/hooks/useI18n";
6
+
7
+ import type { TargetedInputEvent } from "preact";
8
+ import type { ReactElement } from "preact/compat";
9
+
10
+ import type { IconButtonProps } from "../ui/IconButton/IconButton";
11
+ import type { InputProps } from "../ui/Input/Input";
12
+ import type { InputSearchProps } from "../ui/InputSearch/InputSearch";
13
+
14
+ export type SearchBaseProps = {
15
+ cancelButtonProps?: IconButtonProps;
16
+ childrenContainerClassName?: string;
17
+ inputProps?: InputProps;
18
+ withResultsClassName?: string;
19
+ } & InputSearchProps &
20
+ Pick<
21
+ Partial<SearchResultsProps<unknown>>,
22
+ "resultClassName" | "resultsClassName" | "resultsContainerClassName"
23
+ >;
24
+
25
+ import { cloneElement, createContext, toChildArray } from "preact";
26
+ import { twMerge } from "tailwind-merge";
27
+
28
+ import type { SearchResultsProps } from "../SearchResults/SearchResults";
29
+
30
+ export interface SearchContextType {
31
+ open: boolean;
32
+ query: string;
33
+ selectedQuery: string;
34
+ setOpen: (open: boolean) => void;
35
+ setQuery: (query: string) => void;
36
+ setResults: (id: string, results: unknown[]) => void;
37
+ setSelectedQuery: (query: string) => void;
38
+ }
39
+
40
+ export const SearchContext = createContext<null | SearchContextType>({
41
+ open: false,
42
+ query: "",
43
+ selectedQuery: "",
44
+ setOpen: () => {},
45
+ setQuery: () => {},
46
+ setResults: () => {},
47
+ setSelectedQuery: () => {},
48
+ });
49
+
50
+ function SearchBase({
51
+ cancelButtonProps,
52
+ children,
53
+ childrenContainerClassName,
54
+ inputProps,
55
+ resultClassName,
56
+ resultsClassName,
57
+ resultsContainerClassName,
58
+ withResultsClassName,
59
+ ...props
60
+ }: SearchBaseProps) {
61
+ const { t } = useI18n();
62
+ // We use a selectedQuery state to avoid making a new request when we select a result.
63
+ const [selectedQuery, setSelectedQuery] = useState("");
64
+ const [query, setQuery] = useState("");
65
+ const [showResults, setShowResults] = useState(false);
66
+ const [open, setOpen] = useState(false);
67
+ const resultsBySearchRef = useRef<Record<string, unknown[]>>({});
68
+
69
+ const setResults = useCallback(
70
+ (id: string, results: unknown[]) => {
71
+ resultsBySearchRef.current[id] = results;
72
+ setShowResults(
73
+ open &&
74
+ Object.values(resultsBySearchRef.current).some((r) => {
75
+ return r.length > 0;
76
+ }),
77
+ );
78
+ },
79
+ [open],
80
+ );
81
+
82
+ const value = useMemo(() => {
83
+ return {
84
+ open,
85
+ query,
86
+ selectedQuery,
87
+ setOpen,
88
+ setQuery,
89
+ setResults: setResults,
90
+ setSelectedQuery,
91
+ };
92
+ }, [open, query, selectedQuery, setResults]);
93
+
94
+ const inputPropss: InputProps = useMemo(() => {
95
+ return {
96
+ placeholder: t("search_placeholder"),
97
+ ...(inputProps || {}),
98
+ onChange: (evt: TargetedInputEvent<HTMLInputElement>) => {
99
+ setQuery((evt.target as HTMLInputElement).value);
100
+ setSelectedQuery("");
101
+ setOpen(true);
102
+ },
103
+ onFocus: () => {
104
+ setOpen(true);
105
+ },
106
+ value: selectedQuery || query,
107
+ };
108
+ }, [query, selectedQuery, t, inputProps]);
109
+
110
+ const cancelButtonPropss: IconButtonProps = useMemo(() => {
111
+ return {
112
+ ...(cancelButtonProps || {}),
113
+ onClick: () => {
114
+ setSelectedQuery("");
115
+ setQuery("");
116
+ setOpen(false);
117
+ },
118
+ };
119
+ }, [cancelButtonProps]);
120
+
121
+ return (
122
+ <SearchContext.Provider value={value}>
123
+ <InputSearch
124
+ {...props}
125
+ cancelButtonProps={cancelButtonPropss}
126
+ inputProps={inputPropss}
127
+ withResultsClassName={showResults ? withResultsClassName : ""}
128
+ >
129
+ <div className={twMerge("flex flex-col", childrenContainerClassName)}>
130
+ {toChildArray(children)?.map((child: ReactElement) => {
131
+ return cloneElement(child, {
132
+ resultClassName,
133
+ resultsClassName,
134
+ resultsContainerClassName,
135
+ });
136
+ })}
137
+ {/*} {showTrajectoriesResults && (
138
+ <>
139
+ <SearchResultsHeader>
140
+ {t("search_trajectories_results")}
141
+ </SearchResultsHeader>
142
+ <SearchResults
143
+ className={resultsContainerClassName}
144
+ resultsClassName={resultsClassName}
145
+ resultsContainerClassName={"grow"}
146
+ searchResponse={trajectories}
147
+ >
148
+ {trajectories.results.map((trajectory: RealtimeTrajectory) => {
149
+ return (
150
+ <SearchResult
151
+ className={resultClassName}
152
+ key={trajectory.properties.route_identifier}
153
+ >
154
+ <SearchTrajectoriesResult
155
+ onSelect={onSelectTrajectory}
156
+ trajectory={trajectory}
157
+ />
158
+ </SearchResult>
159
+ );
160
+ })}
161
+ </SearchResults>
162
+ </>
163
+ )} */}
164
+ </div>
165
+ </InputSearch>
166
+ </SearchContext.Provider>
167
+ );
168
+ }
169
+ export default memo(SearchBase);
@@ -0,0 +1,79 @@
1
+ import { memo, useCallback } from "preact/compat";
2
+
3
+ import SearchLinesResult from "../SearchLinesResult";
4
+ import SearchLinesResults from "../SearchLinesResults";
5
+ import SearchStopsResult from "../SearchStopsResult";
6
+ import SearchStopsResults from "../SearchStopsResults";
7
+ import SearchTrainsResult from "../SearchTrainsResult";
8
+ import SearchTrainsResults from "../SearchTrainsResults";
9
+ import useFit from "../utils/hooks/useFit";
10
+ import useMapContext from "../utils/hooks/useMapContext";
11
+
12
+ import { SearchBase } from ".";
13
+
14
+ import type { RealtimeRouteIdentifierMatch } from "mobility-toolbox-js/types";
15
+
16
+ import type { IconButtonProps } from "../ui/IconButton/IconButton";
17
+ import type { InputProps } from "../ui/Input/Input";
18
+ import type { LnpLineInfo } from "../utils/hooks/useLnp";
19
+ import type { StopsFeature } from "../utils/hooks/useSearchStops";
20
+
21
+ import type { SearchBaseProps } from "./SearchBase";
22
+
23
+ export type SearchProps = {
24
+ cancelButtonProps?: IconButtonProps;
25
+ childrenContainerClassName?: string;
26
+ inputProps?: InputProps;
27
+ resultClassName?: string;
28
+ resultsClassName?: string;
29
+ resultsContainerClassName?: string;
30
+ withResultsClassName?: string;
31
+ } & SearchBaseProps;
32
+
33
+ /**
34
+ * The search logic. To modifiy default classNames look the Search class.
35
+ */
36
+ function SearchHeadless({ ...props }: SearchProps) {
37
+ const { setLinesIds, setStationId, setTrainId, tenant } = useMapContext();
38
+ const fit = useFit();
39
+
40
+ const onSelectStop = useCallback(
41
+ (stop: StopsFeature) => {
42
+ setStationId(stop.properties.uid);
43
+ // It means that the station wil have informations
44
+ fit.current(stop, !!tenant);
45
+ },
46
+ [fit, setStationId, tenant],
47
+ );
48
+
49
+ const onSelectLine = useCallback(
50
+ (line: LnpLineInfo) => {
51
+ setLinesIds([line.external_id]);
52
+ fit.current(line, true);
53
+ },
54
+ [fit, setLinesIds],
55
+ );
56
+
57
+ const onSelectTrain = useCallback(
58
+ (match: RealtimeRouteIdentifierMatch) => {
59
+ setTrainId(match.trains[0].train_id);
60
+ fit.current(match, true);
61
+ },
62
+ [fit, setTrainId],
63
+ );
64
+
65
+ return (
66
+ <SearchBase {...props}>
67
+ <SearchStopsResults>
68
+ <SearchStopsResult onSelectItem={onSelectStop} />
69
+ </SearchStopsResults>
70
+ <SearchLinesResults>
71
+ <SearchLinesResult onSelectItem={onSelectLine} />
72
+ </SearchLinesResults>
73
+ <SearchTrainsResults>
74
+ <SearchTrainsResult onSelectItem={onSelectTrain} />
75
+ </SearchTrainsResults>
76
+ </SearchBase>
77
+ );
78
+ }
79
+ export default memo(SearchHeadless);
@@ -1 +1,3 @@
1
1
  export { default } from "../RvfSearch";
2
+ export { default as SearchBase } from "./SearchBase";
3
+ export { default as SearchHeadless } from "./SearchHeadless";
@@ -0,0 +1,43 @@
1
+ import { memo } from "preact/compat";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ import RouteIcon from "../RouteIcon";
5
+ import useMapContext from "../utils/hooks/useMapContext";
6
+
7
+ import type { ButtonHTMLAttributes, PreactDOMAttributes } from "preact";
8
+
9
+ import type { LnpLineInfo } from "../utils/hooks/useLnp";
10
+
11
+ export type SearchLinesResultProps = {
12
+ className?: string;
13
+ item?: LnpLineInfo;
14
+ onSelectItem?: (line: LnpLineInfo) => void;
15
+ } & ButtonHTMLAttributes<HTMLButtonElement> &
16
+ PreactDOMAttributes;
17
+
18
+ function SearchLinesResult({
19
+ className,
20
+ item,
21
+ onSelectItem,
22
+ ...props
23
+ }: SearchLinesResultProps) {
24
+ const { linesNetworkPlanLayer } = useMapContext();
25
+ return (
26
+ <button
27
+ {...props}
28
+ className={twMerge(
29
+ "flex w-full cursor-pointer items-center gap-3 text-left",
30
+ className,
31
+ )}
32
+ onClick={() => {
33
+ linesNetworkPlanLayer?.setVisible(true);
34
+ onSelectItem?.(item);
35
+ }}
36
+ >
37
+ <RouteIcon lineInfo={item}></RouteIcon>
38
+ <div className="grow">{item?.long_name || item?.short_name}</div>
39
+ </button>
40
+ );
41
+ }
42
+
43
+ export default memo(SearchLinesResult);
@@ -0,0 +1 @@
1
+ export { default } from "./SearchLinesResult";