@heymantle/litho 0.0.13 → 0.0.15

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 (63) hide show
  1. package/README.md +52 -0
  2. package/dist/cjs/components/Card.js +1 -1
  3. package/dist/cjs/components/Disclosure.js +46 -15
  4. package/dist/cjs/components/DropZone.js +93 -37
  5. package/dist/cjs/components/Layout.js +4 -2
  6. package/dist/cjs/components/Modal.js +14 -3
  7. package/dist/cjs/components/Navigation.js +4 -3
  8. package/dist/cjs/components/Popover.js +58 -13
  9. package/dist/cjs/components/Select.js +4 -0
  10. package/dist/cjs/components/TabNavigation.js +65 -11
  11. package/dist/cjs/components/Table.js +27 -11
  12. package/dist/cjs/components/Tabs.js +33 -2
  13. package/dist/cjs/components/TextField.js +4 -2
  14. package/dist/cjs/components/ToastNotification.js +368 -0
  15. package/dist/cjs/components/ToastProvider.js +342 -0
  16. package/dist/cjs/index.js +11 -0
  17. package/dist/cjs/playwright.config.js +114 -0
  18. package/dist/cjs/styles/Table.js +2 -7
  19. package/dist/cjs/tests/visual/stories.spec.js +637 -0
  20. package/dist/cjs/utilities/dates.js +7 -7
  21. package/dist/esm/components/Card.js +1 -1
  22. package/dist/esm/components/Disclosure.js +36 -5
  23. package/dist/esm/components/DropZone.js +94 -38
  24. package/dist/esm/components/Layout.js +4 -2
  25. package/dist/esm/components/Modal.js +14 -3
  26. package/dist/esm/components/Navigation.js +4 -3
  27. package/dist/esm/components/Popover.js +58 -13
  28. package/dist/esm/components/Select.js +5 -1
  29. package/dist/esm/components/TabNavigation.js +65 -11
  30. package/dist/esm/components/Table.js +27 -11
  31. package/dist/esm/components/Tabs.js +33 -2
  32. package/dist/esm/components/TextField.js +4 -2
  33. package/dist/esm/components/ToastNotification.js +353 -0
  34. package/dist/esm/components/ToastProvider.js +336 -0
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/playwright.config.js +104 -0
  37. package/dist/esm/styles/Table.js +2 -7
  38. package/dist/esm/tests/visual/stories.spec.js +633 -0
  39. package/dist/esm/utilities/dates.js +7 -7
  40. package/dist/types/components/Disclosure.d.ts.map +1 -1
  41. package/dist/types/components/DropZone.d.ts +2 -0
  42. package/dist/types/components/DropZone.d.ts.map +1 -1
  43. package/dist/types/components/Layout.d.ts.map +1 -1
  44. package/dist/types/components/Modal.d.ts.map +1 -1
  45. package/dist/types/components/Navigation.d.ts +1 -0
  46. package/dist/types/components/Navigation.d.ts.map +1 -1
  47. package/dist/types/components/Popover.d.ts +2 -0
  48. package/dist/types/components/Popover.d.ts.map +1 -1
  49. package/dist/types/components/Select.d.ts.map +1 -1
  50. package/dist/types/components/TabNavigation.d.ts.map +1 -1
  51. package/dist/types/components/Table.d.ts.map +1 -1
  52. package/dist/types/components/Tabs.d.ts +45 -1
  53. package/dist/types/components/Tabs.d.ts.map +1 -1
  54. package/dist/types/components/TextField.d.ts +2 -0
  55. package/dist/types/components/TextField.d.ts.map +1 -1
  56. package/dist/types/components/ToastNotification.d.ts +36 -0
  57. package/dist/types/components/ToastNotification.d.ts.map +1 -0
  58. package/dist/types/components/ToastProvider.d.ts +21 -0
  59. package/dist/types/components/ToastProvider.d.ts.map +1 -0
  60. package/dist/types/index.d.ts +2 -0
  61. package/dist/types/styles/Table.d.ts.map +1 -1
  62. package/index.css +3 -0
  63. package/package.json +12 -3
package/README.md CHANGED
@@ -107,6 +107,58 @@ Build a customer list page with search and filtering using Litho components.
107
107
 
108
108
  See **`AI_USAGE_IN_PROJECTS.md`** for detailed instructions on accessing guides and Storybook examples from projects using Litho as a dependency.
109
109
 
110
+ ## Visual Regression Testing
111
+
112
+ Litho includes automated visual regression tests using Playwright to catch unintended UI changes.
113
+
114
+ ### Running Tests
115
+
116
+ ```bash
117
+ # Run visual regression tests (uses Docker for CI parity)
118
+ npm run test:visual:docker
119
+
120
+ # Update baseline screenshots (uses Docker for CI parity)
121
+ npm run test:visual:docker:update
122
+ ```
123
+
124
+ > **Note:** The Docker commands ensure baselines match CI exactly. Native commands (`npm run test:visual`) are also available but may produce different results due to font rendering differences between macOS and Linux.
125
+
126
+ ### When Baselines Need Updating
127
+
128
+ Update baselines when you've made **intentional** visual changes:
129
+ - Modified component styles or layout
130
+ - Changed default props or variants
131
+ - Updated theme colors or typography
132
+ - Added new visual states to existing components
133
+
134
+ ### Adding New Stories
135
+
136
+ When you add a new component or story:
137
+ 1. The visual tests automatically discover all stories from Storybook
138
+ 2. Run `npm run test:visual:update` to generate baseline screenshots for new stories
139
+ 3. Commit the new baseline images in `tests/visual/stories.spec.js-snapshots/`
140
+
141
+ ### Skipped Stories
142
+
143
+ Some stories are skipped because they contain non-deterministic content:
144
+ - **Image stories** - Use random external images from picsum.photos
145
+ - Add problematic stories to `SKIP_STORIES` in `tests/visual/stories.spec.js`
146
+
147
+ ### CI Integration
148
+
149
+ Visual tests run automatically on PRs to `main`. If tests fail:
150
+ 1. Download the `visual-regression-diffs` artifact from the workflow
151
+ 2. Review the diff images to determine if changes are intentional
152
+ 3. If intentional, use the **Update Visual Baselines** workflow to regenerate baselines
153
+
154
+ ### Generating Baselines from CI
155
+
156
+ Since macOS and Linux render fonts differently, generate baselines from CI for best results:
157
+
158
+ 1. Go to **Actions** → **Update Visual Baselines** → **Run workflow**
159
+ 2. The workflow will create a PR with updated baselines
160
+ 3. Review and merge the PR
161
+
110
162
  ## License
111
163
 
112
164
  MIT
@@ -516,7 +516,7 @@ var sectionBodyStyles = (0, _tailwindvariants.tv)({
516
516
  };
517
517
  Card.Section.displayName = "Card.Section";
518
518
  var clickableRowStyles = (0, _tailwindvariants.tv)({
519
- base: "Litho-Card__Row py-1.5 px-2.5 cursor-pointer rounded-md hover:bg-tint-2 active:bg-tint-3",
519
+ base: "Litho-Card__Row py-1.5 px-2.5 @md:px-3 cursor-pointer rounded-md hover:bg-tint-2 active:bg-tint-3",
520
520
  variants: {
521
521
  disabled: {
522
522
  true: "opacity-50 cursor-not-allowed pointer-events-none"
@@ -1,3 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ var _jsxruntime = require("react/jsx-runtime");
12
+ var _tailwindvariants = require("tailwind-variants");
13
+ var styles = (0, _tailwindvariants.tv)({
14
+ base: "Litho-Disclosure relative",
15
+ variants: {
16
+ flush: {
17
+ true: "pl-4",
18
+ false: "pl-7.5 @md:pl-7"
19
+ }
20
+ },
21
+ defaultVariants: {
22
+ flush: false
23
+ }
24
+ });
25
+ var borderStyles = (0, _tailwindvariants.tv)({
26
+ base: "Litho-Disclosure__Border absolute top-0 w-px h-full bg-tint-5 dark:bg-tint-alt-5",
27
+ variants: {
28
+ flush: {
29
+ true: "left-0.5",
30
+ false: "left-[9px]"
31
+ }
32
+ },
33
+ defaultVariants: {
34
+ flush: false
35
+ }
36
+ });
1
37
  /**
2
38
  * Renders a Disclosure component that displays content with disclosure styling.
3
39
  *
@@ -18,25 +54,20 @@
18
54
  * <Disclosure className="mt-4">
19
55
  * <Text>This is disclosure content with margin</Text>
20
56
  * </Disclosure>
21
- */ "use strict";
22
- Object.defineProperty(exports, "__esModule", {
23
- value: true
24
- });
25
- Object.defineProperty(exports, "default", {
26
- enumerable: true,
27
- get: function() {
28
- return _default;
29
- }
30
- });
31
- var _jsxruntime = require("react/jsx-runtime");
32
- function Disclosure() {
57
+ */ function Disclosure() {
33
58
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
34
- var children = props.children, className = props.className;
59
+ var children = props.children, className = props.className, _props_flush = props.flush, flush = _props_flush === void 0 ? false : _props_flush;
60
+ var classes = styles({
61
+ flush: flush
62
+ });
63
+ var borderClasses = borderStyles({
64
+ flush: flush
65
+ });
35
66
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
36
- className: "relative pl-7.5 @md:pl-7".concat(className ? " ".concat(className) : ""),
67
+ className: "".concat(classes).concat(className ? " ".concat(className) : ""),
37
68
  children: [
38
69
  /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
39
- className: "absolute left-[9px] top-0 w-px h-full bg-tint-5 dark:bg-tint-alt-5"
70
+ className: borderClasses
40
71
  }),
41
72
  /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
42
73
  children: children
@@ -84,6 +84,42 @@ function _unsupported_iterable_to_array(o, minLen) {
84
84
  if (n === "Map" || n === "Set") return Array.from(n);
85
85
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
86
86
  }
87
+ var normalizeDropZoneAccept = function(accept) {
88
+ if (!accept) return [];
89
+ return typeof accept === "string" ? [
90
+ accept
91
+ ] : accept;
92
+ };
93
+ var isFileAcceptedForDropZone = function(file) {
94
+ var accept = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
95
+ if (!accept || accept.length === 0) return true;
96
+ return accept.some(function(type) {
97
+ if (!type) return false;
98
+ if (type === "*/*") return true;
99
+ if (type.endsWith("/*")) {
100
+ var _type_split = _sliced_to_array(type.split("/"), 1), prefix = _type_split[0];
101
+ if (prefix === "*") return true;
102
+ return !!(file === null || file === void 0 ? void 0 : file.type) && file.type.startsWith("".concat(prefix, "/"));
103
+ }
104
+ return (file === null || file === void 0 ? void 0 : file.type) === type;
105
+ });
106
+ };
107
+ var partitionFilesByDropZoneAccept = function(files) {
108
+ var accept = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
109
+ var acceptedFiles = [];
110
+ var rejectedFiles = [];
111
+ files.forEach(function(file) {
112
+ if (isFileAcceptedForDropZone(file, accept)) {
113
+ acceptedFiles.push(file);
114
+ } else {
115
+ rejectedFiles.push(file);
116
+ }
117
+ });
118
+ return {
119
+ acceptedFiles: acceptedFiles,
120
+ rejectedFiles: rejectedFiles
121
+ };
122
+ };
87
123
  var dropZoneStyles = (0, _tailwindvariants.tv)({
88
124
  base: "Litho-DropZone w-full flex items-center justify-center p-4 border border-form-border border-dashed rounded-md focus:outline-hidden",
89
125
  variants: {
@@ -145,11 +181,7 @@ var dropZoneStyles = (0, _tailwindvariants.tv)({
145
181
  */ function DropZone() {
146
182
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
147
183
  var id = props.id, label = props.label, labelHidden = props.labelHidden, labelAction = props.labelAction, tooltip = props.tooltip, labelVariant = props.labelVariant, _props_type = props.type, type = _props_type === void 0 ? "file" : _props_type, _accept = props.accept, _props_allowMultiple = props.allowMultiple, allowMultiple = _props_allowMultiple === void 0 ? false : _props_allowMultiple, _props_showDropZoneWhenDisabled = props.showDropZoneWhenDisabled, showDropZoneWhenDisabled = _props_showDropZoneWhenDisabled === void 0 ? true : _props_showDropZoneWhenDisabled, _disabled = props.disabled, onDrop = props.onDrop, onDropAccepted = props.onDropAccepted, onDropRejected = props.onDropRejected, helpText = props.helpText, error = props.error, _props_uploading = props.uploading, uploading = _props_uploading === void 0 ? false : _props_uploading, _props_uploadedFiles = props.uploadedFiles, uploadedFiles = _props_uploadedFiles === void 0 ? [] : _props_uploadedFiles, _props_uploadLabel = props.uploadLabel, uploadLabel = _props_uploadLabel === void 0 ? "Drag and drop files or click to upload" : _props_uploadLabel, _props_uploadingLabel = props.uploadingLabel, uploadingLabel = _props_uploadingLabel === void 0 ? "Uploading..." : _props_uploadingLabel, _props_dragLabel = props.dragLabel, dragLabel = _props_dragLabel === void 0 ? "Release to upload" : _props_dragLabel, tmp = props.showFilePreview, _showFilePreview = tmp === void 0 ? true : tmp, previewLoading = props.previewLoading, _props_previewRows = props.previewRows, previewRows = _props_previewRows === void 0 ? 2 : _props_previewRows, onRemove = props.onRemove, onFilePreviewClick = props.onFilePreviewClick, filename = props.filename;
148
- var accept = _accept ? typeof _accept === "string" ? [
149
- _accept
150
- ] : _accept : [
151
- "*/*"
152
- ];
184
+ var accept = normalizeDropZoneAccept(_accept);
153
185
  var inputRef = (0, _react.useRef)(null);
154
186
  var _useState = _sliced_to_array((0, _react.useState)(false), 2), hasFocusWithin = _useState[0], setHasFocusWithin = _useState[1];
155
187
  var _useState1 = _sliced_to_array((0, _react.useState)(false), 2), isDraggingOver = _useState1[0], setIsDraggingOver = _useState1[1];
@@ -210,22 +242,7 @@ var dropZoneStyles = (0, _tailwindvariants.tv)({
210
242
  setDragFileCount(0);
211
243
  if (disabled) return;
212
244
  var droppedFiles = Array.from(event.dataTransfer.files);
213
- var acceptedFiles = [];
214
- var rejectedFiles = [];
215
- droppedFiles.forEach(function(file) {
216
- var isValidType = typeAccept.length === 0 || typeAccept.some(function(type) {
217
- if (type.endsWith("/*")) {
218
- return file.type.startsWith("".concat(type.split("/")[0], "/"));
219
- }
220
- return file.type === type;
221
- });
222
- var isValid = isValidType;
223
- if (isValid) {
224
- acceptedFiles.push(file);
225
- } else {
226
- rejectedFiles.push(file);
227
- }
228
- });
245
+ var _partitionFilesByDropZoneAccept = partitionFilesByDropZoneAccept(droppedFiles, typeAccept), acceptedFiles = _partitionFilesByDropZoneAccept.acceptedFiles, rejectedFiles = _partitionFilesByDropZoneAccept.rejectedFiles;
229
246
  if (onDrop) {
230
247
  onDrop(droppedFiles);
231
248
  }
@@ -238,22 +255,7 @@ var dropZoneStyles = (0, _tailwindvariants.tv)({
238
255
  };
239
256
  var handleFileChange = function(event) {
240
257
  var selectedFiles = Array.from(event.target.files);
241
- var acceptedFiles = [];
242
- var rejectedFiles = [];
243
- selectedFiles.forEach(function(file) {
244
- var isValidType = typeAccept.length === 0 || typeAccept.some(function(type) {
245
- if (type.endsWith("/*")) {
246
- return file.type.startsWith("".concat(type.split("/")[0], "/"));
247
- }
248
- return file.type === type;
249
- });
250
- var isValid = isValidType;
251
- if (isValid) {
252
- acceptedFiles.push(file);
253
- } else {
254
- rejectedFiles.push(file);
255
- }
256
- });
258
+ var _partitionFilesByDropZoneAccept = partitionFilesByDropZoneAccept(selectedFiles, typeAccept), acceptedFiles = _partitionFilesByDropZoneAccept.acceptedFiles, rejectedFiles = _partitionFilesByDropZoneAccept.rejectedFiles;
257
259
  if (onDrop) {
258
260
  onDrop(selectedFiles);
259
261
  }
@@ -291,6 +293,10 @@ var dropZoneStyles = (0, _tailwindvariants.tv)({
291
293
  }),
292
294
  labelAction && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Link.default, {
293
295
  onClick: labelAction.onAction,
296
+ insight: labelAction.insight,
297
+ icon: labelAction.insight ? _polarisicons.MagicMajor : undefined,
298
+ color: labelAction.insight ? "insight" : undefined,
299
+ disabled: labelAction.disabled,
294
300
  removeUnderline: true,
295
301
  children: labelAction.content
296
302
  })
@@ -369,6 +375,56 @@ var dropZoneStyles = (0, _tailwindvariants.tv)({
369
375
  ]
370
376
  });
371
377
  }
378
+ var DropTarget = function() {
379
+ var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
380
+ var _accept = props.accept, _props_allowMultiple = props.allowMultiple, allowMultiple = _props_allowMultiple === void 0 ? true : _props_allowMultiple, _props_disabled = props.disabled, disabled = _props_disabled === void 0 ? false : _props_disabled, onDrop = props.onDrop, onDropAccepted = props.onDropAccepted, onDropRejected = props.onDropRejected, className = props.className, dragOverClassName = props.dragOverClassName, children = props.children;
381
+ var accept = normalizeDropZoneAccept(_accept);
382
+ var _useState = _sliced_to_array((0, _react.useState)(false), 2), isDraggingOver = _useState[0], setIsDraggingOver = _useState[1];
383
+ var handleDragOver = function(event) {
384
+ event.preventDefault();
385
+ event.stopPropagation();
386
+ if (!disabled) {
387
+ setIsDraggingOver(true);
388
+ }
389
+ };
390
+ var handleDragLeave = function(event) {
391
+ event.preventDefault();
392
+ event.stopPropagation();
393
+ setIsDraggingOver(false);
394
+ };
395
+ var handleDrop = function(event) {
396
+ event.preventDefault();
397
+ event.stopPropagation();
398
+ setIsDraggingOver(false);
399
+ if (disabled) return;
400
+ var droppedFiles = Array.from(event.dataTransfer.files);
401
+ if (allowMultiple === false) {
402
+ droppedFiles = droppedFiles.slice(0, 1);
403
+ }
404
+ var _partitionFilesByDropZoneAccept = partitionFilesByDropZoneAccept(droppedFiles, accept), acceptedFiles = _partitionFilesByDropZoneAccept.acceptedFiles, rejectedFiles = _partitionFilesByDropZoneAccept.rejectedFiles;
405
+ if (onDrop) {
406
+ onDrop(droppedFiles);
407
+ }
408
+ if (onDropAccepted && acceptedFiles.length > 0) {
409
+ onDropAccepted(acceptedFiles);
410
+ }
411
+ if (onDropRejected && rejectedFiles.length > 0) {
412
+ onDropRejected(rejectedFiles);
413
+ }
414
+ };
415
+ var dropTargetClassName = [
416
+ className,
417
+ isDraggingOver && dragOverClassName
418
+ ].filter(Boolean).join(" ");
419
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
420
+ className: dropTargetClassName,
421
+ onDragOver: handleDragOver,
422
+ onDragLeave: handleDragLeave,
423
+ onDrop: handleDrop,
424
+ children: children
425
+ });
426
+ };
427
+ DropZone.DropTarget = DropTarget;
372
428
  var previewContainerStyles = (0, _tailwindvariants.tv)({
373
429
  base: "flex flex-col",
374
430
  variants: {
@@ -211,11 +211,13 @@ var annotatedSectionStyles = (0, _tailwindvariants.tv)({
211
211
  var annotatedSectionClasses = annotatedSectionStyles({
212
212
  embedded: embedded
213
213
  });
214
+ var annotationColSpan = embedded ? "@md-embed:col-span-4" : "@md:col-span-4";
215
+ var contentColSpan = embedded ? "@md-embed:col-span-8" : "@md:col-span-8";
214
216
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
215
217
  className: annotatedSectionClasses,
216
218
  children: [
217
219
  /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
218
- className: "Litho-Layout__Annotation flex flex-col gap-1 @md:col-span-4 py-2",
220
+ className: "Litho-Layout__Annotation flex flex-col gap-1 ".concat(annotationColSpan, " py-2"),
219
221
  children: [
220
222
  title && (typeof title === "string" ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Text.default, {
221
223
  variant: "headingMd",
@@ -228,7 +230,7 @@ var annotatedSectionStyles = (0, _tailwindvariants.tv)({
228
230
  ]
229
231
  }),
230
232
  /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
231
- className: "Litho-Layout__AnnotationContent @md:col-span-8",
233
+ className: "Litho-Layout__AnnotationContent ".concat(contentColSpan),
232
234
  children: children
233
235
  })
234
236
  ]
@@ -127,7 +127,16 @@ function _unsupported_iterable_to_array(o, minLen) {
127
127
  }
128
128
  var ModalContext = /*#__PURE__*/ (0, _react.createContext)(false);
129
129
  var containerStyles = (0, _tailwindvariants.tv)({
130
- base: "Litho-ModalContainer fixed inset-0 block flex flex-col justify-end md:justify-center items-center pointer-events-none"
130
+ base: "Litho-ModalContainer fixed inset-0 block flex flex-col justify-end md:justify-center items-center pointer-events-none",
131
+ variants: {
132
+ hidden: {
133
+ true: "hidden opacity-0 pointer-events-none",
134
+ false: ""
135
+ }
136
+ },
137
+ defaultVariants: {
138
+ hidden: false
139
+ }
131
140
  });
132
141
  var styles = (0, _tailwindvariants.tv)({
133
142
  base: "Litho-Modal relative bg-surface-highest shadow-modal dark:shadow-modal-dark w-full mx-auto pointer-events-auto flex flex-col overflow-hidden",
@@ -206,7 +215,7 @@ var sectionStyles = (0, _tailwindvariants.tv)({
206
215
  * @returns {React.ReactPortal|null} The rendered modal component or null if not open.
207
216
  */ function Modal() {
208
217
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
209
- var children = props.children, title = props.title, subtitle = props.subtitle, open = props.open, onClose = props.onClose, sectioned = props.sectioned, primaryAction = props.primaryAction, secondaryAction = props.secondaryAction, _props_secondaryActions = props.secondaryActions, secondaryActions = _props_secondaryActions === void 0 ? [] : _props_secondaryActions, destructiveAction = props.destructiveAction, loading = props.loading, _props_size = props.size, size = _props_size === void 0 ? "default" : _props_size, leftAccessory = props.leftAccessory, _props_hideCloseButton = props.hideCloseButton, hideCloseButton = _props_hideCloseButton === void 0 ? false : _props_hideCloseButton, backAction = props.backAction, zIndexOverride = props.zIndexOverride, _props_autoFocusFirstInput = props.autoFocusFirstInput, autoFocusFirstInput = _props_autoFocusFirstInput === void 0 ? true : _props_autoFocusFirstInput, bodyClassName = props.bodyClassName;
218
+ var children = props.children, title = props.title, subtitle = props.subtitle, open = props.open, _props_hidden = props.hidden, hidden = _props_hidden === void 0 ? false : _props_hidden, onClose = props.onClose, sectioned = props.sectioned, primaryAction = props.primaryAction, secondaryAction = props.secondaryAction, _props_secondaryActions = props.secondaryActions, secondaryActions = _props_secondaryActions === void 0 ? [] : _props_secondaryActions, destructiveAction = props.destructiveAction, loading = props.loading, _props_size = props.size, size = _props_size === void 0 ? "default" : _props_size, leftAccessory = props.leftAccessory, _props_hideCloseButton = props.hideCloseButton, hideCloseButton = _props_hideCloseButton === void 0 ? false : _props_hideCloseButton, backAction = props.backAction, zIndexOverride = props.zIndexOverride, _props_autoFocusFirstInput = props.autoFocusFirstInput, autoFocusFirstInput = _props_autoFocusFirstInput === void 0 ? true : _props_autoFocusFirstInput, bodyClassName = props.bodyClassName;
210
219
  var setModalIsOpen = (0, _Frame.useFrame)().setModalIsOpen;
211
220
  var modalContentRef = (0, _react.useRef)(null);
212
221
  var hasChildren = !!children;
@@ -214,7 +223,9 @@ var sectionStyles = (0, _tailwindvariants.tv)({
214
223
  sectioned: sectioned,
215
224
  size: size
216
225
  });
217
- var containerClasses = containerStyles();
226
+ var containerClasses = containerStyles({
227
+ hidden: hidden
228
+ });
218
229
  var headerClasses = headerStyles({
219
230
  hasChildren: hasChildren
220
231
  });
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "default", {
9
9
  }
10
10
  });
11
11
  var _jsxruntime = require("react/jsx-runtime");
12
+ var _react = /*#__PURE__*/ _interop_require_default(require("react"));
12
13
  var _tailwindvariants = require("tailwind-variants");
13
14
  var _Badge = /*#__PURE__*/ _interop_require_default(require("./Badge"));
14
15
  var _Icon = /*#__PURE__*/ _interop_require_default(require("./Icon"));
@@ -240,7 +241,7 @@ var linkStyles = (0, _tailwindvariants.tv)({
240
241
  })
241
242
  ]
242
243
  }),
243
- badge ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Badge.default, {
244
+ badge ? /*#__PURE__*/ _react.default.isValidElement(badge) ? badge : /*#__PURE__*/ (0, _jsxruntime.jsx)(_Badge.default, {
244
245
  status: badge.status,
245
246
  children: badge.label
246
247
  }) : external ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Icon.default, {
@@ -329,10 +330,10 @@ var subLinkStyles = (0, _tailwindvariants.tv)({
329
330
  })
330
331
  ]
331
332
  }),
332
- badge && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Badge.default, {
333
+ badge && /*#__PURE__*/ (_react.default.isValidElement(badge) ? badge : /*#__PURE__*/ (0, _jsxruntime.jsx)(_Badge.default, {
333
334
  status: badge.status,
334
335
  children: badge.label
335
- }),
336
+ })),
336
337
  accessory
337
338
  ]
338
339
  });
@@ -189,18 +189,23 @@ var styles = (0, _tailwindvariants.tv)({
189
189
  * @param {boolean} [props.sectioned=false] - Whether the popover content is sectioned with padding.
190
190
  * @param {string} [props.preferredAlignment="center"] - Preferred alignment for the popover (left, center, right).
191
191
  * @param {string} [props.preferredPosition="below"] - Preferred position for the popover (above, below, cover, left, right).
192
+ * @param {number} [props.horizontalOffset] - Custom horizontal offset in pixels to adjust the popover position.
192
193
  * @param {string} [props.className] - Additional class name for the popover.
193
194
  * @param {string} [props.containerClassName] - Additional class name for the popover container.
194
195
  * @returns {JSX.Element} The rendered popover component.
195
196
  */ function Popover() {
196
197
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
197
- var activatorWrapper = props.activatorWrapper, _props_activatorDisplayType = props.activatorDisplayType, activatorDisplayType = _props_activatorDisplayType === void 0 ? "inline-block" : _props_activatorDisplayType, activator = props.activator, children = props.children, onClose = props.onClose, active = props.active, fixed = props.fixed, zIndexOverride = props.zIndexOverride, _props_sectioned = props.sectioned, sectioned = _props_sectioned === void 0 ? false : _props_sectioned, _props_preferredAlignment = props.preferredAlignment, preferredAlignment = _props_preferredAlignment === void 0 ? "center" : _props_preferredAlignment, _props_preferredPosition = props.preferredPosition, preferredPosition = _props_preferredPosition === void 0 ? "below" : _props_preferredPosition, _props_matchActivatorWidth = props.matchActivatorWidth, matchActivatorWidth = _props_matchActivatorWidth === void 0 ? false : _props_matchActivatorWidth, _props_maxWidth = props.maxWidth, maxWidth = _props_maxWidth === void 0 ? "default" : _props_maxWidth, className = props.className, containerClassName = props.containerClassName;
198
+ var activatorWrapper = props.activatorWrapper, _props_activatorDisplayType = props.activatorDisplayType, activatorDisplayType = _props_activatorDisplayType === void 0 ? "inline-block" : _props_activatorDisplayType, activator = props.activator, children = props.children, onClose = props.onClose, activeProp = props.active, fixed = props.fixed, zIndexOverride = props.zIndexOverride, _props_sectioned = props.sectioned, sectioned = _props_sectioned === void 0 ? false : _props_sectioned, _props_preferredAlignment = props.preferredAlignment, preferredAlignment = _props_preferredAlignment === void 0 ? "center" : _props_preferredAlignment, _props_preferredPosition = props.preferredPosition, preferredPosition = _props_preferredPosition === void 0 ? "below" : _props_preferredPosition, _props_horizontalOffset = props.horizontalOffset, horizontalOffset = _props_horizontalOffset === void 0 ? 0 : _props_horizontalOffset, _props_matchActivatorWidth = props.matchActivatorWidth, matchActivatorWidth = _props_matchActivatorWidth === void 0 ? false : _props_matchActivatorWidth, _props_maxWidth = props.maxWidth, maxWidth = _props_maxWidth === void 0 ? "default" : _props_maxWidth, className = props.className, containerClassName = props.containerClassName, _props_closeOnResize = props.closeOnResize, closeOnResize = _props_closeOnResize === void 0 ? true : _props_closeOnResize, _props_debounceResizeObserver = props.debounceResizeObserver, debounceResizeObserver = _props_debounceResizeObserver === void 0 ? false : _props_debounceResizeObserver, _props_usePortal = props.usePortal, usePortal = _props_usePortal === void 0 ? true : _props_usePortal;
198
199
  var ActivatorWrapper = activatorWrapper || "div";
199
200
  var idValue = (0, _react.useId)();
200
201
  var idRef = (0, _react.useRef)(idValue);
201
202
  var initialWidthRef = (0, _react.useRef)(null);
202
203
  var popoverRef = (0, _react.useRef)(null);
203
204
  var activatorRef = (0, _react.useRef)(null);
205
+ // Check if the activator is inside an inert element (e.g., table cells covered by fixed columns)
206
+ var isActivatorInert = activatorRef.current ? activatorRef.current.closest('[inert]') !== null : false;
207
+ // When disabled, prevent popover from opening
208
+ var active = isActivatorInert ? false : activeProp;
204
209
  var _useState = _sliced_to_array((0, _react.useState)(_object_spread({
205
210
  visibility: "hidden",
206
211
  opacity: 0,
@@ -210,6 +215,25 @@ var styles = (0, _tailwindvariants.tv)({
210
215
  }, zIndexOverride ? {
211
216
  zIndex: zIndexOverride
212
217
  } : {})), 2), popoverStyle = _useState[0], setPopoverStyle = _useState[1];
218
+ // Reset popover style to hidden when it becomes active - this prevents flash
219
+ // of stale position from previous open. useLayoutEffect ensures this happens
220
+ // synchronously before paint.
221
+ (0, _react.useLayoutEffect)(function() {
222
+ if (active) {
223
+ setPopoverStyle(_object_spread({
224
+ visibility: "hidden",
225
+ opacity: 0,
226
+ position: "absolute",
227
+ top: 0,
228
+ left: 0
229
+ }, zIndexOverride ? {
230
+ zIndex: zIndexOverride
231
+ } : {}));
232
+ }
233
+ }, [
234
+ active,
235
+ zIndexOverride
236
+ ]);
213
237
  (0, _react.useEffect)(function() {
214
238
  if (active) {
215
239
  _PopoverManager.default.open(idRef.current);
@@ -264,7 +288,7 @@ var styles = (0, _tailwindvariants.tv)({
264
288
  } else if (preferredAlignment === "right") {
265
289
  left = fixed ? activatorRect.right - popoverRect.width : activatorRect.right + window.scrollX - popoverRect.width;
266
290
  } else if (preferredAlignment === "center") {
267
- left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2;
291
+ left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset;
268
292
  }
269
293
  } else if (position === "above") {
270
294
  top = fixed ? activatorRect.top - popoverRect.height - TOP_SPACING : activatorRect.top + window.scrollY - popoverRect.height - TOP_SPACING;
@@ -273,7 +297,7 @@ var styles = (0, _tailwindvariants.tv)({
273
297
  } else if (preferredAlignment === "right") {
274
298
  left = fixed ? activatorRect.right - popoverRect.width : activatorRect.right + window.scrollX - popoverRect.width;
275
299
  } else if (preferredAlignment === "center") {
276
- left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2;
300
+ left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset;
277
301
  }
278
302
  } else if (position === "cover") {
279
303
  top = fixed ? activatorRect.top : activatorRect.top + window.scrollY;
@@ -282,7 +306,7 @@ var styles = (0, _tailwindvariants.tv)({
282
306
  } else if (preferredAlignment === "right") {
283
307
  left = fixed ? activatorRect.right - popoverRect.width : activatorRect.right + window.scrollX - popoverRect.width;
284
308
  } else if (preferredAlignment === "center") {
285
- left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2;
309
+ left = fixed ? activatorRect.left + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset : activatorRect.left + window.scrollX + activatorRect.width / 2 - popoverRect.width / 2 + horizontalOffset;
286
310
  }
287
311
  }
288
312
  // Fallback adjustments for vertical positioning (when not in left/right mode)
@@ -295,11 +319,23 @@ var styles = (0, _tailwindvariants.tv)({
295
319
  }
296
320
  }
297
321
  // Fallback adjustments for horizontal positioning
298
- if (left + popoverRect.width > windowWidth) {
299
- left = windowWidth - popoverRect.width - EDGE_SPACING;
300
- }
301
- if (left < 0) {
302
- left = EDGE_SPACING;
322
+ // Only apply edge constraints if we're not using center alignment with custom offset
323
+ // This allows center alignment to work even near edges
324
+ if (preferredAlignment !== "center" || horizontalOffset === 0) {
325
+ if (left + popoverRect.width > windowWidth) {
326
+ left = windowWidth - popoverRect.width - EDGE_SPACING;
327
+ }
328
+ if (left < 0) {
329
+ left = EDGE_SPACING;
330
+ }
331
+ } else {
332
+ // For center alignment with offset, still respect edges but try to maintain centering
333
+ if (left + popoverRect.width > windowWidth) {
334
+ left = Math.max(EDGE_SPACING, windowWidth - popoverRect.width - EDGE_SPACING);
335
+ }
336
+ if (left < 0) {
337
+ left = EDGE_SPACING;
338
+ }
303
339
  }
304
340
  // Fallback adjustments for vertical positioning (when in left/right mode)
305
341
  if (position === "left" || position === "right") {
@@ -339,7 +375,8 @@ var styles = (0, _tailwindvariants.tv)({
339
375
  observer.disconnect();
340
376
  };
341
377
  }, [
342
- active
378
+ active,
379
+ debounceResizeObserver
343
380
  ]);
344
381
  (0, _react.useLayoutEffect)(function() {
345
382
  if (!active || !popoverRef.current) return;
@@ -384,7 +421,7 @@ var styles = (0, _tailwindvariants.tv)({
384
421
  }
385
422
  };
386
423
  var handleResize = function handleResize() {
387
- if (onClose) {
424
+ if (onClose && closeOnResize) {
388
425
  onClose();
389
426
  }
390
427
  };
@@ -427,7 +464,15 @@ var styles = (0, _tailwindvariants.tv)({
427
464
  className: "Litho-PopoverActivator ".concat(activatorDisplayType),
428
465
  children: memoizedActivator
429
466
  }),
430
- active && typeof children !== "undefined" && /*#__PURE__*/ (0, _reactdom.createPortal)(/*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
467
+ active && typeof children !== "undefined" && (usePortal ? /*#__PURE__*/ (0, _reactdom.createPortal)(/*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
468
+ className: "@container",
469
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
470
+ ref: popoverRef,
471
+ style: popoverStyle,
472
+ className: "".concat(classes).concat(className ? " ".concat(className) : ""),
473
+ children: children
474
+ })
475
+ }), document.body) : /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
431
476
  className: "@container",
432
477
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
433
478
  ref: popoverRef,
@@ -435,7 +480,7 @@ var styles = (0, _tailwindvariants.tv)({
435
480
  className: "".concat(classes).concat(className ? " ".concat(className) : ""),
436
481
  children: children
437
482
  })
438
- }), document.body)
483
+ }))
439
484
  ]
440
485
  });
441
486
  }
@@ -203,6 +203,10 @@ var contentStyles = (0, _tailwindvariants.tv)({
203
203
  }),
204
204
  labelAction && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Link.default, {
205
205
  onClick: labelAction.onAction,
206
+ insight: labelAction.insight,
207
+ icon: labelAction.insight ? _polarisicons.MagicMajor : undefined,
208
+ color: labelAction.insight ? "insight" : undefined,
209
+ disabled: labelAction.disabled,
206
210
  removeUnderline: true,
207
211
  children: labelAction.content
208
212
  })