@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
@@ -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,104 @@
1
+ .pf-heatmap {
2
+ display: inline-block;
3
+ max-width: 100%;
4
+ /* A full year is wider than small screens — scroll horizontally instead of
5
+ overflowing the page. */
6
+ overflow-x: auto;
7
+ font-family: var(--font-family-sans);
8
+ }
9
+
10
+ .pf-heatmap__empty {
11
+ color: var(--color-semantic-text-muted);
12
+ font-size: var(--font-size-sm);
13
+ padding: var(--space-4);
14
+ text-align: center;
15
+ }
16
+
17
+ .pf-heatmap__body {
18
+ display: flex;
19
+ gap: var(--pf-heatmap-cell-gap);
20
+ }
21
+
22
+ .pf-heatmap__weekdays {
23
+ display: flex;
24
+ flex-direction: column;
25
+ }
26
+
27
+ .pf-heatmap__weekday-spacer {
28
+ display: block;
29
+ height: calc(var(--font-size-xs) + var(--space-1));
30
+ }
31
+
32
+ .pf-heatmap__weekday-grid {
33
+ display: grid;
34
+ grid-template-rows: repeat(7, var(--pf-heatmap-cell-size));
35
+ gap: var(--pf-heatmap-cell-gap);
36
+ }
37
+
38
+ .pf-heatmap__weekday {
39
+ align-items: center;
40
+ color: var(--pf-heatmap-label-color);
41
+ display: flex;
42
+ font-size: var(--font-size-xs);
43
+ line-height: 1;
44
+ padding-inline-end: var(--space-1);
45
+ }
46
+
47
+ .pf-heatmap__main {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--space-1);
51
+ }
52
+
53
+ .pf-heatmap__months {
54
+ display: grid;
55
+ gap: var(--pf-heatmap-cell-gap);
56
+ height: var(--font-size-xs);
57
+ }
58
+
59
+ .pf-heatmap__month {
60
+ color: var(--pf-heatmap-label-color);
61
+ font-size: var(--font-size-xs);
62
+ line-height: 1;
63
+ }
64
+
65
+ .pf-heatmap__grid {
66
+ display: grid;
67
+ grid-template-rows: repeat(7, var(--pf-heatmap-cell-size));
68
+ grid-auto-flow: column;
69
+ grid-auto-columns: var(--pf-heatmap-cell-size);
70
+ gap: var(--pf-heatmap-cell-gap);
71
+ }
72
+
73
+ .pf-heatmap__cell {
74
+ border-radius: var(--pf-heatmap-cell-radius);
75
+ height: var(--pf-heatmap-cell-size);
76
+ width: var(--pf-heatmap-cell-size);
77
+ }
78
+
79
+ .pf-heatmap__cell--empty {
80
+ background: transparent;
81
+ }
82
+
83
+ /* Staggered fade-in on mount — per-column animation-delay is set inline so
84
+ the grid wipes in left-to-right (oldest week → newest). */
85
+ .pf-heatmap__cell {
86
+ animation: pf-heatmap-cell-in var(--duration-moderate) var(--easing-decelerate) both;
87
+ }
88
+
89
+ @keyframes pf-heatmap-cell-in {
90
+ from {
91
+ opacity: 0;
92
+ transform: scale(0.6);
93
+ }
94
+ to {
95
+ opacity: 1;
96
+ transform: scale(1);
97
+ }
98
+ }
99
+
100
+ @media (prefers-reduced-motion: reduce) {
101
+ .pf-heatmap__cell {
102
+ animation: none;
103
+ }
104
+ }
@@ -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