@pitchfork-ui/react 0.2.0 → 0.6.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 (69) 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 +100 -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/PieChart/PieChart.css +34 -0
  30. package/dist/components/PieChart/PieChart2.js +1 -1
  31. package/dist/components/ProgressIndicators/ProgressIndicators.css +20 -2
  32. package/dist/components/ProgressIndicators/ProgressIndicators2.js +4 -1
  33. package/dist/components/ProgressSteps/ProgressSteps.css +20 -3
  34. package/dist/components/RadarChart/RadarChart.css +22 -0
  35. package/dist/components/RadarChart/RadarChart2.js +19 -13
  36. package/dist/components/RichTextEditor/RichTextEditor.css +1 -1
  37. package/dist/components/Select/Select.css +21 -2
  38. package/dist/components/Select/Select2.js +3 -4
  39. package/dist/components/SidebarNavigation/SidebarNavigation.css +1 -1
  40. package/dist/components/SlideoutMenu/SlideoutMenu.css +2 -2
  41. package/dist/components/Sparkline/Sparkline.css +48 -0
  42. package/dist/components/Sparkline/Sparkline2.js +3 -2
  43. package/dist/components/Table/Table.css +4 -4
  44. package/dist/components/Tabs/Tabs.css +31 -5
  45. package/dist/components/Tabs/Tabs2.js +51 -4
  46. package/dist/components/Tag/Tag.css +1 -1
  47. package/dist/components/Tooltip/Tooltip.css +35 -0
  48. package/dist/components/Tooltip/Tooltip2.js +4 -4
  49. package/dist/components/TreeView/TreeView.css +2 -2
  50. package/dist/hooks/useExitAnimation.js +25 -0
  51. package/dist/hooks/usePresence.js +31 -0
  52. package/dist/index.cjs +834 -454
  53. package/dist/index.js +12 -8
  54. package/dist/src/components/Accordion/Accordion.d.ts +20 -0
  55. package/dist/src/components/Accordion/Accordion.test.d.ts +1 -0
  56. package/dist/src/components/Accordion/index.d.ts +1 -0
  57. package/dist/src/components/Heatmap/Heatmap.d.ts +28 -0
  58. package/dist/src/components/Heatmap/Heatmap.test.d.ts +1 -0
  59. package/dist/src/components/Heatmap/index.d.ts +1 -0
  60. package/dist/src/components/InlineCTA/InlineCTA.d.ts +2 -0
  61. package/dist/src/components/Sparkline/Sparkline.d.ts +2 -0
  62. package/dist/src/hooks/index.d.ts +2 -0
  63. package/dist/src/hooks/useExitAnimation.d.ts +18 -0
  64. package/dist/src/hooks/usePresence.d.ts +13 -0
  65. package/dist/src/index.d.ts +2 -0
  66. package/dist/styles/theme.css +47 -13
  67. package/dist/styles.css +758 -64
  68. package/package.json +1 -1
  69. package/theme.starter.css +4 -3
@@ -7,8 +7,7 @@ var GaugeChart = forwardRef(function GaugeChart({ className, value, max = 100, c
7
7
  const progress = max > 0 ? Math.min(Math.max(value, 0), max) / max : 0;
8
8
  const radius = (size - strokeWidth) / 2;
9
9
  const circumference = 2 * Math.PI * radius;
10
- const filled = progress * circumference;
11
- const gap = circumference - filled;
10
+ const offset = circumference - progress * circumference;
12
11
  const center = size / 2;
13
12
  const pct = Math.round(progress * 100);
14
13
  const defaultLabel = `${pct}%`;
@@ -48,10 +47,12 @@ var GaugeChart = forwardRef(function GaugeChart({ className, value, max = 100, c
48
47
  stroke: colorVar,
49
48
  strokeWidth,
50
49
  strokeLinecap: "round",
51
- strokeDasharray: `${filled} ${gap}`,
50
+ strokeDasharray: circumference,
52
51
  style: {
53
52
  transform: "rotate(-90deg)",
54
- transformOrigin: `${center}px ${center}px`
53
+ transformOrigin: `${center}px ${center}px`,
54
+ "--pf-gauge-circumference": circumference,
55
+ "--pf-gauge-offset": offset
55
56
  },
56
57
  className: "pf-gauge__fill"
57
58
  })]
@@ -0,0 +1,100 @@
1
+ .pf-heatmap {
2
+ display: inline-block;
3
+ font-family: var(--font-family-sans);
4
+ }
5
+
6
+ .pf-heatmap__empty {
7
+ color: var(--color-semantic-text-muted);
8
+ font-size: var(--font-size-sm);
9
+ padding: var(--space-4);
10
+ text-align: center;
11
+ }
12
+
13
+ .pf-heatmap__body {
14
+ display: flex;
15
+ gap: var(--pf-heatmap-cell-gap);
16
+ }
17
+
18
+ .pf-heatmap__weekdays {
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+
23
+ .pf-heatmap__weekday-spacer {
24
+ display: block;
25
+ height: calc(var(--font-size-xs) + var(--space-1));
26
+ }
27
+
28
+ .pf-heatmap__weekday-grid {
29
+ display: grid;
30
+ grid-template-rows: repeat(7, var(--pf-heatmap-cell-size));
31
+ gap: var(--pf-heatmap-cell-gap);
32
+ }
33
+
34
+ .pf-heatmap__weekday {
35
+ align-items: center;
36
+ color: var(--pf-heatmap-label-color);
37
+ display: flex;
38
+ font-size: var(--font-size-xs);
39
+ line-height: 1;
40
+ padding-inline-end: var(--space-1);
41
+ }
42
+
43
+ .pf-heatmap__main {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: var(--space-1);
47
+ }
48
+
49
+ .pf-heatmap__months {
50
+ display: grid;
51
+ gap: var(--pf-heatmap-cell-gap);
52
+ height: var(--font-size-xs);
53
+ }
54
+
55
+ .pf-heatmap__month {
56
+ color: var(--pf-heatmap-label-color);
57
+ font-size: var(--font-size-xs);
58
+ line-height: 1;
59
+ }
60
+
61
+ .pf-heatmap__grid {
62
+ display: grid;
63
+ grid-template-rows: repeat(7, var(--pf-heatmap-cell-size));
64
+ grid-auto-flow: column;
65
+ grid-auto-columns: var(--pf-heatmap-cell-size);
66
+ gap: var(--pf-heatmap-cell-gap);
67
+ }
68
+
69
+ .pf-heatmap__cell {
70
+ border-radius: var(--pf-heatmap-cell-radius);
71
+ height: var(--pf-heatmap-cell-size);
72
+ width: var(--pf-heatmap-cell-size);
73
+ }
74
+
75
+ .pf-heatmap__cell--empty {
76
+ background: transparent;
77
+ }
78
+
79
+ /* Staggered fade-in on mount — per-column animation-delay is set inline so
80
+ the grid wipes in left-to-right (oldest week → newest). */
81
+ .pf-heatmap__cell {
82
+ animation: pf-heatmap-cell-in var(--duration-moderate) var(--easing-decelerate) both;
83
+ }
84
+
85
+ @keyframes pf-heatmap-cell-in {
86
+ from {
87
+ opacity: 0;
88
+ transform: scale(0.6);
89
+ }
90
+ to {
91
+ opacity: 1;
92
+ transform: scale(1);
93
+ }
94
+ }
95
+
96
+ @media (prefers-reduced-motion: reduce) {
97
+ .pf-heatmap__cell {
98
+ animation: none;
99
+ }
100
+ }
@@ -0,0 +1,160 @@
1
+ import { cx } from "../../utils/cx.js";
2
+ import './Heatmap.css';/* empty css */
3
+ import { forwardRef } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/Heatmap/Heatmap.tsx
6
+ var MONTHS = [
7
+ "Jan",
8
+ "Feb",
9
+ "Mar",
10
+ "Apr",
11
+ "May",
12
+ "Jun",
13
+ "Jul",
14
+ "Aug",
15
+ "Sep",
16
+ "Oct",
17
+ "Nov",
18
+ "Dec"
19
+ ];
20
+ var WEEKDAYS = [
21
+ "Sun",
22
+ "Mon",
23
+ "Tue",
24
+ "Wed",
25
+ "Thu",
26
+ "Fri",
27
+ "Sat"
28
+ ];
29
+ function parseISO(s) {
30
+ const [y, m, d] = s.split("-").map(Number);
31
+ return new Date(y, m - 1, d);
32
+ }
33
+ function toISO(d) {
34
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
35
+ }
36
+ function addDays(d, n) {
37
+ const r = new Date(d);
38
+ r.setDate(r.getDate() + n);
39
+ return r;
40
+ }
41
+ var Heatmap = forwardRef(function Heatmap({ className, data, startDate, endDate, levels = 5, weekStartsOn = 0, cellSize = 12, cellGap = 3, showWeekdayLabels = true, showMonthLabels = true, valueFormatter, label, emptyLabel = "No data", style, ...props }, ref) {
42
+ const sortedDates = data.map((d) => d.date).sort();
43
+ if (!Boolean(startDate || endDate || data.length)) return /* @__PURE__ */ jsx("div", {
44
+ ref,
45
+ className: cx("pf-heatmap", className),
46
+ ...props,
47
+ children: /* @__PURE__ */ jsx("div", {
48
+ className: "pf-heatmap__empty",
49
+ children: emptyLabel
50
+ })
51
+ });
52
+ const valueByDate = new Map(data.map((d) => [d.date, d.value]));
53
+ const maxValue = data.reduce((max, d) => Math.max(max, d.value), 0);
54
+ const total = data.reduce((sum, d) => sum + d.value, 0);
55
+ const levelCount = Math.max(2, levels);
56
+ const start = parseISO(startDate ?? sortedDates[0]);
57
+ const end = parseISO(endDate ?? sortedDates[sortedDates.length - 1]);
58
+ const gridStart = addDays(start, -((start.getDay() - weekStartsOn + 7) % 7));
59
+ const weeks = [];
60
+ let cursor = gridStart;
61
+ while (cursor <= end) {
62
+ const week = [];
63
+ for (let i = 0; i < 7; i++) {
64
+ week.push({
65
+ iso: toISO(cursor),
66
+ date: new Date(cursor),
67
+ inRange: cursor >= start && cursor <= end
68
+ });
69
+ cursor = addDays(cursor, 1);
70
+ }
71
+ weeks.push(week);
72
+ }
73
+ function levelFor(value) {
74
+ if (value <= 0 || maxValue <= 0) return 0;
75
+ return Math.min(levelCount - 1, Math.max(1, Math.ceil(value / maxValue * (levelCount - 1))));
76
+ }
77
+ function cellColor(level) {
78
+ if (level === 0) return "var(--pf-heatmap-empty)";
79
+ return `color-mix(in srgb, var(--pf-heatmap-color) ${Math.round(level / (levelCount - 1) * 100)}%, var(--pf-heatmap-empty))`;
80
+ }
81
+ const monthLabels = [];
82
+ let lastMonth = -1;
83
+ weeks.forEach((week, col) => {
84
+ const firstInRange = week.find((c) => c.inRange);
85
+ if (!firstInRange) return;
86
+ const month = firstInRange.date.getMonth();
87
+ if (month !== lastMonth) {
88
+ monthLabels.push({
89
+ col: col + 1,
90
+ label: MONTHS[month]
91
+ });
92
+ lastMonth = month;
93
+ }
94
+ });
95
+ const styleVars = {
96
+ "--pf-heatmap-cell-size": `${cellSize}px`,
97
+ "--pf-heatmap-cell-gap": `${cellGap}px`,
98
+ ...style
99
+ };
100
+ const ariaLabel = label ?? `Activity heatmap from ${toISO(start)} to ${toISO(end)}, ${total} total`;
101
+ return /* @__PURE__ */ jsx("div", {
102
+ ref,
103
+ className: cx("pf-heatmap", className),
104
+ style: styleVars,
105
+ role: "img",
106
+ "aria-label": ariaLabel,
107
+ ...props,
108
+ children: /* @__PURE__ */ jsxs("div", {
109
+ className: "pf-heatmap__body",
110
+ children: [showWeekdayLabels ? /* @__PURE__ */ jsxs("div", {
111
+ className: "pf-heatmap__weekdays",
112
+ children: [showMonthLabels ? /* @__PURE__ */ jsx("span", {
113
+ className: "pf-heatmap__weekday-spacer",
114
+ "aria-hidden": "true"
115
+ }) : null, /* @__PURE__ */ jsx("div", {
116
+ className: "pf-heatmap__weekday-grid",
117
+ children: Array.from({ length: 7 }, (_, i) => {
118
+ const dow = (weekStartsOn + i) % 7;
119
+ return /* @__PURE__ */ jsx("span", {
120
+ className: "pf-heatmap__weekday",
121
+ children: i % 2 === 1 ? WEEKDAYS[dow] : ""
122
+ }, i);
123
+ })
124
+ })]
125
+ }) : null, /* @__PURE__ */ jsxs("div", {
126
+ className: "pf-heatmap__main",
127
+ children: [showMonthLabels ? /* @__PURE__ */ jsx("div", {
128
+ className: "pf-heatmap__months",
129
+ style: { gridTemplateColumns: `repeat(${weeks.length}, var(--pf-heatmap-cell-size))` },
130
+ "aria-hidden": "true",
131
+ children: monthLabels.map((m) => /* @__PURE__ */ jsx("span", {
132
+ className: "pf-heatmap__month",
133
+ style: { gridColumnStart: m.col },
134
+ children: m.label
135
+ }, `${m.label}-${m.col}`))
136
+ }) : null, /* @__PURE__ */ jsx("div", {
137
+ className: "pf-heatmap__grid",
138
+ children: weeks.flatMap((week, wi) => week.map((cell, di) => {
139
+ if (!cell.inRange) return /* @__PURE__ */ jsx("span", { className: "pf-heatmap__cell pf-heatmap__cell--empty" }, `${wi}-${di}`);
140
+ const value = valueByDate.get(cell.iso) ?? 0;
141
+ const level = levelFor(value);
142
+ const title = valueFormatter ? valueFormatter(value, cell.iso) : `${cell.iso}: ${value}`;
143
+ return /* @__PURE__ */ jsx("span", {
144
+ className: "pf-heatmap__cell",
145
+ style: {
146
+ background: cellColor(level),
147
+ animationDelay: `${wi * 8}ms`
148
+ },
149
+ title,
150
+ "data-level": level
151
+ }, `${wi}-${di}`);
152
+ }))
153
+ })]
154
+ })]
155
+ })
156
+ });
157
+ });
158
+ Heatmap.displayName = "Heatmap";
159
+ //#endregion
160
+ export { Heatmap };
@@ -7,6 +7,64 @@
7
7
  gap: var(--space-3);
8
8
  grid-template-columns: auto 1fr;
9
9
  padding: var(--space-3);
10
+ position: relative;
11
+ }
12
+
13
+ /* Leave room for the centered dismiss button so content/action don't crowd it */
14
+ .pf-inline-cta--dismissible {
15
+ padding-inline-end: var(--space-12);
16
+ }
17
+
18
+ .pf-inline-cta__dismiss {
19
+ align-items: center;
20
+ background: transparent;
21
+ border: 0;
22
+ border-radius: var(--radius-sm);
23
+ color: var(--color-semantic-text-muted);
24
+ cursor: pointer;
25
+ display: inline-flex;
26
+ height: 28px;
27
+ justify-content: center;
28
+ position: absolute;
29
+ inset-inline-end: var(--space-3);
30
+ top: 50%;
31
+ transform: translateY(-50%);
32
+ width: 28px;
33
+ }
34
+
35
+ .pf-inline-cta__dismiss:hover,
36
+ .pf-inline-cta__dismiss:focus-visible {
37
+ background: var(--color-semantic-background-subtle);
38
+ color: var(--color-semantic-text-default);
39
+ outline: none;
40
+ }
41
+
42
+ /* Exit: fade and collapse so surrounding content reflows when dismissed. */
43
+ .pf-inline-cta--exiting {
44
+ animation: pf-inline-cta-out var(--duration-moderate) var(--easing-accelerate) forwards;
45
+ overflow: hidden;
46
+ pointer-events: none;
47
+ }
48
+
49
+ @keyframes pf-inline-cta-out {
50
+ from {
51
+ opacity: 1;
52
+ max-height: 480px;
53
+ }
54
+ to {
55
+ opacity: 0;
56
+ max-height: 0;
57
+ margin-top: 0;
58
+ margin-bottom: 0;
59
+ padding-top: 0;
60
+ padding-bottom: 0;
61
+ }
62
+ }
63
+
64
+ @media (prefers-reduced-motion: reduce) {
65
+ .pf-inline-cta--exiting {
66
+ animation: none;
67
+ }
10
68
  }
11
69
 
12
70
  @media (min-width: 768px) {
@@ -1,17 +1,19 @@
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 './InlineCTA.css';/* empty css */
4
5
  import { forwardRef } from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/components/InlineCTA/InlineCTA.tsx
7
- var InlineCTA = forwardRef(function InlineCTA({ className, heading, description, action, icon, iconName = "circle-question", tone = "default", children, ...props }, ref) {
8
+ var InlineCTA = forwardRef(function InlineCTA({ className, heading, description, action, icon, iconName = "circle-question", tone = "default", dismissible = false, onDismiss, children, ...props }, ref) {
8
9
  const resolvedIcon = icon ?? /* @__PURE__ */ jsx(Icon, {
9
10
  name: iconName,
10
11
  "aria-hidden": true
11
12
  });
13
+ const { isExiting, startExit } = useExitAnimation({ onExited: onDismiss });
12
14
  return /* @__PURE__ */ jsxs("div", {
13
15
  ref,
14
- className: cx("pf-inline-cta", `pf-inline-cta--${tone}`, className),
16
+ className: cx("pf-inline-cta", `pf-inline-cta--${tone}`, dismissible && "pf-inline-cta--dismissible", isExiting && "pf-inline-cta--exiting", className),
15
17
  ...props,
16
18
  children: [
17
19
  /* @__PURE__ */ jsx("span", {
@@ -36,6 +38,16 @@ var InlineCTA = forwardRef(function InlineCTA({ className, heading, description,
36
38
  action ? /* @__PURE__ */ jsx("div", {
37
39
  className: "pf-inline-cta__action",
38
40
  children: action
41
+ }) : null,
42
+ dismissible ? /* @__PURE__ */ jsx("button", {
43
+ type: "button",
44
+ className: "pf-inline-cta__dismiss",
45
+ "aria-label": "Dismiss",
46
+ onClick: startExit,
47
+ children: /* @__PURE__ */ jsx(Icon, {
48
+ name: "circle-xmark",
49
+ "aria-hidden": true
50
+ })
39
51
  }) : null
40
52
  ]
41
53
  });
@@ -89,6 +89,8 @@
89
89
  }
90
90
 
91
91
  .pf-modal__body {
92
+ display: grid;
93
+ gap: var(--space-4);
92
94
  overflow-y: auto;
93
95
  padding: var(--space-3);
94
96
  }
@@ -134,3 +136,63 @@
134
136
  color: var(--pf-modal-text);
135
137
  outline: none;
136
138
  }
139
+
140
+ /* Entrance animation — uses motion tokens via theme aliases */
141
+ @keyframes pf-modal-overlay-in {
142
+ from {
143
+ opacity: 0;
144
+ }
145
+ to {
146
+ opacity: 1;
147
+ }
148
+ }
149
+
150
+ @keyframes pf-modal-in {
151
+ from {
152
+ opacity: 0;
153
+ transform: translateY(8px) scale(0.98);
154
+ }
155
+ to {
156
+ opacity: 1;
157
+ transform: translateY(0) scale(1);
158
+ }
159
+ }
160
+
161
+ .pf-modal__overlay {
162
+ animation: pf-modal-overlay-in var(--pf-transition-fast);
163
+ }
164
+
165
+ .pf-modal {
166
+ animation: pf-modal-in var(--pf-transition-enter);
167
+ }
168
+
169
+ /* Exit animation — plays while the dialog is being dismissed (presence handling) */
170
+ @keyframes pf-modal-overlay-out {
171
+ to {
172
+ opacity: 0;
173
+ }
174
+ }
175
+
176
+ @keyframes pf-modal-out {
177
+ to {
178
+ opacity: 0;
179
+ transform: translateY(8px) scale(0.98);
180
+ }
181
+ }
182
+
183
+ .pf-modal__overlay--exiting {
184
+ animation: pf-modal-overlay-out var(--pf-transition-fast) forwards;
185
+ }
186
+
187
+ .pf-modal--exiting {
188
+ animation: pf-modal-out var(--pf-transition-fast) forwards;
189
+ }
190
+
191
+ @media (prefers-reduced-motion: reduce) {
192
+ .pf-modal__overlay,
193
+ .pf-modal,
194
+ .pf-modal__overlay--exiting,
195
+ .pf-modal--exiting {
196
+ animation: none;
197
+ }
198
+ }
@@ -1,6 +1,7 @@
1
+ import { useFocusTrap } from "../../hooks/useFocusTrap.js";
2
+ import { usePresence } from "../../hooks/usePresence.js";
1
3
  import { cx } from "../../utils/cx.js";
2
4
  import { Icon } from "../Icon/Icon2.js";
3
- import { useFocusTrap } from "../../hooks/useFocusTrap.js";
4
5
  import './Modal.css';/* empty css */
5
6
  import { forwardRef, useEffect, useId, useImperativeHandle, useRef } from "react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -28,25 +29,26 @@ var Modal = forwardRef(function Modal({ className, open, onOpenChange, title, de
28
29
  const titleId = useId();
29
30
  const descriptionId = useId();
30
31
  const dialogRef = useRef(null);
32
+ const { isMounted, isExiting } = usePresence(open);
31
33
  useImperativeHandle(ref, () => dialogRef.current, []);
32
34
  useEffect(() => {
33
- if (!open || typeof document === "undefined") return;
35
+ if (!isMounted || typeof document === "undefined") return;
34
36
  const originalOverflow = document.body.style.overflow;
35
37
  document.body.style.overflow = "hidden";
36
38
  return () => {
37
39
  document.body.style.overflow = originalOverflow;
38
40
  };
39
- }, [open]);
41
+ }, [isMounted]);
40
42
  useFocusTrap({
41
43
  containerRef: dialogRef,
42
44
  enabled: open,
43
45
  onEscape: () => onOpenChange?.(false)
44
46
  });
45
- if (!open || typeof document === "undefined") return null;
47
+ if (!isMounted || typeof document === "undefined") return null;
46
48
  return createPortal(/* @__PURE__ */ jsxs("div", {
47
49
  className: "pf-modal__portal",
48
50
  children: [/* @__PURE__ */ jsx("div", {
49
- className: "pf-modal__overlay",
51
+ className: cx("pf-modal__overlay", isExiting && "pf-modal__overlay--exiting"),
50
52
  onClick: () => {
51
53
  if (closeOnOverlayClick) onOpenChange?.(false);
52
54
  }
@@ -54,7 +56,7 @@ var Modal = forwardRef(function Modal({ className, open, onOpenChange, title, de
54
56
  className: "pf-modal__viewport",
55
57
  children: /* @__PURE__ */ jsxs("div", {
56
58
  ref: dialogRef,
57
- className: cx("pf-modal", `pf-modal--${size}`, className),
59
+ className: cx("pf-modal", `pf-modal--${size}`, isExiting && "pf-modal--exiting", className),
58
60
  role: "dialog",
59
61
  "aria-modal": "true",
60
62
  "aria-labelledby": title ? titleId : void 0,
@@ -15,7 +15,7 @@
15
15
  justify-content: space-between;
16
16
  min-height: 40px;
17
17
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-2);
18
- text-align: left;
18
+ text-align: start;
19
19
  width: 100%;
20
20
  transition:
21
21
  background 0.2s,
@@ -78,7 +78,7 @@
78
78
  justify-content: center;
79
79
  width: 20px;
80
80
  transform-origin: center;
81
- transition: transform 140ms ease;
81
+ transition: transform var(--pf-transition-fast);
82
82
  }
83
83
 
84
84
  .pf-multi-select__icon svg {
@@ -103,6 +103,19 @@
103
103
  padding: var(--space-1);
104
104
  position: fixed;
105
105
  z-index: 1000;
106
+ transform-origin: top center;
107
+ animation: pf-multi-select-menu-in var(--duration-fast) var(--easing-decelerate);
108
+ }
109
+
110
+ @keyframes pf-multi-select-menu-in {
111
+ from {
112
+ opacity: 0;
113
+ transform: translateY(-4px) scaleY(0.96);
114
+ }
115
+ to {
116
+ opacity: 1;
117
+ transform: translateY(0) scaleY(1);
118
+ }
106
119
  }
107
120
 
108
121
  .pf-multi-select__option {
@@ -154,4 +167,8 @@
154
167
  .pf-multi-select__icon {
155
168
  transition: none;
156
169
  }
170
+
171
+ .pf-multi-select__menu {
172
+ animation: none;
173
+ }
157
174
  }
@@ -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 './MultiSelect.css';/* empty css */
12
12
  import { forwardRef, useEffect, useId, useMemo, useRef } from "react";
13
13
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -125,7 +125,6 @@ var MultiSelect = forwardRef(({ id, options, value, defaultValue, onValueChange,
125
125
  "aria-haspopup": "listbox",
126
126
  "aria-expanded": isOpen,
127
127
  "aria-required": required || void 0,
128
- "aria-controls": isOpen ? listboxId : void 0,
129
128
  "aria-describedby": describedBy,
130
129
  onClick: () => {
131
130
  disclosure.toggle();
@@ -59,6 +59,45 @@
59
59
  max-width: none;
60
60
  padding: var(--space-3);
61
61
  width: 100%;
62
+ animation: pf-notification-in var(--duration-moderate) var(--easing-decelerate);
63
+ }
64
+
65
+ @keyframes pf-notification-in {
66
+ from {
67
+ opacity: 0;
68
+ transform: translateX(12px);
69
+ }
70
+ }
71
+
72
+ /* Exit: slide off to the right and collapse so stacked siblings reflow. */
73
+ .pf-notification--exiting {
74
+ animation: pf-notification-out var(--duration-moderate) var(--easing-accelerate) forwards;
75
+ overflow: hidden;
76
+ pointer-events: none;
77
+ }
78
+
79
+ @keyframes pf-notification-out {
80
+ from {
81
+ opacity: 1;
82
+ transform: translateX(0);
83
+ max-height: 480px;
84
+ }
85
+ to {
86
+ opacity: 0;
87
+ transform: translateX(100%);
88
+ max-height: 0;
89
+ margin-top: 0;
90
+ margin-bottom: 0;
91
+ padding-top: 0;
92
+ padding-bottom: 0;
93
+ }
94
+ }
95
+
96
+ @media (prefers-reduced-motion: reduce) {
97
+ .pf-notification,
98
+ .pf-notification--exiting {
99
+ animation: none;
100
+ }
62
101
  }
63
102
 
64
103
  @media (min-width: 768px) {
@@ -69,22 +108,38 @@
69
108
  }
70
109
 
71
110
  .pf-notification--info {
72
- background: color-mix(in srgb, var(--pf-notification-info-bg) 45%, var(--color-base-white));
111
+ background: color-mix(
112
+ in srgb,
113
+ var(--pf-notification-info-bg) 45%,
114
+ var(--pf-notification-mix-base)
115
+ );
73
116
  border-color: var(--pf-notification-info-border);
74
117
  }
75
118
 
76
119
  .pf-notification--success {
77
- background: color-mix(in srgb, var(--pf-notification-success-bg) 45%, var(--color-base-white));
120
+ background: color-mix(
121
+ in srgb,
122
+ var(--pf-notification-success-bg) 45%,
123
+ var(--pf-notification-mix-base)
124
+ );
78
125
  border-color: var(--pf-notification-success-border);
79
126
  }
80
127
 
81
128
  .pf-notification--warning {
82
- background: color-mix(in srgb, var(--pf-notification-warning-bg) 45%, var(--color-base-white));
129
+ background: color-mix(
130
+ in srgb,
131
+ var(--pf-notification-warning-bg) 45%,
132
+ var(--pf-notification-mix-base)
133
+ );
83
134
  border-color: var(--pf-notification-warning-border);
84
135
  }
85
136
 
86
137
  .pf-notification--danger {
87
- background: color-mix(in srgb, var(--pf-notification-danger-bg) 45%, var(--color-base-white));
138
+ background: color-mix(
139
+ in srgb,
140
+ var(--pf-notification-danger-bg) 45%,
141
+ var(--pf-notification-mix-base)
142
+ );
88
143
  border-color: var(--pf-notification-danger-border);
89
144
  }
90
145
 
@@ -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