@geoinsight/react-components 1.1.0 → 1.1.2

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.
Binary file
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react";
2
2
  import Menu from ".";
3
3
  declare const meta: Meta<typeof Menu>;
4
4
  type Story = StoryObj<typeof Menu>;
5
+ export declare const Simple: Story;
5
6
  export declare const PrimaryOpenByDefault: Story;
6
7
  export declare const SecondaryClosedByDefault: Story;
7
8
  export default meta;
@@ -12,7 +12,7 @@ type TimerProps = Props & {
12
12
  type ThumbProps = Props & {
13
13
  panelRef: MutableRefObject<null>;
14
14
  };
15
- export declare function usePositions(panelRef: MutableRefObject<null>): {
15
+ export declare function usePositions(panelRef: MutableRefObject<null>, initial: number): {
16
16
  panelRef: MutableRefObject<null>;
17
17
  allPositions: number[] | undefined;
18
18
  setAllPositions: import("react").Dispatch<import("react").SetStateAction<number[] | undefined>>;
@@ -1,4 +1,4 @@
1
1
  import "./index.css";
2
2
  import { Range as RangeProps } from "./index.types";
3
- export declare function Range({ period, values, handleSelected }: RangeProps): import("react/jsx-runtime").JSX.Element;
3
+ export declare function Range({ initial, period, values, handleSelected }: RangeProps): import("react/jsx-runtime").JSX.Element;
4
4
  export default Range;
@@ -6,4 +6,5 @@ type Story = StoryObj<typeof Range>;
6
6
  export declare const RangeMonths: Story;
7
7
  export declare const RangeTimestamps: Story;
8
8
  export declare const RangeTimestampsPeriod: Story;
9
+ export declare const RangeTimestampsInitialValue: Story;
9
10
  export declare const RangeUseCase: Story;
@@ -1,4 +1,8 @@
1
1
  export interface Range {
2
+ /**
3
+ * Initial value for range
4
+ */
5
+ initial?: number;
2
6
  /**
3
7
  * Function that handles any external logic
4
8
  */
@@ -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,241 @@ 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, initial) {
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[initial]}px`);
318
+ setThumbPosition(`${allPositions[initial] -
319
+ (allPositions[0] / 2 + getThumbBounds().width / 2)}px`);
320
+ }
321
+ }, [allPositions]);
322
+ return {
323
+ panelRef,
324
+ allPositions,
325
+ setAllPositions,
326
+ pixelPosition,
327
+ setPixelPosition,
328
+ thumbPosition,
329
+ setThumbPosition,
330
+ };
331
+ }
332
+ function getThumbBounds() {
333
+ const thumb = document.getElementsByClassName("range__thumb")[0];
334
+ return thumb.getBoundingClientRect();
335
+ }
336
+ function useTimer({ allPositions, indexPosition, handlePosition, handleOverContent, values, }) {
337
+ const timeoutRef = react.useRef(null);
338
+ const [isPlay, setIsPlay] = react.useState(false);
339
+ // timer
340
+ react.useEffect(() => {
341
+ resetTimeout();
342
+ if (isPlay) {
343
+ if (allPositions) {
344
+ timeoutRef.current = setTimeout(() => {
345
+ const newThumbPosition = allPositions[indexPosition] +
346
+ allPositions[0] / 2 -
347
+ getThumbBounds().width / 2;
348
+ handlePosition(allPositions[indexPosition + 1], newThumbPosition, indexPosition + 1, true);
349
+ handleOverContent(values[indexPosition + 1]);
350
+ }, 500);
351
+ if (indexPosition === allPositions.length - 1) {
352
+ setIsPlay(false);
353
+ }
354
+ }
355
+ }
356
+ return () => {
357
+ resetTimeout();
358
+ };
359
+ }, [isPlay, indexPosition]);
360
+ const resetTimeout = () => {
361
+ if (timeoutRef.current) {
362
+ clearTimeout(timeoutRef.current);
363
+ }
364
+ };
365
+ const handlePlay = () => {
366
+ setIsPlay(!isPlay);
367
+ };
368
+ const handleStop = () => {
369
+ if (allPositions) {
370
+ handlePosition(allPositions[0], allPositions[0] / 2 - getThumbBounds().width / 2, 0, false);
371
+ setIsPlay(false);
372
+ }
373
+ };
374
+ return { isPlay, setIsPlay, handlePlay, handleStop };
375
+ }
376
+ function useThumb({ allPositions, panelRef, handlePosition, handleOverContent, values, }) {
377
+ // Add drag
378
+ const startDragging = (event) => {
379
+ if (allPositions && panelRef?.current) {
380
+ const current = panelRef.current;
381
+ const panelBounds = current?.getBoundingClientRect();
382
+ const x = event.pageX - panelBounds.left;
383
+ const absX = Math.abs(x);
384
+ if (x >= 0 && x <= allPositions[allPositions.length - 1]) {
385
+ const desiredPosition = allPositions.reduce((prev, curr) => Math.abs(prev - absX) < Math.abs(curr - absX) ? prev : curr);
386
+ const index = allPositions.indexOf(desiredPosition);
387
+ const newThumbPosition = index === 0
388
+ ? allPositions[index] -
389
+ allPositions[0] / 2 -
390
+ getThumbBounds().width / 2
391
+ : allPositions[index - 1] +
392
+ allPositions[0] / 2 -
393
+ getThumbBounds().width / 2;
394
+ handlePosition(desiredPosition, newThumbPosition, index, true);
395
+ handleOverContent(values[index]);
396
+ }
397
+ }
398
+ };
399
+ const stopDragging = () => {
400
+ window.removeEventListener("mousemove", startDragging, false);
401
+ window.removeEventListener("mouseup", stopDragging, false);
402
+ };
403
+ const handleDownThumb = () => {
404
+ window.addEventListener("mousemove", startDragging, false);
405
+ window.addEventListener("mouseup", stopDragging, false);
406
+ };
407
+ return { handleDownThumb };
408
+ }
409
+
410
+ const ThemeContext = react.createContext(undefined);
411
+ function ThemeProvider({ children, dataTheme: dataThemeProp, dataPalette: dataPaletteProp = "water", }) {
412
+ const [dataTheme, setDataTheme] = react.useState();
413
+ const [dataPalette, setDataPalette] = react.useState();
414
+ react.useEffect(() => {
415
+ if (dataPaletteProp) {
416
+ switchDataPalette(dataPaletteProp);
417
+ }
418
+ }, [dataPaletteProp]);
419
+ react.useEffect(() => {
420
+ if (dataThemeProp) {
421
+ switchDataTheme(dataThemeProp);
422
+ }
423
+ }, [dataThemeProp]);
424
+ react.useEffect(() => {
425
+ if (window.matchMedia) {
426
+ if (localStorage.getItem("data-theme")) {
427
+ document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));
428
+ setDataTheme(localStorage.getItem("data-theme"));
429
+ }
430
+ else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
431
+ document.documentElement.setAttribute("data-theme", "dark");
432
+ setDataTheme("dark");
433
+ }
434
+ }
435
+ }, []);
436
+ const switchDataTheme = (mode) => {
437
+ document.documentElement.setAttribute("data-theme", mode);
438
+ setDataTheme(mode);
439
+ localStorage.setItem("data-theme", mode);
440
+ };
441
+ const switchDataPalette = (mode) => {
442
+ document.documentElement.setAttribute("data-palette", mode);
443
+ setDataPalette(mode);
444
+ localStorage.setItem("palette-theme", mode);
445
+ };
446
+ return (jsxRuntime.jsx(ThemeContext.Provider, { value: { dataTheme, switchDataTheme, dataPalette, switchDataPalette }, children: children }));
447
+ }
448
+ function useTheme() {
449
+ const context = react.useContext(ThemeContext);
450
+ if (context === undefined) {
451
+ throw new Error("useTheme must be used within a ThemeProvider");
452
+ }
453
+ return context;
454
+ }
455
+
456
+ function RangeThumb({ thumbPosition, handleOverContent, handleDownThumb }) {
457
+ const { dataTheme } = useTheme();
458
+ return (jsxRuntime.jsx(Button, { mode: "icon", className: "range__thumb", size: "small", style: {
459
+ left: `${thumbPosition}`,
460
+ }, onMouseOver: () => handleOverContent(thumbPosition), onMouseDown: handleDownThumb, children: dataTheme === "dark" ? (jsxRuntime.jsx(ri.RiMapPinTimeFill, { size: "1.5rem" })) : (jsxRuntime.jsx(ri.RiMapPinTimeLine, { size: "1.5rem" })) }));
461
+ }
462
+
463
+ function RangeContent({ period, values, allPositions, handlePosition, handleOverContent, handleOutContent, over, }) {
464
+ const handleClickRange = (index) => {
465
+ if (allPositions) {
466
+ const newThumbPosition = index === 0
467
+ ? allPositions[index] -
468
+ allPositions[0] / 2 -
469
+ getThumbBounds().width / 2
470
+ : allPositions[index - 1] +
471
+ allPositions[0] / 2 -
472
+ getThumbBounds().width / 2;
473
+ handlePosition(allPositions[index], newThumbPosition, index, true);
474
+ }
475
+ };
476
+ return period
477
+ ? 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", {
478
+ "range__value--over": value === over,
479
+ }), 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", {
480
+ "range__value--over": value === over,
481
+ }), children: value }))] })) })))
482
+ : 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 })] })));
483
+ }
484
+
485
+ function RangeControls({ indexPosition, isPlay, handlePlay, handleStop, }) {
486
+ 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" }) })] }));
487
+ }
488
+
489
+ function Range({ initial = 0, period, values, handleSelected }) {
490
+ const panelRef = react.useRef(null);
491
+ const [indexPosition, setIndexPosition] = react.useState(initial);
492
+ const [over, setOver] = react.useState(undefined);
493
+ const { allPositions, pixelPosition, setPixelPosition, thumbPosition, setThumbPosition, } = usePositions(panelRef, initial);
494
+ const handlePosition = (pixelPosition, thumbPosition, index, isHandleSelected = false) => {
495
+ setPixelPosition(`${pixelPosition}px`);
496
+ setThumbPosition(`${thumbPosition}px`);
497
+ setIndexPosition(index);
498
+ if (isHandleSelected && handleSelected) {
499
+ handleSelected({
500
+ value: values[index],
501
+ index,
502
+ position: pixelPosition,
503
+ });
504
+ }
505
+ };
506
+ const handleOverContent = (value) => {
507
+ setOver(value);
508
+ };
509
+ const handleOutContent = () => {
510
+ setOver(undefined);
511
+ };
512
+ const { isPlay, handlePlay, handleStop } = useTimer({
513
+ allPositions,
514
+ indexPosition,
515
+ handlePosition,
516
+ handleOverContent,
517
+ values,
518
+ });
519
+ const { handleDownThumb } = useThumb({
520
+ allPositions,
521
+ panelRef,
522
+ handlePosition,
523
+ handleOverContent,
524
+ values,
525
+ });
526
+ 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: {
527
+ width: `${pixelPosition}`,
528
+ } }), jsxRuntime.jsx(RangeThumb, { thumbPosition: thumbPosition, handleOverContent: handleOverContent, handleDownThumb: handleDownThumb })] }), jsxRuntime.jsx(RangeControls, { indexPosition: indexPosition, isPlay: isPlay, handlePlay: handlePlay, handleStop: handleStop })] }));
529
+ }
530
+
295
531
  const LoadingContext = react.createContext(undefined);
296
532
  function loadingReducer(state, action) {
297
533
  switch (action.type) {
@@ -423,52 +659,6 @@ function useModal({} = {}) {
423
659
  return context;
424
660
  }
425
661
 
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
662
  exports.Button = Button;
473
663
  exports.Form = Form;
474
664
  exports.FormInput = FormInput;
@@ -482,6 +672,7 @@ exports.Menu = Menu;
482
672
  exports.Modal = Modal;
483
673
  exports.ModalContext = ModalContext;
484
674
  exports.ModalProvider = ModalProvider;
675
+ exports.Range = Range;
485
676
  exports.Select = Select;
486
677
  exports.TextArea = TextArea;
487
678
  exports.ThemeContext = ThemeContext;