@musecat/uikit 0.1.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.
Files changed (180) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +51 -0
  3. package/index.ts +78 -0
  4. package/package.json +103 -0
  5. package/packages/Components/Box/Box.tsx +173 -0
  6. package/packages/Components/Box/Box.types.ts +48 -0
  7. package/packages/Components/Button/Button.tsx +275 -0
  8. package/packages/Components/Button/Button.types.ts +44 -0
  9. package/packages/Components/Card/Card.default.tsx +180 -0
  10. package/packages/Components/Card/Card.foldable.tsx +383 -0
  11. package/packages/Components/Card/Card.module.scss +23 -0
  12. package/packages/Components/Card/Card.tsx +8 -0
  13. package/packages/Components/Card/Card.types.ts +91 -0
  14. package/packages/Components/Card/Card.utils.ts +66 -0
  15. package/packages/Components/Checkbox/Checkbox.module.scss +11 -0
  16. package/packages/Components/Checkbox/Checkbox.tsx +104 -0
  17. package/packages/Components/Checkbox/Checkbox.types.ts +24 -0
  18. package/packages/Components/CodeBox/CodeBox.copy.tsx +43 -0
  19. package/packages/Components/CodeBox/CodeBox.module.scss +72 -0
  20. package/packages/Components/CodeBox/CodeBox.tsx +91 -0
  21. package/packages/Components/CodeBox/CodeBox.types.ts +17 -0
  22. package/packages/Components/ContextMenu/ContextMenu.module.scss +8 -0
  23. package/packages/Components/ContextMenu/ContextMenu.options.tsx +126 -0
  24. package/packages/Components/ContextMenu/ContextMenu.tsx +381 -0
  25. package/packages/Components/ContextMenu/ContextMenu.types.ts +46 -0
  26. package/packages/Components/ContributionGraph/ContributionGraph.module.scss +42 -0
  27. package/packages/Components/ContributionGraph/ContributionGraph.tsx +454 -0
  28. package/packages/Components/ContributionGraph/ContributionGraph.types.ts +27 -0
  29. package/packages/Components/DatePicker/DatePicker.calendar.tsx +161 -0
  30. package/packages/Components/DatePicker/DatePicker.core.tsx +582 -0
  31. package/packages/Components/DatePicker/DatePicker.module.scss +26 -0
  32. package/packages/Components/DatePicker/DatePicker.tsx +786 -0
  33. package/packages/Components/DatePicker/DatePicker.types.ts +36 -0
  34. package/packages/Components/DatePicker/DatePicker.utils.ts +80 -0
  35. package/packages/Components/DatePicker/hooks/useCalendar.ts +61 -0
  36. package/packages/Components/DatePicker/hooks/useSelection.ts +208 -0
  37. package/packages/Components/Divider/Divider.tsx +46 -0
  38. package/packages/Components/Divider/Divider.types.ts +12 -0
  39. package/packages/Components/Header/Header.module.scss +74 -0
  40. package/packages/Components/Header/Header.tsx +47 -0
  41. package/packages/Components/Header/Header.types.ts +8 -0
  42. package/packages/Components/Icon/Icon.group.tsx +122 -0
  43. package/packages/Components/Icon/Icon.group.types.ts +20 -0
  44. package/packages/Components/Icon/Icon.tsx +341 -0
  45. package/packages/Components/Icon/Icon.types.ts +59 -0
  46. package/packages/Components/Icon/SVG/data.tsx +232 -0
  47. package/packages/Components/Input/Input.module.scss +5 -0
  48. package/packages/Components/Input/Input.tsx +87 -0
  49. package/packages/Components/Input/Input.types.ts +14 -0
  50. package/packages/Components/Label/Label.module.scss +19 -0
  51. package/packages/Components/Label/Label.tsx +115 -0
  52. package/packages/Components/Label/Label.types.ts +28 -0
  53. package/packages/Components/Layout/Layout.docs.module.scss +42 -0
  54. package/packages/Components/Layout/Layout.docs.tsx +150 -0
  55. package/packages/Components/Layout/Layout.docs.types.ts +18 -0
  56. package/packages/Components/Layout/Layout.module.scss +91 -0
  57. package/packages/Components/Layout/Layout.tsx +306 -0
  58. package/packages/Components/Layout/Layout.types.ts +71 -0
  59. package/packages/Components/Maps/OSM/MapOSM.module.scss +11 -0
  60. package/packages/Components/Maps/OSM/MapOSM.shared.ts +121 -0
  61. package/packages/Components/Maps/OSM/MapOSM.tsx +279 -0
  62. package/packages/Components/Maps/OSM/MapOSM.types.ts +18 -0
  63. package/packages/Components/Nav/Nav.module.scss +137 -0
  64. package/packages/Components/Nav/Nav.tsx +304 -0
  65. package/packages/Components/Nav/Nav.types.ts +32 -0
  66. package/packages/Components/Nav/hooks/useNavDrag.ts +374 -0
  67. package/packages/Components/Nav/hooks/useNavIndicator.ts +137 -0
  68. package/packages/Components/Pagination/Pagination.tsx +254 -0
  69. package/packages/Components/Pagination/Pagination.types.ts +18 -0
  70. package/packages/Components/Pill/Pill.tsx +127 -0
  71. package/packages/Components/Pill/Pill.types.ts +33 -0
  72. package/packages/Components/Profile/Profile.shared.ts +74 -0
  73. package/packages/Components/Profile/Profile.tsx +382 -0
  74. package/packages/Components/Profile/Profile.types.ts +55 -0
  75. package/packages/Components/Progress/Progress.module.scss +15 -0
  76. package/packages/Components/Progress/Progress.tsx +88 -0
  77. package/packages/Components/Progress/Progress.types.ts +22 -0
  78. package/packages/Components/Radio/Radio.module.scss +11 -0
  79. package/packages/Components/Radio/Radio.tsx +121 -0
  80. package/packages/Components/Radio/Radio.types.ts +20 -0
  81. package/packages/Components/Select/Select.keyboard.tsx +76 -0
  82. package/packages/Components/Select/Select.trigger.tsx +88 -0
  83. package/packages/Components/Select/Select.tsx +11 -0
  84. package/packages/Components/Select/Select.types.ts +38 -0
  85. package/packages/Components/Select/Select.utils.ts +84 -0
  86. package/packages/Components/Select/hooks/useSelectNavigation.ts +461 -0
  87. package/packages/Components/Select/hooks/useSelectSelection.ts +142 -0
  88. package/packages/Components/Select/hooks/useSelectState.ts +314 -0
  89. package/packages/Components/Select/select.control.tsx +228 -0
  90. package/packages/Components/Select/select.inner.tsx +312 -0
  91. package/packages/Components/Skeleton/Skeleton.tsx +30 -0
  92. package/packages/Components/Skeleton/Skeleton.types.ts +12 -0
  93. package/packages/Components/Spinner/Spinner.module.scss +78 -0
  94. package/packages/Components/Spinner/Spinner.tsx +125 -0
  95. package/packages/Components/Spinner/Spinner.types.ts +19 -0
  96. package/packages/Components/Text/Text.module.scss +7 -0
  97. package/packages/Components/Text/Text.tsx +49 -0
  98. package/packages/Components/Text/Text.types.ts +33 -0
  99. package/packages/Components/TimePicker/TimePicker.core.tsx +448 -0
  100. package/packages/Components/TimePicker/TimePicker.module.scss +30 -0
  101. package/packages/Components/TimePicker/TimePicker.tsx +511 -0
  102. package/packages/Components/TimePicker/TimePicker.types.ts +22 -0
  103. package/packages/Components/Timeline/Timeline.tsx +111 -0
  104. package/packages/Components/Timeline/Timeline.types.ts +23 -0
  105. package/packages/Components/Title/Title.tsx +305 -0
  106. package/packages/Components/Title/Title.types.ts +64 -0
  107. package/packages/Components/Toggle/Toggle.module.scss +32 -0
  108. package/packages/Components/Toggle/Toggle.tsx +272 -0
  109. package/packages/Components/Toggle/Toggle.types.ts +17 -0
  110. package/packages/Components/Tooltip/Tooltip.module.scss +44 -0
  111. package/packages/Components/Tooltip/Tooltip.tsx +340 -0
  112. package/packages/Components/Tooltip/Tooltip.types.ts +38 -0
  113. package/packages/Frameworks/Dialog/Dialog.background.tsx +88 -0
  114. package/packages/Frameworks/Dialog/Dialog.boot.tsx +32 -0
  115. package/packages/Frameworks/Dialog/Dialog.store.ts +199 -0
  116. package/packages/Frameworks/Dialog/Dialog.tsx +185 -0
  117. package/packages/Frameworks/Dialog/Dialog.types.ts +264 -0
  118. package/packages/Frameworks/Dialog/Dialog.utils.ts +22 -0
  119. package/packages/Frameworks/Dialog/contents/Dialog.footer.tsx +79 -0
  120. package/packages/Frameworks/Dialog/contents/Dialog.funnel.tsx +260 -0
  121. package/packages/Frameworks/Dialog/contents/Dialog.header.tsx +137 -0
  122. package/packages/Frameworks/Dialog/hooks/useDialogPosition.ts +298 -0
  123. package/packages/Frameworks/Dialog/hooks/useSheetDrag.ts +325 -0
  124. package/packages/Frameworks/Dialog/hooks/useSheetGeometry.ts +201 -0
  125. package/packages/Frameworks/Dialog/hooks/useSheetProgressive.ts +98 -0
  126. package/packages/Frameworks/Dialog/renderers/renderModal.tsx +228 -0
  127. package/packages/Frameworks/Dialog/renderers/renderPopover.tsx +220 -0
  128. package/packages/Frameworks/Dialog/renderers/renderSheet.tsx +571 -0
  129. package/packages/Frameworks/EdgeEffect/EdgeEffect.tsx +52 -0
  130. package/packages/Frameworks/EdgeEffect/EdgeEffect.types.ts +5 -0
  131. package/packages/Frameworks/Motion/Motion.presets.ts +49 -0
  132. package/packages/Frameworks/Pressable/Pressable.module.scss +19 -0
  133. package/packages/Frameworks/Pressable/Pressable.tsx +651 -0
  134. package/packages/Frameworks/Pressable/Pressable.types.ts +73 -0
  135. package/packages/Frameworks/Squircle/Squircle.tsx +263 -0
  136. package/packages/Frameworks/Squircle/Squircle.types.ts +15 -0
  137. package/packages/Frameworks/Theme/Radius.types.ts +55 -0
  138. package/packages/Frameworks/Theme/Theme.types.ts +325 -0
  139. package/packages/Frameworks/Toaster/Toaster.boot.tsx +47 -0
  140. package/packages/Frameworks/Toaster/Toaster.module.scss +168 -0
  141. package/packages/Frameworks/Toaster/Toaster.types.ts +13 -0
  142. package/packages/Frameworks/View/DNDView/DNDView.module.scss +8 -0
  143. package/packages/Frameworks/View/DNDView/DNDView.tsx +470 -0
  144. package/packages/Frameworks/View/DNDView/DNDView.types.ts +61 -0
  145. package/packages/Frameworks/View/HScrollView/HScrollView.module.scss +59 -0
  146. package/packages/Frameworks/View/HScrollView/HScrollView.tsx +386 -0
  147. package/packages/Frameworks/View/HScrollView/HScrollView.types.ts +46 -0
  148. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.counter.tsx +27 -0
  149. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.footer.tsx +76 -0
  150. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.header.tsx +49 -0
  151. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.list.tsx +74 -0
  152. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.slide.tsx +71 -0
  153. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.tsx +175 -0
  154. package/packages/Frameworks/View/ImageView/Image.controls.tsx +39 -0
  155. package/packages/Frameworks/View/ImageView/Image.hooks.ts +82 -0
  156. package/packages/Frameworks/View/ImageView/Image.module.scss +7 -0
  157. package/packages/Frameworks/View/ImageView/Image.tsx +375 -0
  158. package/packages/Frameworks/View/ImageView/Image.types.ts +71 -0
  159. package/packages/Frameworks/View/ImageView/Image.utils.ts +79 -0
  160. package/packages/Frameworks/View/View.module.scss +34 -0
  161. package/packages/Frameworks/View/View.tsx +200 -0
  162. package/packages/Frameworks/View/View.types.ts +36 -0
  163. package/packages/Frameworks/_shared/Padding.types.ts +52 -0
  164. package/packages/Frameworks/_shared/StopParentInteraction.tsx +45 -0
  165. package/packages/Frameworks/_shared/Wind.types.ts +17 -0
  166. package/packages/Frameworks/_shared/bodyScrollLock.ts +127 -0
  167. package/packages/Frameworks/_shared/layer.constants.ts +8 -0
  168. package/packages/Frameworks/_shared/normalize.ts +39 -0
  169. package/packages/Frameworks/_shared/sizing.ts +55 -0
  170. package/packages/Frameworks/_shared/useControllableState.ts +29 -0
  171. package/packages/Styles/_animation.scss +42 -0
  172. package/packages/Styles/_color.scss +131 -0
  173. package/packages/Styles/_font.scss +30 -0
  174. package/packages/Styles/_icon.scss +782 -0
  175. package/packages/Styles/_importer.scss +9 -0
  176. package/packages/Styles/_system.scss +69 -0
  177. package/packages/Styles/_textstyle.scss +111 -0
  178. package/packages/Styles/_theme.scss +452 -0
  179. package/packages/Styles/_viewport-global.scss +99 -0
  180. package/packages/Styles/_viewport.scss +108 -0
@@ -0,0 +1,121 @@
1
+ import type maplibregl from "maplibre-gl";
2
+
3
+ const NUMERIC_FILTER_FALLBACK_MIN = Number.MIN_SAFE_INTEGER;
4
+ const NUMERIC_FILTER_FALLBACK_MAX = Number.MAX_SAFE_INTEGER;
5
+ const NUMERIC_COMPARISON_OPERATORS = new Set([
6
+ "==",
7
+ "!=",
8
+ ">",
9
+ ">=",
10
+ "<",
11
+ "<=",
12
+ ]);
13
+
14
+ function isFiniteNumberLiteral(value: unknown): value is number {
15
+ return typeof value === "number" && Number.isFinite(value);
16
+ }
17
+
18
+ function isGetExpression(value: unknown): value is ["get", string] {
19
+ return (
20
+ Array.isArray(value) && value[0] === "get" && typeof value[1] === "string"
21
+ );
22
+ }
23
+
24
+ function getNumericFilterFallback(operator: string, comparedValue: number) {
25
+ switch (operator) {
26
+ case "<":
27
+ case "<=":
28
+ return NUMERIC_FILTER_FALLBACK_MAX;
29
+ case "!=":
30
+ return comparedValue === NUMERIC_FILTER_FALLBACK_MIN
31
+ ? NUMERIC_FILTER_FALLBACK_MAX
32
+ : NUMERIC_FILTER_FALLBACK_MIN;
33
+ default:
34
+ return NUMERIC_FILTER_FALLBACK_MIN;
35
+ }
36
+ }
37
+
38
+ function wrapNumericOperand(
39
+ operand: unknown,
40
+ operator: string,
41
+ comparedValue: number,
42
+ ) {
43
+ if (!isGetExpression(operand)) {
44
+ return operand;
45
+ }
46
+
47
+ return [
48
+ "to-number",
49
+ operand,
50
+ getNumericFilterFallback(operator, comparedValue),
51
+ ];
52
+ }
53
+
54
+ export function sanitizeStyleFilterExpression(expression: unknown): unknown {
55
+ if (!Array.isArray(expression)) {
56
+ return expression;
57
+ }
58
+
59
+ const [operator, ...args] = expression;
60
+ if (typeof operator !== "string") {
61
+ return expression.map((item) => sanitizeStyleFilterExpression(item));
62
+ }
63
+
64
+ if (NUMERIC_COMPARISON_OPERATORS.has(operator) && args.length === 2) {
65
+ const [left, right] = args;
66
+ const sanitizedLeft = sanitizeStyleFilterExpression(left);
67
+ const sanitizedRight = sanitizeStyleFilterExpression(right);
68
+
69
+ if (isFiniteNumberLiteral(right)) {
70
+ return [
71
+ operator,
72
+ wrapNumericOperand(sanitizedLeft, operator, right),
73
+ sanitizedRight,
74
+ ];
75
+ }
76
+
77
+ if (isFiniteNumberLiteral(left)) {
78
+ return [
79
+ operator,
80
+ sanitizedLeft,
81
+ wrapNumericOperand(sanitizedRight, operator, left),
82
+ ];
83
+ }
84
+ }
85
+
86
+ return [operator, ...args.map((item) => sanitizeStyleFilterExpression(item))];
87
+ }
88
+
89
+ function sanitizeLayerFilter<T extends object>(layer: T): T {
90
+ if (!("filter" in layer)) {
91
+ return layer;
92
+ }
93
+
94
+ const layerWithFilter = layer as T & { filter?: unknown };
95
+ if (!layerWithFilter.filter) {
96
+ return layer;
97
+ }
98
+
99
+ return {
100
+ ...layerWithFilter,
101
+
102
+ filter: sanitizeStyleFilterExpression(layerWithFilter.filter),
103
+ };
104
+ }
105
+
106
+ export function resolveSafeMapStyle(
107
+ styleJson: maplibregl.StyleSpecification,
108
+ disable3D: boolean,
109
+ ): maplibregl.StyleSpecification {
110
+ const layers = (styleJson.layers ?? [])
111
+ .filter((layer) => (disable3D ? layer.type !== "fill-extrusion" : true))
112
+ .map((layer) => sanitizeLayerFilter(layer));
113
+
114
+ return {
115
+ ...styleJson,
116
+
117
+ projection: styleJson.projection ?? { type: "mercator" },
118
+ layers,
119
+ terrain: disable3D ? undefined : styleJson.terrain,
120
+ };
121
+ }
@@ -0,0 +1,279 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import maplibregl from "maplibre-gl";
5
+ import styles from "@/packages/Components/Maps/OSM/MapOSM.module.scss";
6
+ import { resolveSafeMapStyle } from "@/packages/Components/Maps/OSM/MapOSM.shared";
7
+ import type { MapOSMProps } from "@/packages/Components/Maps/OSM/MapOSM.types";
8
+ import View from "@/packages/Frameworks/View/View";
9
+ import "maplibre-gl/dist/maplibre-gl.css";
10
+ import { useCallback, useEffect, useRef } from "react";
11
+
12
+ const STYLE_URL = "https://tiles.openfreemap.org/styles/liberty";
13
+ const DEFAULT_MAP_ZOOM = 16;
14
+ const EMPTY_STYLE_IMAGE = {
15
+ width: 1,
16
+ height: 1,
17
+ data: new Uint8Array([0, 0, 0, 0]),
18
+ } as const;
19
+
20
+ function scheduleMapRecenter(
21
+ map: maplibregl.Map,
22
+ center: [number, number],
23
+ zoom = DEFAULT_MAP_ZOOM,
24
+ ) {
25
+ const recenter = () => {
26
+ map.resize();
27
+ map.jumpTo({ center, zoom });
28
+ };
29
+
30
+ recenter();
31
+ window.requestAnimationFrame(() => {
32
+ recenter();
33
+ window.requestAnimationFrame(recenter);
34
+ });
35
+ }
36
+
37
+ export default function MapOSM({
38
+ lat,
39
+ lon,
40
+ onMapPick,
41
+ height = "400px",
42
+ className,
43
+ style,
44
+ mapClassName,
45
+ mapStyle,
46
+ width = "100%",
47
+ interactive = true,
48
+ disable3D = false,
49
+ showNavigationControl = true,
50
+ mapOptions,
51
+ }: MapOSMProps) {
52
+ const mapContainer = useRef<HTMLDivElement>(null);
53
+ const mapRef = useRef<maplibregl.Map | null>(null);
54
+ const markerRef = useRef<maplibregl.Marker | null>(null);
55
+ const navigationControlRef = useRef<maplibregl.NavigationControl | null>(
56
+ null,
57
+ );
58
+ const hasNavigationControlRef = useRef(false);
59
+ const clickHandlerRef = useRef<
60
+ ((event: maplibregl.MapMouseEvent) => void) | null
61
+ >(null);
62
+ const latestLatRef = useRef(lat);
63
+ const latestLonRef = useRef(lon);
64
+ const latestOnMapPickRef = useRef(onMapPick);
65
+
66
+ const bindMapClickHandler = useCallback((map: maplibregl.Map) => {
67
+ if (clickHandlerRef.current) {
68
+ map.off("click", clickHandlerRef.current);
69
+ clickHandlerRef.current = null;
70
+ }
71
+
72
+ const handleMapClick = (event: maplibregl.MapMouseEvent) => {
73
+ const { lng, lat: clickedLat } = event.lngLat;
74
+ latestOnMapPickRef.current?.(clickedLat, lng);
75
+ };
76
+
77
+ clickHandlerRef.current = handleMapClick;
78
+ map.on("click", handleMapClick);
79
+ }, []);
80
+
81
+ useEffect(() => {
82
+ latestLatRef.current = lat;
83
+ latestLonRef.current = lon;
84
+ latestOnMapPickRef.current = onMapPick;
85
+ }, [lat, lon, onMapPick]);
86
+
87
+ useEffect(() => {
88
+ let map: maplibregl.Map | null = null;
89
+ if (!mapContainer.current || mapRef.current) return;
90
+
91
+ let cancelled = false;
92
+
93
+ const initMap = async () => {
94
+ try {
95
+ const response = await fetch(STYLE_URL);
96
+ if (!response.ok) {
97
+ throw new Error(`Failed to fetch style: ${response.statusText}`);
98
+ }
99
+
100
+ const styleJson =
101
+ (await response.json()) as maplibregl.StyleSpecification;
102
+ if (cancelled || !mapContainer.current || mapRef.current) return;
103
+
104
+ const latNum = Number(latestLatRef.current) || 37.5326;
105
+ const lonNum = Number(latestLonRef.current) || 127.0246;
106
+
107
+ const resolvedStyle = resolveSafeMapStyle(styleJson, disable3D);
108
+
109
+ const resolvedMapOptions: maplibregl.MapOptions = {
110
+ container: mapContainer.current,
111
+ style: resolvedStyle,
112
+ center: [lonNum, latNum],
113
+ zoom: DEFAULT_MAP_ZOOM,
114
+ interactive,
115
+ attributionControl: false,
116
+ ...mapOptions,
117
+ };
118
+
119
+ map = new maplibregl.Map(resolvedMapOptions);
120
+
121
+ map.on("styleimagemissing", ({ id }) => {
122
+ if (!map || map.hasImage(id)) return;
123
+
124
+ map.addImage(id, EMPTY_STYLE_IMAGE);
125
+ });
126
+
127
+ const marker = new maplibregl.Marker({ color: "#FF0000" })
128
+ .setLngLat([lonNum, latNum])
129
+ .addTo(map);
130
+
131
+ map.once("load", () => {
132
+ if (!map) {
133
+ return;
134
+ }
135
+
136
+ scheduleMapRecenter(map, [lonNum, latNum]);
137
+ });
138
+
139
+ if (disable3D) {
140
+ map.once("load", () => {
141
+ map?.setTerrain(null);
142
+ map?.setPitch(0);
143
+ map?.setMaxPitch(0);
144
+ });
145
+ }
146
+
147
+ const navigationControl = new maplibregl.NavigationControl();
148
+ navigationControlRef.current = navigationControl;
149
+
150
+ mapRef.current = map;
151
+ markerRef.current = marker;
152
+
153
+ if (showNavigationControl) {
154
+ map.addControl(navigationControl, "top-right");
155
+ hasNavigationControlRef.current = true;
156
+ }
157
+
158
+ if (interactive) {
159
+ bindMapClickHandler(map);
160
+ }
161
+ } catch (err) {
162
+ if (!cancelled) {
163
+ console.error("MapOSM initialization failed:", err);
164
+ }
165
+ }
166
+ };
167
+
168
+ initMap();
169
+
170
+ return () => {
171
+ cancelled = true;
172
+ if (map) map.remove();
173
+ mapRef.current = null;
174
+ markerRef.current = null;
175
+ navigationControlRef.current = null;
176
+ hasNavigationControlRef.current = false;
177
+ clickHandlerRef.current = null;
178
+ };
179
+ }, [
180
+ bindMapClickHandler,
181
+ disable3D,
182
+ interactive,
183
+ mapOptions,
184
+ showNavigationControl,
185
+ ]);
186
+
187
+ useEffect(() => {
188
+ const map = mapRef.current;
189
+ if (!map) {
190
+ return;
191
+ }
192
+
193
+ if (interactive) {
194
+ map.scrollZoom.enable();
195
+ map.boxZoom.enable();
196
+ map.dragRotate.enable();
197
+ map.dragPan.enable();
198
+ map.keyboard.enable();
199
+ map.doubleClickZoom.enable();
200
+ map.touchZoomRotate.enable();
201
+ } else {
202
+ map.scrollZoom.disable();
203
+ map.boxZoom.disable();
204
+ map.dragRotate.disable();
205
+ map.dragPan.disable();
206
+ map.keyboard.disable();
207
+ map.doubleClickZoom.disable();
208
+ map.touchZoomRotate.disable();
209
+ }
210
+
211
+ if (interactive) {
212
+ bindMapClickHandler(map);
213
+ } else if (clickHandlerRef.current) {
214
+ map.off("click", clickHandlerRef.current);
215
+ clickHandlerRef.current = null;
216
+ }
217
+ }, [bindMapClickHandler, interactive]);
218
+
219
+ useEffect(() => {
220
+ const map = mapRef.current;
221
+ const navigationControl = navigationControlRef.current;
222
+ if (!map || !navigationControl) {
223
+ return;
224
+ }
225
+
226
+ if (showNavigationControl && !hasNavigationControlRef.current) {
227
+ map.addControl(navigationControl, "top-right");
228
+ hasNavigationControlRef.current = true;
229
+ }
230
+
231
+ if (!showNavigationControl && hasNavigationControlRef.current) {
232
+ map.removeControl(navigationControl);
233
+ hasNavigationControlRef.current = false;
234
+ }
235
+ }, [showNavigationControl]);
236
+
237
+ useEffect(() => {
238
+ if (mapRef.current && markerRef.current) {
239
+ const latNum = Number(lat);
240
+ const lonNum = Number(lon);
241
+
242
+ if (
243
+ !Number.isNaN(latNum) &&
244
+ !Number.isNaN(lonNum) &&
245
+ (latNum !== 0 || lonNum !== 0)
246
+ ) {
247
+ const newPos: [number, number] = [lonNum, latNum];
248
+ markerRef.current.setLngLat(newPos);
249
+
250
+ mapRef.current.easeTo({
251
+ center: newPos,
252
+ zoom: DEFAULT_MAP_ZOOM,
253
+ duration: 500,
254
+ });
255
+ }
256
+ }
257
+ }, [lat, lon]);
258
+
259
+ return (
260
+ <View
261
+ className={clsx(styles.MapWrapper, className)}
262
+ style={{
263
+ width,
264
+ height,
265
+ ...style,
266
+ }}
267
+ >
268
+ <View
269
+ className={clsx(styles.Map, mapClassName)}
270
+ ref={mapContainer}
271
+ style={{
272
+ width: "100%",
273
+ height: "100%",
274
+ ...mapStyle,
275
+ }}
276
+ />
277
+ </View>
278
+ );
279
+ }
@@ -0,0 +1,18 @@
1
+ import type maplibregl from "maplibre-gl";
2
+ import type { CSSProperties } from "react";
3
+
4
+ export interface MapOSMProps {
5
+ lat: number | string;
6
+ lon: number | string;
7
+ onMapPick?: (lat: number | string, lon: number | string) => void;
8
+ height?: string;
9
+ className?: string;
10
+ style?: CSSProperties;
11
+ mapClassName?: string;
12
+ mapStyle?: CSSProperties;
13
+ width?: string;
14
+ interactive?: boolean;
15
+ disable3D?: boolean;
16
+ showNavigationControl?: boolean;
17
+ mapOptions?: Partial<maplibregl.MapOptions>;
18
+ }
@@ -0,0 +1,137 @@
1
+ .Nav {
2
+ padding: 0.2rem;
3
+ position: relative;
4
+ width: max-content;
5
+ overflow: visible;
6
+ border-radius: 9999px;
7
+ backdrop-filter: blur(8px);
8
+ cursor: default;
9
+
10
+ &.SizeFull {
11
+ width: 100% !important;
12
+
13
+ .Item {
14
+ flex: 1 1 0;
15
+ min-width: 0;
16
+ width: auto !important;
17
+ }
18
+ }
19
+
20
+ &.DragSelection {
21
+ touch-action: pan-y;
22
+
23
+ .Item {
24
+ min-width: 3.6rem;
25
+ }
26
+ }
27
+
28
+ .ActiveIndicator {
29
+ --nav-indicator-scale: 1;
30
+
31
+ position: absolute;
32
+ top: 0.2rem;
33
+ bottom: 0.2rem;
34
+ left: 0;
35
+ width: 0;
36
+ opacity: 0;
37
+ transition:
38
+ transform 280ms cubic-bezier(0.22, 1, 0.36, 1),
39
+ width 180ms ease,
40
+ opacity 140ms ease,
41
+ var(--transition-surface);
42
+ transform: translate3d(0, 0, 0) scale(var(--nav-indicator-scale, 1));
43
+ transform-origin: center;
44
+ will-change: transform, width, opacity;
45
+ z-index: 0;
46
+ pointer-events: none;
47
+ }
48
+
49
+ .ActiveIndicatorDragging {
50
+ --nav-indicator-scale: 1.4;
51
+
52
+ transition:
53
+ opacity 120ms ease,
54
+ var(--transition-surface);
55
+ }
56
+
57
+ .ActiveIndicatorPressed {
58
+ --nav-indicator-scale: 1.4;
59
+
60
+ transition:
61
+ transform 120ms linear,
62
+ width 180ms ease,
63
+ opacity 120ms ease,
64
+ var(--transition-surface);
65
+ }
66
+
67
+ .ActiveIndicatorReleasing {
68
+ --nav-indicator-scale: 1;
69
+
70
+ transition:
71
+ transform 240ms cubic-bezier(0.22, 1, 0.36, 1),
72
+ width 180ms ease,
73
+ opacity 140ms ease,
74
+ var(--transition-surface);
75
+ }
76
+
77
+ .Item {
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ gap: 0.4rem;
82
+ cursor: default;
83
+ z-index: 2;
84
+ user-select: none;
85
+ text-align: center;
86
+ width: auto !important;
87
+ min-width: 0;
88
+ background: transparent !important;
89
+
90
+ & > span {
91
+ color: var(--color-base-dark-1) !important;
92
+ }
93
+
94
+ & i::before {
95
+ color: var(--color-base-dark-1) !important;
96
+ }
97
+
98
+ @media (hover: hover) and (pointer: fine) {
99
+ &:hover {
100
+ background: transparent !important;
101
+ }
102
+ }
103
+
104
+ &:active {
105
+ background: transparent !important;
106
+ }
107
+
108
+ &:not(&.Checked) > span,
109
+ &:not(&.Checked) i::before {
110
+ opacity: 0.6;
111
+ }
112
+
113
+ &.Checked {
114
+ background: transparent !important;
115
+ box-shadow: none !important;
116
+ cursor: default;
117
+
118
+ @media (hover: hover) and (pointer: fine) {
119
+ &:hover {
120
+ background: transparent !important;
121
+ }
122
+ }
123
+
124
+ &:active {
125
+ background: transparent !important;
126
+ }
127
+ }
128
+ }
129
+
130
+ &.Dragging .Item {
131
+ cursor: default;
132
+ }
133
+
134
+ &.Dragging .Item.Checked {
135
+ cursor: default;
136
+ }
137
+ }