@geoinsight/react-components 1.1.0 → 1.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.
package/dist/esm/index.js CHANGED
@@ -5,7 +5,8 @@ import { TfiAngleDown, TfiAngleUp } from 'react-icons/tfi';
5
5
  import { BsCheckCircleFill, BsXCircleFill } from 'react-icons/bs';
6
6
  import { useForm, FormProvider, useFormContext, Controller } from 'react-hook-form';
7
7
  import { TbArrowsDiagonal2 } from 'react-icons/tb';
8
- import { IoClose } from 'react-icons/io5';
8
+ import { IoClose, IoPause, IoPlay, IoStop } from 'react-icons/io5';
9
+ import { RiMapPinTimeFill, RiMapPinTimeLine } from 'react-icons/ri';
9
10
 
10
11
  const generateId = () => {
11
12
  return `id-${Math.random().toString(36).slice(2, 11)}`;
@@ -290,6 +291,240 @@ function Modal({ className, modalref, children, title, subtitle, footer, hasOver
290
291
  return (jsxs(Fragment, { children: [hasOverlay && jsx("div", { className: "modal-overlay" }), jsxs("div", { ref: modalref, className: clsx("modal", className), ...rest, children: [hasCloseButton && (jsx(Button, { mode: "secondary", className: "modal__close", size: "small", onClick: handleClose, children: jsx(IoClose, {}) })), jsxs("div", { className: "modal__content", children: [title && (jsxs("div", { className: "modal__header", children: [jsx("h3", { children: title }), jsx("h6", { children: subtitle })] })), jsx("div", { className: "modal__children", children: children }), footer && jsx("div", { className: "modal__footer", children: footer })] })] })] }));
291
292
  }
292
293
 
294
+ function usePositions(panelRef) {
295
+ const [allPositions, setAllPositions] = useState(undefined);
296
+ const [pixelPosition, setPixelPosition] = useState(undefined);
297
+ const [thumbPosition, setThumbPosition] = useState(undefined);
298
+ // find positions of all values
299
+ useEffect(() => {
300
+ if (panelRef?.current) {
301
+ const current = panelRef.current;
302
+ if (current) {
303
+ const valuesRef = Array.from(current.getElementsByClassName("range__content"));
304
+ const parentBounds = current.getBoundingClientRect();
305
+ setAllPositions(valuesRef.map((values) => {
306
+ const childBounds = values.getBoundingClientRect();
307
+ return childBounds.right - parentBounds.x;
308
+ }));
309
+ }
310
+ }
311
+ }, []);
312
+ // initial position
313
+ useEffect(() => {
314
+ if (allPositions) {
315
+ setPixelPosition(`${allPositions[0]}px`);
316
+ setThumbPosition(`${allPositions[0] / 2 - getThumbBounds().width / 2}px`);
317
+ }
318
+ }, [allPositions]);
319
+ return {
320
+ panelRef,
321
+ allPositions,
322
+ setAllPositions,
323
+ pixelPosition,
324
+ setPixelPosition,
325
+ thumbPosition,
326
+ setThumbPosition,
327
+ };
328
+ }
329
+ function getThumbBounds() {
330
+ const thumb = document.getElementsByClassName("range__thumb")[0];
331
+ return thumb.getBoundingClientRect();
332
+ }
333
+ function useTimer({ allPositions, indexPosition, handlePosition, handleOverContent, values, }) {
334
+ const timeoutRef = useRef(null);
335
+ const [isPlay, setIsPlay] = useState(false);
336
+ // timer
337
+ useEffect(() => {
338
+ resetTimeout();
339
+ if (isPlay) {
340
+ if (allPositions) {
341
+ timeoutRef.current = setTimeout(() => {
342
+ const newThumbPosition = allPositions[indexPosition] +
343
+ allPositions[0] / 2 -
344
+ getThumbBounds().width / 2;
345
+ handlePosition(allPositions[indexPosition + 1], newThumbPosition, indexPosition + 1, true);
346
+ handleOverContent(values[indexPosition + 1]);
347
+ }, 500);
348
+ if (indexPosition === allPositions.length - 1) {
349
+ setIsPlay(false);
350
+ }
351
+ }
352
+ }
353
+ return () => {
354
+ resetTimeout();
355
+ };
356
+ }, [isPlay, indexPosition]);
357
+ const resetTimeout = () => {
358
+ if (timeoutRef.current) {
359
+ clearTimeout(timeoutRef.current);
360
+ }
361
+ };
362
+ const handlePlay = () => {
363
+ setIsPlay(!isPlay);
364
+ };
365
+ const handleStop = () => {
366
+ if (allPositions) {
367
+ handlePosition(allPositions[0], allPositions[0] / 2 - getThumbBounds().width / 2, 0, false);
368
+ setIsPlay(false);
369
+ }
370
+ };
371
+ return { isPlay, setIsPlay, handlePlay, handleStop };
372
+ }
373
+ function useThumb({ allPositions, panelRef, handlePosition, handleOverContent, values, }) {
374
+ // Add drag
375
+ const startDragging = (event) => {
376
+ if (allPositions && panelRef?.current) {
377
+ const current = panelRef.current;
378
+ const panelBounds = current?.getBoundingClientRect();
379
+ const x = event.pageX - panelBounds.left;
380
+ const absX = Math.abs(x);
381
+ if (x >= 0 && x <= allPositions[allPositions.length - 1]) {
382
+ const desiredPosition = allPositions.reduce((prev, curr) => Math.abs(prev - absX) < Math.abs(curr - absX) ? prev : curr);
383
+ const index = allPositions.indexOf(desiredPosition);
384
+ const newThumbPosition = index === 0
385
+ ? allPositions[index] -
386
+ allPositions[0] / 2 -
387
+ getThumbBounds().width / 2
388
+ : allPositions[index - 1] +
389
+ allPositions[0] / 2 -
390
+ getThumbBounds().width / 2;
391
+ handlePosition(desiredPosition, newThumbPosition, index, true);
392
+ handleOverContent(values[index]);
393
+ }
394
+ }
395
+ };
396
+ const stopDragging = () => {
397
+ window.removeEventListener("mousemove", startDragging, false);
398
+ window.removeEventListener("mouseup", stopDragging, false);
399
+ };
400
+ const handleDownThumb = () => {
401
+ window.addEventListener("mousemove", startDragging, false);
402
+ window.addEventListener("mouseup", stopDragging, false);
403
+ };
404
+ return { handleDownThumb };
405
+ }
406
+
407
+ const ThemeContext = createContext(undefined);
408
+ function ThemeProvider({ children, dataTheme: dataThemeProp, dataPalette: dataPaletteProp = "water", }) {
409
+ const [dataTheme, setDataTheme] = useState();
410
+ const [dataPalette, setDataPalette] = useState();
411
+ useEffect(() => {
412
+ if (dataPaletteProp) {
413
+ switchDataPalette(dataPaletteProp);
414
+ }
415
+ }, [dataPaletteProp]);
416
+ useEffect(() => {
417
+ if (dataThemeProp) {
418
+ switchDataTheme(dataThemeProp);
419
+ }
420
+ }, [dataThemeProp]);
421
+ useEffect(() => {
422
+ if (window.matchMedia) {
423
+ if (localStorage.getItem("data-theme")) {
424
+ document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));
425
+ setDataTheme(localStorage.getItem("data-theme"));
426
+ }
427
+ else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
428
+ document.documentElement.setAttribute("data-theme", "dark");
429
+ setDataTheme("dark");
430
+ }
431
+ }
432
+ }, []);
433
+ const switchDataTheme = (mode) => {
434
+ document.documentElement.setAttribute("data-theme", mode);
435
+ setDataTheme(mode);
436
+ localStorage.setItem("data-theme", mode);
437
+ };
438
+ const switchDataPalette = (mode) => {
439
+ document.documentElement.setAttribute("data-palette", mode);
440
+ setDataPalette(mode);
441
+ localStorage.setItem("palette-theme", mode);
442
+ };
443
+ return (jsx(ThemeContext.Provider, { value: { dataTheme, switchDataTheme, dataPalette, switchDataPalette }, children: children }));
444
+ }
445
+ function useTheme() {
446
+ const context = useContext(ThemeContext);
447
+ if (context === undefined) {
448
+ throw new Error("useTheme must be used within a ThemeProvider");
449
+ }
450
+ return context;
451
+ }
452
+
453
+ function RangeThumb({ thumbPosition, handleOverContent, handleDownThumb }) {
454
+ const { dataTheme } = useTheme();
455
+ return (jsx(Button, { mode: "icon", className: "range__thumb", size: "small", style: {
456
+ left: `${thumbPosition}`,
457
+ }, onMouseOver: () => handleOverContent(thumbPosition), onMouseDown: handleDownThumb, children: dataTheme === "dark" ? (jsx(RiMapPinTimeFill, { size: "1.5rem" })) : (jsx(RiMapPinTimeLine, { size: "1.5rem" })) }));
458
+ }
459
+
460
+ function RangeContent({ period, values, allPositions, handlePosition, handleOverContent, handleOutContent, over, }) {
461
+ const handleClickRange = (index) => {
462
+ if (allPositions) {
463
+ const newThumbPosition = index === 0
464
+ ? allPositions[index] -
465
+ allPositions[0] / 2 -
466
+ getThumbBounds().width / 2
467
+ : allPositions[index - 1] +
468
+ allPositions[0] / 2 -
469
+ getThumbBounds().width / 2;
470
+ handlePosition(allPositions[index], newThumbPosition, index, true);
471
+ }
472
+ };
473
+ return period
474
+ ? values.map((value, index) => (jsx("div", { className: "range__content range__content__period", onClick: () => handleClickRange(index), onMouseOver: () => handleOverContent(value), onMouseOut: () => handleOutContent(), children: index % period === 0 ? (jsxs(Fragment, { children: [jsx("div", { className: "range__tick range__tick__large" }), jsx("div", { className: clsx("range__value", "range__value__period", {
475
+ "range__value--over": value === over,
476
+ }), children: value })] })) : (jsxs(Fragment, { children: [jsx("div", { className: "range__tick range__tick__small" }), value === over && (jsx("div", { className: clsx("range__value", "range__value__period", {
477
+ "range__value--over": value === over,
478
+ }), children: value }))] })) })))
479
+ : values.map((value, index) => (jsxs("div", { className: "range__content", onClick: () => handleClickRange(index), children: [jsx("div", { className: "range__tick range__tick__large" }), jsx("div", { className: "range__value", children: value })] })));
480
+ }
481
+
482
+ function RangeControls({ indexPosition, isPlay, handlePlay, handleStop, }) {
483
+ return (jsxs(Fragment, { children: [jsx(Button, { mode: "icon", size: "small", className: "range__play", onClick: handlePlay, children: isPlay ? (jsx(IoPause, { color: "var(--color-primary-base)", size: "2rem" })) : (jsx(IoPlay, { color: "var(--color-primary-base)", size: "2rem" })) }), jsx(Button, { mode: "icon", size: "small", className: "range__play", disabled: !isPlay && indexPosition === 0, onClick: handleStop, children: jsx(IoStop, { color: "var(--color-primary-base)", size: "2rem" }) })] }));
484
+ }
485
+
486
+ function Range({ period, values, handleSelected }) {
487
+ const panelRef = useRef(null);
488
+ const [indexPosition, setIndexPosition] = useState(0);
489
+ const [over, setOver] = useState(undefined);
490
+ const { allPositions, pixelPosition, setPixelPosition, thumbPosition, setThumbPosition, } = usePositions(panelRef);
491
+ const handlePosition = (pixelPosition, thumbPosition, index, isHandleSelected = false) => {
492
+ setPixelPosition(`${pixelPosition}px`);
493
+ setThumbPosition(`${thumbPosition}px`);
494
+ setIndexPosition(index);
495
+ if (isHandleSelected && handleSelected) {
496
+ handleSelected({
497
+ value: values[index],
498
+ index,
499
+ position: pixelPosition,
500
+ });
501
+ }
502
+ };
503
+ const handleOverContent = (value) => {
504
+ setOver(value);
505
+ };
506
+ const handleOutContent = () => {
507
+ setOver(undefined);
508
+ };
509
+ const { isPlay, handlePlay, handleStop } = useTimer({
510
+ allPositions,
511
+ indexPosition,
512
+ handlePosition,
513
+ handleOverContent,
514
+ values,
515
+ });
516
+ const { handleDownThumb } = useThumb({
517
+ allPositions,
518
+ panelRef,
519
+ handlePosition,
520
+ handleOverContent,
521
+ values,
522
+ });
523
+ return (jsxs("div", { className: "range", children: [jsxs("div", { ref: panelRef, className: "range__panel", children: [jsx(RangeContent, { period: period, values: values, allPositions: allPositions, handlePosition: handlePosition, handleOverContent: handleOverContent, handleOutContent: handleOutContent, over: over }), jsx("div", { className: "range__track", style: {
524
+ width: `${pixelPosition}`,
525
+ } }), jsx(RangeThumb, { thumbPosition: thumbPosition, handleOverContent: handleOverContent, handleDownThumb: handleDownThumb })] }), jsx(RangeControls, { indexPosition: indexPosition, isPlay: isPlay, handlePlay: handlePlay, handleStop: handleStop })] }));
526
+ }
527
+
293
528
  const LoadingContext = createContext(undefined);
294
529
  function loadingReducer(state, action) {
295
530
  switch (action.type) {
@@ -421,51 +656,5 @@ function useModal({} = {}) {
421
656
  return context;
422
657
  }
423
658
 
424
- const ThemeContext = createContext(undefined);
425
- function ThemeProvider({ children, dataTheme: dataThemeProp, dataPalette: dataPaletteProp = "water", }) {
426
- const [dataTheme, setDataTheme] = useState();
427
- const [dataPalette, setDataPalette] = useState();
428
- useEffect(() => {
429
- if (dataPaletteProp) {
430
- switchDataPalette(dataPaletteProp);
431
- }
432
- }, [dataPaletteProp]);
433
- useEffect(() => {
434
- if (dataThemeProp) {
435
- switchDataTheme(dataThemeProp);
436
- }
437
- }, [dataThemeProp]);
438
- useEffect(() => {
439
- if (window.matchMedia) {
440
- if (localStorage.getItem("data-theme")) {
441
- document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));
442
- setDataTheme(localStorage.getItem("data-theme"));
443
- }
444
- else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
445
- document.documentElement.setAttribute("data-theme", "dark");
446
- setDataTheme("dark");
447
- }
448
- }
449
- }, []);
450
- const switchDataTheme = (mode) => {
451
- document.documentElement.setAttribute("data-theme", mode);
452
- setDataTheme(mode);
453
- localStorage.setItem("data-theme", mode);
454
- };
455
- const switchDataPalette = (mode) => {
456
- document.documentElement.setAttribute("data-palette", mode);
457
- setDataPalette(mode);
458
- localStorage.setItem("palette-theme", mode);
459
- };
460
- return (jsx(ThemeContext.Provider, { value: { dataTheme, switchDataTheme, dataPalette, switchDataPalette }, children: children }));
461
- }
462
- function useTheme() {
463
- const context = useContext(ThemeContext);
464
- if (context === undefined) {
465
- throw new Error("useTheme must be used within a ThemeProvider");
466
- }
467
- return context;
468
- }
469
-
470
- export { Button, Form, FormInput, FormSelect, FormTextArea, Input, Loading, LoadingContext, LoadingProvider, Menu, Modal, ModalContext, ModalProvider, Select, TextArea, ThemeContext, ThemeProvider, useLoading, useModal, useTheme };
659
+ export { Button, Form, FormInput, FormSelect, FormTextArea, Input, Loading, LoadingContext, LoadingProvider, Menu, Modal, ModalContext, ModalProvider, Range, Select, TextArea, ThemeContext, ThemeProvider, useLoading, useModal, useTheme };
471
660
  //# sourceMappingURL=index.js.map