@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.
@@ -842,3 +842,123 @@ h6 {
842
842
  .modal__footer {
843
843
  padding-bottom: var(--spacing-16);
844
844
  }
845
+
846
+ .range {
847
+ align-items: center;
848
+ display: flex;
849
+ position: relative;
850
+ min-width: 20rem;
851
+ gap: var(--spacing-16);
852
+ }
853
+
854
+ .range__panel:hover {
855
+ border-radius: var(--spacing-8);
856
+ box-shadow: 2px 4px 6px 0 var(--color-primary-base);
857
+ transition: var(--transition-bg-cubic-bezier),
858
+ var(--transition-box-shadow-cubic-bezier);
859
+ }
860
+
861
+ .range__panel {
862
+ display: flex;
863
+ position: relative;
864
+ justify-content: space-between;
865
+ width: 100%;
866
+ }
867
+
868
+ .range__track {
869
+ background-color: var(--color-primary-bright);
870
+ border-radius: 0 var(--spacing-4) var(--spacing-4) 0;
871
+ height: 100%;
872
+ position: absolute;
873
+ z-index: 0;
874
+ }
875
+
876
+ .range__content {
877
+ align-items: start;
878
+ cursor: pointer;
879
+ display: flex;
880
+ flex-direction: column;
881
+ gap: var(--spacing-8);
882
+ width: var(--spacing-40);
883
+ z-index: 3;
884
+ }
885
+
886
+ .range__content:hover {
887
+ box-shadow: 5px 2px 6px 0 var(--color-main-color);
888
+ transition: var(--transition-box-shadow-cubic-bezier);
889
+ }
890
+
891
+ .range__content__period {
892
+ gap: 0;
893
+ width: var(--spacing-8);
894
+ }
895
+
896
+ .range__tick {
897
+ border-left: 2px solid
898
+ color-mix(in srgb, var(--color-primary-dark) 80%, transparent 10%);
899
+ width: 100%;
900
+ }
901
+
902
+ .range__tick__large {
903
+ height: 1rem;
904
+ }
905
+
906
+ .range__tick__small {
907
+ height: 0.5rem;
908
+ }
909
+
910
+ .range__value {
911
+ text-overflow: ellipsis;
912
+ overflow: hidden;
913
+ white-space: nowrap;
914
+ width: calc(80%);
915
+ }
916
+
917
+ .range__value__period {
918
+ border-left: 2px solid
919
+ color-mix(in srgb, var(--color-primary-dark) 80%, transparent 10%);
920
+ width: calc(100% + var(--spacing-40));
921
+ }
922
+
923
+ .range__value--over {
924
+ background-color: var(--color-main-background) !important;
925
+ overflow: visible;
926
+ white-space: unset;
927
+ width: unset;
928
+ position: absolute;
929
+ top: calc(var(--spacing-40) * 1);
930
+ }
931
+
932
+ .range__thumb {
933
+ position: absolute;
934
+ top: -32px;
935
+ background-color: transparent;
936
+ }
937
+
938
+ .button__icon.range__play {
939
+ background-color: var(--color-main-background);
940
+ border: 1px solid var(--color-primary-dark);
941
+ border-radius: var(--spacing-32);
942
+ z-index: 5;
943
+ }
944
+
945
+ .button__icon.range__play:hover {
946
+ border: 1px solid var(--color-primary-dark);
947
+ }
948
+
949
+ @media (max-width: 699px) {
950
+ .range {
951
+ flex-direction: column;
952
+ }
953
+ .range__panel {
954
+ min-width: none;
955
+ max-width: 30rem;
956
+ }
957
+ }
958
+
959
+ @media (max-width: 500px) {
960
+ .range__panel {
961
+ min-width: none;
962
+ max-width: 20rem;
963
+ }
964
+ }
@@ -10,6 +10,7 @@ export { Loading } from "./components/loading";
10
10
  export { Modal } from "./components/modal";
11
11
  export { Select } from "./components/select";
12
12
  export { TextArea } from "./components/text-area";
13
+ export { Range } from "./components/range";
13
14
  export { LoadingContext, LoadingProvider, useLoading } from "./context/loading";
14
15
  export { ModalContext, ModalProvider, useModal } from "./context/modal";
15
16
  export { ThemeContext, ThemeProvider, useTheme } from "./context/theme";
package/dist/cjs/index.js CHANGED
@@ -8,6 +8,7 @@ var bs = require('react-icons/bs');
8
8
  var reactHookForm = require('react-hook-form');
9
9
  var tb = require('react-icons/tb');
10
10
  var io5 = require('react-icons/io5');
11
+ var ri = require('react-icons/ri');
11
12
 
12
13
  const generateId = () => {
13
14
  return `id-${Math.random().toString(36).slice(2, 11)}`;
@@ -292,6 +293,240 @@ function Modal({ className, modalref, children, title, subtitle, footer, hasOver
292
293
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasOverlay && jsxRuntime.jsx("div", { className: "modal-overlay" }), jsxRuntime.jsxs("div", { ref: modalref, className: clsx("modal", className), ...rest, children: [hasCloseButton && (jsxRuntime.jsx(Button, { mode: "secondary", className: "modal__close", size: "small", onClick: handleClose, children: jsxRuntime.jsx(io5.IoClose, {}) })), jsxRuntime.jsxs("div", { className: "modal__content", children: [title && (jsxRuntime.jsxs("div", { className: "modal__header", children: [jsxRuntime.jsx("h3", { children: title }), jsxRuntime.jsx("h6", { children: subtitle })] })), jsxRuntime.jsx("div", { className: "modal__children", children: children }), footer && jsxRuntime.jsx("div", { className: "modal__footer", children: footer })] })] })] }));
293
294
  }
294
295
 
296
+ function usePositions(panelRef) {
297
+ const [allPositions, setAllPositions] = react.useState(undefined);
298
+ const [pixelPosition, setPixelPosition] = react.useState(undefined);
299
+ const [thumbPosition, setThumbPosition] = react.useState(undefined);
300
+ // find positions of all values
301
+ react.useEffect(() => {
302
+ if (panelRef?.current) {
303
+ const current = panelRef.current;
304
+ if (current) {
305
+ const valuesRef = Array.from(current.getElementsByClassName("range__content"));
306
+ const parentBounds = current.getBoundingClientRect();
307
+ setAllPositions(valuesRef.map((values) => {
308
+ const childBounds = values.getBoundingClientRect();
309
+ return childBounds.right - parentBounds.x;
310
+ }));
311
+ }
312
+ }
313
+ }, []);
314
+ // initial position
315
+ react.useEffect(() => {
316
+ if (allPositions) {
317
+ setPixelPosition(`${allPositions[0]}px`);
318
+ setThumbPosition(`${allPositions[0] / 2 - getThumbBounds().width / 2}px`);
319
+ }
320
+ }, [allPositions]);
321
+ return {
322
+ panelRef,
323
+ allPositions,
324
+ setAllPositions,
325
+ pixelPosition,
326
+ setPixelPosition,
327
+ thumbPosition,
328
+ setThumbPosition,
329
+ };
330
+ }
331
+ function getThumbBounds() {
332
+ const thumb = document.getElementsByClassName("range__thumb")[0];
333
+ return thumb.getBoundingClientRect();
334
+ }
335
+ function useTimer({ allPositions, indexPosition, handlePosition, handleOverContent, values, }) {
336
+ const timeoutRef = react.useRef(null);
337
+ const [isPlay, setIsPlay] = react.useState(false);
338
+ // timer
339
+ react.useEffect(() => {
340
+ resetTimeout();
341
+ if (isPlay) {
342
+ if (allPositions) {
343
+ timeoutRef.current = setTimeout(() => {
344
+ const newThumbPosition = allPositions[indexPosition] +
345
+ allPositions[0] / 2 -
346
+ getThumbBounds().width / 2;
347
+ handlePosition(allPositions[indexPosition + 1], newThumbPosition, indexPosition + 1, true);
348
+ handleOverContent(values[indexPosition + 1]);
349
+ }, 500);
350
+ if (indexPosition === allPositions.length - 1) {
351
+ setIsPlay(false);
352
+ }
353
+ }
354
+ }
355
+ return () => {
356
+ resetTimeout();
357
+ };
358
+ }, [isPlay, indexPosition]);
359
+ const resetTimeout = () => {
360
+ if (timeoutRef.current) {
361
+ clearTimeout(timeoutRef.current);
362
+ }
363
+ };
364
+ const handlePlay = () => {
365
+ setIsPlay(!isPlay);
366
+ };
367
+ const handleStop = () => {
368
+ if (allPositions) {
369
+ handlePosition(allPositions[0], allPositions[0] / 2 - getThumbBounds().width / 2, 0, false);
370
+ setIsPlay(false);
371
+ }
372
+ };
373
+ return { isPlay, setIsPlay, handlePlay, handleStop };
374
+ }
375
+ function useThumb({ allPositions, panelRef, handlePosition, handleOverContent, values, }) {
376
+ // Add drag
377
+ const startDragging = (event) => {
378
+ if (allPositions && panelRef?.current) {
379
+ const current = panelRef.current;
380
+ const panelBounds = current?.getBoundingClientRect();
381
+ const x = event.pageX - panelBounds.left;
382
+ const absX = Math.abs(x);
383
+ if (x >= 0 && x <= allPositions[allPositions.length - 1]) {
384
+ const desiredPosition = allPositions.reduce((prev, curr) => Math.abs(prev - absX) < Math.abs(curr - absX) ? prev : curr);
385
+ const index = allPositions.indexOf(desiredPosition);
386
+ const newThumbPosition = index === 0
387
+ ? allPositions[index] -
388
+ allPositions[0] / 2 -
389
+ getThumbBounds().width / 2
390
+ : allPositions[index - 1] +
391
+ allPositions[0] / 2 -
392
+ getThumbBounds().width / 2;
393
+ handlePosition(desiredPosition, newThumbPosition, index, true);
394
+ handleOverContent(values[index]);
395
+ }
396
+ }
397
+ };
398
+ const stopDragging = () => {
399
+ window.removeEventListener("mousemove", startDragging, false);
400
+ window.removeEventListener("mouseup", stopDragging, false);
401
+ };
402
+ const handleDownThumb = () => {
403
+ window.addEventListener("mousemove", startDragging, false);
404
+ window.addEventListener("mouseup", stopDragging, false);
405
+ };
406
+ return { handleDownThumb };
407
+ }
408
+
409
+ const ThemeContext = react.createContext(undefined);
410
+ function ThemeProvider({ children, dataTheme: dataThemeProp, dataPalette: dataPaletteProp = "water", }) {
411
+ const [dataTheme, setDataTheme] = react.useState();
412
+ const [dataPalette, setDataPalette] = react.useState();
413
+ react.useEffect(() => {
414
+ if (dataPaletteProp) {
415
+ switchDataPalette(dataPaletteProp);
416
+ }
417
+ }, [dataPaletteProp]);
418
+ react.useEffect(() => {
419
+ if (dataThemeProp) {
420
+ switchDataTheme(dataThemeProp);
421
+ }
422
+ }, [dataThemeProp]);
423
+ react.useEffect(() => {
424
+ if (window.matchMedia) {
425
+ if (localStorage.getItem("data-theme")) {
426
+ document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));
427
+ setDataTheme(localStorage.getItem("data-theme"));
428
+ }
429
+ else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
430
+ document.documentElement.setAttribute("data-theme", "dark");
431
+ setDataTheme("dark");
432
+ }
433
+ }
434
+ }, []);
435
+ const switchDataTheme = (mode) => {
436
+ document.documentElement.setAttribute("data-theme", mode);
437
+ setDataTheme(mode);
438
+ localStorage.setItem("data-theme", mode);
439
+ };
440
+ const switchDataPalette = (mode) => {
441
+ document.documentElement.setAttribute("data-palette", mode);
442
+ setDataPalette(mode);
443
+ localStorage.setItem("palette-theme", mode);
444
+ };
445
+ return (jsxRuntime.jsx(ThemeContext.Provider, { value: { dataTheme, switchDataTheme, dataPalette, switchDataPalette }, children: children }));
446
+ }
447
+ function useTheme() {
448
+ const context = react.useContext(ThemeContext);
449
+ if (context === undefined) {
450
+ throw new Error("useTheme must be used within a ThemeProvider");
451
+ }
452
+ return context;
453
+ }
454
+
455
+ function RangeThumb({ thumbPosition, handleOverContent, handleDownThumb }) {
456
+ const { dataTheme } = useTheme();
457
+ return (jsxRuntime.jsx(Button, { mode: "icon", className: "range__thumb", size: "small", style: {
458
+ left: `${thumbPosition}`,
459
+ }, onMouseOver: () => handleOverContent(thumbPosition), onMouseDown: handleDownThumb, children: dataTheme === "dark" ? (jsxRuntime.jsx(ri.RiMapPinTimeFill, { size: "1.5rem" })) : (jsxRuntime.jsx(ri.RiMapPinTimeLine, { size: "1.5rem" })) }));
460
+ }
461
+
462
+ function RangeContent({ period, values, allPositions, handlePosition, handleOverContent, handleOutContent, over, }) {
463
+ const handleClickRange = (index) => {
464
+ if (allPositions) {
465
+ const newThumbPosition = index === 0
466
+ ? allPositions[index] -
467
+ allPositions[0] / 2 -
468
+ getThumbBounds().width / 2
469
+ : allPositions[index - 1] +
470
+ allPositions[0] / 2 -
471
+ getThumbBounds().width / 2;
472
+ handlePosition(allPositions[index], newThumbPosition, index, true);
473
+ }
474
+ };
475
+ return period
476
+ ? values.map((value, index) => (jsxRuntime.jsx("div", { className: "range__content range__content__period", onClick: () => handleClickRange(index), onMouseOver: () => handleOverContent(value), onMouseOut: () => handleOutContent(), children: index % period === 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "range__tick range__tick__large" }), jsxRuntime.jsx("div", { className: clsx("range__value", "range__value__period", {
477
+ "range__value--over": value === over,
478
+ }), children: value })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "range__tick range__tick__small" }), value === over && (jsxRuntime.jsx("div", { className: clsx("range__value", "range__value__period", {
479
+ "range__value--over": value === over,
480
+ }), children: value }))] })) })))
481
+ : values.map((value, index) => (jsxRuntime.jsxs("div", { className: "range__content", onClick: () => handleClickRange(index), children: [jsxRuntime.jsx("div", { className: "range__tick range__tick__large" }), jsxRuntime.jsx("div", { className: "range__value", children: value })] })));
482
+ }
483
+
484
+ function RangeControls({ indexPosition, isPlay, handlePlay, handleStop, }) {
485
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Button, { mode: "icon", size: "small", className: "range__play", onClick: handlePlay, children: isPlay ? (jsxRuntime.jsx(io5.IoPause, { color: "var(--color-primary-base)", size: "2rem" })) : (jsxRuntime.jsx(io5.IoPlay, { color: "var(--color-primary-base)", size: "2rem" })) }), jsxRuntime.jsx(Button, { mode: "icon", size: "small", className: "range__play", disabled: !isPlay && indexPosition === 0, onClick: handleStop, children: jsxRuntime.jsx(io5.IoStop, { color: "var(--color-primary-base)", size: "2rem" }) })] }));
486
+ }
487
+
488
+ function Range({ period, values, handleSelected }) {
489
+ const panelRef = react.useRef(null);
490
+ const [indexPosition, setIndexPosition] = react.useState(0);
491
+ const [over, setOver] = react.useState(undefined);
492
+ const { allPositions, pixelPosition, setPixelPosition, thumbPosition, setThumbPosition, } = usePositions(panelRef);
493
+ const handlePosition = (pixelPosition, thumbPosition, index, isHandleSelected = false) => {
494
+ setPixelPosition(`${pixelPosition}px`);
495
+ setThumbPosition(`${thumbPosition}px`);
496
+ setIndexPosition(index);
497
+ if (isHandleSelected && handleSelected) {
498
+ handleSelected({
499
+ value: values[index],
500
+ index,
501
+ position: pixelPosition,
502
+ });
503
+ }
504
+ };
505
+ const handleOverContent = (value) => {
506
+ setOver(value);
507
+ };
508
+ const handleOutContent = () => {
509
+ setOver(undefined);
510
+ };
511
+ const { isPlay, handlePlay, handleStop } = useTimer({
512
+ allPositions,
513
+ indexPosition,
514
+ handlePosition,
515
+ handleOverContent,
516
+ values,
517
+ });
518
+ const { handleDownThumb } = useThumb({
519
+ allPositions,
520
+ panelRef,
521
+ handlePosition,
522
+ handleOverContent,
523
+ values,
524
+ });
525
+ return (jsxRuntime.jsxs("div", { className: "range", children: [jsxRuntime.jsxs("div", { ref: panelRef, className: "range__panel", children: [jsxRuntime.jsx(RangeContent, { period: period, values: values, allPositions: allPositions, handlePosition: handlePosition, handleOverContent: handleOverContent, handleOutContent: handleOutContent, over: over }), jsxRuntime.jsx("div", { className: "range__track", style: {
526
+ width: `${pixelPosition}`,
527
+ } }), jsxRuntime.jsx(RangeThumb, { thumbPosition: thumbPosition, handleOverContent: handleOverContent, handleDownThumb: handleDownThumb })] }), jsxRuntime.jsx(RangeControls, { indexPosition: indexPosition, isPlay: isPlay, handlePlay: handlePlay, handleStop: handleStop })] }));
528
+ }
529
+
295
530
  const LoadingContext = react.createContext(undefined);
296
531
  function loadingReducer(state, action) {
297
532
  switch (action.type) {
@@ -423,52 +658,6 @@ function useModal({} = {}) {
423
658
  return context;
424
659
  }
425
660
 
426
- const ThemeContext = react.createContext(undefined);
427
- function ThemeProvider({ children, dataTheme: dataThemeProp, dataPalette: dataPaletteProp = "water", }) {
428
- const [dataTheme, setDataTheme] = react.useState();
429
- const [dataPalette, setDataPalette] = react.useState();
430
- react.useEffect(() => {
431
- if (dataPaletteProp) {
432
- switchDataPalette(dataPaletteProp);
433
- }
434
- }, [dataPaletteProp]);
435
- react.useEffect(() => {
436
- if (dataThemeProp) {
437
- switchDataTheme(dataThemeProp);
438
- }
439
- }, [dataThemeProp]);
440
- react.useEffect(() => {
441
- if (window.matchMedia) {
442
- if (localStorage.getItem("data-theme")) {
443
- document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));
444
- setDataTheme(localStorage.getItem("data-theme"));
445
- }
446
- else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
447
- document.documentElement.setAttribute("data-theme", "dark");
448
- setDataTheme("dark");
449
- }
450
- }
451
- }, []);
452
- const switchDataTheme = (mode) => {
453
- document.documentElement.setAttribute("data-theme", mode);
454
- setDataTheme(mode);
455
- localStorage.setItem("data-theme", mode);
456
- };
457
- const switchDataPalette = (mode) => {
458
- document.documentElement.setAttribute("data-palette", mode);
459
- setDataPalette(mode);
460
- localStorage.setItem("palette-theme", mode);
461
- };
462
- return (jsxRuntime.jsx(ThemeContext.Provider, { value: { dataTheme, switchDataTheme, dataPalette, switchDataPalette }, children: children }));
463
- }
464
- function useTheme() {
465
- const context = react.useContext(ThemeContext);
466
- if (context === undefined) {
467
- throw new Error("useTheme must be used within a ThemeProvider");
468
- }
469
- return context;
470
- }
471
-
472
661
  exports.Button = Button;
473
662
  exports.Form = Form;
474
663
  exports.FormInput = FormInput;
@@ -482,6 +671,7 @@ exports.Menu = Menu;
482
671
  exports.Modal = Modal;
483
672
  exports.ModalContext = ModalContext;
484
673
  exports.ModalProvider = ModalProvider;
674
+ exports.Range = Range;
485
675
  exports.Select = Select;
486
676
  exports.TextArea = TextArea;
487
677
  exports.ThemeContext = ThemeContext;