kitzo 2.1.20 → 2.1.21

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/README.md CHANGED
@@ -26,7 +26,7 @@ npm i kitzo
26
26
  or
27
27
 
28
28
  ```javascript
29
- <script src="https://cdn.jsdelivr.net/npm/kitzo@2.1.20/dist/kitzo.umd.min.js"></script>
29
+ <script src="https://cdn.jsdelivr.net/npm/kitzo@2.1.21/dist/kitzo.umd.min.js"></script>
30
30
  ```
31
31
 
32
32
  > Vanilla: Attach this script tag in the html head tag and you are good to go.
@@ -552,9 +552,21 @@ function ToastContainer(props) {
552
552
  }));
553
553
  }
554
554
 
555
- const tooltipStyles = `
555
+ // import './style.css';
556
+ const tooltipStyles = `/* Default styling */
557
+ .kitzo-react-tooltip-root {
558
+ --bg-clr: hsl(0, 0%, 15%);
559
+ --text-clr: hsl(0, 0%, 95%);
560
+
561
+ @media (prefers-color-scheme: dark) {
562
+ --bg-clr: hsl(0, 0%, 95%);
563
+ --text-clr: hsl(0, 0%, 15%);
564
+ }
565
+ }
566
+
556
567
  .kitzo-react-tooltip-content-default-style {
557
568
  font-family:
569
+ inherit,
558
570
  system-ui,
559
571
  -apple-system,
560
572
  BlinkMacSystemFont,
@@ -567,84 +579,184 @@ const tooltipStyles = `
567
579
  'Helvetica Neue',
568
580
  sans-serif;
569
581
  font-size: 0.875rem;
570
- background-color: hsl(0, 0%, 15%);
571
- color: hsl(0, 0%, 95%);
582
+ background-color: var(--bg-clr);
583
+ color: var(--text-clr);
572
584
  padding-block: 0.25rem;
573
585
  padding-inline: 0.5rem;
574
586
  border-radius: 0.325rem;
575
-
576
- @media (prefers-color-scheme: dark) {
577
- background-color: hsl(0, 0%, 95%);
578
- color: hsl(0, 0%, 15%);
579
- }
580
587
  }
581
588
 
589
+ /* Tooltip positioning */
582
590
  .kitzo-react-tooltip-wrapper {
583
- --tooltip-transition-delay: calc(var(--delay) * 1ms);
584
- transition:
585
- transform 110ms var(--tooltip-transition-delay),
586
- opacity 110ms var(--tooltip-transition-delay);
591
+ --tooltip-offset: calc(var(--offset) * 1px + 1px);
592
+ }
593
+ /* Top */
594
+ .kitzo-react-tooltip-wrapper.top {
595
+ bottom: 100%;
596
+ padding-block-end: var(--tooltip-offset);
587
597
  }
588
-
589
598
  .kitzo-react-tooltip-wrapper.top {
590
- --tooltip-offset: calc(var(--offset) * 1px);
591
-
592
- bottom: calc(var(--tooltip-offset) + 100%);
593
599
  left: 50%;
594
- transform: translateX(-50%) translateY(0) scale(0.8);
595
- opacity: 0;
596
- transform-origin: bottom;
600
+ translate: -50% 0;
601
+ }
602
+ .kitzo-react-tooltip-wrapper.top.start {
603
+ left: 0;
604
+ translate: 0 0;
605
+ }
606
+ .kitzo-react-tooltip-wrapper.top.end {
607
+ right: 0;
608
+ translate: 0 0;
597
609
  }
598
610
 
611
+ /* Right */
612
+ .kitzo-react-tooltip-wrapper.right {
613
+ left: 100%;
614
+ padding-inline-start: var(--tooltip-offset);
615
+ }
599
616
  .kitzo-react-tooltip-wrapper.right {
600
- --tooltip-offset: calc(var(--offset) * 1px);
601
-
602
- left: calc(var(--tooltip-offset) + 100%);
603
617
  top: 50%;
604
- transform: translateX(0) translateY(-50%) scale(0.8);
605
- opacity: 0;
606
- transform-origin: left;
618
+ translate: 0 -50%;
619
+ }
620
+ .kitzo-react-tooltip-wrapper.right.start {
621
+ top: 0;
622
+ translate: 0 0;
623
+ }
624
+ .kitzo-react-tooltip-wrapper.right.end {
625
+ top: 100%;
626
+ translate: 0 -100%;
607
627
  }
608
628
 
629
+ /* Bottom */
630
+ .kitzo-react-tooltip-wrapper.bottom {
631
+ top: 100%;
632
+ padding-block-start: var(--tooltip-offset);
633
+ }
609
634
  .kitzo-react-tooltip-wrapper.bottom {
610
- --tooltip-offset: calc(var(--offset) * 1px);
611
-
612
- top: calc(var(--tooltip-offset) + 100%);
613
635
  left: 50%;
614
- transform: translateX(-50%) translateY(0) scale(0.8);
615
- opacity: 0;
616
- transform-origin: top;
636
+ translate: -50% 0;
637
+ }
638
+ .kitzo-react-tooltip-wrapper.bottom.start {
639
+ left: 0;
640
+ translate: 0 0;
641
+ }
642
+ .kitzo-react-tooltip-wrapper.bottom.end {
643
+ left: 100%;
644
+ translate: -100% 0;
617
645
  }
618
646
 
647
+ /* Left */
648
+ .kitzo-react-tooltip-wrapper.left {
649
+ right: 100%;
650
+ padding-inline-end: var(--tooltip-offset);
651
+ }
619
652
  .kitzo-react-tooltip-wrapper.left {
620
- --tooltip-offset: calc(var(--offset) * 1px);
621
-
622
- right: calc(var(--tooltip-offset) + 100%);
623
653
  top: 50%;
624
- transform: translateX(0) translateY(-50%) scale(0.8);
654
+ translate: 0 -50%;
655
+ }
656
+ .kitzo-react-tooltip-wrapper.left.start {
657
+ top: 0;
658
+ translate: 0 0;
659
+ }
660
+ .kitzo-react-tooltip-wrapper.left.end {
661
+ top: 100%;
662
+ translate: 0 -100%;
663
+ }
664
+
665
+ /* Tooltip transitions */
666
+ .kitzo-react-tooltip-root.animate-tooltip {
667
+ --transition-startDuration: calc(var(--startDuration) * 1ms);
668
+ --transition-endDuration: calc(var(--endDuration) * 1ms);
669
+ --transition-startDelay: calc(var(--startDelay) * 1ms);
670
+ --transition-endDelay: calc(var(--endDelay) * 1ms);
671
+
672
+ .kitzo-react-tooltip-content {
673
+ transition:
674
+ transform var(--transition-endDuration) var(--transition-endDelay),
675
+ opacity var(--transition-endDuration) var(--transition-endDelay);
676
+ }
677
+ }
678
+
679
+ .kitzo-react-tooltip-content {
680
+ transform: scale(0.8);
625
681
  opacity: 0;
682
+ }
683
+
684
+ .kitzo-react-tooltip-content.top {
685
+ transform-origin: bottom;
686
+ }
687
+ .kitzo-react-tooltip-content.right {
688
+ transform-origin: left;
689
+ }
690
+ .kitzo-react-tooltip-content.bottom {
691
+ transform-origin: top;
692
+ }
693
+ .kitzo-react-tooltip-content.left {
626
694
  transform-origin: right;
627
695
  }
628
696
 
629
- .kitzo-react-tooltip-root:hover {
630
- .kitzo-react-tooltip-wrapper.top {
631
- transform: translateX(-50%) translateY(0) scale(1);
632
- opacity: 1;
697
+ .kitzo-react-tooltip-root.animate-tooltip:hover {
698
+ .kitzo-react-tooltip-content {
699
+ transition:
700
+ transform var(--transition-startDuration) var(--transition-startDelay),
701
+ opacity var(--transition-startDuration) var(--transition-startDelay);
633
702
  }
634
- .kitzo-react-tooltip-wrapper.right {
635
- transform: translateX(0) translateY(-50%) scale(1);
703
+ }
704
+ .kitzo-react-tooltip-root:hover {
705
+ .kitzo-react-tooltip-content {
706
+ transform: scale(1);
636
707
  opacity: 1;
637
708
  }
638
- .kitzo-react-tooltip-wrapper.bottom {
639
- transform: translateX(-50%) translateY(0) scale(1);
640
- opacity: 1;
709
+ }
710
+
711
+ /* smart hover persistence feature */
712
+ .kitzo-react-tooltip-root {
713
+ .kitzo-react-tooltip-wrapper {
714
+ pointer-events: none;
641
715
  }
642
- .kitzo-react-tooltip-wrapper.left {
643
- transform: translateX(0) translateY(-50%) scale(1);
644
- opacity: 1;
716
+ }
717
+ .kitzo-react-tooltip-root.smart-hover:hover {
718
+ .kitzo-react-tooltip-wrapper {
719
+ pointer-events: all;
645
720
  }
646
721
  }
647
- `;
722
+
723
+ /* Arrow */
724
+ .kitzo-react-tooltip-content.tooltip-arrow {
725
+ --effective-size: calc(var(--arrow-size, 6) * 1px);
726
+ --effective-color: var(--arrow-color, var(--bg-clr));
727
+
728
+ position: relative;
729
+ }
730
+ .kitzo-react-tooltip-content.tooltip-arrow::before {
731
+ content: '';
732
+ position: absolute;
733
+ z-index: -1;
734
+ border: var(--effective-size) solid transparent;
735
+ }
736
+ .kitzo-react-tooltip-content.tooltip-arrow.top::before {
737
+ left: 50%;
738
+ translate: -50% 0;
739
+ top: calc(100% - 1px);
740
+ border-top: var(--effective-size) solid var(--effective-color);
741
+ }
742
+ .kitzo-react-tooltip-content.tooltip-arrow.right::before {
743
+ top: 50%;
744
+ translate: 0 -50%;
745
+ right: calc(100% - 1px);
746
+ border-right: var(--effective-size) solid var(--effective-color);
747
+ }
748
+ .kitzo-react-tooltip-content.tooltip-arrow.bottom::before {
749
+ left: 50%;
750
+ translate: -50% 0;
751
+ bottom: calc(100% - 1px);
752
+ border-bottom: var(--effective-size) solid var(--effective-color);
753
+ }
754
+ .kitzo-react-tooltip-content.tooltip-arrow.left::before {
755
+ top: 50%;
756
+ translate: 0 -50%;
757
+ left: calc(100% - 1px);
758
+ border-left: var(--effective-size) solid var(--effective-color);
759
+ }`;
648
760
  (() => {
649
761
  if (!document.getElementById('kitzo-react-tooltip-styles')) {
650
762
  const styleTag = document.createElement('style');
@@ -653,52 +765,91 @@ const tooltipStyles = `
653
765
  document.head.appendChild(styleTag);
654
766
  }
655
767
  })();
656
- const allowedPositions = ['top', 'right', 'bottom', 'left'];
657
- function getPositionBasedClassName(position) {
658
- let defaultClass = 'kitzo-react-tooltip-wrapper';
659
- if (allowedPositions.includes(position)) {
660
- return defaultClass + ' ' + position;
768
+ const allowedPositions = ['top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-start', 'bottom', 'bottom-end', 'left-start', 'left', 'left-end'];
769
+ function getPositionClass(position = '') {
770
+ const allowedPos = allowedPositions.find(p => p === position);
771
+ if (!allowedPos) return 'top';
772
+ if (allowedPos.includes('-')) {
773
+ const [dir, state] = allowedPos.split('-');
774
+ return `${dir} ${state}`;
661
775
  } else {
662
- return defaultClass + ' ' + 'top';
776
+ return allowedPos;
663
777
  }
664
778
  }
779
+ function getAnimationProperties(animation) {
780
+ const effectiveStartDelay = animation?.startDelay;
781
+ const effectiveEndDelay = animation?.endDelay;
782
+ const effectiveDelay = animation?.delay ?? 0;
783
+ const effectiveStartDuration = animation?.startDuration;
784
+ const effectiveEndDuration = animation?.endDuration;
785
+ const effectiveDuration = animation?.duration ?? 110;
786
+ return {
787
+ startDelay: effectiveStartDelay ?? effectiveDelay,
788
+ endDelay: effectiveEndDelay ?? effectiveDelay,
789
+ startDuration: effectiveStartDuration ?? effectiveDuration,
790
+ endDuration: effectiveEndDuration ?? effectiveDuration
791
+ };
792
+ }
665
793
  function Tooltip({
666
- content = 'Tooltip',
794
+ content,
667
795
  tooltipOptions = {},
796
+ animation = true,
797
+ style = {},
668
798
  children
669
799
  }) {
800
+ const isTouch = window.matchMedia('(pointer: coarse)').matches;
801
+ if (isTouch && finalOptions.hideOnTouch) return children;
802
+ if (typeof content === 'string') {
803
+ if (!content.trim()) throw new Error('kitzo/react: tooltip content is required');
804
+ } else if (!content) {
805
+ throw new Error('kitzo/react: tooltip content is required');
806
+ }
807
+ // Define options
670
808
  const {
671
809
  position = 'top',
672
810
  offset = 8,
673
811
  hideOnTouch = true,
674
- delay = 0
812
+ arrow = true,
813
+ smartHover = true
675
814
  } = tooltipOptions ?? {};
676
815
  const finalOptions = {
677
816
  position: typeof position === 'string' ? position.trim().toLowerCase() : 'top',
678
817
  offset: !isNaN(Number(offset)) ? Number(offset) : 8,
679
- delay: !isNaN(Number(delay)) ? Number(delay) : 0,
818
+ arrow: typeof arrow === 'boolean' ? arrow : true,
819
+ smartHover: typeof smartHover === 'boolean' ? smartHover : true,
680
820
  hideOnTouch: typeof hideOnTouch === 'boolean' ? hideOnTouch : true
681
821
  };
682
- const isTouch = window.matchMedia('(pointer: coarse)').matches;
683
- if (isTouch && finalOptions.hideOnTouch) return children;
822
+ const positionClass = getPositionClass(finalOptions.position);
823
+
824
+ // Define animations
825
+ const animationEnabled = animation ? true : false;
826
+ const animationObj = animation === true ? {} : animation;
827
+ const finalAnimationProperties = getAnimationProperties(animationObj);
684
828
  return /*#__PURE__*/React.createElement("div", {
685
829
  style: {
686
830
  position: 'relative',
687
831
  width: 'fit-content',
688
832
  '--offset': Math.max(0, finalOptions.offset),
689
- '--delay': Math.max(0, finalOptions.delay)
833
+ '--startDuration': Math.max(0, finalAnimationProperties.startDuration),
834
+ '--endDuration': Math.max(0, finalAnimationProperties.endDuration),
835
+ '--startDelay': Math.max(0, finalAnimationProperties.startDelay),
836
+ '--endDelay': Math.max(0, finalAnimationProperties.endDelay),
837
+ '--arrow-color': style?.['--arrow-color'],
838
+ '--arrow-size': style?.['--arrow-size']
690
839
  },
691
- className: "kitzo-react-tooltip-root"
840
+ className: `kitzo-react-tooltip-root ${finalOptions.smartHover ? 'smart-hover' : ''} ${animationEnabled ? 'animate-tooltip' : ''}`
692
841
  }, children, /*#__PURE__*/React.createElement("div", {
693
842
  style: {
694
843
  position: 'absolute',
695
- pointerEvents: 'none'
844
+ whiteSpace: typeof content === 'string' ? 'nowrap' : 'normal'
696
845
  },
697
846
  tabIndex: -1,
698
- className: getPositionBasedClassName(finalOptions.position)
847
+ className: `kitzo-react-tooltip-wrapper ${positionClass}`
848
+ }, /*#__PURE__*/React.createElement("div", {
849
+ className: `kitzo-react-tooltip-content ${positionClass} ${finalOptions.arrow ? 'tooltip-arrow' : ''}`
699
850
  }, typeof content === 'string' || typeof content === 'number' ? /*#__PURE__*/React.createElement("div", {
700
851
  className: "kitzo-react-tooltip-content-default-style"
701
- }, content) : /*#__PURE__*/React.createElement(React.Fragment, null, content)));
852
+ }, content) : /*#__PURE__*/React.createElement(React.Fragment, null, content))));
702
853
  }
703
854
 
704
855
  export { ToastContainer, Tooltip, toast };
@@ -2,29 +2,64 @@ import type { JSX, PropsWithChildren, ReactNode } from 'react';
2
2
 
3
3
  type TooltipOptions = {
4
4
  /**
5
- * Default position: 'top'
5
+ * @default 'top'
6
6
  */
7
- position?: 'top' | 'right' | 'bottom' | 'left';
7
+ position?:
8
+ | 'top-start'
9
+ | 'top'
10
+ | 'top-end'
11
+ | 'right-start'
12
+ | 'right'
13
+ | 'right-end'
14
+ | 'bottom-start'
15
+ | 'bottom'
16
+ | 'bottom-end'
17
+ | 'left-start'
18
+ | 'left'
19
+ | 'left-end';
8
20
  /**
9
- * Default offset: 8
21
+ * @default 8
10
22
  */
11
23
  offset?: number;
12
24
  /**
13
- * Default delay: 0
25
+ * Enables “Smart Hover Persistence”, allowing the tooltip to remain open
26
+ * while the cursor moves between the trigger and the tooltip.
27
+ *
28
+ * @default true
14
29
  */
15
- delay?: number;
30
+ smartHover?: boolean;
16
31
  /**
17
- * Default hideOnTouch: true
32
+ * @default true true
18
33
  */
19
34
  hideOnTouch?: boolean;
20
35
  };
21
36
 
37
+ type AnimationOptions = {
38
+ /**
39
+ * @default 110ms
40
+ */
41
+ duration?: number;
42
+ startDuration?: number;
43
+ endDuration?: number;
44
+ delay?: number;
45
+ startDelay?: number;
46
+ endDelay?: number;
47
+ };
48
+ type TooltipAnimation = boolean | AnimationOptions;
49
+
50
+ type TooltipStyles = {
51
+ '--arrow-size'?: number;
52
+ '--arrow-color'?: string;
53
+ };
54
+
22
55
  type TooltipCoreProps = {
23
56
  /**
24
57
  * content is necessary
25
58
  */
26
- content: ReactNode;
59
+ content: string | ReactNode;
27
60
  tooltipOptions?: TooltipOptions;
61
+ animation?: TooltipAnimation;
62
+ style?: TooltipStyles;
28
63
  };
29
64
 
30
65
  type TooltipProps = PropsWithChildren<TooltipCoreProps>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitzo",
3
- "version": "2.1.20",
3
+ "version": "2.1.21",
4
4
  "description": "A lightweight JavaScript & React UI micro-library.",
5
5
  "type": "module",
6
6
  "main": "./dist/vanilla/vanilla.umd.js",