@cloud-ru/ds-tooltip 1.0.0

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 (75) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +170 -0
  4. package/dist/cjs/components/QuestionTooltip/QuestionTooltip.d.ts +6 -0
  5. package/dist/cjs/components/QuestionTooltip/QuestionTooltip.js +26 -0
  6. package/dist/cjs/components/QuestionTooltip/index.d.ts +2 -0
  7. package/dist/cjs/components/QuestionTooltip/index.js +18 -0
  8. package/dist/cjs/components/QuestionTooltip/styles.module.css +39 -0
  9. package/dist/cjs/components/QuestionTooltip/types.d.ts +19 -0
  10. package/dist/cjs/components/QuestionTooltip/types.js +2 -0
  11. package/dist/cjs/components/Tooltip/Tooltip.d.ts +16 -0
  12. package/dist/cjs/components/Tooltip/Tooltip.js +20 -0
  13. package/dist/cjs/components/Tooltip/index.d.ts +1 -0
  14. package/dist/cjs/components/Tooltip/index.js +17 -0
  15. package/dist/cjs/components/Tooltip/styles.module.css +38 -0
  16. package/dist/cjs/components/WithTooltip/WithTooltip.d.ts +9 -0
  17. package/dist/cjs/components/WithTooltip/WithTooltip.js +11 -0
  18. package/dist/cjs/components/WithTooltip/index.d.ts +1 -0
  19. package/dist/cjs/components/WithTooltip/index.js +17 -0
  20. package/dist/cjs/components/index.d.ts +3 -0
  21. package/dist/cjs/components/index.js +19 -0
  22. package/dist/cjs/constants.d.ts +10 -0
  23. package/dist/cjs/constants.js +13 -0
  24. package/dist/cjs/index.d.ts +5 -0
  25. package/dist/cjs/index.js +23 -0
  26. package/dist/cjs/package.json +3 -0
  27. package/dist/cjs/style.css +77 -0
  28. package/dist/cjs/types.d.ts +5 -0
  29. package/dist/cjs/types.js +2 -0
  30. package/dist/cjs/utils.d.ts +2 -0
  31. package/dist/cjs/utils.js +15 -0
  32. package/dist/esm/components/QuestionTooltip/QuestionTooltip.d.ts +6 -0
  33. package/dist/esm/components/QuestionTooltip/QuestionTooltip.js +20 -0
  34. package/dist/esm/components/QuestionTooltip/index.d.ts +2 -0
  35. package/dist/esm/components/QuestionTooltip/index.js +2 -0
  36. package/dist/esm/components/QuestionTooltip/styles.module.css +39 -0
  37. package/dist/esm/components/QuestionTooltip/types.d.ts +19 -0
  38. package/dist/esm/components/QuestionTooltip/types.js +1 -0
  39. package/dist/esm/components/Tooltip/Tooltip.d.ts +16 -0
  40. package/dist/esm/components/Tooltip/Tooltip.js +13 -0
  41. package/dist/esm/components/Tooltip/index.d.ts +1 -0
  42. package/dist/esm/components/Tooltip/index.js +1 -0
  43. package/dist/esm/components/Tooltip/styles.module.css +38 -0
  44. package/dist/esm/components/WithTooltip/WithTooltip.d.ts +9 -0
  45. package/dist/esm/components/WithTooltip/WithTooltip.js +8 -0
  46. package/dist/esm/components/WithTooltip/index.d.ts +1 -0
  47. package/dist/esm/components/WithTooltip/index.js +1 -0
  48. package/dist/esm/components/index.d.ts +3 -0
  49. package/dist/esm/components/index.js +3 -0
  50. package/dist/esm/constants.d.ts +10 -0
  51. package/dist/esm/constants.js +10 -0
  52. package/dist/esm/index.d.ts +5 -0
  53. package/dist/esm/index.js +3 -0
  54. package/dist/esm/style.css +77 -0
  55. package/dist/esm/types.d.ts +5 -0
  56. package/dist/esm/types.js +1 -0
  57. package/dist/esm/utils.d.ts +2 -0
  58. package/dist/esm/utils.js +12 -0
  59. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  60. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  61. package/package.json +44 -0
  62. package/src/components/QuestionTooltip/QuestionTooltip.tsx +59 -0
  63. package/src/components/QuestionTooltip/index.ts +2 -0
  64. package/src/components/QuestionTooltip/styles.module.scss +51 -0
  65. package/src/components/QuestionTooltip/types.ts +20 -0
  66. package/src/components/Tooltip/Tooltip.tsx +69 -0
  67. package/src/components/Tooltip/index.ts +1 -0
  68. package/src/components/Tooltip/styles.module.scss +41 -0
  69. package/src/components/WithTooltip/WithTooltip.tsx +16 -0
  70. package/src/components/WithTooltip/index.ts +1 -0
  71. package/src/components/index.ts +3 -0
  72. package/src/constants.ts +11 -0
  73. package/src/index.ts +5 -0
  74. package/src/types.ts +7 -0
  75. package/src/utils.ts +14 -0
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@cloud-ru/ds-tooltip",
3
+ "version": "1.0.0",
4
+ "description": "Пакет подсказок — компонент Tooltip для произвольного триггера и QuestionTooltip с иконкой «?» для полей форм и сложной терминологии.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/cloud-ru-tech/snack-v2.git",
8
+ "directory": "packages/tooltip"
9
+ },
10
+ "homepage": "https://github.com/cloud-ru-tech/snack-v2/tree/master/packages/tooltip",
11
+ "license": "Apache-2.0",
12
+ "author": "Anna Kozlova <apkozlova@cloud.ru>",
13
+ "type": "module",
14
+ "main": "./dist/cjs/index.js",
15
+ "module": "./dist/esm/index.js",
16
+ "types": "./dist/esm/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "source": "./src/index.ts",
20
+ "types": "./dist/esm/index.d.ts",
21
+ "import": "./dist/esm/index.js",
22
+ "require": "./dist/cjs/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src",
28
+ "./CHANGELOG.md",
29
+ "./LICENSE"
30
+ ],
31
+ "sideEffects": [
32
+ "*.css",
33
+ "**/*.css"
34
+ ],
35
+ "dependencies": {
36
+ "classnames": "^2.5.1",
37
+ "@cloud-ru/ds-icons": "^1.0.0",
38
+ "@cloud-ru/ds-utils": "^1.0.0",
39
+ "@cloud-ru/ds-popover-private": "^1.0.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }
@@ -0,0 +1,59 @@
1
+ import { QuestionSVG } from '@cloud-ru/ds-icons/interface/system';
2
+ import { TRIGGER } from '@cloud-ru/ds-popover-private';
3
+ import { useUncontrolledProp } from '@cloud-ru/ds-utils';
4
+ import cn from 'classnames';
5
+ import { RefObject } from 'react';
6
+
7
+ import { SIZE, TEST_IDS } from '../../constants';
8
+ import { getIconSize } from '../../utils';
9
+ import { Tooltip } from '../Tooltip';
10
+ import styles from './styles.module.scss';
11
+ import { QuestionTooltipProps } from './types';
12
+
13
+ /**
14
+ * QuestionTooltip — иконка «?» с тултипом-popover для подсказок к полям форм.
15
+ * Поверхность одна на всех раскладках (по договорённости с дизайном mobile не свапается на BottomSheet).
16
+ */
17
+ export function QuestionTooltip({
18
+ tip,
19
+ triggerLabel = 'Подсказка',
20
+ trigger = TRIGGER.Hover,
21
+ className,
22
+ tooltipClassname,
23
+ open,
24
+ onOpenChange,
25
+ tabIndex = 0,
26
+ size = SIZE.XS,
27
+ triggerRef,
28
+ ...rest
29
+ }: QuestionTooltipProps) {
30
+ const [isOpen, setIsOpen] = useUncontrolledProp(open, false, onOpenChange);
31
+
32
+ return (
33
+ <Tooltip
34
+ tip={tip}
35
+ className={tooltipClassname}
36
+ triggerClassName={styles.questionTooltipTrigger}
37
+ open={isOpen}
38
+ onOpenChange={setIsOpen}
39
+ trigger={trigger}
40
+ {...rest}
41
+ >
42
+ <button
43
+ type='button'
44
+ aria-label={triggerLabel}
45
+ data-opened={isOpen}
46
+ data-trigger={trigger}
47
+ tabIndex={tabIndex}
48
+ className={cn(styles.button, className)}
49
+ data-size={size}
50
+ data-test-id={TEST_IDS.questionTooltip.triggerOpen}
51
+ // Ref ведёт на саму кнопку, а не на span-обёртку popover'а: потребителю
52
+ // нужен настоящий триггер (позиционирование, фокус).
53
+ ref={triggerRef as RefObject<HTMLButtonElement>}
54
+ >
55
+ <QuestionSVG size={getIconSize(size)} />
56
+ </button>
57
+ </Tooltip>
58
+ );
59
+ }
@@ -0,0 +1,2 @@
1
+ export * from './QuestionTooltip';
2
+ export * from './types';
@@ -0,0 +1,51 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+
3
+ $sizes: 'xs', 's';
4
+
5
+ .questionTooltipTrigger {
6
+ position: relative;
7
+ display: inline-flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ }
11
+
12
+ .button {
13
+ cursor: pointer;
14
+
15
+ position: relative;
16
+
17
+ display: inline-flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+
21
+ margin: 0;
22
+ padding: 0;
23
+ border: none;
24
+
25
+ font: inherit;
26
+ color: base.$sn-theme-color-available-version-textTertiary;
27
+
28
+ background: transparent;
29
+
30
+ &:focus-visible {
31
+ outline: base.$sn-primitive-strokeWeight-strokeSemiBold solid base.$sn-theme-color-available-complementary;
32
+ }
33
+
34
+ &:active {
35
+ color: base.$sn-theme-color-available-version-textMain;
36
+ }
37
+
38
+ @media (hover: hover) {
39
+ // `:not(:active)` — иначе hover перебивает pressed и мышью он недостижим.
40
+ &:hover:not(:active) {
41
+ color: base.$sn-theme-color-available-version-textSecondary;
42
+ }
43
+ }
44
+
45
+ @each $size in $sizes {
46
+ &[data-size='#{$size}'] {
47
+ width: base.simple-var(base.$sn-density, 'size', 'icon', $size);
48
+ height: base.simple-var(base.$sn-density, 'size', 'icon', $size);
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,20 @@
1
+ import { Size } from '../../types';
2
+ import { TooltipProps } from '../Tooltip';
3
+
4
+ /**
5
+ * QuestionTooltip: иконка «?» с тултипом-popover. Поверхность одна на всех раскладках.
6
+ * `tooltipClassname` применяется к контейнеру подсказки.
7
+ */
8
+ export type QuestionTooltipProps = TooltipProps & {
9
+ /** CSS-класс контейнера подсказки */
10
+ tooltipClassname?: string;
11
+ /** Доступное имя для иконки-триггера */
12
+ triggerLabel?: string;
13
+ /** Tab index для кнопки-триггера */
14
+ tabIndex?: number;
15
+ /**
16
+ * Размер
17
+ * @default xs
18
+ */
19
+ size?: Size;
20
+ };
@@ -0,0 +1,69 @@
1
+ import { PLACEMENT, PopoverPrivate, PopoverPrivateProps, TRIGGER } from '@cloud-ru/ds-popover-private';
2
+ import { WithSupportProps } from '@cloud-ru/ds-utils';
3
+ import cn from 'classnames';
4
+ import { ReactNode } from 'react';
5
+
6
+ import styles from './styles.module.scss';
7
+
8
+ export type TooltipProps = WithSupportProps<{
9
+ /** Содержимое тултипа (текст или разметка) */
10
+ tip: ReactNode;
11
+ /** Отключение ограничения ширины тултипа @default false */
12
+ disableMaxWidth?: boolean;
13
+ }> &
14
+ Pick<
15
+ PopoverPrivateProps,
16
+ | 'className'
17
+ | 'triggerClassName'
18
+ | 'offset'
19
+ | 'open'
20
+ | 'onOpenChange'
21
+ | 'hoverDelayOpen'
22
+ | 'hoverDelayClose'
23
+ | 'triggerRef'
24
+ | 'disableSpanWrapper'
25
+ | 'fallbackPlacements'
26
+ | 'closeOnPopstate'
27
+ > &
28
+ Partial<Pick<PopoverPrivateProps, 'trigger' | 'placement' | 'children'>>;
29
+
30
+ export const DEFAULT_FALLBACK_PLACEMENTS = [PLACEMENT.Top, PLACEMENT.Right, PLACEMENT.Bottom, PLACEMENT.Left];
31
+
32
+ /**
33
+ * Tooltip — всплывающая подсказка при наведении.
34
+ * Стили и анатомия (отступы, скругления, макс. ширина) из @cloud-ru/ds-figma-variables.
35
+ * DOM повторяет структуру из Figma: корневой контейнер и слои (фон, контейнер контента, указатель) с position: absolute.
36
+ */
37
+ export function Tooltip({
38
+ children,
39
+ tip,
40
+ placement = PLACEMENT.Top,
41
+ trigger = TRIGGER.HoverAndFocusVisible,
42
+ hoverDelayOpen = 0,
43
+ hoverDelayClose = 0,
44
+ triggerClassName,
45
+ disableMaxWidth = false,
46
+ ...otherProps
47
+ }: TooltipProps) {
48
+ return (
49
+ <PopoverPrivate
50
+ trigger={trigger}
51
+ placement={placement}
52
+ hasArrow
53
+ arrowContainerClassName={styles.pointerWrapper}
54
+ arrowElementClassName={styles.pointerShape}
55
+ hoverDelayOpen={hoverDelayOpen}
56
+ hoverDelayClose={hoverDelayClose}
57
+ fallbackPlacements={DEFAULT_FALLBACK_PLACEMENTS}
58
+ triggerClassName={cn(styles.triggerClassName, triggerClassName)}
59
+ popoverContent={
60
+ <div className={styles.tooltipContainer} data-disable-max-width={disableMaxWidth} role='tooltip'>
61
+ {tip}
62
+ </div>
63
+ }
64
+ {...otherProps}
65
+ >
66
+ {children}
67
+ </PopoverPrivate>
68
+ );
69
+ }
@@ -0,0 +1 @@
1
+ export * from './Tooltip';
@@ -0,0 +1,41 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/tooltip.module' as tooltip;
3
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/popover.module' as popover;
4
+
5
+ .triggerClassName {
6
+ --offset: #{base.simple-var(popover.$popover, 'anatomy', 'privatePopover', 'offset')};
7
+ }
8
+
9
+ .tooltipContainer {
10
+ @include base.composite-var(tooltip.$tooltip, 'anatomy', 'container');
11
+ @include base.composite-var(base.$base-styles, 'sn', 'regular', 'body', 's');
12
+
13
+ display: flex;
14
+ flex-direction: column;
15
+
16
+ box-sizing: border-box;
17
+
18
+ color: base.$sn-theme-color-available-inversion-textMain;
19
+ overflow-wrap: break-word;
20
+
21
+ background: base.$sn-theme-color-invertNeutral-background2Level;
22
+
23
+ &[data-disable-max-width='true'] {
24
+ max-width: calc(100vw - #{base.$sn-primitive-dimension-16});
25
+ }
26
+ }
27
+
28
+ // Отступ указателя от угла держит кламп floating-ui (`arrow({ padding })`), паддингом его дублировать нельзя.
29
+ .pointerWrapper {
30
+ position: absolute;
31
+
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+
36
+ color: base.$sn-theme-color-invertNeutral-background2Level;
37
+ }
38
+
39
+ .pointerShape {
40
+ fill: currentcolor;
41
+ }
@@ -0,0 +1,16 @@
1
+ import { Tooltip, TooltipProps } from '../Tooltip';
2
+
3
+ type WithTooltipProps = {
4
+ /** Настройки для тултипа */
5
+ tooltip?: Omit<TooltipProps, 'children'>;
6
+ /** Триггер поповера (подробнее в TooltipProps) */
7
+ children: TooltipProps['children'];
8
+ };
9
+
10
+ export function WithTooltip({ tooltip, children }: WithTooltipProps) {
11
+ if (!tooltip?.tip) {
12
+ return <>{children}</>;
13
+ }
14
+
15
+ return <Tooltip {...tooltip}>{children}</Tooltip>;
16
+ }
@@ -0,0 +1 @@
1
+ export * from './WithTooltip';
@@ -0,0 +1,3 @@
1
+ export * from './QuestionTooltip';
2
+ export * from './Tooltip';
3
+ export * from './WithTooltip';
@@ -0,0 +1,11 @@
1
+ export const SIZE = {
2
+ XS: 'xs',
3
+ S: 's',
4
+ } as const;
5
+
6
+ /** Стабильные `data-test-id` на внутренних слотах, которые компонент ставит сам. */
7
+ export const TEST_IDS = {
8
+ questionTooltip: {
9
+ triggerOpen: 'question-tooltip-trigger',
10
+ },
11
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { TRIGGER, PLACEMENT } from '@cloud-ru/ds-popover-private';
2
+ export * from './components';
3
+ export type { Placement } from './types';
4
+ export { SIZE } from './constants';
5
+ export type { Size } from './types';
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { PLACEMENT } from '@cloud-ru/ds-popover-private';
2
+ import { ValueOf } from '@cloud-ru/ds-utils';
3
+
4
+ import { SIZE } from './constants';
5
+
6
+ export type Placement = (typeof PLACEMENT)[keyof typeof PLACEMENT];
7
+ export type Size = ValueOf<typeof SIZE>;
package/src/utils.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { SIZE } from './constants';
2
+ import { Size } from './types';
3
+
4
+ export function getIconSize(size?: Size) {
5
+ switch (size) {
6
+ case SIZE.XS: {
7
+ return 16;
8
+ }
9
+ case SIZE.S:
10
+ default: {
11
+ return 24;
12
+ }
13
+ }
14
+ }