@trackunit/react-drawer 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.
Files changed (53) hide show
  1. package/README.md +21 -0
  2. package/index.cjs.d.ts +1 -0
  3. package/index.cjs.js +443 -0
  4. package/index.esm.d.ts +1 -0
  5. package/index.esm.js +437 -0
  6. package/package.json +21 -0
  7. package/src/components/Drawer/Drawer.d.ts +57 -0
  8. package/src/components/Drawer/Drawer.stories.d.ts +12 -0
  9. package/src/components/Drawer/Drawer.variants.d.ts +7 -0
  10. package/src/components/DrawerToggle/DrawerToggle.d.ts +19 -0
  11. package/src/components/Overlay/Overlay.d.ts +14 -0
  12. package/src/components/SwipeableDrawer/DrawerPuller.d.ts +10 -0
  13. package/src/components/SwipeableDrawer/SwipeableDrawer.d.ts +15 -0
  14. package/src/components/index.d.ts +3 -0
  15. package/src/hooks/index.d.ts +1 -0
  16. package/src/hooks/useSwipeHandlers.d.ts +23 -0
  17. package/src/index.d.ts +1 -0
  18. package/src/translation.d.ts +33 -0
  19. package/src/types.d.ts +2 -0
  20. package/translation.cjs.js +7 -0
  21. package/translation.cjs10.js +7 -0
  22. package/translation.cjs11.js +7 -0
  23. package/translation.cjs12.js +7 -0
  24. package/translation.cjs13.js +7 -0
  25. package/translation.cjs14.js +7 -0
  26. package/translation.cjs15.js +7 -0
  27. package/translation.cjs16.js +7 -0
  28. package/translation.cjs17.js +7 -0
  29. package/translation.cjs2.js +7 -0
  30. package/translation.cjs3.js +7 -0
  31. package/translation.cjs4.js +7 -0
  32. package/translation.cjs5.js +7 -0
  33. package/translation.cjs6.js +7 -0
  34. package/translation.cjs7.js +7 -0
  35. package/translation.cjs8.js +7 -0
  36. package/translation.cjs9.js +7 -0
  37. package/translation.esm.js +5 -0
  38. package/translation.esm10.js +5 -0
  39. package/translation.esm11.js +5 -0
  40. package/translation.esm12.js +5 -0
  41. package/translation.esm13.js +5 -0
  42. package/translation.esm14.js +5 -0
  43. package/translation.esm15.js +5 -0
  44. package/translation.esm16.js +5 -0
  45. package/translation.esm17.js +5 -0
  46. package/translation.esm2.js +5 -0
  47. package/translation.esm3.js +5 -0
  48. package/translation.esm4.js +5 -0
  49. package/translation.esm5.js +5 -0
  50. package/translation.esm6.js +5 -0
  51. package/translation.esm7.js +5 -0
  52. package/translation.esm8.js +5 -0
  53. package/translation.esm9.js +5 -0
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Trackunit react-drawer
2
+
3
+ The `@trackunit/react-drawer` package is the home of Trackunits public react components used for...
4
+
5
+ This library is exposed publicly for use in the the Trackunit [Iris App SDK](https://www.npmjs.com/package/@trackunit/iris-app).
6
+
7
+ To browse all avaliable components visit our [Public Storybook](https://apps.iris.trackunit.com/storybook/).
8
+
9
+ For more info and a full guide on Iris App SDK Development, please visit our [Developer Hub](https://developers.trackunit.com/).
10
+
11
+ ## Development
12
+
13
+ At this point this library is only developed by Trackunit Employees.
14
+ For development related information see the [development readme](https://github.com/Trackunit/manager/blob/master/libs/react/components/DEVELOPMENT.md).
15
+
16
+ ## Trackunit
17
+
18
+ This package was developed by Trackunit ApS.
19
+ Trackunit is the leading SaaS-based IoT solution for the construction industry, offering an ecosystem of hardware, fleet management software & telematics.
20
+
21
+ ![The Trackunit logo](https://trackunit.com/wp-content/uploads/2022/03/top-logo.svg)
package/index.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/index.cjs.js ADDED
@@ -0,0 +1,443 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var i18nLibraryTranslation = require('@trackunit/i18n-library-translation');
5
+ var react = require('react');
6
+ var usePortal = require('react-useportal');
7
+ var reactComponents = require('@trackunit/react-components');
8
+ var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
9
+ var reactSwipeable = require('react-swipeable');
10
+
11
+ var defaultTranslations = {
12
+
13
+ };
14
+
15
+ /** The translation namespace for this library */
16
+ const namespace = "react.react-drawer";
17
+ /**
18
+ * The TranslationResource for this Library.
19
+ * Holds lazy loaded imports for all languages supported by the library.
20
+ *
21
+ * This is used to register the translations for this library before initializing i18next.
22
+ */
23
+ const translations = {
24
+ ns: namespace,
25
+ default: defaultTranslations,
26
+ languages: {
27
+ de: () => Promise.resolve().then(function () { return require('./translation.cjs.js'); }),
28
+ da: () => Promise.resolve().then(function () { return require('./translation.cjs2.js'); }),
29
+ cs: () => Promise.resolve().then(function () { return require('./translation.cjs3.js'); }),
30
+ nl: () => Promise.resolve().then(function () { return require('./translation.cjs4.js'); }),
31
+ fr: () => Promise.resolve().then(function () { return require('./translation.cjs5.js'); }),
32
+ fi: () => Promise.resolve().then(function () { return require('./translation.cjs6.js'); }),
33
+ hu: () => Promise.resolve().then(function () { return require('./translation.cjs7.js'); }),
34
+ it: () => Promise.resolve().then(function () { return require('./translation.cjs8.js'); }),
35
+ nb: () => Promise.resolve().then(function () { return require('./translation.cjs9.js'); }),
36
+ pl: () => Promise.resolve().then(function () { return require('./translation.cjs10.js'); }),
37
+ pt: () => Promise.resolve().then(function () { return require('./translation.cjs11.js'); }),
38
+ ru: () => Promise.resolve().then(function () { return require('./translation.cjs12.js'); }),
39
+ ro: () => Promise.resolve().then(function () { return require('./translation.cjs13.js'); }),
40
+ es: () => Promise.resolve().then(function () { return require('./translation.cjs14.js'); }),
41
+ sv: () => Promise.resolve().then(function () { return require('./translation.cjs15.js'); }),
42
+ ja: () => Promise.resolve().then(function () { return require('./translation.cjs16.js'); }),
43
+ th: () => Promise.resolve().then(function () { return require('./translation.cjs17.js'); }),
44
+ },
45
+ };
46
+ /**
47
+ * Registers the translations for this library
48
+ */
49
+ const setupLibraryTranslations = () => {
50
+ i18nLibraryTranslation.registerTranslations(translations);
51
+ };
52
+
53
+ /**
54
+ * Overlay Component
55
+ *
56
+ * @param {object} props - The Overlay component properties
57
+ * @param {boolean} props.open - Open status of the Overlay
58
+ * @param {Function} props.onClose - Callback function when Overlay is closed
59
+ * @returns {JSX.Element|null} The Overlay component
60
+ */
61
+ const Overlay = ({ open, onClose }) => {
62
+ react.useEffect(() => {
63
+ if (!onClose) {
64
+ return;
65
+ }
66
+ const handleEscape = (event) => {
67
+ if (event.key === "Escape") {
68
+ onClose();
69
+ }
70
+ };
71
+ if (open) {
72
+ window.addEventListener("keyup", handleEscape);
73
+ }
74
+ return () => {
75
+ window.removeEventListener("keyup", handleEscape);
76
+ };
77
+ }, [open, onClose]);
78
+ const handleClick = () => {
79
+ if (onClose) {
80
+ onClose();
81
+ }
82
+ };
83
+ return (jsxRuntime.jsx("div", { "aria-hidden": open, className: cvaOverlayContainer({ open }), "data-testid": "drawer-overlay", onClick: handleClick }));
84
+ };
85
+ const cvaOverlayContainer = cssClassVarianceUtilities.cvaMerge([
86
+ "absolute",
87
+ "flex",
88
+ "items-center",
89
+ "justify-center",
90
+ "inset-0",
91
+ "bg-black/30",
92
+ "bg-opacity-50",
93
+ "transition-opacity",
94
+ "duration-200",
95
+ "ease-[cubic-bezier(0.32,0.72,0,1)]",
96
+ "opacity-0",
97
+ ], {
98
+ variants: {
99
+ open: {
100
+ true: "z-popover opacity-100",
101
+ false: "z-[-1] opacity-0",
102
+ },
103
+ },
104
+ });
105
+
106
+ const cvaDrawer = cssClassVarianceUtilities.cvaMerge([
107
+ "z-toast",
108
+ "pointer-events-auto",
109
+ "absolute",
110
+ "transform",
111
+ "flex-col",
112
+ "transition-transform",
113
+ "duration-300",
114
+ "ease-[cubic-bezier(0.32,0.72,0,1)]",
115
+ ], {
116
+ variants: {
117
+ position: {
118
+ left: "left-0 top-0 h-full w-fit",
119
+ right: "right-0 top-0 h-full w-fit",
120
+ top: "left-0 top-0 h-fit w-full pb-2",
121
+ bottom: "bottom-0 left-0 right-0 max-h-screen w-full pt-3",
122
+ },
123
+ mode: {
124
+ open: "", // No additional classes needed; position determines translation
125
+ closed: "", // Translation handled in compoundVariants
126
+ },
127
+ },
128
+ compoundVariants: [
129
+ {
130
+ mode: "open",
131
+ position: "left",
132
+ className: "translate-x-0",
133
+ },
134
+ {
135
+ mode: "closed",
136
+ position: "left",
137
+ className: "-translate-x-full",
138
+ },
139
+ {
140
+ mode: "open",
141
+ position: "right",
142
+ className: "translate-x-0",
143
+ },
144
+ {
145
+ mode: "closed",
146
+ position: "right",
147
+ className: "translate-x-full",
148
+ },
149
+ {
150
+ mode: "open",
151
+ position: "top",
152
+ className: "translate-y-0",
153
+ },
154
+ {
155
+ mode: "closed",
156
+ position: "top",
157
+ className: "-translate-y-full",
158
+ },
159
+ {
160
+ mode: "open",
161
+ position: "bottom",
162
+ className: "translate-y-0",
163
+ },
164
+ {
165
+ mode: "closed",
166
+ position: "bottom",
167
+ className: "translate-y-full",
168
+ },
169
+ ],
170
+ defaultVariants: {
171
+ position: "left",
172
+ mode: "closed",
173
+ },
174
+ });
175
+ const cvaDrawerContent = cssClassVarianceUtilities.cvaMerge(["flex", "h-full", "shadow-lg", "bg-white"], {
176
+ variants: {
177
+ position: {
178
+ left: " rounded-r-lg",
179
+ right: " rounded-l-lg",
180
+ top: " flex-col rounded-b-lg",
181
+ bottom: " max-h-[calc(100vh-10px)] flex-col rounded-t-lg sm:max-h-none",
182
+ },
183
+ },
184
+ });
185
+
186
+ const TRANSITION_DURATION = 100;
187
+ const DEFAULT_POSITION = "left";
188
+ const NOOP$1 = () => void 0;
189
+ /**
190
+ * Drawers components can be switched between open and closed states.
191
+ * They start closed but can be temporarily opened, appearing on top of other content until the user chooses a section.
192
+ * To close the drawer, users can either click outside of it or press the Esc key.
193
+ *
194
+ * @param {DrawerProps} props - The props for the Drawer component
195
+ * @returns {JSX.Element | null} Drawer component
196
+ */
197
+ const Drawer = react.forwardRef(({ open = false, // Default to closed
198
+ onClose = NOOP$1, hasOverlay = true, position = DEFAULT_POSITION, children, dataTestId, className, renderInPortal = false, keepMountedWhenClosed = false, containerClassName, ...others }, ref) => {
199
+ const { isSm } = reactComponents.useViewportSize();
200
+ const { Portal } = usePortal();
201
+ const shouldUsePortal = !isSm || renderInPortal;
202
+ const [shouldRender, setShouldRender] = react.useState(open);
203
+ const [mode, setMode] = react.useState(open ? "open" : "closed");
204
+ react.useEffect(() => {
205
+ if (open) {
206
+ setShouldRender(true);
207
+ setTimeout(() => setMode("open"), TRANSITION_DURATION);
208
+ }
209
+ else {
210
+ setMode("closed");
211
+ }
212
+ }, [open]);
213
+ const handleAnimationEnd = react.useCallback(() => {
214
+ if (mode === "closed") {
215
+ setShouldRender(false);
216
+ }
217
+ }, [mode, setShouldRender]);
218
+ if (!shouldRender && !keepMountedWhenClosed) {
219
+ return null;
220
+ }
221
+ const content = (jsxRuntime.jsxs("div", { className: containerClassName, children: [hasOverlay ? jsxRuntime.jsx(Overlay, { onClose: onClose, open: mode === "open" }) : null, jsxRuntime.jsx("div", { className: cvaDrawer({
222
+ mode,
223
+ position: position,
224
+ className,
225
+ }), "data-testid": dataTestId, onTransitionEnd: handleAnimationEnd, ref: ref, ...others, children: jsxRuntime.jsx("div", { className: cvaDrawerContent({ position }), children: children }) })] }));
226
+ return shouldUsePortal ? jsxRuntime.jsx(Portal, { children: content }) : content;
227
+ });
228
+ Drawer.displayName = "Drawer";
229
+
230
+ const NOOP = () => void 0;
231
+ const getIconName = (open, position) => {
232
+ switch (position) {
233
+ case "left":
234
+ return open ? "ChevronLeft" : "ChevronRight";
235
+ case "right":
236
+ return open ? "ChevronRight" : "ChevronLeft";
237
+ case "top":
238
+ return open ? "ChevronUp" : "ChevronDown";
239
+ case "bottom":
240
+ return open ? "ChevronDown" : "ChevronUp";
241
+ default:
242
+ return open ? "ChevronLeft" : "ChevronRight";
243
+ }
244
+ };
245
+ /**
246
+ * DrawerToggle is a React functional component that returns a button with a chevron icon.
247
+ * The direction of the chevron changes depending on the state of the 'open' prop and
248
+ * the side the button is positioned ('position' prop).
249
+ * The button might be disabled based on the 'disableButton' prop.
250
+ *
251
+ * @param {object} props - The properties passed to the component
252
+ * @param {boolean} props.open - Indicates if the button is in "open" state
253
+ * @param {Function} [props.onClick] - Optional callback function for when the button is clicked
254
+ * @param {DrawerPosition} props.position - The position of the button relative to its container
255
+ */
256
+ const DrawerToggle = ({ open, position, onClick = NOOP }) => {
257
+ const name = getIconName(open, position);
258
+ return (jsxRuntime.jsx("div", { className: cvaToggleContainer({ position }), children: jsxRuntime.jsx("button", { className: cvaDrawerToggle({ position }), "data-testid": "toggle-button", onClick: onClick, children: jsxRuntime.jsx(reactComponents.Icon, { name: name }) }) }));
259
+ };
260
+ const cvaDrawerToggle = cssClassVarianceUtilities.cvaMerge([
261
+ "flex",
262
+ "cursor-pointer",
263
+ "items-center",
264
+ "justify-center",
265
+ "border-gray-300",
266
+ "bg-neutral-50",
267
+ "bg-center",
268
+ "bg-no-repeat",
269
+ "shadow-md",
270
+ ], {
271
+ variants: {
272
+ position: {
273
+ left: "h-12 w-6 rounded-r-lg",
274
+ right: "h-12 w-6 rounded-l-lg",
275
+ top: "h-6 w-12 rounded-b-lg",
276
+ bottom: "h-6 w-12 rounded-t-lg",
277
+ },
278
+ },
279
+ defaultVariants: {
280
+ position: "left",
281
+ },
282
+ });
283
+ const cvaToggleContainer = cssClassVarianceUtilities.cvaMerge(["z-8", "absolute"], {
284
+ variants: {
285
+ position: {
286
+ left: "right-[-24px] top-[calc(50%-24px)]",
287
+ right: "left-[-24px] top-[calc(50%-24px)]",
288
+ top: "bottom-[-24px] left-[calc(50%-24px)]",
289
+ bottom: "left-[calc(50%-24px)] top-[-24px]",
290
+ },
291
+ },
292
+ defaultVariants: {
293
+ position: "left",
294
+ },
295
+ });
296
+
297
+ /**
298
+ * Retrieves the Y-axis translation value of a given HTML element.
299
+ *
300
+ * @param element - The HTML element for which to get the Y translation. If not provided or null, returns 0.
301
+ * @returns {number} - The Y translation value of the element. Returns 0 if the element is not provided or if the transform style is "none".
302
+ */
303
+ const getElementYTranslation = (element) => {
304
+ if (!element) {
305
+ return 0;
306
+ }
307
+ const style = window.getComputedStyle(element);
308
+ const transform = style.transform;
309
+ if (transform === "none") {
310
+ return 0;
311
+ }
312
+ const matrix = new DOMMatrix(transform);
313
+ return matrix.m42;
314
+ };
315
+ /**
316
+ * Hook for handling swipe events on the drawer.
317
+ */
318
+ const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture }) => {
319
+ const [isDragging, setIsDragging] = react.useState(false);
320
+ const [dragDistance, setDragDistance] = react.useState(0);
321
+ const currentY = react.useRef(0);
322
+ const currentHeight = react.useRef(0);
323
+ const geometry = reactComponents.useGeometry(ref);
324
+ const handlers = reactSwipeable.useSwipeable({
325
+ onTouchStartOrOnMouseDown: () => {
326
+ const y = getElementYTranslation(ref.current);
327
+ const height = geometry.height;
328
+ currentY.current = y;
329
+ currentHeight.current = height;
330
+ setDragDistance(y);
331
+ setIsDragging(true);
332
+ },
333
+ onSwiping: (e) => {
334
+ const newY = currentY.current + e.deltaY;
335
+ if (newY >= 0) {
336
+ setDragDistance(newY);
337
+ }
338
+ },
339
+ onSwipedDown: (e) => {
340
+ const threshold = currentHeight.current * closingThreshold;
341
+ if (e.absY > threshold && onCloseGesture) {
342
+ onCloseGesture();
343
+ }
344
+ setIsDragging(false);
345
+ setDragDistance(0);
346
+ },
347
+ onSwipedUp: (e) => {
348
+ const threshold = currentHeight.current * closingThreshold;
349
+ if (e.absY > threshold && onOpenGesture) {
350
+ onOpenGesture();
351
+ }
352
+ setIsDragging(false);
353
+ setDragDistance(0);
354
+ },
355
+ onSwiped: () => void 0,
356
+ trackMouse: true,
357
+ trackTouch: true,
358
+ preventScrollOnSwipe: true,
359
+ });
360
+ return { handlers, isDragging, dragDistance };
361
+ };
362
+
363
+ /**
364
+ * DrawerPuller is a React component that renders a puller element for a swipeable drawer.
365
+ *
366
+ * @param {CommonProps} props - The props for the DrawerPuller component.
367
+ * @param {string} [props.className] - Additional class names to apply to the puller element.
368
+ * @param {Ref<HTMLDivElement>} ref - The ref to be forwarded to the root div element.
369
+ * @returns {JSX.Element} The rendered DrawerPuller component.
370
+ */
371
+ const DrawerPuller = react.forwardRef(({ className, ...props }, ref) => {
372
+ return (jsxRuntime.jsx("div", { className: cvaPuller({ className }), "data-testid": "drawer-puller", ...props, ref: ref, children: jsxRuntime.jsx("div", { className: cvaPullerIcon() }) }));
373
+ });
374
+ DrawerPuller.displayName = "DrawerPuller";
375
+ const cvaPuller = cssClassVarianceUtilities.cvaMerge(["pt-2", "pb-4", "flex", "items-center", "justify-center"]);
376
+ const cvaPullerIcon = cssClassVarianceUtilities.cvaMerge(["block", "h-1", "w-8", "rounded-full", "bg-gray-400"]);
377
+
378
+ const CLOSING_THRESHOLD = 0.15;
379
+ /**
380
+ *
381
+ * SwipeableDrawer is a component that wraps the Drawer component to add swipeable functionality.
382
+ * It uses swipe handlers to detect swipe gestures for opening and closing the drawer.
383
+ * The component manages its docked state based on the open prop and keepMountedWhenClosed prop.
384
+ * It also applies styles dynamically based on whether the drawer is being dragged.
385
+ */
386
+ const SwipeableDrawer = ({ open, onClose, onOpenGesture, children, keepMountedWhenClosed, className, position = "bottom", ...others }) => {
387
+ const drawerRefs = react.useRef(null);
388
+ const [isDocked, setIsDocked] = react.useState(keepMountedWhenClosed && !open);
389
+ const { handlers, isDragging, dragDistance } = useSwipeHandlers({
390
+ ref: drawerRefs,
391
+ closingThreshold: CLOSING_THRESHOLD,
392
+ onCloseGesture: onClose,
393
+ onOpenGesture,
394
+ });
395
+ const drawerStyle = react.useMemo(() => isDragging
396
+ ? {
397
+ transform: `translateY(${dragDistance}px)`,
398
+ transition: "none",
399
+ }
400
+ : {}, [isDragging, dragDistance]);
401
+ react.useEffect(() => {
402
+ if (!keepMountedWhenClosed) {
403
+ return;
404
+ }
405
+ if (isDocked && open) {
406
+ setIsDocked(false);
407
+ }
408
+ else if (!isDocked && !open) {
409
+ setIsDocked(true);
410
+ }
411
+ }, [open, isDocked, keepMountedWhenClosed]);
412
+ react.useEffect(() => {
413
+ var _a, _b;
414
+ if (drawerRefs.current) {
415
+ drawerRefs.current.style.transform = (_a = drawerStyle.transform) !== null && _a !== void 0 ? _a : "";
416
+ drawerRefs.current.style.transition = (_b = drawerStyle.transition) !== null && _b !== void 0 ? _b : "";
417
+ }
418
+ }, [drawerStyle, drawerRefs]);
419
+ return (jsxRuntime.jsxs(Drawer, { className: cvaSwipeableDrawer({ docked: isDocked, className }), "data-docked": isDocked, keepMountedWhenClosed: keepMountedWhenClosed, onClose: onClose, open: open, position: position, ref: drawerRefs, ...others, children: [jsxRuntime.jsx(DrawerPuller, { ...handlers }), children] }));
420
+ };
421
+ const cvaSwipeableDrawer = cssClassVarianceUtilities.cvaMerge([], {
422
+ variants: {
423
+ docked: {
424
+ true: "translate-y-[calc(100%-30px)]",
425
+ false: "",
426
+ },
427
+ },
428
+ });
429
+
430
+ /*
431
+ * ----------------------------
432
+ * | SETUP TRANSLATIONS START |
433
+ * ----------------------------
434
+ * This import and function call is needed to register translations for this library.
435
+ * Do not remove this if this library has translations.
436
+ */
437
+ setupLibraryTranslations();
438
+
439
+ exports.Drawer = Drawer;
440
+ exports.DrawerToggle = DrawerToggle;
441
+ exports.SwipeableDrawer = SwipeableDrawer;
442
+ exports.TRANSITION_DURATION = TRANSITION_DURATION;
443
+ exports.cvaSwipeableDrawer = cvaSwipeableDrawer;
package/index.esm.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";