@pitchfork-ui/react 0.2.0 → 0.7.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 (70) hide show
  1. package/dist/components/Accordion/Accordion.css +85 -0
  2. package/dist/components/Accordion/Accordion2.js +96 -0
  3. package/dist/components/Alert/Alert.css +29 -1
  4. package/dist/components/Alert/Alert2.js +4 -2
  5. package/dist/components/Avatar/Avatar.css +1 -1
  6. package/dist/components/BadgeGroup/BadgeGroup.css +4 -4
  7. package/dist/components/Button/Button.css +4 -4
  8. package/dist/components/ButtonGroup/ButtonGroup.css +2 -2
  9. package/dist/components/Carousel/Carousel.css +4 -2
  10. package/dist/components/Carousel/Carousel2.js +89 -76
  11. package/dist/components/CodeSnippet/CodeSnippet.css +2 -2
  12. package/dist/components/ContentDivider/ContentDivider.css +2 -2
  13. package/dist/components/DatePicker/DatePicker.css +1 -1
  14. package/dist/components/DatePicker/DatePicker2.js +3 -3
  15. package/dist/components/Dropdown/Dropdown.css +19 -2
  16. package/dist/components/Dropdown/Dropdown2.js +2 -3
  17. package/dist/components/GaugeChart/GaugeChart.css +18 -1
  18. package/dist/components/GaugeChart/GaugeChart2.js +5 -4
  19. package/dist/components/Heatmap/Heatmap.css +104 -0
  20. package/dist/components/Heatmap/Heatmap2.js +160 -0
  21. package/dist/components/InlineCTA/InlineCTA.css +58 -0
  22. package/dist/components/InlineCTA/InlineCTA2.js +14 -2
  23. package/dist/components/Modal/Modal.css +62 -0
  24. package/dist/components/Modal/Modal2.js +8 -6
  25. package/dist/components/MultiSelect/MultiSelect.css +19 -2
  26. package/dist/components/MultiSelect/MultiSelect2.js +3 -4
  27. package/dist/components/Notification/Notification.css +59 -4
  28. package/dist/components/Notification/Notification2.js +4 -2
  29. package/dist/components/Pagination/Pagination.css +5 -2
  30. package/dist/components/PieChart/PieChart.css +34 -0
  31. package/dist/components/PieChart/PieChart2.js +1 -1
  32. package/dist/components/ProgressIndicators/ProgressIndicators.css +20 -2
  33. package/dist/components/ProgressIndicators/ProgressIndicators2.js +4 -1
  34. package/dist/components/ProgressSteps/ProgressSteps.css +20 -3
  35. package/dist/components/RadarChart/RadarChart.css +22 -0
  36. package/dist/components/RadarChart/RadarChart2.js +19 -13
  37. package/dist/components/RichTextEditor/RichTextEditor.css +1 -1
  38. package/dist/components/Select/Select.css +21 -2
  39. package/dist/components/Select/Select2.js +3 -4
  40. package/dist/components/SidebarNavigation/SidebarNavigation.css +1 -1
  41. package/dist/components/SlideoutMenu/SlideoutMenu.css +2 -2
  42. package/dist/components/Sparkline/Sparkline.css +48 -0
  43. package/dist/components/Sparkline/Sparkline2.js +3 -2
  44. package/dist/components/Table/Table.css +4 -4
  45. package/dist/components/Tabs/Tabs.css +31 -5
  46. package/dist/components/Tabs/Tabs2.js +51 -4
  47. package/dist/components/Tag/Tag.css +1 -1
  48. package/dist/components/Tooltip/Tooltip.css +35 -0
  49. package/dist/components/Tooltip/Tooltip2.js +4 -4
  50. package/dist/components/TreeView/TreeView.css +2 -2
  51. package/dist/hooks/useExitAnimation.js +25 -0
  52. package/dist/hooks/usePresence.js +31 -0
  53. package/dist/index.cjs +834 -454
  54. package/dist/index.js +12 -8
  55. package/dist/src/components/Accordion/Accordion.d.ts +20 -0
  56. package/dist/src/components/Accordion/Accordion.test.d.ts +1 -0
  57. package/dist/src/components/Accordion/index.d.ts +1 -0
  58. package/dist/src/components/Heatmap/Heatmap.d.ts +28 -0
  59. package/dist/src/components/Heatmap/Heatmap.test.d.ts +1 -0
  60. package/dist/src/components/Heatmap/index.d.ts +1 -0
  61. package/dist/src/components/InlineCTA/InlineCTA.d.ts +2 -0
  62. package/dist/src/components/Sparkline/Sparkline.d.ts +2 -0
  63. package/dist/src/hooks/index.d.ts +2 -0
  64. package/dist/src/hooks/useExitAnimation.d.ts +18 -0
  65. package/dist/src/hooks/usePresence.d.ts +13 -0
  66. package/dist/src/index.d.ts +2 -0
  67. package/dist/styles/theme.css +47 -13
  68. package/dist/styles.css +767 -66
  69. package/package.json +1 -1
  70. package/theme.starter.css +4 -3
@@ -1,3 +1,4 @@
1
+ import { useExitAnimation } from "../../hooks/useExitAnimation.js";
1
2
  import { cx } from "../../utils/cx.js";
2
3
  import { Icon } from "../Icon/Icon2.js";
3
4
  import './Notification.css';/* empty css */
@@ -22,9 +23,10 @@ var Notification = forwardRef(({ className, variant = "info", heading, descripti
22
23
  "aria-hidden": true
23
24
  });
24
25
  const body = children ?? description;
26
+ const { isExiting, startExit } = useExitAnimation({ onExited: onDismiss });
25
27
  return /* @__PURE__ */ jsxs("div", {
26
28
  ref,
27
- className: cx("pf-notification", `pf-notification--${variant}`, className),
29
+ className: cx("pf-notification", `pf-notification--${variant}`, isExiting && "pf-notification--exiting", className),
28
30
  role: "status",
29
31
  ...props,
30
32
  children: [
@@ -54,7 +56,7 @@ var Notification = forwardRef(({ className, variant = "info", heading, descripti
54
56
  type: "button",
55
57
  className: "pf-notification__dismiss",
56
58
  "aria-label": "Dismiss notification",
57
- onClick: () => onDismiss?.(),
59
+ onClick: startExit,
58
60
  children: /* @__PURE__ */ jsx(Icon, {
59
61
  name: "circle-xmark",
60
62
  "aria-hidden": true
@@ -1,6 +1,7 @@
1
1
  .pf-pagination {
2
2
  align-items: center;
3
- display: inline-flex;
3
+ display: flex;
4
+ flex-wrap: wrap;
4
5
  gap: var(--space-2);
5
6
  }
6
7
 
@@ -16,7 +17,9 @@
16
17
 
17
18
  .pf-pagination__list {
18
19
  align-items: center;
19
- display: inline-flex;
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ justify-content: center;
20
23
  gap: var(--space-2);
21
24
  list-style: none;
22
25
  margin: 0;
@@ -21,6 +21,38 @@
21
21
  position: relative;
22
22
  }
23
23
 
24
+ /* The conic-gradient lives on a masked layer so it can sweep ("build out")
25
+ clockwise on mount without clipping the center label. */
26
+ @property --pf-pie-sweep {
27
+ syntax: '<percentage>';
28
+ inherits: false;
29
+ initial-value: 0%;
30
+ }
31
+
32
+ .pf-pie-chart__visual::before {
33
+ content: '';
34
+ position: absolute;
35
+ inset: 0;
36
+ border-radius: inherit;
37
+ background-image: var(--pf-pie-gradient);
38
+ --pf-pie-sweep: 100%;
39
+ -webkit-mask: conic-gradient(#000 var(--pf-pie-sweep), #0000 0);
40
+ mask: conic-gradient(#000 var(--pf-pie-sweep), #0000 0);
41
+ animation: pf-pie-sweep var(--duration-slow) var(--easing-decelerate);
42
+ }
43
+
44
+ @keyframes pf-pie-sweep {
45
+ from {
46
+ --pf-pie-sweep: 0%;
47
+ }
48
+ }
49
+
50
+ @media (prefers-reduced-motion: reduce) {
51
+ .pf-pie-chart__visual::before {
52
+ animation: none;
53
+ }
54
+ }
55
+
24
56
  .pf-pie-chart__visual--empty {
25
57
  outline: 1px solid var(--pf-piechart-border-muted);
26
58
  }
@@ -35,6 +67,8 @@
35
67
  font-weight: var(--font-weight-semibold);
36
68
  justify-content: center;
37
69
  line-height: 1.2;
70
+ position: relative;
71
+ z-index: 1;
38
72
  text-align: center;
39
73
  }
40
74
 
@@ -52,7 +52,7 @@ var PieChart = forwardRef(function PieChart({ className, data, size = 192, cutou
52
52
  style: {
53
53
  width: chartSize,
54
54
  height: chartSize,
55
- backgroundImage: conicGradient
55
+ "--pf-pie-gradient": conicGradient
56
56
  },
57
57
  role: "img",
58
58
  "aria-label": "Pie chart",
@@ -18,8 +18,16 @@
18
18
  background: var(--pf-progress-accent);
19
19
  border-radius: inherit;
20
20
  height: 100%;
21
- transition: width 200ms ease;
22
21
  width: var(--pf-progress-fill, 0%);
22
+ /* Animate fill in from 0 on mount, and smoothly between values on change */
23
+ transition: width var(--pf-transition-slow);
24
+ animation: pf-progress-bar-in var(--duration-slow) var(--easing-decelerate);
25
+ }
26
+
27
+ @keyframes pf-progress-bar-in {
28
+ from {
29
+ width: 0;
30
+ }
23
31
  }
24
32
 
25
33
  .pf-progress-bar__value {
@@ -56,7 +64,16 @@
56
64
  .pf-progress-circle__fill {
57
65
  stroke: var(--pf-progress-accent);
58
66
  stroke-linecap: round;
59
- transition: stroke-dashoffset 200ms ease;
67
+ stroke-dashoffset: var(--pf-progress-dashoffset);
68
+ /* Animate fill in from empty on mount, and smoothly between values on change */
69
+ transition: stroke-dashoffset var(--pf-transition-slow);
70
+ animation: pf-progress-circle-in var(--duration-slow) var(--easing-decelerate);
71
+ }
72
+
73
+ @keyframes pf-progress-circle-in {
74
+ from {
75
+ stroke-dashoffset: var(--pf-progress-circ);
76
+ }
60
77
  }
61
78
 
62
79
  .pf-progress-circle__value {
@@ -73,5 +90,6 @@
73
90
  .pf-progress-bar__fill,
74
91
  .pf-progress-circle__fill {
75
92
  transition: none;
93
+ animation: none;
76
94
  }
77
95
  }
@@ -63,7 +63,10 @@ var ProgressCircle = forwardRef(function ProgressCircle({ value, max = 100, size
63
63
  r: radius,
64
64
  strokeWidth,
65
65
  strokeDasharray: circumference,
66
- strokeDashoffset: dashOffset
66
+ style: {
67
+ "--pf-progress-dashoffset": dashOffset,
68
+ "--pf-progress-circ": circumference
69
+ }
67
70
  })]
68
71
  }), showValue ? /* @__PURE__ */ jsxs("span", {
69
72
  className: "pf-progress-circle__value",
@@ -62,13 +62,21 @@
62
62
  width: 28px;
63
63
  position: relative;
64
64
  z-index: 1;
65
+ /* Animate marker as its status changes when the current step advances */
66
+ transition:
67
+ background-color var(--pf-transition-medium),
68
+ border-color var(--pf-transition-medium),
69
+ color var(--pf-transition-medium),
70
+ box-shadow var(--pf-transition-medium);
65
71
  }
66
72
 
67
73
  .pf-progress-steps__connector {
68
74
  background: var(--pf-progress-steps-border);
69
75
  display: block;
70
76
  flex: 1;
71
- margin-left: var(--space-2);
77
+ margin-inline-start: var(--space-2);
78
+ /* Connector "activates" with a smooth fill as the step completes */
79
+ transition: background-color var(--pf-transition-slow);
72
80
  }
73
81
 
74
82
  .pf-progress-steps--horizontal .pf-progress-steps__connector {
@@ -80,7 +88,7 @@
80
88
  display: block;
81
89
  height: 1px;
82
90
  left: 50%;
83
- margin-left: 0;
91
+ margin-inline-start: 0;
84
92
  position: absolute;
85
93
  top: 50%;
86
94
  transform: translateY(-50%);
@@ -111,7 +119,7 @@
111
119
 
112
120
  .pf-progress-steps--vertical .pf-progress-steps__connector {
113
121
  bottom: calc(var(--space-3) * -1);
114
- left: 14px;
122
+ inset-inline-start: 14px;
115
123
  margin: 0;
116
124
  position: absolute;
117
125
  top: 28px;
@@ -127,6 +135,7 @@
127
135
  font-size: var(--font-size-sm);
128
136
  font-weight: var(--font-weight-semibold);
129
137
  margin: var(--space-1) 0 0;
138
+ transition: color var(--pf-transition-medium);
130
139
  }
131
140
 
132
141
  .pf-progress-steps__description {
@@ -156,3 +165,11 @@
156
165
  .pf-progress-steps__item--current .pf-progress-steps__title {
157
166
  color: var(--pf-progress-steps-current-title);
158
167
  }
168
+
169
+ @media (prefers-reduced-motion: reduce) {
170
+ .pf-progress-steps__marker,
171
+ .pf-progress-steps__connector,
172
+ .pf-progress-steps__title {
173
+ transition: none;
174
+ }
175
+ }
@@ -95,3 +95,25 @@
95
95
  padding: var(--space-4);
96
96
  text-align: center;
97
97
  }
98
+
99
+ /* Grow the value polygon out from the center on mount */
100
+ .pf-radar-chart__value {
101
+ animation: pf-radar-grow var(--duration-slow) var(--easing-decelerate);
102
+ }
103
+
104
+ @keyframes pf-radar-grow {
105
+ from {
106
+ opacity: 0;
107
+ transform: scale(0);
108
+ }
109
+ to {
110
+ opacity: 1;
111
+ transform: scale(1);
112
+ }
113
+ }
114
+
115
+ @media (prefers-reduced-motion: reduce) {
116
+ .pf-radar-chart__value {
117
+ animation: none;
118
+ }
119
+ }
@@ -69,21 +69,27 @@ var RadarChart = forwardRef(function RadarChart({ className, data, size = 280, m
69
69
  x2: axis.end.x,
70
70
  y2: axis.end.y
71
71
  }, `axis-${index}`)) : null,
72
- /* @__PURE__ */ jsx("polygon", {
73
- className: "pf-radar-chart__area",
74
- points: valuePolygon,
72
+ /* @__PURE__ */ jsxs("g", {
73
+ className: "pf-radar-chart__value",
75
74
  style: {
76
- fill: fillColor,
77
- stroke: strokeColor
78
- }
75
+ transformBox: "view-box",
76
+ transformOrigin: `${center}px ${center}px`
77
+ },
78
+ children: [/* @__PURE__ */ jsx("polygon", {
79
+ className: "pf-radar-chart__area",
80
+ points: valuePolygon,
81
+ style: {
82
+ fill: fillColor,
83
+ stroke: strokeColor
84
+ }
85
+ }), valuePoints.map((point, index) => /* @__PURE__ */ jsx("circle", {
86
+ className: "pf-radar-chart__point",
87
+ cx: point.x,
88
+ cy: point.y,
89
+ r: 3,
90
+ style: { fill: strokeColor }
91
+ }, `point-${index}`))]
79
92
  }),
80
- valuePoints.map((point, index) => /* @__PURE__ */ jsx("circle", {
81
- className: "pf-radar-chart__point",
82
- cx: point.x,
83
- cy: point.y,
84
- r: 3,
85
- style: { fill: strokeColor }
86
- }, `point-${index}`)),
87
93
  axes.map((axis, index) => {
88
94
  const labelPoint = polarToCartesian(center, center, outerRadius + 18, axis.angle);
89
95
  return /* @__PURE__ */ jsx("text", {
@@ -90,5 +90,5 @@
90
90
  color: var(--pf-rte-text-muted);
91
91
  font-size: var(--font-size-sm);
92
92
  margin: var(--space-2) 0 0;
93
- text-align: right;
93
+ text-align: end;
94
94
  }
@@ -15,7 +15,7 @@
15
15
  justify-content: space-between;
16
16
  min-height: 40px;
17
17
  padding: 0 var(--space-3);
18
- text-align: left;
18
+ text-align: start;
19
19
  width: 100%;
20
20
  transition:
21
21
  background 0.2s,
@@ -69,7 +69,7 @@
69
69
  justify-content: center;
70
70
  width: 20px;
71
71
  transform-origin: center;
72
- transition: transform 140ms ease;
72
+ transition: transform var(--pf-transition-fast);
73
73
  }
74
74
 
75
75
  .pf-select__icon svg {
@@ -94,6 +94,25 @@
94
94
  padding: var(--space-1);
95
95
  position: fixed;
96
96
  z-index: 1000;
97
+ transform-origin: top center;
98
+ animation: pf-select-menu-in var(--duration-fast) var(--easing-decelerate);
99
+ }
100
+
101
+ @keyframes pf-select-menu-in {
102
+ from {
103
+ opacity: 0;
104
+ transform: translateY(-4px) scaleY(0.96);
105
+ }
106
+ to {
107
+ opacity: 1;
108
+ transform: translateY(0) scaleY(1);
109
+ }
110
+ }
111
+
112
+ @media (prefers-reduced-motion: reduce) {
113
+ .pf-select__menu {
114
+ animation: none;
115
+ }
97
116
  }
98
117
 
99
118
  .pf-select__option {
@@ -1,13 +1,13 @@
1
1
  import { Keys, composeDescribedBy, isActivationKey } from "../../a11y/index.js";
2
- import { cx } from "../../utils/cx.js";
3
- import { Icon } from "../Icon/Icon2.js";
4
- import { FieldWrapper } from "../../utils/FieldWrapper.js";
5
2
  import { useAnchoredPosition } from "../../hooks/useAnchoredPosition.js";
6
3
  import { useComposedRefs } from "../../hooks/useComposedRefs.js";
7
4
  import { useControllableState } from "../../hooks/useControllableState.js";
8
5
  import { useDisclosure } from "../../hooks/useDisclosure.js";
9
6
  import { useListNavigation } from "../../hooks/useListNavigation.js";
10
7
  import { useOutsideInteraction } from "../../hooks/useOutsideInteraction.js";
8
+ import { cx } from "../../utils/cx.js";
9
+ import { Icon } from "../Icon/Icon2.js";
10
+ import { FieldWrapper } from "../../utils/FieldWrapper.js";
11
11
  import './Select.css';/* empty css */
12
12
  import { forwardRef, useEffect, useId, useMemo, useRef } from "react";
13
13
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -119,7 +119,6 @@ var Select = forwardRef(({ id, options, value, defaultValue, onValueChange, plac
119
119
  disabled,
120
120
  "aria-haspopup": "listbox",
121
121
  "aria-expanded": isOpen,
122
- "aria-controls": isOpen ? listboxId : void 0,
123
122
  "aria-required": required || void 0,
124
123
  "aria-describedby": describedBy,
125
124
  onClick: () => {
@@ -76,7 +76,7 @@
76
76
  grid-template-columns: auto 1fr auto;
77
77
  min-height: 36px;
78
78
  padding: 0 var(--space-2);
79
- text-align: left;
79
+ text-align: start;
80
80
  text-decoration: none;
81
81
  width: 100%;
82
82
  }
@@ -9,7 +9,7 @@
9
9
  opacity: 1;
10
10
  inset: 0;
11
11
  position: absolute;
12
- transition: opacity 220ms ease;
12
+ transition: opacity var(--pf-transition-slow);
13
13
  }
14
14
 
15
15
  .pf-slideout__viewport {
@@ -38,7 +38,7 @@
38
38
  height: 100%;
39
39
  max-width: 100%;
40
40
  pointer-events: auto;
41
- transition: transform 220ms ease;
41
+ transition: transform var(--pf-transition-slow);
42
42
  will-change: transform;
43
43
  }
44
44
 
@@ -15,3 +15,51 @@
15
15
  .pf-sparkline__dot {
16
16
  fill: var(--pf-sparkline-color-override, var(--pf-sparkline-color));
17
17
  }
18
+
19
+ /* Opt-in mount animation: draw the line, fade in the area and end dot.
20
+ The line path sets pathLength="1" so dash math is length-independent. */
21
+ .pf-sparkline--animated .pf-sparkline__line {
22
+ stroke-dasharray: 1;
23
+ animation: pf-sparkline-draw var(--duration-slow) var(--easing-standard) both;
24
+ }
25
+
26
+ @keyframes pf-sparkline-draw {
27
+ from {
28
+ stroke-dashoffset: 1;
29
+ }
30
+ to {
31
+ stroke-dashoffset: 0;
32
+ }
33
+ }
34
+
35
+ .pf-sparkline--animated .pf-sparkline__area {
36
+ animation: pf-sparkline-fade var(--duration-slow) var(--easing-standard) both;
37
+ }
38
+
39
+ @keyframes pf-sparkline-fade {
40
+ from {
41
+ opacity: 0;
42
+ }
43
+ }
44
+
45
+ .pf-sparkline--animated .pf-sparkline__dot {
46
+ animation: pf-sparkline-dot var(--duration-fast) var(--easing-decelerate) var(--duration-moderate)
47
+ both;
48
+ }
49
+
50
+ @keyframes pf-sparkline-dot {
51
+ from {
52
+ opacity: 0;
53
+ }
54
+ to {
55
+ opacity: 1;
56
+ }
57
+ }
58
+
59
+ @media (prefers-reduced-motion: reduce) {
60
+ .pf-sparkline--animated .pf-sparkline__line,
61
+ .pf-sparkline--animated .pf-sparkline__area,
62
+ .pf-sparkline--animated .pf-sparkline__dot {
63
+ animation: none;
64
+ }
65
+ }
@@ -27,7 +27,7 @@ function normalizePoints(data, width, height, padding) {
27
27
  return [padding + i / (data.length - 1) * innerW, padding + (1 - (value - min) / range) * innerH];
28
28
  });
29
29
  }
30
- var Sparkline = forwardRef(function Sparkline({ className, data = [], width = 120, height = 36, variant = "line", strokeWidth = 1.5, color, endDot = false, label, style, ...props }, ref) {
30
+ var Sparkline = forwardRef(function Sparkline({ className, data = [], width = 120, height = 36, variant = "line", strokeWidth = 1.5, color, endDot = false, animate = false, label, style, ...props }, ref) {
31
31
  const padding = strokeWidth + 2;
32
32
  const points = useMemo(() => normalizePoints(data, width, height, padding), [
33
33
  data,
@@ -61,7 +61,7 @@ var Sparkline = forwardRef(function Sparkline({ className, data = [], width = 12
61
61
  width,
62
62
  height,
63
63
  viewBox: `0 0 ${width} ${height}`,
64
- className: cx("pf-sparkline", `pf-sparkline--${variant}`, className),
64
+ className: cx("pf-sparkline", `pf-sparkline--${variant}`, animate && "pf-sparkline--animated", className),
65
65
  "aria-label": label,
66
66
  role: label ? "img" : "presentation",
67
67
  style: {
@@ -82,6 +82,7 @@ var Sparkline = forwardRef(function Sparkline({ className, data = [], width = 12
82
82
  strokeWidth,
83
83
  strokeLinecap: "round",
84
84
  strokeLinejoin: "round",
85
+ pathLength: animate ? 1 : void 0,
85
86
  className: "pf-sparkline__line"
86
87
  }),
87
88
  endDot && lastPoint && /* @__PURE__ */ jsx("circle", {
@@ -20,9 +20,9 @@
20
20
  font-size: var(--font-size-sm);
21
21
  min-height: 56px;
22
22
  padding-top: var(--space-3);
23
- padding-right: var(--space-4, 16px);
23
+ padding-inline-end: var(--space-4, 16px);
24
24
  padding-bottom: var(--space-3);
25
- padding-left: var(--space-4, 16px);
25
+ padding-inline-start: var(--space-4, 16px);
26
26
  }
27
27
 
28
28
  .pf-table__head-cell,
@@ -103,7 +103,7 @@
103
103
  }
104
104
 
105
105
  .pf-table__cell--left {
106
- text-align: left;
106
+ text-align: start;
107
107
  }
108
108
 
109
109
  .pf-table__cell--center {
@@ -111,7 +111,7 @@
111
111
  }
112
112
 
113
113
  .pf-table__cell--right {
114
- text-align: right;
114
+ text-align: end;
115
115
  }
116
116
 
117
117
  .pf-table__empty {
@@ -12,6 +12,31 @@
12
12
  .pf-tabs__list {
13
13
  display: flex;
14
14
  overflow-x: auto;
15
+ position: relative;
16
+ }
17
+
18
+ .pf-tabs__indicator {
19
+ position: absolute;
20
+ pointer-events: none;
21
+ transition:
22
+ left var(--pf-transition-medium),
23
+ width var(--pf-transition-medium),
24
+ top var(--pf-transition-medium),
25
+ height var(--pf-transition-medium);
26
+ }
27
+
28
+ .pf-tabs__indicator--underline {
29
+ bottom: 0;
30
+ height: var(--pf-tabs-underline-width);
31
+ background: var(--pf-tabs-text);
32
+ border-radius: var(--radius-full);
33
+ }
34
+
35
+ .pf-tabs__indicator--pills {
36
+ background: var(--pf-tabs-bg);
37
+ border-radius: var(--radius-sm);
38
+ box-shadow: var(--pf-tabs-elevation-tab-active-shadow, var(--pf-elevation-tab-active-shadow));
39
+ z-index: 0;
15
40
  }
16
41
 
17
42
  .pf-tabs__list--full-width {
@@ -40,7 +65,8 @@
40
65
  font-weight: var(--font-weight-medium);
41
66
  min-width: 0;
42
67
  position: relative;
43
- transition: color 160ms ease;
68
+ z-index: 1;
69
+ transition: color var(--pf-transition-medium);
44
70
  white-space: nowrap;
45
71
  }
46
72
 
@@ -75,7 +101,7 @@
75
101
  }
76
102
 
77
103
  .pf-tabs__tab--underline.pf-tabs__tab--active {
78
- border-bottom-color: var(--pf-tabs-text);
104
+ /* The sliding indicator draws the underline; keep the transparent border for spacing */
79
105
  color: var(--pf-tabs-text);
80
106
  }
81
107
 
@@ -89,8 +115,7 @@
89
115
  }
90
116
 
91
117
  .pf-tabs__tab--pills.pf-tabs__tab--active {
92
- background: var(--pf-tabs-bg);
93
- box-shadow: var(--pf-tabs-elevation-tab-active-shadow, var(--pf-elevation-tab-active-shadow));
118
+ /* The sliding indicator draws the pill background */
94
119
  color: var(--pf-tabs-text);
95
120
  }
96
121
 
@@ -106,7 +131,8 @@
106
131
  }
107
132
 
108
133
  @media (prefers-reduced-motion: reduce) {
109
- .pf-tabs__tab {
134
+ .pf-tabs__tab,
135
+ .pf-tabs__indicator {
110
136
  transition: none;
111
137
  }
112
138
  }
@@ -1,8 +1,9 @@
1
1
  import { cx } from "../../utils/cx.js";
2
2
  import './Tabs.css';/* empty css */
3
- import { forwardRef, useId, useMemo, useRef, useState } from "react";
3
+ import { forwardRef, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/Tabs/Tabs.tsx
6
+ var useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
6
7
  function getFirstEnabledValue(items) {
7
8
  return items.find((item) => !item.disabled)?.value;
8
9
  }
@@ -13,6 +14,39 @@ var Tabs = forwardRef(function Tabs({ className, items, value, defaultValue, onV
13
14
  const selectedValue = isControlled ? value : internalValue;
14
15
  const selectedItem = useMemo(() => items.find((item) => item.value === selectedValue && !item.disabled) ?? items.find((item) => !item.disabled), [items, selectedValue]);
15
16
  const buttonRefs = useRef([]);
17
+ const listRef = useRef(null);
18
+ const [indicator, setIndicator] = useState(null);
19
+ useIsomorphicLayoutEffect(() => {
20
+ const list = listRef.current;
21
+ const activeIndex = items.findIndex((item) => item.value === selectedItem?.value);
22
+ const activeButton = buttonRefs.current[activeIndex];
23
+ if (!list || !activeButton) {
24
+ setIndicator(null);
25
+ return;
26
+ }
27
+ const measure = () => {
28
+ const listRect = list.getBoundingClientRect();
29
+ const tabRect = activeButton.getBoundingClientRect();
30
+ setIndicator({
31
+ left: tabRect.left - listRect.left + list.scrollLeft,
32
+ width: tabRect.width,
33
+ top: tabRect.top - listRect.top,
34
+ height: tabRect.height
35
+ });
36
+ };
37
+ measure();
38
+ if (typeof ResizeObserver === "undefined") return;
39
+ const observer = new ResizeObserver(measure);
40
+ observer.observe(list);
41
+ observer.observe(activeButton);
42
+ return () => observer.disconnect();
43
+ }, [
44
+ items,
45
+ selectedItem?.value,
46
+ variant,
47
+ size,
48
+ fullWidth
49
+ ]);
16
50
  const setSelectedValue = (nextValue) => {
17
51
  if (!isControlled) setInternalValue(nextValue);
18
52
  onValueChange?.(nextValue);
@@ -37,11 +71,24 @@ var Tabs = forwardRef(function Tabs({ className, items, value, defaultValue, onV
37
71
  ref,
38
72
  className: cx("pf-tabs", className),
39
73
  ...props,
40
- children: [/* @__PURE__ */ jsx("div", {
74
+ children: [/* @__PURE__ */ jsxs("div", {
75
+ ref: listRef,
41
76
  className: cx("pf-tabs__list", `pf-tabs__list--${variant}`, `pf-tabs__list--${size}`, fullWidth && "pf-tabs__list--full-width"),
42
77
  role: "tablist",
43
78
  "aria-orientation": "horizontal",
44
- children: items.map((item, index) => {
79
+ children: [indicator ? /* @__PURE__ */ jsx("span", {
80
+ "aria-hidden": true,
81
+ className: cx("pf-tabs__indicator", `pf-tabs__indicator--${variant}`),
82
+ style: variant === "pills" ? {
83
+ left: indicator.left,
84
+ width: indicator.width,
85
+ top: indicator.top,
86
+ height: indicator.height
87
+ } : {
88
+ left: indicator.left,
89
+ width: indicator.width
90
+ }
91
+ }) : null, items.map((item, index) => {
45
92
  const isSelected = item.value === selectedItem?.value;
46
93
  const tabId = `${baseId}-tab-${item.value}`;
47
94
  const panelId = `${baseId}-panel-${item.value}`;
@@ -80,7 +127,7 @@ var Tabs = forwardRef(function Tabs({ className, items, value, defaultValue, onV
80
127
  },
81
128
  children: item.label
82
129
  }, item.value);
83
- })
130
+ })]
84
131
  }), selectedItem ? /* @__PURE__ */ jsx("div", {
85
132
  id: `${baseId}-panel-${selectedItem.value}`,
86
133
  className: "pf-tabs__panel",
@@ -43,7 +43,7 @@
43
43
  display: inline-flex;
44
44
  height: 18px;
45
45
  justify-content: center;
46
- margin-right: -2px;
46
+ margin-inline-end: -2px;
47
47
  padding: 0;
48
48
  width: 18px;
49
49
  }