@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 +21 -0
- package/package.json +1 -1
- package/src/badge.tsx +6 -1
- package/src/indicator.tsx +87 -0
- package/src/toggle-button/toggle-button.tsx +3 -1
- package/src/tooltip.tsx +2 -2
- package/src/unit/index.tsx +1 -1
- package/src/unit/primitive.tsx +5 -4
- package/src/notification-dot.tsx +0 -23
- package/src/unit/unit-with-tooltip.tsx +0 -77
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
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,
|
package/src/unit/index.tsx
CHANGED
|
@@ -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}
|
package/src/unit/primitive.tsx
CHANGED
|
@@ -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
|
-
<
|
|
183
|
-
{
|
|
184
|
-
|
|
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
|
};
|
package/src/notification-dot.tsx
DELETED
|
@@ -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
|
-
};
|