@fibery/ui-kit 4.3.0 → 6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @fibery/ui-kit
2
2
 
3
+ ## 6.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 5d8dd40: Remove UnitWithTooltip — use Tooltip with title/description props and a plain wrapper element instead. Its hand-rolled hover tracking worked around a Safari mouseleave-on-scroll bug that no longer reproduces; unit tooltips now also open on keyboard focus. Tooltip's default content no longer renders empty title/description nodes when only one of the two is provided.
8
+
9
+ ### Minor Changes
10
+
11
+ - b7f6df1: updated badge component added accent state
12
+
13
+ ## 5.0.0
14
+
15
+ ### Major Changes
16
+
17
+ - de29bed: Remove NotificationDot — use Indicator instead: place it inside the host (or add the indicatorHost class to establish positioning) and pass `position`/`offset` instead of relying on hardcoded negative margins
18
+
19
+ ### Minor Changes
20
+
21
+ - de29bed: Add Indicator component — a corner-anchored status dot with configurable color, ring, position and screen-reader label (extracted from the pinned filters overridden dot)
22
+ - de29bed: ToggleButton border radius now scales with size: 8px for small (unchanged), 10px for medium and large
23
+
3
24
  ## 4.3.0
4
25
 
5
26
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "4.3.0",
3
+ "version": "6.0.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "dependencies": {
package/src/badge.tsx CHANGED
@@ -6,7 +6,7 @@ import {space, textClasses, themeVars} from "./design-system";
6
6
 
7
7
  type Props = {
8
8
  children: ReactNode;
9
- kind?: "success" | "error" | "default" | "warning";
9
+ kind?: "success" | "error" | "default" | "warning" | "accent";
10
10
  };
11
11
 
12
12
  export const Badge = forwardRef<HTMLSpanElement, Props>(({children, kind, ...otherProps}, ref) => {
@@ -39,6 +39,11 @@ export const Badge = forwardRef<HTMLSpanElement, Props>(({children, kind, ...oth
39
39
  css`
40
40
  color: ${themeVars.colorTextBadgeWarning};
41
41
  background: ${themeVars.colorBgBadgeWarning};
42
+ `,
43
+ kind === "accent" &&
44
+ css`
45
+ color: ${themeVars.colorTextBadgeAccent};
46
+ background: ${themeVars.colorBgBadgeAccent};
42
47
  `
43
48
  )}
44
49
  >
@@ -0,0 +1,87 @@
1
+ import type {CSSProperties} from "@linaria/core";
2
+ import {css, cx} from "@linaria/core";
3
+
4
+ import {border, space, themeVars} from "./design-system";
5
+ import {visuallyHidden} from "./styles-utils";
6
+
7
+ /** Establishes the positioning context Indicator anchors to; apply to the host unless it is already positioned */
8
+ export const indicatorHost = css`
9
+ position: relative;
10
+ `;
11
+
12
+ const indicatorColorVar = "--fibery-indicator-color";
13
+ const indicatorRingColorVar = "--fibery-indicator-ring-color";
14
+ const indicatorSizeVar = "--fibery-indicator-size";
15
+ const indicatorOffsetVar = "--fibery-indicator-offset";
16
+
17
+ const indicatorCss = css`
18
+ position: absolute;
19
+ width: var(${indicatorSizeVar});
20
+ height: var(${indicatorSizeVar});
21
+ border-radius: ${border.radiusFull};
22
+ background-color: var(${indicatorColorVar});
23
+ /* box-shadow instead of border so the ring doesn't take part in sizing */
24
+ box-shadow: 0 0 0 ${space.s2}px var(${indicatorRingColorVar}, transparent);
25
+ pointer-events: none;
26
+
27
+ &[data-position^="top"] {
28
+ top: var(${indicatorOffsetVar});
29
+ }
30
+
31
+ &[data-position^="bottom"] {
32
+ bottom: var(${indicatorOffsetVar});
33
+ }
34
+
35
+ &[data-position$="start"] {
36
+ left: var(${indicatorOffsetVar});
37
+ }
38
+
39
+ &[data-position$="end"] {
40
+ right: var(${indicatorOffsetVar});
41
+ }
42
+ `;
43
+
44
+ export type IndicatorProps = {
45
+ color?: string;
46
+ /** Cuts the dot out of the host with a 2px ring; must match the surface behind the host */
47
+ ringColor?: string;
48
+ /** Dot diameter in px; ringColor adds 2px around it */
49
+ size?: number;
50
+ /** Corner of the host to anchor to; the host must be positioned (apply indicatorHost if it is not) */
51
+ position?: "top-start" | "top-end" | "bottom-start" | "bottom-end";
52
+ /** Distance in px from the anchored edges; negative values push the dot outside the host */
53
+ offset?: number;
54
+ /** Announced by screen readers; without it the dot is hidden from assistive technology */
55
+ label?: string;
56
+ } & Omit<React.ComponentProps<"div">, "children">;
57
+
58
+ export const Indicator = ({
59
+ color = themeVars.active,
60
+ ringColor,
61
+ size = space.s6,
62
+ position = "top-end",
63
+ offset = 0,
64
+ label,
65
+ className,
66
+ style,
67
+ ...props
68
+ }: IndicatorProps) => {
69
+ const vars: CSSProperties = {
70
+ [indicatorColorVar]: color,
71
+ [indicatorSizeVar]: `${size}px`,
72
+ [indicatorOffsetVar]: `${offset}px`,
73
+ ...(ringColor && {[indicatorRingColorVar]: ringColor}),
74
+ };
75
+
76
+ return (
77
+ <div
78
+ className={cx(className, indicatorCss)}
79
+ style={{...vars, ...style}}
80
+ data-position={position}
81
+ aria-hidden={label ? undefined : true}
82
+ {...props}
83
+ >
84
+ {label ? <span className={visuallyHidden}>{label}</span> : null}
85
+ </div>
86
+ );
87
+ };
@@ -17,7 +17,6 @@ export const toggleButtonCss = css`
17
17
  box-sizing: border-box;
18
18
  display: flex;
19
19
  align-items: center;
20
- border-radius: ${border.radius8}px;
21
20
 
22
21
  transition-property: border, background-color, color, opacity, box-shadow;
23
22
  transition-duration: ${transitionDuration.fast};
@@ -45,6 +44,7 @@ export const toggleButtonCss = css`
45
44
  font-size: ${fontSize.xs}px;
46
45
  line-height: 16px;
47
46
  min-height: 24px;
47
+ border-radius: ${border.radius8}px;
48
48
 
49
49
  ${iconSizeVar}: 16px;
50
50
  }
@@ -53,6 +53,7 @@ export const toggleButtonCss = css`
53
53
  font-size: ${fontSize.sm}px;
54
54
  line-height: 20px;
55
55
  min-height: 28px;
56
+ border-radius: ${border.radius10}px;
56
57
 
57
58
  ${iconSizeVar}: 18px;
58
59
  }
@@ -62,6 +63,7 @@ export const toggleButtonCss = css`
62
63
  line-height: 20px;
63
64
  min-height: 32px;
64
65
  padding-inline: 7px;
66
+ border-radius: ${border.radius10}px;
65
67
 
66
68
  ${iconSizeVar}: 18px;
67
69
  }
package/src/tooltip.tsx CHANGED
@@ -158,8 +158,8 @@ export const Tooltip = forwardRef<HTMLButtonElement, TooltipProps>(
158
158
  description,
159
159
  content = (title || description) && (
160
160
  <>
161
- <TooltipTitle>{title}</TooltipTitle>
162
- <TooltipDescription>{description}</TooltipDescription>
161
+ {title && <TooltipTitle>{title}</TooltipTitle>}
162
+ {description && <TooltipDescription>{description}</TooltipDescription>}
163
163
  </>
164
164
  ),
165
165
  side,
@@ -27,7 +27,7 @@ export const BasicUnit = ({
27
27
  );
28
28
  }
29
29
 
30
- const bgColor = props.bgColor || props.readOnly ? themeVars.inputDisabledBgColor : themeVars.transparent;
30
+ const bgColor = props.bgColor || (props.readOnly ? themeVars.inputDisabledBgColor : themeVars.transparent);
31
31
  return (
32
32
  <UnitPrimitive
33
33
  bgColor={bgColor}
@@ -5,6 +5,7 @@ import type {ComponentProps} from "react";
5
5
  import {inputOverrides} from "../antd/styles";
6
6
  import {border, space, themeVars} from "../design-system";
7
7
  import {FieldIcon} from "../field-icon";
8
+ import {Tooltip} from "../tooltip";
8
9
  import {useTruncated} from "../tooltip-if-overflown";
9
10
  import {
10
11
  basicUnitClassName,
@@ -25,7 +26,6 @@ import {
25
26
  import type {UnitPrimitiveProps} from "./types";
26
27
  import {UnitButtonGroup} from "./unit-button-group";
27
28
  import {UnitContent} from "./unit-content";
28
- import {UnitWithTooltip} from "./unit-with-tooltip";
29
29
 
30
30
  const resetButtonStylesClassName = css`
31
31
  all: unset;
@@ -179,9 +179,10 @@ export const UnitPrimitive = ({
179
179
 
180
180
  return (
181
181
  <div className={positionUnitClass}>
182
- <UnitWithTooltip title={tooltipTitle} description={tooltipDescription}>
183
- {contentWithHeader}
184
- </UnitWithTooltip>
182
+ <Tooltip side="bottom" align="start" title={tooltipTitle} description={tooltipDescription}>
183
+ {/* The trigger needs a host element: the unit content root does not forward refs/handlers */}
184
+ <div>{contentWithHeader}</div>
185
+ </Tooltip>
185
186
  </div>
186
187
  );
187
188
  };
@@ -1,23 +0,0 @@
1
- import type {CSSProperties} from "react";
2
-
3
- import {themeVars} from "./design-system";
4
- import {Dot} from "./dot";
5
-
6
- type Props = {
7
- big?: boolean;
8
- color?: string;
9
- } & React.ComponentProps<"div">;
10
-
11
- export const NotificationDot = ({big = true, color = themeVars.active, style}: Props) => {
12
- const marginTop = big ? -6 : -1;
13
- const marginRight = big ? -1 : 0;
14
- const styleObject = {
15
- position: "absolute",
16
- top: 0,
17
- marginTop,
18
- marginRight,
19
- ...style,
20
- } as CSSProperties;
21
-
22
- return <Dot color={color} size={big ? "medium" : "small"} style={styleObject} />;
23
- };
@@ -1,77 +0,0 @@
1
- import {css} from "@linaria/core";
2
- import {useRef, useState} from "react";
3
-
4
- import {space, tooltipDelay} from "../../src/design-system";
5
- import {Tooltip, TooltipDescription, TooltipTitle} from "../tooltip";
6
-
7
- const contentCss = css`
8
- display: flex;
9
- flex-direction: column;
10
- gap: ${space.s4}px;
11
- `;
12
-
13
- type Props = {
14
- children: React.ReactElement;
15
- title: React.ReactNode;
16
- description: React.ReactNode;
17
- hidden?: boolean;
18
- };
19
-
20
- export const UnitWithTooltip = ({children, title, description, hidden}: Props) => {
21
- const [shouldDisplayTooltip, setShouldDisplayTooltip] = useState(false);
22
- const unitElementRef = useRef<HTMLDivElement>(null);
23
- const isHoveredRef = useRef(false);
24
- const checkHoveredStateTimeoutId = useRef<ReturnType<typeof setTimeout> | null>();
25
-
26
- const onMouseEnter = () => {
27
- isHoveredRef.current = true;
28
-
29
- checkHoveredStateTimeoutId.current = setTimeout(() => {
30
- checkHoveredStateTimeoutId.current = null;
31
- // onMouseLeave works unreliably in Safari, IE on scroll
32
- // Sometimes it was not fired leaving tooltip displayed
33
- // Had to use additional way to validate if we are still over unit
34
- if (isHoveredRef.current && unitElementRef.current && unitElementRef.current.matches(":hover")) {
35
- setShouldDisplayTooltip(true);
36
- const intervalId = window.setInterval(() => {
37
- if (isHoveredRef.current && unitElementRef.current) {
38
- if (!unitElementRef.current.matches(":hover")) {
39
- isHoveredRef.current = false;
40
- setShouldDisplayTooltip(false);
41
- clearInterval(intervalId);
42
- }
43
- } else {
44
- clearInterval(intervalId);
45
- }
46
- }, 250);
47
- }
48
- }, tooltipDelay.enter);
49
- };
50
-
51
- const onMouseLeave = () => {
52
- isHoveredRef.current = false;
53
- if (checkHoveredStateTimeoutId.current) {
54
- clearTimeout(checkHoveredStateTimeoutId.current);
55
- checkHoveredStateTimeoutId.current = null;
56
- }
57
- setShouldDisplayTooltip(false);
58
- };
59
-
60
- return (
61
- <Tooltip
62
- visible={!hidden && shouldDisplayTooltip}
63
- content={
64
- <div className={contentCss}>
65
- <TooltipTitle>{title}</TooltipTitle>
66
- {description && <TooltipDescription>{description}</TooltipDescription>}
67
- </div>
68
- }
69
- side="bottom"
70
- align="start"
71
- >
72
- <div ref={unitElementRef} onMouseDown={onMouseLeave} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
73
- {children}
74
- </div>
75
- </Tooltip>
76
- );
77
- };